Files
clang-p2996/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
Saleem Abdulrasool bbea64250f RISCV: adjust handling of relocation emission for RISCV
This re-architects the RISCV relocation handling to bring the
implementation closer in line with the implementation in binutils.  We
would previously aggressively resolve the relocation.  With this
restructuring, we always will emit a paired relocation for any symbolic
difference of the type of S±T[±C] where S and T are labels and C is a
constant.

GAS has a special target hook controlled by `RELOC_EXPANSION_POSSIBLE`
which indicates that a fixup may be expanded into multiple relocations.
This is used by the RISCV backend to always emit a paired relocation -
either ADD[WIDTH] + SUB[WIDTH] for text relocations or SET[WIDTH] +
SUB[WIDTH] for a debug info relocation.  Irrespective of whether linker
relaxation support is enabled, symbolic difference is always emitted as
a paired relocation.

This change also sinks the target specific behaviour down into the
target specific area rather than exposing it to the shared relocation
handling.  In the process, we also sink the "special" handling for debug
information down into the RISCV target.  Although this improves the path
for the other targets, this is not necessarily entirely ideal either.
The changes in the debug info emission could be done through another
type of hook as this functionality would be required by any other target
which wishes to do linker relaxation.  However, as there are no other
targets in LLVM which currently do this, this is a reasonable thing to
do until such time as the code needs to be shared.

Improve the handling of the relocation (and add a reduced test case from
the Linux kernel) to ensure that we handle complex expressions for
symbolic difference.  This ensures that we correct relocate symbols with
the adddends normalized and associated with the addition portion of the
paired relocation.

This change also addresses some review comments from Alex Bradbury about
the relocations meant for use in the DWARF CFA being named incorrectly
(using ADD6 instead of SET6) in the original change which introduced the
relocation type.

This resolves the issues with the symbolic difference emission
sufficiently to enable building the Linux kernel with clang+IAS+lld
(without linker relaxation).

Resolves PR50153, PR50156!
Fixes: ClangBuiltLinux/linux#1023, ClangBuiltLinux/linux#1143

Reviewed By: nickdesaulniers, maskray

Differential Revision: https://reviews.llvm.org/D103539
2021-06-17 08:20:02 -07:00

263 lines
8.8 KiB
C++

//===-- RISCVELFStreamer.cpp - RISCV ELF Target Streamer Methods ----------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file provides RISCV specific target streamer methods.
//
//===----------------------------------------------------------------------===//
#include "RISCVELFStreamer.h"
#include "RISCVAsmBackend.h"
#include "RISCVBaseInfo.h"
#include "RISCVMCTargetDesc.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCCodeEmitter.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCSectionELF.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/MCValue.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/RISCVAttributes.h"
using namespace llvm;
// This part is for ELF object output.
RISCVTargetELFStreamer::RISCVTargetELFStreamer(MCStreamer &S,
const MCSubtargetInfo &STI)
: RISCVTargetStreamer(S), CurrentVendor("riscv") {
MCAssembler &MCA = getStreamer().getAssembler();
const FeatureBitset &Features = STI.getFeatureBits();
auto &MAB = static_cast<RISCVAsmBackend &>(MCA.getBackend());
RISCVABI::ABI ABI = MAB.getTargetABI();
assert(ABI != RISCVABI::ABI_Unknown && "Improperly initialised target ABI");
unsigned EFlags = MCA.getELFHeaderEFlags();
if (Features[RISCV::FeatureStdExtC])
EFlags |= ELF::EF_RISCV_RVC;
switch (ABI) {
case RISCVABI::ABI_ILP32:
case RISCVABI::ABI_LP64:
break;
case RISCVABI::ABI_ILP32F:
case RISCVABI::ABI_LP64F:
EFlags |= ELF::EF_RISCV_FLOAT_ABI_SINGLE;
break;
case RISCVABI::ABI_ILP32D:
case RISCVABI::ABI_LP64D:
EFlags |= ELF::EF_RISCV_FLOAT_ABI_DOUBLE;
break;
case RISCVABI::ABI_ILP32E:
EFlags |= ELF::EF_RISCV_RVE;
break;
case RISCVABI::ABI_Unknown:
llvm_unreachable("Improperly initialised target ABI");
}
MCA.setELFHeaderEFlags(EFlags);
}
MCELFStreamer &RISCVTargetELFStreamer::getStreamer() {
return static_cast<MCELFStreamer &>(Streamer);
}
void RISCVTargetELFStreamer::emitDirectiveOptionPush() {}
void RISCVTargetELFStreamer::emitDirectiveOptionPop() {}
void RISCVTargetELFStreamer::emitDirectiveOptionPIC() {}
void RISCVTargetELFStreamer::emitDirectiveOptionNoPIC() {}
void RISCVTargetELFStreamer::emitDirectiveOptionRVC() {}
void RISCVTargetELFStreamer::emitDirectiveOptionNoRVC() {}
void RISCVTargetELFStreamer::emitDirectiveOptionRelax() {}
void RISCVTargetELFStreamer::emitDirectiveOptionNoRelax() {}
void RISCVTargetELFStreamer::emitAttribute(unsigned Attribute, unsigned Value) {
setAttributeItem(Attribute, Value, /*OverwriteExisting=*/true);
}
void RISCVTargetELFStreamer::emitTextAttribute(unsigned Attribute,
StringRef String) {
setAttributeItem(Attribute, String, /*OverwriteExisting=*/true);
}
void RISCVTargetELFStreamer::emitIntTextAttribute(unsigned Attribute,
unsigned IntValue,
StringRef StringValue) {
setAttributeItems(Attribute, IntValue, StringValue,
/*OverwriteExisting=*/true);
}
void RISCVTargetELFStreamer::finishAttributeSection() {
if (Contents.empty())
return;
if (AttributeSection) {
Streamer.SwitchSection(AttributeSection);
} else {
MCAssembler &MCA = getStreamer().getAssembler();
AttributeSection = MCA.getContext().getELFSection(
".riscv.attributes", ELF::SHT_RISCV_ATTRIBUTES, 0);
Streamer.SwitchSection(AttributeSection);
Streamer.emitInt8(ELFAttrs::Format_Version);
}
// Vendor size + Vendor name + '\0'
const size_t VendorHeaderSize = 4 + CurrentVendor.size() + 1;
// Tag + Tag Size
const size_t TagHeaderSize = 1 + 4;
const size_t ContentsSize = calculateContentSize();
Streamer.emitInt32(VendorHeaderSize + TagHeaderSize + ContentsSize);
Streamer.emitBytes(CurrentVendor);
Streamer.emitInt8(0); // '\0'
Streamer.emitInt8(ELFAttrs::File);
Streamer.emitInt32(TagHeaderSize + ContentsSize);
// Size should have been accounted for already, now
// emit each field as its type (ULEB or String).
for (AttributeItem item : Contents) {
Streamer.emitULEB128IntValue(item.Tag);
switch (item.Type) {
default:
llvm_unreachable("Invalid attribute type");
case AttributeType::Numeric:
Streamer.emitULEB128IntValue(item.IntValue);
break;
case AttributeType::Text:
Streamer.emitBytes(item.StringValue);
Streamer.emitInt8(0); // '\0'
break;
case AttributeType::NumericAndText:
Streamer.emitULEB128IntValue(item.IntValue);
Streamer.emitBytes(item.StringValue);
Streamer.emitInt8(0); // '\0'
break;
}
}
Contents.clear();
}
size_t RISCVTargetELFStreamer::calculateContentSize() const {
size_t Result = 0;
for (AttributeItem item : Contents) {
switch (item.Type) {
case AttributeType::Hidden:
break;
case AttributeType::Numeric:
Result += getULEB128Size(item.Tag);
Result += getULEB128Size(item.IntValue);
break;
case AttributeType::Text:
Result += getULEB128Size(item.Tag);
Result += item.StringValue.size() + 1; // string + '\0'
break;
case AttributeType::NumericAndText:
Result += getULEB128Size(item.Tag);
Result += getULEB128Size(item.IntValue);
Result += item.StringValue.size() + 1; // string + '\0';
break;
}
}
return Result;
}
namespace {
class RISCVELFStreamer : public MCELFStreamer {
static std::pair<unsigned, unsigned> getRelocPairForSize(unsigned Size) {
switch (Size) {
default:
llvm_unreachable("unsupported fixup size");
case 1:
return std::make_pair(RISCV::fixup_riscv_add_8, RISCV::fixup_riscv_sub_8);
case 2:
return std::make_pair(RISCV::fixup_riscv_add_16,
RISCV::fixup_riscv_sub_16);
case 4:
return std::make_pair(RISCV::fixup_riscv_add_32,
RISCV::fixup_riscv_sub_32);
case 8:
return std::make_pair(RISCV::fixup_riscv_add_64,
RISCV::fixup_riscv_sub_64);
}
}
static bool requiresFixups(MCContext &C, const MCExpr *Value,
const MCExpr *&LHS, const MCExpr *&RHS) {
const auto *MBE = dyn_cast<MCBinaryExpr>(Value);
if (MBE == nullptr)
return false;
MCValue E;
if (!Value->evaluateAsRelocatable(E, nullptr, nullptr))
return false;
if (E.getSymA() == nullptr || E.getSymB() == nullptr)
return false;
const auto &A = E.getSymA()->getSymbol();
const auto &B = E.getSymB()->getSymbol();
LHS =
MCBinaryExpr::create(MCBinaryExpr::Add, MCSymbolRefExpr::create(&A, C),
MCConstantExpr::create(E.getConstant(), C), C);
RHS = E.getSymB();
return (A.isInSection() ? A.getSection().hasInstructions()
: !A.getName().empty()) ||
(B.isInSection() ? B.getSection().hasInstructions()
: !B.getName().empty());
}
public:
RISCVELFStreamer(MCContext &C, std::unique_ptr<MCAsmBackend> MAB,
std::unique_ptr<MCObjectWriter> MOW,
std::unique_ptr<MCCodeEmitter> MCE)
: MCELFStreamer(C, std::move(MAB), std::move(MOW), std::move(MCE)) {}
void emitValueImpl(const MCExpr *Value, unsigned Size, SMLoc Loc) override {
const MCExpr *A, *B;
if (!requiresFixups(getContext(), Value, A, B))
return MCELFStreamer::emitValueImpl(Value, Size, Loc);
MCStreamer::emitValueImpl(Value, Size, Loc);
MCDataFragment *DF = getOrCreateDataFragment();
flushPendingLabels(DF, DF->getContents().size());
MCDwarfLineEntry::make(this, getCurrentSectionOnly());
unsigned Add, Sub;
std::tie(Add, Sub) = getRelocPairForSize(Size);
DF->getFixups().push_back(MCFixup::create(
DF->getContents().size(), A, static_cast<MCFixupKind>(Add), Loc));
DF->getFixups().push_back(MCFixup::create(
DF->getContents().size(), B, static_cast<MCFixupKind>(Sub), Loc));
DF->getContents().resize(DF->getContents().size() + Size, 0);
}
};
} // namespace
namespace llvm {
MCELFStreamer *createRISCVELFStreamer(MCContext &C,
std::unique_ptr<MCAsmBackend> MAB,
std::unique_ptr<MCObjectWriter> MOW,
std::unique_ptr<MCCodeEmitter> MCE,
bool RelaxAll) {
RISCVELFStreamer *S =
new RISCVELFStreamer(C, std::move(MAB), std::move(MOW), std::move(MCE));
S->getAssembler().setRelaxAll(RelaxAll);
return S;
}
} // namespace llvm