全局构造函数调用不在 .init_array 部分

发布于 2024-11-15 11:48:36 字数 636 浏览 1 评论 0原文

我正在尝试在嵌入式目标(ARM Cortex-M3)上添加全局构造函数支持。 假设我有以下代码:

class foobar
{
    int i;

public:
    foobar()
    {
        i = 100;
    }

    void inc()
    {
        i++;
    }
};

foobar foo;

int main()
{
    foo.inc();
    for (;;);
}

我像这样编译它:

arm-none-eabi-g++ -O0 -gdwarf-2 -mcpu=cortex-m3 -mthumb -c foo.cpp -o foo.o

当我使用 objdump 查看 .init_array 部分时,它显示 .init_section 的大小为零。

我确实得到了一个名为 _Z41__static_initialization_and_destruction_0ii 的符号。 当我反汇编目标文件时,我看到全局构造是在 static_initialization_and_destruction 符号中完成的。

为什么没有在 .init_section 中向该符号添加指针?

I'm trying to add global constructor support on an embedded target (ARM Cortex-M3).
Lets say I've the following code:

class foobar
{
    int i;

public:
    foobar()
    {
        i = 100;
    }

    void inc()
    {
        i++;
    }
};

foobar foo;

int main()
{
    foo.inc();
    for (;;);
}

I compile it like this:

arm-none-eabi-g++ -O0 -gdwarf-2 -mcpu=cortex-m3 -mthumb -c foo.cpp -o foo.o

When I look at the .init_array section with objdump it shows the .init_section has a zero size.

I do get an symbol named _Z41__static_initialization_and_destruction_0ii.
When I disassemble the object file I see that the global construction is done in the static_initialization_and_destruction symbol.

Why isn't a pointer added to this symbol in the .init_section?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

只为守护你 2024-11-22 11:48:36

我知道这个问题被问到已经快两年了,但我自己只是想弄清楚用 GCC 进行裸机 C++ 初始化的机制,所以我想我应该在这里分享详细信息。事实证明,网络上有很多过时或令人困惑的信息。例如,经常提到的 collect2 包装器似乎不适用于 ARM ELF 目标,因为它的任意节支持启用了下面描述的方法。

首先,当我使用 Sourcery CodeBench Lite 2012.09-63 使用给定命令行编译上面的代码时,我确实看到正确的 .init_array 部分大小为 4:

$ arm-none-eabi-objdump -h foo.o

foo.o:     file format elf32-littlearm

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
...
 13 .init_array   00000004  00000000  00000000  0000010c  2**2
                  CONTENTS, ALLOC, LOAD, RELOC, DATA
...

当我查看该部分内容时,它只是包含 0:

$ arm-none-eabi-objdump -j .init_array -s foo.o
Contents of section .init_array:
 0000 00000000                             ....

但是,还有一个重定位部分将其正确设置为 _GLOBAL__sub_I_foo

$ arm-none-eabi-objdump -x foo.o
...
RELOCATION RECORDS FOR [.init_array]:
OFFSET   TYPE              VALUE
00000000 R_ARM_TARGET1     _GLOBAL__sub_I_foo

一般来说,.init_array 指向您的所有_GLOBAL__sub_I_XXX 初始化程序存根,每个初始化程序存根都调用自己的 _Z41__static_initialization_and_destruction_0ii 副本(是的,它是多重定义的),该副本使用适当的参数调用构造函数。

因为我在构建中使用 -nostdlib ,所以我无法使用 CodeSourcery 的 __libc_init_array 为我执行 .init_array ,所以我需要自己调用静态初始化器:

extern "C"
{
    extern void (**__init_array_start)();
    extern void (**__init_array_end)();

    inline void static_init()
    {
        for (void (**p)() = __init_array_start; p < __init_array_end; ++p)
            (*p)();
    }
}

__init_array_start__init_array_end 由链接描述文件定义:

. = ALIGN(4);
.init_array :
{
__init_array_start = .;
KEEP (*(.init_array*))
__init_array_end = .;
}

这种方法似乎适用于 CodeSourcery交叉编译器和本机 ARM GCC,例如在 Ubuntu 12.10 for ARM 中。支持这两种编译器是使用 -nostdlib 而不依赖 CodeSourcery CS3 裸机支持的原因之一。

I know it has been almost two years since this question was asked, but I just had to figure out the mechanics of bare-metal C++ initialization with GCC myself, so I thought I'd share the details here. There turns out to be a lot of out-of-date or confusing information on the web. For example, the oft-mentioned collect2 wrapper does not appear to be used for ARM ELF targets, since its arbitrary section support enables the approach described below.

