GOT和GOTOFF之间的区别 [英] Difference between GOT and GOTOFF
问题描述
我是32位汇编的初学者,我试图将一个简单的C程序编译为Assembly.除了使用GOTOFF时,我了解其中的大部分内容.
.file"main.c".文本.section .rodata.LC0:.string"Hello world".文本.globl主.type main,@功能主要的:.LFB0:.cfi_startproc第4张(%esp),%ecx.cfi_def_cfa 1,0andl $ -16,%esppushl -4(%ecx)Pushl%ebp.cfi_escape 0x10,0x5,0x2,0x75,0move%esp,%ebpPushl%ebxpushl%ecx.cfi_escape 0xf,0x3,0x75,0x78,0x6.cfi_escape 0x10,0x3,0x2,0x75,0x7c致电__x86.get_pc_thunk.ax地址$ _GLOBAL_OFFSET_TABLE_,%eaxsubl $ 12,%espleal .LC0 @ GOTOFF(%eax),%edx#<-在这里Pushl%edxmove%eax,%ebx呼叫puts @ PLTaddl $ 16,%espmovl $ 0,%eaxLeal -8(%ebp),%esppopl%ecx.cfi_restore 1.cfi_def_cfa 1,0popl%ebx.cfi_restore 3popl%ebp.cfi_restore 5leal -4(%ecx),%esp.cfi_def_cfa 4,4退回.cfi_endproc.LFE0:.size main,.- main.section .text .__ x86.get_pc_thunk.ax,"axG",@ progbits,__ x86.get_pc_thunk.ax,comdat.globl __x86.get_pc_thunk.ax.hidden __x86.get_pc_thunk.ax.type __x86.get_pc_thunk.ax,@function__x86.get_pc_thunk.ax:.LFB1:.cfi_startprocmovl(%esp),%eax退回.cfi_endproc.LFE1:.ident"GCC:(GNU)9.2.0".section .note.GNU-stack,",@ progbits
为什么使用GOTOFF?%eax中是否已经加载了GOT的地址?GOT和GOTOFF有什么区别?
symbol @ GOTOFF相对于GOT基础(作为方便但任意选择的锚点)来寻址变量本身.其中的 lea
会为您提供符号地址,而 mov
会为您提供符号处的数据.(在这种情况下,字符串的前几个字节.)
symbol @ GOT会为您提供该符号的GOT项的偏移量(在GOT内).从那里加载 mov
会给您符号的地址.(GOT条目由动态链接器填写).
为什么使用共享库本身定义的符号使用全局偏移表吗?有一个访问 extern
变量的示例,该变量的确导致从GOT获取其地址,然后对其取消引用.>
顺便说一句,这是与位置无关的代码.默认情况下,您的GCC是以这种方式配置的.如果您使用 -fno-pie -no-pie
来制作传统的与位置相关的可执行文件,那么您将得到一个普通有效的 pushl $ .LC0
.(由于缺少32位的RIP相对寻址,因此效率很低.)
在非PIE(或64位PIE)中,GOT几乎没有被使用.主可执行文件定义了符号的空间,因此它可以访问符号而无需通过GOT.libc代码仍然使用GOT(主要是因为在64位代码中插入了符号),因此让主可执行文件提供符号不会花费任何费用,而且会使非PIE可执行文件更快.
我们可以获得一个非PIE可执行文件,以通过 -fno-plt
将GOT直接用于共享库函数地址,而不是调用PLT并使其使用GOT.
#include< stdio.h>无效foo(){putchar('\ n');}
Why does it use GOTOFF? Isn't the address of GOT already loaded in %eax? What is the difference between GOT and GOTOFF? symbol@GOTOFF addresses the variable itself, relative to the GOT base (as a convenient but arbitrary choice of anchor). symbol@GOT gives you offset (within the GOT) of the GOT entry, for that symbol. A Why use the Global Offset Table for symbols defined in the shared library itself? has an example of accessing an BTW, this is position-independent code. Your GCC is configured that way by default. If you used In a non-PIE (or in 64-bit PIE), the GOT barely gets used at all. The main executable defines space for symbols so it can access them without going through the GOT. libc code uses the GOT anyway (mostly because of symbol interposition in 64-bit code) so letting the main executable provide the symbol doesn't cost anything and makes the non-PIE executable faster. We can get a non-PIE executable to use the GOT directly for shared library function addresses with gcc9.2 Both 这篇关于GOT和GOTOFF之间的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!lea
of that gives you symbol address, mov
would give you data at the symbol. (The first few bytes of the string in this case.)mov
load from there gives you the address of the symbol. (GOT entries are filled in by the dynamic linker).extern
variable that does result in getting its address from the GOT and then dereferencing that.
-fno-pie -no-pie
to make a traditional position-dependent executable, you'd just get a normal efficient pushl $.LC0
. (32-bit is missing RIP-relative addressing so it's quite inefficient.)-fno-plt
, instead of calling into the PLT and having it use the GOT.#include <stdio.h>
void foo() { putchar('\n'); }
-O3 -m32 -fno-plt
on Godbolt (-fno-pie
is the default on the Godbolt compiler explorer, unlike your system.)foo():
sub esp, 20 # gcc loves to waste an extra 16 bytes of stack
push DWORD PTR stdout # [disp32] absolute address
push 10
call [DWORD PTR _IO_putc@GOT]
add esp, 28
ret
push
and call
have a memory operand using a 32-bit absolute address. push
is loading the FILE*
value of stdout
from a known (link-time-constant) address. (There isn't a text relocation for it.)call
is loading the function pointer saved by the dynamic linker from the GOT. (And loading it directly into EIP.)