如何在NASM中获取共享对象文件中函数的内存地址?
在NASM中调用共享库函数(以libm的
sqrt为例) 一、共享库函数地址的查找逻辑
共享库的函数地址并非固定值,程序启动时由动态链接器(如ld-linux.so)负责解析加载——因为ASLR(地址空间布局随机化)机制的存在,共享库每次加载的基地址都可能变化,因此无法直接硬编码函数在RAM中的地址,必须通过**PLT(过程链接表)和GOT(全局偏移表)**的间接跳转来获取实际地址。
二、NASM中的实现方式
方式1:依赖工具链自动处理PLT/GOT(推荐)
无需手动编写PLT和GOT段的头结构,直接声明外部函数,链接时指定对应共享库即可,汇编器和链接器会自动生成所需的PLT条目与GOT引用。
示例代码(调用sqrt计算平方根):
; sqrt_demo.asm section .data num dq 2.0 ; 待计算的数值:sqrt(2.0) fmt db "sqrt(2.0) = %lf", 0xA, 0 section .text extern sqrt ; 声明sqrt来自libm库 extern printf ; 声明printf来自libc库 global main main: push rbp mov rbp, rsp movsd xmm0, [num] ; 浮点数参数放入xmm0(遵循System V AMD64调用约定) call sqrt ; 调用sqrt,结果存于xmm0 ; 调用printf输出结果 mov rdi, fmt ; 格式字符串地址放入rdi movsd xmm1, xmm0 ; 将计算结果移至xmm1(printf的第二个浮点参数) mov eax, 1 ; 告知printf有1个浮点参数 call printf mov eax, 0 ; 程序返回0 leave ret
编译链接命令:
nasm -f elf64 sqrt_demo.asm -o sqrt_demo.o gcc sqrt_demo.o -lm -o sqrt_demo # -lm 选项用于链接libm共享库
方式2:手动编写PLT/GOT段(仅作原理演示)
如果要手动实现符合ELF格式的PLT/GOT结构,以下是64位环境下的简化示例(实际开发完全没必要,工具链会自动完成):
; 手动PLT/GOT示例(原理演示用) section .plt ; sqrt的PLT条目 plt_sqrt: jmp [GOT_sqrt] ; 跳转到GOT中存储的实际函数地址 push 0 ; 重定位索引(由链接器自动填充) jmp plt_resolve ; 跳转到动态链接器的地址解析函数 section .got GOT_sqrt: dq plt_sqrt ; 初始指向PLT自身,等待动态链接器更新为实际地址 section .text global main extern _GLOBAL_OFFSET_TABLE_ main: push rbp mov rbp, rsp sub rsp, 8 movsd xmm0, [rel num] ; 加载待计算数值 call plt_sqrt ; 调用PLT中的sqrt条目 ; 后续输出逻辑同方式1... section .data num dq 2.0
三、能否直接获取sqrt在RAM中的地址?
不行,原因有二:
- ASLR机制:现代系统默认启用ASLR,每次程序运行时libm的加载基地址都会随机变化,
sqrt的RAM地址也会随之改变。 - 版本兼容性:即使关闭ASLR,不同系统、不同版本的libm库中,
sqrt相对于库基地址的偏移也可能不同,硬编码地址会导致程序在其他环境下无法运行。
正确的做法始终是通过PLT/GOT机制,让动态链接器在程序启动时自动解析函数的实际地址。
内容的提问来源于stack exchange,提问作者aoftuna
相关产品推荐
相关产品推荐

