I already compiled with -fPIC, so what's wrong?
This part of the error message is intended for people who link the code generated by the compiler.
You write asm manually, therefore, as datenwolf wrote correctly, when writing a shared library in an assembly, you should make sure that the code is position-independent.
This means that the file should not contain 32-bit absolute addresses (since moving to an arbitrary 64-bit base is impossible). 64-bit absolute movements are supported, but usually you should use this only for transition tables.
mov var1, %rcx uses the 32-bit absolute addressing mode . Normally you should never do this, even in position-dependent x86-64 code. Common use mov $var1, %edi for 32-bit absolute addresses: putting an address in a 64-bit register using mov $var1, %edi (zero expands in RDI)
and indexing static arrays: mov arr(,%rdx,4), %edx
mov var1(%rip), %rcx uses a relative RIP 32-bit offset . This is an efficient way to access static data, and compilers always use it even without -fPIE or -fPIC for static / global variables.
You have two options:
Normal static data that __attribute__((visibility("hidden"))) long var1; a library , such as C compilers, will create for __attribute__((visibility("hidden"))) long var1; -fno-PIC same as for -fno-PIC .
.data .globl var1
full code, -fPIC character arrangement, for example, compilers generated for -fPIC .
You must use the global offset table. This is how the compiler does it if you tell it to create code for a shared library. Please note that this leads to performance degradation due to additional indirectness.
See Sorry for the state of dynamic libraries on Linux for more information on character insertion and the overhead it imposes on code generation for shared libraries, unless you are careful about restricting character visibility to allow embedding.
var1@GOTPCREL is the address of the pointer to your var1 , the pointer itself is accessible using rip-relative addressing, and the contents (address var1 ) are filled in by the linker during loading the library. This supports the case when a program using your library defined var1 , so var1 in your library should resolve to this memory area, not to the .data or .bss (or .text ) of your .so .
.section .data .globl var1 # without .hidden var1: .quad 0x012345 .section .text .globl func1 func1: xor %eax, %eax mov var1@GOTPCREL (%rip), %rcx mov (%rcx), %rcx ret
See More Information at http://www.bottomupcs.com/global_offset_tables.html.
An example in the Godbolt compiler explorer for -fPIC and -fPIE shows the difference that -fPIE has a character arrangement to get the address of hidden non- global variables :
movl $x, %eax 5 bytes, -fno-pieleaq x(%rip), %rax 7 bytes, -fPIE and hidden globals or static with -fPICy@GOTPCREL (%rip), %rax 7 bytes and loading instead of just ALU, -fPIC with hidden global variables non-.
In fact, loading always uses x(%rip) , with the exception of non-hidden / non- static variables with -fPIC where it must first get the runtime address from GOT, because this is not an offset of the connection time constant relative to the code.
Related: 32-bit absolute addresses are no longer allowed on x86-64 Linux? (Pie executable files).
A previous version of this answer said that DATA and BSS segments can move relative to TEXT when loading a dynamic library. This is incorrect, only the base address of the library is moved. RIP-relative access to other segments in the same library is guaranteed to be in order, and compilers produce code that does this. ELF headers define how segments (which contain sections) should be loaded / mapped into memory.