[lld] r246292 - COFF: Create short import files instead of using lib.exe.

Rui Ueyama via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 28 03:52:05 PDT 2015


Author: ruiu
Date: Fri Aug 28 05:52:05 2015
New Revision: 246292

URL: http://llvm.org/viewvc/llvm-project?rev=246292&view=rev
Log:
COFF: Create short import files instead of using lib.exe.

lib.exe has a feature to create import library files (which contain
short import files) from module-definition files. Previously, we were
using that feature, but it turned out that the feature is not complete
for us.

There seems no way to specify "Import Types" in module-definition file.
lib.exe always adds "_" to given symbols and specify IMPORT_NAME_UNDECORATE.
We need more fine-grainded control on that value.

This patch teaches LLD to create short import files itself.
We are still using lib.exe, but the use of the tool is limited to create
empty import library files. We then create short import files and add them
to the empty files as new members.

This patch does not intend to change the functionality. LLD produces
the same import libraries as before. I'll make another change to create
different import libraries in a follow-up patch.

Modified:
    lld/trunk/COFF/DriverUtils.cpp

Modified: lld/trunk/COFF/DriverUtils.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/DriverUtils.cpp?rev=246292&r1=246291&r2=246292&view=diff
==============================================================================
--- lld/trunk/COFF/DriverUtils.cpp (original)
+++ lld/trunk/COFF/DriverUtils.cpp Fri Aug 28 05:52:05 2015
@@ -20,6 +20,8 @@
 #include "llvm/ADT/Optional.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringSwitch.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/ArchiveWriter.h"
 #include "llvm/Object/COFF.h"
 #include "llvm/Option/Arg.h"
 #include "llvm/Option/ArgList.h"
@@ -479,56 +481,144 @@ static std::string writeToTempFile(Strin
   return Path.str();
 }
 
