[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