2

这是示例程序集文件,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

错误说,在 pie 二进制文件中,GOT只能使用基址寄存器访问条目。

问题。
我不知道为什么编译器会像上面那样抱怨。
更具体地说,为什么__progname@GOTpie 二进制文件不允许寻址?



我的意见。
Loader 知道__progname@GOTpie 二进制文件在加载时的地址。

__progname@GOT因此,加载程序可以简单地在加载时间的位置写入这个地址。
这就是装载机可以做的。

所以我不明白为什么编译器坚持像
mov __progname@GOT(%ebx), %eax.

4

1 回答 1

1

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

请注意,这两个都保持文本“纯”....

于 2018-11-20T19:30:35.297 回答