Skip to content

[MC,llvm-readobj,yaml2obj] Support CREL relocation format #91280

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions llvm/include/llvm/BinaryFormat/DynamicTags.def
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ DYNAMIC_TAG(RELRSZ, 35) // Size of Relr relocation table.
DYNAMIC_TAG(RELR, 36) // Address of relocation table (Relr entries).
DYNAMIC_TAG(RELRENT, 37) // Size of a Relr relocation entry.

// TODO: Experimental CREL relocations. LLVM will change the value and
// break compatibility in the future.
DYNAMIC_TAG(CREL, 0x40000026) // CREL relocation table

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we are using a section type in the [LOOS, HIOS) range, is it worth using a dynamic tag in the [LOOS, HIOS) range.

As an aside, would a tag containing the number of CRELs (NCREL for example). This in theory could be used to pre-allocate memory for an expanded CRELs. I don't think it is worth doing unless there is a clear need though. I could imagine most dynamic linkers iterating through the LEB directly.

Although not strictly necessary a CREL Size (CRELSZ?) could be useful for a consumer to read all the CRELs into memory without having to read the header first to find the size. Again not worth doing unless we have a definite need for it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The commit message mentions:

Without linker/dynamic loader concern, there is even less concern for people unintentionally using this.

I tentatively think it is not worth the trouble to avoid the generic range.

Although not strictly necessary a CREL Size (CRELSZ?) could be useful for a consumer to read all the CRELs into memory without having to read the header first to find the size. Again not worth doing unless we have a definite need for it.

I've thought about this but believe CRELSZ is not so necessary.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to avoid generic numbers for dynamic tags/section types etc, except possibly generic numbers that are a long way from the "normal" range. The reason for this is we don't know what values are going to get standardised, assuming this feature even gets accepted into the standard. If, for example, another feature gets accepted into the standard before CREL, it might use up the slot you've "allocated" for the DT_CREL here, which could cause problems with loaders that have supported the experimental CREL implementation. On the other hand, a different special number (whether in a dedicated range or otherwise) that isn't adjacent to the list will not have the issue of a potential clash.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it might use up the slot you've "allocated"

I understand the concern but this is just impossible given the essential state of the generic ABI...

