2

我编译了一个简单的程序,例如

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第二个加载命令中的字段设置为零?

苹果在该领域的文档指出

该文件从 fileoff 开始映射到内存中段的开头 vmaddr。

最初,这使我相信该字段与 一起filesize指示加载器,如下所示:“将文件的内容从fileoffto 获取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

4

1 回答 1

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 通常在同一个页面中,因此无论如何您都必须映射整个页面。

于 2016-03-19T15:43:34.747 回答