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 GlobaLinkageMask = 0x0000'8000; + static constexpr uint32_t IsEprolMask = 0x0000'4000; + static constexpr uint32_t HasCodeLenMask = 0x0000'2000; + static constexpr uint32_t IntProcMask = 0x0000'1000; + static constexpr uint32_t HasCtlMask = 0x0000'0800; + static constexpr uint32_t TOCLessMask = 0x0000'0400; + static constexpr uint32_t FPPresentMask = 0x0000'0200; + static constexpr uint32_t LogAbortMask = 0x0000'0100; + + // Byte 4 + static constexpr uint32_t IntHandlerMask = 0x0000'0080; + static constexpr uint32_t NamePresentMask = 0x0000'0040; + static constexpr uint32_t UsedAllocaMask = 0x0000'0020; + static constexpr uint32_t ClDisInvMask = 0x0000'001C; + static constexpr uint32_t SavesCRMask = 0x0000'0002; + static constexpr uint32_t SavesLRMask = 0x0000'0001; + + static constexpr uint8_t ClDisInvShift = 2; + + // Byte 5 + static constexpr uint32_t StoresBCMask = 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 HasVecInfoMask = 0x0080'0000; + static constexpr uint32_t Spare4Mask = 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 NumberOfFPParaMask = 0x0000'00FE; + static constexpr uint32_t ParmsOnStackMask = 0x0000'0001; + static constexpr uint8_t NumberOfFPParaShift = 01; + + 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 @@ -391,6 +391,61 @@ bool isFunction() const; }; +class XCOFFTracebackTable { + const uint8_t *TBPtr; + const uint64_t Size; + Optional ParaType; + Optional CodeLen; + Optional HandMask; + Optional CtlInfo; + Optional FunctionNameLen; + Optional FunctionName; + Optional AllocaRegister; + +public: + static Expected create(const uint8_t *Ptr, uint64_t S); + XCOFFTracebackTable(const uint8_t *Ptr, uint64_t S, Error &Err); + + uint8_t getVersion(); + uint8_t getLanguageID(); + + bool isGlobalLinkage(); + bool isEprol(); + bool hasFunctionCodeLen(); + bool isInternalProcedure(); + bool hasCtl(); + bool isTOCLess(); + bool isFloatPointPresent(); + bool isLogAbort(); + + bool isInterruptHandler(); + bool isFuncNamePresent(); + bool usedAlloca(); + uint8_t getClDisInv(); + bool doesCRSaved(); + bool doesLRSaved(); + + bool doesBackChainStore(); + uint8_t getNumOfFPRsSaved(); + + bool hasVecInfo(); + uint8_t getNumofGPRsSaved(); + + uint8_t getNumberOfFixedPara(); + + uint8_t getNumberOfFPPara(); + bool isParaOnStack(); + + Optional getParaType() { return ParaType; } + Optional getCodeLen() { return CodeLen; } + Optional getHandMask() { return HandMask; } + Optional getCtlInfo() { return CtlInfo; } + Optional getFunctionNameLen() { return FunctionNameLen; } + Optional getFunctionName() { return FunctionName; } + Optional getAllocaRegister() { 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,170 @@ template struct XCOFFSectionHeader; template struct XCOFFSectionHeader; +bool doesXCOFFTracebackTableBegin(ArrayRef Bytes) { + assert(Bytes.size() == 4 && "Traceback table started with 4 bytes zero."); + return support::endian::read32be(Bytes.data()) == 0; +} + +static std::string parseParaType(uint32_t Value, unsigned int ParaNum) { + std::string 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, + const uint64_t S) { + Error Err = Error::success(); + XCOFFTracebackTable TBT(Ptr, S, Err); + if (Err) + return std::move(Err); + return std::move(TBT); +} + +XCOFFTracebackTable::XCOFFTracebackTable(const uint8_t *Ptr, const uint64_t S, + Error &Err) + : TBPtr(Ptr), Size(S) { + ErrorAsOutParameter EAO(&Err); + DataExtractor DE(ArrayRef(Ptr, S), false, 4); + uint64_t offset_ptr = 0; + + DE.getU64(&offset_ptr, &Err); + + unsigned int ParaNum = getNumberOfFixedPara() + getNumberOfFPPara(); + + if (!Err && ParaNum) + ParaType = parseParaType(DE.getU32(&offset_ptr, &Err), ParaNum); + + if (!Err && hasFunctionCodeLen()) + CodeLen = DE.getU32(&offset_ptr, &Err); + + if (!Err && isInterruptHandler()) + HandMask = DE.getU32(&offset_ptr, &Err); + + if (!Err && hasCtl()) + CtlInfo = DE.getU32(&offset_ptr, &Err); + + if (!Err && isFuncNamePresent()) { + uint16_t Len = DE.getU16(&offset_ptr, &Err); + if (!Err) + FunctionName = DE.getBytes(&offset_ptr, Len, &Err); + FunctionNameLen = Len; + } + + if (!Err && usedAlloca()) + AllocaRegister = DE.getU8(&offset_ptr, &Err); +} + +#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() { + return GETBITWITHMASKSHIFT(0, VersionMask, VersionShift); +} + +uint8_t XCOFFTracebackTable::getLanguageID() { + return GETBITWITHMASKSHIFT(0, LanguageIdMask, LanguageIdShift); +} + +bool XCOFFTracebackTable::isGlobalLinkage() { + return GETBITWITHMASK(0, GlobaLinkageMask); +} + +bool XCOFFTracebackTable::isEprol() { return GETBITWITHMASK(0, IsEprolMask); } + +bool XCOFFTracebackTable::hasFunctionCodeLen() { + return GETBITWITHMASK(0, HasCodeLenMask); +} + +bool XCOFFTracebackTable::isInternalProcedure() { + return GETBITWITHMASK(0, IntProcMask); +} + +bool XCOFFTracebackTable::hasCtl() { return GETBITWITHMASK(0, HasCtlMask); } + +bool XCOFFTracebackTable::isTOCLess() { return GETBITWITHMASK(0, TOCLessMask); } + +bool XCOFFTracebackTable::isFloatPointPresent() { + return GETBITWITHMASK(0, FPPresentMask); +} + +bool XCOFFTracebackTable::isLogAbort() { + return GETBITWITHMASK(0, LogAbortMask); +} + +bool XCOFFTracebackTable::isInterruptHandler() { + return GETBITWITHMASK(0, IntHandlerMask); +} + +bool XCOFFTracebackTable::isFuncNamePresent() { + return GETBITWITHMASK(0, NamePresentMask); +} + +bool XCOFFTracebackTable::usedAlloca() { + return GETBITWITHMASK(0, UsedAllocaMask); +} + +uint8_t XCOFFTracebackTable::getClDisInv() { + return GETBITWITHMASKSHIFT(0, ClDisInvMask, ClDisInvShift); +} + +bool XCOFFTracebackTable::doesCRSaved() { + return GETBITWITHMASK(0, SavesCRMask); +} + +bool XCOFFTracebackTable::doesLRSaved() { + return GETBITWITHMASK(0, SavesLRMask); +} + +bool XCOFFTracebackTable::doesBackChainStore() { + return GETBITWITHMASK(4, StoresBCMask); +} + +uint8_t XCOFFTracebackTable::getNumOfFPRsSaved() { + return GETBITWITHMASKSHIFT(4, FPSavedMask, FPSavedShift); +} + +bool XCOFFTracebackTable::hasVecInfo() { + return GETBITWITHMASK(4, HasVecInfoMask); +} + +uint8_t XCOFFTracebackTable::getNumofGPRsSaved() { + return GETBITWITHMASKSHIFT(4, GPSavedMask, GPSavedShift); +} + +uint8_t XCOFFTracebackTable::getNumberOfFixedPara() { + return GETBITWITHMASKSHIFT(4, NumberOfFixedParaMask, NumberOfFixedParaShift); +} + +uint8_t XCOFFTracebackTable::getNumberOfFPPara() { + return GETBITWITHMASKSHIFT(4, NumberOfFPParaMask, NumberOfFPParaShift); +} + +bool XCOFFTracebackTable::isParaOnStack() { + return GETBITWITHMASK(4, ParmsOnStackMask); +} + } // 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,118 @@ +//===- 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 "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_TRUE(!!TTOrErr) << "Parse error"; + + XCOFFTracebackTable TT = TTOrErr.get(); + + EXPECT_EQ(TT.getVersion(), 0); + + EXPECT_EQ(TT.getLanguageID(), 0); + + EXPECT_FALSE(TT.isGlobalLinkage()); + EXPECT_FALSE(TT.isEprol()); + EXPECT_TRUE(TT.hasFunctionCodeLen()); + EXPECT_FALSE(TT.isInternalProcedure()); + EXPECT_FALSE(TT.hasCtl()); + EXPECT_FALSE(TT.isTOCLess()); + EXPECT_TRUE(TT.isFloatPointPresent()); + EXPECT_FALSE(TT.isLogAbort()); + + EXPECT_FALSE(TT.isInterruptHandler()); + EXPECT_TRUE(TT.isFuncNamePresent()); + EXPECT_FALSE(TT.usedAlloca()); + EXPECT_EQ(TT.usedAlloca(), 0); + EXPECT_FALSE(TT.doesCRSaved()); + EXPECT_FALSE(TT.doesLRSaved()); + + EXPECT_TRUE(TT.doesBackChainStore()); + EXPECT_EQ(TT.getNumOfFPRsSaved(), 0); + + EXPECT_FALSE(TT.hasVecInfo()); + EXPECT_EQ(TT.getNumofGPRsSaved(), 0); + + EXPECT_EQ(TT.getNumberOfFixedPara(), 1); + + EXPECT_EQ(TT.getNumberOfFPPara(), 2); + EXPECT_TRUE(TT.isParaOnStack()); + + EXPECT_TRUE(TT.getParaType()); + EXPECT_STREQ(TT.getParaType().getValue().data(), "i, f, d"); + + EXPECT_EQ(TT.getCodeLen().getValue(), 64u); + + EXPECT_FALSE(TT.getHandMask()); + EXPECT_FALSE(TT.getCtlInfo()); + + EXPECT_TRUE(TT.getFunctionNameLen()); + EXPECT_EQ(TT.getFunctionNameLen().getValue(), 7); + EXPECT_TRUE(TT.getFunctionName()); + EXPECT_STREQ(TT.getFunctionName().getValue().data(), "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_TRUE(!!TTOrErr1) << "Parse error"; + XCOFFTracebackTable TT1 = TTOrErr1.get(); + EXPECT_EQ(TT1.getVersion(), 1); + EXPECT_EQ(TT1.getLanguageID(), 2); + + EXPECT_TRUE(TT1.isGlobalLinkage()); + EXPECT_EQ(TT1.getNumberOfFixedPara(), 2); + + EXPECT_EQ(TT1.getNumberOfFPPara(), 3); + + EXPECT_TRUE(TT1.getParaType()); + EXPECT_STREQ(TT1.getParaType().getValue().data(), "i, i, f, f, d"); + + V1[8] = 0xAC; + Expected TTOrErr2 = + XCOFFTracebackTable::create(V1, sizeof(V1)); + ASSERT_TRUE(!!TTOrErr2) << "Parse error"; + XCOFFTracebackTable TT2 = TTOrErr2.get(); + EXPECT_STREQ(TT2.getParaType().getValue().data(), "f, f, d, i, i"); + + V1[8] = 0xD4; + Expected TTOrErr3 = + XCOFFTracebackTable::create(V1, sizeof(V1)); + ASSERT_TRUE(!!TTOrErr3) << "Parse error"; + XCOFFTracebackTable TT3 = TTOrErr3.get(); + EXPECT_STREQ(TT3.getParaType().getValue().data(), "d, i, f, f, i"); + + V1[6] = 0x01; + Expected TTOrErr4 = + XCOFFTracebackTable::create(V1, sizeof(V1)); + ASSERT_TRUE(!!TTOrErr4) << "Parse error"; + XCOFFTracebackTable TT4 = TTOrErr4.get(); + EXPECT_STREQ(TT4.getParaType().getValue().data(), "d, i, f, f"); +}