#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
+#include "llvm/Support/ELFAttributes.h"
+#include "llvm/Support/LEB128.h"
+#include "llvm/Support/RISCVAttributeParser.h"
+#include "llvm/Support/RISCVAttributes.h"
+#include "llvm/Support/RISCVISAInfo.h"
#include "llvm/Support/TimeProfiler.h"
using namespace llvm;
}
}
+namespace {
+// Representation of the merged .riscv.attributes input sections. The psABI
+// specifies merge policy for attributes. E.g. if we link an object without an
+// extension with an object with the extension, the output Tag_RISCV_arch shall
+// contain the extension. Some tools like objdump parse .riscv.attributes and
+// disabling some instructions if the first Tag_RISCV_arch does not contain an
+// extension.
+class RISCVAttributesSection final : public SyntheticSection {
+public:
+ RISCVAttributesSection()
+ : SyntheticSection(0, SHT_RISCV_ATTRIBUTES, 1, ".riscv.attributes") {}
+
+ size_t getSize() const override { return size; }
+ void writeTo(uint8_t *buf) override;
+
+ static constexpr StringRef vendor = "riscv";
+ DenseMap<unsigned, unsigned> intAttr;
+ DenseMap<unsigned, StringRef> strAttr;
+ size_t size = 0;
+};
+} // namespace
+
+static void mergeArch(RISCVISAInfo::OrderedExtensionMap &mergedExts,
+ unsigned &mergedXlen, const InputSectionBase *sec,
+ StringRef s) {
+ auto maybeInfo =
+ RISCVISAInfo::parseArchString(s, /*EnableExperimentalExtension=*/true,
+ /*ExperimentalExtensionVersionCheck=*/true);
+ if (!maybeInfo) {
+ errorOrWarn(toString(sec) + ": " + s + ": " +
+ llvm::toString(maybeInfo.takeError()));
+ return;
+ }
+
+ // Merge extensions.
+ RISCVISAInfo &info = **maybeInfo;
+ if (mergedExts.empty()) {
+ mergedExts = info.getExtensions();
+ mergedXlen = info.getXLen();
+ } else {
+ for (const auto &ext : info.getExtensions()) {
+ if (auto it = mergedExts.find(ext.first); it != mergedExts.end()) {
+ // TODO This is untested because RISCVISAInfo::parseArchString does not
+ // accept unsupported versions yet.
+ if (std::tie(it->second.MajorVersion, it->second.MinorVersion) >=
+ std::tie(ext.second.MajorVersion, ext.second.MinorVersion))
+ continue;
+ }
+ mergedExts[ext.first] = ext.second;
+ }
+ }
+}
+
+static RISCVAttributesSection *
+mergeAttributesSection(const SmallVector<InputSectionBase *, 0> §ions) {
+ RISCVISAInfo::OrderedExtensionMap exts;
+ const InputSectionBase *firstStackAlign = nullptr;
+ unsigned firstStackAlignValue = 0, xlen = 0;
+ bool hasArch = false;
+
+ in.riscvAttributes = std::make_unique<RISCVAttributesSection>();
+ auto &merged = static_cast<RISCVAttributesSection &>(*in.riscvAttributes);
+
+ // Collect all tags values from attributes section.
+ const auto &attributesTags = RISCVAttrs::getRISCVAttributeTags();
+ for (const InputSectionBase *sec : sections) {
+ RISCVAttributeParser parser;
+ if (Error e = parser.parse(sec->content(), support::little))
+ warn(toString(sec) + ": " + llvm::toString(std::move(e)));
+ for (const auto &tag : attributesTags) {
+ switch (RISCVAttrs::AttrType(tag.attr)) {
+ // Integer attributes.
+ case RISCVAttrs::STACK_ALIGN:
+ if (auto i = parser.getAttributeValue(tag.attr)) {
+ auto r = merged.intAttr.try_emplace(tag.attr, *i);
+ if (r.second) {
+ firstStackAlign = sec;
+ firstStackAlignValue = *i;
+ } else if (r.first->second != *i) {
+ errorOrWarn(toString(sec) + " has stack_align=" + Twine(*i) +
+ " but " + toString(firstStackAlign) +
+ " has stack_align=" + Twine(firstStackAlignValue));
+ }
+ }
+ continue;
+ case RISCVAttrs::UNALIGNED_ACCESS:
+ if (auto i = parser.getAttributeValue(tag.attr))
+ merged.intAttr[tag.attr] |= *i;
+ continue;
+
+ // String attributes.
+ case RISCVAttrs::ARCH:
+ if (auto s = parser.getAttributeString(tag.attr)) {
+ hasArch = true;
+ mergeArch(exts, xlen, sec, *s);
+ }
+ continue;
+
+ // Attributes which use the default handling.
+ case RISCVAttrs::PRIV_SPEC:
+ case RISCVAttrs::PRIV_SPEC_MINOR:
+ case RISCVAttrs::PRIV_SPEC_REVISION:
+ break;
+ }
+
+ // Fallback for deprecated priv_spec* and other unknown attributes: retain
+ // the attribute if all input sections agree on the value. GNU ld uses 0
+ // and empty strings as default values which are not dumped to the output.
+ // TODO Adjust after resolution to
+ // https://github.com/riscv-non-isa/riscv-elf-psabi-doc/issues/352
+ if (tag.attr % 2 == 0) {
+ if (auto i = parser.getAttributeValue(tag.attr)) {
+ auto r = merged.intAttr.try_emplace(tag.attr, *i);
+ if (!r.second && r.first->second != *i)
+ r.first->second = 0;
+ }
+ } else if (auto s = parser.getAttributeString(tag.attr)) {
+ auto r = merged.strAttr.try_emplace(tag.attr, *s);
+ if (!r.second && r.first->second != *s)
+ r.first->second = {};
+ }
+ }
+ }
+
+ if (hasArch) {
+ if (auto result = RISCVISAInfo::postProcessAndChecking(
+ std::make_unique<RISCVISAInfo>(xlen, exts))) {
+ merged.strAttr.try_emplace(RISCVAttrs::ARCH,
+ saver().save((*result)->toString()));
+ } else {
+ errorOrWarn(llvm::toString(result.takeError()));
+ }
+ }
+
+ // The total size of headers: format-version [ <section-length> "vendor-name"
+ // [ <file-tag> <size>.
+ size_t size = 5 + merged.vendor.size() + 1 + 5;
+ for (auto &attr : merged.intAttr)
+ if (attr.second != 0)
+ size += getULEB128Size(attr.first) + getULEB128Size(attr.second);
+ for (auto &attr : merged.strAttr)
+ if (!attr.second.empty())
+ size += getULEB128Size(attr.first) + attr.second.size() + 1;
+ merged.size = size;
+ return &merged;
+}
+
+void RISCVAttributesSection::writeTo(uint8_t *buf) {
+ const size_t size = getSize();
+ uint8_t *const end = buf + size;
+ *buf = ELFAttrs::Format_Version;
+ write32(buf + 1, size - 1);
+ buf += 5;
+
+ memcpy(buf, vendor.data(), vendor.size());
+ buf += vendor.size() + 1;
+
+ *buf = ELFAttrs::File;
+ write32(buf + 1, end - buf);
+ buf += 5;
+
+ for (auto &attr : intAttr) {
+ if (attr.second == 0)
+ continue;
+ buf += encodeULEB128(attr.first, buf);
+ buf += encodeULEB128(attr.second, buf);
+ }
+ for (auto &attr : strAttr) {
+ if (attr.second.empty())
+ continue;
+ buf += encodeULEB128(attr.first, buf);
+ memcpy(buf, attr.second.data(), attr.second.size());
+ buf += attr.second.size() + 1;
+ }
+}
+
+void elf::mergeRISCVAttributesSections() {
+ // Find the first input SHT_RISCV_ATTRIBUTES; return if not found.
+ size_t place =
+ llvm::find_if(ctx.inputSections,
+ [](auto *s) { return s->type == SHT_RISCV_ATTRIBUTES; }) -
+ ctx.inputSections.begin();
+ if (place == ctx.inputSections.size())
+ return;
+
+ // Extract all SHT_RISCV_ATTRIBUTES sections into `sections`.
+ SmallVector<InputSectionBase *, 0> sections;
+ llvm::erase_if(ctx.inputSections, [&](InputSectionBase *s) {
+ if (s->type != SHT_RISCV_ATTRIBUTES)
+ return false;
+ sections.push_back(s);
+ return true;
+ });
+
+ // Add the merged section.
+ ctx.inputSections.insert(ctx.inputSections.begin() + place,
+ mergeAttributesSection(sections));
+}
+
TargetInfo *elf::getRISCVTargetInfo() {
static RISCV target;
return ⌖
if (!config->relocatable)
combineEhSections();
+ // Merge .riscv.attributes sections.
+ if (config->emachine == EM_RISCV)
+ mergeRISCVAttributesSections();
+
{
llvm::TimeTraceScope timeScope("Assign sections");
}
}
- if (sec.sh_type == SHT_RISCV_ATTRIBUTES && config->emachine == EM_RISCV) {
- RISCVAttributeParser attributes;
- ArrayRef<uint8_t> 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)) {
- 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<InputSection>(*this, sec, name);
- this->sections[i] = in.attributes.get();
- }
- }
- }
-
if (sec.sh_type != SHT_GROUP)
continue;
StringRef signature = getShtGroupSignature(objSections, sec);
void InStruct::reset() {
attributes.reset();
+ riscvAttributes.reset();
bss.reset();
bssRelRo.reset();
got.reset();
// a partition.
struct InStruct {
std::unique_ptr<InputSection> attributes;
+ std::unique_ptr<SyntheticSection> riscvAttributes;
std::unique_ptr<BssSection> bss;
std::unique_ptr<BssSection> bssRelRo;
std::unique_ptr<GotSection> got;
uint64_t getPPC64TocBase();
uint64_t getAArch64Page(uint64_t expr);
void riscvFinalizeRelax(int passes);
+void mergeRISCVAttributesSections();
LLVM_LIBRARY_VISIBILITY extern const TargetInfo *target;
TargetInfo *getTarget();
* ``--no-undefined-version`` is now the default; symbols named in version
scripts that have no matching symbol in the output will be reported. Use
``--undefined-version`` to revert to the old behavior.
+* The output ``SHT_RISCV_ATTRIBUTES`` section now merges all input components
+ instead of picking the first input component.
+ (`D138550 <https://reviews.llvm.org/D138550>`_)
Breaking changes
----------------
--- /dev/null
+; REQUIRES: riscv
+
+; RUN: rm -rf %t && split-file %s %t && cd %t
+; RUN: llvm-mc -filetype=obj -triple=riscv32 1.s -o 1.o
+; RUN: llvm-mc -filetype=obj -triple=riscv32 2.s -o 2.o
+; RUN: llvm-as a.ll -o a.bc
+; RUN: ld.lld 1.o 2.o a.bc -o out
+; RUN: llvm-readelf --arch-specific out | 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: 6
+; CHECK-NEXT: Value: 1
+; CHECK-NEXT: TagName: unaligned_access
+; CHECK-NEXT: Description: Unaligned access
+; 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: }
+; CHECK-NEXT: }
+; CHECK-NEXT: }
+
+;--- 1.s
+.attribute 4, 16
+.attribute 5, "rv32i2p0_m2p0_a2p0_f2p0_d2p0_c2p0"
+;--- 2.s
+.attribute 4, 16
+.attribute 5, "rv32i2p0_m2p0_f2p0_d2p0_zbb1p0"
+.attribute 6, 1
+
+;--- a.ll
+target datalayout = "e-m:e-p:32:32-i64:64-n32-S128"
+target triple = "riscv32"
+
+define void @_start() {
+ ret void
+}
--- /dev/null
+# REQUIRES: riscv
+## The merged SHT_RISCV_ATTRIBUTES is placed at the first input
+## SHT_RISCV_ATTRIBUTES.
+
+# RUN: llvm-mc -filetype=obj -triple=riscv64 %s -o %t.o
+# RUN: ld.lld -e 0 %t.o %t.o -o %t
+# RUN: llvm-readelf -S %t | FileCheck %s
+
+# CHECK: Name Type Address Off Size ES Flg Lk Inf Al
+# CHECK: .riscv.a PROGBITS 0000000000000000 [[#%x,]] 000002 00 0 0 1
+# CHECK-NEXT: .riscv.attributes RISCV_ATTRIBUTES 0000000000000000 [[#%x,]] 00001a 00 0 0 1
+# CHECK-NEXT: .riscv.b PROGBITS 0000000000000000 [[#%x,]] 000002 00 0 0 1
+
+.section .riscv.a,""
+.byte 0
+
+.section .riscv.attributes,"",@0x70000003
+.byte 0x41
+.long .Lend-.riscv.attributes-1
+.asciz "riscv" # vendor
+.Lbegin:
+.byte 1 # Tag_File
+.long .Lend-.Lbegin
+.byte 5 # Tag_RISCV_arch
+.asciz "rv64i2"
+.Lend:
+
+.section .riscv.b,""
+.byte 0
# REQUIRES: riscv
-# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=-relax %s -o %t.o
-# RUN: ld.lld %t.o -o %t
-# RUN: llvm-readelf --arch-specific %t | FileCheck %s
-# RUN: ld.lld %t.o %t.o -o %t2
-# RUN: llvm-readelf --arch-specific %t2 | FileCheck %s
+# RUN: rm -rf %t && split-file %s %t && cd %t
+# RUN: llvm-mc -filetype=obj -triple=riscv64 a.s -o a.o
+# RUN: ld.lld -e 0 a.o -o out 2>&1 | count 0
+# RUN: llvm-readobj --arch-specific out | FileCheck %s
+# RUN: ld.lld -e 0 a.o a.o -o out1 2>&1 | count 0
+# RUN: llvm-readobj --arch-specific out1 | FileCheck %s
+# RUN: ld.lld -r a.o a.o -o out1 2>&1 | count 0
+# RUN: llvm-readobj --arch-specific out1 | FileCheck %s
+
+# RUN: llvm-mc -filetype=obj -triple=riscv64 b.s -o b.o
+# RUN: llvm-mc -filetype=obj -triple=riscv64 c.s -o c.o
+# RUN: ld.lld a.o b.o c.o -o out2
+# RUN: llvm-readobj --arch-specific out2 | FileCheck %s --check-prefix=CHECK2
+
+# RUN: llvm-mc -filetype=obj -triple=riscv64 invalid_ext.s -o invalid_ext.o
+# RUN: not ld.lld invalid_ext.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=INVALID_EXT --implicit-check-not=error:
+# INVALID_EXT: error: invalid_ext.o:(.riscv.attributes): rv64i2p0_y2p0: invalid standard user-level extension 'y'
+
+## A zero value attribute is not printed.
+# RUN: llvm-mc -filetype=obj -triple=riscv64 unaligned_access_0.s -o unaligned_access_0.o
+# RUN: ld.lld -e 0 --fatal-warnings a.o unaligned_access_0.o -o unaligned_access_0
+# RUN: llvm-readobj -A unaligned_access_0 | FileCheck /dev/null --implicit-check-not='TagName: unaligned_access'
+
+## Differing stack_align values lead to an error.
+# RUN: llvm-mc -filetype=obj -triple=riscv64 diff_stack_align.s -o diff_stack_align.o
+# RUN: not ld.lld a.o b.o c.o diff_stack_align.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=STACK_ALIGN --implicit-check-not=error:
+# STACK_ALIGN: error: diff_stack_align.o:(.riscv.attributes) has stack_align=32 but a.o:(.riscv.attributes) has stack_align=16
+
+## The deprecated priv_spec is not handled as GNU ld does.
+## Differing priv_spec attributes lead to an absent attribute.
+# RUN: llvm-mc -filetype=obj -triple=riscv64 diff_priv_spec.s -o diff_priv_spec.o
+# RUN: ld.lld -e 0 --fatal-warnings a.o b.o c.o diff_priv_spec.o -o diff_priv_spec
+# RUN: llvm-readobj -A diff_priv_spec | FileCheck /dev/null --implicit-check-not='TagName: priv_spec'
+
+## Unknown tags currently lead to warnings.
+# RUN: llvm-mc -filetype=obj -triple=riscv64 unknown13.s -o unknown13.o
+# RUN: llvm-mc -filetype=obj -triple=riscv64 unknown13a.s -o unknown13a.o
+# RUN: ld.lld -e 0 unknown13.o unknown13.o unknown13a.o -o unknown13 2>&1 | FileCheck %s --check-prefix=UNKNOWN13 --implicit-check-not=warning:
+# UNKNOWN13-COUNT-2: warning: unknown13.o:(.riscv.attributes): invalid tag 0xd at offset 0x10
+# UNKNOWN13: warning: unknown13a.o:(.riscv.attributes): invalid tag 0xd at offset 0x10
+
+# RUN: llvm-mc -filetype=obj -triple=riscv64 unknown22.s -o unknown22.o
+# RUN: llvm-mc -filetype=obj -triple=riscv64 unknown22a.s -o unknown22a.o
+# RUN: ld.lld -e 0 unknown22.o unknown22.o unknown22a.o -o unknown22 2>&1 | FileCheck %s --check-prefix=UNKNOWN22 --implicit-check-not=warning:
+# UNKNOWN22-COUNT-2: warning: unknown22.o:(.riscv.attributes): invalid tag 0x16 at offset 0x10
+# UNKNOWN22: warning: unknown22a.o:(.riscv.attributes): invalid tag 0x16 at offset 0x10
# CHECK: BuildAttributes {
# CHECK-NEXT: FormatVersion: 0x41
# CHECK-NEXT: }
# CHECK-NEXT: }
-.attribute 4, 16
-.attribute 5, "rv64i2p0_m2p0_a2p0_f2p0_d2p0_c2p0"
+# CHECK2: BuildAttributes {
+# CHECK2-NEXT: FormatVersion: 0x41
+# CHECK2-NEXT: Section 1 {
+# CHECK2-NEXT: SectionLength: 95
+# CHECK2-NEXT: Vendor: riscv
+# CHECK2-NEXT: Tag: Tag_File (0x1)
+# CHECK2-NEXT: Size: 85
+# CHECK2-NEXT: FileAttributes {
+# CHECK2-NEXT: Attribute {
+# CHECK2-NEXT: Tag: 4
+# CHECK2-NEXT: Value: 16
+# CHECK2-NEXT: TagName: stack_align
+# CHECK2-NEXT: Description: Stack alignment is 16-bytes
+# CHECK2-NEXT: }
+# CHECK2-NEXT: Attribute {
+# CHECK2-NEXT: Tag: 6
+# CHECK2-NEXT: Value: 1
+# CHECK2-NEXT: TagName: unaligned_access
+# CHECK2-NEXT: Description: Unaligned access
+# CHECK2-NEXT: }
+# CHECK2-NEXT: Attribute {
+# CHECK2-NEXT: Tag: 8
+# CHECK2-NEXT: TagName: priv_spec
+# CHECK2-NEXT: Value: 2
+# CHECK2-NEXT: }
+# CHECK2-NEXT: Attribute {
+# CHECK2-NEXT: Tag: 10
+# CHECK2-NEXT: TagName: priv_spec_minor
+# CHECK2-NEXT: Value: 2
+# CHECK2-NEXT: }
+# CHECK2-NEXT: Attribute {
+# CHECK2-NEXT: Tag: 5
+# CHECK2-NEXT: TagName: arch
+# CHECK2-NEXT: Value: rv64i2p0_m2p0_a2p0_f2p0_d2p0_c2p0_zkt1p0_zve32f1p0_zve32x1p0_zvl32b1p0
+# CHECK2-NEXT: }
+# CHECK2-NEXT: }
+# CHECK2-NEXT: }
+# CHECK2-NEXT: }
+
+#--- a.s
+.attribute stack_align, 16
+.attribute arch, "rv64i2p0_m2p0_a2p0_f2p0_d2p0_c2p0"
+.attribute unaligned_access, 0
+
+#--- b.s
+.attribute stack_align, 16
+.attribute arch, "rv64i2p0_m2p0_a2p0_f2p0_d2p0_c2p0"
+.attribute priv_spec, 2
+.attribute priv_spec_minor, 2
+
+#--- c.s
+.attribute stack_align, 16
+.attribute arch, "rv64i2p0_f2p0_zkt1p0_zve32f1p0_zve32x1p0_zvl32b1p0"
+.attribute unaligned_access, 1
+.attribute priv_spec, 2
+.attribute priv_spec_minor, 2
+
+#--- invalid_ext.s
+.section .riscv.attributes,"",@0x70000003
+.byte 0x41
+.long .Lend-.riscv.attributes-1
+.asciz "riscv" # vendor
+.Lbegin:
+.byte 1 # Tag_File
+.long .Lend-.Lbegin
+.byte 5 # Tag_RISCV_arch
+.asciz "rv64i2p0_y2p0"
+.Lend:
+
+#--- unaligned_access_0.s
+.attribute unaligned_access, 0
+
+#--- diff_stack_align.s
+.attribute stack_align, 32
+
+#--- diff_priv_spec.s
+.attribute priv_spec, 3
+.attribute priv_spec_minor, 3
+
+#--- unknown13.s
+.attribute 13, "0"
+#--- unknown13a.s
+.attribute 13, "1"
+
+#--- unknown22.s
+.attribute 22, 1
+#--- unknown22a.s
+.attribute 22, 2
typedef std::map<std::string, RISCVExtensionInfo, ExtensionComparator>
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<std::unique_ptr<RISCVISAInfo>>
parseArchString(StringRef Arch, bool EnableExperimentalExtension,
static bool isSupportedExtension(StringRef Ext);
static bool isSupportedExtension(StringRef Ext, unsigned MajorVersion,
unsigned MinorVersion);
+ static llvm::Expected<std::unique_ptr<RISCVISAInfo>>
+ postProcessAndChecking(std::unique_ptr<RISCVISAInfo> &&ISAInfo);
private:
RISCVISAInfo(unsigned XLen)
void updateFLen();
void updateMinVLen();
void updateMaxELen();
-
- static llvm::Expected<std::unique_ptr<RISCVISAInfo>>
- postProcessAndChecking(std::unique_ptr<RISCVISAInfo> &&ISAInfo);
};
} // namespace llvm