Как сделать исполняемый файл ELF в Linux с помощью шестнадцатеричного редактора?

просто любопытно. Это, очевидно, не очень хорошее решение для фактического программирования, но, скажем, я хотел сделать исполняемый файл в Bless (hex editor).

моя архитектура x86. Какую очень простую программу я могу сделать? Привет миру? Бесконечная петля? Похожие на этой вопрос, но в Linux.

2 ответов


Как упоминалось в моем комментарии, вы по существу будете писать свой собственный ELF-заголовок для исполняемого файла, устраняя ненужные разделы. Есть еще несколько необходимых разделов. Документация по адресу Muppetlabs-TinyPrograms делает справедливую работу, объясняя этот процесс. Для удовольствия, вот несколько примеров:

эквивалент /bin / true (45 байт):

00000000  7F 45 4C 46 01 00 00 00  00 00 00 00 00 00 49 25  |.ELF..........I%|
00000010  02 00 03 00 1A 00 49 25  1A 00 49 25 04 00 00 00  |......I%..I%....|
00000020  5B 5F F2 AE 40 22 5F FB  CD 80 20 00 01           |[_..@"_... ..|
0000002d

классический 'Привет, Мир!'(160 байт):

00000000  7f 45 4c 46 01 01 01 03  00 00 00 00 00 00 00 00  |.ELF............|
00000010  02 00 03 00 01 00 00 00  74 80 04 08 34 00 00 00  |........t...4...|
00000020  00 00 00 00 00 00 00 00  34 00 20 00 02 00 28 00  |........4. ...(.|
00000030  00 00 00 00 01 00 00 00  74 00 00 00 74 80 04 08  |........t...t...|
00000040  74 80 04 08 1f 00 00 00  1f 00 00 00 05 00 00 00  |t...............|
00000050  00 10 00 00 01 00 00 00  93 00 00 00 93 90 04 08  |................|
00000060  93 90 04 08 0d 00 00 00  0d 00 00 00 06 00 00 00  |................|
00000070  00 10 00 00 b8 04 00 00  00 bb 01 00 00 00 b9 93  |................|
00000080  90 04 08 ba 0d 00 00 00  cd 80 b8 01 00 00 00 31  |...............1|
00000090  db cd 80 48 65 6c 6c 6f  20 77 6f 72 6c 64 21 0a  |...Hello world!.|
000000a0

Не забудьте сделать их исполняемыми...


Декомпилируйте NASM hello world и поймите каждый байт в нем

версия этого ответа с хорошим TOC и большим содержанием:http://www.cirosantilli.com/elf-hello-world (ударяя предел 30K char здесь)

стандарты

ELF указывается LSB:

LSB в основном ссылается на другие стандарты с незначительными расширениями, в частности:

удобное резюме можно найти по адресу:

man elf

его структура может быть рассмотрена читаемым человеком способом с помощью утилит, таких как readelf и objdump.

создать пример

давайте сломаем минимальный запускаемый Linux x86-64 пример:

section .data
    hello_world db "Hello world!", 10
    hello_world_len  equ $ - hello_world
section .text
    global _start
    _start:
        mov rax, 1
        mov rdi, 1
        mov rsi, hello_world
        mov rdx, hello_world_len
        syscall
        mov rax, 60
        mov rdi, 0
        syscall

составлен с:

nasm -w+all -f elf64 -o 'hello_world.o' 'hello_world.asm'
ld -o 'hello_world.out' 'hello_world.o'

варианты:

  • NASM 2.10.09
  • Binutils версии 2.24 (содержит ld)
  • Ubuntu 14.04

мы не используем программу на C, так как это усложнит анализ, это будет Уровень 2 :-)

Hexdumps

hd hello_world.o
hd hello_world.out

выход на:https://gist.github.com/cirosantilli/7b03f6df2d404c0862c6

глобальный файл структура

файл ELF содержит следующие части:

  • заголовок ELF. Указывает на положение таблицы заголовка раздела и таблицы заголовка программы.

  • таблица заголовка раздела (необязательно для исполняемого файла). У каждого есть e_shnum заголовки разделов, каждый из которых указывает на положение раздела.

  • N секций, с N <= e_shnum (необязательный на исполняемый файл)

  • таблица заголовка программы (только для исполняемого файла). У каждого есть e_phnum заголовки программ, каждый из которых указывает на положение сегмента.

  • N сегментов, с N <= e_phnum (необязательно для исполняемого файла)

порядок этих частей составляет не исправлено: единственная исправленная вещь-заголовок ELF, который должен быть первым в файле: Generic docs say:

эльф заголовок

самый простой способ наблюдать за заголовком:

readelf -h hello_world.o
readelf -h hello_world.out

выход на:https://gist.github.com/cirosantilli/7b03f6df2d404c0862c6

байт в объектном файле:

00000000  7f 45 4c 46 02 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  01 00 3e 00 01 00 00 00  00 00 00 00 00 00 00 00  |..>.............|
00000020  00 00 00 00 00 00 00 00  40 00 00 00 00 00 00 00  |........@.......|
00000030  00 00 00 00 40 00 00 00  00 00 40 00 07 00 03 00  |....@.....@.....|

исполняемый файл:

00000000  7f 45 4c 46 02 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  02 00 3e 00 01 00 00 00  b0 00 40 00 00 00 00 00  |..>.......@.....|
00000020  40 00 00 00 00 00 00 00  10 01 00 00 00 00 00 00  |@...............|
00000030  00 00 00 00 40 00 38 00  02 00 40 00 06 00 03 00  |....@.8...@.....|

структуру:

typedef struct {
    unsigned char   e_ident[EI_NIDENT];
    Elf64_Half      e_type;
    Elf64_Half      e_machine;
    Elf64_Word      e_version;
    Elf64_Addr      e_entry;
    Elf64_Off       e_phoff;
    Elf64_Off       e_shoff;
    Elf64_Word      e_flags;
    Elf64_Half      e_ehsize;
    Elf64_Half      e_phentsize;
    Elf64_Half      e_phnum;
    Elf64_Half      e_shentsize;
    Elf64_Half      e_shnum;
    Elf64_Half      e_shstrndx;
} Elf64_Ehdr;

механическая поломка:

  • 0 0: EI_MAG = 7f 45 4c 46 = 0x7f 'E', 'L', 'F': эльф магия номер

  • 0 4: EI_CLASS = 02 = ELFCLASS64: 64 бит elf

  • 0 5: EI_DATA = 01 = ELFDATA2LSB: big endian data

  • 0 6: EI_VERSION = 01: версия формата

  • 0 7: EI_OSABI (только в обновлении 2003) = 00 = ELFOSABI_NONE: никаких расширений.

  • 0 8: EI_PAD = 8x 00: зарезервировано байт. Должно быть установлено в 0.

  • 1 0: e_type = 01 00 = 1 (обратный порядок байтов) = ET_REl: формат перемещаемых

    на исполняемом файле это 02 00 на ET_EXEC.

  • 1 2: e_machine = 3e 00 = 62 = EM_X86_64: архитектура AMD64

  • 1 4: e_version = 01 00 00 00: должно быть 1

  • 1 8: e_entry = 8x 00: точка входа адреса выполнения или 0, если нет применимо как для объектного файла, так как нет точки входа.

    на исполняемом файле это b0 00 40 00 00 00 00 00. TODO: на что еще мы можем это установить? Ядро, похоже, помещает IP непосредственно в это значение, оно не жестко закодировано.

  • 2 0: e_phoff = 8x 00: смещение таблицы заголовка программы, 0 если нет.

    40 00 00 00 на исполняемом файле, т. е. он запускается сразу после заголовка ELF.

  • 2 8: e_shoff = 40 7x 00 = 0x40: смещение файла таблицы заголовка раздела, 0 если нет.

  • 3 0: e_flags = 00 00 00 00 TODO. Arch specific.

  • 3 4: e_ehsize = 40 00: размер этого заголовка elf. TODO почему это поле? Как она может меняться?

  • 3 6: e_phentsize = 00 00: размер каждого заголовка программы, 0 если нет.

    38 00 on исполняемый файл: это 56 байт

  • 3 8: e_phnum = 00 00: количество записей заголовка программы, 0 если нет.

    02 00 на исполняемом файле: есть 2 записи.

  • 3 A:e_shentsize и e_shnum = 40 00 07 00: размер заголовка раздела и количество записей

  • 3 E:e_shstrndx (Section Header STRing iNDeX) = 03 00: индекс .

заголовок раздела таблица

массив Elf64_Shdr структуры.

каждая запись содержит метаданные о данном разделе.

e_shoff заголовка ELF дает начальную позицию, 0x40 здесь.

e_shentsize и e_shnum из заголовка ELF говорят, что у нас есть 7 записей, каждая 0x40 байт.

таким образом, таблица принимает байты от 0x40 до 0x40 + 7 + 0x40 - 1 = 0x1FF.

некоторые имена разделов зарезервированы для определенных типов разделов: http://www.sco.com/developers/gabi/2003-12-17/ch4.sheader.html#special_sections например .text требует SHT_PROGBITS тип и SHF_ALLOC + SHF_EXECINSTR

readelf -S hello_world.o:

There are 7 section headers, starting at offset 0x40:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .data             PROGBITS         0000000000000000  00000200
       000000000000000d  0000000000000000  WA       0     0     4
  [ 2] .text             PROGBITS         0000000000000000  00000210
       0000000000000027  0000000000000000  AX       0     0     16
  [ 3] .shstrtab         STRTAB           0000000000000000  00000240
       0000000000000032  0000000000000000           0     0     1
  [ 4] .symtab           SYMTAB           0000000000000000  00000280
       00000000000000a8  0000000000000018           5     6     4
  [ 5] .strtab           STRTAB           0000000000000000  00000330
       0000000000000034  0000000000000000           0     0     1
  [ 6] .rela.text        RELA             0000000000000000  00000370
       0000000000000018  0000000000000018           4     2     4
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

struct в лице каждой записи:

typedef struct {
    Elf64_Word  sh_name;
    Elf64_Word  sh_type;
    Elf64_Xword sh_flags;
    Elf64_Addr  sh_addr;
    Elf64_Off   sh_offset;
    Elf64_Xword sh_size;
    Elf64_Word  sh_link;
    Elf64_Word  sh_info;
    Elf64_Xword sh_addralign;
    Elf64_Xword sh_entsize;
} Elf64_Shdr;

разделы

.
  • 80 8: sh_flags = 03 7x 00: SHF_ALLOC и SHF_EXECINSTR: http://www.sco.com/developers/gabi/2003-12-17/ch4.sheader.html#sh_flags, как требуется от .data раздел

  • 90 0: sh_addr = 8x 00: в каком виртуальном адресе будет размещен раздел во время выполнения, 0 если не помещено

  • 90 8: sh_offset = 00 02 00 00 00 00 00 00 = 0x200: число байт от начала программы до первого байта в этом разделе

  • a0 0:sh_size = 0d 00 00 00 00 00 00 00

    если мы возьмем 0xD байта, начиная с sh_offset 200, видим:

    00000200  48 65 6c 6c 6f 20 77 6f  72 6c 64 21 0a 00        |Hello world!..  |
    

    АХА! Так наши "Hello world!" строка находится в разделе данных, как мы сказали, чтобы быть на NASM.

    как только мы закончим hd, мы будет выглядеть так:

    readelf -x .data hello_world.o
    

    выходы:

    Hex dump of section '.data':
      0x00000000 48656c6c 6f20776f 726c6421 0a       Hello world!.
    

    NASM устанавливает приличные свойства для этого раздела, потому что он обрабатывает .data волшебно:http://www.nasm.us/doc/nasmdoc7.html#section-7.9.2

    также обратите внимание, что это был плохой выбор раздела: хороший компилятор C поместил бы строку в .rodata вместо этого, потому что он доступен только для чтения, и это позволит для дальнейшей ОС процессы оптимизации.

  • a0 8:sh_link и sh_info = 8x 0: не применяется к этому типу раздела. http://www.sco.com/developers/gabi/2003-12-17/ch4.sheader.html#special_sections

  • b0 0:sh_addralign = 04 = TODO: почему это выравнивание необходимо? Это только для sh_addr, или также для символов внутри sh_addr?

  • b0 8:sh_entsize = 00 = раздел не содержать таблицу. Если != 0, это означает, что раздел содержит таблицу записей фиксированного размера. В этом файле мы видим из readelf выведите, что это относится к .symtab и .rela.text разделы.

  • .текстовый раздел

    теперь, когда мы сделали один раздел вручную, давайте закончим и используем readelf -S других разделов.

      [Nr] Name              Type             Address           Offset
           Size              EntSize          Flags  Link  Info  Align
      [ 2] .text             PROGBITS         0000000000000000  00000210
           0000000000000027  0000000000000000  AX       0     0     16
    

    .text является исполняемым, но не записываемым: если мы попытаемся написать на него Linux с падениями. Давайте посмотрим, действительно ли у нас есть какой-то код:

    objdump -d hello_world.o
    

    выдает:

    hello_world.o:     file format elf64-x86-64
    
    
    Disassembly of section .text:
    
    0000000000000000 <_start>:
       0:       b8 01 00 00 00          mov    x1,%eax
       5:       bf 01 00 00 00          mov    x1,%edi
       a:       48 be 00 00 00 00 00    movabs x0,%rsi
      11:       00 00 00
      14:       ba 0d 00 00 00          mov    xd,%edx
      19:       0f 05                   syscall
      1b:       b8 3c 00 00 00          mov    x3c,%eax
      20:       bf 00 00 00 00          mov    x0,%edi
      25:       0f 05                   syscall
    

    если мы grep b8 01 00 00 на hd, мы видим, что это происходит только в 00000210, что и говорится в разделе. И размер 27, который также совпадает. Поэтому мы должны говорить о правильном разделе.

    это выглядит как правильный код:write затем exit.

    самая интересная часть-line a что делает:

    movabs x0,%rsi
    

    для передачи адреса строки в системный вызов. В настоящее время 0x0 - это просто заполнитель. После связывания происходит, он будет изменен, чтобы содержать:

    4000ba: 48 be d8 00 60 00 00    movabs x6000d8,%rsi
    

    это изменение возможно из-за данных .

    sht_strtab и атрибут

    разделы sh_type == SHT_STRTAB называют строка таблицы.

    они содержат null разделенный массив веревка.

    такие разделы используются другими разделами при использовании имен строк. В разделе using говорится:

    • какую таблицу строк они используют
    • что такое индекс в целевой таблице строк, где начинается строка

    например, у нас может быть таблица строк, содержащая: TODO: она должна начинаться с ?

    Data:  a b c  d e f 
    Index: 0 1 2 3  4 5 6 7  8
    

    и если другой раздел хочет использовать строку d e f, они должны указывать на index 5 этого раздела (письмо d).

    примечательные разделы таблицы строк:

    • .shstrtab
    • .strtab

    .shstrtab

    тип раздела: sh_type == SHT_STRTAB.

    общее имя: таблица строк заголовка раздела.

    название раздела .shstrtab зарезервирован. Стандарт гласит:

    этот раздел содержит имена разделов.

    на этот раздел указывает e_shstrnd поле самого заголовка ELF.

    строковые индексы этого раздела указывают на sh_name поле заголовков разделов, которые обозначают строки.

    в этом разделе нет SHF_ALLOC отмечен, поэтому он не будет отображаться в исполняющей программе.

    readelf -x .shstrtab hello_world.o
    

    выдает:

    Hex dump of section '.shstrtab':
      0x00000000 002e6461 7461002e 74657874 002e7368 ..data..text..sh
      0x00000010 73747274 6162002e 73796d74 6162002e strtab..symtab..
      0x00000020 73747274 6162002e 72656c61 2e746578 strtab..rela.tex
      0x00000030 7400                                t.
    

    данные в этом раздел имеет фиксированный формат: http://www.sco.com/developers/gabi/2003-12-17/ch4.strtab.html

    если мы посмотрим на названия других разделов, мы увидим, что все они содержат номера, например номером 7.

    затем каждая строка заканчивается, когда найден первый символ NUL, например, символ 12 is сразу после .text.

    .symtab

    тип раздела : sh_type == SHT_SYMTAB.

    общее имя: таблица символов.

    Сначала отметим, что:

    • sh_link = 5
    • sh_info = 6

    на SHT_SYMTAB разделы, эти цифры означают, что:

    • строки, которые дают имена символов, находятся в разделе 5,.strtab
    • данные переселение в разделе 6, .rela.text

    хороший инструмент высокого уровня для разборки этого раздела:

    nm hello_world.o
    

    что дает:

    0000000000000000 T _start
    0000000000000000 d hello_world
    000000000000000d a hello_world_len
    

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

    readelf -s hello_world.o
    

    что дает:

    Symbol table '.symtab' contains 7 entries:
       Num:    Value          Size Type    Bind   Vis      Ndx Name
         0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
         1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS hello_world.asm
         2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1
         3: 0000000000000000     0 SECTION LOCAL  DEFAULT    2
         4: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT    1 hello_world
         5: 000000000000000d     0 NOTYPE  LOCAL  DEFAULT  ABS hello_world_len
         6: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT    2 _start
    

    двоичный формат таблицы описана в http://www.sco.com/developers/gabi/2003-12-17/ch4.symtab.html

    данные:

    readelf -x .symtab hello_world.o
    

    что дает:

    Hex dump of section '.symtab':
      0x00000000 00000000 00000000 00000000 00000000 ................
      0x00000010 00000000 00000000 01000000 0400f1ff ................
      0x00000020 00000000 00000000 00000000 00000000 ................
      0x00000030 00000000 03000100 00000000 00000000 ................
      0x00000040 00000000 00000000 00000000 03000200 ................
      0x00000050 00000000 00000000 00000000 00000000 ................
      0x00000060 11000000 00000100 00000000 00000000 ................
      0x00000070 00000000 00000000 1d000000 0000f1ff ................
      0x00000080 0d000000 00000000 00000000 00000000 ................
      0x00000090 2d000000 10000200 00000000 00000000 -...............
      0x000000a0 00000000 00000000                   ........
    

    записи типа:

    typedef struct {
        Elf64_Word  st_name;
        unsigned char   st_info;
        unsigned char   st_other;
        Elf64_Half  st_shndx;
        Elf64_Addr  st_value;
        Elf64_Xword st_size;
    } Elf64_Sym;
    

    как и в таблице разделов, первая запись является магической и имеет фиксированные бессмысленные значения.

    STT_FILE

    запись 1 имеет ELF64_R_TYPE == STT_FILE. ELF64_R_TYPE продолжение внутри st_info.

    байт анализ:

    • 10 8: st_name = 01000000 = символ 1 в .strtab, который до делает hello_world.asm

      этот информационный файл может использоваться компоновщиком для решения, к какому сегменту идти.

    • 10 12: st_info = 04

      биты 0-3 = ELF64_R_TYPE = Type = 4 = STT_FILE: основная цель этой записи-использовать st_name to Укажите имя файла, который сгенерировал этот объектный файл.

      биты 4-7 = ELF64_ST_BIND = обязательного = 0 = STB_LOCAL. Требуемое значение для STT_FILE.

    • 10 13: st_shndx = индекс заголовка раздела таблицы символов = f1ff = SHN_ABS. Требуется для STT_FILE.

    • 20 0: st_value = 8x 00: требуется для значения для STT_FILE

    • 20 8: st_size = 8x 00: не выделены размере

    теперь из readelf, мы быстро интерпретируем остальные.

    STT_SECTION

    есть две такие записи, одна из которых указывает на .data, а другой .text (индексы разделе 1 и 2).

    Num:    Value          Size Type    Bind   Vis      Ndx Name
      2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1
      3: 0000000000000000     0 SECTION LOCAL  DEFAULT    2
    

    TODO какова их цель?

    STT_NOTYPE

    затем идут самые важные символы:

    Num:    Value          Size Type    Bind   Vis      Ndx Name
      4: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT    1 hello_world
      5: 000000000000000d     0 NOTYPE  LOCAL  DEFAULT  ABS hello_world_len
      6: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT    2 _start
    

    hello_world строка в (индекс 1). Это значение равно 0: оно указывает на первый байт этого раздела.

    _start обозначен GLOBAL видимость, так как мы написали:

    global _start
    

    в NASM. Это необходимо, поскольку она должна рассматриваться как точка входа. В отличие от C, по умолчанию метки NASM являются локальными.

    SHN_ABS

    hello_world_len указывает на специальные st_shndx == SHN_ABS == 0xF1FF.

    0xF1FF выбирается так, чтобы не конфликтовать с другими разделами.

    st_value == 0xD == 13 какое значение мы сохранили там в сборке: длина строки Hello World!.

    это означает, что перемещение не повлияет на это значение: это константа.

    это небольшая оптимизация, которую наш ассемблер делает для нас и которая имеет поддержку ELF.

    если бы мы использовали адрес hello_world_len в любом месте ассемблер не смог бы отметить его как SHN_ABS, и компоновщик будет иметь дополнительную работу по перемещению на нем позже.

    Раздел sht_symtab на исполняемый

    по умолчанию NASM помещает .symtab на исполняемом файле, а также.

    это используется только для отладки. Без символов мы полностью слепы и должны все перестроить.

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

    .strtab

    содержит строки для символа таблица.

    в этом разделе sh_type == SHT_STRTAB.

    он указал sh_link == 5 на .

    readelf -x .strtab hello_world.o
    

    выдает:

    Hex dump of section '.strtab':
      0x00000000 0068656c 6c6f5f77 6f726c64 2e61736d .hello_world.asm
      0x00000010 0068656c 6c6f5f77 6f726c64 0068656c .hello_world.hel
      0x00000020 6c6f5f77 6f726c64 5f6c656e 005f7374 lo_world_len._st
      0x00000030 61727400                            art.
    

    это означает, что это ограничение уровня ELF, что глобальные переменные не могут содержать символы NUL.

    .Рела.текст

    тип раздела: sh_type == SHT_RELA.

    общее имя: раздел переезд.

    .rela.text держит данные перемещения, которые говорят, как адрес должен быть изменен, когда окончательный исполняемый файл связан. Это указывает на байты текстовой области, которые должны быть изменены при связывании происходит указать на правильные места памяти.

    в основном, он переводит текст объекта, содержащий адрес заполнителя 0x0:

       a:       48 be 00 00 00 00 00    movabs x0,%rsi
      11:       00 00 00
    

    к фактическому исполняемому коду, содержащему окончательный 0x6000d8:

    4000ba: 48 be d8 00 60 00 00    movabs x6000d8,%rsi
    4000c1: 00 00 00
    

    на него указал sh_info = 6 of the .

    readelf -r hello_world.o выдает:

    Relocation section '.rela.text' at offset 0x3b0 contains 1 entries:
      Offset          Info           Type           Sym. Value    Sym. Name + Addend
    00000000000c  000200000001 R_X86_64_64       0000000000000000 .data + 0
    

    раздел не существует в исполняемом файле.

    фактические байты:

    00000370  0c 00 00 00 00 00 00 00  01 00 00 00 02 00 00 00  |................|
    00000380  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    

    на struct является:

    typedef struct {
        Elf64_Addr  r_offset;
        Elf64_Xword r_info;
        Elf64_Sxword    r_addend;
    } Elf64_Rela;
    

    так:

    • 370 0: r_offset = 0xC: адрес в .text чей адрес это перемещение изменит

    • 370 8: r_info = 0x200000001. Содержит 2 поля:

      • ELF64_R_TYPE = 0x1: значение зависит от точной архитектуры.
      • ELF64_R_SYM = 0x2: индекс раздела, на который указывает адрес, так что .data который находится в индексе 2.

      amd64 ABI говорит, что тип 1 называется R_X86_64_64 и что он представляет собой операцию S + A где:

      • S: значение символа в объектном файле, здесь 0 потому что мы указываем на 00 00 00 00 00 00 00 00 of movabs x0,%rsi
      • A: добавление, присутствует в поле r_added

      этот адрес добавляется в раздел, на котором выполняется перемещение.

      эта операция перемещения действует в общей сложности 8 байт.

    • 380 0: r_addend = 0

    Итак, в нашем примере мы заключаем, что новый адрес будет:S + A = .data + 0, и таким образом первый вещь в разделе данных.

    таблица заголовка программы

    отображается только в исполняемом файле.

    содержит информацию о том, как исполняемый файл должен быть помещен в виртуальную память процесса.

    исполняемый файл создается компоновщиком из объектных файлов. Основные задания, которые выполняет компоновщик:

    • определите, какие разделы объектных файлов будут входить в какие сегменты выполнимый.

      в Binutils это сводится к разбору скрипта компоновщика и работе с кучей значений по умолчанию.

      вы можете получить сценарий компоновщика, используемый с ld --verbose, и установите пользовательский с ld -T.

    • сделать перемещение по тексту разделов. Это зависит от того, как несколько разделов помещаются в память.

    readelf -l hello_world.out выдает:

    Elf file type is EXEC (Executable file)
    Entry point 0x4000b0
    There are 2 program headers, starting at offset 64
    
    Program Headers:
      Type           Offset             VirtAddr           PhysAddr
                     FileSiz            MemSiz              Flags  Align
      LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                     0x00000000000000d7 0x00000000000000d7  R E    200000
      LOAD           0x00000000000000d8 0x00000000006000d8 0x00000000006000d8
                     0x000000000000000d 0x000000000000000d  RW     200000
    
     Section to Segment mapping:
      Segment Sections...
       00     .text
       01     .data
    

    на заголовке ELF,e_phoff, e_phnum и e_phentsize сказал нам, что есть 2 заголовка программы, которые начинаются с 0x40 и 0x38 байт длиной каждый, поэтому они:

    00000040  01 00 00 00 05 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000050  00 00 40 00 00 00 00 00  00 00 40 00 00 00 00 00  |..@.......@.....|
    00000060  d7 00 00 00 00 00 00 00  d7 00 00 00 00 00 00 00  |................|
    00000070  00 00 20 00 00 00 00 00                           |.. .....        |
    

    и:

    00000070                           01 00 00 00 06 00 00 00  |        ........|
    00000080  d8 00 00 00 00 00 00 00  d8 00 60 00 00 00 00 00  |..........`.....|
    00000090  d8 00 60 00 00 00 00 00  0d 00 00 00 00 00 00 00  |..`.............|
    000000a0  0d 00 00 00 00 00 00 00  00 00 20 00 00 00 00 00  |.......... .....|
    

    структура представлена http://www.sco.com/developers/gabi/2003-12-17/ch5.pheader.html:

    typedef struct {
        Elf64_Word  p_type;
        Elf64_Word  p_flags;
        Elf64_Off   p_offset;
        Elf64_Addr  p_vaddr;
        Elf64_Addr  p_paddr;
        Elf64_Xword p_filesz;
        Elf64_Xword p_memsz;
        Elf64_Xword p_align;
    } Elf64_Phdr;
    

    разбивка первого:

    • 40 0: p_type = 01 00 00 00 = PT_LOAD: TODO. Я думаю, что это будет на самом деле загружено в память. Прочая не обязательно.
    • 40 4: p_flags = 05 00 00 00 = выполнять и читать разрешения, не писать TODO
    • 40 8: p_offset = 8x 00 TODO: что это? Похоже на смещения от начала сегментов. Но это означает, что некоторые сегменты переплетены? С ним можно немного поиграть с:gcc -Wl,-Ttext-segment=0x400030 hello_world.c
    • 50 0: p_vaddr = 00 00 40 00 00 00 00 00: начальный адрес виртуальной памяти для загрузки этого сегмента к
    • 50 8: p_paddr = 00 00 40 00 00 00 00 00: начальный физический адрес для загрузки в память. Имеет значение только для систем, в которых программа может установить свой физический адрес. В противном случае, как и в System V like systems, может быть что угодно. NASM, кажется, просто копирует p_vaddrr
    • 60 0: p_filesz = d7 00 00 00 00 00 00 00: TODO vs p_memsz
    • 60 8: p_memsz = d7 00 00 00 00 00 00 00: TODO
    • 70 0: p_align = 00 00 20 00 00 00 00 00: 0 или 1 не означает, что выравнивание не требуется это значит? в противном случае избыточно с другими полями

    второй-аналогично.

    затем:

     Section to Segment mapping:
    
    на readelf говорит нам, что:
    • 0 является .text сегменте. Ага, так вот почему он исполняемый, а не записываемый
    • 1 является .data сегменте.