2014-10-02 03:04:05 abcso 阅读数 876
  • 《C语言/C++学习指南》Linux开发篇

    本篇面向Linux初级、但已掌握C/C++基本语法的学员,以CentOS为演示平台,介绍Linux下的C/C++开发技术。在内容编排上,先介绍Linux的安装、基本使用和配置,然后再开始介绍在Linux下的C/C++开发技术: gcc命令行、Makefile、gdb调试方法、动态库与静态库、pthread线程、进程、进程间通信、系统调用及杂项技术。(前四章免费)

    60731 人正在学习 去看看 邵发

Xcode debug 命令

Xcode的调试器为用户提供了一个GDB的界面,GDB是GNU组织的开放源代码调试器。您可以在Xcode的图形界面里做任何事情;但是,如果您需要 您可以在命令行里使用GDB的命令。 

要在一个调试的任务里输入GDB命令行命令: 

在工具栏里点击Console Drawer (控制台抽屉窗口)按钮打开控制台。 
您可以在控制台里查看Xcode调试器发送给GDB的命令,或者您可以直接在控制台里输入GDB命令。在控制台窗口里点击然后在gdb提示符后面 输入命令。
  1. 命 令                        解释  
  2. break NUM               在指定的行上设置断点。  
  3. bt                      显 示所有的调用栈帧。该命令可用来显示函数的调用顺序。  
  4. clear                   删 除设置在特定源文件、特定行上的断点。其用法为:clear FILENAME:NUM。  
  5. continue                继续执行正在调试的程序。该命令用在程序 由于处理信号或断点而  
  6.                         导致停止运行 时。  
  7. display EXPR            每次程序停止后显示表达式的值。表达式由程序定 义的变量组成。  
  8. file FILE               装载指定的可执行文件进行调试。  
  9. help NAME               显 示指定命令的帮助信息。  
  10. info break              显 示当前断点清单,包括到达断点处的次数等。  
  11. info files              显 示被调试文件的详细信息。  
  12. info func               显示所有的函数名称。  
  13. info local              显 示当函数中的局部变量信息。  
  14. info prog               显示被调试程序的执行状 态。  
  15. info var                显示所有的全局和静态变量名称。  
  16. kill                    终 止正被调试的程序。  
  17. list                    显示源代码段。  
  18. make                    在 不退出 gdb 的情况下运行 make 工具。  
  19. next                    在 不单步执行进入其他函数的情况下,向前执行一行源代码。  
  20. print EXPR              显 示表达式 EXPR 的值。  
  21.   
  22. print- object            打印一个对象  
  23. print (int) name      打印一个类型  
  24. print- object [artist description]   调用一个函数  
  25. set artist = @"test"    设置变量值  
  26. whatis                      查 看变理的数据类型  
命令 解释 break NUM 在指定的行上设置断点。 bt 显示所有的调用栈帧。该命令可用来显示函数的调用顺序。 clear 删除设置在特定源文件、特定行上的断点。其用法为:clear FILENAME:NUM。 continue 继续执行正在调试的程序。该命令用在程序由于处理信号或断点而 导致停止运行时。 display EXPR 每次程序停止后显示表达式的值。表达式由程序定义的变量组成。 file FILE 装载指定的可执行文件进行调试。 help NAME 显示指定命令的帮助信息。 info break 显示当前断点清单,包括到达断点处的次数等。 info files 显示被调试文件的详细信息。 info func 显示所有的函数名称。 info local 显示当函数中的局部变量信息。 info prog 显示被调试程序的执行状态。 info var 显示所有的全局和静态变量名称。 kill 终止正被调试的程序。 list 显示源代码段。 make 在不退出 gdb 的情况下运行 make 工具。 next 在不单步执行进入其他函数的情况下,向前执行一行源代码。 print EXPR 显示表达式 EXPR 的值。 print-object 打印一个对象 print (int) name 打印一个类型 print-object [artist description] 调用一个函数 set artist = @"test" 设置变量值 whatis 查看变理的数据类型

原文地址 http://www.aisidachina.com/forum/thread-316-1-1.html
2016-04-08 23:59:11 bjtufang 阅读数 10588
  • 《C语言/C++学习指南》Linux开发篇

    本篇面向Linux初级、但已掌握C/C++基本语法的学员,以CentOS为演示平台,介绍Linux下的C/C++开发技术。在内容编排上,先介绍Linux的安装、基本使用和配置,然后再开始介绍在Linux下的C/C++开发技术: gcc命令行、Makefile、gdb调试方法、动态库与静态库、pthread线程、进程、进程间通信、系统调用及杂项技术。(前四章免费)

    60731 人正在学习 去看看 邵发

