和其他传统UNIX变种不同,OSX不支持许多与加载、链接和执行程序相关的工具。

例如,当共享库重定位出错时,我所做的首要事情就是对可执行文件运行ldd。ldd工具列出了可执行文件所依赖的共享库(包括所在路径)。但是在OSX上,试图运行ldd将报错。

evil:~ mohit$ ldd /bin/ls
-bash: ldd: command not found

没找到?但在所有的类UNIX系统上基本上都有的啊!我想知道objdump是否可用。

evil:~ mohit$ objdump -x /bin/ls
-bash: objdump: command not found

命令未找到!怎么回事?

问题在于与Linux、Solaris、HP-UX和其他许多UNIX变种不同,OSX不使用ELF文件格式。另外,OSX不属于GNU项目的一部分。该项目包含像ldd和objdump这样的工具。

为了在OSX获得可执行文件所依赖的共享库列表,需要使用otool工具。

evil:~ mohit$ otool /bin/ls
otool: one of -fahlLtdoOrTMRIHScis must be specified
Usage: otool [-fahlLDtdorSTMRIHvVcXm] <object file> ...-f print the fat headers-a print the archive header-h print the mach header-l print the load commands-L print shared libraries used-D print shared library id name-t print the text section (disassemble with -v)-p <routine name> start dissassemble from routine name-s <segname> <sectname> print contents of section-d print the data section-o print the Objective-C segment-r print the relocation entries-S print the table of contents of a library-T print the table of contents of a dynamic shared library-M print the module table of a dynamic shared library-R print the reference table of a dynamic shared library-I print the indirect symbol table-H print the two-level hints table-v print verbosely (symbolicly) when possible-V print disassembled operands symbolicly-c print argument strings of a core file-X print no leading addresses or headers-m don't use archive(member) syntax
evil:~ mohit$ otool -L /bin/ls
/bin/ls:/usr/lib/libncurses.5.4.dylib (compatibility version 5.4.0, current version 5.4.0)/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 88.0.0)

好多了!我们可以看见/bin/ls引用了二个动态库。尽管,文件扩展名我们根本不熟悉。

我相信许多UNIX、Linux用户使用OSX系统时有过类似的经历,所以我决定写一点目前我所知道的关于 OSX可执行文件的知识。

OSX运行时架构运行时环境是OSX上代码扩展的一个框架。它由一组定义代码如何被加载、被管理、被执行的集合组成。一旦应用程序运行,合适的运行时环境就加载程序到内存,解决外部库的引用,并为执行准备代码。

OSX支持三种运行时环境:

  • Dyld运行时环境:基于Dyld库管理器的推荐环境;
  • CFM运行时环境:OS9遗留环境。实际用来设计需要使用OSX新特色,但还没完全移植到dyld的应用程序。
  • 经典环境:OS9(9.1 or 9.2)程序无需修改直接在OSX运行。

本文主要关注于Dyld运行时环境。

在OSX中几乎所有的可执行文件都使用Mach-O文件格式,如:应用程序、框架、库、内核扩展……都是以Mach-O文件实现。Mach-O是既一种文件格式,也是一种描述可执行文件如何被内核加载并运行的ABI(应用程序二进制接口)。专业一点讲, 它告诉系统:使用哪个动态库加载器;加载哪个共享库;如何组织进程地址空间;函数入口点地址等。

Mach-O不是新事物。最初由开放软件基金会(OSF)用于设计基于Mach微内核的OSF/1操作系统。后来被移植到x86系统OpenStep上。

为了支持Dyld 运行时环境,所有文件应该编译成Mach-O可执行文件格式。

Mach-O文件的组织

Mach-O文件分为三个区域:头部、载入命令区段和原始段数据。头部和载入命令区段描述文件功能、布局和其他特性;原始段数据包含由载入命令引用的字节序列。为了研究和检查Mach-O文件的各部分,OSX自带了一个很有用的程序otool,其位于/usr/bin目录下。

接下来,将使用otool来了解Mach-O文件如何组织的。

头部查看文件的Mach-O头部,使用otool 命令的-h参数

evil:~ mohit$ otool -h /bin/ls
/bin/ls:
Mach headermagic cputype cpusubtype filetype ncmds sizeofcmds flags0xfeedface           18                   0            2        11            1608 0x00000085

头部首先指定的是魔数(magic number)。魔数标明文件是32位还是64位的Mach-O文件。也标明CPU字节顺序。魔数的解释,参看/usr/include/mach-o/loader.h。

头部也指定文件的目标架构。这样就允许内核确保该代码不会在不是为此处理器编写的CPU上运行。例如,在上面的输出,cputype设成18,它代表CPU_TYPE_POWERPC,在/usr/include/mach/machine.h中定义。

