《程序员的自我修养》读书笔记——目标文件

上一篇介绍了编译连接的过程,提到了目标文件是通过汇编过程生成的,最终链接生成可执行文件,这篇介绍一下目标文件里面到底有什么。

本文导图


格式概述

目标文件相对于最终的可执行文件而言,结构上已经和可执行文件的结构基本一样了。只是还没有经过链接过程,某些符号、地址还没被重定位,大部分内容都已经具备了。

可执行文件的格式在Windows(PE-Portable Executable)和Linux(ELF- Executable Linkable Format)、Mac(Mach-O)不同,前两者都是基于COFF(Common file format)格式的,除此之外还有其他不常见的,如Intel/Microsoft的OMF、Unix a.out、MS-Doc .Com格式等。

广义上将,目标文件与可执行文件格式几乎一样,可以看成同一种类型的文件。

动态链接库(window .dll、Linux .so)及静态链接库(window lib、Linux .a)、Mac(dylb,tbd)都是按照可执行文件的格式存储。静态库稍微不同,因为他是把很多目标文件集合在一起,需要在头部加上一些索引。也就是包含了很多目标文件的一个目标文件包。

ELF格式文件更可以做如下归纳(目标文件.o 静态库、可执行文件、动态库):


查看格式

file命令查看相应的文件格式。

Mac下


Linux下


下面以ELF结构作为分析

浅析目标文件内部结构

目标文件至少包含编译后的机器指令代码、数据、和链接所需要的信息比如、符号表、调试信息、字符串等,下面的内容围绕着这几个部分进行总结。

按照信息的不同属性以节(Section)也叫段(Segment)的形式存储,表示一个一定长度 的区域。这些区域里面分别存储了上诉编译后的信息。

  • 代码段:编译之后的机器指令放在代码段(Code Section),一般是叫.code.text
  • 数据段:全局变量和静态变量数据放在数据段(Data Section),一般叫.data
  • BBS段:未初始化的全局变量、未初始化静态变量放在.bbs段。

具体来讲,下面代码与目标文件对应关系:

  • 文件头:ELF文件有个文件头,描述整个文件的文件属性,如是否可执行、静态库还是动态库、及入口地址(可执行文件)、目标硬件、操作系统等。其中还包含一个段表,段表示描述各个段的一个数组,包含各个段在文件的偏移位置、段的属性。文件头后面就是各个段的内容,比如代码段保存指令,数据段保存数据。——其中段表非常重要。

未初始化的全局变量、局部静态变量虽然默认值都是0,但是没必要在.data段为它们分配空间(浪费),存放0是没必要的。所以放在.bbs段,也就给他们预留个位置而已,没有内容,所以在文件中不占空间。

