64位主机上GNU GAS引用stdio.h编译32位汇编报错问题
GNU GAS汇编代码编译与运行问题排查
待编译的汇编代码
#include <stdio.h> .global _start .text message_text: call program str: .string "hello world" .equ len, (. - str) _start: jmp message_text program: call puts@PLT
遇到的问题
- 编译错误:尝试用
gcc -m32 asm.S、gcc -m32 asm.S -fPIC编译时,反复出现类似/usr/include/stdio.h:847: Error: no such instruction: extern void ...的错误。 - 头文件疑问:明白预处理器会将C头文件内容插入汇编文件,导致汇编器把C函数原型当成汇编指令报错,但之前使用
#include <asm/unistd.h>和#include <syscall.h>是正常的,不清楚为什么stdio.h无法使用。 - 运行异常:注释掉
#include <stdio.h>仅保留call puts@PLT后,用以下命令编译成功:
as --32 -o output.o asm.S ld -m elf_i386 -o output output.o -lc
但运行./output时提示“no such file or directory: ./output”(文件实际存在);用file output确认是32位ELF可执行文件,只有显式指定动态链接器后才能正常运行(命令如下):
as --32 -o output.o asm.S ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o output -lc output.o
问题原因与解决方法
1. #include <stdio.h>报错的原因
asm/unistd.h和syscall.h这类头文件是专为汇编设计的,内容以汇编宏、常量定义为主,没有C语法的函数原型;而stdio.h是标准C头文件,包含大量C函数声明、类型定义等C语法代码,预处理器插入后会被汇编器当作汇编指令处理,必然报错。
解决:汇编文件无需#include <stdio.h>,直接调用puts@PLT即可,只要链接时指定-lc(标准C库),动态链接器会自动找到puts的实现。
2. 运行时提示文件不存在的原因
在64位系统上,默认的动态链接器是64位版本,无法加载32位程序;用ld直接链接时如果未显式指定32位动态链接器,系统会找不到合适的加载器,从而抛出“文件不存在”的误导性错误。
解决:链接时必须显式指定32位动态链接器路径,即使用你后来的正确命令。同时确保系统已安装32位C库(比如Ubuntu上的libc6-i386包),否则即使指定链接器也会报错。
3. 用GCC编译的正确方式
如果想用GCC替代手动调用as和ld,可以两种方式处理:
- 将汇编代码中的
_start改为main,GCC默认会链接C运行时,入口为main,直接用gcc -m32 asm.S -o output编译即可。 - 保留
_start,添加-nostartfiles参数让GCC不链接默认启动文件:
这种方式GCC会自动处理汇编、链接流程,并指定正确的动态链接器。gcc -m32 asm.S -nostartfiles -o output
内容的提问来源于stack exchange,提问作者skyfire
相关产品推荐
相关产品推荐

