Разница между GOT и GOTOFF - PullRequest
       59

Разница между GOT и GOTOFF

1 голос
/ 11 октября 2019

Я новичок в 32-битной сборке, и я попытался скомпилировать простую C-программу в Assembly. Я понимаю большинство из них, за исключением случаев, когда он использует 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?

1 Ответ

1 голос
/ 12 октября 2019

Символ @ GOTOFF обращается к самой переменной относительно базы GOT (в качестве удобного, но произвольного выбора привязки) . lea из этого дает вам адрес символа, mov даст вам данные в символе. (Первые несколько байтов строки в этом случае.)

символ @ GOT дает вам смещение (в пределах GOT) записи GOT для этого символа. mov загрузка оттуда дает вам адрес символа. (Записи GOT заполняются динамическим компоновщиком).

Зачем использовать глобальную таблицу смещений для символов, определенных в самой общей библиотеке? * У 1011 * есть пример доступа к переменной extern, котораядействительно приводит к получению своего адреса из GOT и затем разыменовывает его.


Кстати, это независимый от позиции код. Ваш GCC настроен таким образом по умолчанию. Если бы вы использовали -fno-pie -no-pie, чтобы сделать традиционную позицию - зависимой исполняемой, вы бы просто получили нормальный эффективный pushl $.LC0. (32-битная отсутствует RIP-относительная адресация, поэтому она довольно неэффективна.)

В не-PIE (или в 64-битном PIE) GOT практически не используется. Основной исполняемый файл определяет пространство для символов, поэтому он может получить к ним доступ, не проходя через GOT. В любом случае код libc использует GOT (в основном из-за взаимного расположения символов в 64-битном коде), поэтому предоставление основному исполняемому файлу символа обеспечивает ничего не требующее, и ускоряет выполнение не-PIE.

Мы можем получитьнеисполняемый PIE для непосредственного использования GOT для адресов функций совместно используемой библиотеки с -fno-plt вместо вызова в PLT и использования GOT.

#include <stdio.h>
void foo() { putchar('\n'); }

gcc9.2 -O3 -m32 -fno-pltна Godbolt (-fno-pie по умолчанию в проводнике компилятора Godbolt, в отличие от вашей системы.)

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 и call имеют операнд памяти, использующий 32-битный абсолютныйадрес. push загружает значение FILE*, равное stdout, с известного адреса (константа-время-ссылка). (Нет перемещения текста для него.)

call загружает указатель функции, сохраненный динамическим компоновщиком из GOT. (И загружает его прямо в EIP.)

...