Skip to content

Commit 8a900f2

Browse files
committed
[ELF] Merge SHT_RISCV_ATTRIBUTES sections
Currently we take the first SHT_RISCV_ATTRIBUTES (.riscv.attributes) as the output. If we link an object without an extension with an object with the extension, the output Tag_RISCV_arch may not contain the extension and some tools like objdump -d will not decode the related instructions. This patch implements Tag_RISCV_stack_align/Tag_RISCV_arch/Tag_RISCV_unaligned_access merge as specified by https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#attributes For the deprecated Tag_RISCV_priv_spec{,_minor,_revision}, dump the attribute to the output iff all input agree on the value. This is different from GNU ld but our simple approach should be ok for deprecated tags. `RISCVAttributeParser::handler` currently warns about unknown tags. This behavior is retained. In GNU ld arm, tags >= 64 (mod 128) are ignored with a warning. If RISC-V ever wants to do something similar (riscv-non-isa/riscv-elf-psabi-doc#352), consider documenting it in the psABI and changing RISCVAttributeParser. Like GNU ld, zero value integer attributes and empty string attributes are not dumped to the output. Reviewed By: asb, kito-cheng Differential Revision: https://reviews.llvm.org/D138550
1 parent 5ba5aef commit 8a900f2

11 files changed

+435
-34
lines changed

lld/ELF/Arch/RISCV.cpp

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111
#include "Symbols.h"
1212
#include "SyntheticSections.h"
1313
#include "Target.h"
14+
#include "llvm/Support/ELFAttributes.h"
15+
#include "llvm/Support/LEB128.h"
16+
#include "llvm/Support/RISCVAttributeParser.h"
17+
#include "llvm/Support/RISCVAttributes.h"
18+
#include "llvm/Support/RISCVISAInfo.h"
1419
#include "llvm/Support/TimeProfiler.h"
1520

1621
using namespace llvm;
@@ -816,6 +821,205 @@ void elf::riscvFinalizeRelax(int passes) {
816821
}
817822
}
818823

824+
namespace {
825+
// Representation of the merged .riscv.attributes input sections. The psABI
826+
// specifies merge policy for attributes. E.g. if we link an object without an
827+
// extension with an object with the extension, the output Tag_RISCV_arch shall
828+
// contain the extension. Some tools like objdump parse .riscv.attributes and
829+
// disabling some instructions if the first Tag_RISCV_arch does not contain an
830+
// extension.
831+
class RISCVAttributesSection final : public SyntheticSection {
832+
public:
833+
RISCVAttributesSection()
834+
: SyntheticSection(0, SHT_RISCV_ATTRIBUTES, 1, ".riscv.attributes") {}
835+
836+
size_t getSize() const override { return size; }
837+
void writeTo(uint8_t *buf) override;
838+
839+
static constexpr StringRef vendor = "riscv";
840+
DenseMap<unsigned, unsigned> intAttr;
841+
DenseMap<unsigned, StringRef> strAttr;
842+
size_t size = 0;
843+
};
844+
} // namespace
845+
846+
static void mergeArch(RISCVISAInfo::OrderedExtensionMap &mergedExts,
847+
unsigned &mergedXlen, const InputSectionBase *sec,
848+
StringRef s) {
849+
auto maybeInfo =
850+
RISCVISAInfo::parseArchString(s, /*EnableExperimentalExtension=*/true,
851+
/*ExperimentalExtensionVersionCheck=*/true);
852+
if (!maybeInfo) {
853+
errorOrWarn(toString(sec) + ": " + s + ": " +
854+
llvm::toString(maybeInfo.takeError()));
855+
return;
856+
}
857+
858+
// Merge extensions.
859+
RISCVISAInfo &info = **maybeInfo;
860+
if (mergedExts.empty()) {
861+
mergedExts = info.getExtensions();
862+
mergedXlen = info.getXLen();
863+
} else {
864+
for (const auto &ext : info.getExtensions()) {
865+
if (auto it = mergedExts.find(ext.first); it != mergedExts.end()) {
866+
// TODO This is untested because RISCVISAInfo::parseArchString does not
867+
// accept unsupported versions yet.
868+
if (std::tie(it->second.MajorVersion, it->second.MinorVersion) >=
869+
std::tie(ext.second.MajorVersion, ext.second.MinorVersion))
870+
continue;
871+
}
872+
mergedExts[ext.first] = ext.second;
873+
}
874+
}
875+
}
876+
877+
static RISCVAttributesSection *
878+
mergeAttributesSection(const SmallVector<InputSectionBase *, 0> &sections) {
879+
RISCVISAInfo::OrderedExtensionMap exts;
880+
const InputSectionBase *firstStackAlign = nullptr;
881+
unsigned firstStackAlignValue = 0, xlen = 0;
882+
bool hasArch = false;
883+
884+
in.riscvAttributes = std::make_unique<RISCVAttributesSection>();
885+
auto &merged = static_cast<RISCVAttributesSection &>(*in.riscvAttributes);
886+
887+
// Collect all tags values from attributes section.
888+
const auto &attributesTags = RISCVAttrs::getRISCVAttributeTags();
889+
for (const InputSectionBase *sec : sections) {
890+
RISCVAttributeParser parser;
891+
if (Error e = parser.parse(sec->content(), support::little))
892+
warn(toString(sec) + ": " + llvm::toString(std::move(e)));
893+
for (const auto &tag : attributesTags) {
894+
switch (RISCVAttrs::AttrType(tag.attr)) {
895+
// Integer attributes.
896+
case RISCVAttrs::STACK_ALIGN:
897+
if (auto i = parser.getAttributeValue(tag.attr)) {
898+
auto r = merged.intAttr.try_emplace(tag.attr, *i);
899+
if (r.second) {
900+
firstStackAlign = sec;
901+
firstStackAlignValue = *i;
902+
} else if (r.first->second != *i) {
903+
errorOrWarn(toString(sec) + " has stack_align=" + Twine(*i) +
904+
" but " + toString(firstStackAlign) +
905+
" has stack_align=" + Twine(firstStackAlignValue));
906+
}
907+
}
908+
continue;
909+
case RISCVAttrs::UNALIGNED_ACCESS:
910+
if (auto i = parser.getAttributeValue(tag.attr))
911+
merged.intAttr[tag.attr] |= *i;
912+
continue;
913+
914+
// String attributes.
915+
case RISCVAttrs::ARCH:
916+
if (auto s = parser.getAttributeString(tag.attr)) {
917+
hasArch = true;
918+
mergeArch(exts, xlen, sec, *s);
919+
}
920+
continue;
921+
922+
// Attributes which use the default handling.
923+
case RISCVAttrs::PRIV_SPEC:
924+
case RISCVAttrs::PRIV_SPEC_MINOR:
925+
case RISCVAttrs::PRIV_SPEC_REVISION:
926+
break;
927+
}
928+
929+
// Fallback for deprecated priv_spec* and other unknown attributes: retain
930+
// the attribute if all input sections agree on the value. GNU ld uses 0
931+
// and empty strings as default values which are not dumped to the output.
932+
// TODO Adjust after resolution to
933+
// https://github.com/riscv-non-isa/riscv-elf-psabi-doc/issues/352
934+
if (tag.attr % 2 == 0) {
935+
if (auto i = parser.getAttributeValue(tag.attr)) {
936+
auto r = merged.intAttr.try_emplace(tag.attr, *i);
937+
if (!r.second && r.first->second != *i)
938+
r.first->second = 0;
939+
}
940+
} else if (auto s = parser.getAttributeString(tag.attr)) {
941+
auto r = merged.strAttr.try_emplace(tag.attr, *s);
942+
if (!r.second && r.first->second != *s)
943+
r.first->second = {};
944+
}
945+
}
946+
}
947+
948+
if (hasArch) {
949+
if (auto result = RISCVISAInfo::postProcessAndChecking(
950+
std::make_unique<RISCVISAInfo>(xlen, exts))) {
951+
merged.strAttr.try_emplace(RISCVAttrs::ARCH,
952+
saver().save((*result)->toString()));
953+
} else {
954+
errorOrWarn(llvm::toString(result.takeError()));
955+
}
956+
}
957+
958+
// The total size of headers: format-version [ <section-length> "vendor-name"
959+
// [ <file-tag> <size>.
960+
size_t size = 5 + merged.vendor.size() + 1 + 5;
961+
for (auto &attr : merged.intAttr)
962+
if (attr.second != 0)
963+
size += getULEB128Size(attr.first) + getULEB128Size(attr.second);
964+
for (auto &attr : merged.strAttr)
965+
if (!attr.second.empty())
966+
size += getULEB128Size(attr.first) + attr.second.size() + 1;
967+
merged.size = size;
968+
return &merged;
969+
}
970+
971+
void RISCVAttributesSection::writeTo(uint8_t *buf) {
972+
const size_t size = getSize();
973+
uint8_t *const end = buf + size;
974+
*buf = ELFAttrs::Format_Version;
975+
write32(buf + 1, size - 1);
976+
buf += 5;
977+
978+
memcpy(buf, vendor.data(), vendor.size());
979+
buf += vendor.size() + 1;
980+
981+
*buf = ELFAttrs::File;
982+
write32(buf + 1, end - buf);
983+
buf += 5;
984+
985+
for (auto &attr : intAttr) {
986+
if (attr.second == 0)
987+
continue;
988+
buf += encodeULEB128(attr.first, buf);
989+
buf += encodeULEB128(attr.second, buf);
990+
}
991+
for (auto &attr : strAttr) {
992+
if (attr.second.empty())
993+
continue;
994+
buf += encodeULEB128(attr.first, buf);
995+
memcpy(buf, attr.second.data(), attr.second.size());
996+
buf += attr.second.size() + 1;
997+
}
998+
}
999+
1000+
void elf::mergeRISCVAttributesSections() {
1001+
// Find the first input SHT_RISCV_ATTRIBUTES; return if not found.
1002+
size_t place =
1003+
llvm::find_if(ctx.inputSections,
1004+
[](auto *s) { return s->type == SHT_RISCV_ATTRIBUTES; }) -
1005+
ctx.inputSections.begin();
1006+
if (place == ctx.inputSections.size())
1007+
return;
1008+
1009+
// Extract all SHT_RISCV_ATTRIBUTES sections into `sections`.
1010+
SmallVector<InputSectionBase *, 0> sections;
1011+
llvm::erase_if(ctx.inputSections, [&](InputSectionBase *s) {
1012+
if (s->type != SHT_RISCV_ATTRIBUTES)
1013+
return false;
1014+
sections.push_back(s);
1015+
return true;
1016+
});
1017+
1018+
// Add the merged section.
1019+
ctx.inputSections.insert(ctx.inputSections.begin() + place,
1020+
mergeAttributesSection(sections));
1021+
}
1022+
8191023
TargetInfo *elf::getRISCVTargetInfo() {
8201024
static RISCV target;
8211025
return &target;

lld/ELF/Driver.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2820,6 +2820,10 @@ void LinkerDriver::link(opt::InputArgList &args) {
28202820
if (!config->relocatable)
28212821
combineEhSections();
28222822

2823+
// Merge .riscv.attributes sections.
2824+
if (config->emachine == EM_RISCV)
2825+
mergeRISCVAttributesSections();
2826+
28232827
{
28242828
llvm::TimeTraceScope timeScope("Assign sections");
28252829

lld/ELF/InputFiles.cpp

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -581,30 +581,6 @@ template <class ELFT> void ObjFile<ELFT>::parse(bool ignoreComdats) {
581581
}
582582
}
583583

584-
if (sec.sh_type == SHT_RISCV_ATTRIBUTES && config->emachine == EM_RISCV) {
585-
RISCVAttributeParser attributes;
586-
ArrayRef<uint8_t> contents =
587-
check(this->getObj().getSectionContents(sec));
588-
StringRef name = check(obj.getSectionName(sec, shstrtab));
589-
this->sections[i] = &InputSection::discarded;
590-
if (Error e = attributes.parse(contents, support::little)) {
591-
InputSection isec(*this, sec, name);
592-
warn(toString(&isec) + ": " + llvm::toString(std::move(e)));
593-
} else {
594-
// FIXME: Validate arch tag contains C if and only if EF_RISCV_RVC is
595-
// present.
596-
597-
// FIXME: Retain the first attribute section we see. Tools such as
598-
// llvm-objdump make use of the attribute section to determine which
599-
// standard extensions to enable. In a full implementation we would
600-
// merge all attribute sections.
601-
if (in.attributes == nullptr) {
602-
in.attributes = std::make_unique<InputSection>(*this, sec, name);
603-
this->sections[i] = in.attributes.get();
604-
}
605-
}
606-
}
607-
608584
if (sec.sh_type != SHT_GROUP)
609585
continue;
610586
StringRef signature = getShtGroupSignature(objSections, sec);

lld/ELF/SyntheticSections.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3807,6 +3807,7 @@ void PartitionIndexSection::writeTo(uint8_t *buf) {
38073807

38083808
void InStruct::reset() {
38093809
attributes.reset();
3810+
riscvAttributes.reset();
38103811
bss.reset();
38113812
bssRelRo.reset();
38123813
got.reset();

lld/ELF/SyntheticSections.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1273,6 +1273,7 @@ inline Partition &SectionBase::getPartition() const {
12731273
// a partition.
12741274
struct InStruct {
12751275
std::unique_ptr<InputSection> attributes;
1276+
std::unique_ptr<SyntheticSection> riscvAttributes;
12761277
std::unique_ptr<BssSection> bss;
12771278
std::unique_ptr<BssSection> bssRelRo;
12781279
std::unique_ptr<GotSection> got;

lld/ELF/Target.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ void addPPC64SaveRestore();
216216
uint64_t getPPC64TocBase();
217217
uint64_t getAArch64Page(uint64_t expr);
218218
void riscvFinalizeRelax(int passes);
219+
void mergeRISCVAttributesSections();
219220

220221
LLVM_LIBRARY_VISIBILITY extern const TargetInfo *target;
221222
TargetInfo *getTarget();

lld/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ ELF Improvements
3939
* ``--no-undefined-version`` is now the default; symbols named in version
4040
scripts that have no matching symbol in the output will be reported. Use
4141
``--undefined-version`` to revert to the old behavior.
42+
* The output ``SHT_RISCV_ATTRIBUTES`` section now merges all input components
43+
instead of picking the first input component.
44+
(`D138550 <https://reviews.llvm.org/D138550>`_)
4245

4346
Breaking changes
4447
----------------

lld/test/ELF/lto/riscv-attributes.ll

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
; REQUIRES: riscv
2+
3+
; RUN: rm -rf %t && split-file %s %t && cd %t
4+
; RUN: llvm-mc -filetype=obj -triple=riscv32 1.s -o 1.o
5+
; RUN: llvm-mc -filetype=obj -triple=riscv32 2.s -o 2.o
6+
; RUN: llvm-as a.ll -o a.bc
7+
; RUN: ld.lld 1.o 2.o a.bc -o out
8+
; RUN: llvm-readelf --arch-specific out | FileCheck %s
9+
10+
; CHECK: BuildAttributes {
11+
; CHECK-NEXT: FormatVersion: 0x41
12+
; CHECK-NEXT: Section 1 {
13+
; CHECK-NEXT: SectionLength: 61
14+
; CHECK-NEXT: Vendor: riscv
15+
; CHECK-NEXT: Tag: Tag_File (0x1)
16+
; CHECK-NEXT: Size: 51
17+
; CHECK-NEXT: FileAttributes {
18+
; CHECK-NEXT: Attribute {
19+
; CHECK-NEXT: Tag: 4
20+
; CHECK-NEXT: Value: 16
21+
; CHECK-NEXT: TagName: stack_align
22+
; CHECK-NEXT: Description: Stack alignment is 16-bytes
23+
; CHECK-NEXT: }
24+
; CHECK-NEXT: Attribute {
25+
; CHECK-NEXT: Tag: 6
26+
; CHECK-NEXT: Value: 1
27+
; CHECK-NEXT: TagName: unaligned_access
28+
; CHECK-NEXT: Description: Unaligned access
29+
; CHECK-NEXT: }
30+
; CHECK-NEXT: Attribute {
31+
; CHECK-NEXT: Tag: 5
32+
; CHECK-NEXT: TagName: arch
33+
; CHECK-NEXT: Value: rv32i2p0_m2p0_a2p0_f2p0_d2p0_c2p0_zbb1p0
34+
; CHECK-NEXT: }
35+
; CHECK-NEXT: }
36+
; CHECK-NEXT: }
37+
; CHECK-NEXT: }
38+
39+
;--- 1.s
40+
.attribute 4, 16
41+
.attribute 5, "rv32i2p0_m2p0_a2p0_f2p0_d2p0_c2p0"
42+
;--- 2.s
43+
.attribute 4, 16
44+
.attribute 5, "rv32i2p0_m2p0_f2p0_d2p0_zbb1p0"
45+
.attribute 6, 1
46+
47+
;--- a.ll
48+
target datalayout = "e-m:e-p:32:32-i64:64-n32-S128"
49+
target triple = "riscv32"
50+
51+
define void @_start() {
52+
ret void
53+
}

lld/test/ELF/riscv-attributes-place.s

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# REQUIRES: riscv
2+
## The merged SHT_RISCV_ATTRIBUTES is placed at the first input
3+
## SHT_RISCV_ATTRIBUTES.
4+
5+
# RUN: llvm-mc -filetype=obj -triple=riscv64 %s -o %t.o
6+
# RUN: ld.lld -e 0 %t.o %t.o -o %t
7+
# RUN: llvm-readelf -S %t | FileCheck %s
8+
9+
# CHECK: Name Type Address Off Size ES Flg Lk Inf Al
10+
# CHECK: .riscv.a PROGBITS 0000000000000000 [[#%x,]] 000002 00 0 0 1
11+
# CHECK-NEXT: .riscv.attributes RISCV_ATTRIBUTES 0000000000000000 [[#%x,]] 00001a 00 0 0 1
12+
# CHECK-NEXT: .riscv.b PROGBITS 0000000000000000 [[#%x,]] 000002 00 0 0 1
13+
14+
.section .riscv.a,""
15+
.byte 0
16+
17+
.section .riscv.attributes,"",@0x70000003
18+
.byte 0x41
19+
.long .Lend-.riscv.attributes-1
20+
.asciz "riscv" # vendor
21+
.Lbegin:
22+
.byte 1 # Tag_File
23+
.long .Lend-.Lbegin
24+
.byte 5 # Tag_RISCV_arch
25+
.asciz "rv64i2"
26+
.Lend:
27+
28+
.section .riscv.b,""
29+
.byte 0

0 commit comments

Comments
 (0)