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

时间:2016-02-20 03:50:07

标签: gcc assembly x86 ld linuxmint

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

与演练一样,代码为:

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

使用命令nasm -f elf tiny.asm

成功编译nasm

但是,如果我尝试使用gcc链接命令:gcc -Wall -s tiny.o

我收到以下错误: / usr / bin / ld:i386输入文件架构`tiny.o'与i386不兼容:x86-64输出

快速搜索告诉我,我应该使用这个ld命令链接:ld -m elf_i386 -s -o tiny tiny.o

然而,这样做会给我以下警告: ld:警告:找不到入口符号_start;默认为0000000008048060

如果我./tiny我收到了分段错误。并且./tiny ; echo $?也会返回数字' 139'这是......出乎意料的。

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

我应该补充说,编译和链接这个类似的代码(GAS):

.global main
.text
main:
    mov $32, %eax

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

2 个答案:

答案 0 :(得分:0)

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

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

迈克尔的评论告诉你如何使用gcc从汇编代码编译(所以你有libc的_start)。如果你仍然想自己做,你的程序需要看起来像这样:

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

你将exit的系统调用号放在eax中,ebx中的返回码,然后触发0x80中断。

答案 1 :(得分:0)

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

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

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

要组装为32位对象,然后链接到32位可执行文件,您可以执行此操作:

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

由于汇编程序文件顶部有BITS 32,您希望将其编译为32位ELF对象,这是 NASM上的-f elf32选项命令行呢。

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

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

当您在ret函数中执行main时,它将返回 C 运行时代码,并为您彻底退出程序。

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