gcc - 静态 C 变量未初始化

标签 gcc static linker x86 initialization

我有一个未初始化的文件级静态 C 变量。

const size_t VGA_WIDTH = 80;
const size_t VGA_HEIGHT = 25;
static uint16_t* vgat_buffer = (uint16_t*)0x62414756; // VGAb
static char vgat_initialized= '\0';

特别是,vgat_initialized 在第一次被访问时并不总是 0。 (当然,这个问题只出现在某些机器上。)

我正在尝试编写自己的操作系统,所以我很确定这是我的链接器脚本的问题;但是,我不清楚在链接器生成的图像中应该如何组织变量(即,我不确定这个变量是否应该放在 .data 中,.bss,其他一些部分,等等)

VGA_WIDTHVGA_HEIGHT 按预期放置在 .rodata 部分中。 vgat_buffer 被放置在 .data 部分,正如预期的那样(通过将此变量初始化为 0x62417656,我可以清楚地看到链接器将它放在哪里生成的图像文件。)

我不知道 vgat_initialized 应该去哪里。我在下面包含了汇编文件的相关部分。据我了解, .comm 指令应该为数据部分中的变量分配空间;但是,我不知道在哪里。查看链接器的映射文件也没有提供任何线索。

有趣的是,如果我将初始化更改为

 static char vgat_initialized= 'x';

一切都按预期工作:我可以清楚地看到变量在生成的图像文件中的位置(即,我可以在图像文件的 hexdump 中看到 x)。


从 C 文件生成的汇编代码:

.text
.LHOTE15:
    .local  buffer.1138
    .comm   buffer.1138,100,64
    .local  buffer.1125
    .comm   buffer.1125,100,64
    .local  vgat_initialized
    .comm   vgat_initialized,1,1
    .data
    .align 4
    .type   vgat_buffer, @object
    .size   vgat_buffer, 4
vgat_buffer:
    .long   1648445270
    .globl  VGA_HEIGHT
    .section    .rodata
    .align 4
    .type   VGA_HEIGHT, @object
    .size   VGA_HEIGHT, 4
VGA_HEIGHT:
    .long   25
    .globl  VGA_WIDTH
    .align 4
    .type   VGA_WIDTH, @object
    .size   VGA_WIDTH, 4
VGA_WIDTH:
    .long   80
    .ident  "GCC: (GNU) 4.9.2"

最佳答案

编译器当然可以符合他们自己的部分名称,但使用我们从特定编译器中知道的常见 .data、.text、.rodata、.bss,这应该放在 .bss 中。

但这不会以任何方式自动将其归零。需要一种机制,有时取决于您的工具链,工具链会处理它并创建一个二进制文件,除了.data,.rodata(当然还有.text)被填充将在二进制文件中填充.bss。但取决于一些事情,主要是这是一个简单的 ram only 镜像,是链接描述文件中的一个内存空间定义下的所有内容。 例如,您可以将 .data 放在链接器脚本中的 .bss 之后,并根据您使用的二进制格式和/或转换工具,您最终可能会在没有任何其他工作的情况下将二进制中的内存归零。

通常,尽管您应该期望使用特定于工具链的(链接器脚本是特定于链接器的,而不是假定对所有工具都通用)机制来定义 .bss 从您的角度来看的位置,然后从链接器进行某种形式的通信它从哪里开始以及大小,该信息由 Bootstrap 使用,在这种情况下, Bootstrap 将其归零,并且人们可以假设它始终是 Bootstrap 将 .bss 归零的工作,当然也有一些异常(exception)。同样,如果二进制文件打算在只读媒体(rom、flash 等)上,但 .data 和 .bss 是读/写的,则您需要在此媒体上拥有完整的 .data,那么必须有人将其复制到它在 ram 中的运行时位置,而 .bss 是其中的一部分,具体取决于工具链以及您如何使用它,或者起始地址和大小位于只读媒体上,并且有人必须在某些时候将该空间归零 pre-main() .这又是 Bootstrap 的工作。设置堆栈指针,如果需要移动 .data,零 .bss 是 Bootstrap 的典型最小作业,您可以在特殊情况下缩短它们或避免使用 .data 或 .bss。

因为链接器的工作是从被链接的对象中获取所有小的 .data 和 .bss(和其他)定义,并根据用户的指示(链接器脚本、命令行、该工具使用的任何内容)将它们组合起来,链接器最终知道。

在 gcc 的情况下,您使用我称之为在链接描述文件中定义的变量,链接描述文件可以用匹配的变量/标签名称为汇编程序填充这些值,这样就可以使用通用 Bootstrap ,并且您不需要做更多的工作。

像这样,但可能更复杂

MEMORY
{
    bob : ORIGIN = 0x8000, LENGTH = 0x1000
    ted : ORIGIN = 0xA000, LENGTH = 0x1000
}

SECTIONS
{
   .text : { *(.text*) } > bob
   __data_rom_start__ = .;
   .data : {
    __data_start__ = .;
    *(.data*)
   } > ted AT > bob
   __data_end__ = .;
   __data_size__ = __data_end__ - __data_start__;
   .bss  : {
   __bss_start__ = .;
   *(.bss*)
   } > bob
   __bss_end__ = .;
   __bss_size__ = __bss_end__ - __bss_start__;
}

然后你可以将它们拉入汇编语言 Bootstrap

.globl bss_start
bss_start: .word __bss_start__
.globl bss_end
bss_end: .word __bss_end__
.word __bss_size__
.globl data_rom_start
data_rom_start:
.word __data_rom_start__
.globl data_start
data_start:
.word __data_start__
.globl data_end
data_end:
.word __data_end__
.word __data_size__

然后编写一些代码来根据您的设计需要对它们进行操作。

您可以简单地将类似的东西放在汇编语言文件中的链接中,而无需其他代码使用它们并汇编,编译其他代码和链接,然后您喜欢的反汇编或其他工具将向您显示链接器生成的内容,调整直到如果您满意,那么您可以编写或借用或窃取引导代码来使用它们。

对于裸机,我不希望我的代码完全符合标准,没有任何 .data 并且不希望 .bss 为零,因此我的 Bootstrap 设置堆栈指针并调用 main,完成。对于操作系统,您应该遵守。工具链已经为 native 平台解决了这个问题,但是如果您使用自己的链接器脚本和 boostrap 来接管它,那么您需要处理它,如果您想为现有操作系统使用现有的工具链解决方案,那么...... .done...就这样做吧。

关于gcc - 静态 C 变量未初始化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43922511/

相关文章:

c - 如何在 C 中实现 Go 的 defer() 以便允许声明变量?

c++ - 错误 "undefined reference to ' std::cout'"

c++ - 模板类中的静态成员

C++ 链接和 COM 注册问题

linux - 在不使用 yum 的情况下在 AWS EC2 上安装 gcc?

c++ - gcc 警告“没有声明任何东西

java - 静态变量是否位于堆上的永久生成空间上

java - 如何修复非静态变量 this 无法从具有内部类的静态类引用?

c++ - 最小 "extern"使用测试用例的未定义​​引用问题

HTML 下载链接