i was trying to assemble codes below to shared library via gcc.
.section .text .global S_0x400607 .type S_0x400607, @function S_0x400607: push %rbp mov %rsp,%rbp sub $0x10,%rsp mov %edi,-0x4(%rbp) mov -0x4(%rbp),%eax mov %eax,%esi lea S_0x400724(%rip),%rdi mov $0x0,%eax callq printf nop leaveq retq .section .rodata S_0x400724: .byte 0x25 .byte 0x64 .byte 0x0a .byte 0x00
i used command below in terminal and got errors.
$ gcc zzz_out.s -shared -fPIC -o libzzz.so /usr/bin/ld: /tmp/ccq8FvIT.o: relocation R_X86_64_PC32 against symbol `printf@@GLIBC_2.2.5' 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
Then i googled the question and got a seemingly feasible answer in undefined-reference-to-main-for-shared-library. Then i added option -no-pie
, used commands below and got another error.
$ gcc zzz_out.s -shared -fPIC -no-pie -o libzzz.so /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crt1.o: In function `_start': (.text+0x20): undefined reference to `main' collect2: error: ld returned 1 exit status
It seems that the order of options matters. But i used codes in 32-bits like this.
.section .text .globl S_0x8048506 .type S_0x8048506, @function S_0x8048506: push %ebp mov %esp,%ebp push %ebx sub $0x4,%esp call S_0x80485CC add $_GLOBAL_OFFSET_TABLE_,%eax sub $0x8,%esp pushl 0x8(%ebp) lea S_0x8048670,%edx push %edx mov %eax,%ebx call printf add $0x10,%esp nop mov -0x4(%ebp),%ebx leave ret S_0x80485CC: mov (%esp),%eax ret .section .rodata S_0x8048670: .byte 0x25 .byte 0x64 .byte 0x0a .byte 0x00
gcc works perfectly and libzzz.so
generated in current directory.
Sorry if i asked a simple question. I’m totally new in this region.
Advertisement
Answer
You only get undefined reference to `main'
with -no-pie
.
I think -no-pie
is overriding -shared
, so you’re telling GCC to link a position-dependent executable, not a shared-library at all. (ELF shared libraries aka “shared objects” always have to be position-independent, there’s no such thing as a non-PIC .so
. Fun fact: PIE executables are another type of ELF shared object.)
Unlike when linking a non-PIE executable, ld
rewrites call printf
into a call through the PLT for you. But when linking a PIE or shared library, you have to do it manually. (Newer ld
might do this for you, at least in a PIE, but this was a recent change.)
In 32-bit code, call printf
likely assembles and links with a runtime fixup that rewrites the call rel32
instruction at runtime to be printf
‘s actual address in libc, once it’s loaded to a randomized base address. But that doesn’t work for 64-bit code because it might be more than +-2GiB away. Similar to why 32-bit absolute addresses no longer allowed in x86-64 Linux? after distros made PIE executables the default GCC config.
Your options for 64-bit code are:
call *printf@GOTPCREL(%rip)
, likegcc -fno-plt
would emit when compiling C.call printf@plt
like compilers have historically used.- non-PIE executable isn’t an option; you’re making a
-shared
library.
For a NASM-syntax version of this with more detail about what this means, and the machine code, see Can’t call C standard library function on 64-bit Linux from assembly (yasm) code. Also has some examples at the bottom which include AT&T syntax, of how the linker “relaxes” this to a call rel32
if you do call *foobar@GOTPCREL(%rip)
but then foobar
is defined in the same shared library after all (with “hidden” visibility, not global, so symbol interposition isn’t necessary).
See Unexpected value of a function pointer local variable for some examples of compiler output for a PIE vs. non-PIE executable (the PIE executable code will also work in a shared library.)
-fno-plt
style of code-gen is probably not worth it in 32-bit mode, where you don’t have efficient EIP-relative addressing to reach the GOT, unless you already needed a GOT pointer for something else in this funciton.