使用 GCC 修复位置无关可执行文件中全局变量的重定位
Fix relocations for global variables in position-independent executables with GCC
我正在寻找 gcc
命令行标志或其他设置来为我的静态链接、位置无关的 i386 可执行文件生成 GOTOFF
重定位而不是 GOT
重定位。有关我在下面尝试的内容的更多详细信息。
我的源文件 g1.s
如下所示:
extern int answer;
int get_answer1() { return answer; }
我的另一个源文件 g2.s
如下所示:
extern int answer;
int get_answer2() { return answer; }
我用 gcc -m32 -fPIE -Os -static -S -ffreestanding -fomit-frame-pointer -fno-unwind-tables -fno-asynchronous-unwind-tables g1.c
为 i386 编译它们。
我得到以下汇编输出:
.file "g1.c"
.text
.globl get_answer1
.type get_answer1, @function
get_answer1:
call __x86.get_pc_thunk.cx
addl $_GLOBAL_OFFSET_TABLE_, %ecx
movl answer@GOT(%ecx), %eax
movl (%eax), %eax
ret
.size get_answer1, .-get_answer1
.section .text.__x86.get_pc_thunk.cx,"axG",@progbits,__x86.get_pc_thunk.cx,comdat
.globl __x86.get_pc_thunk.cx
.hidden __x86.get_pc_thunk.cx
.type __x86.get_pc_thunk.cx, @function
__x86.get_pc_thunk.cx:
movl (%esp), %ecx
ret
.ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4"
.section .note.GNU-stack,"",@progbits
以下是使用 GCC 7.2 在线重现此行为的方法:https://godbolt.org/g/XXkxJh
而不是上面的 GOT
,我想得到 GOTOFF
,并且 movl %(eax), %eax
应该消失,所以该函数的汇编代码应该如下所示:
get_answer1:
call __x86.get_pc_thunk.cx
addl $_GLOBAL_OFFSET_TABLE_, %ecx
movl answer@GOTOFF(%ecx), %eax
ret
我已验证此 GOTOFF
汇编版本有效,而 GOT
版本无效(因为它有一个额外的指针间接寻址)。
如何说服 gcc
生成 GOTOFF
版本?我尝试了 -fPIC
、-fpic
、-fPIE
、-fpie
、-pie
、-fno-plt
的各种组合。 None 他们成功了,他们都让 gcc
产生了 GOT
版本。
我在 https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html or any generic flag here: https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html
上找不到任何特定于 i386 的标志
事实上,我正在为 "..."
字符串文字获取 GOTOFF
重定位,我还想为 extern
变量获取它们。
最终输出是自定义二进制格式的静态链接可执行文件(我为此编写了 GNU ld 链接器脚本)。没有动态链接,也没有共享库。地址随机化由自定义加载器执行,它可以自由地将可执行文件加载到任何地址。所以我确实需要与位置无关的代码。没有每段内存映射:整个可执行文件按原样连续加载。
我在网上找到的所有文档都在谈论动态链接的位置无关可执行文件,但我在那里找不到任何有用的东西。
我无法用 gcc -fPIE
解决这个问题,所以我通过处理输出文件手动解决了这个问题。
我使用 gcc -Wl,-q
,输出 ELF 可执行文件包含重定位。我 post-process 这个 ELF 可执行文件,并在开头添加以下汇编指令:
call next
next:
pop ebx
add [ebx + R0 + (after_add - next)], ebx
add [ebx + R1 + (after_add - next)], ebx
add [ebx + R2 + (after_add - next)], ebx
...
after_add:
,其中 R0、R1、R2 ... 是 ELF 可执行文件中 R_386_32 重定位的地址。 In use objdump -O binary prog.elf prog.bin', and now
prog.bin' 包含与位置无关的代码,因为它以 `add [ebx + ...], ebx' 指令开头,它在代码执行时对代码进行必要的重定位开始 运行.
根据执行环境,需要 gcc
标志 -Wl,-N
,以使 .text
部分可写(`add [ebx + ...], ebx'说明需要那个)。
我正在寻找 gcc
命令行标志或其他设置来为我的静态链接、位置无关的 i386 可执行文件生成 GOTOFF
重定位而不是 GOT
重定位。有关我在下面尝试的内容的更多详细信息。
我的源文件 g1.s
如下所示:
extern int answer;
int get_answer1() { return answer; }
我的另一个源文件 g2.s
如下所示:
extern int answer;
int get_answer2() { return answer; }
我用 gcc -m32 -fPIE -Os -static -S -ffreestanding -fomit-frame-pointer -fno-unwind-tables -fno-asynchronous-unwind-tables g1.c
为 i386 编译它们。
我得到以下汇编输出:
.file "g1.c"
.text
.globl get_answer1
.type get_answer1, @function
get_answer1:
call __x86.get_pc_thunk.cx
addl $_GLOBAL_OFFSET_TABLE_, %ecx
movl answer@GOT(%ecx), %eax
movl (%eax), %eax
ret
.size get_answer1, .-get_answer1
.section .text.__x86.get_pc_thunk.cx,"axG",@progbits,__x86.get_pc_thunk.cx,comdat
.globl __x86.get_pc_thunk.cx
.hidden __x86.get_pc_thunk.cx
.type __x86.get_pc_thunk.cx, @function
__x86.get_pc_thunk.cx:
movl (%esp), %ecx
ret
.ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4"
.section .note.GNU-stack,"",@progbits
以下是使用 GCC 7.2 在线重现此行为的方法:https://godbolt.org/g/XXkxJh
而不是上面的 GOT
,我想得到 GOTOFF
,并且 movl %(eax), %eax
应该消失,所以该函数的汇编代码应该如下所示:
get_answer1:
call __x86.get_pc_thunk.cx
addl $_GLOBAL_OFFSET_TABLE_, %ecx
movl answer@GOTOFF(%ecx), %eax
ret
我已验证此 GOTOFF
汇编版本有效,而 GOT
版本无效(因为它有一个额外的指针间接寻址)。
如何说服 gcc
生成 GOTOFF
版本?我尝试了 -fPIC
、-fpic
、-fPIE
、-fpie
、-pie
、-fno-plt
的各种组合。 None 他们成功了,他们都让 gcc
产生了 GOT
版本。
我在 https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html or any generic flag here: https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html
上找不到任何特定于 i386 的标志事实上,我正在为 "..."
字符串文字获取 GOTOFF
重定位,我还想为 extern
变量获取它们。
最终输出是自定义二进制格式的静态链接可执行文件(我为此编写了 GNU ld 链接器脚本)。没有动态链接,也没有共享库。地址随机化由自定义加载器执行,它可以自由地将可执行文件加载到任何地址。所以我确实需要与位置无关的代码。没有每段内存映射:整个可执行文件按原样连续加载。
我在网上找到的所有文档都在谈论动态链接的位置无关可执行文件,但我在那里找不到任何有用的东西。
我无法用 gcc -fPIE
解决这个问题,所以我通过处理输出文件手动解决了这个问题。
我使用 gcc -Wl,-q
,输出 ELF 可执行文件包含重定位。我 post-process 这个 ELF 可执行文件,并在开头添加以下汇编指令:
call next
next:
pop ebx
add [ebx + R0 + (after_add - next)], ebx
add [ebx + R1 + (after_add - next)], ebx
add [ebx + R2 + (after_add - next)], ebx
...
after_add:
,其中 R0、R1、R2 ... 是 ELF 可执行文件中 R_386_32 重定位的地址。 In use objdump -O binary prog.elf prog.bin', and now
prog.bin' 包含与位置无关的代码,因为它以 `add [ebx + ...], ebx' 指令开头,它在代码执行时对代码进行必要的重定位开始 运行.
根据执行环境,需要 gcc
标志 -Wl,-N
,以使 .text
部分可写(`add [ebx + ...], ebx'说明需要那个)。