如何在nasm中调用调用c标准库的c函数?

3
首先,我想澄清一下,我知道这个问题可能已经被回答了数百次。然而,在经过几个小时的谷歌搜索后,我却找不到完全符合我的要求的内容。此外,尽管我已经写了相当长时间的C程序,但我对nasm和ld还比较陌生。因此,如果我能得到一个简单的答案,而不必阅读整个nasm/ld教程或完整的手册,我将不胜感激。
我想做的是: 假设我有一个用C语言编写的函数,调用了C标准库中的某些函数:
/* foo.c */
#include <stdio.h>
void foo(int i)
{
    printf("%d\n", i);
}

我想在nasm中调用这个函数,所以我尝试了这个方法:

; main.asm
global _start
extern foo

section .text
_start:
    push 1234567
    call foo
    add esp, 4

    mov eax, 1
    xor ebx, ebx
    int 80h

然后我尝试编译并运行它们:

[user ~/Documents/asm/callc]#make all
nasm main.asm -felf
gcc -c foo.c -o foo.o -m32
ld -o main main.o foo.o -melf_i386 -lc
[user ~/Documents/asm/callc]#ls
foo.c  foo.o  main  main.asm  main.o  Makefile
[user ~/Documents/asm/callc]#./main 
bash: ./main: No such file or directory
[user ~/Documents/asm/callc]#bash main
main: main: cannot execute binary file

我没有收到任何错误信息,但似乎我无法运行可执行的输出文件。
如果C函数不调用任何库函数,则上述代码可以编译并且可以正常运行。我还找出了一种直接调用nasm中的库函数并使用gcc生成最终可执行文件的方法。但是它们都不完全符合我的要求。
编辑: 1. 我正在运行64位Ubuntu,但我正在尝试编写32位程序,因此我使用了像-m32和-melf_i386这样的标志。 2. file *的输出:
[user ~/Documents/asm/sof]#file *
foo.c:     C source, ASCII text
foo.c~:    empty 
foo.o:     ELF 32-bit LSB  relocatable, Intel 80386, version 1 (SYSV), not stripped
main:      ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), not stripped
main.asm:  C source, ASCII text
main.asm~: empty 
main.o:    ELF 32-bit LSB  relocatable, Intel 80386, version 1 (SYSV), not stripped
Makefile:  makefile script, ASCII text
Makefile~: makefile script, ASCII text

3. 我真的不知道如何告诉ld包含C标准库。我在其他帖子中找到了像-lglibc或-lc之类的东西。-lgibc行不通,-lc似乎能够摆脱所有错误,一开始可能会让我以为它起作用了,但也许这就是问题所在,因为它可能没有链接正确的库。

更新
-I/lib32/ld-linux.so.2添加到ld命令中解决了我的问题。
以下是编译/汇编/链接和运行程序的命令:

nasm main.asm -felf
gcc -c foo.c -o foo.o -m32
ld -o main main.o foo.o -melf_i386 -lc -I/lib32/ld-linux.so.2
./main

1
你使用的是32位还是64位的Linux系统?在该目录下运行 file * 命令,并编辑你的回答以包含输出结果。此外,虽然这不会导致无效可执行文件的问题,但如果C运行时代码没有先运行,你就不应该期望调用C标准库函数。 - Jonathon Reinhart
1
顺便问一下:为什么提示看起来像你正在以root身份编译和运行东西? :P - cHao
1
@uNiverselEgacy:提示符中的 # 通常意味着 shell 正在以 root 用户身份运行。对于非 root 用户,通常是 $。有一个转义序列(\$)可以显示正确的提示符,因此要么您正在以 root 用户身份运行,要么您已配置 shell 以欺骗方式运行。 :) - cHao
总的来说,我认为在汇编代码中最好不要创建 _start。你可以将 main 创建为起始点。如果你创建了 _start,那么你就会错过所有正常的支持代码,在调用 main 之前进行排序的小细节,例如“加载标准 C 共享库”等。 - Jonathan Leffler
@rkhb:它起作用了!但是根据上面的一些评论和我在网上找到的东西,有人告诉我在标准C库可以运行之前必须先运行某种init进程。我反汇编了可执行文件,似乎没有找到代码来实现这一点。所以我不确定为什么它能工作,或者我听到的并不一定是真的。 - uNiverselEgacy
显示剩余5条评论
1个回答

4

C库提供使用_start接口的代码来启动C运行时,调用main()函数并关闭运行时。因此,如果您打算在程序中使用C库,则不得使用_start接口,而应提供一个main()函数。

以下是正确的方法:

; main.asm
global main
extern foo

section .text
main:
    push 1234567
    call foo
    add esp, 4

    xor eax, eax
    ret

使用以下技术构建:

nasm -f elf32 -o main.o main.asm
gcc -m32 -o foo.o -c foo.c
gcc -m32 -o main main.o foo.o

两点说明:

  • main()返回,而不是执行exit系统调用,这样可以让C运行时关闭代码运行。
  • 使用gcc进行链接。内部gcc会以适当的参数调用ld与C库链接。这些是特定于平台的并且可能会发生变化。因此,请勿使用ld进行此操作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接