从上两项信息,我们推断出此二进制文件用于基于32位PowerPC的系统。

有时二进制文件可能包含不止一个体系的代码。通常称为Universal Binaries,通常以fat_header这额外的头部开始。检查fat_header内容, 使用otool命令的-f开关参数。

cpusubtype属性制定了CPU确切模型,通常设成CPU_SUBTYPE_POWERPC_ALL或CPU_SUBTYPE_I386_ALL。

filetype指出文件如何对齐如何使用。实际上它告诉你文件是库、静态可执行文件、core file等。上面的filetype等于MH_EXECUTE,指出demand paged executable file。下面是从/usr/include/mach-o/loader.h截取的片段,列出了不同的文件类型。

#define MH_OBJECT 0x1   /* relocatable object file */
#define MH_EXECUTE  0x2   /* demand paged executable file */
#define MH_FVMLIB 0x3   /* fixed VM shared library file */
#define MH_CORE   0x4   /* core file */
#define MH_PRELOAD  0x5   /* preloaded executable file */
#define MH_DYLIB  0x6   /* dynamically bound shared library */
#define MH_DYLINKER 0x7   /* dynamic link editor */
#define MH_BUNDLE 0x8   /* dynamically bound bundle file */
#define MH_DYLIB_STUB 0x9   /* shared library stub for static *//*  linking only, no section contents */

接下来的两个属性涉及到载入命令区段,指定了命令的数目和大小。

最后, 获得了状态信息, 这些可能在装载和执行时被内核使用。

载入命令载入命令区段包含一个告知内核如何载入文件中的各个原始段的命令列表。典型的描述如何对齐,保护每个段及各段在内存中的布局。

查看文件中的载入命令列表,使用otool命令的-l开关参数。

evil:~/Temp mohit$ otool -l /bin/ls
/bin/ls:
Load command 0cmd LC_SEGMENTcmdsize 56segname __PAGEZEROvmaddr 0x00000000vmsize 0x00001000fileoff 0
filesize 0maxprot 0x00000000
initprot 0x00000000nsects 0flags 0x4
Load command 1cmd LC_SEGMENTcmdsize 600segname __TEXTvmaddr 0x00001000vmsize 0x00006000fileoff 0
filesize 24576maxprot 0x00000007
initprot 0x00000005nsects 8flags 0x0
Sectionsectname __textsegname __TEXTaddr 0x00001ac4size 0x000046e8offset 2756align 2^2 (4)reloff 0nreloc 0flags 0x80000400
reserved1 0
reserved2 0[ ___SNIPPED FOR BREVITY___ ]Load command 4cmd LC_LOAD_DYLINKERcmdsize 28name /usr/lib/dyld (offset 12)
Load command 5cmd LC_LOAD_DYLIBcmdsize 56name /usr/lib/libncurses.5.4.dylib (offset 24)time stamp 1111407638 Mon Mar 21 07:20:38 2005current version 5.4.0
compatibility version 5.4.0
Load command 6cmd LC_LOAD_DYLIBcmdsize 52name /usr/lib/libSystem.B.dylib (offset 24)time stamp 1111407267 Mon Mar 21 07:14:27 2005current version 88.0.0
compatibility version 1.0.0
Load command 7cmd LC_SYMTAB
cmdsize 24symoff 28672nsyms 101stroff 31020
strsize 1440
Load command 8cmd LC_DYSYMTABcmdsize 80ilocalsym 0nlocalsym 0iextdefsym 0nextdefsym 18iundefsym 18nundefsym 83tocoff 0ntoc 0modtaboff 0nmodtab 0extrefsymoff 0nextrefsyms 0
indirectsymoff 30216nindirectsyms 201extreloff 0nextrel 0locreloff 0nlocrel 0
Load command 9cmd LC_TWOLEVEL_HINTS
cmdsize 16offset 29884nhints 83
Load command 10cmd LC_UNIXTHREADcmdsize 176     flavor PPC_THREAD_STATEcount PPC_THREAD_STATE_COUNTr0  0x00000000 r1  0x00000000 r2  0x00000000 r3   0x00000000 r4   0x00000000r5  0x00000000 r6  0x00000000 r7  0x00000000 r8   0x00000000 r9   0x00000000r10 0x00000000 r11 0x00000000 r12 0x00000000 r13  0x00000000 r14  0x00000000r15 0x00000000 r16 0x00000000 r17 0x00000000 r18  0x00000000 r19  0x00000000r20 0x00000000 r21 0x00000000 r22 0x00000000 r23  0x00000000 r24  0x00000000r25 0x00000000 r26 0x00000000 r27 0x00000000 r28  0x00000000 r29  0x00000000r30 0x00000000 r31 0x00000000 cr  0x00000000 xer  0x00000000 lr   0x00000000ctr 0x00000000 mq  0x00000000 vrsave 0x00000000 srr0 0x00001ac4 srr1 0x00000000

