Формат файла 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.__ часто находится на одной и той же странице, поэтому вам все равно придется сопоставлять всю страницу.