diff --git a/llvm/include/llvm/BinaryFormat/GOFF.h b/llvm/include/llvm/BinaryFormat/GOFF.h --- a/llvm/include/llvm/BinaryFormat/GOFF.h +++ b/llvm/include/llvm/BinaryFormat/GOFF.h @@ -145,6 +145,12 @@ ESD_ALIGN_4Kpage = 12, }; +enum TXTRecordStyle : uint8_t { + TXT_RS_Byte = 0, + TXT_RS_Structured = 1, + TXT_RS_Unstructured = 2, +}; + enum ENDEntryPointRequest : uint8_t { END_EPR_None = 0, END_EPR_EsdidOffset = 1, diff --git a/llvm/include/llvm/Object/GOFF.h b/llvm/include/llvm/Object/GOFF.h --- a/llvm/include/llvm/Object/GOFF.h +++ b/llvm/include/llvm/Object/GOFF.h @@ -90,6 +90,31 @@ } }; +class TXTRecord : public Record { +public: + /// \brief Maximum length of data. Any more must go in a continuation. + static const uint8_t TXTMaxDataLength = 56; + +public: + TXTRecord() {} + + static Error getData(const uint8_t *Record, SmallString<256> &CompleteData); + + static uint32_t getElementEsdId(const uint8_t *Record) { + uint32_t EsdId; + get(Record, 4, EsdId); + return EsdId; + } + + static void getOffset(const uint8_t *Record, uint32_t &Offset) { + get(Record, 12, Offset); + } + + static void getDataLength(const uint8_t *Record, uint16_t &Length) { + get(Record, 22, Length); + } +}; + class ESDRecord : public Record { public: /// \brief Number of bytes for name; any more must go in continuation. diff --git a/llvm/include/llvm/Object/GOFFObjectFile.h b/llvm/include/llvm/Object/GOFFObjectFile.h --- a/llvm/include/llvm/Object/GOFFObjectFile.h +++ b/llvm/include/llvm/Object/GOFFObjectFile.h @@ -30,7 +30,10 @@ namespace object { class GOFFObjectFile : public ObjectFile { + friend class GOFFSymbolRef; + IndexedMap EsdPtrs; // Indexed by EsdId. + SmallVector TextPtrs; mutable DenseMap EsdNamesCache; @@ -73,29 +76,26 @@ Expected getSymbolFlags(DataRefImpl Symb) const override; Expected getSymbolType(DataRefImpl Symb) const override; Expected getSymbolSection(DataRefImpl Symb) const override; + uint64_t getSymbolSize(DataRefImpl Symb) const; const uint8_t *getSymbolEsdRecord(DataRefImpl Symb) const; bool isSymbolUnresolved(DataRefImpl Symb) const; bool isSymbolIndirect(DataRefImpl Symb) const; // SectionRef. - void moveSectionNext(DataRefImpl &Sec) const override{}; - virtual Expected getSectionName(DataRefImpl Sec) const override { - return StringRef(); - } - uint64_t getSectionAddress(DataRefImpl Sec) const override { return 0; } - uint64_t getSectionSize(DataRefImpl Sec) const override { return 0; } + void moveSectionNext(DataRefImpl &Sec) const override; + virtual Expected getSectionName(DataRefImpl Sec) const override; + uint64_t getSectionAddress(DataRefImpl Sec) const override; + uint64_t getSectionSize(DataRefImpl Sec) const override; virtual Expected> - getSectionContents(DataRefImpl Sec) const override { - return ArrayRef(); - } - uint64_t getSectionIndex(DataRefImpl Sec) const override { return 0; } - uint64_t getSectionAlignment(DataRefImpl Sec) const override { return 0; } - bool isSectionCompressed(DataRefImpl Sec) const override { return false; } - bool isSectionText(DataRefImpl Sec) const override { return false; } - bool isSectionData(DataRefImpl Sec) const override { return false; } - bool isSectionBSS(DataRefImpl Sec) const override { return false; } - bool isSectionVirtual(DataRefImpl Sec) const override { return false; } + getSectionContents(DataRefImpl Sec) const override; + uint64_t getSectionIndex(DataRefImpl Sec) const override; + uint64_t getSectionAlignment(DataRefImpl Sec) const override; + bool isSectionCompressed(DataRefImpl Sec) const override; + bool isSectionText(DataRefImpl Sec) const override; + bool isSectionData(DataRefImpl Sec) const override; + bool isSectionBSS(DataRefImpl Sec) const override; + bool isSectionVirtual(DataRefImpl Sec) const override; relocation_iterator section_rel_begin(DataRefImpl Sec) const override { return relocation_iterator(RelocationRef(Sec, this)); } @@ -107,6 +107,7 @@ const uint8_t *getSectionPrEsdRecord(DataRefImpl &Sec) const; const uint8_t *getSectionEdEsdRecord(uint32_t SectionIndex) const; const uint8_t *getSectionPrEsdRecord(uint32_t SectionIndex) const; + uint32_t getSectionDefEsdId(DataRefImpl &Sec) const; // RelocationRef. void moveRelocationNext(DataRefImpl &Rel) const override{}; @@ -120,6 +121,29 @@ SmallVectorImpl &Result) const override{}; }; +class GOFFSymbolRef : public SymbolRef { +public: + GOFFSymbolRef(SymbolRef B) : SymbolRef(B) { + assert(isa(SymbolRef::getObject())); + } + + const GOFFObjectFile *getObject() const { + return cast(BasicSymbolRef::getObject()); + } + + Expected getSymbolGOFFFlags() const { + return getObject()->getSymbolFlags(getRawDataRefImpl()); + } + + Expected getSymbolGOFFType() const { + return getObject()->getSymbolType(getRawDataRefImpl()); + } + + uint64_t getSize() const { + return getObject()->getSymbolSize(getRawDataRefImpl()); + } +}; + } // namespace object } // namespace llvm diff --git a/llvm/lib/Object/GOFFObjectFile.cpp b/llvm/lib/Object/GOFFObjectFile.cpp --- a/llvm/lib/Object/GOFFObjectFile.cpp +++ b/llvm/lib/Object/GOFFObjectFile.cpp @@ -168,6 +168,11 @@ LLVM_DEBUG(dbgs() << " -- ESD " << EsdId << "\n"); break; } + case GOFF::RT_TXT: + // Save TXT records. + TextPtrs.emplace_back(I); + LLVM_DEBUG(dbgs() << " -- TXT\n"); + break; case GOFF::RT_END: LLVM_DEBUG(dbgs() << " -- END (GOFF record type) unhandled\n"); break; @@ -356,6 +361,13 @@ std::to_string(SymEdId)); } +uint64_t GOFFObjectFile::getSymbolSize(DataRefImpl Symb) const { + const uint8_t *Record = getSymbolEsdRecord(Symb); + uint32_t Length; + ESDRecord::getLength(Record, Length); + return Length; +} + const uint8_t *GOFFObjectFile::getSectionEdEsdRecord(DataRefImpl &Sec) const { SectionEntryImpl EsdIds = SectionList[Sec.d.a]; const uint8_t *EsdRecord = EsdPtrs[EsdIds.d.a]; @@ -386,6 +398,156 @@ return EsdRecord; } +uint32_t GOFFObjectFile::getSectionDefEsdId(DataRefImpl &Sec) const { + const uint8_t *EsdRecord = getSectionEdEsdRecord(Sec); + uint32_t Length; + ESDRecord::getLength(EsdRecord, Length); + if (Length == 0) { + const uint8_t *PrEsdRecord = getSectionPrEsdRecord(Sec); + if (PrEsdRecord) + EsdRecord = PrEsdRecord; + } + + uint32_t DefEsdId; + ESDRecord::getEsdId(EsdRecord, DefEsdId); + LLVM_DEBUG(dbgs() << "Got def EsdId: " << DefEsdId << '\n'); + return DefEsdId; +} + +void GOFFObjectFile::moveSectionNext(DataRefImpl &Sec) const { + if ((Sec.d.a + 1) >= SectionList.size()) + Sec.d.a = 0; // section_end + else + Sec.d.a++; +} + +Expected GOFFObjectFile::getSectionName(DataRefImpl Sec) const { + DataRefImpl EdSym; + SectionEntryImpl EsdIds = SectionList[Sec.d.a]; + EdSym.d.a = EsdIds.d.a; + Expected Name = getSymbolName(EdSym); + if (Name) { + StringRef Res = *Name; + LLVM_DEBUG(dbgs() << "Got section: " << Res << '\n'); + if (Res.equals("C___clangast")) + Res = "__clangast"; + else if (Res.equals("C_autolink")) + Res = ".swift1_autolink_entries"; + LLVM_DEBUG(dbgs() << "Final section name: " << Res << '\n'); + Name = Res; + } + return Name; +} + +uint64_t GOFFObjectFile::getSectionAddress(DataRefImpl Sec) const { + uint32_t Offset; + const uint8_t *EsdRecord = getSectionEdEsdRecord(Sec); + ESDRecord::getOffset(EsdRecord, Offset); + return Offset; +} + +uint64_t GOFFObjectFile::getSectionIndex(DataRefImpl Sec) const { + return Sec.d.a; +} + +uint64_t GOFFObjectFile::getSectionSize(DataRefImpl Sec) const { + uint32_t Length; + uint32_t DefEsdId = getSectionDefEsdId(Sec); + const uint8_t *EsdRecord = EsdPtrs[DefEsdId]; + ESDRecord::getLength(EsdRecord, Length); + LLVM_DEBUG(dbgs() << "Got section size: " << Length << '\n'); + return static_cast(Length); +} + +// Unravel TXT records and expand fill characters to produce +// a contiguous sequence of bytes. +Expected> +GOFFObjectFile::getSectionContents(DataRefImpl Sec) const { + if (SectionDataCache.count(Sec.d.a)) { + StringRef Res = StringRef(SectionDataCache[Sec.d.a]); + return arrayRefFromStringRef(Res); + } + + uint64_t SectionSize = getSectionSize(Sec); + uint32_t DefEsdId = getSectionDefEsdId(Sec); + + const uint8_t *EdEsdRecord = getSectionEdEsdRecord(Sec); + bool FillBytePresent; + ESDRecord::getFillBytePresent(EdEsdRecord, FillBytePresent); + uint8_t FillByte = 0; + if (FillBytePresent) + ESDRecord::getFillByteValue(EdEsdRecord, FillByte); + + // Initialize section with fill byte. + std::string &Data = SectionDataCache[Sec.d.a]; + Data.reserve(SectionSize); + Data.insert(0, SectionSize, FillByte); + + // Replace section with context from text records. + std::vector TxtRecords; + for (const uint8_t *TxtRecordInt : TextPtrs) { + const uint8_t *TxtRecordPtr = TxtRecordInt; + uint32_t TxtEsdId = TXTRecord::getElementEsdId(TxtRecordPtr); + LLVM_DEBUG(dbgs() << "Got txt EsdId: " << TxtEsdId << '\n'); + + if (TxtEsdId == DefEsdId) + TxtRecords.push_back(TxtRecordInt); + } + LLVM_DEBUG(dbgs() << "Found " << TxtRecords.size() << " matching records\n"); + + for (const uint8_t *TxtRecordInt : TxtRecords) { + const uint8_t *TxtRecordPtr = TxtRecordInt; + uint32_t TxtDataOffset; + TXTRecord::getOffset(TxtRecordPtr, TxtDataOffset); + LLVM_DEBUG(dbgs() << "Got record data offset: " << TxtDataOffset << '\n'); + + uint16_t TxtDataSize; + TXTRecord::getDataLength(TxtRecordPtr, TxtDataSize); + LLVM_DEBUG(dbgs() << "Got record data size: " << TxtDataSize << '\n'); + + char *ChrPtr = + const_cast(reinterpret_cast(TxtRecordInt)); + + SmallString<256> CompleteData; + if (auto Err = TXTRecord::getData(TxtRecordPtr, CompleteData)) + return std::move(Err); + Data.replace(TxtDataOffset, TxtDataSize, ChrPtr + 24, TxtDataSize); + } + + return arrayRefFromStringRef(StringRef(Data)); +} + +uint64_t GOFFObjectFile::getSectionAlignment(DataRefImpl Sec) const { + const uint8_t *EsdRecord = getSectionEdEsdRecord(Sec); + GOFF::ESDAlignment Pow2Alignment; + ESDRecord::getAlignment(EsdRecord, Pow2Alignment); + uint64_t ByteAlignment = UINT64_C(1) << (uint64_t)Pow2Alignment; + return ByteAlignment; +} + +bool GOFFObjectFile::isSectionCompressed(DataRefImpl Sec) const { + return false; +} + +bool GOFFObjectFile::isSectionText(DataRefImpl Sec) const { + const uint8_t *EsdRecord = getSectionEdEsdRecord(Sec); + GOFF::ESDExecutable Executable; + ESDRecord::getExecutable(EsdRecord, Executable); + return Executable == GOFF::ESD_EXE_CODE; +} + +bool GOFFObjectFile::isSectionData(DataRefImpl Sec) const { + const uint8_t *EsdRecord = getSectionEdEsdRecord(Sec); + GOFF::ESDExecutable Executable; + ESDRecord::getExecutable(EsdRecord, Executable); + bool IsSectionData = (Executable == GOFF::ESD_EXE_DATA); + return IsSectionData; +} + +bool GOFFObjectFile::isSectionBSS(DataRefImpl Sec) const { return false; } + +bool GOFFObjectFile::isSectionVirtual(DataRefImpl Sec) const { return false; } + section_iterator GOFFObjectFile::section_begin() const { DataRefImpl Sec; moveSectionNext(Sec); @@ -468,6 +630,13 @@ return getContinuousData(Record, DataSize, 72, CompleteData); } +Error TXTRecord::getData(const uint8_t *Record, + SmallString<256> &CompleteData) { + uint16_t Length; + getDataLength(Record, Length); + return getContinuousData(Record, Length, 24, CompleteData); +} + Error ENDRecord::getData(const uint8_t *Record, SmallString<256> &CompleteData) { uint16_t Length = getNameLength(Record); diff --git a/llvm/unittests/Object/GOFFObjectFileTest.cpp b/llvm/unittests/Object/GOFFObjectFileTest.cpp --- a/llvm/unittests/Object/GOFFObjectFileTest.cpp +++ b/llvm/unittests/Object/GOFFObjectFileTest.cpp @@ -502,3 +502,106 @@ FailedWithMessage("ESD record 1 has unknown Executable type 0x03")); } } + +TEST(GOFFObjectFileTest, TXTConstruct) { + char GOFFData[GOFF::RecordLength * 6] = {}; + + // HDR record. + GOFFData[0] = 0x03; + GOFFData[1] = 0xF0; + GOFFData[50] = 0x01; + + // ESD record. + GOFFData[GOFF::RecordLength] = 0x03; + GOFFData[GOFF::RecordLength + 7] = 0x01; // ESDID. + GOFFData[GOFF::RecordLength + 71] = 0x05; // Size of symbol name. + GOFFData[GOFF::RecordLength + 72] = 0xa5; // Symbol name is v. + GOFFData[GOFF::RecordLength + 73] = 0x81; // Symbol name is a. + GOFFData[GOFF::RecordLength + 74] = 0x99; // Symbol name is r. + GOFFData[GOFF::RecordLength + 75] = 0x7b; // Symbol name is #. + GOFFData[GOFF::RecordLength + 76] = 0x83; // Symbol name is c. + + // ESD record. + GOFFData[GOFF::RecordLength * 2] = 0x03; + GOFFData[GOFF::RecordLength * 2 + 3] = 0x01; + GOFFData[GOFF::RecordLength * 2 + 7] = 0x02; // ESDID. + GOFFData[GOFF::RecordLength * 2 + 11] = 0x01; // Parent ESDID. + GOFFData[GOFF::RecordLength * 2 + 27] = 0x08; // Length. + GOFFData[GOFF::RecordLength * 2 + 40] = 0x01; // Name Space ID. + GOFFData[GOFF::RecordLength * 2 + 41] = 0x80; + GOFFData[GOFF::RecordLength * 2 + 60] = 0x04; // Size of symbol name. + GOFFData[GOFF::RecordLength * 2 + 61] = 0x04; // Size of symbol name. + GOFFData[GOFF::RecordLength * 2 + 63] = 0x0a; // Size of symbol name. + GOFFData[GOFF::RecordLength * 2 + 66] = 0x03; // Size of symbol name. + GOFFData[GOFF::RecordLength * 2 + 71] = 0x08; // Size of symbol name. + GOFFData[GOFF::RecordLength * 2 + 72] = 0xc3; // Symbol name is c. + GOFFData[GOFF::RecordLength * 2 + 73] = 0x6d; // Symbol name is _. + GOFFData[GOFF::RecordLength * 2 + 74] = 0xc3; // Symbol name is c. + GOFFData[GOFF::RecordLength * 2 + 75] = 0xd6; // Symbol name is o. + GOFFData[GOFF::RecordLength * 2 + 76] = 0xc4; // Symbol name is D. + GOFFData[GOFF::RecordLength * 2 + 77] = 0xc5; // Symbol name is E. + GOFFData[GOFF::RecordLength * 2 + 78] = 0xf6; // Symbol name is 6. + GOFFData[GOFF::RecordLength * 2 + 79] = 0xf4; // Symbol name is 4. + + // ESD record. + GOFFData[GOFF::RecordLength * 3] = 0x03; + GOFFData[GOFF::RecordLength * 3 + 3] = 0x02; + GOFFData[GOFF::RecordLength * 3 + 7] = 0x03; // ESDID. + GOFFData[GOFF::RecordLength * 3 + 11] = 0x02; // Parent ESDID. + GOFFData[GOFF::RecordLength * 3 + 71] = 0x05; // Size of symbol name. + GOFFData[GOFF::RecordLength * 3 + 72] = 0xa5; // Symbol name is v. + GOFFData[GOFF::RecordLength * 3 + 73] = 0x81; // Symbol name is a. + GOFFData[GOFF::RecordLength * 3 + 74] = 0x99; // Symbol name is r. + GOFFData[GOFF::RecordLength * 3 + 75] = 0x7b; // Symbol name is #. + GOFFData[GOFF::RecordLength * 3 + 76] = 0x83; // Symbol name is c. + + // TXT record. + GOFFData[GOFF::RecordLength * 4] = 0x03; + GOFFData[GOFF::RecordLength * 4 + 1] = 0x10; + GOFFData[GOFF::RecordLength * 4 + 7] = 0x02; + GOFFData[GOFF::RecordLength * 4 + 23] = 0x08; // Data Length. + GOFFData[GOFF::RecordLength * 4 + 24] = 0x12; + GOFFData[GOFF::RecordLength * 4 + 25] = 0x34; + GOFFData[GOFF::RecordLength * 4 + 26] = 0x56; + GOFFData[GOFF::RecordLength * 4 + 27] = 0x78; + GOFFData[GOFF::RecordLength * 4 + 28] = 0x9a; + GOFFData[GOFF::RecordLength * 4 + 29] = 0xbc; + GOFFData[GOFF::RecordLength * 4 + 30] = 0xde; + GOFFData[GOFF::RecordLength * 4 + 31] = 0xf0; + + // END record. + GOFFData[GOFF::RecordLength * 5] = 0x03; + GOFFData[GOFF::RecordLength * 5 + 1] = 0x40; + GOFFData[GOFF::RecordLength * 5 + 11] = 0x06; + + StringRef Data(GOFFData, GOFF::RecordLength * 6); + + Expected> GOFFObjOrErr = + object::ObjectFile::createGOFFObjectFile( + MemoryBufferRef(Data, "dummyGOFF")); + + ASSERT_THAT_EXPECTED(GOFFObjOrErr, Succeeded()); + + GOFFObjectFile *GOFFObj = dyn_cast((*GOFFObjOrErr).get()); + + int SymbolNum = 0; + for (SymbolRef Symbol : GOFFObj->symbols()) { + Expected SymbolNameOrErr = GOFFObj->getSymbolName(Symbol); + ASSERT_THAT_EXPECTED(SymbolNameOrErr, Succeeded()); + StringRef SymbolName = SymbolNameOrErr.get(); + EXPECT_EQ(SymbolName, "var#c"); + SymbolNum ++; + } + EXPECT_EQ(SymbolNum, 1); + + int SectionNum = 0; + for (SectionRef Section : GOFFObj->sections()) { + Expected SectionContent = Section.getContents(); + ASSERT_THAT_EXPECTED(SectionContent, Succeeded()); + StringRef Contents = SectionContent.get(); + + EXPECT_EQ(Contents, "\x12\x34\x56\x78\x9a\xbc\xde\xf0"); + SectionNum ++; + } + EXPECT_EQ(SectionNum, 1); +}