diff --git a/llvm/include/llvm/BinaryFormat/XCOFF.h b/llvm/include/llvm/BinaryFormat/XCOFF.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/BinaryFormat/XCOFF.h @@ -0,0 +1,44 @@ +//===-- llvm/BinaryFormat/XCOFF.h - The XCOFF file format -------*- 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 defines manifest constants for the XCOFF object file format. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_BINARYFORMAT_XCOFF_H +#define LLVM_BINARYFORMAT_XCOFF_H + +namespace llvm { +namespace XCOFF { + +// Constants used in the XCOFF definition. +enum { SectionNameSize = 8 }; + +// Flags for defining the section type. Used for the s_flags field of +// the section header structure. Defined in the system header `scnhdr.h`. +enum SectionTypeFlags { + STYP_PAD = 0x0008, + STYP_DWARF = 0x0010, + STYP_TEXT = 0x0020, + STYP_DATA = 0x0040, + STYP_BSS = 0x0080, + STYP_EXCEPT = 0x0100, + STYP_INFO = 0x0200, + STYP_TDATA = 0x0400, + STYP_TBSS = 0x0800, + STYP_LOADER = 0x1000, + STYP_DEBUG = 0x2000, + STYP_TYPCHK = 0x4000, + STYP_OVRFLO = 0x8000 +}; + +} // end namespace XCOFF +} // end namespace llvm + +#endif + diff --git a/llvm/include/llvm/Object/XCOFFObjectFile.h b/llvm/include/llvm/Object/XCOFFObjectFile.h --- a/llvm/include/llvm/Object/XCOFFObjectFile.h +++ b/llvm/include/llvm/Object/XCOFFObjectFile.h @@ -16,6 +16,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/iterator_range.h" #include "llvm/BinaryFormat/Magic.h" +#include "llvm/BinaryFormat/XCOFF.h" #include "llvm/MC/SubtargetFeature.h" #include "llvm/Object/Binary.h" #include "llvm/Object/Error.h" @@ -47,10 +48,30 @@ support::ubig16_t Flags; }; +struct XCOFFSectionHeader { + char Name[XCOFF::SectionNameSize]; + support::ubig32_t PhysicalAddress; + support::ubig32_t VirtualAddress; + support::ubig32_t SectionSize; + support::ubig32_t FileOffsetToRawData; + support::ubig32_t FileOffsetToRelocationInfo; + support::ubig32_t FileOffsetToLineNumberInfo; + support::ubig16_t NumberOfRelocations; + support::ubig16_t NumberOfLineNumbers; + support::big32_t Flags; +}; + class XCOFFObjectFile : public ObjectFile { private: const XCOFFFileHeader *FileHdrPtr = nullptr; + const XCOFFSectionHeader *SectionHdrTablePtr = nullptr; + + size_t getFileHeaderSize() const; + size_t getSectionHeaderSize() const; + const XCOFFSectionHeader *toSection(DataRefImpl Ref) const; + + uint16_t getNumberOfSections() const; public: void moveSymbolNext(DataRefImpl &Symb) const override; uint32_t getSymbolFlags(DataRefImpl Symb) const override; @@ -97,11 +118,11 @@ SubtargetFeatures getFeatures() const override; bool isRelocatableObject() const override; -public: XCOFFObjectFile(MemoryBufferRef Object, std::error_code &EC); const XCOFFFileHeader *getFileHeader() const { return FileHdrPtr; } + Expected getStartAddress() const override; }; // XCOFFObjectFile } // namespace object diff --git a/llvm/lib/Object/XCOFFObjectFile.cpp b/llvm/lib/Object/XCOFFObjectFile.cpp --- a/llvm/lib/Object/XCOFFObjectFile.cpp +++ b/llvm/lib/Object/XCOFFObjectFile.cpp @@ -22,6 +22,22 @@ namespace llvm { namespace object { +enum { XCOFF32FileHeaderSize = 20 }; +static_assert(sizeof(XCOFFFileHeader) == XCOFF32FileHeaderSize, + "Wrong size for XCOFF file header."); + +// Sets EC and returns false if there is less than 'Size' bytes left in the +// buffer at 'Offset'. +static bool checkSize(MemoryBufferRef M, std::error_code &EC, uint64_t Offset, + uint64_t Size) { + if (M.getBufferSize() < Offset + Size) { + EC = object_error::unexpected_eof; + return false; + } + return true; +} + + // Sets Obj unless any bytes in [addr, addr + size) fall outsize of m. // Returns unexpected_eof on error. template @@ -35,6 +51,40 @@ return std::error_code(); } +template const T *viewAs(uintptr_t in) { + return reinterpret_cast(in); +} + +const XCOFFSectionHeader *XCOFFObjectFile::toSection(DataRefImpl Ref) const { + auto Sec = viewAs(Ref.p); +#ifndef NDEBUG + if (Sec < SectionHdrTablePtr || + Sec >= (SectionHdrTablePtr + getNumberOfSections())) + report_fatal_error("Section header outside of section header table."); + + uintptr_t Offset = uintptr_t(Sec) - uintptr_t(SectionHdrTablePtr); + if (Offset % getSectionHeaderSize() != 0) + report_fatal_error( + "Section header pointer does not point to a valid section header."); +#endif + return Sec; +} + +// The next 2 functions are not exactly necessary yet, but they are useful to +// abstract over the size difference between XCOFF32 and XCOFF64 structure +// definitions. +size_t XCOFFObjectFile::getFileHeaderSize() const { + return sizeof(XCOFFFileHeader); +} + +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; @@ -77,32 +127,31 @@ } void XCOFFObjectFile::moveSectionNext(DataRefImpl &Sec) const { - llvm_unreachable("Not yet implemented!"); - return; + const char *Ptr = reinterpret_cast(Sec.p); + Sec.p = reinterpret_cast(Ptr + getSectionHeaderSize()); } std::error_code XCOFFObjectFile::getSectionName(DataRefImpl Sec, StringRef &Res) const { - llvm_unreachable("Not yet implemented!"); + const XCOFFSectionHeader *SH = toSection(Sec); + Res = (SH->Name[XCOFF::SectionNameSize - 1] == 0) + ? SH->Name + : StringRef(SH->Name, XCOFF::SectionNameSize); return std::error_code(); } uint64_t XCOFFObjectFile::getSectionAddress(DataRefImpl Sec) const { - uint64_t Result = 0; - llvm_unreachable("Not yet implemented!"); - return Result; + return toSection(Sec)->VirtualAddress; } uint64_t XCOFFObjectFile::getSectionIndex(DataRefImpl Sec) const { - uint64_t Result = 0; - llvm_unreachable("Not yet implemented!"); - return Result; + // Section numbers in XCOFF are numbered beginning at 1. A section number of + // zero is used to indicate that a symbol is being imported or is undefined. + return toSection(Sec) - SectionHdrTablePtr + 1; } uint64_t XCOFFObjectFile::getSectionSize(DataRefImpl Sec) const { - uint64_t Result = 0; - llvm_unreachable("Not yet implemented!"); - return Result; + return toSection(Sec)->SectionSize; } std::error_code XCOFFObjectFile::getSectionContents(DataRefImpl Sec, @@ -124,21 +173,17 @@ } bool XCOFFObjectFile::isSectionText(DataRefImpl Sec) const { - bool Result = false; - llvm_unreachable("Not yet implemented!"); - return Result; + return toSection(Sec)->Flags & XCOFF::STYP_TEXT; } bool XCOFFObjectFile::isSectionData(DataRefImpl Sec) const { - bool Result = false; - llvm_unreachable("Not yet implemented!"); - return Result; + unsigned Flags = toSection(Sec)->Flags; + return (Flags & XCOFF::STYP_DATA) || (Flags & XCOFF::STYP_TDATA); } bool XCOFFObjectFile::isSectionBSS(DataRefImpl Sec) const { - bool Result = false; - llvm_unreachable("Not yet implemented!"); - return Result; + unsigned Flags = toSection(Sec)->Flags; + return (Flags & XCOFF::STYP_BSS) || (Flags & XCOFF::STYP_TBSS); } bool XCOFFObjectFile::isSectionVirtual(DataRefImpl Sec) const { @@ -202,13 +247,16 @@ } section_iterator XCOFFObjectFile::section_begin() const { - llvm_unreachable("Not yet implemented!"); - return section_iterator(SectionRef()); + DataRefImpl DRI; + DRI.p = reinterpret_cast(SectionHdrTablePtr); + return section_iterator(SectionRef(DRI, this)); } section_iterator XCOFFObjectFile::section_end() const { - llvm_unreachable("Not yet implemented!"); - return section_iterator(SectionRef()); + DataRefImpl DRI; + DRI.p = + reinterpret_cast(SectionHdrTablePtr + getNumberOfSections()); + return section_iterator(SectionRef(DRI, this)); } uint8_t XCOFFObjectFile::getBytesInAddress() const { @@ -218,13 +266,13 @@ } StringRef XCOFFObjectFile::getFileFormatName() const { - llvm_unreachable("Not yet implemented!"); - return ""; + assert(getFileHeaderSize() == XCOFF32FileHeaderSize); + return "aixcoff-rs6000"; } Triple::ArchType XCOFFObjectFile::getArch() const { - llvm_unreachable("Not yet implemented!"); - return Triple::UnknownArch; + assert(getFileHeaderSize() == XCOFF32FileHeaderSize); + return Triple::ppc; } SubtargetFeatures XCOFFObjectFile::getFeatures() const { @@ -238,6 +286,12 @@ return Result; } +Expected XCOFFObjectFile::getStartAddress() const { + // TODO FIXME Should get from auxiliary_header->o_entry when support for the + // auxiliary_header is added. + return 0; +} + XCOFFObjectFile::XCOFFObjectFile(MemoryBufferRef Object, std::error_code &EC) : ObjectFile(Binary::ID_XCOFF32, Object) { @@ -246,6 +300,20 @@ if ((EC = getObject(FileHdrPtr, Data, base() + CurPtr))) return; + + CurPtr += getFileHeaderSize(); + // TODO FIXME we don't have support for an optional header yet, so just skip + // past it. + CurPtr += FileHdrPtr->AuxHeaderSize; + + if (getNumberOfSections() != 0) { + if (!checkSize(Data, EC, CurPtr, + getNumberOfSections() * getSectionHeaderSize())) + return; + + if ((EC = getObject(SectionHdrTablePtr, Data, base() + CurPtr))) + return; + } } Expected> diff --git a/llvm/test/tools/llvm-objdump/Inputs/xcoff-section-headers-truncate.o b/llvm/test/tools/llvm-objdump/Inputs/xcoff-section-headers-truncate.o new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@&1 \ +# RUN: %p/Inputs/xcoff-section-headers-truncate.o | FileCheck \ +# RUN: --check-prefix=ERROR %s + +# ERROR: The end of the file was unexpectedly encountered + +# CHECK: xcoff-section-headers.o: file format aixcoff-rs6000 +# CHECK: Sections: +# CHECK: Idx Name Size VMA Type +# CHECK: 1 .text 00000100 0000000000000000 TEXT +# CHECK: 2 .data 0000005c 0000000000000100 DATA +# CHECK: 3 .bss 00000004 000000000000015c BSS +# CHECK: 4 .tdata 00000004 0000000000000000 DATA +# CHECK: 5 .tbss 00000008 0000000000000004 BSS + +