-/// Creates a .def file containing the list of exported symbols.
-static std::string createModuleDefinitionFile() {
-  std::string S;
-  llvm::raw_string_ostream OS(S);
-  OS << "  LIBRARY \"" << llvm::sys::path::filename(Config->OutputFile) << "\"\n"
-     << "  EXPORTS\n";
-  for (Export &E : Config->Exports) {
-    OS << "    " << E.ExtLibName;
-    if (E.Ordinal > 0)
-      OS << "   @" << E.Ordinal;
-    if (E.Noname)
-      OS << "   NONAME";
-    if (E.Data)
-      OS << "   DATA";
-    if (E.Private)
-      OS << "   PRIVATE";
-    OS << "\n";
-  }
-  OS.flush();
-  return S;
+void touchFile(StringRef Path) {
+  int FD;
+  std::error_code EC = sys::fs::openFileForWrite(Path, FD, sys::fs::F_Append);
+  error(EC, "failed to create a file");
+  sys::Process::SafelyCloseFileDescriptor(FD);
 }
 
-// Creates a .def file and runs lib.exe on it to create an import library.
-void writeImportLibrary() {
-  std::string Contents = createModuleDefinitionFile();
-  std::string Def = writeToTempFile(Contents);
-  llvm::FileRemover TempFile(Def);
-
-  if (Config->Verbose)
-    llvm::outs() << "Module-definition file:" << Contents;
+static std::string getImplibPath() {
+  if (!Config->Implib.empty())
+    return Config->Implib;
+  SmallString<128> Out = StringRef(Config->OutputFile);
+  sys::path::replace_extension(Out, ".lib");
+  return Out.str();
+}
+
+static std::unique_ptr<MemoryBuffer> createEmptyImportLibrary() {
+  std::string S = (Twine("LIBRARY \"") +
+                   llvm::sys::path::filename(Config->OutputFile) + "\"\n")
+                      .str();
+  std::string Path1 = writeToTempFile(S);
+  std::string Path2 = getImplibPath();
+  llvm::FileRemover Remover1(Path1);
+  llvm::FileRemover Remover2(Path2);
 
   Executor E("lib.exe");
   E.add("/nologo");
   E.add("/machine:" + machineToStr(Config->Machine));
-  E.add(Twine("/def:") + Def);
-  if (Config->Implib.empty()) {
-    SmallString<128> Out = StringRef(Config->OutputFile);
-    sys::path::replace_extension(Out, ".lib");
-    E.add("/out:" + Out);
-  } else {
-    E.add("/out:" + Config->Implib);
-  }
+  E.add(Twine("/def:") + Path1);
+  E.add(Twine("/out:") + Path2);
   E.run();
-}
 
-void touchFile(StringRef Path) {
-  int FD;
-  std::error_code EC = sys::fs::openFileForWrite(Path, FD, sys::fs::F_Append);
-  error(EC, "failed to create a file");
-  sys::Process::SafelyCloseFileDescriptor(FD);
+  ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
+      MemoryBuffer::getFile(Path2, -1, false);
+  error(BufOrErr, Twine("Failed to open ") + Path2);
+  return MemoryBuffer::getMemBufferCopy((*BufOrErr)->getBuffer());
+}
+
+static std::vector<NewArchiveIterator>
+readMembers(const object::Archive &Archive) {
+  std::vector<NewArchiveIterator> V;
+  for (const object::Archive::Child &C : Archive.children()) {
+    ErrorOr<StringRef> NameOrErr = C.getName();
+    error(NameOrErr, "Archive::Child::getName failed");
+    V.emplace_back(C, *NameOrErr);
+  }
+  return V;
+}
+
+// This class creates short import files which is described in
+// PE/COFF spec 7. Import Library Format.
+class ShortImportCreator {
+public:
+  ShortImportCreator(object::Archive *A, StringRef S) : Parent(A), DLLName(S) {}
+
+  NewArchiveIterator create(const Export &E) {
+    std::string Sym = E.ExtLibName;
+    bool Decorated =
+        E.ExtLibName.startswith("?") || E.ExtLibName.startswith("@");
+    if (Config->Machine == I386 && !Decorated)
+      Sym = (Twine("_") + Sym).str();
+    size_t ImpSize = DLLName.size() + Sym.size() + 2; // +2 for NULs
+    size_t Size = sizeof(object::ArchiveMemberHeader) +
+                  sizeof(coff_import_header) + ImpSize;
+    char *Buf = Alloc.Allocate<char>(Size);
+    memset(Buf, 0, Size);
+    char *P = Buf;
+
+    // Write archive member header
+    auto *Hdr = reinterpret_cast<object::ArchiveMemberHeader *>(P);
+    P += sizeof(*Hdr);
+    sprintf(Hdr->Name, "%-12s", "dummy");
+    sprintf(Hdr->LastModified, "%-12d", 0);
+    sprintf(Hdr->UID, "%-6d", 0);
+    sprintf(Hdr->GID, "%-6d", 0);
+    sprintf(Hdr->AccessMode, "%-8d", 0644);
+    sprintf(Hdr->Size, "%-10d", int(sizeof(coff_import_header) + ImpSize));
+
+    // Write short import library.
+    auto *Imp = reinterpret_cast<coff_import_header *>(P);
+    P += sizeof(*Imp);
+    Imp->Sig2 = 0xFFFF;
+    Imp->Machine = Config->Machine;
+    Imp->SizeOfData = ImpSize;
+    if (E.Ordinal > 0)
+      Imp->OrdinalHint = E.Ordinal;
+    Imp->TypeInfo = (E.Data ? IMPORT_DATA : IMPORT_CODE);
+    Imp->TypeInfo = IMPORT_CODE;
+    if (E.Noname) {
+      Imp->TypeInfo |= IMPORT_ORDINAL << 2;
+    } else if (Decorated) {
+      Imp->TypeInfo |= IMPORT_NAME << 2;
+    } else {
+      Imp->TypeInfo |= IMPORT_NAME_NOPREFIX << 2;
+    }
+
+    // Write symbol name and DLL name.
+    sprintf(P, Sym.data(), Sym.size());
+    P += Sym.size() + 1;
+    sprintf(P, DLLName.data(), DLLName.size());
+
+    object::Archive::Child C(Parent, Buf);
+    return NewArchiveIterator(C, nextFilename());
+  }
+
+private:
+  char *nextFilename() {
+    char *P = Alloc.Allocate<char>(16);
+    sprintf(P, "%d.obj", Idx++);
+    return P;
+  }
+
+  BumpPtrAllocator Alloc;
+  object::Archive *Parent;
+  StringRef DLLName;
+  int Idx = 1;
+};
+
+// Creates an import library for a DLL. In this function, we first
+// create an empty import library using lib.exe and then adds short
+// import files to that file.
+void writeImportLibrary() {
+  std::unique_ptr<MemoryBuffer> Buf = createEmptyImportLibrary();
+  std::error_code EC;
+  object::Archive Archive(Buf->getMemBufferRef(), EC);
+  error(EC, "Error reading an empty import file");
+  std::vector<NewArchiveIterator> Members = readMembers(Archive);
+
+  std::string DLLName = llvm::sys::path::filename(Config->OutputFile);
+  ShortImportCreator ShortImport(&Archive, DLLName);
+  for (Export &E : Config->Exports)
+    if (!E.Private)
+      Members.push_back(ShortImport.create(E));
+
+  std::string Path = getImplibPath();
+  std::pair<StringRef, std::error_code> Result =
+      writeArchive(Path, Members, /*WriteSymtab*/ true, object::Archive::K_GNU,
+                   /*Deterministic*/ true, /*Thin*/ false);
+  error(Result.second, Twine("Failed to write ") + Path);
 }
 
 // Create OptTable




More information about the llvm-commits mailing list