Формат файла MachO - значение поля `fileoff` в команде загрузки LC_SEGMENT_64

Я скомпилировал простую программу, такую ​​как

int main()
{
    return 0; 
}

используя Clang в исполняемый файл и спросил otool сообщить о командах загрузки, сгенерированных компилятором. То, что меня интересует, это LC_SEGMENT_64в частности тот, который описывает __TEXT сегмент в файле. Описание, которое я получаю, это:

$ otool -lV foo
foo:
Load command 0
      cmd LC_SEGMENT_64
  cmdsize 72
  segname __PAGEZERO
   vmaddr 0x0000000000000000
   vmsize 0x0000000100000000
  fileoff 0
 filesize 0
  maxprot ---
 initprot ---
   nsects 0
    flags (none)
Load command 1
      cmd LC_SEGMENT_64
  cmdsize 312
  segname __TEXT
   vmaddr 0x0000000100000000
   vmsize 0x0000000000001000
  fileoff 0
 filesize 4096
  maxprot rwx
 initprot r-x
   nsects 3
    flags (none)
Section
  sectname __text
   segname __TEXT
      addr 0x0000000100000f90
      size 0x000000000000000f
    offset 3984
     align 2^4 (16)
    reloff 0
    nreloc 0
      type S_REGULAR
attributes PURE_INSTRUCTIONS SOME_INSTRUCTIONS
 reserved1 0
 reserved2 0

Мой вопрос: почему fileoff поле во второй команде загрузки установлено в ноль?

Документация Apple для этого поля гласит, что

Файл отображается начиная с fileoff до начала сегмента в памяти, vmaddr.

Изначально это привело меня к мысли, что это поле в сочетании с filesize, указал загрузчику что-то вроде этого: "Взять содержимое файла из fileoff в fileoff + filesize и это последовательность инструкций, которые вы попросите запустить процессор ". Но мое предположение не выполняется, если это значение равно нулю, конечно.

Я думал, что, так как сегмент имеет по крайней мере один раздел, загрузчик будет использовать значение соответствующего смещения в описании раздела, чтобы найти код для запуска, и, следовательно, такое значение точно не нужно - мы можем видеть, что фактически первый раздел в этом сегменте имеет значение для offset поле (в данном случае 3984, который я подтвердил с otool -s __TEXT __text -j foo и действительно относится к смещению, при котором этот раздел находится в файле).

Но если я сделаю то же самое для объектного файла, сгенерированного из того же исходного файла (то есть файла с типом MH_OBJECT вместо MH_EXECUTE), результат, который я получаю, это:

$ otool -lV foo.o
foo.o:
Load command 0
      cmd LC_SEGMENT_64
  cmdsize 312
  segname
   vmaddr 0x0000000000000000
   vmsize 0x0000000000000070
  fileoff 464
 filesize 112
  maxprot rwx
 initprot rwx
   nsects 3
    flags (none)
Section
  sectname __text
   segname __TEXT
      addr 0x0000000000000000
      size 0x000000000000000f
    offset 464
     align 2^4 (16)
    reloff 0
    nreloc 0
      type S_REGULAR
attributes PURE_INSTRUCTIONS SOME_INSTRUCTIONS
 reserved1 0
 reserved2 0

В этом случае команда загрузки имеет значение для fileoff поле, которое совпадает с полем для первого раздела, __text,

1 ответ

Решение

otool затрудняет понимание, но ответ прост - обратите внимание:

$ jtool -v -l /tmp/a | grep SEG
LC 00: LC_SEGMENT_64          Mem: 0x000000000-0x100000000  File: Not Mapped    ---/--__PAGEZERO
LC 01: LC_SEGMENT_64          Mem: 0x100000000-0x100001000  File: 0x0-0x1000    r-x/rw__TEXT
LC 02: LC_SEGMENT_64          Mem: 0x100001000-0x100002000  File: 0x1000-0x1098 r--/rw__LINKEDIT

Сегмент __TEXT отображается с начала файла (или среза, если жирный ("универсальный")). То есть с заголовком Mach-O. На самом деле это особенность, потому что Mach-O затем анализируется dyld (вашим дружественным загрузчиком) для других команд загрузки (особенно библиотек). Другая проблема заключается в том, что текст __TEXT.__ часто находится на одной и той же странице, поэтому вам все равно придется сопоставлять всю страницу.

Другие вопросы по тегам