我是32位程序集的初学者,我试着将一个简单的C程序编译成程序集。除了使用GOTOFF之外,我大部分都能理解。
.file "main.c"
.text
.section .rodata
.LC0:
.string "Hello world"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
leal 4(%esp), %ecx
.cfi_def_cfa 1, 0
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
.cfi_escape 0x10,0x5,0x2,0x75,0
movl %esp, %ebp
pushl %ebx
pushl %ecx
.cfi_escape 0xf,0x3,0x75,0x78,0x6
.cfi_escape 0x10,0x3,0x2,0x75,0x7c
call __x86.get_pc_thunk.ax
addl $_GLOBAL_OFFSET_TABLE_, %eax
subl $12, %esp
leal .LC0@GOTOFF(%eax), %edx # <- Here
pushl %edx
movl %eax, %ebx
call puts@PLT
addl $16, %esp
movl $0, %eax
leal -8(%ebp), %esp
popl %ecx
.cfi_restore 1
.cfi_def_cfa 1, 0
popl %ebx
.cfi_restore 3
popl %ebp
.cfi_restore 5
leal -4(%ecx), %esp
.cfi_def_cfa 4, 4
ret
.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_startproc
movl (%esp), %eax
ret
.cfi_endproc
.LFE1:
.ident "GCC: (GNU) 9.2.0"
.section .note.GNU-stack,"",@progbits为什么要使用GOTOFF?GOT的地址不是已经加载在%eax中了吗?GOT和GOTOFF有什么区别?
发布于 2019-10-11 22:45:48
符号@GOTOFF处理变量本身,相对于GOT基(作为一个方便但任意选择的锚)。lea给出符号地址,mov给出符号处的数据。(在本例中,字符串的前几个字节。)
符号@ GOT为该符号提供GOT条目的偏移量(在GOT内)。从那里加载的mov将给出符号的地址。(获取条目由动态链接器填写)。
Why use the Global Offset Table for symbols defined in the shared library itself?有一个访问extern变量的示例,该变量会从GOT获取其地址,然后取消引用。
顺便说一下,这是与位置无关的代码。你的GCC在默认情况下是这样配置的。如果您使用-fno-pie -no-pie来制作一个传统的位置相关的可执行文件,那么您将得到一个正常有效的pushl $.LC0。(32位缺少RIP相对寻址,因此效率很低。)
在非饼类(或64位派)中,GOT几乎不被使用。主可执行文件为符号定义了空间,这样它就可以访问符号,而无需经过GOT。libc代码无论如何都会使用GOT (主要是因为64位代码中的符号插入),因此让主可执行文件提供符号不需要花费任何费用,并且使非饼形可执行文件更快。
我们可以使用-fno-plt直接为共享库函数地址使用GOT,而不是调用PLT并让它使用GOT。
#include <stdio.h>
void foo() { putchar('\n'); }gcc9.2 -O3 -m32 -fno-plt on Godbolt (与您的系统不同,-fno-pie是戈德波特编译器资源管理器的默认设置)。
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
retpush和call都有一个使用32位绝对地址的内存操作数.push正在从一个已知的(链接时间常数)地址加载stdout的FILE*值.(它没有文本重定位。)
call正在加载动态链接器从GOT保存的函数指针。(并直接将其加载到EIP。)
https://stackoverflow.com/questions/58346418
复制相似问题