This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Fix assembly of Thumb pcrel LDRs against global symbols


On Wed, Mar 2, 2011 at 3:18 PM, Richard Earnshaw <rearnsha@arm.com> wrote:
>
> On Mon, 2011-02-28 at 12:59 +0000, Richard Sandiford wrote:
>> The PC-relative LDR instructions have no associated relocation,
>> so even LDRs for global symbols should be resolved by the assembler.
>> We currently handle this correctly for single-register ARM loads,
>> but we're missing the associated relocation types for LDRD and Thumb.
>> This leads to errors like:
>
> I'm not sure I agree with this. ?If I write
>
> .global foo
>
> ...
> ? ? ? ?ldr r0, foo
>
> ...
>
> foo:
> ? ? ? ?...
>
> but then at link/load time pre-empt foo with some other definition, that
> will silently leave me with the wrong answer.
>
> It's clearly OK to do this if foo is protected or private, but not
> otherwise.
>
> The existing error message is not very informative as to what the
> problem is, but I'm not sure I agree with your solution...
>
> R.

That's a reasonable argument, but similarly to the ADR case, such code
referencing global symbols is already allowed through for ARM _with no
relocation emitted_  - the Thumb treatment is inconsistent and causes
existing code which builds for ARM (but where symbol preemption may
silently fail to work).  This raises non-trivial questions about which
behaviour should be considered correct.

Unless there are some tricks I haven't thought of, it seems that ADR
and PC-relative LDR and friends simply can't be generally preemptible,
period -- because the relocation ranges these instructions support
just aren't large enough to guarantee that a relocation on these
instructions can ever be resolved except in a trivial subset of cases.

Solving this inconsistency requires a conclusion regarding whether the
tools should trust the programmer to write correct code (i.e., fix up
pc-relative LDR and ADR in the assembler and emit no relocation) or
not (i.e., treat these instructions as an error when they reference
global symbols).

I would regard the latter option as incorrect, since it's quite
legitimate to use these instructions in this way in static
environments, and the assembler doesn't have knowledge of whether this
is what's intended.  There is also a possible middle way of allowing
the instructions but always emitting a relocation, which allows a tool
further down the line to check whether the preemption is needed and
throw an error if so.  Since the Linux kernel doesn't do symbol
preemption it can safely ignore these relocations with a tiny amount
of extra code in the module loader.

In general, we can't expect to prevent absolutely the programmer from
doing something that generates the wrong answer, such as writing
non-PIC code and the linking it into a PIC environment; though any
diagnostics which help avoid this happening by accident are still
useful.


To give the flipside to this, there is a case when the assembler knows
that preemption absolutely must be supported, and that is when a
symbol is declared weak.  So:

.type g, %function
g:	ldr	r0, d

	.weak d
d:	.long 0

$ arm-linux-gnueabi-as --version
GNU assembler (GNU Binutils for Ubuntu) 2.20.51.20100908
[...]
$ arm-linux-gnueabi-as -o tst.o tst.s && arm-linux-gnueabi-objdump -drt tst.o
tst.s: Assembler messages:
tst.s:2: Error: internal_relocation (type: OFFSET_IMM) not fixed up
$ arm-linux-gnueabi-as -mthumb -o tst.o tst.s &&
arm-linux-gnueabi-objdump -drt tst.o
tst.s: Assembler messages:
tst.s:2: Error: invalid offset, value too big (0xFFFFFFFFFFFFFFFC)

i.e., cryptic error messages aside, the assembler fails sensibly for
ARM and Thumb alike.  This is appropriate, because ".weak" declares
explicitly the need for preemptibility, and the assembler knows that
can't be guaranteed with these instruction forms.  ".globl" does not,
because where the symbol is not weak it's impossible for the assembler
to know what the programmer intends, and so throwing an error for such
cases seems too zealous from my point of view.

For comparison, changing ".weak" to ".globl" in the above example gives:
$ arm-linux-gnueabi-as -o tst.o tst.s && arm-linux-gnueabi-objdump -drt tst.o
[...]
00000004 g       .text	00000000 d
[...]
00000000 <f>:
   0:	e51f0004 	ldr	r0, [pc, #-4]	; 4 <d>
00000004 <d>:
   4:	00000000 	.word	0x00000000

$ arm-linux-gnueabi-as -mthumb -o tst.o tst.s &&
arm-linux-gnueabi-objdump -drt tst.o
tst.s: Assembler messages:
tst.s:2: Error: invalid offset, value too big (0xFFFFFFFFFFFFFFFC)

Notice that the ARM code refernce to d is not preemptible.

Cheers
---Dave


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]