启用PIE时如何正确构建.rodata字符串表?
在x86-64 NASM汇编的.rodata段中构建兼容PIE的只读字符串表
我在编程练习平台用x86-64 NASM汇编编写代码,希望将字符串表放在.rodata只读段中,但遇到了与PIE(位置无关可执行文件)机制相关的编译错误。将字符串表移至.data段时代码可正常编译,但我想了解如何在.rodata段中正确实现只读字符串表。
原.rodata段代码片段:
section .rodata align 8, db 0 table: dq .Leucine ; UUA dq .Phenylalanine ; UUC dq .Phenylalanine ; UUU dq .Leucine ; UUG dq 0 ; UGA dq .Cysteine ; UGC dq .Cysteine ; UGU dq .Tryptophan ; UGG .Leucine: db "Leucine", 0 .Phenylalanine: db "Phenylalanine", 0 .Cysteine: db "Cysteine", 0 .Tryptophan: db "Tryptophan", 0
提交时收到的错误信息:
/usr/lib/gcc/x86_64-alpine-linux-musl/12.2.1/../../../../x86_64-alpine-linux-musl/bin/ld: protein_translation.o: warning: relocation in read-only section `.rodata' /usr/lib/gcc/x86_64-alpine-linux-musl/12.2.1/../../../../x86_64-alpine-linux-musl/bin/ld: warning: creating DT_TEXTREL in a PIE collect2: error: ld returned 1 exit status make: *** [Makefile:32: tests] Error 1
我知道在可执行代码中可以使用RIP相对寻址(如lea rsi, [rel .Leucine]),想了解字符串表如何实现类似效果。
问题原因
PIE模式下,程序的加载地址是动态分配的,.rodata段中直接用dq .Leucine这种绝对地址引用会产生TEXTREL(文本段重定位)——链接器需要在运行时修改只读段中的绝对地址,这违背了只读段的设计初衷,也会触发错误。
解决方案:用相对偏移量替代绝对地址
核心思路是:在.rodata中存储字符串相对于表起始位置的偏移量(编译时可确定的常量),然后在代码中通过RIP相对寻址获取表的动态基地址,再加上偏移量得到字符串的实际地址。
1. 修改.rodata段的字符串表
section .rodata align 8, db 0 table: dq .Leucine - table ; 存储相对于table的偏移量 dq .Phenylalanine - table dq .Phenylalanine - table dq .Leucine - table dq 0 ; UGA 对应空指针 dq .Cysteine - table dq .Cysteine - table dq .Tryptophan - table .Leucine: db "Leucine", 0 .Phenylalanine: db "Phenylalanine", 0 .Cysteine: db "Cysteine", 0 .Tryptophan: db "Tryptophan", 0
2. 在代码中访问字符串
通过RIP相对寻址获取table的基地址,再加上偏移量计算出字符串地址:
; 示例:获取table中第0项对应的字符串地址 lea rbx, [rel table] ; 用RIP相对寻址拿到table的动态基地址 mov rax, [rbx] ; 取出对应的偏移量 add rax, rbx ; 基地址 + 偏移量 = 字符串实际地址 ; 此时rax中存储的就是.Leucine的有效地址
这种方式完全兼容PIE机制:偏移量是编译时确定的常量,可安全存放在只读段中;代码通过RIP相对寻址处理动态加载地址,无需修改只读段,也就不会产生TEXTREL错误。
内容的提问来源于stack exchange,提问作者Venusaur
相关产品推荐
相关产品推荐

