【问题标题】:How does VC++ mangle local static variable names?VC++ 如何处理局部静态变量名?
【发布时间】:2010-09-22 05:50:46
【问题描述】:

这是我的一些代码:

MyClass* MyClass::getInstance()
{
   static MyClass instance;
   return &instance;
}

我想看看这个单例的当前值。但我目前在执行过程中暂停了三个小时,我暂停的原因是我内存不足。所以我不能在这个方法中放断点来查看值是什么。

然后我的问题是如何在全局范围内引用这个 instance 变量。我尝试将其称为MyClass::getInstance::instance,但这不起作用。我猜getInstance 必须以某种方式装饰。有人知道怎么做吗?

这是在 Visual Studio 2008 中。

【问题讨论】:

  • 为什么不让班员去做呢?局部静态变量不是最好的调试方法
  • class MyClass { MyClass *instance; MyClass *getInstance() { 返回实例; } }
  • 是的...我知道如果我可以更改代码,那么这将是微不足道的。但正如我所说,这是中期运行,所以我正在寻找一种让代码保持原样的解决方案。
  • 你不能在启动时得到它的地址吗?然后在内存不足时检查该地址
  • 是的,我可以。但是,如果现在有办法做到这一点,那么我不必等待三个小时!我还认为确定如何从全局上下文中引用变量将是一个有趣的练习。

标签: c++ scope


【解决方案1】:

好吧,函数作用域的静态变量instance 不会出现在.mapcl.exe /Fm 生成的文件中,而且当我在WinDbg 中使用x programname!*MyClass* 时它也不会出现,所以损坏的name 似乎根本不包含 MyClass

方案一:反汇编MyClass::getInstance

这种方法似乎更容易:

0:000> uf 程序名!MyClass::getInstance 程序名!MyClass::getInstance [程序名.cpp @ 14]: 14 00401050 55 推 ebp 14 00401051 8bec 移动 ebp,esp 15 00401053 a160b34200 mov eax,dword ptr [程序名!$S1 (0042b360)] 15 00401058 83e001 和 eax,1 15 0040105b 7526 jne funcstat!MyClass::getInstance+0x33 (00401083) 程序名!MyClass::getInstance+0xd [程序名.cpp @ 15]: 15 0040105d 8b0d60b34200 mov ecx,dword ptr [程序名!$S1 (0042b360)] 15 00401063 83c901 或 ecx,1 15 00401066 890d60b34200 mov dword ptr [程序名!$S1 (0042b360)],ecx 15 0040106c b9b0be4200 mov ecx,偏移程序名!实例(0042beb0) 15 00401071 e88fffffff 调用程序名!ILT+0(??0MyClassQAEXZ) (00401005) 15 00401076 68e03e4200 推送偏移程序名!`MyClass::getInstance'::`2'::`'instance' (00423ee0) 的动态 atexit 析构函数 15 0040107b e8f3010000 调用程序名!atexit (00401273) 15 00401080 83c404 添加 esp,4 程序名!MyClass::getInstance+0x33 [程序名.cpp @ 16]: 16 00401083 b8b0be4200 mov eax,偏移程序名!实例(0042beb0) 17 00401088 5d 弹出 ebp 17 00401089 C3 RET

由此我们可以看出编译器调用了对象$S1。当然,这个名字将取决于你的程序有多少函数范围的静态变量。

选项 2:在内存中搜索对象

进一步扩展@gbjbaanb 的建议,如果MyClass 具有虚函数,您可能很难找到它的位置:

  • 对进程进行完整的内存转储。
  • 将完整的内存转储加载到 WinDbg。
  • 使用x命令查找MyClass的vtable地址:
0:000> x 程序名!MyClass::`vftable' 00425c64 程序名!MyClass::`vftable' =
  • 使用s 命令在进程的虚拟地址空间(本例中为 0-2GB)中搜索指向 MyClass 的 vtable 的指针:
0:000> s -d 0 L?7fffffff 00425c64 004010dc 00425c64 c35de58b cccccccc cccccccc d\B...]......... 0040113c 00425c64 8bfc458b ccc35de5 cccccccc d\B..E...]...... 0042b360 00425c64 00000000 00000000 00000000 分贝......
  • 使用dt 命令查找类的vtable 偏移量,然后从搜索返回的地址中减去该偏移量。这些是对象的可能地址。
0:000> dt 程序名!MyClass +0x000 __VFN_table : Ptr32 +0x008 x : Int4B +0x010 y : 浮动
  • 使用dt programname!MyClass 0042b360 检查对象的成员变量,检验对象位于0042b360(或其他地址)的假设。正如我在上面所做的那样,您可能会得到一些误报,但是通过检查成员变量,您可能能够找出哪个是您的单例。

这是一种查找 C++ 对象的通用技术,当您可以反汇编 MyClass::getInstance 时,这有点过头了。

【讨论】:

  • 由于没有提到我可能会推荐undname工具,它包含在VS中,可用于破译错位符号
  • @EFraim:我同意,undname 对于处理装饰符号非常有用。但是,我认为它对查找函数范围的静态变量没有帮助,因为 MSVC 似乎并没有像完全重命名它们那样修饰它们(到 $S1$S2 等)。跨度>
  • 如果您在.obj 文件上使用dumpbin /symbols,您可能会发现$S1instance 都是静态变量,它们的名称非常难看,例如?instance@?1??getInstance@MyClass@@SAPAV2@XZ@4V2@A?$S1@?1??getInstance@MyClass@@SAPAV2@XZ@4IA .我不太确定同时拥有它们的原因是什么,但如果从函数参数初始化局部静态变量,似乎会生成一个 $Sx 变量。 [请注意,我使用 Studio 2010 进行了测试,自 2008 年以来可能已经改变了约定。]
  • 据我所知,如果局部静态变量是动态初始化的,它会发出一个额外的$Sx 变量,但如果它是静态初始化的则不会。做了一个快速测试的源文件,用cl /c编译,用dumpbin /symbols得到mangled名字,上传到ideone给你:ideone.com/2vjjhu
  • @bk1e 我发现变量名是 abcexe!`ns1::c1::fn1::`2'::$TSS0 我如何查看这个对象? ? <full_name> 给了我一般错误 - 无法解决 的错误
【解决方案2】:

那个代码看起来很危险... :-)

但是无论如何,您的重整名称将取决于您的Calling Convention 因此,在您找到您的重整名称之前,您需要知道您的构建环境使用什么作为调用约定。 MSDN 有更多关于调用约定的信息。

除此之外,找出关于你的类的所有这些信息的一种方法是检查你的 VTable,它位于对象的前 4 个字节中。逆向器使用的一个绝妙技巧是隐藏的 VC++ 标志 reportSingleClassLayout,它以 ASCII 艺术方式打印类结构。

【讨论】:

    【解决方案3】:

    在 gdb 中,您可以在变量名的错位上放置一个观察点。

    例如,用这个函数:

    int f() {
        static int xyz = 0;
        ++xyz;
    
        return xyz;
    }
    

    我可以观看 _ZZ1fvE3xyz(被 gcc 3.2.3 或 gcc 4.0.1 破坏)。

    【讨论】:

    • 太棒了...这就是我一直在寻找的东西,仅适用于 MS 编译器。很高兴知道它可以做到。
    猜你喜欢
    • 2013-02-01
    • 2013-12-14
    • 1970-01-01
    • 2018-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多