Why does gcc force PIC for 64-bit x64 shared libraries?

Attempting to compile non-PIC code into an x64 shared library with gcc results in an error, for example:

 /usr/bin/ld: /tmp/ccQ2ttcT.o: relocation R_X86_64_32 against `a local symbol' can not be used when making a shared object; recompile with -fPIC 

This question is about why this is so. I know that x64 has a RIP-relative addressing that was designed to improve the performance of PIC code. However, this does not mean that time transfer cannot (theoretically) be applied to such a code.

Some online sources, including this one (which is widely cited on this subject) claim that there is some inherent restriction that prohibits non-PIC code in shared libraries, due to RIP-relative addressing. I do not understand why this is true.

Consider "old x86" - the call statement also has an operand related to IP. And yet, the x86 code with call in it compiles just fine into a shared library without a PIC, but with the help of moving R_386_PC32 load R_386_PC32 , Can't do the same for RIP-relative addressing of data in x64?

Please note that I fully understand the benefits of PIC code and also reduces the effectiveness of RIP relative addressing. However, I am curious why it is not allowed to use non-PIC code. Are there any real technical considerations behind this, or is it just an encouragement to write PIC code?

+7
source share
3 answers

Here is the best explanation I read from a post on comp.unix.programmer :

Shared libraries require a PIC on x86-64 or, more precisely, relocatable code must be PIC. This is due to the fact that the 32-bit operand of the direct address used in the code may require more than 32 bits after moving. If this happens, there is nowhere to write a new meaning.

+12
source

The thing is, the PIC and non-PIC code is still different.

Source C:

 extern int x; void func(void) { x += 1; } 

Build, not PIC:

 addl $1, x(%rip) 

Build with PIC:

 movq x@GOTPCREL (%rip), %rax addl $1, (%rax) 

Thus, it seems that the PIC code should go through the movement table in order to access global variables. It actually needs to do the same for functions, but it can perform functions through stubs created during the connection. This is transparent at the assembly level, but access to global variables is not. (If you need a function address, then PIC and non-PIC are different, as are global ones.) Note that if you change the code as follows:

 __attribute__((visibility("hidden"))) extern int x; 

In this case, since GCC knows that the character must be in the same object as the code, it emits the same code as the version other than the PIC.

+2
source

Just say something else.

In the url mentioned in the question , it mentions that you can pass -mcmodel=large to gcc to tell the compiler to generate a 64-bit operand address for your code.

So gcc -mcmodel=large -shared ac will generate a non-PIC-shared object.

-

Demonstrations:

ac:

 #include <stdio.h> void foo(void) { printf("%p\n", main); } 

The 32-bit operand of the immediate address blocks the creation of a non-PIC object.

 xiami@gentoo ~ $ cc -shared -o a.so ac /usr/lib/gcc/x86_64-pc-linux-gnu/4.8.4/../../../../x86_64-pc-linux-gnu/bin/ld: /tmp/cck3FWeL.o: relocation R_X86_64_32 against `main' can not be used when making a shared object; recompile with -fPIC /tmp/cck3FWeL.o: error adding symbols: Bad value collect2: error: ld returned 1 exit status 

Use -mcmodel=large to solve this problem. (Warnings only appear on my system because a change in .text is prohibited by my PaX kernel.)

 xiami@gentoo ~ $ cc -mcmodel=large -shared -o a.so ac /usr/lib/gcc/x86_64-pc-linux-gnu/4.8.4/../../../../x86_64-pc-linux-gnu/bin/ld: /tmp/ccZ3b9Xk.o: warning: relocation in readonly section `.text'. /usr/lib/gcc/x86_64-pc-linux-gnu/4.8.4/../../../../x86_64-pc-linux-gnu/bin/ld: warning: creating a DT_TEXTREL in object. 

Now you can see that the type of move record is R_X86_64_64 instead of R_X86_64_32, R_X86_64_PLT32, R_X86_64_PLTOFF64.

 xiami@gentoo ~ $ objdump -R a.so a.so: file format elf64-x86-64 DYNAMIC RELOCATION RECORDS OFFSET TYPE VALUE ... 0000000000000758 R_X86_64_64 printf ... 

And on my system, associate this shared object with regular code and run the program to fix errors, for example: ./a.out: error while loading shared libraries: ./a.so: cannot make segment writable for relocation: Permission denied

This proves that the dynamic loader redirects to .text , which does not have a PIC library.

+1
source

All Articles