编译工具集详解(三)| 学习笔记

简介: 快速学习编译工具集详解。

开发者学堂课程【剑池系列开发工具 :编译工具集详解(三)】学习笔记,与课程紧密联系,让用户快速学习知识。

课程地址:https://developer.aliyun.com/learning/course/734/detail/13097


编译工具集详解(三)

五、链接

链接的过程,以下图为例,

image.png

main 函数是将所有的 .o 文件、库合并,因为例子用到 pre,是包含在 unker,.o 库里,所以 unker,将用到 .o 加上库通过连接器,最后可以连起来执行程序,在深入讲链接的过程之前,先讲可执行文件的一个结构。在可执行文件里面不只是很多的二进制代码。还有二次结构去区分所有的 o 文件,最后链接出来的可执行文件,根据概念叫做去储存代码。

一般情况下,代码可以分为三个段,第一个段是 text 段,也是代码段。是指令的跳转,和逻辑计算指令,都是储存在text 里的。第二个是 data 段,是储存初始化全局变量的数据,最后一个是 bss 段,储存未初始化或者初始化为零的全新变量。要将 data 段跟 bss 段区分开,因为未初始化或者初始化为零,内容都是零,在文件里只需要记住起始地址和结束地址,不需要在文件里面记录 n 多个0。将它独立出来,可以节省这部分空间,只需要知道里面存的数据是零即可。所以 o 文件是这样一个层次结构。电源接器主要是将一些 o 文件,以一定的编排规则最后编排到alf文件里,形成最终的一个段结构。对一些用户来说,都是不可见的,是黑盒的,但是很多 MCU 或者嵌入式的用户是需要自己去编排,去定制,去指定它的储存地址,组成地址,所以链接提供了一个功能,提供了一个接口:链接脚本,链接脚本可以让用户自定义的去按自己想要的一个规则去组合,去编排,去生成最后的可执行文件。使用方法:在链接或者编译的时候,加一个参数 -t,加一个链接脚本的名称,可以使用链接脚本。举例说明:

image.png

左边可以看到一些比较基础的一些语法,第一行,entry,表示程序的入口,地址,第二 memory 是表示程序的内存空间的划分,这是用来储存 section 使用的,想将 section 储存在哪个内存空间,要先定内存空间,再最后用 section 的时候才可以指定到内存空间。下面两个是定了两个符号,符号是可以在代码里面被引用的。像下面定义了一个起始地址还有一个 global point,是里面特有的。有一个 gbg 存器,它会固定位置。最后一个部分是 section,是用来怎么划分,怎么组合,怎么拼凑,放在什么地方是用下面这些语法来指导的,像第一个 .text:,表示的是最终的 a.out 的 .test,里面包含什么内容,是后面的大括号里面的内容 .text*,是所有输入文件的 .text 复制到最终的目标文件的 .text中,最后〉ROM 放到一个前面定的0地址到40000。内存区域,也是推荐,如果还有需要写东西的话,也可以具体去查手册,因为链接文件是比较难写的,可能会遇到很多的问题,可以使用map文件去查看写的是不是达到想要的目的。map 文件怎么生成,可以加一个选项,-wl,-map=a.map,证明会生成一个 map 文件。

举一个例子,介绍 map 文件大致的一个组成部分,主要有四块,关键字都是在map文件里面可以匹配到的,

第一个是 Archive member included to satisfy reference by file(symbol),在库文件(a)中因某个 symbol 而被包含进来的 object 文件列表 ,调用了 reference 函数,要链接里面的相关的所有的.o文件,在第一块全部都列出来了,

第二个是 Memory Configuration 内存区域,对应链接脚本中的 MEMORY 区域,

第三是 Discarded input sections,因- -gc-section 而被剔除的段,列举了哪些 section 是被 --gc 判断而被剔除的段,并且列出来,最关键的一个是 Linker script and memory map,链接器依据链接脚本生成可执行文件的过程,下图是一个例子得到的 map 文件,

image.png

