Recently started looking at Position Independent Code (PIC) under Nasm
, Uasm
and gcc
.
Looks like PIC is achieved for shared objects since
rel
keyword. (minimal support for GOT/PLT)RIP-relative
addressing in 64-bit mode. (no support for GOT/PLT)-fPIC
option. (full support for GOT/PLT)For example, all these programs below create PIC shared objects containing an Add
function and when loaded by the main program, it calls Add(2,3)
which outputs
./libadd_nasm.so Adding: 5
./libadd_uasm.so Adding: 5
./libadd_c.so Adding: 5
add_nasm.asm
;; nasm -f elf64 add_nasm.asm
;; ld -shared -o libadd_nasm.so add_nasm.o
section .data
firstArg dq 0
section .text
global _add
_add:
; Position-Independent Code (PIC)
xor rax,rax
mov [rel firstArg], rdi
add rax, [rel firstArg]
add rax, rsi
ret
add_uasm.asm
;; uasm -elf64 -Fo=add_uasm.o add_uasm.asm
;; ld -shared -o libadd_uasm.so add_uasm.o
.data
firstArg dq 0
.code
_add proc
; Position-Independent Code (PIC)
xor rax,rax
mov [firstArg], rdi
add rax, [firstArg]
add rax, rsi
ret
_add endp
end
add_c.c
// gcc -shared -o libadd_c.so add_c.c -fPIC
#include <stdint.h>
static int firstArg = 0;
int _add(int a, int b) {
firstArg = a;
return firstArg + b;
}
main.c
// gcc -o main main.c -ldl
#include <stdio.h>
#include <dlfcn.h>
void load_and_run(const char *so_name) {
int (*add)(int, int);
void *handle = dlopen(so_name, RTLD_LAZY);
*(void **)(&add) = dlsym(handle, "_add");
printf("%s Adding: %d\n", so_name, add(2, 3));
dlclose(handle);
}
int main() {
load_and_run("./libadd_nasm.so");
load_and_run("./libadd_uasm.so");
load_and_run("./libadd_c.so");
return 0;
}
Question
Notice that add_c.c
has this line of code
static int firstArg = 0;
and running this command without -fPIC does not generate any errors
gcc -shared -o libadd_c.so add_c.c
However, if the code is changed to remove the static
keyword
int firstArg = 0;
then it produces these errors
/usr/bin/ld: /tmp/cc9pDDY7.o: warning: relocation against `firstArg' in read-only section `.text'
/usr/bin/ld: /tmp/cc9pDDY7.o: relocation R_X86_64_PC32 against symbol `firstArg' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: bad value
collect2: error: ld returned 1 exit status
Why does static
seem to have an affect on PIC?
Why does static seem to have an affect on PIC?
When no static variables are defined, no global .data segment is produced for the library, so all the data is stored in the stack and no global position for the library data is required.
If the global .data segment has some variables, the dynamic linker has to map the data segment, according to the main program to which the dynamic shared is positioned (this can be different for different executables) and so the references to all the relocatable modules have to be solved at runtime.
The difference is a table of relocations that have to be done at load time to be able to locate all relocatable symbols. When pic is specified, this table is included, and used by the dlopen() routine, and if not specified you get the error.
This also happens to code, but in most compilers, branches happen to be relative to PC register and need of absolute branches for local code is seldom needed.
pic option is well documented in gcc manual (the texinfo manual page, not the man page) and will tell you that, when pic is specified, all data local to that module is referenced through a common register value that is dedicated to the module.