C语言扩展属性section——数据段分析

C语言扩展属性section——数据段分析

  • 背景

    在STM32项目开发过程中,我们有需要进行单元测试,所以引入了一个内源的测试框架,这个测试框架非常简洁,当然功能也比较简陋,不过,就这样的框架居然可以直接编写测试用例,而不用考虑如何调用的问题,于是我对此较为感兴趣,研读了一下源码,发现居然是使用GUN的扩展属性__attribute__((section(“”))),于是本文将对此进行展开分析。

1. 网上的现有资料

  • 通过在网上搜索相关的资料,发现网上对这个扩展属性的讨论相对较少,首先是GNU官方手册的资料,官网上对此描述较为简单,只是谈了修饰变量时会将变量放置在单独的数据段中,还有应该修饰全局变量而不是局部变量,反而在CSDN中,有一篇文章对这个扩展属性的讨论更为深入,在这篇文章中有谈到用__start_+段名 和 __stop_+段名来声明外部变量,这两个变量的地址就是数据段的起始地址和结束地址。

  • 这篇文章中也提到了两个问题,自定义段中是否可以存放其他类型的数据? 若可以存放其他类型的数据,这个自定义段的长度是通过什么方法获得的?第一个问题自然是可以的,毕竟是数据段,当然没有必要放入不同的数据类型,不同的数据类型完全可以道不同的数据段中,第二个问题虽然没有资料考证,这个长度应该就是数据的起始地址和结尾地址指针所解析的数据。

2. Demo程序

  • 代码如下
#include <stdio.h>

int x __attribute__((section("mydata"))) = 10;
int y __attribute__((section("mydata"))) = 20;
int z __attribute__((section("mydata"))) = 30;
extern int __start_mydata;
extern int __stop_mydata;

void test()
{
    int *ptr = &__start_mydata;
    printf("ptr[0] = %d\n", *ptr++);
    printf("ptr[1] = %d\n", *ptr++);
    printf("ptr[2] = %d\n", *ptr);
}

int main(const int argc, const char *argv[])
{
    test();
    return 0;
}
  • 运行结果

    在这里插入图片描述

3. 使用 _attribute_((section(“”)))的场景

  • linux内核模块初始化,在学习linux内核模块开发的时候,就发现有在模块的init和exit函数中就会声明这个数据段,当时并不理解这里声明数据段的用法,这里也有一篇文章讲解了Linux内核init的实现,但是这篇文章里讲解的起始地址和末尾地址是通过链接脚本来定义给出的,并不是通过start和end两个外部变量和获取的。

    【精选】利用attribute((section()))构建初始化函数表与Linux内核init的实现-CSDN博客

  • 在测试框架中的使用,就如上面所说,在测试框架中,使用这个扩展属性是非常便捷的,常见的测试框架,比如gtest或者python中的unittest都是直接编写测试用例,而不用考虑测试用例的调用,如果像unity那样的单元测试框架,需要手动去调用测试用例,那边如果编写测试用例的人员遗漏了一些测试用例,那么测试时并没有相应的提示,测试结果将会失真。

  • 其实,这个扩展属性,和start和end两个外部变量,就起到注册的作用,因为在C语言中,不能在全局阶段在一个数组中填写数据,所以引入这个机制就可以有数组的注册机制,如demo程序里ptr数组的三个元素一般。

4. 编译优化对此的影响

  • 开启编译优化和不开启编译优化对数组排列的顺序有一定的影响

    在这里插入图片描述

5. 在嵌入式领域使用的问题

  • 之前使用stm32平台上的测试框架时,就发现一个问题,运行后发现没有测试用例,后来通过排查发现是由于扩展的数据段不属于.data数据段,而需要进行初始化的数据都在.data段里,.data数据是存储在bin程序文件里,待mcu运行后会从flash中拷贝到ram里,这个拷贝动作是在startup启动文件中定义的,所以要解决这个问题,就要把数据拷贝到ram中,这里可以通过修改启动文件中拷贝的结束地址由_edata修改为_sbss,或者再写一段汇编语句将此数据段拷贝到ram中。

在这里插入图片描述

6. 总结

  • 综上,使用_attribute_((section(“”)))这个扩展属性对于需要由注册机制的需求非常友好,比如linux内核初始化,和单元测试用例的执行,在嵌入式中由于链接脚本的问题对于数据拷贝会由遗漏的情况,所以需要在启动文件上做出相应的调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值