GCC链接器,bss段符号等于零长度

GCC链接器,bss段符号等于零长度,c,gcc,linker,arm,embedded,C,Gcc,Linker,Arm,Embedded,为了理解基础知识,我编写了(或者我猜是提取了)以下c代码和链接器脚本。由此产生的二进制工作,led闪烁无问题。但是,在调试时,我发现bss\u开始和bss\u结束符号的值均为0x20000000。基本上跳过了bss零功能。当做objdump时,我可以看到 20000000 g O.bss 0000000 4定时器延迟计数 因此,该节的长度为4字节,位于0x20000000 correclty。并且至少bss\u start指向正确的内存地址。但是,bss_end应该指向0x2000004[我认为

为了理解基础知识,我编写了(或者我猜是提取了)以下c代码和链接器脚本。由此产生的二进制工作,led闪烁无问题。但是,在调试时,我发现bss\u开始bss\u结束符号的值均为0x20000000。基本上跳过了bss零功能。当做objdump时,我可以看到

20000000 g O.bss 0000000 4定时器延迟计数

因此,该节的长度为4字节,位于0x20000000 correclty。并且至少bss\u start指向正确的内存地址。但是,bss_end应该指向0x2000004[我认为],而不是0x2000000

我现在想知道,在放置*(.bss)之后,bss_结束符号是否没有增加,我认为它包含四个字节

微控制器是stm32f103rb,一种cortex-m3芯片。我正在与ARM GNU GCC合作

我的主.c文件:

#define _stackInit 0x20005000U

volatile unsigned int * const RCC_APB2ENR = (unsigned int *)0x40021018;
volatile unsigned int * const GPIOA_CRL = (unsigned int *)0x40010800;
volatile unsigned int * const GPIOA_BSR = (unsigned int *)0x40010810;
volatile unsigned int * const GPIOA_BRR = (unsigned int *)0x40010814;
volatile unsigned int * const STK_CTRL = (unsigned int *)0xE000E010;
volatile unsigned int * const STK_LOAD = (unsigned int *)0xE000E014;
volatile unsigned int * const STK_VAL = (unsigned int *)0xE000E018;

volatile unsigned int timer_delayCount;

int main() {
    // enable GIOA clock and set PB5 to output
    *RCC_APB2ENR |= (unsigned int)0x00000004;
    *GPIOA_CRL = (unsigned int)0x44244444;

    // COnfigure Systick Timer for 1 millisecond interrupts
    *STK_VAL = 0x00;
    *STK_LOAD = 7999U; //tick every 1 ms
    *STK_CTRL = 0x07;


    while (1){
        int c, d;
        timer_delayCount = 500u;
        while(timer_delayCount != 0u);
        *GPIOA_BSR = 0x20;

        timer_delayCount = 500u;
        while(timer_delayCount != 0u);
        *GPIOA_BRR = 0x20;
    }

}


// Begin and End addresses for the .bss section. Symbols defined in linker script
extern unsigned int __bss_start__;
extern unsigned int __bss_end__;

void __attribute__ ((section(".after_vectors"))) Reset_Handler (void)
{

    // Initialize bss section by iterating and clearing word by word.
    // It is assumed that the pointers are word aligned.
    unsigned int *p = &__bss_start__;
    while (p < &__bss_end__) {
        *p++ = 0;   
    }

    main();
}

void SysTick_Handler() {
    // Decrement to coutner to zero.
    if (timer_delayCount != 0u)
    {
        --timer_delayCount;
    }
}

void __attribute__ ((section(".after_vectors"))) Default_Handler(void)
{
    while(1);
}

void __attribute__ ((section(".after_vectors"),weak, alias ("Default_Handler"))) NMI_Handler(void);
void __attribute__ ((section(".after_vectors"),weak, alias ("Default_Handler"))) HardFault_Handler(void);
void __attribute__ ((section(".after_vectors"),weak, alias ("Default_Handler"))) MemManage_Handler(void);
void __attribute__ ((section(".after_vectors"),weak, alias ("Default_Handler"))) BusFault_Handler(void);
void __attribute__ ((section(".after_vectors"),weak, alias ("Default_Handler"))) UsageFault_Handler(void);
void __attribute__ ((section(".after_vectors"),weak, alias ("Default_Handler"))) SVC_Handler(void);
void __attribute__ ((section(".after_vectors"),weak, alias ("Default_Handler"))) DebugMon_Handler(void);
void __attribute__ ((section(".after_vectors"),weak, alias ("Default_Handler"))) PendSV_Handler(void);

typedef void(* const pHandler)(void);

// The vector table.
// The linker script to place at correct location in memory.
__attribute__ ((section(".isr_vector"),used)) pHandler __isr_vectors[] =
{
    //core exceptions
    (pHandler)_stackInit,   // Inital Stack Pointer
    Reset_Handler,          // reset handler
    NMI_Handler,            // NMI handler
    HardFault_Handler,      // hard fault handler
    MemManage_Handler,      // MPU fault handler
    BusFault_Handler,       // bus fault handler
    UsageFault_Handler,     // usage fault handler
    0x00,                   // reserved
    0x00,                   // reserved
    0x00,                   // reserved
    0x00,                   // reserved
    SVC_Handler,            // SVCall handler
    DebugMon_Handler,       // debug monitor handler
    0x00,                   // reserved
    PendSV_Handler,         // PendSV handler
    SysTick_Handler,        // systick handler
};
编译器命令:

/opt/gcc-arm-none-eabi-7-2017-q4-major/bin/arm-none-eabi-gcc -c -mcpu=cortex-m3 -mthumb -g blinky-interrupt.c -o blinky-interrupt.o
/opt/gcc-arm-none-eabi-7-2017-q4-major/bin/arm-none-eabi-ld -T blinky-interrupt.ld blinky-interrupt.o -o blinky-interrupt.elf

这是因为一个单位化的变量进入。如果您用0初始化它,那么它将进入
.bss

查看链接器映射文件(如果您没有它,让链接器使用
-map
)生成它),您应该会看到如下内容

