Linux汇编程序调用sysinfo返回成功但结构体未被修改的问题排查
问题根因
你遇到的现象完全是地址错配导致的,和syscall本身的逻辑没有关系:
sysinfo系统调用本身是成功执行的:你传入的内存地址是合法可写的,内核已经把完整的结构体数据写入了那块内存,所以strace能读到正确的uptime、内存总量这些值,系统调用返回值rax=0也印证了没有执行错误。- 你在gdb里看到全0,是因为gdb查询
&result时用的符号地址,根本不是你传给系统调用的那个缓冲区的真实地址。
地址错配的形成原因
现在主流Linux发行版默认生成PIE(位置无关可执行文件),你的代码踩了两个写汇编时的常见坑:
- 全局符号绑定冲突:你没有把
result标记为本地私有符号,这个名字会被导出到全局动态符号表。程序加载时,动态链接器会把gdb中显示的result符号绑定到共享库映射区域的同名符号上——也就是你看到的0x7ffff7faad80,这个地址属于共享库的只读映射段,根本不是你自己程序的数据区。
但你代码里写leaq result(%rip), %rdi取地址的时候,是编译时基于RIP相对偏移直接算出的你自己程序data段里的真实缓冲区地址,这个过程完全不依赖动态链接的符号绑定逻辑,所以传给内核的是正确的可写地址,内核自然能正常写入数据。 - 你自己拆分标签定义的结构体和内核实际的
sysinfo内存布局没有对齐,但这不是内存全0的原因——只要缓冲区是连续、长度足够、权限可写,内核就会正常写入全部数据,和你怎么给字段打标签没有关系。
验证和修复方法
- 验证方式:在gdb里直接打印
$rdi的值(也就是syscall的第一个参数),和&result的值做对比,会发现两个地址完全不重合。 - 修复方案:
- 在
result定义前加.local result,把它标记为本地私有符号,不要导出到全局符号表,避免动态链接时的绑定冲突 - 这类零初始化的缓冲区本来就该放在.bss段,不要放在.data段,直接用
.comm result, 128, 16预留128字节的对齐缓冲区,就足够覆盖当前内核的sysinfo结构体大小,不用拆成零散的.quad定义
- 在
内容的提问来源于stack exchange,提问作者KMG
相关产品推荐
相关产品推荐

