Исправлено перемещение глобальных переменных в независимые от позиции исполняемые файлы с 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 now
prog.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 -pie
GCC автоматически определит скрытую видимость.