Home > Enterprise >  relocation R_X86_64_32S against symbol `stdout@@GLIBC_2.2.5' can not be used when making a PIE
relocation R_X86_64_32S against symbol `stdout@@GLIBC_2.2.5' can not be used when making a PIE

Time:03-16

The problem

I'm currently reading this book and with the chapter about dynamic linking with the following code:

link_example.s

.globl main

.section .data

output:
    .ascii "Yeet\n\0"

.section .text
main:
    enter $0, $0
    movq stdout, %rdi
    movq $output, %rsi
    call fprintf

    movq $0, %rax
    leave
    ret

Now according to the book, I need to compile it as follows to link the C-library dynamically:

gcc -rdynamic link_example.s -o link_example

But I'm getting the following error message:

/usr/bin/ld: /tmp/cchUlvqS.o: relocation R_X86_64_32S against symbol `stdout@@GLIBC_2.2.5' can not be used when making a PIE object; recompile with -fPIE
/usr/bin/ld: failed to set dynamic section sizes: bad value
collect2: error: ld returned 1 exit status

What am I doing wrong?

What have you tried?

Adding -fPIE flag

I tried what the compiler suggested by adding the -fPIE flag:

gcc -rdynamic -fPIE link_example.s -o link_example

but I'm still getting the same error again.

Searching similar posts

I found a similar post which said, that I just need to use the -shared flag:

gcc -shared link_example.s -o link_example

but this gives me:

/usr/bin/ld: /tmp/ccxktZan.o: relocation R_X86_64_32S against symbol `stdout@@GLIBC_2.2.5' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: failed to set dynamic section sizes: bad value
collect2: error: ld returned 1 exit status

and if I add the -fPIC flag:

gcc -shared -fPIC link_example.s -o link_example

then I'm getting this:

/usr/bin/ld: /tmp/ccKIQ9sl.o: relocation R_X86_64_32S against symbol `stdout@@GLIBC_2.2.5' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: failed to set dynamic section sizes: bad value
collect2: error: ld returned 1 exit status

CodePudding user response:

Let me show you how to fix up the assembly language from your book so it works with your compiler's default settings.

As the comments on the question say, the problem is that your compiler defaults to generating position-independent executables. This means the addresses of stdout, fprintf, and output are not known at link time, so the linker is unable to "relocate" the instructions that refer to them.

What is known at link time, however, is the offsets between the addresses of these things and the program counter. That means, if you just write the assembly a little differently, it will work. Like this:

.globl main

.section .data

output:
    .ascii "Yeet\n\0"

.section .text
main:
    enter $0, $0
    movq stdout(%rip), %rdi
    leaq output(%rip), %rsi
    call fprintf@PLT

    movq $0, %rax
    leave
    ret

Notice that the change is a little different for all three. mov stdout, %rdi becomes mov stdout(%rip), %rdi -- just a different addressing mode for the same instruction. Loading from memory at the fixed address stdout becomes loading from memory at the fixed displacement stdout from the RIP register (aka the program counter). Loading the fixed address output with mov $output, %rsi, on the other hand, becomes lea output(%rip), %rsi. I would suggest you think of this one as always having been a load-effective-address operation, but the old code, with the executable at a fixed address, was able to express that operation with move-immediate instead of an actual lea instruction. Finally, call fprintf becomes call fprintf@PLT. This is telling the linker that the call needs to go through the procedure linkage table -- your book should explain what this is and why it's needed.

Incidentally, I see several other problems with this assembly language, of which the most important are:

  • The string "Yeet\n\0" belongs in the read-only data section.
  • The x86-64 ABI says that variadic functions like fprintf need to be told the number of floating point arguments they are receiving, by setting eax appropriately.
  • enter and leave are unnecessary on x86-64. (Also, enter is a painfully slow microcoded instruction that shouldn't be used at all.)

I would have written something like this instead:

    .section .rodata, "a", @progbits
.output:
    .string "Yeet\n"

    .section .text, "ax", @progbits
    .globl main
    .type  main, @function
main:
    sub    $8, %rsp
    mov    stdout(%rip), %rdi
    lea    .output(%rip), %rsi
    xor               
  • Related