The gcc documentation states, "This option makes a difference on AArch64, m68k, PowerPC and SPARC." I don't see the x86 or x64 architectures listed. AArch64 is related to ARM processors isn't it? If compiling for the linux_x86_x64 then does this option produce any value?
The text you're quoting is from the GCC manual entry for -fPIC
which directly follows the entry for -fpic
. When they say "makes a difference", they mean compared to -fpic
.
-fpic
Generate position-independent code (PIC) suitable for use in a shared library, if supported for the target machine. Such code accesses all constant addresses through a global offset table (GOT). The dynamic loader resolves the GOT entries when the program starts (the dynamic loader is not part of GCC; it is part of the operating system). If the GOT size for the linked executable exceeds a machine-specific maximum size, you get an error message from the linker indicating that -fpic does not work; in that case, recompile with -fPIC instead. (These maximums are 8k on the SPARC, 28k on AArch64 and 32k on the m68k and RS/6000. The x86 has no such limit.)...
-fPIC
If supported for the target machine, emit position-independent code, suitable for dynamic linking and avoiding any limit on the size of the global offset table. This option makes a difference on AArch64, m68k, PowerPC and SPARC....
x86-64 can't save instructions or machine-code size with limited-range displacements because call/jump and RIP-relative addressing use rel32
32-bit displacements, so they can reach anywhere within +-2GiB of the instruction. That's the full size allowed in the "small" and "small-PIC" code models, which is enough for basically anything, except sometimes huge arrays in .bss
. ("medium" and "large" use 64-bit absolute addresses for arrays over a certain threshold, or for everything which makes every function call an indirect jump).
x86-64 does have jmp rel8
(but not call rel8
), but compilers will only use that if one function happens to tailcall another in the same compilation unit, thus the instructions assemble into the same object file and the assembler can see the distance at assemble time and can choose a shorter branch encoding. Jumps to code outside the current object file always get assembled to jmp rel32
, because jmp rel8
would rarely fit, only having -128..+127 byte range. (And there's no way for the linker to make an instruction bigger if it needs more room; that would change the distance between instructions before and after it, which probably don't have relocation info since the assembler assumed distances were constant.)
If you're compiling a shared library, yes you should use -fPIC
. It's not different from -fpic
on x86-64, but you need one or the other.
Although with modern distros configuring GCC with -fPIE
as the default, the main difference on x86-64 is I think assuming symbol interposition is possible for global variables that aren't __attribute__((visibility("hidden")))
.
-fPIE
code-gen is suitable for shared libraries on x86-64 I think, but -fno-pie
or -fno-pic
isn't, and will give link errors when linking into a PIE or normal shared library. (See 32-bit absolute addresses no longer allowed in x86-64 Linux?)