ARM Cortex-M底层技术(五)编译器编译内核原理以及其应用的简单介绍

转载地址: 编译器编译内核原理以及其应用

上篇文档简要介绍了一下编译器的编译内核,当然介绍的很简单,没有深入进去,俗话说不深入怎么High偷笑,所以这里我们深入进去搞一下,看看里面都有些什么有用的东东。

这里以市面上用的最多的Keil MDK为例,我们来研究下。

1、先上一张老图,然后我们仔细看下这个器、那个器的都是干啥的:

上一篇文章的图片,上一篇文章也简要介绍了下,但你可能会问,这东西了解了又能怎样?有什么用吗?那么好,司机我就来给大家带带路,看看了解编译原理是不是真的那么没用?

司机嘛,首先先来带大家认认路,你说这几个东东很重要,但是在Keil里面我们怎么找到他们呢?

你可以在"keil根目录\ARM\ARMCC\bin"下找到他们,如下图所示:

就是这几个东东,他们就是keil编译器的内核,把你编写的C代码变成可以烧写到MCU中执行的2进制文件就是这几个东东来完成的,其中armar.exe就是预处理器、armcc.exe是编译器、armasm.exe是汇编器、armlink.exe是链接器、fromelf.exe是elf文件的实用工具集。

有了这几个东东你可以甚至都不用Keil的编程界面都可以干活,直接在windows的shell界面里面通过命令行就可以编译链接,但这个是真没啥用,纯装B,有IDE不用非要自己敲命令行这不找罪受嘛,这里就不介绍这种没啥用的招数了。

当然了你也可以在集成开发环境中见到这几个货:

如上图:

1、汇编器的相关选项;2、编译器的相关选项;3、预处理器的相关选项;4、链接器描述文件(*.map)中包含的内容;

还有这个:

上图是专门的链接器相关配置的界面;那么这些东东都是干嘛用的呢?

让我们先来举个栗子,考虑比如下面这样一段代码:

Reset_Handler   PROC
                EXPORT  Reset_Handler               [WEAK]
                IMPORT  SystemInit
                IMPORT  __main

                LDR     r0, =errorfunc
                BLX     r0
                LDR     r0, =__main
                BX      r0
                ENDP

启动代码中的Reset_Handler代码,大家可以一眼看出来这段代码是错误的,其中红色字体的errorfunc是我故意填在这里的一个不存在的函数,编译必然报错,那么我们看看编译器是怎么报错的?

重点在途中蓝色底纹的部分,注意第一个单词:assembling……,然后后面跟着出错信息,没错,这是一个汇编器错误;再看下面,我在代码里面给出一个错误(我在main.c里面删掉了一个头文件):

还是看蓝色底纹部分,注意第一个单词:compiling……,然后后面跟着出错信息,没错,这是一个编译器错误,跟上面的不同,这是编译器报错,我们再看下面一个错误,代码如下:

int main(void)
{
    char ch;

    /* Init board hardware. */
    /* attach 12 MHz clock to FLEXCOMM0 (debug console) */
    CLOCK_AttachClk(BOARD_DEBUG_UART_CLK_ATTACH);

    BOARD_InitPins();
    BOARD_BootClockFROHF48M();
    BOARD_InitDebugConsole();

    PRINTF("hello world.\r\n");

    while (1)
    {
        ch = errorfunc();
        PUTCHAR(ch);
    }
}

错误的是红色字体的不存在函数errorfunc,我们make一下整个工程,看看怎么报错:

还是看蓝色底纹部分,注意第一个单词:linking……,然后后面跟着出错信息,这次是一个链接器错误!

发现了吗?大家经常说的编译器报错,其实是几个不同的东西在报错,编译器、汇编器、链接器都会报错,那你可能会问,知道这个有啥用呢?当然是有用的,比如汇编器报错,基本跟C语言没关系,基本可以断定是汇编语言语法错误或者是嵌入C语言的汇编语言出错(在C中嵌入汇编是一种非常有效的编程手段);如果是链接器报错,那就基本跟C语言语法无关,不是你的C语法上出错,很可能是你调用了不存在的函数或者链接器脚本写错了,或者使用了不存在的标号Symbol;而只有编译器报错,才总是你的C写的有问题,而我见过很多程序猿,看见报错就马上在自己的C代码里面翻来覆去的找,结果根本找不到错误,所以看到错误的第一反应是看看是谁报错?汇编器、编译器、还是链接器,这样可以排除掉N多可能性,可以帮助你迅速排查错误,以上示例代码很简单,大家可以一眼就看到错误,但是如果你的代码十分巨大,内部代码调用关系十分复杂的情况下你就会发现这个技巧相当有效。

2、你就是老板,当然要学会看工作报表:

我就是个写代码的,不是BOSS,干好活就得了。但是做开发环境的可不这么想,对于IDE来说,他们才是干活的,你是告诉他们怎么干活的老板,他们干了哪些事情,会通过报表的形式汇报给你,这就是编译器生成的中间文件,要当一个好老板当然得会看报表,所以一个合格的写代码的要学会看编译器的中间文件。

看下图……:

这是一个LPC5408的hello world例程由编译器生成的中间文件,这些就是你的“预处理器员工”、“编译器员工”、“汇编器员工”、“链接器员工”的工作报表。我们来一起看一下:

首先是扩展名为“.i”的预处理器文件,这货几乎很少用到,基本就是把C文件中用到的头文件内容导入到C语言中,如果头文件部分有问题,是比较容易找到问题的,很少会需要“.i”的协助,这里就不列出来看了,大家有兴趣自己打开这种文件看一下。

然后是“*.lst”和“*.txt”编译器&汇编器的报表文件,也很少用到,这里略过。

最后是真正有用的链接器描述文件“*.map”非常有用,绝对是你的“核心员工”的工作报表,也是最复杂的一个,工程配置里面可以看到其配置项,非常的多,在工程配置的Listing选项卡里面,如下图:

整个*.map文件也是巨长,里面大概的内容有:

(1)标号表

链接器生成的所有标号,包括所有函数名&全局变量名称都可以生成标号,每一个标号的地址以及其占有的存储空间情况。

(2)存储空间占用情况

这里只列一部分,map文件里面更详细,连每一个函数的占用情况都会列出来。

(3)所有被链接(不是编译)的函数列表及其地址,没有被链接的也有,如果你有兴趣看的话

如下图,截取了一小部分

但这些东西具体有什么用,我们会在以后的文章中大量涉及,这里很难单独说清楚,先打下一个基础,我们以后的文章会经常涉及这个map文件,如果你真的把这个文件看明白了并能熟练应用,那么恭喜你,你就解锁了一个重要技能包。

这里对编译器部分介绍很简单,以后其他文章里面我们会深入探讨,然后我也打算写一篇介绍GCC/GNU工具链用于嵌入式开发的文章,那是一片更广阔的天地。

原本打算写一篇深入介绍编译器原理&应用的文章,但写到这里还是写不下去了,主要是相互依存的点很多,避开这些点单独谈编译器原理&应用是非常不现实的。想来想去,这些体系性的东西还是要循序渐进,以后随着我们解锁了更多的技能包我们再会深入探讨这部分内容。

猜你喜欢

转载自blog.csdn.net/zuixin369/article/details/86508813