diff --git a/include/llvm/Object/XCOFFObjectFile.h b/include/llvm/Object/XCOFFObjectFile.h index caa792d6b7f..36429b989d7 100644 --- a/include/llvm/Object/XCOFFObjectFile.h +++ b/include/llvm/Object/XCOFFObjectFile.h @@ -71,7 +71,6 @@ private: const XCOFFSectionHeader *toSection(DataRefImpl Ref) const; - uint16_t getNumberOfSections() const; public: void moveSymbolNext(DataRefImpl &Symb) const override; @@ -122,6 +121,18 @@ public: XCOFFObjectFile(MemoryBufferRef Object, std::error_code &EC); const XCOFFFileHeader *getFileHeader() const { return FileHdrPtr; } + + uint16_t getMagic() const; + uint16_t getNumberOfSections() const; + int32_t getTimeStamp() const; + uint32_t getSymbolTableOffset() const; + + // Note that this value is signed and might return a negative value. Negative + // values are reserved for future use. + int32_t getNumberOfSymbolTableEntries() const; + + uint16_t getOptionalHeaderSize() const; + uint16_t getFlags() const; }; // XCOFFObjectFile } // namespace object diff --git a/lib/Object/XCOFFObjectFile.cpp b/lib/Object/XCOFFObjectFile.cpp index bc982593f0b..2a456538966 100644 --- a/lib/Object/XCOFFObjectFile.cpp +++ b/lib/Object/XCOFFObjectFile.cpp @@ -69,10 +69,6 @@ size_t XCOFFObjectFile::getSectionHeaderSize() const { return sizeof(XCOFFSectionHeader); } -uint16_t XCOFFObjectFile::getNumberOfSections() const { - return FileHdrPtr->NumberOfSections; -} - void XCOFFObjectFile::moveSymbolNext(DataRefImpl &Symb) const { llvm_unreachable("Not yet implemented!"); return; @@ -247,9 +243,9 @@ section_iterator XCOFFObjectFile::section_end() const { } uint8_t XCOFFObjectFile::getBytesInAddress() const { - uint8_t Result = 0; - llvm_unreachable("Not yet implemented!"); - return Result; + // Only support 32-bit object files for now ... + assert(getFileHeaderSize() == XCOFF32FileHeaderSize); + return 4; } StringRef XCOFFObjectFile::getFileFormatName() const { @@ -300,6 +296,37 @@ XCOFFObjectFile::XCOFFObjectFile(MemoryBufferRef Object, std::error_code &EC) } } +uint16_t XCOFFObjectFile::getMagic() const { + return FileHdrPtr->Magic; +} + +uint16_t XCOFFObjectFile::getNumberOfSections() const { + return FileHdrPtr->NumberOfSections; +} + +int32_t XCOFFObjectFile::getTimeStamp() const { + return FileHdrPtr->TimeStamp; +} + +uint32_t XCOFFObjectFile::getSymbolTableOffset() const { + return FileHdrPtr->SymbolTableOffset; +} + +int32_t XCOFFObjectFile::getNumberOfSymbolTableEntries() const { + // As far as symbol table size is concerned, if this field is negative it is + // to be treated as a 0. However since this field is also used for printing we + // don't want to truncate any negative values. + return FileHdrPtr->NumberOfSymTableEntries; +} + +uint16_t XCOFFObjectFile::getOptionalHeaderSize() const { + return FileHdrPtr->AuxHeaderSize; +} + +uint16_t XCOFFObjectFile::getFlags() const { + return FileHdrPtr->Flags; +} + Expected> ObjectFile::createXCOFFObjectFile(MemoryBufferRef Object) { StringRef Data = Object.getBuffer(); diff --git a/test/tools/llvm-readobj/Inputs/xcoff-basic-neg-sym-count.o b/test/tools/llvm-readobj/Inputs/xcoff-basic-neg-sym-count.o new file mode 100644 index 00000000000..e7f63664acd Binary files /dev/null and b/test/tools/llvm-readobj/Inputs/xcoff-basic-neg-sym-count.o differ diff --git a/test/tools/llvm-readobj/Inputs/xcoff-basic-neg-time.o b/test/tools/llvm-readobj/Inputs/xcoff-basic-neg-time.o new file mode 100644 index 00000000000..f814965f4f0 Binary files /dev/null and b/test/tools/llvm-readobj/Inputs/xcoff-basic-neg-time.o differ diff --git a/test/tools/llvm-readobj/Inputs/xcoff-basic.o b/test/tools/llvm-readobj/Inputs/xcoff-basic.o new file mode 100644 index 00000000000..c84056bdb00 Binary files /dev/null and b/test/tools/llvm-readobj/Inputs/xcoff-basic.o differ diff --git a/test/tools/llvm-readobj/xcoff-basic.test b/test/tools/llvm-readobj/xcoff-basic.test new file mode 100644 index 00000000000..c361085f599 --- /dev/null +++ b/test/tools/llvm-readobj/xcoff-basic.test @@ -0,0 +1,66 @@ +# RUN: llvm-readobj --file-header %p/Inputs/xcoff-basic.o | \ +# RUN: FileCheck --check-prefix=FILEHEADER %s + +# RUN: llvm-readobj --file-header %p/Inputs/xcoff-basic-neg-time.o | \ +# RUN: FileCheck --check-prefix=NEGTIME %s + +# RUN: llvm-readobj --file-header %p/Inputs/xcoff-basic-neg-sym-count.o | \ +# RUN: FileCheck --check-prefix=NEGSYMCOUNT %s + +# FILEHEADER: File: {{.*}}xcoff-basic.o +# FILEHEADER-NEXT: Format: aixcoff-rs6000 +# FILEHEADER-NEXT: Arch: powerpc +# FILEHEADER-NEXT: AddressSize: 32bit +# FILEHEADER-NEXT: FileHeader { +# FILEHEADER-NEXT: Magic: 0x1DF +# FILEHEADER-NEXT: NumberOfSections: 6 +# FILEHEADER-NEXT: TimeStamp: 2019-03-12T14:04:43Z (0x5C87BC7B) +# FILEHEADER-NEXT: SymbolTableOffset: 0x52E +# FILEHEADER-NEXT: SymbolTableEntries: 120 +# FILEHEADER-NEXT: OptionalHeaderSize: 0x1C +# FILEHEADER-NEXT: Flags: 0x0 +# FILEHEADER-NEXT: } + +# NEGTIME: File: {{.*}}xcoff-basic-neg-time.o +# NEGTIME-NEXT: Format: aixcoff-rs6000 +# NEGTIME-NEXT: Arch: powerpc +# NEGTIME-NEXT: AddressSize: 32bit +# NEGTIME-NEXT: FileHeader { +# NEGTIME-NEXT: Magic: 0x1DF +# NEGTIME-NEXT: NumberOfSections: 6 +# NEGTIME-NEXT: TimeStamp: Reserved Value (0xDC87BC7B) +# NEGTIME-NEXT: SymbolTableOffset: 0x52E +# NEGTIME-NEXT: SymbolTableEntries: 120 +# NEGTIME-NEXT: OptionalHeaderSize: 0x1C +# NEGTIME-NEXT: Flags: 0x0 +# NEGTIME-NEXT: } + +# NEGSYMCOUNT: File: {{.*}}xcoff-basic-neg-sym-count.o +# NEGSYMCOUNT-NEXT: Format: aixcoff-rs6000 +# NEGSYMCOUNT-NEXT: Arch: powerpc +# NEGSYMCOUNT-NEXT: AddressSize: 32bit +# NEGSYMCOUNT-NEXT: FileHeader { +# NEGSYMCOUNT-NEXT: Magic: 0x1DF +# NEGSYMCOUNT-NEXT: NumberOfSections: 5 +# NEGSYMCOUNT-NEXT: TimeStamp: 2019-03-12T14:04:43Z (0x5C87BC7B) +# NEGSYMCOUNT-NEXT: SymbolTableOffset: 0x0 +# NEGSYMCOUNT-NEXT: SymbolTableEntries: Reserved Value (0x80000000) +# NEGSYMCOUNT-NEXT: OptionalHeaderSize: 0x1C +# NEGSYMCOUNT-NEXT: Flags: 0xD +# NEGSYMCOUNT-NEXT: } + +# xcoff-basic.o was compiled with `xlc -qtls -O3 -g -c xcoff-basic.c` +# from the following source: +# int a = 55; +# int b; +# __thread int j = 55; +# __thread double d; +# int A() { return a; } +# int B() { return b; } +# int J() { return j; } +# double D() { return d; } +# +# xcoff-basic-neg-time.o was manually edited to include a negative time stamp. +# xcoff-basic-neg-sym-count.o was stripped using the 'strip' utility, and +# manually edited to have a negative symbol table entry count. + diff --git a/tools/llvm-readobj/CMakeLists.txt b/tools/llvm-readobj/CMakeLists.txt index 389e348bc4d..7c636ef421f 100644 --- a/tools/llvm-readobj/CMakeLists.txt +++ b/tools/llvm-readobj/CMakeLists.txt @@ -22,6 +22,7 @@ add_llvm_tool(llvm-readobj WasmDumper.cpp Win64EHDumper.cpp WindowsResourceDumper.cpp + XCOFFDumper.cpp ) add_llvm_tool_symlink(llvm-readelf llvm-readobj) diff --git a/tools/llvm-readobj/ObjDumper.h b/tools/llvm-readobj/ObjDumper.h index 5767485a192..0bfce2ef3e4 100644 --- a/tools/llvm-readobj/ObjDumper.h +++ b/tools/llvm-readobj/ObjDumper.h @@ -133,6 +133,10 @@ std::error_code createWasmDumper(const object::ObjectFile *Obj, ScopedPrinter &Writer, std::unique_ptr &Result); +std::error_code createXCOFFDumper(const object::ObjectFile *Obj, + ScopedPrinter &Writer, + std::unique_ptr &Result); + void dumpCOFFImportFile(const object::COFFImportFile *File, ScopedPrinter &Writer); diff --git a/tools/llvm-readobj/XCOFFDumper.cpp b/tools/llvm-readobj/XCOFFDumper.cpp new file mode 100644 index 00000000000..009b99c1f7b --- /dev/null +++ b/tools/llvm-readobj/XCOFFDumper.cpp @@ -0,0 +1,121 @@ +//===-- XCOFFDumper.cpp - XCOFF dumping utility -----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements an XCOFF specific dumper for llvm-readobj. +// +//===----------------------------------------------------------------------===// + +#include "Error.h" +#include "ObjDumper.h" +#include "llvm-readobj.h" +#include "llvm/Object/XCOFFObjectFile.h" +#include "llvm/Support/ScopedPrinter.h" + +using namespace llvm; +using namespace object; + +namespace { + +class XCOFFDumper : public ObjDumper { +public: + XCOFFDumper(const XCOFFObjectFile &Obj, ScopedPrinter &Writer) + : ObjDumper(Writer), Obj(Obj) {} + + void printFileHeaders() override; + void printSectionHeaders() override; + void printRelocations() override; + void printSymbols() override; + void printDynamicSymbols() override; + void printUnwindInfo() override; + void printStackMap() const override; + void printNeededLibraries() override; + +private: + const XCOFFObjectFile &Obj; +}; +} // anonymous namespace + +void XCOFFDumper::printFileHeaders() { + DictScope DS(W, "FileHeader"); + W.printHex("Magic", Obj.getMagic()); + W.printNumber("NumberOfSections", Obj.getNumberOfSections()); + + // Negative timestamp values are reserved for future use. + int32_t TimeStamp = Obj.getTimeStamp(); + if (TimeStamp > 0) { + // This handling of the time stamp assumes that the host system's time_t is + // compatible with AIX time_t. If a platform is not compatible, the lit + // tests will let us know. + time_t TimeDate = TimeStamp; + + char FormattedTime[21] = {}; + size_t BytesWritten = + strftime(FormattedTime, 21, "%Y-%m-%dT%H:%M:%SZ", gmtime(&TimeDate)); + if (BytesWritten) + W.printHex("TimeStamp", FormattedTime, TimeStamp); + else + W.printHex("Timestamp", TimeStamp); + } else { + W.printHex("TimeStamp", TimeStamp == 0 ? "None" : "Reserved Value", + TimeStamp); + } + + W.printHex("SymbolTableOffset", Obj.getSymbolTableOffset()); + int32_t SymTabEntries = Obj.getNumberOfSymbolTableEntries(); + if (SymTabEntries >= 0) + W.printNumber("SymbolTableEntries", SymTabEntries); + else + W.printHex("SymbolTableEntries", "Reserved Value", SymTabEntries); + + W.printHex("OptionalHeaderSize", Obj.getOptionalHeaderSize()); + W.printHex("Flags", Obj.getFlags()); + + // TODO FIXME Add support for the auxiliary header (if any) once + // XCOFFObjectFile has the necessary support. +} + +void XCOFFDumper::printSectionHeaders() { + llvm_unreachable("Unimplemented functionality for XCOFFDumper"); +} + +void XCOFFDumper::printRelocations() { + llvm_unreachable("Unimplemented functionality for XCOFFDumper"); +} + +void XCOFFDumper::printSymbols() { + llvm_unreachable("Unimplemented functionality for XCOFFDumper"); +} + +void XCOFFDumper::printDynamicSymbols() { + llvm_unreachable("Unimplemented functionality for XCOFFDumper"); +} + +void XCOFFDumper::printUnwindInfo() { + llvm_unreachable("Unimplemented functionality for XCOFFDumper"); +} + +void XCOFFDumper::printStackMap() const { + llvm_unreachable("Unimplemented functionality for XCOFFDumper"); +} + +void XCOFFDumper::printNeededLibraries() { + llvm_unreachable("Unimplemented functionality for XCOFFDumper"); +} + +namespace llvm { +std::error_code createXCOFFDumper(const object::ObjectFile *Obj, + ScopedPrinter &Writer, + std::unique_ptr &Result) { + const XCOFFObjectFile *XObj = dyn_cast(Obj); + if (!XObj) + return readobj_error::unsupported_obj_file_format; + + Result.reset(new XCOFFDumper(*XObj, Writer)); + return readobj_error::success; +} +} // namespace llvm diff --git a/tools/llvm-readobj/llvm-readobj.cpp b/tools/llvm-readobj/llvm-readobj.cpp index 89fddc7233c..e8cea5a7fff 100644 --- a/tools/llvm-readobj/llvm-readobj.cpp +++ b/tools/llvm-readobj/llvm-readobj.cpp @@ -440,6 +440,8 @@ static std::error_code createDumper(const ObjectFile *Obj, return createMachODumper(Obj, Writer, Result); if (Obj->isWasm()) return createWasmDumper(Obj, Writer, Result); + if (Obj->isXCOFF()) + return createXCOFFDumper(Obj, Writer, Result); return readobj_error::unsupported_obj_file_format; }