clinuxassemblygccfpic

Creating a Linux shared object .so without using -fPIC


Recently started looking at Position Independent Code (PIC) under Nasm, Uasm and gcc.

Looks like PIC is achieved for shared objects since

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?


Solution

  • 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.