为何无法直接从全局变量加载值到寄存器?链接错误解析
汇编访问C全局变量的链接错误问题
问题代码
main.c
int NUM; extern void exitprocnow(); int main(void) { NUM = 10; exitprocnow(); return 1; }
exitprocnow.asm(出错版本)
[bits 64] extern NUM extern ExitProcess section .text global exitprocnow exitprocnow: mov rcx, [NUM] call ExitProcess
编译错误信息
$> nasm -f win64 .\exitproc.asm -o call.o $> gcc .\main.c .\call.o -lkernel32 .\call.o:.\exitproc.asm:(.text+0x4): relocation truncated to fit: IMAGE_REL_AMD64_ADDR32 against symbol "NUM" defined in .bss section in C:\Users\YO\AppData\Local\Temp\cccoJTdm.o collect2.exe: error: ld returned 1 exit status
可行的汇编代码
exitprocnow: mov rdx, NUM mov rcx, [rdx] call ExitProcess
疑问
- 为何无法直接将全局变量的值加载到寄存器?
- 上述链接错误的具体含义是什么?
问题解答
1. 直接加载全局变量值失败的原因
在Windows x64(AMD64)平台上,全局变量的地址是64位的,但mov rcx, [NUM]这种写法,NASM默认会生成使用32位绝对地址偏移的内存访问指令。如果全局变量NUM的实际地址超出了32位整数的表示范围(Windows x64进程地址空间本身是64位,变量可能被分配到高地址区域),32位的偏移量就无法容纳完整的64位地址,导致链接时无法完成地址重定位。
而先执行mov rdx, NUM再mov rcx, [rdx]的写法是正确的:mov rdx, NUM会生成适配x64的寻址方式(通常是RIP相对寻址或直接加载64位绝对地址),能正确获取NUM的64位地址,之后通过寄存器间接访问内存就能拿到变量的值。
2. 链接错误的具体含义
这条错误的核心是重定位类型不匹配导致地址截断:
- 汇编器为
mov rcx, [NUM]生成了IMAGE_REL_AMD64_ADDR32类型的重定位项,也就是指令里预留了32位空间来存放NUM的绝对地址。 - 链接时发现
NUM的实际地址是64位的,无法塞进32位的预留空间里,只能截断地址来适配,而这会导致地址无效,因此链接器抛出错误,提示“重定位截断以适配”。
简单来说:你试图用32位的地址空间去存放一个64位的全局变量地址,空间不够,链接器无法完成地址填充,所以报错。
内容的提问来源于stack exchange,提问作者Yahya Oulha
相关产品推荐
相关产品推荐

