【问题标题】:How to get pointers and sizes of variables from the compiler - from outside the compiled code?如何从编译器获取变量的指针和大小 - 从编译代码之外?
【发布时间】:2010-07-01 17:15:14
【问题描述】:

我希望编译器输出一个文件,其中包含指向它正在编译的源代码中所有全局变量的指针,以及它们的大小。

这可能吗?有没有办法在任何 c 编译器中做到这一点?

【问题讨论】:

  • 指针是在运行时定义的“数字”(地址)。你问的都是废话。
  • 你想达到什么目的?告诉我们您的问题,而不是您的解决方案。
  • @Alexandre:指针是在编译时定义的,相对于分配给进程的内存的开始
  • 也许他在问如何创建一个可链接的目标文件,其中包含指向其中所有 glbl 变量的指针,以及大小。在像struct glblvar { void * ptr; size_t sz; } 这样的结构数组中。如果是这样,这将需要从您的目标文件中提取一些信息并进行一些黑客攻击,但可以做到。

标签: c compiler-construction pointers


【解决方案1】:

类似于地图文件的东西?这将显示全局变量和静态变量的分配位置,而不是它们指向的位置。大多数编译器(链接器)会自动或使用简单的语句输出一个。只需在您的文档中搜索地图文件即可。

【讨论】:

    【解决方案2】:

    此信息可在二进制文件的符号表中找到,尽管它可能与您期望的不同。

    编译器获取一个或多个源文件,将代码编译为目标代码,并生成一个目标文件(Unix 上为 .o,Windows 上为 .obj)。源文件中引用的所有变量和函数都在符号表中提及。源文件中定义的变量和函数具有特定的地址和大小,而源文件中未定义的符号则标记为未定义,并且必须稍后链接。所有符号都相对于特定部分列出。常见的部分是“.text”用于可执行代码,“.bss”用于在程序启动时初始化为零的变量,“.data”用于使用非零值初始化的变量。

    链接器获取一个或多个目标文件,组合这些部分(将每个目标文件中的所有代码和数据放入一个大的代码和数据部分),然后写入一个输出文件。此输出文件可能是可执行文件,也可能是共享库。磁盘上的可执行文件仍然没有每个变量的指针;它仍然将节开头的偏移量存储到变量中。

    运行可执行文件时,操作系统的动态加载程序会读取可执行文件,找到每个部分,并为该部分分配内存。 (它还可能在每个部分上设置不同的权限——“.text”段通常被标记为只读,并且(在支持它的处理器上)数据段有时被标记为不可执行。)只有这样变量获取指针——当代码需要访问一个特定的变量时,它会将节的开头地址加上节开头的偏移量来获取指针。

    您可以使用各种工具来调查每个二进制文件的符号表。 GNU 工具链的objdump(在 Linux 上使用)就是这样一种工具。

    对于一个简单的 C hello-world 程序:

    #include <stdio.h>
    
    const char message[] = "Hello world!\n";
    
    int main(int argc, char ** argv) {
            printf(message);
            return 0;
    }
    

    我在我的 Linux 机器上编译(但不链接)它:

    $ gcc -c hello.c -o hello.o
    

    现在我可以查看符号表了:

    $ objdump -t hello.o
    hello.o:     file format elf32-i386
    
    SYMBOL TABLE:
    00000000 l    df *ABS*  00000000 hello.c
    00000000 l    d  .text  00000000 .text
    00000000 l    d  .data  00000000 .data
    00000000 l    d  .bss   00000000 .bss
    00000000 l    d  .rodata        00000000 .rodata
    00000000 l    d  .note.GNU-stack        00000000 .note.GNU-stack
    00000000 l    d  .comment       00000000 .comment
    00000000 g     O .rodata        0000000e message
    00000000 g     F .text  0000002b main
    00000000         *UND*  00000000 puts
    

    第一列是每个符号的地址,相对于节的开头。每个符号都有不同的标志,其中一些符号用作工具链和调试器其余部分的提示。 (如果我使用调试符号构建,我也会看到许多专门针对它们的条目。)我的简单程序只有一个变量:

    00000000 g     O .rodata        0000000e message
    

    第五列告诉我符号 message 的大小为 0xe -- 14 字节。

    【讨论】:

      【解决方案3】:

      虽然输出这些数据不需要编译器,但大多数链接器可以转储出这些信息。例如,Microsoft 的链接器映射文件包含可执行文件/dll 中的所有公共符号以及它们相对于放入其中的部分的地址(只读、读写、代码、零初始化等)。大小可以从那个,虽然它主要是一个近似值。

      您还可能想出一种方法来检查为可执行文件生成的调试符号,因为这正是调试器必须做的事情。

      【讨论】:

        【解决方案4】:

        通常你会从链接器而不是编译器得到这个——链接器是为事物分配地址的东西。大多数链接器可以生成一个映射文件,该文件将包含全局变量 函数的地址(以及它创建的可执行文件中的任何其他符号)。由您决定哪些是哪些。我见过的所有这些都包含一些要告诉您的内容,但确切的格式因所涉及的链接器而异。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-05-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-01-10
          相关资源
          最近更新 更多