(I have some notes at https://maskray.me/blog/2024-05-26-evolution-of-elf-object-file-format#a-future-in-flux)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand the concern but this is just impossible given the essential state of the generic ABI...

I don't understand this comment. There's an active mailing list, where proposals are discussed and adopted, even if there's no fully published document, so a different proposal could easily come up whilst this is still under review. Perhaps it'll be implemented in GNU/Solaris/... initially with the same generic value. Whose meaning of the value should then win?

In the worst case, maybe the generic ABI list ends up rejecting this crel proposal. The values aren't reserved for LLVM usage, so the next proposal to come along will use those same values and we then have a clash between what LLVM wants to use the value for (and there might be objects out there using that value, if this feature is adopted in an LLVM release), which would cause us all sorts of headaches in the future.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I have more bandwidth to implement the DT_CREL support for binutils and some loader implementations, I'll use 38.
This avoids the potential annoyance of picking a version initially and then needing to change it later, which could cause issues with toolchain/loader interop inconvenience.

In practice, achieving consensus between major ELF toolchain vendors like GNU and LLVM is enough to prevent conflicts...
The generic ABI hasn't seen a new DT_* addition besides RELR since 2013.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still disagree with this. Is it just me? What do @smithp35 and @dwblaikie think?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to avoid generic numbers for dynamic tags/section types etc, except possibly generic numbers that are a long way from the "normal" range.

+1.

I've tried to pushback on using any standard range before it's been standardized (eg: #91280 (comment) #91280 (comment) ) but I haven't looked through the change completely to see all the enumerations used and how they're reserved, whether they use an extension space or what the argument is for them not using one.

Perhaps someone could summarize this aspect of the patch?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed DT_CREL to 0x40000026 for now.

It's complex but the spec on sco.com might get further updates, but it doesn't matter if GNU and LLVM can be kept in sync and the specification is essentially in the public domain. https://news.ycombinator.com/item?id=40486068

DYNAMIC_TAG_MARKER(LOOS, 0x60000000) // Start of environment specific tags.
DYNAMIC_TAG_MARKER(HIOS, 0x6FFFFFFF) // End of environment specific tags.
DYNAMIC_TAG_MARKER(LOPROC, 0x70000000) // Start of processor specific tags.
Expand Down
7 changes: 6 additions & 1 deletion llvm/include/llvm/BinaryFormat/ELF.h
Original file line number Diff line number Diff line change
Expand Up @@ -1082,7 +1082,10 @@ enum : unsigned {
SHT_SYMTAB_SHNDX = 18, // Indices for SHN_XINDEX entries.
// Experimental support for SHT_RELR sections. For details, see proposal
// at https://groups.google.com/forum/#!topic/generic-abi/bX460iggiKg
SHT_RELR = 19, // Relocation entries; only offsets.
SHT_RELR = 19, // Relocation entries; only offsets.
// TODO: Experimental CREL relocations. LLVM will change the value and
// break compatibility in the future.
SHT_CREL = 0x40000014,
SHT_LOOS = 0x60000000, // Lowest operating system-specific type.
// Android packed relocation section types.
// https://android.googlesource.com/platform/bionic/+/6f12bfece5dcc01325e0abba56a46b1bcf991c69/tools/relocation_packer/src/elf_file.cc#37
Expand Down Expand Up @@ -1937,6 +1940,8 @@ enum {
ELFCOMPRESS_HIPROC = 0x7fffffff // End of processor-specific.
};

constexpr unsigned CREL_HDR_ADDEND = 4;

/// Convert an architecture name into ELF's e_machine value.
uint16_t convertArchNameToEMachine(StringRef Arch);

Expand Down
3 changes: 3 additions & 0 deletions llvm/include/llvm/MC/MCTargetOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ class MCTargetOptions {

bool Dwarf64 : 1;

// Use CREL relocation format for ELF.
bool Crel = false;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit here and elsewhere: Is "Crel" a correct way of spelling this? Should it be "CRel" (for "CompressedRelocations")? I'm assuming you're deliberately avoiding using CREL in this context, but an argument could be made for that too.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If LLVM adopts https://llvm.org/docs/Proposals/VariableNames.html , I'd like to use crel instead of cRel. But with the capitalized naming, I'd prefer Crel. This makes many functions' names xxxCrels align better with xxxRels.


// If true, prefer R_X86_64_[REX_]GOTPCRELX to R_X86_64_GOTPCREL on x86-64
// ELF.
bool X86RelaxRelocations = true;
Expand Down
2 changes: 2 additions & 0 deletions llvm/include/llvm/MC/MCTargetOptionsCommandFlags.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ bool getNoTypeCheck();

bool getSaveTempLabels();

bool getCrel();

bool getX86RelaxRelocations();

std::string getABIName();
Expand Down
5 changes: 5 additions & 0 deletions llvm/include/llvm/Object/ELF.h
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,11 @@ class ELFFile {

std::vector<Elf_Rel> decode_relrs(Elf_Relr_Range relrs) const;

Expected<uint64_t> getCrelHeader(ArrayRef<uint8_t> Content) const;
using RelsOrRelas = std::pair<std::vector<Elf_Rel>, std::vector<Elf_Rela>>;
Expected<RelsOrRelas> decodeCrel(ArrayRef<uint8_t> Content) const;
Expected<RelsOrRelas> crels(const Elf_Shdr &Sec) const;

Expected<std::vector<Elf_Rela>> android_relas(const Elf_Shdr &Sec) const;

/// Iterate over program header table.
Expand Down
26 changes: 26 additions & 0 deletions llvm/include/llvm/Object/ELFTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ template <class ELFT> struct Elf_Sym_Impl;
template <class ELFT> struct Elf_Dyn_Impl;
template <class ELFT> struct Elf_Phdr_Impl;
template <class ELFT, bool isRela> struct Elf_Rel_Impl;
template <bool Is64> struct Elf_Crel_Impl;
template <class ELFT> struct Elf_Verdef_Impl;
template <class ELFT> struct Elf_Verdaux_Impl;
template <class ELFT> struct Elf_Verneed_Impl;
Expand Down Expand Up @@ -62,6 +63,7 @@ template <endianness E, bool Is64> struct ELFType {
using Phdr = Elf_Phdr_Impl<ELFType<E, Is64>>;
using Rel = Elf_Rel_Impl<ELFType<E, Is64>, false>;
using Rela = Elf_Rel_Impl<ELFType<E, Is64>, true>;
using Crel = Elf_Crel_Impl<Is64>;
using Relr = packed<uint>;
using Verdef = Elf_Verdef_Impl<ELFType<E, Is64>>;
using Verdaux = Elf_Verdaux_Impl<ELFType<E, Is64>>;
Expand Down Expand Up @@ -117,6 +119,7 @@ using ELF64BE = ELFType<llvm::endianness::big, true>;
using Elf_Phdr = typename ELFT::Phdr; \
using Elf_Rel = typename ELFT::Rel; \
using Elf_Rela = typename ELFT::Rela; \
using Elf_Crel = typename ELFT::Crel; \
using Elf_Relr = typename ELFT::Relr; \
using Elf_Verdef = typename ELFT::Verdef; \
using Elf_Verdaux = typename ELFT::Verdaux; \
Expand Down Expand Up @@ -385,6 +388,7 @@ template <endianness Endianness>
struct Elf_Rel_Impl<ELFType<Endianness, false>, false> {
LLVM_ELF_IMPORT_TYPES(Endianness, false)
static const bool HasAddend = false;
static const bool IsCrel = false;
Elf_Addr r_offset; // Location (file byte offset, or program virtual addr)
Elf_Word r_info; // Symbol table index and type of relocation to apply

Expand Down Expand Up @@ -421,13 +425,15 @@ struct Elf_Rel_Impl<ELFType<Endianness, false>, true>
: public Elf_Rel_Impl<ELFType<Endianness, false>, false> {
LLVM_ELF_IMPORT_TYPES(Endianness, false)
static const bool HasAddend = true;
static const bool IsCrel = false;
Elf_Sword r_addend; // Compute value for relocatable field by adding this
};

template <endianness Endianness>
struct Elf_Rel_Impl<ELFType<Endianness, true>, false> {
LLVM_ELF_IMPORT_TYPES(Endianness, true)
static const bool HasAddend = false;
static const bool IsCrel = false;
Elf_Addr r_offset; // Location (file byte offset, or program virtual addr)
Elf_Xword r_info; // Symbol table index and type of relocation to apply

Expand Down Expand Up @@ -474,9 +480,29 @@ struct Elf_Rel_Impl<ELFType<Endianness, true>, true>
: public Elf_Rel_Impl<ELFType<Endianness, true>, false> {
LLVM_ELF_IMPORT_TYPES(Endianness, true)
static const bool HasAddend = true;
static const bool IsCrel = false;
Elf_Sxword r_addend; // Compute value for relocatable field by adding this.
};

// In-memory representation. The serialized representation uses LEB128.
template <bool Is64> struct Elf_Crel_Impl {
using uint = std::conditional_t<Is64, uint64_t, uint32_t>;
static const bool IsRela = true;
static const bool IsCrel = true;
uint r_offset;
uint32_t r_symidx;
uint32_t r_type;
std::conditional_t<Is64, int64_t, int32_t> r_addend;

// Dummy bool parameter is for compatibility with Elf_Rel_Impl.
uint32_t getType(bool) const { return r_type; }
uint32_t getSymbol(bool) const { return r_symidx; }
void setSymbolAndType(uint32_t s, unsigned char t, bool) {
r_symidx = s;
r_type = t;
}
};

template <class ELFT>
struct Elf_Ehdr_Impl {
LLVM_ELF_IMPORT_TYPES_ELFT(ELFT)
Expand Down
117 changes: 88 additions & 29 deletions llvm/lib/MC/ELFObjectWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "llvm/MC/StringTableBuilder.h"
#include "llvm/Support/Alignment.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compression.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/EndianStream.h"
Expand Down Expand Up @@ -256,7 +257,7 @@ class ELFObjectWriter : public MCObjectWriter {
void recordRelocation(MCAssembler &Asm, const MCFragment *Fragment,
const MCFixup &Fixup, MCValue Target,
uint64_t &FixedValue) override;
bool usesRela(const MCSectionELF &Sec) const;
bool usesRela(const MCTargetOptions *TO, const MCSectionELF &Sec) const;

void executePostLayoutBinding(MCAssembler &Asm,
const MCAsmLayout &Layout) override;
Expand Down Expand Up @@ -810,7 +811,15 @@ MCSectionELF *ELFWriter::createRelocationSection(MCContext &Ctx,
Flags = ELF::SHF_GROUP;

const StringRef SectionName = Sec.getName();
const bool Rela = OWriter.usesRela(Sec);
const MCTargetOptions *TO = Ctx.getTargetOptions();
if (TO && TO->Crel) {
MCSectionELF *RelaSection =
Ctx.createELFRelSection(".crel" + SectionName, ELF::SHT_CREL, Flags,
/*EntrySize=*/1, Sec.getGroup(), &Sec);
return RelaSection;
}

const bool Rela = OWriter.usesRela(TO, Sec);
unsigned EntrySize;
if (Rela)
EntrySize = is64Bit() ? sizeof(ELF::Elf64_Rela) : sizeof(ELF::Elf32_Rela);
Expand Down Expand Up @@ -912,20 +921,61 @@ void ELFWriter::WriteSecHdrEntry(uint32_t Name, uint32_t Type, uint64_t Flags,
WriteWord(EntrySize); // sh_entsize
}

template <class uint>
static void encodeCrel(ArrayRef<ELFRelocationEntry> Relocs, raw_ostream &OS) {
uint OffsetMask = 8, Offset = 0, Addend = 0;
uint32_t SymIdx = 0, Type = 0;
// hdr & 4 indicates 3 flag bits in delta offset and flags members.
for (const ELFRelocationEntry &Entry : Relocs)
OffsetMask |= Entry.Offset;
const int Shift = llvm::countr_zero(OffsetMask);
encodeULEB128(Relocs.size() * 8 + ELF::CREL_HDR_ADDEND + Shift, OS);
for (const ELFRelocationEntry &Entry : Relocs) {
// The delta offset and flags member may be larger than uint64_t. Special
// case the first byte (3 flag bits and 4 offset bits). Other ULEB128 bytes
// encode the remaining delta offset bits.
auto DeltaOffset = static_cast<uint>((Entry.Offset - Offset) >> Shift);
Offset = Entry.Offset;
uint32_t CurSymIdx = Entry.Symbol ? Entry.Symbol->getIndex() : 0;
uint8_t B = (DeltaOffset << 3) + (SymIdx != CurSymIdx) +
(Type != Entry.Type ? 2 : 0) + (Addend != Entry.Addend ? 4 : 0);
if (DeltaOffset < 0x10) {
OS << char(B);
} else {
OS << char(B | 0x80);
encodeULEB128(DeltaOffset >> 4, OS);
}
// Delta symidx/type/addend members (SLEB128).
if (B & 1) {
encodeSLEB128(static_cast<int32_t>(CurSymIdx - SymIdx), OS);
SymIdx = CurSymIdx;
}
if (B & 2) {
encodeSLEB128(static_cast<int32_t>(Entry.Type - Type), OS);
Type = Entry.Type;
}
if (B & 4) {
encodeSLEB128(std::make_signed_t<uint>(Entry.Addend - Addend), OS);
Addend = Entry.Addend;
}
}
}

void ELFWriter::writeRelocations(const MCAssembler &Asm,
const MCSectionELF &Sec) {
std::vector<ELFRelocationEntry> &Relocs = OWriter.Relocations[&Sec];
const bool Rela = OWriter.usesRela(Sec);
const MCTargetOptions *TO = Asm.getContext().getTargetOptions();
const bool Rela = OWriter.usesRela(TO, Sec);

// Sort the relocation entries. MIPS needs this.
OWriter.TargetObjectWriter->sortRelocs(Asm, Relocs);

if (OWriter.TargetObjectWriter->getEMachine() == ELF::EM_MIPS) {
for (const ELFRelocationEntry &Entry : Relocs) {
uint32_t Symidx = Entry.Symbol ? Entry.Symbol->getIndex() : 0;
uint32_t SymIdx = Entry.Symbol ? Entry.Symbol->getIndex() : 0;
if (is64Bit()) {
write(Entry.Offset);
write(uint32_t(Symidx));
write(uint32_t(SymIdx));
write(OWriter.TargetObjectWriter->getRSsym(Entry.Type));
write(OWriter.TargetObjectWriter->getRType3(Entry.Type));
write(OWriter.TargetObjectWriter->getRType2(Entry.Type));
Expand All @@ -935,7 +985,7 @@ void ELFWriter::writeRelocations(const MCAssembler &Asm,
} else {
write(uint32_t(Entry.Offset));
ELF::Elf32_Rela ERE32;
ERE32.setSymbolAndType(Symidx, Entry.Type);
ERE32.setSymbolAndType(SymIdx, Entry.Type);
write(ERE32.r_info);
if (Rela)
write(uint32_t(Entry.Addend));
Expand All @@ -955,24 +1005,29 @@ void ELFWriter::writeRelocations(const MCAssembler &Asm,
}
}
}
return;
}
for (const ELFRelocationEntry &Entry : Relocs) {
uint32_t Symidx = Entry.Symbol ? Entry.Symbol->getIndex() : 0;
if (is64Bit()) {
write(Entry.Offset);
ELF::Elf64_Rela ERE;
ERE.setSymbolAndType(Symidx, Entry.Type);
write(ERE.r_info);
if (Rela)
write(Entry.Addend);
} else {
write(uint32_t(Entry.Offset));
ELF::Elf32_Rela ERE;
ERE.setSymbolAndType(Symidx, Entry.Type);
write(ERE.r_info);
if (Rela)
write(uint32_t(Entry.Addend));
} else if (TO && TO->Crel) {
if (is64Bit())
encodeCrel<uint64_t>(Relocs, W.OS);
else
encodeCrel<uint32_t>(Relocs, W.OS);
} else {
for (const ELFRelocationEntry &Entry : Relocs) {
uint32_t Symidx = Entry.Symbol ? Entry.Symbol->getIndex() : 0;
if (is64Bit()) {
write(Entry.Offset);
ELF::Elf64_Rela ERE;
ERE.setSymbolAndType(Symidx, Entry.Type);
write(ERE.r_info);
if (Rela)
write(Entry.Addend);
} else {
write(uint32_t(Entry.Offset));
ELF::Elf32_Rela ERE;
ERE.setSymbolAndType(Symidx, Entry.Type);
write(ERE.r_info);
if (Rela)
write(uint32_t(Entry.Addend));
}
}
}
}
Expand All @@ -992,7 +1047,8 @@ void ELFWriter::writeSection(const SectionIndexMapTy &SectionIndexMap,
llvm_unreachable("SHT_DYNAMIC in a relocatable object");

case ELF::SHT_REL:
case ELF::SHT_RELA: {
case ELF::SHT_RELA:
case ELF::SHT_CREL: {
sh_link = SymbolTableIndex;
assert(sh_link && ".symtab not found");
const MCSection *InfoSection = Section.getLinkedToSection();
Expand Down Expand Up @@ -1417,6 +1473,7 @@ void ELFObjectWriter::recordRelocation(MCAssembler &Asm,
uint64_t C = Target.getConstant();
uint64_t FixupOffset = Asm.getFragmentOffset(*Fragment) + Fixup.getOffset();
MCContext &Ctx = Asm.getContext();
const MCTargetOptions *TO = Ctx.getTargetOptions();

if (const MCSymbolRefExpr *RefB = Target.getSymB()) {
const auto &SymB = cast<MCSymbolELF>(RefB->getSymbol());
Expand Down Expand Up @@ -1472,7 +1529,7 @@ void ELFObjectWriter::recordRelocation(MCAssembler &Asm,
FixedValue = !RelocateWithSymbol && SymA && !SymA->isUndefined()
? C + Asm.getSymbolOffset(*SymA)
: C;
if (usesRela(FixupSection)) {
if (usesRela(TO, FixupSection)) {
Addend = FixedValue;
FixedValue = 0;
}
Expand Down Expand Up @@ -1501,9 +1558,11 @@ void ELFObjectWriter::recordRelocation(MCAssembler &Asm,
Relocations[&FixupSection].push_back(Rec);
}

bool ELFObjectWriter::usesRela(const MCSectionELF &Sec) const {
return hasRelocationAddend() &&
Sec.getType() != ELF::SHT_LLVM_CALL_GRAPH_PROFILE;
bool ELFObjectWriter::usesRela(const MCTargetOptions *TO,
const MCSectionELF &Sec) const {
return (hasRelocationAddend() &&
Sec.getType() != ELF::SHT_LLVM_CALL_GRAPH_PROFILE) ||
(TO && TO->Crel);
}

bool ELFObjectWriter::isSymbolRefDifferenceFullyResolvedImpl(
Expand Down
6 changes: 6 additions & 0 deletions llvm/lib/MC/MCTargetOptionsCommandFlags.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ MCOPT(bool, NoWarn)
MCOPT(bool, NoDeprecatedWarn)
MCOPT(bool, NoTypeCheck)
MCOPT(bool, SaveTempLabels)
MCOPT(bool, Crel)
MCOPT(bool, X86RelaxRelocations)
MCOPT(std::string, ABIName)
MCOPT(std::string, AsSecureLogFile)
Expand Down Expand Up @@ -128,6 +129,10 @@ llvm::mc::RegisterMCTargetOptionsFlags::RegisterMCTargetOptionsFlags() {
"save-temp-labels", cl::desc("Don't discard temporary labels"));
MCBINDOPT(SaveTempLabels);

static cl::opt<bool> Crel("crel",
cl::desc("Use CREL relocation format for ELF"));
MCBINDOPT(Crel);

static cl::opt<bool> X86RelaxRelocations(
"x86-relax-relocations",
cl::desc(
Expand Down Expand Up @@ -162,6 +167,7 @@ MCTargetOptions llvm::mc::InitMCTargetOptionsFromFlags() {
Options.MCNoDeprecatedWarn = getNoDeprecatedWarn();
Options.MCNoTypeCheck = getNoTypeCheck();
Options.MCSaveTempLabels = getSaveTempLabels();
Options.Crel = getCrel();
Options.X86RelaxRelocations = getX86RelaxRelocations();
Options.EmitDwarfUnwind = getEmitDwarfUnwind();
Options.EmitCompactUnwindNonCanonical = getEmitCompactUnwindNonCanonical();
Expand Down
Loading
Loading