>
之前深入了解过,过去了一年多的时间。现在花些时间好好总结下,毕竟好记性不如烂笔头。其次还有一个目的,对于mach-o文件结构,关于动态加载信息那个数据区中,命令含义没有深刻掰扯清除,希望有同学能够指点下。

摘要:对于mach-o是Mac和iOS可以执行文件的格式。进程就是系统根据该格式将执行文件加载到内存后得到的结果。系统通过解析文件,建立依赖(动态库),初始化运行时环境,才能真正开始执行该App(进程)

Start from Hello World

通过分析下面这个最熟悉的可执行文件,来好好总结和了解下Mach-O这种文件格式,并且也总结下系统在执行可执行文件几个过程:
+ 解析文件
+ 依赖建立
+ 初始化运行环境
+ 执行进程

代码1.0

#include <stdio.h>  
int  main( int  argc,  char  *argv[]) 
{ 
    printf( "Hello World!\n" ); 
     return  0; 
} 

浏览可执行文件格式

小工具介绍下:烂苹果MachOView。通过改工具可以直接review mach-o可执行文件的几个主要的组成部分:
编译下main.c文件(使用gcc -g main.c)
简单看下可执行文件格式:

Run Script Phase

简单浏览mach-o可执行文件,具体可以分为几个部分

  • 文件头 mach64 Header
  • 加载命令 Load Commands
  • 文本段 __TEXT
  • 数据段 __TEXT
  • 动态库加载信息 Dynamic Loader Info
  • 入口函数 Function Starts
  • 符号表 Symbol Table
  • 动态库符号表 Dynamic Symbol Table
  • 字符串表 String Table

具体介绍下各个区域的作用,以及加载时系统是如何使用该可执行文件。

Mach Header - 可执行文件文件头

使用下mac的otool工具

代码3.0

yingfang:mach-o文件结构-src fangying$ otool -h a.out
a.out:
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
 0xfeedfacf 16777223          3  0x80           2    15       1200 0x00200085

上面是mach-o标准的文件头格式,其相关的数据结构在

/*
 * The 32-bit mach header appears at the very beginning of the object file for
 * 32-bit architectures.
 */
struct mach_header {
    uint32_t    magic;      /* mach magic number identifier */
    cpu_type_t  cputype;    /* cpu specifier */
    cpu_subtype_t   cpusubtype; /* machine specifier */
    uint32_t    filetype;   /* type of file */
    uint32_t    ncmds;      /* number of load commands */
    uint32_t    sizeofcmds; /* the size of all the load commands */
    uint32_t    flags;      /* flags */
};

/* Constant for the magic field of the mach_header (32-bit architectures) */
#define MH_MAGIC    0xfeedface  /* the mach magic number */
#define MH_CIGAM    0xcefaedfe  /* NXSwapInt(MH_MAGIC) */

/*
 * The 64-bit mach header appears at the very beginning of object files for
 * 64-bit architectures.
 */
struct mach_header_64 {
    uint32_t    magic;      /* mach magic number identifier */
    cpu_type_t  cputype;    /* cpu specifier */
    cpu_subtype_t   cpusubtype; /* machine specifier */
    uint32_t    filetype;   /* type of file */
    uint32_t    ncmds;      /* number of load commands */
    uint32_t    sizeofcmds; /* the size of all the load commands */
    uint32_t    flags;      /* flags */
    uint32_t    reserved;   /* reserved */
};

/* Constant for the magic field of the mach_header_64 (64-bit architectures) */
#define MH_MAGIC_64 0xfeedfacf /* the 64-bit mach magic number */
#define MH_CIGAM_64 0xcffaedfe /* NXSwapInt(MH_MAGIC_64) */

对照命令结果和具体的数据结构,下表介绍各个字段的具体意义:

