diff --git a/include/llvm/Object/COFF.h b/include/llvm/Object/COFF.h index 431029045f0..f9d0c6e525a 100644 --- a/include/llvm/Object/COFF.h +++ b/include/llvm/Object/COFF.h @@ -24,7 +24,9 @@ namespace llvm { namespace object { class ImportDirectoryEntryRef; +class ExportDirectoryEntryRef; typedef content_iterator import_directory_iterator; +typedef content_iterator export_directory_iterator; /// The DOS compatible header at the front of all PE/COFF executables. struct dos_header { @@ -245,6 +247,7 @@ struct coff_aux_section_definition { class COFFObjectFile : public ObjectFile { private: friend class ImportDirectoryEntryRef; + friend class ExportDirectoryEntryRef; const coff_file_header *COFFHeader; const pe32_header *PE32Header; const data_directory *DataDirectory; @@ -254,6 +257,7 @@ private: uint32_t StringTableSize; const import_directory_table_entry *ImportDirectory; uint32_t NumberOfImportDirectory; + const export_directory_table_entry *ExportDirectory; error_code getString(uint32_t offset, StringRef &Res) const; @@ -263,6 +267,7 @@ private: error_code initSymbolTablePtr(); error_code initImportTablePtr(); + error_code initExportTablePtr(); protected: virtual error_code getSymbolNext(DataRefImpl Symb, SymbolRef &Res) const; @@ -336,6 +341,8 @@ public: import_directory_iterator import_directory_begin() const; import_directory_iterator import_directory_end() const; + export_directory_iterator export_directory_begin() const; + export_directory_iterator export_directory_end() const; error_code getHeader(const coff_file_header *&Res) const; error_code getCOFFHeader(const coff_file_header *&Res) const; @@ -388,6 +395,26 @@ private: uint32_t Index; const COFFObjectFile *OwningObject; }; + +// The iterator for the export directory table entry. +class ExportDirectoryEntryRef { +public: + ExportDirectoryEntryRef() : OwningObject(0) {} + ExportDirectoryEntryRef(const export_directory_table_entry *Table, uint32_t I, + const COFFObjectFile *Owner) + : ExportTable(Table), Index(I), OwningObject(Owner) {} + + bool operator==(const ExportDirectoryEntryRef &Other) const; + error_code getNext(ExportDirectoryEntryRef &Result) const; + error_code getOrdinal(uint32_t &Result) const; + error_code getExportRVA(uint32_t &Result) const; + error_code getName(StringRef &Result) const; + +private: + const export_directory_table_entry *ExportTable; + uint32_t Index; + const COFFObjectFile *OwningObject; +}; } // end namespace object } // end namespace llvm diff --git a/lib/Object/COFFObjectFile.cpp b/lib/Object/COFFObjectFile.cpp index dc8a718c7a3..4ad2e99629d 100644 --- a/lib/Object/COFFObjectFile.cpp +++ b/lib/Object/COFFObjectFile.cpp @@ -445,9 +445,27 @@ error_code COFFObjectFile::initImportTablePtr() { return ec; ImportDirectory = reinterpret_cast< const import_directory_table_entry *>(IntPtr); + return object_error::success; +} - // It's an error if there's no section containing the Import Table RVA. - return object_error::parse_failed; +// Find the export table. +error_code COFFObjectFile::initExportTablePtr() { + // First, we get the RVA of the export table. If the file lacks a pointer to + // the export table, do nothing. + const data_directory *DataEntry; + if (getDataDirectory(COFF::EXPORT_TABLE, DataEntry)) + return object_error::success; + + // Do nothing if the pointer to export table is NULL. + if (DataEntry->RelativeVirtualAddress == 0) + return object_error::success; + + uint32_t ExportTableRva = DataEntry->RelativeVirtualAddress; + uintptr_t IntPtr = 0; + if (error_code EC = getRvaPtr(ExportTableRva, IntPtr)) + return EC; + ExportDirectory = reinterpret_cast(IntPtr); + return object_error::success; } COFFObjectFile::COFFObjectFile(MemoryBuffer *Object, error_code &ec) @@ -460,7 +478,8 @@ COFFObjectFile::COFFObjectFile(MemoryBuffer *Object, error_code &ec) , StringTable(0) , StringTableSize(0) , ImportDirectory(0) - , NumberOfImportDirectory(0) { + , NumberOfImportDirectory(0) + , ExportDirectory(0) { // Check that we at least have enough room for a header. if (!checkSize(Data, ec, sizeof(coff_file_header))) return; @@ -521,6 +540,10 @@ COFFObjectFile::COFFObjectFile(MemoryBuffer *Object, error_code &ec) if ((ec = initImportTablePtr())) return; + // Initialize the pointer to the export table. + if ((ec = initExportTablePtr())) + return; + ec = object_error::success; } @@ -572,6 +595,19 @@ import_directory_iterator COFFObjectFile::import_directory_end() const { ImportDirectoryEntryRef(ImportDirectory, NumberOfImportDirectory, this)); } +export_directory_iterator COFFObjectFile::export_directory_begin() const { + return export_directory_iterator( + ExportDirectoryEntryRef(ExportDirectory, 0, this)); +} + +export_directory_iterator COFFObjectFile::export_directory_end() const { + if (ExportDirectory == 0) + return export_directory_iterator(ExportDirectoryEntryRef(0, 0, this)); + ExportDirectoryEntryRef ref(ExportDirectory, + ExportDirectory->AddressTableEntries, this); + return export_directory_iterator(ref); +} + section_iterator COFFObjectFile::begin_sections() const { DataRefImpl ret; ret.p = reinterpret_cast(SectionTable); @@ -910,6 +946,62 @@ error_code ImportDirectoryEntryRef::getImportLookupEntry( return object_error::success; } +bool ExportDirectoryEntryRef:: +operator==(const ExportDirectoryEntryRef &Other) const { + return ExportTable == Other.ExportTable && Index == Other.Index; +} + +error_code +ExportDirectoryEntryRef::getNext(ExportDirectoryEntryRef &Result) const { + Result = ExportDirectoryEntryRef(ExportTable, Index + 1, OwningObject); + return object_error::success; +} + +// Returns the export ordinal of the current export symbol. +error_code ExportDirectoryEntryRef::getOrdinal(uint32_t &Result) const { + Result = ExportTable->OrdinalBase + Index; + return object_error::success; +} + +// Returns the address of the current export symbol. +error_code ExportDirectoryEntryRef::getExportRVA(uint32_t &Result) const { + uintptr_t IntPtr = 0; + if (error_code EC = OwningObject->getRvaPtr( + ExportTable->ExportAddressTableRVA, IntPtr)) + return EC; + const export_address_table_entry *entry = reinterpret_cast(IntPtr); + Result = entry[Index].ExportRVA; + return object_error::success; +} + +// Returns the name of the current export symbol. If the symbol is exported only +// by ordinal, the empty string is set as a result. +error_code ExportDirectoryEntryRef::getName(StringRef &Result) const { + uintptr_t IntPtr = 0; + if (error_code EC = OwningObject->getRvaPtr( + ExportTable->OrdinalTableRVA, IntPtr)) + return EC; + const ulittle16_t *Start = reinterpret_cast(IntPtr); + + uint32_t NumEntries = ExportTable->NumberOfNamePointers; + int Offset = 0; + for (const ulittle16_t *I = Start, *E = Start + NumEntries; + I < E; ++I, ++Offset) { + if (*I != Index) + continue; + if (error_code EC = OwningObject->getRvaPtr( + ExportTable->NamePointerRVA, IntPtr)) + return EC; + const ulittle32_t *NamePtr = reinterpret_cast(IntPtr); + if (error_code EC = OwningObject->getRvaPtr(NamePtr[Offset], IntPtr)) + return EC; + Result = StringRef(reinterpret_cast(IntPtr)); + return object_error::success; + } + Result = ""; + return object_error::success; +} + namespace llvm { ObjectFile *ObjectFile::createCOFFObjectFile(MemoryBuffer *Object) { error_code ec; diff --git a/test/tools/llvm-objdump/Inputs/export.dll.coff-i386 b/test/tools/llvm-objdump/Inputs/export.dll.coff-i386 new file mode 100644 index 00000000000..7b9371668de Binary files /dev/null and b/test/tools/llvm-objdump/Inputs/export.dll.coff-i386 differ diff --git a/test/tools/llvm-objdump/coff-private-headers.test b/test/tools/llvm-objdump/coff-private-headers.test index d36c148cec6..bc16443bbaf 100644 --- a/test/tools/llvm-objdump/coff-private-headers.test +++ b/test/tools/llvm-objdump/coff-private-headers.test @@ -1,9 +1,16 @@ -// RUN: llvm-objdump -p %p/Inputs/nop.exe.coff-i386 | FileCheck %s +// RUN: llvm-objdump -p %p/Inputs/nop.exe.coff-i386 | \ +// RUN: FileCheck -check-prefix=IMPORT %s -CHECK: The Import Tables: -CHECK-NEXT: lookup 00005028 time 00000000 fwd 00000000 name 00005096 addr 00005058 -CHECK: DLL Name: KERNEL32.dll -CHECK-NEXT: Hint/Ord Name -CHECK-NEXT: 365 ExitProcess +IMPORT: The Import Tables: +IMPORT-NEXT: lookup 00005028 time 00000000 fwd 00000000 name 00005096 addr 00005058 +IMPORT: DLL Name: KERNEL32.dll +IMPORT-NEXT: Hint/Ord Name +IMPORT-NEXT: 365 ExitProcess +// RUN: llvm-objdump -p %p/Inputs/export.dll.coff-i386 | \ +// RUN: FileCheck -check-prefix=EXPORT %s +EXPORT: Export Table: +EXPORT-NEXT: Ordinal RVA Name +EXPORT-NEXT: 5 0x2008 +EXPORT-NEXT: 6 0x2010 exportfn2 diff --git a/tools/llvm-objdump/COFFDump.cpp b/tools/llvm-objdump/COFFDump.cpp index 9c380100e8c..df9c2cf2dac 100644 --- a/tools/llvm-objdump/COFFDump.cpp +++ b/tools/llvm-objdump/COFFDump.cpp @@ -271,6 +271,36 @@ static void printImportTables(const COFFObjectFile *Obj) { } } +// Prints export tables. The export table is a table containing the list of +// exported symbol from the DLL. +static void printExportTable(const COFFObjectFile *Obj) { + outs() << "Export Table:\n"; + export_directory_iterator I = Obj->export_directory_begin(); + export_directory_iterator E = Obj->export_directory_end(); + if (I == E) + return; + outs() << " Ordinal RVA Name\n"; + error_code EC; + for (; I != E; I = I.increment(EC)) { + if (EC) + return; + uint32_t Ordinal; + if (I->getOrdinal(Ordinal)) + return; + uint32_t RVA; + if (I->getExportRVA(RVA)) + return; + outs() << format(" % 4d %# 8x", Ordinal, RVA); + + StringRef Name; + if (I->getName(Name)) + continue; + if (!Name.empty()) + outs() << " " << Name; + outs() << "\n"; + } +} + void llvm::printCOFFUnwindInfo(const COFFObjectFile *Obj) { const coff_file_header *Header; if (error(Obj->getCOFFHeader(Header))) return; @@ -399,5 +429,7 @@ void llvm::printCOFFUnwindInfo(const COFFObjectFile *Obj) { } void llvm::printCOFFFileHeader(const object::ObjectFile *Obj) { - printImportTables(dyn_cast(Obj)); + const COFFObjectFile *file = dyn_cast(Obj); + printImportTables(file); + printExportTable(file); }