程序本身如何知道自身大小?这是鸡生蛋还是蛋生鸡的问题

开发 前端
一般来说,const 声明的函数将放在 RO-data 区。全局(或局部静态)未进行初始化(或初始化为0)的变量放在 ZI-data 区,当然栈(stack)也会放在 ZI-data。

 [[351056]]

本文转载自微信公众号「鱼鹰谈单片机」,作者鱼鹰Osprey。转载本文请联系鱼鹰谈单片机公众号。

有些情况下,我们可能需要知道程序本身占用的空间大小,一般来说,我们可以从编译结果中看到我们的程序到底有多大(不包含 ZI-data 部分):

还可以通过生成的bin文件大小来查看,这个 bin 文件就是不需要经过任何转化直接烧录到 flash 的数据,当然它也不包含 ZI-data,因为它初始化全是 0,只需要在程序开始时清零即可(该工作由库函数自动帮你完成),没必要保存到 flash中浪费空间。

Bin 文件生成方法(fromelf --bin !L --output hello.bin):

我们可以看一看这些数据的空间分布:

一般来说,const 声明的函数将放在 RO-data 区。全局(或局部静态)未进行初始化(或初始化为0)的变量放在 ZI-data 区,当然栈(stack)也会放在 ZI-data。

MDK的编译器为我们提供了一些内置变量,这些变量是由编译链接之后自动生成的,我们可以直接在程序中获取,那么有哪些变量,又该如何获取呢?

据鱼鹰了解,MDK 内置了如下变量(有些变量在有些情况下表示相同值):

  1. Image$$ER_IROM1$$Base; 
  2. Image$$ER_IROM1$$Limit; 
  3. Image$$ER_IROM1$$Length;   // 获取总大小 
  4.  
  5. Load$$LR$$LR_IROM1$$Limit; // 这个和上面的效果一样 
  6.  
  7. Image$$ER_IROM1$$RO$$Limit;  // 这个和上面的效果一样 
  8.  
  9. Image$$RW_IRAM1$$Base; 
  10. Image$$RW_IRAM1$$Limit; 
  11. Image$$RW_IRAM1$$Length; 
  12.  
  13. Image$$RW_IRAM1$$ZI$$Base; 
  14. Image$$RW_IRAM1$$ZI$$Limit; 
  15. Image$$RW_IRAM1$$ZI$$Length; 

Image$$ER_IROM1$$Length 对应于 Code + RO Data 的大小,而 base 和 limit 为这段空间的起始和结束地址。

Image$$RW_IRAM1$$Length 对应于 RW-Data 的大小,而 base 和 limit 为这段空间的起始和结束地址。

Image$$RW_IRAM1$$ZI$$Length 对应于 ZI-Data(包括STACK) 的大小,而 base 和 limit 为这段空间的起始和结束地址。

那么我们该如何使用这些变量呢?下面鱼鹰提供C语言和汇编两个版本:

  1. // C语言 
  2. extern int Image$$ER_IROM1$$Base; 
  3.  
  4. unsigned int base = (uint32_t)&Image$$ER_IROM1$$Base 
  5.  
  6. ; 汇编   
  7. IMPORT |Image$$ER_IROM1$$RO$$Base| 
  8. IMPORT |Image$$ER_IROM1$$RO$$Limit| 
  9.  
  10. IMPORT |Image$$RW_IRAM1$$RW$$Base| 
  11. IMPORT |Image$$RW_IRAM1$$RW$$Limit| 
  12.  
  13. IMPORT |Image$$RW_IRAM1$$ZI$$Base| 
  14. IMPORT |Image$$RW_IRAM1$$ZI$$Limit| 

首先使用 extern 关键声明这个外部变量,int 类型。

但是你通过它的使用方式你会发现,这个变量是不可以直接使用的,需要把对它进行取地址,而它的地址才是你想要的数据。

事实上,这些内置变量本身是不占用空间的的,和用户声明的变量是不同的。

我们可以这样理解,这些变量存放在某个地址空间,这个地址就是它要表示的值(含义),但因为它的特殊性,所以它不占用空间,只能采用取地址的方式获取它代表的值。

通过这些内置变量,原本我们计算 Code + RO-data + RW-Data 的值就可以得到 bin 文件的大小,但当你查看 bin 文件大小之后,你会发现 bin 文件小于该值,这是怎么回事?

通过分析 map 文件我们可以看到如下信息:

你会发现,实际的bin文件包含的 RW 数据大小并不是 372,而是 56,也就是说,有一部分数据并没有被包含进 bin 文件用于拷贝(可能和 RW 的数据有部分初始值为 0 有关而被压缩了)。

具体原因,鱼鹰也没搞懂,但是按照之前的变量来看,我们无法准确获得 bin 文件的大小,只能说获取到一个比 bin 文件大小稍大的数字。

原本以为鱼鹰不可能获得准确的 bin 文件大小了,一个偶然的map文件查看,让鱼鹰看到了这么个变量:

好奇的鱼鹰对它进行了比较深入的研究,发现我需要的bin文件(程序)大小就隐藏在这里。

通过分析,鱼鹰发现这个地址包含的8个数据含义如下:

通过图中数据,减去flash 的基地址,我们就可以获取到 0x2FE8,即我们 bin 文件实际大小。

而另外两个函数地址,原本鱼鹰并不知道这些值是干什么用的,还是通过分析 map 文件,才最终确认是两个函数的地址,至于到底干什么用的,鱼鹰就不是很清楚的,不过看名字也知道应该和变量初始化有关系。

以上就是鱼鹰分享的关于程序本身获取自身大小的知识点,至于你用这些数据干啥用那就是你的事情了。

原本鱼鹰是准备获取到bin的大小后通过指定地址的方式在bin文件最后放一些数据的,但是这就真的变成鸡生蛋蛋生鸡的问题了,看来通过内置变量的方式是不行了,不知道各位道友有没有好的方法让编译器自动在 bin 文件的后面添加想要的数据呢(非第三方工具)?

 

责任编辑:武晓燕 来源: 鱼鹰谈单片机
相关推荐

2018-08-23 05:07:36

神经架构深度学习神经网络

2020-10-19 14:05:50

芯片芯片人才芯片行业

2010-04-30 16:08:20

Unix内核

2020-10-26 11:33:45

编程语言编译器软件

2013-07-22 10:35:32

用户需求技术创新产品

2016-11-02 00:22:13

数字化改造数字化

2018-09-27 10:26:12

物联网

2012-06-18 15:21:38

Facebook竞价广告

2019-11-26 09:21:49

区块链比特币虚拟货币

2020-04-21 15:54:07

戴尔

2018-10-17 09:25:22

2019-11-07 22:00:22

程序员代码规范

2012-08-07 09:29:09

程序员编程

2013-04-12 02:01:55

微信公众平台接口开发

2022-04-07 09:29:04

文件系统硬盘操作系统

2019-09-29 10:45:46

C语言CPU编译器

2021-10-01 00:02:54

CHAR VARCHARMYSQL

2010-03-18 15:15:08

Java线程池

2019-12-06 11:22:00

中国电信
点赞
收藏

51CTO技术栈公众号