字段 说明 举例
magic 魔数,系统加载器通过改字段快速,判断该文件是用于32位or64位。32位-0xfeedface 64位-0xfeedfacf demo中magic值为0xfeedfacf,表明该文件支持64位
cputype CPU类型以及子类型字段,该字段确保系统可以将适合的二进制文件在当前架构下运行 demo值为0x1000007, 根据#define CPU_TYPE_X86_64 (CPU_TYPE_X86
cpusubtype CPU指定子类型,对于inter,arm,powerpc等CPU架构,其都有各个阶段和等级的CPU芯片,该字段就是详细描述其支持CPU子类型 对于Demo,可以查看
#define CPU_SUBTYPE_X86_ALL ((cpu_subtype_t)3)
#define CPU_SUBTYPE_X86_64_ALL ((cpu_subtype_t)3)
#define CPU_SUBTYPE_X86_ARCH1 ((cpu_subtype_t)4)
#define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t)8)
filetype 说明该mach-o文件类型(可执行文件,库文件,核心转储文件,内核扩展,DYSM文件,动态库),具体可以看 demo中该值为2,表示该文件为二进制文件
ncmds 说明加载命令条数 demo中表示该文件加载命令条数为15,通过machoview工具,看到也是15条加载命令
sizeofcmds 表示加载命令大小 demo表示该文件加载命令大小为1200字节
flags 标志位,该字段用位表示二进制文件支持的功能,主要是和系统加载,链接相关,具体可以看 demo中值为0x00200085,分别表示三个功能:
1. MH_PRELOAD
2. MH_TWOLEVEL 动态库加载二级名称空间
3. MH_PIE 对可执行文件类型启用地址空间随机布局化
reserved 保留字段

系统解释,先解释文件头,获得文件支持位数(64位 or 32位),获得CPU类型,获得文件类型,获得加载命令条数和大小,获得文件标识。

Load Commands - 加载命令

Mach-O文件包含非常详细的加载指令,这些指令非常清晰地指示加载器如何设置并且加载二进制数据。Load Commands紧紧跟着二进制文件头。
先使用工具review demo中二进制文件Load Commands

Run Script Phase

也可以使用otool命令读取二进制文件加载命令:(之前分析bitcode是否支持,也是通过otool -l进行分析的)

代码4.0

yingfang:mach-o文件结构-src fangying$ otool -l a.out
a.out:
Load command 0
      cmd LC_SEGMENT_64
  cmdsize 72
  segname __PAGEZERO
   vmaddr 0x0000000000000000
   vmsize 0x0000000100000000
  fileoff 0
 filesize 0
  maxprot 0x00000000
 initprot 0x00000000
   nsects 0
    flags 0x0
Load command 1
      cmd LC_SEGMENT_64
  cmdsize 472
  segname __TEXT
   vmaddr 0x0000000100000000
   vmsize 0x0000000000001000
  fileoff 0
 filesize 4096
  maxprot 0x00000007
 initprot 0x00000005
   nsects 5
    flags 0x0
Section
  sectname __text
   segname __TEXT
      addr 0x0000000100000f50
      size 0x0000000000000034
    offset 3920
     align 2^4 (16)
    reloff 0
    nreloc 0
     flags 0x80000400
 reserved1 0
 reserved2 0
Section
  sectname __stubs
   segname __TEXT
      addr 0x0000000100000f84
      size 0x0000000000000006
    offset 3972
     align 2^1 (2)
    reloff 0
    nreloc 0
     flags 0x80000408
 reserved1 0 (index into indirect symbol table)
 reserved2 6 (size of stubs)
Section
  sectname __stub_helper
   segname __TEXT
      addr 0x0000000100000f8c
      size 0x000000000000001a
    offset 3980
     align 2^2 (4)
    reloff 0
    nreloc 0
     flags 0x80000400
 reserved1 0
 reserved2 0
Section
  sectname __cstring
   segname __TEXT
      addr 0x0000000100000fa6
      size 0x000000000000000e
    offset 4006
     align 2^0 (1)
    reloff 0
    nreloc 0
     flags 0x00000002
 reserved1 0
 reserved2 0
Section
  sectname __unwind_info
   segname __TEXT
      addr 0x0000000100000fb4
      size 0x0000000000000048
    offset 4020
     align 2^2 (4)
    reloff 0
    nreloc 0
     flags 0x00000000
 reserved1 0
 reserved2 0
Load command 2
      cmd LC_SEGMENT_64
  cmdsize 232
  segname __DATA
   vmaddr 0x0000000100001000
   vmsize 0x0000000000001000
  fileoff 4096
 filesize 4096
  maxprot 0x00000007
 initprot 0x00000003
   nsects 2
    flags 0x0
Section
  sectname __nl_symbol_ptr
   segname __DATA
      addr 0x0000000100001000
      size 0x0000000000000010
    offset 4096
     align 2^3 (8)
    reloff 0
    nreloc 0
     flags 0x00000006
 reserved1 1 (index into indirect symbol table)
 reserved2 0
Section
  sectname __la_symbol_ptr
   segname __DATA
      addr 0x0000000100001010
      size 0x0000000000000008
    offset 4112
     align 2^3 (8)
    reloff 0
    nreloc 0
     flags 0x00000007
 reserved1 3 (index into indirect symbol table)
 reserved2 0
Load command 3
      cmd LC_SEGMENT_64
  cmdsize 72
  segname __LINKEDIT
   vmaddr 0x0000000100002000
   vmsize 0x0000000000001000
  fileoff 8192
 filesize 552
  maxprot 0x00000007
 initprot 0x00000001
   nsects 0
    flags 0x0
Load command 4
            cmd LC_DYLD_INFO_ONLY
        cmdsize 48
     rebase_off 8192
    rebase_size 8
       bind_off 8200
      bind_size 24
  weak_bind_off 0
 weak_bind_size 0
  lazy_bind_off 8224
 lazy_bind_size 16
     export_off 8240
    export_size 48
Load command 5
     cmd LC_SYMTAB
 cmdsize 24
  symoff 8296
   nsyms 12
  stroff 8504
 strsize 240
Load command 6
            cmd LC_DYSYMTAB
        cmdsize 80
      ilocalsym 0
      nlocalsym 8
     iextdefsym 8
     nextdefsym 2
      iundefsym 10
      nundefsym 2
         tocoff 0
           ntoc 0
      modtaboff 0
        nmodtab 0
   extrefsymoff 0
    nextrefsyms 0
 indirectsymoff 8488
  nindirectsyms 4
      extreloff 0
        nextrel 0
      locreloff 0
        nlocrel 0
Load command 7
          cmd LC_LOAD_DYLINKER
      cmdsize 32
         name /usr/lib/dyld (offset 12)
Load command 8
     cmd LC_UUID
 cmdsize 24
    uuid A36ECE81-1426-3D1F-928C-62F6CC590A5D
Load command 9
      cmd LC_VERSION_MIN_MACOSX
  cmdsize 16
  version 10.11
      sdk 10.11
Load command 10
      cmd LC_SOURCE_VERSION
  cmdsize 16
  version 0.0
Load command 11
       cmd LC_MAIN
   cmdsize 24
  entryoff 3920
 stacksize 0
Load command 12
          cmd LC_LOAD_DYLIB
      cmdsize 56
         name /usr/lib/libSystem.B.dylib (offset 24)
   time stamp 2 Thu Jan  1 08:00:02 1970
      current version 1225.1.1
compatibility version 1.0.0
Load command 13
      cmd LC_FUNCTION_STARTS
  cmdsize 16
  dataoff 8288
 datasize 8
Load command 14
      cmd LC_DATA_IN_CODE
  cmdsize 16
  dataoff 8296
 datasize 0

从otool展现的加载命令,我们可以分析基本的加载命令(段命令,区命令,段命令中会分为几个区命令)
具体可以看看bsd/kern/mach_loader.c

segname cmd 说明 举例
LC_SEGMENT_64 将文件中(32位或64位)的段映射到进程地址空间中
LC_DYLD_INFO_ONLY
LC_SYMTAB 符号表地址
LC_DYSYMTAB 动态符号表地址
LC_LOAD_DYLINKER 使用何种动态加载库 demo中表明使用/usr/lib/dyld
LC_UUID 文件的唯一标识,crash解析中也会有该只,去确定dysm文件和crash文件是匹配的
LC_VERSION_MIN_MACOSX 二进制文件要求的最低操作系统版本 demo二进制版本,支持最低os版本为10.11
LC_SOURCE_VERSION 构建该二进制文件使用的源代码版本
LC_MAIN 设置程序主线程的入口地址和栈大小 demo二进制的entryoff位OXF50,正式__TEXT段偏移地址,然后看看0XF50的汇编代码,正式熟悉的main函数调用地址,具体请看下面汇编代码
LC_LOAD_DYLIB 加载额外的动态库,仔细看这个命令格式,动态库地址和名,当前版本号,兼容版本号,该设计比较合理,如果对于动态库有版本管理能力
LC_FUNCTION_STARTS 函数起始地址表,如何使用呢?
LC_DATA_IN_CODE /* table of non-instructions in __text */ 不是很理解

代码4.1

