【问题标题】:Structure parameter corrupted on function call函数调用时结构参数损坏
【发布时间】:2014-07-03 13:07:32
【问题描述】:

在将结构作为参数传递时,我无法追踪异常行为的原因。

所讨论的结构 structFoo 具有以下声明:

typedef struct _structFoo {
    int id;
    BSTR szDescription;
    VARIANT vData;
    BOOL bTransient;
} structFoo;

我有两个模块,A 和 B。模块 A 调用 B::foo(int id, uint filter, structFoo sF)。在 A 中,在调用之前,structFoo 结构已正确形成并填充了有效数据。但是,一旦对 B::foo() 进行了函数调用,structFoo 参数中就会包含垃圾数据。进一步检查,原来复制的struct的地址放入了id字段,szDescription指向最近使用的字符串。函数调用后其他参数正确。

我不确定这种错位的原因,或者发生了什么,但在我看来,在进行函数调用之前,一切都在适当的位置。这是导致函数调用的反汇编:

0000000006003211  lea         rdi,[rsp+230h] 
0000000006003219  lea         rsi,[sAttPairId] 
0000000006003221  mov         ecx,30h 
0000000006003226  rep movs    byte ptr [rdi],byte ptr [rsi] 
0000000006003228  mov         rax,qword ptr [piConstruct] 
0000000006003230  mov         rax,qword ptr [rax] 
0000000006003233  lea         r9,[rsp+230h] 
000000000600323B  mov         r8d,800h 
0000000006003241  mov         edx,dword ptr [iHighNodeId] 
0000000006003248  mov         rcx,qword ptr [piConstruct] 
0000000006003250  call        qword ptr [rax+60h] 

这是函数调用后的反汇编:

0000000004B72470  mov         qword ptr [rsp+20h],r9 
0000000004B72475  mov         dword ptr [rsp+18h],r8d 
0000000004B7247A  mov         dword ptr [rsp+10h],edx 
0000000004B7247E  mov         qword ptr [rsp+8],rcx 
0000000004B72483  push        rsi  
0000000004B72484  push        rdi  
0000000004B72485  sub         rsp,0A8h 
0000000004B7248C  mov         rdi,rsp 
0000000004B7248F  mov         rcx,2Ah 
0000000004B72499  mov         eax,0CCCCCCCCh 
0000000004B7249E  rep stos    dword ptr [rdi] 
0000000004B724A0  mov         rcx,qword ptr [rsp+0C0h] 
0000000004B724A8  mov         qword ptr [rsp+90h],0FFFFFFFFFFFFFFFEh

sub rsp, 0A8h之后的参数都是用数据设置的,但是sF参数的id字段里有正确的structFoo信息的地址,而不是用这个地址作为自己的指针。非常感谢任何有关解决此问题的指导。

作为旁注,不幸的是,将 B::foo() 更改为获取结构的地址而不是结构本身不是一种选择。大量遗留代码依赖于我无权更改的此功能。

谢谢!

【问题讨论】:

  • 你能显示B::foo( int id, uint filter, structFoo sF )的代码吗?
  • 在 C++ 中不需要将结构声明为typedef struct TAG_NAME {} REAL_NAME;。就做struct REAL_NAME {};
  • @ahenderson:这通常是 C 的保留。
  • 这两个模块是否使用相同的编译器参数编译?也许它们每个都以不同的对齐方式编译,因此每个模块中结构的物理布局不同?
  • 猜一猜:也许您遇到了 ODR 违规(例如,由于某些影响结构定义的宏)?

标签: c++ visual-studio assembly x86 function-calls


【解决方案1】:

我猜,模块 AB 是使用不同的调用约定编译的。模块A 通过引用/指针将结构传递给函数,而模块B 期望通过值接收堆栈上的结构。

B的头文件中可能有一个修饰符,像这样:

__weird_call void B::foo( int id, uint filter, structFoo sF );

可能模块A 的编译器不理解它,或者其他一些头文件定义了它(#define __weird_call /* nothing */),或者类似的东西。

【讨论】:

    【解决方案2】:

    我在寻找我遇到的类似问题的答案时偶然发现了这个页面。 虽然在网上找不到答案,分享下调试的经验。

    下面的结构通过引用从一个函数传递到另一个函数,接收者会发现接收到的数据是意外的:

    typedef struct
    {
        char time_st[30];
        char pipe_no;
        float loss;
        float power[4];
        int   mode;
        int   count;
    }Parameters;
    

    另一个发现是,如果我在与调用函数相同的文件中定义接收函数,问题就会消失。

    经过调试,发现根本原因是系统中遗留.h文件中“#pragma pack”的意大利面条式使用导致结构打包问题——由于在.c文件中包含了几个遗留头文件调用者函数,结构被打包,但在接收函数的 .c 文件中(在项目活动期间编写的新文件),结构被视为解包。

    解决方案:添加足够的填充以使结构字对齐

    typedef struct
    {
        char time_st[30];
        char pad;
        char pipe_no;
        float loss;
        float power[4];
        int   mode;
        int   count;
    }Parameters;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-10-02
      • 1970-01-01
      • 1970-01-01
      • 2013-06-30
      • 1970-01-01
      • 2015-03-20
      • 1970-01-01
      • 2011-01-02
      相关资源
      最近更新 更多