Почему точка входа ELF 0x8048000 не изменена с опцией "ld -e"?

Вслед за Почему виртуальный адрес точки входа в 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

почему я не могу заставить 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 к линии ссылок.

Update:

Однако, когда я пытаюсь установить связь с программой 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 для 0x400080, 0x400084, 0x400090, 0x400094, 0x400098. Это может быть ошибкой в ​​компоновщике, или я нарушаю какое-то другое ограничение (я не вижу, хотя).

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

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

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

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

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