Does 32bit x86 code need to be specially PIC-compiled for shared library files?

linux shared-libraries (2)

Compiling code to an object file needs to be done position-independent if the object file is intended to be loaded as a shared library (.so), because the base virtual address that the shared object file is loaded into in different processes may be different.

Now I didn't encounter errors when I tried to load an .so file compiled and linked without the -fpic GCC option on 32bit x86 computers, while it fails on 64bit bit x86 computers.

Random websites I found say that I don't need -fpic on 32bit because code compiled without -fpic works by coincidence according to the X86 32bit ABI also when used in a position-independent manner. But I still found software that ship with separate versions of libraries in their 32bit versions: One for PIC, and one for non-PIC. For example, the intel compiler ships with libirc.a and libirc_pic.a, the latter being compiled for position-independent mode (if one wants to link that .a file into an .so file).

I wonder what the precise difference between using -fpic and not using it is for 32bit code, and why some packages, like the intel compiler, still ship with separate versions of libraries?

Answer #1

It's not that non-PIC code works "by coincidence" on x86 (32-bit). It's that the dynamic linker for x86 supports the necessary "textrels" needed to make it work. This comes at a very high cost in memory consumption and startup time, since basically the entire code segment must be patched up at load time (and thus becomes non-shareable memory).

The dynamic linker maintainers claim that non-PIC shared libraries can't be supported on x86_64 because of fundamental issues in the architecture (immediate address displacements can't be larger than 32-bit) but this issue could be easily solved by just always loading libraries in the first 4gb of virtual address space. Of course PIC code is very inexpensive on x86_64 (PIC isn't a performance-killer like it is on 32-bit x86) so they're probably right to keep it unsupported and prevent fools from making non-PIC libraries...

Answer #2

the base virtual address that the shared object file is loaded into in different processes may be different

Because shared objects usually load at their preferred address, they may appear to work correctly. But fPIC is a good idea for all shared code.

I believe the reason that there aren't often two versions of the library is that many distributions use fPIC as the default for all code.