【问题标题】:How to get a pointer to a binary section in MSVC?如何在 MSVC 中获取指向二进制部分的指针?
【发布时间】:2011-04-18 00:11:18
【问题描述】:

我正在编写一些代码,将一些数据结构存储在一个特殊命名的二进制部分中。这些都是相同结构的所有实例,它们分散在许多 C 文件中,并且不在彼此的范围内。通过将它们全部放在命名部分中,我可以遍历所有这些。

在 GCC 中,我使用 _attribute_((section(...)) 加上一些特殊命名的外部指针,这些指针由链接器神奇地填充。这是一个简单的例子:

#include <stdio.h>

extern int __start___mysection[];
extern int __stop___mysection[];

static int x __attribute__((section("__mysection"))) = 4;
static int y __attribute__((section("__mysection"))) = 10;
static int z __attribute__((section("__mysection"))) = 22;

#define SECTION_SIZE(sect) \
    ((size_t)((__stop_##sect - __start_##sect)))

int main(void)
{
    size_t sz = SECTION_SIZE(__mysection);
    int i;

    printf("Section size is %u\n", sz);

    for (i=0; i < sz; i++) {
        printf("%d\n", __start___mysection[i]);
    }

    return 0;
}

我试图弄清楚如何在 MSVC 中执行此操作,但我画的是空白。我从编译器文档中看到,我可以使用 __pragma(section(...)) 声明该部分,并使用 __declspec(allocate(...)) 声明该部分中的数据,但我看不到如何获得在运行时指向节的开始和结束的指针。

我在网上看到了一些与在 MSVC 中执行 _attribute_((constructor)) 相关的示例,但这似乎是针对 CRT 的黑客行为,而不是获取指针的一般方法到部分的开头/结尾。有人有什么想法吗?

【问题讨论】:

  • 请问您为什么要首先控制二进制部分命名?
  • 这是一个高性能的检测框架。想象一下 printf(format, args...) 调用,其中所有格式字符串都存储在二进制部分中,唯一记录的是参数和查找值。参数替换发生在后处理中。
  • 一个更好的例子是一个程序,它允许您通过重新链接而不是重新编译(并且可能重新生成一些代码)来添加模块。如果您可以将整个部分视为某个结构的数组,那么您可以对其进行迭代并对每个条目执行一些操作,例如调用cur_entry[i]-&gt;init(&amp;cur_entry)。通过这样做,您还可以使用有关内存使用模式的特殊知识来优化分页和缓存局部性。通常与 Windows 无关(据我所知),但这也可能是哈佛架构处理器所必需的。
  • 是的,我的日志机制工作方式类似。特殊部分是包含检测点的所有元数据的大型结构数组。我希望能够遍历这个数组以转储所有元数据以供以后在后期处理中使用。
  • 我也在使用这种结构来运行单元测试。通过这种方式,我不需要知道系统中所有模块的“主”函数,但每个模块都声明了存储在特殊部分中的 unittest 结构。 main 函数以与 Andrew B 示例中的 main() 函数相同的方式遍历所有这些结构。

标签: c visual-c++ gcc linker


【解决方案1】:

还有一种方法可以在不使用程序集文件的情况下执行此操作。

#pragma section(".init$a")
#pragma section(".init$u")
#pragma section(".init$z")

__declspec(allocate(".init$a")) int InitSectionStart = 0;
__declspec(allocate(".init$z")) int InitSectionEnd   = 0;

__declspec(allocate(".init$u")) int token1 = 0xdeadbeef;
__declspec(allocate(".init$u")) int token2 = 0xdeadc0de;

前 3 行定义了段。这些定义了部分并代替了程序集文件。与 data_seg pragma 不同,section pragma 只创建节。 __declspec(allocate()) 行告诉编译器将该项目放入该段中。

来自微软页面: 这里的顺序很重要。部分名称不得超过 8 个字符。 $ 之前的同名部分合并为一个部分。它们的合并顺序是通过对 $ 之后的字符进行排序来确定的。

另一个需要记住的重要一点是节是 0 填充到 256 字节。 START 和 END 指针不会像您期望的那样直接位于之前和之后。

如果您将表设置为指向函数或其他非 NULL 值的指针,由于节填充,应该很容易跳过表前后的 NULL 条目

详情请见this msdn page

【讨论】:

    【解决方案2】:

    首先,您需要创建一个 ASM 文件,其中包含您感兴趣的所有部分(例如 section.asm):

    .686
    .model flat
    
    PUBLIC C __InitSectionStart
    PUBLIC C __InitSectionEnd
    
    INIT$A SEGMENT DWORD PUBLIC FLAT alias(".init$a")
            __InitSectionStart EQU $
    INIT$A ENDS
    
    INIT$Z SEGMENT DWORD PUBLIC FLAT alias(".init$z")
            __InitSectionEnd EQU $
    INIT$Z ENDS
    
    END
    

    接下来,您可以在代码中使用以下内容:

    #pragma data_seg(".init$u")
    int token1 = 0xdeadbeef;
    int token2 = 0xdeadc0de;
    #pragma data_seg()
    

    这给出了这样一个 MAP 文件:

     Start         Length     Name                   Class
     0003:00000000 00000000H .init$a                 DATA
     0003:00000000 00000008H .init$u                 DATA
     0003:00000008 00000000H .init$z                 DATA
    
      Address         Publics by Value              Rva+Base       Lib:Object
     0003:00000000       ?token1@@3HA               10005000     dllmain.obj
     0003:00000000       ___InitSectionStart        10005000     section.obj
     0003:00000004       ?token2@@3HA               10005004     dllmain.obj
     0003:00000008       ___InitSectionEnd          10005008     section.obj
    

    因此,如您所见,名称为 .init$u 的部分位于 .init$a.init$z 之间,这使您能够通过 __InitSectionStart 符号和通过__InitSectionEnd 符号到数据末尾。

    【讨论】:

      【解决方案3】:

      我在这里进行了一些试验,并尝试在没有汇编文件的情况下实现该版本,但是在各节之间的随机填充字节数方面遇到了困难,这使得几乎不可能找到 .init$u 节的开头如果内容不仅仅是可以检查 NULL 或其他一些已知模式的指针或其他简单项目,则部分。 是否插入填充似乎与调试选项 Zi 的使用相关。如果给定,则插入填充,没有填充,所有部分都完全按照人们想要的方式显示。

      【讨论】:

        【解决方案4】:

        ML64 可以减少很多装配噪音:

        public foo_start
        public foo_stop
        
        .code foo$a
        foo_start:
        
        .code foo$z
        foo_stop:
        
        end
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多