汇编代码调用C函数时ld与gcc链接glibc失败问题求助
问题:汇编程序链接libc及外部库的错误解决
测试场景与代码
目标是编写调用C标准库printf的NASM汇编程序,实现Hello World。
C版本参考代码
#include <stdio.h> int main(int argc, char** argv) { printf("Hello, world!\n"); return 0; }
自行编写的NASM汇编代码
extern printf section .data msg db "Hello, world!", 0xa, 0x0 section .text global _start _start: push word msg call printf add rsp, 8 mov eax, 60 xor edi, edi syscall
链接阶段的错误情况
执行nasm -f elf64 -o a.o a.asm汇编成功,但链接时出现以下问题:
- 使用ld链接
执行命令:ld -o a a.o -lc
返回错误:
a.o: in function `_start': a.asm:(.text+0x2): relocation truncated to fit: R_X86_64_16 against `.data'
- 使用gcc链接
初始执行gcc -o a a.o报错存在多个_start入口点;将_start改为main后,仍报错:
/usr/bin/ld: a.o: relocation R_X86_64_16 against `.data' can not be used when making a PIE object; recompile with -fPIE /usr/bin/ld: failed to set dynamic section sizes: bad value collect2: error: ld returned 1 exit status
此外,测试将C代码编译为目标文件后用ld链接,虽仅警告但运行时提示文件缺失。
错误原因与解决方法
核心错误原因
- 16位寻址不兼容64位程序:代码中
push word msg使用16位操作,x86_64架构下内存地址为64位,这种16位重定位无法在64位程序中生效,导致R_X86_64_16错误。 - 不符合System V AMD64调用约定:x86_64下C函数参数传递需遵循System V ABI,可变参数函数(如
printf)还需设置rax寄存器标记浮点参数数量。 - PIE模式的地址限制:gcc默认生成位置无关可执行文件(PIE),原代码的绝对地址引用不兼容PIE要求。
修正后的汇编代码
方式1:以main为入口(推荐,由gcc处理libc初始化)
extern printf section .data msg db "Hello, world!", 0xa, 0x0 section .text global main main: ; 遵循System V ABI:第一个参数放入rdi,可变参数函数设置rax=0(无浮点参数) mov rdi, msg mov rax, 0 call printf ; main函数返回0,等价于C中的return 0 mov eax, 0 ret
方式2:以_start为入口(需手动处理libc初始化)
若坚持使用_start,需调用libc的初始化函数,且确保地址引用兼容64位:
extern printf extern __libc_start_main extern exit section .data msg db "Hello, world!", 0xa, 0x0 section .text global _start _start: ; 调用__libc_start_main完成初始化,参数依次为main、argc、argv等 mov rdi, main_func xor rsi, rsi ; argc=0 xor rdx, rdx ; argv=NULL call __libc_start_main main_func: mov rdi, msg mov rax, 0 call printf mov rdi, 0 call exit
正确的链接命令
- 使用gcc链接(推荐)
针对修正后的main入口代码,执行:
nasm -f elf64 -o a.o a.asm gcc -o a a.o
若需关闭PIE模式(适配旧环境),添加-no-pie参数:
gcc -no-pie -o a a.o
- 使用ld链接
需手动指定libc的初始化依赖,命令示例:
ld -o a a.o -lc -dynamic-linker /lib64/ld-linux-x86-64.so.2
(注:/lib64/ld-linux-x86-64.so.2为x86_64系统下的动态链接器路径,需根据实际系统调整)
链接OpenGL等外部库的方法
链接OpenGL这类外部库时,方法与链接libc一致:
- 汇编代码:声明外部函数(如
extern glClear),并遵循System V AMD64调用约定编写调用逻辑。 - 链接命令:使用gcc时,添加对应的库参数,例如:
gcc -o a a.o -lGL -lGLU -lglut
(-lGL、-lGLU、-lglut为OpenGL相关库的链接参数,需确保系统已安装对应库文件)
若使用ld链接,需手动指定库路径和库名,示例:
ld -o a a.o -lc -lGL -lGLU -lglut -L/usr/lib/x86_64-linux-gnu/ -dynamic-linker /lib64/ld-linux-x86-64.so.2
(-L指定库的搜索路径,需根据实际库安装位置调整)
内容的提问来源于stack exchange,提问作者Player_X_YT
相关产品推荐
相关产品推荐

