How can I link dynamically to glibc in Ubuntu

十年热恋 提交于 2021-02-10 05:02:36

问题


I am trying to assemble and link this tiny x86 assembly code in Linux (Ubuntu 18.04 LTS):

;hello.asm
global _start

extern scanf, printf, exit

section .data
    read_name db '%255s', 0
    msg db 'Hello, %s', 0

section .text
_start:
    sub esp, 256
    push esp
    push read_name
    call scanf
    add esp, 8
    push esp
    push msg
    call printf
    add esp, 264
    push dword 0
    call exit

I am using nasm to assemble and ld to link. As you can probably tell, the code uses C functions, so it has to be linked to glibc. Since my code is using a _start, rather than a main, I decided that it would be better to link to a shared library, since the C runtime needs some startup code to run in _start if the binary is linked statically.

The problem is that I cannot get my code to link, most probably because I am not using the correct glibc .so. This is the way I assemble and link:

nasm -f elf32 hello.asm
ld hello.o -o hello -dynamic-linker /lib/libc.so.6 -lc -m elf_i386

The output file gets created, however when I try to run it this is what I get:

./hello
bash: ./hello: No such file or directory

Doing a quick search, it turns out that these are all the libc .sos on my computer:

locate libc.so
/lib/x86_64-linux-gnu/libc.so.6
/snap/core/8268/lib/i386-linux-gnu/libc.so.6
/snap/core/8268/lib/x86_64-linux-gnu/libc.so.6
/snap/core/8689/lib/i386-linux-gnu/libc.so.6
/snap/core/8689/lib/x86_64-linux-gnu/libc.so.6
/snap/core18/1668/lib/i386-linux-gnu/libc.so.6
/snap/core18/1668/lib/x86_64-linux-gnu/libc.so.6
/usr/lib/x86_64-linux-gnu/libc.so

Can anyone tell me how to link to glibc? (I also get the same problem for 64-bit code)


回答1:


ld's default dynamic linker for i386 is /usr/lib/libc.so.1, which is wrong on most Linux systems today. You did try to override it, but the path you gave wasn't right either. Two options:

  1. Manually pass the right one when you link: ld hello.o -o hello -dynamic-linker /lib/ld-linux.so.2 -lc -m elf_i386
  2. Use gcc to link instead, as fuz mentioned: gcc -nostartfiles -m32 -o hello hello.o

If you're curious how I knew what the right dynamic linker was for option 1, I did it by doing option 2 once first and checking which one it used.

See also Red Hat's Bug 868662 - /lib/ld64.so.1: bad ELF Interpreter: No such file or directory, someone else who had basically the exact same problem you did (but they got a more helpful error message than you did for some reason).


Edit: there's two other potential problems with your code, that could cause problems in real code but just happened not to in this tiny example:

First, as Employed Russian pointed out in a comment, glibc expects that its own initialization code from its crt will run before your application code starts calling its functions. You were lucky; since you made a dynamically-linked binary, the dynamic linker's use of glibc resulted it in it being initialized for you. Had you made a statically-linked binary instead, it wouldn't have worked. To avoid relying on the dynamic linker that way, the easiest solution is to use main as your entry point instead of _start, and then use gcc -m32 -o hello hello.o to link (note we're not using -nostartfiles anymore). In theory you could still use ld directly to link, but it's complicated enough to get right that there's basically no reason to bother.

Second, you're not aligning the stack correctly. You need to make sure it's aligned to a 16-byte boundary before you call other functions. At the beginning of _start (if you still use that for some reason), the stack will already be aligned like that, so you just have to maintain it. At the beginning of main or any other function, the 4-byte return address will have gotten pushed, so you'd need to push 12 more bytes to realign it.

With both of the above fixes, here's your new hello.asm:

;hello.asm
global main

extern scanf, printf, exit

section .data
    read_name db '%255s', 0
    msg db 'Hello, %s', 0

section .text
main:
    sub esp, 260 ; the 4 extra bytes here are padding for alignment. If you wanted to get value out of them, you could use %259s instead of %255s now
    push esp
    push read_name
    call scanf
    add esp, 8
    push esp
    push msg
    call printf
    add esp, 260 ; we pushed 268 bytes so far, but I'm leaving 8 bytes for alignment
    push dword 0
    call exit

Also, now that you're using main and not _start, you can just return from it instead of calling exit. You just need to make sure you put the stack pointer back where it was at the beginning. To do that, replace everything after call printf with this:

    add esp, 268
    xor eax, eax
    ret

Final note: if you're wondering why I did xor eax, eax and not mov eax, 0, see What is the best way to set a register to zero in x86 assembly: xor, mov or and?.



来源:https://stackoverflow.com/questions/61432284/how-can-i-link-dynamically-to-glibc-in-ubuntu

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!