yingfang:mach-o文件结构-src fangying$ otool -vt a.out
a.out:
(__TEXT,__text) section
_main:
0000000100000f50    pushq   %rbp
0000000100000f51    movq    %rsp, %rbp
0000000100000f54    subq    $0x20, %rsp
0000000100000f58    leaq    0x47(%rip), %rax
0000000100000f5f    movl    $0x0, -0x4(%rbp)
0000000100000f66    movl    %edi, -0x8(%rbp)
0000000100000f69    movq    %rsi, -0x10(%rbp)
0000000100000f6d    movq    %rax, %rdi
0000000100000f70    movb    $0x0, %al
0000000100000f72    callq   0x100000f84
0000000100000f77    xorl    %ecx, %ecx
0000000100000f79    movl    %eax, -0x14(%rbp)
0000000100000f7c    movl    %ecx, %eax
0000000100000f7e    addq    $0x20, %rsp
0000000100000f82    popq    %rbp
0000000100000f83    retq
section cmd 说明 举例
__text 主程序代码
__stubs 用于动态库链接的桩
__stub_helper 用于动态库链接的桩
__cstring 常亮字符串符号表描述信息,通过该区信息,可以获得常亮字符串符号表地址
__unwind_info 这里字段不是太理解啥意思,希望大家指点下

动态库连接器–动态库链接信息

总结了mach-o文件的两个最重要的部分,那么动态库根据加载命令如何动态链接到内存中的呢?下面总结这个动态过程。

  • 系统通过加载命令,获得动态加载器的地址/usr/lib/dyly(其解决的问题是,把代码段__TEXT中和动态库相关内容进行关联,比如代码中如何调用到哪个动态库的相关代码段的偏移地址)
    (dyly是用户态进程,这个是开源的,不属于kern内核的部分)感兴趣可以看看这里

总结下:在加载命令中,和动态库和链接相关命令有如下几个:

  • 段命令
    • LC_DYLD_INFO_ONLY
    • LC_LOAD_DYLIB
    • LC_LOAD_DYLINKER
    • LC_SYMTAB
    • LC_DYSYMTAB
  • 区命令
    • __stubs
    • __stubs_helper

在进行动态链接器工作前,要先解析相关工作环境参数

  • LC_LOAD_DYLINKER 获得动态加载器地址
  • LC_LOAD_DYLIB 二进制文件依赖动态库信息
    可以使用otool工具,读取该部分信息

代码5.0

yingfang:mach-o文件结构-src fangying$ otool -L a.out
a.out:
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1)
  • LC_DYLD_INFO_ONLY 动态库信息,根据该命令是真正动态库绑定,地址重定向重要的信息。
    结合之前otool -l的信息中的command 4命令,下面struct就是该命令对应的数据结构:

代码5.1

struct dyld_info_command {
    uint32_t   cmd;     /* LC_DYLD_INFO or LC_DYLD_INFO_ONLY */
    uint32_t   cmdsize;     /* sizeof(struct dyld_info_command) */
    uint32_t   rebase_off;
    uint32_t   rebase_size;
    uint32_t   bind_off;
    uint32_t   bind_size;
    uint32_t   weak_bind_off;
    uint32_t   weak_bind_size;
    uint32_t   lazy_bind_off;
    uint32_t   lazy_bind_size;
    uint32_t   export_off;
    uint32_t   export_size;
};

根据该加载命令的字段偏移,系统可以得到压缩动态数据信息区(dymanic load info)。根据上述数据,dymanic load info数据区,主要包含了5种数据:
(下面的一些内容,我的理解可能不是太正确,希望和大家一起讨论)dyld_info_command具体定义地址

dymanic load info 说明 举例
重定向数据 rebase demo中该段数据位 11 22 10 51 11: 高位0x10表示设置立即数类型,低位0x01表示立即数类型为指针
22: 表示REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB + 2 重定向到数据段2。结合上面的信息,就是重定向到数据段2,该段数据信息为一个指针
结合数据段2的数据,获得一个重定向符号指针[0x100001010 -> _printf]
绑定数据 bind 在demo中进行动态绑定依赖的dyld的函数 U dyld_stub_binder
弱绑定数据 weak bind 用于弱绑定动态库,就像weak_framework一样
懒绑定数据 lazy bind 对于需要从动态库加载的函数符号 demo中有两个:
U _printf
U _scanf
export数据 用于对外开放的函数 demo中只有两个
0000000100000000 T __mh_execute_header
0000000100000f50 T _main

对于相关的绑定函数查找,可以使用nm命令

代码5.2

yingfang:mach-o文件结构-src fangying$ nm a.out
0000000100000000 T __mh_execute_header
0000000100000f50 T _main
                 U _printf
                 U dyld_stub_binder