上面的文件在头部下有11加载命令直接定位,从0到10。

Commands 0 and 3 (LC_SEGMENT) 从0到3,定义了文件中的段如何映射到内存中去。段定义了Mach-O二进制文件中的字节序列,可以包含零个或更多的Sections。稍候我们谈谈段。

  • Command 4 (LC_LOAD_DYLINKER) 指定使用哪个动态链接器。几乎总是设成OSX默认动态链接器/usr/lib/dyld。
  • Commands 5 and 6 (LC_LOAD_DYLIB) 指定文件需要链接的共享库。它们由command 4规定的动态链接器载入。
  • Commands 7 and 8 (LC_SYMTAB, LC_DYNSYMTAB) 指定由文件和动态链接器分别使用的符号表。
  • Command 9 (LC_TWOLEVEL_HINTS) 包含两级名称空间的Hint Table。
  • Command 10 (LC_UNIXTHREAD) 定义进程主线程的初始状态。该命令仅仅包含在可执行文件里。

段(Segments)与区(Sections)

上面涉及到的大多数加载命令都引用了文件中的段。段是Mach-O文件直接被内核和动态链接器映射到虚拟内存中的一系列字符序列。头部和加载命令区域认为是文件的首段。一个典型的OSX可执行文件通常由下列五段:

  • __PAGEZERO 定位于虚拟地址0,无任何保护权利。此段在文件中不占用空间,访问NULL导致立即崩溃。
  • __TEXT 包含只读数据和可执行代码。
  • __DATA 包含可写数据。这些section通常由内核标志为copy-on-write。
  • __OBJC 包含Objective C语言运行时环境使用的数据。
  • __LINKEDIT 包含动态链接器用的原始数据。

__TEXT和 __DATA段可能包含0或更多的section。每个section由指定类型的数据,如,可执行代码,常量,C字符串等组成。

查看某section内容,使用otool命令-s选项。

evil:~/Temp mohit$ otool -sv __TEXT __cstring /bin/ls
/bin/ls:
Contents of (__TEXT,__cstring) section
00006320 00000000 5f5f6479 6c645f6d 6f645f74
00006330 65726d5f 66756e63 73000000 5f5f6479
00006340 6c645f6d 616b655f 64656c61 7965645f
00006350 6d6f6475 6c655f69 6e697469 616c697a
__SNIP__

反汇编__text section,使用 the -tv 开关参数。

evil:~/Temp mohit$ otool -tv /bin/ls
/bin/ls:
(__TEXT,__text) section
00001ac4        or      r26,r1,r1
00001ac8        addi    r1,r1,0xfffc
00001acc        rlwinm  r1,r1,0,0,26
00001ad0        li      r0,0x0
00001ad4        stw     r0,0x0(r1)
00001ad8        stwu    r1,0xffc0(r1)
00001adc        lwz     r3,0x0(r26)
00001ae0        addi    r4,r26,0x4
__SNIP__

在 __TEXT段里,存在四个主要的section:

  • __text 编译后的机器码。
  • __const 通用常量数据。
  • __cstring 字面量字符串常量。
  • __picsymbol_stub 动态链接器使用的位置无关码stub路由。

这样保持了可执行的和不可执行的代码在段里的明显隔离。

运行应用程序既然知道了Mach-O文件的格式,接下来看看OSX如何载入并运行应用程序的。运行应用程序时,shell首先调用FORK(2)系统调用。fork创建调用进程(shell)逻辑拷贝并准备好执行。子进程然后调用EXECVE(2)系统调用,当然需要提供要执行的程序路径。

内核载入指定的文件,检查其头部验证是否是合法的Mach-O文件。然后开始解释载入命令,将子进程地址空间替换成文件中的各段。同时,内核也执行有二进制文件指定的动态链接器,着手加载、链接所有依赖库。在绑定了运行所必备的各个符号后,调用entry-point函数。

在build应用程序时entry-point函数通常从/usr/lib/crt1.o静态链接(标准函数)。此函数初始化内核环境,调用可执行文件的main()函数。

应用程序现在运行了。

动态链接器

OSX动态链接器/usr/lib/dyld,负责加载依赖的共享库,导入变量符号和函数,与当前进程的绑定。进程首次运行时,链接器所做的就是把共享库导入到进程地址空间。取决于程序的build方式,实际绑定也足执行不同的方式。

  • 载入后立即绑定 Load-Time 绑定。
  • 当符号引用时 Just-In-Time 绑定。

预绑定:如未指定绑定类型,使用just-in-time绑定。

