UCC编译驱动分析
1. UCC的组成
UCC编译后,生成一个C语言编译器ucl和编译驱动ucc, ucl执行时将输入的C源文件编译输出预处理文件,而ucc则分析输入参数,根据参数调用
不同的程序eg. gcc, as, ucl对输入文件执行相关的操作并输出结果。作者的工作目前是C编译器,预处理,汇编, 链接的功能都直接使用了现有的工
具。
2. 预备知识
2.1 编译的流程
通常,当需要编译C源文件时,只需要执行gcc/cc即可。实际上,gcc是一个编译驱动程序,它会依据编译参数依次调用预处理器CPP, 编译器CC, 汇编器AS, 连接器LD处理相关文件,最后输出可执行文件或者库文件。
2.2 链表List
在程序中,输入文件集合,输出文件集合以及其他参数使用List来保存,该结构的定义,
typedef struct list
{
char *str;
struct list *next;
} *List;
以Append操作为例,分析List的使用,
List ListAppend(List list, char *str)
{
List *tail = &list;
List p = list;
while (p != NULL)
{
tail = &p->next;
p = p->next;
}
*tail = p = Alloc(sizeof(*p));
p->str = str;
p->next = NULL;
return list;
}
我们注意到,在List的Append操作中使用了二级指针,使用二级指针的操作简练精致。
3. ucc概览
3.1 编译选项分析
ucc在开始执行时,会对编译选项进行分析,确定输入文件的阶段。
-E, 输出预处理文件.
-S, 输出汇编文件.
-C, 输出目标文件
-o, 输出可执行文件.
-Wl, 链接器选项.
-Wa, 汇编器选项.
分析得到的数据放置在struct option, 该结构的定义,
struct option
{
List pflags; //预处理选项
List cflags; //编译选项
List aflags; //汇编选项
List lflags; //链接选项
List cfiles; //c源文件集合
List pfiles; //预处理文件集合
List afiles; //汇编文件集合
List ofiles; //目标文件集合
List lfiles; //库文件集合
List linput; //其他链接文件集合
int verbose; //输出信息级别
int oftype; //输出文件类型, .i, .s, .o, .out
char *out; //最终输出文件名称
};
3.2 编译过程
3.2.1 ucc的编译流程
ucc与gcc的编译流程类似,在编译C源文件阶段,调用ucl执行编译任务,
执行流程将根据ucc选项,将依次执行预处理,编译, 汇编,链接的阶段。
enum { C_FILE, PP_FILE, ASM_FILE, OBJ_FILE, LIB_FILE, EXE_FILE };
for (i = PP_FILE; i <= Option.oftype; ++i)
{
if (InvokeProgram(i) != 0)
{
RemoveFiles();
fprintf(stderr, "ucc invoke command error:");
PrintCommand();
return -1;
}
}
3.2.3 预处理示例
以预处理作为示例,进一步详细分析步骤,
case PP_FILE:
for (p = Option.cfiles; p != NULL; p = p->next)
{
ofname = FileName(p->str, ".i");
PPFiles = ListAppend(PPFiles, ofname);
il = ListAppend(NULL, p->str);
ol = ListAppend(NULL, ofname);
cmd = BuildCommand(CPPProg, Option.pflags, il, ol);
status = Execute(cmd);
}
Option.pfiles = ListCombine(Option.pfiles, PPFiles);
break;
可以看到,首先将C源文件推出输出的预处理文件,将后缀改变为.i,合成命令序列,使用gcc做预处理,每次处理一个文件。