MachO 文件格式 - LC_SEGMENT_64 加载命令中“fileoff”字段的值
MachO file format - Value of `fileoff` field in LC_SEGMENT_64 load command
我编译了一个简单的程序如
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 对此字段的文档指出
The file is mapped starting at fileoff to the beginning of the segment in memory, vmaddr.
最初,这让我相信这个字段与 filesize
相结合,表明加载器是这样的:"Take the contents of the file from fileoff
to fileoff + filesize
and this is the sequence of instructions you're gonna ask the processor to run"。但如果这个值为零,我的假设当然不成立。
我认为,由于段至少有一个部分,加载程序将使用部分描述中的相应偏移值将代码定位到 运行,因此该值不是确实需要 --- 我们可以看到,事实上,该段中的第一部分具有 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
.
的值相同
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 段从文件的开头映射(或切片,如果是 fat ("universal"))。即用Mach-Oheader。这实际上是一个特性,因为 Mach-O 随后会被 dyld(您友好的加载器)解析为其他加载命令(尤其是库)。另一个问题是 __TEXT.__text 通常在同一个页面中,因此您无论如何都必须映射整个页面。
我编译了一个简单的程序如
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 对此字段的文档指出
The file is mapped starting at fileoff to the beginning of the segment in memory, vmaddr.
最初,这让我相信这个字段与 filesize
相结合,表明加载器是这样的:"Take the contents of the file from fileoff
to fileoff + filesize
and this is the sequence of instructions you're gonna ask the processor to run"。但如果这个值为零,我的假设当然不成立。
我认为,由于段至少有一个部分,加载程序将使用部分描述中的相应偏移值将代码定位到 运行,因此该值不是确实需要 --- 我们可以看到,事实上,该段中的第一部分具有 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
.
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 段从文件的开头映射(或切片,如果是 fat ("universal"))。即用Mach-Oheader。这实际上是一个特性,因为 Mach-O 随后会被 dyld(您友好的加载器)解析为其他加载命令(尤其是库)。另一个问题是 __TEXT.__text 通常在同一个页面中,因此您无论如何都必须映射整个页面。