zoukankan      html  css  js  c++  java
  • C语言引用连接脚本lds中的符号——清除bss段,c实现方式

    之前我们的启动文件清除bss和拷贝都是通过汇编的方式的实现,但是,我们能够使用C语言,就不使用汇编:

    先看连接脚本:

    SECTIONS
    {
        . = 0x30000000;
    
        __code_start = .;
    
        . = ALIGN(4);
        .text      :
        {
          *(.text)
        }
    
        . = ALIGN(4);
        .rodata : { *(.rodata) }
    
        . = ALIGN(4);
        .data : { *(.data) }
    
        . = ALIGN(4);
        __bss_start = .;
        .bss : { *(.bss) *(COMMON) }
        _end = .;
    }

    现在我们编写C语言的copy和clean函数,但是在我们的c程序中,需要访问连接脚本中的符号。

    先看代码,稍后解释:

    void copy2sdram(void)
    {
        /* 要从lds文件中获得 __code_start, __bss_start
         * 然后从0地址把数据复制到__code_start
         */
    
        extern int __code_start, __bss_start;
    
        volatile unsigned int *dest = (volatile unsigned int *)&__code_start;
        volatile unsigned int *end = (volatile unsigned int *)&__bss_start;
        volatile unsigned int *src = (volatile unsigned int *)0;
    
        while (dest < end)
        {
            *dest++ = *src++;
        }
    }
    
    
    void clean_bss(void)
    {
        /* 要从lds文件中获得 __bss_start, _end
         */
        extern int _end, __bss_start;
    
        volatile unsigned int *start = (volatile unsigned int *)&__bss_start;
        volatile unsigned int *end = (volatile unsigned int *)&_end;
    
    
        while (start <= end)
        {
            *start++ = 0;
        }
    }

    start.S部分展示:

    先不管连接脚本的引用,我们现在讨论一个问题,我们这里使用bl,是相对跳转,我们必须认识到一个问题,此时我们的sdram虽然初始化好了,但是此刻我们sdram里面是没有程序的,需要先进行重定位,把nor flash的内容全部copy到sdram,所以此时(还没有拷贝的时候) copy2sdram是在nor flash里面执行的,那么使用bl也就可以理解了。但是clean_bss这个函数,到底应不应该使用bl呢?似乎好像我们完成了重定位,让text段,rodata段,data段都重定位到了sdram,可是,sdram这上面几个段之后才开始存放bss段,而bss段的变量是为0值得,不会放在bin文件中,如果这里不使用bl clean_bss,而使用 ldr pc,=clean_bss绝对跳转,程序没有输出,因为此时没有保存lr寄存器的值。

    但是,如果非要使用ldr绝对跳转呢?可以这样,增加一个标号:

         ldr pc,=m
    
    m:
         bl clean_bss
         bl  main  /* 绝对跳转, 跳到SDRAM */

    增加标号m,绝对跳转到m处执行,这个时候程序运行于sdram了,后面使用bl指令即可。

    为什么想要这样呢?

    严格来说,重定位完毕,我们的程序就可以在sdram中运行了,而我们之前的程序采用

    bl clean_bss

    ldr pc,=main

    这样会导致清除bss段的代码还是运行于nor flash上,而并非出于sdram上,所以我们采用绝对跳转的方式。清除bss段。

    回到C语言引用连接脚本的方法:

    参考连接(请点击)

    在编译阶段,会有一个叫做符号表的东西,

    c程序中不保存lds中的变量,但是万一c程序需要使用lds的文件,就可以通过编译程序时的符号表来引用。

    上图已经显示了,C语言是变量名,然后变量地址,而连接脚本lds中,是符号名,然后它的值。

    现在,我们这样对比,我们在C语言中要取一个变量的地址,是使用 &符号,就得到符号表中对应的地址了,

    同样的道理,在c中引用连接脚本的符号,我们需要的是lds符号的值,也还是通过 &符号,只不过,此时取地址符 & +lds的符号,得到的是符号的值,而不是符号本身的地址了,而这符号的值,却又恰好是某个地址。(请仔细体会这个对比)

    所以,我们上面采用外部声明变量的方式。

    C函数怎么使用lds文件中的变量abc?
    a. 在C函数中声明改变量为extern类型, 比如:
    extern int abc;

    b. 使用时, 要取址, 比如:
    int *p = &abc; // p的值即为lds文件中abc的值

    声明的类型可以是int也可以char。此时类型不重要,但是,最好是声明成你想使用的那种类型。

    这是官方的参考,所以具体声明成什么类型,虽然不影响,但是最好是声明成你想使用的类型。因为在符号表中,连接脚本的符号表是符号和值,直接存储了,没有再开辟地址,在C语言使用extern声明之前,lds中的符号已经被定义在符号表中了,也就意味着,char或int类型修饰对符号的值没有影响,该值早就在符号表确定了,而我们C语言中之所以要声明一个类型,是为了方便后面要使用lds中这个符号的操作,而且C语言语法也要求标识符必须要有类型修饰。

    对lds中的符号取地址,得到就是这个符号的值,而这个值,正好代表了某个地址。

  • 相关阅读:
    Adobe flash player更新失败
    配置NAT回流导致外网解析到了内网IP
    Android下海康实时视频解码
    红米无线不稳定问题
    TP-Link路由器刷dd-wrt的linux,无线信号增强不少
    发布FTP服务,防火墙配置
    SQL SERVER 实现分组合并实现列数据拼接
    MyGeneration 数据库驱动为空
    WIND2003 安装Zend studio 报错
    TOAD FOR MYSQL 进行数据插入时乱码的解决办法---MariaDB 5.5
  • 原文地址:https://www.cnblogs.com/yangguang-it/p/8068088.html
Copyright © 2011-2022 走看看