总体来说,程序代码被编译后分成两种段,程序指令(代码段)和程序数据(数据端及.BSS

指令和数据分开的好处:

  • 指令和数据分别映射到两个虚拟区域,数据可读可写,而指令是只读的,可以设值区域的权限,防止恶意修改程序指令。——权限,安全
  • 分开有利于程序的局部性(在一段时间内,整个程序的执行仅限于程序中的某一部分。相应地,执行所访问的存储空间也局限于某个内存区域。)CPU有几大的缓存体系。——局部性原理
  • 复用,如果系统有多个程序的副本,那么指令都是一样的。这个也是动态库非常大的优势,节省内存开销——复用、复用!!!

实验目标文件内容

实验代码:



使用objdump -h 查看目标文件内部结构。

在Mac上实验结果

objdump -h  hello.o

hello.o:    file format Mach-O 64-bit x86-64

Sections:
Idx Name          Size      Address          Type
  0 __text        00000067 0000000000000000 TEXT
  1 __data        00000008 0000000000000068 DATA
  2 __cstring     00000004 0000000000000070 DATA
  3 __bss         00000004 0000000000000120 BSS
  4 __compact_unwind 00000040 0000000000000078 DATA
  5 __eh_frame    00000068 00000000000000b8 DATA

在Linux实验结果


除了之前讲到的三个段,这里多了只读数据端,注释端,堆栈提示段。其中包含了一些属性关键词,如长度(size)、端的位置(File off)、该段是否在文件中存在(Contents)、段的各种属性(Alloc)。可以看到.bbs段没有contents所以在文件中没有内容。而堆栈提示段size为0

将上面的内容整理一下:

可以通过size命令查看可执行文件的各个端大小(注意dec所有段的十进制和,hex是16进制的和)

Mac上实验(文件格式是Mach-o)


Linux上实验(文件格式是ELF)


深入目标文件内部结构

下面将深入了解目标文件中的各个段,他们的作用及含义。

核心段

上面提到过核心段有代码段、数据端、BBS段。下面分别介绍!

代码段

代码段可以使用objdump的-s参数打印出来,-d可以将包含指令的段反汇编信息。

  • 最左边是偏移地址,紧跟着的是16进制信息(每两个数字为一个字节,一般都是以字节位单位查看。

Mac上实验(文件格式是Mach-o):最前面的部分是反汇编内容,接下来是各个段的内容。

$ objdump -s -d hello.o

hello.o:    file format Mach-O 64-bit x86-64

Disassembly of section __TEXT,__text:
_fun1:
       0:   55  pushq   %rbp
       1:   48 89 e5    movq    %rsp, %rbp
       4:   48 83 ec 10     subq    $16, %rsp
       8:   48 8d 05 61 00 00 00    leaq    97(%rip), %rax
       f:   89 7d fc    movl    %edi, -4(%rbp)
      12:   8b 75 fc    movl    -4(%rbp), %esi
      15:   48 89 c7    movq    %rax, %rdi
      18:   b0 00   movb    $0, %al
      1a:   e8 00 00 00 00  callq   0 <_fun1+0x1F>
      1f:   89 45 f8    movl    %eax, -8(%rbp)
      22:   48 83 c4 10     addq    $16, %rsp
      26:   5d  popq    %rbp
      27:   c3  retq
      28:   0f 1f 84 00 00 00 00 00     nopl    (%rax,%rax)

_main:
      30:   55  pushq   %rbp
      31:   48 89 e5    movq    %rsp, %rbp
      34:   48 83 ec 10     subq    $16, %rsp
      38:   c7 45 fc 00 00 00 00    movl    $0, -4(%rbp)
      3f:   c7 45 f8 6f 00 00 00    movl    $111, -8(%rbp)
      46:   8b 05 00 00 00 00   movl    (%rip), %eax
      4c:   03 05 00 00 00 00   addl    (%rip), %eax
      52:   03 45 f8    addl    -8(%rbp), %eax
      55:   03 45 f4    addl    -12(%rbp), %eax
      58:   89 c7   movl    %eax, %edi
      5a:   e8 00 00 00 00  callq   0 <_main+0x2F>
      5f:   31 c0   xorl    %eax, %eax
      61:   48 83 c4 10     addq    $16, %rsp
      65:   5d  popq    %rbp
      66:   c3  retq
Contents of section __text:
 0000 554889e5 4883ec10 488d0561 00000089  UH..H...H..a....
 0010 7dfc8b75 fc4889c7 b000e800 00000089  }..u.H..........
 0020 45f84883 c4105dc3 0f1f8400 00000000  E.H...].........
 0030 554889e5 4883ec10 c745fc00 000000c7  UH..H....E......
 0040 45f86f00 00008b05 00000000 03050000  E.o.............
 0050 00000345 f80345f4 89c7e800 00000031  ...E..E........1
 0060 c04883c4 105dc3                      .H...].
Contents of section __data:
 0068 54000000 55000000                    T...U...
Contents of section __cstring:
 0070 25640a00                             %d..