应用程序仅仅当所有需要的符号和段从不同的目标文件解决是才能继续运行。为了寻找库和框架,标准动态链接器/usr/bin/dyld,将搜索预定义的目录集合。要修改目录,或提供回滚路径,可以设置DYLD_LIBRARY_PATH或DYLD_FALLBACK_LIBRARY_PATH环境变量。

分享到: 

Mac OS X 如何执行应用程序相关推荐

  1. 安装Mac OS X虚拟机 编写app程序

    没有Mac没有iPhone,怎么配置环境来看下IOS的开发和打包过程.虚拟机就出现了,比较通俗的回答(适合没有电脑基础的朋友) 虚拟机,顾名思义就是虚拟出来的电脑,这个虚拟出来的电脑和真实的电脑几乎完 ...

  2. mac os下编写对拍程序

    转载自Hist!     http://hist.cnblogs.com/ 介绍 对拍是信息学竞赛中重要的技巧,它通过一个效率低下但正确率可以保证的程序,利用庞大的随机生成数据来验证我们的高级算法程序 ...

  3. java mac sh_如何创建AppleScript或Command文件以在Mac OS上启动Java应用程序?

    我创建了一个 Java应用程序,需要准备它在任何操作系统上运行.对于Windows,我创建了一个类似于launch-win32.bat的批处理文件: @echo off javaw -Xss1024k ...

  4. 黑马程序员——IOS基础---Mac OS X

    ------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 如果你刚开始从事IOS开发,并且对苹果系统环境,不熟悉,从这看起吧. 一.系统简介 1.苹果公司 ...

  5. 如何将Mac OS X10.9下的Python2.7升级到最新的Python3.3

    Mac OS X10.9默认带了Python2.7,不过现在Python3.3.3出来了,如果想使用最新版本,赶紧升级下吧.基本步骤如下. 第1步:下载Python3.3 下载地址如下: Python ...

  6. python2.7怎么升级python3_如何将Mac OS X10.9下的Python2.7升级到最新的Python3.3

    Mac OS X10.9默认带了Python2.7,不过现在Python3.3.3出来了,如果想使用最新版本,赶紧升级下吧.基本步骤如下. 第1步:下载Python3.3 下载地址如下: 这里面有wi ...

  7. 《MAC OS X 技术内幕》读书笔记第一章:MAC OS X的起源

    <MAC OS X 技术内幕>读书笔记第一章:MAC OS X的起源 前言 1 System x.x系列 1.1System 1.0(1984年1月24日) 1.2System 2.x(1 ...

  8. Mac OS X应用程序格式详解

    Mac OS X应用程序格式详解 OS X 如何执行应用程序 译者:51test2003     译自 http://0xfe.blogspot.com/2006/03 ... s-applicati ...

  9. [转]Mac OS守护进程(服务)列表及优化建议

    /sbin/launchd 系统及用户进程管理器,它是内核装载成功后在OS环境下启动的第一个进程,是Mac OS最重要的进程之一.你无法禁用它. /usr/libexec/kextd 内核扩展服务,响 ...

最新文章

  1. 人工智能在能源行业的5个应用
  2. HashMap实现原理
  3. java 网络(socket)
  4. **上海铁路局2004年最新时刻发布!**
  5. linux下vi修改文件用法
  6. php webservice 上传,PHP实现WebService服务
  7. mysql过滤器_MYSQL复制过滤器
  8. [渝粤教育] 中国地质大学 工业卫生技术 复习题 (2)
  9. 两款开源Web功能测试工具
  10. Java-优先级队列(堆)
  11. jq+ajax前端上传多张图片_史上最轻量的前端框架-VanillaJS
  12. CocoaPods管理第三方
  13. 【解决方案】Ehome协议视频融合平台EasyCVR在危化行业的安防监控应用
  14. 怎么查看内网ip?如何分辨IP是公网IP还是内网IP?
  15. 2台计算机网线连接无法ping通,两台电脑PING不通怎么办?
  16. meta中的http-equiv = X-UA-Compatible
  17. 《中国医学大成》目录
  18. 随手笔记(九)———类型装换技巧
  19. ubuntu解决菜单栏和工具栏消失的方案
  20. PS学习笔记----图层锁定

热门文章

  1. EXCEL批量替换公式数据变成真两位小数
  2. python数字推盘游戏怎么显示步数_用 Python 实现手机自动答题,这下百万答题游戏谁也玩不过我!...
  3. 开发板之驱动安装与烧写程序
  4. 百度李彦宏 鼓励狼性,淘汰小资
  5. 【芝麻IP代理】详解Python爬虫必备框架—Scrapy
  6. 使用C++完成一个小型双人对战回合制游戏
  7. 快速打开jupyter lab
  8. 首页 个人简历,个人主页,个人介
  9. yzmcms常用标签
  10. YzmCMS跨站脚本漏洞(CVE-2020-22394)复现