【问题标题】:CLANG compiled MIDL RPC generated code incorrect function argument serializationCLANG 编译 MIDL RPC 生成代码不正确的函数参数序列化
【发布时间】:2018-01-15 14:42:30
【问题描述】:

尝试运行编译后的 CLANG,MIDL 生成 RPC 客户端代码,但它在服务器端爆炸,因为 MIDL 生成客户端代码的方式以相反的顺序序列化函数参数。

MIDL 被调用为

midl MyRPCInterface.idl /env win32 /out"RPCx32" /char ascii7

函数看起来像

int __cdecl f(int a, int b, int c);

函数的 RPC 客户端代码如下所示

int __cdecl f(int a, int b, int c)
{
    CLIENT_CALL_RETURN _RetVal;

    _RetVal = NdrClientCall2(
                  ( PMIDL_STUB_DESC  )&MyRPCInterface_StubDec,
                  (PFORMAT_STRING) & MyRPCInterface  __MIDL_ProcFormatString.Format[1110],
                  ( unsigned char * )&a);
    return ( char * )_RetVal. Simple;
}

MIDL 生成的客户端代码假定内存中的参数是对齐的 [a][b][c],因此它通过获取指向第一个参数 &a 的指针来序列化它们,并假定接下来的变量。

这对于 MSVC 和 BCC32 非常有效,但对于 CLANG 则不行。参数顺序相反。

MSVC 和 BCC32

int* pA = &a;
int* pB = pA + 1;
int* pC = pB + 1;
int* pD = pC + 1;

叮当

int* pD = &d;
int* pC = pD + 1;
int* pB = pC + 1;
int* pA = pB + 1;

在进行一些低级调试后,我可以确认所有 3 个编译器都以正确的顺序传递参数。

当 MSVC 和 BCC32 使用函数体中的参数时,它们通过引用原始堆栈地址直接使用它们。

另一方面,CLANG 创建变量的副本并将它们以相反的顺序存储在内存中,并在请求变量地址时使用该新位置。结果是函数参数未正确序列化,并且服务器因参数无效而崩溃。 尝试了所有优化参数

问题:

  1. 是否有命令行开关或关键字/pragma 告诉 CLANG 直接使用堆栈中的变量而不是先复制它们? (为什么会这样,好像是在浪费指令)

  2. 是否有 MIDL 命令行开关或关键字/pragma 告诉它反转变量顺序?

谢谢!

这是查看内存顺序的简单测试代码:

#include <stdio.h>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <cstdint>

void fdef(int a, int b, int c, int d)
{
    int* pA = &a;
    int* pB = &b;
    int* pC = &c;
    int* pD = &d;

    std::cout << " --------------------- " <<" Default call" << std::endl;
    std::cout << BytesToHexStr(&a, 12, true, "") << std::endl;
}

void __cdecl fcdecl(int a, int b, int c, int d)
{
    std::cout << " --------------------- " <<" __cdecl call" << std::endl;
    std::cout << BytesToHexStr(&a, 12, true, "") << std::endl;
}
void __stdcall fstdcall(int a, int b, int c, int d)
{
    std::cout << " --------------------- " <<" stdcall call"<< std::endl ;
    std::cout << BytesToHexStr(&a, 12, true, "") << std::endl;
}
void __pascal  fpascal(int a, int b, int c, int d)
{
    std::cout << " --------------------- " <<" pascal call"<< std::endl ;
    std::cout << BytesToHexStr(&a, 12, true, "") << std::endl;
}

int _tmain(int argc, _TCHAR* argv[]) 
{
    fdef(1, 2, 3, 4);
    fcdecl(1, 2, 3, 4);
    fstdcall(1, 2, 3, 4);
    fpascal(1, 2, 3, 4);
}

【问题讨论】:

    标签: c++ winapi clang rpc midl


    【解决方案1】:

    通过使用 _pascal 调用约定声明具有多个参数的 RPC 函数解决了我的问题。这会强制 MIDL 编译器生成单独序列化每个函数参数的代码。

    int _pascal f(int a, int b, int c);
    

    生成的“C”代码不是直接使用 CLANG 编译的,而是创建了一个正则表达式魔法脚本来修复所有代码问题。 MIDL 生成一个非常糟糕/旧的代码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-03-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-22
      相关资源
      最近更新 更多