Исправлено перемещение глобальных переменных в независимые от позиции исполняемые файлы с GCC

Я ищу gcc флаг командной строки или другие параметры для создания GOTOFF переезды, а не GOT перемещения для моего статически связанного, независимого от позиции исполняемого файла i386. Подробнее о том, что я пробовал, ниже.

Мой исходный файл 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, Никто из них не работал, все они сделали gcc произвести GOT версия.

Я не смог найти какой-либо специфичный для i386 флаг на https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html или какой-либо универсальный флаг здесь: https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html

На самом деле, я получаю GOTOFF переезд для "..." строковые литералы, и я также хочу получить их для extern переменные.

Окончательный результат - статически связанный исполняемый файл в пользовательском двоичном формате (для которого я написал скрипт компоновщика GNU ld). Там нет динамического связывания и нет общих библиотек. Рандомизация адресов выполняется пользовательским загрузчиком, который может загрузить исполняемый файл по любому адресу. Поэтому мне нужен код, независимый от позиции. Для каждого сегмента нет отображения памяти: весь исполняемый файл загружается как есть, непрерывно.

Всю документацию, которую я смог найти в Интернете, рассказывают о позиционно-независимых исполняемых файлах, которые динамически связаны, и я не смог найти там ничего полезного.

2 ответа

Решение

Я не смог решить это с gcc -fPIE, поэтому я решил это вручную, обработав выходной файл.

я использую gcc -Wl,-q, с выходным исполняемым файлом ELF, содержащим перемещения. Я постобработаю этот исполняемый файл 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 ... - адреса перемещений R_386_32 в исполняемом файле ELF. В использовании objdump -O binary prog.elf prog.bin', and nowprog.bin'содержит независимый от позиции код, потому что он начинается с инструкций `add [ebx + ...], ebx', которые делают необходимые перемещения в коде, когда код начинает выполняться.

В зависимости от среды исполнения, gcc флаг -Wl,-N необходимо, чтобы сделать .text доступный для записи раздел (это нужно инструкциям `add [ebx +...], ebx ').

Вы должны сообщить компилятору, что после связывания глобальные переменные окажутся в одном и том же загружаемом модуле. Это делается путем указания их видимости как «скрытых» либо с помощью атрибута:

      __attribute__((visibility("hidden")))
extern int answer;

int get_answer1() { return answer; }

или используя прагму:

      #pragma GCC visibility push(hidden)
extern int answer;
// In your case there is no need to restore visibility
// #pragma GCC visibility pop
int get_answer1() { return answer; }

(но учтите, что-fvisibility=hiddenвлияет только на определения, а не на объявления, поэтому для этой цели это бесполезно)

Поскольку в конечном итоге вы связываете все вместе, вы можете придать всему скрытую видимость. Вы можете положить

      #pragma GCC visibility push(hidden)

в отдельном файле (скажемvis.h) и включать его везде, используя-include vis.h. Или даже сказатьgcc -include <(echo '#pragma GCC visibility push(hidden)')если ваша система сборки позволяет это.

Под-flto -fpie -pieGCC автоматически определит скрытую видимость.

Другие вопросы по тегам