objc系列译文(6.3):Mach-O 可执行文件

381 查看

当我们在Xcode中构建一个程序的时候,其中有一部分就是把源文件(.m和.h)文件转变成可执行文件。这个可执行文件包含了将会在CPU(iOS设备上的arm处理器或者你mac上的Intel处理器)运行的字节码。

我们将会过一遍编译器这个过程的做了些什么,同时也看一下可执行文件的内部到底是怎样的。其实,里面的东西比你看到的要多很多。

让我们先把Xcode放一边,踏入Commond-Lines的大陆。当我们在Xcode中构建一个App时,Xcode只是简单的调用了一系列的工具而已。希望这将会让你更好的明白一个可执行文件(被称之为Mach-O可执行文件),是怎样组装起来的,并且是怎样在iOS或者os x上执行的

 

XCrun

先从一些基础性的东西开始:我们将会使用一个叫做Xcrun的命令行工具。他看起来很奇怪,但是的确相当出色。这个小工具是用来调用其他工具的。 原先的时候我们执行:

现在在终端中,我们可以执行:

Xcrun定位Clang,并且使用相关的参数来执行Clang。

为什么我们要做这个事情?这看起来毫无重点,胡扯八道。但是Xcrun允许我们使用多个版本的Xcode,或者使用特定Xcode版本里面的工具,或者针对特点的SDK使用不同的工具。如果你恰好有Xcode4.5和xcode5、使用xcode-select和xcrun你可以选择选择使用来自Xcode4.5里面的SDK的工具,或者来自Xcode5里面的SDK的工具。在大多数其他平台上,这将是一个不可能的事情。如果你看一下帮助手册上xcode-select和xcrun的一些细节。你就能在不安装命令行工具的情况下,使用在终端中使用开发者工具。

 

一个不使用IDE的Hello World

回到终端,创建一个包含一个c文件的目录:

现在使用你喜欢的文本编辑器来编辑这个文件,例如TextEdit.app:

录入下面的代码:

保存,并且回到终端执行:

现在你能够在终端上看到熟悉的Hello World!。你编译了一个C程序并且执行了它。所有都是在不使用IDE的情况下做的。深呼吸一下,高兴高兴。

我们在这里做了些什么?我们将hellowrold.c编译成了叫a.out的Mach-o二进制文件。a.out是编译器的默认名字,除非你指定一个别的。

 

Hello World和编译器

现在可选择的编译器是Clang(读作:/’kl /)。Chris写了一些更多关于Clang细节的介绍,可以参考: about the compiler

概括一下就是,编译器将会读入处理hellowrold.c,输出可执行文件a.out。这个过程包含了非常多的步骤。我们所要做的就是正确的执行它们。


预处理:

  • 序列化
  • 宏定义展开
  • #include展开(引用文件展开)

语法和语义分析:

  • 使用预处理后的单词构建词法树
  • 执行语义分析生成语法树
  • 输出AST (Abstract Syntax Tree)

代码生成和优化

  • 将AST转化成更低级的中间码(LLVM IR)
  • 优化生成代码
  • 目标代码生成
  • 输出汇编代码

汇编程序

  • 将汇编代码转化成目标文件

连接器

  • 将多个目标文件合并成可执行文件(或者一个动态库) 我们来看一个关于这些步骤的简单的例子。

 

预处理

编译器将做的第一件事情是处理文件。使用Clang展示一下这个过程:

欧耶。输出了413行内容。打开个编辑器看看到底发生了什么:

在文件顶部我们能看到很多以”#”开头的行。这些被称之为行标记语句的语句告诉我们它后面的内容来自哪里。我们需要这个。如果我再看一下hellowrold.c,第一行是:

我们都用过#include和#import。它们做的就是告诉于处理器在#include语句的地方插入stdio.h的内容。在刚刚的文件里就是插入了一个以#开头的行标记。跟在#后面的数字是在源文件中的行号。每一行最后的数字是在新文件中的行号。回到刚才打开的文件,接下来是系统头文件,或者一些被看成包裹着extern “C”的文件。

如果你滚动到文件末尾,你将会发现我们的helloworld.c的代码: