在 Linux 中链接 32/64 位程序集时出错

Errors when linking 32/64bit assembly in Linux

我是汇编语言编程的新手。我正在尝试按照概述的步骤 here 来更好地理解组装和优化。我的操作系统是 Linux Mint,我正在尝试使用 NASM 汇编程序,尽管没有成功。

如演练中所示,代码为:

BITS 32
GLOBAL main
SECTION .text
main:
    mov eax, 42
    ret

使用命令nasm编译成功:nasm -f elf tiny.asm

但是如果我尝试使用 gcc link 和命令:gcc -Wall -s tiny.o

我收到以下错误: /usr/bin/ld: 输入文件“tiny.o”的 i386 架构与 i386:x86-64 输出

不兼容

快速搜索告诉我应该 link 使用此 ld 命令:ld -m elf_i386 -s -o tiny tiny.o

但是,这样做会给我以下警告: ld:警告:找不到条目符号_start;默认为 0000000008048060

如果我 ./tiny 我得到一个分段错误。 ./tiny ; echo $? 也 returns 数字“139”,这是......意外的。

四处浏览,我看到通过将 1 传递给 eax 寄存器并将 0 传递给 ebx 并使用我不熟悉的 int 命令来结束程序来解决某些问题...但是考虑到我的 objective是为了让程序尽可能的小,我宁愿不加额外的代码行。

我应该补充一点,编译和 linking 这个类似的代码 (GAS):

.global main
.text
main:
    mov , %eax

使用 gcc 编译器似乎 运行 完美。我在这里不知所措。在正确方向上的任何一点将不胜感激。

程序的默认入口点是 _start 符号。当您使用 gcc 编译时,编译器会创建一个名为 _start 的函数,在调用您的 main 之前进行一些设置。我相信它实际上是 C 库的一部分。因为您使用的是更底层的工具,所以您没有链接到 C 库,因此您必须自己执行此操作。

所以只需将您的 main 更改为 _start 并且 ld 不会再抱怨了。现在,您可能最好使用 libc 的 _start,因为您当前的实现仍然会出现段错误:您需要使用 exit 系统调用停止程序,而不是使用 ret.

Michael 的评论告诉您如何使用 gcc 从汇编代码编译(所以您有 libc 的 _start)。如果您仍然想自己做,您的程序需要如下所示:

BITS 32
GLOBAL _start
SECTION .text
_start:
    mov ebx, 42
    mov eax, 1
    int 0x80

你把exit的系统调用号放在eax里,return代码放在ebx里,触发0x80中断

这适用于 Linux Mint、Ubuntu、Xubuntu 和其他基于 Ubuntu 的现代发行版。对于其他 Debian 系统,信息是相同的,但是您可以从安装命令中删除 sudo 命令,并以 root 用户的身份删除 运行 命令。


默认情况下,GCC 创建 32 位程序所需的文件不会安装在 64 位 Linux Mint 上。此命令将安装所需的文件:

sudo apt-get install gcc-multilib g++-multilib 

到 assemble 作为 32 位对象,然后 link 到 32 位可执行文件你可以这样做:

nasm -f elf32 tiny.asm -o tiny.o
gcc -m32 -Wall -s tiny.o -o tiny

由于您在 assembler 文件的顶部有 BITS 32,因此您希望将其编译为 32 位 ELF 对象,这就是 -f elf32 选项 NASM 命令行可以。

GCC 用于 link 最终可执行文件的对象。 -m32 的添加告诉 GCC 到 link 对象到 32 位可执行文件。 -m32 选项覆盖在 64 位 Linux 发行版上生成 64 位可执行文件的正常默认行为。


通过使用 GCC link 可执行文件,生成的程序实际上提供了一个 _start 执行 C 运行 时间初始化,然后调用名为 main 的标签,就好像它是一个 C 函数一样。

当你在 main 函数中执行 ret 时,它 returns 回到 C 运行 时间码和为您干净地退出程序。

使用 GCC 对于新的汇编语言开发人员来说是更简单、更安全的方法。它还允许您添加调用 C 运行time 函数的代码,例如 printfscanfatoi 和大多数其他标准 C 库函数。