Contents of section __bss:
<skipping contents of bss section at [0120, 0124)>
Contents of section __compact_unwind:
 0078 00000000 00000000 28000000 00000001  ........(.......
 0088 00000000 00000000 00000000 00000000  ................
 0098 30000000 00000000 37000000 00000001  0.......7.......
 00a8 00000000 00000000 00000000 00000000  ................
Contents of section __eh_frame:
 00b8 14000000 00000000 017a5200 01781001  .........zR..x..
 00c8 100c0708 90010000 24000000 1c000000  ........$.......
 00d8 28ffffff ffffffff 28000000 00000000  (.......(.......
 00e8 00410e10 8602430d 06000000 00000000  .A....C.........
 00f8 24000000 44000000 30ffffff ffffffff  $...D...0.......
 0108 37000000 00000000 00410e10 8602430d  7........A....C.
 0118 06000000 00000000                    ........

# instanza @ InstanzadeMacBook-Pro in ~/Desktop/Temp [12:12:42]
$

Linux实验(文件格式是ELF)


  • contents of section .text就是数据已十六进制打印出来的内容。中间4列是16进制内容,最右边是.text段的ASCII码形式。
  • .text段包含是.c文件两个函数的指令,比如第一个字节是0x55,就是func1()函数第一条push %ebp指令,最后一个0xc3代表main函数最后一个指令ret

数据段

.data段保存了初始化的全局静态变量和局部静态变量。上面代码中有两个这样的变量,每个变量4个字节一共8个字节。所以.data这个段大小为8个字节。——这是个非常准确的计算,不会存在多一个、少一个字节的情况

注意printf的时候,有一个字符串常量“%d\n”。在linux中放到了.rodata段,分别有四个字符%、d、\n(换行符)、空字符。一个字符一个字节(8位ASCII码),所以一共四个字节。在Mac上放到了字符串常量区。

以Mac下例子为例:

Contents of section __cstring:
 0070 25640a00                             %d..

通过查ASCII表,得到对应情况:%——25,d——64,\n(换行符)——0a,空字符(NULL)——00。刚好和字符串常量区对应

在来看一起.data段(Mac下):

 0060 c04883c4 105dc3                      .H...].
Contents of section __data:
 0068 54000000 55000000                    T...U...

根据偏移范围可以知道.data端一共8个字节。和前面用size看到的不一样,size看到的是12个字节。

Linux下

可以看到Linux也是8个字节。字节从低到高分别是0x54、0x00、0x00、0x00。刚好是十进制的84,特别注意这里的顺序,字节从低到高还是从高到底排列涉及到CPU的字节序问题,也就是大端小端。后面四个字节也刚好是85

BBS段

Mac中的bss段如下:

Contents of section __bss:
<skipping contents of bss section at [0120, 0124)>

可以知道具体的值放到了0120到0124之间,一共四个字节。

Linux下


同样也是四个字节。和global_uninit_varstatic_var2合起来大小8个字节不符。

BBS段最终是通过符号表决定的。在Linux下只有static_var2存放到了bbs段。而global_uninit_var没有存放到任何段。只是一个未定义的common符号,这和语言及编译器实现有关。有一点可以明确——编译单元内部可见的静态变量是存在bbs段的。

其他段

ELF中除了.text、.data、.bbs三个最常用的段还有其他段。具体如下:


.开头的都是系统保留的。如果想要自定义端,则不能以.开头。

GCC提供了扩展机制,可以将变量或者代码放到你指定的段中去。用以下方式实现:


以上只是ELF文件的轮廓,下面用一张图总结:



几点说明一下:

  • 文件头:描述怎个文件的基本属性,文件版本,目标机器型号,程序入口地址。
  • 段表:描述了ELF文件包含所有段的信息,比如每个段的段名,长度,文件中的偏移,读写权限,和其他属性。

下面开始从文件头开始

文件头

Linux用readelf来查看文件头,Mac用otool查看(otool -h hello.o

在Linux中


系统定义文件头数据结构:

#define EI_NIDENT 16
typedef struct {
    unsigned char e_ident[EI_NIDENT];
    Elf32_Half e_type;
    Elf32_Half e_machine;
    Elf32_Word e_version;
    Elf32_Addr e_entry;
    Elf32_Off e_phoff;
    Elf32_Off e_shoff;
    Elf32_Word e_flags;
    Elf32_Half e_ehsize;
    Elf32_Half e_phentsize;
    Elf32_Half e_phnum;
    Elf32_Half e_shentsize;
    Elf32_Half e_shnum;
    Elf32_Half e_shstrndx;
} Elf32_Ehdr;

各个字段代表什么意思根据名称就知道了。关键要知道在哪里定义这些常数的。ELF文件头定义在user/include/elf。mach.o类型的目标文件也有对应的头文件。

下图是对readelf和ELF头文件定义的各个成员的含义解释。这里先大致了解下,后面会详解!

ELF魔数(e_ident前四个字节)

通过readelf之后看到最前面的16个字节刚好是e_ident,这16个ELF标准用来标识ELF文件平台属性,比如ELF字长(32位、64位),字节序、文件版本等。

下表是对e_ident数组各个成员的说明:


  • 前四个字节是所有ELF文件必须相同的标识码:0x7F、0x45、0x4c、0x46。第一个字节赌赢DEL控制符,后三个对应ELF。所有的可执行文件最开始的几个字节都是魔数,用来确认文件类型,操作系统在加载的时候会确认魔数是否正确,不正确就不会加载。
  • 下一个字节标识文件类型;0x01标识32,0x02标识64
  • 第六个字节是字节序
  • 第七个字节:规定ELF文本版本
  • 后面9个字节:标准还没有定义,一般填0

文件类型

e_type表示文件类型,之前提到过3种ELF文件类型。如下表


机器类型

e_machine表示平台属性

段表

ELF最终段结构由段表决定,编译器、链接器和 装载器都是依赖段表来定位和访问各个端的属性。段表的位置有ELF文件头的e_shoff决定,如上面的例子段表的偏移为0x118

直接来查看所有的段信息

这里的数据其实在代码层面来讲都是由对应的数据结构的。无论是mach.o还是ELF。比如这里的Elf32_Shdr结构体是对一个段的封装,段表也就是由Elf32_Shdr组成的数组。对上图而言一个有11个这样的元素。

段表元素数据结构


 typedef struct {
    Elf32_Word sh_name;
    Elf32_Word sh_type;
    Elf32_Word sh_flags;
    Elf32_Addr sh_addr;
    Elf32_Off sh_offset;
    Elf32_Word sh_size;
    Elf32_Word sh_link;
    Elf32_Word sh_info;
    Elf32_Word sh_

各个字段的含义


1

现在把整个.o文件所有段的位置及长度都分析清楚了。

  • 段表长度为0x1b8,一共440个字节,包含11个段描述符,每个段描述符为40个字节,这个长短刚好是结构Elf32_Shdr的长度。
  • 文件最后一段.re.text结束后,长度为0x450也就是1104个字节,刚好是.o文件的大小。

中间有段表和.rel.text都因为对齐的原因,与前面的短之间有一个字节和两个字节的间隔。注意这里是为了对齐。

段类型(sh_type、sh_flags)

段名(sh_name)只有在编译、链接过程有意义,但不能真正表示段的类型,段名(sh_name)其实只是一个索引指向字符串表(SHT_STRTAB)的某个位置,在编译器和链接器中起作用的是段类型字段和段标志位字段。

段类型:


段标志位


系统保留段

段的链接信息(sh_link、sh_info)

段的类型与链接相关的话都需要sh_link、sh_info,无论是动态库还是静态库。比如重定位表,符号表。对于其他段这两个成员没意义


重定位表(段类型SHT_REL)

在刚才的段中,有一个.rel.text的短,类型(sh_type)为SHT_REL,也就是这个段包含的是重定位表。

链接器在处理目标文件的时候,需要对目标文件某些部分重定位,虽然在代码段和数据端中用的是绝对地址的引用位置。重定位信息都记录在重定位表里面,每一个需要重定位的代码段或数据段都会有一个相应的重定位表,比如上面的.rel.text就是对.text的重定位表。因为.text段有一个绝对地址的引用,就是printf函数的调用。这里的决定地址引用也就是写死的地址。而.data段没有绝对地址引用。

字符串表(段类型SHT_STRTAB)

ELF用到很多字符串 ,如段名、变量名、因为字符串长度不确定,所以固定的解构表示比较困难。一种很常见做法就是把字符串集中放到一个表中,使用字符串的时候在表中的偏移来引用字符串。如下图

字符串表一般有两种一种用来保存普通的字符串,比如符号的名字;另一种段表字符串用来来保存段表中用到的字符串,比如段表名。字符串表中包含有若干个以’null’结尾的字符序列。

只要分析ELF头文件,就可以得到段表和段表字符串表的位置,从而解析整个ELF文件。

符号表(symbol table)

符号

假如B文件要用到A文件的函数foo,那么在目标文件A定义了函数foo,目标文件B引用了A中的foo函数。每个函数、变量都有自己的名字,将函数名、变量名统称为符号名。

每一个目标文件都会一个相应的符号表,记录了目标文件所要用到的符号,每个定义的符号有一个对应的值,叫做符号值对于变量和函数来说,符号值就是他们的地址。出了变量和函数外还有其他几种符号,可以将符号表中的符号进行分类。

符号表包含的信息用于定位和重定位程序中的符号定义和引用。目标文件的其它部分通过一个符号在这个表中的索引值来使用 该符号。索引值从 0 开始计数,但值为 0 的表项(即第一项)并没有实际的意义, 它表示未定义的符号。这里用常量 STN_UNDEF 来表示未定义的符号。

符号一般有如下类型:

  • 定义在目标文件的全局符号:可以被其他目标文件引用。如上面的func1,main,global_init_var
  • 引用其他目标文件中的全局符号:一般叫做外部符号,如 printf
  • 段名:由编译器产生,它的值就是该段的其实起始地址。如.text.data
  • 局部符号:只在编译单元内部可见,如static_var、static_var2。调试器可以是用哪个这些符号来分析程序。对于链接没什么作用,链接器也会忽略他们。
  • 行号符号:目标文件指令与源代码中代码行的对应关系。

可以使用nm查看符号表的内容

Mac上


Linux上


符号表结构(SHT_SYMTAB)

符号表示文件中的一个段,段名.symtab。符号表数据结构比较简单Elf32_sym(符号表项),每个结构体对应一个符号。

其结构体如下:

 typedef struct {
  Elf32_Word st_name;
  Elf32_Addr st_value;
  Elf32_Word st_size;
  unsigned char st_info;
  unsigned char st_other;
  Elf32_Half st_shndx;

各个字段的含义


符号类型和属性(st_info)

由低4位表示符号类型,高28位表示符号属性信息。

符号所在段(st_shndx)

如果符号定义在本目标文件,则这个成员表示符号所载的段在段表中的下标。因为符号是为段而定义,在段中被引用。本数据成员即指明了相关联的段。本数据成员是一个索引值,它指向相关联的段在段表中的索引。在重定位过程中,段的位置会改变,本数据成员的值也随之改变,继续指向段的新位置。

如果没有定义在本目标文件,则sh_shndx有些特殊。

符号值(st_value)

每个符号都有一个对应的值。如果是一个变量或者函数,符号值就是其地址。总的来说有以下几种情况:

  • 如果是符号定义且不是Common块类型(st_shndx不为SHN_COMMON),则st_value表示该符号在段中的偏移,即符号所对应的函数、变量位于st_shndx指定段。这种是最常见的,比如func1、main、global_init_var。
  • 如果是COMMON块的类型,则st_value表示该符号的对齐属性,如global_uninit_var
  • 在可执行文件中,st_value表示符号的虚拟地址。

符号表内容解析

内容如下:


  • 第一列表示符号表数组下标,从0开始共15个符号
  • 第二列就是符号值——st_value
  • 第三列Size为符号大小——st_size
  • 第四列、第五列分别为符号类型和绑定信息,对应st_info的低四位和高28位
  • 第六列不知道
  • 第七列表示符号所属的段——st_shndx
  • 第八列表示符号名称

根据前面的内容做出如下解析:

  • func1、main是函数所有在代码段,代码段Ndx为1,所以.text为1.并且类型是STT_Func,并且是全局可见,所以是STB——GLOBAL。
  • global_uninit_var是一个SHN_COMMON类型的符号,本身并没有在BSS段。
  • ....
  • 依次类推可以把各个符号都解析出来

需要说明的:static_var和static_var2变成了static_var.1533和static_var.1534,是因为进行符号修正。其次绑定的属性是STB_LOCAL,表示只在编译单元可见。类型是STT_SECTION类型的符号,表示下标为Ndx段的短名。但是符号没有显示。比如2号符号Ndx为1那么就是.text段。那么符号名字就是.text。可以使用objdump -t查看段名符号。

特殊符号

当使用链接器链接的时候,链接器会定义很多特殊符号,这些特殊符号没有在程序定义,但是可以直接声明并应用它。链接器将这些特殊符号放在了链接脚本中,链接器会在程序最终链接为可执行文件的时候,将其解析为正确的值。

常见特殊符号


符号修饰、函数签名(防止冲突)

最开始编译器产生目标文件的时候,符号名和相应的变量函数名一样。但是如果已经定义这些符号就会产生目标文件冲突。为了解决目标文件冲突,就在对应的符号名签名加一些字符以示区分。如在符号名前、后加上_

如果模块较多,命名规范不严格,同样可能导致冲突。于是就增加了命名空间的方法来解决。

所以看到上面的static_var和static_var2变成了static_var.1533和static_var.1534。也就是进行了一次符号修饰。

C++符号修饰

C++强大而复杂,为了支持C++特性,发明了符号修饰和符号改编机制。

对于不同类,同名函数,引入了一个函数签名的概念。函数签名包含一个函数的信息,函数名,参数类型及所在的命名空间、其他细心,用于识别不同的函数。

编译器在链接处理符号的时候,会使用名称修饰的方法,使得每个函数签名对应一个修饰后的名称。如下图:


弱符号、强符号()

经常在编程中将一个符号重复定义,如果出现定义错误则说明这种事强符号。有些符号可以定义为弱符号,比如未初始化的全局变量,也可以使用__attribut__((weak))定义一个强符号为弱符号。

它们的规则如下:

  • 不允许强符号被多次定义,也就是不同目标文件不能有相当的强符号(iOS中经常出现的符号冲突就是这个意思)
  • 如果符号在某个目标文件是强符号,其他文件是弱符号,那么选择强符号
  • 如果在所有文件中都是弱符号则选择其中占空间最大的一个

弱引用、强引用

注意不是iOS中强弱引用

如果没有找到该符号的定义,链接器就会报未定义错误这种成为强引用。与之相对的是弱引用,如果是弱引用,链接器不认为是个错误,一般对未定义的弱引用,链接器默认为0,或者其他值,以便程序识别。在动态库中使用到,和COMMON块概念联系很紧密

可以使用__attribute__((weakref))扩展自声明对一个外部函数为弱引用。如果把它编译为可执行文件,并不会报链接错误。但是当运行的时候,就会发生非法地址访问。

可以用if加以判断,防止这种情况。

这种弱符号和弱引用对于动态库来讲非常非常有用,比如动态库中定义的弱符号可以被用户定义的强符号覆盖,使得程序可以使用自定义版的库函数。

调试信息

目标文件还可能保存调试信息,比如在函数里面设置断点,可以监视变量变化,可以单步行进等,前提都是编译器必须提前将源代码与目标代码之间的关系(如目标代码中的地址对应源代码中的哪一行、函数和变量的类你先给,结构体的定义,字符串保存到目标文件里面)保存到目标文件。

如果在gcc 中加入-g参数就可以在目标文件里面加上调试信息。gcc -c -g hello.c

比如:

objdump -h hello.o

hello.o:    file format Mach-O 64-bit x86-64

Sections:
Idx Name          Size      Address          Type
  0 __text        00000067 0000000000000000 TEXT
  1 __data        00000008 0000000000000068 DATA
  2 __cstring     00000004 0000000000000070 DATA
  3 __bss         00000004 00000000000004e4 BSS
  4 __debug_str   0000009e 0000000000000074 DATA
  5 __debug_loc   00000000 0000000000000112 DATA
  6 __debug_abbrev 00000087 0000000000000112 DATA
  7 __debug_info  000000e0 0000000000000199 DATA
  8 __debug_ranges 00000000 0000000000000279 DATA
  9 __debug_macinfo 00000001 0000000000000279 DATA
 10 __apple_names 000000c8 000000000000027a DATA
 11 __apple_objc  00000024 0000000000000342 DATA
 12 __apple_namespac 00000024 0000000000000366 DATA
 13 __apple_types 00000047 000000000000038a DATA
 14 __compact_unwind 00000040 00000000000003d8 DATA
 15 __eh_frame    00000068 0000000000000418 DATA
 16 __debug_line  00000062 0000000000000480 DATA

ELF采用一个DWARF的标准的调试信息格式。在Xcode中也有DWARF的身影。

调试信息在目标文件和可执行文件中占用很大的空间,往往比程序的代码和数据大很多倍(这一点在iOS开发中用Instrument调试的时候最终就是用得这个文件实现的。

小结

这一部分内容比较多,主要是解析目标文件格式。如文件头、段、段表、重定位表、符号表、字符串表各自的数据结构是如何的。其实对ELF文件解析的内容内容远远不止这些,也有专门的官方文档对ELF格式记性详细的说明。

最重要的是明白各个部分的关系

扩展阅读

可执行文件格式
理解ELF文件格式
ELF文件格式分析
Comparison of executable file formats
MachOView

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容