Формат файла 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.


person Community    schedule 01.03.2016    source источник
comment
Я скомпилировал простую программу на C, так зачем же тогда добавлял тег C++? (риторический вопрос, не отвечайте)   -  person too honest for this site    schedule 01.03.2016
comment
@Olaf Потому что та же проблема возникает с компилятором C ++, может быть?   -  person    schedule 01.03.2016
comment
возможно. С++ - это другой язык. Одинаковый синтаксис не означает одинаковую семантику. На самом деле вопрос вряд ли даже о C.   -  person too honest for this site    schedule 01.03.2016
comment
Хорошо, моя цель здесь - получить ответ на вопрос. Вежливо прошу прекратить этот бред.   -  person    schedule 01.03.2016
comment
Использование правильных тегов не является ерундой, но помогает структурировать и сортировать вопросы. И обратите внимание, я уже делал сообщение о том, что вопрос в моем комментарии был риторическим. Я просто подумал, что сообщить вам, почему я удалил тег, было вопросом вежливости и поможет избежать этого недостатка в будущем.   -  person too honest for this site    schedule 01.03.2016


Ответы (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.__text часто находится на одной и той же странице, поэтому вам все равно придется отображать всю страницу.

person Technologeeks    schedule 19.03.2016
comment
Вы можете объяснить, что такое jtool, поскольку его нет в OS X или Xcode. Кроме того, fileoff относится к соответствующему фрагменту архитектуры в толстом двоичном файле, не обязательно к началу файла. - person Ken Thomases; 19.03.2016
comment
Fileoff относится к началу среза, да, который является началом двоичного файла Mach-O (поскольку срезы являются отдельными Mach-O). Так что объяснение в порядке, потому что заголовок загружается в любом случае. И jtool — это мой собственный инструмент. Я написал книгу. Но в прошлый раз, когда я давал на него ссылку, надзиратели подумали, что это продвижение по службе, и уничтожили его. - person Technologeeks; 20.03.2016
comment
Я не знал об этой функции, я думал, что команда загрузки LC_SEGMENT_64 имеет дело только с данными, которые должны быть сопоставлены с адресным пространством процесса. На мой взгляд, к этому моменту заголовок Mach-O уже был разобран загрузчиком, и если какие-то библиотеки нужно было загрузить, то их собирались указать с помощью LC_LOAD_DYLIB команд загрузки. Порекомендуете ли вы литературу по этому вопросу? (Кроме вашей книги, которую я только что нашел и собираюсь взглянуть на нее.) - person ; 21.03.2016
comment
Нет; Заголовок A) также анализируется ядром (для LC_UUID, CODE_SIGNATURE и т. д.) и B) остается резидентным. Вы правы, что другие библиотеки загружаются с помощью команд LC * DYLIB. Ссылки: Темы Apple Mach-O Programming хороши, но просты. А вопросы всегда можно задать здесь или на форуме Книги. - person Technologeeks; 22.03.2016