diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp --- a/lld/ELF/Arch/RISCV.cpp +++ b/lld/ELF/Arch/RISCV.cpp @@ -816,6 +816,38 @@ } } +void elf::combineRISCVAttributesSections() { + auto isRISCVAttributesSection = [](InputSectionBase *s) { + return s->type == SHT_RISCV_ATTRIBUTES; + }; + + // Get the current place of the first `.riscv.attributes` section. + auto place = llvm::find_if(ctx.inputSections, isRISCVAttributesSection); + bool beginsWithAttributesSection = place == ctx.inputSections.begin(); + if (!beginsWithAttributesSection) + place = std::prev(place); + + SmallVector sections; + // Collect all `.riscv.attributes` sections. + llvm::erase_if(ctx.inputSections, [&](InputSectionBase *s) { + if (!isRISCVAttributesSection(s)) + return false; + sections.push_back(s); + return true; + }); + + if (sections.empty()) + return; + InputSectionBase *resultAttributesSection = + RISCVMergedAttributeSection::createRISCVMergedAttributesSection( + std::move(sections)); + + // Replace all existing attributes sections with merged one. + place = beginsWithAttributesSection ? ctx.inputSections.begin() + : std::next(place); + ctx.inputSections.insert(place, resultAttributesSection); +} + TargetInfo *elf::getRISCVTargetInfo() { static RISCV target; return ⌖ diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -2812,6 +2812,10 @@ if (!config->relocatable) combineEhSections(); + // Combine RISCV attributes. + if (config->emachine == EM_RISCV) + combineRISCVAttributesSections(); + { llvm::TimeTraceScope timeScope("Assign sections"); diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -28,6 +28,7 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/RISCVAttributeParser.h" +#include "llvm/Support/RISCVISAInfo.h" #include "llvm/Support/TarWriter.h" #include "llvm/Support/raw_ostream.h" @@ -586,22 +587,31 @@ ArrayRef contents = check(this->getObj().getSectionContents(sec)); StringRef name = check(obj.getSectionName(sec, shstrtab)); - this->sections[i] = &InputSection::discarded; if (Error e = attributes.parse(contents, support::little)) { + this->sections[i] = &InputSection::discarded; InputSection isec(*this, sec, name); warn(toString(&isec) + ": " + llvm::toString(std::move(e))); } else { - // FIXME: Validate arch tag contains C if and only if EF_RISCV_RVC is - // present. - - // FIXME: Retain the first attribute section we see. Tools such as - // llvm-objdump make use of the attribute section to determine which - // standard extensions to enable. In a full implementation we would - // merge all attribute sections. - if (in.attributes == nullptr) { - in.attributes = std::make_unique(*this, sec, name); - this->sections[i] = in.attributes.get(); + // Validate arch tag contains C if and only if EF_RISCV_RVC is present. + if (config->eflags & EF_RISCV_RVC) { + Optional Attr = + attributes.getAttributeString(RISCVAttrs::ARCH); + if (Attr) { + StringRef arch = *Attr; + auto parseResult = llvm::RISCVISAInfo::parseArchString( + arch, /*EnableExperimentalExtension=*/true, + /*ExperimentalExtensionVersionCheck=*/true); + if (!parseResult) + error(std::string("invalid arch name: ") + arch.str()); + if (!(*parseResult)->hasExtension("c")) { + errorOrWarn( + toString(this) + + " has set set RISCV_RVC flag, but RISCV_ARCH tag from " + + name.str() + " section doesn't contain C extension"); + } + } } + this->sections[i] = makeThreadLocal(*this, sec, name); } } diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h --- a/lld/ELF/SyntheticSections.h +++ b/lld/ELF/SyntheticSections.h @@ -24,6 +24,7 @@ #include "InputSection.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/MapVector.h" +#include "llvm/ADT/StringRef.h" #include "llvm/MC/StringTableBuilder.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Endian.h" @@ -1176,6 +1177,33 @@ bool finalized = false; }; +// This section is `riscv.attributes` section with merged attributes value +// from all same sections from input files. +class RISCVMergedAttributeSection final : public SyntheticSection { +public: + static InputSectionBase *createRISCVMergedAttributesSection( + llvm::SmallVector &&attributesSections); + size_t getSize() const override; + void writeTo(uint8_t *buf) override; + +private: + const size_t tagHeaderSize = 1 + 4; // Tag + Tag Size + // Size of subsection with attributes values content. + size_t contentsSize = 0; + llvm::DenseMap mergedNumericalAttributes; + llvm::DenseMap mergedStringAttributes; + const llvm::StringRef currentVendor = "riscv"; + + RISCVMergedAttributeSection(uint64_t flags, uint32_t type, + uint32_t alignment); + void calculateContentsSize(); + void mergeAttributes( + llvm::DenseMap> &attributes, + llvm::DenseMap> &attributesStr); + std::string + mergeArchExtensions(const llvm::DenseSet &arches); +}; + template class PartitionElfHeaderSection : public SyntheticSection { public: @@ -1274,6 +1302,7 @@ // a partition. struct InStruct { std::unique_ptr attributes; + std::unique_ptr riscvAttributes; std::unique_ptr bss; std::unique_ptr bssRelRo; std::unique_ptr got; diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -35,9 +35,13 @@ #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h" +#include "llvm/Support/ELFAttributes.h" #include "llvm/Support/Endian.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/Parallel.h" +#include "llvm/Support/RISCVAttributeParser.h" +#include "llvm/Support/RISCVAttributes.h" +#include "llvm/Support/RISCVISAInfo.h" #include "llvm/Support/TimeProfiler.h" #include @@ -3677,6 +3681,200 @@ return !finalized || !entries.empty(); } +static void parseInputRISCVAttributesSections( + const SmallVector &attributesSections, + DenseMap> &attributes, + DenseMap> &attributesStr) { + // Collect all tags values from attributes section. + auto attributesTags = RISCVAttrs::getRISCVAttributeTags(); + for (const InputSectionBase *s : attributesSections) { + RISCVAttributeParser parser; + if (Error e = parser.parse(s->data(), support::little)) + error(toString(s) + ": " + llvm::toString(std::move(e))); + for (const auto &tag : attributesTags) { + Optional value = parser.getAttributeValue(tag.attr); + if (value) { + attributes[tag.attr].insert(*value); + } else { + Optional stringValue = parser.getAttributeString(tag.attr); + if (stringValue) + attributesStr[tag.attr].insert(*stringValue); + } + } + } +} + +// Parse ARCH attributes value and merge all extensions from them. +std::string RISCVMergedAttributeSection::mergeArchExtensions( + const DenseSet &arches) { + RISCVISAInfo::OrderedExtensionMap resultExtensions; + unsigned resultXlen; + for (const StringRef archValue : arches) { + // Parse arch string. + auto parseResult = llvm::RISCVISAInfo::parseArchString( + archValue, /*EnableExperimentalExtension=*/true, + /*ExperimentalExtensionVersionCheck=*/true); + if (!parseResult) { + error(std::string("invalid arch name: ") + archValue.str() + " " + + llvm::toString(parseResult.takeError())); + } + + // Merge extensions. + auto &isaInfo = *parseResult; + auto currentExtensions = isaInfo->getExtensions(); + if (resultExtensions.empty()) { + resultExtensions = currentExtensions; + resultXlen = isaInfo->getXLen(); + } else { + if (resultXlen != isaInfo->getXLen()) { + error(archValue.str() + + " arch isn't compatible with archs from other object files. " + "Expected rv" + + Twine(resultXlen) + " configuration"); + } + for (auto const &extension : currentExtensions) { + if (resultExtensions.count(extension.first) != 0) { + // TODO: Versions should be the same otherwise parser should report + // unsupported version error. + assert(resultExtensions[extension.first].MajorVersion == + extension.second.MajorVersion && + resultExtensions[extension.first].MinorVersion == + extension.second.MinorVersion); + continue; + } + // Add extension. + resultExtensions[extension.first] = extension.second; + } + } + } + // Check result set of merged extensions. + std::unique_ptr isaInfo = + std::make_unique(resultXlen, resultExtensions); + auto checkResult = RISCVISAInfo::postProcessAndChecking(std::move(isaInfo)); + if (!checkResult) + error(llvm::toString(checkResult.takeError())); + auto &resultInfo = *checkResult; + return resultInfo->toString(); +} + +void RISCVMergedAttributeSection::mergeAttributes( + DenseMap> &attributes, + DenseMap> &attributesStr) { + auto attributesTags = RISCVAttrs::getRISCVAttributeTags(); + for (const auto &tag : attributesTags) { + switch (tag.attr) { + case RISCVAttrs::UNALIGNED_ACCESS: + if (attributes[RISCVAttrs::UNALIGNED_ACCESS].size() == 1) { + mergedNumericalAttributes[RISCVAttrs::UNALIGNED_ACCESS] = + *attributes[RISCVAttrs::UNALIGNED_ACCESS].begin(); + } else if (attributes[RISCVAttrs::UNALIGNED_ACCESS].size() > 1) { + mergedNumericalAttributes[RISCVAttrs::UNALIGNED_ACCESS] = + RISCVAttrs::ALLOWED; + } + break; + case RISCVAttrs::ARCH: + if (attributesStr[RISCVAttrs::ARCH].size() > 1) { + std::string mergedAttribute = + mergeArchExtensions(attributesStr[RISCVAttrs::ARCH]); + mergedStringAttributes[RISCVAttrs::ARCH] = mergedAttribute; + } else if (!attributesStr[RISCVAttrs::ARCH].empty()) { + mergedStringAttributes[RISCVAttrs::ARCH] = + attributesStr[RISCVAttrs::ARCH].begin()->str(); + } + break; + default: + if (attributes[tag.attr].size() > 1 || + attributesStr[tag.attr].size() > 1) { + error("input files contain different values for " + tag.tagName.str() + + " attribute."); + } + if (attributes[tag.attr].size() == 1) { + mergedNumericalAttributes[tag.attr] = *attributes[tag.attr].begin(); + } + if (attributesStr[tag.attr].size() == 1) { + mergedStringAttributes[tag.attr] = + attributesStr[tag.attr].begin()->str(); + } + } + } +} + +InputSectionBase * +RISCVMergedAttributeSection::createRISCVMergedAttributesSection( + SmallVector &&attributesSections) { + assert(!attributesSections.empty() && + "No `.riscv.attributes` sections to merge"); + const InputSectionBase *existingSection = attributesSections[0]; + in.riscvAttributes = std::unique_ptr( + new RISCVMergedAttributeSection(existingSection->flags, + existingSection->type, + existingSection->alignment)); + + DenseMap> attributes; + DenseMap> attributesStr; + parseInputRISCVAttributesSections(attributesSections, attributes, + attributesStr); + in.riscvAttributes->mergeAttributes(attributes, attributesStr); + in.riscvAttributes->calculateContentsSize(); + return in.riscvAttributes.get(); +} + +RISCVMergedAttributeSection::RISCVMergedAttributeSection(uint64_t flags, + uint32_t type, + uint32_t alignment) + : SyntheticSection(flags, type, alignment, ".riscv.attributes") {} +// Calculate size of attributes values subsection of `.riscv.attributes`. +void RISCVMergedAttributeSection::calculateContentsSize() { + size_t calculatedSize = 0; + for (const auto &attribute : mergedNumericalAttributes) { + calculatedSize += getULEB128Size(attribute.first); + calculatedSize += getULEB128Size(attribute.second); + } + + for (const auto &attribute : mergedStringAttributes) { + calculatedSize += getULEB128Size(attribute.first); + calculatedSize += attribute.second.size() + 1; // string + '\0' + } + + contentsSize = calculatedSize; +} + +size_t RISCVMergedAttributeSection::getSize() const { + // Vendor size + Vendor name + '\0' + const size_t vendorHeaderSize = 4 + currentVendor.size() + 1; + return vendorHeaderSize + tagHeaderSize + contentsSize + 1; +} + +void RISCVMergedAttributeSection::writeTo(uint8_t *buf) { + endian::write(buf, ELFAttrs::Format_Version, config->endianness); + write32(buf + 1, getSize() - 1); + memcpy(buf + 5, currentVendor.data(), currentVendor.size()); + endian::write(buf + currentVendor.size() + 5, 0, config->endianness); // '\0' + endian::write(buf + currentVendor.size() + 6, ELFAttrs::File, + config->endianness); + write32(buf + currentVendor.size() + 7, tagHeaderSize + contentsSize); + + auto attributesTags = RISCVAttrs::getRISCVAttributeTags(); + uint8_t *position = buf + currentVendor.size() + 11; + + // Write attributes values. + for (auto tagItem : attributesTags) { + if (mergedNumericalAttributes.count(tagItem.attr)) { + position += encodeULEB128(tagItem.attr, position); + position += + encodeULEB128(mergedNumericalAttributes[tagItem.attr], position); + } + if (mergedStringAttributes.count(tagItem.attr)) { + position += encodeULEB128(tagItem.attr, position); + std::string stringValue = mergedStringAttributes[tagItem.attr]; + memcpy(position, stringValue.data(), stringValue.size()); + position += stringValue.size(); + endian::write(position, 0, config->endianness); // '\0' + position++; + } + } +} + static uint8_t getAbiVersion() { // MIPS non-PIC executable gets ABI version 1. if (config->emachine == EM_MIPS) { @@ -3797,6 +3995,7 @@ void InStruct::reset() { attributes.reset(); + riscvAttributes.reset(); bss.reset(); bssRelRo.reset(); got.reset(); diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -216,6 +216,7 @@ uint64_t getPPC64TocBase(); uint64_t getAArch64Page(uint64_t expr); void riscvFinalizeRelax(int passes); +void combineRISCVAttributesSections(); LLVM_LIBRARY_VISIBILITY extern const TargetInfo *target; TargetInfo *getTarget(); diff --git a/lld/test/ELF/Inputs/riscv-attributes1.s b/lld/test/ELF/Inputs/riscv-attributes1.s --- a/lld/test/ELF/Inputs/riscv-attributes1.s +++ b/lld/test/ELF/Inputs/riscv-attributes1.s @@ -1,3 +1,3 @@ // Input that generates an object with a populated SHT_RISCV_ATTRIBUTES section -.attribute 4, 4 +.attribute 4, 16 .attribute 5, "rv32i2p0_m2p0_a2p0_f2p0_d2p0_c2p0" diff --git a/lld/test/ELF/Inputs/riscv-attributes2.s b/lld/test/ELF/Inputs/riscv-attributes2.s --- a/lld/test/ELF/Inputs/riscv-attributes2.s +++ b/lld/test/ELF/Inputs/riscv-attributes2.s @@ -1,3 +1,4 @@ // Input that generates an object with a populated SHT_RISCV_ATTRIBUTES section .attribute 4, 16 .attribute 5, "rv32i2p0_m2p0_f2p0_d2p0_zbb1p0" +.attribute 6, 1 diff --git a/lld/test/ELF/lto/riscv-merge-attributes.ll b/lld/test/ELF/lto/riscv-merge-attributes.ll new file mode 100644 --- /dev/null +++ b/lld/test/ELF/lto/riscv-merge-attributes.ll @@ -0,0 +1,44 @@ +; REQUIRES: riscv + +; RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=-relax %S/../Inputs/riscv-attributes1.s -o %t1.o +; RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=-relax %S/../Inputs/riscv-attributes2.s -o %t2.o +; RUN: llvm-as %s -o %t3.o +; RUN: ld.lld %t1.o %t2.o %t3.o -o %t +; RUN: llvm-readelf --arch-specific %t | FileCheck %s + +; CHECK: BuildAttributes { +; CHECK-NEXT: FormatVersion: 0x41 +; CHECK-NEXT: Section 1 { +; CHECK-NEXT: SectionLength: 61 +; CHECK-NEXT: Vendor: riscv +; CHECK-NEXT: Tag: Tag_File (0x1) +; CHECK-NEXT: Size: 51 +; CHECK-NEXT: FileAttributes { +; CHECK-NEXT: Attribute { +; CHECK-NEXT: Tag: 4 +; CHECK-NEXT: Value: 16 +; CHECK-NEXT: TagName: stack_align +; CHECK-NEXT: Description: Stack alignment is 16-bytes +; CHECK-NEXT: } +; CHECK-NEXT: Attribute { +; CHECK-NEXT: Tag: 5 +; CHECK-NEXT: TagName: arch +; CHECK-NEXT: Value: rv32i2p0_m2p0_a2p0_f2p0_d2p0_c2p0_zbb1p0 +; CHECK-NEXT: } +; CHECK-NEXT: Attribute { +; CHECK-NEXT: Tag: 6 +; CHECK-NEXT: Value: 1 +; CHECK-NEXT: TagName: unaligned_access +; CHECK-NEXT: Description: Unaligned access +; CHECK-NEXT: } +; CHECK-NEXT: } +; CHECK-NEXT: } +; CHECK-NEXT: } + +target datalayout = "e-m:e-p:32:32-i64:64-n32-S128" +target triple = "riscv32-unknown-linux-gnu" +attributes #0 = { "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+32bit,+a,+d,+f,+m,-relax,-save-restore" } + +define void @main() { + ret void +} \ No newline at end of file diff --git a/lld/test/ELF/riscv-merge-attributes.s b/lld/test/ELF/riscv-merge-attributes.s --- a/lld/test/ELF/riscv-merge-attributes.s +++ b/lld/test/ELF/riscv-merge-attributes.s @@ -9,25 +9,31 @@ # CHECK: BuildAttributes { # CHECK-NEXT: FormatVersion: 0x41 # CHECK-NEXT: Section 1 { -# CHECK-NEXT: SectionLength: 52 +# CHECK-NEXT: SectionLength: 61 # CHECK-NEXT: Vendor: riscv # CHECK-NEXT: Tag: Tag_File (0x1) -# CHECK-NEXT: Size: 42 +# CHECK-NEXT: Size: 51 # CHECK-NEXT: FileAttributes { # CHECK-NEXT: Attribute { # CHECK-NEXT: Tag: 4 -# CHECK-NEXT: Value: 4 +# CHECK-NEXT: Value: 16 # CHECK-NEXT: TagName: stack_align -# CHECK-NEXT: Description: Stack alignment is 4-bytes +# CHECK-NEXT: Description: Stack alignment is 16-bytes # CHECK-NEXT: } # CHECK-NEXT: Attribute { # CHECK-NEXT: Tag: 5 # CHECK-NEXT: TagName: arch -# CHECK-NEXT: Value: rv32i2p0_m2p0_a2p0_f2p0_d2p0_c2p0 +# CHECK-NEXT: Value: rv32i2p0_m2p0_a2p0_f2p0_d2p0_c2p0_zbb1p0 +# CHECK-NEXT: } +# CHECK-NEXT: Attribute { +# CHECK-NEXT: Tag: 6 +# CHECK-NEXT: Value: 1 +# CHECK-NEXT: TagName: unaligned_access +# CHECK-NEXT: Description: Unaligned access # CHECK-NEXT: } # CHECK-NEXT: } # CHECK-NEXT: } # CHECK-NEXT: } .attribute 4, 16 -.attribute 5, "rv32i2p0_m2p0_a2p0_f2p0_d2p0" +.attribute 5, "rv32i2p0_m2p0_a2p0_f2p0_d2p0" \ No newline at end of file diff --git a/llvm/include/llvm/Support/RISCVISAInfo.h b/llvm/include/llvm/Support/RISCVISAInfo.h --- a/llvm/include/llvm/Support/RISCVISAInfo.h +++ b/llvm/include/llvm/Support/RISCVISAInfo.h @@ -42,6 +42,9 @@ typedef std::map OrderedExtensionMap; + RISCVISAInfo(unsigned XLen, OrderedExtensionMap &Exts) + : XLen(XLen), FLen(0), MinVLen(0), MaxELen(0), MaxELenFp(0), Exts(Exts) {} + /// Parse RISCV ISA info from arch string. static llvm::Expected> parseArchString(StringRef Arch, bool EnableExperimentalExtension, @@ -73,6 +76,8 @@ static bool isSupportedExtension(StringRef Ext); static bool isSupportedExtension(StringRef Ext, unsigned MajorVersion, unsigned MinorVersion); + static llvm::Expected> + postProcessAndChecking(std::unique_ptr &&ISAInfo); private: RISCVISAInfo(unsigned XLen) @@ -95,9 +100,6 @@ void updateFLen(); void updateMinVLen(); void updateMaxELen(); - - static llvm::Expected> - postProcessAndChecking(std::unique_ptr &&ISAInfo); }; } // namespace llvm diff --git a/llvm/lib/Support/RISCVISAInfo.cpp b/llvm/lib/Support/RISCVISAInfo.cpp --- a/llvm/lib/Support/RISCVISAInfo.cpp +++ b/llvm/lib/Support/RISCVISAInfo.cpp @@ -741,6 +741,11 @@ errc::invalid_argument, "zvl*b requires v or zve* extension to also be specified"); + if (HasF && HasZfinx) + return createStringError( + errc::invalid_argument, + "zfinx extension can't be specified along with f extension"); + // Additional dependency checks. // TODO: The 'q' extension requires rv64. // TODO: It is illegal to specify 'e' extensions with 'f' and 'd'.