diff --git a/llvm/include/llvm/BinaryFormat/XCOFF.h b/llvm/include/llvm/BinaryFormat/XCOFF.h --- a/llvm/include/llvm/BinaryFormat/XCOFF.h +++ b/llvm/include/llvm/BinaryFormat/XCOFF.h @@ -295,6 +295,61 @@ StringRef getMappingClassString(XCOFF::StorageMappingClass SMC); StringRef getRelocationTypeString(XCOFF::RelocationType Type); +struct TracebackTable { + // Byte 1 + static constexpr uint32_t VersionMask = 0xFF00'0000; + static constexpr uint8_t VersionShift = 24; + + // Byte 2 + static constexpr uint32_t LanguageIdMask = 0x00FF'0000; + static constexpr uint8_t LanguageIdShift = 16; + + // Byte 3 + static constexpr uint32_t IsGlobaLinkageMask = 0x0000'8000; + static constexpr uint32_t IsOutOfLineEpilogOrPrologueMask = 0x0000'4000; + static constexpr uint32_t HasTraceBackTableOffsetMask = 0x0000'2000; + static constexpr uint32_t IsInternalProcedureMask = 0x0000'1000; + static constexpr uint32_t HasControlledStorageMask = 0x0000'0800; + static constexpr uint32_t IsTOClessMask = 0x0000'0400; + static constexpr uint32_t IsFloatingPointPresentMask = 0x0000'0200; + static constexpr uint32_t IsFloatingPointOperationLogOrAbortEnabledMask = + 0x0000'0100; + + // Byte 4 + static constexpr uint32_t IsInterruptHandlerMask = 0x0000'0080; + static constexpr uint32_t IsFunctionNamePresentMask = 0x0000'0040; + static constexpr uint32_t IsAllocaUsedMask = 0x0000'0020; + static constexpr uint32_t OnConditionDirectiveMask = 0x0000'001C; + static constexpr uint32_t IsCRSavedMask = 0x0000'0002; + static constexpr uint32_t IsLRSavedMask = 0x0000'0001; + static constexpr uint8_t OnConditionDirectiveShift = 2; + + // Byte 5 + static constexpr uint32_t IsBackChainStoredMask = 0x8000'0000; + static constexpr uint32_t IsFixupMask = 0x4000'0000; + static constexpr uint32_t FPRSavedMask = 0x3F00'0000; + static constexpr uint32_t FPRSavedShift = 24; + + // Byte 6 + static constexpr uint32_t HasExtensionTableMask = 0x0080'0000; + static constexpr uint32_t HasVectorInfoMask = 0x0040'0000; + static constexpr uint32_t GPRSavedMask = 0x003F'0000; + static constexpr uint32_t GPRSavedShift = 16; + + // Byte 7 + static constexpr uint32_t NumberOfFixedParmsMask = 0x0000'FF00; + static constexpr uint8_t NumberOfFixedParmsShift = 8; + + // Byte 8 + static constexpr uint32_t NumberOfFloatingPointParmsMask = 0x0000'00FE; + static constexpr uint32_t HasParmsOnStackMask = 0x0000'0001; + static constexpr uint8_t NumberOfFloatingPointParmsShift = 1; + + // Masks to select leftmost bits for decoding parameter type information. + static constexpr uint32_t ParmTypeIsFloatingBit = 0x8000'0000; + static constexpr uint32_t ParmTypeFloatingIsDoubleBit = 0x4000'0000; +}; + } // end namespace XCOFF } // end namespace llvm 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 @@ -13,6 +13,8 @@ #ifndef LLVM_OBJECT_XCOFFOBJECTFILE_H #define LLVM_OBJECT_XCOFFOBJECTFILE_H +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/BinaryFormat/XCOFF.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/Endian.h" @@ -391,6 +393,83 @@ bool isFunction() const; }; +/// This class provides methods to extract traceback table data from a buffer. +/// The various accessors may reference the buffer provided via the constructor. + +class XCOFFTracebackTable { + const uint8_t *const TBPtr; + Optional> ParmsType; + Optional TraceBackTableOffset; + Optional HandlerMask; + Optional NumOfCtlAnchors; + Optional> ControlledStorageInfoDisp; + Optional FunctionName; + Optional AllocaRegister; + + XCOFFTracebackTable(const uint8_t *Ptr, uint64_t &Size, Error &Err); + +public: + /// Parse an XCOFF Traceback Table from \a Ptr with \a Size bytes. + /// Returns an XCOFFTracebackTable upon successful parsing, otherwise an + /// Error is returned. + /// + /// \param[in] Ptr + /// A pointer that points just past the initial 4 bytes of zeros at the + /// beginning of an XCOFF Traceback Table. + /// + /// \param[in, out] Size + /// A pointer that points to the length of the XCOFF Traceback Table. + /// If the XCOFF Traceback Table is not parsed successfully or there are + /// extra bytes that are not recognized, \a Size will be updated to be the + /// size up to the end of the last successfully parsed field of the table. + static Expected create(const uint8_t *Ptr, + uint64_t &Size); + uint8_t getVersion() const; + uint8_t getLanguageID() const; + + bool isGlobalLinkage() const; + bool isOutOfLineEpilogOrPrologue() const; + bool hasTraceBackTableOffset() const; + bool isInternalProcedure() const; + bool hasControlledStorage() const; + bool isTOCless() const; + bool isFloatingPointPresent() const; + bool isFloatingPointOperationLogOrAbortEnabled() const; + + bool isInterruptHandler() const; + bool isFuncNamePresent() const; + bool isAllocaUsed() const; + uint8_t getOnConditionDirective() const; + bool isCRSaved() const; + bool isLRSaved() const; + + bool isBackChainStored() const; + bool isFixup() const; + uint8_t getNumOfFPRsSaved() const; + + bool hasVectorInfo() const; + bool hasExtensionTable() const; + uint8_t getNumofGPRsSaved() const; + + uint8_t getNumberOfFixedParms() const; + + uint8_t getNumberOfFPParms() const; + bool hasParmsOnStack() const; + + const Optional> &getParmsType() const { return ParmsType; } + const Optional &getTraceBackTableOffset() const { + return TraceBackTableOffset; + } + const Optional &getHandlerMask() const { return HandlerMask; } + const Optional &getNumOfCtlAnchors() { return NumOfCtlAnchors; } + const Optional> &getControlledStorageInfoDisp() { + return ControlledStorageInfoDisp; + } + const Optional &getFunctionName() const { return FunctionName; } + const Optional &getAllocaRegister() const { return AllocaRegister; } +}; + +bool doesXCOFFTracebackTableBegin(ArrayRef Bytes); } // namespace object } // namespace llvm 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 @@ -12,10 +12,14 @@ #include "llvm/Object/XCOFFObjectFile.h" #include "llvm/MC/SubtargetFeature.h" +#include "llvm/Support/DataExtractor.h" #include #include namespace llvm { + +using namespace XCOFF; + namespace object { static const uint8_t FunctionSym = 0x20; @@ -834,5 +838,216 @@ template struct XCOFFSectionHeader; template struct XCOFFSectionHeader; +bool doesXCOFFTracebackTableBegin(ArrayRef Bytes) { + if (Bytes.size() < 4) + return false; + + return support::endian::read32be(Bytes.data()) == 0; +} + +static SmallString<32> parseParmsType(uint32_t Value, unsigned ParmsNum) { + SmallString<32> ParmsType; + for (unsigned I = 0; I < ParmsNum; ++I) { + if (I != 0) + ParmsType += ", "; + if ((Value & TracebackTable::ParmTypeIsFloatingBit) == 0) { + // Fixed parameter type. + ParmsType += "i"; + Value <<= 1; + } else { + if ((Value & TracebackTable::ParmTypeFloatingIsDoubleBit) == 0) + // Float parameter type. + ParmsType += "f"; + else + // Double parameter type. + ParmsType += "d"; + + Value <<= 2; + } + } + return ParmsType; +} + +Expected XCOFFTracebackTable::create(const uint8_t *Ptr, + uint64_t &Size) { + Error Err = Error::success(); + XCOFFTracebackTable TBT(Ptr, Size, Err); + if (Err) + return std::move(Err); + return TBT; +} + +XCOFFTracebackTable::XCOFFTracebackTable(const uint8_t *Ptr, uint64_t &Size, + Error &Err) + : TBPtr(Ptr) { + ErrorAsOutParameter EAO(&Err); + DataExtractor DE(ArrayRef(Ptr, Size), /*IsLittleEndian=*/false, + /*AddressSize=*/0); + DataExtractor::Cursor Cur(/*Offset=*/0); + + // Skip 8 bytes of mandatory fields. + DE.getU64(Cur); + + // Begin to parse optional fields. + if (Cur) { + unsigned ParmNum = getNumberOfFixedParms() + getNumberOfFPParms(); + + // As long as there are no "fixed-point" or floating-point parameters, this + // field remains not present even when hasVectorInfo gives true and + // indicates the presence of vector parameters. + if (ParmNum > 0) { + uint32_t ParamsTypeValue = DE.getU32(Cur); + // TODO: when hasVectorInfo() is true, we need to implement a new version + // of parsing parameter type for vector info. + if (Cur && !hasVectorInfo()) + ParmsType = parseParmsType(ParamsTypeValue, ParmNum); + } + } + + if (Cur && hasTraceBackTableOffset()) + TraceBackTableOffset = DE.getU32(Cur); + + if (Cur && isInterruptHandler()) + HandlerMask = DE.getU32(Cur); + + if (Cur && hasControlledStorage()) { + NumOfCtlAnchors = DE.getU32(Cur); + if (Cur && NumOfCtlAnchors) { + SmallVector Disp; + Disp.reserve(NumOfCtlAnchors.getValue()); + for (uint32_t I = 0; I < NumOfCtlAnchors && Cur; ++I) + Disp.push_back(DE.getU32(Cur)); + if (Cur) + ControlledStorageInfoDisp = std::move(Disp); + } + } + + if (Cur && isFuncNamePresent()) { + uint16_t FunctionNameLen = DE.getU16(Cur); + if (Cur) + FunctionName = DE.getBytes(Cur, FunctionNameLen); + } + + if (Cur && isAllocaUsed()) + AllocaRegister = DE.getU8(Cur); + + // TODO: Need to parse vector info and extension table if there is one. + + if (!Cur) + Err = Cur.takeError(); + Size = Cur.tell(); +} + +#define GETBITWITHMASK(P, X) \ + (support::endian::read32be(TBPtr + (P)) & (TracebackTable::X)) +#define GETBITWITHMASKSHIFT(P, X, S) \ + ((support::endian::read32be(TBPtr + (P)) & (TracebackTable::X)) >> \ + (TracebackTable::S)) + +uint8_t XCOFFTracebackTable::getVersion() const { + return GETBITWITHMASKSHIFT(0, VersionMask, VersionShift); +} + +uint8_t XCOFFTracebackTable::getLanguageID() const { + return GETBITWITHMASKSHIFT(0, LanguageIdMask, LanguageIdShift); +} + +bool XCOFFTracebackTable::isGlobalLinkage() const { + return GETBITWITHMASK(0, IsGlobaLinkageMask); +} + +bool XCOFFTracebackTable::isOutOfLineEpilogOrPrologue() const { + return GETBITWITHMASK(0, IsOutOfLineEpilogOrPrologueMask); +} + +bool XCOFFTracebackTable::hasTraceBackTableOffset() const { + return GETBITWITHMASK(0, HasTraceBackTableOffsetMask); +} + +bool XCOFFTracebackTable::isInternalProcedure() const { + return GETBITWITHMASK(0, IsInternalProcedureMask); +} + +bool XCOFFTracebackTable::hasControlledStorage() const { + return GETBITWITHMASK(0, HasControlledStorageMask); +} + +bool XCOFFTracebackTable::isTOCless() const { + return GETBITWITHMASK(0, IsTOClessMask); +} + +bool XCOFFTracebackTable::isFloatingPointPresent() const { + return GETBITWITHMASK(0, IsFloatingPointPresentMask); +} + +bool XCOFFTracebackTable::isFloatingPointOperationLogOrAbortEnabled() const { + return GETBITWITHMASK(0, IsFloatingPointOperationLogOrAbortEnabledMask); +} + +bool XCOFFTracebackTable::isInterruptHandler() const { + return GETBITWITHMASK(0, IsInterruptHandlerMask); +} + +bool XCOFFTracebackTable::isFuncNamePresent() const { + return GETBITWITHMASK(0, IsFunctionNamePresentMask); +} + +bool XCOFFTracebackTable::isAllocaUsed() const { + return GETBITWITHMASK(0, IsAllocaUsedMask); +} + +uint8_t XCOFFTracebackTable::getOnConditionDirective() const { + return GETBITWITHMASKSHIFT(0, OnConditionDirectiveMask, + OnConditionDirectiveShift); +} + +bool XCOFFTracebackTable::isCRSaved() const { + return GETBITWITHMASK(0, IsCRSavedMask); +} + +bool XCOFFTracebackTable::isLRSaved() const { + return GETBITWITHMASK(0, IsLRSavedMask); +} + +bool XCOFFTracebackTable::isBackChainStored() const { + return GETBITWITHMASK(4, IsBackChainStoredMask); +} + +bool XCOFFTracebackTable::isFixup() const { + return GETBITWITHMASK(4, IsFixupMask); +} + +uint8_t XCOFFTracebackTable::getNumOfFPRsSaved() const { + return GETBITWITHMASKSHIFT(4, FPRSavedMask, FPRSavedShift); +} + +bool XCOFFTracebackTable::hasExtensionTable() const { + return GETBITWITHMASK(4, HasExtensionTableMask); +} + +bool XCOFFTracebackTable::hasVectorInfo() const { + return GETBITWITHMASK(4, HasVectorInfoMask); +} + +uint8_t XCOFFTracebackTable::getNumofGPRsSaved() const { + return GETBITWITHMASKSHIFT(4, GPRSavedMask, GPRSavedShift); +} + +uint8_t XCOFFTracebackTable::getNumberOfFixedParms() const { + return GETBITWITHMASKSHIFT(4, NumberOfFixedParmsMask, + NumberOfFixedParmsShift); +} + +uint8_t XCOFFTracebackTable::getNumberOfFPParms() const { + return GETBITWITHMASKSHIFT(4, NumberOfFloatingPointParmsMask, + NumberOfFloatingPointParmsShift); +} + +bool XCOFFTracebackTable::hasParmsOnStack() const { + return GETBITWITHMASK(4, HasParmsOnStackMask); +} + +#undef GETBITWITHMASK +#undef GETBITWITHMASKSHIFT } // namespace object } // namespace llvm diff --git a/llvm/unittests/Object/CMakeLists.txt b/llvm/unittests/Object/CMakeLists.txt --- a/llvm/unittests/Object/CMakeLists.txt +++ b/llvm/unittests/Object/CMakeLists.txt @@ -12,6 +12,7 @@ ObjectFileTest.cpp SymbolSizeTest.cpp SymbolicFileTest.cpp + XCOFFObjectFileTest.cpp ) target_link_libraries(ObjectTests PRIVATE LLVMTestingSupport) diff --git a/llvm/unittests/Object/XCOFFObjectFileTest.cpp b/llvm/unittests/Object/XCOFFObjectFileTest.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/Object/XCOFFObjectFileTest.cpp @@ -0,0 +1,264 @@ +//===- XCOFFObjectFileTest.cpp - Tests for XCOFFObjectFile ----------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "llvm/Object/XCOFFObjectFile.h" +#include "llvm/Testing/Support/Error.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::object; + +TEST(XCOFFObjectFileTest, doesXCOFFTracebackTableBegin) { + EXPECT_TRUE(doesXCOFFTracebackTableBegin({0, 0, 0, 0})); + EXPECT_TRUE(doesXCOFFTracebackTableBegin({0, 0, 0, 0, 1})); + EXPECT_FALSE(doesXCOFFTracebackTableBegin({0, 0, 0, 1})); + EXPECT_FALSE(doesXCOFFTracebackTableBegin({0, 0, 0})); +} + +TEST(XCOFFObjectFileTest, XCOFFTracebackTableAPIGeneral) { + uint8_t V[] = {0x00, 0x00, 0x22, 0x40, 0x80, 0x00, 0x01, 0x05, 0x58, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x07, 0x61, 0x64, + 0x64, 0x5f, 0x61, 0x6c, 0x6c, 0x00, 0x00, 0x00}; + uint64_t Size = sizeof(V); + Expected TTOrErr = XCOFFTracebackTable::create(V, Size); + ASSERT_THAT_EXPECTED(TTOrErr, Succeeded()); + XCOFFTracebackTable TT = *TTOrErr; + + EXPECT_EQ(TT.getVersion(), 0); + + EXPECT_EQ(TT.getLanguageID(), 0); + + EXPECT_FALSE(TT.isGlobalLinkage()); + EXPECT_FALSE(TT.isOutOfLineEpilogOrPrologue()); + EXPECT_TRUE(TT.hasTraceBackTableOffset()); + EXPECT_FALSE(TT.isInternalProcedure()); + EXPECT_FALSE(TT.hasControlledStorage()); + EXPECT_FALSE(TT.isTOCless()); + EXPECT_TRUE(TT.isFloatingPointPresent()); + EXPECT_FALSE(TT.isFloatingPointOperationLogOrAbortEnabled()); + + EXPECT_FALSE(TT.isInterruptHandler()); + EXPECT_TRUE(TT.isFuncNamePresent()); + EXPECT_FALSE(TT.isAllocaUsed()); + EXPECT_EQ(TT.getOnConditionDirective(), 0); + EXPECT_FALSE(TT.isCRSaved()); + EXPECT_FALSE(TT.isLRSaved()); + + EXPECT_TRUE(TT.isBackChainStored()); + EXPECT_FALSE(TT.isFixup()); + EXPECT_EQ(TT.getNumOfFPRsSaved(), 0); + + EXPECT_FALSE(TT.hasExtensionTable()); + EXPECT_FALSE(TT.hasVectorInfo()); + EXPECT_EQ(TT.getNumofGPRsSaved(), 0); + + EXPECT_EQ(TT.getNumberOfFixedParms(), 1); + + EXPECT_EQ(TT.getNumberOfFPParms(), 2); + EXPECT_TRUE(TT.hasParmsOnStack()); + + ASSERT_TRUE(TT.getParmsType()); + EXPECT_EQ(TT.getParmsType().getValue(), "i, f, d"); + + ASSERT_TRUE(TT.getTraceBackTableOffset()); + EXPECT_EQ(TT.getTraceBackTableOffset().getValue(), 64u); + + EXPECT_FALSE(TT.getHandlerMask()); + + ASSERT_TRUE(TT.getFunctionName()); + EXPECT_EQ(TT.getFunctionName().getValue(), "add_all"); + EXPECT_EQ(TT.getFunctionName().getValue().size(), 7u); + + EXPECT_FALSE(TT.getAllocaRegister()); + EXPECT_EQ(Size, 25u); +} + +TEST(XCOFFObjectFileTest, XCOFFTracebackTableAPIParmsType) { + uint8_t V[] = {0x01, 0x02, 0xA2, 0x40, 0x80, 0x00, 0x02, 0x07, 0x2B, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x07, 0x61, 0x64, + 0x64, 0x5f, 0x61, 0x6c, 0x6c, 0x00, 0x00, 0x00}; + uint64_t Size = sizeof(V); + Expected TTOrErr = XCOFFTracebackTable::create(V, Size); + + ASSERT_THAT_EXPECTED(TTOrErr, Succeeded()); + XCOFFTracebackTable TT = *TTOrErr; + EXPECT_EQ(TT.getVersion(), 1); + EXPECT_EQ(TT.getLanguageID(), 2); + + EXPECT_TRUE(TT.isGlobalLinkage()); + EXPECT_EQ(TT.getNumberOfFixedParms(), 2); + + EXPECT_EQ(TT.getNumberOfFPParms(), 3); + + ASSERT_TRUE(TT.getParmsType()); + EXPECT_EQ(TT.getParmsType().getValue(), "i, i, f, f, d"); + + V[8] = 0xAC; + Size = sizeof(V); + Expected TTOrErr1 = XCOFFTracebackTable::create(V, Size); + ASSERT_THAT_EXPECTED(TTOrErr1, Succeeded()); + XCOFFTracebackTable TT1 = *TTOrErr1; + ASSERT_TRUE(TT1.getParmsType()); + EXPECT_EQ(TT1.getParmsType().getValue(), "f, f, d, i, i"); + + V[8] = 0xD4; + Size = sizeof(V); + Expected TTOrErr2 = XCOFFTracebackTable::create(V, Size); + ASSERT_THAT_EXPECTED(TTOrErr2, Succeeded()); + XCOFFTracebackTable TT2 = *TTOrErr2; + ASSERT_TRUE(TT2.getParmsType()); + EXPECT_EQ(TT2.getParmsType().getValue(), "d, i, f, f, i"); + + V[6] = 0x01; + Size = sizeof(V); + Expected TTOrErr3 = XCOFFTracebackTable::create(V, Size); + ASSERT_THAT_EXPECTED(TTOrErr3, Succeeded()); + XCOFFTracebackTable TT3 = *TTOrErr3; + ASSERT_TRUE(TT3.getParmsType()); + EXPECT_EQ(TT3.getParmsType().getValue(), "d, i, f, f"); +} + +const uint8_t TBTableData[] = {0x00, 0x00, 0x2A, 0x40, 0x80, 0x40, 0x01, 0x05, + 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x00, 0x00, 0x00, 0x02, 0x05, 0x05, 0x00, 0x00, + 0x06, 0x06, 0x00, 0x00, 0x00, 0x07, 0x61, 0x64, + 0x64, 0x5f, 0x61, 0x6c, 0x6c, 0x00, 0x00, 0x00}; + +TEST(XCOFFObjectFileTest, XCOFFTracebackTableAPIControlledStorageInfoDisp) { + uint64_t Size = 40; + Expected TTOrErr = + XCOFFTracebackTable::create(TBTableData, Size); + ASSERT_THAT_EXPECTED(TTOrErr, Succeeded()); + XCOFFTracebackTable TT = *TTOrErr; + EXPECT_TRUE(TT.hasControlledStorage()); + ASSERT_TRUE(TT.getNumOfCtlAnchors()); + EXPECT_EQ(TT.getNumOfCtlAnchors().getValue(), 2u); + + ASSERT_TRUE(TT.getControlledStorageInfoDisp()); + + SmallVector Disp = TT.getControlledStorageInfoDisp().getValue(); + + ASSERT_EQ(Disp.size(), 2); + EXPECT_EQ(Disp[0], 0x05050000u); + EXPECT_EQ(Disp[1], 0x06060000u); +} + +TEST(XCOFFObjectFileTest, XCOFFTracebackTableAPIHasVectorInfo) { + uint64_t Size = 40; + Expected TTOrErr = + XCOFFTracebackTable::create(TBTableData, Size); + ASSERT_THAT_EXPECTED(TTOrErr, Succeeded()); + XCOFFTracebackTable TT = *TTOrErr; + + EXPECT_TRUE(TT.hasVectorInfo()); + EXPECT_FALSE(TT.getParmsType()); +} + +TEST(XCOFFObjectFileTest, XCOFFTracebackTableTruncatedAtMandatory) { + uint64_t Size = 6; + Expected TTOrErr = + XCOFFTracebackTable::create(TBTableData, Size); + EXPECT_THAT_ERROR( + TTOrErr.takeError(), + FailedWithMessage( + "unexpected end of data at offset 0x6 while reading [0x0, 0x8)")); + EXPECT_EQ(Size, 0); +} + +TEST(XCOFFObjectFileTest, XCOFFTracebackTableTruncatedAtParmsType) { + uint64_t Size = 9; + Expected TTOrErr = + XCOFFTracebackTable::create(TBTableData, Size); + EXPECT_THAT_ERROR( + TTOrErr.takeError(), + FailedWithMessage( + "unexpected end of data at offset 0x9 while reading [0x8, 0xc)")); + EXPECT_EQ(Size, 8u); +} + +TEST(XCOFFObjectFileTest, XCOFFTracebackTableTruncatedAtTBOffset) { + uint64_t Size = 14; + Expected TTOrErr = + XCOFFTracebackTable::create(TBTableData, Size); + EXPECT_THAT_ERROR( + TTOrErr.takeError(), + FailedWithMessage( + "unexpected end of data at offset 0xe while reading [0xc, 0x10)")); + EXPECT_EQ(Size, 12u); +} + +TEST(XCOFFObjectFileTest, XCOFFTracebackTableTruncatedAtHandlerMask) { + uint8_t V[] = {0x00, 0x00, 0x22, 0xC0, 0x80, 0x00, 0x01, 0x05, 0x58, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x07}; + uint64_t Size = sizeof(V); + Expected TTOrErr = XCOFFTracebackTable::create(V, Size); + EXPECT_THAT_ERROR( + TTOrErr.takeError(), + FailedWithMessage( + "unexpected end of data at offset 0x12 while reading [0x10, 0x14)")); + EXPECT_EQ(Size, 16u); +} + +TEST(XCOFFObjectFileTest, XCOFFTracebackTableTruncatedAtNumOfCtlAnchors) { + uint64_t Size = 19; + Expected TTOrErr = + XCOFFTracebackTable::create(TBTableData, Size); + EXPECT_THAT_ERROR( + TTOrErr.takeError(), + FailedWithMessage( + "unexpected end of data at offset 0x13 while reading [0x10, 0x14)")); + EXPECT_EQ(Size, 16u); +} + +TEST(XCOFFObjectFileTest, + XCOFFTracebackTableTruncatedAtControlledStorageInfoDisp) { + uint64_t Size = 21; + Expected TTOrErr = + XCOFFTracebackTable::create(TBTableData, Size); + EXPECT_THAT_ERROR( + TTOrErr.takeError(), + FailedWithMessage( + "unexpected end of data at offset 0x15 while reading [0x14, 0x18)")); + EXPECT_EQ(Size, 20u); +} + +TEST(XCOFFObjectFileTest, XCOFFTracebackTableTruncatedAtNameLen) { + uint64_t Size = 29; + Expected TTOrErr = + XCOFFTracebackTable::create(TBTableData, Size); + EXPECT_THAT_ERROR( + TTOrErr.takeError(), + FailedWithMessage( + "unexpected end of data at offset 0x1d while reading [0x1c, 0x1e)")); + EXPECT_EQ(Size, 28u); +} + +TEST(XCOFFObjectFileTest, XCOFFTracebackTableTruncatedAtFunctionName) { + uint64_t Size = 36; + Expected TTOrErr = + XCOFFTracebackTable::create(TBTableData, Size); + EXPECT_THAT_ERROR( + TTOrErr.takeError(), + FailedWithMessage( + "unexpected end of data at offset 0x24 while reading [0x1e, 0x25)")); + EXPECT_EQ(Size, 30u); +} + +TEST(XCOFFObjectFileTest, XCOFFTracebackTableTruncatedAtAllocaUsed) { + uint8_t V[] = {0x00, 0x00, 0x2A, 0x60, 0x80, 0x00, 0x01, 0x05, 0x58, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, + 0x05, 0x05, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x00, 0x07, + 0x61, 0x64, 0x64, 0x5f, 0x61, 0x6c, 0x6c}; + uint64_t Size = sizeof(V); + Expected TTOrErr = XCOFFTracebackTable::create(V, Size); + EXPECT_THAT_ERROR( + TTOrErr.takeError(), + FailedWithMessage( + "unexpected end of data at offset 0x25 while reading [0x25, 0x26)")); + EXPECT_EQ(Size, 37u); +}