>
标注:dymanic load info数据是以命令码(命令码就是一个字节码)的形式,传递具体内容。高四位表示真正命令名,低四位表示一个立即数。00表示该类型命令结束

可以使用dylyinfo获得这部分信息读取

代码5.3

yingfang:mach-o文件结构-src fangying$ xcrun dyldinfo -opcodes a.out
rebase opcodes:
0x0000 REBASE_OPCODE_SET_TYPE_IMM(1)
0x0001 REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(2, 0x00000010)
0x0003 REBASE_OPCODE_DO_REBASE_IMM_TIMES(1)
0x0004 REBASE_OPCODE_DONE()
binding opcodes:
0x0000 BIND_OPCODE_SET_DYLIB_ORDINAL_IMM(1)
0x0001 BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM(0x00, dyld_stub_binder)
0x0013 BIND_OPCODE_SET_TYPE_IMM(1)
0x0014 BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(0x02, 0x00000000)
0x0016 BIND_OPCODE_DO_BIND()
0x0017 BIND_OPCODE_DONE
no compressed weak binding info
lazy binding opcodes:
0x0000 BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(0x02, 0x00000010)
0x0002 BIND_OPCODE_SET_DYLIB_ORDINAL_IMM(1)
0x0003 BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM(0x00, _printf)
0x000C BIND_OPCODE_DO_BIND()
0x000D BIND_OPCODE_DONE
0x000E BIND_OPCODE_DONE
0x000F BIND_OPCODE_DONE

动态库链接器运行结果

上面总结,动态链接器相关信息(加载命令信息,加载命令偏移地址相关信息)。现在总结下动态链接器运行的结果是什么?如何使用相关动态链接信息,完成相关过程的?

从字面上,之前我理解,链接器就是把文本段原来动态库函数相关地址,用真实的动态库地址替换,生成一个真实的完整的可执行文本。比如demo中printf是其它动态库的,但是其真实地址对于可执行文件是不知道,只有这个执行文件运行时,通过动态链接器完善其原来文本段地址。

在总结之前两个问题前,先总结下__stubs(桩)的区概念:该区存放的是二进制文件中未定义符号的占位符,编译器生成代码时会创建对符号桩区的调用,链接器在运行时解决对桩的这些调用。链接器解决方案是在被调用的地址处,放置一条JMP指令。JMP指令将控制权转交给真实的函数体。

回顾下nm命令结果:U表示未定义的符号

yingfang:mach-o文件结构-src fangying$ nm a.out
0000000100000000 T __mh_execute_header
0000000100000f50 T _main
                 U _printf
                 U dyld_stub_binder

看下demo中main函数汇编代码:

  • demo中真实的print函数调用,编译器,编译为callq 0x100000f84 ## symbol stub for: _printf
  • 汇编代码中,callq 0x100000f84 . 注意0x100000f84,这个地址是 __TEXT段的__stubs区的地址。换句话说,就是JMP到__stubs(桩区)– 段1 section位__stubs的起始地址
  • 0x100000f84地址,是一段汇编指令 0x100000f84: jmpq *0x86(%rip) # 0x100001010
  • 0x100001010地址,是指向(代码7.2)数据段__la_symbol_ptr区,由于这个数据都符号指针,查看下该地址指向的数据区域(代码7.3)
  • 0x100001010地址指针,指向地址为4294971292(数据段都在高位,所以相比__TEXT地址,这个地址是正常的)
  • 4294971292地址,执行汇编代码请看(代码7.4) 0x100000fa1: jmpq 0x100000f8c。请注意 0x100000f8c就是__TEXT段section __stub_helper区的起始地址。该地址是执行汇编代码具体请看(代码7.5)0x100000f95: jmpq *0x65(%rip) # 0x100001000
  • 0x100001000地址,恰好是__DATA段, __nl_symbol_ptr区的其实地址,该地址指向的数据值位为 0x100001000: 0x0000000000000000 0x0000000000000000 (代码7.6)

>
总结:

+ __stubs区和__stub_helper区是帮助动态链接器找到指定数据段__nl_symbol_ptr区,二进制文件用0x0000000000000000进行占位,在运行时,系统根据dynamic loader info信息,把占位符换为调用dylib的dyld_stub_binder函数的汇编指令。

+ 当第一次调用完动态库中的符号后,动态链接器会根据dynamic loader info信息,把数据段__la_symbol_ptr指向正在的符号地址,而不是指向_nl_symbol_ptr区

