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,60 @@ 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 IsFloatPointPresentMask = 0x0000'0200; + static constexpr uint32_t IsLogOrAbortFloatPointOperationMask = 0x0000'0100; + + // Byte 4 + static constexpr uint32_t IsInterruptHandlerMask = 0x0000'0080; + static constexpr uint32_t HasFunctionNamePresentMask = 0x0000'0040; + static constexpr uint32_t UsedAllocaMask = 0x0000'0020; + static constexpr uint32_t OnConditionDirectiveMask = 0x0000'001C; + static constexpr uint32_t SavesCRMask = 0x0000'0002; + static constexpr uint32_t SavesLRMask = 0x0000'0001; + static constexpr uint8_t OnConditionDirectiveShift = 2; + + // Byte 5 + static constexpr uint32_t StoresBackChainMask = 0x8000'0000; + static constexpr uint32_t FixupMask = 0x4000'0000; + static constexpr uint32_t FPSavedMask = 0x3F00'0000; + static constexpr uint32_t FPSavedShift = 24; + + // Byte 6 + static constexpr uint32_t ExtensionTableExistMask = 0x0080'0000; + static constexpr uint32_t HasVectorInfoMask = 0x0040'0000; + static constexpr uint32_t GPSavedMask = 0x003F'0000; + static constexpr uint32_t GPSavedShift = 16; + + // Byte 7 + static constexpr uint32_t NumberOfFixedParaMask = 0x0000'FF00; + static constexpr uint8_t NumberOfFixedParaShift = 8; + + // Byte 8 + static constexpr uint32_t NumberOfFloatPointParaMask = 0x0000'00FE; + static constexpr uint32_t ParmsOnStackMask = 0x0000'0001; + static constexpr uint8_t NumberOfFloatPointParaShift = 1; + + // Used for the fixed or float point parameter type identification. + static constexpr uint32_t FixedParaTypeBit = 0x8000'0000; + static constexpr uint32_t FloatPointParaTypeBit = 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,68 @@ bool isFunction() const; }; +class XCOFFTracebackTable { + const uint8_t *TBPtr; + Optional> ParaType; + Optional CodeLen; + Optional HandlerMask; + Optional NumOfCtlAnchors; + Optional> ControlledStorageInfoDisp; + Optional FunctionNameLen; + Optional FunctionName; + Optional AllocaRegister; + + XCOFFTracebackTable(const uint8_t *Ptr, uint64_t Size, Error &Err); + +public: + 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 hasFunctionCodeLen() const; + bool isInternalProcedure() const; + bool hasControlledStorage() const; + bool hasTOC() const; + bool isFloatPointPresent() const; + bool isLogAbort() const; + + bool isInterruptHandler() const; + bool hasFuncNamePresent() const; + bool usedAlloca() const; + uint8_t getClDisInv() const; + bool isCRSaving() const; + bool isLRSaving() const; + + bool isBackChainStoring() const; + uint8_t getNumOfFPRsSaved() const; + + bool hasVectorInfo() const; + bool isExtensionTableExisting() const; + uint8_t getNumofGPRsSaved() const; + + uint8_t getNumberOfFixedPara() const; + + uint8_t getNumberOfFPPara() const; + bool isParaOnStack() const; + + const Optional> &getParaType() const { return ParaType; } + const Optional &getCodeLen() const { return CodeLen; } + const Optional &getHandlerMask() const { return HandlerMask; } + const Optional &getNumOfCtlAnchors() { return NumOfCtlAnchors; } + const Optional> &getControlledStorageInfoDisp() { + return ControlledStorageInfoDisp; + } + const Optional &getFunctionNameLen() const { + return FunctionNameLen; + } + 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,195 @@ 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> parseParaType(uint32_t Value, unsigned ParaNum) { + SmallString<32> ParaType; + for (unsigned I = 0; I < ParaNum; ++I) { + if (I != 0) + ParaType += ", "; + if ((Value & TracebackTable::FixedParaTypeBit) == 0) { + // Fixed parameter type. + ParaType += "i"; + Value <<= 1; + continue; + } else { + if ((Value & TracebackTable::FloatPointParaTypeBit) == 0) + // Float parameter type. + ParaType += "f"; + else + // Double parameter type. + ParaType += "d"; + + Value <<= 2; + continue; + } + } + return ParaType; +} + +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 std::move(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); + uint64_t Offset = 0; + + DE.getU64(&Offset, &Err); + + unsigned ParaNum = getNumberOfFixedPara() + getNumberOfFPPara(); + + if (!Err && ParaNum) + ParaType = parseParaType(DE.getU32(&Offset, &Err), ParaNum); + + if (!Err && hasFunctionCodeLen()) + CodeLen = DE.getU32(&Offset, &Err); + + if (!Err && isInterruptHandler()) + HandlerMask = DE.getU32(&Offset, &Err); + + if (!Err && hasControlledStorage()) { + NumOfCtlAnchors = DE.getU32(&Offset, &Err); + if (!Err && NumOfCtlAnchors) { + SmallVector Disp; + for (uint32_t I = 0; I < NumOfCtlAnchors && !Err; I++) + Disp.push_back(DE.getU32(&Offset, &Err)); + ControlledStorageInfoDisp = Disp; + } + } + + if (!Err && hasFuncNamePresent()) { + FunctionNameLen = DE.getU16(&Offset, &Err); + if (!Err) + FunctionName = DE.getBytes(&Offset, FunctionNameLen.getValue(), &Err); + } + + if (!Err && usedAlloca()) + AllocaRegister = DE.getU8(&Offset, &Err); + + // TODO: Need to parse vector Info and extension table if there is. +} + +#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::hasFunctionCodeLen() const { + return GETBITWITHMASK(0, HasTraceBackTableOffsetMask); +} + +bool XCOFFTracebackTable::isInternalProcedure() const { + return GETBITWITHMASK(0, IsInternalProcedureMask); +} + +bool XCOFFTracebackTable::hasControlledStorage() const { + return GETBITWITHMASK(0, HasControlledStorageMask); +} + +bool XCOFFTracebackTable::hasTOC() const { + return GETBITWITHMASK(0, IsTOCLessMask); +} + +bool XCOFFTracebackTable::isFloatPointPresent() const { + return GETBITWITHMASK(0, IsFloatPointPresentMask); +} + +bool XCOFFTracebackTable::isLogAbort() const { + return GETBITWITHMASK(0, IsLogOrAbortFloatPointOperationMask); +} + +bool XCOFFTracebackTable::isInterruptHandler() const { + return GETBITWITHMASK(0, IsInterruptHandlerMask); +} + +bool XCOFFTracebackTable::hasFuncNamePresent() const { + return GETBITWITHMASK(0, HasFunctionNamePresentMask); +} + +bool XCOFFTracebackTable::usedAlloca() const { + return GETBITWITHMASK(0, UsedAllocaMask); +} + +uint8_t XCOFFTracebackTable::getClDisInv() const { + return GETBITWITHMASKSHIFT(0, OnConditionDirectiveMask, + OnConditionDirectiveShift); +} + +bool XCOFFTracebackTable::isCRSaving() const { + return GETBITWITHMASK(0, SavesCRMask); +} + +bool XCOFFTracebackTable::isLRSaving() const { + return GETBITWITHMASK(0, SavesLRMask); +} + +bool XCOFFTracebackTable::isBackChainStoring() const { + return GETBITWITHMASK(4, StoresBackChainMask); +} + +uint8_t XCOFFTracebackTable::getNumOfFPRsSaved() const { + return GETBITWITHMASKSHIFT(4, FPSavedMask, FPSavedShift); +} + +bool XCOFFTracebackTable::isExtensionTableExisting() const { + return GETBITWITHMASK(4, ExtensionTableExistMask); +} + +bool XCOFFTracebackTable::hasVectorInfo() const { + return GETBITWITHMASK(4, HasVectorInfoMask); +} + +uint8_t XCOFFTracebackTable::getNumofGPRsSaved() const { + return GETBITWITHMASKSHIFT(4, GPSavedMask, GPSavedShift); +} + +uint8_t XCOFFTracebackTable::getNumberOfFixedPara() const { + return GETBITWITHMASKSHIFT(4, NumberOfFixedParaMask, NumberOfFixedParaShift); +} + +uint8_t XCOFFTracebackTable::getNumberOfFPPara() const { + return GETBITWITHMASKSHIFT(4, NumberOfFloatPointParaMask, + NumberOfFloatPointParaShift); +} + +bool XCOFFTracebackTable::isParaOnStack() const { + return GETBITWITHMASK(4, ParmsOnStackMask); +} + +#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,141 @@ +//===- 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_FALSE(doesXCOFFTracebackTableBegin({0, 0, 0, 1})); +} + +TEST(XCOFFObjectFileTest, XCOFFTracebackTableAPItest) { + 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}; + + Expected TTOrErr = + XCOFFTracebackTable::create(V, sizeof(V)); + 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.hasFunctionCodeLen()); + EXPECT_FALSE(TT.isInternalProcedure()); + EXPECT_FALSE(TT.hasControlledStorage()); + EXPECT_FALSE(TT.hasTOC()); + EXPECT_TRUE(TT.isFloatPointPresent()); + EXPECT_FALSE(TT.isLogAbort()); + + EXPECT_FALSE(TT.isInterruptHandler()); + EXPECT_TRUE(TT.hasFuncNamePresent()); + EXPECT_FALSE(TT.usedAlloca()); + EXPECT_EQ(TT.usedAlloca(), 0); + EXPECT_FALSE(TT.isCRSaving()); + EXPECT_FALSE(TT.isLRSaving()); + + EXPECT_TRUE(TT.isBackChainStoring()); + EXPECT_EQ(TT.getNumOfFPRsSaved(), 0); + + EXPECT_FALSE(TT.isExtensionTableExisting()); + EXPECT_FALSE(TT.hasVectorInfo()); + EXPECT_EQ(TT.getNumofGPRsSaved(), 0); + + EXPECT_EQ(TT.getNumberOfFixedPara(), 1); + + EXPECT_EQ(TT.getNumberOfFPPara(), 2); + EXPECT_TRUE(TT.isParaOnStack()); + + ASSERT_TRUE(TT.getParaType()); + EXPECT_EQ(TT.getParaType().getValue(), "i, f, d"); + + ASSERT_TRUE(TT.getCodeLen()); + EXPECT_EQ(TT.getCodeLen().getValue(), 64u); + + ASSERT_FALSE(TT.getHandlerMask()); + + ASSERT_TRUE(TT.getFunctionNameLen()); + EXPECT_EQ(TT.getFunctionNameLen().getValue(), 7); + ASSERT_TRUE(TT.getFunctionName()); + EXPECT_EQ(TT.getFunctionName().getValue(), "add_all"); + + EXPECT_FALSE(TT.getAllocaRegister()); + + uint8_t V1[] = {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}; + + Expected TTOrErr1 = + XCOFFTracebackTable::create(V1, sizeof(V1)); + ASSERT_THAT_EXPECTED(TTOrErr1, Succeeded()); + XCOFFTracebackTable TT1 = *TTOrErr1; + EXPECT_EQ(TT1.getVersion(), 1); + EXPECT_EQ(TT1.getLanguageID(), 2); + + EXPECT_TRUE(TT1.isGlobalLinkage()); + EXPECT_EQ(TT1.getNumberOfFixedPara(), 2); + + EXPECT_EQ(TT1.getNumberOfFPPara(), 3); + + ASSERT_TRUE(TT1.getParaType()); + EXPECT_EQ(TT1.getParaType().getValue(), "i, i, f, f, d"); + + V1[8] = 0xAC; + Expected TTOrErr2 = + XCOFFTracebackTable::create(V1, sizeof(V1)); + ASSERT_THAT_EXPECTED(TTOrErr2, Succeeded()); + XCOFFTracebackTable TT2 = *TTOrErr2; + ASSERT_TRUE(TT2.getParaType()); + EXPECT_EQ(TT2.getParaType().getValue(), "f, f, d, i, i"); + + V1[8] = 0xD4; + Expected TTOrErr3 = + XCOFFTracebackTable::create(V1, sizeof(V1)); + ASSERT_THAT_EXPECTED(TTOrErr3, Succeeded()); + XCOFFTracebackTable TT3 = *TTOrErr3; + ASSERT_TRUE(TT3.getParaType()); + EXPECT_EQ(TT3.getParaType().getValue(), "d, i, f, f, i"); + + V1[6] = 0x01; + Expected TTOrErr4 = + XCOFFTracebackTable::create(V1, sizeof(V1)); + ASSERT_THAT_EXPECTED(TTOrErr4, Succeeded()); + XCOFFTracebackTable TT4 = *TTOrErr4; + ASSERT_TRUE(TT4.getParaType()); + EXPECT_EQ(TT4.getParaType().getValue(), "d, i, f, f"); + + uint8_t V2[] = {0x00, 0x00, 0x2A, 0x40, 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, 0x00, 0x00, 0x00}; + + Expected TTOrErr5 = + XCOFFTracebackTable::create(V2, sizeof(V2)); + ASSERT_THAT_EXPECTED(TTOrErr5, Succeeded()); + XCOFFTracebackTable TT5 = *TTOrErr5; + EXPECT_TRUE(TT5.hasControlledStorage()); + ASSERT_TRUE(TT5.getNumOfCtlAnchors()); + EXPECT_EQ(TT5.getNumOfCtlAnchors().getValue(), 2u); + + ASSERT_TRUE(TT5.getControlledStorageInfoDisp()); + + SmallVector Disp = TT5.getControlledStorageInfoDisp().getValue(); + + EXPECT_EQ(Disp[0], 0x05050000u); + EXPECT_EQ(Disp[1], 0x06060000u); +}