.text 文件是对应到链接的脚本,写的 .text:,那*也是对应到之前大括号里面的*,先将写在链接脚本里的语法给列出来。后面这一列是被语法匹配到的输入文件的 section,会将所有的文件的 section 都列出来,会将这些 section 的起始地址,结束地址,里面的符号,是什么地址都会列出来。写链接脚本是非常有用的,可以去使用。一些用户经常会有需求,想将代码段放到特定的内存区域去,或者是一个函数,一个变量怎样放到一个特定特定的内存地址上去,推荐一个方法:将函数或者变量加一个 attribute 属性。属性的一个指令是 section,放到一个特定的 section中,自定义的 section 中,在链接里去定义一个 memory,是想要放到哪一个的地址上,再通过讲到的一个链接用法将它放到 memory origin 里面去,语法是〉origin。  

从 c 代码或者高级源代码已经变到想要的elf程序,程序看来是一个机器代码,很难阅读,但包含非常多信息,接下来介绍编译套件里面的 BINUTILS 工具集。

 

六、BINUTIL

最后编出来的 elf 文件,会有一些隐含的一些信息,可以用工具集去解析或者去做一些操作。工具集 Binutils 是 GNU Binary Utilities 的简写,一个二进制的工具集,内容很丰富,介绍的某些部分像 a.aut,像 readelf,工具去读 elf 所有的工具可以读的信息。如下:elfhead 是包含了一些标识的一些信息,readelf 的一个文件,是有标识的小端等各种信息,abi 信息可以看到。section 是最后编出来的段,它的大小,属性可以表示。第二个工具是 Objdump,是反汇编器,可以将二进制代码反汇编成代码,第三个是 ar,用来将 o 文件打包成库来使用。nm 工具是用来读符号。举例如下:

image.png

读了很多符号,每个符号有自己的属性,列举如下,代码段,是全局变量,是只读的,是 bss 段的,都有属性去表示,名称加地址,工具有时被用来读 c++,因为 c++是经过编译器改造的,比较难阅读,也可以用工具加一个参数,会将它还原成源代码的 c++ 符号函数,下一个工具是 strip,是剔除程序,程序的一些信息比方说调适信息,符号信息,它可能影响调试符号信息,但它不会影响执行信息,所以有些用户要发布包或者程序的时候,往往会将它切掉,好处是会变小,而且也不会保留一些可能认为是敏感的符号信息。size 是读 elf 的,有统计程序大小的需求,可以用size 工具,会将 rom 的占有情况给打印出来。最后一个是 objcopy,可以将 elf 工具变成 bin 或者 hex 文件,通过工具进行。

 

七、Q&A

问:编译器在哪里查到?

答:在 occ 平台上可以下载。演示如下:登录 occ.t-head.com 官网,可以看到需要的编译器,技术部落-资源下载-工具,可以下载二进制包,技术文档在软件开发指南,其中包含了很多课堂内容,任何疑问可以查询开发指南。

问:内存有分类别吗?每个类别的内存中可以在窗口中打开查看吗?

答:内存,硬件或者芯片都是分类别的,对应到程序也提供分类。以此区分内存的属性,类别,程序对应到代码的属性,可以通过 readelf 工具去读内存的属性,加 -ls a.out,使用 readelf 工具读 a.out,的内存和代码的属性。以seciton 为单位,有一个 Flg,即 flag, 代表属性。像. text 段,a 代表是可分配。x 代表可执行的属性。相对于数据段缺少了一个 w,即 write,即可写的属性,数据段是不可写的,所以只有 AX 属性。对应的 .data 段是不可执行,但是可读,可写,所以有 w 。可以用 readelf 工具去确定内存的属性和类别。

问:想将某些变量定定义在 ROM 任务区域内,非掉电状态下不能修改,并且在升级会复位的过程中不能改变并会用到某些变量,所以变量的地址在第一次确定后,在后续版本中的地址要和第一次的一样,但这些变量又分布在不一样的c文件中,除了给每个变量确定的地址,在编译中是否有方法达到这个效果?

答:利用好链接脚本可以很好的解决问题,PPT 中有一个将变量放到特定 section 里的例子。.text 是一个特殊section,输入各个.o,对应到各个.c,可以用通配符去匹配 section 名称,可以把所有变量都定义到同一个名字的section 名称里。在链接器里把 section 名称加入 bar,加*,代表所有 bar,section 名称都放入这里,不管 c 文件数量,只需要把想要放在区间的变量都加上 stack,就会放到一个特定的空间,*号是匹配的所有的文件。

