Arm, Rlibs, and Relocations

Hi,

I'm working on bringing up a new Arm target and I'm trying to figure out where and how some symbol relocations are getting generated. I know I can control options passed to the cross-compiling compiler/linker for the final linking using the *-link-args options in the target specification, but what I'm really interested in is how to influence the generation of the Rust library (or archive?) rlib files. These files currently carry a type of relocation (R_ARM_GOT_PREL) which seems to be incompatible with my target.

The default target relocation-model of pic seems to be the direction I'd like, however the default target2 relocation type under that model appears to be R_ARM_GOT_PREL and I would like to modify that. The LLVM linker has a known --target2 flag which can be used to change target2 relocations to either R_ARM_ABS32 or R_ARM_REL32. However it is not clear if that flag is even applicable to the .rlib generation process.

Any direction on where .rlibs are generated/emitted, what options are available to modify this process, or thoughts on their symbol relocations would be much appreciated!

.rlibs are native static libraries combined with Rust-specific metadata. Static libraries, however, are glorified archives of object files; there's no actual linking step involved.

rustc will invoke the linker when generating a bin, dylib, or cdylib output, but it currently always uses the system-installed linker rather than supplying its own.

I believe that target2 is a red herring. R_ARM_TARGET2 is a type of relocation which can appear in object files; the linker will then treat it as if it were one of R_ARM_ABS32, R_ARM_REL32, or R_ARM_GOT_PREL, and the --target2 option controls which. Normally, that won't result in R_ARM_GOT_PREL actually appearing in any files, because it's a relocation type which is meant to be resolved to a static value at link time. But you're seeing an actual instance of R_ARM_GOT_PREL, and it's in an object file (that's inside the rlib), not a linker output. That implies that the compiler-generated assembly is using GOT_PREL directly, not via TARGET2.

And indeed, looking at LLVM's source...

unsigned ARMFastISel::ARMLowerPICELF(const GlobalValue *GV,
                                     unsigned Align, MVT VT) {
  bool UseGOT_PREL = !TM.shouldAssumeDSOLocal(*GV->getParent(), GV);

It seems that LLVM always uses GOT_PREL for relocations against symbols that potentially come from a different shared library, when targeting ARM ELF with PIC enabled.

The odd part is that GCC never uses GOT_PREL. There was a patch for it all the way back in 2011, but it seems to have never found its way to upstream GCC.

According to that patch submission, GOT_PREL is more efficient than the older way of doing GOT loads, but requires ld from binutils 2.21 or later, which was released in 2010. Is your target using some ancient linker? Or a custom one? :slight_smile:

(Also, as esoteric as this is, the question is still better suited for users.rust-lang.org, not internals.)

1 Like

Thank you for that detailed reply! You broke some of my fundamental assumptions, which ended up really helping me along.

My target is a Cortex-M3 running uClinux. It turns out to not be compatible with anything using a PIC relocation model.

Also yes I realized that this was probably more suited for the users forum. I knew I was taking a gamble posting it here haha.