[PATCH 02/30] ARM: assembler: introduce adr_l, ldr_l and str_l macros
Dave Martin
Dave.Martin at arm.com
Mon Aug 14 08:50:02 PDT 2017
On Mon, Aug 14, 2017 at 04:38:02PM +0100, Ard Biesheuvel wrote:
> On 14 August 2017 at 16:29, Dave Martin <Dave.Martin at arm.com> wrote:
> > On Mon, Aug 14, 2017 at 01:53:43PM +0100, Ard Biesheuvel wrote:
> >> Like arm64, ARM supports position independent code sequences that
> >> produce symbol references with a greater reach than the ordinary
> >> adr/ldr instructions.
> >>
> >> Currently, we use open coded instruction sequences involving literals
> >> and arithmetic operations. Instead, we can use movw/movt pairs on v7
> >> CPUs, circumventing the D-cache entirely. For older CPUs, we can emit
> >> the literal into a subsection, allowing it to be emitted out of line
> >> while retaining the ability to perform arithmetic on label offsets.
> >>
> >> E.g., on pre-v7 CPUs, we can emit a PC-relative reference as follows:
> >>
> >> ldr <reg>, 222f
> >> 111: add <reg>, <reg>, pc
> >> .subsection 1
> >> 222: .long <sym> - (111b + 8)
> >> .previous
> >>
> >> This is allowed by the assembler because, unlike ordinary sections,
> >> subsections are combined into a single section into the object file,
> >> and so the label references are not true cross-section references that
> >> are visible as relocations. Note that we could even do something like
> >>
> >> add <reg>, pc, #(222f - 111f) & ~0xfff
> >> ldr <reg>, [<reg>, #(222f - 111f) & 0xfff]
> >> 111: add <reg>, <reg>, pc
> >> .subsection 1
> >> 222: .long <sym> - (111b + 8)
> >> .previous
> >
> > This is reinventing ldr=
> >
> > I seem to remember ldr= barfing on things that .long happily accepts
> > though, was this the reason?
> >
>
> Yes. ldr = does not accept expressions involving symbols, only plain
> symbols or expressions that evaluate to constants.
>
> So something like
>
> ldr <reg>, =<sym> - <label>
>
> is rejected while the equivalent
>
> ldr <reg>, 0f
> 0: .long <sym> - <label>
>
> does work.
I wouldn't bother trying to rationalise gas' behaviour here. I think
it's an accident of implementation rather than there being some
fundamental reason for it.
AFAICT gas could quite happily resolve ldr= in exactly the same way as
.long and thus not have this problem. But we can't rewrite history.
[...]
> >> + .macro __adldst_l, op, reg, sym, tmp, c
> >> + .if __LINUX_ARM_ARCH__ < 7
> >> + ldr\c \tmp, 111f
> >> + .subsection 1
> >> + .align 2
> >> +111: .long \sym - (222f + ARM_PC_BIAS)
> >
> > See above comment about ldr=.
> >
> >> + .previous
> >> + .else
> >> + W(movw\c\()) \tmp, #:lower16:\sym - (222f + ARM_PC_BIAS)
> >> + W(movt\c\()) \tmp, #:upper16:\sym - (222f + ARM_PC_BIAS)
> >
> > Why W()?
> >
> > There are no narrow forms of these instructions anyway -- if there were
> > then they couldn't accommodate a 16-bit immediate.
> >
>
> That's a trick, actually, which I failed to add a comment for.
>
> We use .arm sections in the thumb2 kernel, and using these macros
> there would result in the wrong offset to be used. Adding the .w
> suffix forces an error in the assembler which even results in a fairly
> meaningful error message complaining about using .w in ARM code.
Ewww... I think it'd be best to add a comment explaining that.
There's a fair change someone will trip over this at some point (or
worse, "fix" the assembly errors).
>
> >> + .endif
> >> +222:
> >> + .ifc \op, add
> >> + add\c \reg, \tmp, pc
> >> + .elseif CONFIG_THUMB2_KERNEL == 1
> >> + add \tmp, \tmp, pc
> >> + \op\c \reg, [\tmp]
> >
> > Shame
> > \op\c \reg, [pc, \tmp]
> > doesn't work.
> >
> > But it doesn't, apparently.
> >
>
> No, thumb2 does not allow that
Meh. Oh well.
[...]
Cheers
---Dave
More information about the linux-arm-kernel
mailing list