27

I was recently building a certain shared library (ELF) targeting x86-64 architecture, like this:

g++ -o binary.so -shared --no-undefined ... -lfoo -lbar

This failed with the following error:

relocation R_X86_64_32 against `a local symbol' can not be used when making a shared object; recompile with -fPIC

Of course, it means I need to rebuild it as position-independent code, so it's suitable for linking into a shared library.

But this works perfectly well on x86 with exactly the same build arguments. So the question is, how is relocation on x86 different from x86-64 and why don't I need to compile with -fPIC on the former?

Alex B
  • 82,554
  • 44
  • 203
  • 280
  • 1
    I've never understood this. If the compiler can tell you exactly what option to use automatically, why does it require that you say magic words to make it operate correctly? Grrr.. – Billy ONeal Aug 02 '10 at 03:25
  • @Billy ONeal, Now I believe that's the case of leaky abstraction. They differ in how they load global data, which affects whether PIC is needed or not. – Alex B Aug 02 '10 at 03:40
  • I understand the need for the difference. What I don't understand is why you need to give the compiler a switch to make it do that. – Billy ONeal Aug 02 '10 at 12:12
  • 1
    @Billy, the error comes from the linker – Gearoid Murphy Sep 25 '12 at 15:19
  • @GearoidMurphy: Okay, 6 of this, half dozen of another. The compilation was invoked with a single call to g++ (rather than calls to the compiler and linker separately), so g++ should easily be able to tell if some compiler option is going to require a corresponding linker option. – Billy ONeal Sep 25 '12 at 16:32
  • @BillyONeal I agree the command line driver, in the specific case where all source code is provided and linked at the same time, could deal with it. However, the command line in the question brings foo and bar libs, which could just be static archives of objects compiled without -fPIC. Also, we don't know whether the [...] part contains objects or sources. For any non-small build, you do want separate compilation. For fast incremental rebuild, you do want separate compilation. – Johan Boulé Dec 11 '20 at 22:04

3 Answers3

22

I have found a nice and detailed explanation, which boils down to:

  1. x86-64 uses IP-relative offset to load global data, x86-32 cannot, so it dereferences a global offset.
  2. IP-relative offset does not work for shared libraries, because global symbols can be overridden, so x86-64 breaks down when not built with PIC.
  3. If x86-64 built with PIC, the IP-relative offset dereference now yields a pointer to GOT entry, which is then dereferenced.
  4. x86-32, however, already uses a dereference of a global offset, so it is turned into GOT entry directly.
Alex B
  • 82,554
  • 44
  • 203
  • 280
5

It is a code model issues. By default, static code is build assuming the whole program will stay in the lower 2G part of the memory address space. Code for shared libraries need to be compiled for another memory model, either PIC, or with -mcmodel=large which will compile without making that assumption.

Note that -mcmodel=large is not implemented in older gcc version (it is in 4.4, it isn't in 4.2, I don't know for 4.3). .

AProgrammer
  • 51,233
  • 8
  • 91
  • 143
  • This makes sense - the 32 bit absolute address can't be turned into a relative relocation, because the load address of the library might be > 2GB. – caf Jun 30 '10 at 07:13
  • Yes, I understand that position-independent code needs to be different in calculating jump offsets but I struggle to understand why it *does* work on x86 *without* `-fpic`. – Alex B Jul 03 '10 at 14:54
  • @Alex, the dynamic loader is able to handle some but not all relocation records and the reason for which some relocation records are not handled is that they assume a situation which isn't true. There is only one non PIC 32 bit memory model and that model use only handled relocation records. There are several non PIC 64 bit memory models, some compatible with dynamic relocation, some not. If you use -mcmodel=large with gcc 4.4, you don't need -fpic. – AProgrammer Jul 16 '10 at 09:58
1

It's purely an arbitrary requirement the ABI people have imposed on us. There's no logical reason why the dynamic linker on x86_64 couldn't support non-PIC libraries. However, since x86_64 is not under such horrible register pressure as x86 (and has better features for PIC), I don't know of any significant reason not to use PIC.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • Excepted that it does support non-PIC libraries. It doesn't support some relocation records because usually .so are loaded in such a way that the assumptions made by these relocation records are not valid, but if you don't make use of them, there is no problem. – AProgrammer Jun 30 '10 at 07:15