LIB Viewer - .h-Dateien exportieren

Einleitung

Haben Sie sich schon einmal gefragt, welche Funktionen eine Bibliothek (.lib) exportiert? Manchmal beinhalten Bibliotheken versteckte Methoden, die in einer mitgelieferten Header-Datei nicht enthalten sind, oder Sie möchten einfach die Syntax der Funktionen in der Header-Datei mit der Bibliothek vergleichen. Ich habe dazu einen Artikel auf codeproject.com gefunden, das Projekt funktionierte für mich allerdings nicht so gut, deshalb stelle ich hier meine C#-Implementierung zur Verfügung.

Mit dem hier vorgestellten Beispielcode können LIB Dateien erforscht werden und Funktionen in .h-Dateien exportiert werden - was für Visual C++-Entwickler nützlich sein könnte. Da das LIB-Format in seiner Ausprägung variiert, gibt es bezüglich dem universellen Einsatz einige Einschränkungen.

LIB-Datei mit ArchiveFileReader einlesen

Im Namespace MatthiasF.LibViewer.Core befindet sich die Klasse ArchiveFileReader, mit dem LIB-Dateien gelesen werden können. Folgender Schnipsel zeigt die Verwendung von ArchiveFileReader.

static ArchiveFile LoadLibrary(string path)
{
  var fi = new FileInfo(path)
  using (FileStream fs = fi.OpenRead())
  { using (ArchiveFileReader afr = new ArchiveFileReader(fs))
    { afr.UndecorateSymbols = true;
      ArchiveFile af;
      if ((af = afr.Read()) != null))
        return af;

    }
  }
}

Exportieren von Funktionen

Nachdem die Datei als ArchiveFile-Objekt geladen wurde, können die Funktionen einfach in eine .h-Datei exportiert werden.

static void ExportSymbols(string libPath, string hdrFilename)
{
  ArchiveFile af;
  if ((af = LoadLibrary(libPath)) != null)
    af.ExportHeader(hdrFilename);

}

Behind the scenes

Wenn man sich nach dem Laden der Linker-Member die Liste der Symbole anschaut, kann man feststellen, dass an einige Funktionsnamen seltsame Zeichenfolgen angehängt sind; diese sogenannten "Mangeled Names" werden vom C++-Compiler erzeugt und beinhalten Typ-Informationen.

In C++ wird das Überladen von Funktionen unterstützt, als das auch gleichnamige Funktionen in verschiedenen Klassen vorkommen können, sodass der Linker diese Informationen benötigt, um Aufrufe typsicher verbinden zu können. C-Compiler benötigen dies nicht, da ein Symbol nicht mehrfach existieren kann. In einer Header-Datei sind Funktionen undekoriert zu finden - vor dem Export der Symbole in eine .h-Datei müssen diese also wieder in lesbare Bezeichnungen konvertiert werden.

Mangeled Names auflösen

Unter Links finden Sie den Artikel "C++ Name Mangling/Demangling", der das Mangling/Demangling von Symbolen ausführlich beschreibt. Wer (wie ich) die Symbole nicht selbst wandeln möchte, kann das DBGHELP-API verwenden.

[DllImport("dbghelp.dll", EntryPoint="UnDecorateSymbolName", CharSet=CharSet.Ansi)]
static extern Int32 UnDecorateSymbolName([MarshalAs(UnmanagedType.LPStr)] string DecoratedName, IntPtr UnDecoratedName, int UndecoratedLength, int Flags);

static IEnumerable<string> UndecorateSymbolsInternal(LinkerMember linkerMember)
{
  IDictionaryEnumerator ide;
  if ((ide = linkerMember.Symbols.GetEnumerator()) != null)
  {
    IntPtr p = Marshal.AllocHGlobal(4096);
    if (p.Equals(IntPtr.Zero) == false)
    { 
      try
      {
        while (ide.MoveNext())
        {
          string symbolName = ( string ) ide.Value;
          Int32 cch = 0;
          if ((cch = UnDecorateSymbolName(symbolName, p, 4096, 0x0)) == 0)
            yield return symbolName;

          else
          { 
            string undecoratedName = Marshal.PtrToStringAnsi(p, cch);
            yield return undecoratedName;
          }
        }
      }
      finally { Marshal.FreeHGlobal(p); }
    }
  }
}

Einschränkungen

Zunächst einmal hat es den Anschein, dass eine LIB in ihrem Format jeder anderen LIB gleich ist. Dies ist allerdings nicht der Fall, da jeder Compiler unterschiedliche LIB-Typen erzeugen und ein eigenes Name-Mangeling vornehmen kann, sodass der hier vorgestellte Code (Assembly-Projekt) keinesfalls universell einsetzbar ist. Ich habe das Werkzeug mit Hilfe der von Microsoft veröffentlichten PE/COFF-Spezifikation erstellt und mit beliebigen Bibliotheken getestet.

Funktionsparameter von C-Bibliotheken können (leider) nicht gewonnen werden.

Download

Ich stelle das Beispielprojekt hier für alle Interessierten zum Download zur Verfügung - erhebe allerdings keinen Anspruch auf Vollständigkeit der Implementierung, noch kann ich dafür garantieren, dass diese fehlerfrei ist. Viel Spass damit.

Links

Code-Project Artikel: "A tool to view a LIB"
http://www.codeproject.com/KB/debug/LibView.aspx
LIB-Viewer Projekt auf codeproject.com

Specification: Microsoft Portable Executable and Common Object File Format Specification
http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx
This document specifies the structure of executable (image) files and object files under the Microsoft Windows family of operating systems.

Tutorial: C++ Name Mangling/Demangling
http://www.kegel.com/mangle.html
Website of Dan Kegel

Sample: Visual C# Beispielprojekt
Downloads/libcore.zip
Visual Studio 2005 Solution