[ARM] Add support for embedded position-independent code

Description

[ARM] Add support for embedded position-independent code

This patch adds support for some new relocation models to the ARM
backend:

  • Read-only position independence (ROPI): Code and read-only data is accessed PC-relative. The offsets between all code and RO data sections are known at static link time. This does not affect read-write data.
  • Read-write position independence (RWPI): Read-write data is accessed relative to the static base register (r9). The offsets between all writeable data sections are known at static link time. This does not affect read-only data.

These two modes are independent (they specify how different objects
should be addressed), so they can be used individually or together. They
are otherwise the same as the "static" relocation model, and are not
compatible with SysV-style PIC using a global offset table.

These modes are normally used by bare-metal systems or systems with
small real-time operating systems. They are designed to avoid the need
for a dynamic linker, the only initialisation required is setting r9 to
an appropriate value for RWPI code.

I have only added support to SelectionDAG, not FastISel, because
FastISel is currently disabled for bare-metal targets where these modes
would be used.

Differential Revision: https://reviews.llvm.org/D23195

Details

Committed
olista01Aug 8 2016, 8:28 AM
Differential Revision
D23195: [ARM] Add support for embedded position-independent code
Parents
rL278014: [CodeGeneration] Do not set insert position redundantly
Branches
Unknown
Tags
Unknown
alevy added a subscriber: alevy.Sep 5 2016, 11:39 AM

First off, I'm very excited about this change!! Have been hoping for it to go through for a while.

A question, though.

"They are designed to avoid the need
for a dynamic linker, the only initialisation required is setting r9 to
an appropriate value for RWPI code."

Is this accurate? If so, how?

In GCC's equivalent -msingle-pic-base, it's actually not 100% sufficient to set r9, since some relocatable objects are still not resolved relative to r9 at compile time. For example:

const char* str = "Hello";

Ends up as a static reference to RO memory. If RO memory is relocated at load time, it breaks. The same is true for internal RW references, e.g:

int foo = 123;
int* bar = &foo;

This is fixable by storing a minimal relocation section that includes such references that still need fixup. The result is _still_ useful in that the dynamic linker is minimal, and (more importantly?) doesn't require modifications to the RO section which is, ideally, stored in flash.

There are a few ways this can be handled:

  1. Don't do anything special, cases like this will result in a link failure, and the user will have to change their code.
  2. Use a small dynamic linker and relocation table, as you suggest. This requires linker and runtime support, but I'm not aware of any linker which currently implements this. This has the advantage over SysV-style PIC that only global variables initialised to the address of another global need to be in the relocation table, rather than every global. It also avoids the need for a GOT in the RW section. This still can't handle the case where a variable requiring relocation is in an RO section, because the loader won't be able to modify it.
  3. Emit initialisation code in the compiler, to be called from the .init_array section (similar to C++ dynamic initialisers). We have a downstream patch to do this in clang, and this is also the way armcc (our previous bare-metal compiler) solved this problem. The patch is attached to my original RFC email (http://lists.llvm.org/pipermail/llvm-dev/2015-December/093022.html), but the consensus is that the community does not want to maintain this change in clang. This is slightly more powerful than option 2, as it can move values from the RO to RW section if they need a runtime initialiser. However, this only works if that value is only accessed in the same translation unit in which it is defined, as other translation units won't know that it should now be accessed r9-relative.
alevy added a comment.Apr 28 2017, 4:32 PM

Is there an example somewhere of linking a program with feature?

With clang 4.0.0, the object code emitted looks right, but I'm having trouble compiling a basic example due to seeming incompatibilities with how the GNU linker expects the object files to look. I'm getting similar results in a port of the Rust compiler to use these relocation models as well (so directly with the LLVM library vs through clang).

(Sorry if this isn't the right place to ask this)

test.c:

static volatile int foo = 0;

int bar() {
  return foo;
}

void _start() {
  while (bar() != 3) {}
}

Compiling the object file goes without a hitch:

$ clang --target=thumbv7-eabi -frwpi -c -o test.clang.o test.c

but linking is a problem:

$ arm-none-eabi-ld -o test test.clang.o
test.clang.o: In function `bar':
test.c:(.text+0x10): dangerous relocation: unsupported relocation

The relocations in the ELF look a bit different between a GCC compiled version and a CLANG compiled version:

clang:

Relocation section '.rel.text' at offset 0x1b4 contains 2 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
00000010  00000809 R_ARM_SBREL32     00000000   foo
00000020  00000b1c R_ARM_CALL        00000000   bar

arm-none-eabi-gcc:

Relocation section '.rel.text' at offset 0x1e0 contains 4 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
00000020  00000028 R_ARM_V4BX
00000024  0000061a R_ARM_GOT_BREL    00000000   foo
00000034  00000c1c R_ARM_CALL        00000000   bar
00000050  00000028 R_ARM_V4BX

In particular, it looks like clang is emitting R_ARM_SBREL32 instead of R_ARM_GOT_BREL.