问题
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 .so
s 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:
- Manually pass the right one when you link:
ld hello.o -o hello -dynamic-linker /lib/ld-linux.so.2 -lc -m elf_i386
- 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