diff --git a/llvm/include/llvm/MC/MCObjectWriter.h b/llvm/include/llvm/MC/MCObjectWriter.h --- a/llvm/include/llvm/MC/MCObjectWriter.h +++ b/llvm/include/llvm/MC/MCObjectWriter.h @@ -110,6 +110,15 @@ unsigned FunctionSize, bool hasDebug) { report_fatal_error("addExceptionEntry is only supported on XCOFF targets"); } + + virtual void addCInfoSymEntry(StringRef Name, StringRef Data) { + report_fatal_error("addCInfoSymEntry is only supported on XCOFF targets"); + } + + virtual bool hasCInfoSymSection() { + report_fatal_error("hasCInfoSymSection is only supported on XCOFF targets"); + } + /// Write the object file and returns the number of bytes written. /// /// This routine is called by the assembler after layout and relaxation is diff --git a/llvm/include/llvm/MC/MCXCOFFStreamer.h b/llvm/include/llvm/MC/MCXCOFFStreamer.h --- a/llvm/include/llvm/MC/MCXCOFFStreamer.h +++ b/llvm/include/llvm/MC/MCXCOFFStreamer.h @@ -40,10 +40,7 @@ void emitXCOFFExceptDirective(const MCSymbol *Symbol, const MCSymbol *Trap, unsigned Lang, unsigned Reason, unsigned FunctionSize, bool hasDebug) override; - void emitXCOFFCInfoSym(StringRef Name, StringRef Metadata) override { - report_fatal_error("emitXCOFFCInfoSym is not implemented yet on " - "object generation path"); - } + void emitXCOFFCInfoSym(StringRef Name, StringRef Metadata) override; }; } // end namespace llvm diff --git a/llvm/lib/MC/MCXCOFFStreamer.cpp b/llvm/lib/MC/MCXCOFFStreamer.cpp --- a/llvm/lib/MC/MCXCOFFStreamer.cpp +++ b/llvm/lib/MC/MCXCOFFStreamer.cpp @@ -105,6 +105,10 @@ FunctionSize, hasDebug); } +void MCXCOFFStreamer::emitXCOFFCInfoSym(StringRef Name, StringRef Metadata) { + getAssembler().getWriter().addCInfoSymEntry(Name, Metadata); +} + void MCXCOFFStreamer::emitCommonSymbol(MCSymbol *Symbol, uint64_t Size, Align ByteAlignment) { getAssembler().registerSymbol(*Symbol); diff --git a/llvm/lib/MC/XCOFFObjectWriter.cpp b/llvm/lib/MC/XCOFFObjectWriter.cpp --- a/llvm/lib/MC/XCOFFObjectWriter.cpp +++ b/llvm/lib/MC/XCOFFObjectWriter.cpp @@ -233,6 +233,36 @@ virtual ~ExceptionSectionEntry() = default; }; +struct CInfoSymInfo { + std::string MetadataStr; + StringRef Metadata; + // Name of the C_INFO symbol associated with the section + std::string Name; + // Offset into the start of the metadata in the section + uint64_t Offset; + + // Metadata needs to be padded out to an even word size. + uint32_t paddingSize() const { + return alignTo(Metadata.size(), sizeof(uint32_t)) - Metadata.size(); + }; + + // Total size of the entry, including the 4 byte length + uint32_t size() const { + return Metadata.size() + paddingSize() + sizeof(uint32_t); + }; +}; + +struct CInfoSymSectionEntry : public SectionEntry { + std::unique_ptr Entry; + + CInfoSymSectionEntry(StringRef N, int32_t Flags) : SectionEntry(N, Flags){}; + virtual ~CInfoSymSectionEntry() = default; + void reset() override { + SectionEntry::reset(); + Entry.reset(); + } +}; + class XCOFFObjectWriter : public MCObjectWriter { uint32_t SymbolTableEntryCount = 0; @@ -287,6 +317,7 @@ std::vector OverflowSections; ExceptionSectionEntry ExceptionSection; + CInfoSymSectionEntry CInfoSymSection; CsectGroup &getCsectGroup(const MCSectionXCOFF *MCSec); @@ -328,6 +359,10 @@ void writeSectionForExceptionSectionEntry( const MCAssembler &Asm, const MCAsmLayout &Layout, ExceptionSectionEntry &ExceptionEntry, uint64_t &CurrentAddressLocation); + void writeSectionForCInfoSymSectionEntry(const MCAssembler &Asm, + const MCAsmLayout &Layout, + CInfoSymSectionEntry &CInfoSymEntry, + uint64_t &CurrentAddressLocation); void writeSymbolTable(const MCAsmLayout &Layout); void writeSymbolAuxDwarfEntry(uint64_t LengthOfSectionPortion, uint64_t NumberOfRelocEnt = 0); @@ -368,6 +403,11 @@ unsigned getExceptionSectionSize(); unsigned getExceptionOffset(const MCSymbol *Symbol); + void addCInfoSymEntry(StringRef Name, StringRef Metadata) override; + bool hasCInfoSymSection() override { + return CInfoSymSection.Entry != nullptr; + } + size_t auxiliaryHeaderSize() const { // 64-bit object files have no auxiliary header. return HasVisibility && !is64Bit() ? XCOFF::AuxFileHeaderSizeShort : 0; @@ -396,7 +436,8 @@ CsectGroups{&TDataCsects}), TBSS(".tbss", XCOFF::STYP_TBSS, /* IsVirtual */ true, CsectGroups{&TBSSCsects}), - ExceptionSection(".except", XCOFF::STYP_EXCEPT) {} + ExceptionSection(".except", XCOFF::STYP_EXCEPT), + CInfoSymSection(".info", XCOFF::STYP_INFO) {} void XCOFFObjectWriter::reset() { // Clear the mappings we created. @@ -412,6 +453,7 @@ for (auto &OverflowSec : OverflowSections) OverflowSec.reset(); ExceptionSection.reset(); + CInfoSymSection.reset(); // Reset states in XCOFFObjectWriter. SymbolTableEntryCount = 0; @@ -559,6 +601,10 @@ Strings.add(XSym->getSymbolTableName()); } + std::unique_ptr &CISI = CInfoSymSection.Entry; + if (hasCInfoSymSection() && nameShouldBeInStringTable(CISI->Name)) + Strings.add(CISI->Name); + FileNames = Asm.getFileNames(); // Emit ".file" as the source file name when there is no file name. if (FileNames.empty()) @@ -712,6 +758,8 @@ void XCOFFObjectWriter::writeSections(const MCAssembler &Asm, const MCAsmLayout &Layout) { uint64_t CurrentAddressLocation = 0; + writeSectionForCInfoSymSectionEntry(Asm, Layout, CInfoSymSection, + CurrentAddressLocation); for (const auto *Section : Sections) writeSectionForControlSectionEntry(Asm, Layout, *Section, CurrentAddressLocation); @@ -981,6 +1029,8 @@ } void XCOFFObjectWriter::writeSectionHeaderTable() { + if (hasCInfoSymSection()) + writeSectionHeader(&CInfoSymSection); for (const auto *CsectSec : Sections) writeSectionHeader(CsectSec); for (const auto &DwarfSec : DwarfSections) @@ -1037,6 +1087,12 @@ /*NumberOfAuxEntries=*/0); } + if (hasCInfoSymSection()) + writeSymbolEntry(CInfoSymSection.Entry->Name, CInfoSymSection.Entry->Offset, + CInfoSymSection.Index, + /*SymbolType=*/0, XCOFF::C_INFO, + /*NumberOfAuxEntries=*/0); + for (const auto &Csect : UndefinedCsects) { writeSymbolEntryForControlSection(Csect, XCOFF::ReservedSectionNum::N_UNDEF, Csect.MCSec->getStorageClass()); @@ -1156,6 +1212,13 @@ SectionCount * XCOFF::SectionHeaderSize32)) + auxiliaryHeaderSize(); + if (hasCInfoSymSection()) { + CInfoSymSection.FileOffsetToData = RawPointer; + RawPointer += CInfoSymSection.Size; + assert(RawPointer <= MaxRawDataSize && + "Section raw data overflowed this object file."); + } + // Calculate the file offset to the section data. for (auto *Sec : Sections) { if (Sec->Index == SectionEntry::UninitializedIndex || Sec->IsVirtual) @@ -1246,10 +1309,39 @@ : XCOFF::ExceptionSectionEntrySize32); } +void XCOFFObjectWriter::addCInfoSymEntry(StringRef Name, StringRef Data) { + std::unique_ptr NewEntry = std::make_unique(); + NewEntry->MetadataStr = Data.str(); + NewEntry->Metadata = StringRef(NewEntry->MetadataStr); + NewEntry->Name = Name.str(); + NewEntry->Offset = sizeof(uint32_t); + CInfoSymSection.Size += NewEntry->size(); + assert(CInfoSymSection.Entry == nullptr && + "Multiple entries are not supported"); + CInfoSymSection.Entry = std::move(NewEntry); +} + void XCOFFObjectWriter::assignAddressesAndIndices(const MCAsmLayout &Layout) { // The symbol table starts with all the C_FILE symbols. uint32_t SymbolTableIndex = FileNames.size(); + // The address corrresponds to the address of sections and symbols in the + // object file. We place the shared address 0 immediately after the + // section header table. + uint64_t Address = 0; + // Section indices are 1-based in XCOFF. + int32_t SectionIndex = 1; + bool HasTDataSection = false; + + if (hasCInfoSymSection()) { + CInfoSymSection.Index = SectionIndex++; + SectionCount++; + CInfoSymSection.Address = 0; + Address += CInfoSymSection.Size; + Address = alignTo(Address, DefaultSectionAlign); + SymbolTableIndex += 1; + } + // Calculate indices for undefined symbols. for (auto &Csect : UndefinedCsects) { Csect.Size = 0; @@ -1260,14 +1352,6 @@ SymbolTableIndex += 2; } - // The address corrresponds to the address of sections and symbols in the - // object file. We place the shared address 0 immediately after the - // section header table. - uint64_t Address = 0; - // Section indices are 1-based in XCOFF. - int32_t SectionIndex = 1; - bool HasTDataSection = false; - for (auto *Section : Sections) { const bool IsEmpty = llvm::all_of(Section->Groups, @@ -1507,6 +1591,41 @@ CurrentAddressLocation += getExceptionSectionSize(); } +void XCOFFObjectWriter::writeSectionForCInfoSymSectionEntry( + const MCAssembler &Asm, const MCAsmLayout &Layout, + CInfoSymSectionEntry &CInfoSymEntry, uint64_t &CurrentAddressLocation) { + if (!hasCInfoSymSection()) + return; + + constexpr int WordSize = sizeof(uint32_t); + std::unique_ptr &CISI = CInfoSymEntry.Entry; + StringRef &Metadata = CISI->Metadata; + + // Emit the 4-byte length of the metadata. + W.write(Metadata.size()); + + if (Metadata.size() == 0) + return; + + // Write out the payload one word at a time. + size_t Index = 0; + while (Index + WordSize <= Metadata.size()) { + uint32_t NextWord = + llvm::support::endian::read32be(Metadata.data() + Index); + W.write(NextWord); + Index += WordSize; + } + + // If there is padding, we have at least one byte of payload left to emit. + if (CISI->paddingSize()) { + std::array LastWord = {0}; + ::memcpy(LastWord.data(), Metadata.data() + Index, Metadata.size() - Index); + W.write(*reinterpret_cast(LastWord.data())); + } + + CurrentAddressLocation += CISI->size(); +} + // Takes the log base 2 of the alignment and shifts the result into the 5 most // significant bits of a byte, then or's in the csect type into the least // significant 3 bits. diff --git a/llvm/test/CodeGen/PowerPC/aix-command-line-metadata.ll b/llvm/test/CodeGen/PowerPC/aix-command-line-metadata.ll --- a/llvm/test/CodeGen/PowerPC/aix-command-line-metadata.ll +++ b/llvm/test/CodeGen/PowerPC/aix-command-line-metadata.ll @@ -3,21 +3,27 @@ ; RUN: llc -mtriple powerpc64-ibm-aix-xcoff < %s | \ ; RUN: FileCheck --check-prefix=ASM %s -; RUN: not --crash llc -mtriple powerpc-ibm-aix-xcoff -filetype=obj < %s 2>&1 | \ +; RUN: llc -mtriple powerpc-ibm-aix-xcoff -filetype=obj -o %t.o < %s +; RUN: llvm-objdump --full-contents --section=.info %t.o | \ ; RUN: FileCheck --check-prefix=OBJ %s -; RUN: not --crash llc -mtriple powerpc64-ibm-aix-xcoff -filetype=obj < %s 2>&1 | \ +; RUN: llc -mtriple powerpc64-ibm-aix-xcoff -filetype=obj -o %t.o < %s +; RUN: llvm-objdump --full-contents --section=.info %t.o | \ ; RUN: FileCheck --check-prefix=OBJ %s ; Verify that llvm.commandline metadata is emitted to .info sections and that the ; metadata is padded if necessary. -; OBJ: LLVM ERROR: emitXCOFFCInfoSym is not implemented yet on object generation path - ; ASM: .info ".GCC.command.line", 0x0000003a, ; ASM: .info , 0x40282329, 0x6f707420, 0x636c616e, 0x67202d63, 0x6f6d6d61, 0x6e64202d ; ASM: .info , 0x6c696e65, 0x0a004028, 0x23296f70, 0x7420736f, 0x6d657468, 0x696e6720 ; ASM: .info , 0x656c7365, 0x20313233, 0x0a000000 +; OBJ: Contents of section .info: +; OBJ: 0000 0000003a 40282329 6f707420 636c616e ...:@(#)opt clan +; OBJ: 0010 67202d63 6f6d6d61 6e64202d 6c696e65 g -command -line +; OBJ: 0020 0a004028 23296f70 7420736f 6d657468 ..@(#)opt someth +; OBJ: 0030 696e6720 656c7365 20313233 0a000000 ing else 123.... + !llvm.commandline = !{!0, !1} !0 = !{!"clang -command -line"} !1 = !{!"something else 123"}