Почему точка входа ELF 0x8048000 не может быть изменена с опцией "ld -e"? - PullRequest
10 голосов
/ 14 ноября 2011

После Почему виртуальный адрес точки входа ELF имеет вид 0x80xxxxx, а не ноль 0x0? и Почему адреса виртуальной памяти для бинарных файлов Linux начинаются с 0x8048000? я не могу заставить ld использовать точку входа, отличную от значения по умолчанию с ld -e?

Если я это сделаю, я либо получу segmentation fault с кодом возврата 139, даже для адресов, близких к точке входа по умолчанию. Почему?

EDIT:

Я уточню вопрос:

        .text
        .globl _start    
_start:
        movl   $0x4,%eax        # eax = code for 'write' system call   
        movl   $1,%ebx          # ebx = file descriptor to standard output
        movl   $message,%ecx    # ecx = pointer to the message
        movl   $13,%edx         # edx = length of the message
        int    $0x80            # make the system call
        movl   $0x0,%ebx        # the status returned by 'exit'
        movl   $0x1,%eax        # eax = code for 'exit' system call
        int    $0x80            # make the system call
        .data
        .globl message
message:        
        .string "Hello world\n" # The message as data

Если я скомпилирую это с as program.s -o program.o, а затем статически свяжу его с ld -N program.o -o program, readelf -l program покажет 0x0000000000400078 в качестве VirtAddr текстового сегмента и 0x400078 в качестве точки входа. При запуске печатается "Hello world".

Однако, когда я пытаюсь связать с ld -N -e0x400082 -Ttext=0x400082 program.o -o program (перемещение сегмента текста и точки входа на 4 байта), программа будет killed. Проверка с помощью readelf -l теперь показывает два разных заголовка типа LOAD, один в 0x0000000000400082 и один в 0x00000000004000b0.

Когда я пытаюсь 0x400086, все работает, и есть только один LOAD раздел.

  1. Что здесь происходит?
  2. Какие адреса памяти я могу выбрать, какие не могу выбрать и почему?

Спасибо.

1 Ответ

24 голосов
/ 14 ноября 2011

почему я не могу заставить ld использовать точку входа, отличную от значения по умолчанию, с ld -e

Вы можете это сделать.Это:

int foo(int argc, char *argv[]) { return 0; }

gcc main.c -Wl,-e,foo

не будет работать, потому что выполнение не начинается с main.Он начинается с _start, который связан с crt0.o (часть glibc) и обеспечивает правильную работу таких вещей, как динамическое связывание и т. Д.Перенаправив _start на foo, вы обошли все, что требовалось для инициализации glibc, и поэтому все не работает.

Но если вам не нужны динамические ссылки, и вы готовы делать то, что нужноGlibc обычно делает для вас, тогда вы можете назвать точку входа как хотите.Пример:

#include <syscall.h>

int foo()
{
  syscall(SYS_write, 1, "Hello, world\n", 13);
  syscall(SYS_exit, 0);
}

gcc t.c -static -nostartfiles -Wl,-e,foo && ./a.out
Hello, world

Да, и ваш заголовок этого вопроса не соответствует вашему фактическому вопросу (плохая идея (TM)).

Чтобы ответить на вопрос в заголовке, вы уверены, что может изменить адрес, с которым связан ваш исполняемый файл.По умолчанию вы получаете 0x8048000 адрес загрузки (только в 32-битном режиме; 64-битный по умолчанию 0x400000).

Вы можете легко изменить его, например, на 0x80000, добавив -Wl,-Ttext-segment=0x80000 кстрока ссылки.

Обновление:

Однако, когда я пытаюсь установить связь с программой ld -N -e0x400082 -Ttext = 0x400082 program.o -o (перемещение сегмента текста и точки входана 4 байта), программа будет убита.

Ну, невозможно присвоить Ttext на 0x400082, не нарушив .text ограничение выравнивания раздела (которое равно 4).Вы должны сохранять .text адрес выровненным по крайней мере на 4-байтовой границе (или изменить требуемое выравнивание на .text).

Когда я устанавливаю начальный адрес 0x400078, 0x40007c, 0x400080, 0x400084, ..., 0x400098 и использовать GNU-ld 2.20.1, программа работает.

Однако, когда я использую текущий снимок CVS binutils, программа работает для 0x400078, 0x40007c, 0x400088, 0x40008c и получает Killed для 0x4000800x400084, 0x400090, 0x400094, 0x400098.Это может быть ошибка в компоновщике, или я нарушаю какое-то другое ограничение (хотя я не вижу, какое именно).

На данный момент, если вы действительно заинтересованы, я предлагаюзагрузка исходных файлов binutils, сборка ld и выяснение того, что именно заставляет его создавать два PT_LOAD сегмента вместо одного.

Обновление 2:

Форсировать новый сегмент для секцийс перекрывающимися LMA.

Ах!Это просто означает, что вам нужно убрать .data с дороги.Это делает рабочий исполняемый файл:

ld -N -o t t.o -e0x400080 -Ttext=0x400080 -Tdata=0x400180
...