Динамическое связывание простого двоичного файла - PullRequest
1 голос
/ 31 марта 2019

Я хочу написать свой собственный ld.so, и я хочу сделать это шаг за шагом.Я не смог найти «руководства» по кодированию моего ld.so, поэтому я хочу сделать это сам.Я думал, что сначала попробую загрузить простой двоичный файл в память, как показано ниже;тогда позвони.Это очень просто, и это уже не работает.

Двоичный файл:

section .text
global _start

_start:
    mov edi, 123
    mov eax, 60
    syscall

вызывающий выход (123):

$ nasm -f elf64 bin.asm && ld bin.o && ./a.out; echo $?
$ 123

Загрузчик:

FILE *fp = fopen(argv[1], "r");
    if (!fp) {
        fprintf(stderr, "cannot open file %s", argv[1]);
        return 1;
    }

    fseek(fp, 0L, SEEK_END);
    size_t sz = ftell(fp) + 1;
    rewind(fp);

    char *contents = malloc(sizeof(char) * sz);
    size_t pagesize = getpagesize();
    void *base_addr = (void*) (pagesize * (1 << 20));

    char *region = mmap(
            base_addr,
            pagesize,
            PROT_READ | PROT_WRITE | PROT_EXEC,
            MAP_ANON | MAP_PRIVATE,
            0, 0
            );
    if (region == MAP_FAILED) {
        fprintf(stderr, "could not mmap");
        return 1;
    }

    for (int i = 1, nread = 0; nread != sz * sizeof(char) && i > 0; nread += i) {
        i = fread(contents, sizeof(char), sz, fp);
    }
    contents[sz - 1] = 0;
    if (ferror(fp)) {
        fprintf(stderr, "error reading file %s", argv[1]);
        return 1;
    }

    memcpy(region, contents, sz);
    if (mprotect(region, pagesize, PROT_READ | PROT_EXEC)) {
        fprintf(stderr, "mprotect failed");
        return 1;
    }

    return ((int (*)()) base_addr)();

Я думаю, что произойдет: my_linker -> двоичный файл в памяти -> вызов mov edi, 123, возвращение 123.

Что происходит: "SIGSEGV по адресу 0x0"

IЯ запускаю это в Linux x86_64.


EDIT: в ответ на @Ctx.memcpy вместо strncpy.

Я должен был заявить, что он очищен.Я бегу nasm -f elf..., чтобы показать, что он делает то, что ожидается.В качестве аргумента программы, nasm -f bin -o prog.bin ... двоичный файл.

1 Ответ

0 голосов
/ 31 марта 2019

Две основные проблемы:

Неправильное использование strncpy()

Здесь вы используете strncpy(), чтобы скопировать двоичный код на вашу mmap() страницу педа.:

strncpy(region, contents, sz);

Но strncpy() прекращает копирование с первого нулевого байта, и, вероятно, в двоичном файле он находится довольно рано.Вы должны использовать memcpy() для этой задачи!

Второй выпуск:

Формат ELF

Предполагается, что код начинается сначало вашего двоичного файла.Но здесь

$ nasm -f elf64 bin.asm && ld bin.asm && ./a.out; echo $?

вы связываете его с двоичным файлом формата ELF.Так что все начинается с заголовков ELF, а не с кода.По сути, есть две возможности: либо вычислить смещение из заголовков ELF, либо использовать objcopy для извлечения чистого кода из двоичного файла:

objcopy -O binary -j text a.out bin

Редактировать: вы пытались использовать

nasm -f bin -o prog.bin bin.asm

, но по умолчанию получается 16-битный код .Вы должны явно указать

bits 64

в исходном файле ассемблера, чтобы получить 64-битный код.

Зачем использовать fread () / memcpy ()

Нет особого смысла использовать fread() в буфере и memcpy() после этого, вы можете просто mmap() двоичный файл в памяти, не читая его.

char *region = mmap(
        base_addr,
        sz,
        PROT_READ | PROT_EXEC,
        MAP_PRIVATE | MAP_FIXED,
        fileno(fp), 0
        );
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...