代码7.0

yingfang:mach-o文件结构-src fangying$ otool -p _main -tV a1.out 
a1.out:
(__TEXT,__text) section
_main:
0000000100000f50    pushq   %rbp
0000000100000f51    movq    %rsp, %rbp
0000000100000f54    subq    $0x20, %rsp
0000000100000f58    leaq    0x47(%rip), %rax        ## literal pool for: "Hello World!\n"
0000000100000f5f    movl    $0x0, -0x4(%rbp)
0000000100000f66    movl    %edi, -0x8(%rbp)
0000000100000f69    movq    %rsi, -0x10(%rbp)
0000000100000f6d    movq    %rax, %rdi
0000000100000f70    movb    $0x0, %al
0000000100000f72    callq   0x100000f84             ## symbol stub for: _printf
0000000100000f77    xorl    %ecx, %ecx
0000000100000f79    movl    %eax, -0x14(%rbp)
0000000100000f7c    movl    %ecx, %eax
0000000100000f7e    addq    $0x20, %rsp
0000000100000f82    popq    %rbp
0000000100000f83    retq

代码7.1

Section
  sectname __stubs
   segname __TEXT
      addr 0x0000000100000f84
      size 0x0000000000000006
    offset 3972
     align 2^1 (2)
    reloff 0
    nreloc 0
     flags 0x80000408
 reserved1 0 (index into indirect symbol table)
 reserved2 6 (size of stubs)
Section
  sectname __stub_helper
   segname __TEXT
      addr 0x0000000100000f8c
      size 0x000000000000001a
    offset 3980
     align 2^2 (4)
    reloff 0
    nreloc 0
     flags 0x80000400
 reserved1 0
 reserved2 0

代码7.2

yingfang:mach-o文件结构-src fangying$ xcrun dyldinfo -lazy_bind a1.out 
lazy binding information (from lazy_bind part of dyld info):
segment section          address    index  dylib            symbol
__DATA  __la_symbol_ptr  0x100001010 0x0000 libSystem        _printf

代码7.3

(gdb) x/2g 0x100001010
0x100001010:    4294971292

代码7.4

(gdb) x/3i 4294971292
   0x100000f9c: pushq  $0x0
   0x100000fa1: jmpq   0x100000f8c

代码7.5

(gdb) x/3i 0x100000f8c
   0x100000f8c: lea    0x75(%rip),%r11        # 0x100001008
   0x100000f93: push   %r11
   0x100000f95: jmpq   *0x65(%rip)        # 0x100001000

代码7.6

(gdb) x/3g 0x100001000
0x100001000:    0x0000000000000000  0x0000000000000000
0x100001010:    0x0000000100000f9c

可执行文件运行过程

根据上面总结。该段总结下可执行文件运行过程。
我总结如下:

  • 解析mach-o文件
  • 设置运行环境参数
    • 文本段VM映射参数
    • 加载命令
    • 动态库信息
    • 符号表地址信息
    • 动态符号表地址信息
    • 常亮字符串表地址信息
    • 动态库加载信息
    • 符号函数地址
    • 依赖动态库信息
    • 动态链接器地址信息
  • 根据动态库加载信息,把桩占位符,填写为指定调用_nl_symbol_ptr的汇编指令
  • 根据LC_MAIN的entry point调用指定entry offset偏移地址
  • 执行entry offset相关二进制(逻辑是按照汇编指令,进行运行)
  • 第一次运行到动态库函数时,进行一次懒加载动态绑定,并且动态链接器自动修改_la_symbol_ptr区的地址,指向动态库对应符号的地址
  • 第二次运行到动态库函数时,直接jmp到指定的符号地址

>
注意:系统很多动态库都是共有的,所以XOS做了共享库缓存优化,只要有相关进程使用过相关动态库,在另一进程,动态链接器在填桩时,直接会把桩_la_symbol_ptr区的地址,指向动态库对应符号的地址。

具体看系统如何加载文件,动态链接文件,以及进入入口函数,可以这里

parse_machfile(
    struct vnode        *vp,       
    vm_map_t        map,
    thread_t        thread,
    struct mach_header  *header,
    off_t           file_offset,
    off_t           macho_size,
    int         depth,
    int64_t         aslr_offset,
    int64_t         dyld_aslr_offset,
    load_result_t       *result
)

mach-o格式和加载方式 – 可以做啥呢

