我可以在编译期间从 gcc 获取单例的地址或链接时吗?

Can I get the address of a singleton during compile or link time from gcc?

本文关键字:地址 链接 单例 获取 编译 gcc 我可以      更新时间:2023-10-16

我正在处理一个嵌入式项目,并询问我是否可以在编译或链接期间获取单例类的地址。

为了创建我的单例,我使用以下代码,并对实例的地址感兴趣。

class A
{
    public:
    static A& get()
    {
        static A instance;
        return instance;
}:

我想要做的当然是使用调试探针从外部更改值,但不要使用真正的调试会话。

此致敬意安德烈亚斯

如果不确切

了解您正在使用哪些开发工具、硬件架构等,就很难确切地说出您应该做什么,但通常可以将某些变量分配给特定数据段或特定代码段中的函数,然后在链接阶段为该段分配特定地址。

例如,您可以使用 gcc 部分属性:

int init_data __attribute__ ((section ("INITDATA")));

MyObj obj __attribute__((section ("BATTERY_BACKED")));

然后在链接器脚本中使用相同的节名称,将其放置在"正确"地址。

大多数(合理的)嵌入式工具链都会以某种方式支持这一点,但具体完成方式差异很大。

另一种选择是使用放置new

MyObj *obj = new ((void *)0x11220000) MyObj(args);
通常,

调试探测器只能看到物理地址,而用户应用程序只在虚拟地址上运行,虚拟地址会随着应用程序加载时间的变化而变化,因此链接器技巧不起作用。你没有说你使用哪个操作系统,但我想是Linux。如果是这样,您可以执行以下操作:为自己保留一个您知道物理地址且操作系统不使用的暂存器内存区域。例如,如果您的 SoC 具有嵌入式静态内存,请使用它,如果不是询问您当地的 Linux 专家如何在内核内存配置中保留一页 RAM。

然后查看本文以了解如何将物理地址映射到应用程序的虚拟内存空间:

如何从用户空间(在Linux中)访问内核空间?

获得暂存器区域的虚拟地址后,您的应用程序可以在那里读取/写入所需的任何内容。调试探针将能够读取/写入具有物理地址的同一区域。

您可以将 placement-new 与地址在编译或链接时可用的缓冲区一起使用。

#include <new>
extern unsigned char placeA[];
class A {
public:
    static A& get()
    {
        static A *p_instance;
        if(!p_instance) {
            p_instance = new(placeA) A();
        }
        return *p_instance;
    }
};
unsigned char placeA[sizeof(A)] __attribute__ ((aligned (__BIGGEST_ALIGNMENT__)));

不确定这是否是您要做的,但是将"-S"与 gcc 一起使用将在编译阶段后停止所有内容。这样,您就可以深入了解汇编代码并评估变量。以下是手册页摘录:

If you only want some of the stages of compilation, you can use -x (or
filename suffixes) to tell gcc where to start,
and one of the options -c, -S, or -E to say where gcc is to stop.  Note that
some combinations (for example, -x cpp-output -E) instruct gcc to do nothing at all.
-c  Compile or assemble the source files, but do not link.  The linking stage simply is not done.  The ultimate
    output is in the form of an object file for each source file.
    By default, the object file name for a source file is made by replacing the suffix .c, .i, .s, etc., with .o.
    Unrecognized input files, not requiring compilation or assembly, are ignored.
-S  Stop after the stage of compilation proper; do not assemble.  The output is in the form of an assembler code file
    for each non-assembler input file specified.
    By default, the assembler file name for a source file is made by replacing the suffix .c, .i, etc., with .s.
    Input files that don't require compilation are ignored.
-E  Stop after the preprocessing stage; do not run the compiler proper.  The output is in the form of preprocessed
    source code, which is sent to the standard output.
    Input files which don't require preprocessing are ignored.