这是示例汇编文件test.s
.global main
main:
mov __progname@GOT, %eax // failed to compile
mov __progname@GOT(%ebx), %eax //succeed to compile
我尝试用-pie
标志编译它,但失败了。
$ gcc -pie -o test test.s
osboxes@osboxes:/mnt/hgfs/VM_Shared/Reassemblabla/src$ gcc -pie -o test test.s
/usr/bin/ld: /tmp/ccPGMLlH.o: direct GOT relocation R_386_GOT32X against `__progname' without base register can not be used when making a shared object
/usr/bin/ld: failed to set dynamic section sizes: File format not recognized
collect2: error: ld returned 1 exit status
错误表示,在饼图二进制中,只能使用基址寄存器访问GOT
条目
问题
我不知道编译器为什么会像上面那样抱怨
更具体地说,为什么饼图二进制文件不允许__progname@GOT
寻址?
我的看法
Loader在pie二进制文件的加载时间中知道__progname@GOT
的地址。
所以,加载程序只需在加载时将该地址写入__progname@GOT
的位置即可
这就是加载器所能做的。
所以我不明白为什么编译器坚持像mov __progname@GOT(%ebx), %eax
那样的寄存器相对访问。
PIE应该使用pc相对寻址;ia32在这方面很糟糕,所以你需要做一些类似的事情:
call thunk
add $_GLOBAL_OFFSET_TABLE__, %eax
mov __progname@GOT(%eax), %eax
ret
thunk:
mov (%esp), %eax
ret
注意,从该程序地址到_GLOBAL_offset_TABLE__的偏移量被计算为参考GOT。因此,程序可以在任何地址加载(而不是链接或定位(,并将找到GOT和所有变量,因为相对偏移量是相同的。作为参考,上面的amd64版本类似于:
mov __progname(%rip), %rax
ret
请注意,这两者都保持了文本的"纯净"。。。。