基础很重要,mach-o格式理解和加载运行逻辑总结,可以帮助我们正确认识到mac os和ios app可执行文件启动过程。基于该内容,可以做的事情列一下

  • category冲突分析
  • 非OC函数switch
  • bitcode分析
  • 包支持架构分析
  • 常量字符串分析,比如资源为无使用情况分析
  • crash符号化
  • 符号模块查找
  • 学习经典的数据格式,对于设计相关数据协议,网络协议等等,也有很好启发和参考价值

反过来:如果二进制包越来越大,进程启动速度也会越来越慢。因为读取,解析文件格式会比较慢。

通过对于mach-o文件的分析,对于__TEXT系统都是只读区,程序代码逻辑都在该区。但是也发现由于为了保持代码栈形式的运行,系统对于动态库的函数,不是直接调用动态库的函数地址,而是先调用到数据段的_nl_symbol_ptr区,通过_nl_symbol_ptr的信息调用到__DATA段_la_symbol_ptr区,由于__DATA内存区,用户态可以去修改,因此我们可以利用该特性去替换相关函数调用。具体代码我也在基于fishhook代码总结中。

了解mach-o具体的格式和加载,动态链接过程原理。比如mac上使用DYLD_INSERT_LIBRARIES进行代码注入,或者使用fishhook进行c函数hook,都能很容易理解。

2012-02-09 11:56:16 tietao 阅读数 10080
  • 《C语言/C++学习指南》Linux开发篇

    本篇面向Linux初级、但已掌握C/C++基本语法的学员,以CentOS为演示平台,介绍Linux下的C/C++开发技术。在内容编排上,先介绍Linux的安装、基本使用和配置,然后再开始介绍在Linux下的C/C++开发技术: gcc命令行、Makefile、gdb调试方法、动态库与静态库、pthread线程、进程、进程间通信、系统调用及杂项技术。(前四章免费)

    60731 人正在学习 去看看 邵发

1

error: expected expression before 'else'

else之前无表达式。


2

error: lvalue required as left operand of assignment

左值问题。


3

error: invalid storage class for function 'XXXXXX'

在文件的某个地方,丢失了一个大括号‘}’。



2014-05-06 23:34:56 yuanya 阅读数 2645
  • 《C语言/C++学习指南》Linux开发篇

    本篇面向Linux初级、但已掌握C/C++基本语法的学员,以CentOS为演示平台,介绍Linux下的C/C++开发技术。在内容编排上,先介绍Linux的安装、基本使用和配置,然后再开始介绍在Linux下的C/C++开发技术: gcc命令行、Makefile、gdb调试方法、动态库与静态库、pthread线程、进程、进程间通信、系统调用及杂项技术。(前四章免费)

    60731 人正在学习 去看看 邵发

I have an OS X app that's distributed through the Mac App Store, and recently updated to Xcode 4.6.3.

When I run my regular build now, I receive:

 code object is not signed at all In subcomponent



71down voteaccepted

I think I may have figured this one out. I've been running Xcode 4.6.3 on OS X Mavericks, under the impression that any build-specific tools were bundled in the Xcode application.

But, it seems codesign is in /usr/bin. Whether it's put there by one of the Xcode installers or comes with a vanilla system install, I'm not sure. But reading through the man page for codesign, I found this nifty option:

--deep  When signing a bundle, specifies that nested code content such as helpers, frameworks, and plug-ins, should be recursively signed
             in turn. Beware that all signing options you specify will apply, in turn, to such nested content.
             When verifying a bundle, specifies that any nested code content will be recursively verified as to its full content. By default,
             verification of nested content is limited to a shallow investigation that may not detect changes to the nested code.
             When displaying a signature, specifies that a list of directly nested code should be written to the display output. This lists only
             code directly nested within the subject; anything nested indirectly will require recursive application of the codesign command.

And then I found this post (https://alpha.app.net/isaiah/post/6774960) from two weeks ago (~June 2013), which mentions (albeit second-handedly):

@isaiah I asked a guy in the labs about it. He said codesign now requires embedded frameworks to be signed separately before code signing the app bundle as a whole.

Manually re-running the codesign command that Xcode normally runs, while adding the --deep flag to the end, signs the application properly.

I'm not yet sure exactly what ramifications this manual signing has, or whether I can tweak the Xcode build to add the --deep flag automatically, but this seems to be the underlying issue. (codesign no longer automatically deeply signs your app bundle.)

也可以在Xcode Code Signing中选项中的Other Code Signing Flags中加上--deep.

没有更多推荐了,返回首页