.bss            0x0000000020000000        0x4 load address 0x00000000080000e0
                0x0000000020000000                . = ALIGN (0x4)
                0x0000000020000000                __bss_start__ = .
 *(.bss)
                0x0000000020000000                . = ALIGN (0x4)
                0x0000000020000000                __bss_end__ = .
 COMMON         0x0000000020000000        0x4 ./src/app/main.o
                0x0000000020000000                timer_delayCount
当链接器脚本中未指定
COMMON
时,链接器会将其放在其他位置,可能会在末尾转储

更完整的链接器脚本包含以下
.bss
部分

  .bss :
  {
    . = ALIGN(4);
    __bss_start__ = .;
    *(.bss)
    *(.bss*)
    *(COMMON)
    . = ALIGN(4);
    __bss_end__ = .;
  } >RAM
然后,
COMMON
部分将介于
之间


仅供参考,
*(.bss*)
是否包含
-fdata节
选项,当每个变量都有自己的段时,链接器可以删除未引用的段。

如果
timer\u delayCount
是第一个和最后一个对象,那么start和end肯定有相同的地址?在此上下文中,“end”是包含的
0x2000004
将是其他内容的起始地址,而不是bss的结尾。添加第二个对象来演示这一点。@Clifford:如果节中有一个四字节的对象,则起始地址和结束地址相同,那么当节中没有对象时,起始地址和结束地址是什么?@EricPostpischil:我不知道;我只是在用奥卡姆的剃须刀。我想象一个链接器可以完全删除这个部分,但这并不重要,如果没有任何引用空间,那么大小是无关的。通常,gcc实现通过(伪代码)
memset(start\u bss,0,end\u bss-start\u bss)
或者
while(start!=end){*start=0;start++}
(ARM thumb crt执行后者,尽管crt可能是用汇编语言编写的)因此,是的,如果bss_end在本例中是以4结尾的话,这是最有意义的。从我在gcc ARM系统上看到的情况来看,即使没有显式初始化为0,变量也不会有共同点。现在看一个这样的项目,共同点只是bss末尾的映射文件中的一些混乱,其中没有实际数据。@Lundin我想我们可以d使用
-fno common
进行编译以获得该行为(尽管可能是使用Keil工具)就我个人而言,默认的通用行为让我感到困惑。为什么要在全局名称范围内悄悄地合并变量的实例化,而不是出错,这对我来说毫无意义。感谢映射文件指针。通用是答案,尽管我不明白为什么objdump会显示此符号属于.bss节一般情况下是ion,而不是更具体地说是common。除了map文件之外,还有其他的东西可以看到吗?比如在链接器运行之前从对象文件中看到的?还感谢您告知“=0”仍然算作.bss。我认为将一个符号设置为任何值(甚至为零)算作初始化,即…数据节。小,但很好知道信息。@Lundin有没有
-fdata节
?@berendi没有…我认为。我认为该选项与ELF文件生成有关,有单独的节而不是池?
  .bss :
  {
    . = ALIGN(4);
    __bss_start__ = .;
    *(.bss)
    *(.bss*)
    *(COMMON)
    . = ALIGN(4);
    __bss_end__ = .;
  } >RAM