在 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 函数的代码,例如 printf
、scanf
、atoi
和大多数其他标准 C 库函数。
我是汇编语言编程的新手。我正在尝试按照概述的步骤 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 函数的代码,例如 printf
、scanf
、atoi
和大多数其他标准 C 库函数。