【问题标题】:Is there a way to find the C++ mangled name to use in GetProcAddress?有没有办法找到要在 GetProcAddress 中使用的 C++ 错位名称?
【发布时间】:2013-04-15 13:42:05
【问题描述】:

GetProcAddress 与 C++ 一起使用的常见“解决方案”是“extern”C”,但这会破坏重载。名称修改允许多个函数共存,只要它们的签名不同。但是有没有办法找到GetProcAddress 的这些错位名称?

【问题讨论】:

    标签: windows visual-c++


    【解决方案1】:

    VC++ 编译器知道它自己的名称修饰方案,那么为什么不使用它呢?在template<typename T> T GetProcAddress(HMODULE h, const char* name) 内部,宏__FUNCDNAME__ 包含GetProcAddress 的错位名称。这包括T 部分。因此,在GetProcAddress<void(*)(int) 中,我们有一个名称为void(*)(int) 的子字符串。由此,我们可以简单地推导出void foo(int); 的错位名称

    此代码依赖于 VC++ 宏 __FUNCDNAME__对于 MinGW,您需要将其基于 __PRETTY_FUNCTION__

    FARPROC GetProcAddress_CppImpl(HMODULE h, const char* name, std::string const& Signature)
    {
        // The signature of T appears twice in the signature of T GetProcAddress<T>(HMODULE, const char*) 
        size_t len = Signature.find("@@YA");
        std::string templateParam = Signature.substr(0, len);
        std::string returnType    = Signature.substr(len+4);
        returnType.resize(templateParam.size()); // Strip off our own arguments (HMODULE and const char*)
        assert(templateParam == returnType); 
        // templateParam and returnType are _pointers_ to functions (P6), so adjust to function type (Y)
        std::string funName = "?" + std::string(name) + "@@Y" + templateParam.substr(2);
        return ::GetProcAddress(h, funName.c_str());
    }
    
    template <typename T>
    T GetProcAddress(HMODULE h, const char* name)
    {
        // Get our own signature. We use `const char* name` to keep it simple.
        std::string Signature = __FUNCDNAME__ + 18; // Ignore prefix "??$GetProcAddress@"
        return reinterpret_cast<T>(GetProcAddress_CppImpl(h, name, Signature));
    }
    
    // Showing the result
    
    struct Dummy { };
    
    __declspec(dllexport) void foo( const char* s)
    {
        std::cout << s;
    }
    
    __declspec(dllexport) void foo( int i, Dummy )
    {
        std::cout << "Overloaded foo(), got " << i << std::endl;
    }
    
    __declspec(dllexport) void foo( std::string const& s )
    {
        std::cout << "Overloaded foo(), got " << s << std::endl;
    }
    
    __declspec(dllexport) int foo( std::map<std::string, double> volatile& )
    {
        std::cout << "Overloaded foo(), complex type\n";
        return 42;
    }
    
    int main()
    {
        HMODULE h = GetModuleHandleW(0);
        foo("Hello, ");
        auto pFoo1 = GetProcAddress<void (*)( const char*)>(h, "foo");
        // This templated version of GetProcAddress is typesafe: You can't pass 
        // a float to pFoo1. That is a compile-time error.
        pFoo1(" world\n");
        auto pFoo2 = GetProcAddress<void (*)( int, Dummy )>(h, "foo");
        pFoo2(42, Dummy()); // Again, typesafe.
        auto pFoo3 = GetProcAddress<void (*)( std::string const& )>(h, "foo");
        pFoo3("std::string overload\n");
        auto pFoo4 = GetProcAddress<int (*)( std::map<std::string, double> volatile& )>(h, "foo");
        // pFoo4 != NULL, this overload exists.
        auto pFoo5 = GetProcAddress<void (*)( float )>(h, "foo");
        // pFoo5==NULL - no such overload.
    }
    

    【讨论】:

    • 请注意,__PRETTY_FUNCTION__ 不会这样工作,因为它没有被破坏,它看起来像 T GetProcAddress(HMODULE, const char*) [with T = blahblah]。我不确定在 GCC 下是否存在任何诸如损坏的函数名称宏之类的东西(不这么认为)。
    • @Damon:你说得对,我误读了文档。确实没有这个宏,&lt;cxxabi.h&gt; header 只提供了 de-mangle 功能。
    • @MSalters 很有趣,但它不适用于我尝试过的函数,因为 dll 中的损坏名称中有反向引用,但在您的函数中却没有;因为该函数在命名空间中,并且返回同一命名空间中的类的对象;在返回类型中,命名空间名称被替换为 @1 ...
    • 备案:dll中的名称:?myFunction@sfw@@YAAAVEStore@1@XZ 由模板化的GetProcAddress推导出的名称:?myFunction@sfw@@YAAAVEStore@sfw@@XZ
    • @F4:奇怪,__FUNCDNAME__ 里面的 GetProcAddress&lt;T&gt; 是什么?我曾期望它具有相同的 @1 反向引用。
    【解决方案2】:

    使用dumpbin /exports 'file.dll' 获取所有符号的修饰/未修饰名称。

    【讨论】:

      【解决方案3】:

      仅使用GetProcAddress 是不可能做到的。但是,一种方法是枚举该特定模块的所有导出函数,并进行模式匹配以查找所有损坏的名称。

      更具体地说,请参阅this answer here。您需要做的唯一更改是将TRUE 传递给MappedAsImage 参数,并将GetModuleHandle 的返回值作为Base 参数传递给ImageDirectoryEntryToData 函数调用。

      void EnumerateExportedFunctions(HMODULE hModule, vector<string>& slListOfDllFunctions)
      {
          DWORD *dNameRVAs(0);
          _IMAGE_EXPORT_DIRECTORY *ImageExportDirectory;
          unsigned long cDirSize;
          _LOADED_IMAGE LoadedImage;
          string sName;
          slListOfDllFunctions.clear();
      
          ImageExportDirectory = (_IMAGE_EXPORT_DIRECTORY*)
              ImageDirectoryEntryToData(hModule,
              TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &cDirSize);
          if (ImageExportDirectory != NULL)
          {
              dNameRVAs = (DWORD *)ImageRvaToVa(LoadedImage.FileHeader, 
                  LoadedImage.MappedAddress,
              ImageExportDirectory->AddressOfNames, NULL);
              for(size_t i = 0; i < ImageExportDirectory->NumberOfNames; i++)
              {
                  sName = (char *)ImageRvaToVa(LoadedImage.FileHeader, 
                          LoadedImage.MappedAddress,
                         dNameRVAs[i], NULL);
               slListOfDllFunctions.push_back(sName);
              }
          }
      }
      

      【讨论】:

        【解决方案4】:

        我无法完全理解为什么您会想要/需要 MSalters' solution 的 constexpr 版本,但它就是这样,完成了命名空间修改。用作

        using F = int(double*);
        constexpr auto f = mangled::name<F>([]{ return "foo::bar::frobnicate"; });
        constexpr const char* cstr = f.data();
        

        其中F 是函数签名,foo::bar::frobnicate 是函数的(可能是限定的)名称。

        #include<string_view>
        #include<array>
        
        namespace mangled
        {
            namespace detail
            {
                template<typename F>
                inline constexpr std::string_view suffix()
                {
                    auto str = std::string_view(__FUNCDNAME__);
                    return str.substr(14, str.size() - 87);
                }
                
                template<typename L>
                struct constexpr_string
                {
                    constexpr constexpr_string(L) {}
                    static constexpr std::string_view data = L{}();
                };
        
                template<typename Name>
                inline constexpr int qualifiers()
                {
                    int i = -2, count = -1;
                    while(i != std::string_view::npos)
                    {
                        i = Name::data.find("::", i + 2);
                        count++;
                    }
                    return count;
                }
        
                template<typename Name>
                inline constexpr auto split()
                {
                    std::array<std::string_view, qualifiers<Name>() + 1> arr = {};
                    int prev = -2;
                    for(int i = arr.size() - 1; i > 0; i--)
                    {
                        int cur = Name::data.find("::", prev + 2);
                        arr[i] = Name::data.substr(prev + 2, cur - prev - 2);
                        prev = cur;
                    }
                    arr[0] = Name::data.substr(prev + 2);
                    return arr;
                }
        
                template<typename F, typename Name>
                struct name_builder
                {
                    static constexpr auto suf = detail::suffix<F>();
                    static constexpr auto toks = split<Name>();
                    static constexpr auto len = Name::data.size() + suf.size() - toks.size() + 6;
                    static constexpr auto str = [] {
                        std::array<char, len> arr = {};
                        arr[0] = '?';
                        int i = 1;
                        for(int t = 0; t < toks.size(); t++)
                        {
                            if(t > 0)
                                arr[i++] = '@';
                            for(auto c : toks[t])
                                arr[i++] = c;
                        }
        
                        arr[i++] = '@';
                        arr[i++] = '@';
                        arr[i++] = 'Y';
        
                        for(auto c : suf)
                            arr[i++] = c;
                        
                        return arr;
                    }();
                };
            }
        
            template<typename F, typename LambdaString>
            inline constexpr std::string_view name(LambdaString)
            {
                using Cs = detail::constexpr_string<LambdaString>;
                using N = detail::name_builder<F, Cs>;
                return {N::str.data(), N::len};
            }
        }
        

        【讨论】:

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