问:支持哪些微控制器?

答:平头哥所有的芯片都是支持的。

问:支持什么长度?

答:平头哥提供的芯片目前只有32跟64位两种,数据位宽是全都支持,如果是一个 CPU 的位宽,目前 CPU 只有32位和64位。

问:剑池开发工具是否可以支持其他厂商的一些risk的内核?

答:支持。它其实是增量的,即增加优化跟指令集,但是本身的开发也是支持的。

问:可支持的操作系统有几种?

答:如果跟操作系统耦合的编号键,目前是支持 Linux,如果不耦合,则elf不需要和系统做绑定,所以elf套件,都可以用到。

问:支持常用的编辑器吗?

答:支持,因为我们只提供编译工具包。编译有接口提供,需要配置编译器使用,插件暂时没有。

问:该编辑器有哪些提升?包含目标文件优化吗?

答:横向对比不好对比,因为 kio 跟此编译器不一样。mcc 是公司自己开发的一套编译器,此编译器目前是基于 gcc开发的编译器,自我做了很多优化,与 kio 横向对比很难,因为它们不是介于统一平台开发。

问:O3 如果优化的比 O2 好运行时应该是不会出现性能波动吧?

答:会不会出现性能波动跟编译选项没有关系。一般情况下不会出现性能波动,因为编译好的代码已经生成,运行时不可能去改变代码,除非一些意外,这时会有一些性能波动的情况,但是比较少。

问:怎么知道 main.i 中每一行的代码是哪里定义宏?

答:.i 文件会把 include 的头文件解析出并打印出来,所以它是以#号开头的一个字符。提示从哪里展开,没有精确的标识,但可以根据展开的方式去判断宏是哪里展开。

问:从 main.s 文件反编译成 main.i,手动修改汇编代码,可以看到对应的 c 代码效果吗?

答:不可以,因为汇编信息丢失,是做不到的,做不到变成c代码的。除非是调试信息的可执行文件反汇编时会把源码信息打出来,这要基于源码是反汇编时当前存在的情况下,但不可能将汇编重新变成 c 语言,这需要人工去做很多东西。

档案有很多工具,但是很难达到百分百,题结构不可能存在这样的一个工具,像目前主流的X86有,但很多是没有,而且返回编没有代码。不可能完全直接使用,只是一个大概的代码。

相关文章
|
Ubuntu Linux Windows
Linux源码阅读工具:ctags
Linux源码阅读工具:ctags
152 0
|
自然语言处理 编译器 Linux
【Linux篇】第十一篇——动静态库(动静态库的介绍+动静态库的打包与使用)
【Linux篇】第十一篇——动静态库(动静态库的介绍+动静态库的打包与使用)
【Linux篇】第十一篇——动静态库(动静态库的介绍+动静态库的打包与使用)
|
存储 编译器 开发工具
编译工具集详解(二)| 学习笔记
快速学习编译工具集详解。
93 0
 编译工具集详解(二)| 学习笔记
|
编译器 开发工具 C语言
编译工具集详解(一)| 学习笔记
快速学习编译工具集详解。
61 0
编译工具集详解(一)| 学习笔记
|
运维 算法 Cloud Native
第三课(三)|学习笔记
快速学习第三课(三)
142 0
第三课(三)|学习笔记
|
缓存 NoSQL 搜索推荐
第三课(二)|学习笔记
快速学习第三课(二)
94 0
第三课(二)|学习笔记
|
存储 SQL Oracle
第六课(三)|学习笔记
快速学习第六课(三)
93 0
第六课(三)|学习笔记
|
存储 SQL 算法
第六课(二)|学习笔记
快速学习第六课(二)
92 0
第六课(二)|学习笔记
|
负载均衡 搜索推荐 应用服务中间件
第三课(一)|学习笔记
快速学习第三课(一)
109 0
第三课(一)|学习笔记
|
存储 Oracle 关系型数据库
第六课(一)|学习笔记
快速学习第六课(一)
89 0
第六课(一)|学习笔记