First, when I compile the code above with the given command line using Sourcery CodeBench Lite 2012.09-63, I do see the correct .init_array section size of 4:

$ arm-none-eabi-objdump -h foo.o

foo.o:     file format elf32-littlearm

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
...
 13 .init_array   00000004  00000000  00000000  0000010c  2**2
                  CONTENTS, ALLOC, LOAD, RELOC, DATA
...

When I look at the section contents, it just contains 0:

$ arm-none-eabi-objdump -j .init_array -s foo.o
Contents of section .init_array:
 0000 00000000                             ....

However, there is also a relocation section that sets it correctly to _GLOBAL__sub_I_foo:

$ arm-none-eabi-objdump -x foo.o
...
RELOCATION RECORDS FOR [.init_array]:
OFFSET   TYPE              VALUE
00000000 R_ARM_TARGET1     _GLOBAL__sub_I_foo

In general, .init_array points to all of your _GLOBAL__sub_I_XXX initializer stubs, each of which calls its own copy of _Z41__static_initialization_and_destruction_0ii (yes, it is multiply-defined), which calls the constructor with the appropriate arguments.

Because I'm using -nostdlib in my build, I can't use CodeSourcery's __libc_init_array to execute the .init_array for me, so I need to call the static initializers myself:

extern "C"
{
    extern void (**__init_array_start)();
    extern void (**__init_array_end)();

    inline void static_init()
    {
        for (void (**p)() = __init_array_start; p < __init_array_end; ++p)
            (*p)();
    }
}

__init_array_start and __init_array_end are defined by the linker script:

. = ALIGN(4);
.init_array :
{
__init_array_start = .;
KEEP (*(.init_array*))
__init_array_end = .;
}

This approach seems to work with both the CodeSourcery cross-compiler and native ARM GCC, e.g. in Ubuntu 12.10 for ARM. Supporting both compilers is one reason for using -nostdlib and not relying on the CodeSourcery CS3 bare-metal support.

〃安静 2024-11-22 11:48:36

Timmmm,

我刚刚在 nRF51822 上遇到了同样的问题,并通过在 Stock Nordic .ld 文件中的几行周围添加 KEEP() 解决了它:

KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))

同时,我也对 fini_array 区域做了同样的事情。解决了我的问题,链接器仍然可以删除其他未使用的部分......

Timmmm,

I just had the same issue on the nRF51822 and solved it by adding KEEP() around a couple lines in the stock Nordic .ld file:

KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))

While at it, I did the same to the fini_array area too. Solved my problem and the linker can still remove other unused sections...

非要怀念 2024-11-22 11:48:36

由于 gcc 的 -c 参数,您只生成了一个目标文件。要创建 .init 部分,我相信您需要将该 .o 链接到实际的可执行文件或共享库中。尝试删除 -c 参数并将输出文件重命名为“foo”,然后使用反汇编程序检查生成的可执行文件。

You have only produced an object file, due to the -c argument to gcc. To create the .init section, I believe that you need to link that .o into an actual executable or shared library. Try removing the -c argument and renaming the output file to "foo", and then check the resulting executable with the disassembler.

鲜肉鲜肉永远不皱 2024-11-22 11:48:36

如果仔细观察,_Z41__static_initialization_and_destruction_0ii 将在全局构造函数内调用。哪个反过来将链接到 .init_array 部分(在 CodeSourcery 的 arm-none-eabi- 中。)或其他一些函数(__main()如果您使用的是 Linux g++)。 () 这应该在启动时或在 main() 处调用。
另请参阅链接。

If you look carefully _Z41__static_initialization_and_destruction_0ii would be called inside global constructor. Which inturn would be linked in .init_array section (in arm-none-eabi- from CodeSourcery.) or some other function (__main() if you are using Linux g++). () This should be called at startup or at main().
See also this link.

婴鹅 2024-11-22 11:48:36

我遇到了类似的问题,我的构造函数没有被调用(带有 GCC 的 nRF51822 Cortex-M0)。问题原来是由于这个链接器标志造成的:

 -Wl,--gc-sections

不要问我为什么!我认为它只是删除了死代码。

I had a similar issue where my constructors were not being called (nRF51822 Cortex-M0 with GCC). The problem turned out to be due to this linker flag:

 -Wl,--gc-sections

Don't ask me why! I thought it only removed dead code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文