我可以更改损坏的C ++ DLL导出符号吗? (二进制兼容但源不兼容)

时间:2013-03-29 12:47:02

标签: c++ visual-c++ dll dllexport name-mangling

我们的C ++项目(仍然)在VS6上编译时使用了Treat wchar_t as Built-in: No (/Zc:wchar_t-)选项。

这导致wchar_t只是unsigned shortWORD)的typedef。

我们想要更改它,以便wchar_t作为一个合适的内置类型处理 - 这将极大地简化(现代)库的集成。

问题是,我们链接到一些我们无法重新编译的DLL,这些DLL将其字符串公开为unsigned short*,其标题使用wchar_t*。当wchar_t是内置类型时,这将导致链接器错误,因为显然导出符号是不同的。

更改标题需要添加一种强制转换层 - 我当然不希望将强制转换添加到调用这些标题中的类的所有代码中。

是否可以修复DLL,使其导出符号“假装”导出内置wchar_t而不是WORD?毕竟,这两种类型在VC ++中是100%二进制兼容的。

还有其他想法吗?

2 个答案:

答案 0 :(得分:1)

很好,它们是二进制兼容的。

您可能遇到的唯一一个墙是导出函数的名称修改。如果您无法更改的DLL使用其修饰的C ++名称导出这些函数,那么当客户端无法找到导出的名称时,您将在运行时获得失败的鲸鱼。当程序员不使用extern "C"或未使用.def文件重命名导出时,就会发生这种情况。否则很容易看到Dumpbin.exe / exports。解决这个问题很痛苦,你需要修改DLL头来将wchar_t更改为WORD,宏可以这样做,并编写一些带有wchar_t的小适配器函数,并通过强制转换来调用WORD函数。

如果这些是导出的C ++类,那么你就会遇到更大的问题。您需要为这些DLL创建一个新的导入库。从Dumpbin.exe / exports获得的输出开始,为您提供原始名称。并从中创建一个.def文件,使用其foo = bar选项重命名符号。使用lib.exe / def选项创建导入库。创建一个小测试DLL,以确切地确定如何重命名受损名称。

答案 1 :(得分:0)

感谢@HansPassant的answer我能够解决这个问题。

此处的完整步骤供参考:

  1. 为DLL生成导出符号:dumpbin /EXPORTS ddao35u.dll
  2. 用wchar_t替换所有unsigned-short-pointer类型:这意味着用wchar_t string-mangle替换PAG(对于unsigned short*)和PBG(对于unsigned short const*PA_WPB_W
  3. 使用所有符号编写新的def文件,其中带有字符串的函数被替换为名称。例如:

    LIBRARY ddao35u
    EXPORTS
      ??0CdbBSTR@@QAE@PA_W@Z=??0CdbBSTR@@QAE@PAG@Z  @1   ... replaced
                      ^^^^  =                ^^^
      ??0CdbBookmark@@QAE@ABV0@@Z  @2  ... no strings, no replacement needed
    ...
    
  4. 从def文件生成导入库:lib /DEF:ddao35u.def
  5. 从wchar_t-native VS项目链接器选项中引用此新ddao35u.lib,而不是原始lib文件。
  6. 我将转储我以前在这里执行此操作的整个perl脚本函数:

    sub fixModuleLib {
      my $basename = shift;
      my $dllpath = shift;
      # my $basename = "ddao35u";
      print "\n\n";
      my $cmdExport = "dumpbin /EXPORTS $dllpath";
      print "Running >> $cmdExport << for export symbols ...\n";
      die "Input file $dllpath not found!" unless -f "$dllpath"; 
      my @exports  = qx/$cmdExport/;
    
      print "Open $basename.def for writing ...\n";
      open(my $defh, ">", "$basename.def") or die "Unable to open basename.def for writing: $!";
      print $defh "LIBRARY $basename"."\n";
      print $defh "EXPORTS"."\n";
    
      my @expEntries;
      my $usCount = 0;
      foreach (@exports) {
        #           1    0 00002050 ??0CdbBSTR@@QAE@PAG@Z
        my $ws = '\s+';
        my $hnum = '[ABCDEFabcfed0123456789]+';
        if (m/$ws(\d+)$ws$hnum$ws$hnum$ws(\?\S+)/) {
          my $ord = $1;
          my $mangle = $2;
          my $defline;
          # PAG = unsigned short*
          # PBG = unsigned short const*
          # at least two @ signs before parameter list (at least in our case)
          if ($mangle =~ m/(.+@.*@.*)(P[AB]G)(.*)/) { # case sensitive
            my $pre = $1;
            my $uptr = $2;
            my $post = $3;
            # print "$ord\t$pre>>>$uptr<<<$post\n";
            my $wptr = $uptr;
            $wptr =~ s/G/_W/;
            my $wchartMangle = "$pre$wptr$post";
            $defline = "  $wchartMangle=$mangle  \@$ord";
            print "CHANGED: $defline\n";
            $usCount += 1;                 
          } else {
            # line doesn't contain unsigned short ptr
            $defline = "  $mangle  \@$ord";
          }
          push @expEntries, $defline;
        }
      }
      print "There are ".scalar(@expEntries)." export lines and $usCount of them contain an unsigned-short-pointer.\n";
      die "No entries found!" unless $usCount>0;
      print "Writing entries to $basename.def ...\n";
      foreach my $entry (@expEntries) {
        print $defh $entry."\n"; 
      }
      close($defh);
    
      my $cmdLib = "lib /DEF:$basename.def";
      print "Generate Export Lib from DEF-File: >> $cmdLib << ...\n";
      system($cmdLib); 
    }