include(AddLLD)
+option(LLD_ENABLE_WASM
+ "Enable support for WASM."
+ OFF)
+if (LLD_ENABLE_WASM)
+ add_definitions("-DLLD_ENABLE_WASM=1")
+endif()
+
+option(LLD_ENABLE_MACHO
+ "Enable support for MachO."
+ OFF)
+if (LLD_ENABLE_MACHO)
+ add_definitions("-DLLD_ENABLE_MACHO=1")
+endif()
+
+option(LLD_ENABLE_MINGW
+ "Enable support for MINGW."
+ OFF)
+if (LLD_ENABLE_MINGW)
+ add_definitions("-DLLD_ENABLE_MINGW=1")
+endif()
+
+option(LLD_ENABLE_COFF
+ "Enable support for COFF."
+ OFF)
+if (LLD_ENABLE_COFF)
+ add_definitions("-DLLD_ENABLE_COFF=1")
+endif()
+
option(LLD_USE_VTUNE
"Enable VTune user task tracking."
OFF)
add_subdirectory(docs)
add_subdirectory(COFF)
add_subdirectory(ELF)
-add_subdirectory(MachO)
+if (LLD_ENABLE_MACHO)
+ add_subdirectory(MachO)
+endif()
add_subdirectory(MinGW)
add_subdirectory(wasm)
//
//===----------------------------------------------------------------------===//
+#include "OutputSections.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
-#include "Thunks.h"
#include "lld/Common/ErrorHandler.h"
-#include "llvm/Object/ELF.h"
+#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Support/Endian.h"
using namespace llvm;
RelType getDynRel(RelType type) const override;
int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
+ void writeIgotPlt(uint8_t *buf, const Symbol &s) const override;
void writePltHeader(uint8_t *buf) const override;
void writePlt(uint8_t *buf, const Symbol &sym,
uint64_t pltEntryAddr) const override;
void relocate(uint8_t *loc, const Relocation &rel,
uint64_t val) const override;
RelExpr adjustTlsExpr(RelType type, RelExpr expr) const override;
- void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
- uint64_t val) const override;
- void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
- uint64_t val) const override;
- void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
- uint64_t val) const override;
+ void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const override;
+
+private:
+ void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
+ void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
+ void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
+};
+
+struct AArch64Relaxer {
+ bool safeToRelaxAdrpLdr = false;
+
+ AArch64Relaxer(ArrayRef<Relocation> relocs);
+ bool tryRelaxAdrpAdd(const Relocation &adrpRel, const Relocation &addRel,
+ uint64_t secAddr, uint8_t *buf) const;
+ bool tryRelaxAdrpLdr(const Relocation &adrpRel, const Relocation &ldrRel,
+ uint64_t secAddr, uint8_t *buf) const;
};
} // namespace
relativeRel = R_AARCH64_RELATIVE;
iRelativeRel = R_AARCH64_IRELATIVE;
gotRel = R_AARCH64_GLOB_DAT;
- noneRel = R_AARCH64_NONE;
pltRel = R_AARCH64_JUMP_SLOT;
symbolicRel = R_AARCH64_ABS64;
tlsDescRel = R_AARCH64_TLSDESC;
pltEntrySize = 16;
ipltEntrySize = 16;
defaultMaxPageSize = 65536;
- gotBaseSymInGotPlt = false;
// Align to the 2 MiB page size (known as a superpage or huge page).
// FreeBSD automatically promotes 2 MiB-aligned allocations.
switch (type) {
case R_AARCH64_TLSDESC:
return read64(buf + 8);
+ case R_AARCH64_NONE:
+ case R_AARCH64_GLOB_DAT:
+ case R_AARCH64_JUMP_SLOT:
+ return 0;
+ case R_AARCH64_PREL32:
+ return SignExtend64<32>(read32(buf));
+ case R_AARCH64_ABS64:
+ case R_AARCH64_PREL64:
+ case R_AARCH64_RELATIVE:
+ case R_AARCH64_IRELATIVE:
+ case R_AARCH64_TLS_TPREL64:
+ return read64(buf);
default:
internalLinkerError(getErrorLocation(buf),
"cannot read addend for relocation " + toString(type));
write64(buf, in.plt->getVA());
}
+void AArch64::writeIgotPlt(uint8_t *buf, const Symbol &s) const {
+ if (config->writeAddends)
+ write64(buf, s.getVA());
+}
+
void AArch64::writePltHeader(uint8_t *buf) const {
const uint8_t pltData[] = {
0xf0, 0x7b, 0xbf, 0xa9, // stp x16, x30, [sp,#-16]!
- 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[2]))
- 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[2]))]
- 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.plt.got[2]))
+ 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.got.plt[2]))
+ 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.got.plt[2]))]
+ 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.got.plt[2]))
0x20, 0x02, 0x1f, 0xd6, // br x17
0x1f, 0x20, 0x03, 0xd5, // nop
0x1f, 0x20, 0x03, 0xd5, // nop
void AArch64::writePlt(uint8_t *buf, const Symbol &sym,
uint64_t pltEntryAddr) const {
const uint8_t inst[] = {
- 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[n]))
- 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[n]))]
- 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.plt.got[n]))
+ 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.got.plt[n]))
+ 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.got.plt[n]))]
+ 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.got.plt[n]))
0x20, 0x02, 0x1f, 0xd6 // br x17
};
memcpy(buf, inst, sizeof(inst));
bool AArch64::needsThunk(RelExpr expr, RelType type, const InputFile *file,
uint64_t branchAddr, const Symbol &s,
int64_t a) const {
- // If s is an undefined weak symbol and does not have a PLT entry then it
- // will be resolved as a branch to the next instruction.
- if (s.isUndefWeak() && !s.isInPlt())
+ // If s is an undefined weak symbol and does not have a PLT entry then it will
+ // be resolved as a branch to the next instruction. If it is hidden, its
+ // binding has been converted to local, so we just check isUndefined() here. A
+ // undefined non-weak symbol will have been errored.
+ if (s.isUndefined() && !s.isInPlt())
return false;
// ELF for the ARM 64-bit architecture, section Call and Jump relocations
// only permits range extension thunks for R_AARCH64_CALL26 and
case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
case R_AARCH64_TLSDESC_ADR_PAGE21:
checkInt(loc, val, 33, rel);
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case R_AARCH64_ADR_PREL_PG_HI21_NC:
write32AArch64Addr(loc, val >> 12);
break;
// transformation by placing a R_AARCH64_JUMP26 relocation at the offset of
// the instruction we want to patch.
write32le(loc, 0x14000000);
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case R_AARCH64_CALL26:
checkInt(loc, val, 28, rel);
or32le(loc, (val & 0x0FFFFFFC) >> 2);
break;
case R_AARCH64_MOVW_UABS_G0:
checkUInt(loc, val, 16, rel);
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case R_AARCH64_MOVW_UABS_G0_NC:
or32le(loc, (val & 0xFFFF) << 5);
break;
case R_AARCH64_MOVW_UABS_G1:
checkUInt(loc, val, 32, rel);
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case R_AARCH64_MOVW_UABS_G1_NC:
or32le(loc, (val & 0xFFFF0000) >> 11);
break;
case R_AARCH64_MOVW_UABS_G2:
checkUInt(loc, val, 48, rel);
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case R_AARCH64_MOVW_UABS_G2_NC:
or32le(loc, (val & 0xFFFF00000000) >> 27);
break;
case R_AARCH64_MOVW_SABS_G0:
case R_AARCH64_TLSLE_MOVW_TPREL_G0:
checkInt(loc, val, 17, rel);
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case R_AARCH64_MOVW_PREL_G0_NC:
case R_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
writeSMovWImm(loc, val);
case R_AARCH64_MOVW_SABS_G1:
case R_AARCH64_TLSLE_MOVW_TPREL_G1:
checkInt(loc, val, 33, rel);
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case R_AARCH64_MOVW_PREL_G1_NC:
case R_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
writeSMovWImm(loc, val >> 16);
case R_AARCH64_MOVW_SABS_G2:
case R_AARCH64_TLSLE_MOVW_TPREL_G2:
checkInt(loc, val, 49, rel);
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case R_AARCH64_MOVW_PREL_G2_NC:
writeSMovWImm(loc, val >> 32);
break;
llvm_unreachable("invalid relocation for TLS IE to LE relaxation");
}
+AArch64Relaxer::AArch64Relaxer(ArrayRef<Relocation> relocs) {
+ if (!config->relax)
+ return;
+ // Check if R_AARCH64_ADR_GOT_PAGE and R_AARCH64_LD64_GOT_LO12_NC
+ // always appear in pairs.
+ size_t i = 0;
+ const size_t size = relocs.size();
+ for (; i != size; ++i) {
+ if (relocs[i].type == R_AARCH64_ADR_GOT_PAGE) {
+ if (i + 1 < size && relocs[i + 1].type == R_AARCH64_LD64_GOT_LO12_NC) {
+ ++i;
+ continue;
+ }
+ break;
+ } else if (relocs[i].type == R_AARCH64_LD64_GOT_LO12_NC) {
+ break;
+ }
+ }
+ safeToRelaxAdrpLdr = i == size;
+}
+
+bool AArch64Relaxer::tryRelaxAdrpAdd(const Relocation &adrpRel,
+ const Relocation &addRel, uint64_t secAddr,
+ uint8_t *buf) const {
+ // When the address of sym is within the range of ADR then
+ // we may relax
+ // ADRP xn, sym
+ // ADD xn, xn, :lo12: sym
+ // to
+ // NOP
+ // ADR xn, sym
+ if (!config->relax || adrpRel.type != R_AARCH64_ADR_PREL_PG_HI21 ||
+ addRel.type != R_AARCH64_ADD_ABS_LO12_NC)
+ return false;
+ // Check if the relocations apply to consecutive instructions.
+ if (adrpRel.offset + 4 != addRel.offset)
+ return false;
+ if (adrpRel.sym != addRel.sym)
+ return false;
+ if (adrpRel.addend != 0 || addRel.addend != 0)
+ return false;
+
+ uint32_t adrpInstr = read32le(buf + adrpRel.offset);
+ uint32_t addInstr = read32le(buf + addRel.offset);
+ // Check if the first instruction is ADRP and the second instruction is ADD.
+ if ((adrpInstr & 0x9f000000) != 0x90000000 ||
+ (addInstr & 0xffc00000) != 0x91000000)
+ return false;
+ uint32_t adrpDestReg = adrpInstr & 0x1f;
+ uint32_t addDestReg = addInstr & 0x1f;
+ uint32_t addSrcReg = (addInstr >> 5) & 0x1f;
+ if (adrpDestReg != addDestReg || adrpDestReg != addSrcReg)
+ return false;
+
+ Symbol &sym = *adrpRel.sym;
+ // Check if the address difference is within 1MiB range.
+ int64_t val = sym.getVA() - (secAddr + addRel.offset);
+ if (val < -1024 * 1024 || val >= 1024 * 1024)
+ return false;
+
+ Relocation adrRel = {R_ABS, R_AARCH64_ADR_PREL_LO21, addRel.offset,
+ /*addend=*/0, &sym};
+ // nop
+ write32le(buf + adrpRel.offset, 0xd503201f);
+ // adr x_<dest_reg>
+ write32le(buf + adrRel.offset, 0x10000000 | adrpDestReg);
+ target->relocate(buf + adrRel.offset, adrRel, val);
+ return true;
+}
+
+bool AArch64Relaxer::tryRelaxAdrpLdr(const Relocation &adrpRel,
+ const Relocation &ldrRel, uint64_t secAddr,
+ uint8_t *buf) const {
+ if (!safeToRelaxAdrpLdr)
+ return false;
+
+ // When the definition of sym is not preemptible then we may
+ // be able to relax
+ // ADRP xn, :got: sym
+ // LDR xn, [ xn :got_lo12: sym]
+ // to
+ // ADRP xn, sym
+ // ADD xn, xn, :lo_12: sym
+
+ if (adrpRel.type != R_AARCH64_ADR_GOT_PAGE ||
+ ldrRel.type != R_AARCH64_LD64_GOT_LO12_NC)
+ return false;
+ // Check if the relocations apply to consecutive instructions.
+ if (adrpRel.offset + 4 != ldrRel.offset)
+ return false;
+ // Check if the relocations reference the same symbol and
+ // skip undefined, preemptible and STT_GNU_IFUNC symbols.
+ if (!adrpRel.sym || adrpRel.sym != ldrRel.sym || !adrpRel.sym->isDefined() ||
+ adrpRel.sym->isPreemptible || adrpRel.sym->isGnuIFunc())
+ return false;
+ // Check if the addends of the both relocations are zero.
+ if (adrpRel.addend != 0 || ldrRel.addend != 0)
+ return false;
+ uint32_t adrpInstr = read32le(buf + adrpRel.offset);
+ uint32_t ldrInstr = read32le(buf + ldrRel.offset);
+ // Check if the first instruction is ADRP and the second instruction is LDR.
+ if ((adrpInstr & 0x9f000000) != 0x90000000 ||
+ (ldrInstr & 0x3b000000) != 0x39000000)
+ return false;
+ // Check the value of the sf bit.
+ if (!(ldrInstr >> 31))
+ return false;
+ uint32_t adrpDestReg = adrpInstr & 0x1f;
+ uint32_t ldrDestReg = ldrInstr & 0x1f;
+ uint32_t ldrSrcReg = (ldrInstr >> 5) & 0x1f;
+ // Check if ADPR and LDR use the same register.
+ if (adrpDestReg != ldrDestReg || adrpDestReg != ldrSrcReg)
+ return false;
+
+ Symbol &sym = *adrpRel.sym;
+ // GOT references to absolute symbols can't be relaxed to use ADRP/ADD in
+ // position-independent code because these instructions produce a relative
+ // address.
+ if (config->isPic && !cast<Defined>(sym).section)
+ return false;
+ // Check if the address difference is within 4GB range.
+ int64_t val =
+ getAArch64Page(sym.getVA()) - getAArch64Page(secAddr + adrpRel.offset);
+ if (val != llvm::SignExtend64(val, 33))
+ return false;
+
+ Relocation adrpSymRel = {R_AARCH64_PAGE_PC, R_AARCH64_ADR_PREL_PG_HI21,
+ adrpRel.offset, /*addend=*/0, &sym};
+ Relocation addRel = {R_ABS, R_AARCH64_ADD_ABS_LO12_NC, ldrRel.offset,
+ /*addend=*/0, &sym};
+
+ // adrp x_<dest_reg>
+ write32le(buf + adrpSymRel.offset, 0x90000000 | adrpDestReg);
+ // add x_<dest reg>, x_<dest reg>
+ write32le(buf + addRel.offset, 0x91000000 | adrpDestReg | (adrpDestReg << 5));
+
+ target->relocate(buf + adrpSymRel.offset, adrpSymRel,
+ SignExtend64(getAArch64Page(sym.getVA()) -
+ getAArch64Page(secAddr + adrpSymRel.offset),
+ 64));
+ target->relocate(buf + addRel.offset, addRel, SignExtend64(sym.getVA(), 64));
+ tryRelaxAdrpAdd(adrpSymRel, addRel, secAddr, buf);
+ return true;
+}
+
+void AArch64::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const {
+ uint64_t secAddr = sec.getOutputSection()->addr;
+ if (auto *s = dyn_cast<InputSection>(&sec))
+ secAddr += s->outSecOff;
+ AArch64Relaxer relaxer(sec.relocs());
+ for (size_t i = 0, size = sec.relocs().size(); i != size; ++i) {
+ const Relocation &rel = sec.relocs()[i];
+ uint8_t *loc = buf + rel.offset;
+ const uint64_t val =
+ sec.getRelocTargetVA(sec.file, rel.type, rel.addend,
+ secAddr + rel.offset, *rel.sym, rel.expr);
+ switch (rel.expr) {
+ case R_AARCH64_GOT_PAGE_PC:
+ if (i + 1 < size &&
+ relaxer.tryRelaxAdrpLdr(rel, sec.relocs()[i + 1], secAddr, buf)) {
+ ++i;
+ continue;
+ }
+ break;
+ case R_AARCH64_PAGE_PC:
+ if (i + 1 < size &&
+ relaxer.tryRelaxAdrpAdd(rel, sec.relocs()[i + 1], secAddr, buf)) {
+ ++i;
+ continue;
+ }
+ break;
+ case R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC:
+ case R_RELAX_TLS_GD_TO_IE_ABS:
+ relaxTlsGdToIe(loc, rel, val);
+ continue;
+ case R_RELAX_TLS_GD_TO_LE:
+ relaxTlsGdToLe(loc, rel, val);
+ continue;
+ case R_RELAX_TLS_IE_TO_LE:
+ relaxTlsIeToLe(loc, rel, val);
+ continue;
+ default:
+ break;
+ }
+ relocate(loc, rel, val);
+ }
+}
+
// AArch64 may use security features in variant PLT sequences. These are:
// Pointer Authentication (PAC), introduced in armv8.3-a and Branch Target
// Indicator (BTI) introduced in armv8.5-a. The additional instructions used
uint64_t pltEntryAddr) const override;
private:
- bool btiHeader; // bti instruction needed in PLT Header
- bool btiEntry; // bti instruction needed in PLT Entry
+ bool btiHeader; // bti instruction needed in PLT Header and Entry
bool pacEntry; // autia1716 instruction needed in PLT Entry
};
} // namespace
// address of the PLT entry can be taken by the program, which permits an
// indirect jump to the PLT entry. This can happen when the address
// of the PLT entry for a function is canonicalised due to the address of
- // the function in an executable being taken by a shared library.
- // FIXME: There is a potential optimization to omit the BTI if we detect
- // that the address of the PLT entry isn't taken.
+ // the function in an executable being taken by a shared library, or
+ // non-preemptible ifunc referenced by non-GOT-generating, non-PLT-generating
+ // relocations.
// The PAC PLT entries require dynamic loader support and this isn't known
// from properties in the objects, so we use the command line flag.
- btiEntry = btiHeader && !config->shared;
pacEntry = config->zPacPlt;
- if (btiEntry || pacEntry) {
+ if (btiHeader || pacEntry) {
pltEntrySize = 24;
ipltEntrySize = 24;
}
const uint8_t btiData[] = { 0x5f, 0x24, 0x03, 0xd5 }; // bti c
const uint8_t pltData[] = {
0xf0, 0x7b, 0xbf, 0xa9, // stp x16, x30, [sp,#-16]!
- 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[2]))
- 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[2]))]
- 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.plt.got[2]))
+ 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.got.plt[2]))
+ 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.got.plt[2]))]
+ 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.got.plt[2]))
0x20, 0x02, 0x1f, 0xd6, // br x17
0x1f, 0x20, 0x03, 0xd5, // nop
0x1f, 0x20, 0x03, 0xd5 // nop
// [btiData] addrInst (pacBr | stdBr) [nopData]
const uint8_t btiData[] = { 0x5f, 0x24, 0x03, 0xd5 }; // bti c
const uint8_t addrInst[] = {
- 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[n]))
- 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[n]))]
- 0x10, 0x02, 0x00, 0x91 // add x16, x16, Offset(&(.plt.got[n]))
+ 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.got.plt[n]))
+ 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.got.plt[n]))]
+ 0x10, 0x02, 0x00, 0x91 // add x16, x16, Offset(&(.got.plt[n]))
};
const uint8_t pacBr[] = {
0x9f, 0x21, 0x03, 0xd5, // autia1716
};
const uint8_t nopData[] = { 0x1f, 0x20, 0x03, 0xd5 }; // nop
- if (btiEntry) {
+ // NEEDS_COPY indicates a non-ifunc canonical PLT entry whose address may
+ // escape to shared objects. isInIplt indicates a non-preemptible ifunc. Its
+ // address may escape if referenced by a direct relocation. The condition is
+ // conservative.
+ bool hasBti = btiHeader && (sym.hasFlag(NEEDS_COPY) || sym.isInIplt);
+ if (hasBti) {
memcpy(buf, btiData, sizeof(btiData));
buf += sizeof(btiData);
pltEntryAddr += sizeof(btiData);
memcpy(buf + sizeof(addrInst), pacBr, sizeof(pacBr));
else
memcpy(buf + sizeof(addrInst), stdBr, sizeof(stdBr));
- if (!btiEntry)
+ if (!hasBti)
// We didn't add the BTI c instruction so round out size with NOP.
memcpy(buf + sizeof(addrInst) + sizeof(stdBr), nopData, sizeof(nopData));
}
static AArch64BtiPac t;
return &t;
#else
- if (config->andFeatures & (GNU_PROPERTY_AARCH64_FEATURE_1_BTI |
- GNU_PROPERTY_AARCH64_FEATURE_1_PAC)) {
+ if ((config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_BTI) ||
+ config->zPacPlt) {
static AArch64BtiPac t;
return &t;
}
using namespace lld;
using namespace lld::elf;
+// Undefine the macro predefined by GCC powerpc32.
+#undef PPC
+
namespace {
class PPC final : public TargetInfo {
public:
RelExpr getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const override;
RelType getDynRel(RelType type) const override;
+ int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
void writeGotHeader(uint8_t *buf) const override;
void writePltHeader(uint8_t *buf) const override {
llvm_unreachable("should call writePPC32GlinkSection() instead");
uint64_t val) const override;
RelExpr adjustTlsExpr(RelType type, RelExpr expr) const override;
int getTlsGdRelaxSkip(RelType type) const override;
- void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
- uint64_t val) const override;
- void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
- uint64_t val) const override;
- void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
- uint64_t val) const override;
- void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
- uint64_t val) const override;
+ void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const override;
+
+private:
+ void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
+ void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
+ void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
+ void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
};
} // namespace
// non-GOT-non-PLT relocations referencing external functions for -fpie/-fPIE.
uint32_t glink = in.plt->getVA(); // VA of .glink
if (!config->isPic) {
- for (const Symbol *sym : cast<PPC32GlinkSection>(in.plt)->canonical_plts) {
+ for (const Symbol *sym : cast<PPC32GlinkSection>(*in.plt).canonical_plts) {
writePPC32PltCallStub(buf, sym->getGotPltVA(), nullptr, 0);
buf += 16;
glink += 16;
PPC::PPC() {
copyRel = R_PPC_COPY;
gotRel = R_PPC_GLOB_DAT;
- noneRel = R_PPC_NONE;
pltRel = R_PPC_JMP_SLOT;
relativeRel = R_PPC_RELATIVE;
iRelativeRel = R_PPC_IRELATIVE;
symbolicRel = R_PPC_ADDR32;
- gotBaseSymInGotPlt = false;
gotHeaderEntriesNum = 3;
gotPltHeaderEntriesNum = 0;
pltHeaderSize = 0;
void PPC::writeGotPlt(uint8_t *buf, const Symbol &s) const {
// Address of the symbol resolver stub in .glink .
- write32(buf, in.plt->getVA() + in.plt->headerSize + 4 * s.pltIndex);
+ write32(buf, in.plt->getVA() + in.plt->headerSize + 4 * s.getPltIdx());
}
bool PPC::needsThunk(RelExpr expr, RelType type, const InputFile *file,
return R_PPC_NONE;
}
+int64_t PPC::getImplicitAddend(const uint8_t *buf, RelType type) const {
+ switch (type) {
+ case R_PPC_NONE:
+ return 0;
+ case R_PPC_ADDR32:
+ case R_PPC_REL32:
+ return SignExtend64<32>(read32(buf));
+ default:
+ internalLinkerError(getErrorLocation(buf),
+ "cannot read addend for relocation " + toString(type));
+ return 0;
+ }
+}
+
static std::pair<RelType, uint64_t> fromDTPREL(RelType type, uint64_t val) {
uint64_t dtpBiasedVal = val - 0x8000;
switch (type) {
}
}
+void PPC::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const {
+ uint64_t secAddr = sec.getOutputSection()->addr;
+ if (auto *s = dyn_cast<InputSection>(&sec))
+ secAddr += s->outSecOff;
+ for (const Relocation &rel : sec.relocs()) {
+ uint8_t *loc = buf + rel.offset;
+ const uint64_t val = SignExtend64(
+ sec.getRelocTargetVA(sec.file, rel.type, rel.addend,
+ secAddr + rel.offset, *rel.sym, rel.expr),
+ 32);
+ switch (rel.expr) {
+ case R_RELAX_TLS_GD_TO_IE_GOT_OFF:
+ relaxTlsGdToIe(loc, rel, val);
+ break;
+ case R_RELAX_TLS_GD_TO_LE:
+ relaxTlsGdToLe(loc, rel, val);
+ break;
+ case R_RELAX_TLS_LD_TO_LE_ABS:
+ relaxTlsLdToLe(loc, rel, val);
+ break;
+ case R_RELAX_TLS_IE_TO_LE:
+ relaxTlsIeToLe(loc, rel, val);
+ break;
+ default:
+ relocate(loc, rel, val);
+ break;
+ }
+ }
+}
+
TargetInfo *elf::getPPCTargetInfo() {
static PPC target;
return ⌖
//
//===----------------------------------------------------------------------===//
+#include "InputFiles.h"
+#include "OutputSections.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
#include "Thunks.h"
-#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Memory.h"
+#include "lld/Common/CommonLinkerContext.h"
#include "llvm/Support/Endian.h"
using namespace llvm;
0x8000000003e00000, // S/T (6-10) - The [S/T]X bit moves from 28 to 5.
};
+namespace {
+class PPC64 final : public TargetInfo {
+public:
+ PPC64();
+ int getTlsGdRelaxSkip(RelType type) const override;
+ uint32_t calcEFlags() const override;
+ RelExpr getRelExpr(RelType type, const Symbol &s,
+ const uint8_t *loc) const override;
+ RelType getDynRel(RelType type) const override;
+ int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
+ void writePltHeader(uint8_t *buf) const override;
+ void writePlt(uint8_t *buf, const Symbol &sym,
+ uint64_t pltEntryAddr) const override;
+ void writeIplt(uint8_t *buf, const Symbol &sym,
+ uint64_t pltEntryAddr) const override;
+ void relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void writeGotHeader(uint8_t *buf) const override;
+ bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
+ uint64_t branchAddr, const Symbol &s,
+ int64_t a) const override;
+ uint32_t getThunkSectionSpacing() const override;
+ bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override;
+ RelExpr adjustTlsExpr(RelType type, RelExpr expr) const override;
+ RelExpr adjustGotPcExpr(RelType type, int64_t addend,
+ const uint8_t *loc) const override;
+ void relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) const;
+ void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const override;
+
+ bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
+ uint8_t stOther) const override;
+
+private:
+ void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
+ void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
+ void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
+ void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
+};
+} // namespace
+
uint64_t elf::getPPC64TocBase() {
// The TOC consists of sections .got, .toc, .tocbss, .plt in that order. The
// TOC starts where the first of these sections starts. We always create a
return 0;
}
-bool elf::isPPC64SmallCodeModelTocReloc(RelType type) {
- // The only small code model relocations that access the .toc section.
- return type == R_PPC64_TOC16 || type == R_PPC64_TOC16_DS;
-}
-
void elf::writePrefixedInstruction(uint8_t *loc, uint64_t insn) {
insn = config->isLE ? insn << 32 | insn >> 32 : insn;
write64(loc, insn);
static bool addOptional(StringRef name, uint64_t value,
std::vector<Defined *> &defined) {
- Symbol *sym = symtab->find(name);
+ Symbol *sym = symtab.find(name);
if (!sym || sym->isDefined())
return false;
- sym->resolve(Defined{/*file=*/nullptr, saver.save(name), STB_GLOBAL,
- STV_HIDDEN, STT_FUNC, value,
+ sym->resolve(Defined{/*file=*/nullptr, StringRef(), STB_GLOBAL, STV_HIDDEN,
+ STT_FUNC, value,
/*size=*/0, /*section=*/nullptr});
defined.push_back(cast<Defined>(sym));
return true;
// instructions and write [first,end).
auto *sec = make<InputSection>(
nullptr, SHF_ALLOC, SHT_PROGBITS, 4,
- makeArrayRef(reinterpret_cast<uint8_t *>(buf.data() + first),
- 4 * (buf.size() - first)),
+ ArrayRef(reinterpret_cast<uint8_t *>(buf.data() + first),
+ 4 * (buf.size() - first)),
".text");
- inputSections.push_back(sec);
+ ctx.inputSections.push_back(sec);
for (Defined *sym : defined) {
sym->section = sec;
sym->value -= 4 * first;
template <typename ELFT>
static std::pair<Defined *, int64_t>
getRelaTocSymAndAddend(InputSectionBase *tocSec, uint64_t offset) {
- if (tocSec->numRelocations == 0)
- return {};
-
// .rela.toc contains exclusively R_PPC64_ADDR64 relocations sorted by
// r_offset: 0, 8, 16, etc. For a given Offset, Offset / 8 gives us the
// relocation index in most cases.
// points to a relocation with larger r_offset. Do a linear probe then.
// Constants are extremely uncommon in .toc and the extra number of array
// accesses can be seen as a small constant.
- ArrayRef<typename ELFT::Rela> relas = tocSec->template relas<ELFT>();
+ ArrayRef<typename ELFT::Rela> relas =
+ tocSec->template relsOrRelas<ELFT>().relas;
+ if (relas.empty())
+ return {};
uint64_t index = std::min<uint64_t>(offset / 8, relas.size() - 1);
for (;;) {
if (relas[index].r_offset == offset) {
// ld/lwa 3, 0(3) # load the value from the address
//
// Returns true if the relaxation is performed.
-bool elf::tryRelaxPPC64TocIndirection(const Relocation &rel, uint8_t *bufLoc) {
+static bool tryRelaxPPC64TocIndirection(const Relocation &rel,
+ uint8_t *bufLoc) {
assert(config->tocOptimize);
if (rel.addend < 0)
return false;
return false;
// Add PPC64TocOffset that will be subtracted by PPC64::relocate().
- target->relaxGot(bufLoc, rel, tocRelative + ppc64TocOffset);
+ static_cast<const PPC64 &>(*target).relaxGot(bufLoc, rel,
+ tocRelative + ppc64TocOffset);
return true;
}
-namespace {
-class PPC64 final : public TargetInfo {
-public:
- PPC64();
- int getTlsGdRelaxSkip(RelType type) const override;
- uint32_t calcEFlags() const override;
- RelExpr getRelExpr(RelType type, const Symbol &s,
- const uint8_t *loc) const override;
- RelType getDynRel(RelType type) const override;
- void writePltHeader(uint8_t *buf) const override;
- void writePlt(uint8_t *buf, const Symbol &sym,
- uint64_t pltEntryAddr) const override;
- void writeIplt(uint8_t *buf, const Symbol &sym,
- uint64_t pltEntryAddr) const override;
- void relocate(uint8_t *loc, const Relocation &rel,
- uint64_t val) const override;
- void writeGotHeader(uint8_t *buf) const override;
- bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
- uint64_t branchAddr, const Symbol &s,
- int64_t a) const override;
- uint32_t getThunkSectionSpacing() const override;
- bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override;
- RelExpr adjustTlsExpr(RelType type, RelExpr expr) const override;
- RelExpr adjustGotPcExpr(RelType type, int64_t addend,
- const uint8_t *loc) const override;
- void relaxGot(uint8_t *loc, const Relocation &rel,
- uint64_t val) const override;
- void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
- uint64_t val) const override;
- void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
- uint64_t val) const override;
- void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
- uint64_t val) const override;
- void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
- uint64_t val) const override;
-
- bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
- uint8_t stOther) const override;
-};
-} // namespace
-
// Relocation masks following the #lo(value), #hi(value), #ha(value),
// #higher(value), #highera(value), #highest(value), and #highesta(value)
// macros defined in section 4.5.1. Relocation Types of the PPC-elf64abi
PPC64::PPC64() {
copyRel = R_PPC64_COPY;
gotRel = R_PPC64_GLOB_DAT;
- noneRel = R_PPC64_NONE;
pltRel = R_PPC64_JMP_SLOT;
relativeRel = R_PPC64_RELATIVE;
iRelativeRel = R_PPC64_IRELATIVE;
#endif
pltEntrySize = 4;
ipltEntrySize = 16; // PPC64PltCallStub::size
- gotBaseSymInGotPlt = false;
gotHeaderEntriesNum = 1;
gotPltHeaderEntriesNum = 2;
needsThunks = true;
}
static uint32_t getEFlags(InputFile *file) {
- if (config->ekind == ELF64BEKind)
+ if (file->ekind == ELF64BEKind)
return cast<ObjFile<ELF64BE>>(file)->getObj().getHeader().e_flags;
return cast<ObjFile<ELF64LE>>(file)->getObj().getHeader().e_flags;
}
// This file implements v2 ABI. This function makes sure that all
// object files have v2 or an unspecified version as an ABI version.
uint32_t PPC64::calcEFlags() const {
- for (InputFile *f : objectFiles) {
+ for (InputFile *f : ctx.objectFiles) {
uint32_t flag = getEFlags(f);
if (flag == 1)
error(toString(f) + ": ABI version 1 is not supported");
return R_PPC64_NONE;
}
+int64_t PPC64::getImplicitAddend(const uint8_t *buf, RelType type) const {
+ switch (type) {
+ case R_PPC64_NONE:
+ case R_PPC64_GLOB_DAT:
+ case R_PPC64_JMP_SLOT:
+ return 0;
+ case R_PPC64_REL32:
+ return SignExtend64<32>(read32(buf));
+ case R_PPC64_ADDR64:
+ case R_PPC64_REL64:
+ case R_PPC64_RELATIVE:
+ case R_PPC64_IRELATIVE:
+ case R_PPC64_DTPMOD64:
+ case R_PPC64_DTPREL64:
+ case R_PPC64_TPREL64:
+ return read64(buf);
+ default:
+ internalLinkerError(getErrorLocation(buf),
+ "cannot read addend for relocation " + toString(type));
+ return 0;
+ }
+}
+
void PPC64::writeGotHeader(uint8_t *buf) const {
write64(buf, getPPC64TocBase());
}
void PPC64::writePlt(uint8_t *buf, const Symbol &sym,
uint64_t /*pltEntryAddr*/) const {
- int32_t offset = pltHeaderSize + sym.pltIndex * pltEntrySize;
+ int32_t offset = pltHeaderSize + sym.getPltIdx() * pltEntrySize;
// bl __glink_PLTresolve
write32(buf, 0x48000000 | ((-offset) & 0x03FFFFFc));
}
// of the TLS block. Therefore, in the case of R_PPC64_DTPREL34 we first
// need to subtract that value then fallthrough to the general case.
val -= dynamicThreadPointerOffset;
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case R_PPC64_PCREL34:
case R_PPC64_GOT_PCREL34:
case R_PPC64_GOT_TLSGD_PCREL34:
if (type == R_PPC64_REL24_NOTOC && (s.stOther >> 5) > 1)
return true;
- // If a symbol is a weak undefined and we are compiling an executable
- // it doesn't need a range-extending thunk since it can't be called.
- if (s.isUndefWeak() && !config->shared)
+ // An undefined weak symbol not in a PLT does not need a thunk. If it is
+ // hidden, its binding has been converted to local, so we just check
+ // isUndefined() here. A undefined non-weak symbol has been errored.
+ if (s.isUndefined())
return false;
// If the offset exceeds the range of the branch type then it will need
}
}
+void PPC64::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const {
+ uint64_t secAddr = sec.getOutputSection()->addr;
+ if (auto *s = dyn_cast<InputSection>(&sec))
+ secAddr += s->outSecOff;
+ uint64_t lastPPCRelaxedRelocOff = -1;
+ for (const Relocation &rel : sec.relocs()) {
+ uint8_t *loc = buf + rel.offset;
+ const uint64_t val =
+ sec.getRelocTargetVA(sec.file, rel.type, rel.addend,
+ secAddr + rel.offset, *rel.sym, rel.expr);
+ switch (rel.expr) {
+ case R_PPC64_RELAX_GOT_PC: {
+ // The R_PPC64_PCREL_OPT relocation must appear immediately after
+ // R_PPC64_GOT_PCREL34 in the relocations table at the same offset.
+ // We can only relax R_PPC64_PCREL_OPT if we have also relaxed
+ // the associated R_PPC64_GOT_PCREL34 since only the latter has an
+ // associated symbol. So save the offset when relaxing R_PPC64_GOT_PCREL34
+ // and only relax the other if the saved offset matches.
+ if (rel.type == R_PPC64_GOT_PCREL34)
+ lastPPCRelaxedRelocOff = rel.offset;
+ if (rel.type == R_PPC64_PCREL_OPT && rel.offset != lastPPCRelaxedRelocOff)
+ break;
+ relaxGot(loc, rel, val);
+ break;
+ }
+ case R_PPC64_RELAX_TOC:
+ // rel.sym refers to the STT_SECTION symbol associated to the .toc input
+ // section. If an R_PPC64_TOC16_LO (.toc + addend) references the TOC
+ // entry, there may be R_PPC64_TOC16_HA not paired with
+ // R_PPC64_TOC16_LO_DS. Don't relax. This loses some relaxation
+ // opportunities but is safe.
+ if (ppc64noTocRelax.count({rel.sym, rel.addend}) ||
+ !tryRelaxPPC64TocIndirection(rel, loc))
+ relocate(loc, rel, val);
+ break;
+ case R_PPC64_CALL:
+ // If this is a call to __tls_get_addr, it may be part of a TLS
+ // sequence that has been relaxed and turned into a nop. In this
+ // case, we don't want to handle it as a call.
+ if (read32(loc) == 0x60000000) // nop
+ break;
+
+ // Patch a nop (0x60000000) to a ld.
+ if (rel.sym->needsTocRestore) {
+ // gcc/gfortran 5.4, 6.3 and earlier versions do not add nop for
+ // recursive calls even if the function is preemptible. This is not
+ // wrong in the common case where the function is not preempted at
+ // runtime. Just ignore.
+ if ((rel.offset + 8 > sec.content().size() ||
+ read32(loc + 4) != 0x60000000) &&
+ rel.sym->file != sec.file) {
+ // Use substr(6) to remove the "__plt_" prefix.
+ errorOrWarn(getErrorLocation(loc) + "call to " +
+ lld::toString(*rel.sym).substr(6) +
+ " lacks nop, can't restore toc");
+ break;
+ }
+ write32(loc + 4, 0xe8410018); // ld %r2, 24(%r1)
+ }
+ relocate(loc, rel, val);
+ break;
+ case R_RELAX_TLS_GD_TO_IE:
+ case R_RELAX_TLS_GD_TO_IE_GOT_OFF:
+ relaxTlsGdToIe(loc, rel, val);
+ break;
+ case R_RELAX_TLS_GD_TO_LE:
+ relaxTlsGdToLe(loc, rel, val);
+ break;
+ case R_RELAX_TLS_LD_TO_LE_ABS:
+ relaxTlsLdToLe(loc, rel, val);
+ break;
+ case R_RELAX_TLS_IE_TO_LE:
+ relaxTlsIeToLe(loc, rel, val);
+ break;
+ default:
+ relocate(loc, rel, val);
+ break;
+ }
+ }
+}
+
// The prologue for a split-stack function is expected to look roughly
// like this:
// .Lglobal_entry_point:
//===----------------------------------------------------------------------===//
#include "InputFiles.h"
+#include "OutputSections.h"
#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;
using namespace llvm::object;
const uint8_t *loc) const override;
void relocate(uint8_t *loc, const Relocation &rel,
uint64_t val) const override;
+ bool relaxOnce(int pass) const override;
};
} // end anonymous namespace
enum Reg {
X_RA = 1,
+ X_TP = 4,
X_T0 = 5,
X_T1 = 6,
X_T2 = 7,
return op | (rd << 7) | (imm << 12);
}
+// Extract bits v[begin:end], where range is inclusive, and begin must be < 63.
+static uint32_t extractBits(uint64_t v, uint32_t begin, uint32_t end) {
+ return (v & ((1ULL << (begin + 1)) - 1)) >> end;
+}
+
+static uint32_t setLO12_I(uint32_t insn, uint32_t imm) {
+ return (insn & 0xfffff) | (imm << 20);
+}
+static uint32_t setLO12_S(uint32_t insn, uint32_t imm) {
+ return (insn & 0x1fff07f) | (extractBits(imm, 11, 5) << 25) |
+ (extractBits(imm, 4, 0) << 7);
+}
+
RISCV::RISCV() {
copyRel = R_RISCV_COPY;
- noneRel = R_RISCV_NONE;
pltRel = R_RISCV_JUMP_SLOT;
relativeRel = R_RISCV_RELATIVE;
iRelativeRel = R_RISCV_IRELATIVE;
gotRel = symbolicRel;
// .got[0] = _DYNAMIC
- gotBaseSymInGotPlt = false;
gotHeaderEntriesNum = 1;
// .got.plt[0] = _dl_runtime_resolve, .got.plt[1] = link_map
uint32_t RISCV::calcEFlags() const {
// If there are only binary input files (from -b binary), use a
// value of 0 for the ELF header flags.
- if (objectFiles.empty())
+ if (ctx.objectFiles.empty())
return 0;
- uint32_t target = getEFlags(objectFiles.front());
+ uint32_t target = getEFlags(ctx.objectFiles.front());
- for (InputFile *f : objectFiles) {
+ for (InputFile *f : ctx.objectFiles) {
uint32_t eflags = getEFlags(f);
if (eflags & EF_RISCV_RVC)
target |= EF_RISCV_RVC;
if ((eflags & EF_RISCV_FLOAT_ABI) != (target & EF_RISCV_FLOAT_ABI))
- warn(toString(f) +
- ": linking object files with different floating-point ABI");
+ error(
+ toString(f) +
+ ": cannot link object files with different floating-point ABI from " +
+ toString(ctx.objectFiles[0]));
if ((eflags & EF_RISCV_RVE) != (target & EF_RISCV_RVE))
error(toString(f) +
case R_RISCV_32:
case R_RISCV_TLS_DTPMOD32:
case R_RISCV_TLS_DTPREL32:
+ case R_RISCV_TLS_TPREL32:
return SignExtend64<32>(read32le(buf));
case R_RISCV_64:
+ case R_RISCV_TLS_DTPMOD64:
+ case R_RISCV_TLS_DTPREL64:
+ case R_RISCV_TLS_TPREL64:
return read64le(buf);
case R_RISCV_RELATIVE:
case R_RISCV_IRELATIVE:
case R_RISCV_TLS_GD_HI20:
return R_TLSGD_PC;
case R_RISCV_TLS_GOT_HI20:
- config->hasStaticTlsModel = true;
return R_GOT_PC;
case R_RISCV_TPREL_HI20:
case R_RISCV_TPREL_LO12_I:
case R_RISCV_TPREL_LO12_S:
return R_TPREL;
- case R_RISCV_RELAX:
- case R_RISCV_TPREL_ADD:
- return R_NONE;
case R_RISCV_ALIGN:
- // Not just a hint; always padded to the worst-case number of NOPs, so may
- // not currently be aligned, and without linker relaxation support we can't
- // delete NOPs to realign.
- errorOrWarn(getErrorLocation(loc) + "relocation R_RISCV_ALIGN requires "
- "unimplemented linker relaxation; recompile with -mno-relax");
- return R_NONE;
+ return R_RELAX_HINT;
+ case R_RISCV_TPREL_ADD:
+ case R_RISCV_RELAX:
+ return config->relax ? R_RELAX_HINT : R_NONE;
default:
error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
") against symbol " + toString(s));
}
}
-// Extract bits V[Begin:End], where range is inclusive, and Begin must be < 63.
-static uint32_t extractBits(uint64_t v, uint32_t begin, uint32_t end) {
- return (v & ((1ULL << (begin + 1)) - 1)) >> end;
-}
-
void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
const unsigned bits = config->wordsize * 8;
return;
case R_RISCV_RVC_BRANCH: {
- checkInt(loc, static_cast<int64_t>(val) >> 1, 8, rel);
+ checkInt(loc, val, 9, rel);
checkAlignment(loc, val, 2, rel);
uint16_t insn = read16le(loc) & 0xE383;
uint16_t imm8 = extractBits(val, 8, 8) << 12;
}
case R_RISCV_RVC_JUMP: {
- checkInt(loc, static_cast<int64_t>(val) >> 1, 11, rel);
+ checkInt(loc, val, 12, rel);
checkAlignment(loc, val, 2, rel);
uint16_t insn = read16le(loc) & 0xE003;
uint16_t imm11 = extractBits(val, 11, 11) << 12;
}
case R_RISCV_JAL: {
- checkInt(loc, static_cast<int64_t>(val) >> 1, 20, rel);
+ checkInt(loc, val, 21, rel);
checkAlignment(loc, val, 2, rel);
uint32_t insn = read32le(loc) & 0xFFF;
}
case R_RISCV_BRANCH: {
- checkInt(loc, static_cast<int64_t>(val) >> 1, 12, rel);
+ checkInt(loc, val, 13, rel);
checkAlignment(loc, val, 2, rel);
uint32_t insn = read32le(loc) & 0x1FFF07F;
case R_RISCV_LO12_I: {
uint64_t hi = (val + 0x800) >> 12;
uint64_t lo = val - (hi << 12);
- write32le(loc, (read32le(loc) & 0xFFFFF) | ((lo & 0xFFF) << 20));
+ write32le(loc, setLO12_I(read32le(loc), lo & 0xfff));
return;
}
case R_RISCV_LO12_S: {
uint64_t hi = (val + 0x800) >> 12;
uint64_t lo = val - (hi << 12);
- uint32_t imm11_5 = extractBits(lo, 11, 5) << 25;
- uint32_t imm4_0 = extractBits(lo, 4, 0) << 7;
- write32le(loc, (read32le(loc) & 0x1FFF07F) | imm11_5 | imm4_0);
+ write32le(loc, setLO12_S(read32le(loc), lo));
return;
}
}
}
+namespace {
+struct SymbolAnchor {
+ uint64_t offset;
+ Defined *d;
+ bool end; // true for the anchor of st_value+st_size
+};
+} // namespace
+
+struct elf::RISCVRelaxAux {
+ // This records symbol start and end offsets which will be adjusted according
+ // to the nearest relocDeltas element.
+ SmallVector<SymbolAnchor, 0> anchors;
+ // For relocations[i], the actual offset is r_offset - (i ? relocDeltas[i-1] :
+ // 0).
+ std::unique_ptr<uint32_t[]> relocDeltas;
+ // For relocations[i], the actual type is relocTypes[i].
+ std::unique_ptr<RelType[]> relocTypes;
+ SmallVector<uint32_t, 0> writes;
+};
+
+static void initSymbolAnchors() {
+ SmallVector<InputSection *, 0> storage;
+ for (OutputSection *osec : outputSections) {
+ if (!(osec->flags & SHF_EXECINSTR))
+ continue;
+ for (InputSection *sec : getInputSections(*osec, storage)) {
+ sec->relaxAux = make<RISCVRelaxAux>();
+ if (sec->relocs().size()) {
+ sec->relaxAux->relocDeltas =
+ std::make_unique<uint32_t[]>(sec->relocs().size());
+ sec->relaxAux->relocTypes =
+ std::make_unique<RelType[]>(sec->relocs().size());
+ }
+ }
+ }
+ // Store anchors (st_value and st_value+st_size) for symbols relative to text
+ // sections.
+ for (InputFile *file : ctx.objectFiles)
+ for (Symbol *sym : file->getSymbols()) {
+ auto *d = dyn_cast<Defined>(sym);
+ if (!d || d->file != file)
+ continue;
+ if (auto *sec = dyn_cast_or_null<InputSection>(d->section))
+ if (sec->flags & SHF_EXECINSTR && sec->relaxAux) {
+ // If sec is discarded, relaxAux will be nullptr.
+ sec->relaxAux->anchors.push_back({d->value, d, false});
+ sec->relaxAux->anchors.push_back({d->value + d->size, d, true});
+ }
+ }
+ // Sort anchors by offset so that we can find the closest relocation
+ // efficiently. For a zero size symbol, ensure that its start anchor precedes
+ // its end anchor. For two symbols with anchors at the same offset, their
+ // order does not matter.
+ for (OutputSection *osec : outputSections) {
+ if (!(osec->flags & SHF_EXECINSTR))
+ continue;
+ for (InputSection *sec : getInputSections(*osec, storage)) {
+ llvm::sort(sec->relaxAux->anchors, [](auto &a, auto &b) {
+ return std::make_pair(a.offset, a.end) <
+ std::make_pair(b.offset, b.end);
+ });
+ }
+ }
+}
+
+// Relax R_RISCV_CALL/R_RISCV_CALL_PLT auipc+jalr to c.j, c.jal, or jal.
+static void relaxCall(const InputSection &sec, size_t i, uint64_t loc,
+ Relocation &r, uint32_t &remove) {
+ const bool rvc = config->eflags & EF_RISCV_RVC;
+ const Symbol &sym = *r.sym;
+ const uint64_t insnPair = read64le(sec.content().data() + r.offset);
+ const uint32_t rd = extractBits(insnPair, 32 + 11, 32 + 7);
+ const uint64_t dest =
+ (r.expr == R_PLT_PC ? sym.getPltVA() : sym.getVA()) + r.addend;
+ const int64_t displace = dest - loc;
+
+ if (rvc && isInt<12>(displace) && rd == 0) {
+ sec.relaxAux->relocTypes[i] = R_RISCV_RVC_JUMP;
+ sec.relaxAux->writes.push_back(0xa001); // c.j
+ remove = 6;
+ } else if (rvc && isInt<12>(displace) && rd == X_RA &&
+ !config->is64) { // RV32C only
+ sec.relaxAux->relocTypes[i] = R_RISCV_RVC_JUMP;
+ sec.relaxAux->writes.push_back(0x2001); // c.jal
+ remove = 6;
+ } else if (isInt<21>(displace)) {
+ sec.relaxAux->relocTypes[i] = R_RISCV_JAL;
+ sec.relaxAux->writes.push_back(0x6f | rd << 7); // jal
+ remove = 4;
+ }
+}
+
+// Relax local-exec TLS when hi20 is zero.
+static void relaxTlsLe(const InputSection &sec, size_t i, uint64_t loc,
+ Relocation &r, uint32_t &remove) {
+ uint64_t val = r.sym->getVA(r.addend);
+ if (hi20(val) != 0)
+ return;
+ uint32_t insn = read32le(sec.content().data() + r.offset);
+ switch (r.type) {
+ case R_RISCV_TPREL_HI20:
+ case R_RISCV_TPREL_ADD:
+ // Remove lui rd, %tprel_hi(x) and add rd, rd, tp, %tprel_add(x).
+ sec.relaxAux->relocTypes[i] = R_RISCV_RELAX;
+ remove = 4;
+ break;
+ case R_RISCV_TPREL_LO12_I:
+ // addi rd, rd, %tprel_lo(x) => addi rd, tp, st_value(x)
+ sec.relaxAux->relocTypes[i] = R_RISCV_32;
+ insn = (insn & ~(31 << 15)) | (X_TP << 15);
+ sec.relaxAux->writes.push_back(setLO12_I(insn, val));
+ break;
+ case R_RISCV_TPREL_LO12_S:
+ // sw rs, %tprel_lo(x)(rd) => sw rs, st_value(x)(rd)
+ sec.relaxAux->relocTypes[i] = R_RISCV_32;
+ insn = (insn & ~(31 << 15)) | (X_TP << 15);
+ sec.relaxAux->writes.push_back(setLO12_S(insn, val));
+ break;
+ }
+}
+
+static bool relax(InputSection &sec) {
+ const uint64_t secAddr = sec.getVA();
+ auto &aux = *sec.relaxAux;
+ bool changed = false;
+
+ // Get st_value delta for symbols relative to this section from the previous
+ // iteration.
+ DenseMap<const Defined *, uint64_t> valueDelta;
+ ArrayRef<SymbolAnchor> sa = ArrayRef(aux.anchors);
+ uint32_t delta = 0;
+ for (auto [i, r] : llvm::enumerate(sec.relocs())) {
+ for (; sa.size() && sa[0].offset <= r.offset; sa = sa.slice(1))
+ if (!sa[0].end)
+ valueDelta[sa[0].d] = delta;
+ delta = aux.relocDeltas[i];
+ }
+ for (const SymbolAnchor &sa : sa)
+ if (!sa.end)
+ valueDelta[sa.d] = delta;
+ sa = ArrayRef(aux.anchors);
+ delta = 0;
+
+ std::fill_n(aux.relocTypes.get(), sec.relocs().size(), R_RISCV_NONE);
+ aux.writes.clear();
+ for (auto [i, r] : llvm::enumerate(sec.relocs())) {
+ const uint64_t loc = secAddr + r.offset - delta;
+ uint32_t &cur = aux.relocDeltas[i], remove = 0;
+ switch (r.type) {
+ case R_RISCV_ALIGN: {
+ const uint64_t nextLoc = loc + r.addend;
+ const uint64_t align = PowerOf2Ceil(r.addend + 2);
+ // All bytes beyond the alignment boundary should be removed.
+ remove = nextLoc - ((loc + align - 1) & -align);
+ assert(static_cast<int32_t>(remove) >= 0 &&
+ "R_RISCV_ALIGN needs expanding the content");
+ break;
+ }
+ case R_RISCV_CALL:
+ case R_RISCV_CALL_PLT:
+ if (i + 1 != sec.relocs().size() &&
+ sec.relocs()[i + 1].type == R_RISCV_RELAX)
+ relaxCall(sec, i, loc, r, remove);
+ break;
+ case R_RISCV_TPREL_HI20:
+ case R_RISCV_TPREL_ADD:
+ case R_RISCV_TPREL_LO12_I:
+ case R_RISCV_TPREL_LO12_S:
+ if (i + 1 != sec.relocs().size() &&
+ sec.relocs()[i + 1].type == R_RISCV_RELAX)
+ relaxTlsLe(sec, i, loc, r, remove);
+ break;
+ }
+
+ // For all anchors whose offsets are <= r.offset, they are preceded by
+ // the previous relocation whose `relocDeltas` value equals `delta`.
+ // Decrease their st_value and update their st_size.
+ for (; sa.size() && sa[0].offset <= r.offset; sa = sa.slice(1)) {
+ if (sa[0].end)
+ sa[0].d->size = sa[0].offset - delta - sa[0].d->value;
+ else
+ sa[0].d->value -= delta - valueDelta.find(sa[0].d)->second;
+ }
+ delta += remove;
+ if (delta != cur) {
+ cur = delta;
+ changed = true;
+ }
+ }
+
+ for (const SymbolAnchor &a : sa) {
+ if (a.end)
+ a.d->size = a.offset - delta - a.d->value;
+ else
+ a.d->value -= delta - valueDelta.find(a.d)->second;
+ }
+ // Inform assignAddresses that the size has changed.
+ if (!isUInt<16>(delta))
+ fatal("section size decrease is too large");
+ sec.bytesDropped = delta;
+ return changed;
+}
+
+// When relaxing just R_RISCV_ALIGN, relocDeltas is usually changed only once in
+// the absence of a linker script. For call and load/store R_RISCV_RELAX, code
+// shrinkage may reduce displacement and make more relocations eligible for
+// relaxation. Code shrinkage may increase displacement to a call/load/store
+// target at a higher fixed address, invalidating an earlier relaxation. Any
+// change in section sizes can have cascading effect and require another
+// relaxation pass.
+bool RISCV::relaxOnce(int pass) const {
+ llvm::TimeTraceScope timeScope("RISC-V relaxOnce");
+ if (config->relocatable)
+ return false;
+
+ if (pass == 0)
+ initSymbolAnchors();
+
+ SmallVector<InputSection *, 0> storage;
+ bool changed = false;
+ for (OutputSection *osec : outputSections) {
+ if (!(osec->flags & SHF_EXECINSTR))
+ continue;
+ for (InputSection *sec : getInputSections(*osec, storage))
+ changed |= relax(*sec);
+ }
+ return changed;
+}
+
+void elf::riscvFinalizeRelax(int passes) {
+ llvm::TimeTraceScope timeScope("Finalize RISC-V relaxation");
+ log("relaxation passes: " + Twine(passes));
+ SmallVector<InputSection *, 0> storage;
+ for (OutputSection *osec : outputSections) {
+ if (!(osec->flags & SHF_EXECINSTR))
+ continue;
+ for (InputSection *sec : getInputSections(*osec, storage)) {
+ RISCVRelaxAux &aux = *sec->relaxAux;
+ if (!aux.relocDeltas)
+ continue;
+
+ MutableArrayRef<Relocation> rels = sec->relocs();
+ ArrayRef<uint8_t> old = sec->content();
+ size_t newSize = old.size() - aux.relocDeltas[rels.size() - 1];
+ size_t writesIdx = 0;
+ uint8_t *p = context().bAlloc.Allocate<uint8_t>(newSize);
+ uint64_t offset = 0;
+ int64_t delta = 0;
+ sec->content_ = p;
+ sec->size = newSize;
+ sec->bytesDropped = 0;
+
+ // Update section content: remove NOPs for R_RISCV_ALIGN and rewrite
+ // instructions for relaxed relocations.
+ for (size_t i = 0, e = rels.size(); i != e; ++i) {
+ uint32_t remove = aux.relocDeltas[i] - delta;
+ delta = aux.relocDeltas[i];
+ if (remove == 0 && aux.relocTypes[i] == R_RISCV_NONE)
+ continue;
+
+ // Copy from last location to the current relocated location.
+ const Relocation &r = rels[i];
+ uint64_t size = r.offset - offset;
+ memcpy(p, old.data() + offset, size);
+ p += size;
+
+ // For R_RISCV_ALIGN, we will place `offset` in a location (among NOPs)
+ // to satisfy the alignment requirement. If both `remove` and r.addend
+ // are multiples of 4, it is as if we have skipped some NOPs. Otherwise
+ // we are in the middle of a 4-byte NOP, and we need to rewrite the NOP
+ // sequence.
+ int64_t skip = 0;
+ if (r.type == R_RISCV_ALIGN) {
+ if (remove % 4 || r.addend % 4) {
+ skip = r.addend - remove;
+ int64_t j = 0;
+ for (; j + 4 <= skip; j += 4)
+ write32le(p + j, 0x00000013); // nop
+ if (j != skip) {
+ assert(j + 2 == skip);
+ write16le(p + j, 0x0001); // c.nop
+ }
+ }
+ } else if (RelType newType = aux.relocTypes[i]) {
+ switch (newType) {
+ case R_RISCV_RELAX:
+ // Used by relaxTlsLe to indicate the relocation is ignored.
+ break;
+ case R_RISCV_RVC_JUMP:
+ skip = 2;
+ write16le(p, aux.writes[writesIdx++]);
+ break;
+ case R_RISCV_JAL:
+ skip = 4;
+ write32le(p, aux.writes[writesIdx++]);
+ break;
+ case R_RISCV_32:
+ // Used by relaxTlsLe to write a uint32_t then suppress the handling
+ // in relocateAlloc.
+ skip = 4;
+ write32le(p, aux.writes[writesIdx++]);
+ aux.relocTypes[i] = R_RISCV_NONE;
+ break;
+ default:
+ llvm_unreachable("unsupported type");
+ }
+ }
+
+ p += skip;
+ offset = r.offset + skip + remove;
+ }
+ memcpy(p, old.data() + offset, old.size() - offset);
+
+ // Subtract the previous relocDeltas value from the relocation offset.
+ // For a pair of R_RISCV_CALL/R_RISCV_RELAX with the same offset, decrease
+ // their r_offset by the same delta.
+ delta = 0;
+ for (size_t i = 0, e = rels.size(); i != e;) {
+ uint64_t cur = rels[i].offset;
+ do {
+ rels[i].offset -= delta;
+ if (aux.relocTypes[i] != R_RISCV_NONE)
+ rels[i].type = aux.relocTypes[i];
+ } while (++i != e && rels[i].offset == cur);
+ delta = aux.relocDeltas[i - 1];
+ }
+ }
+ }
+}
+
+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::parseNormalizedArchString(s);
+ 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()) {
+ 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 ⌖
//
//===----------------------------------------------------------------------===//
-#include "InputFiles.h"
#include "OutputSections.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
#include "lld/Common/ErrorHandler.h"
-#include "llvm/Object/ELF.h"
+#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Support/Endian.h"
using namespace llvm;
int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
void applyJumpInstrMod(uint8_t *loc, JumpModType type,
unsigned size) const override;
-
RelExpr adjustGotPcExpr(RelType type, int64_t addend,
const uint8_t *loc) const override;
- void relaxGot(uint8_t *loc, const Relocation &rel,
- uint64_t val) const override;
- void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
- uint64_t val) const override;
- void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
- uint64_t val) const override;
- void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
- uint64_t val) const override;
- void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
- uint64_t val) const override;
+ void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const override;
bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
uint8_t stOther) const override;
bool deleteFallThruJmpInsn(InputSection &is, InputFile *file,
X86_64::X86_64() {
copyRel = R_X86_64_COPY;
gotRel = R_X86_64_GLOB_DAT;
- noneRel = R_X86_64_NONE;
pltRel = R_X86_64_JUMP_SLOT;
relativeRel = R_X86_64_RELATIVE;
iRelativeRel = R_X86_64_IRELATIVE;
tlsGotRel = R_X86_64_TPOFF64;
tlsModuleIndexRel = R_X86_64_DTPMOD64;
tlsOffsetRel = R_X86_64_DTPOFF64;
+ gotBaseSymInGotPlt = true;
gotEntrySize = 8;
pltHeaderSize = 16;
pltEntrySize = 16;
defaultImageBase = 0x200000;
}
-int X86_64::getTlsGdRelaxSkip(RelType type) const { return 2; }
+int X86_64::getTlsGdRelaxSkip(RelType type) const {
+ // TLSDESC relocations are processed separately. See relaxTlsGdToLe below.
+ return type == R_X86_64_GOTPC32_TLSDESC || type == R_X86_64_TLSDESC_CALL ? 1
+ : 2;
+}
// Opcodes for the different X86_64 jmp instructions.
enum JmpInsnOpcode : uint32_t {
// Returns the maximum size of the vector if no such relocation is found.
static unsigned getRelocationWithOffset(const InputSection &is,
uint64_t offset) {
- unsigned size = is.relocations.size();
+ unsigned size = is.relocs().size();
for (unsigned i = size - 1; i + 1 > 0; --i) {
- if (is.relocations[i].offset == offset && is.relocations[i].expr != R_NONE)
+ if (is.relocs()[i].offset == offset && is.relocs()[i].expr != R_NONE)
return i;
}
return size;
// If this jmp insn can be removed, it is the last insn and the
// relocation is 4 bytes before the end.
unsigned rIndex = getRelocationWithOffset(is, is.getSize() - 4);
- if (rIndex == is.relocations.size())
+ if (rIndex == is.relocs().size())
return false;
- Relocation &r = is.relocations[rIndex];
+ Relocation &r = is.relocs()[rIndex];
// Check if the relocation corresponds to a direct jmp.
- const uint8_t *secContents = is.data().data();
+ const uint8_t *secContents = is.content().data();
// If it is not a direct jmp instruction, there is nothing to do here.
if (*(secContents + r.offset - 1) != 0xe9)
return false;
// Now, check if flip and delete is possible.
const unsigned sizeOfJmpCCInsn = 6;
- // To flip, there must be atleast one JmpCC and one direct jmp.
+ // To flip, there must be at least one JmpCC and one direct jmp.
if (is.getSize() < sizeOfDirectJmpInsn + sizeOfJmpCCInsn)
- return 0;
+ return false;
unsigned rbIndex =
getRelocationWithOffset(is, (is.getSize() - sizeOfDirectJmpInsn - 4));
- if (rbIndex == is.relocations.size())
- return 0;
+ if (rbIndex == is.relocs().size())
+ return false;
- Relocation &rB = is.relocations[rbIndex];
+ Relocation &rB = is.relocs()[rbIndex];
const uint8_t *jmpInsnB = secContents + rB.offset - 1;
JmpInsnOpcode jmpOpcodeB = getJmpInsnType(jmpInsnB - 1, jmpInsnB);
JmpInsnOpcode jInvert = invertJmpOpcode(jmpOpcodeB);
if (jInvert == J_UNKNOWN)
return false;
- is.jumpInstrMods.push_back({jInvert, (rB.offset - 1), 4});
+ is.jumpInstrMod = make<JumpInstrMod>();
+ *is.jumpInstrMod = {rB.offset - 1, jInvert, 4};
// Move R's values to rB except the offset.
rB = {r.expr, r.type, rB.offset, r.addend, r.sym};
// Cancel R
RelExpr X86_64::getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const {
- if (type == R_X86_64_GOTTPOFF)
- config->hasStaticTlsModel = true;
-
switch (type) {
case R_X86_64_8:
case R_X86_64_16:
return R_GOT_PC;
case R_X86_64_GOTOFF64:
return R_GOTPLTREL;
+ case R_X86_64_PLTOFF64:
+ return R_PLT_GOTPLT;
case R_X86_64_GOTPC32:
case R_X86_64_GOTPC64:
return R_GOTPLTONLY_PC;
memcpy(buf, inst, sizeof(inst));
write32le(buf + 2, sym.getGotPltVA() - pltEntryAddr - 6);
- write32le(buf + 7, sym.pltIndex);
+ write32le(buf + 7, sym.getPltIdx());
write32le(buf + 12, in.plt->getVA() - pltEntryAddr - 16);
}
return R_X86_64_NONE;
}
-void X86_64::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
- uint64_t val) const {
+static void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) {
if (rel.type == R_X86_64_TLSGD) {
// Convert
// .byte 0x66
// The original code used a pc relative relocation and so we have to
// compensate for the -4 in had in the addend.
write32le(loc + 8, val + 4);
- } else {
- // Convert
- // lea x@tlsgd(%rip), %rax
- // call *(%rax)
- // to the following two instructions.
- assert(rel.type == R_X86_64_GOTPC32_TLSDESC);
- if (memcmp(loc - 3, "\x48\x8d\x05", 3)) {
- error(getErrorLocation(loc - 3) + "R_X86_64_GOTPC32_TLSDESC must be used "
- "in callq *x@tlsdesc(%rip), %rax");
+ } else if (rel.type == R_X86_64_GOTPC32_TLSDESC) {
+ // Convert leaq x@tlsdesc(%rip), %REG to movq $x@tpoff, %REG.
+ if ((loc[-3] & 0xfb) != 0x48 || loc[-2] != 0x8d ||
+ (loc[-1] & 0xc7) != 0x05) {
+ errorOrWarn(getErrorLocation(loc - 3) +
+ "R_X86_64_GOTPC32_TLSDESC must be used "
+ "in leaq x@tlsdesc(%rip), %REG");
return;
}
- // movq $x@tpoff(%rip),%rax
+ loc[-3] = 0x48 | ((loc[-3] >> 2) & 1);
loc[-2] = 0xc7;
- loc[-1] = 0xc0;
+ loc[-1] = 0xc0 | ((loc[-1] >> 3) & 7);
write32le(loc, val + 4);
- // xchg ax,ax
- loc[4] = 0x66;
- loc[5] = 0x90;
+ } else {
+ // Convert call *x@tlsdesc(%REG) to xchg ax, ax.
+ assert(rel.type == R_X86_64_TLSDESC_CALL);
+ loc[0] = 0x66;
+ loc[1] = 0x90;
}
}
-void X86_64::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
- uint64_t val) const {
+static void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) {
if (rel.type == R_X86_64_TLSGD) {
// Convert
// .byte 0x66
// Both code sequences are PC relatives, but since we are moving the
// constant forward by 8 bytes we have to subtract the value by 8.
write32le(loc + 8, val - 8);
- } else {
- // Convert
- // lea x@tlsgd(%rip), %rax
- // call *(%rax)
- // to the following two instructions.
+ } else if (rel.type == R_X86_64_GOTPC32_TLSDESC) {
+ // Convert leaq x@tlsdesc(%rip), %REG to movq x@gottpoff(%rip), %REG.
assert(rel.type == R_X86_64_GOTPC32_TLSDESC);
- if (memcmp(loc - 3, "\x48\x8d\x05", 3)) {
- error(getErrorLocation(loc - 3) + "R_X86_64_GOTPC32_TLSDESC must be used "
- "in callq *x@tlsdesc(%rip), %rax");
+ if ((loc[-3] & 0xfb) != 0x48 || loc[-2] != 0x8d ||
+ (loc[-1] & 0xc7) != 0x05) {
+ errorOrWarn(getErrorLocation(loc - 3) +
+ "R_X86_64_GOTPC32_TLSDESC must be used "
+ "in leaq x@tlsdesc(%rip), %REG");
return;
}
- // movq x@gottpoff(%rip),%rax
loc[-2] = 0x8b;
write32le(loc, val);
- // xchg ax,ax
- loc[4] = 0x66;
- loc[5] = 0x90;
+ } else {
+ // Convert call *x@tlsdesc(%rax) to xchg ax, ax.
+ assert(rel.type == R_X86_64_TLSDESC_CALL);
+ loc[0] = 0x66;
+ loc[1] = 0x90;
}
}
// In some conditions, R_X86_64_GOTTPOFF relocation can be optimized to
// R_X86_64_TPOFF32 so that it does not use GOT.
-void X86_64::relaxTlsIeToLe(uint8_t *loc, const Relocation &,
- uint64_t val) const {
+static void relaxTlsIeToLe(uint8_t *loc, const Relocation &, uint64_t val) {
uint8_t *inst = loc - 3;
uint8_t reg = loc[-1] >> 3;
uint8_t *regSlot = loc - 1;
write32le(loc, val + 4);
}
-void X86_64::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
- uint64_t val) const {
- if (rel.type == R_X86_64_DTPOFF64) {
- write64le(loc, val);
- return;
- }
- if (rel.type == R_X86_64_DTPOFF32) {
- write32le(loc, val);
- return;
- }
-
+static void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) {
const uint8_t inst[] = {
0x66, 0x66, // .word 0x6666
0x66, // .byte 0x66
case R_X86_64_GOT64:
case R_X86_64_GOTOFF64:
case R_X86_64_GOTPC64:
+ case R_X86_64_PLTOFF64:
case R_X86_64_IRELATIVE:
case R_X86_64_RELATIVE:
return read64le(buf);
+ case R_X86_64_TLSDESC:
+ return read64le(buf + 8);
case R_X86_64_JUMP_SLOT:
case R_X86_64_NONE:
// These relocations are defined as not having an implicit addend.
}
}
+static void relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val);
+
void X86_64::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
switch (rel.type) {
case R_X86_64_8:
write32le(loc, val);
break;
case R_X86_64_32S:
- case R_X86_64_TPOFF32:
case R_X86_64_GOT32:
case R_X86_64_GOTPC32:
- case R_X86_64_GOTPC32_TLSDESC:
case R_X86_64_GOTPCREL:
- case R_X86_64_GOTPCRELX:
- case R_X86_64_REX_GOTPCRELX:
case R_X86_64_PC32:
- case R_X86_64_GOTTPOFF:
case R_X86_64_PLT32:
- case R_X86_64_TLSGD:
- case R_X86_64_TLSLD:
case R_X86_64_DTPOFF32:
case R_X86_64_SIZE32:
checkInt(loc, val, 32, rel);
case R_X86_64_GOT64:
case R_X86_64_GOTOFF64:
case R_X86_64_GOTPC64:
+ case R_X86_64_PLTOFF64:
write64le(loc, val);
break;
+ case R_X86_64_GOTPCRELX:
+ case R_X86_64_REX_GOTPCRELX:
+ if (rel.expr != R_GOT_PC) {
+ relaxGot(loc, rel, val);
+ } else {
+ checkInt(loc, val, 32, rel);
+ write32le(loc, val);
+ }
+ break;
+ case R_X86_64_GOTPC32_TLSDESC:
+ case R_X86_64_TLSDESC_CALL:
+ case R_X86_64_TLSGD:
+ if (rel.expr == R_RELAX_TLS_GD_TO_LE) {
+ relaxTlsGdToLe(loc, rel, val);
+ } else if (rel.expr == R_RELAX_TLS_GD_TO_IE) {
+ relaxTlsGdToIe(loc, rel, val);
+ } else {
+ checkInt(loc, val, 32, rel);
+ write32le(loc, val);
+ }
+ break;
+ case R_X86_64_TLSLD:
+ if (rel.expr == R_RELAX_TLS_LD_TO_LE) {
+ relaxTlsLdToLe(loc, rel, val);
+ } else {
+ checkInt(loc, val, 32, rel);
+ write32le(loc, val);
+ }
+ break;
+ case R_X86_64_GOTTPOFF:
+ if (rel.expr == R_RELAX_TLS_IE_TO_LE) {
+ relaxTlsIeToLe(loc, rel, val);
+ } else {
+ checkInt(loc, val, 32, rel);
+ write32le(loc, val);
+ }
+ break;
+ case R_X86_64_TPOFF32:
+ checkInt(loc, val, 32, rel);
+ write32le(loc, val);
+ break;
+
+ case R_X86_64_TLSDESC:
+ // The addend is stored in the second 64-bit word.
+ write64le(loc + 8, val);
+ break;
default:
llvm_unreachable("unknown relocation");
}
// with addend != -4. Such an instruction does not load the full GOT entry, so
// we cannot relax the relocation. E.g. movl x@GOTPCREL+4(%rip), %rax
// (addend=0) loads the high 32 bits of the GOT entry.
- if ((type != R_X86_64_GOTPCRELX && type != R_X86_64_REX_GOTPCRELX) ||
- addend != -4)
+ if (!config->relax || addend != -4 ||
+ (type != R_X86_64_GOTPCRELX && type != R_X86_64_REX_GOTPCRELX))
return R_GOT_PC;
const uint8_t op = loc[-2];
const uint8_t modRm = loc[-1];
write32le(loc, val);
}
-void X86_64::relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) const {
+static void relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) {
checkInt(loc, val, 32, rel);
const uint8_t op = loc[-2];
const uint8_t modRm = loc[-1];
bool X86_64::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
uint8_t stOther) const {
if (!config->is64) {
- error("Target doesn't support split stacks.");
+ error("target doesn't support split stacks");
return false;
}
return false;
}
+void X86_64::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const {
+ uint64_t secAddr = sec.getOutputSection()->addr;
+ if (auto *s = dyn_cast<InputSection>(&sec))
+ secAddr += s->outSecOff;
+ for (const Relocation &rel : sec.relocs()) {
+ if (rel.expr == R_NONE) // See deleteFallThruJmpInsn
+ continue;
+ uint8_t *loc = buf + rel.offset;
+ const uint64_t val =
+ sec.getRelocTargetVA(sec.file, rel.type, rel.addend,
+ secAddr + rel.offset, *rel.sym, rel.expr);
+ relocate(loc, rel, val);
+ }
+ if (sec.jumpInstrMod) {
+ applyJumpInstrMod(buf + sec.jumpInstrMod->offset,
+ sec.jumpInstrMod->original, sec.jumpInstrMod->size);
+ }
+}
+
// If Intel Indirect Branch Tracking is enabled, we have to emit special PLT
// entries containing endbr64 instructions. A PLT entry will be split into two
// parts, one in .plt.sec (writePlt), and the other in .plt (writeIBTPlt).
void IntelIBT::writeGotPlt(uint8_t *buf, const Symbol &s) const {
uint64_t va =
- in.ibtPlt->getVA() + IBTPltHeaderSize + s.pltIndex * pltEntrySize;
+ in.ibtPlt->getVA() + IBTPltHeaderSize + s.getPltIdx() * pltEntrySize;
write64le(buf, va);
}
write32le(buf + 7, sym.getGotPltVA() - pltEntryAddr - 11);
write32le(buf + 12, -off - 16 + 32);
write32le(buf + 17, -off - 21 + 18);
- write32le(buf + 22, sym.pltIndex);
+ write32le(buf + 22, sym.getPltIdx());
write32le(buf + 27, -off - 31);
}
#include "lld/Common/ErrorHandler.h"
#include "llvm/ADT/CachedHashString.h"
+#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/Option/ArgList.h"
#include "llvm/Support/CachePruning.h"
#include "llvm/Support/CodeGen.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/Compression.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/GlobPattern.h"
#include "llvm/Support/PrettyStackTrace.h"
#include <atomic>
+#include <memory>
+#include <optional>
#include <vector>
-namespace lld {
-namespace elf {
+namespace lld::elf {
class InputFile;
+class BinaryFile;
+class BitcodeFile;
+class ELFFileBase;
+class SharedFile;
class InputSectionBase;
+class EhInputSection;
+class Symbol;
+class BitcodeCompiler;
-enum ELFKind {
+enum ELFKind : uint8_t {
ELFNoneKind,
ELF32LEKind,
ELF32BEKind,
struct VersionDefinition {
llvm::StringRef name;
uint16_t id;
- std::vector<SymbolVersion> nonLocalPatterns;
- std::vector<SymbolVersion> localPatterns;
+ SmallVector<SymbolVersion, 0> nonLocalPatterns;
+ SmallVector<SymbolVersion, 0> localPatterns;
+};
+
+class LinkerDriver {
+public:
+ void linkerMain(ArrayRef<const char *> args);
+ void addFile(StringRef path, bool withLOption);
+ void addLibrary(StringRef name);
+
+private:
+ void createFiles(llvm::opt::InputArgList &args);
+ void inferMachineType();
+ void link(llvm::opt::InputArgList &args);
+ template <class ELFT> void compileBitcodeFiles(bool skipLinkedOutput);
+
+ // True if we are in --whole-archive and --no-whole-archive.
+ bool inWholeArchive = false;
+
+ // True if we are in --start-lib and --end-lib.
+ bool inLib = false;
+
+ std::unique_ptr<BitcodeCompiler> lto;
+ std::vector<InputFile *> files;
+
+public:
+ SmallVector<std::pair<StringRef, unsigned>, 0> archiveFiles;
};
// This struct contains the global configuration for the linker.
// Most fields are direct mapping from the command line options
// and such fields have the same name as the corresponding options.
-// Most fields are initialized by the driver.
-struct Configuration {
+// Most fields are initialized by the ctx.driver.
+struct Config {
uint8_t osabi = 0;
uint32_t andFeatures = 0;
llvm::CachePruningPolicy thinLTOCachePolicy;
llvm::StringRef mapFile;
llvm::StringRef outputFile;
llvm::StringRef optRemarksFilename;
- llvm::Optional<uint64_t> optRemarksHotnessThreshold = 0;
+ std::optional<uint64_t> optRemarksHotnessThreshold = 0;
llvm::StringRef optRemarksPasses;
llvm::StringRef optRemarksFormat;
+ llvm::StringRef optStatsFilename;
llvm::StringRef progName;
llvm::StringRef printArchiveStats;
llvm::StringRef printSymbolOrder;
llvm::StringRef sysroot;
llvm::StringRef thinLTOCacheDir;
llvm::StringRef thinLTOIndexOnlyArg;
+ llvm::StringRef whyExtract;
+ StringRef zBtiReport = "none";
+ StringRef zCetReport = "none";
llvm::StringRef ltoBasicBlockSections;
std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;
std::pair<llvm::StringRef, llvm::StringRef> thinLTOPrefixReplace;
std::string rpath;
- std::vector<VersionDefinition> versionDefinitions;
- std::vector<llvm::StringRef> auxiliaryList;
- std::vector<llvm::StringRef> filterList;
- std::vector<llvm::StringRef> searchPaths;
- std::vector<llvm::StringRef> symbolOrderingFile;
- std::vector<llvm::StringRef> thinLTOModulesToCompile;
- std::vector<llvm::StringRef> undefined;
- std::vector<SymbolVersion> dynamicList;
- std::vector<uint8_t> buildIdVector;
+ llvm::SmallVector<VersionDefinition, 0> versionDefinitions;
+ llvm::SmallVector<llvm::StringRef, 0> auxiliaryList;
+ llvm::SmallVector<llvm::StringRef, 0> filterList;
+ llvm::SmallVector<llvm::StringRef, 0> passPlugins;
+ llvm::SmallVector<llvm::StringRef, 0> searchPaths;
+ llvm::SmallVector<llvm::StringRef, 0> symbolOrderingFile;
+ llvm::SmallVector<llvm::StringRef, 0> thinLTOModulesToCompile;
+ llvm::SmallVector<llvm::StringRef, 0> undefined;
+ llvm::SmallVector<SymbolVersion, 0> dynamicList;
+ llvm::SmallVector<uint8_t, 0> buildIdVector;
+ llvm::SmallVector<llvm::StringRef, 0> mllvmOpts;
llvm::MapVector<std::pair<const InputSectionBase *, const InputSectionBase *>,
uint64_t>
callGraphProfile;
bool allowMultipleDefinition;
- bool androidPackDynRelocs;
+ bool androidPackDynRelocs = false;
bool armHasBlx = false;
bool armHasMovtMovw = false;
bool armJ1J2BranchEncoding = false;
bool callGraphProfileSort;
bool checkSections;
bool checkDynamicRelocs;
- bool compressDebugSections;
+ llvm::DebugCompressionType compressDebugSections;
bool cref;
- std::vector<std::pair<llvm::GlobPattern, uint64_t>> deadRelocInNonAlloc;
- bool defineCommon;
+ llvm::SmallVector<std::pair<llvm::GlobPattern, uint64_t>, 0>
+ deadRelocInNonAlloc;
bool demangle = true;
bool dependentLibraries;
bool disableVerify;
bool ignoreDataAddressEquality;
bool ignoreFunctionAddressEquality;
bool ltoCSProfileGenerate;
+ bool ltoPGOWarnMismatch;
bool ltoDebugPassManager;
bool ltoEmitAsm;
- bool ltoNewPassManager;
- bool ltoPseudoProbeForProfiling;
bool ltoUniqueBasicBlockSectionNames;
bool ltoWholeProgramVisibility;
bool mergeArmExidx;
bool nostdlib;
bool oFormatBinary;
bool omagic;
+ bool opaquePointers;
bool optEB = false;
bool optEL = false;
bool optimizeBBJumps;
bool pie;
bool printGcSections;
bool printIcfSections;
+ bool relax;
bool relocatable;
- bool relrPackDynRelocs;
- bool saveTemps;
- std::vector<std::pair<llvm::GlobPattern, uint32_t>> shuffleSections;
+ bool relrGlibc = false;
+ bool relrPackDynRelocs = false;
+ llvm::DenseSet<llvm::StringRef> saveTempsArgs;
+ llvm::SmallVector<std::pair<llvm::GlobPattern, uint32_t>, 0> shuffleSections;
bool singleRoRx;
bool shared;
bool symbolic;
bool target1Rel;
bool trace;
bool thinLTOEmitImportsFiles;
+ bool thinLTOEmitIndexFiles;
bool thinLTOIndexOnly;
bool timeTraceEnabled;
bool tocOptimize;
bool unique;
bool useAndroidRelrTags = false;
bool warnBackrefs;
- std::vector<llvm::GlobPattern> warnBackrefsExclude;
+ llvm::SmallVector<llvm::GlobPattern, 0> warnBackrefsExclude;
bool warnCommon;
bool warnMissingEntry;
bool warnSymbolOrdering;
UnresolvedPolicy unresolvedSymbols;
UnresolvedPolicy unresolvedSymbolsInShlib;
Target2Policy target2;
- bool Power10Stub;
+ bool power10Stubs;
ARMVFPArgKind armVFPArgs = ARMVFPArgKind::Default;
BuildIdKind buildId = BuildIdKind::None;
SeparateSegmentKind zSeparate;
ELFKind ekind = ELFNoneKind;
uint16_t emachine = llvm::ELF::EM_NONE;
- llvm::Optional<uint64_t> imageBase;
+ std::optional<uint64_t> imageBase;
// commonPageSize and maxPageSize are influenced by nmagic or omagic
// so may be set to 1 if either of those options is given.
uint64_t commonPageSize;
StringRef thinLTOJobs;
unsigned timeTraceGranularity;
int32_t splitStackAdjustSize;
+ StringRef packageMetadata;
// The following config options do not directly correspond to any
// particular command line options.
// if that's true.)
bool isMips64EL;
- // True if we need to set the DF_STATIC_TLS flag to an output file,
- // which works as a hint to the dynamic loader that the file contains
- // code compiled with the static TLS model. The thread-local variable
- // compiled with the static TLS model is faster but less flexible, and
- // it may not be loaded using dlopen().
- //
- // We set this flag to true when we see a relocation for the static TLS
- // model. Once this becomes true, it will never become false.
- //
- // Since the flag is updated by multi-threaded code, we use std::atomic.
- // (Writing to a variable is not considered thread-safe even if the
- // variable is boolean and we always set the same value from all threads.)
- std::atomic<bool> hasStaticTlsModel{false};
+ // True if we need to set the DF_STATIC_TLS flag to an output file, which
+ // works as a hint to the dynamic loader that the shared object contains code
+ // compiled with the initial-exec TLS model.
+ bool hasTlsIe = false;
// Holds set of ELF header flags for the target.
uint32_t eflags = 0;
// 4 for ELF32, 8 for ELF64.
int wordsize;
+
+ // Mode of MTE to write to the ELF note. Should be one of NT_MEMTAG_ASYNC (for
+ // async), NT_MEMTAG_SYNC (for sync), or NT_MEMTAG_LEVEL_NONE (for none). If
+ // async or sync is enabled, write the ELF note specifying the default MTE
+ // mode.
+ int androidMemtagMode;
+ // Signal to the dynamic loader to enable heap MTE.
+ bool androidMemtagHeap;
+ // Signal to the dynamic loader that this binary expects stack MTE. Generally,
+ // this means to map the primary and thread stacks as PROT_MTE. Note: This is
+ // not supported on Android 11 & 12.
+ bool androidMemtagStack;
+
+ unsigned threadCount;
+};
+struct ConfigWrapper {
+ Config c;
+ Config *operator->() { return &c; }
+};
+
+LLVM_LIBRARY_VISIBILITY extern ConfigWrapper config;
+
+struct DuplicateSymbol {
+ const Symbol *sym;
+ const InputFile *file;
+ InputSectionBase *section;
+ uint64_t value;
+};
+
+struct Ctx {
+ LinkerDriver driver;
+ SmallVector<std::unique_ptr<MemoryBuffer>> memoryBuffers;
+ SmallVector<ELFFileBase *, 0> objectFiles;
+ SmallVector<SharedFile *, 0> sharedFiles;
+ SmallVector<BinaryFile *, 0> binaryFiles;
+ SmallVector<BitcodeFile *, 0> bitcodeFiles;
+ SmallVector<BitcodeFile *, 0> lazyBitcodeFiles;
+ SmallVector<InputSectionBase *, 0> inputSections;
+ SmallVector<EhInputSection *, 0> ehInputSections;
+ // Duplicate symbol candidates.
+ SmallVector<DuplicateSymbol, 0> duplicates;
+ // Symbols in a non-prevailing COMDAT group which should be changed to an
+ // Undefined.
+ SmallVector<std::pair<Symbol *, unsigned>, 0> nonPrevailingSyms;
+ // A tuple of (reference, extractedFile, sym). Used by --why-extract=.
+ SmallVector<std::tuple<std::string, const InputFile *, const Symbol &>, 0>
+ whyExtractRecords;
+ // A mapping from a symbol to an InputFile referencing it backward. Used by
+ // --warn-backrefs.
+ llvm::DenseMap<const Symbol *,
+ std::pair<const InputFile *, const InputFile *>>
+ backwardReferences;
+ // True if SHT_LLVM_SYMPART is used.
+ std::atomic<bool> hasSympart{false};
+ // True if there are TLS IE relocations. Set DF_STATIC_TLS if -shared.
+ std::atomic<bool> hasTlsIe{false};
+ // True if we need to reserve two .got entries for local-dynamic TLS model.
+ std::atomic<bool> needsTlsLd{false};
+
+ void reset();
};
-// The only instance of Configuration struct.
-extern Configuration *config;
+LLVM_LIBRARY_VISIBILITY extern Ctx ctx;
// The first two elements of versionDefinitions represent VER_NDX_LOCAL and
// VER_NDX_GLOBAL. This helper returns other elements.
static inline ArrayRef<VersionDefinition> namedVersionDefs() {
- return llvm::makeArrayRef(config->versionDefinitions).slice(2);
+ return llvm::ArrayRef(config->versionDefinitions).slice(2);
}
-static inline void errorOrWarn(const Twine &msg) {
- if (!config->noinhibitExec)
- error(msg);
- else
- warn(msg);
-}
+void errorOrWarn(const Twine &msg);
static inline void internalLinkerError(StringRef loc, const Twine &msg) {
errorOrWarn(loc + "internal linker error: " + msg + "\n" +
llvm::getBugReportMsg());
}
-} // namespace elf
-} // namespace lld
+} // namespace lld::elf
#endif
// linker path or library paths) for each host OS.
//
// I don't think implicit default values are useful because they are
-// usually explicitly specified by the compiler driver. They can even
+// usually explicitly specified by the compiler ctx.driver. They can even
// be harmful when you are doing cross-linking. Therefore, in LLD, we
// simply trust the compiler driver to pass all required options and
// don't try to make effort on our side.
#include "ICF.h"
#include "InputFiles.h"
#include "InputSection.h"
+#include "LTO.h"
#include "LinkerScript.h"
#include "MarkLive.h"
#include "OutputSections.h"
#include "Target.h"
#include "Writer.h"
#include "lld/Common/Args.h"
+#include "lld/Common/CommonLinkerContext.h"
#include "lld/Common/Driver.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Filesystem.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/LTO/LTO.h"
+#include "llvm/Object/Archive.h"
#include "llvm/Remarks/HotnessThresholdParser.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compression.h"
+#include "llvm/Support/FileSystem.h"
#include "llvm/Support/GlobPattern.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/Parallel.h"
using namespace lld;
using namespace lld::elf;
-Configuration *elf::config;
-LinkerDriver *elf::driver;
+ConfigWrapper elf::config;
+Ctx elf::ctx;
static void setConfigs(opt::InputArgList &args);
static void readConfigs(opt::InputArgList &args);
-bool elf::link(ArrayRef<const char *> args, bool canExitEarly,
- raw_ostream &stdoutOS, raw_ostream &stderrOS) {
- lld::stdoutOS = &stdoutOS;
- lld::stderrOS = &stderrOS;
-
- errorHandler().cleanupCallback = []() {
- freeArena();
+void elf::errorOrWarn(const Twine &msg) {
+ if (config->noinhibitExec)
+ warn(msg);
+ else
+ error(msg);
+}
+
+void Ctx::reset() {
+ driver = LinkerDriver();
+ memoryBuffers.clear();
+ objectFiles.clear();
+ sharedFiles.clear();
+ binaryFiles.clear();
+ bitcodeFiles.clear();
+ lazyBitcodeFiles.clear();
+ inputSections.clear();
+ ehInputSections.clear();
+ duplicates.clear();
+ nonPrevailingSyms.clear();
+ whyExtractRecords.clear();
+ backwardReferences.clear();
+ hasSympart.store(false, std::memory_order_relaxed);
+ needsTlsLd.store(false, std::memory_order_relaxed);
+}
+
+bool elf::link(ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS,
+ llvm::raw_ostream &stderrOS, bool exitEarly,
+ bool disableOutput) {
+ // This driver-specific context will be freed later by lldMain().
+ auto *ctx = new CommonLinkerContext;
+
+ ctx->e.initialize(stdoutOS, stderrOS, exitEarly, disableOutput);
+ ctx->e.cleanupCallback = []() {
+ elf::ctx.reset();
+ symtab = SymbolTable();
- inputSections.clear();
outputSections.clear();
- archiveFiles.clear();
- binaryFiles.clear();
- bitcodeFiles.clear();
- lazyObjFiles.clear();
- objectFiles.clear();
- sharedFiles.clear();
- backwardReferences.clear();
+ symAux.clear();
tar = nullptr;
- memset(&in, 0, sizeof(in));
+ in.reset();
- partitions = {Partition()};
+ partitions.clear();
+ partitions.emplace_back();
SharedFile::vernauxNum = 0;
};
+ ctx->e.logName = args::getFilenameWithoutExe(args[0]);
+ ctx->e.errorLimitExceededMsg = "too many errors emitted, stopping now (use "
+ "--error-limit=0 to see all errors)";
- errorHandler().logName = args::getFilenameWithoutExe(args[0]);
- errorHandler().errorLimitExceededMsg =
- "too many errors emitted, stopping now (use "
- "-error-limit=0 to see all errors)";
- errorHandler().exitEarly = canExitEarly;
- stderrOS.enable_colors(stderrOS.has_colors());
+ config = ConfigWrapper();
+ script = std::make_unique<LinkerScript>();
- config = make<Configuration>();
- driver = make<LinkerDriver>();
- script = make<LinkerScript>();
- symtab = make<SymbolTable>();
+ symAux.emplace_back();
- partitions = {Partition()};
+ partitions.clear();
+ partitions.emplace_back();
config->progName = args[0];
- driver->linkerMain(args);
-
- // Exit immediately if we don't need to return to the caller.
- // This saves time because the overhead of calling destructors
- // for all globally-allocated objects is not negligible.
- if (canExitEarly)
- exitLld(errorCount() ? 1 : 0);
+ elf::ctx.driver.linkerMain(args);
- bool ret = errorCount() == 0;
- if (!canExitEarly)
- errorHandler().reset();
- return ret;
+ return errorCount() == 0;
}
// Parses a linker -m option.
.Case("elf_iamcu", {ELF32LEKind, EM_IAMCU})
.Case("elf64_sparc", {ELF64BEKind, EM_SPARCV9})
.Case("msp430elf", {ELF32LEKind, EM_MSP430})
+ .Case("elf64_amdgpu", {ELF64LEKind, EM_AMDGPU})
.Default({ELFNoneKind, EM_NONE});
if (ret.first == ELFNoneKind)
error("unknown emulation: " + emul);
if (ret.second == EM_MSP430)
osabi = ELFOSABI_STANDALONE;
+ else if (ret.second == EM_AMDGPU)
+ osabi = ELFOSABI_AMDGPU_HSA;
return std::make_tuple(ret.first, ret.second, osabi);
}
toString(std::move(err)));
// Take ownership of memory buffers created for members of thin archives.
- for (std::unique_ptr<MemoryBuffer> &mb : file->takeThinBuffers())
- make<std::unique_ptr<MemoryBuffer>>(std::move(mb));
+ std::vector<std::unique_ptr<MemoryBuffer>> mbs = file->takeThinBuffers();
+ std::move(mbs.begin(), mbs.end(), std::back_inserter(ctx.memoryBuffers));
return v;
}
+static bool isBitcode(MemoryBufferRef mb) {
+ return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode;
+}
+
// Opens a file and create a file object. Path has to be resolved already.
void LinkerDriver::addFile(StringRef path, bool withLOption) {
using namespace sys::fs;
- Optional<MemoryBufferRef> buffer = readFile(path);
- if (!buffer.hasValue())
+ std::optional<MemoryBufferRef> buffer = readFile(path);
+ if (!buffer)
return;
MemoryBufferRef mbref = *buffer;
readLinkerScript(mbref);
return;
case file_magic::archive: {
- // Handle -whole-archive.
+ auto members = getArchiveMembers(mbref);
if (inWholeArchive) {
- for (const auto &p : getArchiveMembers(mbref))
- files.push_back(createObjectFile(p.first, path, p.second));
+ for (const std::pair<MemoryBufferRef, uint64_t> &p : members) {
+ if (isBitcode(p.first))
+ files.push_back(make<BitcodeFile>(p.first, path, p.second, false));
+ else
+ files.push_back(createObjFile(p.first, path));
+ }
return;
}
- std::unique_ptr<Archive> file =
- CHECK(Archive::create(mbref), path + ": failed to parse archive");
-
- // If an archive file has no symbol table, it is likely that a user
- // is attempting LTO and using a default ar command that doesn't
- // understand the LLVM bitcode file. It is a pretty common error, so
- // we'll handle it as if it had a symbol table.
- if (!file->isEmpty() && !file->hasSymbolTable()) {
- // Check if all members are bitcode files. If not, ignore, which is the
- // default action without the LTO hack described above.
- for (const std::pair<MemoryBufferRef, uint64_t> &p :
- getArchiveMembers(mbref))
- if (identify_magic(p.first.getBuffer()) != file_magic::bitcode) {
- error(path + ": archive has no index; run ranlib to add one");
- return;
- }
-
- for (const std::pair<MemoryBufferRef, uint64_t> &p :
- getArchiveMembers(mbref))
- files.push_back(make<LazyObjFile>(p.first, path, p.second));
- return;
+ archiveFiles.emplace_back(path, members.size());
+
+ // Handle archives and --start-lib/--end-lib using the same code path. This
+ // scans all the ELF relocatable object files and bitcode files in the
+ // archive rather than just the index file, with the benefit that the
+ // symbols are only loaded once. For many projects archives see high
+ // utilization rates and it is a net performance win. --start-lib scans
+ // symbols in the same order that llvm-ar adds them to the index, so in the
+ // common case the semantics are identical. If the archive symbol table was
+ // created in a different order, or is incomplete, this strategy has
+ // different semantics. Such output differences are considered user error.
+ //
+ // All files within the archive get the same group ID to allow mutual
+ // references for --warn-backrefs.
+ bool saved = InputFile::isInGroup;
+ InputFile::isInGroup = true;
+ for (const std::pair<MemoryBufferRef, uint64_t> &p : members) {
+ auto magic = identify_magic(p.first.getBuffer());
+ if (magic == file_magic::elf_relocatable)
+ files.push_back(createObjFile(p.first, path, true));
+ else if (magic == file_magic::bitcode)
+ files.push_back(make<BitcodeFile>(p.first, path, p.second, true));
+ else
+ warn(path + ": archive member '" + p.first.getBufferIdentifier() +
+ "' is neither ET_REL nor LLVM bitcode");
}
-
- // Handle the regular case.
- files.push_back(make<ArchiveFile>(std::move(file)));
+ InputFile::isInGroup = saved;
+ if (!saved)
+ ++InputFile::nextGroupId;
return;
}
- case file_magic::elf_shared_object:
+ case file_magic::elf_shared_object: {
if (config->isStatic || config->relocatable) {
error("attempted static link of dynamic object " + path);
return;
}
- // DSOs usually have DT_SONAME tags in their ELF headers, and the
- // sonames are used to identify DSOs. But if they are missing,
- // they are identified by filenames. We don't know whether the new
- // file has a DT_SONAME or not because we haven't parsed it yet.
- // Here, we set the default soname for the file because we might
- // need it later.
- //
- // If a file was specified by -lfoo, the directory part is not
- // significant, as a user did not specify it. This behavior is
- // compatible with GNU.
- files.push_back(
- make<SharedFile>(mbref, withLOption ? path::filename(path) : path));
+ // Shared objects are identified by soname. soname is (if specified)
+ // DT_SONAME and falls back to filename. If a file was specified by -lfoo,
+ // the directory part is ignored. Note that path may be a temporary and
+ // cannot be stored into SharedFile::soName.
+ path = mbref.getBufferIdentifier();
+ auto *f =
+ make<SharedFile>(mbref, withLOption ? path::filename(path) : path);
+ f->init();
+ files.push_back(f);
return;
+ }
case file_magic::bitcode:
+ files.push_back(make<BitcodeFile>(mbref, "", 0, inLib));
+ break;
case file_magic::elf_relocatable:
- if (inLib)
- files.push_back(make<LazyObjFile>(mbref, "", 0));
- else
- files.push_back(createObjectFile(mbref));
+ files.push_back(createObjFile(mbref, "", inLib));
break;
default:
error(path + ": unknown file type");
// Add a given library by searching it from input search paths.
void LinkerDriver::addLibrary(StringRef name) {
- if (Optional<std::string> path = searchLibrary(name))
- addFile(*path, /*withLOption=*/true);
+ if (std::optional<std::string> path = searchLibrary(name))
+ addFile(saver().save(*path), /*withLOption=*/true);
else
error("unable to find library -l" + name, ErrorTag::LibNotFound, {name});
}
if (!config->shared && !config->auxiliaryList.empty())
error("-f may not be used without -shared");
- if (!config->relocatable && !config->defineCommon)
- error("-no-define-common not supported in non relocatable output");
-
if (config->strip == StripPolicy::All && config->emitRelocs)
error("--strip-all and --emit-relocs may not be used together");
}
if (config->singleRoRx && !script->hasSectionsCommand)
- error("-execute-only and -no-rosegment cannot be used together");
+ error("--execute-only and --no-rosegment cannot be used together");
}
if (config->zRetpolineplt && config->zForceIbt)
error("-z pac-plt only supported on AArch64");
if (config->zForceBti)
error("-z force-bti only supported on AArch64");
+ if (config->zBtiReport != "none")
+ error("-z bti-report only supported on AArch64");
}
+
+ if (config->emachine != EM_386 && config->emachine != EM_X86_64 &&
+ config->zCetReport != "none")
+ error("-z cet-report only supported on X86 and X86_64");
}
static const char *getReproduceOption(opt::InputArgList &args) {
return STV_PROTECTED;
}
+constexpr const char *knownZFlags[] = {
+ "combreloc",
+ "copyreloc",
+ "defs",
+ "execstack",
+ "force-bti",
+ "force-ibt",
+ "global",
+ "hazardplt",
+ "ifunc-noplt",
+ "initfirst",
+ "interpose",
+ "keep-text-section-prefix",
+ "lazy",
+ "muldefs",
+ "nobtcfi",
+ "nocombreloc",
+ "nocopyreloc",
+ "nodefaultlib",
+ "nodelete",
+ "nodlopen",
+ "noexecstack",
+ "nognustack",
+ "nokeep-text-section-prefix",
+ "nopack-relative-relocs",
+ "norelro",
+ "noretpolineplt",
+ "noseparate-code",
+ "nostart-stop-gc",
+ "notext",
+ "now",
+ "origin",
+ "pac-plt",
+ "pack-relative-relocs",
+ "rel",
+ "rela",
+ "relro",
+ "retpolineplt",
+ "rodynamic",
+ "separate-code",
+ "separate-loadable-segments",
+ "shstk",
+ "start-stop-gc",
+ "text",
+ "undefs",
+ "wxneeded",
+};
+
static bool isKnownZFlag(StringRef s) {
- return s == "combreloc" || s == "copyreloc" || s == "defs" ||
- s == "execstack" || s == "force-bti" || s == "force-ibt" ||
- s == "global" || s == "hazardplt" || s == "ifunc-noplt" ||
- s == "initfirst" || s == "interpose" ||
- s == "keep-text-section-prefix" || s == "lazy" || s == "muldefs" ||
- s == "separate-code" || s == "separate-loadable-segments" ||
- s == "start-stop-gc" || s == "nobtcfi" ||
- s == "nocombreloc" || s == "nocopyreloc" ||
- s == "nodefaultlib" || s == "nodelete" || s == "nodlopen" ||
- s == "noexecstack" || s == "nognustack" ||
- s == "nokeep-text-section-prefix" || s == "norelro" ||
- s == "noretpolineplt" ||
- s == "noseparate-code" || s == "nostart-stop-gc" || s == "notext" ||
- s == "now" || s == "origin" || s == "pac-plt" || s == "rel" ||
- s == "rela" || s == "relro" || s == "retpolineplt" ||
- s == "rodynamic" || s == "shstk" || s == "text" || s == "undefs" ||
- s == "wxneeded" || s.startswith("common-page-size=") ||
+ return llvm::is_contained(knownZFlags, s) ||
+ s.startswith("common-page-size=") || s.startswith("bti-report=") ||
+ s.startswith("cet-report=") ||
s.startswith("dead-reloc-in-nonalloc=") ||
s.startswith("max-page-size=") || s.startswith("stack-size=") ||
s.startswith("start-stop-visibility=");
}
-// Report an error for an unknown -z option.
+// Report a warning for an unknown -z option.
static void checkZOptions(opt::InputArgList &args) {
for (auto *arg : args.filtered(OPT_z))
if (!isKnownZFlag(arg->getValue()))
- error("unknown -z value: " + StringRef(arg->getValue()));
+ warn("unknown -z value: " + StringRef(arg->getValue()));
}
+constexpr const char *saveTempsValues[] = {
+ "resolution", "preopt", "promote", "internalize", "import",
+ "opt", "precodegen", "prelink", "combinedindex"};
+
void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
ELFOptTable parser;
opt::InputArgList args = parser.parse(argsArr.slice(1));
- // Interpret this flag early because error() depends on them.
+ // Interpret these flags early because error()/warn() depend on them.
errorHandler().errorLimit = args::getInteger(args, OPT_error_limit, 20);
+ errorHandler().fatalWarnings =
+ args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false) &&
+ !args.hasArg(OPT_no_warnings);
+ errorHandler().suppressWarnings = args.hasArg(OPT_no_warnings);
checkZOptions(args);
// Handle -help
// Handle -v or -version.
//
// A note about "compatible with GNU linkers" message: this is a hack for
- // scripts generated by GNU Libtool 2.4.6 (released in February 2014 and
- // still the newest version in March 2017) or earlier to recognize LLD as
- // a GNU compatible linker. As long as an output for the -v option
- // contains "GNU" or "with BFD", they recognize us as GNU-compatible.
+ // scripts generated by GNU Libtool up to 2021-10 to recognize LLD as
+ // a GNU compatible linker. See
+ // <https://lists.gnu.org/archive/html/libtool/2017-01/msg00007.html>.
//
// This is somewhat ugly hack, but in reality, we had no choice other
// than doing this. Considering the very long release cycle of Libtool,
if (errorCount())
return;
- // The Target instance handles target-specific stuff, such as applying
- // relocations or writing a PLT section. It also contains target-dependent
- // values such as a default image base address.
- target = getTarget();
-
- switch (config->ekind) {
- case ELF32LEKind:
- link<ELF32LE>(args);
- break;
- case ELF32BEKind:
- link<ELF32BE>(args);
- break;
- case ELF64LEKind:
- link<ELF64LE>(args);
- break;
- case ELF64BEKind:
- link<ELF64BE>(args);
- break;
- default:
- llvm_unreachable("unknown Config->EKind");
- }
+ link(args);
}
if (config->timeTraceEnabled) {
- if (auto E = timeTraceProfilerWrite(args.getLastArgValue(OPT_time_trace_file_eq).str(),
- config->outputFile)) {
- handleAllErrors(std::move(E), [&](const StringError &SE) {
- error(SE.getMessage());
- });
- return;
- }
-
+ checkError(timeTraceProfilerWrite(
+ args.getLastArgValue(OPT_time_trace_eq).str(), config->outputFile));
timeTraceProfilerCleanup();
}
}
static std::string getRpath(opt::InputArgList &args) {
- std::vector<StringRef> v = args::getStrings(args, OPT_rpath);
+ SmallVector<StringRef, 0> v = args::getStrings(args, OPT_rpath);
return llvm::join(v.begin(), v.end(), ":");
}
OPT_warn_unresolved_symbols, true)
? UnresolvedPolicy::ReportError
: UnresolvedPolicy::Warn;
- // -shared implies -unresolved-symbols=ignore-all because missing
+ // -shared implies --unresolved-symbols=ignore-all because missing
// symbols are likely to be resolved at runtime.
bool diagRegular = !config->shared, diagShlib = !config->shared;
return arg->getValue();
}
+static int getMemtagMode(opt::InputArgList &args) {
+ StringRef memtagModeArg = args.getLastArgValue(OPT_android_memtag_mode);
+ if (!config->androidMemtagHeap && !config->androidMemtagStack) {
+ if (!memtagModeArg.empty())
+ error("when using --android-memtag-mode, at least one of "
+ "--android-memtag-heap or "
+ "--android-memtag-stack is required");
+ return ELF::NT_MEMTAG_LEVEL_NONE;
+ }
+
+ if (memtagModeArg == "sync" || memtagModeArg.empty())
+ return ELF::NT_MEMTAG_LEVEL_SYNC;
+ if (memtagModeArg == "async")
+ return ELF::NT_MEMTAG_LEVEL_ASYNC;
+ if (memtagModeArg == "none")
+ return ELF::NT_MEMTAG_LEVEL_NONE;
+
+ error("unknown --android-memtag-mode value: \"" + memtagModeArg +
+ "\", should be one of {async, sync, none}");
+ return ELF::NT_MEMTAG_LEVEL_NONE;
+}
+
static ICFLevel getICF(opt::InputArgList &args) {
auto *arg = args.getLastArg(OPT_icf_none, OPT_icf_safe, OPT_icf_all);
if (!arg || arg->getOption().getID() == OPT_icf_none)
return OrphanHandlingPolicy::Place;
}
-// Parses --power10-stubs= flags, to disable or enable Power 10
-// instructions in stubs.
-static bool getP10StubOpt(opt::InputArgList &args) {
-
- if (args.getLastArgValue(OPT_power10_stubs_eq)== "no")
- return false;
-
- if (!args.hasArg(OPT_power10_stubs_eq) &&
- args.hasArg(OPT_no_power10_stubs))
- return false;
-
- return true;
-}
-
// Parse --build-id or --build-id=<style>. We handle "tree" as a
// synonym for "sha1" because all our hash functions including
-// -build-id=sha1 are actually tree hashes for performance reasons.
-static std::pair<BuildIdKind, std::vector<uint8_t>>
+// --build-id=sha1 are actually tree hashes for performance reasons.
+static std::pair<BuildIdKind, SmallVector<uint8_t, 0>>
getBuildId(opt::InputArgList &args) {
- auto *arg = args.getLastArg(OPT_build_id, OPT_build_id_eq);
+ auto *arg = args.getLastArg(OPT_build_id);
if (!arg)
return {BuildIdKind::None, {}};
- if (arg->getOption().getID() == OPT_build_id)
- return {BuildIdKind::Fast, {}};
-
StringRef s = arg->getValue();
if (s == "fast")
return {BuildIdKind::Fast, {}};
return {true, true};
if (s != "none")
- error("unknown -pack-dyn-relocs format: " + s);
+ error("unknown --pack-dyn-relocs format: " + s);
return {false, false};
}
static void readCallGraph(MemoryBufferRef mb) {
// Build a map from symbol name to section
DenseMap<StringRef, Symbol *> map;
- for (InputFile *file : objectFiles)
+ for (ELFFileBase *file : ctx.objectFiles)
for (Symbol *sym : file->getSymbols())
map[sym->getName()] = sym;
processCallGraphRelocations(SmallVector<uint32_t, 32> &symbolIndices,
ArrayRef<typename ELFT::CGProfile> &cgProfile,
ObjFile<ELFT> *inputObj) {
- symbolIndices.clear();
- const ELFFile<ELFT> &obj = inputObj->getObj();
- ArrayRef<Elf_Shdr_Impl<ELFT>> objSections =
- CHECK(obj.sections(), "could not retrieve object sections");
-
if (inputObj->cgProfileSectionIndex == SHN_UNDEF)
return false;
+ ArrayRef<Elf_Shdr_Impl<ELFT>> objSections =
+ inputObj->template getELFShdrs<ELFT>();
+ symbolIndices.clear();
+ const ELFFile<ELFT> &obj = inputObj->getObj();
cgProfile =
check(obj.template getSectionContentsAsArray<typename ELFT::CGProfile>(
objSections[inputObj->cgProfileSectionIndex]));
template <class ELFT> static void readCallGraphsFromObjectFiles() {
SmallVector<uint32_t, 32> symbolIndices;
ArrayRef<typename ELFT::CGProfile> cgProfile;
- for (auto file : objectFiles) {
+ for (auto file : ctx.objectFiles) {
auto *obj = cast<ObjFile<ELFT>>(file);
if (!processCallGraphRelocations(symbolIndices, cgProfile, obj))
continue;
}
}
-static bool getCompressDebugSections(opt::InputArgList &args) {
+static DebugCompressionType getCompressDebugSections(opt::InputArgList &args) {
StringRef s = args.getLastArgValue(OPT_compress_debug_sections, "none");
- if (s == "none")
- return false;
- if (s != "zlib")
+ if (s == "zlib") {
+ if (!compression::zlib::isAvailable())
+ error("--compress-debug-sections: zlib is not available");
+ return DebugCompressionType::Zlib;
+ }
+ if (s == "zstd") {
+ if (!compression::zstd::isAvailable())
+ error("--compress-debug-sections: zstd is not available");
+ return DebugCompressionType::Zstd;
+ }
+ if (s != "none")
error("unknown --compress-debug-sections value: " + s);
- if (!zlib::isAvailable())
- error("--compress-debug-sections: zlib is not available");
- return true;
+ return DebugCompressionType::None;
}
static StringRef getAliasSpelling(opt::Arg *arg) {
}
// Parse the symbol ordering file and warn for any duplicate entries.
-static std::vector<StringRef> getSymbolOrderingFile(MemoryBufferRef mb) {
- SetVector<StringRef> names;
+static SmallVector<StringRef, 0> getSymbolOrderingFile(MemoryBufferRef mb) {
+ SetVector<StringRef, SmallVector<StringRef, 0>> names;
for (StringRef s : args::getLines(mb))
if (!names.insert(s) && config->warnSymbolOrdering)
warn(mb.getBufferIdentifier() + ": duplicate ordered symbol: " + s);
error(msg + ": " + StringRef(err).trim());
}
+// Checks the parameter of the bti-report and cet-report options.
+static bool isValidReportString(StringRef arg) {
+ return arg == "none" || arg == "warning" || arg == "error";
+}
+
// Initializes Config members by the command line options.
static void readConfigs(opt::InputArgList &args) {
errorHandler().verbose = args.hasArg(OPT_verbose);
- errorHandler().fatalWarnings =
- args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false);
errorHandler().vsDiagnostics =
args.hasArg(OPT_visual_studio_diagnostics_format, false);
args.hasFlag(OPT_allow_multiple_definition,
OPT_no_allow_multiple_definition, false) ||
hasZOption(args, "muldefs");
+ config->androidMemtagHeap =
+ args.hasFlag(OPT_android_memtag_heap, OPT_no_android_memtag_heap, false);
+ config->androidMemtagStack = args.hasFlag(OPT_android_memtag_stack,
+ OPT_no_android_memtag_stack, false);
+ config->androidMemtagMode = getMemtagMode(args);
config->auxiliaryList = args::getStrings(args, OPT_auxiliary);
if (opt::Arg *arg =
args.getLastArg(OPT_Bno_symbolic, OPT_Bsymbolic_non_weak_functions,
config->chroot = args.getLastArgValue(OPT_chroot);
config->compressDebugSections = getCompressDebugSections(args);
config->cref = args.hasArg(OPT_cref);
- config->defineCommon = args.hasFlag(OPT_define_common, OPT_no_define_common,
- !args.hasArg(OPT_relocatable));
config->optimizeBBJumps =
args.hasFlag(OPT_optimize_bb_jumps, OPT_no_optimize_bb_jumps, false);
config->demangle = args.hasFlag(OPT_demangle, OPT_no_demangle, true);
args.getLastArgValue(OPT_error_handling_script);
config->exportDynamic =
- args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, false);
+ args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, false) ||
+ args.hasArg(OPT_shared);
config->filterList = args::getStrings(args, OPT_filter);
config->fini = args.getLastArgValue(OPT_fini, "_fini");
config->fixCortexA53Errata843419 = args.hasArg(OPT_fix_cortex_a53_843419) &&
config->fixCortexA8 =
args.hasArg(OPT_fix_cortex_a8) && !args.hasArg(OPT_relocatable);
config->fortranCommon =
- args.hasFlag(OPT_fortran_common, OPT_no_fortran_common, true);
+ args.hasFlag(OPT_fortran_common, OPT_no_fortran_common, false);
config->gcSections = args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, false);
config->gnuUnique = args.hasFlag(OPT_gnu_unique, OPT_no_gnu_unique, true);
config->gdbIndex = args.hasFlag(OPT_gdb_index, OPT_no_gdb_index, false);
config->ignoreDataAddressEquality =
args.hasArg(OPT_ignore_data_address_equality);
config->ignoreFunctionAddressEquality =
- args.hasFlag(OPT_ignore_function_address_equality,
- OPT_no_ignore_function_address_equality, true);
+ args.hasArg(OPT_ignore_function_address_equality);
config->init = args.getLastArgValue(OPT_init, "_init");
config->ltoAAPipeline = args.getLastArgValue(OPT_lto_aa_pipeline);
config->ltoCSProfileGenerate = args.hasArg(OPT_lto_cs_profile_generate);
config->ltoCSProfileFile = args.getLastArgValue(OPT_lto_cs_profile_file);
+ config->ltoPGOWarnMismatch = args.hasFlag(OPT_lto_pgo_warn_mismatch,
+ OPT_no_lto_pgo_warn_mismatch, true);
config->ltoDebugPassManager = args.hasArg(OPT_lto_debug_pass_manager);
config->ltoEmitAsm = args.hasArg(OPT_lto_emit_asm);
- config->ltoNewPassManager =
- args.hasFlag(OPT_no_lto_legacy_pass_manager, OPT_lto_legacy_pass_manager,
- LLVM_ENABLE_NEW_PASS_MANAGER);
config->ltoNewPmPasses = args.getLastArgValue(OPT_lto_newpm_passes);
config->ltoWholeProgramVisibility =
args.hasFlag(OPT_lto_whole_program_visibility,
config->ltoo = args::getInteger(args, OPT_lto_O, 2);
config->ltoObjPath = args.getLastArgValue(OPT_lto_obj_path_eq);
config->ltoPartitions = args::getInteger(args, OPT_lto_partitions, 1);
- config->ltoPseudoProbeForProfiling =
- args.hasArg(OPT_lto_pseudo_probe_for_profiling);
config->ltoSampleProfile = args.getLastArgValue(OPT_lto_sample_profile);
config->ltoBasicBlockSections =
args.getLastArgValue(OPT_lto_basic_block_sections);
config->nostdlib = args.hasArg(OPT_nostdlib);
config->oFormatBinary = isOutputFormatBinary(args);
config->omagic = args.hasFlag(OPT_omagic, OPT_no_omagic, false);
+ config->opaquePointers = args.hasFlag(
+ OPT_plugin_opt_opaque_pointers, OPT_plugin_opt_no_opaque_pointers, true);
config->optRemarksFilename = args.getLastArgValue(OPT_opt_remarks_filename);
+ config->optStatsFilename = args.getLastArgValue(OPT_plugin_opt_stats_file);
// Parse remarks hotness threshold. Valid value is either integer or 'auto'.
if (auto *arg = args.getLastArg(OPT_opt_remarks_hotness_threshold)) {
config->optimize = args::getInteger(args, OPT_O, 1);
config->orphanHandling = getOrphanHandling(args);
config->outputFile = args.getLastArgValue(OPT_o);
+ config->packageMetadata = args.getLastArgValue(OPT_package_metadata);
#ifdef __OpenBSD__
config->pie = args.hasFlag(OPT_pie, OPT_no_pie,
!args.hasArg(OPT_shared) && !args.hasArg(OPT_relocatable));
config->printArchiveStats = args.getLastArgValue(OPT_print_archive_stats);
config->printSymbolOrder =
args.getLastArgValue(OPT_print_symbol_order);
+ config->relax = args.hasFlag(OPT_relax, OPT_no_relax, true);
config->rpath = getRpath(args);
config->relocatable = args.hasArg(OPT_relocatable);
- config->saveTemps = args.hasArg(OPT_save_temps);
+
+ if (args.hasArg(OPT_save_temps)) {
+ // --save-temps implies saving all temps.
+ for (const char *s : saveTempsValues)
+ config->saveTempsArgs.insert(s);
+ } else {
+ for (auto *arg : args.filtered(OPT_save_temps_eq)) {
+ StringRef s = arg->getValue();
+ if (llvm::is_contained(saveTempsValues, s))
+ config->saveTempsArgs.insert(s);
+ else
+ error("unknown --save-temps value: " + s);
+ }
+ }
+
config->searchPaths = args::getStrings(args, OPT_library_path);
config->sectionStartMap = getSectionStartMap(args);
config->shared = args.hasArg(OPT_shared);
parseCachePruningPolicy(args.getLastArgValue(OPT_thinlto_cache_policy)),
"--thinlto-cache-policy: invalid cache policy");
config->thinLTOEmitImportsFiles = args.hasArg(OPT_thinlto_emit_imports_files);
+ config->thinLTOEmitIndexFiles = args.hasArg(OPT_thinlto_emit_index_files) ||
+ args.hasArg(OPT_thinlto_index_only) ||
+ args.hasArg(OPT_thinlto_index_only_eq);
config->thinLTOIndexOnly = args.hasArg(OPT_thinlto_index_only) ||
args.hasArg(OPT_thinlto_index_only_eq);
config->thinLTOIndexOnlyArg = args.getLastArgValue(OPT_thinlto_index_only_eq);
getOldNewOptions(args, OPT_thinlto_object_suffix_replace_eq);
config->thinLTOPrefixReplace =
getOldNewOptions(args, OPT_thinlto_prefix_replace_eq);
+ if (config->thinLTOEmitIndexFiles && !config->thinLTOIndexOnly) {
+ if (args.hasArg(OPT_thinlto_object_suffix_replace_eq))
+ error("--thinlto-object-suffix-replace is not supported with "
+ "--thinlto-emit-index-files");
+ else if (args.hasArg(OPT_thinlto_prefix_replace_eq))
+ error("--thinlto-prefix-replace is not supported with "
+ "--thinlto-emit-index-files");
+ }
config->thinLTOModulesToCompile =
args::getStrings(args, OPT_thinlto_single_module_eq);
- config->timeTraceEnabled = args.hasArg(OPT_time_trace);
+ config->timeTraceEnabled = args.hasArg(OPT_time_trace_eq);
config->timeTraceGranularity =
args::getInteger(args, OPT_time_trace_granularity, 500);
config->trace = args.hasArg(OPT_trace);
config->undefined = args::getStrings(args, OPT_undefined);
config->undefinedVersion =
- args.hasFlag(OPT_undefined_version, OPT_no_undefined_version, true);
+ args.hasFlag(OPT_undefined_version, OPT_no_undefined_version, false);
config->unique = args.hasArg(OPT_unique);
config->useAndroidRelrTags = args.hasFlag(
OPT_use_android_relr_tags, OPT_no_use_android_relr_tags, false);
config->warnCommon = args.hasFlag(OPT_warn_common, OPT_no_warn_common, false);
config->warnSymbolOrdering =
args.hasFlag(OPT_warn_symbol_ordering, OPT_no_warn_symbol_ordering, true);
+ config->whyExtract = args.getLastArgValue(OPT_why_extract);
config->zCombreloc = getZFlag(args, "combreloc", "nocombreloc", true);
config->zCopyreloc = getZFlag(args, "copyreloc", "nocopyreloc", true);
config->zForceBti = hasZOption(args, "force-bti");
config->zText = getZFlag(args, "text", "notext", true);
config->zWxneeded = hasZOption(args, "wxneeded");
setUnresolvedSymbolPolicy(args);
- config->Power10Stub = getP10StubOpt(args);
+ config->power10Stubs = args.getLastArgValue(OPT_power10_stubs_eq) != "no";
if (opt::Arg *arg = args.getLastArg(OPT_eb, OPT_el)) {
if (arg->getOption().matches(OPT_eb))
error(errPrefix + toString(pat.takeError()));
}
+ auto reports = {std::make_pair("bti-report", &config->zBtiReport),
+ std::make_pair("cet-report", &config->zCetReport)};
+ for (opt::Arg *arg : args.filtered(OPT_z)) {
+ std::pair<StringRef, StringRef> option =
+ StringRef(arg->getValue()).split('=');
+ for (auto reportArg : reports) {
+ if (option.first != reportArg.first)
+ continue;
+ if (!isValidReportString(option.second)) {
+ error(Twine("-z ") + reportArg.first + "= parameter " + option.second +
+ " is not recognized");
+ continue;
+ }
+ *reportArg.second = option.second;
+ }
+ }
+
for (opt::Arg *arg : args.filtered(OPT_z)) {
std::pair<StringRef, StringRef> option =
StringRef(arg->getValue()).split('=');
// Parse LTO options.
if (auto *arg = args.getLastArg(OPT_plugin_opt_mcpu_eq))
- parseClangOption(saver.save("-mcpu=" + StringRef(arg->getValue())),
+ parseClangOption(saver().save("-mcpu=" + StringRef(arg->getValue())),
arg->getSpelling());
for (opt::Arg *arg : args.filtered(OPT_plugin_opt_eq_minus))
parseClangOption(std::string("-") + arg->getValue(), arg->getSpelling());
// GCC collect2 passes -plugin-opt=path/to/lto-wrapper with an absolute or
- // relative path. Just ignore. If not ended with "lto-wrapper", consider it an
+ // relative path. Just ignore. If not ended with "lto-wrapper" (or
+ // "lto-wrapper.exe" for GCC cross-compiled for Windows), consider it an
// unsupported LLVMgold.so option and error.
- for (opt::Arg *arg : args.filtered(OPT_plugin_opt_eq))
- if (!StringRef(arg->getValue()).endswith("lto-wrapper"))
+ for (opt::Arg *arg : args.filtered(OPT_plugin_opt_eq)) {
+ StringRef v(arg->getValue());
+ if (!v.endswith("lto-wrapper") && !v.endswith("lto-wrapper.exe"))
error(arg->getSpelling() + ": unknown plugin option '" + arg->getValue() +
"'");
+ }
+
+ config->passPlugins = args::getStrings(args, OPT_load_pass_plugins);
// Parse -mllvm options.
- for (auto *arg : args.filtered(OPT_mllvm))
+ for (const auto *arg : args.filtered(OPT_mllvm)) {
parseClangOption(arg->getValue(), arg->getSpelling());
+ config->mllvmOpts.emplace_back(arg->getValue());
+ }
// --threads= takes a positive integer and provides the default value for
// --thinlto-jobs=.
parallel::strategy = hardware_concurrency(threads);
config->thinLTOJobs = v;
}
- if (auto *arg = args.getLastArg(OPT_thinlto_jobs))
+ if (auto *arg = args.getLastArg(OPT_thinlto_jobs_eq))
config->thinLTOJobs = arg->getValue();
+ config->threadCount = parallel::strategy.compute_thread_count();
if (config->ltoo > 3)
error("invalid optimization level for LTO: " + Twine(config->ltoo));
config->emulation = s;
}
- // Parse -hash-style={sysv,gnu,both}.
+ // Parse --hash-style={sysv,gnu,both}.
if (auto *arg = args.getLastArg(OPT_hash_style)) {
StringRef s = arg->getValue();
if (s == "sysv")
else if (s == "both")
config->sysvHash = config->gnuHash = true;
else
- error("unknown -hash-style: " + s);
+ error("unknown --hash-style: " + s);
}
if (args.hasArg(OPT_print_map))
std::tie(config->buildId, config->buildIdVector) = getBuildId(args);
- std::tie(config->androidPackDynRelocs, config->relrPackDynRelocs) =
- getPackDynRelocs(args);
+ if (getZFlag(args, "pack-relative-relocs", "nopack-relative-relocs", false)) {
+ config->relrGlibc = true;
+ config->relrPackDynRelocs = true;
+ } else {
+ std::tie(config->androidPackDynRelocs, config->relrPackDynRelocs) =
+ getPackDynRelocs(args);
+ }
if (auto *arg = args.getLastArg(OPT_symbol_ordering_file)){
if (args.hasArg(OPT_call_graph_ordering_file))
error("--symbol-ordering-file and --call-graph-order-file "
"may not be used together");
- if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue())){
+ if (std::optional<MemoryBufferRef> buffer = readFile(arg->getValue())) {
config->symbolOrderingFile = getSymbolOrderingFile(*buffer);
// Also need to disable CallGraphProfileSort to prevent
// LLD order symbols with CGProfile
if (auto *arg = args.getLastArg(OPT_retain_symbols_file)) {
config->versionDefinitions[VER_NDX_LOCAL].nonLocalPatterns.push_back(
{"*", /*isExternCpp=*/false, /*hasWildcard=*/true});
- if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue()))
+ if (std::optional<MemoryBufferRef> buffer = readFile(arg->getValue()))
for (StringRef s : args::getLines(*buffer))
config->versionDefinitions[VER_NDX_GLOBAL].nonLocalPatterns.push_back(
{s, /*isExternCpp=*/false, /*hasWildcard=*/false});
error(arg->getSpelling() + ": " + toString(pat.takeError()));
}
- // When producing an executable, --dynamic-list specifies non-local defined
- // symbols which are required to be exported. When producing a shared object,
- // symbols not specified by --dynamic-list are non-preemptible.
- config->symbolic =
- config->bsymbolic == BsymbolicKind::All || args.hasArg(OPT_dynamic_list);
- for (auto *arg : args.filtered(OPT_dynamic_list))
- if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue()))
- readDynamicList(*buffer);
-
- // --export-dynamic-symbol specifies additional --dynamic-list symbols if any
- // other option expresses a symbolic intention: -no-pie, -pie, -Bsymbolic,
+ // For -no-pie and -pie, --export-dynamic-symbol specifies defined symbols
+ // which should be exported. For -shared, references to matched non-local
+ // STV_DEFAULT symbols are not bound to definitions within the shared object,
+ // even if other options express a symbolic intention: -Bsymbolic,
// -Bsymbolic-functions (if STT_FUNC), --dynamic-list.
for (auto *arg : args.filtered(OPT_export_dynamic_symbol))
config->dynamicList.push_back(
{arg->getValue(), /*isExternCpp=*/false,
/*hasWildcard=*/hasWildcard(arg->getValue())});
+ // --export-dynamic-symbol-list specifies a list of --export-dynamic-symbol
+ // patterns. --dynamic-list is --export-dynamic-symbol-list plus -Bsymbolic
+ // like semantics.
+ config->symbolic =
+ config->bsymbolic == BsymbolicKind::All || args.hasArg(OPT_dynamic_list);
+ for (auto *arg :
+ args.filtered(OPT_dynamic_list, OPT_export_dynamic_symbol_list))
+ if (std::optional<MemoryBufferRef> buffer = readFile(arg->getValue()))
+ readDynamicList(*buffer);
+
for (auto *arg : args.filtered(OPT_version_script))
- if (Optional<std::string> path = searchScript(arg->getValue())) {
- if (Optional<MemoryBufferRef> buffer = readFile(*path))
+ if (std::optional<std::string> path = searchScript(arg->getValue())) {
+ if (std::optional<MemoryBufferRef> buffer = readFile(*path))
readVersionScript(*buffer);
} else {
error(Twine("cannot find version script ") + arg->getValue());
// enable the debug checks for all targets, but currently not all targets
// have support for reading Elf_Rel addends, so we only enable for a subset.
#ifndef NDEBUG
- bool checkDynamicRelocsDefault = m == EM_ARM || m == EM_386 || m == EM_MIPS ||
+ bool checkDynamicRelocsDefault = m == EM_AARCH64 || m == EM_ARM ||
+ m == EM_386 || m == EM_MIPS ||
m == EM_X86_64 || m == EM_RISCV;
#else
bool checkDynamicRelocsDefault = false;
args.hasFlag(OPT_execute_only, OPT_no_execute_only, config->executeOnly);
}
-// Returns a value of "-format" option.
static bool isFormatBinary(StringRef s) {
if (s == "binary")
return true;
if (s == "elf" || s == "default")
return false;
- error("unknown -format value: " + s +
+ error("unknown --format value: " + s +
" (supported formats: elf, default, binary)");
return false;
}
// Iterate over argv to process input files and positional arguments.
InputFile::isInGroup = false;
+ bool hasInput = false;
for (auto *arg : args) {
switch (arg->getOption().getID()) {
case OPT_library:
addLibrary(arg->getValue());
+ hasInput = true;
break;
case OPT_INPUT:
addFile(arg->getValue(), /*withLOption=*/false);
+ hasInput = true;
break;
case OPT_defsym: {
StringRef from;
StringRef to;
std::tie(from, to) = StringRef(arg->getValue()).split('=');
if (from.empty() || to.empty())
- error("-defsym: syntax error: " + StringRef(arg->getValue()));
+ error("--defsym: syntax error: " + StringRef(arg->getValue()));
else
- readDefsym(from, MemoryBufferRef(to, "-defsym"));
+ readDefsym(from, MemoryBufferRef(to, "--defsym"));
break;
}
case OPT_script:
- if (Optional<std::string> path = searchScript(arg->getValue())) {
- if (Optional<MemoryBufferRef> mb = readFile(*path))
+ if (std::optional<std::string> path = searchScript(arg->getValue())) {
+ if (std::optional<MemoryBufferRef> mb = readFile(*path))
readLinkerScript(*mb);
break;
}
inWholeArchive = false;
break;
case OPT_just_symbols:
- if (Optional<MemoryBufferRef> mb = readFile(arg->getValue())) {
- files.push_back(createObjectFile(*mb));
+ if (std::optional<MemoryBufferRef> mb = readFile(arg->getValue())) {
+ files.push_back(createObjFile(*mb));
files.back()->justSymbols = true;
}
break;
}
}
- if (files.empty() && errorCount() == 0)
+ if (files.empty() && !hasInput && errorCount() == 0)
error("no input files");
}
static uint64_t getMaxPageSize(opt::InputArgList &args) {
uint64_t val = args::getZOptionValue(args, OPT_z, "max-page-size",
target->defaultMaxPageSize);
- if (!isPowerOf2_64(val))
+ if (!isPowerOf2_64(val)) {
error("max-page-size: value isn't a power of 2");
+ return target->defaultMaxPageSize;
+ }
if (config->nmagic || config->omagic) {
if (val != target->defaultMaxPageSize)
warn("-z max-page-size set, but paging disabled by omagic or nmagic");
static uint64_t getCommonPageSize(opt::InputArgList &args) {
uint64_t val = args::getZOptionValue(args, OPT_z, "common-page-size",
target->defaultCommonPageSize);
- if (!isPowerOf2_64(val))
+ if (!isPowerOf2_64(val)) {
error("common-page-size: value isn't a power of 2");
+ return target->defaultCommonPageSize;
+ }
if (config->nmagic || config->omagic) {
if (val != target->defaultCommonPageSize)
warn("-z common-page-size set, but paging disabled by omagic or nmagic");
return val;
}
-// Parses -image-base option.
-static Optional<uint64_t> getImageBase(opt::InputArgList &args) {
+// Parses --image-base option.
+static std::optional<uint64_t> getImageBase(opt::InputArgList &args) {
// Because we are using "Config->maxPageSize" here, this function has to be
// called after the variable is initialized.
auto *arg = args.getLastArg(OPT_image_base);
if (!arg)
- return None;
+ return std::nullopt;
StringRef s = arg->getValue();
uint64_t v;
if (!to_integer(s, v)) {
- error("-image-base: number expected, but got " + s);
+ error("--image-base: number expected, but got " + s);
return 0;
}
if ((v % config->maxPageSize) != 0)
- warn("-image-base: address isn't multiple of page size: " + s);
+ warn("--image-base: address isn't multiple of page size: " + s);
return v;
}
return ret;
}
-// Handles the -exclude-libs option. If a static library file is specified
-// by the -exclude-libs option, all public symbols from the archive become
+// Handles the --exclude-libs option. If a static library file is specified
+// by the --exclude-libs option, all public symbols from the archive become
// private unless otherwise specified by version scripts or something.
// A special library name "ALL" means all archive files.
//
bool all = libs.count("ALL");
auto visit = [&](InputFile *file) {
- if (!file->archiveName.empty())
- if (all || libs.count(path::filename(file->archiveName)))
- for (Symbol *sym : file->getSymbols())
- if (!sym->isUndefined() && !sym->isLocal() && sym->file == file)
- sym->versionId = VER_NDX_LOCAL;
+ if (file->archiveName.empty() ||
+ !(all || libs.count(path::filename(file->archiveName))))
+ return;
+ ArrayRef<Symbol *> symbols = file->getSymbols();
+ if (isa<ELFFileBase>(file))
+ symbols = cast<ELFFileBase>(file)->getGlobalSymbols();
+ for (Symbol *sym : symbols)
+ if (!sym->isUndefined() && sym->file == file)
+ sym->versionId = VER_NDX_LOCAL;
};
- for (InputFile *file : objectFiles)
+ for (ELFFileBase *file : ctx.objectFiles)
visit(file);
- for (BitcodeFile *file : bitcodeFiles)
+ for (BitcodeFile *file : ctx.bitcodeFiles)
visit(file);
}
// Force Sym to be entered in the output.
-static void handleUndefined(Symbol *sym) {
+static void handleUndefined(Symbol *sym, const char *option) {
// Since a symbol may not be used inside the program, LTO may
// eliminate it. Mark the symbol as "used" to prevent it.
sym->isUsedInRegularObj = true;
- if (sym->isLazy())
- sym->fetch();
+ if (!sym->isLazy())
+ return;
+ sym->extract();
+ if (!config->whyExtract.empty())
+ ctx.whyExtractRecords.emplace_back(option, sym->file, *sym);
}
// As an extension to GNU linkers, lld supports a variant of `-u`
return;
}
- std::vector<Symbol *> syms;
- for (Symbol *sym : symtab->symbols()) {
- // Calling Sym->fetch() from here is not safe because it may
- // add new symbols to the symbol table, invalidating the
- // current iterator. So we just keep a note.
- if (pat->match(sym->getName()))
+ // Calling sym->extract() in the loop is not safe because it may add new
+ // symbols to the symbol table, invalidating the current iterator.
+ SmallVector<Symbol *, 0> syms;
+ for (Symbol *sym : symtab.getSymbols())
+ if (!sym->isPlaceholder() && pat->match(sym->getName()))
syms.push_back(sym);
- }
for (Symbol *sym : syms)
- handleUndefined(sym);
+ handleUndefined(sym, "--undefined-glob");
}
static void handleLibcall(StringRef name) {
- Symbol *sym = symtab->find(name);
+ Symbol *sym = symtab.find(name);
if (!sym || !sym->isLazy())
return;
MemoryBufferRef mb;
- if (auto *lo = dyn_cast<LazyObject>(sym))
- mb = lo->file->mb;
- else
- mb = cast<LazyArchive>(sym)->getMemberBuffer();
+ mb = cast<LazyObject>(sym)->file->mb;
if (isBitcode(mb))
- sym->fetch();
+ sym->extract();
+}
+
+static void writeArchiveStats() {
+ if (config->printArchiveStats.empty())
+ return;
+
+ std::error_code ec;
+ raw_fd_ostream os(config->printArchiveStats, ec, sys::fs::OF_None);
+ if (ec) {
+ error("--print-archive-stats=: cannot open " + config->printArchiveStats +
+ ": " + ec.message());
+ return;
+ }
+
+ os << "members\textracted\tarchive\n";
+
+ SmallVector<StringRef, 0> archives;
+ DenseMap<CachedHashStringRef, unsigned> all, extracted;
+ for (ELFFileBase *file : ctx.objectFiles)
+ if (file->archiveName.size())
+ ++extracted[CachedHashStringRef(file->archiveName)];
+ for (BitcodeFile *file : ctx.bitcodeFiles)
+ if (file->archiveName.size())
+ ++extracted[CachedHashStringRef(file->archiveName)];
+ for (std::pair<StringRef, unsigned> f : ctx.driver.archiveFiles) {
+ unsigned &v = extracted[CachedHashString(f.first)];
+ os << f.second << '\t' << v << '\t' << f.first << '\n';
+ // If the archive occurs multiple times, other instances have a count of 0.
+ v = 0;
+ }
+}
+
+static void writeWhyExtract() {
+ if (config->whyExtract.empty())
+ return;
+
+ std::error_code ec;
+ raw_fd_ostream os(config->whyExtract, ec, sys::fs::OF_None);
+ if (ec) {
+ error("cannot open --why-extract= file " + config->whyExtract + ": " +
+ ec.message());
+ return;
+ }
+
+ os << "reference\textracted\tsymbol\n";
+ for (auto &entry : ctx.whyExtractRecords) {
+ os << std::get<0>(entry) << '\t' << toString(std::get<1>(entry)) << '\t'
+ << toString(std::get<2>(entry)) << '\n';
+ }
+}
+
+static void reportBackrefs() {
+ for (auto &ref : ctx.backwardReferences) {
+ const Symbol &sym = *ref.first;
+ std::string to = toString(ref.second.second);
+ // Some libraries have known problems and can cause noise. Filter them out
+ // with --warn-backrefs-exclude=. The value may look like (for --start-lib)
+ // *.o or (archive member) *.a(*.o).
+ bool exclude = false;
+ for (const llvm::GlobPattern &pat : config->warnBackrefsExclude)
+ if (pat.match(to)) {
+ exclude = true;
+ break;
+ }
+ if (!exclude)
+ warn("backward reference detected: " + sym.getName() + " in " +
+ toString(ref.second.first) + " refers to " + to);
+ }
}
// Handle --dependency-file=<path>. If that option is given, lld creates a
// symbols of type CommonSymbol.
static void replaceCommonSymbols() {
llvm::TimeTraceScope timeScope("Replace common symbols");
- for (Symbol *sym : symtab->symbols()) {
- auto *s = dyn_cast<CommonSymbol>(sym);
- if (!s)
+ for (ELFFileBase *file : ctx.objectFiles) {
+ if (!file->hasCommonSyms)
continue;
+ for (Symbol *sym : file->getGlobalSymbols()) {
+ auto *s = dyn_cast<CommonSymbol>(sym);
+ if (!s)
+ continue;
- auto *bss = make<BssSection>("COMMON", s->size, s->alignment);
- bss->file = s->file;
- bss->markDead();
- inputSections.push_back(bss);
- s->replace(Defined{s->file, s->getName(), s->binding, s->stOther, s->type,
- /*value=*/0, s->size, bss});
+ auto *bss = make<BssSection>("COMMON", s->size, s->alignment);
+ bss->file = s->file;
+ ctx.inputSections.push_back(bss);
+ Defined(s->file, StringRef(), s->binding, s->stOther, s->type,
+ /*value=*/0, s->size, bss)
+ .overwrite(*s);
+ }
}
}
-// If all references to a DSO happen to be weak, the DSO is not added
-// to DT_NEEDED. If that happens, we need to eliminate shared symbols
-// created from the DSO. Otherwise, they become dangling references
-// that point to a non-existent DSO.
-static void demoteSharedSymbols() {
- llvm::TimeTraceScope timeScope("Demote shared symbols");
- for (Symbol *sym : symtab->symbols()) {
+// If all references to a DSO happen to be weak, the DSO is not added to
+// DT_NEEDED. If that happens, replace ShardSymbol with Undefined to avoid
+// dangling references to an unneeded DSO. Use a weak binding to avoid
+// --no-allow-shlib-undefined diagnostics. Similarly, demote lazy symbols.
+static void demoteSharedAndLazySymbols() {
+ llvm::TimeTraceScope timeScope("Demote shared and lazy symbols");
+ for (Symbol *sym : symtab.getSymbols()) {
auto *s = dyn_cast<SharedSymbol>(sym);
- if (!s || s->getFile().isNeeded)
+ if (!(s && !cast<SharedFile>(s->file)->isNeeded) && !sym->isLazy())
continue;
- bool used = s->used;
- s->replace(Undefined{nullptr, s->getName(), STB_WEAK, s->stOther, s->type});
- s->used = used;
+ uint8_t binding = sym->isLazy() ? sym->binding : uint8_t(STB_WEAK);
+ Undefined(nullptr, sym->getName(), binding, sym->stOther, sym->type)
+ .overwrite(*sym);
+ sym->versionId = VER_NDX_GLOBAL;
}
}
static void findKeepUniqueSections(opt::InputArgList &args) {
for (auto *arg : args.filtered(OPT_keep_unique)) {
StringRef name = arg->getValue();
- auto *d = dyn_cast_or_null<Defined>(symtab->find(name));
+ auto *d = dyn_cast_or_null<Defined>(symtab.find(name));
if (!d || !d->section) {
warn("could not find symbol " + name + " to keep unique");
continue;
// Symbols in the dynsym could be address-significant in other executables
// or DSOs, so we conservatively mark them as address-significant.
- for (Symbol *sym : symtab->symbols())
+ for (Symbol *sym : symtab.getSymbols())
if (sym->includeInDynsym())
markAddrsig(sym);
// Visit the address-significance table in each object file and mark each
// referenced symbol as address-significant.
- for (InputFile *f : objectFiles) {
+ for (InputFile *f : ctx.objectFiles) {
auto *obj = cast<ObjFile<ELFT>>(f);
ArrayRef<Symbol *> syms = obj->getSymbols();
if (obj->addrsigSec) {
static void readSymbolPartitionSection(InputSectionBase *s) {
// Read the relocation that refers to the partition's entry point symbol.
Symbol *sym;
- if (s->areRelocsRela)
- sym = &s->getFile<ELFT>()->getRelocTargetSym(s->template relas<ELFT>()[0]);
+ const RelsOrRelas<ELFT> rels = s->template relsOrRelas<ELFT>();
+ if (rels.areRelocsRel())
+ sym = &s->getFile<ELFT>()->getRelocTargetSym(rels.rels[0]);
else
- sym = &s->getFile<ELFT>()->getRelocTargetSym(s->template rels<ELFT>()[0]);
+ sym = &s->getFile<ELFT>()->getRelocTargetSym(rels.relas[0]);
if (!isa<Defined>(sym) || !sym->includeInDynsym())
return;
- StringRef partName = reinterpret_cast<const char *>(s->data().data());
+ StringRef partName = reinterpret_cast<const char *>(s->content().data());
for (Partition &part : partitions) {
if (part.name == partName) {
sym->partition = part.getNumber();
sym->partition = newPart.getNumber();
}
-static Symbol *addUndefined(StringRef name) {
- return symtab->addSymbol(
- Undefined{nullptr, name, STB_GLOBAL, STV_DEFAULT, 0});
-}
-
static Symbol *addUnusedUndefined(StringRef name,
uint8_t binding = STB_GLOBAL) {
- Undefined sym{nullptr, name, binding, STV_DEFAULT, 0};
- sym.isUsedInRegularObj = false;
- return symtab->addSymbol(sym);
+ return symtab.addSymbol(Undefined{nullptr, name, binding, STV_DEFAULT, 0});
+}
+
+static void markBuffersAsDontNeed(bool skipLinkedOutput) {
+ // With --thinlto-index-only, all buffers are nearly unused from now on
+ // (except symbol/section names used by infrequent passes). Mark input file
+ // buffers as MADV_DONTNEED so that these pages can be reused by the expensive
+ // thin link, saving memory.
+ if (skipLinkedOutput) {
+ for (MemoryBuffer &mb : llvm::make_pointee_range(ctx.memoryBuffers))
+ mb.dontNeedIfMmap();
+ return;
+ }
+
+ // Otherwise, just mark MemoryBuffers backing BitcodeFiles.
+ DenseSet<const char *> bufs;
+ for (BitcodeFile *file : ctx.bitcodeFiles)
+ bufs.insert(file->mb.getBufferStart());
+ for (BitcodeFile *file : ctx.lazyBitcodeFiles)
+ bufs.insert(file->mb.getBufferStart());
+ for (MemoryBuffer &mb : llvm::make_pointee_range(ctx.memoryBuffers))
+ if (bufs.count(mb.getBufferStart()))
+ mb.dontNeedIfMmap();
}
// This function is where all the optimizations of link-time
// using LLVM functions and replaces bitcode symbols with the results.
// Because all bitcode files that the program consists of are passed to
// the compiler at once, it can do a whole-program optimization.
-template <class ELFT> void LinkerDriver::compileBitcodeFiles() {
+template <class ELFT>
+void LinkerDriver::compileBitcodeFiles(bool skipLinkedOutput) {
llvm::TimeTraceScope timeScope("LTO");
// Compile bitcode files and replace bitcode symbols.
lto.reset(new BitcodeCompiler);
- for (BitcodeFile *file : bitcodeFiles)
+ for (BitcodeFile *file : ctx.bitcodeFiles)
lto->add(*file);
+ if (!ctx.bitcodeFiles.empty())
+ markBuffersAsDontNeed(skipLinkedOutput);
+
for (InputFile *file : lto->compile()) {
auto *obj = cast<ObjFile<ELFT>>(file);
obj->parse(/*ignoreComdats=*/true);
// Parse '@' in symbol names for non-relocatable output.
if (!config->relocatable)
for (Symbol *sym : obj->getGlobalSymbols())
- sym->parseSymbolVersion();
- objectFiles.push_back(file);
+ if (sym->hasVersionSuffix)
+ sym->parseSymbolVersion();
+ ctx.objectFiles.push_back(obj);
}
}
// The --wrap option is a feature to rename symbols so that you can write
-// wrappers for existing functions. If you pass `-wrap=foo`, all
+// wrappers for existing functions. If you pass `--wrap=foo`, all
// occurrences of symbol `foo` are resolved to `__wrap_foo` (so, you are
// expected to write `__wrap_foo` function as a wrapper). The original
// symbol becomes accessible as `__real_foo`, so you can call that from your
// wrapper.
//
-// This data structure is instantiated for each -wrap option.
+// This data structure is instantiated for each --wrap option.
struct WrappedSymbol {
Symbol *sym;
Symbol *real;
Symbol *wrap;
};
-// Handles -wrap option.
+// Handles --wrap option.
//
// This function instantiates wrapper symbols. At this point, they seem
// like they are not being used at all, so we explicitly set some flags so
if (!seen.insert(name).second)
continue;
- Symbol *sym = symtab->find(name);
+ Symbol *sym = symtab.find(name);
if (!sym)
continue;
- Symbol *real = addUnusedUndefined(saver.save("__real_" + name));
Symbol *wrap =
- addUnusedUndefined(saver.save("__wrap_" + name), sym->binding);
+ addUnusedUndefined(saver().save("__wrap_" + name), sym->binding);
+
+ // If __real_ is referenced, pull in the symbol if it is lazy. Do this after
+ // processing __wrap_ as that may have referenced __real_.
+ StringRef realName = saver().save("__real_" + name);
+ if (symtab.find(realName))
+ addUnusedUndefined(name, sym->binding);
+
+ Symbol *real = addUnusedUndefined(realName);
v.push_back({sym, real, wrap});
// We want to tell LTO not to inline symbols to be overwritten
// because LTO doesn't know the final symbol contents after renaming.
- real->canInline = false;
- sym->canInline = false;
-
- // Tell LTO not to eliminate these symbols.
- sym->isUsedInRegularObj = true;
- // If sym is referenced in any object file, bitcode file or shared object,
- // retain wrap which is the redirection target of sym. If the object file
- // defining sym has sym references, we cannot easily distinguish the case
- // from cases where sym is not referenced. Retain wrap because we choose to
- // wrap sym references regardless of whether sym is defined
+ real->scriptDefined = true;
+ sym->scriptDefined = true;
+
+ // If a symbol is referenced in any object file, bitcode file or shared
+ // object, mark its redirection target (foo for __real_foo and __wrap_foo
+ // for foo) as referenced after redirection, which will be used to tell LTO
+ // to not eliminate the redirection target. If the object file defining the
+ // symbol also references it, we cannot easily distinguish the case from
+ // cases where the symbol is not referenced. Retain the redirection target
+ // in this case because we choose to wrap symbol references regardless of
+ // whether the symbol is defined
// (https://sourceware.org/bugzilla/show_bug.cgi?id=26358).
+ if (real->referenced || real->isDefined())
+ sym->referencedAfterWrap = true;
if (sym->referenced || sym->isDefined())
- wrap->isUsedInRegularObj = true;
+ wrap->referencedAfterWrap = true;
}
return v;
}
-// Do renaming for -wrap and foo@v1 by updating pointers to symbols.
+static void combineVersionedSymbol(Symbol &sym,
+ DenseMap<Symbol *, Symbol *> &map) {
+ const char *suffix1 = sym.getVersionSuffix();
+ if (suffix1[0] != '@' || suffix1[1] == '@')
+ return;
+
+ // Check the existing symbol foo. We have two special cases to handle:
+ //
+ // * There is a definition of foo@v1 and foo@@v1.
+ // * There is a definition of foo@v1 and foo.
+ Defined *sym2 = dyn_cast_or_null<Defined>(symtab.find(sym.getName()));
+ if (!sym2)
+ return;
+ const char *suffix2 = sym2->getVersionSuffix();
+ if (suffix2[0] == '@' && suffix2[1] == '@' &&
+ strcmp(suffix1 + 1, suffix2 + 2) == 0) {
+ // foo@v1 and foo@@v1 should be merged, so redirect foo@v1 to foo@@v1.
+ map.try_emplace(&sym, sym2);
+ // If both foo@v1 and foo@@v1 are defined and non-weak, report a
+ // duplicate definition error.
+ if (sym.isDefined()) {
+ sym2->checkDuplicate(cast<Defined>(sym));
+ sym2->resolve(cast<Defined>(sym));
+ } else if (sym.isUndefined()) {
+ sym2->resolve(cast<Undefined>(sym));
+ } else {
+ sym2->resolve(cast<SharedSymbol>(sym));
+ }
+ // Eliminate foo@v1 from the symbol table.
+ sym.symbolKind = Symbol::PlaceholderKind;
+ sym.isUsedInRegularObj = false;
+ } else if (auto *sym1 = dyn_cast<Defined>(&sym)) {
+ if (sym2->versionId > VER_NDX_GLOBAL
+ ? config->versionDefinitions[sym2->versionId].name == suffix1 + 1
+ : sym1->section == sym2->section && sym1->value == sym2->value) {
+ // Due to an assembler design flaw, if foo is defined, .symver foo,
+ // foo@v1 defines both foo and foo@v1. Unless foo is bound to a
+ // different version, GNU ld makes foo@v1 canonical and eliminates
+ // foo. Emulate its behavior, otherwise we would have foo or foo@@v1
+ // beside foo@v1. foo@v1 and foo combining does not apply if they are
+ // not defined in the same place.
+ map.try_emplace(sym2, &sym);
+ sym2->symbolKind = Symbol::PlaceholderKind;
+ sym2->isUsedInRegularObj = false;
+ }
+ }
+}
+
+// Do renaming for --wrap and foo@v1 by updating pointers to symbols.
//
// When this function is executed, only InputFiles and symbol table
// contain pointers to symbol objects. We visit them to replace pointers,
map[w.sym] = w.wrap;
map[w.real] = w.sym;
}
- for (Symbol *sym : symtab->symbols()) {
- // Enumerate symbols with a non-default version (foo@v1).
- StringRef name = sym->getName();
- const char *suffix1 = sym->getVersionSuffix();
- if (suffix1[0] != '@' || suffix1[1] == '@')
- continue;
- // Check the existing symbol foo. We have two special cases to handle:
- //
- // * There is a definition of foo@v1 and foo@@v1.
- // * There is a definition of foo@v1 and foo.
- Defined *sym2 = dyn_cast_or_null<Defined>(symtab->find(name));
- if (!sym2)
- continue;
- const char *suffix2 = sym2->getVersionSuffix();
- if (suffix2[0] == '@' && suffix2[1] == '@' &&
- strcmp(suffix1 + 1, suffix2 + 2) == 0) {
- // foo@v1 and foo@@v1 should be merged, so redirect foo@v1 to foo@@v1.
- map.try_emplace(sym, sym2);
- // If both foo@v1 and foo@@v1 are defined and non-weak, report a duplicate
- // definition error.
- sym2->resolve(*sym);
- // Eliminate foo@v1 from the symbol table.
- sym->symbolKind = Symbol::PlaceholderKind;
- } else if (auto *sym1 = dyn_cast<Defined>(sym)) {
- if (sym2->versionId > VER_NDX_GLOBAL
- ? config->versionDefinitions[sym2->versionId].name == suffix1 + 1
- : sym1->section == sym2->section && sym1->value == sym2->value) {
- // Due to an assembler design flaw, if foo is defined, .symver foo,
- // foo@v1 defines both foo and foo@v1. Unless foo is bound to a
- // different version, GNU ld makes foo@v1 canonical and elimiates foo.
- // Emulate its behavior, otherwise we would have foo or foo@@v1 beside
- // foo@v1. foo@v1 and foo combining does not apply if they are not
- // defined in the same place.
- map.try_emplace(sym2, sym);
- sym2->symbolKind = Symbol::PlaceholderKind;
- }
- }
- }
+ // If there are version definitions (versionDefinitions.size() > 2), enumerate
+ // symbols with a non-default version (foo@v1) and check whether it should be
+ // combined with foo or foo@@v1.
+ if (config->versionDefinitions.size() > 2)
+ for (Symbol *sym : symtab.getSymbols())
+ if (sym->hasVersionSuffix)
+ combineVersionedSymbol(*sym, map);
if (map.empty())
return;
// Update pointers in input files.
- parallelForEach(objectFiles, [&](InputFile *file) {
- MutableArrayRef<Symbol *> syms = file->getMutableSymbols();
- for (size_t i = 0, e = syms.size(); i != e; ++i)
- if (Symbol *s = map.lookup(syms[i]))
- syms[i] = s;
+ parallelForEach(ctx.objectFiles, [&](ELFFileBase *file) {
+ for (Symbol *&sym : file->getMutableGlobalSymbols())
+ if (Symbol *s = map.lookup(sym))
+ sym = s;
});
// Update pointers in the symbol table.
for (const WrappedSymbol &w : wrapped)
- symtab->wrap(w.sym, w.real, w.wrap);
+ symtab.wrap(w.sym, w.real, w.wrap);
+}
+
+static void checkAndReportMissingFeature(StringRef config, uint32_t features,
+ uint32_t mask, const Twine &report) {
+ if (!(features & mask)) {
+ if (config == "error")
+ error(report);
+ else if (config == "warning")
+ warn(report);
+ }
}
-// To enable CET (x86's hardware-assited control flow enforcement), each
+// To enable CET (x86's hardware-assisted control flow enforcement), each
// source file must be compiled with -fcf-protection. Object files compiled
// with the flag contain feature flags indicating that they are compatible
// with CET. We enable the feature only when all object files are compatible
//
// This is also the case with AARCH64's BTI and PAC which use the similar
// GNU_PROPERTY_AARCH64_FEATURE_1_AND mechanism.
-template <class ELFT> static uint32_t getAndFeatures() {
+static uint32_t getAndFeatures() {
if (config->emachine != EM_386 && config->emachine != EM_X86_64 &&
config->emachine != EM_AARCH64)
return 0;
uint32_t ret = -1;
- for (InputFile *f : objectFiles) {
- uint32_t features = cast<ObjFile<ELFT>>(f)->andFeatures;
+ for (ELFFileBase *f : ctx.objectFiles) {
+ uint32_t features = f->andFeatures;
+
+ checkAndReportMissingFeature(
+ config->zBtiReport, features, GNU_PROPERTY_AARCH64_FEATURE_1_BTI,
+ toString(f) + ": -z bti-report: file does not have "
+ "GNU_PROPERTY_AARCH64_FEATURE_1_BTI property");
+
+ checkAndReportMissingFeature(
+ config->zCetReport, features, GNU_PROPERTY_X86_FEATURE_1_IBT,
+ toString(f) + ": -z cet-report: file does not have "
+ "GNU_PROPERTY_X86_FEATURE_1_IBT property");
+
+ checkAndReportMissingFeature(
+ config->zCetReport, features, GNU_PROPERTY_X86_FEATURE_1_SHSTK,
+ toString(f) + ": -z cet-report: file does not have "
+ "GNU_PROPERTY_X86_FEATURE_1_SHSTK property");
+
if (config->zForceBti && !(features & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)) {
- warn(toString(f) + ": -z force-bti: file does not have "
- "GNU_PROPERTY_AARCH64_FEATURE_1_BTI property");
features |= GNU_PROPERTY_AARCH64_FEATURE_1_BTI;
+ if (config->zBtiReport == "none")
+ warn(toString(f) + ": -z force-bti: file does not have "
+ "GNU_PROPERTY_AARCH64_FEATURE_1_BTI property");
} else if (config->zForceIbt &&
!(features & GNU_PROPERTY_X86_FEATURE_1_IBT)) {
- warn(toString(f) + ": -z force-ibt: file does not have "
- "GNU_PROPERTY_X86_FEATURE_1_IBT property");
+ if (config->zCetReport == "none")
+ warn(toString(f) + ": -z force-ibt: file does not have "
+ "GNU_PROPERTY_X86_FEATURE_1_IBT property");
features |= GNU_PROPERTY_X86_FEATURE_1_IBT;
}
if (config->zPacPlt && !(features & GNU_PROPERTY_AARCH64_FEATURE_1_PAC)) {
return ret;
}
+static void initSectionsAndLocalSyms(ELFFileBase *file, bool ignoreComdats) {
+ switch (file->ekind) {
+ case ELF32LEKind:
+ cast<ObjFile<ELF32LE>>(file)->initSectionsAndLocalSyms(ignoreComdats);
+ break;
+ case ELF32BEKind:
+ cast<ObjFile<ELF32BE>>(file)->initSectionsAndLocalSyms(ignoreComdats);
+ break;
+ case ELF64LEKind:
+ cast<ObjFile<ELF64LE>>(file)->initSectionsAndLocalSyms(ignoreComdats);
+ break;
+ case ELF64BEKind:
+ cast<ObjFile<ELF64BE>>(file)->initSectionsAndLocalSyms(ignoreComdats);
+ break;
+ default:
+ llvm_unreachable("");
+ }
+}
+
+static void postParseObjectFile(ELFFileBase *file) {
+ switch (file->ekind) {
+ case ELF32LEKind:
+ cast<ObjFile<ELF32LE>>(file)->postParse();
+ break;
+ case ELF32BEKind:
+ cast<ObjFile<ELF32BE>>(file)->postParse();
+ break;
+ case ELF64LEKind:
+ cast<ObjFile<ELF64LE>>(file)->postParse();
+ break;
+ case ELF64BEKind:
+ cast<ObjFile<ELF64BE>>(file)->postParse();
+ break;
+ default:
+ llvm_unreachable("");
+ }
+}
+
// Do actual linking. Note that when this function is called,
// all linker scripts have already been parsed.
-template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
+void LinkerDriver::link(opt::InputArgList &args) {
llvm::TimeTraceScope timeScope("Link", StringRef("LinkerDriver::Link"));
- // If a -hash-style option was not given, set to a default value,
+ // If a --hash-style option was not given, set to a default value,
// which varies depending on the target.
if (!args.hasArg(OPT_hash_style)) {
if (config->emachine == EM_MIPS)
e.message());
if (auto e = tryCreateFile(config->mapFile))
error("cannot open map file " + config->mapFile + ": " + e.message());
+ if (auto e = tryCreateFile(config->whyExtract))
+ error("cannot open --why-extract= file " + config->whyExtract + ": " +
+ e.message());
}
if (errorCount())
return;
// Handle --trace-symbol.
for (auto *arg : args.filtered(OPT_trace_symbol))
- symtab->insert(arg->getValue())->traced = true;
+ symtab.insert(arg->getValue())->traced = true;
// Handle -u/--undefined before input files. If both a.a and b.so define foo,
- // -u foo a.a b.so will fetch a.a.
+ // -u foo a.a b.so will extract a.a.
for (StringRef name : config->undefined)
addUnusedUndefined(name)->referenced = true;
// We also need one if any shared libraries are used and for pie executables
// (probably because the dynamic linker needs it).
config->hasDynSymTab =
- !sharedFiles.empty() || config->isPic || config->exportDynamic;
+ !ctx.sharedFiles.empty() || config->isPic || config->exportDynamic;
// Some symbols (such as __ehdr_start) are defined lazily only when there
// are undefined symbols for them, so we add these to trigger that logic.
- for (StringRef name : script->referencedSymbols)
- addUndefined(name);
+ for (StringRef name : script->referencedSymbols) {
+ Symbol *sym = addUnusedUndefined(name);
+ sym->isUsedInRegularObj = true;
+ sym->referenced = true;
+ }
// Prevent LTO from removing any definition referenced by -u.
for (StringRef name : config->undefined)
- if (Defined *sym = dyn_cast_or_null<Defined>(symtab->find(name)))
+ if (Defined *sym = dyn_cast_or_null<Defined>(symtab.find(name)))
sym->isUsedInRegularObj = true;
// If an entry symbol is in a static archive, pull out that file now.
- if (Symbol *sym = symtab->find(config->entry))
- handleUndefined(sym);
+ if (Symbol *sym = symtab.find(config->entry))
+ handleUndefined(sym, "--entry");
// Handle the `--undefined-glob <pattern>` options.
for (StringRef pat : args::getStrings(args, OPT_undefined_glob))
handleUndefinedGlob(pat);
// Mark -init and -fini symbols so that the LTO doesn't eliminate them.
- if (Symbol *sym = dyn_cast_or_null<Defined>(symtab->find(config->init)))
+ if (Symbol *sym = dyn_cast_or_null<Defined>(symtab.find(config->init)))
sym->isUsedInRegularObj = true;
- if (Symbol *sym = dyn_cast_or_null<Defined>(symtab->find(config->fini)))
+ if (Symbol *sym = dyn_cast_or_null<Defined>(symtab.find(config->fini)))
sym->isUsedInRegularObj = true;
// If any of our inputs are bitcode files, the LTO code generator may create
// to, i.e. if the symbol's definition is in bitcode. Any other required
// libcall symbols will be added to the link after LTO when we add the LTO
// object file to the link.
- if (!bitcodeFiles.empty())
+ if (!ctx.bitcodeFiles.empty())
for (auto *s : lto::LTO::getRuntimeLibcallSymbols())
handleLibcall(s);
+ // Archive members defining __wrap symbols may be extracted.
+ std::vector<WrappedSymbol> wrapped = addWrappedSymbols(args);
+
+ // No more lazy bitcode can be extracted at this point. Do post parse work
+ // like checking duplicate symbols.
+ parallelForEach(ctx.objectFiles, [](ELFFileBase *file) {
+ initSectionsAndLocalSyms(file, /*ignoreComdats=*/false);
+ });
+ parallelForEach(ctx.objectFiles, postParseObjectFile);
+ parallelForEach(ctx.bitcodeFiles,
+ [](BitcodeFile *file) { file->postParse(); });
+ for (auto &it : ctx.nonPrevailingSyms) {
+ Symbol &sym = *it.first;
+ Undefined(sym.file, sym.getName(), sym.binding, sym.stOther, sym.type,
+ it.second)
+ .overwrite(sym);
+ cast<Undefined>(sym).nonPrevailing = true;
+ }
+ ctx.nonPrevailingSyms.clear();
+ for (const DuplicateSymbol &d : ctx.duplicates)
+ reportDuplicate(*d.sym, d.file, d.section, d.value);
+ ctx.duplicates.clear();
+
// Return if there were name resolution errors.
if (errorCount())
return;
// Create elfHeader early. We need a dummy section in
// addReservedSymbols to mark the created symbols as not absolute.
Out::elfHeader = make<OutputSection>("", 0, SHF_ALLOC);
- Out::elfHeader->size = sizeof(typename ELFT::Ehdr);
-
- // Create wrapped symbols for -wrap option.
- std::vector<WrappedSymbol> wrapped = addWrappedSymbols(args);
// We need to create some reserved symbols such as _end. Create them.
if (!config->relocatable)
// name "foo@ver1") rather do harm, so we don't call this if -r is given.
if (!config->relocatable) {
llvm::TimeTraceScope timeScope("Process symbol versions");
- symtab->scanVersionScript();
+ symtab.scanVersionScript();
}
+ // Skip the normal linked output if some LTO options are specified.
+ //
+ // For --thinlto-index-only, index file creation is performed in
+ // compileBitcodeFiles, so we are done afterwards. --plugin-opt=emit-llvm and
+ // --plugin-opt=emit-asm create output files in bitcode or assembly code,
+ // respectively. When only certain thinLTO modules are specified for
+ // compilation, the intermediate object file are the expected output.
+ const bool skipLinkedOutput = config->thinLTOIndexOnly || config->emitLLVM ||
+ config->ltoEmitAsm ||
+ !config->thinLTOModulesToCompile.empty();
+
// Do link-time optimization if given files are LLVM bitcode files.
// This compiles bitcode files into real object files.
//
// With this the symbol table should be complete. After this, no new names
// except a few linker-synthesized ones will be added to the symbol table.
- compileBitcodeFiles<ELFT>();
+ const size_t numObjsBeforeLTO = ctx.objectFiles.size();
+ invokeELFT(compileBitcodeFiles, skipLinkedOutput);
- // Handle --exclude-libs again because lto.tmp may reference additional
- // libcalls symbols defined in an excluded archive. This may override
- // versionId set by scanVersionScript().
- if (args.hasArg(OPT_exclude_libs))
- excludeLibs(args);
-
- // Symbol resolution finished. Report backward reference problems.
+ // Symbol resolution finished. Report backward reference problems,
+ // --print-archive-stats=, and --why-extract=.
reportBackrefs();
+ writeArchiveStats();
+ writeWhyExtract();
if (errorCount())
return;
- // If -thinlto-index-only is given, we should create only "index
- // files" and not object files. Index file creation is already done
- // in addCombinedLTOObject, so we are done if that's the case.
- // Likewise, --plugin-opt=emit-llvm and --plugin-opt=emit-asm are the
- // options to create output files in bitcode or assembly code
- // respectively. No object files are generated.
- // Also bail out here when only certain thinLTO modules are specified for
- // compilation. The intermediate object file are the expected output.
- if (config->thinLTOIndexOnly || config->emitLLVM || config->ltoEmitAsm ||
- !config->thinLTOModulesToCompile.empty())
+ // Bail out if normal linked output is skipped due to LTO.
+ if (skipLinkedOutput)
return;
- // Apply symbol renames for -wrap and combine foo@v1 and foo@@v1.
+ // compileBitcodeFiles may have produced lto.tmp object files. After this, no
+ // more file will be added.
+ auto newObjectFiles = ArrayRef(ctx.objectFiles).slice(numObjsBeforeLTO);
+ parallelForEach(newObjectFiles, [](ELFFileBase *file) {
+ initSectionsAndLocalSyms(file, /*ignoreComdats=*/true);
+ });
+ parallelForEach(newObjectFiles, postParseObjectFile);
+ for (const DuplicateSymbol &d : ctx.duplicates)
+ reportDuplicate(*d.sym, d.file, d.section, d.value);
+
+ // Handle --exclude-libs again because lto.tmp may reference additional
+ // libcalls symbols defined in an excluded archive. This may override
+ // versionId set by scanVersionScript().
+ if (args.hasArg(OPT_exclude_libs))
+ excludeLibs(args);
+
+ // Apply symbol renames for --wrap and combine foo@v1 and foo@@v1.
redirectSymbols(wrapped);
+ // Replace common symbols with regular symbols.
+ replaceCommonSymbols();
+
{
llvm::TimeTraceScope timeScope("Aggregate sections");
// Now that we have a complete list of input files.
// Beyond this point, no new files are added.
// Aggregate all input sections into one place.
- for (InputFile *f : objectFiles)
- for (InputSectionBase *s : f->getSections())
- if (s && s != &InputSection::discarded)
- inputSections.push_back(s);
- for (BinaryFile *f : binaryFiles)
+ for (InputFile *f : ctx.objectFiles) {
+ for (InputSectionBase *s : f->getSections()) {
+ if (!s || s == &InputSection::discarded)
+ continue;
+ if (LLVM_UNLIKELY(isa<EhInputSection>(s)))
+ ctx.ehInputSections.push_back(cast<EhInputSection>(s));
+ else
+ ctx.inputSections.push_back(s);
+ }
+ }
+ for (BinaryFile *f : ctx.binaryFiles)
for (InputSectionBase *s : f->getSections())
- inputSections.push_back(cast<InputSection>(s));
+ ctx.inputSections.push_back(cast<InputSection>(s));
}
{
llvm::TimeTraceScope timeScope("Strip sections");
- llvm::erase_if(inputSections, [](InputSectionBase *s) {
- if (s->type == SHT_LLVM_SYMPART) {
- readSymbolPartitionSection<ELFT>(s);
+ if (ctx.hasSympart.load(std::memory_order_relaxed)) {
+ llvm::erase_if(ctx.inputSections, [](InputSectionBase *s) {
+ if (s->type != SHT_LLVM_SYMPART)
+ return false;
+ invokeELFT(readSymbolPartitionSection, s);
return true;
- }
+ });
+ }
+ // We do not want to emit debug sections if --strip-all
+ // or --strip-debug are given.
+ if (config->strip != StripPolicy::None) {
+ llvm::erase_if(ctx.inputSections, [](InputSectionBase *s) {
+ if (isDebugSection(*s))
+ return true;
+ if (auto *isec = dyn_cast<InputSection>(s))
+ if (InputSectionBase *rel = isec->getRelocatedSection())
+ if (isDebugSection(*rel))
+ return true;
- // We do not want to emit debug sections if --strip-all
- // or -strip-debug are given.
- if (config->strip == StripPolicy::None)
return false;
-
- if (isDebugSection(*s))
- return true;
- if (auto *isec = dyn_cast<InputSection>(s))
- if (InputSectionBase *rel = isec->getRelocatedSection())
- if (isDebugSection(*rel))
- return true;
-
- return false;
- });
+ });
+ }
}
// Since we now have a complete set of input files, we can create
// Read .note.gnu.property sections from input object files which
// contain a hint to tweak linker's and loader's behaviors.
- config->andFeatures = getAndFeatures<ELFT>();
+ config->andFeatures = getAndFeatures();
// The Target instance handles target-specific stuff, such as applying
// relocations or writing a PLT section. It also contains target-dependent
config->imageBase = getImageBase(args);
- if (config->emachine == EM_ARM) {
- // FIXME: These warnings can be removed when lld only uses these features
- // when the input objects have been compiled with an architecture that
- // supports them.
- if (config->armHasBlx == false)
- warn("lld uses blx instruction, no object with architecture supporting "
- "feature detected");
- }
-
// This adds a .comment section containing a version string.
if (!config->relocatable)
- inputSections.push_back(createCommentSection());
-
- // Replace common symbols with regular symbols.
- replaceCommonSymbols();
+ ctx.inputSections.push_back(createCommentSection());
// Split SHF_MERGE and .eh_frame sections into pieces in preparation for garbage collection.
- splitSections<ELFT>();
+ invokeELFT(splitSections);
// Garbage collection and removal of shared symbols from unused shared objects.
- markLive<ELFT>();
- demoteSharedSymbols();
+ invokeELFT(markLive);
+ demoteSharedAndLazySymbols();
// Make copies of any input sections that need to be copied into each
// partition.
// Create synthesized sections such as .got and .plt. This is called before
// processSectionCommands() so that they can be placed by SECTIONS commands.
- createSyntheticSections<ELFT>();
+ invokeELFT(createSyntheticSections);
// Some input sections that are used for exception handling need to be moved
// into synthetic sections. Do that now so that they aren't assigned to
if (!config->relocatable)
combineEhSections();
+ // Merge .riscv.attributes sections.
+ if (config->emachine == EM_RISCV)
+ mergeRISCVAttributesSections();
+
{
llvm::TimeTraceScope timeScope("Assign sections");
// merging MergeInputSections into a single MergeSyntheticSection. From this
// point onwards InputSectionDescription::sections should be used instead of
// sectionBases.
- for (BaseCommand *base : script->sectionCommands)
- if (auto *sec = dyn_cast<OutputSection>(base))
- sec->finalizeInputSections();
- llvm::erase_if(inputSections, [](InputSectionBase *s) {
- return isa<MergeInputSection>(s);
- });
+ for (SectionCommand *cmd : script->sectionCommands)
+ if (auto *osd = dyn_cast<OutputDesc>(cmd))
+ osd->osec.finalizeInputSections();
}
// Two input sections with different output sections should not be folded.
// ICF runs after processSectionCommands() so that we know the output sections.
if (config->icf != ICFLevel::None) {
- findKeepUniqueSections<ELFT>(args);
- doIcf<ELFT>();
+ invokeELFT(findKeepUniqueSections, args);
+ invokeELFT(doIcf);
}
// Read the callgraph now that we know what was gced or icfed
if (config->callGraphProfileSort) {
if (auto *arg = args.getLastArg(OPT_call_graph_ordering_file))
- if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue()))
+ if (std::optional<MemoryBufferRef> buffer = readFile(arg->getValue()))
readCallGraph(*buffer);
- readCallGraphsFromObjectFiles<ELFT>();
+ invokeELFT(readCallGraphsFromObjectFiles);
}
// Write the result to the file.
- writeResult<ELFT>();
+ invokeELFT(writeResult);
}
//
//===----------------------------------------------------------------------===//
//
-// This file contains utility functions for the driver. Because there
+// This file contains utility functions for the ctx.driver. Because there
// are so many small functions, we created this separate file to make
// Driver.cpp less cluttered.
//
//===----------------------------------------------------------------------===//
+#include "Config.h"
#include "Driver.h"
-#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Memory.h"
+#include "lld/Common/CommonLinkerContext.h"
#include "lld/Common/Reproduce.h"
-#include "lld/Common/Version.h"
-#include "llvm/ADT/Optional.h"
-#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/Path.h"
-#include "llvm/Support/Process.h"
#include "llvm/Support/TimeProfiler.h"
+#include <optional>
using namespace llvm;
using namespace llvm::sys;
// Create OptTable
// Create prefix string literals used in Options.td
-#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#define PREFIX(NAME, VALUE) \
+ static constexpr StringLiteral NAME##_init[] = VALUE; \
+ static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \
+ std::size(NAME##_init) - 1);
#include "Options.inc"
#undef PREFIX
// Create table mapping all options defined in Options.td
-static const opt::OptTable::Info optInfo[] = {
+static constexpr opt::OptTable::Info optInfo[] = {
#define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \
{X1, X2, X10, X11, OPT_##ID, opt::Option::KIND##Class, \
X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12},
#undef OPTION
};
-ELFOptTable::ELFOptTable() : OptTable(optInfo) {}
+ELFOptTable::ELFOptTable() : GenericOptTable(optInfo) {}
-// Set color diagnostics according to -color-diagnostics={auto,always,never}
-// or -no-color-diagnostics flags.
+// Set color diagnostics according to --color-diagnostics={auto,always,never}
+// or --no-color-diagnostics flags.
static void handleColorDiagnostics(opt::InputArgList &args) {
- auto *arg = args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq,
- OPT_no_color_diagnostics);
+ auto *arg = args.getLastArg(OPT_color_diagnostics);
if (!arg)
return;
- if (arg->getOption().getID() == OPT_color_diagnostics) {
+ StringRef s = arg->getValue();
+ if (s == "always")
lld::errs().enable_colors(true);
- } else if (arg->getOption().getID() == OPT_no_color_diagnostics) {
+ else if (s == "never")
lld::errs().enable_colors(false);
- } else {
- StringRef s = arg->getValue();
- if (s == "always")
- lld::errs().enable_colors(true);
- else if (s == "never")
- lld::errs().enable_colors(false);
- else if (s != "auto")
- error("unknown option: --color-diagnostics=" + s);
- }
+ else if (s != "auto")
+ error("unknown option: --color-diagnostics=" + s);
}
static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &args) {
for (size_t i = 0, e = args.size(); i != e; ++i) {
StringRef s = args[i];
if ((s == "-plugin-opt" || s == "--plugin-opt") && i + 1 != e) {
- v.push_back(saver.save(s + "=" + args[i + 1]).data());
+ v.push_back(saver().save(s + "=" + args[i + 1]).data());
++i;
} else {
v.push_back(args[i]);
// Expand response files (arguments in the form of @<filename>)
// and then parse the argument again.
- cl::ExpandResponseFiles(saver, getQuotingStyle(args), vec);
+ cl::ExpandResponseFiles(saver(), getQuotingStyle(args), vec);
concatLTOPluginOptions(vec);
args = this->ParseArgs(vec, missingIndex, missingCount);
"lld", false /*ShowHidden*/, true /*ShowAllAliases*/);
lld::outs() << "\n";
- // Scripts generated by Libtool versions up to at least 2.4.6 (the most
- // recent version as of March 2017) expect /: supported targets:.* elf/
- // in a message for the -help option. If it doesn't match, the scripts
- // assume that the linker doesn't support very basic features such as
- // shared libraries. Therefore, we need to print out at least "elf".
+ // Scripts generated by Libtool versions up to 2021-10 expect /: supported
+ // targets:.* elf/ in a message for the --help option. If it doesn't match,
+ // the scripts assume that the linker doesn't support very basic features
+ // such as shared libraries. Therefore, we need to print out at least "elf".
lld::outs() << config->progName << ": supported targets: elf\n";
}
os << quote(rewritePath(arg->getValue())) << "\n";
break;
case OPT_o:
- // If -o path contains directories, "lld @response.txt" will likely
- // fail because the archive we are creating doesn't contain empty
+ case OPT_Map:
+ case OPT_print_archive_stats:
+ case OPT_why_extract:
+ // If an output path contains directories, "lld @response.txt" will
+ // likely fail because the archive we are creating doesn't contain empty
// directories for the output path (-o doesn't create directories).
// Strip directories to prevent the issue.
- os << "-o " << quote(path::filename(arg->getValue())) << "\n";
+ os << arg->getSpelling();
+ if (arg->getOption().getRenderStyle() == opt::Option::RenderSeparateStyle)
+ os << ' ';
+ os << quote(path::filename(arg->getValue())) << '\n';
break;
case OPT_lto_sample_profile:
os << arg->getSpelling() << quote(rewritePath(arg->getValue())) << "\n";
break;
case OPT_call_graph_ordering_file:
case OPT_dynamic_list:
+ case OPT_export_dynamic_symbol_list:
case OPT_just_symbols:
case OPT_library_path:
case OPT_retain_symbols_file:
// Find a file by concatenating given paths. If a resulting path
// starts with "=", the character is replaced with a --sysroot value.
-static Optional<std::string> findFile(StringRef path1, const Twine &path2) {
+static std::optional<std::string> findFile(StringRef path1,
+ const Twine &path2) {
SmallString<128> s;
if (path1.startswith("="))
path::append(s, config->sysroot, path1.substr(1), path2);
if (fs::exists(s))
return std::string(s);
- return None;
+ return std::nullopt;
}
-Optional<std::string> elf::findFromSearchPaths(StringRef path) {
+std::optional<std::string> elf::findFromSearchPaths(StringRef path) {
for (StringRef dir : config->searchPaths)
- if (Optional<std::string> s = findFile(dir, path))
+ if (std::optional<std::string> s = findFile(dir, path))
return s;
- return None;
+ return std::nullopt;
}
namespace {
// Must be in sync with findMajMinShlib in clang/lib/Driver/Driver.cpp.
-llvm::Optional<std::string> findMajMinShlib(StringRef dir, const Twine& libNameSo) {
+ std::optional<std::string> findMajMinShlib(StringRef dir, const Twine& libNameSo) {
// Handle OpenBSD-style maj/min shlib scheme
llvm::SmallString<128> Scratch;
const StringRef LibName = (libNameSo + ".").toStringRef(Scratch);
}
if (MaxMaj >= 0)
return findFile(dir, LibName + Twine(MaxMaj) + "." + Twine(MaxMin));
- return None;
+ return std::nullopt;
}
} // namespace
// This is for -l<basename>. We'll look for lib<basename>.so or lib<basename>.a from
// search paths.
-Optional<std::string> elf::searchLibraryBaseName(StringRef name) {
+std::optional<std::string> elf::searchLibraryBaseName(StringRef name) {
for (StringRef dir : config->searchPaths) {
if (!config->isStatic) {
- if (Optional<std::string> s = findFile(dir, "lib" + name + ".so"))
+ if (std::optional<std::string> s = findFile(dir, "lib" + name + ".so"))
return s;
- if (Optional<std::string> s = findMajMinShlib(dir, "lib" + name + ".so"))
+ if (std::optional<std::string> s = findMajMinShlib(dir, "lib" + name + ".so"))
return s;
}
- if (Optional<std::string> s = findFile(dir, "lib" + name + ".a"))
+ if (std::optional<std::string> s = findFile(dir, "lib" + name + ".a"))
return s;
}
- return None;
+ return std::nullopt;
}
// This is for -l<namespec>.
-Optional<std::string> elf::searchLibrary(StringRef name) {
+std::optional<std::string> elf::searchLibrary(StringRef name) {
llvm::TimeTraceScope timeScope("Locate library", name);
if (name.startswith(":"))
return findFromSearchPaths(name.substr(1));
// If a linker/version script doesn't exist in the current directory, we also
// look for the script in the '-L' search paths. This matches the behaviour of
// '-T', --version-script=, and linker script INPUT() command in ld.bfd.
-Optional<std::string> elf::searchScript(StringRef name) {
+std::optional<std::string> elf::searchScript(StringRef name) {
if (fs::exists(name))
return name.str();
return findFromSearchPaths(name);
//===----------------------------------------------------------------------===//
#include "InputFiles.h"
+#include "Config.h"
+#include "DWARF.h"
#include "Driver.h"
#include "InputSection.h"
#include "LinkerScript.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "SyntheticSections.h"
+#include "Target.h"
+#include "lld/Common/CommonLinkerContext.h"
#include "lld/Common/DWARF.h"
-#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Memory.h"
+#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/STLExtras.h"
-#include "llvm/CodeGen/Analysis.h"
-#include "llvm/IR/LLVMContext.h"
-#include "llvm/IR/Module.h"
#include "llvm/LTO/LTO.h"
-#include "llvm/MC/StringTableBuilder.h"
-#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Object/IRObjectFile.h"
#include "llvm/Support/ARMAttributeParser.h"
#include "llvm/Support/ARMBuildAttributes.h"
#include "llvm/Support/Endian.h"
+#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/RISCVAttributeParser.h"
#include "llvm/Support/TarWriter.h"
bool InputFile::isInGroup;
uint32_t InputFile::nextGroupId;
-std::vector<ArchiveFile *> elf::archiveFiles;
-std::vector<BinaryFile *> elf::binaryFiles;
-std::vector<BitcodeFile *> elf::bitcodeFiles;
-std::vector<LazyObjFile *> elf::lazyObjFiles;
-std::vector<InputFile *> elf::objectFiles;
-std::vector<SharedFile *> elf::sharedFiles;
-
std::unique_ptr<TarWriter> elf::tar;
DenseMap<StringRef, StringRef> elf::gnuWarnings;
// Returns "<internal>", "foo.a(bar.o)" or "baz.o".
std::string lld::toString(const InputFile *f) {
+ static std::mutex mu;
if (!f)
return "<internal>";
- if (f->toStringCache.empty()) {
- if (f->archiveName.empty())
- f->toStringCache = std::string(f->getName());
- else
- f->toStringCache = (f->archiveName + "(" + f->getName() + ")").str();
+ {
+ std::lock_guard<std::mutex> lock(mu);
+ if (f->toStringCache.empty()) {
+ if (f->archiveName.empty())
+ f->toStringCache = f->getName();
+ else
+ (f->archiveName + "(" + f->getName() + ")").toVector(f->toStringCache);
+ }
}
- return f->toStringCache;
+ return std::string(f->toStringCache);
}
// .gnu.warning.SYMBOL are treated as warning symbols for the given symbol
StringRef wsym = name.substr(13);
StringRef s(data.begin());
StringRef wng(s.substr(0, size));
- symtab->insert(wsym)->gwarn = true;
+ symtab.insert(wsym)->gwarn = true;
gnuWarnings.insert({wsym, wng});
}
}
return (endian == ELFDATA2LSB) ? ELF64LEKind : ELF64BEKind;
}
+// For ARM only, to set the EF_ARM_ABI_FLOAT_SOFT or EF_ARM_ABI_FLOAT_HARD
+// flag in the ELF Header we need to look at Tag_ABI_VFP_args to find out how
+// the input objects have been compiled.
+static void updateARMVFPArgs(const ARMAttributeParser &attributes,
+ const InputFile *f) {
+ std::optional<unsigned> attr =
+ attributes.getAttributeValue(ARMBuildAttrs::ABI_VFP_args);
+ if (!attr)
+ // If an ABI tag isn't present then it is implicitly given the value of 0
+ // which maps to ARMBuildAttrs::BaseAAPCS. However many assembler files,
+ // including some in glibc that don't use FP args (and should have value 3)
+ // don't have the attribute so we do not consider an implicit value of 0
+ // as a clash.
+ return;
+
+ unsigned vfpArgs = *attr;
+ ARMVFPArgKind arg;
+ switch (vfpArgs) {
+ case ARMBuildAttrs::BaseAAPCS:
+ arg = ARMVFPArgKind::Base;
+ break;
+ case ARMBuildAttrs::HardFPAAPCS:
+ arg = ARMVFPArgKind::VFP;
+ break;
+ case ARMBuildAttrs::ToolChainFPPCS:
+ // Tool chain specific convention that conforms to neither AAPCS variant.
+ arg = ARMVFPArgKind::ToolChain;
+ break;
+ case ARMBuildAttrs::CompatibleFPAAPCS:
+ // Object compatible with all conventions.
+ return;
+ default:
+ error(toString(f) + ": unknown Tag_ABI_VFP_args value: " + Twine(vfpArgs));
+ return;
+ }
+ // Follow ld.bfd and error if there is a mix of calling conventions.
+ if (config->armVFPArgs != arg && config->armVFPArgs != ARMVFPArgKind::Default)
+ error(toString(f) + ": incompatible Tag_ABI_VFP_args");
+ else
+ config->armVFPArgs = arg;
+}
+
+// The ARM support in lld makes some use of instructions that are not available
+// on all ARM architectures. Namely:
+// - Use of BLX instruction for interworking between ARM and Thumb state.
+// - Use of the extended Thumb branch encoding in relocation.
+// - Use of the MOVT/MOVW instructions in Thumb Thunks.
+// The ARM Attributes section contains information about the architecture chosen
+// at compile time. We follow the convention that if at least one input object
+// is compiled with an architecture that supports these features then lld is
+// permitted to use them.
+static void updateSupportedARMFeatures(const ARMAttributeParser &attributes) {
+ std::optional<unsigned> attr =
+ attributes.getAttributeValue(ARMBuildAttrs::CPU_arch);
+ if (!attr)
+ return;
+ auto arch = *attr;
+ switch (arch) {
+ case ARMBuildAttrs::Pre_v4:
+ case ARMBuildAttrs::v4:
+ case ARMBuildAttrs::v4T:
+ // Architectures prior to v5 do not support BLX instruction
+ break;
+ case ARMBuildAttrs::v5T:
+ case ARMBuildAttrs::v5TE:
+ case ARMBuildAttrs::v5TEJ:
+ case ARMBuildAttrs::v6:
+ case ARMBuildAttrs::v6KZ:
+ case ARMBuildAttrs::v6K:
+ config->armHasBlx = true;
+ // Architectures used in pre-Cortex processors do not support
+ // The J1 = 1 J2 = 1 Thumb branch range extension, with the exception
+ // of Architecture v6T2 (arm1156t2-s and arm1156t2f-s) that do.
+ break;
+ default:
+ // All other Architectures have BLX and extended branch encoding
+ config->armHasBlx = true;
+ config->armJ1J2BranchEncoding = true;
+ if (arch != ARMBuildAttrs::v6_M && arch != ARMBuildAttrs::v6S_M)
+ // All Architectures used in Cortex processors with the exception
+ // of v6-M and v6S-M have the MOVT and MOVW instructions.
+ config->armHasMovtMovw = true;
+ break;
+ }
+}
+
InputFile::InputFile(Kind k, MemoryBufferRef m)
: mb(m), groupId(nextGroupId), fileKind(k) {
// All files within the same --{start,end}-group get the same group ID.
++nextGroupId;
}
-Optional<MemoryBufferRef> elf::readFile(StringRef path) {
+std::optional<MemoryBufferRef> elf::readFile(StringRef path) {
llvm::TimeTraceScope timeScope("Load input files", path);
// The --chroot option changes our virtual root directory.
// This is useful when you are dealing with files created by --reproduce.
if (!config->chroot.empty() && path.startswith("/"))
- path = saver.save(config->chroot + path);
+ path = saver().save(config->chroot + path);
log(path);
config->dependencyFiles.insert(llvm::CachedHashString(path));
/*RequiresNullTerminator=*/false);
if (auto ec = mbOrErr.getError()) {
error("cannot open " + path + ": " + ec.message());
- return None;
+ return std::nullopt;
}
- std::unique_ptr<MemoryBuffer> &mb = *mbOrErr;
- MemoryBufferRef mbref = mb->getMemBufferRef();
- make<std::unique_ptr<MemoryBuffer>>(std::move(mb)); // take MB ownership
+ MemoryBufferRef mbref = (*mbOrErr)->getMemBufferRef();
+ ctx.memoryBuffers.push_back(std::move(*mbOrErr)); // take MB ownership
if (tar)
tar->append(relativeToRoot(path), mbref.getBuffer());
return false;
}
- InputFile *existing;
- if (!objectFiles.empty())
- existing = objectFiles[0];
- else if (!sharedFiles.empty())
- existing = sharedFiles[0];
- else if (!bitcodeFiles.empty())
- existing = bitcodeFiles[0];
- else
- llvm_unreachable("Must have -m, OUTPUT_FORMAT or existing input file to "
- "determine target emulation");
-
- error(toString(file) + " is incompatible with " + toString(existing));
+ InputFile *existing = nullptr;
+ if (!ctx.objectFiles.empty())
+ existing = ctx.objectFiles[0];
+ else if (!ctx.sharedFiles.empty())
+ existing = ctx.sharedFiles[0];
+ else if (!ctx.bitcodeFiles.empty())
+ existing = ctx.bitcodeFiles[0];
+ std::string with;
+ if (existing)
+ with = " with " + toString(existing);
+ error(toString(file) + " is incompatible" + with);
return false;
}
// Binary file
if (auto *f = dyn_cast<BinaryFile>(file)) {
- binaryFiles.push_back(f);
- f->parse();
- return;
- }
-
- // .a file
- if (auto *f = dyn_cast<ArchiveFile>(file)) {
- archiveFiles.push_back(f);
+ ctx.binaryFiles.push_back(f);
f->parse();
return;
}
// Lazy object file
- if (auto *f = dyn_cast<LazyObjFile>(file)) {
- lazyObjFiles.push_back(f);
- f->parse<ELFT>();
+ if (file->lazy) {
+ if (auto *f = dyn_cast<BitcodeFile>(file)) {
+ ctx.lazyBitcodeFiles.push_back(f);
+ f->parseLazy();
+ } else {
+ cast<ObjFile<ELFT>>(file)->parseLazy();
+ }
return;
}
// LLVM bitcode file
if (auto *f = dyn_cast<BitcodeFile>(file)) {
- bitcodeFiles.push_back(f);
- f->parse<ELFT>();
+ ctx.bitcodeFiles.push_back(f);
+ f->parse();
return;
}
// Regular object file
- objectFiles.push_back(file);
+ ctx.objectFiles.push_back(cast<ELFFileBase>(file));
cast<ObjFile<ELFT>>(file)->parse();
}
// Add symbols in File to the symbol table.
-void elf::parseFile(InputFile *file) {
- switch (config->ekind) {
- case ELF32LEKind:
- doParseFile<ELF32LE>(file);
- return;
- case ELF32BEKind:
- doParseFile<ELF32BE>(file);
- return;
- case ELF64LEKind:
- doParseFile<ELF64LE>(file);
- return;
- case ELF64BEKind:
- doParseFile<ELF64BE>(file);
- return;
- default:
- llvm_unreachable("unknown ELFT");
- }
-}
+void elf::parseFile(InputFile *file) { invokeELFT(doParseFile, file); }
// Concatenates arguments to construct a string representing an error location.
static std::string createFileLineMsg(StringRef path, unsigned line) {
static std::string getSrcMsgAux(ObjFile<ELFT> &file, const Symbol &sym,
InputSectionBase &sec, uint64_t offset) {
// In DWARF, functions and variables are stored to different places.
- // First, lookup a function for a given offset.
- if (Optional<DILineInfo> info = file.getDILineInfo(&sec, offset))
+ // First, look up a function for a given offset.
+ if (std::optional<DILineInfo> info = file.getDILineInfo(&sec, offset))
return createFileLineMsg(info->FileName, info->Line);
- // If it failed, lookup again as a variable.
- if (Optional<std::pair<std::string, unsigned>> fileLine =
+ // If it failed, look up again as a variable.
+ if (std::optional<std::pair<std::string, unsigned>> fileLine =
file.getVariableLoc(sym.getName()))
return createFileLineMsg(fileLine->first, fileLine->second);
uint64_t offset) {
if (kind() != ObjKind)
return "";
- switch (config->ekind) {
+ switch (ekind) {
default:
llvm_unreachable("Invalid kind");
case ELF32LEKind:
return nameForScriptCache;
}
+// An ELF object file may contain a `.deplibs` section. If it exists, the
+// section contains a list of library specifiers such as `m` for libm. This
+// function resolves a given name by finding the first matching library checking
+// the various ways that a library can be specified to LLD. This ELF extension
+// is a form of autolinking and is called `dependent libraries`. It is currently
+// unique to LLVM and lld.
+static void addDependentLibrary(StringRef specifier, const InputFile *f) {
+ if (!config->dependentLibraries)
+ return;
+ if (std::optional<std::string> s = searchLibraryBaseName(specifier))
+ ctx.driver.addFile(saver().save(*s), /*withLOption=*/true);
+ else if (std::optional<std::string> s = findFromSearchPaths(specifier))
+ ctx.driver.addFile(saver().save(*s), /*withLOption=*/true);
+ else if (fs::exists(specifier))
+ ctx.driver.addFile(specifier, /*withLOption=*/false);
+ else
+ error(toString(f) +
+ ": unable to find library from dependent library specifier: " +
+ specifier);
+}
+
+// Record the membership of a section group so that in the garbage collection
+// pass, section group members are kept or discarded as a unit.
+template <class ELFT>
+static void handleSectionGroup(ArrayRef<InputSectionBase *> sections,
+ ArrayRef<typename ELFT::Word> entries) {
+ bool hasAlloc = false;
+ for (uint32_t index : entries.slice(1)) {
+ if (index >= sections.size())
+ return;
+ if (InputSectionBase *s = sections[index])
+ if (s != &InputSection::discarded && s->flags & SHF_ALLOC)
+ hasAlloc = true;
+ }
+
+ // If any member has the SHF_ALLOC flag, the whole group is subject to garbage
+ // collection. See the comment in markLive(). This rule retains .debug_types
+ // and .rela.debug_types.
+ if (!hasAlloc)
+ return;
+
+ // Connect the members in a circular doubly-linked list via
+ // nextInSectionGroup.
+ InputSectionBase *head;
+ InputSectionBase *prev = nullptr;
+ for (uint32_t index : entries.slice(1)) {
+ InputSectionBase *s = sections[index];
+ if (!s || s == &InputSection::discarded)
+ continue;
+ if (prev)
+ prev->nextInSectionGroup = s;
+ else
+ head = s;
+ prev = s;
+ }
+ if (prev)
+ prev->nextInSectionGroup = head;
+}
+
template <class ELFT> DWARFCache *ObjFile<ELFT>::getDwarf() {
llvm::call_once(initDwarf, [this]() {
dwarf = std::make_unique<DWARFCache>(std::make_unique<DWARFContext>(
// Returns the pair of file name and line number describing location of data
// object (variable, array, etc) definition.
template <class ELFT>
-Optional<std::pair<std::string, unsigned>>
+std::optional<std::pair<std::string, unsigned>>
ObjFile<ELFT>::getVariableLoc(StringRef name) {
return getDwarf()->getVariableLoc(name);
}
// Returns source line information for a given offset
// using DWARF debug info.
template <class ELFT>
-Optional<DILineInfo> ObjFile<ELFT>::getDILineInfo(InputSectionBase *s,
- uint64_t offset) {
+std::optional<DILineInfo> ObjFile<ELFT>::getDILineInfo(InputSectionBase *s,
+ uint64_t offset) {
// Detect SectionIndex for specified section.
uint64_t sectionIndex = object::SectionedAddress::UndefSection;
ArrayRef<InputSectionBase *> sections = s->file->getSections();
return getDwarf()->getDILineInfo(offset, sectionIndex);
}
-ELFFileBase::ELFFileBase(Kind k, MemoryBufferRef mb) : InputFile(k, mb) {
- ekind = getELFKind(mb, "");
+ELFFileBase::ELFFileBase(Kind k, ELFKind ekind, MemoryBufferRef mb)
+ : InputFile(k, mb) {
+ this->ekind = ekind;
+}
+
+template <typename Elf_Shdr>
+static const Elf_Shdr *findSection(ArrayRef<Elf_Shdr> sections, uint32_t type) {
+ for (const Elf_Shdr &sec : sections)
+ if (sec.sh_type == type)
+ return &sec;
+ return nullptr;
+}
+void ELFFileBase::init() {
switch (ekind) {
case ELF32LEKind:
- init<ELF32LE>();
+ init<ELF32LE>(fileKind);
break;
case ELF32BEKind:
- init<ELF32BE>();
+ init<ELF32BE>(fileKind);
break;
case ELF64LEKind:
- init<ELF64LE>();
+ init<ELF64LE>(fileKind);
break;
case ELF64BEKind:
- init<ELF64BE>();
+ init<ELF64BE>(fileKind);
break;
default:
llvm_unreachable("getELFKind");
}
}
-template <typename Elf_Shdr>
-static const Elf_Shdr *findSection(ArrayRef<Elf_Shdr> sections, uint32_t type) {
- for (const Elf_Shdr &sec : sections)
- if (sec.sh_type == type)
- return &sec;
- return nullptr;
-}
-
-template <class ELFT> void ELFFileBase::init() {
+template <class ELFT> void ELFFileBase::init(InputFile::Kind k) {
using Elf_Shdr = typename ELFT::Shdr;
using Elf_Sym = typename ELFT::Sym;
abiVersion = obj.getHeader().e_ident[llvm::ELF::EI_ABIVERSION];
ArrayRef<Elf_Shdr> sections = CHECK(obj.sections(), this);
+ elfShdrs = sections.data();
+ numELFShdrs = sections.size();
// Find a symbol table.
- bool isDSO =
- (identify_magic(mb.getBuffer()) == file_magic::elf_shared_object);
const Elf_Shdr *symtabSec =
- findSection(sections, isDSO ? SHT_DYNSYM : SHT_SYMTAB);
+ findSection(sections, k == SharedKind ? SHT_DYNSYM : SHT_SYMTAB);
if (!symtabSec)
return;
fatal(toString(this) + ": invalid sh_info in symbol table");
elfSyms = reinterpret_cast<const void *>(eSyms.data());
- numELFSyms = eSyms.size();
+ numELFSyms = uint32_t(eSyms.size());
stringTable = CHECK(obj.getStringTableForSymtab(*symtabSec, sections), this);
}
this);
}
-template <class ELFT> ArrayRef<Symbol *> ObjFile<ELFT>::getLocalSymbols() {
- if (this->symbols.empty())
- return {};
- return makeArrayRef(this->symbols).slice(1, this->firstGlobal - 1);
-}
-
-template <class ELFT> ArrayRef<Symbol *> ObjFile<ELFT>::getGlobalSymbols() {
- return makeArrayRef(this->symbols).slice(this->firstGlobal);
-}
-
template <class ELFT> void ObjFile<ELFT>::parse(bool ignoreComdats) {
+ object::ELFFile<ELFT> obj = this->getObj();
// Read a section table. justSymbols is usually false.
- if (this->justSymbols)
+ if (this->justSymbols) {
initializeJustSymbols();
- else
- initializeSections(ignoreComdats);
+ initializeSymbols(obj);
+ return;
+ }
+
+ // Handle dependent libraries and selection of section groups as these are not
+ // done in parallel.
+ ArrayRef<Elf_Shdr> objSections = getELFShdrs<ELFT>();
+ StringRef shstrtab = CHECK(obj.getSectionStringTable(objSections), this);
+ uint64_t size = objSections.size();
+ sections.resize(size);
+ for (size_t i = 0; i != size; ++i) {
+ const Elf_Shdr &sec = objSections[i];
+ if (sec.sh_type == SHT_LLVM_DEPENDENT_LIBRARIES && !config->relocatable) {
+ StringRef name = check(obj.getSectionName(sec, shstrtab));
+ ArrayRef<char> data = CHECK(
+ this->getObj().template getSectionContentsAsArray<char>(sec), this);
+ if (!data.empty() && data.back() != '\0') {
+ error(
+ toString(this) +
+ ": corrupted dependent libraries section (unterminated string): " +
+ name);
+ } else {
+ for (const char *d = data.begin(), *e = data.end(); d < e;) {
+ StringRef s(d);
+ addDependentLibrary(s, this);
+ d += s.size() + 1;
+ }
+ }
+ this->sections[i] = &InputSection::discarded;
+ continue;
+ }
+
+ if (sec.sh_type == SHT_ARM_ATTRIBUTES && config->emachine == EM_ARM) {
+ ARMAttributeParser 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, ekind == ELF32LEKind ? support::little
+ : support::big)) {
+ InputSection isec(*this, sec, name);
+ warn(toString(&isec) + ": " + llvm::toString(std::move(e)));
+ } else {
+ updateSupportedARMFeatures(attributes);
+ updateARMVFPArgs(attributes, this);
+
+ // FIXME: Retain the first attribute section we see. The eglibc ARM
+ // dynamic loaders require the presence of an attribute section for
+ // dlopen to work. 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);
+ ArrayRef<Elf_Word> entries =
+ CHECK(obj.template getSectionContentsAsArray<Elf_Word>(sec), this);
+ if (entries.empty())
+ fatal(toString(this) + ": empty SHT_GROUP");
+
+ Elf_Word flag = entries[0];
+ if (flag && flag != GRP_COMDAT)
+ fatal(toString(this) + ": unsupported SHT_GROUP format");
+
+ bool keepGroup =
+ (flag & GRP_COMDAT) == 0 || ignoreComdats ||
+ symtab.comdatGroups.try_emplace(CachedHashStringRef(signature), this)
+ .second;
+ if (keepGroup) {
+ if (config->relocatable)
+ this->sections[i] = createInputSection(
+ i, sec, check(obj.getSectionName(sec, shstrtab)));
+ continue;
+ }
+
+ // Otherwise, discard group members.
+ for (uint32_t secIndex : entries.slice(1)) {
+ if (secIndex >= size)
+ fatal(toString(this) +
+ ": invalid section index in group: " + Twine(secIndex));
+ this->sections[secIndex] = &InputSection::discarded;
+ }
+ }
// Read a symbol table.
- initializeSymbols();
+ initializeSymbols(obj);
}
// Sections with SHT_GROUP and comdat bits define comdat section groups.
if (sec.sh_info >= symbols.size())
fatal(toString(this) + ": invalid symbol index");
const typename ELFT::Sym &sym = symbols[sec.sh_info];
- StringRef signature = CHECK(sym.getName(this->stringTable), this);
-
- // As a special case, if a symbol is a section symbol and has no name,
- // we use a section name as a signature.
- //
- // Such SHT_GROUP sections are invalid from the perspective of the ELF
- // standard, but GNU gold 1.14 (the newest version as of July 2017) or
- // older produce such sections as outputs for the -r option, so we need
- // a bug-compatibility.
- if (signature.empty() && sym.getType() == STT_SECTION)
- return getSectionName(sec);
- return signature;
+ return CHECK(sym.getName(this->stringTable), this);
}
template <class ELFT>
bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &sec, StringRef name) {
- if (!(sec.sh_flags & SHF_MERGE))
- return false;
-
// On a regular link we don't merge sections if -O0 (default is -O1). This
// sometimes makes the linker significantly faster, although the output will
// be bigger.
// When the option is given, we link "just symbols". The section table is
// initialized with null pointers.
template <class ELFT> void ObjFile<ELFT>::initializeJustSymbols() {
- ArrayRef<Elf_Shdr> sections = CHECK(this->getObj().sections(), this);
- this->sections.resize(sections.size());
-}
-
-// An ELF object file may contain a `.deplibs` section. If it exists, the
-// section contains a list of library specifiers such as `m` for libm. This
-// function resolves a given name by finding the first matching library checking
-// the various ways that a library can be specified to LLD. This ELF extension
-// is a form of autolinking and is called `dependent libraries`. It is currently
-// unique to LLVM and lld.
-static void addDependentLibrary(StringRef specifier, const InputFile *f) {
- if (!config->dependentLibraries)
- return;
- if (fs::exists(specifier))
- driver->addFile(specifier, /*withLOption=*/false);
- else if (Optional<std::string> s = findFromSearchPaths(specifier))
- driver->addFile(*s, /*withLOption=*/true);
- else if (Optional<std::string> s = searchLibraryBaseName(specifier))
- driver->addFile(*s, /*withLOption=*/true);
- else
- error(toString(f) +
- ": unable to find library from dependent library specifier: " +
- specifier);
+ sections.resize(numELFShdrs);
}
-// Record the membership of a section group so that in the garbage collection
-// pass, section group members are kept or discarded as a unit.
template <class ELFT>
-static void handleSectionGroup(ArrayRef<InputSectionBase *> sections,
- ArrayRef<typename ELFT::Word> entries) {
- bool hasAlloc = false;
- for (uint32_t index : entries.slice(1)) {
- if (index >= sections.size())
- return;
- if (InputSectionBase *s = sections[index])
- if (s != &InputSection::discarded && s->flags & SHF_ALLOC)
- hasAlloc = true;
- }
-
- // If any member has the SHF_ALLOC flag, the whole group is subject to garbage
- // collection. See the comment in markLive(). This rule retains .debug_types
- // and .rela.debug_types.
- if (!hasAlloc)
- return;
-
- // Connect the members in a circular doubly-linked list via
- // nextInSectionGroup.
- InputSectionBase *head;
- InputSectionBase *prev = nullptr;
- for (uint32_t index : entries.slice(1)) {
- InputSectionBase *s = sections[index];
- if (!s || s == &InputSection::discarded)
- continue;
- if (prev)
- prev->nextInSectionGroup = s;
- else
- head = s;
- prev = s;
- }
- if (prev)
- prev->nextInSectionGroup = head;
-}
-
-template <class ELFT>
-void ObjFile<ELFT>::initializeSections(bool ignoreComdats) {
- const ELFFile<ELFT> &obj = this->getObj();
-
- ArrayRef<Elf_Shdr> objSections = CHECK(obj.sections(), this);
+void ObjFile<ELFT>::initializeSections(bool ignoreComdats,
+ const llvm::object::ELFFile<ELFT> &obj) {
+ ArrayRef<Elf_Shdr> objSections = getELFShdrs<ELFT>();
+ StringRef shstrtab = CHECK(obj.getSectionStringTable(objSections), this);
uint64_t size = objSections.size();
- this->sections.resize(size);
- this->sectionStringTable =
- CHECK(obj.getSectionStringTable(objSections), this);
-
- std::vector<ArrayRef<Elf_Word>> selectedGroups;
-
- for (size_t i = 0, e = objSections.size(); i < e; ++i) {
+ SmallVector<ArrayRef<Elf_Word>, 0> selectedGroups;
+ for (size_t i = 0; i != size; ++i) {
if (this->sections[i] == &InputSection::discarded)
continue;
const Elf_Shdr &sec = objSections[i];
- if (sec.sh_type == ELF::SHT_LLVM_CALL_GRAPH_PROFILE)
- cgProfileSectionIndex = i;
-
// SHF_EXCLUDE'ed sections are discarded by the linker. However,
// if -r is given, we'll let the final link discard such sections.
// This is compatible with GNU.
if ((sec.sh_flags & SHF_EXCLUDE) && !config->relocatable) {
+ if (sec.sh_type == SHT_LLVM_CALL_GRAPH_PROFILE)
+ cgProfileSectionIndex = i;
if (sec.sh_type == SHT_LLVM_ADDRSIG) {
// We ignore the address-significance table if we know that the object
// file was created by objcopy or ld -r. This is because these tools
switch (sec.sh_type) {
case SHT_GROUP: {
- // De-duplicate section groups by their signatures.
- StringRef signature = getShtGroupSignature(objSections, sec);
- this->sections[i] = &InputSection::discarded;
-
+ if (!config->relocatable)
+ sections[i] = &InputSection::discarded;
+ StringRef signature =
+ cantFail(this->getELFSyms<ELFT>()[sec.sh_info].getName(stringTable));
ArrayRef<Elf_Word> entries =
- CHECK(obj.template getSectionContentsAsArray<Elf_Word>(sec), this);
- if (entries.empty())
- fatal(toString(this) + ": empty SHT_GROUP");
-
- Elf_Word flag = entries[0];
- if (flag && flag != GRP_COMDAT)
- fatal(toString(this) + ": unsupported SHT_GROUP format");
-
- bool keepGroup =
- (flag & GRP_COMDAT) == 0 || ignoreComdats ||
- symtab->comdatGroups.try_emplace(CachedHashStringRef(signature), this)
- .second;
- if (keepGroup) {
- if (config->relocatable)
- this->sections[i] = createInputSection(sec);
+ cantFail(obj.template getSectionContentsAsArray<Elf_Word>(sec));
+ if ((entries[0] & GRP_COMDAT) == 0 || ignoreComdats ||
+ symtab.comdatGroups.find(CachedHashStringRef(signature))->second ==
+ this)
selectedGroups.push_back(entries);
- continue;
- }
-
- // Otherwise, discard group members.
- for (uint32_t secIndex : entries.slice(1)) {
- if (secIndex >= size)
- fatal(toString(this) +
- ": invalid section index in group: " + Twine(secIndex));
- this->sections[secIndex] = &InputSection::discarded;
- }
break;
}
case SHT_SYMTAB_SHNDX:
case SHT_NULL:
break;
case SHT_PROGBITS: {
- this->sections[i] = createInputSection(sec);
- StringRef name = CHECK(obj.getSectionName(sec, this->sectionStringTable), this);
+ this->sections[i] = createInputSection(i, sec, check(obj.getSectionName(sec, shstrtab)));
+ StringRef name = check(obj.getSectionName(sec, shstrtab));
ArrayRef<char> data =
CHECK(obj.template getSectionContentsAsArray<char>(sec), this);
parseGNUWarning(name, data, sec.sh_size);
}
break;
+ case SHT_LLVM_SYMPART:
+ ctx.hasSympart.store(true, std::memory_order_relaxed);
+ [[fallthrough]];
default:
- this->sections[i] = createInputSection(sec);
+ this->sections[i] =
+ createInputSection(i, sec, check(obj.getSectionName(sec, shstrtab)));
}
}
// such cases, the relocation section would attempt to reference a target
// section that has not yet been created. For simplicity, delay creation of
// relocation sections until now.
- for (size_t i = 0, e = objSections.size(); i < e; ++i) {
+ for (size_t i = 0; i != size; ++i) {
if (this->sections[i] == &InputSection::discarded)
continue;
const Elf_Shdr &sec = objSections[i];
- if (sec.sh_type == SHT_REL || sec.sh_type == SHT_RELA)
- this->sections[i] = createInputSection(sec);
-
- // A SHF_LINK_ORDER section with sh_link=0 is handled as if it did not have
- // the flag.
- if (!(sec.sh_flags & SHF_LINK_ORDER) || !sec.sh_link)
- continue;
-
- InputSectionBase *linkSec = nullptr;
- if (sec.sh_link < this->sections.size())
- linkSec = this->sections[sec.sh_link];
- if (!linkSec)
- fatal(toString(this) + ": invalid sh_link index: " + Twine(sec.sh_link));
+ if (sec.sh_type == SHT_REL || sec.sh_type == SHT_RELA) {
+ // Find a relocation target section and associate this section with that.
+ // Target may have been discarded if it is in a different section group
+ // and the group is discarded, even though it's a violation of the spec.
+ // We handle that situation gracefully by discarding dangling relocation
+ // sections.
+ const uint32_t info = sec.sh_info;
+ InputSectionBase *s = getRelocTarget(i, sec, info);
+ if (!s)
+ continue;
- // A SHF_LINK_ORDER section is discarded if its linked-to section is
- // discarded.
- InputSection *isec = cast<InputSection>(this->sections[i]);
- linkSec->dependentSections.push_back(isec);
- if (!isa<InputSection>(linkSec))
- error("a section " + isec->name +
- " with SHF_LINK_ORDER should not refer a non-regular section: " +
- toString(linkSec));
- }
+ // ELF spec allows mergeable sections with relocations, but they are rare,
+ // and it is in practice hard to merge such sections by contents, because
+ // applying relocations at end of linking changes section contents. So, we
+ // simply handle such sections as non-mergeable ones. Degrading like this
+ // is acceptable because section merging is optional.
+ if (auto *ms = dyn_cast<MergeInputSection>(s)) {
+ s = makeThreadLocal<InputSection>(
+ ms->file, ms->flags, ms->type, ms->addralign,
+ ms->contentMaybeDecompress(), ms->name);
+ sections[info] = s;
+ }
- for (ArrayRef<Elf_Word> entries : selectedGroups)
- handleSectionGroup<ELFT>(this->sections, entries);
-}
+ if (s->relSecIdx != 0)
+ error(
+ toString(s) +
+ ": multiple relocation sections to one section are not supported");
+ s->relSecIdx = i;
+
+ // Relocation sections are usually removed from the output, so return
+ // `nullptr` for the normal case. However, if -r or --emit-relocs is
+ // specified, we need to copy them to the output. (Some post link analysis
+ // tools specify --emit-relocs to obtain the information.)
+ if (config->copyRelocs) {
+ auto *isec = makeThreadLocal<InputSection>(
+ *this, sec, check(obj.getSectionName(sec, shstrtab)));
+ // If the relocated section is discarded (due to /DISCARD/ or
+ // --gc-sections), the relocation section should be discarded as well.
+ s->dependentSections.push_back(isec);
+ sections[i] = isec;
+ }
+ continue;
+ }
-// For ARM only, to set the EF_ARM_ABI_FLOAT_SOFT or EF_ARM_ABI_FLOAT_HARD
-// flag in the ELF Header we need to look at Tag_ABI_VFP_args to find out how
-// the input objects have been compiled.
-static void updateARMVFPArgs(const ARMAttributeParser &attributes,
- const InputFile *f) {
- Optional<unsigned> attr =
- attributes.getAttributeValue(ARMBuildAttrs::ABI_VFP_args);
- if (!attr.hasValue())
- // If an ABI tag isn't present then it is implicitly given the value of 0
- // which maps to ARMBuildAttrs::BaseAAPCS. However many assembler files,
- // including some in glibc that don't use FP args (and should have value 3)
- // don't have the attribute so we do not consider an implicit value of 0
- // as a clash.
- return;
+ // A SHF_LINK_ORDER section with sh_link=0 is handled as if it did not have
+ // the flag.
+ if (!sec.sh_link || !(sec.sh_flags & SHF_LINK_ORDER))
+ continue;
- unsigned vfpArgs = attr.getValue();
- ARMVFPArgKind arg;
- switch (vfpArgs) {
- case ARMBuildAttrs::BaseAAPCS:
- arg = ARMVFPArgKind::Base;
- break;
- case ARMBuildAttrs::HardFPAAPCS:
- arg = ARMVFPArgKind::VFP;
- break;
- case ARMBuildAttrs::ToolChainFPPCS:
- // Tool chain specific convention that conforms to neither AAPCS variant.
- arg = ARMVFPArgKind::ToolChain;
- break;
- case ARMBuildAttrs::CompatibleFPAAPCS:
- // Object compatible with all conventions.
- return;
- default:
- error(toString(f) + ": unknown Tag_ABI_VFP_args value: " + Twine(vfpArgs));
- return;
- }
- // Follow ld.bfd and error if there is a mix of calling conventions.
- if (config->armVFPArgs != arg && config->armVFPArgs != ARMVFPArgKind::Default)
- error(toString(f) + ": incompatible Tag_ABI_VFP_args");
- else
- config->armVFPArgs = arg;
-}
+ InputSectionBase *linkSec = nullptr;
+ if (sec.sh_link < size)
+ linkSec = this->sections[sec.sh_link];
+ if (!linkSec)
+ fatal(toString(this) + ": invalid sh_link index: " + Twine(sec.sh_link));
-// The ARM support in lld makes some use of instructions that are not available
-// on all ARM architectures. Namely:
-// - Use of BLX instruction for interworking between ARM and Thumb state.
-// - Use of the extended Thumb branch encoding in relocation.
-// - Use of the MOVT/MOVW instructions in Thumb Thunks.
-// The ARM Attributes section contains information about the architecture chosen
-// at compile time. We follow the convention that if at least one input object
-// is compiled with an architecture that supports these features then lld is
-// permitted to use them.
-static void updateSupportedARMFeatures(const ARMAttributeParser &attributes) {
- Optional<unsigned> attr =
- attributes.getAttributeValue(ARMBuildAttrs::CPU_arch);
- if (!attr.hasValue())
- return;
- auto arch = attr.getValue();
- switch (arch) {
- case ARMBuildAttrs::Pre_v4:
- case ARMBuildAttrs::v4:
- case ARMBuildAttrs::v4T:
- // Architectures prior to v5 do not support BLX instruction
- break;
- case ARMBuildAttrs::v5T:
- case ARMBuildAttrs::v5TE:
- case ARMBuildAttrs::v5TEJ:
- case ARMBuildAttrs::v6:
- case ARMBuildAttrs::v6KZ:
- case ARMBuildAttrs::v6K:
- config->armHasBlx = true;
- // Architectures used in pre-Cortex processors do not support
- // The J1 = 1 J2 = 1 Thumb branch range extension, with the exception
- // of Architecture v6T2 (arm1156t2-s and arm1156t2f-s) that do.
- break;
- default:
- // All other Architectures have BLX and extended branch encoding
- config->armHasBlx = true;
- config->armJ1J2BranchEncoding = true;
- if (arch != ARMBuildAttrs::v6_M && arch != ARMBuildAttrs::v6S_M)
- // All Architectures used in Cortex processors with the exception
- // of v6-M and v6S-M have the MOVT and MOVW instructions.
- config->armHasMovtMovw = true;
- break;
+ // A SHF_LINK_ORDER section is discarded if its linked-to section is
+ // discarded.
+ InputSection *isec = cast<InputSection>(this->sections[i]);
+ linkSec->dependentSections.push_back(isec);
+ if (!isa<InputSection>(linkSec))
+ error("a section " + isec->name +
+ " with SHF_LINK_ORDER should not refer a non-regular section: " +
+ toString(linkSec));
}
+
+ for (ArrayRef<Elf_Word> entries : selectedGroups)
+ handleSectionGroup<ELFT>(this->sections, entries);
}
// If a source file is compiled with x86 hardware-assisted call flow control
using Elf_Note = typename ELFT::Note;
uint32_t featuresSet = 0;
- ArrayRef<uint8_t> data = sec.data();
+ ArrayRef<uint8_t> data = sec.content();
auto reportFatal = [&](const uint8_t *place, const char *msg) {
fatal(toString(sec.file) + ":(" + sec.name + "+0x" +
- Twine::utohexstr(place - sec.data().data()) + "): " + msg);
+ Twine::utohexstr(place - sec.content().data()) + "): " + msg);
};
while (!data.empty()) {
// Read one NOTE record.
}
template <class ELFT>
-InputSectionBase *ObjFile<ELFT>::getRelocTarget(const Elf_Shdr &sec) {
- uint32_t idx = sec.sh_info;
- if (idx >= this->sections.size())
- fatal(toString(this) + ": invalid relocated section index: " + Twine(idx));
- InputSectionBase *target = this->sections[idx];
-
- // Strictly speaking, a relocation section must be included in the
- // group of the section it relocates. However, LLVM 3.3 and earlier
- // would fail to do so, so we gracefully handle that case.
- if (target == &InputSection::discarded)
- return nullptr;
-
- if (!target)
- fatal(toString(this) + ": unsupported relocation reference");
- return target;
-}
+InputSectionBase *ObjFile<ELFT>::getRelocTarget(uint32_t idx,
+ const Elf_Shdr &sec,
+ uint32_t info) {
+ if (info < this->sections.size()) {
+ InputSectionBase *target = this->sections[info];
+
+ // Strictly speaking, a relocation section must be included in the
+ // group of the section it relocates. However, LLVM 3.3 and earlier
+ // would fail to do so, so we gracefully handle that case.
+ if (target == &InputSection::discarded)
+ return nullptr;
+
+ if (target != nullptr)
+ return target;
+ }
-// Create a regular InputSection class that has the same contents
-// as a given section.
-static InputSection *toRegularSection(MergeInputSection *sec) {
- return make<InputSection>(sec->file, sec->flags, sec->type, sec->alignment,
- sec->data(), sec->name);
+ error(toString(this) + Twine(": relocation section (index ") + Twine(idx) +
+ ") has invalid sh_info (" + Twine(info) + ")");
+ return nullptr;
}
+// The function may be called concurrently for different input files. For
+// allocation, prefer makeThreadLocal which does not require holding a lock.
template <class ELFT>
-InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &sec) {
- StringRef name = getSectionName(sec);
-
- if (config->emachine == EM_ARM && sec.sh_type == SHT_ARM_ATTRIBUTES) {
- ARMAttributeParser attributes;
- ArrayRef<uint8_t> contents = check(this->getObj().getSectionContents(sec));
- if (Error e = attributes.parse(contents, config->ekind == ELF32LEKind
- ? support::little
- : support::big)) {
- auto *isec = make<InputSection>(*this, sec, name);
- warn(toString(isec) + ": " + llvm::toString(std::move(e)));
- } else {
- updateSupportedARMFeatures(attributes);
- updateARMVFPArgs(attributes, this);
+InputSectionBase *ObjFile<ELFT>::createInputSection(uint32_t idx,
+ const Elf_Shdr &sec,
+ StringRef name) {
+ if (name.startswith(".n")) {
+ // The GNU linker uses .note.GNU-stack section as a marker indicating
+ // that the code in the object file does not expect that the stack is
+ // executable (in terms of NX bit). If all input files have the marker,
+ // the GNU linker adds a PT_GNU_STACK segment to tells the loader to
+ // make the stack non-executable. Most object files have this section as
+ // of 2017.
+ //
+ // But making the stack non-executable is a norm today for security
+ // reasons. Failure to do so may result in a serious security issue.
+ // Therefore, we make LLD always add PT_GNU_STACK unless it is
+ // explicitly told to do otherwise (by -z execstack). Because the stack
+ // executable-ness is controlled solely by command line options,
+ // .note.GNU-stack sections are simply ignored.
+ if (name == ".note.GNU-stack")
+ return &InputSection::discarded;
- // FIXME: Retain the first attribute section we see. The eglibc ARM
- // dynamic loaders require the presence of an attribute section for dlopen
- // to work. In a full implementation we would merge all attribute
- // sections.
- if (in.attributes == nullptr) {
- in.attributes = make<InputSection>(*this, sec, name);
- return in.attributes;
- }
+ // Object files that use processor features such as Intel Control-Flow
+ // Enforcement (CET) or AArch64 Branch Target Identification BTI, use a
+ // .note.gnu.property section containing a bitfield of feature bits like the
+ // GNU_PROPERTY_X86_FEATURE_1_IBT flag. Read a bitmap containing the flag.
+ //
+ // Since we merge bitmaps from multiple object files to create a new
+ // .note.gnu.property containing a single AND'ed bitmap, we discard an input
+ // file's .note.gnu.property section.
+ if (name == ".note.gnu.property") {
+ this->andFeatures = readAndFeatures<ELFT>(InputSection(*this, sec, name));
return &InputSection::discarded;
}
- }
- if (config->emachine == EM_RISCV && sec.sh_type == SHT_RISCV_ATTRIBUTES) {
- RISCVAttributeParser attributes;
- ArrayRef<uint8_t> contents = check(this->getObj().getSectionContents(sec));
- if (Error e = attributes.parse(contents, support::little)) {
- auto *isec = make<InputSection>(*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 = make<InputSection>(*this, sec, name);
- return in.attributes;
+ // Split stacks is a feature to support a discontiguous stack,
+ // commonly used in the programming language Go. For the details,
+ // see https://gcc.gnu.org/wiki/SplitStacks. An object file compiled
+ // for split stack will include a .note.GNU-split-stack section.
+ if (name == ".note.GNU-split-stack") {
+ if (config->relocatable) {
+ error(
+ "cannot mix split-stack and non-split-stack in a relocatable link");
+ return &InputSection::discarded;
}
+ this->splitStack = true;
return &InputSection::discarded;
}
- }
- switch (sec.sh_type) {
- case SHT_LLVM_DEPENDENT_LIBRARIES: {
- if (config->relocatable)
- break;
- ArrayRef<char> data =
- CHECK(this->getObj().template getSectionContentsAsArray<char>(sec), this);
- if (!data.empty() && data.back() != '\0') {
- error(toString(this) +
- ": corrupted dependent libraries section (unterminated string): " +
- name);
+ // An object file compiled for split stack, but where some of the
+ // functions were compiled with the no_split_stack_attribute will
+ // include a .note.GNU-no-split-stack section.
+ if (name == ".note.GNU-no-split-stack") {
+ this->someNoSplitStack = true;
return &InputSection::discarded;
}
- for (const char *d = data.begin(), *e = data.end(); d < e;) {
- StringRef s(d);
- addDependentLibrary(s, this);
- d += s.size() + 1;
- }
- return &InputSection::discarded;
- }
- case SHT_RELA:
- case SHT_REL: {
- // Find a relocation target section and associate this section with that.
- // Target may have been discarded if it is in a different section group
- // and the group is discarded, even though it's a violation of the
- // spec. We handle that situation gracefully by discarding dangling
- // relocation sections.
- InputSectionBase *target = getRelocTarget(sec);
- if (!target)
- return nullptr;
-
- // ELF spec allows mergeable sections with relocations, but they are
- // rare, and it is in practice hard to merge such sections by contents,
- // because applying relocations at end of linking changes section
- // contents. So, we simply handle such sections as non-mergeable ones.
- // Degrading like this is acceptable because section merging is optional.
- if (auto *ms = dyn_cast<MergeInputSection>(target)) {
- target = toRegularSection(ms);
- this->sections[sec.sh_info] = target;
- }
-
- if (target->firstRelocation)
- fatal(toString(this) +
- ": multiple relocation sections to one section are not supported");
-
- if (sec.sh_type == SHT_RELA) {
- ArrayRef<Elf_Rela> rels = CHECK(getObj().relas(sec), this);
- target->firstRelocation = rels.begin();
- target->numRelocations = rels.size();
- target->areRelocsRela = true;
- } else {
- ArrayRef<Elf_Rel> rels = CHECK(getObj().rels(sec), this);
- target->firstRelocation = rels.begin();
- target->numRelocations = rels.size();
- target->areRelocsRela = false;
- }
- assert(isUInt<31>(target->numRelocations));
-
- // Relocation sections are usually removed from the output, so return
- // `nullptr` for the normal case. However, if -r or --emit-relocs is
- // specified, we need to copy them to the output. (Some post link analysis
- // tools specify --emit-relocs to obtain the information.)
- if (!config->relocatable && !config->emitRelocs)
- return nullptr;
- InputSection *relocSec = make<InputSection>(*this, sec, name);
- // If the relocated section is discarded (due to /DISCARD/ or
- // --gc-sections), the relocation section should be discarded as well.
- target->dependentSections.push_back(relocSec);
- return relocSec;
- }
- }
-
- // The GNU linker uses .note.GNU-stack section as a marker indicating
- // that the code in the object file does not expect that the stack is
- // executable (in terms of NX bit). If all input files have the marker,
- // the GNU linker adds a PT_GNU_STACK segment to tells the loader to
- // make the stack non-executable. Most object files have this section as
- // of 2017.
- //
- // But making the stack non-executable is a norm today for security
- // reasons. Failure to do so may result in a serious security issue.
- // Therefore, we make LLD always add PT_GNU_STACK unless it is
- // explicitly told to do otherwise (by -z execstack). Because the stack
- // executable-ness is controlled solely by command line options,
- // .note.GNU-stack sections are simply ignored.
- if (name == ".note.GNU-stack")
- return &InputSection::discarded;
-
- // Object files that use processor features such as Intel Control-Flow
- // Enforcement (CET) or AArch64 Branch Target Identification BTI, use a
- // .note.gnu.property section containing a bitfield of feature bits like the
- // GNU_PROPERTY_X86_FEATURE_1_IBT flag. Read a bitmap containing the flag.
- //
- // Since we merge bitmaps from multiple object files to create a new
- // .note.gnu.property containing a single AND'ed bitmap, we discard an input
- // file's .note.gnu.property section.
- if (name == ".note.gnu.property") {
- this->andFeatures = readAndFeatures<ELFT>(InputSection(*this, sec, name));
- return &InputSection::discarded;
- }
- // Split stacks is a feature to support a discontiguous stack,
- // commonly used in the programming language Go. For the details,
- // see https://gcc.gnu.org/wiki/SplitStacks. An object file compiled
- // for split stack will include a .note.GNU-split-stack section.
- if (name == ".note.GNU-split-stack") {
- if (config->relocatable) {
- error("cannot mix split-stack and non-split-stack in a relocatable link");
+ // Strip existing .note.gnu.build-id sections so that the output won't have
+ // more than one build-id. This is not usually a problem because input
+ // object files normally don't have .build-id sections, but you can create
+ // such files by "ld.{bfd,gold,lld} -r --build-id", and we want to guard
+ // against it.
+ if (name == ".note.gnu.build-id")
return &InputSection::discarded;
- }
- this->splitStack = true;
- return &InputSection::discarded;
- }
-
- // An object file cmpiled for split stack, but where some of the
- // functions were compiled with the no_split_stack_attribute will
- // include a .note.GNU-no-split-stack section.
- if (name == ".note.GNU-no-split-stack") {
- this->someNoSplitStack = true;
- return &InputSection::discarded;
}
- // The linkonce feature is a sort of proto-comdat. Some glibc i386 object
- // files contain definitions of symbol "__x86.get_pc_thunk.bx" in linkonce
- // sections. Drop those sections to avoid duplicate symbol errors.
- // FIXME: This is glibc PR20543, we should remove this hack once that has been
- // fixed for a while.
- if (name == ".gnu.linkonce.t.__x86.get_pc_thunk.bx" ||
- name == ".gnu.linkonce.t.__i686.get_pc_thunk.bx")
- return &InputSection::discarded;
-
- // If we are creating a new .build-id section, strip existing .build-id
- // sections so that the output won't have more than one .build-id.
- // This is not usually a problem because input object files normally don't
- // have .build-id sections, but you can create such files by
- // "ld.{bfd,gold,lld} -r --build-id", and we want to guard against it.
- if (name == ".note.gnu.build-id" && config->buildId != BuildIdKind::None)
- return &InputSection::discarded;
-
// The linker merges EH (exception handling) frames and creates a
// .eh_frame_hdr section for runtime. So we handle them with a special
// class. For relocatable outputs, they are just passed through.
if (name == ".eh_frame" && !config->relocatable)
- return make<EhInputSection>(*this, sec, name);
-
- if (shouldMerge(sec, name))
- return make<MergeInputSection>(*this, sec, name);
- return make<InputSection>(*this, sec, name);
-}
+ return makeThreadLocal<EhInputSection>(*this, sec, name);
-template <class ELFT>
-StringRef ObjFile<ELFT>::getSectionName(const Elf_Shdr &sec) {
- return CHECK(getObj().getSectionName(sec, sectionStringTable), this);
+ if ((sec.sh_flags & SHF_MERGE) && shouldMerge(sec, name))
+ return makeThreadLocal<MergeInputSection>(*this, sec, name);
+ return makeThreadLocal<InputSection>(*this, sec, name);
}
// Initialize this->Symbols. this->Symbols is a parallel array as
// its corresponding ELF symbol table.
-template <class ELFT> void ObjFile<ELFT>::initializeSymbols() {
+template <class ELFT>
+void ObjFile<ELFT>::initializeSymbols(const object::ELFFile<ELFT> &obj) {
ArrayRef<Elf_Sym> eSyms = this->getELFSyms<ELFT>();
- this->symbols.resize(eSyms.size());
-
- // Fill in InputFile::symbols. Some entries have been initialized
- // because of LazyObjFile.
- for (size_t i = 0, end = eSyms.size(); i != end; ++i) {
- if (this->symbols[i])
- continue;
- const Elf_Sym &eSym = eSyms[i];
- uint32_t secIdx = getSectionIndex(eSym);
- if (secIdx >= this->sections.size())
- fatal(toString(this) + ": invalid section index: " + Twine(secIdx));
- if (eSym.getBinding() != STB_LOCAL) {
- if (i < firstGlobal)
- error(toString(this) + ": non-local symbol (" + Twine(i) +
- ") found at index < .symtab's sh_info (" + Twine(firstGlobal) +
- ")");
- this->symbols[i] =
- symtab->insert(CHECK(eSyms[i].getName(this->stringTable), this));
- continue;
- }
-
- // Handle local symbols. Local symbols are not added to the symbol
- // table because they are not visible from other object files. We
- // allocate symbol instances and add their pointers to symbols.
- if (i >= firstGlobal)
- errorOrWarn(toString(this) + ": STB_LOCAL symbol (" + Twine(i) +
- ") found at index >= .symtab's sh_info (" +
- Twine(firstGlobal) + ")");
-
- InputSectionBase *sec = this->sections[secIdx];
- uint8_t type = eSym.getType();
- if (type == STT_FILE)
- sourceFile = CHECK(eSym.getName(this->stringTable), this);
- if (this->stringTable.size() <= eSym.st_name)
- fatal(toString(this) + ": invalid symbol name offset");
- StringRefZ name = this->stringTable.data() + eSym.st_name;
-
- if (eSym.st_shndx == SHN_UNDEF)
- this->symbols[i] =
- make<Undefined>(this, name, STB_LOCAL, eSym.st_other, type);
- else if (sec == &InputSection::discarded)
- this->symbols[i] =
- make<Undefined>(this, name, STB_LOCAL, eSym.st_other, type,
- /*discardedSecIdx=*/secIdx);
- else
- this->symbols[i] = make<Defined>(this, name, STB_LOCAL, eSym.st_other,
- type, eSym.st_value, eSym.st_size, sec);
+ if (numSymbols == 0) {
+ numSymbols = eSyms.size();
+ symbols = std::make_unique<Symbol *[]>(numSymbols);
}
- // Symbol resolution of non-local symbols.
+ // Some entries have been filled by LazyObjFile.
+ for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i)
+ if (!symbols[i])
+ symbols[i] = symtab.insert(CHECK(eSyms[i].getName(stringTable), this));
+
+ // Perform symbol resolution on non-local symbols.
SmallVector<unsigned, 32> undefineds;
for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) {
const Elf_Sym &eSym = eSyms[i];
- uint8_t binding = eSym.getBinding();
- if (binding == STB_LOCAL)
- continue; // Errored above.
+ uint32_t secIdx = eSym.st_shndx;
+ if (secIdx == SHN_UNDEF) {
+ undefineds.push_back(i);
+ continue;
+ }
- uint32_t secIdx = getSectionIndex(eSym);
- InputSectionBase *sec = this->sections[secIdx];
+ uint8_t binding = eSym.getBinding();
uint8_t stOther = eSym.st_other;
uint8_t type = eSym.getType();
uint64_t value = eSym.st_value;
uint64_t size = eSym.st_size;
- StringRefZ name = this->stringTable.data() + eSym.st_name;
-
- // Handle global undefined symbols.
- if (eSym.st_shndx == SHN_UNDEF) {
- undefineds.push_back(i);
- continue;
- }
- // Handle global common symbols.
- if (eSym.st_shndx == SHN_COMMON) {
+ Symbol *sym = symbols[i];
+ sym->isUsedInRegularObj = true;
+ if (LLVM_UNLIKELY(eSym.st_shndx == SHN_COMMON)) {
if (value == 0 || value >= UINT32_MAX)
- fatal(toString(this) + ": common symbol '" + StringRef(name.data) +
+ fatal(toString(this) + ": common symbol '" + sym->getName() +
"' has invalid alignment: " + Twine(value));
- this->symbols[i]->resolve(
- CommonSymbol{this, name, binding, stOther, type, value, size});
+ hasCommonSyms = true;
+ sym->resolve(
+ CommonSymbol{this, StringRef(), binding, stOther, type, value, size});
continue;
}
- // If a defined symbol is in a discarded section, handle it as if it
- // were an undefined symbol. Such symbol doesn't comply with the
- // standard, but in practice, a .eh_frame often directly refer
- // COMDAT member sections, and if a comdat group is discarded, some
- // defined symbol in a .eh_frame becomes dangling symbols.
- if (sec == &InputSection::discarded) {
- Undefined und{this, name, binding, stOther, type, secIdx};
- Symbol *sym = this->symbols[i];
- // !ArchiveFile::parsed or LazyObjFile::fetched means that the file
- // containing this object has not finished processing, i.e. this symbol is
- // a result of a lazy symbol fetch. We should demote the lazy symbol to an
- // Undefined so that any relocations outside of the group to it will
- // trigger a discarded section error.
- if ((sym->symbolKind == Symbol::LazyArchiveKind &&
- !cast<ArchiveFile>(sym->file)->parsed) ||
- (sym->symbolKind == Symbol::LazyObjectKind &&
- cast<LazyObjFile>(sym->file)->fetched))
- sym->replace(und);
- else
- sym->resolve(und);
- continue;
- }
-
- // Handle global defined symbols.
- if (binding == STB_GLOBAL || binding == STB_WEAK ||
- binding == STB_GNU_UNIQUE) {
- this->symbols[i]->resolve(
- Defined{this, name, binding, stOther, type, value, size, sec});
- continue;
- }
-
- fatal(toString(this) + ": unexpected binding: " + Twine((int)binding));
+ // Handle global defined symbols. Defined::section will be set in postParse.
+ sym->resolve(Defined{this, StringRef(), binding, stOther, type, value, size,
+ nullptr});
}
// Undefined symbols (excluding those defined relative to non-prevailing
- // sections) can trigger recursive fetch. Process defined symbols first so
+ // sections) can trigger recursive extract. Process defined symbols first so
// that the relative order between a defined symbol and an undefined symbol
// does not change the symbol resolution behavior. In addition, a set of
// interconnected symbols will all be resolved to the same file, instead of
// being resolved to different files.
for (unsigned i : undefineds) {
const Elf_Sym &eSym = eSyms[i];
- StringRefZ name = this->stringTable.data() + eSym.st_name;
- this->symbols[i]->resolve(Undefined{this, name, eSym.getBinding(),
- eSym.st_other, eSym.getType()});
- this->symbols[i]->referenced = true;
+ Symbol *sym = symbols[i];
+ sym->resolve(Undefined{this, StringRef(), eSym.getBinding(), eSym.st_other,
+ eSym.getType()});
+ sym->isUsedInRegularObj = true;
+ sym->referenced = true;
}
}
-ArchiveFile::ArchiveFile(std::unique_ptr<Archive> &&file)
- : InputFile(ArchiveKind, file->getMemoryBufferRef()),
- file(std::move(file)) {}
+template <class ELFT>
+void ObjFile<ELFT>::initSectionsAndLocalSyms(bool ignoreComdats) {
+ if (!justSymbols)
+ initializeSections(ignoreComdats, getObj());
-void ArchiveFile::parse() {
- for (const Archive::Symbol &sym : file->symbols())
- symtab->addSymbol(LazyArchive{*this, sym});
+ if (!firstGlobal)
+ return;
+ SymbolUnion *locals = makeThreadLocalN<SymbolUnion>(firstGlobal);
+ memset(locals, 0, sizeof(SymbolUnion) * firstGlobal);
- // Inform a future invocation of ObjFile<ELFT>::initializeSymbols() that this
- // archive has been processed.
- parsed = true;
-}
+ ArrayRef<Elf_Sym> eSyms = this->getELFSyms<ELFT>();
+ for (size_t i = 0, end = firstGlobal; i != end; ++i) {
+ const Elf_Sym &eSym = eSyms[i];
+ uint32_t secIdx = eSym.st_shndx;
+ if (LLVM_UNLIKELY(secIdx == SHN_XINDEX))
+ secIdx = check(getExtendedSymbolTableIndex<ELFT>(eSym, i, shndxTable));
+ else if (secIdx >= SHN_LORESERVE)
+ secIdx = 0;
+ if (LLVM_UNLIKELY(secIdx >= sections.size()))
+ fatal(toString(this) + ": invalid section index: " + Twine(secIdx));
+ if (LLVM_UNLIKELY(eSym.getBinding() != STB_LOCAL))
+ error(toString(this) + ": non-local symbol (" + Twine(i) +
+ ") found at index < .symtab's sh_info (" + Twine(end) + ")");
+
+ InputSectionBase *sec = sections[secIdx];
+ uint8_t type = eSym.getType();
+ if (type == STT_FILE)
+ sourceFile = CHECK(eSym.getName(stringTable), this);
+ if (LLVM_UNLIKELY(stringTable.size() <= eSym.st_name))
+ fatal(toString(this) + ": invalid symbol name offset");
+ StringRef name(stringTable.data() + eSym.st_name);
-// Returns a buffer pointing to a member file containing a given symbol.
-void ArchiveFile::fetch(const Archive::Symbol &sym) {
- Archive::Child c =
- CHECK(sym.getMember(), toString(this) +
- ": could not get the member for symbol " +
- toELFString(sym));
+ symbols[i] = reinterpret_cast<Symbol *>(locals + i);
+ if (eSym.st_shndx == SHN_UNDEF || sec == &InputSection::discarded)
+ new (symbols[i]) Undefined(this, name, STB_LOCAL, eSym.st_other, type,
+ /*discardedSecIdx=*/secIdx);
+ else
+ new (symbols[i]) Defined(this, name, STB_LOCAL, eSym.st_other, type,
+ eSym.st_value, eSym.st_size, sec);
+ symbols[i]->partition = 1;
+ symbols[i]->isUsedInRegularObj = true;
+ }
+}
- if (!seen.insert(c.getChildOffset()).second)
- return;
+// Called after all ObjFile::parse is called for all ObjFiles. This checks
+// duplicate symbols and may do symbol property merge in the future.
+template <class ELFT> void ObjFile<ELFT>::postParse() {
+ static std::mutex mu;
+ ArrayRef<Elf_Sym> eSyms = this->getELFSyms<ELFT>();
+ for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) {
+ const Elf_Sym &eSym = eSyms[i];
+ Symbol &sym = *symbols[i];
+ uint32_t secIdx = eSym.st_shndx;
+ uint8_t binding = eSym.getBinding();
+ if (LLVM_UNLIKELY(binding != STB_GLOBAL && binding != STB_WEAK &&
+ binding != STB_GNU_UNIQUE))
+ errorOrWarn(toString(this) + ": symbol (" + Twine(i) +
+ ") has invalid binding: " + Twine((int)binding));
+
+ // st_value of STT_TLS represents the assigned offset, not the actual
+ // address which is used by STT_FUNC and STT_OBJECT. STT_TLS symbols can
+ // only be referenced by special TLS relocations. It is usually an error if
+ // a STT_TLS symbol is replaced by a non-STT_TLS symbol, vice versa.
+ if (LLVM_UNLIKELY(sym.isTls()) && eSym.getType() != STT_TLS &&
+ eSym.getType() != STT_NOTYPE)
+ errorOrWarn("TLS attribute mismatch: " + toString(sym) + "\n>>> in " +
+ toString(sym.file) + "\n>>> in " + toString(this));
+
+ // Handle non-COMMON defined symbol below. !sym.file allows a symbol
+ // assignment to redefine a symbol without an error.
+ if (!sym.file || !sym.isDefined() || secIdx == SHN_UNDEF ||
+ secIdx == SHN_COMMON)
+ continue;
- MemoryBufferRef mb =
- CHECK(c.getMemoryBufferRef(),
- toString(this) +
- ": could not get the buffer for the member defining symbol " +
- toELFString(sym));
+ if (LLVM_UNLIKELY(secIdx == SHN_XINDEX))
+ secIdx = check(getExtendedSymbolTableIndex<ELFT>(eSym, i, shndxTable));
+ else if (secIdx >= SHN_LORESERVE)
+ secIdx = 0;
+ if (LLVM_UNLIKELY(secIdx >= sections.size()))
+ fatal(toString(this) + ": invalid section index: " + Twine(secIdx));
+ InputSectionBase *sec = sections[secIdx];
+ if (sec == &InputSection::discarded) {
+ if (sym.traced) {
+ printTraceSymbol(Undefined{this, sym.getName(), sym.binding,
+ sym.stOther, sym.type, secIdx},
+ sym.getName());
+ }
+ if (sym.file == this) {
+ std::lock_guard<std::mutex> lock(mu);
+ ctx.nonPrevailingSyms.emplace_back(&sym, secIdx);
+ }
+ continue;
+ }
- if (tar && c.getParent()->isThin())
- tar->append(relativeToRoot(CHECK(c.getFullName(), this)), mb.getBuffer());
+ if (sym.file == this) {
+ cast<Defined>(sym).section = sec;
+ continue;
+ }
- InputFile *file = createObjectFile(mb, getName(), c.getChildOffset());
- file->groupId = groupId;
- parseFile(file);
+ if (sym.binding == STB_WEAK || binding == STB_WEAK)
+ continue;
+ std::lock_guard<std::mutex> lock(mu);
+ ctx.duplicates.push_back({&sym, this, sec, eSym.st_value});
+ }
}
// The handling of tentative definitions (COMMON symbols) in archives is murky.
}
template <class ELFT>
-static bool isNonCommonDef(MemoryBufferRef mb, StringRef symName,
+static bool isNonCommonDef(ELFKind ekind, MemoryBufferRef mb, StringRef symName,
StringRef archiveName) {
- ObjFile<ELFT> *obj = make<ObjFile<ELFT>>(mb, archiveName);
+ ObjFile<ELFT> *obj = make<ObjFile<ELFT>>(ekind, mb, archiveName);
+ obj->init();
StringRef stringtable = obj->getStringTable();
for (auto sym : obj->template getGlobalELFSyms<ELFT>()) {
StringRef archiveName) {
switch (getELFKind(mb, archiveName)) {
case ELF32LEKind:
- return isNonCommonDef<ELF32LE>(mb, symName, archiveName);
+ return isNonCommonDef<ELF32LE>(ELF32LEKind, mb, symName, archiveName);
case ELF32BEKind:
- return isNonCommonDef<ELF32BE>(mb, symName, archiveName);
+ return isNonCommonDef<ELF32BE>(ELF32BEKind, mb, symName, archiveName);
case ELF64LEKind:
- return isNonCommonDef<ELF64LE>(mb, symName, archiveName);
+ return isNonCommonDef<ELF64LE>(ELF64LEKind, mb, symName, archiveName);
case ELF64BEKind:
- return isNonCommonDef<ELF64BE>(mb, symName, archiveName);
+ return isNonCommonDef<ELF64BE>(ELF64BEKind, mb, symName, archiveName);
default:
llvm_unreachable("getELFKind");
}
}
-bool ArchiveFile::shouldFetchForCommon(const Archive::Symbol &sym) {
- Archive::Child c =
- CHECK(sym.getMember(), toString(this) +
- ": could not get the member for symbol " +
- toELFString(sym));
- MemoryBufferRef mb =
- CHECK(c.getMemoryBufferRef(),
- toString(this) +
- ": could not get the buffer for the member defining symbol " +
- toELFString(sym));
-
- if (isBitcode(mb))
- return isBitcodeNonCommonDef(mb, sym.getName(), getName());
-
- return isNonCommonDef(mb, sym.getName(), getName());
-}
-
-size_t ArchiveFile::getMemberCount() const {
- size_t count = 0;
- Error err = Error::success();
- for (const Archive::Child &c : file->children(err)) {
- (void)c;
- ++count;
- }
- // This function is used by --print-archive-stats=, where an error does not
- // really matter.
- consumeError(std::move(err));
- return count;
-}
-
unsigned SharedFile::vernauxNum;
+SharedFile::SharedFile(MemoryBufferRef m, StringRef defaultSoName)
+ : ELFFileBase(SharedKind, getELFKind(m, ""), m), soName(defaultSoName),
+ isNeeded(!config->asNeeded) {}
+
// Parse the version definitions in the object file if present, and return a
// vector whose nth element contains a pointer to the Elf_Verdef for version
// identifier n. Version identifiers that are not definitions map to nullptr.
template <typename ELFT>
-static std::vector<const void *> parseVerdefs(const uint8_t *base,
- const typename ELFT::Shdr *sec) {
+static SmallVector<const void *, 0>
+parseVerdefs(const uint8_t *base, const typename ELFT::Shdr *sec) {
if (!sec)
return {};
- // We cannot determine the largest verdef identifier without inspecting
- // every Elf_Verdef, but both bfd and gold assign verdef identifiers
- // sequentially starting from 1, so we predict that the largest identifier
- // will be verdefCount.
- unsigned verdefCount = sec->sh_info;
- std::vector<const void *> verdefs(verdefCount + 1);
-
// Build the Verdefs array by following the chain of Elf_Verdef objects
// from the start of the .gnu.version_d section.
+ SmallVector<const void *, 0> verdefs;
const uint8_t *verdef = base + sec->sh_offset;
- for (unsigned i = 0; i != verdefCount; ++i) {
+ for (unsigned i = 0, e = sec->sh_info; i != e; ++i) {
auto *curVerdef = reinterpret_cast<const typename ELFT::Verdef *>(verdef);
verdef += curVerdef->vd_next;
unsigned verdefIndex = curVerdef->vd_ndx;
- verdefs.resize(verdefIndex + 1);
+ if (verdefIndex >= verdefs.size())
+ verdefs.resize(verdefIndex + 1);
verdefs[verdefIndex] = curVerdef;
}
return verdefs;
ArrayRef<Elf_Dyn> dynamicTags;
const ELFFile<ELFT> obj = this->getObj<ELFT>();
- ArrayRef<Elf_Shdr> sections = CHECK(obj.sections(), this);
+ ArrayRef<Elf_Shdr> sections = getELFShdrs<ELFT>();
StringRef sectionStringTable =
CHECK(obj.getSectionStringTable(sections), this);
}
// DSOs are uniquified not by filename but by soname.
- DenseMap<StringRef, SharedFile *>::iterator it;
+ DenseMap<CachedHashStringRef, SharedFile *>::iterator it;
bool wasInserted;
- std::tie(it, wasInserted) = symtab->soNames.try_emplace(soName, this);
+ std::tie(it, wasInserted) =
+ symtab.soNames.try_emplace(CachedHashStringRef(soName), this);
// If a DSO appears more than once on the command line with and without
// --as-needed, --no-as-needed takes precedence over --as-needed because a
if (!wasInserted)
return;
- sharedFiles.push_back(this);
+ ctx.sharedFiles.push_back(this);
verdefs = parseVerdefs<ELFT>(obj.base(), verdefSec);
std::vector<uint32_t> verneeds = parseVerneed<ELFT>(obj, verneedSec);
// Add symbols to the symbol table.
ArrayRef<Elf_Sym> syms = this->getGlobalELFSyms<ELFT>();
- for (size_t i = 0; i < syms.size(); ++i) {
+ for (size_t i = 0, e = syms.size(); i != e; ++i) {
const Elf_Sym &sym = syms[i];
// ELF spec requires that all local symbols precede weak or global
// symbols in each symbol table, and the index of first non-local symbol
// is stored to sh_info. If a local symbol appears after some non-local
// symbol, that's a violation of the spec.
- StringRef name = CHECK(sym.getName(this->stringTable), this);
+ StringRef name = CHECK(sym.getName(stringTable), this);
if (sym.getBinding() == STB_LOCAL) {
- warn("found local symbol '" + name +
- "' in global part of symbol table in file " + toString(this));
+ errorOrWarn(toString(this) + ": invalid local symbol '" + name +
+ "' in global part of symbol table");
continue;
}
- uint16_t idx = versyms[i] & ~VERSYM_HIDDEN;
+ const uint16_t ver = versyms[i], idx = ver & ~VERSYM_HIDDEN;
if (sym.isUndefined()) {
// For unversioned undefined symbols, VER_NDX_GLOBAL makes more sense but
// as of binutils 2.34, GNU ld produces VER_NDX_LOCAL.
- if (idx != VER_NDX_LOCAL && idx != VER_NDX_GLOBAL) {
+ if (ver != VER_NDX_LOCAL && ver != VER_NDX_GLOBAL) {
if (idx >= verneeds.size()) {
error("corrupt input file: version need index " + Twine(idx) +
" for symbol " + name + " is out of bounds\n>>> defined in " +
toString(this));
continue;
}
- StringRef verName = this->stringTable.data() + verneeds[idx];
+ StringRef verName = stringTable.data() + verneeds[idx];
versionedNameBuffer.clear();
- name =
- saver.save((name + "@" + verName).toStringRef(versionedNameBuffer));
+ name = saver().save(
+ (name + "@" + verName).toStringRef(versionedNameBuffer));
}
- Symbol *s = symtab->addSymbol(
+ Symbol *s = symtab.addSymbol(
Undefined{this, name, sym.getBinding(), sym.st_other, sym.getType()});
s->exportDynamic = true;
if (s->isUndefined() && sym.getBinding() != STB_WEAK &&
continue;
}
- // MIPS BFD linker puts _gp_disp symbol into DSO files and incorrectly
- // assigns VER_NDX_LOCAL to this section global symbol. Here is a
- // workaround for this bug.
- if (config->emachine == EM_MIPS && idx == VER_NDX_LOCAL &&
- name == "_gp_disp")
+ if (ver == VER_NDX_LOCAL ||
+ (ver != VER_NDX_GLOBAL && idx >= verdefs.size())) {
+ // In GNU ld < 2.31 (before 3be08ea4728b56d35e136af4e6fd3086ade17764), the
+ // MIPS port puts _gp_disp symbol into DSO files and incorrectly assigns
+ // VER_NDX_LOCAL. Workaround this bug.
+ if (config->emachine == EM_MIPS && name == "_gp_disp")
+ continue;
+ error("corrupt input file: version definition index " + Twine(idx) +
+ " for symbol " + name + " is out of bounds\n>>> defined in " +
+ toString(this));
continue;
+ }
uint32_t alignment = getAlignment<ELFT>(sections, sym);
- if (!(versyms[i] & VERSYM_HIDDEN)) {
- symtab->addSymbol(SharedSymbol{*this, name, sym.getBinding(),
- sym.st_other, sym.getType(), sym.st_value,
- sym.st_size, alignment, idx});
+ if (ver == idx) {
+ auto *s = symtab.addSymbol(
+ SharedSymbol{*this, name, sym.getBinding(), sym.st_other,
+ sym.getType(), sym.st_value, sym.st_size, alignment});
+ if (s->file == this)
+ s->verdefIndex = ver;
}
// Also add the symbol with the versioned name to handle undefined symbols
// with explicit versions.
- if (idx == VER_NDX_GLOBAL)
- continue;
-
- if (idx >= verdefs.size() || idx == VER_NDX_LOCAL) {
- error("corrupt input file: version definition index " + Twine(idx) +
- " for symbol " + name + " is out of bounds\n>>> defined in " +
- toString(this));
+ if (ver == VER_NDX_GLOBAL)
continue;
- }
StringRef verName =
- this->stringTable.data() +
+ stringTable.data() +
reinterpret_cast<const Elf_Verdef *>(verdefs[idx])->getAux()->vda_name;
versionedNameBuffer.clear();
name = (name + "@" + verName).toStringRef(versionedNameBuffer);
- symtab->addSymbol(SharedSymbol{*this, saver.save(name), sym.getBinding(),
- sym.st_other, sym.getType(), sym.st_value,
- sym.st_size, alignment, idx});
+ auto *s = symtab.addSymbol(
+ SharedSymbol{*this, saver().save(name), sym.getBinding(), sym.st_other,
+ sym.getType(), sym.st_value, sym.st_size, alignment});
+ if (s->file == this)
+ s->verdefIndex = idx;
}
}
return EM_ARM;
case Triple::avr:
return EM_AVR;
+ case Triple::hexagon:
+ return EM_HEXAGON;
case Triple::mips:
case Triple::mipsel:
case Triple::mips64:
}
BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
- uint64_t offsetInArchive)
+ uint64_t offsetInArchive, bool lazy)
: InputFile(BitcodeKind, mb) {
- this->archiveName = std::string(archiveName);
+ this->archiveName = archiveName;
+ this->lazy = lazy;
std::string path = mb.getBufferIdentifier().str();
if (config->thinLTOIndexOnly)
// into consideration at LTO time (which very likely causes undefined
// symbols later in the link stage). So we append file offset to make
// filename unique.
- StringRef name =
- archiveName.empty()
- ? saver.save(path)
- : saver.save(archiveName + "(" + path::filename(path) + " at " +
- utostr(offsetInArchive) + ")");
+ StringRef name = archiveName.empty()
+ ? saver().save(path)
+ : saver().save(archiveName + "(" + path::filename(path) +
+ " at " + utostr(offsetInArchive) + ")");
MemoryBufferRef mbref(mb.getBuffer(), name);
obj = CHECK(lto::InputFile::create(mbref), this);
llvm_unreachable("unknown visibility");
}
-template <class ELFT>
-static Symbol *createBitcodeSymbol(const std::vector<bool> &keptComdats,
- const lto::InputFile::Symbol &objSym,
- BitcodeFile &f) {
- StringRef name = saver.save(objSym.getName());
+static void
+createBitcodeSymbol(Symbol *&sym, const std::vector<bool> &keptComdats,
+ const lto::InputFile::Symbol &objSym, BitcodeFile &f) {
uint8_t binding = objSym.isWeak() ? STB_WEAK : STB_GLOBAL;
uint8_t type = objSym.isTLS() ? STT_TLS : STT_NOTYPE;
uint8_t visibility = mapVisibility(objSym.getVisibility());
- bool canOmitFromDynSym = objSym.canBeOmittedFromSymbolTable();
+
+ if (!sym)
+ sym = symtab.insert(saver().save(objSym.getName()));
int c = objSym.getComdatIndex();
if (objSym.isUndefined() || (c != -1 && !keptComdats[c])) {
- Undefined newSym(&f, name, binding, visibility, type);
- if (canOmitFromDynSym)
- newSym.exportDynamic = false;
- Symbol *ret = symtab->addSymbol(newSym);
- ret->referenced = true;
- return ret;
+ Undefined newSym(&f, StringRef(), binding, visibility, type);
+ sym->resolve(newSym);
+ sym->referenced = true;
+ return;
}
- if (objSym.isCommon())
- return symtab->addSymbol(
- CommonSymbol{&f, name, binding, visibility, STT_OBJECT,
- objSym.getCommonAlignment(), objSym.getCommonSize()});
-
- Defined newSym(&f, name, binding, visibility, type, 0, 0, nullptr);
- if (canOmitFromDynSym)
- newSym.exportDynamic = false;
- return symtab->addSymbol(newSym);
+ if (objSym.isCommon()) {
+ sym->resolve(CommonSymbol{&f, StringRef(), binding, visibility, STT_OBJECT,
+ objSym.getCommonAlignment(),
+ objSym.getCommonSize()});
+ } else {
+ Defined newSym(&f, StringRef(), binding, visibility, type, 0, 0, nullptr);
+ if (objSym.canBeOmittedFromSymbolTable())
+ newSym.exportDynamic = false;
+ sym->resolve(newSym);
+ }
}
-template <class ELFT> void BitcodeFile::parse() {
- std::vector<bool> keptComdats;
+void BitcodeFile::parse() {
for (std::pair<StringRef, Comdat::SelectionKind> s : obj->getComdatTable()) {
keptComdats.push_back(
s.second == Comdat::NoDeduplicate ||
- symtab->comdatGroups.try_emplace(CachedHashStringRef(s.first), this)
+ symtab.comdatGroups.try_emplace(CachedHashStringRef(s.first), this)
.second);
}
- for (const lto::InputFile::Symbol &objSym : obj->symbols())
- symbols.push_back(createBitcodeSymbol<ELFT>(keptComdats, objSym, *this));
+ if (numSymbols == 0) {
+ numSymbols = obj->symbols().size();
+ symbols = std::make_unique<Symbol *[]>(numSymbols);
+ }
+ // Process defined symbols first. See the comment in
+ // ObjFile<ELFT>::initializeSymbols.
+ for (auto [i, irSym] : llvm::enumerate(obj->symbols()))
+ if (!irSym.isUndefined())
+ createBitcodeSymbol(symbols[i], keptComdats, irSym, *this);
+ for (auto [i, irSym] : llvm::enumerate(obj->symbols()))
+ if (irSym.isUndefined())
+ createBitcodeSymbol(symbols[i], keptComdats, irSym, *this);
for (auto l : obj->getDependentLibraries())
addDependentLibrary(l, this);
}
+void BitcodeFile::parseLazy() {
+ numSymbols = obj->symbols().size();
+ symbols = std::make_unique<Symbol *[]>(numSymbols);
+ for (auto [i, irSym] : llvm::enumerate(obj->symbols()))
+ if (!irSym.isUndefined()) {
+ auto *sym = symtab.insert(saver().save(irSym.getName()));
+ sym->resolve(LazyObject{*this});
+ symbols[i] = sym;
+ }
+}
+
+void BitcodeFile::postParse() {
+ for (auto [i, irSym] : llvm::enumerate(obj->symbols())) {
+ const Symbol &sym = *symbols[i];
+ if (sym.file == this || !sym.isDefined() || irSym.isUndefined() ||
+ irSym.isCommon() || irSym.isWeak())
+ continue;
+ int c = irSym.getComdatIndex();
+ if (c != -1 && !keptComdats[c])
+ continue;
+ reportDuplicate(sym, this, nullptr, 0);
+ }
+}
+
void BinaryFile::parse() {
ArrayRef<uint8_t> data = arrayRefFromStringRef(mb.getBuffer());
auto *section = make<InputSection>(this, SHF_ALLOC | SHF_WRITE, SHT_PROGBITS,
if (!isAlnum(s[i]))
s[i] = '_';
- symtab->addSymbol(Defined{nullptr, saver.save(s + "_start"), STB_GLOBAL,
- STV_DEFAULT, STT_OBJECT, 0, 0, section});
- symtab->addSymbol(Defined{nullptr, saver.save(s + "_end"), STB_GLOBAL,
- STV_DEFAULT, STT_OBJECT, data.size(), 0, section});
- symtab->addSymbol(Defined{nullptr, saver.save(s + "_size"), STB_GLOBAL,
- STV_DEFAULT, STT_OBJECT, data.size(), 0, nullptr});
+ llvm::StringSaver &saver = lld::saver();
+
+ symtab.addAndCheckDuplicate(Defined{nullptr, saver.save(s + "_start"),
+ STB_GLOBAL, STV_DEFAULT, STT_OBJECT, 0, 0,
+ section});
+ symtab.addAndCheckDuplicate(Defined{nullptr, saver.save(s + "_end"),
+ STB_GLOBAL, STV_DEFAULT, STT_OBJECT,
+ data.size(), 0, section});
+ symtab.addAndCheckDuplicate(Defined{nullptr, saver.save(s + "_size"),
+ STB_GLOBAL, STV_DEFAULT, STT_OBJECT,
+ data.size(), 0, nullptr});
}
-InputFile *elf::createObjectFile(MemoryBufferRef mb, StringRef archiveName,
- uint64_t offsetInArchive) {
- if (isBitcode(mb))
- return make<BitcodeFile>(mb, archiveName, offsetInArchive);
-
+ELFFileBase *elf::createObjFile(MemoryBufferRef mb, StringRef archiveName,
+ bool lazy) {
+ ELFFileBase *f;
switch (getELFKind(mb, archiveName)) {
case ELF32LEKind:
- return make<ObjFile<ELF32LE>>(mb, archiveName);
+ f = make<ObjFile<ELF32LE>>(ELF32LEKind, mb, archiveName);
+ break;
case ELF32BEKind:
- return make<ObjFile<ELF32BE>>(mb, archiveName);
+ f = make<ObjFile<ELF32BE>>(ELF32BEKind, mb, archiveName);
+ break;
case ELF64LEKind:
- return make<ObjFile<ELF64LE>>(mb, archiveName);
+ f = make<ObjFile<ELF64LE>>(ELF64LEKind, mb, archiveName);
+ break;
case ELF64BEKind:
- return make<ObjFile<ELF64BE>>(mb, archiveName);
+ f = make<ObjFile<ELF64BE>>(ELF64BEKind, mb, archiveName);
+ break;
default:
llvm_unreachable("getELFKind");
}
+ f->init();
+ f->lazy = lazy;
+ return f;
}
-void LazyObjFile::fetch() {
- if (fetched)
- return;
- fetched = true;
-
- InputFile *file = createObjectFile(mb, archiveName, offsetInArchive);
- file->groupId = groupId;
-
- // Copy symbol vector so that the new InputFile doesn't have to
- // insert the same defined symbols to the symbol table again.
- file->symbols = std::move(symbols);
-
- parseFile(file);
-}
-
-template <class ELFT> void LazyObjFile::parse() {
- using Elf_Sym = typename ELFT::Sym;
-
- // A lazy object file wraps either a bitcode file or an ELF file.
- if (isBitcode(this->mb)) {
- std::unique_ptr<lto::InputFile> obj =
- CHECK(lto::InputFile::create(this->mb), this);
- for (const lto::InputFile::Symbol &sym : obj->symbols()) {
- if (sym.isUndefined())
- continue;
- symtab->addSymbol(LazyObject{*this, saver.save(sym.getName())});
- }
- return;
- }
-
- if (getELFKind(this->mb, archiveName) != config->ekind) {
- error("incompatible file: " + this->mb.getBufferIdentifier());
- return;
- }
-
- // Find a symbol table.
- ELFFile<ELFT> obj = check(ELFFile<ELFT>::create(mb.getBuffer()));
- ArrayRef<typename ELFT::Shdr> sections = CHECK(obj.sections(), this);
+template <class ELFT> void ObjFile<ELFT>::parseLazy() {
+ const ArrayRef<typename ELFT::Sym> eSyms = this->getELFSyms<ELFT>();
+ numSymbols = eSyms.size();
+ symbols = std::make_unique<Symbol *[]>(numSymbols);
- for (const typename ELFT::Shdr &sec : sections) {
- if (sec.sh_type != SHT_SYMTAB)
+ // resolve() may trigger this->extract() if an existing symbol is an undefined
+ // symbol. If that happens, this function has served its purpose, and we can
+ // exit from the loop early.
+ for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) {
+ if (eSyms[i].st_shndx == SHN_UNDEF)
continue;
-
- // A symbol table is found.
- ArrayRef<Elf_Sym> eSyms = CHECK(obj.symbols(&sec), this);
- uint32_t firstGlobal = sec.sh_info;
- StringRef strtab = CHECK(obj.getStringTableForSymtab(sec, sections), this);
- this->symbols.resize(eSyms.size());
-
- // Get existing symbols or insert placeholder symbols.
- for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i)
- if (eSyms[i].st_shndx != SHN_UNDEF)
- this->symbols[i] = symtab->insert(CHECK(eSyms[i].getName(strtab), this));
-
- // Replace existing symbols with LazyObject symbols.
- //
- // resolve() may trigger this->fetch() if an existing symbol is an
- // undefined symbol. If that happens, this LazyObjFile has served
- // its purpose, and we can exit from the loop early.
- for (Symbol *sym : this->symbols) {
- if (!sym)
- continue;
- sym->resolve(LazyObject{*this, sym->getName()});
-
- // If fetched, stop iterating because this->symbols has been transferred
- // to the instantiated ObjFile.
- if (fetched)
- return;
- }
- return;
+ symbols[i] = symtab.insert(CHECK(eSyms[i].getName(stringTable), this));
+ symbols[i]->resolve(LazyObject{*this});
+ if (!lazy)
+ break;
}
}
-bool LazyObjFile::shouldFetchForCommon(const StringRef &name) {
- if (isBitcode(mb))
+bool InputFile::shouldExtractForCommon(StringRef name) {
+ if (isa<BitcodeFile>(this))
return isBitcodeNonCommonDef(mb, name, archiveName);
return isNonCommonDef(mb, name, archiveName);
}
std::string elf::replaceThinLTOSuffix(StringRef path) {
- StringRef suffix = config->thinLTOObjectSuffixReplace.first;
- StringRef repl = config->thinLTOObjectSuffixReplace.second;
-
+ auto [suffix, repl] = config->thinLTOObjectSuffixReplace;
if (path.consume_back(suffix))
return (path + repl).str();
return std::string(path);
}
-template void BitcodeFile::parse<ELF32LE>();
-template void BitcodeFile::parse<ELF32BE>();
-template void BitcodeFile::parse<ELF64LE>();
-template void BitcodeFile::parse<ELF64BE>();
-
-template void LazyObjFile::parse<ELF32LE>();
-template void LazyObjFile::parse<ELF32BE>();
-template void LazyObjFile::parse<ELF64LE>();
-template void LazyObjFile::parse<ELF64BE>();
-
template class elf::ObjFile<ELF32LE>;
template class elf::ObjFile<ELF32BE>;
template class elf::ObjFile<ELF64LE>;
#define LLD_ELF_INPUT_FILES_H
#include "Config.h"
+#include "Symbols.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/LLVM.h"
#include "lld/Common/Reproduce.h"
-#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/DenseSet.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/IR/Comdat.h"
-#include "llvm/Object/Archive.h"
+#include "llvm/BinaryFormat/Magic.h"
#include "llvm/Object/ELF.h"
-#include "llvm/Object/IRObjectFile.h"
+#include "llvm/Support/MemoryBufferRef.h"
#include "llvm/Support/Threading.h"
-#include <map>
namespace llvm {
struct DILineInfo;
namespace elf {
-using llvm::object::Archive;
-
+class InputSection;
class Symbol;
-// If -reproduce option is given, all input files are written
-// to this tar archive.
+// If --reproduce is specified, all input files are written to this tar archive.
extern std::unique_ptr<llvm::TarWriter> tar;
// Opens a given file.
-llvm::Optional<MemoryBufferRef> readFile(StringRef path);
+std::optional<MemoryBufferRef> readFile(StringRef path);
// Add symbols in File to the symbol table.
void parseFile(InputFile *file);
// The root class of input files.
class InputFile {
+protected:
+ std::unique_ptr<Symbol *[]> symbols;
+ uint32_t numSymbols = 0;
+ SmallVector<InputSectionBase *, 0> sections;
+
public:
- enum Kind {
+ enum Kind : uint8_t {
ObjKind,
SharedKind,
- LazyObjKind,
ArchiveKind,
BitcodeKind,
BinaryKind,
// Returns object file symbols. It is a runtime error to call this
// function on files of other types.
- ArrayRef<Symbol *> getSymbols() { return getMutableSymbols(); }
-
- MutableArrayRef<Symbol *> getMutableSymbols() {
+ ArrayRef<Symbol *> getSymbols() const {
assert(fileKind == BinaryKind || fileKind == ObjKind ||
fileKind == BitcodeKind);
- return symbols;
+ return {symbols.get(), numSymbols};
}
// Get filename to use for linker script processing.
StringRef getNameForScript() const;
- // If not empty, this stores the name of the archive containing this file.
- // We use this string for creating error messages.
- std::string archiveName;
+ // Check if a non-common symbol should be extracted to override a common
+ // definition.
+ bool shouldExtractForCommon(StringRef name);
+
+ // .got2 in the current file. This is used by PPC32 -fPIC/-fPIE to compute
+ // offsets in PLT call stubs.
+ InputSection *ppc32Got2 = nullptr;
+
+ // Index of MIPS GOT built for this file.
+ uint32_t mipsGotIndex = -1;
+
+ // groupId is used for --warn-backrefs which is an optional error
+ // checking feature. All files within the same --{start,end}-group or
+ // --{start,end}-lib get the same group ID. Otherwise, each file gets a new
+ // group ID. For more info, see checkDependency() in SymbolTable.cpp.
+ uint32_t groupId;
+ static bool isInGroup;
+ static uint32_t nextGroupId;
// If this is an architecture-specific file, the following members
// have ELF type (i.e. ELF{32,64}{LE,BE}) and target machine type.
- ELFKind ekind = ELFNoneKind;
uint16_t emachine = llvm::ELF::EM_NONE;
+ const Kind fileKind;
+ ELFKind ekind = ELFNoneKind;
uint8_t osabi = 0;
uint8_t abiVersion = 0;
- // Cache for toString(). Only toString() should use this member.
- mutable std::string toStringCache;
-
- std::string getSrcMsg(const Symbol &sym, InputSectionBase &sec,
- uint64_t offset);
+ // True if this is a relocatable object file/bitcode file between --start-lib
+ // and --end-lib.
+ bool lazy = false;
// True if this is an argument for --just-symbols. Usually false.
bool justSymbols = false;
- // outSecOff of .got2 in the current file. This is used by PPC32 -fPIC/-fPIE
- // to compute offsets in PLT call stubs.
- uint32_t ppc32Got2OutSecOff = 0;
+ std::string getSrcMsg(const Symbol &sym, InputSectionBase &sec,
+ uint64_t offset);
// On PPC64 we need to keep track of which files contain small code model
// relocations that access the .toc section. To minimize the chance of a
// R_PPC64_TLSLD. Disable TLS relaxation to avoid bad code generation.
bool ppc64DisableTLSRelax = false;
- // groupId is used for --warn-backrefs which is an optional error
- // checking feature. All files within the same --{start,end}-group or
- // --{start,end}-lib get the same group ID. Otherwise, each file gets a new
- // group ID. For more info, see checkDependency() in SymbolTable.cpp.
- uint32_t groupId;
- static bool isInGroup;
- static uint32_t nextGroupId;
-
- // Index of MIPS GOT built for this file.
- llvm::Optional<size_t> mipsGotIndex;
-
- std::vector<Symbol *> symbols;
-
protected:
InputFile(Kind k, MemoryBufferRef m);
- std::vector<InputSectionBase *> sections;
-private:
- const Kind fileKind;
+public:
+ // If not empty, this stores the name of the archive containing this file.
+ // We use this string for creating error messages.
+ SmallString<0> archiveName;
+ // Cache for toString(). Only toString() should use this member.
+ mutable SmallString<0> toStringCache;
+private:
// Cache for getNameForScript().
- mutable std::string nameForScriptCache;
+ mutable SmallString<0> nameForScriptCache;
};
class ELFFileBase : public InputFile {
public:
- ELFFileBase(Kind k, MemoryBufferRef m);
+ ELFFileBase(Kind k, ELFKind ekind, MemoryBufferRef m);
static bool classof(const InputFile *f) { return f->isElf(); }
+ void init();
template <typename ELFT> llvm::object::ELFFile<ELFT> getObj() const {
return check(llvm::object::ELFFile<ELFT>::create(mb.getBuffer()));
}
StringRef getStringTable() const { return stringTable; }
+ ArrayRef<Symbol *> getLocalSymbols() {
+ if (numSymbols == 0)
+ return {};
+ return llvm::ArrayRef(symbols.get() + 1, firstGlobal - 1);
+ }
+ ArrayRef<Symbol *> getGlobalSymbols() {
+ return llvm::ArrayRef(symbols.get() + firstGlobal,
+ numSymbols - firstGlobal);
+ }
+ MutableArrayRef<Symbol *> getMutableGlobalSymbols() {
+ return llvm::MutableArrayRef(symbols.get() + firstGlobal,
+ numSymbols - firstGlobal);
+ }
+
+ template <typename ELFT> typename ELFT::ShdrRange getELFShdrs() const {
+ return typename ELFT::ShdrRange(
+ reinterpret_cast<const typename ELFT::Shdr *>(elfShdrs), numELFShdrs);
+ }
template <typename ELFT> typename ELFT::SymRange getELFSyms() const {
return typename ELFT::SymRange(
reinterpret_cast<const typename ELFT::Sym *>(elfSyms), numELFSyms);
protected:
// Initializes this class's member variables.
- template <typename ELFT> void init();
+ template <typename ELFT> void init(InputFile::Kind k);
+ StringRef stringTable;
+ const void *elfShdrs = nullptr;
const void *elfSyms = nullptr;
- size_t numELFSyms = 0;
+ uint32_t numELFShdrs = 0;
+ uint32_t numELFSyms = 0;
uint32_t firstGlobal = 0;
- StringRef stringTable;
+
+public:
+ uint32_t andFeatures = 0;
+ bool hasCommonSyms = false;
};
// .o file.
return this->ELFFileBase::getObj<ELFT>();
}
- ArrayRef<Symbol *> getLocalSymbols();
- ArrayRef<Symbol *> getGlobalSymbols();
-
- ObjFile(MemoryBufferRef m, StringRef archiveName) : ELFFileBase(ObjKind, m) {
- this->archiveName = std::string(archiveName);
+ ObjFile(ELFKind ekind, MemoryBufferRef m, StringRef archiveName)
+ : ELFFileBase(ObjKind, ekind, m) {
+ this->archiveName = archiveName;
}
void parse(bool ignoreComdats = false);
+ void parseLazy();
StringRef getShtGroupSignature(ArrayRef<Elf_Shdr> sections,
const Elf_Shdr &sec);
Symbol &getSymbol(uint32_t symbolIndex) const {
- if (symbolIndex >= this->symbols.size())
+ if (symbolIndex >= numSymbols)
fatal(toString(this) + ": invalid symbol index");
return *this->symbols[symbolIndex];
}
return getSymbol(symIndex);
}
- llvm::Optional<llvm::DILineInfo> getDILineInfo(InputSectionBase *, uint64_t);
- llvm::Optional<std::pair<std::string, unsigned>> getVariableLoc(StringRef name);
-
- // MIPS GP0 value defined by this file. This value represents the gp value
- // used to create the relocatable object and required to support
- // R_MIPS_GPREL16 / R_MIPS_GPREL32 relocations.
- uint32_t mipsGp0 = 0;
-
- uint32_t andFeatures = 0;
+ std::optional<llvm::DILineInfo> getDILineInfo(InputSectionBase *, uint64_t);
+ std::optional<std::pair<std::string, unsigned>>
+ getVariableLoc(StringRef name);
// Name of source file obtained from STT_FILE symbol value,
// or empty string if there is no such symbol in object file
// symbol table.
StringRef sourceFile;
+ // Pointer to this input file's .llvm_addrsig section, if it has one.
+ const Elf_Shdr *addrsigSec = nullptr;
+
+ // SHT_LLVM_CALL_GRAPH_PROFILE section index.
+ uint32_t cgProfileSectionIndex = 0;
+
+ // MIPS GP0 value defined by this file. This value represents the gp value
+ // used to create the relocatable object and required to support
+ // R_MIPS_GPREL16 / R_MIPS_GPREL32 relocations.
+ uint32_t mipsGp0 = 0;
+
// True if the file defines functions compiled with
// -fsplit-stack. Usually false.
bool splitStack = false;
// but had one or more functions with the no_split_stack attribute.
bool someNoSplitStack = false;
- // Pointer to this input file's .llvm_addrsig section, if it has one.
- const Elf_Shdr *addrsigSec = nullptr;
-
- // SHT_LLVM_CALL_GRAPH_PROFILE section index.
- uint32_t cgProfileSectionIndex = 0;
-
// Get cached DWARF information.
DWARFCache *getDwarf();
+ void initSectionsAndLocalSyms(bool ignoreComdats);
+ void postParse();
+
private:
- void initializeSections(bool ignoreComdats);
- void initializeSymbols();
+ void initializeSections(bool ignoreComdats,
+ const llvm::object::ELFFile<ELFT> &obj);
+ void initializeSymbols(const llvm::object::ELFFile<ELFT> &obj);
void initializeJustSymbols();
- InputSectionBase *getRelocTarget(const Elf_Shdr &sec);
- InputSectionBase *createInputSection(const Elf_Shdr &sec);
- StringRef getSectionName(const Elf_Shdr &sec);
+ InputSectionBase *getRelocTarget(uint32_t idx, const Elf_Shdr &sec,
+ uint32_t info);
+ InputSectionBase *createInputSection(uint32_t idx, const Elf_Shdr &sec,
+ StringRef name);
bool shouldMerge(const Elf_Shdr &sec, StringRef name);
// If the section does not exist (which is common), the array is empty.
ArrayRef<Elf_Word> shndxTable;
- // .shstrtab contents.
- StringRef sectionStringTable;
-
// Debugging information to retrieve source file and line for error
// reporting. Linker may find reasonable number of errors in a
// single object file, so we cache debugging information in order to
llvm::once_flag initDwarf;
};
-// LazyObjFile is analogous to ArchiveFile in the sense that
-// the file contains lazy symbols. The difference is that
-// LazyObjFile wraps a single file instead of multiple files.
-//
-// This class is used for --start-lib and --end-lib options which
-// instruct the linker to link object files between them with the
-// archive file semantics.
-class LazyObjFile : public InputFile {
-public:
- LazyObjFile(MemoryBufferRef m, StringRef archiveName,
- uint64_t offsetInArchive)
- : InputFile(LazyObjKind, m), offsetInArchive(offsetInArchive) {
- this->archiveName = std::string(archiveName);
- }
-
- static bool classof(const InputFile *f) { return f->kind() == LazyObjKind; }
-
- template <class ELFT> void parse();
- void fetch();
-
- // Check if a non-common symbol should be fetched to override a common
- // definition.
- bool shouldFetchForCommon(const StringRef &name);
-
- bool fetched = false;
-
-private:
- uint64_t offsetInArchive;
-};
-
-// An ArchiveFile object represents a .a file.
-class ArchiveFile : public InputFile {
-public:
- explicit ArchiveFile(std::unique_ptr<Archive> &&file);
- static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; }
- void parse();
-
- // Pulls out an object file that contains a definition for Sym and
- // returns it. If the same file was instantiated before, this
- // function does nothing (so we don't instantiate the same file
- // more than once.)
- void fetch(const Archive::Symbol &sym);
-
- // Check if a non-common symbol should be fetched to override a common
- // definition.
- bool shouldFetchForCommon(const Archive::Symbol &sym);
-
- size_t getMemberCount() const;
- size_t getFetchedMemberCount() const { return seen.size(); }
-
- bool parsed = false;
-
-private:
- std::unique_ptr<Archive> file;
- llvm::DenseSet<uint64_t> seen;
-};
-
class BitcodeFile : public InputFile {
public:
BitcodeFile(MemoryBufferRef m, StringRef archiveName,
- uint64_t offsetInArchive);
+ uint64_t offsetInArchive, bool lazy);
static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
- template <class ELFT> void parse();
+ void parse();
+ void parseLazy();
+ void postParse();
std::unique_ptr<llvm::lto::InputFile> obj;
+ std::vector<bool> keptComdats;
};
// .so file.
class SharedFile : public ELFFileBase {
public:
- SharedFile(MemoryBufferRef m, StringRef defaultSoName)
- : ELFFileBase(SharedKind, m), soName(std::string(defaultSoName)),
- isNeeded(!config->asNeeded) {}
+ SharedFile(MemoryBufferRef m, StringRef defaultSoName);
// This is actually a vector of Elf_Verdef pointers.
- std::vector<const void *> verdefs;
+ SmallVector<const void *, 0> verdefs;
// If the output file needs Elf_Verneed data structures for this file, this is
// a vector of Elf_Vernaux version identifiers that map onto the entries in
// Verdefs, otherwise it is empty.
- std::vector<unsigned> vernauxs;
+ SmallVector<uint32_t, 0> vernauxs;
static unsigned vernauxNum;
- std::vector<StringRef> dtNeeded;
- std::string soName;
+ SmallVector<StringRef, 0> dtNeeded;
+ StringRef soName;
static bool classof(const InputFile *f) { return f->kind() == SharedKind; }
// Non-weak undefined symbols which are not yet resolved when the SO is
// parsed. Only filled for `--no-allow-shlib-undefined`.
- std::vector<Symbol *> requiredSymbols;
+ SmallVector<Symbol *, 0> requiredSymbols;
private:
template <typename ELFT>
void parse();
};
-InputFile *createObjectFile(MemoryBufferRef mb, StringRef archiveName = "",
- uint64_t offsetInArchive = 0);
-
-inline bool isBitcode(MemoryBufferRef mb) {
- return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode;
-}
+ELFFileBase *createObjFile(MemoryBufferRef mb, StringRef archiveName = "",
+ bool lazy = false);
std::string replaceThinLTOSuffix(StringRef path);
-extern std::vector<ArchiveFile *> archiveFiles;
-extern std::vector<BinaryFile *> binaryFiles;
-extern std::vector<BitcodeFile *> bitcodeFiles;
-extern std::vector<LazyObjFile *> lazyObjFiles;
-extern std::vector<InputFile *> objectFiles;
-extern std::vector<SharedFile *> sharedFiles;
-
} // namespace elf
} // namespace lld
#include "InputSection.h"
#include "Config.h"
-#include "EhFrame.h"
#include "InputFiles.h"
-#include "LinkerScript.h"
#include "OutputSections.h"
#include "Relocations.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
-#include "Thunks.h"
-#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Memory.h"
+#include "lld/Common/CommonLinkerContext.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Compression.h"
#include "llvm/Support/Endian.h"
-#include "llvm/Support/Threading.h"
#include "llvm/Support/xxhash.h"
#include <algorithm>
#include <mutex>
-#include <set>
-#include <unordered_set>
#include <vector>
using namespace llvm;
using namespace lld;
using namespace lld::elf;
-std::vector<InputSectionBase *> elf::inputSections;
DenseSet<std::pair<const Symbol *, uint64_t>> elf::ppc64noTocRelax;
// Returns a string to construct an error message.
static ArrayRef<uint8_t> getSectionContents(ObjFile<ELFT> &file,
const typename ELFT::Shdr &hdr) {
if (hdr.sh_type == SHT_NOBITS)
- return makeArrayRef<uint8_t>(nullptr, hdr.sh_size);
+ return ArrayRef<uint8_t>(nullptr, hdr.sh_size);
return check(file.getObj().getSectionContents(hdr));
}
InputSectionBase::InputSectionBase(InputFile *file, uint64_t flags,
uint32_t type, uint64_t entsize,
uint32_t link, uint32_t info,
- uint32_t alignment, ArrayRef<uint8_t> data,
+ uint32_t addralign, ArrayRef<uint8_t> data,
StringRef name, Kind sectionKind)
- : SectionBase(sectionKind, name, flags, entsize, alignment, type, info,
+ : SectionBase(sectionKind, name, flags, entsize, addralign, type, info,
link),
- file(file), rawData(data) {
+ file(file), content_(data.data()), size(data.size()) {
// In order to reduce memory allocation, we assume that mergeable
// sections are smaller than 4 GiB, which is not an unreasonable
// assumption as of 2017.
- if (sectionKind == SectionBase::Merge && rawData.size() > UINT32_MAX)
+ if (sectionKind == SectionBase::Merge && content().size() > UINT32_MAX)
error(toString(this) + ": section too large");
- numRelocations = 0;
- areRelocsRela = false;
-
// The ELF spec states that a value of 0 means the section has
// no alignment constraints.
- uint32_t v = std::max<uint32_t>(alignment, 1);
+ uint32_t v = std::max<uint32_t>(addralign, 1);
if (!isPowerOf2_64(v))
fatal(toString(this) + ": sh_addralign is not a power of 2");
- this->alignment = v;
-
- // In ELF, each section can be compressed by zlib, and if compressed,
- // section name may be mangled by appending "z" (e.g. ".zdebug_info").
- // If that's the case, demangle section name so that we can handle a
- // section as if it weren't compressed.
- if ((flags & SHF_COMPRESSED) || name.startswith(".zdebug")) {
- if (!zlib::isAvailable())
- error(toString(file) + ": contains a compressed section, " +
- "but zlib is not available");
- switch (config->ekind) {
- case ELF32LEKind:
- parseCompressedHeader<ELF32LE>();
- break;
- case ELF32BEKind:
- parseCompressedHeader<ELF32BE>();
- break;
- case ELF64LEKind:
- parseCompressedHeader<ELF64LE>();
- break;
- case ELF64BEKind:
- parseCompressedHeader<ELF64BE>();
- break;
- default:
- llvm_unreachable("unknown ELFT");
- }
- }
+ this->addralign = v;
+
+ // If SHF_COMPRESSED is set, parse the header. The legacy .zdebug format is no
+ // longer supported.
+ if (flags & SHF_COMPRESSED)
+ invokeELFT(parseCompressedHeader);
}
// Drop SHF_GROUP bit unless we are producing a re-linkable object file.
return flags;
}
-// GNU assembler 2.24 and LLVM 4.0.0's MC (the newest release as of
-// March 2017) fail to infer section types for sections starting with
-// ".init_array." or ".fini_array.". They set SHT_PROGBITS instead of
-// SHF_INIT_ARRAY. As a result, the following assembler directive
-// creates ".init_array.100" with SHT_PROGBITS, for example.
-//
-// .section .init_array.100, "aw"
-//
-// This function forces SHT_{INIT,FINI}_ARRAY so that we can handle
-// incorrect inputs as if they were correct from the beginning.
-static uint64_t getType(uint64_t type, StringRef name) {
- if (type == SHT_PROGBITS && name.startswith(".init_array."))
- return SHT_INIT_ARRAY;
- if (type == SHT_PROGBITS && name.startswith(".fini_array."))
- return SHT_FINI_ARRAY;
- return type;
-}
-
template <class ELFT>
InputSectionBase::InputSectionBase(ObjFile<ELFT> &file,
const typename ELFT::Shdr &hdr,
StringRef name, Kind sectionKind)
- : InputSectionBase(&file, getFlags(hdr.sh_flags),
- getType(hdr.sh_type, name), hdr.sh_entsize, hdr.sh_link,
- hdr.sh_info, hdr.sh_addralign,
- getSectionContents(file, hdr), name, sectionKind) {
+ : InputSectionBase(&file, getFlags(hdr.sh_flags), hdr.sh_type,
+ hdr.sh_entsize, hdr.sh_link, hdr.sh_info,
+ hdr.sh_addralign, getSectionContents(file, hdr), name,
+ sectionKind) {
// We reject object files having insanely large alignments even though
// they are allowed by the spec. I think 4GB is a reasonable limitation.
// We might want to relax this in the future.
size_t InputSectionBase::getSize() const {
if (auto *s = dyn_cast<SyntheticSection>(this))
return s->getSize();
- if (uncompressedSize >= 0)
- return uncompressedSize;
- return rawData.size() - bytesDropped;
+ return size - bytesDropped;
}
-void InputSectionBase::uncompress() const {
- size_t size = uncompressedSize;
- char *uncompressedBuf;
+template <class ELFT>
+static void decompressAux(const InputSectionBase &sec, uint8_t *out,
+ size_t size) {
+ auto *hdr = reinterpret_cast<const typename ELFT::Chdr *>(sec.content_);
+ auto compressed = ArrayRef<uint8_t>(sec.content_, sec.compressedSize)
+ .slice(sizeof(typename ELFT::Chdr));
+ if (Error e = hdr->ch_type == ELFCOMPRESS_ZLIB
+ ? compression::zlib::decompress(compressed, out, size)
+ : compression::zstd::decompress(compressed, out, size))
+ fatal(toString(&sec) +
+ ": decompress failed: " + llvm::toString(std::move(e)));
+}
+
+void InputSectionBase::decompress() const {
+ uint8_t *uncompressedBuf;
{
static std::mutex mu;
std::lock_guard<std::mutex> lock(mu);
- uncompressedBuf = bAlloc.Allocate<char>(size);
+ uncompressedBuf = bAlloc().Allocate<uint8_t>(size);
}
- if (Error e = zlib::uncompress(toStringRef(rawData), uncompressedBuf, size))
- fatal(toString(this) +
- ": uncompress failed: " + llvm::toString(std::move(e)));
- rawData = makeArrayRef((uint8_t *)uncompressedBuf, size);
- uncompressedSize = -1;
+ invokeELFT(decompressAux, *this, uncompressedBuf, size);
+ content_ = uncompressedBuf;
+ compressed = false;
}
-uint64_t InputSectionBase::getOffsetInFile() const {
- const uint8_t *fileStart = (const uint8_t *)file->mb.getBufferStart();
- const uint8_t *secStart = data().begin();
- return secStart - fileStart;
+template <class ELFT> RelsOrRelas<ELFT> InputSectionBase::relsOrRelas() const {
+ if (relSecIdx == 0)
+ return {};
+ RelsOrRelas<ELFT> ret;
+ typename ELFT::Shdr shdr =
+ cast<ELFFileBase>(file)->getELFShdrs<ELFT>()[relSecIdx];
+ if (shdr.sh_type == SHT_REL) {
+ ret.rels = ArrayRef(reinterpret_cast<const typename ELFT::Rel *>(
+ file->mb.getBufferStart() + shdr.sh_offset),
+ shdr.sh_size / sizeof(typename ELFT::Rel));
+ } else {
+ assert(shdr.sh_type == SHT_RELA);
+ ret.relas = ArrayRef(reinterpret_cast<const typename ELFT::Rela *>(
+ file->mb.getBufferStart() + shdr.sh_offset),
+ shdr.sh_size / sizeof(typename ELFT::Rela));
+ }
+ return ret;
}
uint64_t SectionBase::getOffset(uint64_t offset) const {
}
case Regular:
case Synthetic:
- return cast<InputSection>(this)->getOffset(offset);
- case EHFrame:
- // The file crtbeginT.o has relocations pointing to the start of an empty
- // .eh_frame that is known to be the first in the link. It does that to
- // identify the start of the output .eh_frame.
+ return cast<InputSection>(this)->outSecOff + offset;
+ case EHFrame: {
+ // Two code paths may reach here. First, clang_rt.crtbegin.o and GCC
+ // crtbeginT.o may reference the start of an empty .eh_frame to identify the
+ // start of the output .eh_frame. Just return offset.
+ //
+ // Second, InputSection::copyRelocations on .eh_frame. Some pieces may be
+ // discarded due to GC/ICF. We should compute the output section offset.
+ const EhInputSection *es = cast<EhInputSection>(this);
+ if (!es->content().empty())
+ if (InputSection *isec = es->getParent())
+ return isec->outSecOff + es->getParentOffset(offset);
return offset;
+ }
case Merge:
const MergeInputSection *ms = cast<MergeInputSection>(this);
if (InputSection *isec = ms->getParent())
- return isec->getOffset(ms->getParentOffset(offset));
+ return isec->outSecOff + ms->getParentOffset(offset);
return ms->getParentOffset(offset);
}
llvm_unreachable("invalid section kind");
// by zlib-compressed data. This function parses a header to initialize
// `uncompressedSize` member and remove the header from `rawData`.
template <typename ELFT> void InputSectionBase::parseCompressedHeader() {
- // Old-style header
- if (name.startswith(".zdebug")) {
- if (!toStringRef(rawData).startswith("ZLIB")) {
- error(toString(this) + ": corrupted compressed section header");
- return;
- }
- rawData = rawData.slice(4);
-
- if (rawData.size() < 8) {
- error(toString(this) + ": corrupted compressed section header");
- return;
- }
-
- uncompressedSize = read64be(rawData.data());
- rawData = rawData.slice(8);
-
- // Restore the original section name.
- // (e.g. ".zdebug_info" -> ".debug_info")
- name = saver.save("." + name.substr(2));
- return;
- }
-
- assert(flags & SHF_COMPRESSED);
flags &= ~(uint64_t)SHF_COMPRESSED;
// New-style header
- if (rawData.size() < sizeof(typename ELFT::Chdr)) {
+ if (content().size() < sizeof(typename ELFT::Chdr)) {
error(toString(this) + ": corrupted compressed section");
return;
}
- auto *hdr = reinterpret_cast<const typename ELFT::Chdr *>(rawData.data());
- if (hdr->ch_type != ELFCOMPRESS_ZLIB) {
- error(toString(this) + ": unsupported compression type");
+ auto *hdr = reinterpret_cast<const typename ELFT::Chdr *>(content().data());
+ if (hdr->ch_type == ELFCOMPRESS_ZLIB) {
+ if (!compression::zlib::isAvailable())
+ error(toString(this) + " is compressed with ELFCOMPRESS_ZLIB, but lld is "
+ "not built with zlib support");
+ } else if (hdr->ch_type == ELFCOMPRESS_ZSTD) {
+ if (!compression::zstd::isAvailable())
+ error(toString(this) + " is compressed with ELFCOMPRESS_ZSTD, but lld is "
+ "not built with zstd support");
+ } else {
+ error(toString(this) + ": unsupported compression type (" +
+ Twine(hdr->ch_type) + ")");
return;
}
- uncompressedSize = hdr->ch_size;
- alignment = std::max<uint32_t>(hdr->ch_addralign, 1);
- rawData = rawData.slice(sizeof(*hdr));
+ compressed = true;
+ compressedSize = size;
+ size = hdr->ch_size;
+ addralign = std::max<uint32_t>(hdr->ch_addralign, 1);
}
InputSection *InputSectionBase::getLinkOrderDep() const {
}
// Find a function symbol that encloses a given location.
-template <class ELFT>
Defined *InputSectionBase::getEnclosingFunction(uint64_t offset) {
for (Symbol *b : file->getSymbols())
if (Defined *d = dyn_cast<Defined>(b))
return nullptr;
}
-// Returns a source location string. Used to construct an error message.
-template <class ELFT>
+// Returns an object file location string. Used to construct an error message.
std::string InputSectionBase::getLocation(uint64_t offset) {
- std::string secAndOffset = (name + "+0x" + utohexstr(offset)).str();
+ std::string secAndOffset =
+ (name + "+0x" + Twine::utohexstr(offset) + ")").str();
// We don't have file for synthetic sections.
- if (getFile<ELFT>() == nullptr)
- return (config->outputFile + ":(" + secAndOffset + ")")
- .str();
-
- // First check if we can get desired values from debugging information.
- if (Optional<DILineInfo> info = getFile<ELFT>()->getDILineInfo(this, offset))
- return info->FileName + ":" + std::to_string(info->Line) + ":(" +
- secAndOffset + ")";
-
- // File->sourceFile contains STT_FILE symbol that contains a
- // source file name. If it's missing, we use an object file name.
- std::string srcFile = std::string(getFile<ELFT>()->sourceFile);
- if (srcFile.empty())
- srcFile = toString(file);
+ if (file == nullptr)
+ return (config->outputFile + ":(" + secAndOffset).str();
- if (Defined *d = getEnclosingFunction<ELFT>(offset))
- return srcFile + ":(function " + toString(*d) + ": " + secAndOffset + ")";
+ std::string filename = toString(file);
+ if (Defined *d = getEnclosingFunction(offset))
+ return filename + ":(function " + toString(*d) + ": " + secAndOffset;
- // If there's no symbol, print out the offset in the section.
- return (srcFile + ":(" + secAndOffset + ")");
+ return filename + ":(" + secAndOffset;
}
// This function is intended to be used for constructing an error message.
std::string archive;
if (!file->archiveName.empty())
- archive = " in archive " + file->archiveName;
+ archive = (" in archive " + file->archiveName).str();
- // Find a symbol that encloses a given location.
+ // Find a symbol that encloses a given location. getObjMsg may be called
+ // before ObjFile::initSectionsAndLocalSyms where local symbols are
+ // initialized.
for (Symbol *b : file->getSymbols())
- if (auto *d = dyn_cast<Defined>(b))
+ if (auto *d = dyn_cast_or_null<Defined>(b))
if (d->section == this && d->value <= off && off < d->value + d->size)
return filename + ":(" + toString(*d) + ")" + archive;
InputSection InputSection::discarded(nullptr, 0, 0, 0, ArrayRef<uint8_t>(), "");
InputSection::InputSection(InputFile *f, uint64_t flags, uint32_t type,
- uint32_t alignment, ArrayRef<uint8_t> data,
+ uint32_t addralign, ArrayRef<uint8_t> data,
StringRef name, Kind k)
: InputSectionBase(f, flags, type,
- /*Entsize*/ 0, /*Link*/ 0, /*Info*/ 0, alignment, data,
+ /*Entsize*/ 0, /*Link*/ 0, /*Info*/ 0, addralign, data,
name, k) {}
template <class ELFT>
StringRef name)
: InputSectionBase(f, header, name, InputSectionBase::Regular) {}
-bool InputSection::classof(const SectionBase *s) {
- return s->kind() == SectionBase::Regular ||
- s->kind() == SectionBase::Synthetic;
-}
-
-OutputSection *InputSection::getParent() const {
- return cast_or_null<OutputSection>(parent);
-}
-
// Copy SHT_GROUP section contents. Used only for the -r option.
template <class ELFT> void InputSection::copyShtGroup(uint8_t *buf) {
// ELFT::Word is the 32-bit integral type in the target endianness.
// different in the output. We also need to handle combined or discarded
// members.
ArrayRef<InputSectionBase *> sections = file->getSections();
- std::unordered_set<uint32_t> seen;
+ DenseSet<uint32_t> seen;
for (uint32_t idx : from.slice(1)) {
OutputSection *osec = sections[idx]->getOutputSection();
if (osec && seen.insert(osec->sectionIndex).second)
// for each relocation. So we copy relocations one by one.
template <class ELFT, class RelTy>
void InputSection::copyRelocations(uint8_t *buf, ArrayRef<RelTy> rels) {
+ const TargetInfo &target = *elf::target;
InputSectionBase *sec = getRelocatedSection();
+ (void)sec->contentMaybeDecompress(); // uncompress if needed
for (const RelTy &rel : rels) {
RelType type = rel.getType(config->isMips64EL);
sec->name != ".gcc_except_table" && sec->name != ".got2" &&
sec->name != ".toc") {
uint32_t secIdx = cast<Undefined>(sym).discardedSecIdx;
- Elf_Shdr_Impl<ELFT> sec =
- CHECK(file->getObj().sections(), file)[secIdx];
+ Elf_Shdr_Impl<ELFT> sec = file->template getELFShdrs<ELFT>()[secIdx];
warn("relocation refers to a discarded section: " +
CHECK(file->getObj().getSectionName(sec), file) +
"\n>>> referenced by " + getObjMsg(p->r_offset));
p->setSymbolAndType(0, 0, false);
continue;
}
- SectionBase *section = d->section->repl;
+ SectionBase *section = d->section;
if (!section->isLive()) {
p->setSymbolAndType(0, 0, false);
continue;
}
int64_t addend = getAddend<ELFT>(rel);
- const uint8_t *bufLoc = sec->data().begin() + rel.r_offset;
+ const uint8_t *bufLoc = sec->content().begin() + rel.r_offset;
if (!RelTy::IsRela)
- addend = target->getImplicitAddend(bufLoc, type);
+ addend = target.getImplicitAddend(bufLoc, type);
if (config->emachine == EM_MIPS &&
- target->getRelExpr(type, sym, bufLoc) == R_MIPS_GOTREL) {
+ target.getRelExpr(type, sym, bufLoc) == R_MIPS_GOTREL) {
// Some MIPS relocations depend on "gp" value. By default,
// this value has 0x7ff0 offset from a .got section. But
// relocatable files produced by a compiler or a linker
if (RelTy::IsRela)
p->r_addend = sym.getVA(addend) - section->getOutputSection()->addr;
- else if (config->relocatable && type != target->noneRel)
- sec->relocations.push_back({R_ABS, type, rel.r_offset, addend, &sym});
+ else if (config->relocatable && type != target.noneRel)
+ sec->addReloc({R_ABS, type, rel.r_offset, addend, &sym});
} else if (config->emachine == EM_PPC && type == R_PPC_PLTREL24 &&
- p->r_addend >= 0x8000) {
+ p->r_addend >= 0x8000 && sec->file->ppc32Got2) {
// Similar to R_MIPS_GPREL{16,32}. If the addend of R_PPC_PLTREL24
// indicates that r30 is relative to the input section .got2
// (r_addend>=0x8000), after linking, r30 should be relative to the output
// section .got2 . To compensate for the shift, adjust r_addend by
- // ppc32Got2OutSecOff.
- p->r_addend += sec->file->ppc32Got2OutSecOff;
+ // ppc32Got->outSecOff.
+ p->r_addend += sec->file->ppc32Got2->outSecOff;
}
}
}
switch (type) {
// Unresolved branch relocations to weak references resolve to next
// instruction, this will be either 2 or 4 bytes on from P.
+ case R_ARM_THM_JUMP8:
case R_ARM_THM_JUMP11:
return p + 2 + a;
case R_ARM_CALL:
static Relocation *getRISCVPCRelHi20(const Symbol *sym, uint64_t addend) {
const Defined *d = cast<Defined>(sym);
if (!d->section) {
- error("R_RISCV_PCREL_LO12 relocation points to an absolute symbol: " +
- sym->getName());
+ errorOrWarn("R_RISCV_PCREL_LO12 relocation points to an absolute symbol: " +
+ sym->getName());
return nullptr;
}
InputSection *isec = cast<InputSection>(d->section);
if (addend != 0)
- warn("Non-zero addend in R_RISCV_PCREL_LO12 relocation to " +
+ warn("non-zero addend in R_RISCV_PCREL_LO12 relocation to " +
isec->getObjMsg(d->value) + " is ignored");
// Relocations are sorted by offset, so we can use std::equal_range to do
Relocation r;
r.offset = d->value;
auto range =
- std::equal_range(isec->relocations.begin(), isec->relocations.end(), r,
+ std::equal_range(isec->relocs().begin(), isec->relocs().end(), r,
[](const Relocation &lhs, const Relocation &rhs) {
return lhs.offset < rhs.offset;
});
it->type == R_RISCV_TLS_GD_HI20 || it->type == R_RISCV_TLS_GOT_HI20)
return &*it;
- error("R_RISCV_PCREL_LO12 relocation points to " + isec->getObjMsg(d->value) +
- " without an associated R_RISCV_PCREL_HI20 relocation");
+ errorOrWarn("R_RISCV_PCREL_LO12 relocation points to " +
+ isec->getObjMsg(d->value) +
+ " without an associated R_RISCV_PCREL_HI20 relocation");
return nullptr;
}
return sym.getVA(a);
case R_ADDEND:
return a;
+ case R_RELAX_HINT:
+ return 0;
case R_ARM_SBREL:
return sym.getVA(a) - getARMStaticBase(sym);
case R_GOT:
if (expr == R_ARM_PCA)
// Some PC relative ARM (Thumb) relocations align down the place.
p = p & 0xfffffffc;
- if (sym.isUndefWeak()) {
+ if (sym.isUndefined()) {
// On ARM and AArch64 a branch to an undefined weak resolves to the next
// instruction, otherwise the place. On RISCV, resolve an undefined weak
// to the same instruction to cause an infinite loop (making the user
// aware of the issue) while ensuring no overflow.
+ // Note: if the symbol is hidden, its binding has been converted to local,
+ // so we just check isUndefined() here.
if (config->emachine == EM_ARM)
dest = getARMUndefinedRelativeWeakVA(type, a, p);
else if (config->emachine == EM_AARCH64)
case R_PLT_PC:
case R_PPC64_CALL_PLT:
return sym.getPltVA() + a - p;
+ case R_PLT_GOTPLT:
+ return sym.getPltVA() + a - in.gotPlt->getVA();
case R_PPC32_PLTREL:
// R_PPC_PLTREL24 uses the addend (usually 0 or 0x8000) to indicate r30
// stores _GLOBAL_OFFSET_TABLE_ or .got2+0x8000. The addend is ignored for
// --noinhibit-exec, even a non-weak undefined reference may reach here.
// Just return A, which matches R_ABS, and the behavior of some dynamic
// loaders.
- if (sym.isUndefined() || sym.isLazy())
+ if (sym.isUndefined())
return a;
return getTlsTpOffset(sym) + a;
case R_RELAX_TLS_GD_TO_LE_NEG:
case R_SIZE:
return sym.getSize() + a;
case R_TLSDESC:
- return in.got->getGlobalDynAddr(sym) + a;
+ return in.got->getTlsDescAddr(sym) + a;
case R_TLSDESC_PC:
- return in.got->getGlobalDynAddr(sym) + a - p;
+ return in.got->getTlsDescAddr(sym) + a - p;
+ case R_TLSDESC_GOTPLT:
+ return in.got->getTlsDescAddr(sym) + a - in.gotPlt->getVA();
case R_AARCH64_TLSDESC_PAGE:
- return getAArch64Page(in.got->getGlobalDynAddr(sym) + a) -
- getAArch64Page(p);
+ return getAArch64Page(in.got->getTlsDescAddr(sym) + a) - getAArch64Page(p);
case R_TLSGD_GOT:
return in.got->getGlobalDynOffset(sym) + a;
case R_TLSGD_GOTPLT:
template <class ELFT, class RelTy>
void InputSection::relocateNonAlloc(uint8_t *buf, ArrayRef<RelTy> rels) {
const unsigned bits = sizeof(typename ELFT::uint) * 8;
+ const TargetInfo &target = *elf::target;
const bool isDebug = isDebugSection(*this);
const bool isDebugLocOrRanges =
isDebug && (name == ".debug_loc" || name == ".debug_ranges");
const bool isDebugLine = isDebug && name == ".debug_line";
- Optional<uint64_t> tombstone;
+ std::optional<uint64_t> tombstone;
for (const auto &patAndValue : llvm::reverse(config->deadRelocInNonAlloc))
if (patAndValue.first.match(this->name)) {
tombstone = patAndValue.second;
uint8_t *bufLoc = buf + offset;
int64_t addend = getAddend<ELFT>(rel);
if (!RelTy::IsRela)
- addend += target->getImplicitAddend(bufLoc, type);
+ addend += target.getImplicitAddend(bufLoc, type);
Symbol &sym = getFile<ELFT>()->getRelocTargetSym(rel);
- RelExpr expr = target->getRelExpr(type, sym, bufLoc);
+ RelExpr expr = target.getRelExpr(type, sym, bufLoc);
if (expr == R_NONE)
continue;
- if (expr == R_SIZE) {
- target->relocateNoSym(bufLoc, type,
- SignExtend64<bits>(sym.getSize() + addend));
- continue;
- }
-
- // R_ABS/R_DTPREL and some other relocations can be used from non-SHF_ALLOC
- // sections.
- if (expr != R_ABS && expr != R_DTPREL && expr != R_GOTPLTREL &&
- expr != R_RISCV_ADD) {
- std::string msg = getLocation<ELFT>(offset) +
- ": has non-ABS relocation " + toString(type) +
- " against symbol '" + toString(sym) + "'";
- if (expr != R_PC && expr != R_ARM_PCA) {
- error(msg);
- return;
- }
-
- // If the control reaches here, we found a PC-relative relocation in a
- // non-ALLOC section. Since non-ALLOC section is not loaded into memory
- // at runtime, the notion of PC-relative doesn't make sense here. So,
- // this is a usage error. However, GNU linkers historically accept such
- // relocations without any errors and relocate them as if they were at
- // address 0. For bug-compatibilty, we accept them with warnings. We
- // know Steel Bank Common Lisp as of 2018 have this bug.
- warn(msg);
- target->relocateNoSym(
- bufLoc, type,
- SignExtend64<bits>(sym.getVA(addend - offset - outSecOff)));
- continue;
- }
-
if (tombstone ||
- (isDebug && (type == target->symbolicRel || expr == R_DTPREL))) {
+ (isDebug && (type == target.symbolicRel || expr == R_DTPREL))) {
// Resolve relocations in .debug_* referencing (discarded symbols or ICF
// folded section symbols) to a tombstone value. Resolving to addend is
// unsatisfactory because the result address range may collide with a
//
// If the referenced symbol is discarded (made Undefined), or the
// section defining the referenced symbol is garbage collected,
- // sym.getOutputSection() is nullptr. `ds->section->repl != ds->section`
- // catches the ICF folded case. However, resolving a relocation in
- // .debug_line to -1 would stop debugger users from setting breakpoints on
- // the folded-in function, so exclude .debug_line.
+ // sym.getOutputSection() is nullptr. `ds->folded` catches the ICF folded
+ // case. However, resolving a relocation in .debug_line to -1 would stop
+ // debugger users from setting breakpoints on the folded-in function, so
+ // exclude .debug_line.
//
// For pre-DWARF-v5 .debug_loc and .debug_ranges, -1 is a reserved value
// (base address selection entry), use 1 (which is used by GNU ld for
// TODO To reduce disruption, we use 0 instead of -1 as the tombstone
// value. Enable -1 in a future release.
auto *ds = dyn_cast<Defined>(&sym);
- if (!sym.getOutputSection() ||
- (ds && ds->section->repl != ds->section && !isDebugLine)) {
+ if (!sym.getOutputSection() || (ds && ds->folded && !isDebugLine)) {
// If -z dead-reloc-in-nonalloc= is specified, respect it.
const uint64_t value = tombstone ? SignExtend64<bits>(*tombstone)
: (isDebugLocOrRanges ? 1 : 0);
- target->relocateNoSym(bufLoc, type, value);
+ target.relocateNoSym(bufLoc, type, value);
continue;
}
}
- target->relocateNoSym(bufLoc, type, SignExtend64<bits>(sym.getVA(addend)));
+
+ // For a relocatable link, only tombstone values are applied.
+ if (config->relocatable)
+ continue;
+
+ if (expr == R_SIZE) {
+ target.relocateNoSym(bufLoc, type,
+ SignExtend64<bits>(sym.getSize() + addend));
+ continue;
+ }
+
+ // R_ABS/R_DTPREL and some other relocations can be used from non-SHF_ALLOC
+ // sections.
+ if (expr == R_ABS || expr == R_DTPREL || expr == R_GOTPLTREL ||
+ expr == R_RISCV_ADD) {
+ target.relocateNoSym(bufLoc, type, SignExtend64<bits>(sym.getVA(addend)));
+ continue;
+ }
+
+ std::string msg = getLocation(offset) + ": has non-ABS relocation " +
+ toString(type) + " against symbol '" + toString(sym) +
+ "'";
+ if (expr != R_PC && expr != R_ARM_PCA) {
+ error(msg);
+ return;
+ }
+
+ // If the control reaches here, we found a PC-relative relocation in a
+ // non-ALLOC section. Since non-ALLOC section is not loaded into memory
+ // at runtime, the notion of PC-relative doesn't make sense here. So,
+ // this is a usage error. However, GNU linkers historically accept such
+ // relocations without any errors and relocate them as if they were at
+ // address 0. For bug-compatibility, we accept them with warnings. We
+ // know Steel Bank Common Lisp as of 2018 have this bug.
+ warn(msg);
+ target.relocateNoSym(
+ bufLoc, type,
+ SignExtend64<bits>(sym.getVA(addend - offset - outSecOff)));
}
}
static void relocateNonAllocForRelocatable(InputSection *sec, uint8_t *buf) {
const unsigned bits = config->is64 ? 64 : 32;
- for (const Relocation &rel : sec->relocations) {
+ for (const Relocation &rel : sec->relocs()) {
// InputSection::copyRelocations() adds only R_ABS relocations.
assert(rel.expr == R_ABS);
uint8_t *bufLoc = buf + rel.offset;
template <class ELFT>
void InputSectionBase::relocate(uint8_t *buf, uint8_t *bufEnd) {
- if (flags & SHF_EXECINSTR)
+ if ((flags & SHF_EXECINSTR) && LLVM_UNLIKELY(getFile<ELFT>()->splitStack))
adjustSplitStackFunctionPrologues<ELFT>(buf, bufEnd);
if (flags & SHF_ALLOC) {
- relocateAlloc(buf, bufEnd);
+ target->relocateAlloc(*this, buf);
return;
}
auto *sec = cast<InputSection>(this);
if (config->relocatable)
relocateNonAllocForRelocatable(sec, buf);
- else if (sec->areRelocsRela)
- sec->relocateNonAlloc<ELFT>(buf, sec->template relas<ELFT>());
+ // For a relocatable link, also call relocateNonAlloc() to rewrite applicable
+ // locations with tombstone values.
+ const RelsOrRelas<ELFT> rels = sec->template relsOrRelas<ELFT>();
+ if (rels.areRelocsRel())
+ sec->relocateNonAlloc<ELFT>(buf, rels.rels);
else
- sec->relocateNonAlloc<ELFT>(buf, sec->template rels<ELFT>());
-}
-
-void InputSectionBase::relocateAlloc(uint8_t *buf, uint8_t *bufEnd) {
- assert(flags & SHF_ALLOC);
- const unsigned bits = config->wordsize * 8;
- uint64_t lastPPCRelaxedRelocOff = UINT64_C(-1);
-
- for (const Relocation &rel : relocations) {
- if (rel.expr == R_NONE)
- continue;
- uint64_t offset = rel.offset;
- uint8_t *bufLoc = buf + offset;
- RelType type = rel.type;
-
- uint64_t addrLoc = getOutputSection()->addr + offset;
- if (auto *sec = dyn_cast<InputSection>(this))
- addrLoc += sec->outSecOff;
- RelExpr expr = rel.expr;
- uint64_t targetVA = SignExtend64(
- getRelocTargetVA(file, type, rel.addend, addrLoc, *rel.sym, expr),
- bits);
-
- switch (expr) {
- case R_RELAX_GOT_PC:
- case R_RELAX_GOT_PC_NOPIC:
- target->relaxGot(bufLoc, rel, targetVA);
- break;
- case R_PPC64_RELAX_GOT_PC: {
- // The R_PPC64_PCREL_OPT relocation must appear immediately after
- // R_PPC64_GOT_PCREL34 in the relocations table at the same offset.
- // We can only relax R_PPC64_PCREL_OPT if we have also relaxed
- // the associated R_PPC64_GOT_PCREL34 since only the latter has an
- // associated symbol. So save the offset when relaxing R_PPC64_GOT_PCREL34
- // and only relax the other if the saved offset matches.
- if (type == R_PPC64_GOT_PCREL34)
- lastPPCRelaxedRelocOff = offset;
- if (type == R_PPC64_PCREL_OPT && offset != lastPPCRelaxedRelocOff)
- break;
- target->relaxGot(bufLoc, rel, targetVA);
- break;
- }
- case R_PPC64_RELAX_TOC:
- // rel.sym refers to the STT_SECTION symbol associated to the .toc input
- // section. If an R_PPC64_TOC16_LO (.toc + addend) references the TOC
- // entry, there may be R_PPC64_TOC16_HA not paired with
- // R_PPC64_TOC16_LO_DS. Don't relax. This loses some relaxation
- // opportunities but is safe.
- if (ppc64noTocRelax.count({rel.sym, rel.addend}) ||
- !tryRelaxPPC64TocIndirection(rel, bufLoc))
- target->relocate(bufLoc, rel, targetVA);
- break;
- case R_RELAX_TLS_IE_TO_LE:
- target->relaxTlsIeToLe(bufLoc, rel, targetVA);
- break;
- case R_RELAX_TLS_LD_TO_LE:
- case R_RELAX_TLS_LD_TO_LE_ABS:
- target->relaxTlsLdToLe(bufLoc, rel, targetVA);
- break;
- case R_RELAX_TLS_GD_TO_LE:
- case R_RELAX_TLS_GD_TO_LE_NEG:
- target->relaxTlsGdToLe(bufLoc, rel, targetVA);
- break;
- case R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC:
- case R_RELAX_TLS_GD_TO_IE:
- case R_RELAX_TLS_GD_TO_IE_ABS:
- case R_RELAX_TLS_GD_TO_IE_GOT_OFF:
- case R_RELAX_TLS_GD_TO_IE_GOTPLT:
- target->relaxTlsGdToIe(bufLoc, rel, targetVA);
- break;
- case R_PPC64_CALL:
- // If this is a call to __tls_get_addr, it may be part of a TLS
- // sequence that has been relaxed and turned into a nop. In this
- // case, we don't want to handle it as a call.
- if (read32(bufLoc) == 0x60000000) // nop
- break;
-
- // Patch a nop (0x60000000) to a ld.
- if (rel.sym->needsTocRestore) {
- // gcc/gfortran 5.4, 6.3 and earlier versions do not add nop for
- // recursive calls even if the function is preemptible. This is not
- // wrong in the common case where the function is not preempted at
- // runtime. Just ignore.
- if ((bufLoc + 8 > bufEnd || read32(bufLoc + 4) != 0x60000000) &&
- rel.sym->file != file) {
- // Use substr(6) to remove the "__plt_" prefix.
- errorOrWarn(getErrorLocation(bufLoc) + "call to " +
- lld::toString(*rel.sym).substr(6) +
- " lacks nop, can't restore toc");
- break;
- }
- write32(bufLoc + 4, 0xe8410018); // ld %r2, 24(%r1)
- }
- target->relocate(bufLoc, rel, targetVA);
- break;
- default:
- target->relocate(bufLoc, rel, targetVA);
- break;
- }
- }
-
- // Apply jumpInstrMods. jumpInstrMods are created when the opcode of
- // a jmp insn must be modified to shrink the jmp insn or to flip the jmp
- // insn. This is primarily used to relax and optimize jumps created with
- // basic block sections.
- if (isa<InputSection>(this)) {
- for (const JumpInstrMod &jumpMod : jumpInstrMods) {
- uint64_t offset = jumpMod.offset;
- uint8_t *bufLoc = buf + offset;
- target->applyJumpInstrMod(bufLoc, jumpMod.original, jumpMod.size);
- }
- }
+ sec->relocateNonAlloc<ELFT>(buf, rels.relas);
}
// For each function-defining prologue, find any calls to __morestack,
// and replace them with calls to __morestack_non_split.
static void switchMorestackCallsToMorestackNonSplit(
- DenseSet<Defined *> &prologues, std::vector<Relocation *> &morestackCalls) {
+ DenseSet<Defined *> &prologues,
+ SmallVector<Relocation *, 0> &morestackCalls) {
// If the target adjusted a function's prologue, all calls to
// __morestack inside that function should be switched to
// __morestack_non_split.
- Symbol *moreStackNonSplit = symtab->find("__morestack_non_split");
+ Symbol *moreStackNonSplit = symtab.find("__morestack_non_split");
if (!moreStackNonSplit) {
- error("Mixing split-stack objects requires a definition of "
+ error("mixing split-stack objects requires a definition of "
"__morestack_non_split");
return;
}
template <class ELFT>
void InputSectionBase::adjustSplitStackFunctionPrologues(uint8_t *buf,
uint8_t *end) {
- if (!getFile<ELFT>()->splitStack)
- return;
DenseSet<Defined *> prologues;
- std::vector<Relocation *> morestackCalls;
-
- for (Relocation &rel : relocations) {
- // Local symbols can't possibly be cross-calls, and should have been
- // resolved long before this line.
- if (rel.sym->isLocal())
- continue;
+ SmallVector<Relocation *, 0> morestackCalls;
+ for (Relocation &rel : relocs()) {
// Ignore calls into the split-stack api.
if (rel.sym->getName().startswith("__morestack")) {
if (rel.sym->getName().equals("__morestack"))
if (enclosingPrologueAttempted(rel.offset, prologues))
continue;
- if (Defined *f = getEnclosingFunction<ELFT>(rel.offset)) {
+ if (Defined *f = getEnclosingFunction(rel.offset)) {
prologues.insert(f);
if (target->adjustPrologueForCrossSplitStack(buf + f->value, end,
f->stOther))
}
template <class ELFT> void InputSection::writeTo(uint8_t *buf) {
- if (type == SHT_NOBITS)
+ if (LLVM_UNLIKELY(type == SHT_NOBITS))
return;
-
- if (auto *s = dyn_cast<SyntheticSection>(this)) {
- s->writeTo(buf + outSecOff);
- return;
- }
-
// If -r or --emit-relocs is given, then an InputSection
// may be a relocation section.
- if (type == SHT_RELA) {
- copyRelocations<ELFT>(buf + outSecOff, getDataAs<typename ELFT::Rela>());
+ if (LLVM_UNLIKELY(type == SHT_RELA)) {
+ copyRelocations<ELFT>(buf, getDataAs<typename ELFT::Rela>());
return;
}
- if (type == SHT_REL) {
- copyRelocations<ELFT>(buf + outSecOff, getDataAs<typename ELFT::Rel>());
+ if (LLVM_UNLIKELY(type == SHT_REL)) {
+ copyRelocations<ELFT>(buf, getDataAs<typename ELFT::Rel>());
return;
}
// If -r is given, we may have a SHT_GROUP section.
- if (type == SHT_GROUP) {
- copyShtGroup<ELFT>(buf + outSecOff);
+ if (LLVM_UNLIKELY(type == SHT_GROUP)) {
+ copyShtGroup<ELFT>(buf);
return;
}
// If this is a compressed section, uncompress section contents directly
// to the buffer.
- if (uncompressedSize >= 0) {
- size_t size = uncompressedSize;
- if (Error e = zlib::uncompress(toStringRef(rawData),
- (char *)(buf + outSecOff), size))
+ if (compressed) {
+ auto *hdr = reinterpret_cast<const typename ELFT::Chdr *>(content_);
+ auto compressed = ArrayRef<uint8_t>(content_, compressedSize)
+ .slice(sizeof(typename ELFT::Chdr));
+ size_t size = this->size;
+ if (Error e = hdr->ch_type == ELFCOMPRESS_ZLIB
+ ? compression::zlib::decompress(compressed, buf, size)
+ : compression::zstd::decompress(compressed, buf, size))
fatal(toString(this) +
- ": uncompress failed: " + llvm::toString(std::move(e)));
- uint8_t *bufEnd = buf + outSecOff + size;
- relocate<ELFT>(buf + outSecOff, bufEnd);
+ ": decompress failed: " + llvm::toString(std::move(e)));
+ uint8_t *bufEnd = buf + size;
+ relocate<ELFT>(buf, bufEnd);
return;
}
// Copy section contents from source object file to output file
// and then apply relocations.
- memcpy(buf + outSecOff, data().data(), data().size());
- uint8_t *bufEnd = buf + outSecOff + data().size();
- relocate<ELFT>(buf + outSecOff, bufEnd);
+ memcpy(buf, content().data(), content().size());
+ relocate<ELFT>(buf, buf + content().size());
}
void InputSection::replace(InputSection *other) {
- alignment = std::max(alignment, other->alignment);
+ addralign = std::max(addralign, other->addralign);
// When a section is replaced with another section that was allocated to
// another partition, the replacement section (and its associated sections)
return cast_or_null<SyntheticSection>(parent);
}
-// Returns the index of the first relocation that points to a region between
-// Begin and Begin+Size.
-template <class IntTy, class RelTy>
-static unsigned getReloc(IntTy begin, IntTy size, const ArrayRef<RelTy> &rels,
- unsigned &relocI) {
- // Start search from RelocI for fast access. That works because the
- // relocations are sorted in .eh_frame.
- for (unsigned n = rels.size(); relocI < n; ++relocI) {
- const RelTy &rel = rels[relocI];
- if (rel.r_offset < begin)
- continue;
-
- if (rel.r_offset < begin + size)
- return relocI;
- return -1;
- }
- return -1;
-}
-
// .eh_frame is a sequence of CIE or FDE records.
// This function splits an input section into records and returns them.
template <class ELFT> void EhInputSection::split() {
- if (areRelocsRela)
- split<ELFT>(relas<ELFT>());
- else
- split<ELFT>(rels<ELFT>());
+ const RelsOrRelas<ELFT> rels = relsOrRelas<ELFT>();
+ // getReloc expects the relocations to be sorted by r_offset. See the comment
+ // in scanRelocs.
+ if (rels.areRelocsRel()) {
+ SmallVector<typename ELFT::Rel, 0> storage;
+ split<ELFT>(sortRels(rels.rels, storage));
+ } else {
+ SmallVector<typename ELFT::Rela, 0> storage;
+ split<ELFT>(sortRels(rels.relas, storage));
+ }
}
template <class ELFT, class RelTy>
void EhInputSection::split(ArrayRef<RelTy> rels) {
- // getReloc expects the relocations to be sorted by r_offset. See the comment
- // in scanRelocs.
- SmallVector<RelTy, 0> storage;
- rels = sortRels(rels, storage);
-
+ ArrayRef<uint8_t> d = content();
+ const char *msg = nullptr;
unsigned relI = 0;
- for (size_t off = 0, end = data().size(); off != end;) {
- size_t size = readEhRecordSize(this, off);
- pieces.emplace_back(off, this, size, getReloc(off, size, rels, relI));
- // The empty record is the end marker.
- if (size == 4)
+ while (!d.empty()) {
+ if (d.size() < 4) {
+ msg = "CIE/FDE too small";
+ break;
+ }
+ uint64_t size = endian::read32<ELFT::TargetEndianness>(d.data());
+ if (size == 0) // ZERO terminator
break;
- off += size;
+ uint32_t id = endian::read32<ELFT::TargetEndianness>(d.data() + 4);
+ size += 4;
+ if (LLVM_UNLIKELY(size > d.size())) {
+ // If it is 0xFFFFFFFF, the next 8 bytes contain the size instead,
+ // but we do not support that format yet.
+ msg = size == UINT32_MAX + uint64_t(4)
+ ? "CIE/FDE too large"
+ : "CIE/FDE ends past the end of the section";
+ break;
+ }
+
+ // Find the first relocation that points to [off,off+size). Relocations
+ // have been sorted by r_offset.
+ const uint64_t off = d.data() - content().data();
+ while (relI != rels.size() && rels[relI].r_offset < off)
+ ++relI;
+ unsigned firstRel = -1;
+ if (relI != rels.size() && rels[relI].r_offset < off + size)
+ firstRel = relI;
+ (id == 0 ? cies : fdes).emplace_back(off, this, size, firstRel);
+ d = d.slice(size);
}
+ if (msg)
+ errorOrWarn("corrupted .eh_frame: " + Twine(msg) + "\n>>> defined in " +
+ getObjMsg(d.data() - content().data()));
}
-static size_t findNull(StringRef s, size_t entSize) {
- // Optimize the common case.
- if (entSize == 1)
- return s.find(0);
+// Return the offset in an output section for a given input offset.
+uint64_t EhInputSection::getParentOffset(uint64_t offset) const {
+ auto it = partition_point(
+ fdes, [=](EhSectionPiece p) { return p.inputOff <= offset; });
+ if (it == fdes.begin() || it[-1].inputOff + it[-1].size <= offset) {
+ it = partition_point(
+ cies, [=](EhSectionPiece p) { return p.inputOff <= offset; });
+ if (it == cies.begin()) // invalid piece
+ return offset;
+ }
+ if (it[-1].outputOff == -1) // invalid piece
+ return offset - it[-1].inputOff;
+ return it[-1].outputOff + (offset - it[-1].inputOff);
+}
+static size_t findNull(StringRef s, size_t entSize) {
for (unsigned i = 0, n = s.size(); i != n; i += entSize) {
const char *b = s.begin() + i;
if (std::all_of(b, b + entSize, [](char c) { return c == 0; }))
return i;
}
- return StringRef::npos;
-}
-
-SyntheticSection *MergeInputSection::getParent() const {
- return cast_or_null<SyntheticSection>(parent);
+ llvm_unreachable("");
}
// Split SHF_STRINGS section. Such section is a sequence of
// null-terminated strings.
-void MergeInputSection::splitStrings(ArrayRef<uint8_t> data, size_t entSize) {
- size_t off = 0;
- bool isAlloc = flags & SHF_ALLOC;
- StringRef s = toStringRef(data);
-
- while (!s.empty()) {
- size_t end = findNull(s, entSize);
- if (end == StringRef::npos)
- fatal(toString(this) + ": string is not null terminated");
- size_t size = end + entSize;
-
- pieces.emplace_back(off, xxHash64(s.substr(0, size)), !isAlloc);
- s = s.substr(size);
- off += size;
+void MergeInputSection::splitStrings(StringRef s, size_t entSize) {
+ const bool live = !(flags & SHF_ALLOC) || !config->gcSections;
+ const char *p = s.data(), *end = s.data() + s.size();
+ if (!std::all_of(end - entSize, end, [](char c) { return c == 0; }))
+ fatal(toString(this) + ": string is not null terminated");
+ if (entSize == 1) {
+ // Optimize the common case.
+ do {
+ size_t size = strlen(p);
+ pieces.emplace_back(p - s.begin(), xxHash64(StringRef(p, size)), live);
+ p += size + 1;
+ } while (p != end);
+ } else {
+ do {
+ size_t size = findNull(StringRef(p, end - p), entSize);
+ pieces.emplace_back(p - s.begin(), xxHash64(StringRef(p, size)), live);
+ p += size + entSize;
+ } while (p != end);
}
}
size_t entSize) {
size_t size = data.size();
assert((size % entSize) == 0);
- bool isAlloc = flags & SHF_ALLOC;
+ const bool live = !(flags & SHF_ALLOC) || !config->gcSections;
- for (size_t i = 0; i != size; i += entSize)
- pieces.emplace_back(i, xxHash64(data.slice(i, entSize)), !isAlloc);
+ pieces.resize_for_overwrite(size / entSize);
+ for (size_t i = 0, j = 0; i != size; i += entSize, j++)
+ pieces[j] = {i, (uint32_t)xxHash64(data.slice(i, entSize)), live};
}
template <class ELFT>
assert(pieces.empty());
if (flags & SHF_STRINGS)
- splitStrings(data(), entsize);
+ splitStrings(toStringRef(contentMaybeDecompress()), entsize);
else
- splitNonStrings(data(), entsize);
+ splitNonStrings(contentMaybeDecompress(), entsize);
}
-SectionPiece *MergeInputSection::getSectionPiece(uint64_t offset) {
- if (this->data().size() <= offset)
+SectionPiece &MergeInputSection::getSectionPiece(uint64_t offset) {
+ if (content().size() <= offset)
fatal(toString(this) + ": offset is outside the section");
-
- // If Offset is not at beginning of a section piece, it is not in the map.
- // In that case we need to do a binary search of the original section piece vector.
- auto it = partition_point(
- pieces, [=](SectionPiece p) { return p.inputOff <= offset; });
- return &it[-1];
+ return partition_point(
+ pieces, [=](SectionPiece p) { return p.inputOff <= offset; })[-1];
}
-// Returns the offset in an output section for a given input offset.
-// Because contents of a mergeable section is not contiguous in output,
-// it is not just an addition to a base output offset.
+// Return the offset in an output section for a given input offset.
uint64_t MergeInputSection::getParentOffset(uint64_t offset) const {
- // If Offset is not at beginning of a section piece, it is not in the map.
- // In that case we need to search from the original section piece vector.
- const SectionPiece &piece = *getSectionPiece(offset);
- uint64_t addend = offset - piece.inputOff;
- return piece.outputOff + addend;
+ const SectionPiece &piece = getSectionPiece(offset);
+ return piece.outputOff + (offset - piece.inputOff);
}
template InputSection::InputSection(ObjFile<ELF32LE> &, const ELF32LE::Shdr &,
template InputSection::InputSection(ObjFile<ELF64BE> &, const ELF64BE::Shdr &,
StringRef);
-template std::string InputSectionBase::getLocation<ELF32LE>(uint64_t);
-template std::string InputSectionBase::getLocation<ELF32BE>(uint64_t);
-template std::string InputSectionBase::getLocation<ELF64LE>(uint64_t);
-template std::string InputSectionBase::getLocation<ELF64BE>(uint64_t);
-
template void InputSection::writeTo<ELF32LE>(uint8_t *);
template void InputSection::writeTo<ELF32BE>(uint8_t *);
template void InputSection::writeTo<ELF64LE>(uint8_t *);
template void InputSection::writeTo<ELF64BE>(uint8_t *);
+template RelsOrRelas<ELF32LE> InputSectionBase::relsOrRelas<ELF32LE>() const;
+template RelsOrRelas<ELF32BE> InputSectionBase::relsOrRelas<ELF32BE>() const;
+template RelsOrRelas<ELF64LE> InputSectionBase::relsOrRelas<ELF64LE>() const;
+template RelsOrRelas<ELF64BE> InputSectionBase::relsOrRelas<ELF64BE>() const;
+
template MergeInputSection::MergeInputSection(ObjFile<ELF32LE> &,
const ELF32LE::Shdr &, StringRef);
template MergeInputSection::MergeInputSection(ObjFile<ELF32BE> &,
#include "LinkerScript.h"
#include "Config.h"
+#include "InputFiles.h"
#include "InputSection.h"
#include "OutputSections.h"
#include "SymbolTable.h"
#include "SyntheticSections.h"
#include "Target.h"
#include "Writer.h"
-#include "lld/Common/Memory.h"
+#include "lld/Common/CommonLinkerContext.h"
#include "lld/Common/Strings.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/ErrorHandling.h"
-#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/Parallel.h"
-#include "llvm/Support/Path.h"
#include "llvm/Support/TimeProfiler.h"
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
-#include <iterator>
#include <limits>
#include <string>
#include <vector>
using namespace lld;
using namespace lld::elf;
-LinkerScript *elf::script;
+std::unique_ptr<LinkerScript> elf::script;
-static uint64_t getOutputSectionVA(SectionBase *sec) {
- OutputSection *os = sec->getOutputSection();
- assert(os && "input section has no output section assigned");
- return os ? os->addr : 0;
+static bool isSectionPrefix(StringRef prefix, StringRef name) {
+ return name.consume_front(prefix) && (name.empty() || name[0] == '.');
+}
+
+static StringRef getOutputSectionName(const InputSectionBase *s) {
+ if (config->relocatable)
+ return s->name;
+
+ // This is for --emit-relocs. If .text.foo is emitted as .text.bar, we want
+ // to emit .rela.text.foo as .rela.text.bar for consistency (this is not
+ // technically required, but not doing it is odd). This code guarantees that.
+ if (auto *isec = dyn_cast<InputSection>(s)) {
+ if (InputSectionBase *rel = isec->getRelocatedSection()) {
+ OutputSection *out = rel->getOutputSection();
+ if (s->type == SHT_RELA)
+ return saver().save(".rela" + out->name);
+ return saver().save(".rel" + out->name);
+ }
+ }
+
+ // A BssSection created for a common symbol is identified as "COMMON" in
+ // linker scripts. It should go to .bss section.
+ if (s->name == "COMMON")
+ return ".bss";
+
+ if (script->hasSectionsCommand)
+ return s->name;
+
+ // When no SECTIONS is specified, emulate GNU ld's internal linker scripts
+ // by grouping sections with certain prefixes.
+
+ // GNU ld places text sections with prefix ".text.hot.", ".text.unknown.",
+ // ".text.unlikely.", ".text.startup." or ".text.exit." before others.
+ // We provide an option -z keep-text-section-prefix to group such sections
+ // into separate output sections. This is more flexible. See also
+ // sortISDBySectionOrder().
+ // ".text.unknown" means the hotness of the section is unknown. When
+ // SampleFDO is used, if a function doesn't have sample, it could be very
+ // cold or it could be a new function never being sampled. Those functions
+ // will be kept in the ".text.unknown" section.
+ // ".text.split." holds symbols which are split out from functions in other
+ // input sections. For example, with -fsplit-machine-functions, placing the
+ // cold parts in .text.split instead of .text.unlikely mitigates against poor
+ // profile inaccuracy. Techniques such as hugepage remapping can make
+ // conservative decisions at the section granularity.
+ if (isSectionPrefix(".text", s->name)) {
+ if (config->zKeepTextSectionPrefix)
+ for (StringRef v : {".text.hot", ".text.unknown", ".text.unlikely",
+ ".text.startup", ".text.exit", ".text.split"})
+ if (isSectionPrefix(v.substr(5), s->name.substr(5)))
+ return v;
+ return ".text";
+ }
+
+ for (StringRef v :
+ {".data.rel.ro", ".data", ".rodata", ".bss.rel.ro", ".bss",
+ ".gcc_except_table", ".init_array", ".fini_array", ".tbss", ".tdata",
+ ".ARM.exidx", ".ARM.extab", ".ctors", ".dtors",
+ ".openbsd.randomdata.", ".openbsd.mutable."})
+ if (isSectionPrefix(v, s->name))
+ return v;
+
+ return s->name;
}
uint64_t ExprValue::getValue() const {
if (sec)
- return alignTo(sec->getOffset(val) + getOutputSectionVA(sec),
- alignment);
- return alignTo(val, alignment);
+ return alignToPowerOf2(sec->getOutputSection()->addr + sec->getOffset(val),
+ alignment);
+ return alignToPowerOf2(val, alignment);
}
uint64_t ExprValue::getSecAddr() const {
- if (sec)
- return sec->getOffset(0) + getOutputSectionVA(sec);
- return 0;
+ return sec ? sec->getOutputSection()->addr + sec->getOffset(0) : 0;
}
uint64_t ExprValue::getSectionOffset() const {
return getValue() - getSecAddr();
}
-OutputSection *LinkerScript::createOutputSection(StringRef name,
- StringRef location) {
- OutputSection *&secRef = nameToOutputSection[name];
- OutputSection *sec;
- if (secRef && secRef->location.empty()) {
+OutputDesc *LinkerScript::createOutputSection(StringRef name,
+ StringRef location) {
+ OutputDesc *&secRef = nameToOutputSection[CachedHashStringRef(name)];
+ OutputDesc *sec;
+ if (secRef && secRef->osec.location.empty()) {
// There was a forward reference.
sec = secRef;
} else {
- sec = make<OutputSection>(name, SHT_PROGBITS, 0);
+ sec = make<OutputDesc>(name, SHT_PROGBITS, 0);
if (!secRef)
secRef = sec;
}
- sec->location = std::string(location);
+ sec->osec.location = std::string(location);
return sec;
}
-OutputSection *LinkerScript::getOrCreateOutputSection(StringRef name) {
- OutputSection *&cmdRef = nameToOutputSection[name];
+OutputDesc *LinkerScript::getOrCreateOutputSection(StringRef name) {
+ OutputDesc *&cmdRef = nameToOutputSection[CachedHashStringRef(name)];
if (!cmdRef)
- cmdRef = make<OutputSection>(name, SHT_PROGBITS, 0);
+ cmdRef = make<OutputDesc>(name, SHT_PROGBITS, 0);
return cmdRef;
}
// Expands the memory region by the specified size.
static void expandMemoryRegion(MemoryRegion *memRegion, uint64_t size,
- StringRef regionName, StringRef secName) {
+ StringRef secName) {
memRegion->curPos += size;
uint64_t newSize = memRegion->curPos - (memRegion->origin)().getValue();
uint64_t length = (memRegion->length)().getValue();
if (newSize > length)
- error("section '" + secName + "' will not fit in region '" + regionName +
- "': overflowed by " + Twine(newSize - length) + " bytes");
+ error("section '" + secName + "' will not fit in region '" +
+ memRegion->name + "': overflowed by " + Twine(newSize - length) +
+ " bytes");
}
void LinkerScript::expandMemoryRegions(uint64_t size) {
- if (ctx->memRegion)
- expandMemoryRegion(ctx->memRegion, size, ctx->memRegion->name,
- ctx->outSec->name);
+ if (state->memRegion)
+ expandMemoryRegion(state->memRegion, size, state->outSec->name);
// Only expand the LMARegion if it is different from memRegion.
- if (ctx->lmaRegion && ctx->memRegion != ctx->lmaRegion)
- expandMemoryRegion(ctx->lmaRegion, size, ctx->lmaRegion->name,
- ctx->outSec->name);
+ if (state->lmaRegion && state->memRegion != state->lmaRegion)
+ expandMemoryRegion(state->lmaRegion, size, state->outSec->name);
}
void LinkerScript::expandOutputSection(uint64_t size) {
- ctx->outSec->size += size;
+ state->outSec->size += size;
expandMemoryRegions(size);
}
uint64_t val = e().getValue();
if (val < dot && inSec)
error(loc + ": unable to move location counter backward for: " +
- ctx->outSec->name);
+ state->outSec->name);
// Update to location counter means update to section size.
if (inSec)
// If a symbol was in PROVIDE(), we need to define it only
// when it is a referenced undefined symbol.
- Symbol *b = symtab->find(cmd->name);
- if (b && !b->isDefined())
+ Symbol *b = symtab.find(cmd->name);
+ if (b && !b->isDefined() && !b->isCommon())
return true;
return false;
}
Defined newSym(nullptr, cmd->name, STB_GLOBAL, visibility, value.type,
symValue, 0, sec);
- Symbol *sym = symtab->insert(cmd->name);
+ Symbol *sym = symtab.insert(cmd->name);
sym->mergeProperties(newSym);
- sym->replace(newSym);
+ newSym.overwrite(*sym);
+ sym->isUsedInRegularObj = true;
cmd->sym = cast<Defined>(sym);
}
nullptr);
// We can't calculate final value right now.
- Symbol *sym = symtab->insert(cmd->name);
+ Symbol *sym = symtab.insert(cmd->name);
sym->mergeProperties(newSym);
- sym->replace(newSym);
+ newSym.overwrite(*sym);
cmd->sym = cast<Defined>(sym);
cmd->provide = false;
+ sym->isUsedInRegularObj = true;
sym->scriptDefined = true;
}
// Collect section/value pairs of linker-script-defined symbols. This is used to
// check whether symbol values converge.
static SymbolAssignmentMap
-getSymbolAssignmentValues(const std::vector<BaseCommand *> §ionCommands) {
+getSymbolAssignmentValues(ArrayRef<SectionCommand *> sectionCommands) {
SymbolAssignmentMap ret;
- for (BaseCommand *base : sectionCommands) {
- if (auto *cmd = dyn_cast<SymbolAssignment>(base)) {
- if (cmd->sym) // sym is nullptr for dot.
- ret.try_emplace(cmd->sym,
- std::make_pair(cmd->sym->section, cmd->sym->value));
+ for (SectionCommand *cmd : sectionCommands) {
+ if (auto *assign = dyn_cast<SymbolAssignment>(cmd)) {
+ if (assign->sym) // sym is nullptr for dot.
+ ret.try_emplace(assign->sym, std::make_pair(assign->sym->section,
+ assign->sym->value));
continue;
}
- for (BaseCommand *sub_base : cast<OutputSection>(base)->sectionCommands)
- if (auto *cmd = dyn_cast<SymbolAssignment>(sub_base))
- if (cmd->sym)
- ret.try_emplace(cmd->sym,
- std::make_pair(cmd->sym->section, cmd->sym->value));
+ for (SectionCommand *subCmd : cast<OutputDesc>(cmd)->osec.commands)
+ if (auto *assign = dyn_cast<SymbolAssignment>(subCmd))
+ if (assign->sym)
+ ret.try_emplace(assign->sym, std::make_pair(assign->sym->section,
+ assign->sym->value));
}
return ret;
}
// Process INSERT [AFTER|BEFORE] commands. For each command, we move the
// specified output section to the designated place.
void LinkerScript::processInsertCommands() {
- std::vector<OutputSection *> moves;
+ SmallVector<OutputDesc *, 0> moves;
for (const InsertCommand &cmd : insertCommands) {
for (StringRef name : cmd.names) {
// If base is empty, it may have been discarded by
- // adjustSectionsBeforeSorting(). We do not handle such output sections.
- auto from = llvm::find_if(sectionCommands, [&](BaseCommand *base) {
- return isa<OutputSection>(base) &&
- cast<OutputSection>(base)->name == name;
+ // adjustOutputSections(). We do not handle such output sections.
+ auto from = llvm::find_if(sectionCommands, [&](SectionCommand *subCmd) {
+ return isa<OutputDesc>(subCmd) &&
+ cast<OutputDesc>(subCmd)->osec.name == name;
});
if (from == sectionCommands.end())
continue;
- moves.push_back(cast<OutputSection>(*from));
+ moves.push_back(cast<OutputDesc>(*from));
sectionCommands.erase(from);
}
- auto insertPos = llvm::find_if(sectionCommands, [&cmd](BaseCommand *base) {
- auto *to = dyn_cast<OutputSection>(base);
- return to != nullptr && to->name == cmd.where;
- });
+ auto insertPos =
+ llvm::find_if(sectionCommands, [&cmd](SectionCommand *subCmd) {
+ auto *to = dyn_cast<OutputDesc>(subCmd);
+ return to != nullptr && to->osec.name == cmd.where;
+ });
if (insertPos == sectionCommands.end()) {
error("unable to insert " + cmd.names[0] +
(cmd.isAfter ? " after " : " before ") + cmd.where);
// we don't know their final values until late stages of link. Here we scan
// over symbol assignment commands and create placeholder symbols if needed.
void LinkerScript::declareSymbols() {
- assert(!ctx);
- for (BaseCommand *base : sectionCommands) {
- if (auto *cmd = dyn_cast<SymbolAssignment>(base)) {
- declareSymbol(cmd);
+ assert(!state);
+ for (SectionCommand *cmd : sectionCommands) {
+ if (auto *assign = dyn_cast<SymbolAssignment>(cmd)) {
+ declareSymbol(assign);
continue;
}
// we can't say for sure if it is going to be included or not.
// Skip such sections for now. Improve the checks if we ever
// need symbols from that sections to be declared early.
- auto *sec = cast<OutputSection>(base);
- if (sec->constraint != ConstraintKind::NoConstraint)
+ const OutputSection &sec = cast<OutputDesc>(cmd)->osec;
+ if (sec.constraint != ConstraintKind::NoConstraint)
continue;
- for (BaseCommand *base2 : sec->sectionCommands)
- if (auto *cmd = dyn_cast<SymbolAssignment>(base2))
- declareSymbol(cmd);
+ for (SectionCommand *cmd : sec.commands)
+ if (auto *assign = dyn_cast<SymbolAssignment>(cmd))
+ declareSymbol(assign);
}
}
// ">" is not a mistake. Sections with larger alignments are placed
// before sections with smaller alignments in order to reduce the
// amount of padding necessary. This is compatible with GNU.
- return a->alignment > b->alignment;
+ return a->addralign > b->addralign;
};
auto nameComparator = [](InputSectionBase *a, InputSectionBase *b) {
return a->name < b->name;
}
// Compute and remember which sections the InputSectionDescription matches.
-std::vector<InputSectionBase *>
+SmallVector<InputSectionBase *, 0>
LinkerScript::computeInputSections(const InputSectionDescription *cmd,
ArrayRef<InputSectionBase *> sections) {
- std::vector<InputSectionBase *> ret;
- std::vector<size_t> indexes;
+ SmallVector<InputSectionBase *, 0> ret;
+ SmallVector<size_t, 0> indexes;
DenseSet<size_t> seen;
auto sortByPositionThenCommandLine = [&](size_t begin, size_t end) {
llvm::sort(MutableArrayRef<size_t>(indexes).slice(begin, end - begin));
if (!sec->isLive() || sec->parent || seen.contains(i))
continue;
- // For -emit-relocs we have to ignore entries like
+ // For --emit-relocs we have to ignore entries like
// .rela.dyn : { *(.rela.data) }
// which are common because they are in the default bfd script.
// We do not ignore SHT_REL[A] linker-synthesized sections here because
return ret;
}
-void LinkerScript::discard(InputSectionBase *s) {
- if (s == in.shStrTab || s == mainPart->relrDyn)
- error("discarding " + s->name + " section is not allowed");
-
- // You can discard .hash and .gnu.hash sections by linker scripts. Since
- // they are synthesized sections, we need to handle them differently than
- // other regular sections.
- if (s == mainPart->gnuHashTab)
- mainPart->gnuHashTab = nullptr;
- if (s == mainPart->hashTab)
- mainPart->hashTab = nullptr;
-
- s->markDead();
- s->parent = nullptr;
- for (InputSection *ds : s->dependentSections)
- discard(ds);
+void LinkerScript::discard(InputSectionBase &s) {
+ if (&s == in.shStrTab.get())
+ error("discarding " + s.name + " section is not allowed");
+
+ s.markDead();
+ s.parent = nullptr;
+ for (InputSection *sec : s.dependentSections)
+ discard(*sec);
}
void LinkerScript::discardSynthetic(OutputSection &outCmd) {
for (Partition &part : partitions) {
if (!part.armExidx || !part.armExidx->isLive())
continue;
- std::vector<InputSectionBase *> secs(part.armExidx->exidxSections.begin(),
- part.armExidx->exidxSections.end());
- for (BaseCommand *base : outCmd.sectionCommands)
- if (auto *cmd = dyn_cast<InputSectionDescription>(base)) {
- std::vector<InputSectionBase *> matches =
- computeInputSections(cmd, secs);
- for (InputSectionBase *s : matches)
- discard(s);
- }
+ SmallVector<InputSectionBase *, 0> secs(
+ part.armExidx->exidxSections.begin(),
+ part.armExidx->exidxSections.end());
+ for (SectionCommand *cmd : outCmd.commands)
+ if (auto *isd = dyn_cast<InputSectionDescription>(cmd))
+ for (InputSectionBase *s : computeInputSections(isd, secs))
+ discard(*s);
}
}
-std::vector<InputSectionBase *>
+SmallVector<InputSectionBase *, 0>
LinkerScript::createInputSectionList(OutputSection &outCmd) {
- std::vector<InputSectionBase *> ret;
+ SmallVector<InputSectionBase *, 0> ret;
- for (BaseCommand *base : outCmd.sectionCommands) {
- if (auto *cmd = dyn_cast<InputSectionDescription>(base)) {
- cmd->sectionBases = computeInputSections(cmd, inputSections);
- for (InputSectionBase *s : cmd->sectionBases)
+ for (SectionCommand *cmd : outCmd.commands) {
+ if (auto *isd = dyn_cast<InputSectionDescription>(cmd)) {
+ isd->sectionBases = computeInputSections(isd, ctx.inputSections);
+ for (InputSectionBase *s : isd->sectionBases)
s->parent = &outCmd;
- ret.insert(ret.end(), cmd->sectionBases.begin(), cmd->sectionBases.end());
+ ret.insert(ret.end(), isd->sectionBases.begin(), isd->sectionBases.end());
}
}
return ret;
// Create output sections described by SECTIONS commands.
void LinkerScript::processSectionCommands() {
auto process = [this](OutputSection *osec) {
- std::vector<InputSectionBase *> v = createInputSectionList(*osec);
+ SmallVector<InputSectionBase *, 0> v = createInputSectionList(*osec);
// The output section name `/DISCARD/' is special.
// Any input section assigned to it is discarded.
if (osec->name == "/DISCARD/") {
for (InputSectionBase *s : v)
- discard(s);
+ discard(*s);
discardSynthetic(*osec);
- osec->sectionCommands.clear();
+ osec->commands.clear();
return false;
}
if (!matchConstraints(v, osec->constraint)) {
for (InputSectionBase *s : v)
s->parent = nullptr;
- osec->sectionCommands.clear();
+ osec->commands.clear();
return false;
}
if (osec->subalignExpr) {
uint32_t subalign = osec->subalignExpr().getValue();
for (InputSectionBase *s : v)
- s->alignment = subalign;
+ s->addralign = subalign;
}
// Set the partition field the same way OutputSection::recordSection()
// Process OVERWRITE_SECTIONS first so that it can overwrite the main script
// or orphans.
- DenseMap<StringRef, OutputSection *> map;
+ DenseMap<CachedHashStringRef, OutputDesc *> map;
size_t i = 0;
- for (OutputSection *osec : overwriteSections)
- if (process(osec) && !map.try_emplace(osec->name, osec).second)
+ for (OutputDesc *osd : overwriteSections) {
+ OutputSection *osec = &osd->osec;
+ if (process(osec) &&
+ !map.try_emplace(CachedHashStringRef(osec->name), osd).second)
warn("OVERWRITE_SECTIONS specifies duplicate " + osec->name);
- for (BaseCommand *&base : sectionCommands)
- if (auto *osec = dyn_cast<OutputSection>(base)) {
- if (OutputSection *overwrite = map.lookup(osec->name)) {
- log(overwrite->location + " overwrites " + osec->name);
- overwrite->sectionIndex = i++;
+ }
+ for (SectionCommand *&base : sectionCommands)
+ if (auto *osd = dyn_cast<OutputDesc>(base)) {
+ OutputSection *osec = &osd->osec;
+ if (OutputDesc *overwrite = map.lookup(CachedHashStringRef(osec->name))) {
+ log(overwrite->osec.location + " overwrites " + osec->name);
+ overwrite->osec.sectionIndex = i++;
base = overwrite;
} else if (process(osec)) {
osec->sectionIndex = i++;
// If an OVERWRITE_SECTIONS specified output section is not in
// sectionCommands, append it to the end. The section will be inserted by
// orphan placement.
- for (OutputSection *osec : overwriteSections)
- if (osec->partition == 1 && osec->sectionIndex == UINT32_MAX)
- sectionCommands.push_back(osec);
+ for (OutputDesc *osd : overwriteSections)
+ if (osd->osec.partition == 1 && osd->osec.sectionIndex == UINT32_MAX)
+ sectionCommands.push_back(osd);
}
void LinkerScript::processSymbolAssignments() {
aether = make<OutputSection>("", 0, SHF_ALLOC);
aether->sectionIndex = 1;
- // ctx captures the local AddressState and makes it accessible deliberately.
+ // `st` captures the local AddressState and makes it accessible deliberately.
// This is needed as there are some cases where we cannot just thread the
// current state through to a lambda function created by the script parser.
- AddressState state;
- ctx = &state;
- ctx->outSec = aether;
+ AddressState st;
+ state = &st;
+ st.outSec = aether;
- for (BaseCommand *base : sectionCommands) {
- if (auto *cmd = dyn_cast<SymbolAssignment>(base))
- addSymbol(cmd);
+ for (SectionCommand *cmd : sectionCommands) {
+ if (auto *assign = dyn_cast<SymbolAssignment>(cmd))
+ addSymbol(assign);
else
- for (BaseCommand *sub_base : cast<OutputSection>(base)->sectionCommands)
- if (auto *cmd = dyn_cast<SymbolAssignment>(sub_base))
- addSymbol(cmd);
+ for (SectionCommand *subCmd : cast<OutputDesc>(cmd)->osec.commands)
+ if (auto *assign = dyn_cast<SymbolAssignment>(subCmd))
+ addSymbol(assign);
}
- ctx = nullptr;
+ state = nullptr;
}
-static OutputSection *findByName(ArrayRef<BaseCommand *> vec,
+static OutputSection *findByName(ArrayRef<SectionCommand *> vec,
StringRef name) {
- for (BaseCommand *base : vec)
- if (auto *sec = dyn_cast<OutputSection>(base))
- if (sec->name == name)
- return sec;
+ for (SectionCommand *cmd : vec)
+ if (auto *osd = dyn_cast<OutputDesc>(cmd))
+ if (osd->osec.name == name)
+ return &osd->osec;
return nullptr;
}
-static OutputSection *createSection(InputSectionBase *isec,
- StringRef outsecName) {
- OutputSection *sec = script->createOutputSection(outsecName, "<internal>");
- sec->recordSection(isec);
- return sec;
+static OutputDesc *createSection(InputSectionBase *isec, StringRef outsecName) {
+ OutputDesc *osd = script->createOutputSection(outsecName, "<internal>");
+ osd->osec.recordSection(isec);
+ return osd;
}
-static OutputSection *
-addInputSec(StringMap<TinyPtrVector<OutputSection *>> &map,
- InputSectionBase *isec, StringRef outsecName) {
+static OutputDesc *addInputSec(StringMap<TinyPtrVector<OutputSection *>> &map,
+ InputSectionBase *isec, StringRef outsecName) {
// Sections with SHT_GROUP or SHF_GROUP attributes reach here only when the -r
// option is given. A section with SHT_GROUP defines a "section group", and
// its members have SHF_GROUP attribute. Usually these flags have already been
return nullptr;
}
- out->relocationSection = createSection(isec, outsecName);
- return out->relocationSection;
+ OutputDesc *osd = createSection(isec, outsecName);
+ out->relocationSection = &osd->osec;
+ return osd;
}
// The ELF spec just says
// end up being linked to the same output section. The casts are fine
// because everything in the map was created by the orphan placement code.
auto *firstIsec = cast<InputSectionBase>(
- cast<InputSectionDescription>(sec->sectionCommands[0])
- ->sectionBases[0]);
+ cast<InputSectionDescription>(sec->commands[0])->sectionBases[0]);
OutputSection *firstIsecOut =
firstIsec->flags & SHF_LINK_ORDER
? firstIsec->getLinkOrderDep()->getOutputSection()
return nullptr;
}
- OutputSection *sec = createSection(isec, outsecName);
- v.push_back(sec);
- return sec;
+ OutputDesc *osd = createSection(isec, outsecName);
+ v.push_back(&osd->osec);
+ return osd;
}
// Add sections that didn't match any sections command.
void LinkerScript::addOrphanSections() {
StringMap<TinyPtrVector<OutputSection *>> map;
- std::vector<OutputSection *> v;
+ SmallVector<OutputDesc *, 0> v;
- std::function<void(InputSectionBase *)> add;
- add = [&](InputSectionBase *s) {
+ auto add = [&](InputSectionBase *s) {
if (s->isLive() && !s->parent) {
orphanSections.push_back(s);
} else if (OutputSection *sec = findByName(sectionCommands, name)) {
sec->recordSection(s);
} else {
- if (OutputSection *os = addInputSec(map, s, name))
- v.push_back(os);
+ if (OutputDesc *osd = addInputSec(map, s, name))
+ v.push_back(osd);
assert(isa<MergeInputSection>(s) ||
s->getOutputSection()->sectionIndex == UINT32_MAX);
}
}
-
- if (config->relocatable)
- for (InputSectionBase *depSec : s->dependentSections)
- if (depSec->flags & SHF_LINK_ORDER)
- add(depSec);
};
// For further --emit-reloc handling code we need target output section
// to be created before we create relocation output section, so we want
// to create target sections first. We do not want priority handling
// for synthetic sections because them are special.
- for (InputSectionBase *isec : inputSections) {
+ size_t n = 0;
+ for (InputSectionBase *isec : ctx.inputSections) {
+ // Process InputSection and MergeInputSection.
+ if (LLVM_LIKELY(isa<InputSection>(isec)))
+ ctx.inputSections[n++] = isec;
+
// In -r links, SHF_LINK_ORDER sections are added while adding their parent
// sections because we need to know the parent's output section before we
// can select an output section for the SHF_LINK_ORDER section.
if (auto *relIS = dyn_cast_or_null<InputSectionBase>(rel->parent))
add(relIS);
add(isec);
+ if (config->relocatable)
+ for (InputSectionBase *depSec : isec->dependentSections)
+ if (depSec->flags & SHF_LINK_ORDER)
+ add(depSec);
}
+ // Keep just InputSection.
+ ctx.inputSections.resize(n);
// If no SECTIONS command was given, we should insert sections commands
// before others, so that we can handle scripts which refers them,
}
}
-uint64_t LinkerScript::advance(uint64_t size, unsigned alignment) {
- dot = alignTo(dot, alignment) + size;
- return dot;
-}
-
-void LinkerScript::output(InputSection *s) {
- assert(ctx->outSec == s->getParent());
- uint64_t before = advance(0, 1);
- uint64_t pos = advance(s->getSize(), s->alignment);
- s->outSecOff = pos - s->getSize() - ctx->outSec->addr;
-
- // Update output section size after adding each section. This is so that
- // SIZEOF works correctly in the case below:
- // .foo { *(.aaa) a = SIZEOF(.foo); *(.bbb) }
- expandOutputSection(pos - before);
-}
-
-void LinkerScript::switchTo(OutputSection *sec) {
- ctx->outSec = sec;
-
- uint64_t pos = advance(0, 1);
- if (sec->addrExpr && script->hasSectionsCommand) {
- // The alignment is ignored.
- ctx->outSec->addr = pos;
- } else {
- // ctx->outSec->alignment is the max of ALIGN and the maximum of input
- // section alignments.
- ctx->outSec->addr = advance(0, ctx->outSec->alignment);
- expandMemoryRegions(ctx->outSec->addr - pos);
- }
-}
-
// This function searches for a memory region to place the given output
// section in. If found, a pointer to the appropriate memory region is
-// returned. Otherwise, a nullptr is returned.
-MemoryRegion *LinkerScript::findMemoryRegion(OutputSection *sec) {
+// returned in the first member of the pair. Otherwise, a nullptr is returned.
+// The second member of the pair is a hint that should be passed to the
+// subsequent call of this method.
+std::pair<MemoryRegion *, MemoryRegion *>
+LinkerScript::findMemoryRegion(OutputSection *sec, MemoryRegion *hint) {
+ // Non-allocatable sections are not part of the process image.
+ if (!(sec->flags & SHF_ALLOC)) {
+ if (!sec->memoryRegionName.empty())
+ warn("ignoring memory region assignment for non-allocatable section '" +
+ sec->name + "'");
+ return {nullptr, nullptr};
+ }
+
// If a memory region name was specified in the output section command,
// then try to find that region first.
if (!sec->memoryRegionName.empty()) {
if (MemoryRegion *m = memoryRegions.lookup(sec->memoryRegionName))
- return m;
+ return {m, m};
error("memory region '" + sec->memoryRegionName + "' not declared");
- return nullptr;
+ return {nullptr, nullptr};
}
// If at least one memory region is defined, all sections must
// belong to some memory region. Otherwise, we don't need to do
// anything for memory regions.
if (memoryRegions.empty())
- return nullptr;
+ return {nullptr, nullptr};
+
+ // An orphan section should continue the previous memory region.
+ if (sec->sectionIndex == UINT32_MAX && hint)
+ return {hint, hint};
// See if a region can be found by matching section flags.
for (auto &pair : memoryRegions) {
MemoryRegion *m = pair.second;
- if ((m->flags & sec->flags) && (m->negFlags & sec->flags) == 0)
- return m;
+ if (m->compatibleWith(sec->flags))
+ return {m, nullptr};
}
// Otherwise, no suitable region was found.
- if (sec->flags & SHF_ALLOC)
- error("no memory region specified for section '" + sec->name + "'");
- return nullptr;
+ error("no memory region specified for section '" + sec->name + "'");
+ return {nullptr, nullptr};
}
static OutputSection *findFirstSection(PhdrEntry *load) {
// for a single sections command (e.g. ".text { *(.text); }").
void LinkerScript::assignOffsets(OutputSection *sec) {
const bool isTbss = (sec->flags & SHF_TLS) && sec->type == SHT_NOBITS;
- const bool sameMemRegion = ctx->memRegion == sec->memRegion;
- const bool prevLMARegionIsDefault = ctx->lmaRegion == nullptr;
+ const bool sameMemRegion = state->memRegion == sec->memRegion;
+ const bool prevLMARegionIsDefault = state->lmaRegion == nullptr;
const uint64_t savedDot = dot;
- ctx->memRegion = sec->memRegion;
- ctx->lmaRegion = sec->lmaRegion;
+ state->memRegion = sec->memRegion;
+ state->lmaRegion = sec->lmaRegion;
if (!(sec->flags & SHF_ALLOC)) {
// Non-SHF_ALLOC sections have zero addresses.
} else if (isTbss) {
// Allow consecutive SHF_TLS SHT_NOBITS output sections. The address range
// starts from the end address of the previous tbss section.
- if (ctx->tbssAddr == 0)
- ctx->tbssAddr = dot;
+ if (state->tbssAddr == 0)
+ state->tbssAddr = dot;
else
- dot = ctx->tbssAddr;
+ dot = state->tbssAddr;
} else {
- if (ctx->memRegion)
- dot = ctx->memRegion->curPos;
+ if (state->memRegion)
+ dot = state->memRegion->curPos;
if (sec->addrExpr)
setDot(sec->addrExpr, sec->location, false);
// expression so that it now starts past the current curPos of the enclosing
// region, we need to expand the current region to account for the space
// between the previous section, if any, and the start of this section.
- if (ctx->memRegion && ctx->memRegion->curPos < dot)
- expandMemoryRegion(ctx->memRegion, dot - ctx->memRegion->curPos,
- ctx->memRegion->name, sec->name);
+ if (state->memRegion && state->memRegion->curPos < dot)
+ expandMemoryRegion(state->memRegion, dot - state->memRegion->curPos,
+ sec->name);
}
+ // This section was previously a call to switchTo(), but switchTo()
+ // was unrolled here.
+ // On OpenBSD, we had consistently moved the call to switchTo()
+ // below the next section.
+ state->outSec = sec;
+ if (sec->addrExpr && script->hasSectionsCommand) {
+ // The alignment is ignored.
+ sec->addr = dot;
+ } else {
+ // sec->alignment is the max of ALIGN and the maximum of input
+ // section alignments.
+ const uint64_t pos = dot;
+ dot = alignToPowerOf2(dot, sec->addralign);
+ sec->addr = dot;
+ expandMemoryRegions(dot - pos);
+ }
- // ctx->lmaOffset is LMA minus VMA. If LMA is explicitly specified via AT() or
- // AT>, recompute ctx->lmaOffset; otherwise, if both previous/current LMA
+ // state->lmaOffset is LMA minus VMA. If LMA is explicitly specified via AT()
+ // or AT>, recompute state->lmaOffset; otherwise, if both previous/current LMA
// region is the default, and the two sections are in the same memory region,
// reuse previous lmaOffset; otherwise, reset lmaOffset to 0. This emulates
// heuristics described in
// https://sourceware.org/binutils/docs/ld/Output-Section-LMA.html
- if (sec->lmaExpr)
- ctx->lmaOffset = sec->lmaExpr().getValue() - dot;
- else if (MemoryRegion *mr = sec->lmaRegion)
- ctx->lmaOffset = alignTo(mr->curPos, sec->alignment) - dot;
- else if (!sameMemRegion || !prevLMARegionIsDefault)
- ctx->lmaOffset = 0;
+ if (sec->lmaExpr) {
+ state->lmaOffset = sec->lmaExpr().getValue() - dot;
+ } else if (MemoryRegion *mr = sec->lmaRegion) {
+ uint64_t lmaStart = alignToPowerOf2(mr->curPos, sec->addralign);
+ if (mr->curPos < lmaStart)
+ expandMemoryRegion(mr, lmaStart - mr->curPos, sec->name);
+ state->lmaOffset = lmaStart - dot;
+ } else if (!sameMemRegion || !prevLMARegionIsDefault) {
+ state->lmaOffset = 0;
+ }
- switchTo(sec);
+ // On OpenBSD, the switchTo() call was here.
- // Propagate ctx->lmaOffset to the first "non-header" section.
- if (PhdrEntry *l = ctx->outSec->ptLoad)
+ // Propagate state->lmaOffset to the first "non-header" section.
+ if (PhdrEntry *l = sec->ptLoad)
if (sec == findFirstSection(l))
- l->lmaOffset = ctx->lmaOffset;
+ l->lmaOffset = state->lmaOffset;
// We can call this method multiple times during the creation of
// thunks and want to start over calculation each time.
// We visited SectionsCommands from processSectionCommands to
// layout sections. Now, we visit SectionsCommands again to fix
// section offsets.
- for (BaseCommand *base : sec->sectionCommands) {
+ for (SectionCommand *cmd : sec->commands) {
// This handles the assignments to symbol or to the dot.
- if (auto *cmd = dyn_cast<SymbolAssignment>(base)) {
- cmd->addr = dot;
- assignSymbol(cmd, true);
- cmd->size = dot - cmd->addr;
+ if (auto *assign = dyn_cast<SymbolAssignment>(cmd)) {
+ assign->addr = dot;
+ assignSymbol(assign, true);
+ assign->size = dot - assign->addr;
continue;
}
// Handle BYTE(), SHORT(), LONG(), or QUAD().
- if (auto *cmd = dyn_cast<ByteCommand>(base)) {
- cmd->offset = dot - ctx->outSec->addr;
- dot += cmd->size;
- expandOutputSection(cmd->size);
+ if (auto *data = dyn_cast<ByteCommand>(cmd)) {
+ data->offset = dot - sec->addr;
+ dot += data->size;
+ expandOutputSection(data->size);
continue;
}
// Handle a single input section description command.
// It calculates and assigns the offsets for each section and also
// updates the output section size.
- for (InputSection *sec : cast<InputSectionDescription>(base)->sections)
- output(sec);
+ for (InputSection *isec : cast<InputSectionDescription>(cmd)->sections) {
+ assert(isec->getParent() == sec);
+ const uint64_t pos = dot;
+ dot = alignToPowerOf2(dot, isec->addralign);
+ isec->outSecOff = dot - sec->addr;
+ dot += isec->getSize();
+
+ // Update output section size after adding each section. This is so that
+ // SIZEOF works correctly in the case below:
+ // .foo { *(.aaa) a = SIZEOF(.foo); *(.bbb) }
+ expandOutputSection(dot - pos);
+ }
}
// Non-SHF_ALLOC sections do not affect the addresses of other OutputSections
dot = savedDot;
} else if (isTbss) {
// NOBITS TLS sections are similar. Additionally save the end address.
- ctx->tbssAddr = dot;
+ state->tbssAddr = dot;
dot = savedDot;
}
}
-static bool isDiscardable(OutputSection &sec) {
+static bool isDiscardable(const OutputSection &sec) {
if (sec.name == "/DISCARD/")
return true;
if (sec.usedInExpression)
return false;
- for (BaseCommand *base : sec.sectionCommands) {
- if (auto cmd = dyn_cast<SymbolAssignment>(base))
+ for (SectionCommand *cmd : sec.commands) {
+ if (auto assign = dyn_cast<SymbolAssignment>(cmd))
// Don't create empty output sections just for unreferenced PROVIDE
// symbols.
- if (cmd->name != "." && !cmd->sym)
+ if (assign->name != "." && !assign->sym)
continue;
- if (!isa<InputSectionDescription>(*base))
+ if (!isa<InputSectionDescription>(*cmd))
return false;
}
return true;
}
+bool LinkerScript::isDiscarded(const OutputSection *sec) const {
+ return hasSectionsCommand && (getFirstInputSection(sec) == nullptr) &&
+ isDiscardable(*sec);
+}
+
static void maybePropagatePhdrs(OutputSection &sec,
- std::vector<StringRef> &phdrs) {
+ SmallVector<StringRef, 0> &phdrs) {
if (sec.phdrs.empty()) {
// To match the bfd linker script behaviour, only propagate program
// headers to sections that are allocated.
}
}
-void LinkerScript::adjustSectionsBeforeSorting() {
+void LinkerScript::adjustOutputSections() {
// If the output section contains only symbol assignments, create a
// corresponding output section. The issue is what to do with linker script
// like ".foo : { symbol = 42; }". One option would be to convert it to
// the previous sections. Only a few flags are needed to keep the impact low.
uint64_t flags = SHF_ALLOC;
- std::vector<StringRef> defPhdrs;
- for (BaseCommand *&cmd : sectionCommands) {
- auto *sec = dyn_cast<OutputSection>(cmd);
- if (!sec)
+ SmallVector<StringRef, 0> defPhdrs;
+ for (SectionCommand *&cmd : sectionCommands) {
+ if (!isa<OutputDesc>(cmd))
continue;
+ auto *sec = &cast<OutputDesc>(cmd)->osec;
// Handle align (e.g. ".foo : ALIGN(16) { ... }").
if (sec->alignExpr)
- sec->alignment =
- std::max<uint32_t>(sec->alignment, sec->alignExpr().getValue());
+ sec->addralign =
+ std::max<uint32_t>(sec->addralign, sec->alignExpr().getValue());
- // The input section might have been removed (if it was an empty synthetic
- // section), but we at least know the flags.
- if (sec->hasInputSections)
+ bool isEmpty = (getFirstInputSection(sec) == nullptr);
+ bool discardable = isEmpty && isDiscardable(*sec);
+ // If sec has at least one input section and not discarded, remember its
+ // flags to be inherited by subsequent output sections. (sec may contain
+ // just one empty synthetic section.)
+ if (sec->hasInputSections && !discardable)
flags = sec->flags;
// We do not want to keep any special flags for output section
// in case it is empty.
- bool isEmpty = (getFirstInputSection(sec) == nullptr);
if (isEmpty)
sec->flags = flags & ((sec->nonAlloc ? 0 : (uint64_t)SHF_ALLOC) |
SHF_WRITE | SHF_EXECINSTR);
if (sec->sectionIndex != UINT32_MAX)
maybePropagatePhdrs(*sec, defPhdrs);
- if (isEmpty && isDiscardable(*sec)) {
+ if (discardable) {
sec->markDead();
cmd = nullptr;
}
// clutter the output.
// We instead remove trivially empty sections. The bfd linker seems even
// more aggressive at removing them.
- llvm::erase_if(sectionCommands, [&](BaseCommand *base) { return !base; });
+ llvm::erase_if(sectionCommands, [&](SectionCommand *cmd) { return !cmd; });
}
void LinkerScript::adjustSectionsAfterSorting() {
// Try and find an appropriate memory region to assign offsets in.
- for (BaseCommand *base : sectionCommands) {
- if (auto *sec = dyn_cast<OutputSection>(base)) {
+ MemoryRegion *hint = nullptr;
+ for (SectionCommand *cmd : sectionCommands) {
+ if (auto *osd = dyn_cast<OutputDesc>(cmd)) {
+ OutputSection *sec = &osd->osec;
if (!sec->lmaRegionName.empty()) {
if (MemoryRegion *m = memoryRegions.lookup(sec->lmaRegionName))
sec->lmaRegion = m;
else
error("memory region '" + sec->lmaRegionName + "' not declared");
}
- sec->memRegion = findMemoryRegion(sec);
+ std::tie(sec->memRegion, hint) = findMemoryRegion(sec, hint);
}
}
// Below is an example of such linker script:
// PHDRS { seg PT_LOAD; }
// SECTIONS { .aaa : { *(.aaa) } }
- std::vector<StringRef> defPhdrs;
+ SmallVector<StringRef, 0> defPhdrs;
auto firstPtLoad = llvm::find_if(phdrsCommands, [](const PhdrsCommand &cmd) {
return cmd.type == PT_LOAD;
});
// Walk the commands and propagate the program headers to commands that don't
// explicitly specify them.
- for (BaseCommand *base : sectionCommands)
- if (auto *sec = dyn_cast<OutputSection>(base))
- maybePropagatePhdrs(*sec, defPhdrs);
+ for (SectionCommand *cmd : sectionCommands)
+ if (auto *osd = dyn_cast<OutputDesc>(cmd))
+ maybePropagatePhdrs(osd->osec, defPhdrs);
}
static uint64_t computeBase(uint64_t min, bool allocateHeaders) {
// We check if the headers fit below the first allocated section. If there isn't
// enough space for these sections, we'll remove them from the PT_LOAD segment,
// and we'll also remove the PT_PHDR segment.
-void LinkerScript::allocateHeaders(std::vector<PhdrEntry *> &phdrs) {
+void LinkerScript::allocateHeaders(SmallVector<PhdrEntry *, 0> &phdrs) {
uint64_t min = std::numeric_limits<uint64_t>::max();
for (OutputSection *sec : outputSections)
if (sec->flags & SHF_ALLOC)
if (script->hasSectionsCommand) {
// With a linker script, assignment of addresses to headers is covered by
// allocateHeaders().
- dot = config->imageBase.getValueOr(0);
+ dot = config->imageBase.value_or(0);
} else {
// Assign addresses to headers right now.
dot = target->getImageBase();
dot += getHeaderSize();
}
- auto deleter = std::make_unique<AddressState>();
- ctx = deleter.get();
+ AddressState st;
+ state = &st;
errorOnMissingSection = true;
- switchTo(aether);
+ st.outSec = aether;
SymbolAssignmentMap oldValues = getSymbolAssignmentValues(sectionCommands);
- for (BaseCommand *base : sectionCommands) {
- if (auto *cmd = dyn_cast<SymbolAssignment>(base)) {
- cmd->addr = dot;
- assignSymbol(cmd, false);
- cmd->size = dot - cmd->addr;
+ for (SectionCommand *cmd : sectionCommands) {
+ if (auto *assign = dyn_cast<SymbolAssignment>(cmd)) {
+ assign->addr = dot;
+ assignSymbol(assign, false);
+ assign->size = dot - assign->addr;
continue;
}
- assignOffsets(cast<OutputSection>(base));
+ assignOffsets(&cast<OutputDesc>(cmd)->osec);
}
- ctx = nullptr;
+ state = nullptr;
return getChangedSymbolAssignment(oldValues);
}
// Creates program headers as instructed by PHDRS linker script command.
-std::vector<PhdrEntry *> LinkerScript::createPhdrs() {
- std::vector<PhdrEntry *> ret;
+SmallVector<PhdrEntry *, 0> LinkerScript::createPhdrs() {
+ SmallVector<PhdrEntry *, 0> ret;
// Process PHDRS and FILEHDR keywords because they are not
// real output sections and cannot be added in the following loop.
for (const PhdrsCommand &cmd : phdrsCommands) {
- PhdrEntry *phdr = make<PhdrEntry>(cmd.type, cmd.flags ? *cmd.flags : PF_R);
+ PhdrEntry *phdr = make<PhdrEntry>(cmd.type, cmd.flags.value_or(PF_R));
if (cmd.hasFilehdr)
phdr->add(Out::elfHeader);
// Assign headers specified by linker script
for (size_t id : getPhdrIndices(sec)) {
ret[id]->add(sec);
- if (!phdrsCommands[id].flags.hasValue())
+ if (!phdrsCommands[id].flags)
ret[id]->p_flags |= sec->getPhdrFlags();
}
}
ExprValue LinkerScript::getSymbolValue(StringRef name, const Twine &loc) {
if (name == ".") {
- if (ctx)
- return {ctx->outSec, false, dot - ctx->outSec->addr, loc};
+ if (state)
+ return {state->outSec, false, dot - state->outSec->addr, loc};
error(loc + ": unable to get location counter value");
return 0;
}
- if (Symbol *sym = symtab->find(name)) {
+ if (Symbol *sym = symtab.find(name)) {
if (auto *ds = dyn_cast<Defined>(sym)) {
ExprValue v{ds->section, false, ds->value, loc};
// Retain the original st_type, so that the alias will get the same
}
// Returns the index of the segment named Name.
-static Optional<size_t> getPhdrIndex(ArrayRef<PhdrsCommand> vec,
- StringRef name) {
+static std::optional<size_t> getPhdrIndex(ArrayRef<PhdrsCommand> vec,
+ StringRef name) {
for (size_t i = 0; i < vec.size(); ++i)
if (vec[i].name == name)
return i;
- return None;
+ return std::nullopt;
}
// Returns indices of ELF headers containing specific section. Each index is a
// zero based number of ELF header listed within PHDRS {} script block.
-std::vector<size_t> LinkerScript::getPhdrIndices(OutputSection *cmd) {
- std::vector<size_t> ret;
+SmallVector<size_t, 0> LinkerScript::getPhdrIndices(OutputSection *cmd) {
+ SmallVector<size_t, 0> ret;
for (StringRef s : cmd->phdrs) {
- if (Optional<size_t> idx = getPhdrIndex(phdrsCommands, s))
+ if (std::optional<size_t> idx = getPhdrIndex(phdrsCommands, s))
ret.push_back(*idx);
else if (s != "NONE")
error(cmd->location + ": program header '" + s +
def Bstatic: F<"Bstatic">, HelpText<"Do not link against shared libraries">;
-def build_id: F<"build-id">, HelpText<"Alias for --build-id=fast">;
-
-def build_id_eq: J<"build-id=">, HelpText<"Generate build ID note">,
+def build_id: J<"build-id=">, HelpText<"Generate build ID note">,
MetaVarName<"[fast,md5,sha1,uuid,0x<hexstring>]">;
+def : F<"build-id">, Alias<build_id>, AliasArgs<["fast"]>, HelpText<"Alias for --build-id=fast">;
defm check_sections: B<"check-sections",
"Check section addresses for overlaps (default)",
defm compress_debug_sections:
Eq<"compress-debug-sections", "Compress DWARF debug sections">,
- MetaVarName<"[none,zlib]">;
+ MetaVarName<"[none,zlib,zstd]">;
defm defsym: Eq<"defsym", "Define a symbol alias">, MetaVarName<"<symbol>=<value>">;
"non-split-stack function">,
MetaVarName<"<value>">;
-defm library_path:
- Eq<"library-path", "Add a directory to the library search path">, MetaVarName<"<dir>">;
-
def O: JoinedOrSeparate<["-"], "O">, HelpText<"Optimize output file size">;
defm Tbss: Eq<"Tbss", "Same as --section-start with .bss as the sectionname">;
"Reorder sections with call graph profile (default)",
"Do not reorder sections with call graph profile">;
-// -chroot doesn't have a help text because it is an internal option.
-def chroot: Separate<["--", "-"], "chroot">;
+// --chroot doesn't have a help text because it is an internal option.
+def chroot: Separate<["--"], "chroot">;
-defm color_diagnostics: B<"color-diagnostics",
- "Alias for --color-diagnostics=always",
- "Alias for --color-diagnostics=never">;
-def color_diagnostics_eq: J<"color-diagnostics=">,
+def color_diagnostics: JJ<"color-diagnostics=">,
HelpText<"Use colors in diagnostics (default: auto)">,
MetaVarName<"[auto,always,never]">;
+def : Flag<["--"], "color-diagnostics">, Alias<color_diagnostics>, AliasArgs<["always"]>,
+ HelpText<"Alias for --color-diagnostics=always">;
+def : Flag<["--"], "no-color-diagnostics">, Alias<color_diagnostics>, AliasArgs<["never"]>,
+ HelpText<"Alias for --color-diagnostics=never">;
-def cref: FF<"cref">, HelpText<"Output cross reference table">;
-
-defm define_common: B<"define-common",
- "Assign space to common symbols",
- "Do not assign space to common symbols">;
+def cref: FF<"cref">,
+ HelpText<"Output cross reference table. If -Map is specified, print to the map file">;
defm demangle: B<"demangle",
"Demangle symbol names (default)",
defm dynamic_linker: Eq<"dynamic-linker", "Which dynamic linker to use">;
defm dynamic_list : Eq<"dynamic-list",
- "Read a list of dynamic symbols. (executable) Put matched non-local defined"
- "symbols to the dynamic symbol table. (shared object) References to matched"
- "non-local STV_DEFAULT symbols shouldn't be bound to definitions within the "
- "shared object. Implies -Bsymbolic but does not set DF_SYMBOLIC">,
+ "Similar to --export-dynamic-symbol-list. When creating a shared object, "
+ "this additionally implies -Bsymbolic but does not set DF_SYMBOLIC">,
MetaVarName<"<file>">;
def eb: F<"EB">, HelpText<"Select the big-endian format in OUTPUT_FORMAT">;
MetaVarName<"<entry>">;
defm error_limit:
- Eq<"error-limit", "Maximum number of errors to emit before stopping (0 = no limit)">;
+ EEq<"error-limit", "Maximum number of errors to emit before stopping (0 = no limit)">;
def error_unresolved_symbols: F<"error-unresolved-symbols">,
HelpText<"Report unresolved symbols as errors">;
"Does not imply -Bsymbolic.">,
MetaVarName<"glob">;
+defm export_dynamic_symbol_list : EEq<"export-dynamic-symbol-list",
+ "Read a list of dynamic symbol patterns. Apply --export-dynamic-symbol on each pattern">,
+ MetaVarName<"file">;
+
defm fatal_warnings: B<"fatal-warnings",
"Treat warnings as errors",
"Do not treat warnings as errors (default)">;
"lld can break the address equality of functions",
"lld cannot break the address equality of functions">;
-def ignore_data_address_equality: F<"ignore-data-address-equality">,
+def ignore_data_address_equality: FF<"ignore-data-address-equality">,
HelpText<"lld can break the address equality of data">;
-defm image_base: Eq<"image-base", "Set the base address">;
+defm image_base: EEq<"image-base", "Set the base address">;
defm init: Eq<"init", "Specify an initializer function">,
MetaVarName<"<symbol>">;
defm keep_unique: Eq<"keep-unique", "Do not fold this symbol during ICF">;
-defm library: Eq<"library", "Root name of library to use">,
- MetaVarName<"<libName>">;
+def library: JoinedOrSeparate<["-"], "l">, MetaVarName<"<libname>">,
+ HelpText<"Search for library <libname>">;
+def library_path: JoinedOrSeparate<["-"], "L">, MetaVarName<"<dir>">,
+ HelpText<"Add <dir> to the library search path">;
def m: JoinedOrSeparate<["-"], "m">, HelpText<"Set target emulation">;
def no_undefined: F<"no-undefined">,
HelpText<"Report unresolved symbols even if the linker is creating a shared library">;
+def no_warnings: FF<"no-warnings">, HelpText<"Suppress warnings and cancel --fatal-warnings">;
+def : Flag<["-"], "w">, Alias<no_warnings>, HelpText<"Alias for --no-warnings">;
+
def o: JoinedOrSeparate<["-"], "o">, MetaVarName<"<path>">,
HelpText<"Path to file to write output">;
-def oformat: Separate<["--"], "oformat">, MetaVarName<"<format>">,
- HelpText<"Specify the binary format for the output object file">;
+defm oformat: EEq<"oformat", "Specify the binary format for the output object file">,
+ MetaVarName<"[elf,binary]">;
def omagic: FF<"omagic">, MetaVarName<"<magic>">,
HelpText<"Set the text and data sections to be readable and writable, do not page align sections, link against static libraries">;
Eq<"orphan-handling", "Control how orphan sections are handled when linker script used">;
defm pack_dyn_relocs:
- Eq<"pack-dyn-relocs", "Pack dynamic relocations in the given format">,
+ EEq<"pack-dyn-relocs", "Pack dynamic relocations in the given format">,
MetaVarName<"[none,android,relr,android+relr]">;
defm use_android_relr_tags: BB<"use-android-relr-tags",
def print_archive_stats: J<"print-archive-stats=">,
HelpText<"Write archive usage statistics to the specified file. "
- "Print the numbers of members and fetched members for each archive">;
+ "Print the numbers of members and extracted members for each archive">;
defm print_symbol_order: Eq<"print-symbol-order",
"Print a symbol order specified by --call-graph-ordering-file into the specified file">;
def pop_state: F<"pop-state">,
- HelpText<"Undo the effect of -push-state">;
+ HelpText<"Restore the states saved by --push-state">;
def push_state: F<"push-state">,
- HelpText<"Save the current state of -as-needed, -static and -whole-archive">;
+ HelpText<"Save the current state of --as-needed, -static and --whole-archive">;
def print_map: F<"print-map">,
HelpText<"Print a link map to the standard output">;
+defm relax: BB<"relax",
+ "Enable target-specific relaxations if supported (default)",
+ "Disable target-specific relaxations">;
+
defm reproduce:
- Eq<"reproduce",
+ EEq<"reproduce",
"Write tar file containing inputs and command to reproduce link">;
defm rosegment: BB<"rosegment",
def strip_debug: F<"strip-debug">, HelpText<"Strip debugging information">;
defm symbol_ordering_file:
- Eq<"symbol-ordering-file", "Layout sections to place symbols in the order specified by symbol ordering file">;
+ EEq<"symbol-ordering-file", "Layout sections to place symbols in the order specified by symbol ordering file">;
defm sysroot: Eq<"sysroot", "Set the system root">;
MetaVarName<"<type>">;
defm threads
- : Eq<"threads",
+ : EEq<"threads",
"Number of threads. '1' disables multi-threading. By default all "
"available hardware threads are used">;
-def time_trace: FF<"time-trace">, HelpText<"Record time trace">;
-def time_trace_file_eq: JJ<"time-trace-file=">, HelpText<"Specify time trace output file">;
+def time_trace_eq: JJ<"time-trace=">, MetaVarName<"<file>">,
+ HelpText<"Record time trace to <file>">;
+def : FF<"time-trace">, Alias<time_trace_eq>,
+ HelpText<"Record time trace to file next to output">;
defm time_trace_granularity: EEq<"time-trace-granularity",
"Minimum time granularity (in microseconds) traced by time profiler">;
Eq<"unresolved-symbols", "Determine how to handle unresolved symbols">;
defm undefined_version: B<"undefined-version",
- "Allow unused version in version script (default)",
+ "Allow unused version in version script (disabled by default)",
"Report version scripts that refer undefined symbols">;
-defm rsp_quoting: Eq<"rsp-quoting", "Quoting style for response files">,
+defm rsp_quoting: EEq<"rsp-quoting", "Quoting style for response files">,
MetaVarName<"[posix,windows]">;
def v: Flag<["-"], "v">, HelpText<"Display the version number">;
def version: F<"version">, HelpText<"Display the version number and exit">;
-def power10_stubs: F<"power10-stubs">, HelpText<"Alias for --power10-stubs=auto">;
-
-def no_power10_stubs: F<"no-power10-stubs">, HelpText<"Alias for --power10-stubs=no">;
-
-def power10_stubs_eq:
- J<"power10-stubs=">, HelpText<
- "Enables Power10 instructions in all stubs without options, "
- "options override previous flags."
- "auto: Allow Power10 instructions in stubs if applicable."
- "no: No Power10 instructions in stubs.">;
+def power10_stubs_eq: JJ<"power10-stubs=">, MetaVarName<"<mode>">,
+ HelpText<"Whether to use Power10 instructions in call stubs for R_PPC64_REL24_NOTOC and TOC/NOTOC "
+ "interworking (yes (default): use; no: don't use). \"auto\" is currently the same as \"yes\"">;
+def power10_stubs: FF<"power10-stubs">, Alias<power10_stubs_eq>, AliasArgs<["yes"]>,
+ HelpText<"Alias for --power10-stubs=auto">;
+def no_power10_stubs: FF<"no-power10-stubs">, Alias<power10_stubs_eq>, AliasArgs<["no"]>,
+ HelpText<"Alias for --power10-stubs=no">;
defm version_script: Eq<"version-script", "Read a version script">;
defm warn_backrefs: BB<"warn-backrefs",
- "Warn about backward symbol references to fetch archive members",
- "Do not warn about backward symbol references to fetch archive members (default)">;
+ "Warn about backward symbol references to extract archive members",
+ "Do not warn about backward symbol references to extract archive members (default)">;
defm warn_backrefs_exclude
: EEq<"warn-backrefs-exclude",
"Force load of all members in a static library",
"Do not force load of all members in a static library (default)">;
+def why_extract: JJ<"why-extract=">, HelpText<"Print to a file about why archive members are extracted">;
+
defm wrap : Eq<"wrap", "Redirect symbol references to __wrap_symbol and "
"__real_symbol references to symbol">,
MetaVarName<"<symbol>">;
def z: JoinedOrSeparate<["-"], "z">, MetaVarName<"<option>">,
HelpText<"Linker option extensions">;
-def visual_studio_diagnostics_format : F<"vs-diagnostics">,
+def visual_studio_diagnostics_format : FF<"vs-diagnostics">,
HelpText<"Format diagnostics for Visual Studio compatibility">;
+def package_metadata: JJ<"package-metadata=">, HelpText<"Emit package metadata note">;
+
// Aliases
def: Separate<["-"], "f">, Alias<auxiliary>, HelpText<"Alias for --auxiliary">;
def: F<"call_shared">, Alias<Bdynamic>, HelpText<"Alias for --Bdynamic">;
def: F<"dn">, Alias<Bstatic>, HelpText<"Alias for --Bstatic">;
def: F<"non_shared">, Alias<Bstatic>, HelpText<"Alias for --Bstatic">;
def: F<"static">, Alias<Bstatic>, HelpText<"Alias for --Bstatic">;
-def: Flag<["-"], "d">, Alias<define_common>, HelpText<"Alias for --define-common">;
-def: F<"dc">, Alias<define_common>, HelpText<"Alias for --define-common">;
-def: F<"dp">, Alias<define_common>, HelpText<"Alias for --define-common">;
def: Flag<["-"], "x">, Alias<discard_all>, HelpText<"Alias for --discard-all">;
def: Flag<["-"], "X">, Alias<discard_locals>, HelpText<"Alias for --discard-locals">;
def: Flag<["-"], "q">, Alias<emit_relocs>, HelpText<"Alias for --emit-relocs">;
def: Flag<["-"], "E">, Alias<export_dynamic>, HelpText<"Alias for --export-dynamic">;
def: Separate<["-"], "F">, Alias<filter>, HelpText<"Alias for --filter">;
def: Separate<["-"], "b">, Alias<format>, HelpText<"Alias for --format">;
-def: JoinedOrSeparate<["-"], "l">, Alias<library>, HelpText<"Alias for --library">;
-def: JoinedOrSeparate<["-"], "L">, Alias<library_path>, HelpText<"Alias for --library-path">;
-def: F<"nopie">, Alias<no_pie>, HelpText<"Alias for --no-pie">;
-def: F<"no-pic-executable">, Alias<no_pie>, HelpText<"Alias for --no-pie">;
+def: Separate<["--", "-"], "library">, Alias<library>;
+def: Joined<["--", "-"], "library=">, Alias<library>;
+def: Separate<["--", "-"], "library-path">, Alias<library_path>;
+def: Joined<["--", "-"], "library-path=">, Alias<library_path>;
def: Flag<["-"], "n">, Alias<nmagic>, HelpText<"Alias for --nmagic">;
def: Flag<["-"], "N">, Alias<omagic>, HelpText<"Alias for --omagic">;
def: Joined<["--"], "output=">, Alias<o>, HelpText<"Alias for -o">;
def: Separate<["--"], "output">, Alias<o>, HelpText<"Alias for -o">;
+def: F<"nopie">, Alias<no_pie>, HelpText<"Alias for --no-pie">;
def: F<"pic-executable">, Alias<pie>, HelpText<"Alias for --pie">;
def: Flag<["-"], "M">, Alias<print_map>, HelpText<"Alias for --print-map">;
def: Flag<["-"], "r">, Alias<relocatable>, HelpText<"Alias for --relocatable">;
def: Joined<["-", "--"], "Ttext-segment=">, Alias<Ttext_segment>;
def: JoinedOrSeparate<["-"], "y">, Alias<trace_symbol>, HelpText<"Alias for --trace-symbol">;
def: JoinedOrSeparate<["-"], "u">, Alias<undefined>, HelpText<"Alias for --undefined">;
-def: Flag<["-"], "V">, Alias<version>, HelpText<"Alias for --version">;
+def: Flag<["-"], "V">, Alias<v>, HelpText<"Alias for -v">;
// LTO-related options.
def lto_aa_pipeline: JJ<"lto-aa-pipeline=">,
HelpText<"Debug new pass manager">;
def lto_emit_asm: FF<"lto-emit-asm">,
HelpText<"Emit assembly code">;
-defm lto_legacy_pass_manager: BB<"lto-legacy-pass-manager",
- "Use the legacy pass manager in LLVM",
- "Use the new pass manager in LLVM">;
+def no_lto_legacy_pass_manager: FF<"no-lto-legacy-pass-manager">,
+ HelpText<"Use the new pass manager in LLVM">;
def lto_newpm_passes: JJ<"lto-newpm-passes=">,
HelpText<"Passes to run during LTO">;
def lto_O: JJ<"lto-O">, MetaVarName<"<opt-level>">,
HelpText<"Perform context sensitive PGO instrumentation">;
def lto_cs_profile_file: JJ<"lto-cs-profile-file=">,
HelpText<"Context sensitive profile file path">;
+defm lto_pgo_warn_mismatch: BB<"lto-pgo-warn-mismatch",
+ "turn on warnings about profile cfg mismatch (default)>",
+ "turn off warnings about profile cfg mismatch">;
def lto_obj_path_eq: JJ<"lto-obj-path=">;
def lto_sample_profile: JJ<"lto-sample-profile=">,
HelpText<"Sample profile file path">;
defm lto_whole_program_visibility: BB<"lto-whole-program-visibility",
"Asserts that the LTO link has whole program visibility",
"Asserts that the LTO link does not have whole program visibility">;
-def lto_pseudo_probe_for_profiling: F<"lto-pseudo-probe-for-profiling">,
- HelpText<"Emit pseudo probes for sample profiling">;
def disable_verify: F<"disable-verify">;
defm mllvm: Eq<"mllvm", "Additional arguments to forward to LLVM's option processing">;
def opt_remarks_filename: Separate<["--"], "opt-remarks-filename">,
def opt_remarks_format: Separate<["--"], "opt-remarks-format">,
HelpText<"The format used for serializing remarks (default: YAML)">;
def save_temps: F<"save-temps">, HelpText<"Save intermediate LTO compilation results">;
+def save_temps_eq: JJ<"save-temps=">, HelpText<"Save select intermediate LTO compilation results">,
+ Values<"resolution,preopt,promote,internalize,import,opt,precodegen,prelink,combinedindex">;
def lto_basic_block_sections: JJ<"lto-basic-block-sections=">,
HelpText<"Enable basic block sections for LTO">;
defm lto_unique_basic_block_section_names: BB<"lto-unique-basic-block-section-names",
HelpText<"Path to ThinLTO cached object file directory">;
defm thinlto_cache_policy: EEq<"thinlto-cache-policy", "Pruning policy for the ThinLTO cache">;
def thinlto_emit_imports_files: FF<"thinlto-emit-imports-files">;
+def thinlto_emit_index_files: FF<"thinlto-emit-index-files">;
def thinlto_index_only: FF<"thinlto-index-only">;
def thinlto_index_only_eq: JJ<"thinlto-index-only=">;
-def thinlto_jobs: JJ<"thinlto-jobs=">,
+def thinlto_jobs_eq: JJ<"thinlto-jobs=">,
HelpText<"Number of ThinLTO jobs. Default to --threads=">;
def thinlto_object_suffix_replace_eq: JJ<"thinlto-object-suffix-replace=">;
def thinlto_prefix_replace_eq: JJ<"thinlto-prefix-replace=">;
def thinlto_single_module_eq: JJ<"thinlto-single-module=">,
- HelpText<"Specific a single module to compile in ThinLTO mode, for debugging only">;
+ HelpText<"Specify a single module to compile in ThinLTO mode, for debugging only">;
def: J<"plugin-opt=O">, Alias<lto_O>, HelpText<"Alias for --lto-O">;
def: F<"plugin-opt=debug-pass-manager">,
def plugin_opt_emit_asm: F<"plugin-opt=emit-asm">,
Alias<lto_emit_asm>, HelpText<"Alias for --lto-emit-asm">;
def plugin_opt_emit_llvm: F<"plugin-opt=emit-llvm">;
-def: J<"plugin-opt=jobs=">, Alias<thinlto_jobs>, HelpText<"Alias for --thinlto-jobs">;
+def: J<"plugin-opt=jobs=">, Alias<thinlto_jobs_eq>, HelpText<"Alias for --thinlto-jobs=">;
def: J<"plugin-opt=lto-partitions=">, Alias<lto_partitions>, HelpText<"Alias for --lto-partitions">;
def plugin_opt_mcpu_eq: J<"plugin-opt=mcpu=">;
def: F<"plugin-opt=new-pass-manager">,
Alias<no_lto_legacy_pass_manager>, HelpText<"Alias for --no-lto-legacy-pass-manager">;
-def: F<"plugin-opt=legacy-pass-manager">,
- Alias<lto_legacy_pass_manager>, HelpText<"Alias for --no-legacy-pass-manager">;
def: F<"plugin-opt=cs-profile-generate">,
Alias<lto_cs_profile_generate>, HelpText<"Alias for --lto-cs-profile-generate">;
def: J<"plugin-opt=cs-profile-path=">,
def: J<"plugin-opt=obj-path=">,
Alias<lto_obj_path_eq>,
HelpText<"Alias for --lto-obj-path=">;
+def plugin_opt_opaque_pointers: F<"plugin-opt=opaque-pointers">,
+ HelpText<"Use opaque pointers in IR during LTO (default)">;
+def plugin_opt_no_opaque_pointers: F<"plugin-opt=no-opaque-pointers">,
+ HelpText<"Use typed pointers in IR during LTO">;
def: J<"plugin-opt=opt-remarks-filename=">,
Alias<opt_remarks_filename>,
HelpText<"Alias for --opt-remarks-filename">;
def: J<"plugin-opt=opt-remarks-hotness-threshold=">,
Alias<opt_remarks_hotness_threshold>,
HelpText<"Alias for --opt-remarks-hotness-threshold">;
-def: J<"plugin-opt=pseudo-probe-for-profiling">,
- Alias<lto_pseudo_probe_for_profiling>, HelpText<"Alias for --lto-pseudo-probe-for-profiling">;
def: J<"plugin-opt=sample-profile=">,
Alias<lto_sample_profile>, HelpText<"Alias for --lto-sample-profile">;
def: F<"plugin-opt=save-temps">, Alias<save_temps>, HelpText<"Alias for --save-temps">;
+def plugin_opt_stats_file: J<"plugin-opt=stats-file=">,
+ HelpText<"Filename to write LTO statistics to">;
def: F<"plugin-opt=thinlto-emit-imports-files">,
Alias<thinlto_emit_imports_files>,
HelpText<"Alias for --thinlto-emit-imports-files">;
def plugin_opt_eq : J<"plugin-opt=">;
// Options listed below are silently ignored for now for compatibility.
-def: F<"detect-odr-violations">;
+def: Flag<["-"], "d">;
def: Flag<["-"], "g">;
def: F<"long-plt">;
-def: F<"no-add-needed">;
+def: FF<"no-add-needed">;
def: F<"no-copy-dt-needed-entries">;
def: F<"no-ctors-in-init-array">;
def: F<"no-keep-memory">;
-def: F<"no-pipeline-knowledge">;
-def: F<"no-relax">;
def: F<"no-warn-mismatch">;
-def: Flag<["-"], "p">;
def: Separate<["--", "-"], "rpath-link">;
def: J<"rpath-link=">;
def: F<"secure-plt">;
def: F<"warn-once">;
def: F<"warn-shared-textrel">;
def: JoinedOrSeparate<["-"], "G">;
-def: F<"Qy">;
// Hidden option used for testing MIPS multi-GOT implementation.
defm mips_got_size:
"Perform additional validation of the written dynamic relocations",
"Do not perform additional validation of the written dynamic relocations">,
Flags<[HelpHidden]>;
+
+defm load_pass_plugins: EEq<"load-pass-plugin", "Load passes from plugin library">;
+
+// Hidden options, used by clang's -fsanitize=memtag-* options to emit an ELF
+// note to designate what kinds of memory (stack/heap) should be protected using
+// ARM's MTE on armv8.5+. A binary's desire for stack MTE can't be obtained
+// implicitly, so we have a specific bit in the note to signal to the loader to
+// remap the stack as PROT_MTE.
+defm android_memtag_stack: BB<"android-memtag-stack",
+ "Instruct the dynamic loader to prepare for MTE stack instrumentation", "">;
+defm android_memtag_heap: BB<"android-memtag-heap",
+ "Instruct the dynamic loader to enable MTE protection for the heap", "">;
+defm android_memtag_mode: EEq<"android-memtag-mode",
+ "Instruct the dynamic loader to start under MTE mode {async, sync, none}">;
#include "Relocations.h"
#include "Config.h"
+#include "InputFiles.h"
#include "LinkerScript.h"
#include "OutputSections.h"
#include "SymbolTable.h"
#include "Thunks.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
-#include "lld/Common/Strings.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/Demangle/Demangle.h"
#include "llvm/Support/Endian.h"
-#include "llvm/Support/raw_ostream.h"
#include <algorithm>
using namespace llvm;
using namespace lld;
using namespace lld::elf;
-static Optional<std::string> getLinkerScriptLocation(const Symbol &sym) {
- for (BaseCommand *base : script->sectionCommands)
- if (auto *cmd = dyn_cast<SymbolAssignment>(base))
- if (cmd->sym == &sym)
- return cmd->location;
- return None;
+static std::optional<std::string> getLinkerScriptLocation(const Symbol &sym) {
+ for (SectionCommand *cmd : script->sectionCommands)
+ if (auto *assign = dyn_cast<SymbolAssignment>(cmd))
+ if (assign->sym == &sym)
+ return assign->location;
+ return std::nullopt;
}
static std::string getDefinedLocation(const Symbol &sym) {
const char msg[] = "\n>>> defined in ";
if (sym.file)
return msg + toString(sym.file);
- if (Optional<std::string> loc = getLinkerScriptLocation(sym))
+ if (std::optional<std::string> loc = getLinkerScriptLocation(sym))
return msg + *loc;
return "";
}
int64_t min, uint64_t max) {
ErrorPlace errPlace = getErrorPlace(loc);
std::string hint;
- if (rel.sym && !rel.sym->isLocal())
- hint = "; references " + lld::toString(*rel.sym) +
- getDefinedLocation(*rel.sym);
+ if (rel.sym && !rel.sym->isSection())
+ hint = "; references " + lld::toString(*rel.sym);
+ if (!errPlace.srcLoc.empty())
+ hint += "\n>>> referenced by " + errPlace.srcLoc;
+ if (rel.sym && !rel.sym->isSection())
+ hint += getDefinedLocation(*rel.sym);
if (errPlace.isec && errPlace.isec->name.startswith(".debug"))
hint += "; consider recompiling with -fdebug-types-section to reduce size "
Twine(llvm::maxIntN(n)) + "]" + hint);
}
-namespace {
-// Build a bitmask with one bit set for each RelExpr.
-//
-// Constexpr function arguments can't be used in static asserts, so we
-// use template arguments to build the mask.
-// But function template partial specializations don't exist (needed
-// for base case of the recursion), so we need a dummy struct.
-template <RelExpr... Exprs> struct RelExprMaskBuilder {
- static inline uint64_t build() { return 0; }
-};
+// Build a bitmask with one bit set for each 64 subset of RelExpr.
+static constexpr uint64_t buildMask() { return 0; }
-// Specialization for recursive case.
-template <RelExpr Head, RelExpr... Tail>
-struct RelExprMaskBuilder<Head, Tail...> {
- static inline uint64_t build() {
- static_assert(0 <= Head && Head < 64,
- "RelExpr is too large for 64-bit mask!");
- return (uint64_t(1) << Head) | RelExprMaskBuilder<Tail...>::build();
- }
-};
-} // namespace
-
-// Return true if `Expr` is one of `Exprs`.
-// There are fewer than 64 RelExpr's, so we can represent any set of
-// RelExpr's as a constant bit mask and test for membership with a
-// couple cheap bitwise operations.
-template <RelExpr... Exprs> bool oneof(RelExpr expr) {
- assert(0 <= expr && (int)expr < 64 &&
- "RelExpr is too large for 64-bit mask!");
- return (uint64_t(1) << expr) & RelExprMaskBuilder<Exprs...>::build();
+template <typename... Tails>
+static constexpr uint64_t buildMask(int head, Tails... tails) {
+ return (0 <= head && head < 64 ? uint64_t(1) << head : 0) |
+ buildMask(tails...);
}
-// This function is similar to the `handleTlsRelocation`. MIPS does not
-// support any relaxations for TLS relocations so by factoring out MIPS
-// handling in to the separate function we can simplify the code and do not
-// pollute other `handleTlsRelocation` by MIPS `ifs` statements.
-// Mips has a custom MipsGotSection that handles the writing of GOT entries
-// without dynamic relocations.
-static unsigned handleMipsTlsRelocation(RelType type, Symbol &sym,
- InputSectionBase &c, uint64_t offset,
- int64_t addend, RelExpr expr) {
- if (expr == R_MIPS_TLSLD) {
- in.mipsGot->addTlsIndex(*c.file);
- c.relocations.push_back({expr, type, offset, addend, &sym});
- return 1;
- }
- if (expr == R_MIPS_TLSGD) {
- in.mipsGot->addDynTlsEntry(*c.file, sym);
- c.relocations.push_back({expr, type, offset, addend, &sym});
- return 1;
- }
- return 0;
-}
-
-// Notes about General Dynamic and Local Dynamic TLS models below. They may
-// require the generation of a pair of GOT entries that have associated dynamic
-// relocations. The pair of GOT entries created are of the form GOT[e0] Module
-// Index (Used to find pointer to TLS block at run-time) GOT[e1] Offset of
-// symbol in TLS block.
-//
-// Returns the number of relocations processed.
-template <class ELFT>
-static unsigned
-handleTlsRelocation(RelType type, Symbol &sym, InputSectionBase &c,
- typename ELFT::uint offset, int64_t addend, RelExpr expr) {
- if (!sym.isTls())
- return 0;
-
- if (config->emachine == EM_MIPS)
- return handleMipsTlsRelocation(type, sym, c, offset, addend, expr);
-
- if (oneof<R_AARCH64_TLSDESC_PAGE, R_TLSDESC, R_TLSDESC_CALL, R_TLSDESC_PC>(
- expr) &&
- config->shared) {
- if (in.got->addDynTlsEntry(sym)) {
- uint64_t off = in.got->getGlobalDynOffset(sym);
- mainPart->relaDyn->addAddendOnlyRelocIfNonPreemptible(
- target->tlsDescRel, in.got, off, sym, target->tlsDescRel);
- }
- if (expr != R_TLSDESC_CALL)
- c.relocations.push_back({expr, type, offset, addend, &sym});
- return 1;
- }
-
- // ARM, Hexagon and RISC-V do not support GD/LD to IE/LE relaxation. For
- // PPC64, if the file has missing R_PPC64_TLSGD/R_PPC64_TLSLD, disable
- // relaxation as well.
- bool toExecRelax = !config->shared && config->emachine != EM_ARM &&
- config->emachine != EM_HEXAGON &&
- config->emachine != EM_RISCV &&
- !c.file->ppc64DisableTLSRelax;
-
- // If we are producing an executable and the symbol is non-preemptable, it
- // must be defined and the code sequence can be relaxed to use Local-Exec.
- //
- // ARM and RISC-V do not support any relaxations for TLS relocations, however,
- // we can omit the DTPMOD dynamic relocations and resolve them at link time
- // because them are always 1. This may be necessary for static linking as
- // DTPMOD may not be expected at load time.
- bool isLocalInExecutable = !sym.isPreemptible && !config->shared;
-
- // Local Dynamic is for access to module local TLS variables, while still
- // being suitable for being dynamically loaded via dlopen. GOT[e0] is the
- // module index, with a special value of 0 for the current module. GOT[e1] is
- // unused. There only needs to be one module index entry.
- if (oneof<R_TLSLD_GOT, R_TLSLD_GOTPLT, R_TLSLD_PC, R_TLSLD_HINT>(
- expr)) {
- // Local-Dynamic relocs can be relaxed to Local-Exec.
- if (toExecRelax) {
- c.relocations.push_back(
- {target->adjustTlsExpr(type, R_RELAX_TLS_LD_TO_LE), type, offset,
- addend, &sym});
- return target->getTlsGdRelaxSkip(type);
- }
- if (expr == R_TLSLD_HINT)
- return 1;
- if (in.got->addTlsIndex()) {
- if (isLocalInExecutable)
- in.got->relocations.push_back(
- {R_ADDEND, target->symbolicRel, in.got->getTlsIndexOff(), 1, &sym});
- else
- mainPart->relaDyn->addReloc(
- {target->tlsModuleIndexRel, in.got, in.got->getTlsIndexOff()});
- }
- c.relocations.push_back({expr, type, offset, addend, &sym});
- return 1;
- }
-
- // Local-Dynamic relocs can be relaxed to Local-Exec.
- if (expr == R_DTPREL && toExecRelax) {
- c.relocations.push_back({target->adjustTlsExpr(type, R_RELAX_TLS_LD_TO_LE),
- type, offset, addend, &sym});
- return 1;
- }
-
- // Local-Dynamic sequence where offset of tls variable relative to dynamic
- // thread pointer is stored in the got. This cannot be relaxed to Local-Exec.
- if (expr == R_TLSLD_GOT_OFF) {
- if (!sym.isInGot()) {
- in.got->addEntry(sym);
- uint64_t off = sym.getGotOffset();
- in.got->relocations.push_back(
- {R_ABS, target->tlsOffsetRel, off, 0, &sym});
- }
- c.relocations.push_back({expr, type, offset, addend, &sym});
- return 1;
- }
-
- if (oneof<R_AARCH64_TLSDESC_PAGE, R_TLSDESC, R_TLSDESC_CALL, R_TLSDESC_PC,
- R_TLSGD_GOT, R_TLSGD_GOTPLT, R_TLSGD_PC>(expr)) {
- if (!toExecRelax) {
- if (in.got->addDynTlsEntry(sym)) {
- uint64_t off = in.got->getGlobalDynOffset(sym);
-
- if (isLocalInExecutable)
- // Write one to the GOT slot.
- in.got->relocations.push_back(
- {R_ADDEND, target->symbolicRel, off, 1, &sym});
- else
- mainPart->relaDyn->addSymbolReloc(target->tlsModuleIndexRel, in.got,
- off, sym);
-
- // If the symbol is preemptible we need the dynamic linker to write
- // the offset too.
- uint64_t offsetOff = off + config->wordsize;
- if (sym.isPreemptible)
- mainPart->relaDyn->addSymbolReloc(target->tlsOffsetRel, in.got,
- offsetOff, sym);
- else
- in.got->relocations.push_back(
- {R_ABS, target->tlsOffsetRel, offsetOff, 0, &sym});
- }
- c.relocations.push_back({expr, type, offset, addend, &sym});
- return 1;
- }
-
- // Global-Dynamic relocs can be relaxed to Initial-Exec or Local-Exec
- // depending on the symbol being locally defined or not.
- if (sym.isPreemptible) {
- c.relocations.push_back(
- {target->adjustTlsExpr(type, R_RELAX_TLS_GD_TO_IE), type, offset,
- addend, &sym});
- if (!sym.isInGot()) {
- in.got->addEntry(sym);
- mainPart->relaDyn->addSymbolReloc(target->tlsGotRel, in.got,
- sym.getGotOffset(), sym);
- }
- } else {
- c.relocations.push_back(
- {target->adjustTlsExpr(type, R_RELAX_TLS_GD_TO_LE), type, offset,
- addend, &sym});
- }
- return target->getTlsGdRelaxSkip(type);
- }
-
- // Initial-Exec relocs can be relaxed to Local-Exec if the symbol is locally
- // defined.
- if (oneof<R_GOT, R_GOTPLT, R_GOT_PC, R_AARCH64_GOT_PAGE_PC, R_GOT_OFF,
- R_TLSIE_HINT>(expr) &&
- toExecRelax && isLocalInExecutable) {
- c.relocations.push_back({R_RELAX_TLS_IE_TO_LE, type, offset, addend, &sym});
- return 1;
- }
-
- if (expr == R_TLSIE_HINT)
- return 1;
- return 0;
+// Return true if `Expr` is one of `Exprs`.
+// There are more than 64 but less than 128 RelExprs, so we divide the set of
+// exprs into [0, 64) and [64, 128) and represent each range as a constant
+// 64-bit mask. Then we decide which mask to test depending on the value of
+// expr and use a simple shift and bitwise-and to test for membership.
+template <RelExpr... Exprs> static bool oneof(RelExpr expr) {
+ assert(0 <= expr && (int)expr < 128 &&
+ "RelExpr is too large for 128-bit mask!");
+
+ if (expr >= 64)
+ return (uint64_t(1) << (expr - 64)) & buildMask((Exprs - 64)...);
+ return (uint64_t(1) << expr) & buildMask(Exprs...);
}
static RelType getMipsPairType(RelType type, bool isLocal) {
// Returns true if Expr refers a PLT entry.
static bool needsPlt(RelExpr expr) {
- return oneof<R_PLT_PC, R_PPC32_PLTREL, R_PPC64_CALL_PLT, R_PLT>(expr);
+ return oneof<R_PLT, R_PLT_PC, R_PLT_GOTPLT, R_PPC32_PLTREL, R_PPC64_CALL_PLT>(
+ expr);
}
// Returns true if Expr refers a GOT entry. Note that this function
R_RISCV_PC_INDIRECT, R_PPC64_RELAX_GOT_PC>(expr);
}
-// Returns true if a given relocation can be computed at link-time.
-//
-// For instance, we know the offset from a relocation to its target at
-// link-time if the relocation is PC-relative and refers a
-// non-interposable function in the same executable. This function
-// will return true for such relocation.
-//
-// If this function returns false, that means we need to emit a
-// dynamic relocation so that the relocation will be fixed at load-time.
-static bool isStaticLinkTimeConstant(RelExpr e, RelType type, const Symbol &sym,
- InputSectionBase &s, uint64_t relOff) {
- // These expressions always compute a constant
- if (oneof<R_DTPREL, R_GOTPLT, R_GOT_OFF, R_TLSLD_GOT_OFF,
- R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOTREL, R_MIPS_GOT_OFF,
- R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC, R_MIPS_TLSGD,
- R_AARCH64_GOT_PAGE_PC, R_GOT_PC, R_GOTONLY_PC, R_GOTPLTONLY_PC,
- R_PLT_PC, R_TLSGD_GOT, R_TLSGD_GOTPLT, R_TLSGD_PC, R_PPC32_PLTREL,
- R_PPC64_CALL_PLT, R_PPC64_RELAX_TOC, R_RISCV_ADD, R_TLSDESC_CALL,
- R_TLSDESC_PC, R_AARCH64_TLSDESC_PAGE, R_TLSLD_HINT, R_TLSIE_HINT,
- R_AARCH64_GOT_PAGE>(
- e))
- return true;
-
- // These never do, except if the entire file is position dependent or if
- // only the low bits are used.
- if (e == R_GOT || e == R_PLT || e == R_TLSDESC)
- return target->usesOnlyLowPageBits(type) || !config->isPic;
-
- if (sym.isPreemptible)
- return false;
- if (!config->isPic)
- return true;
-
- // The size of a non preemptible symbol is a constant.
- if (e == R_SIZE)
- return true;
-
- // For the target and the relocation, we want to know if they are
- // absolute or relative.
- bool absVal = isAbsoluteValue(sym);
- bool relE = isRelExpr(e);
- if (absVal && !relE)
- return true;
- if (!absVal && relE)
- return true;
- if (!absVal && !relE)
- return target->usesOnlyLowPageBits(type);
-
- assert(absVal && relE);
-
- // Allow R_PLT_PC (optimized to R_PC here) to a hidden undefined weak symbol
- // in PIC mode. This is a little strange, but it allows us to link function
- // calls to such symbols (e.g. glibc/stdlib/exit.c:__run_exit_handlers).
- // Normally such a call will be guarded with a comparison, which will load a
- // zero from the GOT.
- if (sym.isUndefWeak())
- return true;
-
- // We set the final symbols values for linker script defined symbols later.
- // They always can be computed as a link time constant.
- if (sym.scriptDefined)
- return true;
-
- error("relocation " + toString(type) + " cannot refer to absolute symbol: " +
- toString(sym) + getLocation(s, sym, relOff));
- return true;
-}
static RelExpr toPlt(RelExpr expr) {
switch (expr) {
return R_PPC64_CALL;
case R_PLT:
return R_ABS;
+ case R_PLT_GOTPLT:
+ return R_GOTPLTREL;
default:
return expr;
}
using Elf_Phdr = typename ELFT::Phdr;
// Determine if the symbol is read-only by scanning the DSO's program headers.
- const SharedFile &file = ss.getFile();
+ const auto &file = cast<SharedFile>(*ss.file);
for (const Elf_Phdr &phdr :
check(file.template getObj<ELFT>().program_headers()))
if ((phdr.p_type == ELF::PT_LOAD || phdr.p_type == ELF::PT_GNU_RELRO) &&
static SmallSet<SharedSymbol *, 4> getSymbolsAt(SharedSymbol &ss) {
using Elf_Sym = typename ELFT::Sym;
- SharedFile &file = ss.getFile();
+ const auto &file = cast<SharedFile>(*ss.file);
SmallSet<SharedSymbol *, 4> ret;
for (const Elf_Sym &s : file.template getGlobalELFSyms<ELFT>()) {
s.getType() == STT_TLS || s.st_value != ss.value)
continue;
StringRef name = check(s.getName(file.getStringTable()));
- Symbol *sym = symtab->find(name);
+ Symbol *sym = symtab.find(name);
if (auto *alias = dyn_cast_or_null<SharedSymbol>(sym))
ret.insert(alias);
}
// in .bss and in the case of a canonical plt entry it is in .plt. This function
// replaces the existing symbol with a Defined pointing to the appropriate
// location.
-static void replaceWithDefined(Symbol &sym, SectionBase *sec, uint64_t value,
+static void replaceWithDefined(Symbol &sym, SectionBase &sec, uint64_t value,
uint64_t size) {
Symbol old = sym;
+ Defined(sym.file, StringRef(), sym.binding, sym.stOther, sym.type, value,
+ size, &sec)
+ .overwrite(sym);
- sym.replace(Defined{sym.file, sym.getName(), sym.binding, sym.stOther,
- sym.type, value, size, sec});
-
- sym.pltIndex = old.pltIndex;
- sym.gotIndex = old.gotIndex;
sym.verdefIndex = old.verdefIndex;
sym.exportDynamic = true;
sym.isUsedInRegularObj = true;
+ // A copy relocated alias may need a GOT entry.
+ sym.flags.store(old.flags.load(std::memory_order_relaxed) & NEEDS_GOT,
+ std::memory_order_relaxed);
}
// Reserve space in .bss or .bss.rel.ro for copy relocation.
// At this point, sectionBases has been migrated to sections. Append sec to
// sections.
- if (osec->sectionCommands.empty() ||
- !isa<InputSectionDescription>(osec->sectionCommands.back()))
- osec->sectionCommands.push_back(make<InputSectionDescription>(""));
- auto *isd = cast<InputSectionDescription>(osec->sectionCommands.back());
+ if (osec->commands.empty() ||
+ !isa<InputSectionDescription>(osec->commands.back()))
+ osec->commands.push_back(make<InputSectionDescription>(""));
+ auto *isd = cast<InputSectionDescription>(osec->commands.back());
isd->sections.push_back(sec);
osec->commitSection(sec);
// dynamic symbol for each one. This causes the copy relocation to correctly
// interpose any aliases.
for (SharedSymbol *sym : getSymbolsAt<ELFT>(ss))
- replaceWithDefined(*sym, sec, 0, sym->size);
+ replaceWithDefined(*sym, *sec, 0, sym->size);
- mainPart->relaDyn->addSymbolReloc(target->copyRel, sec, 0, ss);
+ mainPart->relaDyn->addSymbolReloc(target->copyRel, *sec, 0, ss);
}
+// .eh_frame sections are mergeable input sections, so their input
+// offsets are not linearly mapped to output section. For each input
+// offset, we need to find a section piece containing the offset and
+// add the piece's base address to the input offset to compute the
+// output offset. That isn't cheap.
+//
+// This class is to speed up the offset computation. When we process
+// relocations, we access offsets in the monotonically increasing
+// order. So we can optimize for that access pattern.
+//
+// For sections other than .eh_frame, this class doesn't do anything.
+namespace {
+class OffsetGetter {
+public:
+ OffsetGetter() = default;
+ explicit OffsetGetter(InputSectionBase &sec) {
+ if (auto *eh = dyn_cast<EhInputSection>(&sec)) {
+ cies = eh->cies;
+ fdes = eh->fdes;
+ i = cies.begin();
+ j = fdes.begin();
+ }
+ }
+
+ // Translates offsets in input sections to offsets in output sections.
+ // Given offset must increase monotonically. We assume that Piece is
+ // sorted by inputOff.
+ uint64_t get(uint64_t off) {
+ if (cies.empty())
+ return off;
+
+ while (j != fdes.end() && j->inputOff <= off)
+ ++j;
+ auto it = j;
+ if (j == fdes.begin() || j[-1].inputOff + j[-1].size <= off) {
+ while (i != cies.end() && i->inputOff <= off)
+ ++i;
+ if (i == cies.begin() || i[-1].inputOff + i[-1].size <= off)
+ fatal(".eh_frame: relocation is not in any piece");
+ it = i;
+ }
+
+ // Offset -1 means that the piece is dead (i.e. garbage collected).
+ if (it[-1].outputOff == -1)
+ return -1;
+ return it[-1].outputOff + (off - it[-1].inputOff);
+ }
+
+private:
+ ArrayRef<EhSectionPiece> cies, fdes;
+ ArrayRef<EhSectionPiece>::iterator i, j;
+};
+
+// This class encapsulates states needed to scan relocations for one
+// InputSectionBase.
+class RelocationScanner {
+public:
+ template <class ELFT> void scanSection(InputSectionBase &s);
+
+private:
+ InputSectionBase *sec;
+ OffsetGetter getter;
+
+ // End of relocations, used by Mips/PPC64.
+ const void *end = nullptr;
+
+ template <class RelTy> RelType getMipsN32RelType(RelTy *&rel) const;
+ template <class ELFT, class RelTy>
+ int64_t computeMipsAddend(const RelTy &rel, RelExpr expr, bool isLocal) const;
+ bool isStaticLinkTimeConstant(RelExpr e, RelType type, const Symbol &sym,
+ uint64_t relOff) const;
+ void processAux(RelExpr expr, RelType type, uint64_t offset, Symbol &sym,
+ int64_t addend) const;
+ template <class ELFT, class RelTy> void scanOne(RelTy *&i);
+ template <class ELFT, class RelTy> void scan(ArrayRef<RelTy> rels);
+};
+} // namespace
+
// MIPS has an odd notion of "paired" relocations to calculate addends.
// For example, if a relocation is of R_MIPS_HI16, there must be a
// R_MIPS_LO16 relocation after that, and an addend is calculated using
// the two relocations.
template <class ELFT, class RelTy>
-static int64_t computeMipsAddend(const RelTy &rel, const RelTy *end,
- InputSectionBase &sec, RelExpr expr,
- bool isLocal) {
+int64_t RelocationScanner::computeMipsAddend(const RelTy &rel, RelExpr expr,
+ bool isLocal) const {
if (expr == R_MIPS_GOTREL && isLocal)
- return sec.getFile<ELFT>()->mipsGp0;
+ return sec->getFile<ELFT>()->mipsGp0;
// The ABI says that the paired relocation is used only for REL.
// See p. 4-17 at ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
if (pairTy == R_MIPS_NONE)
return 0;
- const uint8_t *buf = sec.data().data();
+ const uint8_t *buf = sec->content().data();
uint32_t symIndex = rel.getSymbol(config->isMips64EL);
// To make things worse, paired relocations might not be contiguous in
// the relocation table, so we need to do linear search. *sigh*
- for (const RelTy *ri = &rel; ri != end; ++ri)
+ for (const RelTy *ri = &rel; ri != static_cast<const RelTy *>(end); ++ri)
if (ri->getType(config->isMips64EL) == pairTy &&
ri->getSymbol(config->isMips64EL) == symIndex)
return target->getImplicitAddend(buf + ri->r_offset, pairTy);
return 0;
}
-// Returns an addend of a given relocation. If it is RELA, an addend
-// is in a relocation itself. If it is REL, we need to read it from an
-// input section.
-template <class ELFT, class RelTy>
-static int64_t computeAddend(const RelTy &rel, const RelTy *end,
- InputSectionBase &sec, RelExpr expr,
- bool isLocal) {
- int64_t addend;
- RelType type = rel.getType(config->isMips64EL);
-
- if (RelTy::IsRela) {
- addend = getAddend<ELFT>(rel);
- } else {
- const uint8_t *buf = sec.data().data();
- addend = target->getImplicitAddend(buf + rel.r_offset, type);
- }
-
- if (config->emachine == EM_PPC64 && config->isPic && type == R_PPC64_TOC)
- addend += getPPC64TocBase();
- if (config->emachine == EM_MIPS)
- addend += computeMipsAddend<ELFT>(rel, end, sec, expr, isLocal);
-
- return addend;
-}
-
// Custom error message if Sym is defined in a discarded section.
template <class ELFT>
static std::string maybeReportDiscarded(Undefined &sym) {
if (!file || !sym.discardedSecIdx ||
file->getSections()[sym.discardedSecIdx] != &InputSection::discarded)
return "";
- ArrayRef<Elf_Shdr_Impl<ELFT>> objSections =
- CHECK(file->getObj().sections(), file);
+ ArrayRef<typename ELFT::Shdr> objSections =
+ file->template getELFShdrs<ELFT>();
std::string msg;
if (sym.type == ELF::STT_SECTION) {
// If the discarded section is a COMDAT.
StringRef signature = file->getShtGroupSignature(objSections, elfSec);
if (const InputFile *prevailing =
- symtab->comdatGroups.lookup(CachedHashStringRef(signature)))
+ symtab.comdatGroups.lookup(CachedHashStringRef(signature))) {
msg += "\n>>> section group signature: " + signature.str() +
"\n>>> prevailing definition is in " + toString(prevailing);
+ if (sym.nonPrevailing) {
+ msg += "\n>>> or the symbol in the prevailing group had STB_WEAK "
+ "binding and the symbol in a non-prevailing group had STB_GLOBAL "
+ "binding. Mixing groups with STB_WEAK and STB_GLOBAL binding "
+ "signature is not supported";
+ }
+ }
return msg;
}
+namespace {
// Undefined diagnostics are collected in a vector and emitted once all of
// them are known, so that some postprocessing on the list of undefined symbols
// can happen before lld emits diagnostics.
struct UndefinedDiag {
- Symbol *sym;
+ Undefined *sym;
struct Loc {
InputSectionBase *sec;
uint64_t offset;
bool isWarning;
};
-static std::vector<UndefinedDiag> undefs;
+std::vector<UndefinedDiag> undefs;
+std::mutex relocMutex;
+}
// Check whether the definition name def is a mangled function name that matches
// the reference name ref.
// Suggest an alternative spelling of an "undefined symbol" diagnostic. Returns
// the suggested symbol, which is either in the symbol table, or in the same
// file of sym.
-template <class ELFT>
static const Symbol *getAlternativeSpelling(const Undefined &sym,
std::string &pre_hint,
std::string &post_hint) {
DenseMap<StringRef, const Symbol *> map;
- if (auto *file = dyn_cast_or_null<ObjFile<ELFT>>(sym.file)) {
+ if (sym.file && sym.file->kind() == InputFile::ObjKind) {
+ auto *file = cast<ELFFileBase>(sym.file);
// If sym is a symbol defined in a discarded section, maybeReportDiscarded()
// will give an error. Don't suggest an alternative spelling.
if (file && sym.discardedSecIdx != 0 &&
return s;
// If in the symbol table and not undefined.
- if (const Symbol *s = symtab->find(newName))
+ if (const Symbol *s = symtab.find(newName))
if (!s->isUndefined())
return s;
for (auto &it : map)
if (name.equals_insensitive(it.first))
return it.second;
- for (Symbol *sym : symtab->symbols())
+ for (Symbol *sym : symtab.getSymbols())
if (!sym->isUndefined() && name.equals_insensitive(sym->getName()))
return sym;
break;
}
if (!s)
- for (Symbol *sym : symtab->symbols())
+ for (Symbol *sym : symtab.getSymbols())
if (canSuggestExternCForCXX(name, sym->getName())) {
s = sym;
break;
return nullptr;
}
-template <class ELFT>
static void reportUndefinedSymbol(const UndefinedDiag &undef,
bool correctSpelling) {
- Symbol &sym = *undef.sym;
+ Undefined &sym = *undef.sym;
auto visibility = [&]() -> std::string {
- switch (sym.visibility) {
+ switch (sym.visibility()) {
case STV_INTERNAL:
return "internal ";
case STV_HIDDEN:
}
};
- std::string msg = maybeReportDiscarded<ELFT>(cast<Undefined>(sym));
+ std::string msg;
+ switch (config->ekind) {
+ case ELF32LEKind:
+ msg = maybeReportDiscarded<ELF32LE>(sym);
+ break;
+ case ELF32BEKind:
+ msg = maybeReportDiscarded<ELF32BE>(sym);
+ break;
+ case ELF64LEKind:
+ msg = maybeReportDiscarded<ELF64LE>(sym);
+ break;
+ case ELF64BEKind:
+ msg = maybeReportDiscarded<ELF64BE>(sym);
+ break;
+ default:
+ llvm_unreachable("");
+ }
if (msg.empty())
msg = "undefined " + visibility() + "symbol: " + toString(sym);
if (correctSpelling) {
std::string pre_hint = ": ", post_hint;
- if (const Symbol *corrected = getAlternativeSpelling<ELFT>(
- cast<Undefined>(sym), pre_hint, post_hint)) {
+ if (const Symbol *corrected =
+ getAlternativeSpelling(sym, pre_hint, post_hint)) {
msg += "\n>>> did you mean" + pre_hint + toString(*corrected) + post_hint;
if (corrected->file)
msg += "\n>>> defined in: " + toString(corrected->file);
msg +=
"\n>>> the vtable symbol may be undefined because the class is missing "
"its key function (see https://lld.llvm.org/missingkeyfunction)";
+ if (config->gcSections && config->zStartStopGC &&
+ sym.getName().startswith("__start_")) {
+ msg += "\n>>> the encapsulation symbol needs to be retained under "
+ "--gc-sections properly; consider -z nostart-stop-gc "
+ "(see https://lld.llvm.org/ELF/start-stop-gc)";
+ }
if (undef.isWarning)
warn(msg);
error(msg, ErrorTag::SymbolNotFound, {sym.getName()});
}
-template <class ELFT> void elf::reportUndefinedSymbols() {
+void elf::reportUndefinedSymbols() {
// Find the first "undefined symbol" diagnostic for each diagnostic, and
// collect all "referenced from" lines at the first diagnostic.
DenseMap<Symbol *, UndefinedDiag *> firstRef;
}
// Enable spell corrector for the first 2 diagnostics.
- for (auto it : enumerate(undefs))
- if (!it.value().locs.empty())
- reportUndefinedSymbol<ELFT>(it.value(), it.index() < 2);
+ for (const auto &[i, undef] : llvm::enumerate(undefs))
+ if (!undef.locs.empty())
+ reportUndefinedSymbol(undef, i < 2);
undefs.clear();
}
// Report an undefined symbol if necessary.
// Returns true if the undefined symbol will produce an error message.
-static bool maybeReportUndefined(Symbol &sym, InputSectionBase &sec,
+static bool maybeReportUndefined(Undefined &sym, InputSectionBase &sec,
uint64_t offset) {
- if (!sym.isUndefined())
- return false;
+ std::lock_guard<std::mutex> lock(relocMutex);
// If versioned, issue an error (even if the symbol is weak) because we don't
// know the defining filename which is required to construct a Verneed entry.
- if (*sym.getVersionSuffix() == '@') {
+ if (sym.hasVersionSuffix) {
undefs.push_back({&sym, {{&sec, offset}}, false});
return true;
}
if (sym.isWeak())
return false;
- bool canBeExternal = !sym.isLocal() && sym.visibility == STV_DEFAULT;
+ bool canBeExternal = !sym.isLocal() && sym.visibility() == STV_DEFAULT;
if (config->unresolvedSymbols == UnresolvedPolicy::Ignore && canBeExternal)
return false;
// PPC32 .got2 is similar but cannot be fixed. Multiple .got2 is infeasible
// because .LC0-.LTOC is not representable if the two labels are in different
// .got2
- if (cast<Undefined>(sym).discardedSecIdx != 0 &&
- (sec.name == ".got2" || sec.name == ".toc"))
+ if (sym.discardedSecIdx != 0 && (sec.name == ".got2" || sec.name == ".toc"))
return false;
bool isWarning =
// packs all relocations into the single relocation record. Here we emulate
// this for the N32 ABI. Iterate over relocation with the same offset and put
// theirs types into the single bit-set.
-template <class RelTy> static RelType getMipsN32RelType(RelTy *&rel, RelTy *end) {
+template <class RelTy>
+RelType RelocationScanner::getMipsN32RelType(RelTy *&rel) const {
RelType type = 0;
uint64_t offset = rel->r_offset;
int n = 0;
- while (rel != end && rel->r_offset == offset)
+ while (rel != static_cast<const RelTy *>(end) && rel->r_offset == offset)
type |= (rel++)->getType(config->isMips64EL) << (8 * n++);
return type;
}
-// .eh_frame sections are mergeable input sections, so their input
-// offsets are not linearly mapped to output section. For each input
-// offset, we need to find a section piece containing the offset and
-// add the piece's base address to the input offset to compute the
-// output offset. That isn't cheap.
-//
-// This class is to speed up the offset computation. When we process
-// relocations, we access offsets in the monotonically increasing
-// order. So we can optimize for that access pattern.
-//
-// For sections other than .eh_frame, this class doesn't do anything.
-namespace {
-class OffsetGetter {
-public:
- explicit OffsetGetter(InputSectionBase &sec) {
- if (auto *eh = dyn_cast<EhInputSection>(&sec))
- pieces = eh->pieces;
- }
-
- // Translates offsets in input sections to offsets in output sections.
- // Given offset must increase monotonically. We assume that Piece is
- // sorted by inputOff.
- uint64_t get(uint64_t off) {
- if (pieces.empty())
- return off;
-
- while (i != pieces.size() && pieces[i].inputOff + pieces[i].size <= off)
- ++i;
- if (i == pieces.size())
- fatal(".eh_frame: relocation is not in any piece");
-
- // Pieces must be contiguous, so there must be no holes in between.
- assert(pieces[i].inputOff <= off && "Relocation not in any piece");
-
- // Offset -1 means that the piece is dead (i.e. garbage collected).
- if (pieces[i].outputOff == -1)
- return -1;
- return pieces[i].outputOff + off - pieces[i].inputOff;
- }
-
-private:
- ArrayRef<EhSectionPiece> pieces;
- size_t i = 0;
-};
-} // namespace
-
-static void addRelativeReloc(InputSectionBase *isec, uint64_t offsetInSec,
+template <bool shard = false>
+static void addRelativeReloc(InputSectionBase &isec, uint64_t offsetInSec,
Symbol &sym, int64_t addend, RelExpr expr,
RelType type) {
- Partition &part = isec->getPartition();
+ Partition &part = isec.getPartition();
// Add a relative relocation. If relrDyn section is enabled, and the
// relocation offset is guaranteed to be even, add the relocation to
// relrDyn sections don't support odd offsets. Also, relrDyn sections
// don't store the addend values, so we must write it to the relocated
// address.
- if (part.relrDyn && isec->alignment >= 2 && offsetInSec % 2 == 0) {
- isec->relocations.push_back({expr, type, offsetInSec, addend, &sym});
- part.relrDyn->relocs.push_back({isec, offsetInSec});
+ if (part.relrDyn && isec.addralign >= 2 && offsetInSec % 2 == 0) {
+ isec.addReloc({expr, type, offsetInSec, addend, &sym});
+ if (shard)
+ part.relrDyn->relocsVec[parallel::getThreadIndex()].push_back(
+ {&isec, offsetInSec});
+ else
+ part.relrDyn->relocs.push_back({&isec, offsetInSec});
return;
}
- part.relaDyn->addRelativeReloc(target->relativeRel, isec, offsetInSec, sym,
- addend, type, expr);
+ part.relaDyn->addRelativeReloc<shard>(target->relativeRel, isec, offsetInSec,
+ sym, addend, type, expr);
}
template <class PltSection, class GotPltSection>
-static void addPltEntry(PltSection *plt, GotPltSection *gotPlt,
- RelocationBaseSection *rel, RelType type, Symbol &sym) {
- plt->addEntry(sym);
- gotPlt->addEntry(sym);
- rel->addReloc({type, gotPlt, sym.getGotPltOffset(),
- sym.isPreemptible ? DynamicReloc::AgainstSymbol
- : DynamicReloc::AddendOnlyWithTargetVA,
- sym, 0, R_ABS});
+static void addPltEntry(PltSection &plt, GotPltSection &gotPlt,
+ RelocationBaseSection &rel, RelType type, Symbol &sym) {
+ plt.addEntry(sym);
+ gotPlt.addEntry(sym);
+ rel.addReloc({type, &gotPlt, sym.getGotPltOffset(),
+ sym.isPreemptible ? DynamicReloc::AgainstSymbol
+ : DynamicReloc::AddendOnlyWithTargetVA,
+ sym, 0, R_ABS});
}
static void addGotEntry(Symbol &sym) {
in.got->addEntry(sym);
-
- RelExpr expr = sym.isTls() ? R_TPREL : R_ABS;
uint64_t off = sym.getGotOffset();
- // If a GOT slot value can be calculated at link-time, which is now,
- // we can just fill that out.
- //
- // (We don't actually write a value to a GOT slot right now, but we
- // add a static relocation to a Relocations vector so that
- // InputSection::relocate will do the work for us. We may be able
- // to just write a value now, but it is a TODO.)
- bool isLinkTimeConstant =
- !sym.isPreemptible && (!config->isPic || isAbsolute(sym));
- if (isLinkTimeConstant) {
- in.got->relocations.push_back({expr, target->symbolicRel, off, 0, &sym});
+ // If preemptible, emit a GLOB_DAT relocation.
+ if (sym.isPreemptible) {
+ mainPart->relaDyn->addReloc({target->gotRel, in.got.get(), off,
+ DynamicReloc::AgainstSymbol, sym, 0, R_ABS});
return;
}
- // Otherwise, we emit a dynamic relocation to .rel[a].dyn so that
- // the GOT slot will be fixed at load-time.
- if (!sym.isTls() && !sym.isPreemptible && config->isPic) {
- addRelativeReloc(in.got, off, sym, 0, R_ABS, target->symbolicRel);
+ // Otherwise, the value is either a link-time constant or the load base
+ // plus a constant.
+ if (!config->isPic || isAbsolute(sym))
+ in.got->addConstant({R_ABS, target->symbolicRel, off, 0, &sym});
+ else
+ addRelativeReloc(*in.got, off, sym, 0, R_ABS, target->symbolicRel);
+}
+
+static void addTpOffsetGotEntry(Symbol &sym) {
+ in.got->addEntry(sym);
+ uint64_t off = sym.getGotOffset();
+ if (!sym.isPreemptible && !config->isPic) {
+ in.got->addConstant({R_TPREL, target->symbolicRel, off, 0, &sym});
return;
}
mainPart->relaDyn->addAddendOnlyRelocIfNonPreemptible(
- sym.isTls() ? target->tlsGotRel : target->gotRel, in.got, off, sym,
- target->symbolicRel);
+ target->tlsGotRel, *in.got, off, sym, target->symbolicRel);
}
// Return true if we can define a symbol in the executable that
// If the symbol has default visibility the symbol defined in the
// executable will preempt it.
// Note that we want the visibility of the shared symbol itself, not
- // the visibility of the symbol in the output file we are producing. That is
- // why we use Sym.stOther.
- if ((sym.stOther & 0x3) == STV_DEFAULT)
+ // the visibility of the symbol in the output file we are producing.
+ if (!sym.dsoProtected)
return true;
// If we are allowed to break address equality of functions, defining
(sym.isObject() && config->ignoreDataAddressEquality));
}
+// Returns true if a given relocation can be computed at link-time.
+// This only handles relocation types expected in processAux.
+//
+// For instance, we know the offset from a relocation to its target at
+// link-time if the relocation is PC-relative and refers a
+// non-interposable function in the same executable. This function
+// will return true for such relocation.
+//
+// If this function returns false, that means we need to emit a
+// dynamic relocation so that the relocation will be fixed at load-time.
+bool RelocationScanner::isStaticLinkTimeConstant(RelExpr e, RelType type,
+ const Symbol &sym,
+ uint64_t relOff) const {
+ // These expressions always compute a constant
+ if (oneof<R_GOTPLT, R_GOT_OFF, R_RELAX_HINT, R_MIPS_GOT_LOCAL_PAGE,
+ R_MIPS_GOTREL, R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC,
+ R_AARCH64_GOT_PAGE_PC, R_GOT_PC, R_GOTONLY_PC, R_GOTPLTONLY_PC,
+ R_PLT_PC, R_PLT_GOTPLT, R_PPC32_PLTREL, R_PPC64_CALL_PLT,
+ R_PPC64_RELAX_TOC, R_RISCV_ADD, R_AARCH64_GOT_PAGE>(e))
+ return true;
+
+ // These never do, except if the entire file is position dependent or if
+ // only the low bits are used.
+ if (e == R_GOT || e == R_PLT)
+ return target->usesOnlyLowPageBits(type) || !config->isPic;
+
+ if (sym.isPreemptible)
+ return false;
+ if (!config->isPic)
+ return true;
+
+ // The size of a non preemptible symbol is a constant.
+ if (e == R_SIZE)
+ return true;
+
+ // For the target and the relocation, we want to know if they are
+ // absolute or relative.
+ bool absVal = isAbsoluteValue(sym);
+ bool relE = isRelExpr(e);
+ if (absVal && !relE)
+ return true;
+ if (!absVal && relE)
+ return true;
+ if (!absVal && !relE)
+ return target->usesOnlyLowPageBits(type);
+
+ assert(absVal && relE);
+
+ // Allow R_PLT_PC (optimized to R_PC here) to a hidden undefined weak symbol
+ // in PIC mode. This is a little strange, but it allows us to link function
+ // calls to such symbols (e.g. glibc/stdlib/exit.c:__run_exit_handlers).
+ // Normally such a call will be guarded with a comparison, which will load a
+ // zero from the GOT.
+ if (sym.isUndefWeak())
+ return true;
+
+ // We set the final symbols values for linker script defined symbols later.
+ // They always can be computed as a link time constant.
+ if (sym.scriptDefined)
+ return true;
+
+ error("relocation " + toString(type) + " cannot refer to absolute symbol: " +
+ toString(sym) + getLocation(*sec, sym, relOff));
+ return true;
+}
+
// The reason we have to do this early scan is as follows
// * To mmap the output file, we need to know the size
// * For that, we need to know how many dynamic relocs we will have.
// sections. Given that it is ro, we will need an extra PT_LOAD. This
// complicates things for the dynamic linker and means we would have to reserve
// space for the extra PT_LOAD even if we end up not using it.
-template <class ELFT, class RelTy>
-static void processRelocAux(InputSectionBase &sec, RelExpr expr, RelType type,
- uint64_t offset, Symbol &sym, const RelTy &rel,
- int64_t addend) {
+void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset,
+ Symbol &sym, int64_t addend) const {
+ // If non-ifunc non-preemptible, change PLT to direct call and optimize GOT
+ // indirection.
+ const bool isIfunc = sym.isGnuIFunc();
+ if (!sym.isPreemptible && (!isIfunc || config->zIfuncNoplt)) {
+ if (expr != R_GOT_PC) {
+ // The 0x8000 bit of r_addend of R_PPC_PLTREL24 is used to choose call
+ // stub type. It should be ignored if optimized to R_PC.
+ if (config->emachine == EM_PPC && expr == R_PPC32_PLTREL)
+ addend &= ~0x8000;
+ // R_HEX_GD_PLT_B22_PCREL (call a@GDPLT) is transformed into
+ // call __tls_get_addr even if the symbol is non-preemptible.
+ if (!(config->emachine == EM_HEXAGON &&
+ (type == R_HEX_GD_PLT_B22_PCREL ||
+ type == R_HEX_GD_PLT_B22_PCREL_X ||
+ type == R_HEX_GD_PLT_B32_PCREL_X)))
+ expr = fromPlt(expr);
+ } else if (!isAbsoluteValue(sym)) {
+ expr =
+ target->adjustGotPcExpr(type, addend, sec->content().data() + offset);
+ }
+ }
+
+ // We were asked not to generate PLT entries for ifuncs. Instead, pass the
+ // direct relocation on through.
+ if (LLVM_UNLIKELY(isIfunc) && config->zIfuncNoplt) {
+ std::lock_guard<std::mutex> lock(relocMutex);
+ sym.exportDynamic = true;
+ mainPart->relaDyn->addSymbolReloc(type, *sec, offset, sym, addend, type);
+ return;
+ }
+
+ if (needsGot(expr)) {
+ if (config->emachine == EM_MIPS) {
+ // MIPS ABI has special rules to process GOT entries and doesn't
+ // require relocation entries for them. A special case is TLS
+ // relocations. In that case dynamic loader applies dynamic
+ // relocations to initialize TLS GOT entries.
+ // See "Global Offset Table" in Chapter 5 in the following document
+ // for detailed description:
+ // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
+ in.mipsGot->addEntry(*sec->file, sym, addend, expr);
+ } else {
+ sym.setFlags(NEEDS_GOT);
+ }
+ } else if (needsPlt(expr)) {
+ sym.setFlags(NEEDS_PLT);
+ } else if (LLVM_UNLIKELY(isIfunc)) {
+ sym.setFlags(HAS_DIRECT_RELOC);
+ }
+
// If the relocation is known to be a link-time constant, we know no dynamic
// relocation will be created, pass the control to relocateAlloc() or
// relocateNonAlloc() to resolve it.
// -shared matches the spirit of its -z undefs default. -pie has freedom on
// choices, and we choose dynamic relocations to be consistent with the
// handling of GOT-generating relocations.
- if (isStaticLinkTimeConstant(expr, type, sym, sec, offset) ||
+ if (isStaticLinkTimeConstant(expr, type, sym, offset) ||
(!config->isPic && sym.isUndefWeak())) {
- sec.relocations.push_back({expr, type, offset, addend, &sym});
+ sec->addReloc({expr, type, offset, addend, &sym});
return;
}
- bool canWrite = (sec.flags & SHF_WRITE) || !config->zText;
+ // Use a simple -z notext rule that treats all sections except .eh_frame as
+ // writable. GNU ld does not produce dynamic relocations in .eh_frame (and our
+ // SectionBase::getOffset would incorrectly adjust the offset).
+ //
+ // For MIPS, we don't implement GNU ld's DW_EH_PE_absptr to DW_EH_PE_pcrel
+ // conversion. We still emit a dynamic relocation.
+ bool canWrite = (sec->flags & SHF_WRITE) ||
+ !(config->zText ||
+ (isa<EhInputSection>(sec) && config->emachine != EM_MIPS));
if (canWrite) {
RelType rel = target->getDynRel(type);
if (expr == R_GOT || (rel == target->symbolicRel && !sym.isPreemptible)) {
- addRelativeReloc(&sec, offset, sym, addend, expr, type);
+ addRelativeReloc<true>(*sec, offset, sym, addend, expr, type);
return;
} else if (rel != 0) {
if (config->emachine == EM_MIPS && rel == target->symbolicRel)
rel = target->relativeRel;
- sec.getPartition().relaDyn->addSymbolReloc(rel, &sec, offset, sym, addend,
- type);
+ std::lock_guard<std::mutex> lock(relocMutex);
+ sec->getPartition().relaDyn->addSymbolReloc(rel, *sec, offset, sym,
+ addend, type);
// MIPS ABI turns using of GOT and dynamic relocations inside out.
// While regular ABI uses dynamic relocations to fill up GOT entries
// a dynamic relocation.
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf p.4-19
if (config->emachine == EM_MIPS)
- in.mipsGot->addEntry(*sec.file, sym, addend, expr);
+ in.mipsGot->addEntry(*sec->file, sym, addend, expr);
return;
}
}
if (!config->shared) {
if (!canDefineSymbolInExecutable(sym)) {
errorOrWarn("cannot preempt symbol: " + toString(sym) +
- getLocation(sec, sym, offset));
+ getLocation(*sec, sym, offset));
return;
}
error("unresolvable relocation " + toString(type) +
" against symbol '" + toString(*ss) +
"'; recompile with -fPIC or remove '-z nocopyreloc'" +
- getLocation(sec, sym, offset));
- addCopyRelSymbol<ELFT>(*ss);
+ getLocation(*sec, sym, offset));
+ sym.setFlags(NEEDS_COPY);
}
- sec.relocations.push_back({expr, type, offset, addend, &sym});
+ sec->addReloc({expr, type, offset, addend, &sym});
return;
}
if (config->pie && config->emachine == EM_386)
errorOrWarn("symbol '" + toString(sym) +
"' cannot be preempted; recompile with -fPIE" +
- getLocation(sec, sym, offset));
- if (!sym.isInPlt())
- addPltEntry(in.plt, in.gotPlt, in.relaPlt, target->pltRel, sym);
- if (!sym.isDefined()) {
- replaceWithDefined(
- sym, in.plt,
- target->pltHeaderSize + target->pltEntrySize * sym.pltIndex, 0);
- if (config->emachine == EM_PPC) {
- // PPC32 canonical PLT entries are at the beginning of .glink
- cast<Defined>(sym).value = in.plt->headerSize;
- in.plt->headerSize += 16;
- cast<PPC32GlinkSection>(in.plt)->canonical_plts.push_back(&sym);
- }
- }
- sym.needsPltAddr = true;
- sec.relocations.push_back({expr, type, offset, addend, &sym});
+ getLocation(*sec, sym, offset));
+ sym.setFlags(NEEDS_COPY | NEEDS_PLT);
+ sec->addReloc({expr, type, offset, addend, &sym});
return;
}
}
- if (config->isPic) {
- if (!canWrite && !isRelExpr(expr))
- errorOrWarn(
- "can't create dynamic relocation " + toString(type) + " against " +
- (sym.getName().empty() ? "local symbol"
- : "symbol: " + toString(sym)) +
- " in readonly segment; recompile object files with -fPIC "
- "or pass '-Wl,-z,notext' to allow text relocations in the output" +
- getLocation(sec, sym, offset));
- else
- errorOrWarn(
- "relocation " + toString(type) + " cannot be used against " +
- (sym.getName().empty() ? "local symbol" : "symbol " + toString(sym)) +
- "; recompile with -fPIC" + getLocation(sec, sym, offset));
- return;
+ errorOrWarn("relocation " + toString(type) + " cannot be used against " +
+ (sym.getName().empty() ? "local symbol"
+ : "symbol '" + toString(sym) + "'") +
+ "; recompile with -fPIC" + getLocation(*sec, sym, offset));
+}
+
+// This function is similar to the `handleTlsRelocation`. MIPS does not
+// support any relaxations for TLS relocations so by factoring out MIPS
+// handling in to the separate function we can simplify the code and do not
+// pollute other `handleTlsRelocation` by MIPS `ifs` statements.
+// Mips has a custom MipsGotSection that handles the writing of GOT entries
+// without dynamic relocations.
+static unsigned handleMipsTlsRelocation(RelType type, Symbol &sym,
+ InputSectionBase &c, uint64_t offset,
+ int64_t addend, RelExpr expr) {
+ if (expr == R_MIPS_TLSLD) {
+ in.mipsGot->addTlsIndex(*c.file);
+ c.addReloc({expr, type, offset, addend, &sym});
+ return 1;
+ }
+ if (expr == R_MIPS_TLSGD) {
+ in.mipsGot->addDynTlsEntry(*c.file, sym);
+ c.addReloc({expr, type, offset, addend, &sym});
+ return 1;
+ }
+ return 0;
+}
+
+// Notes about General Dynamic and Local Dynamic TLS models below. They may
+// require the generation of a pair of GOT entries that have associated dynamic
+// relocations. The pair of GOT entries created are of the form GOT[e0] Module
+// Index (Used to find pointer to TLS block at run-time) GOT[e1] Offset of
+// symbol in TLS block.
+//
+// Returns the number of relocations processed.
+static unsigned handleTlsRelocation(RelType type, Symbol &sym,
+ InputSectionBase &c, uint64_t offset,
+ int64_t addend, RelExpr expr) {
+ if (expr == R_TPREL || expr == R_TPREL_NEG) {
+ if (config->shared) {
+ errorOrWarn("relocation " + toString(type) + " against " + toString(sym) +
+ " cannot be used with -shared" + getLocation(c, sym, offset));
+ return 1;
+ }
+ return 0;
+ }
+
+ if (config->emachine == EM_MIPS)
+ return handleMipsTlsRelocation(type, sym, c, offset, addend, expr);
+
+ if (oneof<R_AARCH64_TLSDESC_PAGE, R_TLSDESC, R_TLSDESC_CALL, R_TLSDESC_PC,
+ R_TLSDESC_GOTPLT>(expr) &&
+ config->shared) {
+ if (expr != R_TLSDESC_CALL) {
+ sym.setFlags(NEEDS_TLSDESC);
+ c.addReloc({expr, type, offset, addend, &sym});
+ }
+ return 1;
+ }
+
+ // ARM, Hexagon and RISC-V do not support GD/LD to IE/LE relaxation. For
+ // PPC64, if the file has missing R_PPC64_TLSGD/R_PPC64_TLSLD, disable
+ // relaxation as well.
+ bool toExecRelax = !config->shared && config->emachine != EM_ARM &&
+ config->emachine != EM_HEXAGON &&
+ config->emachine != EM_RISCV &&
+ !c.file->ppc64DisableTLSRelax;
+
+ // If we are producing an executable and the symbol is non-preemptable, it
+ // must be defined and the code sequence can be relaxed to use Local-Exec.
+ //
+ // ARM and RISC-V do not support any relaxations for TLS relocations, however,
+ // we can omit the DTPMOD dynamic relocations and resolve them at link time
+ // because them are always 1. This may be necessary for static linking as
+ // DTPMOD may not be expected at load time.
+ bool isLocalInExecutable = !sym.isPreemptible && !config->shared;
+
+ // Local Dynamic is for access to module local TLS variables, while still
+ // being suitable for being dynamically loaded via dlopen. GOT[e0] is the
+ // module index, with a special value of 0 for the current module. GOT[e1] is
+ // unused. There only needs to be one module index entry.
+ if (oneof<R_TLSLD_GOT, R_TLSLD_GOTPLT, R_TLSLD_PC, R_TLSLD_HINT>(
+ expr)) {
+ // Local-Dynamic relocs can be relaxed to Local-Exec.
+ if (toExecRelax) {
+ c.addReloc({target->adjustTlsExpr(type, R_RELAX_TLS_LD_TO_LE), type,
+ offset, addend, &sym});
+ return target->getTlsGdRelaxSkip(type);
+ }
+ if (expr == R_TLSLD_HINT)
+ return 1;
+ ctx.needsTlsLd.store(true, std::memory_order_relaxed);
+ c.addReloc({expr, type, offset, addend, &sym});
+ return 1;
+ }
+
+ // Local-Dynamic relocs can be relaxed to Local-Exec.
+ if (expr == R_DTPREL) {
+ if (toExecRelax)
+ expr = target->adjustTlsExpr(type, R_RELAX_TLS_LD_TO_LE);
+ c.addReloc({expr, type, offset, addend, &sym});
+ return 1;
+ }
+
+ // Local-Dynamic sequence where offset of tls variable relative to dynamic
+ // thread pointer is stored in the got. This cannot be relaxed to Local-Exec.
+ if (expr == R_TLSLD_GOT_OFF) {
+ sym.setFlags(NEEDS_GOT_DTPREL);
+ c.addReloc({expr, type, offset, addend, &sym});
+ return 1;
+ }
+
+ if (oneof<R_AARCH64_TLSDESC_PAGE, R_TLSDESC, R_TLSDESC_CALL, R_TLSDESC_PC,
+ R_TLSDESC_GOTPLT, R_TLSGD_GOT, R_TLSGD_GOTPLT, R_TLSGD_PC>(expr)) {
+ if (!toExecRelax) {
+ sym.setFlags(NEEDS_TLSGD);
+ c.addReloc({expr, type, offset, addend, &sym});
+ return 1;
+ }
+
+ // Global-Dynamic relocs can be relaxed to Initial-Exec or Local-Exec
+ // depending on the symbol being locally defined or not.
+ if (sym.isPreemptible) {
+ sym.setFlags(NEEDS_TLSGD_TO_IE);
+ c.addReloc({target->adjustTlsExpr(type, R_RELAX_TLS_GD_TO_IE), type,
+ offset, addend, &sym});
+ } else {
+ c.addReloc({target->adjustTlsExpr(type, R_RELAX_TLS_GD_TO_LE), type,
+ offset, addend, &sym});
+ }
+ return target->getTlsGdRelaxSkip(type);
+ }
+
+ if (oneof<R_GOT, R_GOTPLT, R_GOT_PC, R_AARCH64_GOT_PAGE_PC, R_GOT_OFF,
+ R_TLSIE_HINT>(expr)) {
+ ctx.hasTlsIe.store(true, std::memory_order_relaxed);
+ // Initial-Exec relocs can be relaxed to Local-Exec if the symbol is locally
+ // defined.
+ if (toExecRelax && isLocalInExecutable) {
+ c.addReloc({R_RELAX_TLS_IE_TO_LE, type, offset, addend, &sym});
+ } else if (expr != R_TLSIE_HINT) {
+ sym.setFlags(NEEDS_TLSIE);
+ // R_GOT needs a relative relocation for PIC on i386 and Hexagon.
+ if (expr == R_GOT && config->isPic && !target->usesOnlyLowPageBits(type))
+ addRelativeReloc<true>(c, offset, sym, addend, expr, type);
+ else
+ c.addReloc({expr, type, offset, addend, &sym});
+ }
+ return 1;
}
- errorOrWarn("symbol '" + toString(sym) + "' has no type" +
- getLocation(sec, sym, offset));
+ return 0;
}
-template <class ELFT, class RelTy>
-static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
- RelTy *start, RelTy *end) {
+template <class ELFT, class RelTy> void RelocationScanner::scanOne(RelTy *&i) {
const RelTy &rel = *i;
uint32_t symIndex = rel.getSymbol(config->isMips64EL);
- Symbol &sym = sec.getFile<ELFT>()->getSymbol(symIndex);
+ Symbol &sym = sec->getFile<ELFT>()->getSymbol(symIndex);
RelType type;
-
- // Deal with MIPS oddity.
if (config->mipsN32Abi) {
- type = getMipsN32RelType(i, end);
+ type = getMipsN32RelType(i);
} else {
type = rel.getType(config->isMips64EL);
++i;
}
-
// Get an offset in an output section this relocation is applied to.
- uint64_t offset = getOffset.get(rel.r_offset);
+ uint64_t offset = getter.get(rel.r_offset);
if (offset == uint64_t(-1))
return;
- // Error if the target symbol is undefined. Symbol index 0 may be used by
- // marker relocations, e.g. R_*_NONE and R_ARM_V4BX. Don't error on them.
- if (symIndex != 0 && maybeReportUndefined(sym, sec, rel.r_offset))
- return;
-
- reportGNUWarning(sym, sec, rel.r_offset);
+ reportGNUWarning(sym, *sec, rel.r_offset);
- const uint8_t *relocatedAddr = sec.data().begin() + rel.r_offset;
- RelExpr expr = target->getRelExpr(type, sym, relocatedAddr);
+ RelExpr expr = target->getRelExpr(type, sym, sec->content().data() + offset);
+ int64_t addend = RelTy::IsRela
+ ? getAddend<ELFT>(rel)
+ : target->getImplicitAddend(
+ sec->content().data() + rel.r_offset, type);
+ if (LLVM_UNLIKELY(config->emachine == EM_MIPS))
+ addend += computeMipsAddend<ELFT>(rel, expr, sym.isLocal());
+ else if (config->emachine == EM_PPC64 && config->isPic && type == R_PPC64_TOC)
+ addend += getPPC64TocBase();
// Ignore R_*_NONE and other marker relocations.
if (expr == R_NONE)
return;
- // Read an addend.
- int64_t addend = computeAddend<ELFT>(rel, end, sec, expr, sym.isLocal());
+ // Error if the target symbol is undefined. Symbol index 0 may be used by
+ // marker relocations, e.g. R_*_NONE and R_ARM_V4BX. Don't error on them.
+ if (sym.isUndefined() && symIndex != 0 &&
+ maybeReportUndefined(cast<Undefined>(sym), *sec, offset))
+ return;
if (config->emachine == EM_PPC64) {
// We can separate the small code model relocations into 2 categories:
// have got-based small code model relocs. The .toc sections get placed
// after the end of the linker allocated .got section and we do sort those
// so sections addressed with small code model relocations come first.
- if (isPPC64SmallCodeModelTocReloc(type))
- sec.file->ppc64SmallCodeModelTocRelocs = true;
+ if (type == R_PPC64_TOC16 || type == R_PPC64_TOC16_DS)
+ sec->file->ppc64SmallCodeModelTocRelocs = true;
// Record the TOC entry (.toc + addend) as not relaxable. See the comment in
// InputSectionBase::relocateAlloc().
if (i == end) {
errorOrWarn("R_PPC64_TLSGD/R_PPC64_TLSLD may not be the last "
"relocation" +
- getLocation(sec, sym, offset));
+ getLocation(*sec, sym, offset));
return;
}
}
}
- // Relax relocations.
- //
- // If we know that a PLT entry will be resolved within the same ELF module, we
- // can skip PLT access and directly jump to the destination function. For
- // example, if we are linking a main executable, all dynamic symbols that can
- // be resolved within the executable will actually be resolved that way at
- // runtime, because the main executable is always at the beginning of a search
- // list. We can leverage that fact.
- if (!sym.isPreemptible && (!sym.isGnuIFunc() || config->zIfuncNoplt)) {
- if (expr != R_GOT_PC) {
- // The 0x8000 bit of r_addend of R_PPC_PLTREL24 is used to choose call
- // stub type. It should be ignored if optimized to R_PC.
- if (config->emachine == EM_PPC && expr == R_PPC32_PLTREL)
- addend &= ~0x8000;
- // R_HEX_GD_PLT_B22_PCREL (call a@GDPLT) is transformed into
- // call __tls_get_addr even if the symbol is non-preemptible.
- if (!(config->emachine == EM_HEXAGON &&
- (type == R_HEX_GD_PLT_B22_PCREL ||
- type == R_HEX_GD_PLT_B22_PCREL_X ||
- type == R_HEX_GD_PLT_B32_PCREL_X)))
- expr = fromPlt(expr);
- } else if (!isAbsoluteValue(sym)) {
- expr = target->adjustGotPcExpr(type, addend, relocatedAddr);
- }
- }
-
// If the relocation does not emit a GOT or GOTPLT entry but its computation
// uses their addresses, we need GOT or GOTPLT to be created.
//
- // The 4 types that relative GOTPLT are all x86 and x86-64 specific.
- if (oneof<R_GOTPLTONLY_PC, R_GOTPLTREL, R_GOTPLT, R_TLSGD_GOTPLT>(expr)) {
- in.gotPlt->hasGotPltOffRel = true;
+ // The 5 types that relative GOTPLT are all x86 and x86-64 specific.
+ if (oneof<R_GOTPLTONLY_PC, R_GOTPLTREL, R_GOTPLT, R_PLT_GOTPLT,
+ R_TLSDESC_GOTPLT, R_TLSGD_GOTPLT>(expr)) {
+ in.gotPlt->hasGotPltOffRel.store(true, std::memory_order_relaxed);
} else if (oneof<R_GOTONLY_PC, R_GOTREL, R_PPC32_PLTREL, R_PPC64_TOCBASE,
R_PPC64_RELAX_TOC>(expr)) {
- in.got->hasGotOffRel = true;
+ in.got->hasGotOffRel.store(true, std::memory_order_relaxed);
}
// Process TLS relocations, including relaxing TLS relocations. Note that
- // R_TPREL and R_TPREL_NEG relocations are resolved in processRelocAux.
- if (expr == R_TPREL || expr == R_TPREL_NEG) {
- if (config->shared) {
- errorOrWarn("relocation " + toString(type) + " against " + toString(sym) +
- " cannot be used with -shared" +
- getLocation(sec, sym, offset));
+ // R_TPREL and R_TPREL_NEG relocations are resolved in processAux.
+ if (sym.isTls()) {
+ if (unsigned processed =
+ handleTlsRelocation(type, sym, *sec, offset, addend, expr)) {
+ i += processed - 1;
return;
}
- } else if (unsigned processed = handleTlsRelocation<ELFT>(
- type, sym, sec, offset, addend, expr)) {
- i += (processed - 1);
- return;
- }
-
- // We were asked not to generate PLT entries for ifuncs. Instead, pass the
- // direct relocation on through.
- if (sym.isGnuIFunc() && config->zIfuncNoplt) {
- sym.exportDynamic = true;
- mainPart->relaDyn->addSymbolReloc(type, &sec, offset, sym, addend, type);
- return;
- }
-
- // Non-preemptible ifuncs require special handling. First, handle the usual
- // case where the symbol isn't one of these.
- if (!sym.isGnuIFunc() || sym.isPreemptible) {
- // If a relocation needs PLT, we create PLT and GOTPLT slots for the symbol.
- if (needsPlt(expr) && !sym.isInPlt())
- addPltEntry(in.plt, in.gotPlt, in.relaPlt, target->pltRel, sym);
-
- // Create a GOT slot if a relocation needs GOT.
- if (needsGot(expr)) {
- if (config->emachine == EM_MIPS) {
- // MIPS ABI has special rules to process GOT entries and doesn't
- // require relocation entries for them. A special case is TLS
- // relocations. In that case dynamic loader applies dynamic
- // relocations to initialize TLS GOT entries.
- // See "Global Offset Table" in Chapter 5 in the following document
- // for detailed description:
- // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
- in.mipsGot->addEntry(*sec.file, sym, addend, expr);
- } else if (!sym.isInGot()) {
- addGotEntry(sym);
- }
- }
- } else {
- // Handle a reference to a non-preemptible ifunc. These are special in a
- // few ways:
- //
- // - Unlike most non-preemptible symbols, non-preemptible ifuncs do not have
- // a fixed value. But assuming that all references to the ifunc are
- // GOT-generating or PLT-generating, the handling of an ifunc is
- // relatively straightforward. We create a PLT entry in Iplt, which is
- // usually at the end of .plt, which makes an indirect call using a
- // matching GOT entry in igotPlt, which is usually at the end of .got.plt.
- // The GOT entry is relocated using an IRELATIVE relocation in relaIplt,
- // which is usually at the end of .rela.plt. Unlike most relocations in
- // .rela.plt, which may be evaluated lazily without -z now, dynamic
- // loaders evaluate IRELATIVE relocs eagerly, which means that for
- // IRELATIVE relocs only, GOT-generating relocations can point directly to
- // .got.plt without requiring a separate GOT entry.
- //
- // - Despite the fact that an ifunc does not have a fixed value, compilers
- // that are not passed -fPIC will assume that they do, and will emit
- // direct (non-GOT-generating, non-PLT-generating) relocations to the
- // symbol. This means that if a direct relocation to the symbol is
- // seen, the linker must set a value for the symbol, and this value must
- // be consistent no matter what type of reference is made to the symbol.
- // This can be done by creating a PLT entry for the symbol in the way
- // described above and making it canonical, that is, making all references
- // point to the PLT entry instead of the resolver. In lld we also store
- // the address of the PLT entry in the dynamic symbol table, which means
- // that the symbol will also have the same value in other modules.
- // Because the value loaded from the GOT needs to be consistent with
- // the value computed using a direct relocation, a non-preemptible ifunc
- // may end up with two GOT entries, one in .got.plt that points to the
- // address returned by the resolver and is used only by the PLT entry,
- // and another in .got that points to the PLT entry and is used by
- // GOT-generating relocations.
- //
- // - The fact that these symbols do not have a fixed value makes them an
- // exception to the general rule that a statically linked executable does
- // not require any form of dynamic relocation. To handle these relocations
- // correctly, the IRELATIVE relocations are stored in an array which a
- // statically linked executable's startup code must enumerate using the
- // linker-defined symbols __rela?_iplt_{start,end}.
- if (!sym.isInPlt()) {
- // Create PLT and GOTPLT slots for the symbol.
- sym.isInIplt = true;
-
- // Create a copy of the symbol to use as the target of the IRELATIVE
- // relocation in the igotPlt. This is in case we make the PLT canonical
- // later, which would overwrite the original symbol.
- //
- // FIXME: Creating a copy of the symbol here is a bit of a hack. All
- // that's really needed to create the IRELATIVE is the section and value,
- // so ideally we should just need to copy those.
- auto *directSym = make<Defined>(cast<Defined>(sym));
- addPltEntry(in.iplt, in.igotPlt, in.relaIplt, target->iRelativeRel,
- *directSym);
- sym.pltIndex = directSym->pltIndex;
- }
- if (needsGot(expr)) {
- // Redirect GOT accesses to point to the Igot.
- //
- // This field is also used to keep track of whether we ever needed a GOT
- // entry. If we did and we make the PLT canonical later, we'll need to
- // create a GOT entry pointing to the PLT entry for Sym.
- sym.gotInIgot = true;
- } else if (!needsPlt(expr)) {
- // Make the ifunc's PLT entry canonical by changing the value of its
- // symbol to redirect all references to point to it.
- auto &d = cast<Defined>(sym);
- d.section = in.iplt;
- d.value = sym.pltIndex * target->ipltEntrySize;
- d.size = 0;
- // It's important to set the symbol type here so that dynamic loaders
- // don't try to call the PLT as if it were an ifunc resolver.
- d.type = STT_FUNC;
-
- if (sym.gotInIgot) {
- // We previously encountered a GOT generating reference that we
- // redirected to the Igot. Now that the PLT entry is canonical we must
- // clear the redirection to the Igot and add a GOT entry. As we've
- // changed the symbol type to STT_FUNC future GOT generating references
- // will naturally use this GOT entry.
- //
- // We don't need to worry about creating a MIPS GOT here because ifuncs
- // aren't a thing on MIPS.
- sym.gotInIgot = false;
- addGotEntry(sym);
- }
- }
}
- processRelocAux<ELFT>(sec, expr, type, offset, sym, rel, addend);
+ processAux(expr, type, offset, sym, addend);
}
// R_PPC64_TLSGD/R_PPC64_TLSLD is required to mark `bl __tls_get_addr` for
}
template <class ELFT, class RelTy>
-static void scanRelocs(InputSectionBase &sec, ArrayRef<RelTy> rels) {
- OffsetGetter getOffset(sec);
-
- // Not all relocations end up in Sec.Relocations, but a lot do.
- sec.relocations.reserve(rels.size());
+void RelocationScanner::scan(ArrayRef<RelTy> rels) {
+ // Not all relocations end up in Sec->Relocations, but a lot do.
+ sec->relocations.reserve(rels.size());
if (config->emachine == EM_PPC64)
- checkPPC64TLSRelax<RelTy>(sec, rels);
+ checkPPC64TLSRelax<RelTy>(*sec, rels);
// For EhInputSection, OffsetGetter expects the relocations to be sorted by
// r_offset. In rare cases (.eh_frame pieces are reordered by a linker
if (isa<EhInputSection>(sec))
rels = sortRels(rels, storage);
- for (auto i = rels.begin(), end = rels.end(); i != end;)
- scanReloc<ELFT>(sec, getOffset, i, rels.begin(), end);
+ end = static_cast<const void *>(rels.end());
+ for (auto i = rels.begin(); i != end;)
+ scanOne<ELFT>(i);
// Sort relocations by offset for more efficient searching for
// R_RISCV_PCREL_HI20 and R_PPC64_ADDR64.
if (config->emachine == EM_RISCV ||
- (config->emachine == EM_PPC64 && sec.name == ".toc"))
- llvm::stable_sort(sec.relocations,
+ (config->emachine == EM_PPC64 && sec->name == ".toc"))
+ llvm::stable_sort(sec->relocs(),
[](const Relocation &lhs, const Relocation &rhs) {
return lhs.offset < rhs.offset;
});
}
-template <class ELFT> void elf::scanRelocations(InputSectionBase &s) {
- if (s.areRelocsRela)
- scanRelocs<ELFT>(s, s.relas<ELFT>());
+template <class ELFT> void RelocationScanner::scanSection(InputSectionBase &s) {
+ sec = &s;
+ getter = OffsetGetter(s);
+ const RelsOrRelas<ELFT> rels = s.template relsOrRelas<ELFT>();
+ if (rels.areRelocsRel())
+ scan<ELFT>(rels.rels);
else
- scanRelocs<ELFT>(s, s.rels<ELFT>());
+ scan<ELFT>(rels.relas);
+}
+
+template <class ELFT> void elf::scanRelocations() {
+ // Scan all relocations. Each relocation goes through a series of tests to
+ // determine if it needs special treatment, such as creating GOT, PLT,
+ // copy relocations, etc. Note that relocations for non-alloc sections are
+ // directly processed by InputSection::relocateNonAlloc.
+
+ // Deterministic parallellism needs sorting relocations which is unsuitable
+ // for -z nocombreloc. MIPS and PPC64 use global states which are not suitable
+ // for parallelism.
+ bool serial = !config->zCombreloc || config->emachine == EM_MIPS ||
+ config->emachine == EM_PPC64;
+ parallel::TaskGroup tg;
+ for (ELFFileBase *f : ctx.objectFiles) {
+ auto fn = [f]() {
+ RelocationScanner scanner;
+ for (InputSectionBase *s : f->getSections()) {
+ if (s && s->kind() == SectionBase::Regular && s->isLive() &&
+ (s->flags & SHF_ALLOC) &&
+ !(s->type == SHT_ARM_EXIDX && config->emachine == EM_ARM))
+ scanner.template scanSection<ELFT>(*s);
+ }
+ };
+ if (serial)
+ fn();
+ else
+ tg.execute(fn);
+ }
+
+ // Both the main thread and thread pool index 0 use getThreadIndex()==0. Be
+ // careful that they don't concurrently run scanSections. When serial is
+ // true, fn() has finished at this point, so running execute is safe.
+ tg.execute([] {
+ RelocationScanner scanner;
+ for (Partition &part : partitions) {
+ for (EhInputSection *sec : part.ehFrame->sections)
+ scanner.template scanSection<ELFT>(*sec);
+ if (part.armExidx && part.armExidx->isLive())
+ for (InputSection *sec : part.armExidx->exidxSections)
+ scanner.template scanSection<ELFT>(*sec);
+ }
+ });
+}
+
+static bool handleNonPreemptibleIfunc(Symbol &sym, uint16_t flags) {
+ // Handle a reference to a non-preemptible ifunc. These are special in a
+ // few ways:
+ //
+ // - Unlike most non-preemptible symbols, non-preemptible ifuncs do not have
+ // a fixed value. But assuming that all references to the ifunc are
+ // GOT-generating or PLT-generating, the handling of an ifunc is
+ // relatively straightforward. We create a PLT entry in Iplt, which is
+ // usually at the end of .plt, which makes an indirect call using a
+ // matching GOT entry in igotPlt, which is usually at the end of .got.plt.
+ // The GOT entry is relocated using an IRELATIVE relocation in relaIplt,
+ // which is usually at the end of .rela.plt. Unlike most relocations in
+ // .rela.plt, which may be evaluated lazily without -z now, dynamic
+ // loaders evaluate IRELATIVE relocs eagerly, which means that for
+ // IRELATIVE relocs only, GOT-generating relocations can point directly to
+ // .got.plt without requiring a separate GOT entry.
+ //
+ // - Despite the fact that an ifunc does not have a fixed value, compilers
+ // that are not passed -fPIC will assume that they do, and will emit
+ // direct (non-GOT-generating, non-PLT-generating) relocations to the
+ // symbol. This means that if a direct relocation to the symbol is
+ // seen, the linker must set a value for the symbol, and this value must
+ // be consistent no matter what type of reference is made to the symbol.
+ // This can be done by creating a PLT entry for the symbol in the way
+ // described above and making it canonical, that is, making all references
+ // point to the PLT entry instead of the resolver. In lld we also store
+ // the address of the PLT entry in the dynamic symbol table, which means
+ // that the symbol will also have the same value in other modules.
+ // Because the value loaded from the GOT needs to be consistent with
+ // the value computed using a direct relocation, a non-preemptible ifunc
+ // may end up with two GOT entries, one in .got.plt that points to the
+ // address returned by the resolver and is used only by the PLT entry,
+ // and another in .got that points to the PLT entry and is used by
+ // GOT-generating relocations.
+ //
+ // - The fact that these symbols do not have a fixed value makes them an
+ // exception to the general rule that a statically linked executable does
+ // not require any form of dynamic relocation. To handle these relocations
+ // correctly, the IRELATIVE relocations are stored in an array which a
+ // statically linked executable's startup code must enumerate using the
+ // linker-defined symbols __rela?_iplt_{start,end}.
+ if (!sym.isGnuIFunc() || sym.isPreemptible || config->zIfuncNoplt)
+ return false;
+ // Skip unreferenced non-preemptible ifunc.
+ if (!(flags & (NEEDS_GOT | NEEDS_PLT | HAS_DIRECT_RELOC)))
+ return true;
+
+ sym.isInIplt = true;
+
+ // Create an Iplt and the associated IRELATIVE relocation pointing to the
+ // original section/value pairs. For non-GOT non-PLT relocation case below, we
+ // may alter section/value, so create a copy of the symbol to make
+ // section/value fixed.
+ auto *directSym = makeDefined(cast<Defined>(sym));
+ directSym->allocateAux();
+ addPltEntry(*in.iplt, *in.igotPlt, *in.relaIplt, target->iRelativeRel,
+ *directSym);
+ sym.allocateAux();
+ symAux.back().pltIdx = symAux[directSym->auxIdx].pltIdx;
+
+ if (flags & HAS_DIRECT_RELOC) {
+ // Change the value to the IPLT and redirect all references to it.
+ auto &d = cast<Defined>(sym);
+ d.section = in.iplt.get();
+ d.value = d.getPltIdx() * target->ipltEntrySize;
+ d.size = 0;
+ // It's important to set the symbol type here so that dynamic loaders
+ // don't try to call the PLT as if it were an ifunc resolver.
+ d.type = STT_FUNC;
+
+ if (flags & NEEDS_GOT)
+ addGotEntry(sym);
+ } else if (flags & NEEDS_GOT) {
+ // Redirect GOT accesses to point to the Igot.
+ sym.gotInIgot = true;
+ }
+ return true;
+}
+
+void elf::postScanRelocations() {
+ auto fn = [](Symbol &sym) {
+ auto flags = sym.flags.load(std::memory_order_relaxed);
+ if (handleNonPreemptibleIfunc(sym, flags))
+ return;
+ if (!sym.needsDynReloc())
+ return;
+ sym.allocateAux();
+
+ if (flags & NEEDS_GOT)
+ addGotEntry(sym);
+ if (flags & NEEDS_PLT)
+ addPltEntry(*in.plt, *in.gotPlt, *in.relaPlt, target->pltRel, sym);
+ if (flags & NEEDS_COPY) {
+ if (sym.isObject()) {
+ invokeELFT(addCopyRelSymbol, cast<SharedSymbol>(sym));
+ // NEEDS_COPY is cleared for sym and its aliases so that in
+ // later iterations aliases won't cause redundant copies.
+ assert(!sym.hasFlag(NEEDS_COPY));
+ } else {
+ assert(sym.isFunc() && sym.hasFlag(NEEDS_PLT));
+ if (!sym.isDefined()) {
+ replaceWithDefined(sym, *in.plt,
+ target->pltHeaderSize +
+ target->pltEntrySize * sym.getPltIdx(),
+ 0);
+ sym.setFlags(NEEDS_COPY);
+ if (config->emachine == EM_PPC) {
+ // PPC32 canonical PLT entries are at the beginning of .glink
+ cast<Defined>(sym).value = in.plt->headerSize;
+ in.plt->headerSize += 16;
+ cast<PPC32GlinkSection>(*in.plt).canonical_plts.push_back(&sym);
+ }
+ }
+ }
+ }
+
+ if (!sym.isTls())
+ return;
+ bool isLocalInExecutable = !sym.isPreemptible && !config->shared;
+ GotSection *got = in.got.get();
+
+ if (flags & NEEDS_TLSDESC) {
+ got->addTlsDescEntry(sym);
+ mainPart->relaDyn->addAddendOnlyRelocIfNonPreemptible(
+ target->tlsDescRel, *got, got->getTlsDescOffset(sym), sym,
+ target->tlsDescRel);
+ }
+ if (flags & NEEDS_TLSGD) {
+ got->addDynTlsEntry(sym);
+ uint64_t off = got->getGlobalDynOffset(sym);
+ if (isLocalInExecutable)
+ // Write one to the GOT slot.
+ got->addConstant({R_ADDEND, target->symbolicRel, off, 1, &sym});
+ else
+ mainPart->relaDyn->addSymbolReloc(target->tlsModuleIndexRel, *got, off,
+ sym);
+
+ // If the symbol is preemptible we need the dynamic linker to write
+ // the offset too.
+ uint64_t offsetOff = off + config->wordsize;
+ if (sym.isPreemptible)
+ mainPart->relaDyn->addSymbolReloc(target->tlsOffsetRel, *got, offsetOff,
+ sym);
+ else
+ got->addConstant({R_ABS, target->tlsOffsetRel, offsetOff, 0, &sym});
+ }
+ if (flags & NEEDS_TLSGD_TO_IE) {
+ got->addEntry(sym);
+ mainPart->relaDyn->addSymbolReloc(target->tlsGotRel, *got,
+ sym.getGotOffset(), sym);
+ }
+ if (flags & NEEDS_GOT_DTPREL) {
+ got->addEntry(sym);
+ got->addConstant(
+ {R_ABS, target->tlsOffsetRel, sym.getGotOffset(), 0, &sym});
+ }
+
+ if ((flags & NEEDS_TLSIE) && !(flags & NEEDS_TLSGD_TO_IE))
+ addTpOffsetGotEntry(sym);
+ };
+
+ GotSection *got = in.got.get();
+ if (ctx.needsTlsLd.load(std::memory_order_relaxed) && got->addTlsIndex()) {
+ static Undefined dummy(nullptr, "", STB_LOCAL, 0, 0);
+ if (config->shared)
+ mainPart->relaDyn->addReloc(
+ {target->tlsModuleIndexRel, got, got->getTlsIndexOff()});
+ else
+ got->addConstant(
+ {R_ADDEND, target->symbolicRel, got->getTlsIndexOff(), 1, &dummy});
+ }
+
+ assert(symAux.size() == 1);
+ for (Symbol *sym : symtab.getSymbols())
+ fn(*sym);
+
+ // Local symbols may need the aforementioned non-preemptible ifunc and GOT
+ // handling. They don't need regular PLT.
+ for (ELFFileBase *file : ctx.objectFiles)
+ for (Symbol *sym : file->getLocalSymbols())
+ fn(*sym);
}
static bool mergeCmp(const InputSection *a, const InputSection *b) {
if (a->outSecOff < b->outSecOff)
return true;
- if (a->outSecOff == b->outSecOff) {
+ // FIXME dyn_cast<ThunkSection> is non-null for any SyntheticSection.
+ if (a->outSecOff == b->outSecOff && a != b) {
auto *ta = dyn_cast<ThunkSection>(a);
auto *tb = dyn_cast<ThunkSection>(b);
for (OutputSection *os : outputSections) {
if (!(os->flags & SHF_ALLOC) || !(os->flags & SHF_EXECINSTR))
continue;
- for (BaseCommand *bc : os->sectionCommands)
+ for (SectionCommand *bc : os->commands)
if (auto *isd = dyn_cast<InputSectionDescription>(bc))
fn(os, isd);
}
});
// Merge sorted vectors of Thunks and InputSections by outSecOff
- std::vector<InputSection *> tmp;
+ SmallVector<InputSection *, 0> tmp;
tmp.reserve(isd->sections.size() + newThunks.size());
std::merge(isd->sections.begin(), isd->sections.end(),
});
}
+static int64_t getPCBias(RelType type) {
+ if (config->emachine != EM_ARM)
+ return 0;
+ switch (type) {
+ case R_ARM_THM_JUMP19:
+ case R_ARM_THM_JUMP24:
+ case R_ARM_THM_CALL:
+ return 4;
+ default:
+ return 8;
+ }
+}
+
// Find or create a ThunkSection within the InputSectionDescription (ISD) that
// is in range of Src. An ISD maps to a range of InputSections described by a
// linker script section pattern such as { .text .text.* }.
InputSectionDescription *isd,
const Relocation &rel,
uint64_t src) {
+ // See the comment in getThunk for -pcBias below.
+ const int64_t pcBias = getPCBias(rel.type);
for (std::pair<ThunkSection *, uint32_t> tp : isd->thunkSections) {
ThunkSection *ts = tp.first;
- uint64_t tsBase = os->addr + ts->outSecOff + rel.addend;
- uint64_t tsLimit = tsBase + ts->getSize() + rel.addend;
+ uint64_t tsBase = os->addr + ts->outSecOff - pcBias;
+ uint64_t tsLimit = tsBase + ts->getSize();
if (target->inBranchRange(rel.type, src,
(src > tsLimit) ? tsBase : tsLimit))
return ts;
// Find InputSectionRange within Target Output Section (TOS) that the
// InputSection (IS) that we need to precede is in.
OutputSection *tos = isec->getParent();
- for (BaseCommand *bc : tos->sectionCommands) {
+ for (SectionCommand *bc : tos->commands) {
auto *isd = dyn_cast<InputSectionDescription>(bc);
if (!isd || isd->sections.empty())
continue;
return true;
}
-static int64_t getPCBias(RelType type) {
- if (config->emachine != EM_ARM)
- return 0;
- switch (type) {
- case R_ARM_THM_JUMP19:
- case R_ARM_THM_JUMP24:
- case R_ARM_THM_CALL:
- return 4;
- default:
- return 8;
- }
-}
-
std::pair<Thunk *, bool> ThunkCreator::getThunk(InputSection *isec,
Relocation &rel, uint64_t src) {
std::vector<Thunk *> *thunkVec = nullptr;
// out in the relocation addend. We compensate for the PC bias so that
// an Arm and Thumb relocation to the same destination get the same keyAddend,
// which is usually 0.
- int64_t keyAddend = rel.addend + getPCBias(rel.type);
+ const int64_t pcBias = getPCBias(rel.type);
+ const int64_t keyAddend = rel.addend + pcBias;
// We use a ((section, offset), addend) pair to find the thunk position if
// possible so that we create only one thunk for aliased symbols or ICFed
// non-Thunk target, so we cannot fold offset + addend.
if (auto *d = dyn_cast<Defined>(rel.sym))
if (!d->isInPlt() && d->section)
- thunkVec = &thunkedSymbolsBySectionAndAddend[{
- {d->section->repl, d->value}, keyAddend}];
+ thunkVec = &thunkedSymbolsBySectionAndAddend[{{d->section, d->value},
+ keyAddend}];
if (!thunkVec)
thunkVec = &thunkedSymbols[{rel.sym, keyAddend}];
if (isThunkSectionCompatible(isec, t->getThunkTargetSym()->section) &&
t->isCompatibleWith(*isec, rel) &&
target->inBranchRange(rel.type, src,
- t->getThunkTargetSym()->getVA(rel.addend)))
+ t->getThunkTargetSym()->getVA(-pcBias)))
return std::make_pair(t, false);
// No existing compatible Thunk in range, create a new one
// made no changes. If the target requires range extension thunks, currently
// ARM, then any future change in offset between caller and callee risks a
// relocation out of range error.
-bool ThunkCreator::createThunks(ArrayRef<OutputSection *> outputSections) {
+bool ThunkCreator::createThunks(uint32_t pass,
+ ArrayRef<OutputSection *> outputSections) {
+ this->pass = pass;
bool addressesChanged = false;
if (pass == 0 && target->getThunkSectionSpacing())
forEachInputSectionDescription(
outputSections, [&](OutputSection *os, InputSectionDescription *isd) {
for (InputSection *isec : isd->sections)
- for (Relocation &rel : isec->relocations) {
+ for (Relocation &rel : isec->relocs()) {
uint64_t src = isec->getVA(rel.offset);
// If we are a relocation to an existing Thunk, check if it is
// Merge all created synthetic ThunkSections back into OutputSection
mergeThunks(outputSections);
- ++pass;
return addressesChanged;
}
forEachInputSectionDescription(
outputSections, [&](OutputSection *os, InputSectionDescription *isd) {
for (InputSection *isec : isd->sections)
- for (Relocation &rel : isec->relocations)
+ for (Relocation &rel : isec->relocs())
if (rel.sym->type == llvm::ELF::STT_TLS && rel.expr == R_PLT_PC) {
needTlsSymbol = true;
return;
}
void elf::hexagonTLSSymbolUpdate(ArrayRef<OutputSection *> outputSections) {
- Symbol *sym = symtab->find("__tls_get_addr");
+ Symbol *sym = symtab.find("__tls_get_addr");
if (!sym)
return;
bool needEntry = true;
forEachInputSectionDescription(
outputSections, [&](OutputSection *os, InputSectionDescription *isd) {
for (InputSection *isec : isd->sections)
- for (Relocation &rel : isec->relocations)
+ for (Relocation &rel : isec->relocs())
if (rel.sym->type == llvm::ELF::STT_TLS && rel.expr == R_PLT_PC) {
if (needEntry) {
- addPltEntry(in.plt, in.gotPlt, in.relaPlt, target->pltRel,
+ sym->allocateAux();
+ addPltEntry(*in.plt, *in.gotPlt, *in.relaPlt, target->pltRel,
*sym);
needEntry = false;
}
});
}
-template void elf::scanRelocations<ELF32LE>(InputSectionBase &);
-template void elf::scanRelocations<ELF32BE>(InputSectionBase &);
-template void elf::scanRelocations<ELF64LE>(InputSectionBase &);
-template void elf::scanRelocations<ELF64BE>(InputSectionBase &);
-template void elf::reportUndefinedSymbols<ELF32LE>();
-template void elf::reportUndefinedSymbols<ELF32BE>();
-template void elf::reportUndefinedSymbols<ELF64LE>();
-template void elf::reportUndefinedSymbols<ELF64BE>();
+template void elf::scanRelocations<ELF32LE>();
+template void elf::scanRelocations<ELF32BE>();
+template void elf::scanRelocations<ELF64LE>();
+template void elf::scanRelocations<ELF64BE>();
#include "ScriptParser.h"
#include "Config.h"
#include "Driver.h"
-#include "InputSection.h"
+#include "InputFiles.h"
#include "LinkerScript.h"
#include "OutputSections.h"
#include "ScriptLexer.h"
+#include "SymbolTable.h"
#include "Symbols.h"
#include "Target.h"
-#include "lld/Common/Memory.h"
+#include "lld/Common/CommonLinkerContext.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/Path.h"
-#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/SaveAndRestore.h"
#include "llvm/Support/TimeProfiler.h"
#include <cassert>
#include <limits>
std::array<uint8_t, 4> readFill();
bool readSectionDirective(OutputSection *cmd, StringRef tok1, StringRef tok2);
void readSectionAddressType(OutputSection *cmd);
- OutputSection *readOverlaySectionDescription();
- OutputSection *readOutputSectionDescription(StringRef outSec);
- std::vector<BaseCommand *> readOverlay();
- std::vector<StringRef> readOutputSectionPhdrs();
+ OutputDesc *readOverlaySectionDescription();
+ OutputDesc *readOutputSectionDescription(StringRef outSec);
+ SmallVector<SectionCommand *, 0> readOverlay();
+ SmallVector<StringRef, 0> readOutputSectionPhdrs();
std::pair<uint64_t, uint64_t> readInputSectionFlags();
InputSectionDescription *readInputSectionDescription(StringRef tok);
StringMatcher readFilePatterns();
- std::vector<SectionPattern> readInputSectionsList();
+ SmallVector<SectionPattern, 0> readInputSectionsList();
InputSectionDescription *readInputSectionRules(StringRef filePattern,
uint64_t withFlags,
uint64_t withoutFlags);
Expr getPageSize();
Expr readMemoryAssignment(StringRef, StringRef, StringRef);
- std::pair<uint32_t, uint32_t> readMemoryAttributes();
+ void readMemoryAttributes(uint32_t &flags, uint32_t &invFlags,
+ uint32_t &negFlags, uint32_t &negInvFlags);
Expr combine(StringRef op, Expr l, Expr r);
Expr readExpr();
Expr readParenExpr();
// For parsing version script.
- std::vector<SymbolVersion> readVersionExtern();
+ SmallVector<SymbolVersion, 0> readVersionExtern();
void readAnonymousDeclaration();
void readVersionDeclaration(StringRef verStr);
- std::pair<std::vector<SymbolVersion>, std::vector<SymbolVersion>>
+ std::pair<SmallVector<SymbolVersion, 0>, SmallVector<SymbolVersion, 0>>
readSymbols();
- // True if a script being read is in a subdirectory specified by -sysroot.
+ // True if a script being read is in the --sysroot directory.
bool isUnderSysroot = false;
+ bool seenDataAlign = false;
+ bool seenRelroEnd = false;
+
// A set to detect an INCLUDE() cycle.
StringSet<> seen;
};
void ScriptParser::readDynamicList() {
expect("{");
- std::vector<SymbolVersion> locals;
- std::vector<SymbolVersion> globals;
+ SmallVector<SymbolVersion, 0> locals;
+ SmallVector<SymbolVersion, 0> globals;
std::tie(locals, globals) = readSymbols();
expect(";");
SmallString<128> pathData;
StringRef path = (config->sysroot + s).toStringRef(pathData);
if (sys::fs::exists(path))
- driver->addFile(saver.save(path), /*withLOption=*/false);
+ ctx.driver.addFile(saver().save(path), /*withLOption=*/false);
else
setError("cannot find " + s + " inside " + config->sysroot);
return;
if (s.startswith("/")) {
// Case 1: s is an absolute path. Just open it.
- driver->addFile(s, /*withLOption=*/false);
+ ctx.driver.addFile(s, /*withLOption=*/false);
} else if (s.startswith("=")) {
// Case 2: relative to the sysroot.
if (config->sysroot.empty())
- driver->addFile(s.substr(1), /*withLOption=*/false);
+ ctx.driver.addFile(s.substr(1), /*withLOption=*/false);
else
- driver->addFile(saver.save(config->sysroot + "/" + s.substr(1)),
- /*withLOption=*/false);
+ ctx.driver.addFile(saver().save(config->sysroot + "/" + s.substr(1)),
+ /*withLOption=*/false);
} else if (s.startswith("-l")) {
// Case 3: search in the list of library paths.
- driver->addLibrary(s.substr(2));
+ ctx.driver.addLibrary(s.substr(2));
} else {
// Case 4: s is a relative path. Search in the directory of the script file.
std::string filename = std::string(getCurrentMB().getBufferIdentifier());
SmallString<0> path(directory);
sys::path::append(path, s);
if (sys::fs::exists(path)) {
- driver->addFile(path, /*withLOption=*/false);
+ ctx.driver.addFile(path, /*withLOption=*/false);
return;
}
}
// Then search in the current working directory.
if (sys::fs::exists(s)) {
- driver->addFile(s, /*withLOption=*/false);
+ ctx.driver.addFile(s, /*withLOption=*/false);
} else {
// Finally, search in the list of library paths.
- if (Optional<std::string> path = findFromSearchPaths(s))
- driver->addFile(saver.save(*path), /*withLOption=*/true);
+ if (std::optional<std::string> path = findFromSearchPaths(s))
+ ctx.driver.addFile(saver().save(*path), /*withLOption=*/true);
else
setError("unable to find " + s);
}
expect("(");
StringRef tok = next();
if (config->entry.empty())
- config->entry = tok;
+ config->entry = unquote(tok);
expect(")");
}
return;
}
- if (Optional<std::string> path = searchScript(tok)) {
- if (Optional<MemoryBufferRef> mb = readFile(*path))
+ if (std::optional<std::string> path = searchScript(tok)) {
+ if (std::optional<MemoryBufferRef> mb = readFile(*path))
tokenize(*mb);
return;
}
static std::pair<ELFKind, uint16_t> parseBfdName(StringRef s) {
return StringSwitch<std::pair<ELFKind, uint16_t>>(s)
.Case("elf32-i386", {ELF32LEKind, EM_386})
+ .Case("elf32-avr", {ELF32LEKind, EM_AVR})
.Case("elf32-iamcu", {ELF32LEKind, EM_IAMCU})
.Case("elf32-littlearm", {ELF32LEKind, EM_ARM})
.Case("elf32-x86-64", {ELF32LEKind, EM_X86_64})
// sections that use the same virtual memory range and normally would trigger
// linker's sections sanity check failures.
// https://sourceware.org/binutils/docs/ld/Overlay-Description.html#Overlay-Description
-std::vector<BaseCommand *> ScriptParser::readOverlay() {
+SmallVector<SectionCommand *, 0> ScriptParser::readOverlay() {
// VA and LMA expressions are optional, though for simplicity of
// implementation we assume they are not. That is what OVERLAY was designed
// for first of all: to allow sections with overlapping VAs at different LMAs.
Expr lmaExpr = readParenExpr();
expect("{");
- std::vector<BaseCommand *> v;
+ SmallVector<SectionCommand *, 0> v;
OutputSection *prev = nullptr;
while (!errorCount() && !consume("}")) {
// VA is the same for all sections. The LMAs are consecutive in memory
// starting from the base load address specified.
- OutputSection *os = readOverlaySectionDescription();
- os->addrExpr = addrExpr;
+ OutputDesc *osd = readOverlaySectionDescription();
+ osd->osec.addrExpr = addrExpr;
if (prev)
- os->lmaExpr = [=] { return prev->getLMA() + prev->size; };
+ osd->osec.lmaExpr = [=] { return prev->getLMA() + prev->size; };
else
- os->lmaExpr = lmaExpr;
- v.push_back(os);
- prev = os;
+ osd->osec.lmaExpr = lmaExpr;
+ v.push_back(osd);
+ prev = &osd->osec;
}
// According to the specification, at the end of the overlay, the location
// Here we want to create the Dot assignment command to achieve that.
Expr moveDot = [=] {
uint64_t max = 0;
- for (BaseCommand *cmd : v)
- max = std::max(max, cast<OutputSection>(cmd)->size);
+ for (SectionCommand *cmd : v)
+ max = std::max(max, cast<OutputDesc>(cmd)->osec.size);
return addrExpr().getValue() + max;
};
v.push_back(make<SymbolAssignment>(".", moveDot, getCurrentLocation()));
void ScriptParser::readSections() {
expect("{");
- std::vector<BaseCommand *> v;
+ SmallVector<SectionCommand *, 0> v;
while (!errorCount() && !consume("}")) {
StringRef tok = next();
if (tok == "OVERLAY") {
- for (BaseCommand *cmd : readOverlay())
+ for (SectionCommand *cmd : readOverlay())
v.push_back(cmd);
continue;
} else if (tok == "INCLUDE") {
continue;
}
- if (BaseCommand *cmd = readAssignment(tok))
+ if (SectionCommand *cmd = readAssignment(tok))
v.push_back(cmd);
else
v.push_back(readOutputSectionDescription(tok));
}
+
+ // If DATA_SEGMENT_RELRO_END is absent, for sections after DATA_SEGMENT_ALIGN,
+ // the relro fields should be cleared.
+ if (!seenRelroEnd)
+ for (SectionCommand *cmd : v)
+ if (auto *osd = dyn_cast<OutputDesc>(cmd))
+ osd->osec.relro = false;
+
script->sectionCommands.insert(script->sectionCommands.end(), v.begin(),
v.end());
else if (!consume("BEFORE"))
setError("expected AFTER/BEFORE, but got '" + next() + "'");
StringRef where = next();
- std::vector<StringRef> names;
- for (BaseCommand *cmd : v)
- if (auto *os = dyn_cast<OutputSection>(cmd))
- names.push_back(os->name);
+ SmallVector<StringRef, 0> names;
+ for (SectionCommand *cmd : v)
+ if (auto *os = dyn_cast<OutputDesc>(cmd))
+ names.push_back(os->osec.name);
if (!names.empty())
script->insertCommands.push_back({std::move(names), isAfter, where});
}
// for --format. We recognize only /^elf/ and "binary" in the linker
// script as well.
expect("(");
- StringRef tok = next();
+ StringRef tok = unquote(next());
expect(")");
if (tok.startswith("elf"))
static int precedence(StringRef op) {
return StringSwitch<int>(op)
- .Cases("*", "/", "%", 8)
- .Cases("+", "-", 7)
- .Cases("<<", ">>", 6)
- .Cases("<", "<=", ">", ">=", "==", "!=", 5)
- .Case("&", 4)
- .Case("|", 3)
- .Case("&&", 2)
- .Case("||", 1)
+ .Cases("*", "/", "%", 10)
+ .Cases("+", "-", 9)
+ .Cases("<<", ">>", 8)
+ .Cases("<", "<=", ">", ">=", 7)
+ .Cases("==", "!=", 6)
+ .Case("&", 5)
+ .Case("|", 4)
+ .Case("&&", 3)
+ .Case("||", 2)
+ .Case("?", 1)
.Default(-1);
}
// is parsed as ".foo", ".bar" with "a.o", and ".baz" with "b.o".
// The semantics of that is section .foo in any file, section .bar in
// any file but a.o, and section .baz in any file but b.o.
-std::vector<SectionPattern> ScriptParser::readInputSectionsList() {
- std::vector<SectionPattern> ret;
+SmallVector<SectionPattern, 0> ScriptParser::readInputSectionsList() {
+ SmallVector<SectionPattern, 0> ret;
while (!errorCount() && peek() != ")") {
StringMatcher excludeFilePat;
if (consume("EXCLUDE_FILE")) {
while (!errorCount() && !consume(")")) {
SortSectionPolicy outer = readSortKind();
SortSectionPolicy inner = SortSectionPolicy::Default;
- std::vector<SectionPattern> v;
+ SmallVector<SectionPattern, 0> v;
if (outer != SortSectionPolicy::Default) {
expect("(");
inner = readSortKind();
};
}
+#define ECase(X) \
+ { #X, X }
+constexpr std::pair<const char *, unsigned> typeMap[] = {
+ ECase(SHT_PROGBITS), ECase(SHT_NOTE), ECase(SHT_NOBITS),
+ ECase(SHT_INIT_ARRAY), ECase(SHT_FINI_ARRAY), ECase(SHT_PREINIT_ARRAY),
+};
+#undef ECase
+
// Tries to read the special directive for an output section definition which
-// can be one of following: "(NOLOAD)", "(COPY)", "(INFO)" or "(OVERLAY)".
-// Tok1 and Tok2 are next 2 tokens peeked. See comment for readSectionAddressType below.
+// can be one of following: "(NOLOAD)", "(COPY)", "(INFO)", "(OVERLAY)", and
+// "(TYPE=<value>)".
+// Tok1 and Tok2 are next 2 tokens peeked. See comment for
+// readSectionAddressType below.
bool ScriptParser::readSectionDirective(OutputSection *cmd, StringRef tok1, StringRef tok2) {
if (tok1 != "(")
return false;
- if (tok2 != "NOLOAD" && tok2 != "COPY" && tok2 != "INFO" && tok2 != "OVERLAY")
+ if (tok2 != "NOLOAD" && tok2 != "COPY" && tok2 != "INFO" &&
+ tok2 != "OVERLAY" && tok2 != "TYPE")
return false;
expect("(");
if (consume("NOLOAD")) {
- cmd->noload = true;
cmd->type = SHT_NOBITS;
+ cmd->typeIsSet = true;
+ } else if (consume("TYPE")) {
+ expect("=");
+ StringRef value = peek();
+ auto it = llvm::find_if(typeMap, [=](auto e) { return e.first == value; });
+ if (it != std::end(typeMap)) {
+ // The value is a recognized literal SHT_*.
+ cmd->type = it->second;
+ skip();
+ } else if (value.startswith("SHT_")) {
+ setError("unknown section type " + value);
+ } else {
+ // Otherwise, read an expression.
+ cmd->type = readExpr()().getValue();
+ }
+ cmd->typeIsSet = true;
} else {
skip(); // This is "COPY", "INFO" or "OVERLAY".
cmd->nonAlloc = true;
// https://sourceware.org/binutils/docs/ld/Output-Section-Address.html
// https://sourceware.org/binutils/docs/ld/Output-Section-Type.html
void ScriptParser::readSectionAddressType(OutputSection *cmd) {
- if (readSectionDirective(cmd, peek(), peek2()))
+ // Temporarily set inExpr to support TYPE=<value> without spaces.
+ bool saved = std::exchange(inExpr, true);
+ bool isDirective = readSectionDirective(cmd, peek(), peek2());
+ inExpr = saved;
+ if (isDirective)
return;
cmd->addrExpr = readExpr();
};
}
-OutputSection *ScriptParser::readOverlaySectionDescription() {
- OutputSection *cmd =
- script->createOutputSection(next(), getCurrentLocation());
- cmd->inOverlay = true;
+OutputDesc *ScriptParser::readOverlaySectionDescription() {
+ OutputDesc *osd = script->createOutputSection(next(), getCurrentLocation());
+ osd->osec.inOverlay = true;
expect("{");
while (!errorCount() && !consume("}")) {
uint64_t withFlags = 0;
uint64_t withoutFlags = 0;
if (consume("INPUT_SECTION_FLAGS"))
std::tie(withFlags, withoutFlags) = readInputSectionFlags();
- cmd->sectionCommands.push_back(
+ osd->osec.commands.push_back(
readInputSectionRules(next(), withFlags, withoutFlags));
}
- return cmd;
+ return osd;
}
-OutputSection *ScriptParser::readOutputSectionDescription(StringRef outSec) {
- OutputSection *cmd =
- script->createOutputSection(outSec, getCurrentLocation());
+OutputDesc *ScriptParser::readOutputSectionDescription(StringRef outSec) {
+ OutputDesc *cmd = script->createOutputSection(outSec, getCurrentLocation());
+ OutputSection *osec = &cmd->osec;
+ // Maybe relro. Will reset to false if DATA_SEGMENT_RELRO_END is absent.
+ osec->relro = seenDataAlign && !seenRelroEnd;
size_t symbolsReferenced = script->referencedSymbols.size();
if (peek() != ":")
- readSectionAddressType(cmd);
+ readSectionAddressType(osec);
expect(":");
std::string location = getCurrentLocation();
if (consume("AT"))
- cmd->lmaExpr = readParenExpr();
+ osec->lmaExpr = readParenExpr();
if (consume("ALIGN"))
- cmd->alignExpr = checkAlignment(readParenExpr(), location);
+ osec->alignExpr = checkAlignment(readParenExpr(), location);
if (consume("SUBALIGN"))
- cmd->subalignExpr = checkAlignment(readParenExpr(), location);
+ osec->subalignExpr = checkAlignment(readParenExpr(), location);
// Parse constraints.
if (consume("ONLY_IF_RO"))
- cmd->constraint = ConstraintKind::ReadOnly;
+ osec->constraint = ConstraintKind::ReadOnly;
if (consume("ONLY_IF_RW"))
- cmd->constraint = ConstraintKind::ReadWrite;
+ osec->constraint = ConstraintKind::ReadWrite;
expect("{");
while (!errorCount() && !consume("}")) {
if (tok == ";") {
// Empty commands are allowed. Do nothing here.
} else if (SymbolAssignment *assign = readAssignment(tok)) {
- cmd->sectionCommands.push_back(assign);
+ osec->commands.push_back(assign);
} else if (ByteCommand *data = readByteCommand(tok)) {
- cmd->sectionCommands.push_back(data);
+ osec->commands.push_back(data);
} else if (tok == "CONSTRUCTORS") {
// CONSTRUCTORS is a keyword to make the linker recognize C++ ctors/dtors
// by name. This is for very old file formats such as ECOFF/XCOFF.
// https://sourceware.org/binutils/docs/ld/Output-Section-Data.html
if (peek() != "(")
setError("( expected, but got " + peek());
- cmd->filler = readFill();
+ osec->filler = readFill();
} else if (tok == "SORT") {
readSort();
} else if (tok == "INCLUDE") {
readInclude();
+ } else if (tok == "(" || tok == ")") {
+ setError("expected filename pattern");
} else if (peek() == "(") {
- cmd->sectionCommands.push_back(readInputSectionDescription(tok));
+ osec->commands.push_back(readInputSectionDescription(tok));
} else {
// We have a file name and no input sections description. It is not a
// commonly used syntax, but still acceptable. In that case, all sections
// case above.
auto *isd = make<InputSectionDescription>(tok);
isd->sectionPatterns.push_back({{}, StringMatcher("*")});
- cmd->sectionCommands.push_back(isd);
+ osec->commands.push_back(isd);
}
}
if (consume(">"))
- cmd->memoryRegionName = std::string(next());
+ osec->memoryRegionName = std::string(next());
if (consume("AT")) {
expect(">");
- cmd->lmaRegionName = std::string(next());
+ osec->lmaRegionName = std::string(next());
}
- if (cmd->lmaExpr && !cmd->lmaRegionName.empty())
+ if (osec->lmaExpr && !osec->lmaRegionName.empty())
error("section can't have both LMA and a load region");
- cmd->phdrs = readOutputSectionPhdrs();
+ osec->phdrs = readOutputSectionPhdrs();
if (peek() == "=" || peek().startswith("=")) {
inExpr = true;
consume("=");
- cmd->filler = readFill();
+ osec->filler = readFill();
inExpr = false;
}
consume(",");
if (script->referencedSymbols.size() > symbolsReferenced)
- cmd->expressionsUseSymbols = true;
+ osec->expressionsUseSymbols = true;
return cmd;
}
SymbolAssignment *ScriptParser::readProvideHidden(bool provide, bool hidden) {
expect("(");
- SymbolAssignment *cmd = readSymbolAssignment(next());
+ StringRef name = next(), eq = peek();
+ if (eq != "=") {
+ setError("= expected, but got " + next());
+ while (!atEOF() && next() != ")")
+ ;
+ return nullptr;
+ }
+ SymbolAssignment *cmd = readSymbolAssignment(name);
cmd->provide = provide;
cmd->hidden = hidden;
expect(")");
size_t oldPos = pos;
SymbolAssignment *cmd = nullptr;
- if (peek() == "=" || peek() == "+=")
+ const StringRef op = peek();
+ if (op.startswith("=")) {
+ // Support = followed by an expression without whitespace.
+ SaveAndRestore saved(inExpr, true);
+ cmd = readSymbolAssignment(tok);
+ } else if ((op.size() == 2 && op[1] == '=' && strchr("*/+-&|", op[0])) ||
+ op == "<<=" || op == ">>=") {
cmd = readSymbolAssignment(tok);
- else if (tok == "PROVIDE")
+ } else if (tok == "PROVIDE") {
+ SaveAndRestore saved(inExpr, true);
cmd = readProvideHidden(true, false);
- else if (tok == "HIDDEN")
+ } else if (tok == "HIDDEN") {
+ SaveAndRestore saved(inExpr, true);
cmd = readProvideHidden(false, true);
- else if (tok == "PROVIDE_HIDDEN")
+ } else if (tok == "PROVIDE_HIDDEN") {
+ SaveAndRestore saved(inExpr, true);
cmd = readProvideHidden(true, true);
+ }
if (cmd) {
cmd->commandString =
SymbolAssignment *ScriptParser::readSymbolAssignment(StringRef name) {
name = unquote(name);
StringRef op = next();
- assert(op == "=" || op == "+=");
+ assert(op == "=" || op == "*=" || op == "/=" || op == "+=" || op == "-=" ||
+ op == "&=" || op == "|=" || op == "<<=" || op == ">>=");
+ // Note: GNU ld does not support %= or ^=.
Expr e = readExpr();
- if (op == "+=") {
+ if (op != "=") {
std::string loc = getCurrentLocation();
- e = [=] { return add(script->getSymbolValue(name, loc), e()); };
+ e = [=, c = op[0]]() -> ExprValue {
+ ExprValue lhs = script->getSymbolValue(name, loc);
+ switch (c) {
+ case '*':
+ return lhs.getValue() * e().getValue();
+ case '/':
+ if (uint64_t rv = e().getValue())
+ return lhs.getValue() / rv;
+ error(loc + ": division by zero");
+ return 0;
+ case '+':
+ return add(lhs, e());
+ case '-':
+ return sub(lhs, e());
+ case '<':
+ return lhs.getValue() << e().getValue();
+ case '>':
+ return lhs.getValue() >> e().getValue();
+ case '&':
+ return lhs.getValue() & e().getValue();
+ case '|':
+ return lhs.getValue() | e().getValue();
+ default:
+ llvm_unreachable("");
+ }
+ };
}
return make<SymbolAssignment>(name, e, getCurrentLocation());
}
Expr ScriptParser::readExpr1(Expr lhs, int minPrec) {
while (!atEOF() && !errorCount()) {
// Read an operator and an expression.
- if (consume("?"))
- return readTernary(lhs);
StringRef op1 = peek();
if (precedence(op1) < minPrec)
break;
+ if (consume("?"))
+ return readTernary(lhs);
skip();
Expr rhs = readPrimary();
// Parses Tok as an integer. It recognizes hexadecimal (prefixed with
// "0x" or suffixed with "H") and decimal numbers. Decimal numbers may
// have "K" (Ki) or "M" (Mi) suffixes.
-static Optional<uint64_t> parseInt(StringRef tok) {
+static std::optional<uint64_t> parseInt(StringRef tok) {
// Hexadecimal
uint64_t val;
if (tok.startswith_insensitive("0x")) {
if (!to_integer(tok.substr(2), val, 16))
- return None;
+ return std::nullopt;
return val;
}
if (tok.endswith_insensitive("H")) {
if (!to_integer(tok.drop_back(), val, 16))
- return None;
+ return std::nullopt;
return val;
}
// Decimal
if (tok.endswith_insensitive("K")) {
if (!to_integer(tok.drop_back(), val, 10))
- return None;
+ return std::nullopt;
return val * 1024;
}
if (tok.endswith_insensitive("M")) {
if (!to_integer(tok.drop_back(), val, 10))
- return None;
+ return std::nullopt;
return val * 1024 * 1024;
}
if (!to_integer(tok, val, 10))
- return None;
+ return std::nullopt;
return val;
}
return make<ByteCommand>(e, size, commandString);
}
-static llvm::Optional<uint64_t> parseFlag(StringRef tok) {
- if (llvm::Optional<uint64_t> asInt = parseInt(tok))
+static std::optional<uint64_t> parseFlag(StringRef tok) {
+ if (std::optional<uint64_t> asInt = parseInt(tok))
return asInt;
#define CASE_ENT(enum) #enum, ELF::enum
- return StringSwitch<llvm::Optional<uint64_t>>(tok)
+ return StringSwitch<std::optional<uint64_t>>(tok)
.Case(CASE_ENT(SHF_WRITE))
.Case(CASE_ENT(SHF_ALLOC))
.Case(CASE_ENT(SHF_EXECINSTR))
.Case(CASE_ENT(SHF_COMPRESSED))
.Case(CASE_ENT(SHF_EXCLUDE))
.Case(CASE_ENT(SHF_ARM_PURECODE))
- .Default(None);
+ .Default(std::nullopt);
#undef CASE_ENT
}
while (!errorCount()) {
StringRef tok = unquote(next());
bool without = tok.consume_front("!");
- if (llvm::Optional<uint64_t> flag = parseFlag(tok)) {
+ if (std::optional<uint64_t> flag = parseFlag(tok)) {
if (without)
withoutFlags |= *flag;
else
return tok;
}
-static void checkIfExists(OutputSection *cmd, StringRef location) {
- if (cmd->location.empty() && script->errorOnMissingSection)
- error(location + ": undefined section " + cmd->name);
+static void checkIfExists(const OutputSection &osec, StringRef location) {
+ if (osec.location.empty() && script->errorOnMissingSection)
+ error(location + ": undefined section " + osec.name);
}
static bool isValidSymbolName(StringRef s) {
}
if (tok == "ADDR") {
StringRef name = readParenLiteral();
- OutputSection *sec = script->getOrCreateOutputSection(name);
- sec->usedInExpression = true;
+ OutputSection *osec = &script->getOrCreateOutputSection(name)->osec;
+ osec->usedInExpression = true;
return [=]() -> ExprValue {
- checkIfExists(sec, location);
- return {sec, false, 0, location};
+ checkIfExists(*osec, location);
+ return {osec, false, 0, location};
};
}
if (tok == "ALIGN") {
Expr e = readExpr();
if (consume(")")) {
e = checkAlignment(e, location);
- return [=] { return alignTo(script->getDot(), e().getValue()); };
+ return [=] { return alignToPowerOf2(script->getDot(), e().getValue()); };
}
expect(",");
Expr e2 = checkAlignment(readExpr(), location);
}
if (tok == "ALIGNOF") {
StringRef name = readParenLiteral();
- OutputSection *cmd = script->getOrCreateOutputSection(name);
+ OutputSection *osec = &script->getOrCreateOutputSection(name)->osec;
return [=] {
- checkIfExists(cmd, location);
- return cmd->alignment;
+ checkIfExists(*osec, location);
+ return osec->addralign;
};
}
if (tok == "ASSERT")
expect(",");
readExpr();
expect(")");
+ seenDataAlign = true;
return [=] {
- return alignTo(script->getDot(), std::max((uint64_t)1, e().getValue()));
+ uint64_t align = std::max(uint64_t(1), e().getValue());
+ return (script->getDot() + align - 1) & -align;
};
}
if (tok == "DATA_SEGMENT_END") {
expect(",");
readExpr();
expect(")");
+ seenRelroEnd = true;
Expr e = getPageSize();
- return [=] { return alignTo(script->getDot(), e().getValue()); };
+ return [=] { return alignToPowerOf2(script->getDot(), e().getValue()); };
}
if (tok == "DEFINED") {
StringRef name = unquote(readParenLiteral());
return [=] {
- Symbol *b = symtab->find(name);
+ Symbol *b = symtab.find(name);
return (b && b->isDefined()) ? 1 : 0;
};
}
}
if (tok == "LOADADDR") {
StringRef name = readParenLiteral();
- OutputSection *cmd = script->getOrCreateOutputSection(name);
- cmd->usedInExpression = true;
+ OutputSection *osec = &script->getOrCreateOutputSection(name)->osec;
+ osec->usedInExpression = true;
return [=] {
- checkIfExists(cmd, location);
- return cmd->getLMA();
+ checkIfExists(*osec, location);
+ return osec->getLMA();
};
}
if (tok == "LOG2CEIL") {
}
if (tok == "SIZEOF") {
StringRef name = readParenLiteral();
- OutputSection *cmd = script->getOrCreateOutputSection(name);
+ OutputSection *cmd = &script->getOrCreateOutputSection(name)->osec;
// Linker script does not create an output section if its content is empty.
// We want to allow SIZEOF(.foo) where .foo is a section which happened to
// be empty.
return [=] { return script->getSymbolValue(tok, location); };
// Tok is a literal number.
- if (Optional<uint64_t> val = parseInt(tok))
+ if (std::optional<uint64_t> val = parseInt(tok))
return [=] { return *val; };
// Tok is a symbol name.
- tok = unquote(tok);
- if (!isValidSymbolName(tok))
+ if (tok.startswith("\""))
+ tok = unquote(tok);
+ else if (!isValidSymbolName(tok))
setError("malformed number: " + tok);
script->referencedSymbols.push_back(tok);
return [=] { return script->getSymbolValue(tok, location); };
return e;
}
-std::vector<StringRef> ScriptParser::readOutputSectionPhdrs() {
- std::vector<StringRef> phdrs;
+SmallVector<StringRef, 0> ScriptParser::readOutputSectionPhdrs() {
+ SmallVector<StringRef, 0> phdrs;
while (!errorCount() && peek().startswith(":")) {
StringRef tok = next();
phdrs.push_back((tok.size() == 1) ? next() : tok.substr(1));
// name of a program header type or a constant (e.g. "0x3").
unsigned ScriptParser::readPhdrType() {
StringRef tok = next();
- if (Optional<uint64_t> val = parseInt(tok))
+ if (std::optional<uint64_t> val = parseInt(tok))
return *val;
unsigned ret = StringSwitch<unsigned>(tok)
// Reads an anonymous version declaration.
void ScriptParser::readAnonymousDeclaration() {
- std::vector<SymbolVersion> locals;
- std::vector<SymbolVersion> globals;
+ SmallVector<SymbolVersion, 0> locals;
+ SmallVector<SymbolVersion, 0> globals;
std::tie(locals, globals) = readSymbols();
for (const SymbolVersion &pat : locals)
config->versionDefinitions[VER_NDX_LOCAL].localPatterns.push_back(pat);
// e.g. "VerStr { global: foo; bar; local: *; };".
void ScriptParser::readVersionDeclaration(StringRef verStr) {
// Read a symbol list.
- std::vector<SymbolVersion> locals;
- std::vector<SymbolVersion> globals;
+ SmallVector<SymbolVersion, 0> locals;
+ SmallVector<SymbolVersion, 0> globals;
std::tie(locals, globals) = readSymbols();
// Create a new version definition and add that to the global symbols.
}
// Reads a list of symbols, e.g. "{ global: foo; bar; local: *; };".
-std::pair<std::vector<SymbolVersion>, std::vector<SymbolVersion>>
+std::pair<SmallVector<SymbolVersion, 0>, SmallVector<SymbolVersion, 0>>
ScriptParser::readSymbols() {
- std::vector<SymbolVersion> locals;
- std::vector<SymbolVersion> globals;
- std::vector<SymbolVersion> *v = &globals;
+ SmallVector<SymbolVersion, 0> locals;
+ SmallVector<SymbolVersion, 0> globals;
+ SmallVector<SymbolVersion, 0> *v = &globals;
while (!errorCount()) {
if (consume("}"))
}
if (consume("extern")) {
- std::vector<SymbolVersion> ext = readVersionExtern();
+ SmallVector<SymbolVersion, 0> ext = readVersionExtern();
v->insert(v->end(), ext.begin(), ext.end());
} else {
StringRef tok = next();
//
// The last semicolon is optional. E.g. this is OK:
// "extern "C++" { ns::*; "f(int, double)" };"
-std::vector<SymbolVersion> ScriptParser::readVersionExtern() {
+SmallVector<SymbolVersion, 0> ScriptParser::readVersionExtern() {
StringRef tok = next();
bool isCXX = tok == "\"C++\"";
if (!isCXX && tok != "\"C\"")
setError("Unknown language");
expect("{");
- std::vector<SymbolVersion> ret;
+ SmallVector<SymbolVersion, 0> ret;
while (!errorCount() && peek() != "}") {
StringRef tok = next();
ret.push_back(
}
uint32_t flags = 0;
+ uint32_t invFlags = 0;
uint32_t negFlags = 0;
+ uint32_t negInvFlags = 0;
if (consume("(")) {
- std::tie(flags, negFlags) = readMemoryAttributes();
+ readMemoryAttributes(flags, invFlags, negFlags, negInvFlags);
expect(")");
}
expect(":");
Expr length = readMemoryAssignment("LENGTH", "len", "l");
// Add the memory region to the region map.
- MemoryRegion *mr = make<MemoryRegion>(tok, origin, length, flags, negFlags);
+ MemoryRegion *mr = make<MemoryRegion>(tok, origin, length, flags, invFlags,
+ negFlags, negInvFlags);
if (!script->memoryRegions.insert({tok, mr}).second)
setError("region '" + tok + "' already defined");
}
// This function parses the attributes used to match against section
// flags when placing output sections in a memory region. These flags
// are only used when an explicit memory region name is not used.
-std::pair<uint32_t, uint32_t> ScriptParser::readMemoryAttributes() {
- uint32_t flags = 0;
- uint32_t negFlags = 0;
+void ScriptParser::readMemoryAttributes(uint32_t &flags, uint32_t &invFlags,
+ uint32_t &negFlags,
+ uint32_t &negInvFlags) {
bool invert = false;
for (char c : next().lower()) {
- uint32_t flag = 0;
- if (c == '!')
+ if (c == '!') {
invert = !invert;
- else if (c == 'w')
- flag = SHF_WRITE;
+ std::swap(flags, negFlags);
+ std::swap(invFlags, negInvFlags);
+ continue;
+ }
+ if (c == 'w')
+ flags |= SHF_WRITE;
else if (c == 'x')
- flag = SHF_EXECINSTR;
+ flags |= SHF_EXECINSTR;
else if (c == 'a')
- flag = SHF_ALLOC;
- else if (c != 'r')
+ flags |= SHF_ALLOC;
+ else if (c == 'r')
+ invFlags |= SHF_WRITE;
+ else
setError("invalid memory region attribute");
+ }
- if (invert)
- negFlags |= flag;
- else
- flags |= flag;
+ if (invert) {
+ std::swap(flags, negFlags);
+ std::swap(invFlags, negInvFlags);
}
- return {flags, negFlags};
}
void elf::readLinkerScript(MemoryBufferRef mb) {
#include "SymbolTable.h"
#include "Config.h"
-#include "LinkerScript.h"
+#include "InputFiles.h"
#include "Symbols.h"
-#include "SyntheticSections.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Strings.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/Demangle/Demangle.h"
using namespace llvm;
using namespace llvm::object;
using namespace lld;
using namespace lld::elf;
-SymbolTable *elf::symtab;
+SymbolTable elf::symtab;
void SymbolTable::wrap(Symbol *sym, Symbol *real, Symbol *wrap) {
- // Swap symbols as instructed by -wrap.
+ // Redirect __real_foo to the original foo and foo to the original __wrap_foo.
int &idx1 = symMap[CachedHashStringRef(sym->getName())];
int &idx2 = symMap[CachedHashStringRef(real->getName())];
int &idx3 = symMap[CachedHashStringRef(wrap->getName())];
idx2 = idx1;
idx1 = idx3;
- if (real->exportDynamic)
- sym->exportDynamic = true;
- if (!real->isUsedInRegularObj && sym->isUndefined())
+ // Propagate symbol usage information to the redirected symbols.
+ if (sym->isUsedInRegularObj)
+ wrap->isUsedInRegularObj = true;
+ if (real->isUsedInRegularObj)
+ sym->isUsedInRegularObj = true;
+ else if (!sym->isDefined())
+ // Now that all references to sym have been redirected to wrap, if there are
+ // no references to real (which has been redirected to sym), we only need to
+ // keep sym if it was defined, otherwise it's unused and can be dropped.
sym->isUsedInRegularObj = false;
// Now renaming is complete, and no one refers to real. We drop real from
// Since this is a hot path, the following string search code is
// optimized for speed. StringRef::find(char) is much faster than
// StringRef::find(StringRef).
+ StringRef stem = name;
size_t pos = name.find('@');
if (pos != StringRef::npos && pos + 1 < name.size() && name[pos + 1] == '@')
- name = name.take_front(pos);
-
- auto p = symMap.insert({CachedHashStringRef(name), (int)symVector.size()});
- int &symIndex = p.first->second;
- bool isNew = p.second;
-
- if (!isNew)
- return symVector[symIndex];
+ stem = name.take_front(pos);
+
+ auto p = symMap.insert({CachedHashStringRef(stem), (int)symVector.size()});
+ if (!p.second) {
+ Symbol *sym = symVector[p.first->second];
+ if (stem.size() != name.size()) {
+ sym->setName(name);
+ sym->hasVersionSuffix = true;
+ }
+ return sym;
+ }
Symbol *sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
symVector.push_back(sym);
- // *sym was not initialized by a constructor. Fields that may get referenced
- // when it is a placeholder must be initialized here.
+ // *sym was not initialized by a constructor. Initialize all Symbol fields.
+ memset(sym, 0, sizeof(Symbol));
sym->setName(name);
- sym->symbolKind = Symbol::PlaceholderKind;
- sym->versionId = VER_NDX_GLOBAL;
- sym->visibility = STV_DEFAULT;
- sym->isUsedInRegularObj = false;
- sym->exportDynamic = false;
- sym->inDynamicList = false;
- sym->canInline = true;
- sym->referenced = false;
- sym->traced = false;
- sym->gwarn = false;
- sym->scriptDefined = false;
sym->partition = 1;
+ sym->verdefIndex = -1;
+ sym->gwarn = false;
+ sym->versionId = VER_NDX_GLOBAL;
+ if (pos != StringRef::npos)
+ sym->hasVersionSuffix = true;
return sym;
}
-Symbol *SymbolTable::addSymbol(const Symbol &newSym) {
+// This variant of addSymbol is used by BinaryFile::parse to check duplicate
+// symbol errors.
+Symbol *SymbolTable::addAndCheckDuplicate(const Defined &newSym) {
Symbol *sym = insert(newSym.getName());
+ if (sym->isDefined())
+ sym->checkDuplicate(newSym);
sym->resolve(newSym);
+ sym->isUsedInRegularObj = true;
return sym;
}
auto it = symMap.find(CachedHashStringRef(name));
if (it == symMap.end())
return nullptr;
- Symbol *sym = symVector[it->second];
- if (sym->isPlaceholder())
- return nullptr;
- return sym;
+ return symVector[it->second];
}
// A version script/dynamic list is only meaningful for a Defined symbol.
// A CommonSymbol will be converted to a Defined in replaceCommonSymbols().
-// A lazy symbol may be made Defined if an LTO libcall fetches it.
+// A lazy symbol may be made Defined if an LTO libcall extracts it.
static bool canBeVersioned(const Symbol &sym) {
return sym.isDefined() || sym.isCommon() || sym.isLazy();
}
// other than trying to match a pattern against all demangled symbols.
// So, if "extern C++" feature is used, we need to demangle all known
// symbols.
-StringMap<std::vector<Symbol *>> &SymbolTable::getDemangledSyms() {
+StringMap<SmallVector<Symbol *, 0>> &SymbolTable::getDemangledSyms() {
if (!demangledSyms) {
demangledSyms.emplace();
std::string demangled;
StringRef name = sym->getName();
size_t pos = name.find('@');
if (pos == std::string::npos)
- demangled = demangleItanium(name);
+ demangled = demangle(name.str());
else if (pos + 1 == name.size() || name[pos + 1] == '@')
- demangled = demangleItanium(name.substr(0, pos));
+ demangled = demangle(name.substr(0, pos).str());
else
demangled =
- (demangleItanium(name.substr(0, pos)) + name.substr(pos)).str();
+ (demangle(name.substr(0, pos).str()) + name.substr(pos)).str();
(*demangledSyms)[demangled].push_back(sym);
}
}
return *demangledSyms;
}
-std::vector<Symbol *> SymbolTable::findByVersion(SymbolVersion ver) {
+SmallVector<Symbol *, 0> SymbolTable::findByVersion(SymbolVersion ver) {
if (ver.isExternCpp)
return getDemangledSyms().lookup(ver.name);
if (Symbol *sym = find(ver.name))
return {};
}
-std::vector<Symbol *> SymbolTable::findAllByVersion(SymbolVersion ver,
- bool includeNonDefault) {
- std::vector<Symbol *> res;
+SmallVector<Symbol *, 0> SymbolTable::findAllByVersion(SymbolVersion ver,
+ bool includeNonDefault) {
+ SmallVector<Symbol *, 0> res;
SingleStringMatcher m(ver.name);
auto check = [&](StringRef name) {
size_t pos = name.find('@');
return res;
}
-// Handles -dynamic-list.
void SymbolTable::handleDynamicList() {
+ SmallVector<Symbol *, 0> syms;
for (SymbolVersion &ver : config->dynamicList) {
- std::vector<Symbol *> syms;
if (ver.hasWildcard)
syms = findAllByVersion(ver, /*includeNonDefault=*/true);
else
StringRef versionName,
bool includeNonDefault) {
// Get a list of symbols which we need to assign the version to.
- std::vector<Symbol *> syms = findByVersion(ver);
+ SmallVector<Symbol *, 0> syms = findByVersion(ver);
auto getName = [](uint16_t ver) -> std::string {
if (ver == VER_NDX_LOCAL)
// If the version has not been assigned, verdefIndex is -1. Use an arbitrary
// number (0) to indicate the version has been assigned.
- if (sym->verdefIndex == UINT32_C(-1)) {
+ if (sym->verdefIndex == uint16_t(-1)) {
sym->verdefIndex = 0;
sym->versionId = versionId;
}
// so we set a version to a symbol only if no version has been assigned
// to the symbol. This behavior is compatible with GNU.
for (Symbol *sym : findAllByVersion(ver, includeNonDefault))
- if (sym->verdefIndex == UINT32_C(-1)) {
+ if (sym->verdefIndex == uint16_t(-1)) {
sym->verdefIndex = 0;
sym->versionId = versionId;
}
SmallString<128> buf;
// First, we assign versions to exact matching symbols,
// i.e. version definitions not containing any glob meta-characters.
- std::vector<Symbol *> syms;
for (VersionDefinition &v : config->versionDefinitions) {
auto assignExact = [&](SymbolVersion pat, uint16_t id, StringRef ver) {
bool found =
pat.isExternCpp, /*hasWildCard=*/false},
id, ver, /*includeNonDefault=*/true);
if (!found && !config->undefinedVersion)
- errorOrWarn("version script assignment of '" + ver + "' to symbol '" +
- pat.name + "' failed: symbol not defined");
+ warn("version script assignment of '" + ver + "' to symbol '" +
+ pat.name + "' failed: symbol not defined");
};
for (SymbolVersion &pat : v.nonLocalPatterns)
if (!pat.hasWildcard)
// can contain versions in the form of <name>@<version>.
// Let them parse and update their names to exclude version suffix.
for (Symbol *sym : symVector)
- sym->parseSymbolVersion();
+ if (sym->hasVersionSuffix)
+ sym->parseSymbolVersion();
// isPreemptible is false at this point. To correctly compute the binding of a
// Defined (which is used by includeInDynsym()), we need to know if it is
//===----------------------------------------------------------------------===//
#include "Symbols.h"
+#include "Driver.h"
#include "InputFiles.h"
#include "InputSection.h"
#include "OutputSections.h"
#include "Target.h"
#include "Writer.h"
#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Strings.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/Path.h"
+#include "llvm/Demangle/Demangle.h"
+#include "llvm/Support/Compiler.h"
#include <cstring>
using namespace llvm;
using namespace lld;
using namespace lld::elf;
+static_assert(sizeof(SymbolUnion) <= 64, "SymbolUnion too large");
+
+template <typename T> struct AssertSymbol {
+ static_assert(std::is_trivially_destructible<T>(),
+ "Symbol types must be trivially destructible");
+ static_assert(sizeof(T) <= sizeof(SymbolUnion), "SymbolUnion too small");
+ static_assert(alignof(T) <= alignof(SymbolUnion),
+ "SymbolUnion not aligned enough");
+};
+
+LLVM_ATTRIBUTE_UNUSED static inline void assertSymbols() {
+ AssertSymbol<Defined>();
+ AssertSymbol<CommonSymbol>();
+ AssertSymbol<Undefined>();
+ AssertSymbol<SharedSymbol>();
+ AssertSymbol<LazyObject>();
+}
+
// Returns a symbol for an error message.
-static std::string demangle(StringRef symName) {
+static std::string maybeDemangleSymbol(StringRef symName) {
if (elf::config->demangle)
- return demangleItanium(symName);
- return std::string(symName);
+ return demangle(symName.str());
+ return symName.str();
}
std::string lld::toString(const elf::Symbol &sym) {
StringRef name = sym.getName();
- std::string ret = demangle(name);
+ std::string ret = maybeDemangleSymbol(name);
const char *suffix = sym.getVersionSuffix();
if (*suffix == '@')
return ret;
}
-std::string lld::toELFString(const Archive::Symbol &b) {
- return demangle(b.getName());
-}
-
Defined *ElfSym::bss;
Defined *ElfSym::data;
Defined *ElfSym::etext1;
Defined *ElfSym::mipsLocalGp;
Defined *ElfSym::relaIpltStart;
Defined *ElfSym::relaIpltEnd;
-Defined *ElfSym::riscvGlobalPointer;
Defined *ElfSym::tlsModuleBase;
-DenseMap<const Symbol *, std::pair<const InputFile *, const InputFile *>>
- elf::backwardReferences;
+SmallVector<SymbolAux, 0> elf::symAux;
-static uint64_t getSymVA(const Symbol &sym, int64_t &addend) {
+static uint64_t getSymVA(const Symbol &sym, int64_t addend) {
switch (sym.kind()) {
case Symbol::DefinedKind: {
auto &d = cast<Defined>(sym);
return d.value;
assert(isec != &InputSection::discarded);
- isec = isec->repl;
uint64_t offset = d.value;
// To make this work, we incorporate the addend into the section
// offset (and zero out the addend for later processing) so that
// we find the right object in the section.
- if (d.isSection()) {
+ if (d.isSection())
offset += addend;
- addend = 0;
- }
// In the typical case, this is actually very simple and boils
// down to adding together 3 numbers:
// line (and how they get built), then you have a pretty good
// understanding of the linker.
uint64_t va = isec->getVA(offset);
+ if (d.isSection())
+ va -= addend;
// MIPS relocatable files can mix regular and microMIPS code.
// Linker needs to distinguish such code. To do so microMIPS
// field etc) do the same trick as compiler uses to mark microMIPS
// for CPU - set the less-significant bit.
if (config->emachine == EM_MIPS && isMicroMips() &&
- ((sym.stOther & STO_MIPS_MICROMIPS) || sym.needsPltAddr))
+ ((sym.stOther & STO_MIPS_MICROMIPS) || sym.hasFlag(NEEDS_COPY)))
va |= 1;
if (d.isTls() && !config->relocatable) {
case Symbol::SharedKind:
case Symbol::UndefinedKind:
return 0;
- case Symbol::LazyArchiveKind:
case Symbol::LazyObjectKind:
- assert(sym.isUsedInRegularObj && "lazy symbol reached writer");
- return 0;
+ llvm_unreachable("lazy symbol reached writer");
case Symbol::CommonKind:
llvm_unreachable("common symbol reached writer");
case Symbol::PlaceholderKind:
}
uint64_t Symbol::getVA(int64_t addend) const {
- uint64_t outVA = getSymVA(*this, addend);
- return outVA + addend;
+ return getSymVA(*this, addend) + addend;
}
uint64_t Symbol::getGotVA() const {
}
uint64_t Symbol::getGotOffset() const {
- return gotIndex * target->gotEntrySize;
+ return getGotIdx() * target->gotEntrySize;
}
uint64_t Symbol::getGotPltVA() const {
uint64_t Symbol::getGotPltOffset() const {
if (isInIplt)
- return pltIndex * target->gotEntrySize;
- return (pltIndex + target->gotPltHeaderEntriesNum) * target->gotEntrySize;
+ return getPltIdx() * target->gotEntrySize;
+ return (getPltIdx() + target->gotPltHeaderEntriesNum) * target->gotEntrySize;
}
uint64_t Symbol::getPltVA() const {
uint64_t outVA = isInIplt
- ? in.iplt->getVA() + pltIndex * target->ipltEntrySize
+ ? in.iplt->getVA() + getPltIdx() * target->ipltEntrySize
: in.plt->getVA() + in.plt->headerSize +
- pltIndex * target->pltEntrySize;
+ getPltIdx() * target->pltEntrySize;
// While linking microMIPS code PLT code are always microMIPS
// code. Set the less-significant bit to track that fact.
OutputSection *Symbol::getOutputSection() const {
if (auto *s = dyn_cast<Defined>(this)) {
if (auto *sec = s->section)
- return sec->repl->getOutputSection();
+ return sec->getOutputSection();
return nullptr;
}
return nullptr;
return;
StringRef s = getName();
size_t pos = s.find('@');
- if (pos == 0 || pos == StringRef::npos)
+ if (pos == StringRef::npos)
return;
StringRef verstr = s.substr(pos + 1);
- if (verstr.empty())
- return;
// Truncate the symbol name so that it doesn't include the version string.
nameSize = pos;
+ if (verstr.empty())
+ return;
+
// If this is not in this DSO, it is not a definition.
if (!isDefined())
return;
verstr);
}
-void Symbol::fetch() const {
- if (auto *sym = dyn_cast<LazyArchive>(this)) {
- cast<ArchiveFile>(sym->file)->fetch(sym->sym);
- return;
- }
-
- if (auto *sym = dyn_cast<LazyObject>(this)) {
- dyn_cast<LazyObjFile>(sym->file)->fetch();
- return;
+void Symbol::extract() const {
+ if (file->lazy) {
+ file->lazy = false;
+ parseFile(file);
}
-
- llvm_unreachable("Symbol::fetch() is called on a non-lazy symbol");
-}
-
-MemoryBufferRef LazyArchive::getMemberBuffer() {
- Archive::Child c =
- CHECK(sym.getMember(),
- "could not get the member for symbol " + toELFString(sym));
-
- return CHECK(c.getMemoryBufferRef(),
- "could not get the buffer for the member defining symbol " +
- toELFString(sym));
}
uint8_t Symbol::computeBinding() const {
- if (config->relocatable)
- return binding;
- if ((visibility != STV_DEFAULT && visibility != STV_PROTECTED) ||
- (versionId == VER_NDX_LOCAL && !isLazy()))
+ auto v = visibility();
+ if ((v != STV_DEFAULT && v != STV_PROTECTED) || versionId == VER_NDX_LOCAL)
return STB_LOCAL;
- if (!config->gnuUnique && binding == STB_GNU_UNIQUE)
+ if (binding == STB_GNU_UNIQUE && !config->gnuUnique)
return STB_GLOBAL;
return binding;
}
bool Symbol::includeInDynsym() const {
- if (!config->hasDynSymTab)
- return false;
if (computeBinding() == STB_LOCAL)
return false;
if (!isDefined() && !isCommon())
// expects undefined weak symbols not to exist in .dynsym, e.g.
// __pthread_mutex_lock reference in _dl_add_to_namespace_list,
// __pthread_initialize_minimal reference in csu/libc-start.c.
- return !(config->noDynamicLinker && isUndefWeak());
+ return !(isUndefWeak() && config->noDynamicLinker);
return exportDynamic || inDynamicList;
}
// Print out a log message for --trace-symbol.
-void elf::printTraceSymbol(const Symbol *sym) {
+void elf::printTraceSymbol(const Symbol &sym, StringRef name) {
std::string s;
- if (sym->isUndefined())
+ if (sym.isUndefined())
s = ": reference to ";
- else if (sym->isLazy())
+ else if (sym.isLazy())
s = ": lazy definition of ";
- else if (sym->isShared())
+ else if (sym.isShared())
s = ": shared definition of ";
- else if (sym->isCommon())
+ else if (sym.isCommon())
s = ": common definition of ";
else
s = ": definition of ";
- message(toString(sym->file) + s + sym->getName());
+ message(toString(sym.file) + s + name);
+}
+
+static void recordWhyExtract(const InputFile *reference,
+ const InputFile &extracted, const Symbol &sym) {
+ ctx.whyExtractRecords.emplace_back(toString(reference), &extracted, sym);
}
void elf::maybeWarnUnorderableSymbol(const Symbol *sym) {
report(": unable to order absolute symbol: ");
else if (d && isa<OutputSection>(d->section))
report(": unable to order synthetic symbol: ");
- else if (d && !d->section->repl->isLive())
+ else if (d && !d->section->isLive())
report(": unable to order discarded symbol: ");
}
// Returns true if a symbol can be replaced at load-time by a symbol
// with the same name defined in other ELF executable or DSO.
bool elf::computeIsPreemptible(const Symbol &sym) {
- assert(!sym.isLocal());
+ assert(!sym.isLocal() || sym.isPlaceholder());
// Only symbols with default visibility that appear in dynsym can be
// preempted. Symbols with protected visibility cannot be preempted.
- if (!sym.includeInDynsym() || sym.visibility != STV_DEFAULT)
+ if (!sym.includeInDynsym() || sym.visibility() != STV_DEFAULT)
return false;
// At this point copy relocations have not been created yet, so any
return true;
}
-void elf::reportBackrefs() {
- for (auto &it : backwardReferences) {
- const Symbol &sym = *it.first;
- std::string to = toString(it.second.second);
- // Some libraries have known problems and can cause noise. Filter them out
- // with --warn-backrefs-exclude=. to may look like *.o or *.a(*.o).
- bool exclude = false;
- for (const llvm::GlobPattern &pat : config->warnBackrefsExclude)
- if (pat.match(to)) {
- exclude = true;
- break;
- }
- if (!exclude)
- warn("backward reference detected: " + sym.getName() + " in " +
- toString(it.second.first) + " refers to " + to);
- }
-}
-
-static uint8_t getMinVisibility(uint8_t va, uint8_t vb) {
- if (va == STV_DEFAULT)
- return vb;
- if (vb == STV_DEFAULT)
- return va;
- return std::min(va, vb);
-}
-
// Merge symbol properties.
//
// When we have many symbols of the same name, we choose one of them,
void Symbol::mergeProperties(const Symbol &other) {
if (other.exportDynamic)
exportDynamic = true;
- if (other.isUsedInRegularObj)
- isUsedInRegularObj = true;
// DSO symbols do not affect visibility in the output.
- if (!other.isShared())
- visibility = getMinVisibility(visibility, other.visibility);
-}
-
-void Symbol::resolve(const Symbol &other) {
- mergeProperties(other);
-
- if (isPlaceholder()) {
- replace(other);
- return;
- }
-
- switch (other.kind()) {
- case Symbol::UndefinedKind:
- resolveUndefined(cast<Undefined>(other));
- break;
- case Symbol::CommonKind:
- resolveCommon(cast<CommonSymbol>(other));
- break;
- case Symbol::DefinedKind:
- resolveDefined(cast<Defined>(other));
- break;
- case Symbol::LazyArchiveKind:
- resolveLazy(cast<LazyArchive>(other));
- break;
- case Symbol::LazyObjectKind:
- resolveLazy(cast<LazyObject>(other));
- break;
- case Symbol::SharedKind:
- resolveShared(cast<SharedSymbol>(other));
- break;
- case Symbol::PlaceholderKind:
- llvm_unreachable("bad symbol kind");
+ if (!other.isShared() && other.visibility() != STV_DEFAULT) {
+ uint8_t v = visibility(), ov = other.visibility();
+ setVisibility(v == STV_DEFAULT ? ov : std::min(v, ov));
}
}
-void Symbol::resolveUndefined(const Undefined &other) {
+void Symbol::resolve(const Undefined &other) {
+ if (other.visibility() != STV_DEFAULT) {
+ uint8_t v = visibility(), ov = other.visibility();
+ setVisibility(v == STV_DEFAULT ? ov : std::min(v, ov));
+ }
// An undefined symbol with non default visibility must be satisfied
// in the same DSO.
//
// If this is a non-weak defined symbol in a discarded section, override the
// existing undefined symbol for better error message later.
- if ((isShared() && other.visibility != STV_DEFAULT) ||
+ if (isPlaceholder() || (isShared() && other.visibility() != STV_DEFAULT) ||
(isUndefined() && other.binding != STB_WEAK && other.discardedSecIdx)) {
- replace(other);
+ other.overwrite(*this);
return;
}
if (traced)
- printTraceSymbol(&other);
+ printTraceSymbol(other, getName());
if (isLazy()) {
- // An undefined weak will not fetch archive members. See comment on Lazy in
- // Symbols.h for the details.
+ // An undefined weak will not extract archive members. See comment on Lazy
+ // in Symbols.h for the details.
if (other.binding == STB_WEAK) {
binding = STB_WEAK;
type = other.type;
// Do extra check for --warn-backrefs.
//
// --warn-backrefs is an option to prevent an undefined reference from
- // fetching an archive member written earlier in the command line. It can be
- // used to keep compatibility with GNU linkers to some degree.
- // I'll explain the feature and why you may find it useful in this comment.
+ // extracting an archive member written earlier in the command line. It can
+ // be used to keep compatibility with GNU linkers to some degree. I'll
+ // explain the feature and why you may find it useful in this comment.
//
// lld's symbol resolution semantics is more relaxed than traditional Unix
// linkers. For example,
// group assignment rule simulates the traditional linker's semantics.
bool backref = config->warnBackrefs && other.file &&
file->groupId < other.file->groupId;
- fetch();
+ extract();
+
+ if (!config->whyExtract.empty())
+ recordWhyExtract(other.file, *file, *this);
// We don't report backward references to weak symbols as they can be
// overridden later.
// definition. this->file needs to be saved because in the case of LTO it
// may be reset to nullptr or be replaced with a file named lto.tmp.
if (backref && !isWeak())
- backwardReferences.try_emplace(this, std::make_pair(other.file, file));
+ ctx.backwardReferences.try_emplace(this,
+ std::make_pair(other.file, file));
return;
}
// Undefined symbols in a SharedFile do not change the binding.
- if (dyn_cast_or_null<SharedFile>(other.file))
+ if (isa_and_nonnull<SharedFile>(other.file))
return;
if (isUndefined() || isShared()) {
}
}
-// Using .symver foo,foo@@VER unfortunately creates two symbols: foo and
-// foo@@VER. We want to effectively ignore foo, so give precedence to
-// foo@@VER.
-// FIXME: If users can transition to using
-// .symver foo,foo@@@VER
-// we can delete this hack.
-static int compareVersion(StringRef a, StringRef b) {
- bool x = a.contains("@@");
- bool y = b.contains("@@");
- if (!x && y)
- return 1;
- if (x && !y)
- return -1;
- return 0;
-}
-
-// Compare two symbols. Return 1 if the new symbol should win, -1 if
-// the new symbol should lose, or 0 if there is a conflict.
-int Symbol::compare(const Symbol *other) const {
- assert(other->isDefined() || other->isCommon());
-
- if (!isDefined() && !isCommon())
- return 1;
-
- if (int cmp = compareVersion(getName(), other->getName()))
- return cmp;
-
- if (other->isWeak())
- return -1;
-
- if (isWeak())
- return 1;
-
- if (isCommon() && other->isCommon()) {
- if (config->warnCommon)
- warn("multiple common of " + getName());
- return 0;
- }
-
- if (isCommon()) {
+// Compare two symbols. Return true if the new symbol should win.
+bool Symbol::shouldReplace(const Defined &other) const {
+ if (LLVM_UNLIKELY(isCommon())) {
if (config->warnCommon)
warn("common " + getName() + " is overridden");
- return 1;
+ return !other.isWeak();
}
+ if (!isDefined())
+ return true;
- if (other->isCommon()) {
- if (config->warnCommon)
- warn("common " + getName() + " is overridden");
- return -1;
- }
-
- auto *oldSym = cast<Defined>(this);
- auto *newSym = cast<Defined>(other);
-
- if (dyn_cast_or_null<BitcodeFile>(other->file))
- return 0;
-
- if (!oldSym->section && !newSym->section && oldSym->value == newSym->value &&
- newSym->binding == STB_GLOBAL)
- return -1;
-
- return 0;
+ // Incoming STB_GLOBAL overrides STB_WEAK/STB_GNU_UNIQUE. -fgnu-unique changes
+ // some vague linkage data in COMDAT from STB_WEAK to STB_GNU_UNIQUE. Treat
+ // STB_GNU_UNIQUE like STB_WEAK so that we prefer the first among all
+ // STB_WEAK/STB_GNU_UNIQUE copies. If we prefer an incoming STB_GNU_UNIQUE to
+ // an existing STB_WEAK, there may be discarded section errors because the
+ // selected copy may be in a non-prevailing COMDAT.
+ return !isGlobal() && other.isGlobal();
}
-static void reportDuplicate(Symbol *sym, InputFile *newFile,
- InputSectionBase *errSec, uint64_t errOffset) {
+void elf::reportDuplicate(const Symbol &sym, const InputFile *newFile,
+ InputSectionBase *errSec, uint64_t errOffset) {
if (config->allowMultipleDefinition)
return;
-
- Defined *d = cast<Defined>(sym);
+ // In glibc<2.32, crti.o has .gnu.linkonce.t.__x86.get_pc_thunk.bx, which
+ // is sort of proto-comdat. There is actually no duplicate if we have
+ // full support for .gnu.linkonce.
+ const Defined *d = dyn_cast<Defined>(&sym);
+ if (!d || d->getName() == "__x86.get_pc_thunk.bx")
+ return;
+ // Allow absolute symbols with the same value for GNU ld compatibility.
+ if (!d->section && !errSec && errOffset && d->value == errOffset)
+ return;
if (!d->section || !errSec) {
- error("duplicate symbol: " + toString(*sym) + "\n>>> defined in " +
- toString(sym->file) + "\n>>> defined in " + toString(newFile));
+ error("duplicate symbol: " + toString(sym) + "\n>>> defined in " +
+ toString(sym.file) + "\n>>> defined in " + toString(newFile));
return;
}
// >>> defined at baz.c:563
// >>> baz.o in archive libbaz.a
auto *sec1 = cast<InputSectionBase>(d->section);
- std::string src1 = sec1->getSrcMsg(*sym, d->value);
+ std::string src1 = sec1->getSrcMsg(sym, d->value);
std::string obj1 = sec1->getObjMsg(d->value);
- std::string src2 = errSec->getSrcMsg(*sym, errOffset);
+ std::string src2 = errSec->getSrcMsg(sym, errOffset);
std::string obj2 = errSec->getObjMsg(errOffset);
- std::string msg = "duplicate symbol: " + toString(*sym) + "\n>>> defined at ";
+ std::string msg = "duplicate symbol: " + toString(sym) + "\n>>> defined at ";
if (!src1.empty())
msg += src1 + "\n>>> ";
msg += obj1 + "\n>>> defined at ";
error(msg);
}
-void Symbol::resolveCommon(const CommonSymbol &other) {
- int cmp = compare(&other);
- if (cmp < 0)
+void Symbol::checkDuplicate(const Defined &other) const {
+ if (isDefined() && !isWeak() && !other.isWeak())
+ reportDuplicate(*this, other.file,
+ dyn_cast_or_null<InputSectionBase>(other.section),
+ other.value);
+}
+
+void Symbol::resolve(const CommonSymbol &other) {
+ if (other.exportDynamic)
+ exportDynamic = true;
+ if (other.visibility() != STV_DEFAULT) {
+ uint8_t v = visibility(), ov = other.visibility();
+ setVisibility(v == STV_DEFAULT ? ov : std::min(v, ov));
+ }
+ if (isDefined() && !isWeak()) {
+ if (config->warnCommon)
+ warn("common " + getName() + " is overridden");
return;
+ }
- if (cmp > 0) {
- if (auto *s = dyn_cast<SharedSymbol>(this)) {
- // Increase st_size if the shared symbol has a larger st_size. The shared
- // symbol may be created from common symbols. The fact that some object
- // files were linked into a shared object first should not change the
- // regular rule that picks the largest st_size.
- uint64_t size = s->size;
- replace(other);
- if (size > cast<CommonSymbol>(this)->size)
- cast<CommonSymbol>(this)->size = size;
- } else {
- replace(other);
+ if (CommonSymbol *oldSym = dyn_cast<CommonSymbol>(this)) {
+ if (config->warnCommon)
+ warn("multiple common of " + getName());
+ oldSym->alignment = std::max(oldSym->alignment, other.alignment);
+ if (oldSym->size < other.size) {
+ oldSym->file = other.file;
+ oldSym->size = other.size;
}
return;
}
- CommonSymbol *oldSym = cast<CommonSymbol>(this);
-
- oldSym->alignment = std::max(oldSym->alignment, other.alignment);
- if (oldSym->size < other.size) {
- oldSym->file = other.file;
- oldSym->size = other.size;
+ if (auto *s = dyn_cast<SharedSymbol>(this)) {
+ // Increase st_size if the shared symbol has a larger st_size. The shared
+ // symbol may be created from common symbols. The fact that some object
+ // files were linked into a shared object first should not change the
+ // regular rule that picks the largest st_size.
+ uint64_t size = s->size;
+ other.overwrite(*this);
+ if (size > cast<CommonSymbol>(this)->size)
+ cast<CommonSymbol>(this)->size = size;
+ } else {
+ other.overwrite(*this);
}
}
-void Symbol::resolveDefined(const Defined &other) {
- int cmp = compare(&other);
- if (cmp > 0)
- replace(other);
- else if (cmp == 0)
- reportDuplicate(this, other.file,
- dyn_cast_or_null<InputSectionBase>(other.section),
- other.value);
+void Symbol::resolve(const Defined &other) {
+ if (other.exportDynamic)
+ exportDynamic = true;
+ if (other.visibility() != STV_DEFAULT) {
+ uint8_t v = visibility(), ov = other.visibility();
+ setVisibility(v == STV_DEFAULT ? ov : std::min(v, ov));
+ }
+ if (shouldReplace(other))
+ other.overwrite(*this);
}
-template <class LazyT>
-static void replaceCommon(Symbol &oldSym, const LazyT &newSym) {
- backwardReferences.erase(&oldSym);
- oldSym.replace(newSym);
- newSym.fetch();
-}
+void Symbol::resolve(const LazyObject &other) {
+ if (isPlaceholder()) {
+ other.overwrite(*this);
+ return;
+ }
-template <class LazyT> void Symbol::resolveLazy(const LazyT &other) {
// For common objects, we want to look for global or weak definitions that
- // should be fetched as the canonical definition instead.
- if (isCommon() && elf::config->fortranCommon) {
- if (auto *laSym = dyn_cast<LazyArchive>(&other)) {
- ArchiveFile *archive = cast<ArchiveFile>(laSym->file);
- const Archive::Symbol &archiveSym = laSym->sym;
- if (archive->shouldFetchForCommon(archiveSym)) {
- replaceCommon(*this, other);
- return;
- }
- } else if (auto *loSym = dyn_cast<LazyObject>(&other)) {
- LazyObjFile *obj = cast<LazyObjFile>(loSym->file);
- if (obj->shouldFetchForCommon(loSym->getName())) {
- replaceCommon(*this, other);
- return;
- }
- }
+ // should be extracted as the canonical definition instead.
+ if (LLVM_UNLIKELY(isCommon()) && elf::config->fortranCommon &&
+ other.file->shouldExtractForCommon(getName())) {
+ ctx.backwardReferences.erase(this);
+ other.overwrite(*this);
+ other.extract();
+ return;
}
if (!isUndefined()) {
// See the comment in resolveUndefined().
if (isDefined())
- backwardReferences.erase(this);
+ ctx.backwardReferences.erase(this);
return;
}
- // An undefined weak will not fetch archive members. See comment on Lazy in
+ // An undefined weak will not extract archive members. See comment on Lazy in
// Symbols.h for the details.
if (isWeak()) {
uint8_t ty = type;
- replace(other);
+ other.overwrite(*this);
type = ty;
binding = STB_WEAK;
return;
}
- other.fetch();
+ const InputFile *oldFile = file;
+ other.extract();
+ if (!config->whyExtract.empty())
+ recordWhyExtract(oldFile, *file, *this);
}
-void Symbol::resolveShared(const SharedSymbol &other) {
+void Symbol::resolve(const SharedSymbol &other) {
+ exportDynamic = true;
+ if (isPlaceholder()) {
+ other.overwrite(*this);
+ return;
+ }
if (isCommon()) {
// See the comment in resolveCommon() above.
if (other.size > cast<CommonSymbol>(this)->size)
cast<CommonSymbol>(this)->size = other.size;
return;
}
- if (visibility == STV_DEFAULT && (isUndefined() || isLazy())) {
+ if (visibility() == STV_DEFAULT && (isUndefined() || isLazy())) {
// An undefined symbol with non default visibility must be satisfied
// in the same DSO.
uint8_t bind = binding;
- replace(other);
+ other.overwrite(*this);
binding = bind;
} else if (traced)
- printTraceSymbol(&other);
+ printTraceSymbol(other, getName());
}
#ifndef LLD_ELF_SYMBOLS_H
#define LLD_ELF_SYMBOLS_H
-#include "InputFiles.h"
-#include "InputSection.h"
+#include "Config.h"
#include "lld/Common/LLVM.h"
-#include "lld/Common/Strings.h"
+#include "lld/Common/Memory.h"
#include "llvm/ADT/DenseMap.h"
-#include "llvm/Object/Archive.h"
#include "llvm/Object/ELF.h"
+#include "llvm/Support/Compiler.h"
+#include <tuple>
namespace lld {
+namespace elf {
+class Symbol;
+}
// Returns a string representation for a symbol for diagnostics.
std::string toString(const elf::Symbol &);
-// There are two different ways to convert an Archive::Symbol to a string:
-// One for Microsoft name mangling and one for Itanium name mangling.
-// Call the functions toCOFFString and toELFString, not just toString.
-std::string toELFString(const llvm::object::Archive::Symbol &);
-
namespace elf {
class CommonSymbol;
class Defined;
-class InputFile;
-class LazyArchive;
-class LazyObject;
+class OutputSection;
+class SectionBase;
+class InputSectionBase;
class SharedSymbol;
class Symbol;
class Undefined;
+class LazyObject;
+class InputFile;
-// This is a StringRef-like container that doesn't run strlen().
-//
-// ELF string tables contain a lot of null-terminated strings. Most of them
-// are not necessary for the linker because they are names of local symbols,
-// and the linker doesn't use local symbol names for name resolution. So, we
-// use this class to represents strings read from string tables.
-struct StringRefZ {
- StringRefZ(const char *s) : data(s), size(-1) {}
- StringRefZ(StringRef s) : data(s.data()), size(s.size()) {}
-
- const char *data;
- const uint32_t size;
+void printTraceSymbol(const Symbol &sym, StringRef name);
+
+enum {
+ NEEDS_GOT = 1 << 0,
+ NEEDS_PLT = 1 << 1,
+ HAS_DIRECT_RELOC = 1 << 2,
+ // True if this symbol needs a canonical PLT entry, or (during
+ // postScanRelocations) a copy relocation.
+ NEEDS_COPY = 1 << 3,
+ NEEDS_TLSDESC = 1 << 4,
+ NEEDS_TLSGD = 1 << 5,
+ NEEDS_TLSGD_TO_IE = 1 << 6,
+ NEEDS_GOT_DTPREL = 1 << 7,
+ NEEDS_TLSIE = 1 << 8,
+};
+
+// Some index properties of a symbol are stored separately in this auxiliary
+// struct to decrease sizeof(SymbolUnion) in the majority of cases.
+struct SymbolAux {
+ uint32_t gotIdx = -1;
+ uint32_t pltIdx = -1;
+ uint32_t tlsDescIdx = -1;
+ uint32_t tlsGdIdx = -1;
};
+LLVM_LIBRARY_VISIBILITY extern SmallVector<SymbolAux, 0> symAux;
+
// The base class for real symbol classes.
class Symbol {
public:
CommonKind,
SharedKind,
UndefinedKind,
- LazyArchiveKind,
LazyObjectKind,
};
// The file from which this symbol was created.
InputFile *file;
+ // The default copy constructor is deleted due to atomic flags. Define one for
+ // places where no atomic is needed.
+ Symbol(const Symbol &o) { memcpy(this, &o, sizeof(o)); }
+
protected:
const char *nameData;
- mutable uint32_t nameSize;
+ // 32-bit size saves space.
+ uint32_t nameSize;
public:
- uint32_t dynsymIndex = 0;
- uint32_t gotIndex = -1;
- uint32_t pltIndex = -1;
-
- uint32_t globalDynIndex = -1;
-
- // This field is a index to the symbol's version definition.
- uint32_t verdefIndex = -1;
-
- // Version definition index.
- uint16_t versionId;
+ // The next three fields have the same meaning as the ELF symbol attributes.
+ // type and binding are placed in this order to optimize generating st_info,
+ // which is defined as (binding << 4) + (type & 0xf), on a little-endian
+ // system.
+ uint8_t type : 4; // symbol type
// Symbol binding. This is not overwritten by replace() to track
// changes during resolution. In particular:
// - An undefined weak is still weak when it resolves to a shared library.
- // - An undefined weak will not fetch archive members, but we have to
+ // - An undefined weak will not extract archive members, but we have to
// remember it is weak.
- uint8_t binding;
+ uint8_t binding : 4;
- // The following fields have the same meaning as the ELF symbol attributes.
- uint8_t type; // symbol type
uint8_t stOther; // st_other field value
uint8_t symbolKind;
- // Symbol visibility. This is the computed minimum visibility of all
- // observed non-DSO symbols.
- uint8_t visibility : 2;
+ // The partition whose dynamic symbol table contains this symbol's definition.
+ uint8_t partition;
+
+ // True if this symbol is preemptible at load time.
+ uint8_t isPreemptible : 1;
// True if the symbol was used for linking and thus need to be added to the
// output file's symbol table. This is true for all symbols except for
// are unreferenced except by other bitcode objects.
uint8_t isUsedInRegularObj : 1;
+ // True if an undefined or shared symbol is used from a live section.
+ //
+ // NOTE: In Writer.cpp the field is used to mark local defined symbols
+ // which are referenced by relocations when -r or --emit-relocs is given.
+ uint8_t used : 1;
+
// Used by a Defined symbol with protected or default visibility, to record
// whether it is required to be exported into .dynsym. This is set when any of
// the following conditions hold:
//
- // - If there is an interposable symbol from a DSO.
+ // - If there is an interposable symbol from a DSO. Note: We also do this for
+ // STV_PROTECTED symbols which can't be interposed (to match BFD behavior).
// - If -shared or --export-dynamic is specified, any symbol in an object
// file/bitcode sets this property, unless suppressed by LTO
// canBeOmittedFromSymbolTable().
// exported into .dynsym.
uint8_t inDynamicList : 1;
- // False if LTO shouldn't inline whatever this symbol points to. If a symbol
- // is overwritten after LTO, LTO shouldn't inline the symbol because it
- // doesn't know the final contents of the symbol.
- uint8_t canInline : 1;
-
// Used to track if there has been at least one undefined reference to the
// symbol. For Undefined and SharedSymbol, the binding may change to STB_WEAK
// if the first undefined reference from a non-shared object is weak.
- //
- // This is also used to retain __wrap_foo when foo is referenced.
uint8_t referenced : 1;
+ // Used to track if this symbol will be referenced after wrapping is performed
+ // (i.e. this will be true for foo if __real_foo is referenced, and will be
+ // true for __wrap_foo if foo is referenced).
+ uint8_t referencedAfterWrap : 1;
+
// True if this symbol is specified by --trace-symbol option.
uint8_t traced : 1;
+ // True if the name contains '@'.
+ uint8_t hasVersionSuffix : 1;
+
// True if the .gnu.warning.SYMBOL is set for the symbol
uint8_t gwarn : 1;
- inline void replace(const Symbol &newSym);
+ // Symbol visibility. This is the computed minimum visibility of all
+ // observed non-DSO symbols.
+ uint8_t visibility() const { return stOther & 3; }
+ void setVisibility(uint8_t visibility) {
+ stOther = (stOther & ~3) | visibility;
+ }
bool includeInDynsym() const;
uint8_t computeBinding() const;
+ bool isGlobal() const { return binding == llvm::ELF::STB_GLOBAL; }
bool isWeak() const { return binding == llvm::ELF::STB_WEAK; }
bool isUndefined() const { return symbolKind == UndefinedKind; }
bool isLocal() const { return binding == llvm::ELF::STB_LOCAL; }
- bool isLazy() const {
- return symbolKind == LazyArchiveKind || symbolKind == LazyObjectKind;
- }
+ bool isLazy() const { return symbolKind == LazyObjectKind; }
// True if this is an undefined weak symbol. This only works once
// all input files have been added.
- bool isUndefWeak() const {
- // See comment on lazy symbols for details.
- return isWeak() && (isUndefined() || isLazy());
- }
+ bool isUndefWeak() const { return isWeak() && isUndefined(); }
- StringRef getName() const {
- if (nameSize == (uint32_t)-1)
- nameSize = strlen(nameData);
- return {nameData, nameSize};
- }
+ StringRef getName() const { return {nameData, nameSize}; }
void setName(StringRef s) {
nameData = s.data();
//
// For @@, the name has been truncated by insert(). For @, the name has been
// truncated by Symbol::parseSymbolVersion().
- const char *getVersionSuffix() const {
- (void)getName();
- return nameData + nameSize;
- }
+ const char *getVersionSuffix() const { return nameData + nameSize; }
+
+ uint32_t getGotIdx() const { return symAux[auxIdx].gotIdx; }
+ uint32_t getPltIdx() const { return symAux[auxIdx].pltIdx; }
+ uint32_t getTlsDescIdx() const { return symAux[auxIdx].tlsDescIdx; }
+ uint32_t getTlsGdIdx() const { return symAux[auxIdx].tlsGdIdx; }
- bool isInGot() const { return gotIndex != -1U; }
- bool isInPlt() const { return pltIndex != -1U; }
+ bool isInGot() const { return getGotIdx() != uint32_t(-1); }
+ bool isInPlt() const { return getPltIdx() != uint32_t(-1); }
uint64_t getVA(int64_t addend = 0) const;
// For example, if "this" is an undefined symbol and a new symbol is
// a defined symbol, "this" is replaced with the new symbol.
void mergeProperties(const Symbol &other);
- void resolve(const Symbol &other);
+ void resolve(const Undefined &other);
+ void resolve(const CommonSymbol &other);
+ void resolve(const Defined &other);
+ void resolve(const LazyObject &other);
+ void resolve(const SharedSymbol &other);
- // If this is a lazy symbol, fetch an input file and add the symbol
+ // If this is a lazy symbol, extract an input file and add the symbol
// in the file to the symbol table. Calling this function on
// non-lazy object causes a runtime error.
- void fetch() const;
+ void extract() const;
- static bool isExportDynamic(Kind k, uint8_t visibility) {
- if (k == SharedKind)
- return visibility == llvm::ELF::STV_DEFAULT;
- return config->shared || config->exportDynamic;
- }
+ void checkDuplicate(const Defined &other) const;
private:
- void resolveUndefined(const Undefined &other);
- void resolveCommon(const CommonSymbol &other);
- void resolveDefined(const Defined &other);
- template <class LazyT> void resolveLazy(const LazyT &other);
- void resolveShared(const SharedSymbol &other);
-
- int compare(const Symbol *other) const;
-
- inline size_t getSymbolSize() const;
+ bool shouldReplace(const Defined &other) const;
protected:
- Symbol(Kind k, InputFile *file, StringRefZ name, uint8_t binding,
+ Symbol(Kind k, InputFile *file, StringRef name, uint8_t binding,
uint8_t stOther, uint8_t type)
- : file(file), nameData(name.data), nameSize(name.size), binding(binding),
- type(type), stOther(stOther), symbolKind(k), visibility(stOther & 3),
- isUsedInRegularObj(!file || file->kind() == InputFile::ObjKind),
- exportDynamic(isExportDynamic(k, visibility)), inDynamicList(false),
- canInline(false), referenced(false), traced(false), gwarn(false), needsPltAddr(false),
- isInIplt(false), gotInIgot(false), isPreemptible(false),
- used(!config->gcSections), needsTocRestore(false),
- scriptDefined(false) {}
+ : file(file), nameData(name.data()), nameSize(name.size()), type(type),
+ binding(binding), stOther(stOther), symbolKind(k),
+ exportDynamic(false), gwarn(false) {}
+
+ void overwrite(Symbol &sym, Kind k) const {
+ if (sym.traced)
+ printTraceSymbol(*this, sym.getName());
+ sym.file = file;
+ sym.type = type;
+ sym.binding = binding;
+ sym.stOther = (stOther & ~3) | sym.visibility();
+ sym.symbolKind = k;
+ }
public:
- // True the symbol should point to its PLT entry.
- // For SharedSymbol only.
- uint8_t needsPltAddr : 1;
-
// True if this symbol is in the Iplt sub-section of the Plt and the Igot
// sub-section of the .got.plt or .got.
uint8_t isInIplt : 1;
// Igot. This will be true only for certain non-preemptible ifuncs.
uint8_t gotInIgot : 1;
- // True if this symbol is preemptible at load time.
- uint8_t isPreemptible : 1;
-
- // True if an undefined or shared symbol is used from a live section.
- //
- // NOTE: In Writer.cpp the field is used to mark local defined symbols
- // which are referenced by relocations when -r or --emit-relocs is given.
- uint8_t used : 1;
+ // True if defined relative to a section discarded by ICF.
+ uint8_t folded : 1;
// True if a call to this symbol needs to be followed by a restore of the
// PPC64 toc pointer.
uint8_t needsTocRestore : 1;
- // True if this symbol is defined by a linker script.
+ // True if this symbol is defined by a symbol assignment or wrapped by --wrap.
+ //
+ // LTO shouldn't inline the symbol because it doesn't know the final content
+ // of the symbol.
uint8_t scriptDefined : 1;
- // The partition whose dynamic symbol table contains this symbol's definition.
- uint8_t partition = 1;
+ // True if defined in a DSO as protected visibility.
+ uint8_t dsoProtected : 1;
+
+ // Temporary flags used to communicate which symbol entries need PLT and GOT
+ // entries during postScanRelocations();
+ std::atomic<uint16_t> flags;
+
+ // A symAux index used to access GOT/PLT entry indexes. This is allocated in
+ // postScanRelocations().
+ uint32_t auxIdx;
+ uint32_t dynsymIndex;
+
+ // This field is a index to the symbol's version definition.
+ uint16_t verdefIndex;
+
+ // Version definition index.
+ uint16_t versionId;
+
+ void setFlags(uint16_t bits) {
+ flags.fetch_or(bits, std::memory_order_relaxed);
+ }
+ bool hasFlag(uint16_t bit) const {
+ assert(bit && (bit & (bit - 1)) == 0 && "bit must be a power of 2");
+ return flags.load(std::memory_order_relaxed) & bit;
+ }
+
+ bool needsDynReloc() const {
+ return flags.load(std::memory_order_relaxed) &
+ (NEEDS_COPY | NEEDS_GOT | NEEDS_PLT | NEEDS_TLSDESC | NEEDS_TLSGD |
+ NEEDS_TLSGD_TO_IE | NEEDS_GOT_DTPREL | NEEDS_TLSIE);
+ }
+ void allocateAux() {
+ assert(auxIdx == 0);
+ auxIdx = symAux.size();
+ symAux.emplace_back();
+ }
bool isSection() const { return type == llvm::ELF::STT_SECTION; }
bool isTls() const { return type == llvm::ELF::STT_TLS; }
// Represents a symbol that is defined in the current output file.
class Defined : public Symbol {
public:
- Defined(InputFile *file, StringRefZ name, uint8_t binding, uint8_t stOther,
+ Defined(InputFile *file, StringRef name, uint8_t binding, uint8_t stOther,
uint8_t type, uint64_t value, uint64_t size, SectionBase *section)
: Symbol(DefinedKind, file, name, binding, stOther, type), value(value),
- size(size), section(section) {}
+ size(size), section(section) {
+ exportDynamic = config->exportDynamic;
+ }
+ void overwrite(Symbol &sym) const {
+ Symbol::overwrite(sym, DefinedKind);
+ sym.verdefIndex = -1;
+ auto &s = static_cast<Defined &>(sym);
+ s.value = value;
+ s.size = size;
+ s.section = section;
+ }
static bool classof(const Symbol *s) { return s->isDefined(); }
// section. (Therefore, the later passes don't see any CommonSymbols.)
class CommonSymbol : public Symbol {
public:
- CommonSymbol(InputFile *file, StringRefZ name, uint8_t binding,
+ CommonSymbol(InputFile *file, StringRef name, uint8_t binding,
uint8_t stOther, uint8_t type, uint64_t alignment, uint64_t size)
: Symbol(CommonKind, file, name, binding, stOther, type),
- alignment(alignment), size(size) {}
+ alignment(alignment), size(size) {
+ exportDynamic = config->exportDynamic;
+ }
+ void overwrite(Symbol &sym) const {
+ Symbol::overwrite(sym, CommonKind);
+ auto &s = static_cast<CommonSymbol &>(sym);
+ s.alignment = alignment;
+ s.size = size;
+ }
static bool classof(const Symbol *s) { return s->isCommon(); }
class Undefined : public Symbol {
public:
- Undefined(InputFile *file, StringRefZ name, uint8_t binding, uint8_t stOther,
+ Undefined(InputFile *file, StringRef name, uint8_t binding, uint8_t stOther,
uint8_t type, uint32_t discardedSecIdx = 0)
: Symbol(UndefinedKind, file, name, binding, stOther, type),
discardedSecIdx(discardedSecIdx) {}
+ void overwrite(Symbol &sym) const {
+ Symbol::overwrite(sym, UndefinedKind);
+ auto &s = static_cast<Undefined &>(sym);
+ s.discardedSecIdx = discardedSecIdx;
+ s.nonPrevailing = nonPrevailing;
+ }
static bool classof(const Symbol *s) { return s->kind() == UndefinedKind; }
// The section index if in a discarded section, 0 otherwise.
uint32_t discardedSecIdx;
+ bool nonPrevailing = false;
};
class SharedSymbol : public Symbol {
SharedSymbol(InputFile &file, StringRef name, uint8_t binding,
uint8_t stOther, uint8_t type, uint64_t value, uint64_t size,
- uint32_t alignment, uint32_t verdefIndex)
+ uint32_t alignment)
: Symbol(SharedKind, &file, name, binding, stOther, type), value(value),
size(size), alignment(alignment) {
- this->verdefIndex = verdefIndex;
+ exportDynamic = true;
+ dsoProtected = visibility() == llvm::ELF::STV_PROTECTED;
// GNU ifunc is a mechanism to allow user-supplied functions to
// resolve PLT slot values at load-time. This is contrary to the
// regular symbol resolution scheme in which symbols are resolved just
if (this->type == llvm::ELF::STT_GNU_IFUNC)
this->type = llvm::ELF::STT_FUNC;
}
-
- SharedFile &getFile() const { return *cast<SharedFile>(file); }
+ void overwrite(Symbol &sym) const {
+ Symbol::overwrite(sym, SharedKind);
+ auto &s = static_cast<SharedSymbol &>(sym);
+ s.dsoProtected = dsoProtected;
+ s.value = value;
+ s.size = size;
+ s.alignment = alignment;
+ }
uint64_t value; // st_value
uint64_t size; // st_size
uint32_t alignment;
};
-// LazyArchive and LazyObject represent a symbols that is not yet in the link,
-// but we know where to find it if needed. If the resolver finds both Undefined
-// and Lazy for the same name, it will ask the Lazy to load a file.
+// LazyObject symbols represent symbols in object files between --start-lib and
+// --end-lib options. LLD also handles traditional archives as if all the files
+// in the archive are surrounded by --start-lib and --end-lib.
//
// A special complication is the handling of weak undefined symbols. They should
// not load a file, but we have to remember we have seen both the weak undefined
// and the lazy. We represent that with a lazy symbol with a weak binding. This
// means that code looking for undefined symbols normally also has to take lazy
// symbols into consideration.
-
-// This class represents a symbol defined in an archive file. It is
-// created from an archive file header, and it knows how to load an
-// object file from an archive to replace itself with a defined
-// symbol.
-class LazyArchive : public Symbol {
-public:
- LazyArchive(InputFile &file, const llvm::object::Archive::Symbol s)
- : Symbol(LazyArchiveKind, &file, s.getName(), llvm::ELF::STB_GLOBAL,
- llvm::ELF::STV_DEFAULT, llvm::ELF::STT_NOTYPE),
- sym(s) {}
-
- static bool classof(const Symbol *s) { return s->kind() == LazyArchiveKind; }
-
- MemoryBufferRef getMemberBuffer();
-
- const llvm::object::Archive::Symbol sym;
-};
-
-// LazyObject symbols represents symbols in object files between
-// --start-lib and --end-lib options.
class LazyObject : public Symbol {
public:
- LazyObject(InputFile &file, StringRef name)
- : Symbol(LazyObjectKind, &file, name, llvm::ELF::STB_GLOBAL,
+ LazyObject(InputFile &file)
+ : Symbol(LazyObjectKind, &file, {}, llvm::ELF::STB_GLOBAL,
llvm::ELF::STV_DEFAULT, llvm::ELF::STT_NOTYPE) {}
+ void overwrite(Symbol &sym) const { Symbol::overwrite(sym, LazyObjectKind); }
static bool classof(const Symbol *s) { return s->kind() == LazyObjectKind; }
};
static Defined *relaIpltStart;
static Defined *relaIpltEnd;
- // __global_pointer$ for RISC-V.
- static Defined *riscvGlobalPointer;
-
// _TLS_MODULE_BASE_ on targets that support TLSDESC.
static Defined *tlsModuleBase;
};
// A buffer class that is large enough to hold any Symbol-derived
// object. We allocate memory using this class and instantiate a symbol
// using the placement new.
+
+// It is important to keep the size of SymbolUnion small for performance and
+// memory usage reasons. 64 bytes is a soft limit based on the size of Defined
+// on a 64-bit system. This is enforced by a static_assert in Symbols.cpp.
union SymbolUnion {
alignas(Defined) char a[sizeof(Defined)];
alignas(CommonSymbol) char b[sizeof(CommonSymbol)];
alignas(Undefined) char c[sizeof(Undefined)];
alignas(SharedSymbol) char d[sizeof(SharedSymbol)];
- alignas(LazyArchive) char e[sizeof(LazyArchive)];
- alignas(LazyObject) char f[sizeof(LazyObject)];
-};
-
-// It is important to keep the size of SymbolUnion small for performance and
-// memory usage reasons. 80 bytes is a soft limit based on the size of Defined
-// on a 64-bit system.
-static_assert(sizeof(SymbolUnion) <= 80, "SymbolUnion too large");
-
-template <typename T> struct AssertSymbol {
- static_assert(std::is_trivially_destructible<T>(),
- "Symbol types must be trivially destructible");
- static_assert(sizeof(T) <= sizeof(SymbolUnion), "SymbolUnion too small");
- static_assert(alignof(T) <= alignof(SymbolUnion),
- "SymbolUnion not aligned enough");
+ alignas(LazyObject) char e[sizeof(LazyObject)];
};
-static inline void assertSymbols() {
- AssertSymbol<Defined>();
- AssertSymbol<CommonSymbol>();
- AssertSymbol<Undefined>();
- AssertSymbol<SharedSymbol>();
- AssertSymbol<LazyArchive>();
- AssertSymbol<LazyObject>();
-}
-
-void printTraceSymbol(const Symbol *sym);
-
-size_t Symbol::getSymbolSize() const {
- switch (kind()) {
- case CommonKind:
- return sizeof(CommonSymbol);
- case DefinedKind:
- return sizeof(Defined);
- case LazyArchiveKind:
- return sizeof(LazyArchive);
- case LazyObjectKind:
- return sizeof(LazyObject);
- case SharedKind:
- return sizeof(SharedSymbol);
- case UndefinedKind:
- return sizeof(Undefined);
- case PlaceholderKind:
- return sizeof(Symbol);
- }
- llvm_unreachable("unknown symbol kind");
-}
-
-// replace() replaces "this" object with a given symbol by memcpy'ing
-// it over to "this". This function is called as a result of name
-// resolution, e.g. to replace an undefind symbol with a defined symbol.
-void Symbol::replace(const Symbol &newSym) {
- using llvm::ELF::STT_TLS;
-
- // st_value of STT_TLS represents the assigned offset, not the actual address
- // which is used by STT_FUNC and STT_OBJECT. STT_TLS symbols can only be
- // referenced by special TLS relocations. It is usually an error if a STT_TLS
- // symbol is replaced by a non-STT_TLS symbol, vice versa. There are two
- // exceptions: (a) a STT_NOTYPE lazy/undefined symbol can be replaced by a
- // STT_TLS symbol, (b) a STT_TLS undefined symbol can be replaced by a
- // STT_NOTYPE lazy symbol.
- if (symbolKind != PlaceholderKind && !newSym.isLazy() &&
- (type == STT_TLS) != (newSym.type == STT_TLS) &&
- type != llvm::ELF::STT_NOTYPE)
- error("TLS attribute mismatch: " + toString(*this) + "\n>>> defined in " +
- toString(newSym.file) + "\n>>> defined in " + toString(file));
-
- Symbol old = *this;
- memcpy(this, &newSym, newSym.getSymbolSize());
-
- // old may be a placeholder. The referenced fields must be initialized in
- // SymbolTable::insert.
- versionId = old.versionId;
- visibility = old.visibility;
- isUsedInRegularObj = old.isUsedInRegularObj;
- exportDynamic = old.exportDynamic;
- inDynamicList = old.inDynamicList;
- canInline = old.canInline;
- referenced = old.referenced;
- traced = old.traced;
- gwarn = old.gwarn;
- isPreemptible = old.isPreemptible;
- scriptDefined = old.scriptDefined;
- partition = old.partition;
-
- // Symbol length is computed lazily. If we already know a symbol length,
- // propagate it.
- if (nameData == old.nameData && nameSize == 0 && old.nameSize != 0)
- nameSize = old.nameSize;
-
- // Print out a log message if --trace-symbol was specified.
- // This is for debugging.
- if (traced)
- printTraceSymbol(this);
+template <typename... T> Defined *makeDefined(T &&...args) {
+ auto *sym = getSpecificAllocSingleton<SymbolUnion>().Allocate();
+ memset(sym, 0, sizeof(Symbol));
+ auto &s = *new (reinterpret_cast<Defined *>(sym)) Defined(std::forward<T>(args)...);
+ return &s;
}
+void reportDuplicate(const Symbol &sym, const InputFile *newFile,
+ InputSectionBase *errSec, uint64_t errOffset);
void maybeWarnUnorderableSymbol(const Symbol *sym);
bool computeIsPreemptible(const Symbol &sym);
-void reportBackrefs();
extern llvm::DenseMap<StringRef, StringRef> gnuWarnings;
-// A mapping from a symbol to an InputFile referencing it backward. Used by
-// --warn-backrefs.
-extern llvm::DenseMap<const Symbol *,
- std::pair<const InputFile *, const InputFile *>>
- backwardReferences;
-
} // namespace elf
} // namespace lld
#include "SyntheticSections.h"
#include "Config.h"
+#include "DWARF.h"
+#include "EhFrame.h"
#include "InputFiles.h"
#include "LinkerScript.h"
#include "OutputSections.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "Target.h"
+#include "Thunks.h"
#include "Writer.h"
+#include "lld/Common/CommonLinkerContext.h"
#include "lld/Common/DWARF.h"
-#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Memory.h"
#include "lld/Common/Strings.h"
#include "lld/Common/Version.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SetOperations.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/BinaryFormat/Dwarf.h"
+#include "llvm/BinaryFormat/ELF.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h"
-#include "llvm/Object/ELFObjectFile.h"
-#include "llvm/Support/Compression.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/LEB128.h"
-#include "llvm/Support/MD5.h"
#include "llvm/Support/Parallel.h"
#include "llvm/Support/TimeProfiler.h"
#include <cstdlib>
-#include <thread>
using namespace llvm;
using namespace llvm::dwarf;
// This is only for testing.
StringRef s = getenv("LLD_VERSION");
if (s.empty())
- s = saver.save(Twine("Linker: ") + getLLDVersion());
+ s = saver().save(Twine("Linker: ") + getLLDVersion());
// +1 to include the terminating '\0'.
return {(const uint8_t *)s.data(), s.size() + 1};
// by "readelf --string-dump .comment <file>".
// The returned object is a mergeable string section.
MergeInputSection *elf::createCommentSection() {
- return make<MergeInputSection>(SHF_MERGE | SHF_STRINGS, SHT_PROGBITS, 1,
- getVersion(), ".comment");
+ auto *sec = make<MergeInputSection>(SHF_MERGE | SHF_STRINGS, SHT_PROGBITS, 1,
+ getVersion(), ".comment");
+ sec->splitIntoPieces();
+ return sec;
}
// .MIPS.abiflags section.
}
template <class ELFT>
-MipsAbiFlagsSection<ELFT> *MipsAbiFlagsSection<ELFT>::create() {
+std::unique_ptr<MipsAbiFlagsSection<ELFT>> MipsAbiFlagsSection<ELFT>::create() {
Elf_Mips_ABIFlags flags = {};
bool create = false;
- for (InputSectionBase *sec : inputSections) {
+ for (InputSectionBase *sec : ctx.inputSections) {
if (sec->type != SHT_MIPS_ABIFLAGS)
continue;
sec->markDead();
create = true;
std::string filename = toString(sec->file);
- const size_t size = sec->data().size();
+ const size_t size = sec->content().size();
// Older version of BFD (such as the default FreeBSD linker) concatenate
// .MIPS.abiflags instead of merging. To allow for this case (or potential
// zero padding) we ignore everything after the first Elf_Mips_ABIFlags
Twine(size) + " instead of " + Twine(sizeof(Elf_Mips_ABIFlags)));
return nullptr;
}
- auto *s = reinterpret_cast<const Elf_Mips_ABIFlags *>(sec->data().data());
+ auto *s =
+ reinterpret_cast<const Elf_Mips_ABIFlags *>(sec->content().data());
if (s->version != 0) {
error(filename + ": unexpected .MIPS.abiflags version " +
Twine(s->version));
};
if (create)
- return make<MipsAbiFlagsSection<ELFT>>(flags);
+ return std::make_unique<MipsAbiFlagsSection<ELFT>>(flags);
return nullptr;
}
}
template <class ELFT>
-MipsOptionsSection<ELFT> *MipsOptionsSection<ELFT>::create() {
+std::unique_ptr<MipsOptionsSection<ELFT>> MipsOptionsSection<ELFT>::create() {
// N64 ABI only.
if (!ELFT::Is64Bits)
return nullptr;
- std::vector<InputSectionBase *> sections;
- for (InputSectionBase *sec : inputSections)
+ SmallVector<InputSectionBase *, 0> sections;
+ for (InputSectionBase *sec : ctx.inputSections)
if (sec->type == SHT_MIPS_OPTIONS)
sections.push_back(sec);
sec->markDead();
std::string filename = toString(sec->file);
- ArrayRef<uint8_t> d = sec->data();
+ ArrayRef<uint8_t> d = sec->content();
while (!d.empty()) {
if (d.size() < sizeof(Elf_Mips_Options)) {
}
};
- return make<MipsOptionsSection<ELFT>>(reginfo);
+ return std::make_unique<MipsOptionsSection<ELFT>>(reginfo);
}
// MIPS .reginfo section.
}
template <class ELFT>
-MipsReginfoSection<ELFT> *MipsReginfoSection<ELFT>::create() {
+std::unique_ptr<MipsReginfoSection<ELFT>> MipsReginfoSection<ELFT>::create() {
// Section should be alive for O32 and N32 ABIs only.
if (ELFT::Is64Bits)
return nullptr;
- std::vector<InputSectionBase *> sections;
- for (InputSectionBase *sec : inputSections)
+ SmallVector<InputSectionBase *, 0> sections;
+ for (InputSectionBase *sec : ctx.inputSections)
if (sec->type == SHT_MIPS_REGINFO)
sections.push_back(sec);
for (InputSectionBase *sec : sections) {
sec->markDead();
- if (sec->data().size() != sizeof(Elf_Mips_RegInfo)) {
+ if (sec->content().size() != sizeof(Elf_Mips_RegInfo)) {
error(toString(sec->file) + ": invalid size of .reginfo section");
return nullptr;
}
- auto *r = reinterpret_cast<const Elf_Mips_RegInfo *>(sec->data().data());
+ auto *r = reinterpret_cast<const Elf_Mips_RegInfo *>(sec->content().data());
reginfo.ri_gprmask |= r->ri_gprmask;
sec->getFile<ELFT>()->mipsGp0 = r->ri_gp_value;
};
- return make<MipsReginfoSection<ELFT>>(reginfo);
+ return std::make_unique<MipsReginfoSection<ELFT>>(reginfo);
}
InputSection *elf::createInterpSection() {
// StringSaver guarantees that the returned string ends with '\0'.
- StringRef s = saver.save(config->dynamicLinker);
+ StringRef s = saver().save(config->dynamicLinker);
ArrayRef<uint8_t> contents = {(const uint8_t *)s.data(), s.size() + 1};
return make<InputSection>(nullptr, SHF_ALLOC, SHT_PROGBITS, 1, contents,
Defined *elf::addSyntheticLocal(StringRef name, uint8_t type, uint64_t value,
uint64_t size, InputSectionBase §ion) {
- auto *s = make<Defined>(section.file, name, STB_LOCAL, STV_DEFAULT, type,
- value, size, §ion);
+ Defined *s = makeDefined(section.file, name, STB_LOCAL, STV_DEFAULT, type,
+ value, size, §ion);
if (in.symTab)
in.symTab->addSymbol(s);
return s;
// FDEs for garbage-collected or merged-by-ICF sections, or sections in
// another partition, are dead.
if (auto *d = dyn_cast<Defined>(&b))
- if (d->section && d->section->partition == partition)
+ if (!d->folded && d->section && d->section->partition == partition)
return d;
return nullptr;
}
template <class ELFT, class RelTy>
void EhFrameSection::addRecords(EhInputSection *sec, ArrayRef<RelTy> rels) {
offsetToCie.clear();
- for (EhSectionPiece &piece : sec->pieces) {
- // The empty record is the end marker.
- if (piece.size == 4)
- return;
-
- size_t offset = piece.inputOff;
- uint32_t id = read32(piece.data().data() + 4);
- if (id == 0) {
- offsetToCie[offset] = addCie<ELFT>(piece, rels);
- continue;
- }
-
- uint32_t cieOffset = offset + 4 - id;
- CieRecord *rec = offsetToCie[cieOffset];
+ for (EhSectionPiece &cie : sec->cies)
+ offsetToCie[cie.inputOff] = addCie<ELFT>(cie, rels);
+ for (EhSectionPiece &fde : sec->fdes) {
+ uint32_t id = endian::read32<ELFT::TargetEndianness>(fde.data().data() + 4);
+ CieRecord *rec = offsetToCie[fde.inputOff + 4 - id];
if (!rec)
fatal(toString(sec) + ": invalid CIE reference");
- if (!isFdeLive<ELFT>(piece, rels))
+ if (!isFdeLive<ELFT>(fde, rels))
continue;
- rec->fdes.push_back(&piece);
+ rec->fdes.push_back(&fde);
numFdes++;
}
}
void EhFrameSection::addSectionAux(EhInputSection *sec) {
if (!sec->isLive())
return;
- if (sec->areRelocsRela)
- addRecords<ELFT>(sec, sec->template relas<ELFT>());
+ const RelsOrRelas<ELFT> rels = sec->template relsOrRelas<ELFT>();
+ if (rels.areRelocsRel())
+ addRecords<ELFT>(sec, rels.rels);
else
- addRecords<ELFT>(sec, sec->template rels<ELFT>());
-}
-
-void EhFrameSection::addSection(EhInputSection *sec) {
- sec->parent = this;
-
- alignment = std::max(alignment, sec->alignment);
- sections.push_back(sec);
-
- for (auto *ds : sec->dependentSections)
- dependentSections.push_back(ds);
+ addRecords<ELFT>(sec, rels.relas);
}
// Used by ICF<ELFT>::handleLSDA(). This function is very similar to
void EhFrameSection::iterateFDEWithLSDAAux(
EhInputSection &sec, ArrayRef<RelTy> rels, DenseSet<size_t> &ciesWithLSDA,
llvm::function_ref<void(InputSection &)> fn) {
- for (EhSectionPiece &piece : sec.pieces) {
- // Skip ZERO terminator.
- if (piece.size == 4)
- continue;
-
- size_t offset = piece.inputOff;
- uint32_t id =
- endian::read32<ELFT::TargetEndianness>(piece.data().data() + 4);
- if (id == 0) {
- if (hasLSDA(piece))
- ciesWithLSDA.insert(offset);
- continue;
- }
- uint32_t cieOffset = offset + 4 - id;
- if (ciesWithLSDA.count(cieOffset) == 0)
+ for (EhSectionPiece &cie : sec.cies)
+ if (hasLSDA(cie))
+ ciesWithLSDA.insert(cie.inputOff);
+ for (EhSectionPiece &fde : sec.fdes) {
+ uint32_t id = endian::read32<ELFT::TargetEndianness>(fde.data().data() + 4);
+ if (!ciesWithLSDA.contains(fde.inputOff + 4 - id))
continue;
// The CIE has a LSDA argument. Call fn with d's section.
- if (Defined *d = isFdeLive<ELFT>(piece, rels))
+ if (Defined *d = isFdeLive<ELFT>(fde, rels))
if (auto *s = dyn_cast_or_null<InputSection>(d->section))
fn(*s);
}
DenseSet<size_t> ciesWithLSDA;
for (EhInputSection *sec : sections) {
ciesWithLSDA.clear();
- if (sec->areRelocsRela)
- iterateFDEWithLSDAAux<ELFT>(*sec, sec->template relas<ELFT>(),
- ciesWithLSDA, fn);
+ const RelsOrRelas<ELFT> rels = sec->template relsOrRelas<ELFT>();
+ if (rels.areRelocsRel())
+ iterateFDEWithLSDAAux<ELFT>(*sec, rels.rels, ciesWithLSDA, fn);
else
- iterateFDEWithLSDAAux<ELFT>(*sec, sec->template rels<ELFT>(),
- ciesWithLSDA, fn);
+ iterateFDEWithLSDAAux<ELFT>(*sec, rels.relas, ciesWithLSDA, fn);
}
}
static void writeCieFde(uint8_t *buf, ArrayRef<uint8_t> d) {
memcpy(buf, d.data(), d.size());
-
- size_t aligned = alignTo(d.size(), config->wordsize);
-
- // Zero-clear trailing padding if it exists.
- memset(buf + d.size(), 0, aligned - d.size());
-
// Fix the size field. -4 since size does not include the size field itself.
- write32(buf, aligned - 4);
+ write32(buf, d.size() - 4);
}
void EhFrameSection::finalizeContents() {
size_t off = 0;
for (CieRecord *rec : cieRecords) {
rec->cie->outputOff = off;
- off += alignTo(rec->cie->size, config->wordsize);
+ off += rec->cie->size;
for (EhSectionPiece *fde : rec->fdes) {
fde->outputOff = off;
- off += alignTo(fde->size, config->wordsize);
+ off += fde->size;
}
}
// Returns data for .eh_frame_hdr. .eh_frame_hdr is a binary search table
// to get an FDE from an address to which FDE is applied. This function
// returns a list of such pairs.
-std::vector<EhFrameSection::FdeData> EhFrameSection::getFdeData() const {
+SmallVector<EhFrameSection::FdeData, 0> EhFrameSection::getFdeData() const {
uint8_t *buf = Out::bufferStart + getParent()->offset + outSecOff;
- std::vector<FdeData> ret;
+ SmallVector<FdeData, 0> ret;
uint64_t va = getPartition().ehFrameHdr->getVA();
for (CieRecord *rec : cieRecords) {
// in the output buffer, but relocateAlloc() still works because
// getOffset() takes care of discontiguous section pieces.
for (EhInputSection *s : sections)
- s->relocateAlloc(buf, nullptr);
+ target->relocateAlloc(*s, buf);
if (getPartition().ehFrameHdr && getPartition().ehFrameHdr->getParent())
getPartition().ehFrameHdr->write();
numEntries = target->gotHeaderEntriesNum;
}
+void GotSection::addConstant(const Relocation &r) { relocations.push_back(r); }
void GotSection::addEntry(Symbol &sym) {
- sym.gotIndex = numEntries;
- ++numEntries;
+ assert(sym.auxIdx == symAux.size() - 1);
+ symAux.back().gotIdx = numEntries++;
+}
+
+bool GotSection::addTlsDescEntry(Symbol &sym) {
+ assert(sym.auxIdx == symAux.size() - 1);
+ symAux.back().tlsDescIdx = numEntries;
+ numEntries += 2;
+ return true;
}
bool GotSection::addDynTlsEntry(Symbol &sym) {
- if (sym.globalDynIndex != -1U)
- return false;
- sym.globalDynIndex = numEntries;
+ assert(sym.auxIdx == symAux.size() - 1);
+ symAux.back().tlsGdIdx = numEntries;
// Global Dynamic TLS entries take two GOT slots.
numEntries += 2;
return true;
return true;
}
+uint32_t GotSection::getTlsDescOffset(const Symbol &sym) const {
+ return sym.getTlsDescIdx() * config->wordsize;
+}
+
+uint64_t GotSection::getTlsDescAddr(const Symbol &sym) const {
+ return getVA() + getTlsDescOffset(sym);
+}
+
uint64_t GotSection::getGlobalDynAddr(const Symbol &b) const {
- return this->getVA() + b.globalDynIndex * config->wordsize;
+ return this->getVA() + b.getTlsGdIdx() * config->wordsize;
}
uint64_t GotSection::getGlobalDynOffset(const Symbol &b) const {
- return b.globalDynIndex * config->wordsize;
+ return b.getTlsGdIdx() * config->wordsize;
}
void GotSection::finalizeContents() {
}
void GotSection::writeTo(uint8_t *buf) {
+ // On PPC64 .got may be needed but empty. Skip the write.
+ if (size == 0)
+ return;
target->writeGotHeader(buf);
- relocateAlloc(buf, buf + size);
+ target->relocateAlloc(*this, buf);
}
static uint64_t getMipsPageAddr(uint64_t addr) {
}
MipsGotSection::FileGot &MipsGotSection::getGot(InputFile &f) {
- if (!f.mipsGotIndex.hasValue()) {
+ if (f.mipsGotIndex == uint32_t(-1)) {
gots.emplace_back();
gots.back().file = &f;
f.mipsGotIndex = gots.size() - 1;
}
- return gots[*f.mipsGotIndex];
+ return gots[f.mipsGotIndex];
}
uint64_t MipsGotSection::getPageEntryOffset(const InputFile *f,
const Symbol &sym,
int64_t addend) const {
- const FileGot &g = gots[*f->mipsGotIndex];
+ const FileGot &g = gots[f->mipsGotIndex];
uint64_t index = 0;
if (const OutputSection *outSec = sym.getOutputSection()) {
uint64_t secAddr = getMipsPageAddr(outSec->addr);
uint64_t MipsGotSection::getSymEntryOffset(const InputFile *f, const Symbol &s,
int64_t addend) const {
- const FileGot &g = gots[*f->mipsGotIndex];
+ const FileGot &g = gots[f->mipsGotIndex];
Symbol *sym = const_cast<Symbol *>(&s);
if (sym->isTls())
return g.tls.lookup(sym) * config->wordsize;
}
uint64_t MipsGotSection::getTlsIndexOffset(const InputFile *f) const {
- const FileGot &g = gots[*f->mipsGotIndex];
+ const FileGot &g = gots[f->mipsGotIndex];
return g.dynTlsSymbols.lookup(nullptr) * config->wordsize;
}
uint64_t MipsGotSection::getGlobalDynOffset(const InputFile *f,
const Symbol &s) const {
- const FileGot &g = gots[*f->mipsGotIndex];
+ const FileGot &g = gots[f->mipsGotIndex];
Symbol *sym = const_cast<Symbol *>(&s);
return g.dynTlsSymbols.lookup(sym) * config->wordsize;
}
got.pagesMap) {
const OutputSection *os = p.first;
uint64_t secSize = 0;
- for (BaseCommand *cmd : os->sectionCommands) {
+ for (SectionCommand *cmd : os->commands) {
if (auto *isd = dyn_cast<InputSectionDescription>(cmd))
for (InputSection *isec : isd->sections) {
- uint64_t off = alignTo(secSize, isec->alignment);
+ uint64_t off = alignToPowerOf2(secSize, isec->addralign);
secSize = off + isec->getSize();
}
}
}
}
- // Update Symbol::gotIndex field to use this
+ // Update SymbolAux::gotIdx field to use this
// value later in the `sortMipsSymbols` function.
- for (auto &p : primGot->global)
- p.first->gotIndex = p.second;
- for (auto &p : primGot->relocs)
- p.first->gotIndex = p.second;
+ for (auto &p : primGot->global) {
+ if (p.first->auxIdx == 0)
+ p.first->allocateAux();
+ symAux.back().gotIdx = p.second;
+ }
+ for (auto &p : primGot->relocs) {
+ if (p.first->auxIdx == 0)
+ p.first->allocateAux();
+ symAux.back().gotIdx = p.second;
+ }
// Create dynamic relocations.
for (FileGot &got : gots) {
// thread-locals that have been marked as local through a linker script)
if (!s->isPreemptible && !config->shared)
continue;
- mainPart->relaDyn->addSymbolReloc(target->tlsModuleIndexRel, this,
+ mainPart->relaDyn->addSymbolReloc(target->tlsModuleIndexRel, *this,
offset, *s);
// However, we can skip writing the TLS offset reloc for non-preemptible
// symbols since it is known even in shared libraries
if (!s->isPreemptible)
continue;
offset += config->wordsize;
- mainPart->relaDyn->addSymbolReloc(target->tlsOffsetRel, this, offset,
+ mainPart->relaDyn->addSymbolReloc(target->tlsOffsetRel, *this, offset,
*s);
}
}
// Dynamic relocations for "global" entries.
for (const std::pair<Symbol *, size_t> &p : got.global) {
uint64_t offset = p.second * config->wordsize;
- mainPart->relaDyn->addSymbolReloc(target->relativeRel, this, offset,
+ mainPart->relaDyn->addSymbolReloc(target->relativeRel, *this, offset,
*p.first);
}
if (!config->isPic)
// For files without related GOT or files refer a primary GOT
// returns "common" _gp value. For secondary GOTs calculate
// individual _gp values.
- if (!f || !f->mipsGotIndex.hasValue() || *f->mipsGotIndex == 0)
+ if (!f || f->mipsGotIndex == uint32_t(-1) || f->mipsGotIndex == 0)
return ElfSym::mipsGp->getVA(0);
- return getVA() + gots[*f->mipsGotIndex].startIndex * config->wordsize +
- 0x7ff0;
+ return getVA() + gots[f->mipsGotIndex].startIndex * config->wordsize + 0x7ff0;
}
void MipsGotSection::writeTo(uint8_t *buf) {
if (p.first == nullptr && !config->shared)
write(p.second, nullptr, 1);
else if (p.first && !p.first->isPreemptible) {
- // If we are emitting a shared libary with relocations we mustn't write
+ // If we are emitting a shared library with relocations we mustn't write
// anything to the GOT here. When using Elf_Rel relocations the value
// one will be treated as an addend and will cause crashes at runtime
if (!config->shared)
}
void GotPltSection::addEntry(Symbol &sym) {
- assert(sym.pltIndex == entries.size());
+ assert(sym.auxIdx == symAux.size() - 1 &&
+ symAux.back().pltIdx == entries.size());
entries.push_back(&sym);
}
target->gotEntrySize, getIgotPltName()) {}
void IgotPltSection::addEntry(Symbol &sym) {
- assert(sym.pltIndex == entries.size());
+ assert(symAux.back().pltIdx == entries.size());
entries.push_back(&sym);
}
: SyntheticSection(dynamic ? (uint64_t)SHF_ALLOC : 0, SHT_STRTAB, 1, name),
dynamic(dynamic) {
// ELF string tables start with a NUL byte.
- addString("");
+ strings.push_back("");
+ stringMap.try_emplace(CachedHashStringRef(""), 0);
+ size = 1;
}
// Adds a string to the string table. If `hashIt` is true we hash and check for
// them with some other string that happens to be the same.
unsigned StringTableSection::addString(StringRef s, bool hashIt) {
if (hashIt) {
- auto r = stringMap.insert(std::make_pair(s, this->size));
+ auto r = stringMap.try_emplace(CachedHashStringRef(s), size);
if (!r.second)
return r.first->second;
}
+ if (s.empty())
+ return 0;
unsigned ret = this->size;
this->size = this->size + s.size() + 1;
strings.push_back(s);
this->flags = SHF_ALLOC;
}
-template <class ELFT>
-void DynamicSection<ELFT>::add(int32_t tag, std::function<uint64_t()> fn) {
- entries.push_back({tag, fn});
-}
-
-template <class ELFT>
-void DynamicSection<ELFT>::addInt(int32_t tag, uint64_t val) {
- entries.push_back({tag, [=] { return val; }});
-}
-
-template <class ELFT>
-void DynamicSection<ELFT>::addInSec(int32_t tag, InputSection *sec) {
- entries.push_back({tag, [=] { return sec->getVA(0); }});
-}
-
-template <class ELFT>
-void DynamicSection<ELFT>::addInSecRelative(int32_t tag, InputSection *sec) {
- size_t tagOffset = entries.size() * entsize;
- entries.push_back(
- {tag, [=] { return sec->getVA(0) - (getVA() + tagOffset); }});
-}
-
-template <class ELFT>
-void DynamicSection<ELFT>::addOutSec(int32_t tag, OutputSection *sec) {
- entries.push_back({tag, [=] { return sec->addr; }});
-}
-
-template <class ELFT>
-void DynamicSection<ELFT>::addSize(int32_t tag, OutputSection *sec) {
- entries.push_back({tag, [=] { return sec->size; }});
-}
-
-template <class ELFT>
-void DynamicSection<ELFT>::addSym(int32_t tag, Symbol *sym) {
- entries.push_back({tag, [=] { return sym->getVA(); }});
-}
-
// The output section .rela.dyn may include these synthetic sections:
//
// - part.relaDyn
// .rela.dyn
//
// DT_RELASZ is the total size of the included sections.
-static std::function<uint64_t()> addRelaSz(RelocationBaseSection *relaDyn) {
- return [=]() {
- size_t size = relaDyn->getSize();
- if (in.relaIplt->getParent() == relaDyn->getParent())
- size += in.relaIplt->getSize();
- if (in.relaPlt->getParent() == relaDyn->getParent())
- size += in.relaPlt->getSize();
- return size;
- };
+static uint64_t addRelaSz(const RelocationBaseSection &relaDyn) {
+ size_t size = relaDyn.getSize();
+ if (in.relaIplt->getParent() == relaDyn.getParent())
+ size += in.relaIplt->getSize();
+ if (in.relaPlt->getParent() == relaDyn.getParent())
+ size += in.relaPlt->getSize();
+ return size;
}
// A Linker script may assign the RELA relocation sections to the same
}
// Add remaining entries to complete .dynamic contents.
-template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
+template <class ELFT>
+std::vector<std::pair<int32_t, uint64_t>>
+DynamicSection<ELFT>::computeContents() {
elf::Partition &part = getPartition();
bool isMain = part.name.empty();
+ std::vector<std::pair<int32_t, uint64_t>> entries;
+
+ auto addInt = [&](int32_t tag, uint64_t val) {
+ entries.emplace_back(tag, val);
+ };
+ auto addInSec = [&](int32_t tag, const InputSection &sec) {
+ entries.emplace_back(tag, sec.getVA());
+ };
for (StringRef s : config->filterList)
addInt(DT_FILTER, part.dynStrTab->addString(s));
addInt(config->enableNewDtags ? DT_RUNPATH : DT_RPATH,
part.dynStrTab->addString(config->rpath));
- for (SharedFile *file : sharedFiles)
+ for (SharedFile *file : ctx.sharedFiles)
if (file->isNeeded)
addInt(DT_NEEDED, part.dynStrTab->addString(file->soName));
}
if (!config->zText)
dtFlags |= DF_TEXTREL;
- if (config->hasStaticTlsModel)
+ if (ctx.hasTlsIe && config->shared)
dtFlags |= DF_STATIC_TLS;
if (dtFlags)
if (!config->shared && !config->relocatable && !config->zRodynamic)
addInt(DT_DEBUG, 0);
- if (OutputSection *sec = part.dynStrTab->getParent())
- this->link = sec->sectionIndex;
-
if (part.relaDyn->isNeeded() ||
(in.relaIplt->isNeeded() &&
part.relaDyn->getParent() == in.relaIplt->getParent())) {
- addInSec(part.relaDyn->dynamicTag, part.relaDyn);
- entries.push_back({part.relaDyn->sizeDynamicTag, addRelaSz(part.relaDyn)});
+ addInSec(part.relaDyn->dynamicTag, *part.relaDyn);
+ entries.emplace_back(part.relaDyn->sizeDynamicTag,
+ addRelaSz(*part.relaDyn));
bool isRela = config->isRela;
addInt(isRela ? DT_RELAENT : DT_RELENT,
addInt(isRela ? DT_RELACOUNT : DT_RELCOUNT, numRelativeRels);
}
}
- if (part.relrDyn && !part.relrDyn->relocs.empty()) {
+ if (part.relrDyn && part.relrDyn->getParent() &&
+ !part.relrDyn->relocs.empty()) {
addInSec(config->useAndroidRelrTags ? DT_ANDROID_RELR : DT_RELR,
- part.relrDyn);
- addSize(config->useAndroidRelrTags ? DT_ANDROID_RELRSZ : DT_RELRSZ,
- part.relrDyn->getParent());
+ *part.relrDyn);
+ addInt(config->useAndroidRelrTags ? DT_ANDROID_RELRSZ : DT_RELRSZ,
+ part.relrDyn->getParent()->size);
addInt(config->useAndroidRelrTags ? DT_ANDROID_RELRENT : DT_RELRENT,
sizeof(Elf_Relr));
}
// case, so here we always use relaPlt as marker for the beginning of
// .rel[a].plt section.
if (isMain && (in.relaPlt->isNeeded() || in.relaIplt->isNeeded())) {
- addInSec(DT_JMPREL, in.relaPlt);
- entries.push_back({DT_PLTRELSZ, addPltRelSz});
+ addInSec(DT_JMPREL, *in.relaPlt);
+ entries.emplace_back(DT_PLTRELSZ, addPltRelSz());
switch (config->emachine) {
case EM_MIPS:
- addInSec(DT_MIPS_PLTGOT, in.gotPlt);
+ addInSec(DT_MIPS_PLTGOT, *in.gotPlt);
break;
case EM_SPARCV9:
- addInSec(DT_PLTGOT, in.plt);
+ addInSec(DT_PLTGOT, *in.plt);
break;
case EM_AARCH64:
if (llvm::find_if(in.relaPlt->relocs, [](const DynamicReloc &r) {
r.sym->stOther & STO_AARCH64_VARIANT_PCS;
}) != in.relaPlt->relocs.end())
addInt(DT_AARCH64_VARIANT_PCS, 0);
- LLVM_FALLTHROUGH;
+ addInSec(DT_PLTGOT, *in.gotPlt);
+ break;
+ case EM_RISCV:
+ if (llvm::any_of(in.relaPlt->relocs, [](const DynamicReloc &r) {
+ return r.type == target->pltRel &&
+ (r.sym->stOther & STO_RISCV_VARIANT_CC);
+ }))
+ addInt(DT_RISCV_VARIANT_CC, 0);
+ [[fallthrough]];
default:
- addInSec(DT_PLTGOT, in.gotPlt);
+ addInSec(DT_PLTGOT, *in.gotPlt);
break;
}
addInt(DT_PLTREL, config->isRela ? DT_RELA : DT_REL);
addInt(DT_AARCH64_PAC_PLT, 0);
}
- addInSec(DT_SYMTAB, part.dynSymTab);
+ addInSec(DT_SYMTAB, *part.dynSymTab);
addInt(DT_SYMENT, sizeof(Elf_Sym));
- addInSec(DT_STRTAB, part.dynStrTab);
+ addInSec(DT_STRTAB, *part.dynStrTab);
addInt(DT_STRSZ, part.dynStrTab->getSize());
if (!config->zText)
addInt(DT_TEXTREL, 0);
- if (part.gnuHashTab)
- addInSec(DT_GNU_HASH, part.gnuHashTab);
- if (part.hashTab)
- addInSec(DT_HASH, part.hashTab);
+ if (part.gnuHashTab && part.gnuHashTab->getParent())
+ addInSec(DT_GNU_HASH, *part.gnuHashTab);
+ if (part.hashTab && part.hashTab->getParent())
+ addInSec(DT_HASH, *part.hashTab);
if (isMain) {
if (Out::preinitArray) {
- addOutSec(DT_PREINIT_ARRAY, Out::preinitArray);
- addSize(DT_PREINIT_ARRAYSZ, Out::preinitArray);
+ addInt(DT_PREINIT_ARRAY, Out::preinitArray->addr);
+ addInt(DT_PREINIT_ARRAYSZ, Out::preinitArray->size);
}
if (Out::initArray) {
- addOutSec(DT_INIT_ARRAY, Out::initArray);
- addSize(DT_INIT_ARRAYSZ, Out::initArray);
+ addInt(DT_INIT_ARRAY, Out::initArray->addr);
+ addInt(DT_INIT_ARRAYSZ, Out::initArray->size);
}
if (Out::finiArray) {
- addOutSec(DT_FINI_ARRAY, Out::finiArray);
- addSize(DT_FINI_ARRAYSZ, Out::finiArray);
+ addInt(DT_FINI_ARRAY, Out::finiArray->addr);
+ addInt(DT_FINI_ARRAYSZ, Out::finiArray->size);
}
- if (Symbol *b = symtab->find(config->init))
+ if (Symbol *b = symtab.find(config->init))
if (b->isDefined())
- addSym(DT_INIT, b);
- if (Symbol *b = symtab->find(config->fini))
+ addInt(DT_INIT, b->getVA());
+ if (Symbol *b = symtab.find(config->fini))
if (b->isDefined())
- addSym(DT_FINI, b);
+ addInt(DT_FINI, b->getVA());
}
if (part.verSym && part.verSym->isNeeded())
- addInSec(DT_VERSYM, part.verSym);
+ addInSec(DT_VERSYM, *part.verSym);
if (part.verDef && part.verDef->isLive()) {
- addInSec(DT_VERDEF, part.verDef);
+ addInSec(DT_VERDEF, *part.verDef);
addInt(DT_VERDEFNUM, getVerDefNum());
}
if (part.verNeed && part.verNeed->isNeeded()) {
- addInSec(DT_VERNEED, part.verNeed);
+ addInSec(DT_VERNEED, *part.verNeed);
unsigned needNum = 0;
- for (SharedFile *f : sharedFiles)
+ for (SharedFile *f : ctx.sharedFiles)
if (!f->vernauxs.empty())
++needNum;
addInt(DT_VERNEEDNUM, needNum);
addInt(DT_MIPS_FLAGS, RHF_NOTPOT);
addInt(DT_MIPS_BASE_ADDRESS, target->getImageBase());
addInt(DT_MIPS_SYMTABNO, part.dynSymTab->getNumSymbols());
-
- add(DT_MIPS_LOCAL_GOTNO, [] { return in.mipsGot->getLocalEntriesNum(); });
+ addInt(DT_MIPS_LOCAL_GOTNO, in.mipsGot->getLocalEntriesNum());
if (const Symbol *b = in.mipsGot->getFirstGlobalEntry())
addInt(DT_MIPS_GOTSYM, b->dynsymIndex);
else
addInt(DT_MIPS_GOTSYM, part.dynSymTab->getNumSymbols());
- addInSec(DT_PLTGOT, in.mipsGot);
+ addInSec(DT_PLTGOT, *in.mipsGot);
if (in.mipsRldMap) {
if (!config->pie)
- addInSec(DT_MIPS_RLD_MAP, in.mipsRldMap);
+ addInSec(DT_MIPS_RLD_MAP, *in.mipsRldMap);
// Store the offset to the .rld_map section
// relative to the address of the tag.
- addInSecRelative(DT_MIPS_RLD_MAP_REL, in.mipsRldMap);
+ addInt(DT_MIPS_RLD_MAP_REL,
+ in.mipsRldMap->getVA() - (getVA() + entries.size() * entsize));
}
}
// DT_PPC_GOT indicates to glibc Secure PLT is used. If DT_PPC_GOT is absent,
// glibc assumes the old-style BSS PLT layout which we don't support.
if (config->emachine == EM_PPC)
- add(DT_PPC_GOT, [] { return in.got->getVA(); });
+ addInSec(DT_PPC_GOT, *in.got);
// Glink dynamic tag is required by the V2 abi if the plt section isn't empty.
if (config->emachine == EM_PPC64 && in.plt->isNeeded()) {
// The Glink tag points to 32 bytes before the first lazy symbol resolution
// stub, which starts directly after the header.
- entries.push_back({DT_PPC64_GLINK, [=] {
- unsigned offset = target->pltHeaderSize - 32;
- return in.plt->getVA(0) + offset;
- }});
+ addInt(DT_PPC64_GLINK, in.plt->getVA() + target->pltHeaderSize - 32);
}
addInt(DT_NULL, 0);
+ return entries;
+}
- getParent()->link = this->link;
- this->size = entries.size() * this->entsize;
+template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
+ if (OutputSection *sec = getPartition().dynStrTab->getParent())
+ getParent()->link = sec->sectionIndex;
+ this->size = computeContents().size() * this->entsize;
}
template <class ELFT> void DynamicSection<ELFT>::writeTo(uint8_t *buf) {
auto *p = reinterpret_cast<Elf_Dyn *>(buf);
- for (std::pair<int32_t, std::function<uint64_t()>> &kv : entries) {
+ for (std::pair<int32_t, uint64_t> kv : computeContents()) {
p->d_tag = kv.first;
- p->d_un.d_val = kv.second();
+ p->d_un.d_val = kv.second;
++p;
}
}
}
uint32_t DynamicReloc::getSymIndex(SymbolTableBaseSection *symTab) const {
- if (needsDynSymIndex())
- return symTab->getSymbolIndex(sym);
- return 0;
+ if (!needsDynSymIndex())
+ return 0;
+
+ size_t index = symTab->getSymbolIndex(sym);
+ assert((index != 0 || (type != target->gotRel && type != target->pltRel) ||
+ !mainPart->dynSymTab->getParent()) &&
+ "GOT or PLT relocation must refer to symbol in dynamic symbol table");
+ return index;
}
RelocationBaseSection::RelocationBaseSection(StringRef name, uint32_t type,
int32_t dynamicTag,
- int32_t sizeDynamicTag)
+ int32_t sizeDynamicTag,
+ bool combreloc,
+ unsigned concurrency)
: SyntheticSection(SHF_ALLOC, type, config->wordsize, name),
- dynamicTag(dynamicTag), sizeDynamicTag(sizeDynamicTag) {}
+ dynamicTag(dynamicTag), sizeDynamicTag(sizeDynamicTag),
+ relocsVec(concurrency), combreloc(combreloc) {}
-void RelocationBaseSection::addSymbolReloc(RelType dynType,
- InputSectionBase *isec,
- uint64_t offsetInSec, Symbol &sym,
- int64_t addend,
- Optional<RelType> addendRelType) {
+void RelocationBaseSection::addSymbolReloc(
+ RelType dynType, InputSectionBase &isec, uint64_t offsetInSec, Symbol &sym,
+ int64_t addend, std::optional<RelType> addendRelType) {
addReloc(DynamicReloc::AgainstSymbol, dynType, isec, offsetInSec, sym, addend,
R_ADDEND, addendRelType ? *addendRelType : target->noneRel);
}
-void RelocationBaseSection::addRelativeReloc(
- RelType dynType, InputSectionBase *inputSec, uint64_t offsetInSec,
- Symbol &sym, int64_t addend, RelType addendRelType, RelExpr expr) {
- // This function should only be called for non-preemptible symbols or
- // RelExpr values that refer to an address inside the output file (e.g. the
- // address of the GOT entry for a potentially preemptible symbol).
- assert((!sym.isPreemptible || expr == R_GOT) &&
- "cannot add relative relocation against preemptible symbol");
- assert(expr != R_ADDEND && "expected non-addend relocation expression");
- addReloc(DynamicReloc::AddendOnlyWithTargetVA, dynType, inputSec, offsetInSec,
- sym, addend, expr, addendRelType);
-}
-
void RelocationBaseSection::addAddendOnlyRelocIfNonPreemptible(
- RelType dynType, InputSectionBase *isec, uint64_t offsetInSec, Symbol &sym,
+ RelType dynType, GotSection &sec, uint64_t offsetInSec, Symbol &sym,
RelType addendRelType) {
// No need to write an addend to the section for preemptible symbols.
if (sym.isPreemptible)
- addReloc({dynType, isec, offsetInSec, DynamicReloc::AgainstSymbol, sym, 0,
+ addReloc({dynType, &sec, offsetInSec, DynamicReloc::AgainstSymbol, sym, 0,
R_ABS});
else
- addReloc(DynamicReloc::AddendOnlyWithTargetVA, dynType, isec, offsetInSec,
+ addReloc(DynamicReloc::AddendOnlyWithTargetVA, dynType, sec, offsetInSec,
sym, 0, R_ABS, addendRelType);
}
-void RelocationBaseSection::addReloc(DynamicReloc::Kind kind, RelType dynType,
- InputSectionBase *inputSec,
- uint64_t offsetInSec, Symbol &sym,
- int64_t addend, RelExpr expr,
- RelType addendRelType) {
- // Write the addends to the relocated address if required. We skip
- // it if the written value would be zero.
- if (config->writeAddends && (expr != R_ADDEND || addend != 0))
- inputSec->relocations.push_back(
- {expr, addendRelType, offsetInSec, addend, &sym});
- addReloc({dynType, inputSec, offsetInSec, kind, sym, addend, expr});
+void RelocationBaseSection::mergeRels() {
+ size_t newSize = relocs.size();
+ for (const auto &v : relocsVec)
+ newSize += v.size();
+ relocs.reserve(newSize);
+ for (const auto &v : relocsVec)
+ llvm::append_range(relocs, v);
+ relocsVec.clear();
}
-void RelocationBaseSection::addReloc(const DynamicReloc &reloc) {
- if (reloc.type == target->relativeRel)
- ++numRelativeRelocs;
- relocs.push_back(reloc);
+void RelocationBaseSection::partitionRels() {
+ if (!combreloc)
+ return;
+ const RelType relativeRel = target->relativeRel;
+ numRelativeRelocs =
+ llvm::partition(relocs, [=](auto &r) { return r.type == relativeRel; }) -
+ relocs.begin();
}
void RelocationBaseSection::finalizeContents() {
- SymbolTableBaseSection *symTab = getPartition().dynSymTab;
+ SymbolTableBaseSection *symTab = getPartition().dynSymTab.get();
// When linking glibc statically, .rel{,a}.plt contains R_*_IRELATIVE
// relocations due to IFUNC (e.g. strcpy). sh_link will be set to 0 in that
else
getParent()->link = 0;
- if (in.relaPlt == this) {
+ if (in.relaPlt.get() == this && in.gotPlt->getParent()) {
getParent()->flags |= ELF::SHF_INFO_LINK;
getParent()->info = in.gotPlt->getParent()->sectionIndex;
}
- if (in.relaIplt == this) {
+ if (in.relaIplt.get() == this && in.igotPlt->getParent()) {
getParent()->flags |= ELF::SHF_INFO_LINK;
getParent()->info = in.igotPlt->getParent()->sectionIndex;
}
}
-RelrBaseSection::RelrBaseSection()
- : SyntheticSection(SHF_ALLOC,
- config->useAndroidRelrTags ? SHT_ANDROID_RELR : SHT_RELR,
- config->wordsize, ".relr.dyn") {}
+void DynamicReloc::computeRaw(SymbolTableBaseSection *symtab) {
+ r_offset = getOffset();
+ r_sym = getSymIndex(symtab);
+ addend = computeAddend();
+ kind = AddendOnly; // Catch errors
+}
-template <class ELFT>
-static void encodeDynamicReloc(SymbolTableBaseSection *symTab,
- typename ELFT::Rela *p,
- const DynamicReloc &rel) {
- if (config->isRela)
- p->r_addend = rel.computeAddend();
- p->r_offset = rel.getOffset();
- p->setSymbolAndType(rel.getSymIndex(symTab), rel.type, config->isMips64EL);
+void RelocationBaseSection::computeRels() {
+ SymbolTableBaseSection *symTab = getPartition().dynSymTab.get();
+ parallelForEach(relocs,
+ [symTab](DynamicReloc &rel) { rel.computeRaw(symTab); });
+ // Sort by (!IsRelative,SymIndex,r_offset). DT_REL[A]COUNT requires us to
+ // place R_*_RELATIVE first. SymIndex is to improve locality, while r_offset
+ // is to make results easier to read.
+ if (combreloc) {
+ auto nonRelative = relocs.begin() + numRelativeRelocs;
+ parallelSort(relocs.begin(), nonRelative,
+ [&](auto &a, auto &b) { return a.r_offset < b.r_offset; });
+ // Non-relative relocations are few, so don't bother with parallelSort.
+ llvm::sort(nonRelative, relocs.end(), [&](auto &a, auto &b) {
+ return std::tie(a.r_sym, a.r_offset) < std::tie(b.r_sym, b.r_offset);
+ });
+ }
}
template <class ELFT>
-RelocationSection<ELFT>::RelocationSection(StringRef name, bool sort)
+RelocationSection<ELFT>::RelocationSection(StringRef name, bool combreloc,
+ unsigned concurrency)
: RelocationBaseSection(name, config->isRela ? SHT_RELA : SHT_REL,
config->isRela ? DT_RELA : DT_REL,
- config->isRela ? DT_RELASZ : DT_RELSZ),
- sort(sort) {
+ config->isRela ? DT_RELASZ : DT_RELSZ, combreloc,
+ concurrency) {
this->entsize = config->isRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel);
}
template <class ELFT> void RelocationSection<ELFT>::writeTo(uint8_t *buf) {
- SymbolTableBaseSection *symTab = getPartition().dynSymTab;
-
- // Sort by (!IsRelative,SymIndex,r_offset). DT_REL[A]COUNT requires us to
- // place R_*_RELATIVE first. SymIndex is to improve locality, while r_offset
- // is to make results easier to read.
- if (sort)
- llvm::stable_sort(
- relocs, [&](const DynamicReloc &a, const DynamicReloc &b) {
- return std::make_tuple(a.type != target->relativeRel,
- a.getSymIndex(symTab), a.getOffset()) <
- std::make_tuple(b.type != target->relativeRel,
- b.getSymIndex(symTab), b.getOffset());
- });
-
+ computeRels();
for (const DynamicReloc &rel : relocs) {
- encodeDynamicReloc<ELFT>(symTab, reinterpret_cast<Elf_Rela *>(buf), rel);
+ auto *p = reinterpret_cast<Elf_Rela *>(buf);
+ p->r_offset = rel.r_offset;
+ p->setSymbolAndType(rel.r_sym, rel.type, config->isMips64EL);
+ if (config->isRela)
+ p->r_addend = rel.addend;
buf += config->isRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel);
}
}
+RelrBaseSection::RelrBaseSection(unsigned concurrency)
+ : SyntheticSection(SHF_ALLOC,
+ config->useAndroidRelrTags ? SHT_ANDROID_RELR : SHT_RELR,
+ config->wordsize, ".relr.dyn"),
+ relocsVec(concurrency) {}
+
+void RelrBaseSection::mergeRels() {
+ size_t newSize = relocs.size();
+ for (const auto &v : relocsVec)
+ newSize += v.size();
+ relocs.reserve(newSize);
+ for (const auto &v : relocsVec)
+ llvm::append_range(relocs, v);
+ relocsVec.clear();
+}
+
template <class ELFT>
AndroidPackedRelocationSection<ELFT>::AndroidPackedRelocationSection(
- StringRef name)
+ StringRef name, unsigned concurrency)
: RelocationBaseSection(
name, config->isRela ? SHT_ANDROID_RELA : SHT_ANDROID_REL,
config->isRela ? DT_ANDROID_RELA : DT_ANDROID_REL,
- config->isRela ? DT_ANDROID_RELASZ : DT_ANDROID_RELSZ) {
+ config->isRela ? DT_ANDROID_RELASZ : DT_ANDROID_RELSZ,
+ /*combreloc=*/false, concurrency) {
this->entsize = 1;
}
for (const DynamicReloc &rel : relocs) {
Elf_Rela r;
- encodeDynamicReloc<ELFT>(getPartition().dynSymTab, &r, rel);
+ r.r_offset = rel.getOffset();
+ r.setSymbolAndType(rel.getSymIndex(getPartition().dynSymTab.get()),
+ rel.type, false);
+ r.r_addend = config->isRela ? rel.computeAddend() : 0;
if (r.getType(config->isMips64EL) == target->relativeRel)
relatives.push_back(r);
//
// For Rela, we also want to sort by r_addend when r_info is the same. This
// enables us to group by r_addend as well.
- llvm::stable_sort(nonRelatives, [](const Elf_Rela &a, const Elf_Rela &b) {
+ llvm::sort(nonRelatives, [](const Elf_Rela &a, const Elf_Rela &b) {
if (a.r_info != b.r_info)
return a.r_info < b.r_info;
- if (config->isRela)
+ if (a.r_addend != b.r_addend)
return a.r_addend < b.r_addend;
- return false;
+ return a.r_offset < b.r_offset;
});
// Group relocations with the same r_info. Note that each group emits a group
add(config->wordsize);
add(target->relativeRel);
if (config->isRela) {
- for (auto i = g.begin() + 1, e = g.end(); i != e; ++i) {
- add(i->r_addend - addend);
- addend = i->r_addend;
+ for (const auto &i : llvm::drop_begin(g)) {
+ add(i.r_addend - addend);
+ addend = i.r_addend;
}
}
return relocData.size() != oldSize;
}
-template <class ELFT> RelrSection<ELFT>::RelrSection() {
+template <class ELFT>
+RelrSection<ELFT>::RelrSection(unsigned concurrency)
+ : RelrBaseSection(concurrency) {
this->entsize = config->wordsize;
}
const size_t nBits = wordsize * 8 - 1;
// Get offsets for all relative relocations and sort them.
- std::vector<uint64_t> offsets;
- for (const RelativeReloc &rel : relocs)
- offsets.push_back(rel.getOffset());
- llvm::sort(offsets);
+ std::unique_ptr<uint64_t[]> offsets(new uint64_t[relocs.size()]);
+ for (auto [i, r] : llvm::enumerate(relocs))
+ offsets[i] = r.getOffset();
+ llvm::sort(offsets.get(), offsets.get() + relocs.size());
// For each leading relocation, find following ones that can be folded
// as a bitmap and fold them.
- for (size_t i = 0, e = offsets.size(); i < e;) {
+ for (size_t i = 0, e = relocs.size(); i != e;) {
// Add a leading relocation.
relrRelocs.push_back(Elf_Relr(offsets[i]));
uint64_t base = offsets[i] + wordsize;
++i;
// Find foldable relocations to construct bitmaps.
- while (i < e) {
+ for (;;) {
uint64_t bitmap = 0;
-
- while (i < e) {
- uint64_t delta = offsets[i] - base;
-
- // If it is too far, it cannot be folded.
- if (delta >= nBits * wordsize)
+ for (; i != e; ++i) {
+ uint64_t d = offsets[i] - base;
+ if (d >= nBits * wordsize || d % wordsize)
break;
-
- // If it is not a multiple of wordsize away, it cannot be folded.
- if (delta % wordsize)
- break;
-
- // Fold it.
- bitmap |= 1ULL << (delta / wordsize);
- ++i;
+ bitmap |= uint64_t(1) << (d / wordsize);
}
-
if (!bitmap)
break;
-
relrRelocs.push_back(Elf_Relr((bitmap << 1) | 1));
base += nBits * wordsize;
}
// Sort entries related to non-local preemptible symbols by GOT indexes.
// All other entries go to the beginning of a dynsym in arbitrary order.
if (l.sym->isInGot() && r.sym->isInGot())
- return l.sym->gotIndex < r.sym->gotIndex;
+ return l.sym->getGotIdx() < r.sym->getGotIdx();
if (!l.sym->isInGot() && !r.sym->isInGot())
return false;
return !l.sym->isInGot();
// Only the main partition's dynsym indexes are stored in the symbols
// themselves. All other partitions use a lookup table.
- if (this == mainPart->dynSymTab) {
+ if (this == mainPart->dynSymTab.get()) {
size_t i = 0;
for (const SymbolTableEntry &s : symbols)
s.sym->dynsymIndex = ++i;
void SymbolTableBaseSection::sortSymTabSymbols() {
// Move all local symbols before global symbols.
auto e = std::stable_partition(
- symbols.begin(), symbols.end(), [](const SymbolTableEntry &s) {
- return s.sym->isLocal() || s.sym->computeBinding() == STB_LOCAL;
- });
+ symbols.begin(), symbols.end(),
+ [](const SymbolTableEntry &s) { return s.sym->isLocal(); });
size_t numLocals = e - symbols.begin();
getParent()->info = numLocals + 1;
// symbols, they are already naturally placed first in each group. That
// happens because STT_FILE is always the first symbol in the object and hence
// precede all other local symbols we add for a file.
- MapVector<InputFile *, std::vector<SymbolTableEntry>> arr;
+ MapVector<InputFile *, SmallVector<SymbolTableEntry, 0>> arr;
for (const SymbolTableEntry &s : llvm::make_range(symbols.begin(), e))
arr[s.sym->file].push_back(s);
auto i = symbols.begin();
- for (std::pair<InputFile *, std::vector<SymbolTableEntry>> &p : arr)
+ for (auto &p : arr)
for (SymbolTableEntry &entry : p.second)
*i++ = entry;
}
void SymbolTableBaseSection::addSymbol(Symbol *b) {
// Adding a local symbol to a .dynsym is a bug.
assert(this->type != SHT_DYNSYM || !b->isLocal());
-
- bool hashIt = b->isLocal();
- symbols.push_back({b, strTabSec.addString(b->getName(), hashIt)});
+ symbols.push_back({b, strTabSec.addString(b->getName(), false)});
}
size_t SymbolTableBaseSection::getSymbolIndex(Symbol *sym) {
- if (this == mainPart->dynSymTab)
+ if (this == mainPart->dynSymTab.get())
return sym->dynsymIndex;
// Initializes symbol lookup tables lazily. This is used only for -r,
- // -emit-relocs and dynsyms in partitions other than the main one.
+ // --emit-relocs and dynsyms in partitions other than the main one.
llvm::call_once(onceFlag, [&] {
symbolIndexMap.reserve(symbols.size());
size_t i = 0;
}
static BssSection *getCommonSec(Symbol *sym) {
- if (!config->defineCommon)
+ if (config->relocatable)
if (auto *d = dyn_cast<Defined>(sym))
return dyn_cast_or_null<BssSection>(d->section);
return nullptr;
}
static uint32_t getSymSectionIndex(Symbol *sym) {
- if (getCommonSec(sym))
- return SHN_COMMON;
- if (!isa<Defined>(sym) || sym->needsPltAddr)
+ assert(!(sym->hasFlag(NEEDS_COPY) && sym->isObject()));
+ if (!isa<Defined>(sym) || sym->hasFlag(NEEDS_COPY))
return SHN_UNDEF;
if (const OutputSection *os = sym->getOutputSection())
return os->sectionIndex >= SHN_LORESERVE ? (uint32_t)SHN_XINDEX
// Write the internal symbol table contents to the output symbol table.
template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *buf) {
// The first entry is a null entry as per the ELF spec.
- memset(buf, 0, sizeof(Elf_Sym));
buf += sizeof(Elf_Sym);
auto *eSym = reinterpret_cast<Elf_Sym *>(buf);
Symbol *sym = ent.sym;
bool isDefinedHere = type == SHT_SYMTAB || sym->partition == partition;
- // Set st_info and st_other.
- eSym->st_other = 0;
- if (sym->isLocal()) {
- eSym->setBindingAndType(STB_LOCAL, sym->type);
+ // Set st_name, st_info and st_other.
+ eSym->st_name = ent.strTabOffset;
+ eSym->setBindingAndType(sym->binding, sym->type);
+ eSym->st_other = sym->stOther;
+
+ if (BssSection *commonSec = getCommonSec(sym)) {
+ // When -r is specified, a COMMON symbol is not allocated. Its st_shndx
+ // holds SHN_COMMON and st_value holds the alignment.
+ eSym->st_shndx = SHN_COMMON;
+ eSym->st_value = commonSec->addralign;
+ eSym->st_size = cast<Defined>(sym)->size;
} else {
- eSym->setBindingAndType(sym->computeBinding(), sym->type);
- eSym->setVisibility(sym->visibility);
+ const uint32_t shndx = getSymSectionIndex(sym);
+ if (isDefinedHere) {
+ eSym->st_shndx = shndx;
+ eSym->st_value = sym->getVA();
+ // Copy symbol size if it is a defined symbol. st_size is not
+ // significant for undefined symbols, so whether copying it or not is up
+ // to us if that's the case. We'll leave it as zero because by not
+ // setting a value, we can get the exact same outputs for two sets of
+ // input files that differ only in undefined symbol size in DSOs.
+ eSym->st_size = shndx != SHN_UNDEF ? cast<Defined>(sym)->size : 0;
+ } else {
+ eSym->st_shndx = 0;
+ eSym->st_value = 0;
+ eSym->st_size = 0;
+ }
}
- // The 3 most significant bits of st_other are used by OpenPOWER ABI.
- // See getPPC64GlobalEntryToLocalEntryOffset() for more details.
- if (config->emachine == EM_PPC64)
- eSym->st_other |= sym->stOther & 0xe0;
- // The most significant bit of st_other is used by AArch64 ABI for the
- // variant PCS.
- else if (config->emachine == EM_AARCH64)
- eSym->st_other |= sym->stOther & STO_AARCH64_VARIANT_PCS;
-
- eSym->st_name = ent.strTabOffset;
- if (isDefinedHere)
- eSym->st_shndx = getSymSectionIndex(ent.sym);
- else
- eSym->st_shndx = 0;
-
- // Copy symbol size if it is a defined symbol. st_size is not significant
- // for undefined symbols, so whether copying it or not is up to us if that's
- // the case. We'll leave it as zero because by not setting a value, we can
- // get the exact same outputs for two sets of input files that differ only
- // in undefined symbol size in DSOs.
- if (eSym->st_shndx == SHN_UNDEF || !isDefinedHere)
- eSym->st_size = 0;
- else
- eSym->st_size = sym->getSize();
-
- // st_value is usually an address of a symbol, but that has a special
- // meaning for uninstantiated common symbols (--no-define-common).
- if (BssSection *commonSec = getCommonSec(ent.sym))
- eSym->st_value = commonSec->alignment;
- else if (isDefinedHere)
- eSym->st_value = sym->getVA();
- else
- eSym->st_value = 0;
-
++eSym;
}
for (SymbolTableEntry &ent : symbols) {
Symbol *sym = ent.sym;
- if (sym->isInPlt() && sym->needsPltAddr)
+ if (sym->isInPlt() && sym->hasFlag(NEEDS_COPY))
eSym->st_other |= STO_MIPS_PLT;
if (isMicroMips()) {
// We already set the less-significant bit for symbols
// like `objdump` will be able to deal with a correct
// symbol position.
if (sym->isDefined() &&
- ((sym->stOther & STO_MIPS_MICROMIPS) || sym->needsPltAddr)) {
+ ((sym->stOther & STO_MIPS_MICROMIPS) || sym->hasFlag(NEEDS_COPY))) {
if (!strTabSec.isDynamic())
eSym->st_value &= ~1;
eSym->st_other |= STO_MIPS_MICROMIPS;
// we need to write actual index, otherwise, we must write SHN_UNDEF(0).
buf += 4; // Ignore .symtab[0] entry.
for (const SymbolTableEntry &entry : in.symTab->getSymbols()) {
- if (getSymSectionIndex(entry.sym) == SHN_XINDEX)
+ if (!getCommonSec(entry.sym) && getSymSectionIndex(entry.sym) == SHN_XINDEX)
write32(buf, entry.sym->getOutputSection()->sectionIndex);
buf += 4;
}
// late, and we do not know them here. For simplicity, we just always create
// a .symtab_shndx section when the amount of output sections is huge.
size_t size = 0;
- for (BaseCommand *base : script->sectionCommands)
- if (isa<OutputSection>(base))
+ for (SectionCommand *cmd : script->sectionCommands)
+ if (isa<OutputDesc>(cmd))
++size;
return size >= SHN_LORESERVE;
}
// is to help the dynamic linker resolve symbols quickly. If ELF files
// don't have them, the dynamic linker has to do linear search on all
// dynamic symbols, which makes programs slower. Therefore, a .hash
-// section is added to a DSO by default. A .gnu.hash is added if you
-// give the -hash-style=gnu or -hash-style=both option.
+// section is added to a DSO by default.
//
// The Unix semantics of resolving dynamic symbols is somewhat expensive.
// Each ELF file has a list of DSOs that the ELF file depends on and a
// and better version of .hash. .hash is just an on-disk hash table, but
// .gnu.hash has a bloom filter in addition to a hash table to skip
// DSOs very quickly. If you are sure that your dynamic linker knows
-// about .gnu.hash, you want to specify -hash-style=gnu. Otherwise, a
-// safe bet is to specify -hash-style=both for backward compatibility.
+// about .gnu.hash, you want to specify --hash-style=gnu. Otherwise, a
+// safe bet is to specify --hash-style=both for backward compatibility.
GnuHashTableSection::GnuHashTableSection()
: SyntheticSection(SHF_ALLOC, SHT_GNU_HASH, config->wordsize, ".gnu.hash") {
}
}
void GnuHashTableSection::writeTo(uint8_t *buf) {
- // The output buffer is not guaranteed to be zero-cleared because we pre-
- // fill executable sections with trap instructions. This is a precaution
- // for that case, which happens only when -no-rosegment is given.
- memset(buf, 0, size);
-
// Write a header.
write32(buf, nBuckets);
write32(buf + 4, getPartition().dynSymTab->getNumSymbols() - symbols.size());
write32(buf + 12, Shift2);
buf += 16;
- // Write a bloom filter and a hash table.
- writeBloomFilter(buf);
- buf += config->wordsize * maskWords;
- writeHashTable(buf);
-}
-
-// This function writes a 2-bit bloom filter. This bloom filter alone
-// usually filters out 80% or more of all symbol lookups [1].
-// The dynamic linker uses the hash table only when a symbol is not
-// filtered out by a bloom filter.
-//
-// [1] Ulrich Drepper (2011), "How To Write Shared Libraries" (Ver. 4.1.2),
-// p.9, https://www.akkadia.org/drepper/dsohowto.pdf
-void GnuHashTableSection::writeBloomFilter(uint8_t *buf) {
- unsigned c = config->is64 ? 64 : 32;
+ // Write the 2-bit bloom filter.
+ const unsigned c = config->is64 ? 64 : 32;
for (const Entry &sym : symbols) {
// When C = 64, we choose a word with bits [6:...] and set 1 to two bits in
// the word using bits [0:5] and [26:31].
val |= uint64_t(1) << ((sym.hash >> Shift2) % c);
writeUint(buf + i * config->wordsize, val);
}
-}
+ buf += config->wordsize * maskWords;
-void GnuHashTableSection::writeHashTable(uint8_t *buf) {
+ // Write the hash table.
uint32_t *buckets = reinterpret_cast<uint32_t *>(buf);
uint32_t oldBucket = -1;
uint32_t *values = buckets + nBuckets;
}
}
-static uint32_t hashGnu(StringRef name) {
- uint32_t h = 5381;
- for (uint8_t c : name)
- h = (h << 5) + h + c;
- return h;
-}
-
// Add symbols to this symbol hash table. Note that this function
// destructively sort a given vector -- which is needed because
// GNU-style hash table places some sorting requirements.
-void GnuHashTableSection::addSymbols(std::vector<SymbolTableEntry> &v) {
+void GnuHashTableSection::addSymbols(SmallVectorImpl<SymbolTableEntry> &v) {
// We cannot use 'auto' for Mid because GCC 6.1 cannot deduce
// its type correctly.
- std::vector<SymbolTableEntry>::iterator mid =
+ auto mid =
std::stable_partition(v.begin(), v.end(), [&](const SymbolTableEntry &s) {
return !s.sym->isDefined() || s.sym->partition != partition;
});
symbols.push_back({b, ent.strTabOffset, hash, bucketIdx});
}
- llvm::stable_sort(symbols, [](const Entry &l, const Entry &r) {
- return l.bucketIdx < r.bucketIdx;
+ llvm::sort(symbols, [](const Entry &l, const Entry &r) {
+ return std::tie(l.bucketIdx, l.strTabOffset) <
+ std::tie(r.bucketIdx, r.strTabOffset);
});
v.erase(mid, v.end());
}
void HashTableSection::finalizeContents() {
- SymbolTableBaseSection *symTab = getPartition().dynSymTab;
+ SymbolTableBaseSection *symTab = getPartition().dynSymTab.get();
if (OutputSection *sec = symTab->getParent())
getParent()->link = sec->sectionIndex;
}
void HashTableSection::writeTo(uint8_t *buf) {
- SymbolTableBaseSection *symTab = getPartition().dynSymTab;
-
- // See comment in GnuHashTableSection::writeTo.
- memset(buf, 0, size);
-
+ SymbolTableBaseSection *symTab = getPartition().dynSymTab.get();
unsigned numSymbols = symTab->getNumSymbols();
uint32_t *p = reinterpret_cast<uint32_t *>(buf);
// On PowerPC, this section contains lazy symbol resolvers.
if (config->emachine == EM_PPC64) {
name = ".glink";
- alignment = 4;
+ addralign = 4;
}
// On x86 when IBT is enabled, this section contains the second PLT (lazy
}
void PltSection::addEntry(Symbol &sym) {
- sym.pltIndex = entries.size();
+ assert(sym.auxIdx == symAux.size() - 1);
+ symAux.back().pltIdx = entries.size();
entries.push_back(&sym);
}
: SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 16, ".iplt") {
if (config->emachine == EM_PPC || config->emachine == EM_PPC64) {
name = ".glink";
- alignment = 4;
+ addralign = 4;
}
}
}
void IpltSection::addEntry(Symbol &sym) {
- sym.pltIndex = entries.size();
+ assert(sym.auxIdx == symAux.size() - 1);
+ symAux.back().pltIdx = entries.size();
entries.push_back(&sym);
}
PPC32GlinkSection::PPC32GlinkSection() {
name = ".glink";
- alignment = 4;
+ addralign = 4;
}
void PPC32GlinkSection::writeTo(uint8_t *buf) {
return std::max<size_t>(NextPowerOf2(symbols.size() * 4 / 3), 1024);
}
-// Compute the output section size.
-void GdbIndexSection::initOutputSize() {
- size = sizeof(GdbIndexHeader) + computeSymtabSize() * 8;
-
- for (GdbChunk &chunk : chunks)
- size += chunk.compilationUnits.size() * 16 + chunk.addressAreas.size() * 20;
-
- // Add the constant pool size if exists.
- if (!symbols.empty()) {
- GdbSymbol &sym = symbols.back();
- size += sym.nameOff + sym.name.size() + 1;
- }
-}
-
-static std::vector<GdbIndexSection::CuEntry> readCuList(DWARFContext &dwarf) {
- std::vector<GdbIndexSection::CuEntry> ret;
+static SmallVector<GdbIndexSection::CuEntry, 0>
+readCuList(DWARFContext &dwarf) {
+ SmallVector<GdbIndexSection::CuEntry, 0> ret;
for (std::unique_ptr<DWARFUnit> &cu : dwarf.compile_units())
ret.push_back({cu->getOffset(), cu->getLength() + 4});
return ret;
}
-static std::vector<GdbIndexSection::AddressEntry>
+static SmallVector<GdbIndexSection::AddressEntry, 0>
readAddressAreas(DWARFContext &dwarf, InputSection *sec) {
- std::vector<GdbIndexSection::AddressEntry> ret;
+ SmallVector<GdbIndexSection::AddressEntry, 0> ret;
uint32_t cuIdx = 0;
for (std::unique_ptr<DWARFUnit> &cu : dwarf.compile_units()) {
}
template <class ELFT>
-static std::vector<GdbIndexSection::NameAttrEntry>
+static SmallVector<GdbIndexSection::NameAttrEntry, 0>
readPubNamesAndTypes(const LLDDwarfObj<ELFT> &obj,
- const std::vector<GdbIndexSection::CuEntry> &cus) {
+ const SmallVectorImpl<GdbIndexSection::CuEntry> &cus) {
const LLDDWARFSection &pubNames = obj.getGnuPubnamesSection();
const LLDDWARFSection &pubTypes = obj.getGnuPubtypesSection();
- std::vector<GdbIndexSection::NameAttrEntry> ret;
+ SmallVector<GdbIndexSection::NameAttrEntry, 0> ret;
for (const LLDDWARFSection *pub : {&pubNames, &pubTypes}) {
DWARFDataExtractor data(obj, *pub, config->isLE, config->wordsize);
DWARFDebugPubTable table;
// Create a list of symbols from a given list of symbol names and types
// by uniquifying them by name.
-static std::vector<GdbIndexSection::GdbSymbol>
-createSymbols(ArrayRef<std::vector<GdbIndexSection::NameAttrEntry>> nameAttrs,
- const std::vector<GdbIndexSection::GdbChunk> &chunks) {
+static std::pair<SmallVector<GdbIndexSection::GdbSymbol, 0>, size_t>
+createSymbols(
+ ArrayRef<SmallVector<GdbIndexSection::NameAttrEntry, 0>> nameAttrs,
+ const SmallVector<GdbIndexSection::GdbChunk, 0> &chunks) {
using GdbSymbol = GdbIndexSection::GdbSymbol;
using NameAttrEntry = GdbIndexSection::NameAttrEntry;
// For each chunk, compute the number of compilation units preceding it.
uint32_t cuIdx = 0;
- std::vector<uint32_t> cuIdxs(chunks.size());
+ std::unique_ptr<uint32_t[]> cuIdxs(new uint32_t[chunks.size()]);
for (uint32_t i = 0, e = chunks.size(); i != e; ++i) {
cuIdxs[i] = cuIdx;
cuIdx += chunks[i].compilationUnits.size();
// of millions for very large executables, so we use multi-threading to
// speed it up.
constexpr size_t numShards = 32;
- size_t concurrency = PowerOf2Floor(
- std::min<size_t>(hardware_concurrency(parallel::strategy.ThreadsRequested)
- .compute_thread_count(),
- numShards));
+ const size_t concurrency =
+ PowerOf2Floor(std::min<size_t>(config->threadCount, numShards));
// A sharded map to uniquify symbols by name.
- std::vector<DenseMap<CachedHashStringRef, size_t>> map(numShards);
+ auto map =
+ std::make_unique<DenseMap<CachedHashStringRef, size_t>[]>(numShards);
size_t shift = 32 - countTrailingZeros(numShards);
// Instantiate GdbSymbols while uniqufying them by name.
- std::vector<std::vector<GdbSymbol>> symbols(numShards);
- parallelForEachN(0, concurrency, [&](size_t threadId) {
+ auto symbols = std::make_unique<SmallVector<GdbSymbol, 0>[]>(numShards);
+
+ parallelFor(0, concurrency, [&](size_t threadId) {
uint32_t i = 0;
for (ArrayRef<NameAttrEntry> entries : nameAttrs) {
for (const NameAttrEntry &ent : entries) {
});
size_t numSymbols = 0;
- for (ArrayRef<GdbSymbol> v : symbols)
+ for (ArrayRef<GdbSymbol> v : ArrayRef(symbols.get(), numShards))
numSymbols += v.size();
// The return type is a flattened vector, so we'll copy each vector
// contents to Ret.
- std::vector<GdbSymbol> ret;
+ SmallVector<GdbSymbol, 0> ret;
ret.reserve(numSymbols);
- for (std::vector<GdbSymbol> &vec : symbols)
+ for (SmallVector<GdbSymbol, 0> &vec :
+ MutableArrayRef(symbols.get(), numShards))
for (GdbSymbol &sym : vec)
ret.push_back(std::move(sym));
sym.nameOff = off;
off += sym.name.size() + 1;
}
+ // If off overflows, the last symbol's nameOff likely overflows.
+ if (!isUInt<32>(off))
+ errorOrWarn("--gdb-index: constant pool size (" + Twine(off) +
+ ") exceeds UINT32_MAX");
- return ret;
+ return {ret, off};
}
// Returns a newly-created .gdb_index section.
template <class ELFT> GdbIndexSection *GdbIndexSection::create() {
+ llvm::TimeTraceScope timeScope("Create gdb index");
+
// Collect InputFiles with .debug_info. See the comment in
// LLDDwarfObj<ELFT>::LLDDwarfObj. If we do lightweight parsing in the future,
// note that isec->data() may uncompress the full content, which should be
// parallelized.
SetVector<InputFile *> files;
- for (InputSectionBase *s : inputSections) {
+ for (InputSectionBase *s : ctx.inputSections) {
InputSection *isec = dyn_cast<InputSection>(s);
if (!isec)
continue;
files.insert(isec->file);
}
// Drop .rel[a].debug_gnu_pub{names,types} for --emit-relocs.
- llvm::erase_if(inputSections, [](InputSectionBase *s) {
+ llvm::erase_if(ctx.inputSections, [](InputSectionBase *s) {
if (auto *isec = dyn_cast<InputSection>(s))
if (InputSectionBase *rel = isec->getRelocatedSection())
return !rel->isLive();
return !s->isLive();
});
- std::vector<GdbChunk> chunks(files.size());
- std::vector<std::vector<NameAttrEntry>> nameAttrs(files.size());
+ SmallVector<GdbChunk, 0> chunks(files.size());
+ SmallVector<SmallVector<NameAttrEntry, 0>, 0> nameAttrs(files.size());
- parallelForEachN(0, files.size(), [&](size_t i) {
+ parallelFor(0, files.size(), [&](size_t i) {
// To keep memory usage low, we don't want to keep cached DWARFContext, so
// avoid getDwarf() here.
ObjFile<ELFT> *file = cast<ObjFile<ELFT>>(files[i]);
auto *ret = make<GdbIndexSection>();
ret->chunks = std::move(chunks);
- ret->symbols = createSymbols(nameAttrs, ret->chunks);
- ret->initOutputSize();
+ std::tie(ret->symbols, ret->size) = createSymbols(nameAttrs, ret->chunks);
+
+ // Count the areas other than the constant pool.
+ ret->size += sizeof(GdbIndexHeader) + ret->computeSymtabSize() * 8;
+ for (GdbChunk &chunk : ret->chunks)
+ ret->size +=
+ chunk.compilationUnits.size() * 16 + chunk.addressAreas.size() * 20;
+
return ret;
}
void EhFrameHeader::write() {
uint8_t *buf = Out::bufferStart + getParent()->offset + outSecOff;
using FdeData = EhFrameSection::FdeData;
-
- std::vector<FdeData> fdes = getPartition().ehFrame->getFdeData();
+ SmallVector<FdeData, 0> fdes = getPartition().ehFrame->getFdeData();
buf[0] = 1;
buf[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4;
void VersionTableSection::writeTo(uint8_t *buf) {
buf += 2;
for (const SymbolTableEntry &s : getPartition().dynSymTab->getSymbols()) {
- // Use the original versionId for an unfetched lazy symbol (undefined weak),
- // which must be VER_NDX_GLOBAL (an undefined versioned symbol is an error).
- write16(buf, s.sym->isLazy() ? static_cast<uint16_t>(VER_NDX_GLOBAL)
- : s.sym->versionId);
+ // For an unextracted lazy symbol (undefined weak), it must have been
+ // converted to Undefined and have VER_NDX_GLOBAL version here.
+ assert(!s.sym->isLazy());
+ write16(buf, s.sym->versionId);
buf += 2;
}
}
".gnu.version_r") {}
template <class ELFT> void VersionNeedSection<ELFT>::finalizeContents() {
- for (SharedFile *f : sharedFiles) {
+ for (SharedFile *f : ctx.sharedFiles) {
if (f->vernauxs.empty())
continue;
verneeds.emplace_back();
Verneed &vn = verneeds.back();
vn.nameStrTab = getPartition().dynStrTab->addString(f->soName);
+ bool isLibc = config->relrGlibc && f->soName.startswith("libc.so.");
+ bool isGlibc2 = false;
for (unsigned i = 0; i != f->vernauxs.size(); ++i) {
if (f->vernauxs[i] == 0)
continue;
auto *verdef =
reinterpret_cast<const typename ELFT::Verdef *>(f->verdefs[i]);
- vn.vernauxs.push_back(
- {verdef->vd_hash, f->vernauxs[i],
- getPartition().dynStrTab->addString(f->getStringTable().data() +
- verdef->getAux()->vda_name)});
+ StringRef ver(f->getStringTable().data() + verdef->getAux()->vda_name);
+ if (isLibc && ver.startswith("GLIBC_2."))
+ isGlibc2 = true;
+ vn.vernauxs.push_back({verdef->vd_hash, f->vernauxs[i],
+ getPartition().dynStrTab->addString(ver)});
+ }
+ if (isGlibc2) {
+ const char *ver = "GLIBC_ABI_DT_RELR";
+ vn.vernauxs.push_back({hashSysV(ver),
+ ++SharedFile::vernauxNum + getVerDefNum(),
+ getPartition().dynStrTab->addString(ver)});
}
}
void MergeSyntheticSection::addSection(MergeInputSection *ms) {
ms->parent = this;
sections.push_back(ms);
- assert(alignment == ms->alignment || !(ms->flags & SHF_STRINGS));
- alignment = std::max(alignment, ms->alignment);
+ assert(addralign == ms->addralign || !(ms->flags & SHF_STRINGS));
+ addralign = std::max(addralign, ms->addralign);
}
MergeTailSection::MergeTailSection(StringRef name, uint32_t type,
uint64_t flags, uint32_t alignment)
: MergeSyntheticSection(name, type, flags, alignment),
- builder(StringTableBuilder::RAW, alignment) {}
+ builder(StringTableBuilder::RAW, llvm::Align(alignment)) {}
size_t MergeTailSection::getSize() const { return builder.getSize(); }
}
void MergeNoTailSection::writeTo(uint8_t *buf) {
- for (size_t i = 0; i < numShards; ++i)
- shards[i].write(buf + shardOffsets[i]);
+ parallelFor(0, numShards,
+ [&](size_t i) { shards[i].write(buf + shardOffsets[i]); });
}
// This function is very hot (i.e. it can take several seconds to finish)
void MergeNoTailSection::finalizeContents() {
// Initializes string table builders.
for (size_t i = 0; i < numShards; ++i)
- shards.emplace_back(StringTableBuilder::RAW, alignment);
+ shards.emplace_back(StringTableBuilder::RAW, llvm::Align(addralign));
// Concurrency level. Must be a power of 2 to avoid expensive modulo
// operations in the following tight loop.
- size_t concurrency = PowerOf2Floor(
- std::min<size_t>(hardware_concurrency(parallel::strategy.ThreadsRequested)
- .compute_thread_count(),
- numShards));
+ const size_t concurrency =
+ PowerOf2Floor(std::min<size_t>(config->threadCount, numShards));
// Add section pieces to the builders.
- parallelForEachN(0, concurrency, [&](size_t threadId) {
+ parallelFor(0, concurrency, [&](size_t threadId) {
for (MergeInputSection *sec : sections) {
for (size_t i = 0, e = sec->pieces.size(); i != e; ++i) {
if (!sec->pieces[i].live)
for (size_t i = 0; i < numShards; ++i) {
shards[i].finalizeInOrder();
if (shards[i].getSize() > 0)
- off = alignTo(off, alignment);
+ off = alignToPowerOf2(off, addralign);
shardOffsets[i] = off;
off += shards[i].getSize();
}
});
}
-MergeSyntheticSection *elf::createMergeSynthetic(StringRef name, uint32_t type,
- uint64_t flags,
- uint32_t alignment) {
- bool shouldTailMerge = (flags & SHF_STRINGS) && config->optimize >= 2;
- if (shouldTailMerge)
- return make<MergeTailSection>(name, type, flags, alignment);
- return make<MergeNoTailSection>(name, type, flags, alignment);
-}
-
template <class ELFT> void elf::splitSections() {
llvm::TimeTraceScope timeScope("Split sections");
// splitIntoPieces needs to be called on each MergeInputSection
// before calling finalizeContents().
- parallelForEach(inputSections, [](InputSectionBase *sec) {
- if (auto *s = dyn_cast<MergeInputSection>(sec))
- s->splitIntoPieces();
- else if (auto *eh = dyn_cast<EhInputSection>(sec))
- eh->split<ELFT>();
+ parallelForEach(ctx.objectFiles, [](ELFFileBase *file) {
+ for (InputSectionBase *sec : file->getSections()) {
+ if (!sec)
+ continue;
+ if (auto *s = dyn_cast<MergeInputSection>(sec))
+ s->splitIntoPieces();
+ else if (auto *eh = dyn_cast<EhInputSection>(sec))
+ eh->split<ELFT>();
+ }
+ });
+}
+
+void elf::combineEhSections() {
+ llvm::TimeTraceScope timeScope("Combine EH sections");
+ for (EhInputSection *sec : ctx.ehInputSections) {
+ EhFrameSection &eh = *sec->getPartition().ehFrame;
+ sec->parent = &eh;
+ eh.addralign = std::max(eh.addralign, sec->addralign);
+ eh.sections.push_back(sec);
+ llvm::append_range(eh.dependentSections, sec->dependentSections);
+ }
+
+ if (!mainPart->armExidx)
+ return;
+ llvm::erase_if(ctx.inputSections, [](InputSectionBase *s) {
+ // Ignore dead sections and the partition end marker (.part.end),
+ // whose partition number is out of bounds.
+ if (!s->isLive() || s->partition == 255)
+ return false;
+ Partition &part = s->getPartition();
+ return s->kind() == SectionBase::Regular && part.armExidx &&
+ part.armExidx->addSection(cast<InputSection>(s));
});
}
}
// The .ARM.exidx table must be sorted in ascending order of the address of the
-// functions the table describes. Optionally duplicate adjacent table entries
-// can be removed. At the end of the function the executableSections must be
-// sorted in ascending order of address, Sentinel is set to the InputSection
-// with the highest address and any InputSections that have mergeable
-// .ARM.exidx table entries are removed from it.
+// functions the table describes. std::optionally duplicate adjacent table
+// entries can be removed. At the end of the function the executableSections
+// must be sorted in ascending order of address, Sentinel is set to the
+// InputSection with the highest address and any InputSections that have
+// mergeable .ARM.exidx table entries are removed from it.
void ARMExidxSyntheticSection::finalizeContents() {
// The executableSections and exidxSections that we use to derive the final
// contents of this SyntheticSection are populated before
};
llvm::stable_sort(executableSections, compareByFilePosition);
sentinel = executableSections.back();
- // Optionally merge adjacent duplicate entries.
+ // std::optionally merge adjacent duplicate entries.
if (config->mergeArmExidx) {
- std::vector<InputSection *> selectedSections;
+ SmallVector<InputSection *, 0> selectedSections;
selectedSections.reserve(executableSections.size());
selectedSections.push_back(executableSections[0]);
size_t prev = 0;
for (InputSection *isec : executableSections) {
assert(isec->getParent() != nullptr);
if (InputSection *d = findExidxSection(isec)) {
- memcpy(buf + offset, d->data().data(), d->data().size());
- d->relocateAlloc(buf + d->outSecOff, buf + d->outSecOff + d->getSize());
+ memcpy(buf + offset, d->content().data(), d->content().size());
+ target->relocateAlloc(*d, buf + d->outSecOff);
offset += d->getSize();
} else {
// A Linker generated CANTUNWIND section.
}
bool ARMExidxSyntheticSection::isNeeded() const {
- return llvm::find_if(exidxSections, [](InputSection *isec) {
- return isec->isLive();
- }) != exidxSections.end();
-}
-
-bool ARMExidxSyntheticSection::classof(const SectionBase *d) {
- return d->kind() == InputSectionBase::Synthetic && d->type == SHT_ARM_EXIDX;
+ return llvm::any_of(exidxSections,
+ [](InputSection *isec) { return isec->isLive(); });
}
ThunkSection::ThunkSection(OutputSection *os, uint64_t off)
bool ThunkSection::assignOffsets() {
uint64_t off = 0;
for (Thunk *t : thunks) {
- off = alignTo(off, t->alignment);
+ off = alignToPowerOf2(off, t->alignment);
t->setOffset(off);
uint32_t size = t->size();
t->getThunkTargetSym()->size = size;
bool PPC32Got2Section::isNeeded() const {
// See the comment below. This is not needed if there is no other
// InputSection.
- for (BaseCommand *base : getParent()->sectionCommands)
- if (auto *isd = dyn_cast<InputSectionDescription>(base))
+ for (SectionCommand *cmd : getParent()->commands)
+ if (auto *isd = dyn_cast<InputSectionDescription>(cmd))
for (InputSection *isec : isd->sections)
if (isec != this)
return true;
// .got2 . This function computes outSecOff of each .got2 to be used in
// PPC32PltCallStub::writeTo(). The purpose of this empty synthetic section is
// to collect input sections named ".got2".
- uint32_t offset = 0;
- for (BaseCommand *base : getParent()->sectionCommands)
- if (auto *isd = dyn_cast<InputSectionDescription>(base)) {
+ for (SectionCommand *cmd : getParent()->commands)
+ if (auto *isd = dyn_cast<InputSectionDescription>(cmd)) {
for (InputSection *isec : isd->sections) {
- if (isec == this)
- continue;
- isec->file->ppc32Got2OutSecOff = offset;
- offset += (uint32_t)isec->getSize();
+ // isec->file may be nullptr for MergeSyntheticSection.
+ if (isec != this && isec->file)
+ isec->file->ppc32Got2 = isec;
}
}
}
return getVA() + entry_index.find({sym, addend})->second * 8;
}
-Optional<uint32_t> PPC64LongBranchTargetSection::addEntry(const Symbol *sym,
- int64_t addend) {
+std::optional<uint32_t>
+PPC64LongBranchTargetSection::addEntry(const Symbol *sym, int64_t addend) {
auto res =
entry_index.try_emplace(std::make_pair(sym, addend), entries.size());
if (!res.second)
- return None;
+ return std::nullopt;
entries.emplace_back(sym, addend);
return res.first->second;
}
void PPC64LongBranchTargetSection::writeTo(uint8_t *buf) {
// If linking non-pic we have the final addresses of the targets and they get
// written to the table directly. For pic the dynamic linker will allocate
- // the section and fill it it.
+ // the section and fill it.
if (config->isPic)
return;
return 0;
}
- if (config->emachine == EM_AMDGPU) {
- uint8_t ver = objectFiles[0]->abiVersion;
- for (InputFile *file : makeArrayRef(objectFiles).slice(1))
+ if (config->emachine == EM_AMDGPU && !ctx.objectFiles.empty()) {
+ uint8_t ver = ctx.objectFiles[0]->abiVersion;
+ for (InputFile *file : ArrayRef(ctx.objectFiles).slice(1))
if (file->abiVersion != ver)
error("incompatible ABI version: " + toString(file));
return ver;
}
template <typename ELFT> void elf::writeEhdr(uint8_t *buf, Partition &part) {
- // For executable segments, the trap instructions are written before writing
- // the header. Setting Elf header bytes to zero ensures that any unused bytes
- // in header are zero-cleared, instead of having trap instructions.
- memset(buf, 0, sizeof(typename ELFT::Ehdr));
memcpy(buf, "\177ELF", 4);
auto *eHdr = reinterpret_cast<typename ELFT::Ehdr *>(buf);
write32(buf, mainPart->dynStrTab->getVA() + partitions[i].nameStrTab - va);
write32(buf + 4, partitions[i].elfHeader->getVA() - (va + 4));
- SyntheticSection *next =
- i == partitions.size() - 1 ? in.partEnd : partitions[i + 1].elfHeader;
+ SyntheticSection *next = i == partitions.size() - 1
+ ? in.partEnd.get()
+ : partitions[i + 1].elfHeader.get();
write32(buf + 8, next->getVA() - partitions[i].elfHeader->getVA());
va += 12;
}
}
+void InStruct::reset() {
+ attributes.reset();
+ riscvAttributes.reset();
+ bss.reset();
+ bssRelRo.reset();
+ got.reset();
+ gotPlt.reset();
+ igotPlt.reset();
+ ppc64LongBranchTarget.reset();
+ mipsAbiFlags.reset();
+ mipsGot.reset();
+ mipsOptions.reset();
+ mipsReginfo.reset();
+ mipsRldMap.reset();
+ partEnd.reset();
+ partIndex.reset();
+ plt.reset();
+ iplt.reset();
+ ppc32Got2.reset();
+ ibtPlt.reset();
+ relaPlt.reset();
+ relaIplt.reset();
+ shStrTab.reset();
+ strTab.reset();
+ symTab.reset();
+ symTabShndx.reset();
+}
+
+constexpr char kMemtagAndroidNoteName[] = "Android";
+void MemtagAndroidNote::writeTo(uint8_t *buf) {
+ static_assert(sizeof(kMemtagAndroidNoteName) == 8,
+ "ABI check for Android 11 & 12.");
+ assert((config->androidMemtagStack || config->androidMemtagHeap) &&
+ "Should only be synthesizing a note if heap || stack is enabled.");
+
+ write32(buf, sizeof(kMemtagAndroidNoteName));
+ write32(buf + 4, sizeof(uint32_t));
+ write32(buf + 8, ELF::NT_ANDROID_TYPE_MEMTAG);
+ memcpy(buf + 12, kMemtagAndroidNoteName, sizeof(kMemtagAndroidNoteName));
+ buf += 12 + sizeof(kMemtagAndroidNoteName);
+
+ uint32_t value = 0;
+ value |= config->androidMemtagMode;
+ if (config->androidMemtagHeap)
+ value |= ELF::NT_MEMTAG_HEAP;
+ // Note, MTE stack is an ABI break. Attempting to run an MTE stack-enabled
+ // binary on Android 11 or 12 will result in a checkfail in the loader.
+ if (config->androidMemtagStack)
+ value |= ELF::NT_MEMTAG_STACK;
+ write32(buf, value); // note value
+}
+
+size_t MemtagAndroidNote::getSize() const {
+ return sizeof(llvm::ELF::Elf64_Nhdr) +
+ /*namesz=*/sizeof(kMemtagAndroidNoteName) +
+ /*descsz=*/sizeof(uint32_t);
+}
+
+void PackageMetadataNote::writeTo(uint8_t *buf) {
+ write32(buf, 4);
+ write32(buf + 4, config->packageMetadata.size() + 1);
+ write32(buf + 8, FDO_PACKAGING_METADATA);
+ memcpy(buf + 12, "FDO", 4);
+ memcpy(buf + 16, config->packageMetadata.data(),
+ config->packageMetadata.size());
+}
+
+size_t PackageMetadataNote::getSize() const {
+ return sizeof(llvm::ELF::Elf64_Nhdr) + 4 +
+ alignTo(config->packageMetadata.size() + 1, 4);
+}
+
InStruct elf::in;
std::vector<Partition> elf::partitions;
#ifndef LLD_ELF_SYNTHETIC_SECTIONS_H
#define LLD_ELF_SYNTHETIC_SECTIONS_H
-#include "DWARF.h"
-#include "EhFrame.h"
+#include "Config.h"
#include "InputSection.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/MC/StringTableBuilder.h"
+#include "llvm/Support/Compiler.h"
#include "llvm/Support/Endian.h"
-#include <functional>
+#include "llvm/Support/Parallel.h"
+#include "llvm/Support/Threading.h"
-namespace lld {
-namespace elf {
+namespace lld::elf {
class Defined;
struct PhdrEntry;
class SymbolTableBaseSection;
-class VersionNeedBaseSection;
-
-class SyntheticSection : public InputSection {
-public:
- SyntheticSection(uint64_t flags, uint32_t type, uint32_t alignment,
- StringRef name)
- : InputSection(nullptr, flags, type, alignment, {}, name,
- InputSectionBase::Synthetic) {
- markLive();
- }
-
- virtual ~SyntheticSection() = default;
- virtual void writeTo(uint8_t *buf) = 0;
- virtual size_t getSize() const = 0;
- virtual void finalizeContents() {}
- // If the section has the SHF_ALLOC flag and the size may be changed if
- // thunks are added, update the section size.
- virtual bool updateAllocSize() { return false; }
- virtual bool isNeeded() const { return true; }
-
- static bool classof(const SectionBase *d) {
- return d->kind() == InputSectionBase::Synthetic;
- }
-};
struct CieRecord {
EhSectionPiece *cie = nullptr;
- std::vector<EhSectionPiece *> fdes;
+ SmallVector<EhSectionPiece *, 0> fdes;
};
// Section for .eh_frame.
return SyntheticSection::classof(d) && d->name == ".eh_frame";
}
- void addSection(EhInputSection *sec);
-
- std::vector<EhInputSection *> sections;
+ SmallVector<EhInputSection *, 0> sections;
size_t numFdes = 0;
struct FdeData {
uint32_t fdeVARel;
};
- std::vector<FdeData> getFdeData() const;
+ SmallVector<FdeData, 0> getFdeData() const;
ArrayRef<CieRecord *> getCieRecords() const { return cieRecords; }
template <class ELFT>
void iterateFDEWithLSDA(llvm::function_ref<void(InputSection &)> fn);
uint64_t getFdePc(uint8_t *buf, size_t off, uint8_t enc) const;
- std::vector<CieRecord *> cieRecords;
+ SmallVector<CieRecord *, 0> cieRecords;
// CIE records are uniquified by their contents and personality functions.
llvm::DenseMap<std::pair<ArrayRef<uint8_t>, Symbol *>, CieRecord *> cieMap;
};
-class GotSection : public SyntheticSection {
+class GotSection final : public SyntheticSection {
public:
GotSection();
size_t getSize() const override { return size; }
bool isNeeded() const override;
void writeTo(uint8_t *buf) override;
+ void addConstant(const Relocation &r);
void addEntry(Symbol &sym);
+ bool addTlsDescEntry(Symbol &sym);
bool addDynTlsEntry(Symbol &sym);
bool addTlsIndex();
+ uint32_t getTlsDescOffset(const Symbol &sym) const;
+ uint64_t getTlsDescAddr(const Symbol &sym) const;
uint64_t getGlobalDynAddr(const Symbol &b) const;
uint64_t getGlobalDynOffset(const Symbol &b) const;
// Flag to force GOT to be in output if we have relocations
// that relies on its address.
- bool hasGotOffRel = false;
+ std::atomic<bool> hasGotOffRel = false;
protected:
size_t numEntries = 0;
size_t getSize() const override { return 0; }
};
-class GnuPropertySection : public SyntheticSection {
+class GnuPropertySection final : public SyntheticSection {
public:
GnuPropertySection();
void writeTo(uint8_t *buf) override;
// respectively.
class BssSection final : public SyntheticSection {
public:
- BssSection(StringRef name, uint64_t size, uint32_t alignment);
- void writeTo(uint8_t *) override {
- llvm_unreachable("unexpected writeTo() call for SHT_NOBITS section");
- }
+ BssSection(StringRef name, uint64_t size, uint32_t addralign);
+ void writeTo(uint8_t *) override {}
bool isNeeded() const override { return size != 0; }
size_t getSize() const override { return size; }
// Flag to force GotPlt to be in output if we have relocations
// that relies on its address.
- bool hasGotPltOffRel = false;
+ std::atomic<bool> hasGotPltOffRel = false;
private:
- std::vector<const Symbol *> entries;
+ SmallVector<const Symbol *, 0> entries;
};
// The IgotPltSection is a Got associated with the PltSection for GNU Ifunc
bool isNeeded() const override { return !entries.empty(); }
private:
- std::vector<const Symbol *> entries;
+ SmallVector<const Symbol *, 0> entries;
};
class StringTableSection final : public SyntheticSection {
uint64_t size = 0;
- llvm::DenseMap<StringRef, unsigned> stringMap;
- std::vector<StringRef> strings;
+ llvm::DenseMap<llvm::CachedHashStringRef, unsigned> stringMap;
+ SmallVector<StringRef, 0> strings;
};
class DynamicReloc {
DynamicReloc(RelType type, const InputSectionBase *inputSec,
uint64_t offsetInSec, Kind kind, Symbol &sym, int64_t addend,
RelExpr expr)
- : type(type), sym(&sym), inputSec(inputSec), offsetInSec(offsetInSec),
- kind(kind), expr(expr), addend(addend) {}
+ : sym(&sym), inputSec(inputSec), offsetInSec(offsetInSec), type(type),
+ addend(addend), kind(kind), expr(expr) {}
/// This constructor records a relative relocation with no symbol.
DynamicReloc(RelType type, const InputSectionBase *inputSec,
uint64_t offsetInSec, int64_t addend = 0)
- : type(type), sym(nullptr), inputSec(inputSec), offsetInSec(offsetInSec),
- kind(AddendOnly), expr(R_ADDEND), addend(addend) {}
+ : sym(nullptr), inputSec(inputSec), offsetInSec(offsetInSec), type(type),
+ addend(addend), kind(AddendOnly), expr(R_ADDEND) {}
/// This constructor records dynamic relocation settings used by the MIPS
/// multi-GOT implementation.
DynamicReloc(RelType type, const InputSectionBase *inputSec,
uint64_t offsetInSec, const OutputSection *outputSec,
int64_t addend)
- : type(type), sym(nullptr), inputSec(inputSec), offsetInSec(offsetInSec),
- kind(MipsMultiGotPage), expr(R_ADDEND), addend(addend),
- outputSec(outputSec) {}
+ : sym(nullptr), outputSec(outputSec), inputSec(inputSec),
+ offsetInSec(offsetInSec), type(type), addend(addend),
+ kind(MipsMultiGotPage), expr(R_ADDEND) {}
uint64_t getOffset() const;
uint32_t getSymIndex(SymbolTableBaseSection *symTab) const;
/// address/the address of the corresponding GOT entry/etc.
int64_t computeAddend() const;
- RelType type;
+ void computeRaw(SymbolTableBaseSection *symtab);
+
Symbol *sym;
+ const OutputSection *outputSec = nullptr;
const InputSectionBase *inputSec;
uint64_t offsetInSec;
+ uint64_t r_offset;
+ RelType type;
+ uint32_t r_sym;
+ // Initially input addend, then the output addend after
+ // RelocationSection<ELFT>::writeTo.
+ int64_t addend;
private:
Kind kind;
// The kind of expression used to calculate the added (required e.g. for
// relative GOT relocations).
RelExpr expr;
- int64_t addend;
- const OutputSection *outputSec = nullptr;
};
template <class ELFT> class DynamicSection final : public SyntheticSection {
LLVM_ELF_IMPORT_TYPES_ELFT(ELFT)
- // finalizeContents() fills this vector with the section contents.
- std::vector<std::pair<int32_t, std::function<uint64_t()>>> entries;
-
public:
DynamicSection();
void finalizeContents() override;
size_t getSize() const override { return size; }
private:
- void add(int32_t tag, std::function<uint64_t()> fn);
- void addInt(int32_t tag, uint64_t val);
- void addInSec(int32_t tag, InputSection *sec);
- void addInSecRelative(int32_t tag, InputSection *sec);
- void addOutSec(int32_t tag, OutputSection *sec);
- void addSize(int32_t tag, OutputSection *sec);
- void addSym(int32_t tag, Symbol *sym);
-
+ std::vector<std::pair<int32_t, uint64_t>> computeContents();
uint64_t size = 0;
};
class RelocationBaseSection : public SyntheticSection {
public:
RelocationBaseSection(StringRef name, uint32_t type, int32_t dynamicTag,
- int32_t sizeDynamicTag);
+ int32_t sizeDynamicTag, bool combreloc,
+ unsigned concurrency);
/// Add a dynamic relocation without writing an addend to the output section.
/// This overload can be used if the addends are written directly instead of
/// using relocations on the input section (e.g. MipsGotSection::writeTo()).
- void addReloc(const DynamicReloc &reloc);
+ template <bool shard = false> void addReloc(const DynamicReloc &reloc) {
+ relocs.push_back(reloc);
+ }
/// Add a dynamic relocation against \p sym with an optional addend.
- void addSymbolReloc(RelType dynType, InputSectionBase *isec,
+ void addSymbolReloc(RelType dynType, InputSectionBase &isec,
uint64_t offsetInSec, Symbol &sym, int64_t addend = 0,
- llvm::Optional<RelType> addendRelType = llvm::None);
+ std::optional<RelType> addendRelType = {});
/// Add a relative dynamic relocation that uses the target address of \p sym
/// (i.e. InputSection::getRelocTargetVA()) + \p addend as the addend.
- void addRelativeReloc(RelType dynType, InputSectionBase *isec,
+ /// This function should only be called for non-preemptible symbols or
+ /// RelExpr values that refer to an address inside the output file (e.g. the
+ /// address of the GOT entry for a potentially preemptible symbol).
+ template <bool shard = false>
+ void addRelativeReloc(RelType dynType, InputSectionBase &isec,
uint64_t offsetInSec, Symbol &sym, int64_t addend,
- RelType addendRelType, RelExpr expr);
+ RelType addendRelType, RelExpr expr) {
+ assert(expr != R_ADDEND && "expected non-addend relocation expression");
+ addReloc<shard>(DynamicReloc::AddendOnlyWithTargetVA, dynType, isec,
+ offsetInSec, sym, addend, expr, addendRelType);
+ }
/// Add a dynamic relocation using the target address of \p sym as the addend
/// if \p sym is non-preemptible. Otherwise add a relocation against \p sym.
- void addAddendOnlyRelocIfNonPreemptible(RelType dynType,
- InputSectionBase *isec,
+ void addAddendOnlyRelocIfNonPreemptible(RelType dynType, GotSection &sec,
uint64_t offsetInSec, Symbol &sym,
RelType addendRelType);
- void addReloc(DynamicReloc::Kind kind, RelType dynType,
- InputSectionBase *inputSec, uint64_t offsetInSec, Symbol &sym,
- int64_t addend, RelExpr expr, RelType addendRelType);
- bool isNeeded() const override { return !relocs.empty(); }
+ template <bool shard = false>
+ void addReloc(DynamicReloc::Kind kind, RelType dynType, InputSectionBase &sec,
+ uint64_t offsetInSec, Symbol &sym, int64_t addend, RelExpr expr,
+ RelType addendRelType) {
+ // Write the addends to the relocated address if required. We skip
+ // it if the written value would be zero.
+ if (config->writeAddends && (expr != R_ADDEND || addend != 0))
+ sec.addReloc({expr, addendRelType, offsetInSec, addend, &sym});
+ addReloc<shard>({dynType, &sec, offsetInSec, kind, sym, addend, expr});
+ }
+ bool isNeeded() const override {
+ return !relocs.empty() ||
+ llvm::any_of(relocsVec, [](auto &v) { return !v.empty(); });
+ }
size_t getSize() const override { return relocs.size() * this->entsize; }
size_t getRelativeRelocCount() const { return numRelativeRelocs; }
+ void mergeRels();
+ void partitionRels();
void finalizeContents() override;
static bool classof(const SectionBase *d) {
return SyntheticSection::classof(d) &&
d->type == llvm::ELF::SHT_RELR);
}
int32_t dynamicTag, sizeDynamicTag;
- std::vector<DynamicReloc> relocs;
+ SmallVector<DynamicReloc, 0> relocs;
protected:
- size_t numRelativeRelocs = 0;
+ void computeRels();
+ // Used when parallel relocation scanning adds relocations. The elements
+ // will be moved into relocs by mergeRel().
+ SmallVector<SmallVector<DynamicReloc, 0>, 0> relocsVec;
+ size_t numRelativeRelocs = 0; // used by -z combreloc
+ bool combreloc;
};
+template <>
+inline void RelocationBaseSection::addReloc<true>(const DynamicReloc &reloc) {
+ relocsVec[llvm::parallel::getThreadIndex()].push_back(reloc);
+}
+
template <class ELFT>
class RelocationSection final : public RelocationBaseSection {
using Elf_Rel = typename ELFT::Rel;
using Elf_Rela = typename ELFT::Rela;
public:
- RelocationSection(StringRef name, bool sort);
+ RelocationSection(StringRef name, bool combreloc, unsigned concurrency);
void writeTo(uint8_t *buf) override;
-
-private:
- bool sort;
};
template <class ELFT>
using Elf_Rela = typename ELFT::Rela;
public:
- AndroidPackedRelocationSection(StringRef name);
+ AndroidPackedRelocationSection(StringRef name, unsigned concurrency);
bool updateAllocSize() override;
size_t getSize() const override { return relocData.size(); }
class RelrBaseSection : public SyntheticSection {
public:
- RelrBaseSection();
- bool isNeeded() const override { return !relocs.empty(); }
- std::vector<RelativeReloc> relocs;
+ RelrBaseSection(unsigned concurrency);
+ void mergeRels();
+ bool isNeeded() const override {
+ return !relocs.empty() ||
+ llvm::any_of(relocsVec, [](auto &v) { return !v.empty(); });
+ }
+ SmallVector<RelativeReloc, 0> relocs;
+ SmallVector<SmallVector<RelativeReloc, 0>, 0> relocsVec;
};
// RelrSection is used to encode offsets for relative relocations.
using Elf_Relr = typename ELFT::Relr;
public:
- RelrSection();
+ RelrSection(unsigned concurrency);
bool updateAllocSize() override;
size_t getSize() const override { return relrRelocs.size() * this->entsize; }
}
private:
- std::vector<Elf_Relr> relrRelocs;
+ SmallVector<Elf_Relr, 0> relrRelocs;
};
struct SymbolTableEntry {
void sortSymTabSymbols();
// A vector of symbols and their string table offsets.
- std::vector<SymbolTableEntry> symbols;
+ SmallVector<SymbolTableEntry, 0> symbols;
StringTableSection &strTabSec;
// Adds symbols to the hash table.
// Sorts the input to satisfy GNU hash section requirements.
- void addSymbols(std::vector<SymbolTableEntry> &symbols);
+ void addSymbols(llvm::SmallVectorImpl<SymbolTableEntry> &symbols);
private:
// See the comment in writeBloomFilter.
enum { Shift2 = 26 };
- void writeBloomFilter(uint8_t *buf);
- void writeHashTable(uint8_t *buf);
-
struct Entry {
Symbol *sym;
size_t strTabOffset;
uint32_t bucketIdx;
};
- std::vector<Entry> symbols;
+ SmallVector<Entry, 0> symbols;
size_t maskWords;
size_t nBuckets = 0;
size_t size = 0;
size_t headerSize;
- std::vector<const Symbol *> entries;
+ SmallVector<const Symbol *, 0> entries;
};
// Used for non-preemptible ifuncs. It does not have a header. Each entry is
// runtime. PltSection can only contain entries associated with JUMP_SLOT
// relocations, so IPLT entries are in a separate section.
class IpltSection final : public SyntheticSection {
- std::vector<const Symbol *> entries;
+ SmallVector<const Symbol *, 0> entries;
public:
IpltSection();
void writeTo(uint8_t *buf) override;
size_t getSize() const override;
- std::vector<const Symbol *> canonical_plts;
+ SmallVector<const Symbol *, 0> canonical_plts;
static constexpr size_t footerSize = 64;
};
struct GdbChunk {
InputSection *sec;
- std::vector<AddressEntry> addressAreas;
- std::vector<CuEntry> compilationUnits;
+ SmallVector<AddressEntry, 0> addressAreas;
+ SmallVector<CuEntry, 0> compilationUnits;
};
struct GdbSymbol {
llvm::CachedHashStringRef name;
- std::vector<uint32_t> cuVector;
+ SmallVector<uint32_t, 0> cuVector;
uint32_t nameOff;
uint32_t cuVectorOff;
};
llvm::support::ulittle32_t constantPoolOff;
};
- void initOutputSize();
size_t computeSymtabSize() const;
// Each chunk contains information gathered from debug sections of a
// single object file.
- std::vector<GdbChunk> chunks;
+ SmallVector<GdbChunk, 0> chunks;
// A symbol table for this .gdb_index section.
- std::vector<GdbSymbol> symbols;
+ SmallVector<GdbSymbol, 0> symbols;
size_t size;
};
StringRef getFileDefName();
unsigned fileDefNameOff;
- std::vector<unsigned> verDefNameOffs;
+ SmallVector<unsigned, 0> verDefNameOffs;
};
// The .gnu.version section specifies the required version of each symbol in the
std::vector<Vernaux> vernauxs;
};
- std::vector<Verneed> verneeds;
+ SmallVector<Verneed, 0> verneeds;
public:
VersionNeedSection();
class MergeSyntheticSection : public SyntheticSection {
public:
void addSection(MergeInputSection *ms);
- std::vector<MergeInputSection *> sections;
+ SmallVector<MergeInputSection *, 0> sections;
protected:
MergeSyntheticSection(StringRef name, uint32_t type, uint64_t flags,
- uint32_t alignment)
- : SyntheticSection(flags, type, alignment, name) {}
+ uint32_t addralign)
+ : SyntheticSection(flags, type, addralign, name) {}
};
class MergeTailSection final : public MergeSyntheticSection {
public:
MergeTailSection(StringRef name, uint32_t type, uint64_t flags,
- uint32_t alignment);
+ uint32_t addralign);
size_t getSize() const override;
void writeTo(uint8_t *buf) override;
class MergeNoTailSection final : public MergeSyntheticSection {
public:
MergeNoTailSection(StringRef name, uint32_t type, uint64_t flags,
- uint32_t alignment)
- : MergeSyntheticSection(name, type, flags, alignment) {}
+ uint32_t addralign)
+ : MergeSyntheticSection(name, type, flags, addralign) {}
size_t getSize() const override { return size; }
void writeTo(uint8_t *buf) override;
// The reason why we don't want to use the least significant bits is
// because DenseMap also uses lower bits to determine a bucket ID.
// If we use lower bits, it significantly increases the probability of
- // hash collisons.
+ // hash collisions.
size_t getShardId(uint32_t hash) {
assert((hash >> 31) == 0);
return hash >> (31 - llvm::countTrailingZeros(numShards));
// String table contents
constexpr static size_t numShards = 32;
- std::vector<llvm::StringTableBuilder> shards;
+ SmallVector<llvm::StringTableBuilder, 0> shards;
size_t shardOffsets[numShards];
};
using Elf_Mips_ABIFlags = llvm::object::Elf_Mips_ABIFlags<ELFT>;
public:
- static MipsAbiFlagsSection *create();
+ static std::unique_ptr<MipsAbiFlagsSection> create();
MipsAbiFlagsSection(Elf_Mips_ABIFlags flags);
size_t getSize() const override { return sizeof(Elf_Mips_ABIFlags); }
using Elf_Mips_RegInfo = llvm::object::Elf_Mips_RegInfo<ELFT>;
public:
- static MipsOptionsSection *create();
+ static std::unique_ptr<MipsOptionsSection<ELFT>> create();
MipsOptionsSection(Elf_Mips_RegInfo reginfo);
void writeTo(uint8_t *buf) override;
using Elf_Mips_RegInfo = llvm::object::Elf_Mips_RegInfo<ELFT>;
public:
- static MipsReginfoSection *create();
+ static std::unique_ptr<MipsReginfoSection> create();
MipsReginfoSection(Elf_Mips_RegInfo reginfo);
size_t getSize() const override { return sizeof(Elf_Mips_RegInfo); }
// of executable file which is pointed to by the DT_MIPS_RLD_MAP entry.
// See "Dynamic section" in Chapter 5 in the following document:
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
-class MipsRldMapSection : public SyntheticSection {
+class MipsRldMapSection final : public SyntheticSection {
public:
MipsRldMapSection();
size_t getSize() const override { return config->wordsize; }
void finalizeContents() override;
InputSection *getLinkOrderDep() const;
- static bool classof(const SectionBase *d);
+ static bool classof(const SectionBase *sec) {
+ return sec->kind() == InputSectionBase::Synthetic &&
+ sec->type == llvm::ELF::SHT_ARM_EXIDX;
+ }
// Links to the ARMExidxSections so we can transfer the relocations once the
// layout is known.
- std::vector<InputSection *> exidxSections;
+ SmallVector<InputSection *, 0> exidxSections;
private:
size_t size = 0;
// InputObjects, we store pointers to the executable sections that need
// .ARM.exidx sections. We can then use the dependentSections of these to
// either find the .ARM.exidx section or know that we need to generate one.
- std::vector<InputSection *> executableSections;
+ SmallVector<InputSection *, 0> executableSections;
// The executable InputSection with the highest address to use for the
// sentinel. We store separately from ExecutableSections as merging of
// A container for one or more linker generated thunks. Instances of these
// thunks including ARM interworking and Mips LA25 PI to non-PI thunks.
-class ThunkSection : public SyntheticSection {
+class ThunkSection final : public SyntheticSection {
public:
// ThunkSection in OS, with desired outSecOff of Off
ThunkSection(OutputSection *os, uint64_t off);
bool roundUpSizeForErrata = false;
private:
- std::vector<Thunk *> thunks;
+ SmallVector<Thunk *, 0> thunks;
size_t size = 0;
};
public:
PPC64LongBranchTargetSection();
uint64_t getEntryVA(const Symbol *sym, int64_t addend);
- llvm::Optional<uint32_t> addEntry(const Symbol *sym, int64_t addend);
+ std::optional<uint32_t> addEntry(const Symbol *sym, int64_t addend);
size_t getSize() const override;
void writeTo(uint8_t *buf) override;
bool isNeeded() const override;
void finalizeContents() override { finalized = true; }
private:
- std::vector<std::pair<const Symbol *, int64_t>> entries;
+ SmallVector<std::pair<const Symbol *, int64_t>, 0> entries;
llvm::DenseMap<std::pair<const Symbol *, int64_t>, uint32_t> entry_index;
bool finalized = false;
};
template <typename ELFT>
-class PartitionElfHeaderSection : public SyntheticSection {
+class PartitionElfHeaderSection final : public SyntheticSection {
public:
PartitionElfHeaderSection();
size_t getSize() const override;
};
template <typename ELFT>
-class PartitionProgramHeadersSection : public SyntheticSection {
+class PartitionProgramHeadersSection final : public SyntheticSection {
public:
PartitionProgramHeadersSection();
size_t getSize() const override;
void writeTo(uint8_t *buf) override;
};
-class PartitionIndexSection : public SyntheticSection {
+class PartitionIndexSection final : public SyntheticSection {
public:
PartitionIndexSection();
size_t getSize() const override;
void writeTo(uint8_t *buf) override;
};
+// See the following link for the Android-specific loader code that operates on
+// this section:
+// https://cs.android.com/android/platform/superproject/+/master:bionic/libc/bionic/libc_init_static.cpp;drc=9425b16978f9c5aa8f2c50c873db470819480d1d;l=192
+class MemtagAndroidNote final : public SyntheticSection {
+public:
+ MemtagAndroidNote()
+ : SyntheticSection(llvm::ELF::SHF_ALLOC, llvm::ELF::SHT_NOTE,
+ /*alignment=*/4, ".note.android.memtag") {}
+ void writeTo(uint8_t *buf) override;
+ size_t getSize() const override;
+};
+
+class PackageMetadataNote final : public SyntheticSection {
+public:
+ PackageMetadataNote()
+ : SyntheticSection(llvm::ELF::SHF_ALLOC, llvm::ELF::SHT_NOTE,
+ /*alignment=*/4, ".note.package") {}
+ void writeTo(uint8_t *buf) override;
+ size_t getSize() const override;
+};
+
InputSection *createInterpSection();
MergeInputSection *createCommentSection();
-MergeSyntheticSection *createMergeSynthetic(StringRef name, uint32_t type,
- uint64_t flags, uint32_t alignment);
template <class ELFT> void splitSections();
+void combineEhSections();
template <typename ELFT> void writeEhdr(uint8_t *buf, Partition &part);
template <typename ELFT> void writePhdrs(uint8_t *buf, Partition &part);
StringRef name;
uint64_t nameStrTab;
- SyntheticSection *elfHeader;
- SyntheticSection *programHeaders;
- std::vector<PhdrEntry *> phdrs;
-
- ARMExidxSyntheticSection *armExidx;
- BuildIdSection *buildId;
- SyntheticSection *dynamic;
- StringTableSection *dynStrTab;
- SymbolTableBaseSection *dynSymTab;
- EhFrameHeader *ehFrameHdr;
- EhFrameSection *ehFrame;
- GnuHashTableSection *gnuHashTab;
- HashTableSection *hashTab;
- RelocationBaseSection *relaDyn;
- RelrBaseSection *relrDyn;
- VersionDefinitionSection *verDef;
- SyntheticSection *verNeed;
- VersionTableSection *verSym;
+ std::unique_ptr<SyntheticSection> elfHeader;
+ std::unique_ptr<SyntheticSection> programHeaders;
+ SmallVector<PhdrEntry *, 0> phdrs;
+
+ std::unique_ptr<ARMExidxSyntheticSection> armExidx;
+ std::unique_ptr<BuildIdSection> buildId;
+ std::unique_ptr<SyntheticSection> dynamic;
+ std::unique_ptr<StringTableSection> dynStrTab;
+ std::unique_ptr<SymbolTableBaseSection> dynSymTab;
+ std::unique_ptr<EhFrameHeader> ehFrameHdr;
+ std::unique_ptr<EhFrameSection> ehFrame;
+ std::unique_ptr<GnuHashTableSection> gnuHashTab;
+ std::unique_ptr<HashTableSection> hashTab;
+ std::unique_ptr<MemtagAndroidNote> memtagAndroidNote;
+ std::unique_ptr<PackageMetadataNote> packageMetadataNote;
+ std::unique_ptr<RelocationBaseSection> relaDyn;
+ std::unique_ptr<RelrBaseSection> relrDyn;
+ std::unique_ptr<VersionDefinitionSection> verDef;
+ std::unique_ptr<SyntheticSection> verNeed;
+ std::unique_ptr<VersionTableSection> verSym;
unsigned getNumber() const { return this - &partitions[0] + 1; }
};
-extern Partition *mainPart;
+LLVM_LIBRARY_VISIBILITY extern Partition *mainPart;
inline Partition &SectionBase::getPartition() const {
assert(isLive());
// Linker generated sections which can be used as inputs and are not specific to
// a partition.
struct InStruct {
- InputSection *attributes;
- BssSection *bss;
- BssSection *bssRelRo;
- GotSection *got;
- GotPltSection *gotPlt;
- IgotPltSection *igotPlt;
- PPC64LongBranchTargetSection *ppc64LongBranchTarget;
- MipsGotSection *mipsGot;
- MipsRldMapSection *mipsRldMap;
- SyntheticSection *partEnd;
- SyntheticSection *partIndex;
- PltSection *plt;
- IpltSection *iplt;
- PPC32Got2Section *ppc32Got2;
- IBTPltSection *ibtPlt;
- RelocationBaseSection *relaPlt;
- RelocationBaseSection *relaIplt;
- StringTableSection *shStrTab;
- StringTableSection *strTab;
- SymbolTableBaseSection *symTab;
- SymtabShndxSection *symTabShndx;
+ 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;
+ std::unique_ptr<GotPltSection> gotPlt;
+ std::unique_ptr<IgotPltSection> igotPlt;
+ std::unique_ptr<PPC64LongBranchTargetSection> ppc64LongBranchTarget;
+ std::unique_ptr<SyntheticSection> mipsAbiFlags;
+ std::unique_ptr<MipsGotSection> mipsGot;
+ std::unique_ptr<SyntheticSection> mipsOptions;
+ std::unique_ptr<SyntheticSection> mipsReginfo;
+ std::unique_ptr<MipsRldMapSection> mipsRldMap;
+ std::unique_ptr<SyntheticSection> partEnd;
+ std::unique_ptr<SyntheticSection> partIndex;
+ std::unique_ptr<PltSection> plt;
+ std::unique_ptr<IpltSection> iplt;
+ std::unique_ptr<PPC32Got2Section> ppc32Got2;
+ std::unique_ptr<IBTPltSection> ibtPlt;
+ std::unique_ptr<RelocationBaseSection> relaPlt;
+ std::unique_ptr<RelocationBaseSection> relaIplt;
+ std::unique_ptr<StringTableSection> shStrTab;
+ std::unique_ptr<StringTableSection> strTab;
+ std::unique_ptr<SymbolTableBaseSection> symTab;
+ std::unique_ptr<SymtabShndxSection> symTabShndx;
+
+ void reset();
};
-extern InStruct in;
+LLVM_LIBRARY_VISIBILITY extern InStruct in;
-} // namespace elf
-} // namespace lld
+} // namespace lld::elf
#endif
#include "ARMErrataFix.h"
#include "CallGraphSort.h"
#include "Config.h"
+#include "InputFiles.h"
#include "LinkerScript.h"
#include "MapFile.h"
#include "OutputSections.h"
#include "SyntheticSections.h"
#include "Target.h"
#include "lld/Common/Arrays.h"
+#include "lld/Common/CommonLinkerContext.h"
#include "lld/Common/Filesystem.h"
-#include "lld/Common/Memory.h"
#include "lld/Common/Strings.h"
#include "llvm/ADT/StringMap.h"
-#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/BLAKE3.h"
#include "llvm/Support/Parallel.h"
#include "llvm/Support/RandomNumberGenerator.h"
-#include "llvm/Support/SHA1.h"
#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/xxhash.h"
#include <climits>
private:
void copyLocalSymbols();
void addSectionSymbols();
- void forEachRelSec(llvm::function_ref<void(InputSectionBase &)> fn);
void sortSections();
void resolveShfLinkOrder();
void finalizeAddressDependentContent();
void checkExecuteOnly();
void setReservedSymbolSections();
- std::vector<PhdrEntry *> createPhdrs(Partition &part);
+ SmallVector<PhdrEntry *, 0> createPhdrs(Partition &part);
void addPhdrForSection(Partition &part, unsigned shType, unsigned pType,
unsigned pFlags);
void assignFileOffsets();
void addRelIpltSymbols();
void addStartEndSymbols();
- void addStartStopSymbols(OutputSection *sec);
+ void addStartStopSymbols(OutputSection &osec);
uint64_t fileSize;
uint64_t sectionHeaderOff;
};
} // anonymous namespace
-static bool isSectionPrefix(StringRef prefix, StringRef name) {
- return name.startswith(prefix) || name == prefix.drop_back();
-}
-
-StringRef elf::getOutputSectionName(const InputSectionBase *s) {
- if (config->relocatable)
- return s->name;
-
- // This is for --emit-relocs. If .text.foo is emitted as .text.bar, we want
- // to emit .rela.text.foo as .rela.text.bar for consistency (this is not
- // technically required, but not doing it is odd). This code guarantees that.
- if (auto *isec = dyn_cast<InputSection>(s)) {
- if (InputSectionBase *rel = isec->getRelocatedSection()) {
- OutputSection *out = rel->getOutputSection();
- if (s->type == SHT_RELA)
- return saver.save(".rela" + out->name);
- return saver.save(".rel" + out->name);
- }
- }
-
- // A BssSection created for a common symbol is identified as "COMMON" in
- // linker scripts. It should go to .bss section.
- if (s->name == "COMMON")
- return ".bss";
-
- if (script->hasSectionsCommand)
- return s->name;
-
- // When no SECTIONS is specified, emulate GNU ld's internal linker scripts
- // by grouping sections with certain prefixes.
-
- // GNU ld places text sections with prefix ".text.hot.", ".text.unknown.",
- // ".text.unlikely.", ".text.startup." or ".text.exit." before others.
- // We provide an option -z keep-text-section-prefix to group such sections
- // into separate output sections. This is more flexible. See also
- // sortISDBySectionOrder().
- // ".text.unknown" means the hotness of the section is unknown. When
- // SampleFDO is used, if a function doesn't have sample, it could be very
- // cold or it could be a new function never being sampled. Those functions
- // will be kept in the ".text.unknown" section.
- // ".text.split." holds symbols which are split out from functions in other
- // input sections. For example, with -fsplit-machine-functions, placing the
- // cold parts in .text.split instead of .text.unlikely mitigates against poor
- // profile inaccuracy. Techniques such as hugepage remapping can make
- // conservative decisions at the section granularity.
- if (config->zKeepTextSectionPrefix)
- for (StringRef v : {".text.hot.", ".text.unknown.", ".text.unlikely.",
- ".text.startup.", ".text.exit.", ".text.split."})
- if (isSectionPrefix(v, s->name))
- return v.drop_back();
-
- for (StringRef v :
- {".text.", ".rodata.", ".data.rel.ro.", ".data.", ".bss.rel.ro.",
- ".bss.", ".init_array.", ".fini_array.", ".ctors.", ".dtors.", ".tbss.",
- ".gcc_except_table.", ".tdata.", ".ARM.exidx.", ".ARM.extab.",
- ".openbsd.randomdata.", ".openbsd.mutable." })
- if (isSectionPrefix(v, s->name))
- return v.drop_back();
-
- return s->name;
-}
-
static bool needsInterpSection() {
return !config->relocatable && !config->shared &&
!config->dynamicLinker.empty() && script->needsInterpSection();
Writer<ELFT>().run();
}
-static void removeEmptyPTLoad(std::vector<PhdrEntry *> &phdrs) {
+static void removeEmptyPTLoad(SmallVector<PhdrEntry *, 0> &phdrs) {
auto it = std::stable_partition(
phdrs.begin(), phdrs.end(), [&](const PhdrEntry *p) {
if (p->p_type != PT_LOAD)
}
void elf::copySectionsIntoPartitions() {
- std::vector<InputSectionBase *> newSections;
+ SmallVector<InputSectionBase *, 0> newSections;
+ const size_t ehSize = ctx.ehInputSections.size();
for (unsigned part = 2; part != partitions.size() + 1; ++part) {
- for (InputSectionBase *s : inputSections) {
- if (!(s->flags & SHF_ALLOC) || !s->isLive())
- continue;
- InputSectionBase *copy;
- if (s->type == SHT_NOTE)
- copy = make<InputSection>(cast<InputSection>(*s));
- else if (auto *es = dyn_cast<EhInputSection>(s))
- copy = make<EhInputSection>(*es);
- else
+ for (InputSectionBase *s : ctx.inputSections) {
+ if (!(s->flags & SHF_ALLOC) || !s->isLive() || s->type != SHT_NOTE)
continue;
+ auto *copy = make<InputSection>(cast<InputSection>(*s));
copy->partition = part;
newSections.push_back(copy);
}
- }
-
- inputSections.insert(inputSections.end(), newSections.begin(),
- newSections.end());
-}
-
-void elf::combineEhSections() {
- llvm::TimeTraceScope timeScope("Combine EH sections");
- for (InputSectionBase *&s : inputSections) {
- // Ignore dead sections and the partition end marker (.part.end),
- // whose partition number is out of bounds.
- if (!s->isLive() || s->partition == 255)
- continue;
-
- Partition &part = s->getPartition();
- if (auto *es = dyn_cast<EhInputSection>(s)) {
- part.ehFrame->addSection(es);
- s = nullptr;
- } else if (s->kind() == SectionBase::Regular && part.armExidx &&
- part.armExidx->addSection(cast<InputSection>(s))) {
- s = nullptr;
+ for (size_t i = 0; i != ehSize; ++i) {
+ assert(ctx.ehInputSections[i]->isLive());
+ auto *copy = make<EhInputSection>(*ctx.ehInputSections[i]);
+ copy->partition = part;
+ ctx.ehInputSections.push_back(copy);
}
}
- std::vector<InputSectionBase *> &v = inputSections;
- v.erase(std::remove(v.begin(), v.end(), nullptr), v.end());
+ ctx.inputSections.insert(ctx.inputSections.end(), newSections.begin(),
+ newSections.end());
}
static Defined *addOptionalRegular(StringRef name, SectionBase *sec,
- uint64_t val, uint8_t stOther = STV_HIDDEN,
- uint8_t binding = STB_GLOBAL) {
- Symbol *s = symtab->find(name);
- if (!s || s->isDefined())
+ uint64_t val, uint8_t stOther = STV_HIDDEN) {
+ Symbol *s = symtab.find(name);
+ if (!s || s->isDefined() || s->isCommon())
return nullptr;
- s->resolve(Defined{/*file=*/nullptr, name, binding, stOther, STT_NOTYPE, val,
+ s->resolve(Defined{nullptr, StringRef(), STB_GLOBAL, stOther, STT_NOTYPE, val,
/*size=*/0, sec});
+ s->isUsedInRegularObj = true;
return cast<Defined>(s);
}
static Defined *addAbsolute(StringRef name) {
- Symbol *sym = symtab->addSymbol(Defined{nullptr, name, STB_GLOBAL, STV_HIDDEN,
- STT_NOTYPE, 0, 0, nullptr});
+ Symbol *sym = symtab.addSymbol(Defined{nullptr, name, STB_GLOBAL, STV_HIDDEN,
+ STT_NOTYPE, 0, 0, nullptr});
+ sym->isUsedInRegularObj = true;
return cast<Defined>(sym);
}
// On MIPS O32 ABI, _gp_disp is a magic symbol designates offset between
// start of function and 'gp' pointer into GOT.
- if (symtab->find("_gp_disp"))
+ if (symtab.find("_gp_disp"))
ElfSym::mipsGpDisp = addAbsolute("_gp_disp");
// The __gnu_local_gp is a magic symbol equal to the current value of 'gp'
// pointer. This symbol is used in the code generated by .cpload pseudo-op
// in case of using -mno-shared option.
// https://sourceware.org/ml/binutils/2004-12/msg00094.html
- if (symtab->find("__gnu_local_gp"))
+ if (symtab.find("__gnu_local_gp"))
ElfSym::mipsLocalGp = addAbsolute("__gnu_local_gp");
} else if (config->emachine == EM_PPC) {
// glibc *crt1.o has a undefined reference to _SDA_BASE_. Since we don't
StringRef gotSymName =
(config->emachine == EM_PPC64) ? ".TOC." : "_GLOBAL_OFFSET_TABLE_";
- if (Symbol *s = symtab->find(gotSymName)) {
+ if (Symbol *s = symtab.find(gotSymName)) {
if (s->isDefined()) {
error(toString(s->file) + " cannot redefine linker defined symbol '" +
gotSymName + "'");
if (config->emachine == EM_PPC64)
gotOff = 0x8000;
- s->resolve(Defined{/*file=*/nullptr, gotSymName, STB_GLOBAL, STV_HIDDEN,
+ s->resolve(Defined{/*file=*/nullptr, StringRef(), STB_GLOBAL, STV_HIDDEN,
STT_NOTYPE, gotOff, /*size=*/0, Out::elfHeader});
ElfSym::globalOffsetTable = cast<Defined>(s);
}
}
static OutputSection *findSection(StringRef name, unsigned partition = 1) {
- for (BaseCommand *base : script->sectionCommands)
- if (auto *sec = dyn_cast<OutputSection>(base))
- if (sec->name == name && sec->partition == partition)
- return sec;
+ for (SectionCommand *cmd : script->sectionCommands)
+ if (auto *osd = dyn_cast<OutputDesc>(cmd))
+ if (osd->osec.name == name && osd->osec.partition == partition)
+ return &osd->osec;
return nullptr;
}
template <class ELFT> void elf::createSyntheticSections() {
// Initialize all pointers with NULL. This is needed because
// you can call lld::elf::main more than once as a library.
- memset(&Out::first, 0, sizeof(Out));
+ Out::tlsPhdr = nullptr;
+ Out::preinitArray = nullptr;
+ Out::initArray = nullptr;
+ Out::finiArray = nullptr;
// Add the .interp section first because it is not a SyntheticSection.
// The removeUnusedSyntheticSections() function relies on the
for (size_t i = 1; i <= partitions.size(); ++i) {
InputSection *sec = createInterpSection();
sec->partition = i;
- inputSections.push_back(sec);
+ ctx.inputSections.push_back(sec);
}
}
- auto add = [](SyntheticSection *sec) { inputSections.push_back(sec); };
+ auto add = [](SyntheticSection &sec) { ctx.inputSections.push_back(&sec); };
- in.shStrTab = make<StringTableSection>(".shstrtab", false);
+ in.shStrTab = std::make_unique<StringTableSection>(".shstrtab", false);
Out::programHeaders = make<OutputSection>("", 0, SHF_ALLOC);
- Out::programHeaders->alignment = config->wordsize;
+ Out::programHeaders->addralign = config->wordsize;
if (config->strip != StripPolicy::All) {
- in.strTab = make<StringTableSection>(".strtab", false);
- in.symTab = make<SymbolTableSection<ELFT>>(*in.strTab);
- in.symTabShndx = make<SymtabShndxSection>();
+ in.strTab = std::make_unique<StringTableSection>(".strtab", false);
+ in.symTab = std::make_unique<SymbolTableSection<ELFT>>(*in.strTab);
+ in.symTabShndx = std::make_unique<SymtabShndxSection>();
}
- in.bss = make<BssSection>(".bss", 0, 1);
- add(in.bss);
+ in.bss = std::make_unique<BssSection>(".bss", 0, 1);
+ add(*in.bss);
// If there is a SECTIONS command and a .data.rel.ro section name use name
// .data.rel.ro.bss so that we match in the .data.rel.ro output section.
// This makes sure our relro is contiguous.
- bool hasDataRelRo =
- script->hasSectionsCommand && findSection(".data.rel.ro", 0);
- in.bssRelRo =
- make<BssSection>(hasDataRelRo ? ".data.rel.ro.bss" : ".bss.rel.ro", 0, 1);
- add(in.bssRelRo);
+ bool hasDataRelRo = script->hasSectionsCommand && findSection(".data.rel.ro");
+ in.bssRelRo = std::make_unique<BssSection>(
+ hasDataRelRo ? ".data.rel.ro.bss" : ".bss.rel.ro", 0, 1);
+ add(*in.bssRelRo);
// Add MIPS-specific sections.
if (config->emachine == EM_MIPS) {
if (!config->shared && config->hasDynSymTab) {
- in.mipsRldMap = make<MipsRldMapSection>();
- add(in.mipsRldMap);
+ in.mipsRldMap = std::make_unique<MipsRldMapSection>();
+ add(*in.mipsRldMap);
}
- if (auto *sec = MipsAbiFlagsSection<ELFT>::create())
- add(sec);
- if (auto *sec = MipsOptionsSection<ELFT>::create())
- add(sec);
- if (auto *sec = MipsReginfoSection<ELFT>::create())
- add(sec);
+ if ((in.mipsAbiFlags = MipsAbiFlagsSection<ELFT>::create()))
+ add(*in.mipsAbiFlags);
+ if ((in.mipsOptions = MipsOptionsSection<ELFT>::create()))
+ add(*in.mipsOptions);
+ if ((in.mipsReginfo = MipsReginfoSection<ELFT>::create()))
+ add(*in.mipsReginfo);
}
StringRef relaDynName = config->isRela ? ".rela.dyn" : ".rel.dyn";
+ const unsigned threadCount = config->threadCount;
for (Partition &part : partitions) {
- auto add = [&](SyntheticSection *sec) {
- sec->partition = part.getNumber();
- inputSections.push_back(sec);
+ auto add = [&](SyntheticSection &sec) {
+ sec.partition = part.getNumber();
+ ctx.inputSections.push_back(&sec);
};
if (!part.name.empty()) {
- part.elfHeader = make<PartitionElfHeaderSection<ELFT>>();
+ part.elfHeader = std::make_unique<PartitionElfHeaderSection<ELFT>>();
part.elfHeader->name = part.name;
- add(part.elfHeader);
+ add(*part.elfHeader);
- part.programHeaders = make<PartitionProgramHeadersSection<ELFT>>();
- add(part.programHeaders);
+ part.programHeaders =
+ std::make_unique<PartitionProgramHeadersSection<ELFT>>();
+ add(*part.programHeaders);
}
if (config->buildId != BuildIdKind::None) {
- part.buildId = make<BuildIdSection>();
- add(part.buildId);
+ part.buildId = std::make_unique<BuildIdSection>();
+ add(*part.buildId);
+ }
+
+ part.dynStrTab = std::make_unique<StringTableSection>(".dynstr", true);
+ part.dynSymTab =
+ std::make_unique<SymbolTableSection<ELFT>>(*part.dynStrTab);
+ part.dynamic = std::make_unique<DynamicSection<ELFT>>();
+
+ if (config->emachine == EM_AARCH64 &&
+ config->androidMemtagMode != ELF::NT_MEMTAG_LEVEL_NONE) {
+ part.memtagAndroidNote = std::make_unique<MemtagAndroidNote>();
+ add(*part.memtagAndroidNote);
}
- part.dynStrTab = make<StringTableSection>(".dynstr", true);
- part.dynSymTab = make<SymbolTableSection<ELFT>>(*part.dynStrTab);
- part.dynamic = make<DynamicSection<ELFT>>();
if (config->androidPackDynRelocs)
- part.relaDyn = make<AndroidPackedRelocationSection<ELFT>>(relaDynName);
+ part.relaDyn = std::make_unique<AndroidPackedRelocationSection<ELFT>>(
+ relaDynName, threadCount);
else
- part.relaDyn =
- make<RelocationSection<ELFT>>(relaDynName, config->zCombreloc);
+ part.relaDyn = std::make_unique<RelocationSection<ELFT>>(
+ relaDynName, config->zCombreloc, threadCount);
if (config->hasDynSymTab) {
- part.dynSymTab = make<SymbolTableSection<ELFT>>(*part.dynStrTab);
- add(part.dynSymTab);
+ add(*part.dynSymTab);
- part.verSym = make<VersionTableSection>();
- add(part.verSym);
+ part.verSym = std::make_unique<VersionTableSection>();
+ add(*part.verSym);
if (!namedVersionDefs().empty()) {
- part.verDef = make<VersionDefinitionSection>();
- add(part.verDef);
+ part.verDef = std::make_unique<VersionDefinitionSection>();
+ add(*part.verDef);
}
- part.verNeed = make<VersionNeedSection<ELFT>>();
- add(part.verNeed);
+ part.verNeed = std::make_unique<VersionNeedSection<ELFT>>();
+ add(*part.verNeed);
if (config->gnuHash) {
- part.gnuHashTab = make<GnuHashTableSection>();
- add(part.gnuHashTab);
+ part.gnuHashTab = std::make_unique<GnuHashTableSection>();
+ add(*part.gnuHashTab);
}
if (config->sysvHash) {
- part.hashTab = make<HashTableSection>();
- add(part.hashTab);
+ part.hashTab = std::make_unique<HashTableSection>();
+ add(*part.hashTab);
}
- add(part.dynamic);
- add(part.dynStrTab);
- add(part.relaDyn);
+ add(*part.dynamic);
+ add(*part.dynStrTab);
+ add(*part.relaDyn);
}
if (config->relrPackDynRelocs) {
- part.relrDyn = make<RelrSection<ELFT>>();
- add(part.relrDyn);
+ part.relrDyn = std::make_unique<RelrSection<ELFT>>(threadCount);
+ add(*part.relrDyn);
}
if (!config->relocatable) {
if (config->ehFrameHdr) {
- part.ehFrameHdr = make<EhFrameHeader>();
- add(part.ehFrameHdr);
+ part.ehFrameHdr = std::make_unique<EhFrameHeader>();
+ add(*part.ehFrameHdr);
+ }
+ part.ehFrame = std::make_unique<EhFrameSection>();
+ add(*part.ehFrame);
+
+ if (config->emachine == EM_ARM) {
+ // This section replaces all the individual .ARM.exidx InputSections.
+ part.armExidx = std::make_unique<ARMExidxSyntheticSection>();
+ add(*part.armExidx);
}
- part.ehFrame = make<EhFrameSection>();
- add(part.ehFrame);
}
- if (config->emachine == EM_ARM && !config->relocatable) {
- // The ARMExidxsyntheticsection replaces all the individual .ARM.exidx
- // InputSections.
- part.armExidx = make<ARMExidxSyntheticSection>();
- add(part.armExidx);
+ if (!config->packageMetadata.empty()) {
+ part.packageMetadataNote = std::make_unique<PackageMetadataNote>();
+ add(*part.packageMetadataNote);
}
}
// Create the partition end marker. This needs to be in partition number 255
// so that it is sorted after all other partitions. It also has other
// special handling (see createPhdrs() and combineEhSections()).
- in.partEnd = make<BssSection>(".part.end", config->maxPageSize, 1);
+ in.partEnd =
+ std::make_unique<BssSection>(".part.end", config->maxPageSize, 1);
in.partEnd->partition = 255;
- add(in.partEnd);
+ add(*in.partEnd);
- in.partIndex = make<PartitionIndexSection>();
- addOptionalRegular("__part_index_begin", in.partIndex, 0);
- addOptionalRegular("__part_index_end", in.partIndex,
+ in.partIndex = std::make_unique<PartitionIndexSection>();
+ addOptionalRegular("__part_index_begin", in.partIndex.get(), 0);
+ addOptionalRegular("__part_index_end", in.partIndex.get(),
in.partIndex->getSize());
- add(in.partIndex);
+ add(*in.partIndex);
}
// Add .got. MIPS' .got is so different from the other archs,
// it has its own class.
if (config->emachine == EM_MIPS) {
- in.mipsGot = make<MipsGotSection>();
- add(in.mipsGot);
+ in.mipsGot = std::make_unique<MipsGotSection>();
+ add(*in.mipsGot);
} else {
- in.got = make<GotSection>();
- add(in.got);
+ in.got = std::make_unique<GotSection>();
+ add(*in.got);
}
if (config->emachine == EM_PPC) {
- in.ppc32Got2 = make<PPC32Got2Section>();
- add(in.ppc32Got2);
+ in.ppc32Got2 = std::make_unique<PPC32Got2Section>();
+ add(*in.ppc32Got2);
}
if (config->emachine == EM_PPC64) {
- in.ppc64LongBranchTarget = make<PPC64LongBranchTargetSection>();
- add(in.ppc64LongBranchTarget);
+ in.ppc64LongBranchTarget = std::make_unique<PPC64LongBranchTargetSection>();
+ add(*in.ppc64LongBranchTarget);
}
- in.gotPlt = make<GotPltSection>();
- add(in.gotPlt);
- in.igotPlt = make<IgotPltSection>();
- add(in.igotPlt);
+ in.gotPlt = std::make_unique<GotPltSection>();
+ add(*in.gotPlt);
+ in.igotPlt = std::make_unique<IgotPltSection>();
+ add(*in.igotPlt);
// _GLOBAL_OFFSET_TABLE_ is defined relative to either .got.plt or .got. Treat
// it as a relocation and ensure the referenced section is created.
}
if (config->gdbIndex)
- add(GdbIndexSection::create<ELFT>());
+ add(*GdbIndexSection::create<ELFT>());
// We always need to add rel[a].plt to output if it has entries.
// Even for static linking it can contain R_[*]_IRELATIVE relocations.
- in.relaPlt = make<RelocationSection<ELFT>>(
- config->isRela ? ".rela.plt" : ".rel.plt", /*sort=*/false);
- add(in.relaPlt);
+ in.relaPlt = std::make_unique<RelocationSection<ELFT>>(
+ config->isRela ? ".rela.plt" : ".rel.plt", /*sort=*/false,
+ /*threadCount=*/1);
+ add(*in.relaPlt);
// The relaIplt immediately follows .rel[a].dyn to ensure that the IRelative
// relocations are processed last by the dynamic loader. We cannot place the
// that would cause a section type mismatch. However, because the Android
// dynamic loader reads .rel.plt after .rel.dyn, we can get the desired
// behaviour by placing the iplt section in .rel.plt.
- in.relaIplt = make<RelocationSection<ELFT>>(
+ in.relaIplt = std::make_unique<RelocationSection<ELFT>>(
config->androidPackDynRelocs ? in.relaPlt->name : relaDynName,
- /*sort=*/false);
- add(in.relaIplt);
+ /*sort=*/false, /*threadCount=*/1);
+ add(*in.relaIplt);
if ((config->emachine == EM_386 || config->emachine == EM_X86_64) &&
(config->andFeatures & GNU_PROPERTY_X86_FEATURE_1_IBT)) {
- in.ibtPlt = make<IBTPltSection>();
- add(in.ibtPlt);
+ in.ibtPlt = std::make_unique<IBTPltSection>();
+ add(*in.ibtPlt);
}
- in.plt = config->emachine == EM_PPC ? make<PPC32GlinkSection>()
- : make<PltSection>();
- add(in.plt);
- in.iplt = make<IpltSection>();
- add(in.iplt);
+ if (config->emachine == EM_PPC)
+ in.plt = std::make_unique<PPC32GlinkSection>();
+ else
+ in.plt = std::make_unique<PltSection>();
+ add(*in.plt);
+ in.iplt = std::make_unique<IpltSection>();
+ add(*in.iplt);
if (config->andFeatures)
- add(make<GnuPropertySection>());
+ add(*make<GnuPropertySection>());
// .note.GNU-stack is always added when we are creating a re-linkable
// object file. Other linkers are using the presence of this marker
// is irrelevant these days. Stack area should always be non-executable
// by default. So we emit this section unconditionally.
if (config->relocatable)
- add(make<GnuStackSection>());
+ add(*make<GnuStackSection>());
if (in.symTab)
- add(in.symTab);
+ add(*in.symTab);
if (in.symTabShndx)
- add(in.symTabShndx);
- add(in.shStrTab);
+ add(*in.symTabShndx);
+ add(*in.shStrTab);
if (in.strTab)
- add(in.strTab);
+ add(*in.strTab);
}
// The main function of the writer.
// finalizeSections does that.
finalizeSections();
checkExecuteOnly();
- if (errorCount())
- return;
- // If -compressed-debug-sections is specified, we need to compress
- // .debug_* sections. Do it right now because it changes the size of
- // output sections.
+ // If --compressed-debug-sections is specified, compress .debug_* sections.
+ // Do it right now because it changes the size of output sections.
for (OutputSection *sec : outputSections)
sec->maybeCompress<ELFT>();
for (Partition &part : partitions)
setPhdrs(part);
- if (config->relocatable)
- for (OutputSection *sec : outputSections)
- sec->addr = 0;
-
- // Handle --print-map(-M)/--Map, --cref and --print-archive-stats=. Dump them
- // before checkSections() because the files may be useful in case
- // checkSections() or openFile() fails, for example, due to an erroneous file
- // size.
- writeMapFile();
- writeCrossReferenceTable();
- writeArchiveStats();
+ // Handle --print-map(-M)/--Map and --cref. Dump them before checkSections()
+ // because the files may be useful in case checkSections() or openFile()
+ // fails, for example, due to an erroneous file size.
+ writeMapAndCref();
if (config->checkSections)
checkSections();
return;
if (auto e = buffer->commit())
- error("failed to write to the output file: " + toString(std::move(e)));
+ fatal("failed to write output '" + buffer->getPath() +
+ "': " + toString(std::move(e)));
}
}
// See MarkLive<ELFT>::resolveReloc().
if (config->gcSections)
return;
- // Without --gc-sections, the field is initialized with "true".
- // Drop the flag first and then rise for symbols referenced in relocations.
- for (InputFile *file : objectFiles) {
+ for (ELFFileBase *file : ctx.objectFiles) {
ObjFile<ELFT> *f = cast<ObjFile<ELFT>>(file);
- for (Symbol *b : f->getLocalSymbols())
- b->used = false;
for (InputSectionBase *s : f->getSections()) {
InputSection *isec = dyn_cast_or_null<InputSection>(s);
if (!isec)
// If --emit-reloc or -r is given, preserve symbols referenced by relocations
// from live sections.
- if (config->copyRelocs && sym.used)
+ if (sym.used && config->copyRelocs)
return true;
// Exclude local symbols pointing to .ARM.exidx sections.
// * --discard-locals is used.
// * The symbol is in a SHF_MERGE section, which is normally the reason for
// the assembler keeping the .L symbol.
- StringRef name = sym.getName();
- bool isLocal = name.startswith(".L") || name.empty();
- if (!isLocal)
- return true;
-
- if (config->discard == DiscardPolicy::Locals)
+ if ((sym.getName().startswith(".L") || sym.getName().empty()) &&
+ (config->discard == DiscardPolicy::Locals ||
+ (sym.section && (sym.section->flags & SHF_MERGE))))
return false;
-
- SectionBase *sec = sym.section;
- return !sec || !(sec->flags & SHF_MERGE);
+ return true;
}
static bool includeInSymtab(const Symbol &b) {
- if (!b.isLocal() && !b.isUsedInRegularObj)
- return false;
-
if (auto *d = dyn_cast<Defined>(&b)) {
// Always include absolute symbols.
SectionBase *sec = d->section;
if (!sec)
return true;
- sec = sec->repl;
-
- // Exclude symbols pointing to garbage-collected sections.
- if (isa<InputSectionBase>(sec) && !sec->isLive())
- return false;
if (auto *s = dyn_cast<MergeInputSection>(sec))
- if (!s->getSectionPiece(d->value)->live)
- return false;
- return true;
+ return s->getSectionPiece(d->value).live;
+ return sec->isLive();
}
- return b.used;
+ return b.used || !config->gcSections;
}
// Local symbols are not in the linker's symbol table. This function scans
llvm::TimeTraceScope timeScope("Add local symbols");
if (config->copyRelocs && config->discard != DiscardPolicy::None)
markUsedLocalSymbols<ELFT>();
- for (InputFile *file : objectFiles) {
- ObjFile<ELFT> *f = cast<ObjFile<ELFT>>(file);
- for (Symbol *b : f->getLocalSymbols()) {
+ for (ELFFileBase *file : ctx.objectFiles) {
+ for (Symbol *b : file->getLocalSymbols()) {
assert(b->isLocal() && "should have been caught in initializeSymbols()");
auto *dr = dyn_cast<Defined>(b);
// No reason to keep local undefined symbol in symtab.
if (!dr)
continue;
- if (!includeInSymtab(*b))
- continue;
- if (!shouldKeepInSymtab(*dr))
- continue;
- in.symTab->addSymbol(b);
+ if (includeInSymtab(*b) && shouldKeepInSymtab(*dr))
+ in.symTab->addSymbol(b);
}
}
}
// referring to a section (that happens if the section is a synthetic one), we
// don't create a section symbol for that section.
template <class ELFT> void Writer<ELFT>::addSectionSymbols() {
- for (BaseCommand *base : script->sectionCommands) {
- auto *sec = dyn_cast<OutputSection>(base);
- if (!sec)
+ for (SectionCommand *cmd : script->sectionCommands) {
+ auto *osd = dyn_cast<OutputDesc>(cmd);
+ if (!osd)
continue;
- auto i = llvm::find_if(sec->sectionCommands, [](BaseCommand *base) {
- if (auto *isd = dyn_cast<InputSectionDescription>(base))
- return !isd->sections.empty();
- return false;
- });
- if (i == sec->sectionCommands.end())
- continue;
- InputSectionBase *isec = cast<InputSectionDescription>(*i)->sections[0];
-
- // Relocations are not using REL[A] section symbols.
- if (isec->type == SHT_REL || isec->type == SHT_RELA)
- continue;
-
- // Unlike other synthetic sections, mergeable output sections contain data
- // copied from input sections, and there may be a relocation pointing to its
- // contents if -r or -emit-reloc are given.
- if (isa<SyntheticSection>(isec) && !(isec->flags & SHF_MERGE))
+ OutputSection &osec = osd->osec;
+ InputSectionBase *isec = nullptr;
+ // Iterate over all input sections and add a STT_SECTION symbol if any input
+ // section may be a relocation target.
+ for (SectionCommand *cmd : osec.commands) {
+ auto *isd = dyn_cast<InputSectionDescription>(cmd);
+ if (!isd)
+ continue;
+ for (InputSectionBase *s : isd->sections) {
+ // Relocations are not using REL[A] section symbols.
+ if (s->type == SHT_REL || s->type == SHT_RELA)
+ continue;
+
+ // Unlike other synthetic sections, mergeable output sections contain
+ // data copied from input sections, and there may be a relocation
+ // pointing to its contents if -r or --emit-reloc is given.
+ if (isa<SyntheticSection>(s) && !(s->flags & SHF_MERGE))
+ continue;
+
+ isec = s;
+ break;
+ }
+ }
+ if (!isec)
continue;
// Set the symbol to be relative to the output section so that its st_value
// equals the output section address. Note, there may be a gap between the
// start of the output section and isec.
- auto *sym =
- make<Defined>(isec->file, "", STB_LOCAL, /*stOther=*/0, STT_SECTION,
- /*value=*/0, /*size=*/0, isec->getOutputSection());
- in.symTab->addSymbol(sym);
+ in.symTab->addSymbol(makeDefined(isec->file, "", STB_LOCAL, /*stOther=*/0,
+ STT_SECTION,
+ /*value=*/0, /*size=*/0, &osec));
}
}
static bool isRelroSection(const OutputSection *sec) {
if (!config->zRelro)
return false;
+ if (sec->relro)
+ return true;
uint64_t flags = sec->flags;
RF_MIPS_NOT_GOT = 1 << 0
};
-static unsigned getSectionRank(const OutputSection *sec) {
- unsigned rank = sec->partition * RF_PARTITION;
+static unsigned getSectionRank(const OutputSection &osec) {
+ unsigned rank = osec.partition * RF_PARTITION;
// We want to put section specified by -T option first, so we
// can start assigning VA starting from them later.
- if (config->sectionStartMap.count(sec->name))
+ if (config->sectionStartMap.count(osec.name))
return rank;
rank |= RF_NOT_ADDR_SET;
// Allocatable sections go first to reduce the total PT_LOAD size and
// so debug info doesn't change addresses in actual code.
- if (!(sec->flags & SHF_ALLOC))
+ if (!(osec.flags & SHF_ALLOC))
return rank | RF_NOT_ALLOC;
- if (sec->type == SHT_LLVM_PART_EHDR)
+ if (osec.type == SHT_LLVM_PART_EHDR)
return rank;
rank |= RF_NOT_PART_EHDR;
- if (sec->type == SHT_LLVM_PART_PHDR)
+ if (osec.type == SHT_LLVM_PART_PHDR)
return rank;
rank |= RF_NOT_PART_PHDR;
// Put .interp first because some loaders want to see that section
// on the first page of the executable file when loaded into memory.
- if (sec->name == ".interp")
+ if (osec.name == ".interp")
return rank;
rank |= RF_NOT_INTERP;
// they are likely to be included in a core file even if core file size is
// limited. In particular, we want a .note.gnu.build-id and a .note.tag to be
// included in a core to match core files with executables.
- if (sec->type == SHT_NOTE)
+ if (osec.type == SHT_NOTE)
return rank;
rank |= RF_NOT_NOTE;
// between .text and .data.
// * Writable sections come last, such that .bss lands at the very
// end of the last PT_LOAD.
- bool isExec = sec->flags & SHF_EXECINSTR;
- bool isWrite = sec->flags & SHF_WRITE;
+ bool isExec = osec.flags & SHF_EXECINSTR;
+ bool isWrite = osec.flags & SHF_WRITE;
if (isExec) {
if (isWrite)
rank |= RF_EXEC;
} else if (isWrite) {
rank |= RF_WRITE;
- } else if (sec->type == SHT_PROGBITS) {
+ } else if (osec.type == SHT_PROGBITS) {
// Make non-executable and non-writable PROGBITS sections (e.g .rodata
// .eh_frame) closer to .text. They likely contain PC or GOT relative
// relocations and there could be relocation overflow if other huge sections
// where | marks where page alignment happens. An alternative ordering is
// PT_LOAD(.data | PT_GNU_RELRO( .data.rel.ro .bss.rel.ro) | .bss), but it may
// waste more bytes due to 2 alignment places.
- if (!isRelroSection(sec))
+ if (!isRelroSection(&osec))
rank |= RF_NOT_RELRO;
// If we got here we know that both A and B are in the same PT_LOAD.
// PT_LOAD, so stick TLS sections directly before the other RelRo R/W
// sections. Since p_filesz can be less than p_memsz, place NOBITS sections
// after PROGBITS.
- if (!(sec->flags & SHF_TLS))
+ if (!(osec.flags & SHF_TLS))
rank |= RF_NOT_TLS;
// Within TLS sections, or within other RelRo sections, or within non-RelRo
// sections, place non-NOBITS sections first.
- if (sec->type == SHT_NOBITS)
+ if (osec.type == SHT_NOBITS)
rank |= RF_BSS;
// Some architectures have additional ordering restrictions for sections
// pointer. Conversely, the special .tocbss section should be first among
// all SHT_NOBITS sections. This will put it next to the loaded special
// PPC64 sections (and, thus, within reach of the TOC base pointer).
- StringRef name = sec->name;
+ StringRef name = osec.name;
if (name != ".tocbss")
rank |= RF_PPC_NOT_TOCBSS;
if (config->emachine == EM_MIPS) {
// All sections with SHF_MIPS_GPREL flag should be grouped together
// because data in these sections is addressable with a gp relative address.
- if (sec->flags & SHF_MIPS_GPREL)
+ if (osec.flags & SHF_MIPS_GPREL)
rank |= RF_MIPS_GPREL;
- if (sec->name != ".got")
+ if (osec.name != ".got")
rank |= RF_MIPS_NOT_GOT;
}
return rank;
}
-static bool compareSections(const BaseCommand *aCmd, const BaseCommand *bCmd) {
- const OutputSection *a = cast<OutputSection>(aCmd);
- const OutputSection *b = cast<OutputSection>(bCmd);
+static bool compareSections(const SectionCommand *aCmd,
+ const SectionCommand *bCmd) {
+ const OutputSection *a = &cast<OutputDesc>(aCmd)->osec;
+ const OutputSection *b = &cast<OutputDesc>(bCmd)->osec;
if (a->sortRank != b->sortRank)
return a->sortRank < b->sortRank;
lastSec = sec;
if (!firstSec)
firstSec = sec;
- p_align = std::max(p_align, sec->alignment);
+ p_align = std::max(p_align, sec->addralign);
if (p_type == PT_LOAD)
sec->ptLoad = this;
}
// need these symbols, since IRELATIVE relocs are resolved through GOT
// and PLT. For details, see http://www.airs.com/blog/archives/403.
template <class ELFT> void Writer<ELFT>::addRelIpltSymbols() {
- if (config->relocatable || config->isPic)
+ if (config->isPic)
return;
// By default, __rela_iplt_{start,end} belong to a dummy section 0
// sure that .rela.plt exists in output.
ElfSym::relaIpltStart = addOptionalRegular(
config->isRela ? "__rela_iplt_start" : "__rel_iplt_start",
- Out::elfHeader, 0, STV_HIDDEN, STB_WEAK);
+ Out::elfHeader, 0, STV_HIDDEN);
ElfSym::relaIpltEnd = addOptionalRegular(
config->isRela ? "__rela_iplt_end" : "__rel_iplt_end",
- Out::elfHeader, 0, STV_HIDDEN, STB_WEAK);
-}
-
-template <class ELFT>
-void Writer<ELFT>::forEachRelSec(
- llvm::function_ref<void(InputSectionBase &)> fn) {
- // Scan all relocations. Each relocation goes through a series
- // of tests to determine if it needs special treatment, such as
- // creating GOT, PLT, copy relocations, etc.
- // Note that relocations for non-alloc sections are directly
- // processed by InputSection::relocateNonAlloc.
- for (InputSectionBase *isec : inputSections)
- if (isec->isLive() && isa<InputSection>(isec) && (isec->flags & SHF_ALLOC))
- fn(*isec);
- for (Partition &part : partitions) {
- for (EhInputSection *es : part.ehFrame->sections)
- fn(*es);
- if (part.armExidx && part.armExidx->isLive())
- for (InputSection *ex : part.armExidx->exidxSections)
- fn(*ex);
- }
+ Out::elfHeader, 0, STV_HIDDEN);
}
// This function generates assignments for predefined symbols (e.g. _end or
if (ElfSym::globalOffsetTable) {
// The _GLOBAL_OFFSET_TABLE_ symbol is defined by target convention usually
// to the start of the .got or .got.plt section.
- InputSection *gotSection = in.gotPlt;
+ InputSection *sec = in.gotPlt.get();
if (!target->gotBaseSymInGotPlt)
- gotSection = in.mipsGot ? cast<InputSection>(in.mipsGot)
- : cast<InputSection>(in.got);
- ElfSym::globalOffsetTable->section = gotSection;
+ sec = in.mipsGot ? cast<InputSection>(in.mipsGot.get())
+ : cast<InputSection>(in.got.get());
+ ElfSym::globalOffsetTable->section = sec;
}
// .rela_iplt_{start,end} mark the start and the end of in.relaIplt.
if (ElfSym::relaIpltStart && in.relaIplt->isNeeded()) {
- ElfSym::relaIpltStart->section = in.relaIplt;
- ElfSym::relaIpltEnd->section = in.relaIplt;
+ ElfSym::relaIpltStart->section = in.relaIplt.get();
+ ElfSym::relaIpltEnd->section = in.relaIplt.get();
ElfSym::relaIpltEnd->value = in.relaIplt->getSize();
}
// The more branches in getSectionRank that match, the more similar they are.
// Since each branch corresponds to a bit flag, we can just use
// countLeadingZeros.
-static int getRankProximityAux(OutputSection *a, OutputSection *b) {
- return countLeadingZeros(a->sortRank ^ b->sortRank);
-}
-
-static int getRankProximity(OutputSection *a, BaseCommand *b) {
- auto *sec = dyn_cast<OutputSection>(b);
- return (sec && sec->hasInputSections) ? getRankProximityAux(a, sec) : -1;
+static int getRankProximity(OutputSection *a, SectionCommand *b) {
+ auto *osd = dyn_cast<OutputDesc>(b);
+ return (osd && osd->osec.hasInputSections)
+ ? countLeadingZeros(a->sortRank ^ osd->osec.sortRank)
+ : -1;
}
// When placing orphan sections, we want to place them after symbol assignments
// /* The RW PT_LOAD starts here*/
// rw_sec : { *(rw_sec) }
// would mean that the RW PT_LOAD would become unaligned.
-static bool shouldSkip(BaseCommand *cmd) {
+static bool shouldSkip(SectionCommand *cmd) {
if (auto *assign = dyn_cast<SymbolAssignment>(cmd))
return assign->name != ".";
return false;
// We want to place orphan sections so that they share as much
// characteristics with their neighbors as possible. For example, if
// both are rw, or both are tls.
-static std::vector<BaseCommand *>::iterator
-findOrphanPos(std::vector<BaseCommand *>::iterator b,
- std::vector<BaseCommand *>::iterator e) {
- OutputSection *sec = cast<OutputSection>(*e);
+static SmallVectorImpl<SectionCommand *>::iterator
+findOrphanPos(SmallVectorImpl<SectionCommand *>::iterator b,
+ SmallVectorImpl<SectionCommand *>::iterator e) {
+ OutputSection *sec = &cast<OutputDesc>(*e)->osec;
// Find the first element that has as close a rank as possible.
- auto i = std::max_element(b, e, [=](BaseCommand *a, BaseCommand *b) {
+ auto i = std::max_element(b, e, [=](SectionCommand *a, SectionCommand *b) {
return getRankProximity(sec, a) < getRankProximity(sec, b);
});
if (i == e)
return e;
+ if (!isa<OutputDesc>(*i))
+ return e;
+ auto foundSec = &cast<OutputDesc>(*i)->osec;
// Consider all existing sections with the same proximity.
int proximity = getRankProximity(sec, *i);
+ unsigned sortRank = sec->sortRank;
+ if (script->hasPhdrsCommands() || !script->memoryRegions.empty())
+ // Prevent the orphan section to be placed before the found section. If
+ // custom program headers are defined, that helps to avoid adding it to a
+ // previous segment and changing flags of that segment, for example, making
+ // a read-only segment writable. If memory regions are defined, an orphan
+ // section should continue the same region as the found section to better
+ // resemble the behavior of GNU ld.
+ sortRank = std::max(sortRank, foundSec->sortRank);
for (; i != e; ++i) {
- auto *curSec = dyn_cast<OutputSection>(*i);
- if (!curSec || !curSec->hasInputSections)
+ auto *curSecDesc = dyn_cast<OutputDesc>(*i);
+ if (!curSecDesc || !curSecDesc->osec.hasInputSections)
continue;
- if (getRankProximity(sec, curSec) != proximity ||
- sec->sortRank < curSec->sortRank)
+ if (getRankProximity(sec, curSecDesc) != proximity ||
+ sortRank < curSecDesc->osec.sortRank)
break;
}
- auto isOutputSecWithInputSections = [](BaseCommand *cmd) {
- auto *os = dyn_cast<OutputSection>(cmd);
- return os && os->hasInputSections;
+ auto isOutputSecWithInputSections = [](SectionCommand *cmd) {
+ auto *osd = dyn_cast<OutputDesc>(cmd);
+ return osd && osd->osec.hasInputSections;
};
- auto j = std::find_if(llvm::make_reverse_iterator(i),
- llvm::make_reverse_iterator(b),
- isOutputSecWithInputSections);
+ auto j =
+ std::find_if(std::make_reverse_iterator(i), std::make_reverse_iterator(b),
+ isOutputSecWithInputSections);
i = j.base();
// As a special case, if the orphan section is the last section, put
if (config->shuffleSections.empty())
return;
- std::vector<InputSectionBase *> matched, sections = inputSections;
+ SmallVector<InputSectionBase *, 0> matched, sections = ctx.inputSections;
matched.reserve(sections.size());
for (const auto &patAndSeed : config->shuffleSections) {
matched.clear();
// Builds section order for handling --symbol-ordering-file.
static DenseMap<const InputSectionBase *, int> buildSectionOrder() {
DenseMap<const InputSectionBase *, int> sectionOrder;
- // Use the rarely used option -call-graph-ordering-file to sort sections.
+ // Use the rarely used option --call-graph-ordering-file to sort sections.
if (!config->callGraphProfile.empty())
return computeCallGraphProfileOrder();
// Build a map from symbols to their priorities. Symbols that didn't
// appear in the symbol ordering file have the lowest priority 0.
// All explicitly mentioned symbols have negative (higher) priorities.
- DenseMap<StringRef, SymbolOrderEntry> symbolOrder;
+ DenseMap<CachedHashStringRef, SymbolOrderEntry> symbolOrder;
int priority = -config->symbolOrderingFile.size();
for (StringRef s : config->symbolOrderingFile)
- symbolOrder.insert({s, {priority++, false}});
+ symbolOrder.insert({CachedHashStringRef(s), {priority++, false}});
// Build a map from sections to their priorities.
auto addSym = [&](Symbol &sym) {
- auto it = symbolOrder.find(sym.getName());
+ auto it = symbolOrder.find(CachedHashStringRef(sym.getName()));
if (it == symbolOrder.end())
return;
SymbolOrderEntry &ent = it->second;
if (auto *d = dyn_cast<Defined>(&sym)) {
if (auto *sec = dyn_cast_or_null<InputSectionBase>(d->section)) {
- int &priority = sectionOrder[cast<InputSectionBase>(sec->repl)];
+ int &priority = sectionOrder[cast<InputSectionBase>(sec)];
priority = std::min(priority, ent.priority);
}
}
// We want both global and local symbols. We get the global ones from the
// symbol table and iterate the object files for the local ones.
- for (Symbol *sym : symtab->symbols())
- if (!sym->isLazy())
- addSym(*sym);
+ for (Symbol *sym : symtab.getSymbols())
+ addSym(*sym);
- for (InputFile *file : objectFiles)
- for (Symbol *sym : file->getSymbols()) {
- if (!sym->isLocal())
- break;
+ for (ELFFileBase *file : ctx.objectFiles)
+ for (Symbol *sym : file->getLocalSymbols())
addSym(*sym);
- }
if (config->warnSymbolOrdering)
for (auto orderEntry : symbolOrder)
if (!orderEntry.second.present)
- warn("symbol ordering file: no such symbol: " + orderEntry.first);
+ warn("symbol ordering file: no such symbol: " + orderEntry.first.val());
return sectionOrder;
}
// Sorts the sections in ISD according to the provided section order.
static void
sortISDBySectionOrder(InputSectionDescription *isd,
- const DenseMap<const InputSectionBase *, int> &order) {
- std::vector<InputSection *> unorderedSections;
- std::vector<std::pair<InputSection *, int>> orderedSections;
+ const DenseMap<const InputSectionBase *, int> &order,
+ bool executableOutputSection) {
+ SmallVector<InputSection *, 0> unorderedSections;
+ SmallVector<std::pair<InputSection *, int>, 0> orderedSections;
uint64_t unorderedSize = 0;
+ uint64_t totalSize = 0;
for (InputSection *isec : isd->sections) {
+ if (executableOutputSection)
+ totalSize += isec->getSize();
auto i = order.find(isec);
if (i == order.end()) {
unorderedSections.push_back(isec);
// of the second block of cold code can call the hot code without a thunk. So
// we effectively double the amount of code that could potentially call into
// the hot code without a thunk.
+ //
+ // The above is not necessary if total size of input sections in this "isd"
+ // is small. Note that we assume all input sections are executable if the
+ // output section is executable (which is not always true but supposed to
+ // cover most cases).
size_t insPt = 0;
- if (target->getThunkSectionSpacing() && !orderedSections.empty()) {
+ if (executableOutputSection && !orderedSections.empty() &&
+ target->getThunkSectionSpacing() &&
+ totalSize >= target->getThunkSectionSpacing()) {
uint64_t unorderedPos = 0;
for (; insPt != unorderedSections.size(); ++insPt) {
unorderedPos += unorderedSections[insPt]->getSize();
}
isd->sections.clear();
- for (InputSection *isec : makeArrayRef(unorderedSections).slice(0, insPt))
+ for (InputSection *isec : ArrayRef(unorderedSections).slice(0, insPt))
isd->sections.push_back(isec);
for (std::pair<InputSection *, int> p : orderedSections)
isd->sections.push_back(p.first);
- for (InputSection *isec : makeArrayRef(unorderedSections).slice(insPt))
+ for (InputSection *isec : ArrayRef(unorderedSections).slice(insPt))
isd->sections.push_back(isec);
}
-static void sortSection(OutputSection *sec,
+static void sortSection(OutputSection &osec,
const DenseMap<const InputSectionBase *, int> &order) {
- StringRef name = sec->name;
+ StringRef name = osec.name;
// Never sort these.
if (name == ".init" || name == ".fini")
// sections in the required order from the beginning so that the in.relaIplt
// section is placed last in an output section. Here we just do not apply
// sorting for an output section which holds the in.relaIplt section.
- if (in.relaIplt->getParent() == sec)
+ if (in.relaIplt->getParent() == &osec)
return;
// Sort input sections by priority using the list provided by
// digit radix sort. The sections may be sorted stably again by a more
// significant key.
if (!order.empty())
- for (BaseCommand *b : sec->sectionCommands)
+ for (SectionCommand *b : osec.commands)
if (auto *isd = dyn_cast<InputSectionDescription>(b))
- sortISDBySectionOrder(isd, order);
-
- // Sort input sections by section name suffixes for
- // __attribute__((init_priority(N))).
- if (name == ".init_array" || name == ".fini_array") {
- if (!script->hasSectionsCommand)
- sec->sortInitFini();
- return;
- }
+ sortISDBySectionOrder(isd, order, osec.flags & SHF_EXECINSTR);
- // Sort input sections by the special rule for .ctors and .dtors.
- if (name == ".ctors" || name == ".dtors") {
- if (!script->hasSectionsCommand)
- sec->sortCtorsDtors();
+ if (script->hasSectionsCommand)
return;
- }
- // .toc is allocated just after .got and is accessed using GOT-relative
- // relocations. Object files compiled with small code model have an
- // addressable range of [.got, .got + 0xFFFC] for GOT-relative relocations.
- // To reduce the risk of relocation overflow, .toc contents are sorted so that
- // sections having smaller relocation offsets are at beginning of .toc
- if (config->emachine == EM_PPC64 && name == ".toc") {
- if (script->hasSectionsCommand)
- return;
- assert(sec->sectionCommands.size() == 1);
- auto *isd = cast<InputSectionDescription>(sec->sectionCommands[0]);
+ if (name == ".init_array" || name == ".fini_array") {
+ osec.sortInitFini();
+ } else if (name == ".ctors" || name == ".dtors") {
+ osec.sortCtorsDtors();
+ } else if (config->emachine == EM_PPC64 && name == ".toc") {
+ // .toc is allocated just after .got and is accessed using GOT-relative
+ // relocations. Object files compiled with small code model have an
+ // addressable range of [.got, .got + 0xFFFC] for GOT-relative relocations.
+ // To reduce the risk of relocation overflow, .toc contents are sorted so
+ // that sections having smaller relocation offsets are at beginning of .toc
+ assert(osec.commands.size() == 1);
+ auto *isd = cast<InputSectionDescription>(osec.commands[0]);
llvm::stable_sort(isd->sections,
[](const InputSection *a, const InputSection *b) -> bool {
return a->file->ppc64SmallCodeModelTocRelocs &&
!b->file->ppc64SmallCodeModelTocRelocs;
});
- return;
}
}
// Build the order once since it is expensive.
DenseMap<const InputSectionBase *, int> order = buildSectionOrder();
maybeShuffle(order);
- for (BaseCommand *base : script->sectionCommands)
- if (auto *sec = dyn_cast<OutputSection>(base))
- sortSection(sec, order);
+ for (SectionCommand *cmd : script->sectionCommands)
+ if (auto *osd = dyn_cast<OutputDesc>(cmd))
+ sortSection(osd->osec, order);
}
template <class ELFT> void Writer<ELFT>::sortSections() {
llvm::TimeTraceScope timeScope("Sort sections");
- script->adjustSectionsBeforeSorting();
// Don't sort if using -r. It is not necessary and we want to preserve the
// relative order for SHF_LINK_ORDER sections.
- if (config->relocatable)
+ if (config->relocatable) {
+ script->adjustOutputSections();
return;
+ }
sortInputSections();
- for (BaseCommand *base : script->sectionCommands) {
- auto *os = dyn_cast<OutputSection>(base);
- if (!os)
- continue;
- os->sortRank = getSectionRank(os);
-
- // We want to assign rude approximation values to outSecOff fields
- // to know the relative order of the input sections. We use it for
- // sorting SHF_LINK_ORDER sections. See resolveShfLinkOrder().
- uint64_t i = 0;
- for (InputSection *sec : getInputSections(os))
- sec->outSecOff = i++;
- }
-
+ for (SectionCommand *cmd : script->sectionCommands)
+ if (auto *osd = dyn_cast<OutputDesc>(cmd))
+ osd->osec.sortRank = getSectionRank(osd->osec);
if (!script->hasSectionsCommand) {
// We know that all the OutputSections are contiguous in this case.
- auto isSection = [](BaseCommand *base) { return isa<OutputSection>(base); };
+ auto isSection = [](SectionCommand *cmd) { return isa<OutputDesc>(cmd); };
std::stable_sort(
llvm::find_if(script->sectionCommands, isSection),
llvm::find_if(llvm::reverse(script->sectionCommands), isSection).base(),
compareSections);
-
- // Process INSERT commands. From this point onwards the order of
- // script->sectionCommands is fixed.
- script->processInsertCommands();
- return;
}
+ // Process INSERT commands and update output section attributes. From this
+ // point onwards the order of script->sectionCommands is fixed.
script->processInsertCommands();
+ script->adjustOutputSections();
+
+ if (!script->hasSectionsCommand)
+ return;
// Orphan sections are sections present in the input files which are
// not explicitly placed into the output file by the linker script.
auto i = script->sectionCommands.begin();
auto e = script->sectionCommands.end();
- auto nonScriptI = std::find_if(i, e, [](BaseCommand *base) {
- if (auto *sec = dyn_cast<OutputSection>(base))
- return sec->sectionIndex == UINT32_MAX;
+ auto nonScriptI = std::find_if(i, e, [](SectionCommand *cmd) {
+ if (auto *osd = dyn_cast<OutputDesc>(cmd))
+ return osd->osec.sectionIndex == UINT32_MAX;
return false;
});
// the script with ". = 0xabcd" and the expectation is that every section is
// after that.
auto firstSectionOrDotAssignment =
- std::find_if(i, e, [](BaseCommand *cmd) { return !shouldSkip(cmd); });
+ std::find_if(i, e, [](SectionCommand *cmd) { return !shouldSkip(cmd); });
if (firstSectionOrDotAssignment != e &&
isa<SymbolAssignment>(**firstSectionOrDotAssignment))
++firstSectionOrDotAssignment;
while (nonScriptI != e) {
auto pos = findOrphanPos(i, nonScriptI);
- OutputSection *orphan = cast<OutputSection>(*nonScriptI);
+ OutputSection *orphan = &cast<OutputDesc>(*nonScriptI)->osec;
// As an optimization, find all sections with the same sort rank
// and insert them with one rotate.
unsigned rank = orphan->sortRank;
- auto end = std::find_if(nonScriptI + 1, e, [=](BaseCommand *cmd) {
- return cast<OutputSection>(cmd)->sortRank != rank;
+ auto end = std::find_if(nonScriptI + 1, e, [=](SectionCommand *cmd) {
+ return cast<OutputDesc>(cmd)->osec.sortRank != rank;
});
std::rotate(pos, nonScriptI, end);
nonScriptI = end;
// Link order may be distributed across several InputSectionDescriptions.
// Sorting is performed separately.
- std::vector<InputSection **> scriptSections;
- std::vector<InputSection *> sections;
- for (BaseCommand *base : sec->sectionCommands) {
- auto *isd = dyn_cast<InputSectionDescription>(base);
+ SmallVector<InputSection **, 0> scriptSections;
+ SmallVector<InputSection *, 0> sections;
+ for (SectionCommand *cmd : sec->commands) {
+ auto *isd = dyn_cast<InputSectionDescription>(cmd);
if (!isd)
continue;
bool hasLinkOrder = false;
// can assign Virtual Addresses to OutputSections that are not monotonically
// increasing.
for (Partition &part : partitions)
- finalizeSynthetic(part.armExidx);
+ finalizeSynthetic(part.armExidx.get());
resolveShfLinkOrder();
// Converts call x@GDPLT to call __tls_get_addr
if (config->emachine == EM_HEXAGON)
hexagonTLSSymbolUpdate(outputSections);
- int assignPasses = 0;
+ uint32_t pass = 0, assignPasses = 0;
for (;;) {
- bool changed = target->needsThunks && tc.createThunks(outputSections);
+ bool changed = target->needsThunks ? tc.createThunks(pass, outputSections)
+ : target->relaxOnce(pass);
+ ++pass;
// With Thunk Size much smaller than branch range we expect to
// converge quickly; if we get to 15 something has gone wrong.
- if (changed && tc.pass >= 15) {
- error("thunk creation not converged");
+ if (changed && pass >= 15) {
+ error(target->needsThunks ? "thunk creation not converged"
+ : "relaxation not converged");
break;
}
}
}
}
+ if (!config->relocatable && config->emachine == EM_RISCV)
+ riscvFinalizeRelax(pass);
+
+ if (config->relocatable)
+ for (OutputSection *sec : outputSections)
+ sec->addr = 0;
// If addrExpr is set, the address may not be a multiple of the alignment.
// Warn because this is error-prone.
- for (BaseCommand *cmd : script->sectionCommands)
- if (auto *os = dyn_cast<OutputSection>(cmd))
- if (os->addr % os->alignment != 0)
- warn("address (0x" + Twine::utohexstr(os->addr) + ") of section " +
- os->name + " is not a multiple of alignment (" +
- Twine(os->alignment) + ")");
+ for (SectionCommand *cmd : script->sectionCommands)
+ if (auto *osd = dyn_cast<OutputDesc>(cmd)) {
+ OutputSection *osec = &osd->osec;
+ if (osec->addr % osec->addralign != 0)
+ warn("address (0x" + Twine::utohexstr(osec->addr) + ") of section " +
+ osec->name + " is not a multiple of alignment (" +
+ Twine(osec->addralign) + ")");
+ }
}
// If Input Sections have been shrunk (basic block sections) then
// block sections, input sections can shrink when the jump instructions at
// the end of the section are relaxed.
static void fixSymbolsAfterShrinking() {
- for (InputFile *File : objectFiles) {
+ for (InputFile *File : ctx.objectFiles) {
parallelForEach(File->getSymbols(), [&](Symbol *Sym) {
auto *def = dyn_cast<Defined>(Sym);
if (!def)
if (!sec)
return;
- const InputSectionBase *inputSec = dyn_cast<InputSectionBase>(sec->repl);
+ const InputSectionBase *inputSec = dyn_cast<InputSectionBase>(sec);
if (!inputSec || !inputSec->bytesDropped)
return;
- const size_t OldSize = inputSec->data().size();
+ const size_t OldSize = inputSec->content().size();
const size_t NewSize = OldSize - inputSec->bytesDropped;
if (def->value > NewSize && def->value <= OldSize) {
// option is used.
template <class ELFT> void Writer<ELFT>::optimizeBasicBlockJumps() {
assert(config->optimizeBBJumps);
+ SmallVector<InputSection *, 0> storage;
script->assignAddresses();
// For every output section that has executable input sections, this
// jump to the following section as it is not required.
// 2. If there are two consecutive jump instructions, it checks
// if they can be flipped and one can be deleted.
- for (OutputSection *os : outputSections) {
- if (!(os->flags & SHF_EXECINSTR))
+ for (OutputSection *osec : outputSections) {
+ if (!(osec->flags & SHF_EXECINSTR))
continue;
- std::vector<InputSection *> sections = getInputSections(os);
- std::vector<unsigned> result(sections.size());
+ ArrayRef<InputSection *> sections = getInputSections(*osec, storage);
+ size_t numDeleted = 0;
// Delete all fall through jump instructions. Also, check if two
// consecutive jump instructions can be flipped so that a fall
// through jmp instruction can be deleted.
- parallelForEachN(0, sections.size(), [&](size_t i) {
+ for (size_t i = 0, e = sections.size(); i != e; ++i) {
InputSection *next = i + 1 < sections.size() ? sections[i + 1] : nullptr;
- InputSection &is = *sections[i];
- result[i] =
- target->deleteFallThruJmpInsn(is, is.getFile<ELFT>(), next) ? 1 : 0;
- });
- size_t numDeleted = std::count(result.begin(), result.end(), 1);
+ InputSection &sec = *sections[i];
+ numDeleted += target->deleteFallThruJmpInsn(sec, sec.file, next);
+ }
if (numDeleted > 0) {
script->assignAddresses();
LLVM_DEBUG(llvm::dbgs()
fixSymbolsAfterShrinking();
- for (OutputSection *os : outputSections) {
- std::vector<InputSection *> sections = getInputSections(os);
- for (InputSection *is : sections)
+ for (OutputSection *osec : outputSections)
+ for (InputSection *is : getInputSections(*osec, storage))
is->trim();
- }
}
// In order to allow users to manipulate linker-synthesized sections,
// All input synthetic sections that can be empty are placed after
// all regular ones. Reverse iterate to find the first synthetic section
// after a non-synthetic one which will be our starting point.
- auto start = std::find_if(inputSections.rbegin(), inputSections.rend(),
- [](InputSectionBase *s) {
- return !isa<SyntheticSection>(s);
- })
- .base();
-
- DenseSet<InputSectionDescription *> isdSet;
- // Mark unused synthetic sections for deletion
- auto end = std::stable_partition(
- start, inputSections.end(), [&](InputSectionBase *s) {
- SyntheticSection *ss = dyn_cast<SyntheticSection>(s);
- OutputSection *os = ss->getParent();
- if (!os || ss->isNeeded())
- return true;
-
- // If we reach here, then ss is an unused synthetic section and we want
- // to remove it from the corresponding input section description, and
- // orphanSections.
- for (BaseCommand *b : os->sectionCommands)
- if (auto *isd = dyn_cast<InputSectionDescription>(b))
- isdSet.insert(isd);
-
- llvm::erase_if(
- script->orphanSections,
- [=](const InputSectionBase *isec) { return isec == ss; });
-
- return false;
+ auto start =
+ llvm::find_if(llvm::reverse(ctx.inputSections), [](InputSectionBase *s) {
+ return !isa<SyntheticSection>(s);
+ }).base();
+
+ // Remove unused synthetic sections from ctx.inputSections;
+ DenseSet<InputSectionBase *> unused;
+ auto end =
+ std::remove_if(start, ctx.inputSections.end(), [&](InputSectionBase *s) {
+ auto *sec = cast<SyntheticSection>(s);
+ if (sec->getParent() && sec->isNeeded())
+ return false;
+ unused.insert(sec);
+ return true;
});
-
- DenseSet<InputSectionBase *> unused(end, inputSections.end());
- for (auto *isd : isdSet)
- llvm::erase_if(isd->sections,
- [=](InputSection *isec) { return unused.count(isec); });
-
- // Erase unused synthetic sections.
- inputSections.erase(end, inputSections.end());
+ ctx.inputSections.erase(end, ctx.inputSections.end());
+
+ // Remove unused synthetic sections from the corresponding input section
+ // description and orphanSections.
+ for (auto *sec : unused)
+ if (OutputSection *osec = cast<SyntheticSection>(sec)->getParent())
+ for (SectionCommand *cmd : osec->commands)
+ if (auto *isd = dyn_cast<InputSectionDescription>(cmd))
+ llvm::erase_if(isd->sections, [&](InputSection *isec) {
+ return unused.count(isec);
+ });
+ llvm::erase_if(script->orphanSections, [&](const InputSectionBase *sec) {
+ return unused.count(sec);
+ });
}
// Create output section objects and add them to OutputSections.
template <class ELFT> void Writer<ELFT>::finalizeSections() {
- Out::preinitArray = findSection(".preinit_array");
- Out::initArray = findSection(".init_array");
- Out::finiArray = findSection(".fini_array");
-
- // The linker needs to define SECNAME_start, SECNAME_end and SECNAME_stop
- // symbols for sections, so that the runtime can get the start and end
- // addresses of each section by section name. Add such symbols.
if (!config->relocatable) {
+ Out::preinitArray = findSection(".preinit_array");
+ Out::initArray = findSection(".init_array");
+ Out::finiArray = findSection(".fini_array");
+
+ // The linker needs to define SECNAME_start, SECNAME_end and SECNAME_stop
+ // symbols for sections, so that the runtime can get the start and end
+ // addresses of each section by section name. Add such symbols.
addStartEndSymbols();
- for (BaseCommand *base : script->sectionCommands)
- if (auto *sec = dyn_cast<OutputSection>(base))
- addStartStopSymbols(sec);
- }
-
- // Add _DYNAMIC symbol. Unlike GNU gold, our _DYNAMIC symbol has no type.
- // It should be okay as no one seems to care about the type.
- // Even the author of gold doesn't remember why gold behaves that way.
- // https://sourceware.org/ml/binutils/2002-03/msg00360.html
- if (mainPart->dynamic->parent)
- symtab->addSymbol(Defined{/*file=*/nullptr, "_DYNAMIC", STB_WEAK,
- STV_HIDDEN, STT_NOTYPE,
- /*value=*/0, /*size=*/0, mainPart->dynamic});
-
- // Define __rel[a]_iplt_{start,end} symbols if needed.
- addRelIpltSymbols();
-
- // RISC-V's gp can address +/- 2 KiB, set it to .sdata + 0x800. This symbol
- // should only be defined in an executable. If .sdata does not exist, its
- // value/section does not matter but it has to be relative, so set its
- // st_shndx arbitrarily to 1 (Out::elfHeader).
- if (config->emachine == EM_RISCV && !config->shared) {
- OutputSection *sec = findSection(".sdata");
- ElfSym::riscvGlobalPointer =
- addOptionalRegular("__global_pointer$", sec ? sec : Out::elfHeader,
- 0x800, STV_DEFAULT, STB_GLOBAL);
- }
-
- if (config->emachine == EM_X86_64) {
- // On targets that support TLSDESC, _TLS_MODULE_BASE_ is defined in such a
- // way that:
- //
- // 1) Without relaxation: it produces a dynamic TLSDESC relocation that
- // computes 0.
- // 2) With LD->LE relaxation: _TLS_MODULE_BASE_@tpoff = 0 (lowest address in
- // the TLS block).
- //
- // 2) is special cased in @tpoff computation. To satisfy 1), we define it as
- // an absolute symbol of zero. This is different from GNU linkers which
- // define _TLS_MODULE_BASE_ relative to the first TLS section.
- Symbol *s = symtab->find("_TLS_MODULE_BASE_");
- if (s && s->isUndefined()) {
- s->resolve(Defined{/*file=*/nullptr, s->getName(), STB_GLOBAL, STV_HIDDEN,
- STT_TLS, /*value=*/0, 0,
- /*section=*/nullptr});
- ElfSym::tlsModuleBase = cast<Defined>(s);
+ for (SectionCommand *cmd : script->sectionCommands)
+ if (auto *osd = dyn_cast<OutputDesc>(cmd))
+ addStartStopSymbols(osd->osec);
+
+ // Add _DYNAMIC symbol. Unlike GNU gold, our _DYNAMIC symbol has no type.
+ // It should be okay as no one seems to care about the type.
+ // Even the author of gold doesn't remember why gold behaves that way.
+ // https://sourceware.org/ml/binutils/2002-03/msg00360.html
+ if (mainPart->dynamic->parent) {
+ Symbol *s = symtab.addSymbol(Defined{
+ /*file=*/nullptr, "_DYNAMIC", STB_WEAK, STV_HIDDEN, STT_NOTYPE,
+ /*value=*/0, /*size=*/0, mainPart->dynamic.get()});
+ s->isUsedInRegularObj = true;
+ }
+
+ // Define __rel[a]_iplt_{start,end} symbols if needed.
+ addRelIpltSymbols();
+
+ // RISC-V's gp can address +/- 2 KiB, set it to .sdata + 0x800. This symbol
+ // should only be defined in an executable. If .sdata does not exist, its
+ // value/section does not matter but it has to be relative, so set its
+ // st_shndx arbitrarily to 1 (Out::elfHeader).
+ if (config->emachine == EM_RISCV && !config->shared) {
+ OutputSection *sec = findSection(".sdata");
+ addOptionalRegular("__global_pointer$", sec ? sec : Out::elfHeader, 0x800,
+ STV_DEFAULT);
+ }
+
+ if (config->emachine == EM_386 || config->emachine == EM_X86_64) {
+ // On targets that support TLSDESC, _TLS_MODULE_BASE_ is defined in such a
+ // way that:
+ //
+ // 1) Without relaxation: it produces a dynamic TLSDESC relocation that
+ // computes 0.
+ // 2) With LD->LE relaxation: _TLS_MODULE_BASE_@tpoff = 0 (lowest address
+ // in the TLS block).
+ //
+ // 2) is special cased in @tpoff computation. To satisfy 1), we define it
+ // as an absolute symbol of zero. This is different from GNU linkers which
+ // define _TLS_MODULE_BASE_ relative to the first TLS section.
+ Symbol *s = symtab.find("_TLS_MODULE_BASE_");
+ if (s && s->isUndefined()) {
+ s->resolve(Defined{/*file=*/nullptr, StringRef(), STB_GLOBAL,
+ STV_HIDDEN, STT_TLS, /*value=*/0, 0,
+ /*section=*/nullptr});
+ ElfSym::tlsModuleBase = cast<Defined>(s);
+ }
}
- }
- {
- llvm::TimeTraceScope timeScope("Finalize .eh_frame");
// This responsible for splitting up .eh_frame section into
// pieces. The relocation scan uses those pieces, so this has to be
// earlier.
- for (Partition &part : partitions)
- finalizeSynthetic(part.ehFrame);
- }
+ {
+ llvm::TimeTraceScope timeScope("Finalize .eh_frame");
+ for (Partition &part : partitions)
+ finalizeSynthetic(part.ehFrame.get());
+ }
- for (Symbol *sym : symtab->symbols())
- sym->isPreemptible = computeIsPreemptible(*sym);
+ if (config->hasDynSymTab) {
+ parallelForEach(symtab.getSymbols(), [](Symbol *sym) {
+ sym->isPreemptible = computeIsPreemptible(*sym);
+ });
+ }
+ }
// Change values of linker-script-defined symbols from placeholders (assigned
// by declareSymbols) to actual definitions.
script->processSymbolAssignments();
- {
+ if (!config->relocatable) {
llvm::TimeTraceScope timeScope("Scan relocations");
// Scan relocations. This must be done after every symbol is declared so
// that we can correctly decide if a dynamic relocation is needed. This is
// called after processSymbolAssignments() because it needs to know whether
// a linker-script-defined symbol is absolute.
ppc64noTocRelax.clear();
- if (!config->relocatable) {
- forEachRelSec(scanRelocations<ELFT>);
- reportUndefinedSymbols<ELFT>();
- }
- }
-
- if (in.plt && in.plt->isNeeded())
- in.plt->addSymbols();
- if (in.iplt && in.iplt->isNeeded())
- in.iplt->addSymbols();
-
- if (config->unresolvedSymbolsInShlib != UnresolvedPolicy::Ignore) {
- auto diagnose =
- config->unresolvedSymbolsInShlib == UnresolvedPolicy::ReportError
- ? errorOrWarn
- : warn;
- // Error on undefined symbols in a shared object, if all of its DT_NEEDED
- // entries are seen. These cases would otherwise lead to runtime errors
- // reported by the dynamic linker.
- //
- // ld.bfd traces all DT_NEEDED to emulate the logic of the dynamic linker to
- // catch more cases. That is too much for us. Our approach resembles the one
- // used in ld.gold, achieves a good balance to be useful but not too smart.
- for (SharedFile *file : sharedFiles) {
- bool allNeededIsKnown =
- llvm::all_of(file->dtNeeded, [&](StringRef needed) {
- return symtab->soNames.count(needed);
- });
- if (!allNeededIsKnown)
- continue;
- for (Symbol *sym : file->requiredSymbols)
- if (sym->isUndefined() && !sym->isWeak())
- diagnose(toString(file) + ": undefined reference to " +
- toString(*sym) + " [--no-allow-shlib-undefined]");
+ scanRelocations<ELFT>();
+ reportUndefinedSymbols();
+ postScanRelocations();
+
+ if (in.plt && in.plt->isNeeded())
+ in.plt->addSymbols();
+ if (in.iplt && in.iplt->isNeeded())
+ in.iplt->addSymbols();
+
+ if (config->unresolvedSymbolsInShlib != UnresolvedPolicy::Ignore) {
+ auto diagnose =
+ config->unresolvedSymbolsInShlib == UnresolvedPolicy::ReportError
+ ? errorOrWarn
+ : warn;
+ // Error on undefined symbols in a shared object, if all of its DT_NEEDED
+ // entries are seen. These cases would otherwise lead to runtime errors
+ // reported by the dynamic linker.
+ //
+ // ld.bfd traces all DT_NEEDED to emulate the logic of the dynamic linker
+ // to catch more cases. That is too much for us. Our approach resembles
+ // the one used in ld.gold, achieves a good balance to be useful but not
+ // too smart.
+ for (SharedFile *file : ctx.sharedFiles) {
+ bool allNeededIsKnown =
+ llvm::all_of(file->dtNeeded, [&](StringRef needed) {
+ return symtab.soNames.count(CachedHashStringRef(needed));
+ });
+ if (!allNeededIsKnown)
+ continue;
+ for (Symbol *sym : file->requiredSymbols)
+ if (sym->isUndefined() && !sym->isWeak())
+ diagnose("undefined reference due to --no-allow-shlib-undefined: " +
+ toString(*sym) + "\n>>> referenced by " + toString(file));
+ }
}
}
llvm::TimeTraceScope timeScope("Add symbols to symtabs");
// Now that we have defined all possible global symbols including linker-
// synthesized ones. Visit all symbols to give the finishing touches.
- for (Symbol *sym : symtab->symbols()) {
- if (!includeInSymtab(*sym))
+ for (Symbol *sym : symtab.getSymbols()) {
+ if (!sym->isUsedInRegularObj || !includeInSymtab(*sym))
continue;
+ if (!config->relocatable)
+ sym->binding = sym->computeBinding();
if (in.symTab)
in.symTab->addSymbol(sym);
}
}
- // Do not proceed if there was an undefined symbol.
- if (errorCount())
- return;
-
if (in.mipsGot)
in.mipsGot->build();
sortSections();
- // Now that we have the final list, create a list of all the
- // OutputSections for convenience.
- for (BaseCommand *base : script->sectionCommands)
- if (auto *sec = dyn_cast<OutputSection>(base))
- outputSections.push_back(sec);
+ // Create a list of OutputSections, assign sectionIndex, and populate
+ // in.shStrTab.
+ for (SectionCommand *cmd : script->sectionCommands)
+ if (auto *osd = dyn_cast<OutputDesc>(cmd)) {
+ OutputSection *osec = &osd->osec;
+ outputSections.push_back(osec);
+ osec->sectionIndex = outputSections.size();
+ osec->shName = in.shStrTab->addString(osec->name);
+ }
// Prefer command line supplied address over other constraints.
for (OutputSection *sec : outputSections) {
// With the outputSections available check for GDPLT relocations
// and add __tls_get_addr symbol if needed.
if (config->emachine == EM_HEXAGON && hexagonNeedsTLSSymbol(outputSections)) {
- Symbol *sym = symtab->addSymbol(Undefined{
+ Symbol *sym = symtab.addSymbol(Undefined{
nullptr, "__tls_get_addr", STB_GLOBAL, STV_DEFAULT, STT_NOTYPE});
sym->isPreemptible = true;
partitions[0].dynSymTab->addSymbol(sym);
// to 1 to make __ehdr_start defined. The section number is not
// particularly relevant.
Out::elfHeader->sectionIndex = 1;
-
- for (size_t i = 0, e = outputSections.size(); i != e; ++i) {
- OutputSection *sec = outputSections[i];
- sec->sectionIndex = i + 1;
- sec->shName = in.shStrTab->addString(sec->name);
- }
+ Out::elfHeader->size = sizeof(typename ELFT::Ehdr);
// Binary and relocatable output does not have PHDRS.
// The headers have to be created before finalize as that can influence the
{
llvm::TimeTraceScope timeScope("Finalize synthetic sections");
- finalizeSynthetic(in.bss);
- finalizeSynthetic(in.bssRelRo);
- finalizeSynthetic(in.symTabShndx);
- finalizeSynthetic(in.shStrTab);
- finalizeSynthetic(in.strTab);
- finalizeSynthetic(in.got);
- finalizeSynthetic(in.mipsGot);
- finalizeSynthetic(in.igotPlt);
- finalizeSynthetic(in.gotPlt);
- finalizeSynthetic(in.relaIplt);
- finalizeSynthetic(in.relaPlt);
- finalizeSynthetic(in.plt);
- finalizeSynthetic(in.iplt);
- finalizeSynthetic(in.ppc32Got2);
- finalizeSynthetic(in.partIndex);
+ finalizeSynthetic(in.bss.get());
+ finalizeSynthetic(in.bssRelRo.get());
+ finalizeSynthetic(in.symTabShndx.get());
+ finalizeSynthetic(in.shStrTab.get());
+ finalizeSynthetic(in.strTab.get());
+ finalizeSynthetic(in.got.get());
+ finalizeSynthetic(in.mipsGot.get());
+ finalizeSynthetic(in.igotPlt.get());
+ finalizeSynthetic(in.gotPlt.get());
+ finalizeSynthetic(in.relaIplt.get());
+ finalizeSynthetic(in.relaPlt.get());
+ finalizeSynthetic(in.plt.get());
+ finalizeSynthetic(in.iplt.get());
+ finalizeSynthetic(in.ppc32Got2.get());
+ finalizeSynthetic(in.partIndex.get());
// Dynamic section must be the last one in this list and dynamic
// symbol table section (dynSymTab) must be the first one.
for (Partition &part : partitions) {
- finalizeSynthetic(part.dynSymTab);
- finalizeSynthetic(part.gnuHashTab);
- finalizeSynthetic(part.hashTab);
- finalizeSynthetic(part.verDef);
- finalizeSynthetic(part.relaDyn);
- finalizeSynthetic(part.relrDyn);
- finalizeSynthetic(part.ehFrameHdr);
- finalizeSynthetic(part.verSym);
- finalizeSynthetic(part.verNeed);
- finalizeSynthetic(part.dynamic);
+ if (part.relaDyn) {
+ part.relaDyn->mergeRels();
+ // Compute DT_RELACOUNT to be used by part.dynamic.
+ part.relaDyn->partitionRels();
+ finalizeSynthetic(part.relaDyn.get());
+ }
+ if (part.relrDyn) {
+ part.relrDyn->mergeRels();
+ finalizeSynthetic(part.relrDyn.get());
+ }
+
+ finalizeSynthetic(part.dynSymTab.get());
+ finalizeSynthetic(part.gnuHashTab.get());
+ finalizeSynthetic(part.hashTab.get());
+ finalizeSynthetic(part.verDef.get());
+ finalizeSynthetic(part.ehFrameHdr.get());
+ finalizeSynthetic(part.verSym.get());
+ finalizeSynthetic(part.verNeed.get());
+ finalizeSynthetic(part.dynamic.get());
}
}
// sometimes using forward symbol declarations. We want to set the correct
// values. They also might change after adding the thunks.
finalizeAddressDependentContent();
+
+ // All information needed for OutputSection part of Map file is available.
if (errorCount())
return;
llvm::TimeTraceScope timeScope("Finalize synthetic sections");
// finalizeAddressDependentContent may have added local symbols to the
// static symbol table.
- finalizeSynthetic(in.symTab);
- finalizeSynthetic(in.ppc64LongBranchTarget);
+ finalizeSynthetic(in.symTab.get());
+ finalizeSynthetic(in.ppc64LongBranchTarget.get());
}
// Relaxation to delete inter-basic block jumps created by basic block
}
// Ensure data sections are not mixed with executable sections when
-// -execute-only is used. -execute-only is a feature to make pages executable
-// but not readable, and the feature is currently supported only on AArch64.
+// --execute-only is used. --execute-only make pages executable but not
+// readable.
template <class ELFT> void Writer<ELFT>::checkExecuteOnly() {
if (!config->executeOnly)
return;
- for (OutputSection *os : outputSections)
- if (os->flags & SHF_EXECINSTR)
- for (InputSection *isec : getInputSections(os))
+ SmallVector<InputSection *, 0> storage;
+ for (OutputSection *osec : outputSections)
+ if (osec->flags & SHF_EXECINSTR)
+ for (InputSection *isec : getInputSections(*osec, storage))
if (!(isec->flags & SHF_EXECINSTR))
- error("cannot place " + toString(isec) + " into " + toString(os->name) +
- ": -execute-only does not support intermingling data and code");
+ error("cannot place " + toString(isec) + " into " +
+ toString(osec->name) +
+ ": --execute-only does not support intermingling data and code");
}
// The linker is expected to define SECNAME_start and SECNAME_end
Default = Out::elfHeader;
auto define = [=](StringRef start, StringRef end, OutputSection *os) {
- if (os) {
+ if (os && !script->isDiscarded(os)) {
addOptionalRegular(start, os, 0);
addOptionalRegular(end, os, -1);
} else {
// respectively. This is not requested by the ELF standard, but GNU ld and
// gold provide the feature, and used by many programs.
template <class ELFT>
-void Writer<ELFT>::addStartStopSymbols(OutputSection *sec) {
- StringRef s = sec->name;
+void Writer<ELFT>::addStartStopSymbols(OutputSection &osec) {
+ StringRef s = osec.name;
if (!isValidCIdentifier(s))
return;
- addOptionalRegular(saver.save("__start_" + s), sec, 0,
+ addOptionalRegular(saver().save("__start_" + s), &osec, 0,
config->zStartStopVisibility);
- addOptionalRegular(saver.save("__stop_" + s), sec, -1,
+ addOptionalRegular(saver().save("__stop_" + s), &osec, -1,
config->zStartStopVisibility);
}
// Decide which program headers to create and which sections to include in each
// one.
template <class ELFT>
-std::vector<PhdrEntry *> Writer<ELFT>::createPhdrs(Partition &part) {
- std::vector<PhdrEntry *> ret;
+SmallVector<PhdrEntry *, 0> Writer<ELFT>::createPhdrs(Partition &part) {
+ SmallVector<PhdrEntry *, 0> ret;
auto addHdr = [&](unsigned type, unsigned flags) -> PhdrEntry * {
ret.push_back(make<PhdrEntry>(type, flags));
return ret.back();
if (sec->partition != partNo)
continue;
if (sec->type == SHT_NOTE && (sec->flags & SHF_ALLOC)) {
- if (!note || sec->lmaExpr || note->lastSec->alignment != sec->alignment)
+ if (!note || sec->lmaExpr || note->lastSec->addralign != sec->addralign)
note = addHdr(PT_NOTE, PF_R);
note->add(sec);
} else {
OutputSection *cmd = p->firstSec;
if (!cmd)
return;
- cmd->alignExpr = [align = cmd->alignment]() { return align; };
+ cmd->alignExpr = [align = cmd->addralign]() { return align; };
if (!cmd->addrExpr) {
// Prefer advancing to align(dot, maxPageSize) + dot%maxPageSize to avoid
// padding in the file contents.
(prev->p_flags & PF_X) != (p->p_flags & PF_X)) ||
cmd->type == SHT_LLVM_PART_EHDR)
cmd->addrExpr = [] {
- return alignTo(script->getDot(), config->maxPageSize);
+ return alignToPowerOf2(script->getDot(), config->maxPageSize);
};
// PT_TLS is at the start of the first RW PT_LOAD. If `p` includes PT_TLS,
// it must be the RW. Align to p_align(PT_TLS) to make sure
// blocks correctly. We need to keep the workaround for a while.
else if (Out::tlsPhdr && Out::tlsPhdr->firstSec == p->firstSec)
cmd->addrExpr = [] {
- return alignTo(script->getDot(), config->maxPageSize) +
- alignTo(script->getDot() % config->maxPageSize,
- Out::tlsPhdr->p_align);
+ return alignToPowerOf2(script->getDot(), config->maxPageSize) +
+ alignToPowerOf2(script->getDot() % config->maxPageSize,
+ Out::tlsPhdr->p_align);
};
else
cmd->addrExpr = [] {
- return alignTo(script->getDot(), config->maxPageSize) +
+ return alignToPowerOf2(script->getDot(), config->maxPageSize) +
script->getDot() % config->maxPageSize;
};
}
return alignTo(off, os->ptLoad->p_align, os->addr);
// File offsets are not significant for .bss sections other than the first one
- // in a PT_LOAD. By convention, we keep section offsets monotonically
+ // in a PT_LOAD/PT_TLS. By convention, we keep section offsets monotonically
// increasing rather than setting to zero.
- if (os->type == SHT_NOBITS)
+ if (os->type == SHT_NOBITS &&
+ (!Out::tlsPhdr || Out::tlsPhdr->firstSec != os))
return off;
// If the section is not in a PT_LOAD, we just have to align it.
if (!os->ptLoad)
- return alignTo(off, os->alignment);
+ return alignToPowerOf2(off, os->addralign);
// If two sections share the same PT_LOAD the file offset is calculated
// using this formula: Off2 = Off1 + (VA2 - VA1).
return first->offset + os->addr - first->addr;
}
-// Set an in-file position to a given section and returns the end position of
-// the section.
-static uint64_t setFileOffset(OutputSection *os, uint64_t off) {
- off = computeFileOffset(os, off);
- os->offset = off;
-
- if (os->type == SHT_NOBITS)
- return off;
- return off + os->size;
-}
-
template <class ELFT> void Writer<ELFT>::assignFileOffsetsBinary() {
// Compute the minimum LMA of all non-empty non-NOBITS sections as minAddr.
auto needsOffset = [](OutputSection &sec) {
// Assign file offsets to output sections.
template <class ELFT> void Writer<ELFT>::assignFileOffsets() {
- uint64_t off = 0;
- off = setFileOffset(Out::elfHeader, off);
- off = setFileOffset(Out::programHeaders, off);
+ Out::programHeaders->offset = Out::elfHeader->size;
+ uint64_t off = Out::elfHeader->size + Out::programHeaders->size;
PhdrEntry *lastRX = nullptr;
for (Partition &part : partitions)
for (OutputSection *sec : outputSections) {
if (!(sec->flags & SHF_ALLOC))
continue;
- off = setFileOffset(sec, off);
+ off = computeFileOffset(sec, off);
+ sec->offset = off;
+ if (sec->type != SHT_NOBITS)
+ off += sec->size;
// If this is a last section of the last executable segment and that
// segment is the last loadable segment, align the offset of the
// following section to avoid loading non-segments parts of the file.
if (config->zSeparate != SeparateSegmentKind::None && lastRX &&
lastRX->lastSec == sec)
- off = alignTo(off, config->commonPageSize);
+ off = alignToPowerOf2(off, config->maxPageSize);
}
- for (OutputSection *sec : outputSections)
- if (!(sec->flags & SHF_ALLOC))
- off = setFileOffset(sec, off);
+ for (OutputSection *osec : outputSections)
+ if (!(osec->flags & SHF_ALLOC)) {
+ osec->offset = alignToPowerOf2(off, osec->addralign);
+ off = osec->offset + osec->size;
+ }
- sectionHeaderOff = alignTo(off, config->wordsize);
+ sectionHeaderOff = alignToPowerOf2(off, config->wordsize);
fileSize = sectionHeaderOff + (outputSections.size() + 1) * sizeof(Elf_Shdr);
// Our logic assumes that sections have rising VA within the same segment.
// musl/glibc ld.so rounds the size down, so we need to round up
// to protect the last page. This is a no-op on FreeBSD which always
// rounds up.
- p->p_memsz = alignTo(p->p_offset + p->p_memsz, config->commonPageSize) -
- p->p_offset;
+ p->p_memsz =
+ alignToPowerOf2(p->p_offset + p->p_memsz, config->commonPageSize) -
+ p->p_offset;
}
}
}
// First, check that section's VAs fit in available address space for target.
for (OutputSection *os : outputSections)
if ((os->addr + os->size < os->addr) ||
- (!ELFT::Is64Bits && os->addr + os->size > UINT32_MAX))
+ (!ELFT::Is64Bits && os->addr + os->size > uint64_t(UINT32_MAX) + 1))
errorOrWarn("section " + os->name + " at 0x" + utohexstr(os->addr) +
" of size 0x" + utohexstr(os->size) +
" exceeds available address space");
// 2. the ENTRY(symbol) command in a linker control script;
// 3. the value of the symbol _start, if present;
// 4. the number represented by the entry symbol, if it is a number;
-// 5. the address of the first byte of the .text section, if present;
-// 6. the address 0.
+// 5. the address 0.
static uint64_t getEntryAddr() {
// Case 1, 2 or 3
- if (Symbol *b = symtab->find(config->entry))
+ if (Symbol *b = symtab.find(config->entry))
return b->getVA();
// Case 4
return addr;
// Case 5
- if (OutputSection *sec = findSection(".text")) {
- if (config->warnMissingEntry)
- warn("cannot find entry symbol " + config->entry + "; defaulting to 0x" +
- utohexstr(sec->addr));
- return sec->addr;
- }
-
- // Case 6
if (config->warnMissingEntry)
warn("cannot find entry symbol " + config->entry +
"; not setting start address");
}
template <class ELFT> void Writer<ELFT>::writeSectionsBinary() {
+ parallel::TaskGroup tg;
for (OutputSection *sec : outputSections)
if (sec->flags & SHF_ALLOC)
- sec->writeTo<ELFT>(Out::bufferStart + sec->offset);
+ sec->writeTo<ELFT>(Out::bufferStart + sec->offset, tg);
}
static void fillTrap(uint8_t *i, uint8_t *end) {
// Fill the last page.
for (PhdrEntry *p : part.phdrs)
if (p->p_type == PT_LOAD && (p->p_flags & PF_X))
- fillTrap(Out::bufferStart + alignDown(p->firstSec->offset + p->p_filesz,
- config->commonPageSize),
- Out::bufferStart + alignTo(p->firstSec->offset + p->p_filesz,
- config->commonPageSize));
+ fillTrap(Out::bufferStart +
+ alignDown(p->firstSec->offset + p->p_filesz, 4),
+ Out::bufferStart +
+ alignToPowerOf2(p->firstSec->offset + p->p_filesz,
+ config->maxPageSize));
// Round up the file size of the last segment to the page boundary iff it is
// an executable segment to ensure that other tools don't accidentally
if (last && (last->p_flags & PF_X))
last->p_memsz = last->p_filesz =
- alignTo(last->p_filesz, config->commonPageSize);
+ alignToPowerOf2(last->p_filesz, config->maxPageSize);
}
}
// Write section contents to a mmap'ed file.
template <class ELFT> void Writer<ELFT>::writeSections() {
- // In -r or -emit-relocs mode, write the relocation sections first as in
- // ELf_Rel targets we might find out that we need to modify the relocated
- // section while doing it.
- for (OutputSection *sec : outputSections)
- if (sec->type == SHT_REL || sec->type == SHT_RELA)
- sec->writeTo<ELFT>(Out::bufferStart + sec->offset);
+ llvm::TimeTraceScope timeScope("Write sections");
- for (OutputSection *sec : outputSections)
- if (sec->type != SHT_REL && sec->type != SHT_RELA)
- sec->writeTo<ELFT>(Out::bufferStart + sec->offset);
+ {
+ // In -r or --emit-relocs mode, write the relocation sections first as in
+ // ELf_Rel targets we might find out that we need to modify the relocated
+ // section while doing it.
+ parallel::TaskGroup tg;
+ for (OutputSection *sec : outputSections)
+ if (sec->type == SHT_REL || sec->type == SHT_RELA)
+ sec->writeTo<ELFT>(Out::bufferStart + sec->offset, tg);
+ }
+ {
+ parallel::TaskGroup tg;
+ for (OutputSection *sec : outputSections)
+ if (sec->type != SHT_REL && sec->type != SHT_RELA)
+ sec->writeTo<ELFT>(Out::bufferStart + sec->offset, tg);
+ }
// Finally, check that all dynamic relocation addends were written correctly.
if (config->checkDynamicRelocs && config->writeAddends) {
llvm::ArrayRef<uint8_t> data,
std::function<void(uint8_t *dest, ArrayRef<uint8_t> arr)> hashFn) {
std::vector<ArrayRef<uint8_t>> chunks = split(data, 1024 * 1024);
- std::vector<uint8_t> hashes(chunks.size() * hashBuf.size());
+ const size_t hashesSize = chunks.size() * hashBuf.size();
+ std::unique_ptr<uint8_t[]> hashes(new uint8_t[hashesSize]);
// Compute hash values.
- parallelForEachN(0, chunks.size(), [&](size_t i) {
- hashFn(hashes.data() + i * hashBuf.size(), chunks[i]);
+ parallelFor(0, chunks.size(), [&](size_t i) {
+ hashFn(hashes.get() + i * hashBuf.size(), chunks[i]);
});
// Write to the final output buffer.
- hashFn(hashBuf.data(), hashes);
+ hashFn(hashBuf.data(), ArrayRef(hashes.get(), hashesSize));
}
template <class ELFT> void Writer<ELFT>::writeBuildId() {
// Compute a hash of all sections of the output file.
size_t hashSize = mainPart->buildId->hashSize;
- std::vector<uint8_t> buildId(hashSize);
- llvm::ArrayRef<uint8_t> buf{Out::bufferStart, size_t(fileSize)};
-
+ std::unique_ptr<uint8_t[]> buildId(new uint8_t[hashSize]);
+ MutableArrayRef<uint8_t> output(buildId.get(), hashSize);
+ llvm::ArrayRef<uint8_t> input{Out::bufferStart, size_t(fileSize)};
+
+ // Fedora introduced build ID as "approximation of true uniqueness across all
+ // binaries that might be used by overlapping sets of people". It does not
+ // need some security goals that some hash algorithms strive to provide, e.g.
+ // (second-)preimage and collision resistance. In practice people use 'md5'
+ // and 'sha1' just for different lengths. Implement them with the more
+ // efficient BLAKE3.
switch (config->buildId) {
case BuildIdKind::Fast:
- computeHash(buildId, buf, [](uint8_t *dest, ArrayRef<uint8_t> arr) {
+ computeHash(output, input, [](uint8_t *dest, ArrayRef<uint8_t> arr) {
write64le(dest, xxHash64(arr));
});
break;
case BuildIdKind::Md5:
- computeHash(buildId, buf, [&](uint8_t *dest, ArrayRef<uint8_t> arr) {
- memcpy(dest, MD5::hash(arr).data(), hashSize);
+ computeHash(output, input, [&](uint8_t *dest, ArrayRef<uint8_t> arr) {
+ memcpy(dest, BLAKE3::hash<16>(arr).data(), hashSize);
});
break;
case BuildIdKind::Sha1:
- computeHash(buildId, buf, [&](uint8_t *dest, ArrayRef<uint8_t> arr) {
- memcpy(dest, SHA1::hash(arr).data(), hashSize);
+ computeHash(output, input, [&](uint8_t *dest, ArrayRef<uint8_t> arr) {
+ memcpy(dest, BLAKE3::hash<20>(arr).data(), hashSize);
});
break;
case BuildIdKind::Uuid:
- if (auto ec = llvm::getRandomBytes(buildId.data(), hashSize))
+ if (auto ec = llvm::getRandomBytes(buildId.get(), hashSize))
error("entropy source failure: " + ec.message());
break;
default:
llvm_unreachable("unknown BuildIdKind");
}
for (Partition &part : partitions)
- part.buildId->writeBuildId(buildId);
+ part.buildId->writeBuildId(output);
}
template void elf::createSyntheticSections<ELF32LE>();
#define LLD_ELF_WRITER_H
#include "Config.h"
-#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include <cstdint>
-#include <memory>
-namespace lld {
-namespace elf {
+namespace lld::elf {
class InputFile;
class OutputSection;
-class InputSectionBase;
void copySectionsIntoPartitions();
template <class ELFT> void createSyntheticSections();
-void combineEhSections();
template <class ELFT> void writeResult();
// This describes a program header entry.
};
void addReservedSymbols();
-llvm::StringRef getOutputSectionName(const InputSectionBase *s);
template <class ELFT> uint32_t calcMipsEFlags();
bool isMipsN32Abi(const InputFile *f);
bool isMicroMips();
bool isMipsR6();
-} // namespace elf
-} // namespace lld
+} // namespace lld::elf
#endif
+++ /dev/null
-ATOM-based lld
-==============
-
-Note: this document discuss Mach-O port of LLD. For ELF and COFF,
-see :doc:`index`.
-
-ATOM-based lld is a new set of modular code for creating linker tools.
-Currently it supports Mach-O.
-
-* End-User Features:
-
- * Compatible with existing linker options
- * Reads standard Object Files
- * Writes standard Executable Files
- * Remove clang's reliance on "the system linker"
- * Uses the LLVM `"UIUC" BSD-Style license`__.
-
-* Applications:
-
- * Modular design
- * Support cross linking
- * Easy to add new CPU support
- * Can be built as static tool or library
-
-* Design and Implementation:
-
- * Extensive unit tests
- * Internal linker model can be dumped/read to textual format
- * Additional linking features can be plugged in as "passes"
- * OS specific and CPU specific code factored out
-
-Why a new linker?
------------------
-
-The fact that clang relies on whatever linker tool you happen to have installed
-means that clang has been very conservative adopting features which require a
-recent linker.
-
-In the same way that the MC layer of LLVM has removed clang's reliance on the
-system assembler tool, the lld project will remove clang's reliance on the
-system linker tool.
-
-
-Contents
---------
-
-.. toctree::
- :maxdepth: 2
-
- design
- getting_started
- development
- open_projects
- sphinx_intro
-
-Indices and tables
-------------------
-
-* :ref:`genindex`
-* :ref:`search`
-
-__ https://llvm.org/docs/DeveloperPolicy.html#license
+++ /dev/null
-======
-Driver
-======
-
-Note: this document discuss Mach-O port of LLD. For ELF and COFF,
-see :doc:`index`.
-
-.. contents::
- :local:
-
-Introduction
-============
-
-This document describes the lld driver. The purpose of this document is to
-describe both the motivation and design goals for the driver, as well as details
-of the internal implementation.
-
-Overview
-========
-
-The lld driver is designed to support a number of different command line
-interfaces. The main interfaces we plan to support are binutils' ld, Apple's
-ld, and Microsoft's link.exe.
-
-Flavors
--------
-
-Each of these different interfaces is referred to as a flavor. There is also an
-extra flavor "core" which is used to exercise the core functionality of the
-linker it the test suite.
-
-* gnu
-* darwin
-* link
-* core
-
-Selecting a Flavor
-^^^^^^^^^^^^^^^^^^
-
-There are two different ways to tell lld which flavor to be. They are checked in
-order, so the second overrides the first. The first is to symlink :program:`lld`
-as :program:`lld-{flavor}` or just :program:`{flavor}`. You can also specify
-it as the first command line argument using ``-flavor``::
-
- $ lld -flavor gnu
-
-There is a shortcut for ``-flavor core`` as ``-core``.
-
-
-Adding an Option to an existing Flavor
-======================================
-
-#. Add the option to the desired :file:`lib/Driver/{flavor}Options.td`.
-
-#. Add to :cpp:class:`lld::FlavorLinkingContext` a getter and setter method
- for the option.
-
-#. Modify :cpp:func:`lld::FlavorDriver::parse` in :file:
- `lib/Driver/{Flavor}Driver.cpp` to call the targetInfo setter
- for the option.
-
-#. Modify {Flavor}Reader and {Flavor}Writer to use the new targetInfo option.
-
-
-Adding a Flavor
-===============
-
-#. Add an entry for the flavor in :file:`include/lld/Common/Driver.h` to
- :cpp:class:`lld::UniversalDriver::Flavor`.
-
-#. Add an entry in :file:`lib/Driver/UniversalDriver.cpp` to
- :cpp:func:`lld::Driver::strToFlavor` and
- :cpp:func:`lld::UniversalDriver::link`.
- This allows the flavor to be selected via symlink and `-flavor`.
-
-#. Add a tablegen file called :file:`lib/Driver/{flavor}Options.td` that
- describes the options. If the options are a superset of another driver, that
- driver's td file can simply be included. The :file:`{flavor}Options.td` file
- must also be added to :file:`lib/Driver/CMakeLists.txt`.
-
-#. Add a ``{flavor}Driver`` as a subclass of :cpp:class:`lld::Driver`
- in :file:`lib/Driver/{flavor}Driver.cpp`.
+++ /dev/null
-.. _Readers:
-
-Developing lld Readers
-======================
-
-Note: this document discuss Mach-O port of LLD. For ELF and COFF,
-see :doc:`index`.
-
-Introduction
-------------
-
-The purpose of a "Reader" is to take an object file in a particular format
-and create an `lld::File`:cpp:class: (which is a graph of Atoms)
-representing the object file. A Reader inherits from
-`lld::Reader`:cpp:class: which lives in
-:file:`include/lld/Core/Reader.h` and
-:file:`lib/Core/Reader.cpp`.
-
-The Reader infrastructure for an object format ``Foo`` requires the
-following pieces in order to fit into lld:
-
-:file:`include/lld/ReaderWriter/ReaderFoo.h`
-
- .. cpp:class:: ReaderOptionsFoo : public ReaderOptions
-
- This Options class is the only way to configure how the Reader will
- parse any file into an `lld::Reader`:cpp:class: object. This class
- should be declared in the `lld`:cpp:class: namespace.
-
- .. cpp:function:: Reader *createReaderFoo(ReaderOptionsFoo &reader)
-
- This factory function configures and create the Reader. This function
- should be declared in the `lld`:cpp:class: namespace.
-
-:file:`lib/ReaderWriter/Foo/ReaderFoo.cpp`
-
- .. cpp:class:: ReaderFoo : public Reader
-
- This is the concrete Reader class which can be called to parse
- object files. It should be declared in an anonymous namespace or
- if there is shared code with the `lld::WriterFoo`:cpp:class: you
- can make a nested namespace (e.g. `lld::foo`:cpp:class:).
-
-You may have noticed that :cpp:class:`ReaderFoo` is not declared in the
-``.h`` file. An important design aspect of lld is that all Readers are
-created *only* through an object-format-specific
-:cpp:func:`createReaderFoo` factory function. The creation of the Reader is
-parametrized through a :cpp:class:`ReaderOptionsFoo` class. This options
-class is the one-and-only way to control how the Reader operates when
-parsing an input file into an Atom graph. For instance, you may want the
-Reader to only accept certain architectures. The options class can be
-instantiated from command line options or be programmatically configured.
-
-Where to start
---------------
-
-The lld project already has a skeleton of source code for Readers for
-``ELF``, ``PECOFF``, ``MachO``, and lld's native ``YAML`` graph format.
-If your file format is a variant of one of those, you should modify the
-existing Reader to support your variant. This is done by customizing the Options
-class for the Reader and making appropriate changes to the ``.cpp`` file to
-interpret those options and act accordingly.
-
-If your object file format is not a variant of any existing Reader, you'll need
-to create a new Reader subclass with the organization described above.
-
-Readers are factories
----------------------
-
-The linker will usually only instantiate your Reader once. That one Reader will
-have its loadFile() method called many times with different input files.
-To support multithreaded linking, the Reader may be parsing multiple input
-files in parallel. Therefore, there should be no parsing state in you Reader
-object. Any parsing state should be in ivars of your File subclass or in
-some temporary object.
-
-The key function to implement in a reader is::
-
- virtual error_code loadFile(LinkerInput &input,
- std::vector<std::unique_ptr<File>> &result);
-
-It takes a memory buffer (which contains the contents of the object file
-being read) and returns an instantiated lld::File object which is
-a collection of Atoms. The result is a vector of File pointers (instead of
-simple a File pointer) because some file formats allow multiple object
-"files" to be encoded in one file system file.
-
-
-Memory Ownership
-----------------
-
-Atoms are always owned by their File object. During core linking when Atoms
-are coalesced or stripped away, core linking does not delete them.
-Core linking just removes those unused Atoms from its internal list.
-The destructor of a File object is responsible for deleting all Atoms it
-owns, and if ownership of the MemoryBuffer was passed to it, the File
-destructor needs to delete that too.
-
-Making Atoms
-------------
-
-The internal model of lld is purely Atom based. But most object files do not
-have an explicit concept of Atoms, instead most have "sections". The way
-to think of this is that a section is just a list of Atoms with common
-attributes.
-
-The first step in parsing section-based object files is to cleave each
-section into a list of Atoms. The technique may vary by section type. For
-code sections (e.g. .text), there are usually symbols at the start of each
-function. Those symbol addresses are the points at which the section is
-cleaved into discrete Atoms. Some file formats (like ELF) also include the
-length of each symbol in the symbol table. Otherwise, the length of each
-Atom is calculated to run to the start of the next symbol or the end of the
-section.
-
-Other sections types can be implicitly cleaved. For instance c-string literals
-or unwind info (e.g. .eh_frame) can be cleaved by having the Reader look at
-the content of the section. It is important to cleave sections into Atoms
-to remove false dependencies. For instance the .eh_frame section often
-has no symbols, but contains "pointers" to the functions for which it
-has unwind info. If the .eh_frame section was not cleaved (but left as one
-big Atom), there would always be a reference (from the eh_frame Atom) to
-each function. So the linker would be unable to coalesce or dead stripped
-away the function atoms.
-
-The lld Atom model also requires that a reference to an undefined symbol be
-modeled as a Reference to an UndefinedAtom. So the Reader also needs to
-create an UndefinedAtom for each undefined symbol in the object file.
-
-Once all Atoms have been created, the second step is to create References
-(recall that Atoms are "nodes" and References are "edges"). Most References
-are created by looking at the "relocation records" in the object file. If
-a function contains a call to "malloc", there is usually a relocation record
-specifying the address in the section and the symbol table index. Your
-Reader will need to convert the address to an Atom and offset and the symbol
-table index into a target Atom. If "malloc" is not defined in the object file,
-the target Atom of the Reference will be an UndefinedAtom.
-
-
-Performance
------------
-Once you have the above working to parse an object file into Atoms and
-References, you'll want to look at performance. Some techniques that can
-help performance are:
-
-* Use llvm::BumpPtrAllocator or pre-allocate one big vector<Reference> and then
- just have each atom point to its subrange of References in that vector.
- This can be faster that allocating each Reference as separate object.
-* Pre-scan the symbol table and determine how many atoms are in each section
- then allocate space for all the Atom objects at once.
-* Don't copy symbol names or section content to each Atom, instead use
- StringRef and ArrayRef in each Atom to point to its name and content in the
- MemoryBuffer.
-
-
-Testing
--------
-
-We are still working on infrastructure to test Readers. The issue is that
-you don't want to check in binary files to the test suite. And the tools
-for creating your object file from assembly source may not be available on
-every OS.
-
-We are investigating a way to use YAML to describe the section, symbols,
-and content of a file. Then have some code which will write out an object
-file from that YAML description.
-
-Once that is in place, you can write test cases that contain section/symbols
-YAML and is run through the linker to produce Atom/References based YAML which
-is then run through FileCheck to verify the Atoms and References are as
-expected.
-
-
-
+++ /dev/null
-.. _design:
-
-Linker Design
-=============
-
-Note: this document discuss Mach-O port of LLD. For ELF and COFF,
-see :doc:`index`.
-
-Introduction
-------------
-
-lld is a new generation of linker. It is not "section" based like traditional
-linkers which mostly just interlace sections from multiple object files into the
-output file. Instead, lld is based on "Atoms". Traditional section based
-linking work well for simple linking, but their model makes advanced linking
-features difficult to implement. Features like dead code stripping, reordering
-functions for locality, and C++ coalescing require the linker to work at a finer
-grain.
-
-An atom is an indivisible chunk of code or data. An atom has a set of
-attributes, such as: name, scope, content-type, alignment, etc. An atom also
-has a list of References. A Reference contains: a kind, an optional offset, an
-optional addend, and an optional target atom.
-
-The Atom model allows the linker to use standard graph theory models for linking
-data structures. Each atom is a node, and each Reference is an edge. The
-feature of dead code stripping is implemented by following edges to mark all
-live atoms, and then delete the non-live atoms.
-
-
-Atom Model
-----------
-
-An atom is an indivisible chunk of code or data. Typically each user written
-function or global variable is an atom. In addition, the compiler may emit
-other atoms, such as for literal c-strings or floating point constants, or for
-runtime data structures like dwarf unwind info or pointers to initializers.
-
-A simple "hello world" object file would be modeled like this:
-
-.. image:: hello.png
-
-There are three atoms: main, a proxy for printf, and an anonymous atom
-containing the c-string literal "hello world". The Atom "main" has two
-references. One is the call site for the call to printf, and the other is a
-reference for the instruction that loads the address of the c-string literal.
-
-There are only four different types of atoms:
-
- * DefinedAtom
- 95% of all atoms. This is a chunk of code or data
-
- * UndefinedAtom
- This is a place holder in object files for a reference to some atom
- outside the translation unit.During core linking it is usually replaced
- by (coalesced into) another Atom.
-
- * SharedLibraryAtom
- If a required symbol name turns out to be defined in a dynamic shared
- library (and not some object file). A SharedLibraryAtom is the
- placeholder Atom used to represent that fact.
-
- It is similar to an UndefinedAtom, but it also tracks information
- about the associated shared library.
-
- * AbsoluteAtom
- This is for embedded support where some stuff is implemented in ROM at
- some fixed address. This atom has no content. It is just an address
- that the Writer needs to fix up any references to point to.
-
-
-File Model
-----------
-
-The linker views the input files as basically containers of Atoms and
-References, and just a few attributes of their own. The linker works with three
-kinds of files: object files, static libraries, and dynamic shared libraries.
-Each kind of file has reader object which presents the file in the model
-expected by the linker.
-
-Object File
-~~~~~~~~~~~
-
-An object file is just a container of atoms. When linking an object file, a
-reader is instantiated which parses the object file and instantiates a set of
-atoms representing all content in the .o file. The linker adds all those atoms
-to a master graph.
-
-Static Library (Archive)
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-This is the traditional unix static archive which is just a collection of object
-files with a "table of contents". When linking with a static library, by default
-nothing is added to the master graph of atoms. Instead, if after merging all
-atoms from object files into a master graph, if any "undefined" atoms are left
-remaining in the master graph, the linker reads the table of contents for each
-static library to see if any have the needed definitions. If so, the set of
-atoms from the specified object file in the static library is added to the
-master graph of atoms.
-
-Dynamic Library (Shared Object)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Dynamic libraries are different than object files and static libraries in that
-they don't directly add any content. Their purpose is to check at build time
-that the remaining undefined references can be resolved at runtime, and provide
-a list of dynamic libraries (SO_NEEDED) that will be needed at runtime. The way
-this is modeled in the linker is that a dynamic library contributes no atoms to
-the initial graph of atoms. Instead, (like static libraries) if there are
-"undefined" atoms in the master graph of all atoms, then each dynamic library is
-checked to see if exports the required symbol. If so, a "shared library" atom is
-instantiated by the by the reader which the linker uses to replace the
-"undefined" atom.
-
-Linking Steps
--------------
-
-Through the use of abstract Atoms, the core of linking is architecture
-independent and file format independent. All command line parsing is factored
-out into a separate "options" abstraction which enables the linker to be driven
-with different command line sets.
-
-The overall steps in linking are:
-
- #. Command line processing
-
- #. Parsing input files
-
- #. Resolving
-
- #. Passes/Optimizations
-
- #. Generate output file
-
-The Resolving and Passes steps are done purely on the master graph of atoms, so
-they have no notion of file formats such as mach-o or ELF.
-
-
-Input Files
-~~~~~~~~~~~
-
-Existing developer tools using different file formats for object files.
-A goal of lld is to be file format independent. This is done
-through a plug-in model for reading object files. The lld::Reader is the base
-class for all object file readers. A Reader follows the factory method pattern.
-A Reader instantiates an lld::File object (which is a graph of Atoms) from a
-given object file (on disk or in-memory).
-
-Every Reader subclass defines its own "options" class (for instance the mach-o
-Reader defines the class ReaderOptionsMachO). This options class is the
-one-and-only way to control how the Reader operates when parsing an input file
-into an Atom graph. For instance, you may want the Reader to only accept
-certain architectures. The options class can be instantiated from command
-line options, or it can be subclassed and the ivars programmatically set.
-
-Resolving
-~~~~~~~~~
-
-The resolving step takes all the atoms' graphs from each object file and
-combines them into one master object graph. Unfortunately, it is not as simple
-as appending the atom list from each file into one big list. There are many
-cases where atoms need to be coalesced. That is, two or more atoms need to be
-coalesced into one atom. This is necessary to support: C language "tentative
-definitions", C++ weak symbols for templates and inlines defined in headers,
-replacing undefined atoms with actual definition atoms, and for merging copies
-of constants like c-strings and floating point constants.
-
-The linker support coalescing by-name and by-content. By-name is used for
-tentative definitions and weak symbols. By-content is used for constant data
-that can be merged.
-
-The resolving process maintains some global linking "state", including a "symbol
-table" which is a map from llvm::StringRef to lld::Atom*. With these data
-structures, the linker iterates all atoms in all input files. For each atom, it
-checks if the atom is named and has a global or hidden scope. If so, the atom
-is added to the symbol table map. If there already is a matching atom in that
-table, that means the current atom needs to be coalesced with the found atom, or
-it is a multiple definition error.
-
-When all initial input file atoms have been processed by the resolver, a scan is
-made to see if there are any undefined atoms in the graph. If there are, the
-linker scans all libraries (both static and dynamic) looking for definitions to
-replace the undefined atoms. It is an error if any undefined atoms are left
-remaining.
-
-Dead code stripping (if requested) is done at the end of resolving. The linker
-does a simple mark-and-sweep. It starts with "root" atoms (like "main" in a main
-executable) and follows each references and marks each Atom that it visits as
-"live". When done, all atoms not marked "live" are removed.
-
-The result of the Resolving phase is the creation of an lld::File object. The
-goal is that the lld::File model is **the** internal representation
-throughout the linker. The file readers parse (mach-o, ELF, COFF) into an
-lld::File. The file writers (mach-o, ELF, COFF) taken an lld::File and produce
-their file kind, and every Pass only operates on an lld::File. This is not only
-a simpler, consistent model, but it enables the state of the linker to be dumped
-at any point in the link for testing purposes.
-
-
-Passes
-~~~~~~
-
-The Passes step is an open ended set of routines that each get a change to
-modify or enhance the current lld::File object. Some example Passes are:
-
- * stub (PLT) generation
-
- * GOT instantiation
-
- * order_file optimization
-
- * branch island generation
-
- * branch shim generation
-
- * Objective-C optimizations (Darwin specific)
-
- * TLV instantiation (Darwin specific)
-
- * DTrace probe processing (Darwin specific)
-
- * compact unwind encoding (Darwin specific)
-
-
-Some of these passes are specific to Darwin's runtime environments. But many of
-the passes are applicable to any OS (such as generating branch island for out of
-range branch instructions).
-
-The general structure of a pass is to iterate through the atoms in the current
-lld::File object, inspecting each atom and doing something. For instance, the
-stub pass, looks for call sites to shared library atoms (e.g. call to printf).
-It then instantiates a "stub" atom (PLT entry) and a "lazy pointer" atom for
-each proxy atom needed, and these new atoms are added to the current lld::File
-object. Next, all the noted call sites to shared library atoms have their
-References altered to point to the stub atom instead of the shared library atom.
-
-
-Generate Output File
-~~~~~~~~~~~~~~~~~~~~
-
-Once the passes are done, the output file writer is given current lld::File
-object. The writer's job is to create the executable content file wrapper and
-place the content of the atoms into it.
-
-lld uses a plug-in model for writing output files. All concrete writers (e.g.
-ELF, mach-o, etc) are subclasses of the lld::Writer class.
-
-Unlike the Reader class which has just one method to instantiate an lld::File,
-the Writer class has multiple methods. The crucial method is to generate the
-output file, but there are also methods which allow the Writer to contribute
-Atoms to the resolver and specify passes to run.
-
-An example of contributing
-atoms is that if the Writer knows a main executable is being linked and such
-an executable requires a specially named entry point (e.g. "_main"), the Writer
-can add an UndefinedAtom with that special name to the resolver. This will
-cause the resolver to issue an error if that symbol is not defined.
-
-Sometimes a Writer supports lazily created symbols, such as names for the start
-of sections. To support this, the Writer can create a File object which vends
-no initial atoms, but does lazily supply atoms by name as needed.
-
-Every Writer subclass defines its own "options" class (for instance the mach-o
-Writer defines the class WriterOptionsMachO). This options class is the
-one-and-only way to control how the Writer operates when producing an output
-file from an Atom graph. For instance, you may want the Writer to optimize
-the output for certain OS versions, or strip local symbols, etc. The options
-class can be instantiated from command line options, or it can be subclassed
-and the ivars programmatically set.
-
-
-lld::File representations
--------------------------
-
-Just as LLVM has three representations of its IR model, lld has two
-representations of its File/Atom/Reference model:
-
- * In memory, abstract C++ classes (lld::Atom, lld::Reference, and lld::File).
-
- * textual (in YAML)
-
-
-Textual representations in YAML
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-In designing a textual format we want something easy for humans to read and easy
-for the linker to parse. Since an atom has lots of attributes most of which are
-usually just the default, we should define default values for every attribute so
-that those can be omitted from the text representation. Here is the atoms for a
-simple hello world program expressed in YAML::
-
- target-triple: x86_64-apple-darwin11
-
- atoms:
- - name: _main
- scope: global
- type: code
- content: [ 55, 48, 89, e5, 48, 8d, 3d, 00, 00, 00, 00, 30, c0, e8, 00, 00,
- 00, 00, 31, c0, 5d, c3 ]
- fixups:
- - offset: 07
- kind: pcrel32
- target: 2
- - offset: 0E
- kind: call32
- target: _fprintf
-
- - type: c-string
- content: [ 73, 5A, 00 ]
-
- ...
-
-The biggest use for the textual format will be writing test cases. Writing test
-cases in C is problematic because the compiler may vary its output over time for
-its own optimization reasons which my inadvertently disable or break the linker
-feature trying to be tested. By writing test cases in the linkers own textual
-format, we can exactly specify every attribute of every atom and thus target
-specific linker logic.
-
-The textual/YAML format follows the ReaderWriter patterns used in lld. The lld
-library comes with the classes: ReaderYAML and WriterYAML.
-
-
-Testing
--------
-
-The lld project contains a test suite which is being built up as new code is
-added to lld. All new lld functionality should have a tests added to the test
-suite. The test suite is `lit <https://llvm.org/cmds/lit.html/>`_ driven. Each
-test is a text file with comments telling lit how to run the test and check the
-result To facilitate testing, the lld project builds a tool called lld-core.
-This tool reads a YAML file (default from stdin), parses it into one or more
-lld::File objects in memory and then feeds those lld::File objects to the
-resolver phase.
-
-
-Resolver testing
-~~~~~~~~~~~~~~~~
-
-Basic testing is the "core linking" or resolving phase. That is where the
-linker merges object files. All test cases are written in YAML. One feature of
-YAML is that it allows multiple "documents" to be encoding in one YAML stream.
-That means one text file can appear to the linker as multiple .o files - the
-normal case for the linker.
-
-Here is a simple example of a core linking test case. It checks that an
-undefined atom from one file will be replaced by a definition from another
-file::
-
- # RUN: lld-core %s | FileCheck %s
-
- #
- # Test that undefined atoms are replaced with defined atoms.
- #
-
- ---
- atoms:
- - name: foo
- definition: undefined
- ---
- atoms:
- - name: foo
- scope: global
- type: code
- ...
-
- # CHECK: name: foo
- # CHECK: scope: global
- # CHECK: type: code
- # CHECK-NOT: name: foo
- # CHECK: ...
-
-
-Passes testing
-~~~~~~~~~~~~~~
-
-Since Passes just operate on an lld::File object, the lld-core tool has the
-option to run a particular pass (after resolving). Thus, you can write a YAML
-test case with carefully crafted input to exercise areas of a Pass and the check
-the resulting lld::File object as represented in YAML.
-
-
-Design Issues
--------------
-
-There are a number of open issues in the design of lld. The plan is to wait and
-make these design decisions when we need to.
-
-
-Debug Info
-~~~~~~~~~~
-
-Currently, the lld model says nothing about debug info. But the most popular
-debug format is DWARF and there is some impedance mismatch with the lld model
-and DWARF. In lld there are just Atoms and only Atoms that need to be in a
-special section at runtime have an associated section. Also, Atoms do not have
-addresses. The way DWARF is spec'ed different parts of DWARF are supposed to go
-into specially named sections and the DWARF references function code by address.
-
-CPU and OS specific functionality
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Currently, lld has an abstract "Platform" that deals with any CPU or OS specific
-differences in linking. We just keep adding virtual methods to the base
-Platform class as we find linking areas that might need customization. At some
-point we'll need to structure this better.
-
-
-File Attributes
-~~~~~~~~~~~~~~~
-
-Currently, lld::File just has a path and a way to iterate its atoms. We will
-need to add more attributes on a File. For example, some equivalent to the
-target triple. There is also a number of cached or computed attributes that
-could make various Passes more efficient. For instance, on Darwin there are a
-number of Objective-C optimizations that can be done by a Pass. But it would
-improve the plain C case if the Objective-C optimization Pass did not have to
-scan all atoms looking for any Objective-C data structures. This could be done
-if the lld::File object had an attribute that said if the file had any
-Objective-C data in it. The Resolving phase would then be required to "merge"
-that attribute as object files are added.
+++ /dev/null
-.. _development:
-
-Development
-===========
-
-Note: this document discuss Mach-O port of LLD. For ELF and COFF,
-see :doc:`index`.
-
-lld is developed as part of the `LLVM <https://llvm.org>`_ project.
-
-Creating a Reader
------------------
-
-See the :ref:`Creating a Reader <Readers>` guide.
-
-
-Modifying the Driver
---------------------
-
-See :doc:`Driver`.
-
-
-Debugging
----------
-
-You can run lld with ``-mllvm -debug`` command line options to enable debugging
-printouts. If you want to enable debug information for some specific pass, you
-can run it with ``-mllvm '-debug-only=<pass>'``, where pass is a name used in
-the ``DEBUG_WITH_TYPE()`` macro.
-
-
-
-Documentation
--------------
-
-The project documentation is written in reStructuredText and generated using the
-`Sphinx <http://sphinx.pocoo.org/>`_ documentation generator. For more
-information on writing documentation for the project, see the
-:ref:`sphinx_intro`.
-
-.. toctree::
- :hidden:
-
- Readers
- Driver
+++ /dev/null
-.. _getting_started:
-
-Getting Started: Building and Running lld
-=========================================
-
-This page gives you the shortest path to checking out and building lld. If you
-run into problems, please file bugs in the `LLVM Bugzilla`__
-
-__ https://bugs.llvm.org/
-
-Building lld
-------------
-
-On Unix-like Systems
-~~~~~~~~~~~~~~~~~~~~
-
-1. Get the required tools.
-
- * `CMake 2.8`_\+.
- * make (or any build system CMake supports).
- * `Clang 3.1`_\+ or GCC 4.7+ (C++11 support is required).
-
- * If using Clang, you will also need `libc++`_.
- * `Python 2.4`_\+ (not 3.x) for running tests.
-
-.. _CMake 2.8: http://www.cmake.org/cmake/resources/software.html
-.. _Clang 3.1: http://clang.llvm.org/
-.. _libc++: http://libcxx.llvm.org/
-.. _Python 2.4: http://python.org/download/
-
-2. Check out LLVM and subprojects (including lld)::
-
- $ git clone https://github.com/llvm/llvm-project.git
-
-4. Build LLVM and lld::
-
- $ cd llvm-project
- $ mkdir build && cd build
- $ cmake -G "Unix Makefiles" -DLLVM_ENABLE_PROJECTS=lld ../llvm
- $ make
-
- * If you want to build with clang and it is not the default compiler or
- it is installed in an alternate location, you'll need to tell the cmake tool
- the location of the C and C++ compiler via CMAKE_C_COMPILER and
- CMAKE_CXX_COMPILER. For example::
-
- $ cmake -DCMAKE_CXX_COMPILER=/path/to/clang++ -DCMAKE_C_COMPILER=/path/to/clang ...
-
-5. Test::
-
- $ make check-lld
-
-Using Visual Studio
-~~~~~~~~~~~~~~~~~~~
-
-#. Get the required tools.
-
- * `CMake 2.8`_\+.
- * `Visual Studio 12 (2013) or later`_ (required for C++11 support)
- * `Python 2.4`_\+ (not 3.x) for running tests.
-
-.. _CMake 2.8: http://www.cmake.org/cmake/resources/software.html
-.. _Visual Studio 12 (2013) or later: http://www.microsoft.com/visualstudio/11/en-us
-.. _Python 2.4: http://python.org/download/
-
-#. Check out LLVM as above.
-
-#. Generate Visual Studio project files::
-
- $ cd llvm-project/build (out of source build required)
- $ cmake -G "Visual Studio 11" -DLLVM_ENABLE_PROJECTS=lld ../llvm
-
-#. Build
-
- * Open LLVM.sln in Visual Studio.
- * Build the ``ALL_BUILD`` target.
-
-#. Test
-
- * Build the ``lld-test`` target.
-
-More Information
-~~~~~~~~~~~~~~~~
-
-For more information on using CMake see the `LLVM CMake guide`_.
-
-.. _LLVM CMake guide: https://llvm.org/docs/CMake.html
.Fl -color-diagnostics Ns = Ns Cm auto .
.It Fl -compress-debug-sections Ns = Ns Ar value
Compress DWARF debug sections.
-.Ar value
+.Cm value
may be
-.Cm none
-or
-.Cm zlib .
+.Pp
+.Bl -tag -width 2n -compact
+.It Cm none
+No compression.
+.It Cm zlib
The default compression level is 1 (fastest) as the debug info usually
-compresses well at that level, but if you want to compress it more,
+compresses well at that level. If you want to compress it more,
you can specify
.Fl O2
to set the compression level to 6.
+.It Cm zstd
+The compression level is 5.
+.El
+.Pp
.It Fl -cref
-Output cross reference table.
-.It Fl -define-common , Fl d
-Assign space to common symbols.
+Output cross reference table. If
+.Fl Map
+is specified, print to the map file.
.It Fl -defsym Ns = Ns Ar symbol Ns = Ns Ar expression
Define a symbol alias.
.Ar expression
This is recorded in an ELF segment of type
.Dv PT_INTERP .
.It Fl -dynamic-list Ns = Ns Ar file
-Read a list of dynamic symbols from
-.Ar file .
-(executable) Put matched non-local defined symbols to the dynamic symbol table.
-(shared object) References to matched non-local STV_DEFAULT symbols shouldn't be bound to definitions within the shared object. Implies
+Similar to
+.Cm --export-dynamic-symbol-list .
+When creating a shared object, implies
.Cm -Bsymbolic
but does not set DF_SYMBOLIC
.It Fl -EB
.Cm -Bsymbolic-functions
or
.Cm --dynamic-list
+.It Fl -export-dynamic-symbol-list Ns = Ns Ar file
+Read a list of dynamic symbol patterns from
+.Ar file .
+Apply
+.Cm --export-dynamic-symbol
+on each pattern.
.It Fl -fatal-warnings
Treat warnings as errors.
.It Fl -filter Ns = Ns Ar value , Fl F Ar value
for shared libraries.
.It Fl -no-color-diagnostics
Do not use colors in diagnostics.
-.It Fl -no-define-common
-Do not assign space to common symbols.
.It Fl -no-demangle
Do not demangle symbol names.
.It Fl -no-dynamic-linker
.It Fl -no-omagic
Do not set the text data sections to be writable, page align sections.
.It Fl -no-relax
-Disable target-specific relaxations. This is currently a no-op.
+Disable target-specific relaxations. For x86-64 this disables R_X86_64_GOTPCRELX and R_X86_64_REX_GOTPCRELX GOT optimization.
.It Fl -no-rosegment
Do not put read-only non-executable sections in their own segment.
-.It Fl -no-undefined-version
-Report version scripts that refer undefined symbols.
+.It Fl -undefined-version
+Do not report version scripts that refer to undefined symbols.
.It Fl -no-undefined
Report unresolved symbols even if the linker is creating a shared library.
.It Fl -no-warn-symbol-ordering
Do not warn about problems with the symbol ordering file or call graph profile.
+.It Fl -no-warnings , Fl w
+Suppress warnings and cancel
+.Cm --fatal-warnings.
.It Fl -no-whole-archive
Restores the default behavior of loading archive members.
.It Fl -no-pie , Fl -no-pic-executable
Always generate position independent thunks.
.It Fl -pie , Fl -pic-executable
Create a position independent executable.
+.It Fl -power10-stubs Ns = Ns Cm mode
+Whether to use Power10 instructions in call stubs for R_PPC64_REL24_NOTOC and TOC/NOTOC interworking.
+.Ar mode
+may be:
+.Pp
+.Bl -tag -width 2n -compact
+.It Cm yes
+(default) Use.
+.It Cm auto
+Currently the same as yes.
+.It Cm no
+Don't use.
+.El
+
.It Fl -print-gc-sections
List removed unused sections.
.It Fl -print-icf-sections
and
.Fl -whole-archive.
.It Fl -pop-state
-Undo the effect of
+Restore the states saved by
.Fl -push-state.
.It Fl -relocatable , Fl r
Create relocatable object file.
Determine how to handle unresolved symbols.
.It Fl -use-android-relr-tags
Use SHT_ANDROID_RELR / DT_ANDROID_RELR* tags instead of SHT_RELR / DT_RELR*.
-.It Fl v
+.It Fl v , Fl V
Display the version number and proceed with linking if object files are
specified.
-.It Fl V , Fl -version
+.It Fl -version
Display the version number and exit.
.It Fl -verbose
Verbose mode.
Report unresolved symbols as warnings.
.It Fl -whole-archive
Force load of all members in a static library.
+.It Fl -why-extract Ns = Ns Ar file
+Print to a file about why archive members are extracted.
.It Fl -wrap Ns = Ns Ar symbol
Redirect
.Ar symbol
.Dv PT_GNU_STACK
segment.
.Pp
+.It Cm bti-report Ns = Ns Ar [none|warning|error]
+Specify how to report the missing GNU_PROPERTY_AARCH64_FEATURE_1_BTI property.
+.Cm none
+is the default, linker will not report the missing property otherwise will be reported as a warning or an error.
+.Pp
+.It Cm cet-report Ns = Ns Ar [none|warning|error]
+Specify how to report the missing GNU_PROPERTY_X86_FEATURE_1_IBT or GNU_PROPERTY_X86_FEATURE_1_SHSTK properties.
+.Cm none
+is the default, linker will not report the missing property otherwise will be reported as a warning or an error.
+.Pp
.It Cm force-bti
Force enable AArch64 BTI instruction in PLT, warn if Input ELF file does not have GNU_PROPERTY_AARCH64_FEATURE_1_BTI property.
.Pp
.It Cm pac-plt
AArch64 only, use pointer authentication in PLT.
.Pp
+.It Cm pack-relative-relocs
+Similar to
+.Cm -pack-dyn-relocs=relr
+, but synthesizes the GLIBC_ABI_DT_RELR version dependency if there is a GLIBC_2.* version dependency.
+glibc ld.so rejects loading a dynamically linked object without the GLIBC_ABI_DT_RELR version dependency.
+.Pp
.It Cm rel
Use REL format for dynamic relocations.
.Pp
+++ /dev/null
-.. _open_projects:
-
-Open Projects
-=============
-
-Documentation TODOs
-~~~~~~~~~~~~~~~~~~~
-
-.. todolist::
+++ /dev/null
-.. _sphinx_intro:
-
-Sphinx Introduction for LLVM Developers
-=======================================
-
-This document is intended as a short and simple introduction to the Sphinx
-documentation generation system for LLVM developers.
-
-Quickstart
-----------
-
-To get started writing documentation, you will need to:
-
- 1. Have the Sphinx tools :ref:`installed <installing_sphinx>`.
-
- 2. Understand how to :ref:`build the documentation
- <building_the_documentation>`.
-
- 3. Start :ref:`writing documentation <writing_documentation>`!
-
-.. _installing_sphinx:
-
-Installing Sphinx
-~~~~~~~~~~~~~~~~~
-
-You should be able to install Sphinx using the standard Python package
-installation tool ``easy_install``, as follows::
-
- $ sudo easy_install sphinx
- Searching for sphinx
- Reading http://pypi.python.org/simple/sphinx/
- Reading http://sphinx.pocoo.org/
- Best match: Sphinx 1.1.3
- ... more lines here ..
-
-If you do not have root access (or otherwise want to avoid installing Sphinx in
-system directories) see the section on :ref:`installing_sphinx_in_a_venv` .
-
-If you do not have the ``easy_install`` tool on your system, you should be able
-to install it using:
-
- Linux
- Use your distribution's standard package management tool to install it,
- i.e., ``apt-get install easy_install`` or ``yum install easy_install``.
-
- macOS
- All modern macOS systems come with ``easy_install`` as part of the base
- system.
-
- Windows
- See the `setuptools <http://pypi.python.org/pypi/setuptools>`_ package web
- page for instructions.
-
-
-.. _building_the_documentation:
-
-Building the documentation
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-In order to build the documentation need to add ``-DLLVM_ENABLE_SPHINX=ON`` to
-your ``cmake`` command. Once you do this you can build the docs using
-``docs-lld-html`` build (``ninja`` or ``make``) target.
-
-That build target will invoke ``sphinx-build`` with the appropriate options for
-the project, and generate the HTML documentation in a ``tools/lld/docs/html``
-subdirectory.
-
-.. _writing_documentation:
-
-Writing documentation
-~~~~~~~~~~~~~~~~~~~~~
-
-The documentation itself is written in the reStructuredText (ReST) format, and
-Sphinx defines additional tags to support features like cross-referencing.
-
-The ReST format itself is organized around documents mostly being readable
-plaintext documents. You should generally be able to write new documentation
-easily just by following the style of the existing documentation.
-
-If you want to understand the formatting of the documents more, the best place
-to start is Sphinx's own `ReST Primer <http://sphinx.pocoo.org/rest.html>`_.
-
-
-Learning More
--------------
-
-If you want to learn more about the Sphinx system, the best place to start is
-the Sphinx documentation itself, available `here
-<http://sphinx.pocoo.org/contents.html>`_.
-
-
-.. _installing_sphinx_in_a_venv:
-
-Installing Sphinx in a Virtual Environment
-------------------------------------------
-
-Most Python developers prefer to work with tools inside a *virtualenv* (virtual
-environment) instance, which functions as an application sandbox. This avoids
-polluting your system installation with different packages used by various
-projects (and ensures that dependencies for different packages don't conflict
-with one another). Of course, you need to first have the virtualenv software
-itself which generally would be installed at the system level::
-
- $ sudo easy_install virtualenv
-
-but after that you no longer need to install additional packages in the system
-directories.
-
-Once you have the *virtualenv* tool itself installed, you can create a
-virtualenv for Sphinx using::
-
- $ virtualenv ~/my-sphinx-install
- New python executable in /Users/dummy/my-sphinx-install/bin/python
- Installing setuptools............done.
- Installing pip...............done.
-
- $ ~/my-sphinx-install/bin/easy_install sphinx
- ... install messages here ...
-
-and from now on you can "activate" the *virtualenv* using::
-
- $ source ~/my-sphinx-install/bin/activate
-
-which will change your PATH to ensure the sphinx-build tool from inside the
-virtual environment will be used. See the `virtualenv website
-<http://www.virtualenv.org/en/latest/index.html>`_ for more information on using
-virtual environments.
+++ /dev/null
-//===- Core/AbsoluteAtom.h - An absolute Atom -----------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_CORE_ABSOLUTE_ATOM_H
-#define LLD_CORE_ABSOLUTE_ATOM_H
-
-#include "lld/Core/Atom.h"
-
-namespace lld {
-
-/// An AbsoluteAtom has no content.
-/// It exists to represent content at fixed addresses in memory.
-class AbsoluteAtom : public Atom {
-public:
-
- virtual uint64_t value() const = 0;
-
- /// scope - The visibility of this atom to other atoms. C static functions
- /// have scope scopeTranslationUnit. Regular C functions have scope
- /// scopeGlobal. Functions compiled with visibility=hidden have scope
- /// scopeLinkageUnit so they can be see by other atoms being linked but not
- /// by the OS loader.
- virtual Scope scope() const = 0;
-
- static bool classof(const Atom *a) {
- return a->definition() == definitionAbsolute;
- }
-
- static bool classof(const AbsoluteAtom *) { return true; }
-
-protected:
- AbsoluteAtom() : Atom(definitionAbsolute) {}
-};
-
-} // namespace lld
-
-#endif // LLD_CORE_ABSOLUTE_ATOM_H
+++ /dev/null
-//===- Core/ArchiveLibraryFile.h - Models static library ------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_CORE_ARCHIVE_LIBRARY_FILE_H
-#define LLD_CORE_ARCHIVE_LIBRARY_FILE_H
-
-#include "lld/Core/File.h"
-#include <set>
-
-namespace lld {
-
-///
-/// The ArchiveLibraryFile subclass of File is used to represent unix
-/// static library archives. These libraries provide no atoms to the
-/// initial set of atoms linked. Instead, when the Resolver will query
-/// ArchiveLibraryFile instances for specific symbols names using the
-/// find() method. If the archive contains an object file which has a
-/// DefinedAtom whose scope is not translationUnit, then that entire
-/// object file File is returned.
-///
-class ArchiveLibraryFile : public File {
-public:
- static bool classof(const File *f) {
- return f->kind() == kindArchiveLibrary;
- }
-
- /// Check if any member of the archive contains an Atom with the
- /// specified name and return the File object for that member, or nullptr.
- virtual File *find(StringRef name) = 0;
-
- virtual std::error_code
- parseAllMembers(std::vector<std::unique_ptr<File>> &result) = 0;
-
-protected:
- /// only subclasses of ArchiveLibraryFile can be instantiated
- ArchiveLibraryFile(StringRef path) : File(path, kindArchiveLibrary) {}
-};
-
-} // namespace lld
-
-#endif // LLD_CORE_ARCHIVE_LIBRARY_FILE_H
+++ /dev/null
-//===- Core/Atom.h - A node in linking graph --------------------*- C++ -*-===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_CORE_ATOM_H
-#define LLD_CORE_ATOM_H
-
-#include "lld/Common/LLVM.h"
-#include "llvm/ADT/StringRef.h"
-
-namespace lld {
-
-class File;
-
-template<typename T>
-class OwningAtomPtr;
-
-///
-/// The linker has a Graph Theory model of linking. An object file is seen
-/// as a set of Atoms with References to other Atoms. Each Atom is a node
-/// and each Reference is an edge. An Atom can be a DefinedAtom which has
-/// content or a UndefinedAtom which is a placeholder and represents an
-/// undefined symbol (extern declaration).
-///
-class Atom {
- template<typename T> friend class OwningAtomPtr;
-
-public:
- /// Whether this atom is defined or a proxy for an undefined symbol
- enum Definition {
- definitionRegular, ///< Normal C/C++ function or global variable.
- definitionAbsolute, ///< Asm-only (foo = 10). Not tied to any content.
- definitionUndefined, ///< Only in .o files to model reference to undef.
- definitionSharedLibrary ///< Only in shared libraries to model export.
- };
-
- /// The scope in which this atom is accessible to other atoms.
- enum Scope {
- scopeTranslationUnit, ///< Accessible only to atoms in the same translation
- /// unit (e.g. a C static).
- scopeLinkageUnit, ///< Accessible to atoms being linked but not visible
- /// to runtime loader (e.g. visibility=hidden).
- scopeGlobal ///< Accessible to all atoms and visible to runtime
- /// loader (e.g. visibility=default).
- };
-
- /// file - returns the File that produced/owns this Atom
- virtual const File& file() const = 0;
-
- /// name - The name of the atom. For a function atom, it is the (mangled)
- /// name of the function.
- virtual StringRef name() const = 0;
-
- /// definition - Whether this atom is a definition or represents an undefined
- /// symbol.
- Definition definition() const { return _definition; }
-
- static bool classof(const Atom *a) { return true; }
-
-protected:
- /// Atom is an abstract base class. Only subclasses can access constructor.
- explicit Atom(Definition def) : _definition(def) {}
-
- /// The memory for Atom objects is always managed by the owning File
- /// object. Therefore, no one but the owning File object should call
- /// delete on an Atom. In fact, some File objects may bulk allocate
- /// an array of Atoms, so they cannot be individually deleted by anyone.
- virtual ~Atom() = default;
-
-private:
- Definition _definition;
-};
-
-/// Class which owns an atom pointer and runs the atom destructor when the
-/// owning pointer goes out of scope.
-template<typename T>
-class OwningAtomPtr {
-private:
- OwningAtomPtr(const OwningAtomPtr &) = delete;
- void operator=(const OwningAtomPtr &) = delete;
-
-public:
- OwningAtomPtr() = default;
- OwningAtomPtr(T *atom) : atom(atom) { }
-
- ~OwningAtomPtr() {
- if (atom)
- runDestructor(atom);
- }
-
- void runDestructor(Atom *atom) {
- atom->~Atom();
- }
-
- OwningAtomPtr(OwningAtomPtr &&ptr) : atom(ptr.atom) {
- ptr.atom = nullptr;
- }
-
- void operator=(OwningAtomPtr&& ptr) {
- if (atom)
- runDestructor(atom);
- atom = ptr.atom;
- ptr.atom = nullptr;
- }
-
- T *const &get() const {
- return atom;
- }
-
- T *&get() {
- return atom;
- }
-
- T *release() {
- auto *v = atom;
- atom = nullptr;
- return v;
- }
-
-private:
- T *atom = nullptr;
-};
-
-} // end namespace lld
-
-#endif // LLD_CORE_ATOM_H
+++ /dev/null
-//===- Core/DefinedAtom.h - An Atom with content --------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_CORE_DEFINED_ATOM_H
-#define LLD_CORE_DEFINED_ATOM_H
-
-#include "lld/Common/LLVM.h"
-#include "lld/Core/Atom.h"
-#include "lld/Core/Reference.h"
-#include "llvm/Support/ErrorHandling.h"
-
-namespace lld {
-class File;
-
-/// The fundamental unit of linking.
-///
-/// A C function or global variable is an atom. An atom has content and
-/// attributes. The content of a function atom is the instructions that
-/// implement the function. The content of a global variable atom is its
-/// initial bytes.
-///
-/// Here are some example attribute sets for common atoms. If a particular
-/// attribute is not listed, the default values are: definition=regular,
-/// sectionChoice=basedOnContent, scope=translationUnit, merge=no,
-/// deadStrip=normal, interposable=no
-///
-/// C function: void foo() {} <br>
-/// name=foo, type=code, perm=r_x, scope=global
-///
-/// C static function: staic void func() {} <br>
-/// name=func, type=code, perm=r_x
-///
-/// C global variable: int count = 1; <br>
-/// name=count, type=data, perm=rw_, scope=global
-///
-/// C tentative definition: int bar; <br>
-/// name=bar, type=zerofill, perm=rw_, scope=global,
-/// merge=asTentative, interposable=yesAndRuntimeWeak
-///
-/// Uninitialized C static variable: static int stuff; <br>
-/// name=stuff, type=zerofill, perm=rw_
-///
-/// Weak C function: __attribute__((weak)) void foo() {} <br>
-/// name=foo, type=code, perm=r_x, scope=global, merge=asWeak
-///
-/// Hidden C function: __attribute__((visibility("hidden"))) void foo() {}<br>
-/// name=foo, type=code, perm=r_x, scope=linkageUnit
-///
-/// No-dead-strip function: __attribute__((used)) void foo() {} <br>
-/// name=foo, type=code, perm=r_x, scope=global, deadStrip=never
-///
-/// Non-inlined C++ inline method: inline void Foo::doit() {} <br>
-/// name=_ZN3Foo4doitEv, type=code, perm=r_x, scope=global,
-/// mergeDupes=asWeak
-///
-/// Non-inlined C++ inline method whose address is taken:
-/// inline void Foo::doit() {} <br>
-/// name=_ZN3Foo4doitEv, type=code, perm=r_x, scope=global,
-/// mergeDupes=asAddressedWeak
-///
-/// literal c-string: "hello" <br>
-/// name="" type=cstring, perm=r__, scope=linkageUnit
-///
-/// literal double: 1.234 <br>
-/// name="" type=literal8, perm=r__, scope=linkageUnit
-///
-/// constant: { 1,2,3 } <br>
-/// name="" type=constant, perm=r__, scope=linkageUnit
-///
-/// Pointer to initializer function: <br>
-/// name="" type=initializer, perm=rw_l,
-/// sectionChoice=customRequired
-///
-/// C function place in custom section: __attribute__((section("__foo")))
-/// void foo() {} <br>
-/// name=foo, type=code, perm=r_x, scope=global,
-/// sectionChoice=customRequired, customSectionName=__foo
-///
-class DefinedAtom : public Atom {
-public:
- enum Interposable {
- interposeNo, // linker can directly bind uses of this atom
- interposeYes, // linker must indirect (through GOT) uses
- interposeYesAndRuntimeWeak // must indirect and mark symbol weak in final
- // linked image
- };
-
- enum Merge {
- mergeNo, // Another atom with same name is error
- mergeAsTentative, // Is ANSI C tentative definition, can be coalesced
- mergeAsWeak, // Is C++ inline definition that was not inlined,
- // but address was not taken, so atom can be hidden
- // by linker
- mergeAsWeakAndAddressUsed, // Is C++ definition inline definition whose
- // address was taken.
- mergeSameNameAndSize, // Another atom with different size is error
- mergeByLargestSection, // Choose an atom whose section is the largest.
- mergeByContent, // Merge with other constants with same content.
- };
-
- enum ContentType {
- typeUnknown, // for use with definitionUndefined
- typeMachHeader, // atom representing mach_header [Darwin]
- typeCode, // executable code
- typeResolver, // function which returns address of target
- typeBranchIsland, // linker created for large binaries
- typeBranchShim, // linker created to switch thumb mode
- typeStub, // linker created for calling external function
- typeStubHelper, // linker created for initial stub binding
- typeConstant, // a read-only constant
- typeCString, // a zero terminated UTF8 C string
- typeUTF16String, // a zero terminated UTF16 string
- typeCFI, // a FDE or CIE from dwarf unwind info
- typeLSDA, // extra unwinding info
- typeLiteral4, // a four-btye read-only constant
- typeLiteral8, // an eight-btye read-only constant
- typeLiteral16, // a sixteen-btye read-only constant
- typeData, // read-write data
- typeDataFast, // allow data to be quickly accessed
- typeZeroFill, // zero-fill data
- typeZeroFillFast, // allow zero-fill data to be quicky accessed
- typeConstData, // read-only data after dynamic linker is done
- typeObjC1Class, // ObjC1 class [Darwin]
- typeLazyPointer, // pointer through which a stub jumps
- typeLazyDylibPointer, // pointer through which a stub jumps [Darwin]
- typeNonLazyPointer, // pointer to external symbol
- typeCFString, // NS/CFString object [Darwin]
- typeGOT, // pointer to external symbol
- typeInitializerPtr, // pointer to initializer function
- typeTerminatorPtr, // pointer to terminator function
- typeCStringPtr, // pointer to UTF8 C string [Darwin]
- typeObjCClassPtr, // pointer to ObjC class [Darwin]
- typeObjC2CategoryList, // pointers to ObjC category [Darwin]
- typeObjCImageInfo, // pointer to ObjC class [Darwin]
- typeObjCMethodList, // pointer to ObjC method list [Darwin]
- typeDTraceDOF, // runtime data for Dtrace [Darwin]
- typeInterposingTuples, // tuples of interposing info for dyld [Darwin]
- typeTempLTO, // temporary atom for bitcode reader
- typeCompactUnwindInfo, // runtime data for unwinder [Darwin]
- typeProcessedUnwindInfo,// compressed compact unwind info [Darwin]
- typeThunkTLV, // thunk used to access a TLV [Darwin]
- typeTLVInitialData, // initial data for a TLV [Darwin]
- typeTLVInitialZeroFill, // TLV initial zero fill data [Darwin]
- typeTLVInitializerPtr, // pointer to thread local initializer [Darwin]
- typeDSOHandle, // atom representing DSO handle [Darwin]
- typeSectCreate, // Created via the -sectcreate option [Darwin]
- };
-
- // Permission bits for atoms and segments. The order of these values are
- // important, because the layout pass may sort atoms by permission if other
- // attributes are the same.
- enum ContentPermissions {
- perm___ = 0, // mapped as unaccessible
- permR__ = 8, // mapped read-only
- permRW_ = 8 + 2, // mapped readable and writable
- permRW_L = 8 + 2 + 1, // initially mapped r/w, then made read-only
- // loader writable
- permR_X = 8 + 4, // mapped readable and executable
- permRWX = 8 + 2 + 4, // mapped readable and writable and executable
- permUnknown = 16 // unknown or invalid permissions
- };
-
- enum SectionChoice {
- sectionBasedOnContent, // linker infers final section based on content
- sectionCustomPreferred, // linker may place in specific section
- sectionCustomRequired // linker must place in specific section
- };
-
- enum DeadStripKind {
- deadStripNormal, // linker may dead strip this atom
- deadStripNever, // linker must never dead strip this atom
- deadStripAlways // linker must remove this atom if unused
- };
-
- enum DynamicExport {
- /// The linker may or may not export this atom dynamically depending
- /// on the output type and other context of the link.
- dynamicExportNormal,
- /// The linker will always export this atom dynamically.
- dynamicExportAlways,
- };
-
- // Attributes describe a code model used by the atom.
- enum CodeModel {
- codeNA, // no specific code model
- // MIPS code models
- codeMipsPIC, // PIC function in a PIC / non-PIC mixed file
- codeMipsMicro, // microMIPS instruction encoding
- codeMipsMicroPIC, // microMIPS instruction encoding + PIC
- codeMips16, // MIPS-16 instruction encoding
- // ARM code models
- codeARMThumb, // ARM Thumb instruction set
- codeARM_a, // $a-like mapping symbol (for ARM code)
- codeARM_d, // $d-like mapping symbol (for data)
- codeARM_t, // $t-like mapping symbol (for Thumb code)
- };
-
- struct Alignment {
- Alignment(int v, int m = 0) : value(v), modulus(m) {}
-
- uint16_t value;
- uint16_t modulus;
-
- bool operator==(const Alignment &rhs) const {
- return (value == rhs.value) && (modulus == rhs.modulus);
- }
- };
-
- /// returns a value for the order of this Atom within its file.
- ///
- /// This is used by the linker to order the layout of Atoms so that the
- /// resulting image is stable and reproducible.
- virtual uint64_t ordinal() const = 0;
-
- /// the number of bytes of space this atom's content will occupy in the
- /// final linked image.
- ///
- /// For a function atom, it is the number of bytes of code in the function.
- virtual uint64_t size() const = 0;
-
- /// The size of the section from which the atom is instantiated.
- ///
- /// Merge::mergeByLargestSection is defined in terms of section size
- /// and not in terms of atom size, so we need this function separate
- /// from size().
- virtual uint64_t sectionSize() const { return 0; }
-
- /// The visibility of this atom to other atoms.
- ///
- /// C static functions have scope scopeTranslationUnit. Regular C functions
- /// have scope scopeGlobal. Functions compiled with visibility=hidden have
- /// scope scopeLinkageUnit so they can be see by other atoms being linked but
- /// not by the OS loader.
- virtual Scope scope() const = 0;
-
- /// Whether the linker should use direct or indirect access to this
- /// atom.
- virtual Interposable interposable() const = 0;
-
- /// how the linker should handle if multiple atoms have the same name.
- virtual Merge merge() const = 0;
-
- /// The type of this atom, such as code or data.
- virtual ContentType contentType() const = 0;
-
- /// The alignment constraints on how this atom must be laid out in the
- /// final linked image (e.g. 16-byte aligned).
- virtual Alignment alignment() const = 0;
-
- /// Whether this atom must be in a specially named section in the final
- /// linked image, or if the linker can infer the section based on the
- /// contentType().
- virtual SectionChoice sectionChoice() const = 0;
-
- /// If sectionChoice() != sectionBasedOnContent, then this return the
- /// name of the section the atom should be placed into.
- virtual StringRef customSectionName() const = 0;
-
- /// constraints on whether the linker may dead strip away this atom.
- virtual DeadStripKind deadStrip() const = 0;
-
- /// Under which conditions should this atom be dynamically exported.
- virtual DynamicExport dynamicExport() const {
- return dynamicExportNormal;
- }
-
- /// Code model used by the atom.
- virtual CodeModel codeModel() const { return codeNA; }
-
- /// Returns the OS memory protections required for this atom's content
- /// at runtime.
- ///
- /// A function atom is R_X, a global variable is RW_, and a read-only constant
- /// is R__.
- virtual ContentPermissions permissions() const;
-
- /// returns a reference to the raw (unrelocated) bytes of this Atom's
- /// content.
- virtual ArrayRef<uint8_t> rawContent() const = 0;
-
- /// This class abstracts iterating over the sequence of References
- /// in an Atom. Concrete instances of DefinedAtom must implement
- /// the derefIterator() and incrementIterator() methods.
- class reference_iterator {
- public:
- reference_iterator(const DefinedAtom &a, const void *it)
- : _atom(a), _it(it) { }
-
- const Reference *operator*() const {
- return _atom.derefIterator(_it);
- }
-
- const Reference *operator->() const {
- return _atom.derefIterator(_it);
- }
-
- bool operator==(const reference_iterator &other) const {
- return _it == other._it;
- }
-
- bool operator!=(const reference_iterator &other) const {
- return !(*this == other);
- }
-
- reference_iterator &operator++() {
- _atom.incrementIterator(_it);
- return *this;
- }
- private:
- const DefinedAtom &_atom;
- const void *_it;
- };
-
- /// Returns an iterator to the beginning of this Atom's References.
- virtual reference_iterator begin() const = 0;
-
- /// Returns an iterator to the end of this Atom's References.
- virtual reference_iterator end() const = 0;
-
- /// Adds a reference to this atom.
- virtual void addReference(Reference::KindNamespace ns,
- Reference::KindArch arch,
- Reference::KindValue kindValue, uint64_t off,
- const Atom *target, Reference::Addend a) {
- llvm_unreachable("Subclass does not permit adding references");
- }
-
- static bool classof(const Atom *a) {
- return a->definition() == definitionRegular;
- }
-
- /// Utility for deriving permissions from content type
- static ContentPermissions permissions(ContentType type);
-
- /// Utility function to check if the atom occupies file space
- bool occupiesDiskSpace() const {
- ContentType atomContentType = contentType();
- return !(atomContentType == DefinedAtom::typeZeroFill ||
- atomContentType == DefinedAtom::typeZeroFillFast ||
- atomContentType == DefinedAtom::typeTLVInitialZeroFill);
- }
-
- /// Utility function to check if relocations in this atom to other defined
- /// atoms can be implicitly generated, and so we don't need to explicitly
- /// emit those relocations.
- bool relocsToDefinedCanBeImplicit() const {
- ContentType atomContentType = contentType();
- return atomContentType == typeCFI;
- }
-
-protected:
- // DefinedAtom is an abstract base class. Only subclasses can access
- // constructor.
- DefinedAtom() : Atom(definitionRegular) { }
-
- ~DefinedAtom() override = default;
-
- /// Returns a pointer to the Reference object that the abstract
- /// iterator "points" to.
- virtual const Reference *derefIterator(const void *iter) const = 0;
-
- /// Adjusts the abstract iterator to "point" to the next Reference
- /// object for this Atom.
- virtual void incrementIterator(const void *&iter) const = 0;
-};
-} // end namespace lld
-
-#endif
+++ /dev/null
-//===- Error.h - system_error extensions for lld ----------------*- C++ -*-===//
-//
-// 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 declares a new error_category for the lld library.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_CORE_ERROR_H
-#define LLD_CORE_ERROR_H
-
-#include "lld/Common/LLVM.h"
-#include "llvm/ADT/Twine.h"
-#include "llvm/Support/Error.h"
-#include <system_error>
-
-namespace lld {
-
-const std::error_category &YamlReaderCategory();
-
-enum class YamlReaderError {
- unknown_keyword,
- illegal_value
-};
-
-inline std::error_code make_error_code(YamlReaderError e) {
- return std::error_code(static_cast<int>(e), YamlReaderCategory());
-}
-
-/// Creates an error_code object that has associated with it an arbitrary
-/// error message. The value() of the error_code will always be non-zero
-/// but its value is meaningless. The message() will be (a copy of) the
-/// supplied error string.
-/// Note: Once ErrorOr<> is updated to work with errors other than error_code,
-/// this can be updated to return some other kind of error.
-std::error_code make_dynamic_error_code(StringRef msg);
-
-/// Generic error.
-///
-/// For errors that don't require their own specific sub-error (most errors)
-/// this class can be used to describe the error via a string message.
-class GenericError : public llvm::ErrorInfo<GenericError> {
-public:
- static char ID;
- GenericError(Twine Msg);
- const std::string &getMessage() const { return Msg; }
- void log(llvm::raw_ostream &OS) const override;
-
- std::error_code convertToErrorCode() const override {
- return make_dynamic_error_code(getMessage());
- }
-
-private:
- std::string Msg;
-};
-
-} // end namespace lld
-
-namespace std {
-template <> struct is_error_code_enum<lld::YamlReaderError> : std::true_type {};
-}
-
-#endif
+++ /dev/null
-//===- Core/File.h - A Container of Atoms ---------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_CORE_FILE_H
-#define LLD_CORE_FILE_H
-
-#include "lld/Core/AbsoluteAtom.h"
-#include "lld/Core/DefinedAtom.h"
-#include "lld/Core/SharedLibraryAtom.h"
-#include "lld/Core/UndefinedAtom.h"
-#include "llvm/ADT/Optional.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/Twine.h"
-#include "llvm/Support/Allocator.h"
-#include "llvm/Support/ErrorHandling.h"
-#include <functional>
-#include <memory>
-#include <mutex>
-#include <vector>
-
-namespace lld {
-
-class LinkingContext;
-
-/// Every Atom is owned by some File. A common scenario is for a single
-/// object file (.o) to be parsed by some reader and produce a single
-/// File object that represents the content of that object file.
-///
-/// To iterate through the Atoms in a File there are four methods that
-/// return collections. For instance to iterate through all the DefinedAtoms
-/// in a File object use:
-/// for (const DefinedAtoms *atom : file->defined()) {
-/// }
-///
-/// The Atom objects in a File are owned by the File object. The Atom objects
-/// are destroyed when the File object is destroyed.
-class File {
-public:
- virtual ~File();
-
- /// Kinds of files that are supported.
- enum Kind {
- kindErrorObject, ///< a error object file (.o)
- kindNormalizedObject, ///< a normalized file (.o)
- kindMachObject, ///< a MachO object file (.o)
- kindCEntryObject, ///< a file for CEntries
- kindHeaderObject, ///< a file for file headers
- kindEntryObject, ///< a file for the entry
- kindUndefinedSymsObject, ///< a file for undefined symbols
- kindStubHelperObject, ///< a file for stub helpers
- kindResolverMergedObject, ///< the resolver merged file.
- kindSectCreateObject, ///< a sect create object file (.o)
- kindSharedLibrary, ///< shared library (.so)
- kindArchiveLibrary ///< archive (.a)
- };
-
- /// Returns file kind. Need for dyn_cast<> on File objects.
- Kind kind() const {
- return _kind;
- }
-
- /// This returns the path to the file which was used to create this object
- /// (e.g. "/tmp/foo.o"). If the file is a member of an archive file, the
- /// returned string includes the archive file name.
- StringRef path() const {
- if (_archivePath.empty())
- return _path;
- if (_archiveMemberPath.empty())
- _archiveMemberPath = (_archivePath + "(" + _path + ")").str();
- return _archiveMemberPath;
- }
-
- /// Returns the path of the archive file name if this file is instantiated
- /// from an archive file. Otherwise returns the empty string.
- StringRef archivePath() const { return _archivePath; }
- void setArchivePath(StringRef path) { _archivePath = std::string(path); }
-
- /// Returns the path name of this file. It doesn't include archive file name.
- StringRef memberPath() const { return _path; }
-
- /// Returns the command line order of the file.
- uint64_t ordinal() const {
- assert(_ordinal != UINT64_MAX);
- return _ordinal;
- }
-
- /// Returns true/false depending on whether an ordinal has been set.
- bool hasOrdinal() const { return (_ordinal != UINT64_MAX); }
-
- /// Sets the command line order of the file.
- void setOrdinal(uint64_t ordinal) const { _ordinal = ordinal; }
-
- /// Returns the ordinal for the next atom to be defined in this file.
- uint64_t getNextAtomOrdinalAndIncrement() const {
- return _nextAtomOrdinal++;
- }
-
- /// For allocating any objects owned by this File.
- llvm::BumpPtrAllocator &allocator() const {
- return _allocator;
- }
-
- /// The type of atom mutable container.
- template <typename T> using AtomVector = std::vector<OwningAtomPtr<T>>;
-
- /// The range type for the atoms.
- template <typename T> class AtomRange {
- public:
- AtomRange(AtomVector<T> &v) : _v(v) {}
- AtomRange(const AtomVector<T> &v) : _v(const_cast<AtomVector<T> &>(v)) {}
-
- using ConstDerefFn = const T* (*)(const OwningAtomPtr<T>&);
- using DerefFn = T* (*)(OwningAtomPtr<T>&);
-
- typedef llvm::mapped_iterator<typename AtomVector<T>::const_iterator,
- ConstDerefFn> ConstItTy;
- typedef llvm::mapped_iterator<typename AtomVector<T>::iterator,
- DerefFn> ItTy;
-
- static const T* DerefConst(const OwningAtomPtr<T> &p) {
- return p.get();
- }
-
- static T* Deref(OwningAtomPtr<T> &p) {
- return p.get();
- }
-
- ConstItTy begin() const {
- return ConstItTy(_v.begin(), ConstDerefFn(DerefConst));
- }
- ConstItTy end() const {
- return ConstItTy(_v.end(), ConstDerefFn(DerefConst));
- }
-
- ItTy begin() {
- return ItTy(_v.begin(), DerefFn(Deref));
- }
- ItTy end() {
- return ItTy(_v.end(), DerefFn(Deref));
- }
-
- llvm::iterator_range<typename AtomVector<T>::iterator> owning_ptrs() {
- return llvm::make_range(_v.begin(), _v.end());
- }
-
- llvm::iterator_range<typename AtomVector<T>::iterator> owning_ptrs() const {
- return llvm::make_range(_v.begin(), _v.end());
- }
-
- bool empty() const {
- return _v.empty();
- }
-
- size_t size() const {
- return _v.size();
- }
-
- const OwningAtomPtr<T> &operator[](size_t idx) const {
- return _v[idx];
- }
-
- OwningAtomPtr<T> &operator[](size_t idx) {
- return _v[idx];
- }
-
- private:
- AtomVector<T> &_v;
- };
-
- /// Must be implemented to return the AtomVector object for
- /// all DefinedAtoms in this File.
- virtual const AtomRange<DefinedAtom> defined() const = 0;
-
- /// Must be implemented to return the AtomVector object for
- /// all UndefinedAtomw in this File.
- virtual const AtomRange<UndefinedAtom> undefined() const = 0;
-
- /// Must be implemented to return the AtomVector object for
- /// all SharedLibraryAtoms in this File.
- virtual const AtomRange<SharedLibraryAtom> sharedLibrary() const = 0;
-
- /// Must be implemented to return the AtomVector object for
- /// all AbsoluteAtoms in this File.
- virtual const AtomRange<AbsoluteAtom> absolute() const = 0;
-
- /// Drop all of the atoms owned by this file. This will result in all of
- /// the atoms running their destructors.
- /// This is required because atoms may be allocated on a BumpPtrAllocator
- /// of a different file. We need to destruct all atoms before any files.
- virtual void clearAtoms() = 0;
-
- /// If a file is parsed using a different method than doParse(),
- /// one must use this method to set the last error status, so that
- /// doParse will not be called twice. Only YAML reader uses this
- /// (because YAML reader does not read blobs but structured data).
- void setLastError(std::error_code err) { _lastError = err; }
-
- std::error_code parse();
-
- // Usually each file owns a std::unique_ptr<MemoryBuffer>.
- // However, there's one special case. If a file is an archive file,
- // the archive file and its children all shares the same memory buffer.
- // This method is used by the ArchiveFile to give its children
- // co-ownership of the buffer.
- void setSharedMemoryBuffer(std::shared_ptr<MemoryBuffer> mb) {
- _sharedMemoryBuffer = mb;
- }
-
-protected:
- /// only subclasses of File can be instantiated
- File(StringRef p, Kind kind)
- : _path(p), _kind(kind), _ordinal(UINT64_MAX),
- _nextAtomOrdinal(0) {}
-
- /// Subclasses should override this method to parse the
- /// memory buffer passed to this file's constructor.
- virtual std::error_code doParse() { return std::error_code(); }
-
- static AtomVector<DefinedAtom> _noDefinedAtoms;
- static AtomVector<UndefinedAtom> _noUndefinedAtoms;
- static AtomVector<SharedLibraryAtom> _noSharedLibraryAtoms;
- static AtomVector<AbsoluteAtom> _noAbsoluteAtoms;
- mutable llvm::BumpPtrAllocator _allocator;
-
-private:
- StringRef _path;
- std::string _archivePath;
- mutable std::string _archiveMemberPath;
- Kind _kind;
- mutable uint64_t _ordinal;
- mutable uint64_t _nextAtomOrdinal;
- std::shared_ptr<MemoryBuffer> _sharedMemoryBuffer;
- llvm::Optional<std::error_code> _lastError;
- std::mutex _parseMutex;
-};
-
-/// An ErrorFile represents a file that doesn't exist.
-/// If you try to parse a file which doesn't exist, an instance of this
-/// class will be returned. That's parse method always returns an error.
-/// This is useful to delay erroring on non-existent files, so that we
-/// can do unit testing a driver using non-existing file paths.
-class ErrorFile : public File {
-public:
- ErrorFile(StringRef path, std::error_code ec)
- : File(path, kindErrorObject), _ec(ec) {}
-
- std::error_code doParse() override { return _ec; }
-
- const AtomRange<DefinedAtom> defined() const override {
- llvm_unreachable("internal error");
- }
- const AtomRange<UndefinedAtom> undefined() const override {
- llvm_unreachable("internal error");
- }
- const AtomRange<SharedLibraryAtom> sharedLibrary() const override {
- llvm_unreachable("internal error");
- }
- const AtomRange<AbsoluteAtom> absolute() const override {
- llvm_unreachable("internal error");
- }
-
- void clearAtoms() override {
- }
-
-private:
- std::error_code _ec;
-};
-
-} // end namespace lld
-
-#endif
+++ /dev/null
-//===- include/Core/Instrumentation.h - Instrumentation API ---------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// Provide an Instrumentation API that optionally uses VTune interfaces.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_CORE_INSTRUMENTATION_H
-#define LLD_CORE_INSTRUMENTATION_H
-
-#include "llvm/Support/Compiler.h"
-#include <utility>
-
-#ifdef LLD_HAS_VTUNE
-# include <ittnotify.h>
-#endif
-
-namespace lld {
-#ifdef LLD_HAS_VTUNE
-/// A unique global scope for instrumentation data.
-///
-/// Domains last for the lifetime of the application and cannot be destroyed.
-/// Multiple Domains created with the same name represent the same domain.
-class Domain {
- __itt_domain *_domain;
-
-public:
- explicit Domain(const char *name) : _domain(__itt_domain_createA(name)) {}
-
- operator __itt_domain *() const { return _domain; }
- __itt_domain *operator->() const { return _domain; }
-};
-
-/// A global reference to a string constant.
-///
-/// These are uniqued by the ITT runtime and cannot be deleted. They are not
-/// specific to a domain.
-///
-/// Prefer reusing a single StringHandle over passing a ntbs when the same
-/// string will be used often.
-class StringHandle {
- __itt_string_handle *_handle;
-
-public:
- StringHandle(const char *name) : _handle(__itt_string_handle_createA(name)) {}
-
- operator __itt_string_handle *() const { return _handle; }
-};
-
-/// A task on a single thread. Nests within other tasks.
-///
-/// Each thread has its own task stack and tasks nest recursively on that stack.
-/// A task cannot transfer threads.
-///
-/// SBRM is used to ensure task starts and ends are balanced. The lifetime of
-/// a task is either the lifetime of this object, or until end is called.
-class ScopedTask {
- __itt_domain *_domain;
-
- ScopedTask(const ScopedTask &) = delete;
- ScopedTask &operator=(const ScopedTask &) = delete;
-
-public:
- /// Create a task in Domain \p d named \p s.
- ScopedTask(const Domain &d, const StringHandle &s) : _domain(d) {
- __itt_task_begin(d, __itt_null, __itt_null, s);
- }
-
- ScopedTask(ScopedTask &&other) {
- *this = std::move(other);
- }
-
- ScopedTask &operator=(ScopedTask &&other) {
- _domain = other._domain;
- other._domain = nullptr;
- return *this;
- }
-
- /// Prematurely end this task.
- void end() {
- if (_domain)
- __itt_task_end(_domain);
- _domain = nullptr;
- }
-
- ~ScopedTask() { end(); }
-};
-
-/// A specific point in time. Allows metadata to be associated.
-class Marker {
-public:
- Marker(const Domain &d, const StringHandle &s) {
- __itt_marker(d, __itt_null, s, __itt_scope_global);
- }
-};
-#else
-class Domain {
-public:
- Domain(const char *name) {}
-};
-
-class StringHandle {
-public:
- StringHandle(const char *name) {}
-};
-
-class ScopedTask {
-public:
- ScopedTask(const Domain &d, const StringHandle &s) {}
- void end() {}
-};
-
-class Marker {
-public:
- Marker(const Domain &d, const StringHandle &s) {}
-};
-#endif
-
-inline const Domain &getDefaultDomain() {
- static Domain domain("org.llvm.lld");
- return domain;
-}
-} // end namespace lld.
-
-#endif
+++ /dev/null
-//===- lld/Core/LinkingContext.h - Linker Target Info Interface -*- C++ -*-===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_CORE_LINKING_CONTEXT_H
-#define LLD_CORE_LINKING_CONTEXT_H
-
-#include "lld/Core/Node.h"
-#include "lld/Core/Reader.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/Allocator.h"
-#include "llvm/Support/Error.h"
-#include <cassert>
-#include <cstdint>
-#include <memory>
-#include <string>
-#include <vector>
-
-namespace lld {
-
-class PassManager;
-class File;
-class Writer;
-class Node;
-class SharedLibraryFile;
-
-/// The LinkingContext class encapsulates "what and how" to link.
-///
-/// The base class LinkingContext contains the options needed by core linking.
-/// Subclasses of LinkingContext have additional options needed by specific
-/// Writers.
-class LinkingContext {
-public:
- virtual ~LinkingContext();
-
- /// \name Methods needed by core linking
- /// @{
-
- /// Name of symbol linker should use as "entry point" to program,
- /// usually "main" or "start".
- virtual StringRef entrySymbolName() const { return _entrySymbolName; }
-
- /// Whether core linking should remove Atoms not reachable by following
- /// References from the entry point Atom or from all global scope Atoms
- /// if globalsAreDeadStripRoots() is true.
- bool deadStrip() const { return _deadStrip; }
-
- /// Only used if deadStrip() returns true. Means all global scope Atoms
- /// should be marked live (along with all Atoms they reference). Usually
- /// this method returns false for main executables, but true for dynamic
- /// shared libraries.
- bool globalsAreDeadStripRoots() const { return _globalsAreDeadStripRoots; }
-
- /// Only used if deadStrip() returns true. This method returns the names
- /// of DefinedAtoms that should be marked live (along with all Atoms they
- /// reference). Only Atoms with scope scopeLinkageUnit or scopeGlobal can
- /// be kept live using this method.
- ArrayRef<StringRef> deadStripRoots() const {
- return _deadStripRoots;
- }
-
- /// Add the given symbol name to the dead strip root set. Only used if
- /// deadStrip() returns true.
- void addDeadStripRoot(StringRef symbolName) {
- assert(!symbolName.empty() && "Empty symbol cannot be a dead strip root");
- _deadStripRoots.push_back(symbolName);
- }
-
- /// Normally, every UndefinedAtom must be replaced by a DefinedAtom or a
- /// SharedLibraryAtom for the link to be successful. This method controls
- /// whether core linking prints out a list of remaining UndefinedAtoms.
- ///
- /// \todo This should be a method core linking calls with a list of the
- /// UndefinedAtoms so that different drivers can format the error message
- /// as needed.
- bool printRemainingUndefines() const { return _printRemainingUndefines; }
-
- /// Normally, every UndefinedAtom must be replaced by a DefinedAtom or a
- /// SharedLibraryAtom for the link to be successful. This method controls
- /// whether core linking considers remaining undefines to be an error.
- bool allowRemainingUndefines() const { return _allowRemainingUndefines; }
-
- /// Normally, every UndefinedAtom must be replaced by a DefinedAtom or a
- /// SharedLibraryAtom for the link to be successful. This method controls
- /// whether core linking considers remaining undefines from the shared library
- /// to be an error.
- bool allowShlibUndefines() const { return _allowShlibUndefines; }
-
- /// If true, core linking will write the path to each input file to stdout
- /// (i.e. llvm::outs()) as it is used. This is used to implement the -t
- /// linker option.
- ///
- /// \todo This should be a method core linking calls so that drivers can
- /// format the line as needed.
- bool logInputFiles() const { return _logInputFiles; }
-
- /// Parts of LLVM use global variables which are bound to command line
- /// options (see llvm::cl::Options). This method returns "command line"
- /// options which are used to configure LLVM's command line settings.
- /// For instance the -debug-only XXX option can be used to dynamically
- /// trace different parts of LLVM and lld.
- ArrayRef<const char *> llvmOptions() const { return _llvmOptions; }
-
- /// \name Methods used by Drivers to configure TargetInfo
- /// @{
- void setOutputPath(StringRef str) { _outputPath = str; }
-
- // Set the entry symbol name. You may also need to call addDeadStripRoot() for
- // the symbol if your platform supports dead-stripping, so that the symbol
- // will not be removed from the output.
- void setEntrySymbolName(StringRef name) {
- _entrySymbolName = name;
- }
-
- void setDeadStripping(bool enable) { _deadStrip = enable; }
- void setGlobalsAreDeadStripRoots(bool v) { _globalsAreDeadStripRoots = v; }
-
- void setPrintRemainingUndefines(bool print) {
- _printRemainingUndefines = print;
- }
-
- void setAllowRemainingUndefines(bool allow) {
- _allowRemainingUndefines = allow;
- }
-
- void setAllowShlibUndefines(bool allow) { _allowShlibUndefines = allow; }
- void setLogInputFiles(bool log) { _logInputFiles = log; }
-
- void appendLLVMOption(const char *opt) { _llvmOptions.push_back(opt); }
-
- std::vector<std::unique_ptr<Node>> &getNodes() { return _nodes; }
- const std::vector<std::unique_ptr<Node>> &getNodes() const { return _nodes; }
-
- /// This method adds undefined symbols specified by the -u option to the to
- /// the list of undefined symbols known to the linker. This option essentially
- /// forces an undefined symbol to be created. You may also need to call
- /// addDeadStripRoot() for the symbol if your platform supports dead
- /// stripping, so that the symbol will not be removed from the output.
- void addInitialUndefinedSymbol(StringRef symbolName) {
- _initialUndefinedSymbols.push_back(symbolName);
- }
-
- /// Iterators for symbols that appear on the command line.
- typedef std::vector<StringRef> StringRefVector;
- typedef StringRefVector::iterator StringRefVectorIter;
- typedef StringRefVector::const_iterator StringRefVectorConstIter;
-
- /// Create linker internal files containing atoms for the linker to include
- /// during link. Flavors can override this function in their LinkingContext
- /// to add more internal files. These internal files are positioned before
- /// the actual input files.
- virtual void createInternalFiles(std::vector<std::unique_ptr<File>> &) const;
-
- /// Return the list of undefined symbols that are specified in the
- /// linker command line, using the -u option.
- ArrayRef<StringRef> initialUndefinedSymbols() const {
- return _initialUndefinedSymbols;
- }
-
- /// After all set* methods are called, the Driver calls this method
- /// to validate that there are no missing options or invalid combinations
- /// of options. If there is a problem, a description of the problem
- /// is written to the global error handler.
- ///
- /// \returns true if there is an error with the current settings.
- bool validate();
-
- /// Formats symbol name for use in error messages.
- virtual std::string demangle(StringRef symbolName) const = 0;
-
- /// @}
- /// \name Methods used by Driver::link()
- /// @{
-
- /// Returns the file system path to which the linked output should be written.
- ///
- /// \todo To support in-memory linking, we need an abstraction that allows
- /// the linker to write to an in-memory buffer.
- StringRef outputPath() const { return _outputPath; }
-
- /// Accessor for Register object embedded in LinkingContext.
- const Registry ®istry() const { return _registry; }
- Registry ®istry() { return _registry; }
-
- /// This method is called by core linking to give the Writer a chance
- /// to add file format specific "files" to set of files to be linked. This is
- /// how file format specific atoms can be added to the link.
- virtual void createImplicitFiles(std::vector<std::unique_ptr<File>> &) = 0;
-
- /// This method is called by core linking to build the list of Passes to be
- /// run on the merged/linked graph of all input files.
- virtual void addPasses(PassManager &pm) = 0;
-
- /// Calls through to the writeFile() method on the specified Writer.
- ///
- /// \param linkedFile This is the merged/linked graph of all input file Atoms.
- virtual llvm::Error writeFile(const File &linkedFile) const;
-
- /// Return the next ordinal and Increment it.
- virtual uint64_t getNextOrdinalAndIncrement() const { return _nextOrdinal++; }
-
- // This function is called just before the Resolver kicks in.
- // Derived classes may use it to change the list of input files.
- virtual void finalizeInputFiles() = 0;
-
- /// Callback invoked for each file the Resolver decides we are going to load.
- /// This can be used to update context state based on the file, and emit
- /// errors for any differences between the context state and a loaded file.
- /// For example, we can error if we try to load a file which is a different
- /// arch from that being linked.
- virtual llvm::Error handleLoadedFile(File &file) = 0;
-
- /// @}
-protected:
- LinkingContext(); // Must be subclassed
-
- /// Abstract method to lazily instantiate the Writer.
- virtual Writer &writer() const = 0;
-
- /// Method to create an internal file for the entry symbol
- virtual std::unique_ptr<File> createEntrySymbolFile() const;
- std::unique_ptr<File> createEntrySymbolFile(StringRef filename) const;
-
- /// Method to create an internal file for an undefined symbol
- virtual std::unique_ptr<File> createUndefinedSymbolFile() const;
- std::unique_ptr<File> createUndefinedSymbolFile(StringRef filename) const;
-
- StringRef _outputPath;
- StringRef _entrySymbolName;
- bool _deadStrip = false;
- bool _globalsAreDeadStripRoots = false;
- bool _printRemainingUndefines = true;
- bool _allowRemainingUndefines = false;
- bool _logInputFiles = false;
- bool _allowShlibUndefines = false;
- std::vector<StringRef> _deadStripRoots;
- std::vector<const char *> _llvmOptions;
- StringRefVector _initialUndefinedSymbols;
- std::vector<std::unique_ptr<Node>> _nodes;
- mutable llvm::BumpPtrAllocator _allocator;
- mutable uint64_t _nextOrdinal = 0;
- Registry _registry;
-
-private:
- /// Validate the subclass bits. Only called by validate.
- virtual bool validateImpl() = 0;
-};
-
-} // end namespace lld
-
-#endif // LLD_CORE_LINKING_CONTEXT_H
+++ /dev/null
-//===- lld/Core/Node.h - Input file class -----------------------*- C++ -*-===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-///
-/// The classes in this file represents inputs to the linker.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_CORE_NODE_H
-#define LLD_CORE_NODE_H
-
-#include "lld/Core/File.h"
-#include <algorithm>
-#include <memory>
-
-namespace lld {
-
-// A Node represents a FileNode or other type of Node. In the latter case,
-// the node contains meta information about the input file list.
-// Currently only GroupEnd node is defined as a meta node.
-class Node {
-public:
- enum class Kind { File, GroupEnd };
-
- explicit Node(Kind type) : _kind(type) {}
- virtual ~Node() = default;
-
- virtual Kind kind() const { return _kind; }
-
-private:
- Kind _kind;
-};
-
-// This is a marker for --end-group. getSize() returns the number of
-// files between the corresponding --start-group and this marker.
-class GroupEnd : public Node {
-public:
- explicit GroupEnd(int size) : Node(Kind::GroupEnd), _size(size) {}
-
- int getSize() const { return _size; }
-
- static bool classof(const Node *a) {
- return a->kind() == Kind::GroupEnd;
- }
-
-private:
- int _size;
-};
-
-// A container of File.
-class FileNode : public Node {
-public:
- explicit FileNode(std::unique_ptr<File> f)
- : Node(Node::Kind::File), _file(std::move(f)) {}
-
- static bool classof(const Node *a) {
- return a->kind() == Node::Kind::File;
- }
-
- File *getFile() { return _file.get(); }
-
-protected:
- std::unique_ptr<File> _file;
-};
-
-} // end namespace lld
-
-#endif // LLD_CORE_NODE_H
+++ /dev/null
-//===------ Core/Pass.h - Base class for linker passes ----------*- C++ -*-===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_CORE_PASS_H
-#define LLD_CORE_PASS_H
-
-#include "llvm/Support/Error.h"
-
-namespace lld {
-
-class SimpleFile;
-
-/// Once the core linking is done (which resolves references, coalesces atoms
-/// and produces a complete Atom graph), the linker runs a series of passes
-/// on the Atom graph. The graph is modeled as a File, which means the pass
-/// has access to all the atoms and to File level attributes. Each pass does
-/// a particular transformation to the Atom graph or to the File attributes.
-///
-/// This is the abstract base class for all passes. A Pass does its
-/// actual work in it perform() method. It can iterator over Atoms in the
-/// graph using the *begin()/*end() atom iterator of the File. It can add
-/// new Atoms to the graph using the File's addAtom() method.
-class Pass {
-public:
- virtual ~Pass() = default;
-
- /// Do the actual work of the Pass.
- virtual llvm::Error perform(SimpleFile &mergedFile) = 0;
-
-protected:
- // Only subclassess can be instantiated.
- Pass() = default;
-};
-
-} // end namespace lld
-
-#endif // LLD_CORE_PASS_H
+++ /dev/null
-//===- lld/Core/PassManager.h - Manage linker passes ----------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_CORE_PASS_MANAGER_H
-#define LLD_CORE_PASS_MANAGER_H
-
-#include "lld/Common/LLVM.h"
-#include "lld/Core/Pass.h"
-#include "llvm/Support/Error.h"
-#include <memory>
-#include <vector>
-
-namespace lld {
-class SimpleFile;
-class Pass;
-
-/// Owns and runs a collection of passes.
-///
-/// This class is currently just a container for passes and a way to run them.
-///
-/// In the future this should handle timing pass runs, running parallel passes,
-/// and validate/satisfy pass dependencies.
-class PassManager {
-public:
- void add(std::unique_ptr<Pass> pass) {
- _passes.push_back(std::move(pass));
- }
-
- llvm::Error runOnFile(SimpleFile &file) {
- for (std::unique_ptr<Pass> &pass : _passes)
- if (llvm::Error EC = pass->perform(file))
- return EC;
- return llvm::Error::success();
- }
-
-private:
- /// Passes in the order they should run.
- std::vector<std::unique_ptr<Pass>> _passes;
-};
-} // end namespace lld
-
-#endif
+++ /dev/null
-//===- lld/Core/Reader.h - Abstract File Format Reading Interface ---------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_CORE_READER_H
-#define LLD_CORE_READER_H
-
-#include "lld/Common/LLVM.h"
-#include "lld/Core/Reference.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/BinaryFormat/Magic.h"
-#include "llvm/Support/ErrorOr.h"
-#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/MemoryBuffer.h"
-#include <memory>
-#include <vector>
-
-namespace llvm {
-namespace yaml {
-class IO;
-} // end namespace yaml
-} // end namespace llvm
-
-namespace lld {
-
-class File;
-class LinkingContext;
-class MachOLinkingContext;
-
-/// An abstract class for reading object files, library files, and
-/// executable files.
-///
-/// Each file format (e.g. mach-o, etc) has a concrete subclass of Reader.
-class Reader {
-public:
- virtual ~Reader() = default;
-
- /// Sniffs the file to determine if this Reader can parse it.
- /// The method is called with:
- /// 1) the file_magic enumeration returned by identify_magic()
- /// 2) the whole file content buffer if the above is not enough.
- virtual bool canParse(llvm::file_magic magic, MemoryBufferRef mb) const = 0;
-
- /// Parse a supplied buffer (already filled with the contents of a
- /// file) and create a File object.
- /// The resulting File object takes ownership of the MemoryBuffer.
- virtual ErrorOr<std::unique_ptr<File>>
- loadFile(std::unique_ptr<MemoryBuffer> mb, const class Registry &) const = 0;
-};
-
-/// An abstract class for handling alternate yaml representations
-/// of object files.
-///
-/// The YAML syntax allows "tags" which are used to specify the type of
-/// the YAML node. In lld, top level YAML documents can be in many YAML
-/// representations (e.g mach-o encoded as yaml, etc). A tag is used to
-/// specify which representation is used in the following YAML document.
-/// To work, there must be a YamlIOTaggedDocumentHandler registered that
-/// handles each tag type.
-class YamlIOTaggedDocumentHandler {
-public:
- virtual ~YamlIOTaggedDocumentHandler();
-
- /// This method is called on each registered YamlIOTaggedDocumentHandler
- /// until one returns true. If the subclass handles tag type !xyz, then
- /// this method should call io.mapTag("!xzy") to see if that is the current
- /// document type, and if so, process the rest of the document using
- /// YAML I/O, then convert the result into an lld::File* and return it.
- virtual bool handledDocTag(llvm::yaml::IO &io, const lld::File *&f) const = 0;
-};
-
-/// A registry to hold the list of currently registered Readers and
-/// tables which map Reference kind values to strings.
-/// The linker does not directly invoke Readers. Instead, it registers
-/// Readers based on it configuration and command line options, then calls
-/// the Registry object to parse files.
-class Registry {
-public:
- Registry();
-
- /// Walk the list of registered Readers and find one that can parse the
- /// supplied file and parse it.
- ErrorOr<std::unique_ptr<File>>
- loadFile(std::unique_ptr<MemoryBuffer> mb) const;
-
- /// Walk the list of registered kind tables to convert a Reference Kind
- /// name to a value.
- bool referenceKindFromString(StringRef inputStr, Reference::KindNamespace &ns,
- Reference::KindArch &a,
- Reference::KindValue &value) const;
-
- /// Walk the list of registered kind tables to convert a Reference Kind
- /// value to a string.
- bool referenceKindToString(Reference::KindNamespace ns, Reference::KindArch a,
- Reference::KindValue value, StringRef &) const;
-
- /// Walk the list of registered tag handlers and have the one that handles
- /// the current document type process the yaml into an lld::File*.
- bool handleTaggedDoc(llvm::yaml::IO &io, const lld::File *&file) const;
-
- // These methods are called to dynamically add support for various file
- // formats. The methods are also implemented in the appropriate lib*.a
- // library, so that the code for handling a format is only linked in, if this
- // method is used. Any options that a Reader might need must be passed
- // as parameters to the addSupport*() method.
- void addSupportArchives(bool logLoading);
- void addSupportYamlFiles();
- void addSupportMachOObjects(MachOLinkingContext &);
-
- /// To convert between kind values and names, the registry walks the list
- /// of registered kind tables. Each table is a zero terminated array of
- /// KindStrings elements.
- struct KindStrings {
- Reference::KindValue value;
- StringRef name;
- };
-
- /// A Reference Kind value is a tuple of <namespace, arch, value>. All
- /// entries in a conversion table have the same <namespace, arch>. The
- /// array then contains the value/name pairs.
- void addKindTable(Reference::KindNamespace ns, Reference::KindArch arch,
- const KindStrings array[]);
-
-private:
- struct KindEntry {
- Reference::KindNamespace ns;
- Reference::KindArch arch;
- const KindStrings *array;
- };
-
- void add(std::unique_ptr<Reader>);
- void add(std::unique_ptr<YamlIOTaggedDocumentHandler>);
-
- std::vector<std::unique_ptr<Reader>> _readers;
- std::vector<std::unique_ptr<YamlIOTaggedDocumentHandler>> _yamlHandlers;
- std::vector<KindEntry> _kindEntries;
-};
-
-// Utilities for building a KindString table. For instance:
-// static const Registry::KindStrings table[] = {
-// LLD_KIND_STRING_ENTRY(R_VAX_ADDR16),
-// LLD_KIND_STRING_ENTRY(R_VAX_DATA16),
-// LLD_KIND_STRING_END
-// };
-#define LLD_KIND_STRING_ENTRY(name) { name, #name }
-#define LLD_KIND_STRING_END { 0, "" }
-
-} // end namespace lld
-
-#endif // LLD_CORE_READER_H
+++ /dev/null
-//===- Core/References.h - A Reference to Another Atom ----------*- C++ -*-===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_CORE_REFERENCES_H
-#define LLD_CORE_REFERENCES_H
-
-#include <cstdint>
-
-namespace lld {
-
-class Atom;
-
-///
-/// The linker has a Graph Theory model of linking. An object file is seen
-/// as a set of Atoms with References to other Atoms. Each Atom is a node
-/// and each Reference is an edge.
-///
-/// For example if a function contains a call site to "malloc" 40 bytes into
-/// the Atom, then the function Atom will have a Reference of: offsetInAtom=40,
-/// kind=callsite, target=malloc, addend=0.
-///
-/// Besides supporting traditional "relocations", references are also used
-/// forcing layout (one atom must follow another), marking data-in-code
-/// (jump tables or ARM constants), etc.
-///
-/// The "kind" of a reference is a tuple of <namespace, arch, value>. This
-/// enable us to re-use existing relocation types definded for various
-/// file formats and architectures.
-///
-/// References and atoms form a directed graph. The dead-stripping pass
-/// traverses them starting from dead-strip root atoms to garbage collect
-/// unreachable ones.
-///
-/// References of any kind are considered as directed edges. In addition to
-/// that, references of some kind is considered as bidirected edges.
-class Reference {
-public:
- /// Which universe defines the kindValue().
- enum class KindNamespace {
- all = 0,
- testing = 1,
- mach_o = 2,
- };
-
- KindNamespace kindNamespace() const { return (KindNamespace)_kindNamespace; }
- void setKindNamespace(KindNamespace ns) { _kindNamespace = (uint8_t)ns; }
-
- // Which architecture the kind value is for.
- enum class KindArch { all, AArch64, ARM, x86, x86_64};
-
- KindArch kindArch() const { return (KindArch)_kindArch; }
- void setKindArch(KindArch a) { _kindArch = (uint8_t)a; }
-
- typedef uint16_t KindValue;
-
- KindValue kindValue() const { return _kindValue; }
-
- /// setKindValue() is needed because during linking, some optimizations may
- /// change the codegen and hence the reference kind.
- void setKindValue(KindValue value) {
- _kindValue = value;
- }
-
- /// KindValues used with KindNamespace::all and KindArch::all.
- enum {
- // kindLayoutAfter is treated as a bidirected edge by the dead-stripping
- // pass.
- kindLayoutAfter = 1,
- kindAssociate,
- };
-
- // A value to be added to the value of a target
- typedef int64_t Addend;
-
- /// If the reference is a fixup in the Atom, then this returns the
- /// byte offset into the Atom's content to do the fix up.
- virtual uint64_t offsetInAtom() const = 0;
-
- /// Returns the atom this reference refers to.
- virtual const Atom *target() const = 0;
-
- /// During linking, the linker may merge graphs which coalesces some nodes
- /// (i.e. Atoms). To switch the target of a reference, this method is called.
- virtual void setTarget(const Atom *) = 0;
-
- /// Some relocations require a symbol and a value (e.g. foo + 4).
- virtual Addend addend() const = 0;
-
- /// During linking, some optimizations may change addend value.
- virtual void setAddend(Addend) = 0;
-
- /// Returns target specific attributes of the reference.
- virtual uint32_t tag() const { return 0; }
-
-protected:
- /// Reference is an abstract base class. Only subclasses can use constructor.
- Reference(KindNamespace ns, KindArch a, KindValue value)
- : _kindValue(value), _kindNamespace((uint8_t)ns), _kindArch((uint8_t)a) {}
-
- /// The memory for Reference objects is always managed by the owning File
- /// object. Therefore, no one but the owning File object should call
- /// delete on a Reference. In fact, some File objects may bulk allocate
- /// an array of References, so they cannot be individually deleted by anyone.
- virtual ~Reference() = default;
-
- KindValue _kindValue;
- uint8_t _kindNamespace;
- uint8_t _kindArch;
-};
-
-} // end namespace lld
-
-#endif // LLD_CORE_REFERENCES_H
+++ /dev/null
-//===- Core/Resolver.h - Resolves Atom References -------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_CORE_RESOLVER_H
-#define LLD_CORE_RESOLVER_H
-
-#include "lld/Core/ArchiveLibraryFile.h"
-#include "lld/Core/File.h"
-#include "lld/Core/SharedLibraryFile.h"
-#include "lld/Core/Simple.h"
-#include "lld/Core/SymbolTable.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/DenseSet.h"
-#include "llvm/Support/ErrorOr.h"
-#include <set>
-#include <unordered_map>
-#include <unordered_set>
-#include <vector>
-
-namespace lld {
-
-class Atom;
-class LinkingContext;
-
-/// The Resolver is responsible for merging all input object files
-/// and producing a merged graph.
-class Resolver {
-public:
- Resolver(LinkingContext &ctx) : _ctx(ctx), _result(new MergedFile()) {}
-
- // InputFiles::Handler methods
- void doDefinedAtom(OwningAtomPtr<DefinedAtom> atom);
- bool doUndefinedAtom(OwningAtomPtr<UndefinedAtom> atom);
- void doSharedLibraryAtom(OwningAtomPtr<SharedLibraryAtom> atom);
- void doAbsoluteAtom(OwningAtomPtr<AbsoluteAtom> atom);
-
- // Handle files, this adds atoms from the current file thats
- // being processed by the resolver
- llvm::Expected<bool> handleFile(File &);
-
- // Handle an archive library file.
- llvm::Expected<bool> handleArchiveFile(File &);
-
- // Handle a shared library file.
- llvm::Error handleSharedLibrary(File &);
-
- /// do work of merging and resolving and return list
- bool resolve();
-
- std::unique_ptr<SimpleFile> resultFile() { return std::move(_result); }
-
-private:
- typedef std::function<llvm::Expected<bool>(StringRef)> UndefCallback;
-
- bool undefinesAdded(int begin, int end);
- File *getFile(int &index);
-
- /// The main function that iterates over the files to resolve
- bool resolveUndefines();
- void updateReferences();
- void deadStripOptimize();
- bool checkUndefines();
- void removeCoalescedAwayAtoms();
- llvm::Expected<bool> forEachUndefines(File &file, UndefCallback callback);
-
- void markLive(const Atom *atom);
-
- class MergedFile : public SimpleFile {
- public:
- MergedFile() : SimpleFile("<linker-internal>", kindResolverMergedObject) {}
- void addAtoms(llvm::MutableArrayRef<OwningAtomPtr<Atom>> atoms);
- };
-
- LinkingContext &_ctx;
- SymbolTable _symbolTable;
- std::vector<OwningAtomPtr<Atom>> _atoms;
- std::set<const Atom *> _deadStripRoots;
- llvm::DenseSet<const Atom *> _liveAtoms;
- llvm::DenseSet<const Atom *> _deadAtoms;
- std::unique_ptr<MergedFile> _result;
- std::unordered_multimap<const Atom *, const Atom *> _reverseRef;
-
- // --start-group and --end-group
- std::vector<File *> _files;
- std::map<File *, bool> _newUndefinesAdded;
-
- // List of undefined symbols.
- std::vector<StringRef> _undefines;
-
- // Start position in _undefines for each archive/shared library file.
- // Symbols from index 0 to the start position are already searched before.
- // Searching them again would never succeed. When we look for undefined
- // symbols from an archive/shared library file, start from its start
- // position to save time.
- std::map<File *, size_t> _undefineIndex;
-};
-
-} // namespace lld
-
-#endif // LLD_CORE_RESOLVER_H
+++ /dev/null
-//===- Core/SharedLibraryAtom.h - A Shared Library Atom -------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_CORE_SHARED_LIBRARY_ATOM_H
-#define LLD_CORE_SHARED_LIBRARY_ATOM_H
-
-#include "lld/Core/Atom.h"
-
-namespace lld {
-
-/// A SharedLibraryAtom has no content.
-/// It exists to represent a symbol which will be bound at runtime.
-class SharedLibraryAtom : public Atom {
-public:
- enum class Type : uint32_t {
- Unknown,
- Code,
- Data,
- };
-
- /// Returns shared library name used to load it at runtime.
- /// On Darwin it is the LC_DYLIB_LOAD dylib name.
- virtual StringRef loadName() const = 0;
-
- /// Returns if shared library symbol can be missing at runtime and if
- /// so the loader should silently resolve address of symbol to be nullptr.
- virtual bool canBeNullAtRuntime() const = 0;
-
- virtual Type type() const = 0;
-
- virtual uint64_t size() const = 0;
-
- static bool classof(const Atom *a) {
- return a->definition() == definitionSharedLibrary;
- }
-
- static inline bool classof(const SharedLibraryAtom *) { return true; }
-
-protected:
- SharedLibraryAtom() : Atom(definitionSharedLibrary) {}
-
- ~SharedLibraryAtom() override = default;
-};
-
-} // namespace lld
-
-#endif // LLD_CORE_SHARED_LIBRARY_ATOM_H
+++ /dev/null
-//===- Core/SharedLibraryFile.h - Models shared libraries as Atoms --------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_CORE_SHARED_LIBRARY_FILE_H
-#define LLD_CORE_SHARED_LIBRARY_FILE_H
-
-#include "lld/Core/File.h"
-
-namespace lld {
-
-///
-/// The SharedLibraryFile subclass of File is used to represent dynamic
-/// shared libraries being linked against.
-///
-class SharedLibraryFile : public File {
-public:
- static bool classof(const File *f) {
- return f->kind() == kindSharedLibrary;
- }
-
- /// Check if the shared library exports a symbol with the specified name.
- /// If so, return a SharedLibraryAtom which represents that exported
- /// symbol. Otherwise return nullptr.
- virtual OwningAtomPtr<SharedLibraryAtom> exports(StringRef name) const = 0;
-
- // Returns the install name.
- virtual StringRef getDSOName() const = 0;
-
- const AtomRange<DefinedAtom> defined() const override {
- return _definedAtoms;
- }
-
- const AtomRange<UndefinedAtom> undefined() const override {
- return _undefinedAtoms;
- }
-
- const AtomRange<SharedLibraryAtom> sharedLibrary() const override {
- return _sharedLibraryAtoms;
- }
-
- const AtomRange<AbsoluteAtom> absolute() const override {
- return _absoluteAtoms;
- }
-
- void clearAtoms() override {
- _definedAtoms.clear();
- _undefinedAtoms.clear();
- _sharedLibraryAtoms.clear();
- _absoluteAtoms.clear();
- }
-
-protected:
- /// only subclasses of SharedLibraryFile can be instantiated
- explicit SharedLibraryFile(StringRef path) : File(path, kindSharedLibrary) {}
-
- AtomVector<DefinedAtom> _definedAtoms;
- AtomVector<UndefinedAtom> _undefinedAtoms;
- AtomVector<SharedLibraryAtom> _sharedLibraryAtoms;
- AtomVector<AbsoluteAtom> _absoluteAtoms;
-};
-
-} // namespace lld
-
-#endif // LLD_CORE_SHARED_LIBRARY_FILE_H
+++ /dev/null
-//===- lld/Core/Simple.h - Simple implementations of Atom and File --------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// Provide simple implementations for Atoms and File.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_CORE_SIMPLE_H
-#define LLD_CORE_SIMPLE_H
-
-#include "lld/Core/AbsoluteAtom.h"
-#include "lld/Core/Atom.h"
-#include "lld/Core/DefinedAtom.h"
-#include "lld/Core/File.h"
-#include "lld/Core/Reference.h"
-#include "lld/Core/SharedLibraryAtom.h"
-#include "lld/Core/UndefinedAtom.h"
-#include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/ADT/ilist.h"
-#include "llvm/ADT/ilist_node.h"
-#include "llvm/Support/Allocator.h"
-#include "llvm/Support/Casting.h"
-#include "llvm/Support/ErrorHandling.h"
-#include <algorithm>
-#include <cassert>
-#include <cstdint>
-#include <functional>
-
-namespace lld {
-
-class SimpleFile : public File {
-public:
- SimpleFile(StringRef path, File::Kind kind)
- : File(path, kind) {}
-
- ~SimpleFile() override {
- _defined.clear();
- _undefined.clear();
- _shared.clear();
- _absolute.clear();
- }
-
- void addAtom(DefinedAtom &a) {
- _defined.push_back(OwningAtomPtr<DefinedAtom>(&a));
- }
- void addAtom(UndefinedAtom &a) {
- _undefined.push_back(OwningAtomPtr<UndefinedAtom>(&a));
- }
- void addAtom(SharedLibraryAtom &a) {
- _shared.push_back(OwningAtomPtr<SharedLibraryAtom>(&a));
- }
- void addAtom(AbsoluteAtom &a) {
- _absolute.push_back(OwningAtomPtr<AbsoluteAtom>(&a));
- }
-
- void addAtom(const Atom &atom) {
- if (auto *p = dyn_cast<DefinedAtom>(&atom)) {
- addAtom(const_cast<DefinedAtom &>(*p));
- } else if (auto *p = dyn_cast<UndefinedAtom>(&atom)) {
- addAtom(const_cast<UndefinedAtom &>(*p));
- } else if (auto *p = dyn_cast<SharedLibraryAtom>(&atom)) {
- addAtom(const_cast<SharedLibraryAtom &>(*p));
- } else if (auto *p = dyn_cast<AbsoluteAtom>(&atom)) {
- addAtom(const_cast<AbsoluteAtom &>(*p));
- } else {
- llvm_unreachable("atom has unknown definition kind");
- }
- }
-
- void removeDefinedAtomsIf(std::function<bool(const DefinedAtom *)> pred) {
- auto &atoms = _defined;
- auto newEnd = std::remove_if(atoms.begin(), atoms.end(),
- [&pred](OwningAtomPtr<DefinedAtom> &p) {
- return pred(p.get());
- });
- atoms.erase(newEnd, atoms.end());
- }
-
- const AtomRange<DefinedAtom> defined() const override { return _defined; }
-
- const AtomRange<UndefinedAtom> undefined() const override {
- return _undefined;
- }
-
- const AtomRange<SharedLibraryAtom> sharedLibrary() const override {
- return _shared;
- }
-
- const AtomRange<AbsoluteAtom> absolute() const override {
- return _absolute;
- }
-
- void clearAtoms() override {
- _defined.clear();
- _undefined.clear();
- _shared.clear();
- _absolute.clear();
- }
-
-private:
- AtomVector<DefinedAtom> _defined;
- AtomVector<UndefinedAtom> _undefined;
- AtomVector<SharedLibraryAtom> _shared;
- AtomVector<AbsoluteAtom> _absolute;
-};
-
-class SimpleReference : public Reference,
- public llvm::ilist_node<SimpleReference> {
-public:
- SimpleReference(Reference::KindNamespace ns, Reference::KindArch arch,
- Reference::KindValue value, uint64_t off, const Atom *t,
- Reference::Addend a)
- : Reference(ns, arch, value), _target(t), _offsetInAtom(off), _addend(a) {
- }
- SimpleReference()
- : Reference(Reference::KindNamespace::all, Reference::KindArch::all, 0),
- _target(nullptr), _offsetInAtom(0), _addend(0) {}
-
- uint64_t offsetInAtom() const override { return _offsetInAtom; }
-
- const Atom *target() const override {
- assert(_target);
- return _target;
- }
-
- Addend addend() const override { return _addend; }
- void setAddend(Addend a) override { _addend = a; }
- void setTarget(const Atom *newAtom) override { _target = newAtom; }
-
-private:
- const Atom *_target;
- uint64_t _offsetInAtom;
- Addend _addend;
-};
-
-class SimpleDefinedAtom : public DefinedAtom {
-public:
- explicit SimpleDefinedAtom(const File &f)
- : _file(f), _ordinal(f.getNextAtomOrdinalAndIncrement()) {}
-
- ~SimpleDefinedAtom() override {
- _references.clearAndLeakNodesUnsafely();
- }
-
- const File &file() const override { return _file; }
-
- StringRef name() const override { return StringRef(); }
-
- uint64_t ordinal() const override { return _ordinal; }
-
- Scope scope() const override { return DefinedAtom::scopeLinkageUnit; }
-
- Interposable interposable() const override {
- return DefinedAtom::interposeNo;
- }
-
- Merge merge() const override { return DefinedAtom::mergeNo; }
-
- Alignment alignment() const override { return 1; }
-
- SectionChoice sectionChoice() const override {
- return DefinedAtom::sectionBasedOnContent;
- }
-
- StringRef customSectionName() const override { return StringRef(); }
- DeadStripKind deadStrip() const override {
- return DefinedAtom::deadStripNormal;
- }
-
- DefinedAtom::reference_iterator begin() const override {
- const void *it =
- reinterpret_cast<const void *>(_references.begin().getNodePtr());
- return reference_iterator(*this, it);
- }
-
- DefinedAtom::reference_iterator end() const override {
- const void *it =
- reinterpret_cast<const void *>(_references.end().getNodePtr());
- return reference_iterator(*this, it);
- }
-
- const Reference *derefIterator(const void *it) const override {
- return &*RefList::const_iterator(
- *reinterpret_cast<const llvm::ilist_node<SimpleReference> *>(it));
- }
-
- void incrementIterator(const void *&it) const override {
- RefList::const_iterator ref(
- *reinterpret_cast<const llvm::ilist_node<SimpleReference> *>(it));
- it = reinterpret_cast<const void *>(std::next(ref).getNodePtr());
- }
-
- void addReference(Reference::KindNamespace ns,
- Reference::KindArch arch,
- Reference::KindValue kindValue, uint64_t off,
- const Atom *target, Reference::Addend a) override {
- assert(target && "trying to create reference to nothing");
- auto node = new (_file.allocator())
- SimpleReference(ns, arch, kindValue, off, target, a);
- _references.push_back(node);
- }
-
- /// Sort references in a canonical order (by offset, then by kind).
- void sortReferences() const {
- // Cannot sort a linked list, so move elements into a temporary vector,
- // sort the vector, then reconstruct the list.
- llvm::SmallVector<SimpleReference *, 16> elements;
- for (SimpleReference &node : _references) {
- elements.push_back(&node);
- }
- std::sort(elements.begin(), elements.end(),
- [] (const SimpleReference *lhs, const SimpleReference *rhs) -> bool {
- uint64_t lhsOffset = lhs->offsetInAtom();
- uint64_t rhsOffset = rhs->offsetInAtom();
- if (rhsOffset != lhsOffset)
- return (lhsOffset < rhsOffset);
- if (rhs->kindNamespace() != lhs->kindNamespace())
- return (lhs->kindNamespace() < rhs->kindNamespace());
- if (rhs->kindArch() != lhs->kindArch())
- return (lhs->kindArch() < rhs->kindArch());
- return (lhs->kindValue() < rhs->kindValue());
- });
- _references.clearAndLeakNodesUnsafely();
- for (SimpleReference *node : elements) {
- _references.push_back(node);
- }
- }
-
- void setOrdinal(uint64_t ord) { _ordinal = ord; }
-
-private:
- typedef llvm::ilist<SimpleReference> RefList;
-
- const File &_file;
- uint64_t _ordinal;
- mutable RefList _references;
-};
-
-class SimpleUndefinedAtom : public UndefinedAtom {
-public:
- SimpleUndefinedAtom(const File &f, StringRef name) : _file(f), _name(name) {
- assert(!name.empty() && "UndefinedAtoms must have a name");
- }
-
- ~SimpleUndefinedAtom() override = default;
-
- /// file - returns the File that produced/owns this Atom
- const File &file() const override { return _file; }
-
- /// name - The name of the atom. For a function atom, it is the (mangled)
- /// name of the function.
- StringRef name() const override { return _name; }
-
- CanBeNull canBeNull() const override { return UndefinedAtom::canBeNullNever; }
-
-private:
- const File &_file;
- StringRef _name;
-};
-
-} // end namespace lld
-
-#endif // LLD_CORE_SIMPLE_H
+++ /dev/null
-//===- Core/SymbolTable.h - Main Symbol Table -----------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_CORE_SYMBOL_TABLE_H
-#define LLD_CORE_SYMBOL_TABLE_H
-
-#include "lld/Common/LLVM.h"
-#include "llvm/ADT/DenseSet.h"
-#include "llvm/Support/DJB.h"
-#include <cstring>
-#include <map>
-#include <vector>
-
-namespace lld {
-
-class AbsoluteAtom;
-class Atom;
-class DefinedAtom;
-class LinkingContext;
-class ResolverOptions;
-class SharedLibraryAtom;
-class UndefinedAtom;
-
-/// The SymbolTable class is responsible for coalescing atoms.
-///
-/// All atoms coalescable by-name or by-content should be added.
-/// The method replacement() can be used to find the replacement atom
-/// if an atom has been coalesced away.
-class SymbolTable {
-public:
- /// add atom to symbol table
- bool add(const DefinedAtom &);
-
- /// add atom to symbol table
- bool add(const UndefinedAtom &);
-
- /// add atom to symbol table
- bool add(const SharedLibraryAtom &);
-
- /// add atom to symbol table
- bool add(const AbsoluteAtom &);
-
- /// returns atom in symbol table for specified name (or nullptr)
- const Atom *findByName(StringRef sym);
-
- /// returns vector of remaining UndefinedAtoms
- std::vector<const UndefinedAtom *> undefines();
-
- /// if atom has been coalesced away, return replacement, else return atom
- const Atom *replacement(const Atom *);
-
- /// if atom has been coalesced away, return true
- bool isCoalescedAway(const Atom *);
-
-private:
- typedef llvm::DenseMap<const Atom *, const Atom *> AtomToAtom;
-
- struct StringRefMappingInfo {
- static StringRef getEmptyKey() { return StringRef(); }
- static StringRef getTombstoneKey() { return StringRef(" ", 1); }
- static unsigned getHashValue(StringRef const val) {
- return llvm::djbHash(val, 0);
- }
- static bool isEqual(StringRef const lhs, StringRef const rhs) {
- return lhs.equals(rhs);
- }
- };
- typedef llvm::DenseMap<StringRef, const Atom *,
- StringRefMappingInfo> NameToAtom;
-
- struct AtomMappingInfo {
- static const DefinedAtom * getEmptyKey() { return nullptr; }
- static const DefinedAtom * getTombstoneKey() { return (DefinedAtom*)(-1); }
- static unsigned getHashValue(const DefinedAtom * const Val);
- static bool isEqual(const DefinedAtom * const LHS,
- const DefinedAtom * const RHS);
- };
- typedef llvm::DenseSet<const DefinedAtom*, AtomMappingInfo> AtomContentSet;
-
- bool addByName(const Atom &);
- bool addByContent(const DefinedAtom &);
-
- AtomToAtom _replacedAtoms;
- NameToAtom _nameTable;
- AtomContentSet _contentTable;
-};
-
-} // namespace lld
-
-#endif // LLD_CORE_SYMBOL_TABLE_H
+++ /dev/null
-//===- Core/UndefinedAtom.h - An Undefined Atom ---------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_CORE_UNDEFINED_ATOM_H
-#define LLD_CORE_UNDEFINED_ATOM_H
-
-#include "lld/Core/Atom.h"
-
-namespace lld {
-
-/// An UndefinedAtom has no content.
-/// It exists as a placeholder for a future atom.
-class UndefinedAtom : public Atom {
-public:
- /// Whether this undefined symbol needs to be resolved,
- /// or whether it can just evaluate to nullptr.
- /// This concept is often called "weak", but that term
- /// is overloaded to mean other things too.
- enum CanBeNull {
- /// Normal symbols must be resolved at build time
- canBeNullNever,
-
- /// This symbol can be missing at runtime and will evaluate to nullptr.
- /// That is, the static linker still must find a definition (usually
- /// is some shared library), but at runtime, the dynamic loader
- /// will allow the symbol to be missing and resolved to nullptr.
- ///
- /// On Darwin this is generated using a function prototype with
- /// __attribute__((weak_import)).
- /// On linux this is generated using a function prototype with
- /// __attribute__((weak)).
- /// On Windows this feature is not supported.
- canBeNullAtRuntime,
-
- /// This symbol can be missing at build time.
- /// That is, the static linker will not error if a definition for
- /// this symbol is not found at build time. Instead, the linker
- /// will build an executable that lets the dynamic loader find the
- /// symbol at runtime.
- /// This feature is not supported on Darwin nor Windows.
- /// On linux this is generated using a function prototype with
- /// __attribute__((weak)).
- canBeNullAtBuildtime
- };
-
- virtual CanBeNull canBeNull() const = 0;
-
- static bool classof(const Atom *a) {
- return a->definition() == definitionUndefined;
- }
-
- static bool classof(const UndefinedAtom *) { return true; }
-
-protected:
- UndefinedAtom() : Atom(definitionUndefined) {}
-
- ~UndefinedAtom() override = default;
-};
-
-} // namespace lld
-
-#endif // LLD_CORE_UNDEFINED_ATOM_H
+++ /dev/null
-//===- lld/Core/Writer.h - Abstract File Format Interface -----------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_CORE_WRITER_H
-#define LLD_CORE_WRITER_H
-
-#include "lld/Common/LLVM.h"
-#include "llvm/Support/Error.h"
-#include <memory>
-#include <vector>
-
-namespace lld {
-class File;
-class LinkingContext;
-class MachOLinkingContext;
-
-/// The Writer is an abstract class for writing object files, shared
-/// library files, and executable files. Each file format (e.g. mach-o, etc)
-/// has a concrete subclass of Writer.
-class Writer {
-public:
- virtual ~Writer();
-
- /// Write a file from the supplied File object
- virtual llvm::Error writeFile(const File &linkedFile, StringRef path) = 0;
-
- /// This method is called by Core Linking to give the Writer a chance
- /// to add file format specific "files" to set of files to be linked. This is
- /// how file format specific atoms can be added to the link.
- virtual void createImplicitFiles(std::vector<std::unique_ptr<File>> &) {}
-
-protected:
- // only concrete subclasses can be instantiated
- Writer();
-};
-
-std::unique_ptr<Writer> createWriterMachO(const MachOLinkingContext &);
-std::unique_ptr<Writer> createWriterYAML(const LinkingContext &);
-} // end namespace lld
-
-#endif
+++ /dev/null
-//===- lld/ReaderWriter/MachOLinkingContext.h -----------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_READER_WRITER_MACHO_LINKING_CONTEXT_H
-#define LLD_READER_WRITER_MACHO_LINKING_CONTEXT_H
-
-#include "lld/Core/LinkingContext.h"
-#include "lld/Core/Reader.h"
-#include "lld/Core/Writer.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/StringMap.h"
-#include "llvm/ADT/StringSet.h"
-#include "llvm/BinaryFormat/MachO.h"
-#include "llvm/Support/ErrorHandling.h"
-#include <set>
-
-using llvm::MachO::HeaderFileType;
-
-namespace lld {
-
-namespace mach_o {
-class ArchHandler;
-class MachODylibFile;
-class MachOFile;
-class SectCreateFile;
-}
-
-class MachOLinkingContext : public LinkingContext {
-public:
- MachOLinkingContext();
- ~MachOLinkingContext() override;
-
- enum Arch {
- arch_unknown,
- arch_ppc,
- arch_x86,
- arch_x86_64,
- arch_armv6,
- arch_armv7,
- arch_armv7s,
- arch_arm64,
- };
-
- enum class OS {
- unknown,
- macOSX,
- iOS,
- iOS_simulator
- };
-
- enum class ExportMode {
- globals, // Default, all global symbols exported.
- exported, // -exported_symbol[s_list], only listed symbols exported.
- unexported // -unexported_symbol[s_list], no listed symbol exported.
- };
-
- enum class DebugInfoMode {
- addDebugMap, // Default
- noDebugMap // -S option
- };
-
- enum class UndefinedMode {
- error,
- warning,
- suppress,
- dynamicLookup
- };
-
- enum ObjCConstraint {
- objc_unknown = 0,
- objc_supports_gc = 2,
- objc_gc_only = 4,
- // Image optimized by dyld = 8
- // GC compaction = 16
- objc_retainReleaseForSimulator = 32,
- objc_retainRelease
- };
-
- /// Initializes the context to sane default values given the specified output
- /// file type, arch, os, and minimum os version. This should be called before
- /// other setXXX() methods.
- void configure(HeaderFileType type, Arch arch, OS os, uint32_t minOSVersion,
- bool exportDynamicSymbols);
-
- void addPasses(PassManager &pm) override;
- bool validateImpl() override;
- std::string demangle(StringRef symbolName) const override;
-
- void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override;
-
- /// Creates a new file which is owned by the context. Returns a pointer to
- /// the new file.
- template <class T, class... Args>
- typename std::enable_if<!std::is_array<T>::value, T *>::type
- make_file(Args &&... args) const {
- auto file = std::unique_ptr<T>(new T(std::forward<Args>(args)...));
- auto *filePtr = file.get();
- auto *ctx = const_cast<MachOLinkingContext *>(this);
- ctx->getNodes().push_back(std::make_unique<FileNode>(std::move(file)));
- return filePtr;
- }
-
- uint32_t getCPUType() const;
- uint32_t getCPUSubType() const;
-
- bool addEntryPointLoadCommand() const;
- bool addUnixThreadLoadCommand() const;
- bool outputTypeHasEntry() const;
- bool is64Bit() const;
-
- virtual uint64_t pageZeroSize() const { return _pageZeroSize; }
- virtual uint64_t pageSize() const { return _pageSize; }
-
- mach_o::ArchHandler &archHandler() const;
-
- HeaderFileType outputMachOType() const { return _outputMachOType; }
-
- Arch arch() const { return _arch; }
- StringRef archName() const { return nameFromArch(_arch); }
- OS os() const { return _os; }
-
- ExportMode exportMode() const { return _exportMode; }
- void setExportMode(ExportMode mode) { _exportMode = mode; }
- void addExportSymbol(StringRef sym);
- bool exportRestrictMode() const { return _exportMode != ExportMode::globals; }
- bool exportSymbolNamed(StringRef sym) const;
-
- DebugInfoMode debugInfoMode() const { return _debugInfoMode; }
- void setDebugInfoMode(DebugInfoMode mode) {
- _debugInfoMode = mode;
- }
-
- void appendOrderedSymbol(StringRef symbol, StringRef filename);
-
- bool keepPrivateExterns() const { return _keepPrivateExterns; }
- void setKeepPrivateExterns(bool v) { _keepPrivateExterns = v; }
- bool demangleSymbols() const { return _demangle; }
- void setDemangleSymbols(bool d) { _demangle = d; }
- bool mergeObjCCategories() const { return _mergeObjCCategories; }
- void setMergeObjCCategories(bool v) { _mergeObjCCategories = v; }
- /// Create file at specified path which will contain a binary encoding
- /// of all input and output file paths.
- std::error_code createDependencyFile(StringRef path);
- void addInputFileDependency(StringRef path) const;
- void addInputFileNotFound(StringRef path) const;
- void addOutputFileDependency(StringRef path) const;
-
- bool minOS(StringRef mac, StringRef iOS) const;
- void setDoNothing(bool value) { _doNothing = value; }
- bool doNothing() const { return _doNothing; }
- bool printAtoms() const { return _printAtoms; }
- bool testingFileUsage() const { return _testingFileUsage; }
- const StringRefVector &searchDirs() const { return _searchDirs; }
- const StringRefVector &frameworkDirs() const { return _frameworkDirs; }
- void setSysLibRoots(const StringRefVector &paths);
- const StringRefVector &sysLibRoots() const { return _syslibRoots; }
- bool PIE() const { return _pie; }
- void setPIE(bool pie) { _pie = pie; }
- bool generateVersionLoadCommand() const {
- return _generateVersionLoadCommand;
- }
- void setGenerateVersionLoadCommand(bool v) {
- _generateVersionLoadCommand = v;
- }
-
- bool generateFunctionStartsLoadCommand() const {
- return _generateFunctionStartsLoadCommand;
- }
- void setGenerateFunctionStartsLoadCommand(bool v) {
- _generateFunctionStartsLoadCommand = v;
- }
-
- bool generateDataInCodeLoadCommand() const {
- return _generateDataInCodeLoadCommand;
- }
- void setGenerateDataInCodeLoadCommand(bool v) {
- _generateDataInCodeLoadCommand = v;
- }
-
- uint64_t stackSize() const { return _stackSize; }
- void setStackSize(uint64_t stackSize) { _stackSize = stackSize; }
-
- uint64_t baseAddress() const { return _baseAddress; }
- void setBaseAddress(uint64_t baseAddress) { _baseAddress = baseAddress; }
-
- ObjCConstraint objcConstraint() const { return _objcConstraint; }
-
- uint32_t osMinVersion() const { return _osMinVersion; }
-
- uint32_t sdkVersion() const { return _sdkVersion; }
- void setSdkVersion(uint64_t v) { _sdkVersion = v; }
-
- uint64_t sourceVersion() const { return _sourceVersion; }
- void setSourceVersion(uint64_t v) { _sourceVersion = v; }
-
- uint32_t swiftVersion() const { return _swiftVersion; }
-
- /// Checks whether a given path on the filesystem exists.
- ///
- /// When running in -test_file_usage mode, this method consults an
- /// internally maintained list of files that exist (provided by -path_exists)
- /// instead of the actual filesystem.
- bool pathExists(StringRef path) const;
-
- /// Like pathExists() but only used on files - not directories.
- bool fileExists(StringRef path) const;
-
- /// Adds any library search paths derived from the given base, possibly
- /// modified by -syslibroots.
- ///
- /// The set of paths added consists of approximately all syslibroot-prepended
- /// versions of libPath that exist, or the original libPath if there are none
- /// for whatever reason. With various edge-cases for compatibility.
- void addModifiedSearchDir(StringRef libPath, bool isSystemPath = false);
-
- /// Determine whether -lFoo can be resolve within the given path, and
- /// return the filename if so.
- ///
- /// The -lFoo option is documented to search for libFoo.dylib and libFoo.a in
- /// that order, unless Foo ends in ".o", in which case only the exact file
- /// matches (e.g. -lfoo.o would only find foo.o).
- llvm::Optional<StringRef> searchDirForLibrary(StringRef path,
- StringRef libName) const;
-
- /// Iterates through all search path entries looking for libName (as
- /// specified by -lFoo).
- llvm::Optional<StringRef> searchLibrary(StringRef libName) const;
-
- /// Add a framework search path. Internally, this method may be prepended
- /// the path with syslibroot.
- void addFrameworkSearchDir(StringRef fwPath, bool isSystemPath = false);
-
- /// Iterates through all framework directories looking for
- /// Foo.framework/Foo (when fwName = "Foo").
- llvm::Optional<StringRef> findPathForFramework(StringRef fwName) const;
-
- /// The dylib's binary compatibility version, in the raw uint32 format.
- ///
- /// When building a dynamic library, this is the compatibility version that
- /// gets embedded into the result. Other Mach-O binaries that link against
- /// this library will store the compatibility version in its load command. At
- /// runtime, the loader will verify that the binary is compatible with the
- /// installed dynamic library.
- uint32_t compatibilityVersion() const { return _compatibilityVersion; }
-
- /// The dylib's current version, in the raw uint32 format.
- ///
- /// When building a dynamic library, this is the current version that gets
- /// embedded into the result. Other Mach-O binaries that link against
- /// this library will store the compatibility version in its load command.
- uint32_t currentVersion() const { return _currentVersion; }
-
- /// The dylib's install name.
- ///
- /// Binaries that link against the dylib will embed this path into the dylib
- /// load command. When loading the binaries at runtime, this is the location
- /// on disk that the loader will look for the dylib.
- StringRef installName() const { return _installName; }
-
- /// Whether or not the dylib has side effects during initialization.
- ///
- /// Dylibs marked as being dead strippable provide the guarantee that loading
- /// the dylib has no side effects, allowing the linker to strip out the dylib
- /// when linking a binary that does not use any of its symbols.
- bool deadStrippableDylib() const { return _deadStrippableDylib; }
-
- /// Whether or not to use flat namespace.
- ///
- /// MachO usually uses a two-level namespace, where each external symbol
- /// referenced by the target is associated with the dylib that will provide
- /// the symbol's definition at runtime. Using flat namespace overrides this
- /// behavior: the linker searches all dylibs on the command line and all
- /// dylibs those original dylibs depend on, but does not record which dylib
- /// an external symbol came from. At runtime dyld again searches all images
- /// and uses the first definition it finds. In addition, any undefines in
- /// loaded flat_namespace dylibs must be resolvable at build time.
- bool useFlatNamespace() const { return _flatNamespace; }
-
- /// How to handle undefined symbols.
- ///
- /// Options are:
- /// * error: Report an error and terminate linking.
- /// * warning: Report a warning, but continue linking.
- /// * suppress: Ignore and continue linking.
- /// * dynamic_lookup: For use with -twolevel namespace: Records source dylibs
- /// for symbols that are defined in a linked dylib at static link time.
- /// Undefined symbols are handled by searching all loaded images at
- /// runtime.
- UndefinedMode undefinedMode() const { return _undefinedMode; }
-
- /// The path to the executable that will load the bundle at runtime.
- ///
- /// When building a Mach-O bundle, this executable will be examined if there
- /// are undefined symbols after the main link phase. It is expected that this
- /// binary will be loading the bundle at runtime and will provide the symbols
- /// at that point.
- StringRef bundleLoader() const { return _bundleLoader; }
-
- void setCompatibilityVersion(uint32_t vers) { _compatibilityVersion = vers; }
- void setCurrentVersion(uint32_t vers) { _currentVersion = vers; }
- void setInstallName(StringRef name) { _installName = name; }
- void setDeadStrippableDylib(bool deadStrippable) {
- _deadStrippableDylib = deadStrippable;
- }
- void setUseFlatNamespace(bool flatNamespace) {
- _flatNamespace = flatNamespace;
- }
-
- void setUndefinedMode(UndefinedMode undefinedMode) {
- _undefinedMode = undefinedMode;
- }
-
- void setBundleLoader(StringRef loader) { _bundleLoader = loader; }
- void setPrintAtoms(bool value=true) { _printAtoms = value; }
- void setTestingFileUsage(bool value = true) {
- _testingFileUsage = value;
- }
- void addExistingPathForDebug(StringRef path) {
- _existingPaths.insert(path);
- }
-
- void addRpath(StringRef rpath);
- const StringRefVector &rpaths() const { return _rpaths; }
-
- /// Add section alignment constraint on final layout.
- void addSectionAlignment(StringRef seg, StringRef sect, uint16_t align);
-
- /// Add a section based on a command-line sectcreate option.
- void addSectCreateSection(StringRef seg, StringRef sect,
- std::unique_ptr<MemoryBuffer> content);
-
- /// Returns true if specified section had alignment constraints.
- bool sectionAligned(StringRef seg, StringRef sect, uint16_t &align) const;
-
- StringRef dyldPath() const { return "/usr/lib/dyld"; }
-
- /// Stub creation Pass should be run.
- bool needsStubsPass() const;
-
- // GOT creation Pass should be run.
- bool needsGOTPass() const;
-
- /// Pass to add TLV sections.
- bool needsTLVPass() const;
-
- /// Pass to transform __compact_unwind into __unwind_info should be run.
- bool needsCompactUnwindPass() const;
-
- /// Pass to add shims switching between thumb and arm mode.
- bool needsShimPass() const;
-
- /// Pass to add objc image info and optimized objc data.
- bool needsObjCPass() const;
-
- /// Magic symbol name stubs will need to help lazy bind.
- StringRef binderSymbolName() const;
-
- /// Used to keep track of direct and indirect dylibs.
- void registerDylib(mach_o::MachODylibFile *dylib, bool upward) const;
-
- // Reads a file from disk to memory. Returns only a needed chunk
- // if a fat binary.
- ErrorOr<std::unique_ptr<MemoryBuffer>> getMemoryBuffer(StringRef path);
-
- /// Used to find indirect dylibs. Instantiates a MachODylibFile if one
- /// has not already been made for the requested dylib. Uses -L and -F
- /// search paths to allow indirect dylibs to be overridden.
- mach_o::MachODylibFile* findIndirectDylib(StringRef path);
-
- uint32_t dylibCurrentVersion(StringRef installName) const;
-
- uint32_t dylibCompatVersion(StringRef installName) const;
-
- ArrayRef<mach_o::MachODylibFile*> allDylibs() const {
- return _allDylibs;
- }
-
- /// Creates a copy (owned by this MachOLinkingContext) of a string.
- StringRef copy(StringRef str) { return str.copy(_allocator); }
-
- /// If the memoryBuffer is a fat file with a slice for the current arch,
- /// this method will return the offset and size of that slice.
- bool sliceFromFatFile(MemoryBufferRef mb, uint32_t &offset, uint32_t &size);
-
- /// Returns if a command line option specified dylib is an upward link.
- bool isUpwardDylib(StringRef installName) const;
-
- static bool isThinObjectFile(StringRef path, Arch &arch);
- static Arch archFromCpuType(uint32_t cputype, uint32_t cpusubtype);
- static Arch archFromName(StringRef archName);
- static StringRef nameFromArch(Arch arch);
- static uint32_t cpuTypeFromArch(Arch arch);
- static uint32_t cpuSubtypeFromArch(Arch arch);
- static bool is64Bit(Arch arch);
- static bool isHostEndian(Arch arch);
- static bool isBigEndian(Arch arch);
-
- /// Construct 32-bit value from string "X.Y.Z" where
- /// bits are xxxx.yy.zz. Largest number is 65535.255.255
- static bool parsePackedVersion(StringRef str, uint32_t &result);
-
- /// Construct 64-bit value from string "A.B.C.D.E" where
- /// bits are aaaa.bb.cc.dd.ee. Largest number is 16777215.1023.1023.1023.1023
- static bool parsePackedVersion(StringRef str, uint64_t &result);
-
- void finalizeInputFiles() override;
-
- llvm::Error handleLoadedFile(File &file) override;
-
- bool customAtomOrderer(const DefinedAtom *left, const DefinedAtom *right,
- bool &leftBeforeRight) const;
-
- /// Return the 'flat namespace' file. This is the file that supplies
- /// atoms for otherwise undefined symbols when the -flat_namespace or
- /// -undefined dynamic_lookup options are used.
- File* flatNamespaceFile() const { return _flatNamespaceFile; }
-
-private:
- Writer &writer() const override;
- mach_o::MachODylibFile* loadIndirectDylib(StringRef path);
- struct ArchInfo {
- StringRef archName;
- MachOLinkingContext::Arch arch;
- bool littleEndian;
- uint32_t cputype;
- uint32_t cpusubtype;
- };
-
- struct SectionAlign {
- StringRef segmentName;
- StringRef sectionName;
- uint16_t align;
- };
-
- struct OrderFileNode {
- StringRef fileFilter;
- unsigned order;
- };
-
- static bool findOrderOrdinal(const std::vector<OrderFileNode> &nodes,
- const DefinedAtom *atom, unsigned &ordinal);
-
- static ArchInfo _s_archInfos[];
-
- std::set<StringRef> _existingPaths; // For testing only.
- StringRefVector _searchDirs;
- StringRefVector _syslibRoots;
- StringRefVector _frameworkDirs;
- HeaderFileType _outputMachOType = llvm::MachO::MH_EXECUTE;
- bool _outputMachOTypeStatic = false; // Disambiguate static vs dynamic prog
- bool _doNothing = false; // for -help and -v which just print info
- bool _pie = false;
- Arch _arch = arch_unknown;
- OS _os = OS::macOSX;
- uint32_t _osMinVersion = 0;
- uint32_t _sdkVersion = 0;
- uint64_t _sourceVersion = 0;
- uint64_t _pageZeroSize = 0;
- uint64_t _pageSize = 4096;
- uint64_t _baseAddress = 0;
- uint64_t _stackSize = 0;
- uint32_t _compatibilityVersion = 0;
- uint32_t _currentVersion = 0;
- ObjCConstraint _objcConstraint = objc_unknown;
- uint32_t _swiftVersion = 0;
- StringRef _installName;
- StringRefVector _rpaths;
- bool _flatNamespace = false;
- UndefinedMode _undefinedMode = UndefinedMode::error;
- bool _deadStrippableDylib = false;
- bool _printAtoms = false;
- bool _testingFileUsage = false;
- bool _keepPrivateExterns = false;
- bool _demangle = false;
- bool _mergeObjCCategories = true;
- bool _generateVersionLoadCommand = false;
- bool _generateFunctionStartsLoadCommand = false;
- bool _generateDataInCodeLoadCommand = false;
- StringRef _bundleLoader;
- mutable std::unique_ptr<mach_o::ArchHandler> _archHandler;
- mutable std::unique_ptr<Writer> _writer;
- std::vector<SectionAlign> _sectAligns;
- mutable llvm::StringMap<mach_o::MachODylibFile*> _pathToDylibMap;
- mutable std::vector<mach_o::MachODylibFile*> _allDylibs;
- mutable std::set<mach_o::MachODylibFile*> _upwardDylibs;
- mutable std::vector<std::unique_ptr<File>> _indirectDylibs;
- mutable std::mutex _dylibsMutex;
- ExportMode _exportMode = ExportMode::globals;
- llvm::StringSet<> _exportedSymbols;
- DebugInfoMode _debugInfoMode = DebugInfoMode::addDebugMap;
- std::unique_ptr<llvm::raw_fd_ostream> _dependencyInfo;
- llvm::StringMap<std::vector<OrderFileNode>> _orderFiles;
- unsigned _orderFileEntries = 0;
- File *_flatNamespaceFile = nullptr;
- mach_o::SectCreateFile *_sectCreateFile = nullptr;
-};
-
-} // end namespace lld
-
-#endif // LLD_READER_WRITER_MACHO_LINKING_CONTEXT_H
+++ /dev/null
-//===- lld/ReaderWriter/YamlContext.h - object used in YAML I/O context ---===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_READER_WRITER_YAML_CONTEXT_H
-#define LLD_READER_WRITER_YAML_CONTEXT_H
-
-#include "lld/Common/LLVM.h"
-#include <functional>
-#include <memory>
-#include <vector>
-
-namespace lld {
-class File;
-class LinkingContext;
-class Registry;
-namespace mach_o {
-namespace normalized {
-struct NormalizedFile;
-}
-}
-
-using lld::mach_o::normalized::NormalizedFile;
-
-/// When YAML I/O is used in lld, the yaml context always holds a YamlContext
-/// object. We need to support hetergenous yaml documents which each require
-/// different context info. This struct supports all clients.
-struct YamlContext {
- const LinkingContext *_ctx = nullptr;
- const Registry *_registry = nullptr;
- File *_file = nullptr;
- NormalizedFile *_normalizeMachOFile = nullptr;
- StringRef _path;
-};
-
-} // end namespace lld
-
-#endif // LLD_READER_WRITER_YAML_CONTEXT_H
+++ /dev/null
-add_subdirectory(Core)
-add_subdirectory(Driver)
-add_subdirectory(ReaderWriter)
+++ /dev/null
-add_lld_library(lldCore
- DefinedAtom.cpp
- Error.cpp
- File.cpp
- LinkingContext.cpp
- Reader.cpp
- Resolver.cpp
- SymbolTable.cpp
- Writer.cpp
-
- ADDITIONAL_HEADER_DIRS
- ${LLD_INCLUDE_DIR}/lld/Core
-
- LINK_COMPONENTS
- BinaryFormat
- MC
- Support
-
- LINK_LIBS
- ${LLVM_PTHREAD_LIB}
-
- DEPENDS
- intrinsics_gen
- )
+++ /dev/null
-//===- DefinedAtom.cpp ------------------------------------------*- C++ -*-===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "llvm/Support/ErrorHandling.h"
-#include "lld/Core/DefinedAtom.h"
-#include "lld/Core/File.h"
-
-namespace lld {
-
-DefinedAtom::ContentPermissions DefinedAtom::permissions() const {
- // By default base permissions on content type.
- return permissions(this->contentType());
-}
-
-// Utility function for deriving permissions from content type
-DefinedAtom::ContentPermissions DefinedAtom::permissions(ContentType type) {
- switch (type) {
- case typeCode:
- case typeResolver:
- case typeBranchIsland:
- case typeBranchShim:
- case typeStub:
- case typeStubHelper:
- case typeMachHeader:
- return permR_X;
-
- case typeConstant:
- case typeCString:
- case typeUTF16String:
- case typeCFI:
- case typeLSDA:
- case typeLiteral4:
- case typeLiteral8:
- case typeLiteral16:
- case typeDTraceDOF:
- case typeCompactUnwindInfo:
- case typeProcessedUnwindInfo:
- case typeObjCImageInfo:
- case typeObjCMethodList:
- return permR__;
-
- case typeData:
- case typeDataFast:
- case typeZeroFill:
- case typeZeroFillFast:
- case typeObjC1Class:
- case typeLazyPointer:
- case typeLazyDylibPointer:
- case typeNonLazyPointer:
- case typeThunkTLV:
- return permRW_;
-
- case typeGOT:
- case typeConstData:
- case typeCFString:
- case typeInitializerPtr:
- case typeTerminatorPtr:
- case typeCStringPtr:
- case typeObjCClassPtr:
- case typeObjC2CategoryList:
- case typeInterposingTuples:
- case typeTLVInitialData:
- case typeTLVInitialZeroFill:
- case typeTLVInitializerPtr:
- return permRW_L;
-
- case typeUnknown:
- case typeTempLTO:
- case typeSectCreate:
- case typeDSOHandle:
- return permUnknown;
- }
- llvm_unreachable("unknown content type");
-}
-
-} // namespace
+++ /dev/null
-//===- Error.cpp - system_error extensions for lld --------------*- C++ -*-===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "lld/Core/Error.h"
-#include "llvm/ADT/Twine.h"
-#include "llvm/Support/ErrorHandling.h"
-#include <mutex>
-#include <string>
-#include <vector>
-
-using namespace lld;
-
-namespace {
-class _YamlReaderErrorCategory : public std::error_category {
-public:
- const char* name() const noexcept override {
- return "lld.yaml.reader";
- }
-
- std::string message(int ev) const override {
- switch (static_cast<YamlReaderError>(ev)) {
- case YamlReaderError::unknown_keyword:
- return "Unknown keyword found in yaml file";
- case YamlReaderError::illegal_value:
- return "Bad value found in yaml file";
- }
- llvm_unreachable("An enumerator of YamlReaderError does not have a "
- "message defined.");
- }
-};
-} // end anonymous namespace
-
-const std::error_category &lld::YamlReaderCategory() {
- static _YamlReaderErrorCategory o;
- return o;
-}
-
-namespace lld {
-
-/// Temporary class to enable make_dynamic_error_code() until
-/// llvm::ErrorOr<> is updated to work with error encapsulations
-/// other than error_code.
-class dynamic_error_category : public std::error_category {
-public:
- ~dynamic_error_category() override = default;
-
- const char *name() const noexcept override {
- return "lld.dynamic_error";
- }
-
- std::string message(int ev) const override {
- assert(ev >= 0);
- assert(ev < (int)_messages.size());
- // The value is an index into the string vector.
- return _messages[ev];
- }
-
- int add(std::string msg) {
- std::lock_guard<std::recursive_mutex> lock(_mutex);
- // Value zero is always the success value.
- if (_messages.empty())
- _messages.push_back("Success");
- _messages.push_back(msg);
- // Return the index of the string just appended.
- return _messages.size() - 1;
- }
-
-private:
- std::vector<std::string> _messages;
- std::recursive_mutex _mutex;
-};
-
-static dynamic_error_category categorySingleton;
-
-std::error_code make_dynamic_error_code(StringRef msg) {
- return std::error_code(categorySingleton.add(std::string(msg)),
- categorySingleton);
-}
-
-char GenericError::ID = 0;
-
-GenericError::GenericError(Twine Msg) : Msg(Msg.str()) { }
-
-void GenericError::log(raw_ostream &OS) const {
- OS << Msg;
-}
-
-} // namespace lld
+++ /dev/null
-//===- Core/File.cpp - A Container of Atoms -------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "lld/Core/File.h"
-#include <mutex>
-
-namespace lld {
-
-File::~File() = default;
-
-File::AtomVector<DefinedAtom> File::_noDefinedAtoms;
-File::AtomVector<UndefinedAtom> File::_noUndefinedAtoms;
-File::AtomVector<SharedLibraryAtom> File::_noSharedLibraryAtoms;
-File::AtomVector<AbsoluteAtom> File::_noAbsoluteAtoms;
-
-std::error_code File::parse() {
- std::lock_guard<std::mutex> lock(_parseMutex);
- if (!_lastError.hasValue())
- _lastError = doParse();
- return _lastError.getValue();
-}
-
-} // end namespace lld
+++ /dev/null
-//===- lib/Core/LinkingContext.cpp - Linker Context Object Interface ------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "lld/Core/LinkingContext.h"
-#include "lld/Core/File.h"
-#include "lld/Core/Node.h"
-#include "lld/Core/Simple.h"
-#include "lld/Core/Writer.h"
-#include <algorithm>
-
-namespace lld {
-
-LinkingContext::LinkingContext() = default;
-
-LinkingContext::~LinkingContext() = default;
-
-bool LinkingContext::validate() {
- return validateImpl();
-}
-
-llvm::Error LinkingContext::writeFile(const File &linkedFile) const {
- return this->writer().writeFile(linkedFile, _outputPath);
-}
-
-std::unique_ptr<File> LinkingContext::createEntrySymbolFile() const {
- return createEntrySymbolFile("<command line option -e>");
-}
-
-std::unique_ptr<File>
-LinkingContext::createEntrySymbolFile(StringRef filename) const {
- if (entrySymbolName().empty())
- return nullptr;
- std::unique_ptr<SimpleFile> entryFile(new SimpleFile(filename,
- File::kindEntryObject));
- entryFile->addAtom(
- *(new (_allocator) SimpleUndefinedAtom(*entryFile, entrySymbolName())));
- return std::move(entryFile);
-}
-
-std::unique_ptr<File> LinkingContext::createUndefinedSymbolFile() const {
- return createUndefinedSymbolFile("<command line option -u or --defsym>");
-}
-
-std::unique_ptr<File>
-LinkingContext::createUndefinedSymbolFile(StringRef filename) const {
- if (_initialUndefinedSymbols.empty())
- return nullptr;
- std::unique_ptr<SimpleFile> undefinedSymFile(
- new SimpleFile(filename, File::kindUndefinedSymsObject));
- for (StringRef undefSym : _initialUndefinedSymbols)
- undefinedSymFile->addAtom(*(new (_allocator) SimpleUndefinedAtom(
- *undefinedSymFile, undefSym)));
- return std::move(undefinedSymFile);
-}
-
-void LinkingContext::createInternalFiles(
- std::vector<std::unique_ptr<File>> &result) const {
- if (std::unique_ptr<File> file = createEntrySymbolFile())
- result.push_back(std::move(file));
- if (std::unique_ptr<File> file = createUndefinedSymbolFile())
- result.push_back(std::move(file));
-}
-
-} // end namespace lld
+++ /dev/null
-//===- lib/Core/Reader.cpp ------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "lld/Core/Reader.h"
-#include "lld/Core/File.h"
-#include "lld/Core/Reference.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/BinaryFormat/Magic.h"
-#include "llvm/Support/Errc.h"
-#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/MemoryBuffer.h"
-#include <algorithm>
-#include <memory>
-
-using llvm::file_magic;
-using llvm::identify_magic;
-
-namespace lld {
-
-YamlIOTaggedDocumentHandler::~YamlIOTaggedDocumentHandler() = default;
-
-void Registry::add(std::unique_ptr<Reader> reader) {
- _readers.push_back(std::move(reader));
-}
-
-void Registry::add(std::unique_ptr<YamlIOTaggedDocumentHandler> handler) {
- _yamlHandlers.push_back(std::move(handler));
-}
-
-ErrorOr<std::unique_ptr<File>>
-Registry::loadFile(std::unique_ptr<MemoryBuffer> mb) const {
- // Get file magic.
- StringRef content(mb->getBufferStart(), mb->getBufferSize());
- file_magic fileType = identify_magic(content);
-
- // Ask each registered reader if it can handle this file type or extension.
- for (const std::unique_ptr<Reader> &reader : _readers) {
- if (!reader->canParse(fileType, mb->getMemBufferRef()))
- continue;
- return reader->loadFile(std::move(mb), *this);
- }
-
- // No Reader could parse this file.
- return make_error_code(llvm::errc::executable_format_error);
-}
-
-static const Registry::KindStrings kindStrings[] = {
- {Reference::kindLayoutAfter, "layout-after"},
- {Reference::kindAssociate, "associate"},
- LLD_KIND_STRING_END};
-
-Registry::Registry() {
- addKindTable(Reference::KindNamespace::all, Reference::KindArch::all,
- kindStrings);
-}
-
-bool Registry::handleTaggedDoc(llvm::yaml::IO &io,
- const lld::File *&file) const {
- for (const std::unique_ptr<YamlIOTaggedDocumentHandler> &h : _yamlHandlers)
- if (h->handledDocTag(io, file))
- return true;
- return false;
-}
-
-void Registry::addKindTable(Reference::KindNamespace ns,
- Reference::KindArch arch,
- const KindStrings array[]) {
- KindEntry entry = { ns, arch, array };
- _kindEntries.push_back(entry);
-}
-
-bool Registry::referenceKindFromString(StringRef inputStr,
- Reference::KindNamespace &ns,
- Reference::KindArch &arch,
- Reference::KindValue &value) const {
- for (const KindEntry &entry : _kindEntries) {
- for (const KindStrings *pair = entry.array; !pair->name.empty(); ++pair) {
- if (!inputStr.equals(pair->name))
- continue;
- ns = entry.ns;
- arch = entry.arch;
- value = pair->value;
- return true;
- }
- }
- return false;
-}
-
-bool Registry::referenceKindToString(Reference::KindNamespace ns,
- Reference::KindArch arch,
- Reference::KindValue value,
- StringRef &str) const {
- for (const KindEntry &entry : _kindEntries) {
- if (entry.ns != ns)
- continue;
- if (entry.arch != arch)
- continue;
- for (const KindStrings *pair = entry.array; !pair->name.empty(); ++pair) {
- if (pair->value != value)
- continue;
- str = pair->name;
- return true;
- }
- }
- return false;
-}
-
-} // end namespace lld
+++ /dev/null
-//===- Core/Resolver.cpp - Resolves Atom References -----------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "lld/Core/Resolver.h"
-#include "lld/Common/LLVM.h"
-#include "lld/Core/ArchiveLibraryFile.h"
-#include "lld/Core/Atom.h"
-#include "lld/Core/File.h"
-#include "lld/Core/Instrumentation.h"
-#include "lld/Core/LinkingContext.h"
-#include "lld/Core/SharedLibraryFile.h"
-#include "lld/Core/SymbolTable.h"
-#include "lld/Core/UndefinedAtom.h"
-#include "llvm/ADT/iterator_range.h"
-#include "llvm/Support/Debug.h"
-#include "llvm/Support/Error.h"
-#include "llvm/Support/ErrorHandling.h"
-#include "llvm/Support/Format.h"
-#include "llvm/Support/raw_ostream.h"
-#include <algorithm>
-#include <cassert>
-#include <utility>
-#include <vector>
-
-namespace lld {
-
-llvm::Expected<bool> Resolver::handleFile(File &file) {
- if (auto ec = _ctx.handleLoadedFile(file))
- return std::move(ec);
- bool undefAdded = false;
- for (auto &atom : file.defined().owning_ptrs())
- doDefinedAtom(std::move(atom));
- for (auto &atom : file.undefined().owning_ptrs()) {
- if (doUndefinedAtom(std::move(atom)))
- undefAdded = true;
- }
- for (auto &atom : file.sharedLibrary().owning_ptrs())
- doSharedLibraryAtom(std::move(atom));
- for (auto &atom : file.absolute().owning_ptrs())
- doAbsoluteAtom(std::move(atom));
- return undefAdded;
-}
-
-llvm::Expected<bool> Resolver::forEachUndefines(File &file,
- UndefCallback callback) {
- size_t i = _undefineIndex[&file];
- bool undefAdded = false;
- do {
- for (; i < _undefines.size(); ++i) {
- StringRef undefName = _undefines[i];
- if (undefName.empty())
- continue;
- const Atom *atom = _symbolTable.findByName(undefName);
- if (!isa<UndefinedAtom>(atom) || _symbolTable.isCoalescedAway(atom)) {
- // The symbol was resolved by some other file. Cache the result.
- _undefines[i] = "";
- continue;
- }
- auto undefAddedOrError = callback(undefName);
- if (auto ec = undefAddedOrError.takeError())
- return std::move(ec);
- undefAdded |= undefAddedOrError.get();
- }
- } while (i < _undefines.size());
- _undefineIndex[&file] = i;
- return undefAdded;
-}
-
-llvm::Expected<bool> Resolver::handleArchiveFile(File &file) {
- ArchiveLibraryFile *archiveFile = cast<ArchiveLibraryFile>(&file);
- return forEachUndefines(file,
- [&](StringRef undefName) -> llvm::Expected<bool> {
- if (File *member = archiveFile->find(undefName)) {
- member->setOrdinal(_ctx.getNextOrdinalAndIncrement());
- return handleFile(*member);
- }
- return false;
- });
-}
-
-llvm::Error Resolver::handleSharedLibrary(File &file) {
- // Add all the atoms from the shared library
- SharedLibraryFile *sharedLibrary = cast<SharedLibraryFile>(&file);
- auto undefAddedOrError = handleFile(*sharedLibrary);
- if (auto ec = undefAddedOrError.takeError())
- return ec;
- undefAddedOrError =
- forEachUndefines(file, [&](StringRef undefName) -> llvm::Expected<bool> {
- auto atom = sharedLibrary->exports(undefName);
- if (atom.get())
- doSharedLibraryAtom(std::move(atom));
- return false;
- });
-
- if (auto ec = undefAddedOrError.takeError())
- return ec;
- return llvm::Error::success();
-}
-
-bool Resolver::doUndefinedAtom(OwningAtomPtr<UndefinedAtom> atom) {
- DEBUG_WITH_TYPE("resolver", llvm::dbgs()
- << " UndefinedAtom: "
- << llvm::format("0x%09lX", atom.get())
- << ", name=" << atom.get()->name() << "\n");
-
- // tell symbol table
- bool newUndefAdded = _symbolTable.add(*atom.get());
- if (newUndefAdded)
- _undefines.push_back(atom.get()->name());
-
- // add to list of known atoms
- _atoms.push_back(OwningAtomPtr<Atom>(atom.release()));
-
- return newUndefAdded;
-}
-
-// Called on each atom when a file is added. Returns true if a given
-// atom is added to the symbol table.
-void Resolver::doDefinedAtom(OwningAtomPtr<DefinedAtom> atom) {
- DEBUG_WITH_TYPE("resolver", llvm::dbgs()
- << " DefinedAtom: "
- << llvm::format("0x%09lX", atom.get())
- << ", file=#"
- << atom.get()->file().ordinal()
- << ", atom=#"
- << atom.get()->ordinal()
- << ", name="
- << atom.get()->name()
- << ", type="
- << atom.get()->contentType()
- << "\n");
-
- // An atom that should never be dead-stripped is a dead-strip root.
- if (_ctx.deadStrip() &&
- atom.get()->deadStrip() == DefinedAtom::deadStripNever) {
- _deadStripRoots.insert(atom.get());
- }
-
- // add to list of known atoms
- _symbolTable.add(*atom.get());
- _atoms.push_back(OwningAtomPtr<Atom>(atom.release()));
-}
-
-void Resolver::doSharedLibraryAtom(OwningAtomPtr<SharedLibraryAtom> atom) {
- DEBUG_WITH_TYPE("resolver", llvm::dbgs()
- << " SharedLibraryAtom: "
- << llvm::format("0x%09lX", atom.get())
- << ", name="
- << atom.get()->name()
- << "\n");
-
- // tell symbol table
- _symbolTable.add(*atom.get());
-
- // add to list of known atoms
- _atoms.push_back(OwningAtomPtr<Atom>(atom.release()));
-}
-
-void Resolver::doAbsoluteAtom(OwningAtomPtr<AbsoluteAtom> atom) {
- DEBUG_WITH_TYPE("resolver", llvm::dbgs()
- << " AbsoluteAtom: "
- << llvm::format("0x%09lX", atom.get())
- << ", name="
- << atom.get()->name()
- << "\n");
-
- // tell symbol table
- if (atom.get()->scope() != Atom::scopeTranslationUnit)
- _symbolTable.add(*atom.get());
-
- // add to list of known atoms
- _atoms.push_back(OwningAtomPtr<Atom>(atom.release()));
-}
-
-// Returns true if at least one of N previous files has created an
-// undefined symbol.
-bool Resolver::undefinesAdded(int begin, int end) {
- std::vector<std::unique_ptr<Node>> &inputs = _ctx.getNodes();
- for (int i = begin; i < end; ++i)
- if (FileNode *node = dyn_cast<FileNode>(inputs[i].get()))
- if (_newUndefinesAdded[node->getFile()])
- return true;
- return false;
-}
-
-File *Resolver::getFile(int &index) {
- std::vector<std::unique_ptr<Node>> &inputs = _ctx.getNodes();
- if ((size_t)index >= inputs.size())
- return nullptr;
- if (GroupEnd *group = dyn_cast<GroupEnd>(inputs[index].get())) {
- // We are at the end of the current group. If one or more new
- // undefined atom has been added in the last groupSize files, we
- // reiterate over the files.
- int size = group->getSize();
- if (undefinesAdded(index - size, index)) {
- index -= size;
- return getFile(index);
- }
- ++index;
- return getFile(index);
- }
- return cast<FileNode>(inputs[index++].get())->getFile();
-}
-
-// Keep adding atoms until _ctx.getNextFile() returns an error. This
-// function is where undefined atoms are resolved.
-bool Resolver::resolveUndefines() {
- DEBUG_WITH_TYPE("resolver",
- llvm::dbgs() << "******** Resolving undefines:\n");
- ScopedTask task(getDefaultDomain(), "resolveUndefines");
- int index = 0;
- std::set<File *> seen;
- for (;;) {
- bool undefAdded = false;
- DEBUG_WITH_TYPE("resolver",
- llvm::dbgs() << "Loading file #" << index << "\n");
- File *file = getFile(index);
- if (!file)
- return true;
- if (std::error_code ec = file->parse()) {
- llvm::errs() << "Cannot open " + file->path() << ": " << ec.message()
- << "\n";
- return false;
- }
- DEBUG_WITH_TYPE("resolver",
- llvm::dbgs() << "Loaded file: " << file->path() << "\n");
- switch (file->kind()) {
- case File::kindErrorObject:
- case File::kindNormalizedObject:
- case File::kindMachObject:
- case File::kindCEntryObject:
- case File::kindHeaderObject:
- case File::kindEntryObject:
- case File::kindUndefinedSymsObject:
- case File::kindStubHelperObject:
- case File::kindResolverMergedObject:
- case File::kindSectCreateObject: {
- // The same file may be visited more than once if the file is
- // in --start-group and --end-group. Only library files should
- // be processed more than once.
- if (seen.count(file))
- break;
- seen.insert(file);
- assert(!file->hasOrdinal());
- file->setOrdinal(_ctx.getNextOrdinalAndIncrement());
- auto undefAddedOrError = handleFile(*file);
- if (auto EC = undefAddedOrError.takeError()) {
- // FIXME: This should be passed to logAllUnhandledErrors but it needs
- // to be passed a Twine instead of a string.
- llvm::errs() << "Error in " + file->path() << ": ";
- logAllUnhandledErrors(std::move(EC), llvm::errs(), std::string());
- return false;
- }
- undefAdded = undefAddedOrError.get();
- break;
- }
- case File::kindArchiveLibrary: {
- if (!file->hasOrdinal())
- file->setOrdinal(_ctx.getNextOrdinalAndIncrement());
- auto undefAddedOrError = handleArchiveFile(*file);
- if (auto EC = undefAddedOrError.takeError()) {
- // FIXME: This should be passed to logAllUnhandledErrors but it needs
- // to be passed a Twine instead of a string.
- llvm::errs() << "Error in " + file->path() << ": ";
- logAllUnhandledErrors(std::move(EC), llvm::errs(), std::string());
- return false;
- }
- undefAdded = undefAddedOrError.get();
- break;
- }
- case File::kindSharedLibrary:
- if (!file->hasOrdinal())
- file->setOrdinal(_ctx.getNextOrdinalAndIncrement());
- if (auto EC = handleSharedLibrary(*file)) {
- // FIXME: This should be passed to logAllUnhandledErrors but it needs
- // to be passed a Twine instead of a string.
- llvm::errs() << "Error in " + file->path() << ": ";
- logAllUnhandledErrors(std::move(EC), llvm::errs(), std::string());
- return false;
- }
- break;
- }
- _newUndefinesAdded[file] = undefAdded;
- }
-}
-
-// switch all references to undefined or coalesced away atoms
-// to the new defined atom
-void Resolver::updateReferences() {
- DEBUG_WITH_TYPE("resolver",
- llvm::dbgs() << "******** Updating references:\n");
- ScopedTask task(getDefaultDomain(), "updateReferences");
- for (const OwningAtomPtr<Atom> &atom : _atoms) {
- if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom.get())) {
- for (const Reference *ref : *defAtom) {
- // A reference of type kindAssociate shouldn't be updated.
- // Instead, an atom having such reference will be removed
- // if the target atom is coalesced away, so that they will
- // go away as a group.
- if (ref->kindNamespace() == lld::Reference::KindNamespace::all &&
- ref->kindValue() == lld::Reference::kindAssociate) {
- if (_symbolTable.isCoalescedAway(atom.get()))
- _deadAtoms.insert(ref->target());
- continue;
- }
- const Atom *newTarget = _symbolTable.replacement(ref->target());
- const_cast<Reference *>(ref)->setTarget(newTarget);
- }
- }
- }
-}
-
-// For dead code stripping, recursively mark atoms "live"
-void Resolver::markLive(const Atom *atom) {
- // Mark the atom is live. If it's already marked live, then stop recursion.
- auto exists = _liveAtoms.insert(atom);
- if (!exists.second)
- return;
-
- // Mark all atoms it references as live
- if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom)) {
- for (const Reference *ref : *defAtom)
- markLive(ref->target());
- for (auto &p : llvm::make_range(_reverseRef.equal_range(defAtom))) {
- const Atom *target = p.second;
- markLive(target);
- }
- }
-}
-
-static bool isBackref(const Reference *ref) {
- if (ref->kindNamespace() != lld::Reference::KindNamespace::all)
- return false;
- return (ref->kindValue() == lld::Reference::kindLayoutAfter);
-}
-
-// remove all atoms not actually used
-void Resolver::deadStripOptimize() {
- DEBUG_WITH_TYPE("resolver",
- llvm::dbgs() << "******** Dead stripping unused atoms:\n");
- ScopedTask task(getDefaultDomain(), "deadStripOptimize");
- // only do this optimization with -dead_strip
- if (!_ctx.deadStrip())
- return;
-
- // Some type of references prevent referring atoms to be dead-striped.
- // Make a reverse map of such references before traversing the graph.
- // While traversing the list of atoms, mark AbsoluteAtoms as live
- // in order to avoid reclaim.
- for (const OwningAtomPtr<Atom> &atom : _atoms) {
- if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom.get()))
- for (const Reference *ref : *defAtom)
- if (isBackref(ref))
- _reverseRef.insert(std::make_pair(ref->target(), atom.get()));
- if (const AbsoluteAtom *absAtom = dyn_cast<AbsoluteAtom>(atom.get()))
- markLive(absAtom);
- }
-
- // By default, shared libraries are built with all globals as dead strip roots
- if (_ctx.globalsAreDeadStripRoots())
- for (const OwningAtomPtr<Atom> &atom : _atoms)
- if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom.get()))
- if (defAtom->scope() == DefinedAtom::scopeGlobal)
- _deadStripRoots.insert(defAtom);
-
- // Or, use list of names that are dead strip roots.
- for (const StringRef &name : _ctx.deadStripRoots()) {
- const Atom *symAtom = _symbolTable.findByName(name);
- assert(symAtom);
- _deadStripRoots.insert(symAtom);
- }
-
- // mark all roots as live, and recursively all atoms they reference
- for (const Atom *dsrAtom : _deadStripRoots)
- markLive(dsrAtom);
-
- // now remove all non-live atoms from _atoms
- _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(),
- [&](OwningAtomPtr<Atom> &a) {
- return _liveAtoms.count(a.get()) == 0;
- }),
- _atoms.end());
-}
-
-// error out if some undefines remain
-bool Resolver::checkUndefines() {
- DEBUG_WITH_TYPE("resolver",
- llvm::dbgs() << "******** Checking for undefines:\n");
-
- // build vector of remaining undefined symbols
- std::vector<const UndefinedAtom *> undefinedAtoms = _symbolTable.undefines();
- if (_ctx.deadStrip()) {
- // When dead code stripping, we don't care if dead atoms are undefined.
- undefinedAtoms.erase(
- std::remove_if(undefinedAtoms.begin(), undefinedAtoms.end(),
- [&](const Atom *a) { return _liveAtoms.count(a) == 0; }),
- undefinedAtoms.end());
- }
-
- if (undefinedAtoms.empty())
- return false;
-
- // Warn about unresolved symbols.
- bool foundUndefines = false;
- for (const UndefinedAtom *undef : undefinedAtoms) {
- // Skip over a weak symbol.
- if (undef->canBeNull() != UndefinedAtom::canBeNullNever)
- continue;
-
- // If this is a library and undefined symbols are allowed on the
- // target platform, skip over it.
- if (isa<SharedLibraryFile>(undef->file()) && _ctx.allowShlibUndefines())
- continue;
-
- // If the undefine is coalesced away, skip over it.
- if (_symbolTable.isCoalescedAway(undef))
- continue;
-
- // Seems like this symbol is undefined. Warn that.
- foundUndefines = true;
- if (_ctx.printRemainingUndefines()) {
- llvm::errs() << "Undefined symbol: " << undef->file().path() << ": "
- << _ctx.demangle(undef->name()) << "\n";
- }
- }
- if (!foundUndefines)
- return false;
- if (_ctx.printRemainingUndefines())
- llvm::errs() << "symbol(s) not found\n";
- return true;
-}
-
-// Remove from _atoms all coalesced away atoms.
-void Resolver::removeCoalescedAwayAtoms() {
- DEBUG_WITH_TYPE("resolver",
- llvm::dbgs() << "******** Removing coalesced away atoms:\n");
- ScopedTask task(getDefaultDomain(), "removeCoalescedAwayAtoms");
- _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(),
- [&](OwningAtomPtr<Atom> &a) {
- return _symbolTable.isCoalescedAway(a.get()) ||
- _deadAtoms.count(a.get());
- }),
- _atoms.end());
-}
-
-bool Resolver::resolve() {
- DEBUG_WITH_TYPE("resolver",
- llvm::dbgs() << "******** Resolving atom references:\n");
- if (!resolveUndefines())
- return false;
- updateReferences();
- deadStripOptimize();
- if (checkUndefines()) {
- DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "Found undefines... ");
- if (!_ctx.allowRemainingUndefines()) {
- DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "which we don't allow\n");
- return false;
- }
- DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "which we are ok with\n");
- }
- removeCoalescedAwayAtoms();
- _result->addAtoms(_atoms);
- DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "******** Finished resolver\n");
- return true;
-}
-
-void Resolver::MergedFile::addAtoms(
- llvm::MutableArrayRef<OwningAtomPtr<Atom>> all) {
- ScopedTask task(getDefaultDomain(), "addAtoms");
- DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "Resolver final atom list:\n");
-
- for (OwningAtomPtr<Atom> &atom : all) {
-#ifndef NDEBUG
- if (auto *definedAtom = dyn_cast<DefinedAtom>(atom.get())) {
- DEBUG_WITH_TYPE("resolver", llvm::dbgs()
- << llvm::format(" 0x%09lX", definedAtom)
- << ", file=#"
- << definedAtom->file().ordinal()
- << ", atom=#"
- << definedAtom->ordinal()
- << ", name="
- << definedAtom->name()
- << ", type="
- << definedAtom->contentType()
- << "\n");
- } else {
- DEBUG_WITH_TYPE("resolver", llvm::dbgs()
- << llvm::format(" 0x%09lX", atom.get())
- << ", name="
- << atom.get()->name()
- << "\n");
- }
-#endif
- addAtom(*atom.release());
- }
-}
-
-} // namespace lld
+++ /dev/null
-//===- Core/SymbolTable.cpp - Main Symbol Table ---------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "lld/Core/SymbolTable.h"
-#include "lld/Common/LLVM.h"
-#include "lld/Core/AbsoluteAtom.h"
-#include "lld/Core/Atom.h"
-#include "lld/Core/DefinedAtom.h"
-#include "lld/Core/File.h"
-#include "lld/Core/LinkingContext.h"
-#include "lld/Core/Resolver.h"
-#include "lld/Core/SharedLibraryAtom.h"
-#include "lld/Core/UndefinedAtom.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/DenseMapInfo.h"
-#include "llvm/ADT/Hashing.h"
-#include "llvm/Support/ErrorHandling.h"
-#include "llvm/Support/raw_ostream.h"
-#include <algorithm>
-#include <cassert>
-#include <cstdlib>
-#include <vector>
-
-namespace lld {
-bool SymbolTable::add(const UndefinedAtom &atom) { return addByName(atom); }
-
-bool SymbolTable::add(const SharedLibraryAtom &atom) { return addByName(atom); }
-
-bool SymbolTable::add(const AbsoluteAtom &atom) { return addByName(atom); }
-
-bool SymbolTable::add(const DefinedAtom &atom) {
- if (!atom.name().empty() &&
- atom.scope() != DefinedAtom::scopeTranslationUnit) {
- // Named atoms cannot be merged by content.
- assert(atom.merge() != DefinedAtom::mergeByContent);
- // Track named atoms that are not scoped to file (static).
- return addByName(atom);
- }
- if (atom.merge() == DefinedAtom::mergeByContent) {
- // Named atoms cannot be merged by content.
- assert(atom.name().empty());
- // Currently only read-only constants can be merged.
- if (atom.permissions() == DefinedAtom::permR__)
- return addByContent(atom);
- // TODO: support mergeByContent of data atoms by comparing content & fixups.
- }
- return false;
-}
-
-enum NameCollisionResolution {
- NCR_First,
- NCR_Second,
- NCR_DupDef,
- NCR_DupUndef,
- NCR_DupShLib,
- NCR_Error
-};
-
-static NameCollisionResolution cases[4][4] = {
- //regular absolute undef sharedLib
- {
- // first is regular
- NCR_DupDef, NCR_Error, NCR_First, NCR_First
- },
- {
- // first is absolute
- NCR_Error, NCR_Error, NCR_First, NCR_First
- },
- {
- // first is undef
- NCR_Second, NCR_Second, NCR_DupUndef, NCR_Second
- },
- {
- // first is sharedLib
- NCR_Second, NCR_Second, NCR_First, NCR_DupShLib
- }
-};
-
-static NameCollisionResolution collide(Atom::Definition first,
- Atom::Definition second) {
- return cases[first][second];
-}
-
-enum MergeResolution {
- MCR_First,
- MCR_Second,
- MCR_Largest,
- MCR_SameSize,
- MCR_Error
-};
-
-static MergeResolution mergeCases[][6] = {
- // no tentative weak weakAddress sameNameAndSize largest
- {MCR_Error, MCR_First, MCR_First, MCR_First, MCR_SameSize, MCR_Largest}, // no
- {MCR_Second, MCR_Largest, MCR_Second, MCR_Second, MCR_SameSize, MCR_Largest}, // tentative
- {MCR_Second, MCR_First, MCR_First, MCR_Second, MCR_SameSize, MCR_Largest}, // weak
- {MCR_Second, MCR_First, MCR_First, MCR_First, MCR_SameSize, MCR_Largest}, // weakAddress
- {MCR_SameSize, MCR_SameSize, MCR_SameSize, MCR_SameSize, MCR_SameSize, MCR_SameSize}, // sameSize
- {MCR_Largest, MCR_Largest, MCR_Largest, MCR_Largest, MCR_SameSize, MCR_Largest}, // largest
-};
-
-static MergeResolution mergeSelect(DefinedAtom::Merge first,
- DefinedAtom::Merge second) {
- assert(first != DefinedAtom::mergeByContent);
- assert(second != DefinedAtom::mergeByContent);
- return mergeCases[first][second];
-}
-
-bool SymbolTable::addByName(const Atom &newAtom) {
- StringRef name = newAtom.name();
- assert(!name.empty());
- const Atom *existing = findByName(name);
- if (existing == nullptr) {
- // Name is not in symbol table yet, add it associate with this atom.
- _nameTable[name] = &newAtom;
- return true;
- }
-
- // Do nothing if the same object is added more than once.
- if (existing == &newAtom)
- return false;
-
- // Name is already in symbol table and associated with another atom.
- bool useNew = true;
- switch (collide(existing->definition(), newAtom.definition())) {
- case NCR_First:
- useNew = false;
- break;
- case NCR_Second:
- useNew = true;
- break;
- case NCR_DupDef: {
- const auto *existingDef = cast<DefinedAtom>(existing);
- const auto *newDef = cast<DefinedAtom>(&newAtom);
- switch (mergeSelect(existingDef->merge(), newDef->merge())) {
- case MCR_First:
- useNew = false;
- break;
- case MCR_Second:
- useNew = true;
- break;
- case MCR_Largest: {
- uint64_t existingSize = existingDef->sectionSize();
- uint64_t newSize = newDef->sectionSize();
- useNew = (newSize >= existingSize);
- break;
- }
- case MCR_SameSize: {
- uint64_t existingSize = existingDef->sectionSize();
- uint64_t newSize = newDef->sectionSize();
- if (existingSize == newSize) {
- useNew = true;
- break;
- }
- llvm::errs() << "Size mismatch: " << existing->name() << " ("
- << existingSize << ") " << newAtom.name() << " (" << newSize
- << ")\n";
- LLVM_FALLTHROUGH;
- }
- case MCR_Error:
- llvm::errs() << "Duplicate symbols: " << existing->name() << ":"
- << existing->file().path() << " and " << newAtom.name()
- << ":" << newAtom.file().path() << "\n";
- llvm::report_fatal_error("duplicate symbol error");
- break;
- }
- break;
- }
- case NCR_DupUndef: {
- const UndefinedAtom* existingUndef = cast<UndefinedAtom>(existing);
- const UndefinedAtom* newUndef = cast<UndefinedAtom>(&newAtom);
-
- bool sameCanBeNull = (existingUndef->canBeNull() == newUndef->canBeNull());
- if (sameCanBeNull)
- useNew = false;
- else
- useNew = (newUndef->canBeNull() < existingUndef->canBeNull());
- break;
- }
- case NCR_DupShLib: {
- useNew = false;
- break;
- }
- case NCR_Error:
- llvm::errs() << "SymbolTable: error while merging " << name << "\n";
- llvm::report_fatal_error("duplicate symbol error");
- break;
- }
-
- if (useNew) {
- // Update name table to use new atom.
- _nameTable[name] = &newAtom;
- // Add existing atom to replacement table.
- _replacedAtoms[existing] = &newAtom;
- } else {
- // New atom is not being used. Add it to replacement table.
- _replacedAtoms[&newAtom] = existing;
- }
- return false;
-}
-
-unsigned SymbolTable::AtomMappingInfo::getHashValue(const DefinedAtom *atom) {
- auto content = atom->rawContent();
- return llvm::hash_combine(atom->size(),
- atom->contentType(),
- llvm::hash_combine_range(content.begin(),
- content.end()));
-}
-
-bool SymbolTable::AtomMappingInfo::isEqual(const DefinedAtom * const l,
- const DefinedAtom * const r) {
- if (l == r)
- return true;
- if (l == getEmptyKey() || r == getEmptyKey())
- return false;
- if (l == getTombstoneKey() || r == getTombstoneKey())
- return false;
- if (l->contentType() != r->contentType())
- return false;
- if (l->size() != r->size())
- return false;
- if (l->sectionChoice() != r->sectionChoice())
- return false;
- if (l->sectionChoice() == DefinedAtom::sectionCustomRequired) {
- if (!l->customSectionName().equals(r->customSectionName()))
- return false;
- }
- ArrayRef<uint8_t> lc = l->rawContent();
- ArrayRef<uint8_t> rc = r->rawContent();
- return memcmp(lc.data(), rc.data(), lc.size()) == 0;
-}
-
-bool SymbolTable::addByContent(const DefinedAtom &newAtom) {
- AtomContentSet::iterator pos = _contentTable.find(&newAtom);
- if (pos == _contentTable.end()) {
- _contentTable.insert(&newAtom);
- return true;
- }
- const Atom* existing = *pos;
- // New atom is not being used. Add it to replacement table.
- _replacedAtoms[&newAtom] = existing;
- return false;
-}
-
-const Atom *SymbolTable::findByName(StringRef sym) {
- NameToAtom::iterator pos = _nameTable.find(sym);
- if (pos == _nameTable.end())
- return nullptr;
- return pos->second;
-}
-
-const Atom *SymbolTable::replacement(const Atom *atom) {
- // Find the replacement for a given atom. Atoms in _replacedAtoms
- // may be chained, so find the last one.
- for (;;) {
- AtomToAtom::iterator pos = _replacedAtoms.find(atom);
- if (pos == _replacedAtoms.end())
- return atom;
- atom = pos->second;
- }
-}
-
-bool SymbolTable::isCoalescedAway(const Atom *atom) {
- return _replacedAtoms.count(atom) > 0;
-}
-
-std::vector<const UndefinedAtom *> SymbolTable::undefines() {
- std::vector<const UndefinedAtom *> ret;
- for (auto it : _nameTable) {
- const Atom *atom = it.second;
- assert(atom != nullptr);
- if (const auto *undef = dyn_cast<const UndefinedAtom>(atom))
- if (_replacedAtoms.count(undef) == 0)
- ret.push_back(undef);
- }
- return ret;
-}
-
-} // namespace lld
+++ /dev/null
-//===- lib/Core/Writer.cpp ------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "lld/Core/Writer.h"
-
-namespace lld {
-
-Writer::Writer() = default;
-
-Writer::~Writer() = default;
-
-} // end namespace lld
+++ /dev/null
-set(LLVM_TARGET_DEFINITIONS DarwinLdOptions.td)
-tablegen(LLVM DarwinLdOptions.inc -gen-opt-parser-defs)
-add_public_tablegen_target(DriverOptionsTableGen)
-
-add_lld_library(lldDriver
- DarwinLdDriver.cpp
-
- ADDITIONAL_HEADER_DIRS
- ${LLD_INCLUDE_DIR}/lld/Driver
-
- LINK_COMPONENTS
- Option
- Support
-
- LINK_LIBS
- lldCommon
- lldCore
- lldMachO
- lldReaderWriter
- lldYAML
- )
-
-add_dependencies(lldDriver DriverOptionsTableGen)
+++ /dev/null
-//===- lib/Driver/DarwinLdDriver.cpp --------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-///
-/// Concrete instance of the Driver for darwin's ld.
-///
-//===----------------------------------------------------------------------===//
-
-#include "lld/Common/Args.h"
-#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/LLVM.h"
-#include "lld/Core/ArchiveLibraryFile.h"
-#include "lld/Core/Error.h"
-#include "lld/Core/File.h"
-#include "lld/Core/Instrumentation.h"
-#include "lld/Core/LinkingContext.h"
-#include "lld/Core/Node.h"
-#include "lld/Core/PassManager.h"
-#include "lld/Core/Resolver.h"
-#include "lld/Core/SharedLibraryFile.h"
-#include "lld/Core/Simple.h"
-#include "lld/ReaderWriter/MachOLinkingContext.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/Optional.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/SmallString.h"
-#include "llvm/ADT/StringExtras.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/ADT/Twine.h"
-#include "llvm/BinaryFormat/MachO.h"
-#include "llvm/Option/Arg.h"
-#include "llvm/Option/ArgList.h"
-#include "llvm/Option/OptTable.h"
-#include "llvm/Option/Option.h"
-#include "llvm/Support/Casting.h"
-#include "llvm/Support/CommandLine.h"
-#include "llvm/Support/Error.h"
-#include "llvm/Support/ErrorOr.h"
-#include "llvm/Support/Format.h"
-#include "llvm/Support/MathExtras.h"
-#include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/Path.h"
-#include "llvm/Support/raw_ostream.h"
-#include <algorithm>
-#include <cstdint>
-#include <memory>
-#include <string>
-#include <system_error>
-#include <utility>
-#include <vector>
-
-using namespace lld;
-
-namespace {
-
-// Create enum with OPT_xxx values for each option in DarwinLdOptions.td
-enum {
- OPT_INVALID = 0,
-#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
- HELP, META, VALUES) \
- OPT_##ID,
-#include "DarwinLdOptions.inc"
-#undef OPTION
-};
-
-// Create prefix string literals used in DarwinLdOptions.td
-#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
-#include "DarwinLdOptions.inc"
-#undef PREFIX
-
-// Create table mapping all options defined in DarwinLdOptions.td
-static const llvm::opt::OptTable::Info InfoTable[] = {
-#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
- HELPTEXT, METAVAR, VALUES) \
- {PREFIX, NAME, HELPTEXT, \
- METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \
- PARAM, FLAGS, OPT_##GROUP, \
- OPT_##ALIAS, ALIASARGS, VALUES},
-#include "DarwinLdOptions.inc"
-#undef OPTION
-};
-
-// Create OptTable class for parsing actual command line arguments
-class DarwinLdOptTable : public llvm::opt::OptTable {
-public:
- DarwinLdOptTable() : OptTable(InfoTable) {}
-};
-
-static std::vector<std::unique_ptr<File>>
-makeErrorFile(StringRef path, std::error_code ec) {
- std::vector<std::unique_ptr<File>> result;
- result.push_back(std::make_unique<ErrorFile>(path, ec));
- return result;
-}
-
-static std::vector<std::unique_ptr<File>>
-parseMemberFiles(std::unique_ptr<File> file) {
- std::vector<std::unique_ptr<File>> members;
- if (auto *archive = dyn_cast<ArchiveLibraryFile>(file.get())) {
- if (std::error_code ec = archive->parseAllMembers(members))
- return makeErrorFile(file->path(), ec);
- } else {
- members.push_back(std::move(file));
- }
- return members;
-}
-
-std::vector<std::unique_ptr<File>> loadFile(MachOLinkingContext &ctx,
- StringRef path, bool wholeArchive,
- bool upwardDylib) {
- if (ctx.logInputFiles())
- message(path);
-
- ErrorOr<std::unique_ptr<MemoryBuffer>> mbOrErr = ctx.getMemoryBuffer(path);
- if (std::error_code ec = mbOrErr.getError())
- return makeErrorFile(path, ec);
- ErrorOr<std::unique_ptr<File>> fileOrErr =
- ctx.registry().loadFile(std::move(mbOrErr.get()));
- if (std::error_code ec = fileOrErr.getError())
- return makeErrorFile(path, ec);
- std::unique_ptr<File> &file = fileOrErr.get();
-
- // If file is a dylib, inform LinkingContext about it.
- if (SharedLibraryFile *shl = dyn_cast<SharedLibraryFile>(file.get())) {
- if (std::error_code ec = shl->parse())
- return makeErrorFile(path, ec);
- ctx.registerDylib(reinterpret_cast<mach_o::MachODylibFile *>(shl),
- upwardDylib);
- }
- if (wholeArchive)
- return parseMemberFiles(std::move(file));
- std::vector<std::unique_ptr<File>> files;
- files.push_back(std::move(file));
- return files;
-}
-
-} // end anonymous namespace
-
-// Test may be running on Windows. Canonicalize the path
-// separator to '/' to get consistent outputs for tests.
-static std::string canonicalizePath(StringRef path) {
- char sep = llvm::sys::path::get_separator().front();
- if (sep != '/') {
- std::string fixedPath = std::string(path);
- std::replace(fixedPath.begin(), fixedPath.end(), sep, '/');
- return fixedPath;
- } else {
- return std::string(path);
- }
-}
-
-static void addFile(StringRef path, MachOLinkingContext &ctx,
- bool loadWholeArchive, bool upwardDylib) {
- std::vector<std::unique_ptr<File>> files =
- loadFile(ctx, path, loadWholeArchive, upwardDylib);
- for (std::unique_ptr<File> &file : files)
- ctx.getNodes().push_back(std::make_unique<FileNode>(std::move(file)));
-}
-
-// Export lists are one symbol per line. Blank lines are ignored.
-// Trailing comments start with #.
-static std::error_code parseExportsList(StringRef exportFilePath,
- MachOLinkingContext &ctx) {
- // Map in export list file.
- ErrorOr<std::unique_ptr<MemoryBuffer>> mb =
- MemoryBuffer::getFileOrSTDIN(exportFilePath);
- if (std::error_code ec = mb.getError())
- return ec;
- ctx.addInputFileDependency(exportFilePath);
- StringRef buffer = mb->get()->getBuffer();
- while (!buffer.empty()) {
- // Split off each line in the file.
- std::pair<StringRef, StringRef> lineAndRest = buffer.split('\n');
- StringRef line = lineAndRest.first;
- // Ignore trailing # comments.
- std::pair<StringRef, StringRef> symAndComment = line.split('#');
- StringRef sym = symAndComment.first.trim();
- if (!sym.empty())
- ctx.addExportSymbol(sym);
- buffer = lineAndRest.second;
- }
- return std::error_code();
-}
-
-/// Order files are one symbol per line. Blank lines are ignored.
-/// Trailing comments start with #. Symbol names can be prefixed with an
-/// architecture name and/or .o leaf name. Examples:
-/// _foo
-/// bar.o:_bar
-/// libfrob.a(bar.o):_bar
-/// x86_64:_foo64
-static std::error_code parseOrderFile(StringRef orderFilePath,
- MachOLinkingContext &ctx) {
- // Map in order file.
- ErrorOr<std::unique_ptr<MemoryBuffer>> mb =
- MemoryBuffer::getFileOrSTDIN(orderFilePath);
- if (std::error_code ec = mb.getError())
- return ec;
- ctx.addInputFileDependency(orderFilePath);
- StringRef buffer = mb->get()->getBuffer();
- while (!buffer.empty()) {
- // Split off each line in the file.
- std::pair<StringRef, StringRef> lineAndRest = buffer.split('\n');
- StringRef line = lineAndRest.first;
- buffer = lineAndRest.second;
- // Ignore trailing # comments.
- std::pair<StringRef, StringRef> symAndComment = line.split('#');
- if (symAndComment.first.empty())
- continue;
- StringRef sym = symAndComment.first.trim();
- if (sym.empty())
- continue;
- // Check for prefix.
- StringRef prefix;
- std::pair<StringRef, StringRef> prefixAndSym = sym.split(':');
- if (!prefixAndSym.second.empty()) {
- sym = prefixAndSym.second;
- prefix = prefixAndSym.first;
- if (!prefix.endswith(".o") && !prefix.endswith(".o)")) {
- // If arch name prefix does not match arch being linked, ignore symbol.
- if (!ctx.archName().equals(prefix))
- continue;
- prefix = "";
- }
- } else
- sym = prefixAndSym.first;
- if (!sym.empty()) {
- ctx.appendOrderedSymbol(sym, prefix);
- // llvm::errs() << sym << ", prefix=" << prefix << "\n";
- }
- }
- return std::error_code();
-}
-
-//
-// There are two variants of the -filelist option:
-//
-// -filelist <path>
-// In this variant, the path is to a text file which contains one file path
-// per line. There are no comments or trimming of whitespace.
-//
-// -fileList <path>,<dir>
-// In this variant, the path is to a text file which contains a partial path
-// per line. The <dir> prefix is prepended to each partial path.
-//
-static llvm::Error loadFileList(StringRef fileListPath,
- MachOLinkingContext &ctx, bool forceLoad) {
- // If there is a comma, split off <dir>.
- std::pair<StringRef, StringRef> opt = fileListPath.split(',');
- StringRef filePath = opt.first;
- StringRef dirName = opt.second;
- ctx.addInputFileDependency(filePath);
- // Map in file list file.
- ErrorOr<std::unique_ptr<MemoryBuffer>> mb =
- MemoryBuffer::getFileOrSTDIN(filePath);
- if (std::error_code ec = mb.getError())
- return llvm::errorCodeToError(ec);
- StringRef buffer = mb->get()->getBuffer();
- while (!buffer.empty()) {
- // Split off each line in the file.
- std::pair<StringRef, StringRef> lineAndRest = buffer.split('\n');
- StringRef line = lineAndRest.first;
- StringRef path;
- if (!dirName.empty()) {
- // If there is a <dir> then prepend dir to each line.
- SmallString<256> fullPath;
- fullPath.assign(dirName);
- llvm::sys::path::append(fullPath, Twine(line));
- path = ctx.copy(fullPath.str());
- } else {
- // No <dir> use whole line as input file path.
- path = ctx.copy(line);
- }
- if (!ctx.pathExists(path)) {
- return llvm::make_error<GenericError>(Twine("File not found '")
- + path
- + "'");
- }
- if (ctx.testingFileUsage()) {
- message("Found filelist entry " + canonicalizePath(path));
- }
- addFile(path, ctx, forceLoad, false);
- buffer = lineAndRest.second;
- }
- return llvm::Error::success();
-}
-
-/// Parse number assuming it is base 16, but allow 0x prefix.
-static bool parseNumberBase16(StringRef numStr, uint64_t &baseAddress) {
- if (numStr.startswith_insensitive("0x"))
- numStr = numStr.drop_front(2);
- return numStr.getAsInteger(16, baseAddress);
-}
-
-static void parseLLVMOptions(const LinkingContext &ctx) {
- // Honor -mllvm
- if (!ctx.llvmOptions().empty()) {
- unsigned numArgs = ctx.llvmOptions().size();
- auto **args = new const char *[numArgs + 2];
- args[0] = "lld (LLVM option parsing)";
- for (unsigned i = 0; i != numArgs; ++i)
- args[i + 1] = ctx.llvmOptions()[i];
- args[numArgs + 1] = nullptr;
- llvm::cl::ResetAllOptionOccurrences();
- llvm::cl::ParseCommandLineOptions(numArgs + 1, args);
- }
-}
-
-namespace lld {
-namespace mach_o {
-
-bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx) {
- // Parse command line options using DarwinLdOptions.td
- DarwinLdOptTable table;
- unsigned missingIndex;
- unsigned missingCount;
- llvm::opt::InputArgList parsedArgs =
- table.ParseArgs(args.slice(1), missingIndex, missingCount);
- if (missingCount) {
- error("missing arg value for '" +
- Twine(parsedArgs.getArgString(missingIndex)) + "' expected " +
- Twine(missingCount) + " argument(s).");
- return false;
- }
-
- for (auto unknownArg : parsedArgs.filtered(OPT_UNKNOWN)) {
- warn("ignoring unknown argument: " +
- Twine(unknownArg->getAsString(parsedArgs)));
- }
-
- errorHandler().verbose = parsedArgs.hasArg(OPT_v);
- errorHandler().errorLimit = args::getInteger(parsedArgs, OPT_error_limit, 20);
-
- // Figure out output kind ( -dylib, -r, -bundle, -preload, or -static )
- llvm::MachO::HeaderFileType fileType = llvm::MachO::MH_EXECUTE;
- bool isStaticExecutable = false;
- if (llvm::opt::Arg *kind = parsedArgs.getLastArg(
- OPT_dylib, OPT_relocatable, OPT_bundle, OPT_static, OPT_preload)) {
- switch (kind->getOption().getID()) {
- case OPT_dylib:
- fileType = llvm::MachO::MH_DYLIB;
- break;
- case OPT_relocatable:
- fileType = llvm::MachO::MH_OBJECT;
- break;
- case OPT_bundle:
- fileType = llvm::MachO::MH_BUNDLE;
- break;
- case OPT_static:
- fileType = llvm::MachO::MH_EXECUTE;
- isStaticExecutable = true;
- break;
- case OPT_preload:
- fileType = llvm::MachO::MH_PRELOAD;
- break;
- }
- }
-
- // Handle -arch xxx
- MachOLinkingContext::Arch arch = MachOLinkingContext::arch_unknown;
- if (llvm::opt::Arg *archStr = parsedArgs.getLastArg(OPT_arch)) {
- arch = MachOLinkingContext::archFromName(archStr->getValue());
- if (arch == MachOLinkingContext::arch_unknown) {
- error("unknown arch named '" + Twine(archStr->getValue()) + "'");
- return false;
- }
- }
- // If no -arch specified, scan input files to find first non-fat .o file.
- if (arch == MachOLinkingContext::arch_unknown) {
- for (auto &inFile : parsedArgs.filtered(OPT_INPUT)) {
- // This is expensive because it opens and maps the file. But that is
- // ok because no -arch is rare.
- if (MachOLinkingContext::isThinObjectFile(inFile->getValue(), arch))
- break;
- }
- if (arch == MachOLinkingContext::arch_unknown &&
- !parsedArgs.getLastArg(OPT_test_file_usage)) {
- // If no -arch and no options at all, print usage message.
- if (parsedArgs.size() == 0) {
- table.printHelp(llvm::outs(),
- (std::string(args[0]) + " [options] file...").c_str(),
- "LLVM Linker", false);
- } else {
- error("-arch not specified and could not be inferred");
- }
- return false;
- }
- }
-
- // Handle -macosx_version_min or -ios_version_min
- MachOLinkingContext::OS os = MachOLinkingContext::OS::unknown;
- uint32_t minOSVersion = 0;
- if (llvm::opt::Arg *minOS =
- parsedArgs.getLastArg(OPT_macosx_version_min, OPT_ios_version_min,
- OPT_ios_simulator_version_min)) {
- switch (minOS->getOption().getID()) {
- case OPT_macosx_version_min:
- os = MachOLinkingContext::OS::macOSX;
- if (MachOLinkingContext::parsePackedVersion(minOS->getValue(),
- minOSVersion)) {
- error("malformed macosx_version_min value");
- return false;
- }
- break;
- case OPT_ios_version_min:
- os = MachOLinkingContext::OS::iOS;
- if (MachOLinkingContext::parsePackedVersion(minOS->getValue(),
- minOSVersion)) {
- error("malformed ios_version_min value");
- return false;
- }
- break;
- case OPT_ios_simulator_version_min:
- os = MachOLinkingContext::OS::iOS_simulator;
- if (MachOLinkingContext::parsePackedVersion(minOS->getValue(),
- minOSVersion)) {
- error("malformed ios_simulator_version_min value");
- return false;
- }
- break;
- }
- } else {
- // No min-os version on command line, check environment variables
- }
-
- // Handle export_dynamic
- // FIXME: Should we warn when this applies to something other than a static
- // executable or dylib? Those are the only cases where this has an effect.
- // Note, this has to come before ctx.configure() so that we get the correct
- // value for _globalsAreDeadStripRoots.
- bool exportDynamicSymbols = parsedArgs.hasArg(OPT_export_dynamic);
-
- // Now that there's enough information parsed in, let the linking context
- // set up default values.
- ctx.configure(fileType, arch, os, minOSVersion, exportDynamicSymbols);
-
- // Handle -e xxx
- if (llvm::opt::Arg *entry = parsedArgs.getLastArg(OPT_entry))
- ctx.setEntrySymbolName(entry->getValue());
-
- // Handle -o xxx
- if (llvm::opt::Arg *outpath = parsedArgs.getLastArg(OPT_output))
- ctx.setOutputPath(outpath->getValue());
- else
- ctx.setOutputPath("a.out");
-
- // Handle -image_base XXX and -seg1addr XXXX
- if (llvm::opt::Arg *imageBase = parsedArgs.getLastArg(OPT_image_base)) {
- uint64_t baseAddress;
- if (parseNumberBase16(imageBase->getValue(), baseAddress)) {
- error("image_base expects a hex number");
- return false;
- } else if (baseAddress < ctx.pageZeroSize()) {
- error("image_base overlaps with __PAGEZERO");
- return false;
- } else if (baseAddress % ctx.pageSize()) {
- error("image_base must be a multiple of page size (0x" +
- llvm::utohexstr(ctx.pageSize()) + ")");
- return false;
- }
-
- ctx.setBaseAddress(baseAddress);
- }
-
- // Handle -dead_strip
- if (parsedArgs.getLastArg(OPT_dead_strip))
- ctx.setDeadStripping(true);
-
- bool globalWholeArchive = false;
- // Handle -all_load
- if (parsedArgs.getLastArg(OPT_all_load))
- globalWholeArchive = true;
-
- // Handle -install_name
- if (llvm::opt::Arg *installName = parsedArgs.getLastArg(OPT_install_name))
- ctx.setInstallName(installName->getValue());
- else
- ctx.setInstallName(ctx.outputPath());
-
- // Handle -mark_dead_strippable_dylib
- if (parsedArgs.getLastArg(OPT_mark_dead_strippable_dylib))
- ctx.setDeadStrippableDylib(true);
-
- // Handle -compatibility_version and -current_version
- if (llvm::opt::Arg *vers = parsedArgs.getLastArg(OPT_compatibility_version)) {
- if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB) {
- error("-compatibility_version can only be used with -dylib");
- return false;
- }
- uint32_t parsedVers;
- if (MachOLinkingContext::parsePackedVersion(vers->getValue(), parsedVers)) {
- error("-compatibility_version value is malformed");
- return false;
- }
- ctx.setCompatibilityVersion(parsedVers);
- }
-
- if (llvm::opt::Arg *vers = parsedArgs.getLastArg(OPT_current_version)) {
- if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB) {
- error("-current_version can only be used with -dylib");
- return false;
- }
- uint32_t parsedVers;
- if (MachOLinkingContext::parsePackedVersion(vers->getValue(), parsedVers)) {
- error("-current_version value is malformed");
- return false;
- }
- ctx.setCurrentVersion(parsedVers);
- }
-
- // Handle -bundle_loader
- if (llvm::opt::Arg *loader = parsedArgs.getLastArg(OPT_bundle_loader))
- ctx.setBundleLoader(loader->getValue());
-
- // Handle -sectalign segname sectname align
- for (auto &alignArg : parsedArgs.filtered(OPT_sectalign)) {
- const char* segName = alignArg->getValue(0);
- const char* sectName = alignArg->getValue(1);
- const char* alignStr = alignArg->getValue(2);
- if ((alignStr[0] == '0') && (alignStr[1] == 'x'))
- alignStr += 2;
- unsigned long long alignValue;
- if (llvm::getAsUnsignedInteger(alignStr, 16, alignValue)) {
- error("-sectalign alignment value '" + Twine(alignStr) +
- "' not a valid number");
- return false;
- }
- uint16_t align = 1 << llvm::countTrailingZeros(alignValue);
- if (!llvm::isPowerOf2_64(alignValue)) {
- std::string Msg;
- llvm::raw_string_ostream OS(Msg);
- OS << "alignment for '-sectalign " << segName << " " << sectName
- << llvm::format(" 0x%llX", alignValue)
- << "' is not a power of two, using " << llvm::format("0x%08X", align);
- OS.flush();
- warn(Msg);
- }
- ctx.addSectionAlignment(segName, sectName, align);
- }
-
- // Handle -mllvm
- for (auto &llvmArg : parsedArgs.filtered(OPT_mllvm)) {
- ctx.appendLLVMOption(llvmArg->getValue());
- }
-
- // Handle -print_atoms
- if (parsedArgs.getLastArg(OPT_print_atoms))
- ctx.setPrintAtoms();
-
- // Handle -t (trace) option.
- if (parsedArgs.getLastArg(OPT_t))
- ctx.setLogInputFiles(true);
-
- // Handle -demangle option.
- if (parsedArgs.getLastArg(OPT_demangle))
- ctx.setDemangleSymbols(true);
-
- // Handle -keep_private_externs
- if (parsedArgs.getLastArg(OPT_keep_private_externs)) {
- ctx.setKeepPrivateExterns(true);
- if (ctx.outputMachOType() != llvm::MachO::MH_OBJECT)
- warn("-keep_private_externs only used in -r mode");
- }
-
- // Handle -dependency_info <path> used by Xcode.
- if (llvm::opt::Arg *depInfo = parsedArgs.getLastArg(OPT_dependency_info))
- if (std::error_code ec = ctx.createDependencyFile(depInfo->getValue()))
- warn(ec.message() + ", processing '-dependency_info " +
- depInfo->getValue());
-
- // In -test_file_usage mode, we'll be given an explicit list of paths that
- // exist. We'll also be expected to print out information about how we located
- // libraries and so on that the user specified, but not to actually do any
- // linking.
- if (parsedArgs.getLastArg(OPT_test_file_usage)) {
- ctx.setTestingFileUsage();
-
- // With paths existing by fiat, linking is not going to end well.
- ctx.setDoNothing(true);
-
- // Only bother looking for an existence override if we're going to use it.
- for (auto existingPath : parsedArgs.filtered(OPT_path_exists)) {
- ctx.addExistingPathForDebug(existingPath->getValue());
- }
- }
-
- // Register possible input file parsers.
- if (!ctx.doNothing()) {
- ctx.registry().addSupportMachOObjects(ctx);
- ctx.registry().addSupportArchives(ctx.logInputFiles());
- ctx.registry().addSupportYamlFiles();
- }
-
- // Now construct the set of library search directories, following ld64's
- // baroque set of accumulated hacks. Mostly, the algorithm constructs
- // { syslibroots } x { libpaths }
- //
- // Unfortunately, there are numerous exceptions:
- // 1. Only absolute paths get modified by syslibroot options.
- // 2. If there is just 1 -syslibroot, system paths not found in it are
- // skipped.
- // 3. If the last -syslibroot is "/", all of them are ignored entirely.
- // 4. If { syslibroots } x path == {}, the original path is kept.
- std::vector<StringRef> sysLibRoots;
- for (auto syslibRoot : parsedArgs.filtered(OPT_syslibroot)) {
- sysLibRoots.push_back(syslibRoot->getValue());
- }
- if (!sysLibRoots.empty()) {
- // Ignore all if last -syslibroot is "/".
- if (sysLibRoots.back() != "/")
- ctx.setSysLibRoots(sysLibRoots);
- }
-
- // Paths specified with -L come first, and are not considered system paths for
- // the case where there is precisely 1 -syslibroot.
- for (auto libPath : parsedArgs.filtered(OPT_L)) {
- ctx.addModifiedSearchDir(libPath->getValue());
- }
-
- // Process -F directories (where to look for frameworks).
- for (auto fwPath : parsedArgs.filtered(OPT_F)) {
- ctx.addFrameworkSearchDir(fwPath->getValue());
- }
-
- // -Z suppresses the standard search paths.
- if (!parsedArgs.hasArg(OPT_Z)) {
- ctx.addModifiedSearchDir("/usr/lib", true);
- ctx.addModifiedSearchDir("/usr/local/lib", true);
- ctx.addFrameworkSearchDir("/Library/Frameworks", true);
- ctx.addFrameworkSearchDir("/System/Library/Frameworks", true);
- }
-
- // Now that we've constructed the final set of search paths, print out those
- // search paths in verbose mode.
- if (errorHandler().verbose) {
- message("Library search paths:");
- for (auto path : ctx.searchDirs()) {
- message(" " + path);
- }
- message("Framework search paths:");
- for (auto path : ctx.frameworkDirs()) {
- message(" " + path);
- }
- }
-
- // Handle -exported_symbols_list <file>
- for (auto expFile : parsedArgs.filtered(OPT_exported_symbols_list)) {
- if (ctx.exportMode() == MachOLinkingContext::ExportMode::unexported) {
- error("-exported_symbols_list cannot be combined with "
- "-unexported_symbol[s_list]");
- return false;
- }
- ctx.setExportMode(MachOLinkingContext::ExportMode::exported);
- if (std::error_code ec = parseExportsList(expFile->getValue(), ctx)) {
- error(ec.message() + ", processing '-exported_symbols_list " +
- expFile->getValue());
- return false;
- }
- }
-
- // Handle -exported_symbol <symbol>
- for (auto symbol : parsedArgs.filtered(OPT_exported_symbol)) {
- if (ctx.exportMode() == MachOLinkingContext::ExportMode::unexported) {
- error("-exported_symbol cannot be combined with "
- "-unexported_symbol[s_list]");
- return false;
- }
- ctx.setExportMode(MachOLinkingContext::ExportMode::exported);
- ctx.addExportSymbol(symbol->getValue());
- }
-
- // Handle -unexported_symbols_list <file>
- for (auto expFile : parsedArgs.filtered(OPT_unexported_symbols_list)) {
- if (ctx.exportMode() == MachOLinkingContext::ExportMode::exported) {
- error("-unexported_symbols_list cannot be combined with "
- "-exported_symbol[s_list]");
- return false;
- }
- ctx.setExportMode(MachOLinkingContext::ExportMode::unexported);
- if (std::error_code ec = parseExportsList(expFile->getValue(), ctx)) {
- error(ec.message() + ", processing '-unexported_symbols_list " +
- expFile->getValue());
- return false;
- }
- }
-
- // Handle -unexported_symbol <symbol>
- for (auto symbol : parsedArgs.filtered(OPT_unexported_symbol)) {
- if (ctx.exportMode() == MachOLinkingContext::ExportMode::exported) {
- error("-unexported_symbol cannot be combined with "
- "-exported_symbol[s_list]");
- return false;
- }
- ctx.setExportMode(MachOLinkingContext::ExportMode::unexported);
- ctx.addExportSymbol(symbol->getValue());
- }
-
- // Handle obosolete -multi_module and -single_module
- if (llvm::opt::Arg *mod =
- parsedArgs.getLastArg(OPT_multi_module, OPT_single_module)) {
- if (mod->getOption().getID() == OPT_multi_module)
- warn("-multi_module is obsolete and being ignored");
- else if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB)
- warn("-single_module being ignored. It is only for use when producing a "
- "dylib");
- }
-
- // Handle obsolete ObjC options: -objc_gc_compaction, -objc_gc, -objc_gc_only
- if (parsedArgs.getLastArg(OPT_objc_gc_compaction)) {
- error("-objc_gc_compaction is not supported");
- return false;
- }
-
- if (parsedArgs.getLastArg(OPT_objc_gc)) {
- error("-objc_gc is not supported");
- return false;
- }
-
- if (parsedArgs.getLastArg(OPT_objc_gc_only)) {
- error("-objc_gc_only is not supported");
- return false;
- }
-
- // Handle -pie or -no_pie
- if (llvm::opt::Arg *pie = parsedArgs.getLastArg(OPT_pie, OPT_no_pie)) {
- switch (ctx.outputMachOType()) {
- case llvm::MachO::MH_EXECUTE:
- switch (ctx.os()) {
- case MachOLinkingContext::OS::macOSX:
- if ((minOSVersion < 0x000A0500) &&
- (pie->getOption().getID() == OPT_pie)) {
- error("-pie can only be used when targeting Mac OS X 10.5 or later");
- return false;
- }
- break;
- case MachOLinkingContext::OS::iOS:
- if ((minOSVersion < 0x00040200) &&
- (pie->getOption().getID() == OPT_pie)) {
- error("-pie can only be used when targeting iOS 4.2 or later");
- return false;
- }
- break;
- case MachOLinkingContext::OS::iOS_simulator:
- if (pie->getOption().getID() == OPT_no_pie) {
- error("iOS simulator programs must be built PIE");
- return false;
- }
- break;
- case MachOLinkingContext::OS::unknown:
- break;
- }
- ctx.setPIE(pie->getOption().getID() == OPT_pie);
- break;
- case llvm::MachO::MH_PRELOAD:
- break;
- case llvm::MachO::MH_DYLIB:
- case llvm::MachO::MH_BUNDLE:
- warn(pie->getSpelling() +
- " being ignored. It is only used when linking main executables");
- break;
- default:
- error(pie->getSpelling() +
- " can only used when linking main executables");
- return false;
- }
- }
-
- // Handle -version_load_command or -no_version_load_command
- {
- bool flagOn = false;
- bool flagOff = false;
- if (auto *arg = parsedArgs.getLastArg(OPT_version_load_command,
- OPT_no_version_load_command)) {
- flagOn = arg->getOption().getID() == OPT_version_load_command;
- flagOff = arg->getOption().getID() == OPT_no_version_load_command;
- }
-
- // default to adding version load command for dynamic code,
- // static code must opt-in
- switch (ctx.outputMachOType()) {
- case llvm::MachO::MH_OBJECT:
- ctx.setGenerateVersionLoadCommand(false);
- break;
- case llvm::MachO::MH_EXECUTE:
- // dynamic executables default to generating a version load command,
- // while static executables only generate it if required.
- if (isStaticExecutable) {
- if (flagOn)
- ctx.setGenerateVersionLoadCommand(true);
- } else {
- if (!flagOff)
- ctx.setGenerateVersionLoadCommand(true);
- }
- break;
- case llvm::MachO::MH_PRELOAD:
- case llvm::MachO::MH_KEXT_BUNDLE:
- if (flagOn)
- ctx.setGenerateVersionLoadCommand(true);
- break;
- case llvm::MachO::MH_DYLINKER:
- case llvm::MachO::MH_DYLIB:
- case llvm::MachO::MH_BUNDLE:
- if (!flagOff)
- ctx.setGenerateVersionLoadCommand(true);
- break;
- case llvm::MachO::MH_FVMLIB:
- case llvm::MachO::MH_DYLDLINK:
- case llvm::MachO::MH_DYLIB_STUB:
- case llvm::MachO::MH_DSYM:
- // We don't generate load commands for these file types, even if
- // forced on.
- break;
- }
- }
-
- // Handle -function_starts or -no_function_starts
- {
- bool flagOn = false;
- bool flagOff = false;
- if (auto *arg = parsedArgs.getLastArg(OPT_function_starts,
- OPT_no_function_starts)) {
- flagOn = arg->getOption().getID() == OPT_function_starts;
- flagOff = arg->getOption().getID() == OPT_no_function_starts;
- }
-
- // default to adding functions start for dynamic code, static code must
- // opt-in
- switch (ctx.outputMachOType()) {
- case llvm::MachO::MH_OBJECT:
- ctx.setGenerateFunctionStartsLoadCommand(false);
- break;
- case llvm::MachO::MH_EXECUTE:
- // dynamic executables default to generating a version load command,
- // while static executables only generate it if required.
- if (isStaticExecutable) {
- if (flagOn)
- ctx.setGenerateFunctionStartsLoadCommand(true);
- } else {
- if (!flagOff)
- ctx.setGenerateFunctionStartsLoadCommand(true);
- }
- break;
- case llvm::MachO::MH_PRELOAD:
- case llvm::MachO::MH_KEXT_BUNDLE:
- if (flagOn)
- ctx.setGenerateFunctionStartsLoadCommand(true);
- break;
- case llvm::MachO::MH_DYLINKER:
- case llvm::MachO::MH_DYLIB:
- case llvm::MachO::MH_BUNDLE:
- if (!flagOff)
- ctx.setGenerateFunctionStartsLoadCommand(true);
- break;
- case llvm::MachO::MH_FVMLIB:
- case llvm::MachO::MH_DYLDLINK:
- case llvm::MachO::MH_DYLIB_STUB:
- case llvm::MachO::MH_DSYM:
- // We don't generate load commands for these file types, even if
- // forced on.
- break;
- }
- }
-
- // Handle -data_in_code_info or -no_data_in_code_info
- {
- bool flagOn = false;
- bool flagOff = false;
- if (auto *arg = parsedArgs.getLastArg(OPT_data_in_code_info,
- OPT_no_data_in_code_info)) {
- flagOn = arg->getOption().getID() == OPT_data_in_code_info;
- flagOff = arg->getOption().getID() == OPT_no_data_in_code_info;
- }
-
- // default to adding data in code for dynamic code, static code must
- // opt-in
- switch (ctx.outputMachOType()) {
- case llvm::MachO::MH_OBJECT:
- if (!flagOff)
- ctx.setGenerateDataInCodeLoadCommand(true);
- break;
- case llvm::MachO::MH_EXECUTE:
- // dynamic executables default to generating a version load command,
- // while static executables only generate it if required.
- if (isStaticExecutable) {
- if (flagOn)
- ctx.setGenerateDataInCodeLoadCommand(true);
- } else {
- if (!flagOff)
- ctx.setGenerateDataInCodeLoadCommand(true);
- }
- break;
- case llvm::MachO::MH_PRELOAD:
- case llvm::MachO::MH_KEXT_BUNDLE:
- if (flagOn)
- ctx.setGenerateDataInCodeLoadCommand(true);
- break;
- case llvm::MachO::MH_DYLINKER:
- case llvm::MachO::MH_DYLIB:
- case llvm::MachO::MH_BUNDLE:
- if (!flagOff)
- ctx.setGenerateDataInCodeLoadCommand(true);
- break;
- case llvm::MachO::MH_FVMLIB:
- case llvm::MachO::MH_DYLDLINK:
- case llvm::MachO::MH_DYLIB_STUB:
- case llvm::MachO::MH_DSYM:
- // We don't generate load commands for these file types, even if
- // forced on.
- break;
- }
- }
-
- // Handle sdk_version
- if (llvm::opt::Arg *arg = parsedArgs.getLastArg(OPT_sdk_version)) {
- uint32_t sdkVersion = 0;
- if (MachOLinkingContext::parsePackedVersion(arg->getValue(),
- sdkVersion)) {
- error("malformed sdkVersion value");
- return false;
- }
- ctx.setSdkVersion(sdkVersion);
- } else if (ctx.generateVersionLoadCommand()) {
- // If we don't have an sdk version, but were going to emit a load command
- // with min_version, then we need to give a warning as we have no sdk
- // version to put in that command.
- // FIXME: We need to decide whether to make this an error.
- warn("-sdk_version is required when emitting min version load command. "
- "Setting sdk version to match provided min version");
- ctx.setSdkVersion(ctx.osMinVersion());
- }
-
- // Handle source_version
- if (llvm::opt::Arg *arg = parsedArgs.getLastArg(OPT_source_version)) {
- uint64_t version = 0;
- if (MachOLinkingContext::parsePackedVersion(arg->getValue(),
- version)) {
- error("malformed source_version value");
- return false;
- }
- ctx.setSourceVersion(version);
- }
-
- // Handle stack_size
- if (llvm::opt::Arg *stackSize = parsedArgs.getLastArg(OPT_stack_size)) {
- uint64_t stackSizeVal;
- if (parseNumberBase16(stackSize->getValue(), stackSizeVal)) {
- error("stack_size expects a hex number");
- return false;
- }
- if ((stackSizeVal % ctx.pageSize()) != 0) {
- error("stack_size must be a multiple of page size (0x" +
- llvm::utohexstr(ctx.pageSize()) + ")");
- return false;
- }
-
- ctx.setStackSize(stackSizeVal);
- }
-
- // Handle debug info handling options: -S
- if (parsedArgs.hasArg(OPT_S))
- ctx.setDebugInfoMode(MachOLinkingContext::DebugInfoMode::noDebugMap);
-
- // Handle -order_file <file>
- for (auto orderFile : parsedArgs.filtered(OPT_order_file)) {
- if (std::error_code ec = parseOrderFile(orderFile->getValue(), ctx)) {
- error(ec.message() + ", processing '-order_file " + orderFile->getValue()
- + "'");
- return false;
- }
- }
-
- // Handle -flat_namespace.
- if (llvm::opt::Arg *ns =
- parsedArgs.getLastArg(OPT_flat_namespace, OPT_twolevel_namespace)) {
- if (ns->getOption().getID() == OPT_flat_namespace)
- ctx.setUseFlatNamespace(true);
- }
-
- // Handle -undefined
- if (llvm::opt::Arg *undef = parsedArgs.getLastArg(OPT_undefined)) {
- MachOLinkingContext::UndefinedMode UndefMode;
- if (StringRef(undef->getValue()).equals("error"))
- UndefMode = MachOLinkingContext::UndefinedMode::error;
- else if (StringRef(undef->getValue()).equals("warning"))
- UndefMode = MachOLinkingContext::UndefinedMode::warning;
- else if (StringRef(undef->getValue()).equals("suppress"))
- UndefMode = MachOLinkingContext::UndefinedMode::suppress;
- else if (StringRef(undef->getValue()).equals("dynamic_lookup"))
- UndefMode = MachOLinkingContext::UndefinedMode::dynamicLookup;
- else {
- error("invalid option to -undefined [ warning | error | suppress | "
- "dynamic_lookup ]");
- return false;
- }
-
- if (ctx.useFlatNamespace()) {
- // If we're using -flat_namespace then 'warning', 'suppress' and
- // 'dynamic_lookup' are all equivalent, so map them to 'suppress'.
- if (UndefMode != MachOLinkingContext::UndefinedMode::error)
- UndefMode = MachOLinkingContext::UndefinedMode::suppress;
- } else {
- // If we're using -twolevel_namespace then 'warning' and 'suppress' are
- // illegal. Emit a diagnostic if they've been (mis)used.
- if (UndefMode == MachOLinkingContext::UndefinedMode::warning ||
- UndefMode == MachOLinkingContext::UndefinedMode::suppress) {
- error("can't use -undefined warning or suppress with "
- "-twolevel_namespace");
- return false;
- }
- }
-
- ctx.setUndefinedMode(UndefMode);
- }
-
- // Handle -no_objc_category_merging.
- if (parsedArgs.getLastArg(OPT_no_objc_category_merging))
- ctx.setMergeObjCCategories(false);
-
- // Handle -rpath <path>
- if (parsedArgs.hasArg(OPT_rpath)) {
- switch (ctx.outputMachOType()) {
- case llvm::MachO::MH_EXECUTE:
- case llvm::MachO::MH_DYLIB:
- case llvm::MachO::MH_BUNDLE:
- if (!ctx.minOS("10.5", "2.0")) {
- if (ctx.os() == MachOLinkingContext::OS::macOSX)
- error("-rpath can only be used when targeting OS X 10.5 or later");
- else
- error("-rpath can only be used when targeting iOS 2.0 or later");
- return false;
- }
- break;
- default:
- error("-rpath can only be used when creating a dynamic final linked "
- "image");
- return false;
- }
-
- for (auto rPath : parsedArgs.filtered(OPT_rpath)) {
- ctx.addRpath(rPath->getValue());
- }
- }
-
- // Parse the LLVM options before we process files in case the file handling
- // makes use of things like LLVM_DEBUG().
- parseLLVMOptions(ctx);
-
- // Handle input files and sectcreate.
- for (auto &arg : parsedArgs) {
- bool upward;
- llvm::Optional<StringRef> resolvedPath;
- switch (arg->getOption().getID()) {
- default:
- continue;
- case OPT_INPUT:
- addFile(arg->getValue(), ctx, globalWholeArchive, false);
- break;
- case OPT_upward_library:
- addFile(arg->getValue(), ctx, false, true);
- break;
- case OPT_force_load:
- addFile(arg->getValue(), ctx, true, false);
- break;
- case OPT_l:
- case OPT_upward_l:
- upward = (arg->getOption().getID() == OPT_upward_l);
- resolvedPath = ctx.searchLibrary(arg->getValue());
- if (!resolvedPath) {
- error("Unable to find library for " + arg->getSpelling() +
- arg->getValue());
- return false;
- } else if (ctx.testingFileUsage()) {
- message(Twine("Found ") + (upward ? "upward " : " ") + "library " +
- canonicalizePath(resolvedPath.getValue()));
- }
- addFile(resolvedPath.getValue(), ctx, globalWholeArchive, upward);
- break;
- case OPT_framework:
- case OPT_upward_framework:
- upward = (arg->getOption().getID() == OPT_upward_framework);
- resolvedPath = ctx.findPathForFramework(arg->getValue());
- if (!resolvedPath) {
- error("Unable to find framework for " + arg->getSpelling() + " " +
- arg->getValue());
- return false;
- } else if (ctx.testingFileUsage()) {
- message(Twine("Found ") + (upward ? "upward " : " ") + "framework " +
- canonicalizePath(resolvedPath.getValue()));
- }
- addFile(resolvedPath.getValue(), ctx, globalWholeArchive, upward);
- break;
- case OPT_filelist:
- if (auto ec = loadFileList(arg->getValue(), ctx, globalWholeArchive)) {
- handleAllErrors(std::move(ec), [&](const llvm::ErrorInfoBase &EI) {
- error(EI.message() + ", processing '-filelist " + arg->getValue());
- });
- return false;
- }
- break;
- case OPT_sectcreate: {
- const char* seg = arg->getValue(0);
- const char* sect = arg->getValue(1);
- const char* fileName = arg->getValue(2);
-
- ErrorOr<std::unique_ptr<MemoryBuffer>> contentOrErr =
- MemoryBuffer::getFile(fileName);
-
- if (!contentOrErr) {
- error("can't open -sectcreate file " + Twine(fileName));
- return false;
- }
-
- ctx.addSectCreateSection(seg, sect, std::move(*contentOrErr));
- }
- break;
- }
- }
-
- if (ctx.getNodes().empty()) {
- error("No input files");
- return false;
- }
-
- // Validate the combination of options used.
- return ctx.validate();
-}
-
-static void createFiles(MachOLinkingContext &ctx, bool Implicit) {
- std::vector<std::unique_ptr<File>> Files;
- if (Implicit)
- ctx.createImplicitFiles(Files);
- else
- ctx.createInternalFiles(Files);
- for (auto i = Files.rbegin(), e = Files.rend(); i != e; ++i) {
- auto &members = ctx.getNodes();
- members.insert(members.begin(), std::make_unique<FileNode>(std::move(*i)));
- }
-}
-
-/// This is where the link is actually performed.
-bool link(llvm::ArrayRef<const char *> args, bool CanExitEarly,
- raw_ostream &StdoutOS, raw_ostream &StderrOS) {
- lld::stdoutOS = &StdoutOS;
- lld::stderrOS = &StderrOS;
-
- errorHandler().logName = args::getFilenameWithoutExe(args[0]);
- errorHandler().errorLimitExceededMsg =
- "too many errors emitted, stopping now (use "
- "'-error-limit 0' to see all errors)";
- errorHandler().exitEarly = CanExitEarly;
- StderrOS.enable_colors(StderrOS.has_colors());
-
- MachOLinkingContext ctx;
- if (!parse(args, ctx))
- return false;
- if (ctx.doNothing())
- return true;
- if (ctx.getNodes().empty())
- return false;
-
- for (std::unique_ptr<Node> &ie : ctx.getNodes())
- if (FileNode *node = dyn_cast<FileNode>(ie.get()))
- node->getFile()->parse();
-
- createFiles(ctx, false /* Implicit */);
-
- // Give target a chance to add files
- createFiles(ctx, true /* Implicit */);
-
- // Give target a chance to postprocess input files.
- // Mach-O uses this chance to move all object files before library files.
- ctx.finalizeInputFiles();
-
- // Do core linking.
- ScopedTask resolveTask(getDefaultDomain(), "Resolve");
- Resolver resolver(ctx);
- if (!resolver.resolve())
- return false;
- SimpleFile *merged = nullptr;
- {
- std::unique_ptr<SimpleFile> mergedFile = resolver.resultFile();
- merged = mergedFile.get();
- auto &members = ctx.getNodes();
- members.insert(members.begin(),
- std::make_unique<FileNode>(std::move(mergedFile)));
- }
- resolveTask.end();
-
- // Run passes on linked atoms.
- ScopedTask passTask(getDefaultDomain(), "Passes");
- PassManager pm;
- ctx.addPasses(pm);
- if (auto ec = pm.runOnFile(*merged)) {
- // FIXME: This should be passed to logAllUnhandledErrors but it needs
- // to be passed a Twine instead of a string.
- lld::errs() << "Failed to run passes on file '" << ctx.outputPath()
- << "': ";
- logAllUnhandledErrors(std::move(ec), lld::errs(), std::string());
- return false;
- }
-
- passTask.end();
-
- // Give linked atoms to Writer to generate output file.
- ScopedTask writeTask(getDefaultDomain(), "Write");
- if (auto ec = ctx.writeFile(*merged)) {
- // FIXME: This should be passed to logAllUnhandledErrors but it needs
- // to be passed a Twine instead of a string.
- lld::errs() << "Failed to write file '" << ctx.outputPath() << "': ";
- logAllUnhandledErrors(std::move(ec), lld::errs(), std::string());
- return false;
- }
-
- // Call exit() if we can to avoid calling destructors.
- if (CanExitEarly)
- exitLld(errorCount() ? 1 : 0);
-
-
- return true;
-}
-
-} // end namespace mach_o
-} // end namespace lld
+++ /dev/null
-include "llvm/Option/OptParser.td"
-
-
-// output kinds
-def grp_kind : OptionGroup<"outs">, HelpText<"OUTPUT KIND">;
-def relocatable : Flag<["-"], "r">,
- HelpText<"Create relocatable object file">, Group<grp_kind>;
-def static : Flag<["-"], "static">,
- HelpText<"Create static executable">, Group<grp_kind>;
-def dynamic : Flag<["-"], "dynamic">,
- HelpText<"Create dynamic executable (default)">,Group<grp_kind>;
-def dylib : Flag<["-"], "dylib">,
- HelpText<"Create dynamic library">, Group<grp_kind>;
-def bundle : Flag<["-"], "bundle">,
- HelpText<"Create dynamic bundle">, Group<grp_kind>;
-def execute : Flag<["-"], "execute">,
- HelpText<"Create main executable (default)">, Group<grp_kind>;
-def preload : Flag<["-"], "preload">,
- HelpText<"Create binary for use with embedded systems">, Group<grp_kind>;
-
-// optimizations
-def grp_opts : OptionGroup<"opts">, HelpText<"OPTIMIZATIONS">;
-def dead_strip : Flag<["-"], "dead_strip">,
- HelpText<"Remove unreference code and data">, Group<grp_opts>;
-def macosx_version_min : Separate<["-"], "macosx_version_min">,
- MetaVarName<"<version>">,
- HelpText<"Minimum Mac OS X version">, Group<grp_opts>;
-def ios_version_min : Separate<["-"], "ios_version_min">,
- MetaVarName<"<version>">,
- HelpText<"Minimum iOS version">, Group<grp_opts>;
-def iphoneos_version_min : Separate<["-"], "iphoneos_version_min">,
- Alias<ios_version_min>;
-def ios_simulator_version_min : Separate<["-"], "ios_simulator_version_min">,
- MetaVarName<"<version>">,
- HelpText<"Minimum iOS simulator version">, Group<grp_opts>;
-def sdk_version : Separate<["-"], "sdk_version">,
- MetaVarName<"<version>">,
- HelpText<"SDK version">, Group<grp_opts>;
-def source_version : Separate<["-"], "source_version">,
- MetaVarName<"<version>">,
- HelpText<"Source version">, Group<grp_opts>;
-def version_load_command : Flag<["-"], "version_load_command">,
- HelpText<"Force generation of a version load command">, Group<grp_opts>;
-def no_version_load_command : Flag<["-"], "no_version_load_command">,
- HelpText<"Disable generation of a version load command">, Group<grp_opts>;
-def function_starts : Flag<["-"], "function_starts">,
- HelpText<"Force generation of a function starts load command">,
- Group<grp_opts>;
-def no_function_starts : Flag<["-"], "no_function_starts">,
- HelpText<"Disable generation of a function starts load command">,
- Group<grp_opts>;
-def data_in_code_info : Flag<["-"], "data_in_code_info">,
- HelpText<"Force generation of a data in code load command">,
- Group<grp_opts>;
-def no_data_in_code_info : Flag<["-"], "no_data_in_code_info">,
- HelpText<"Disable generation of a data in code load command">,
- Group<grp_opts>;
-def mllvm : Separate<["-"], "mllvm">,
- MetaVarName<"<option>">,
- HelpText<"Options to pass to LLVM during LTO">, Group<grp_opts>;
-def exported_symbols_list : Separate<["-"], "exported_symbols_list">,
- MetaVarName<"<file-path>">,
- HelpText<"Restricts which symbols will be exported">, Group<grp_opts>;
-def exported_symbol : Separate<["-"], "exported_symbol">,
- MetaVarName<"<symbol>">,
- HelpText<"Restricts which symbols will be exported">, Group<grp_opts>;
-def unexported_symbols_list : Separate<["-"], "unexported_symbols_list">,
- MetaVarName<"<file-path>">,
- HelpText<"Lists symbols that should not be exported">, Group<grp_opts>;
-def unexported_symbol : Separate<["-"], "unexported_symbol">,
- MetaVarName<"<symbol>">,
- HelpText<"A symbol which should not be exported">, Group<grp_opts>;
-def keep_private_externs : Flag<["-"], "keep_private_externs">,
- HelpText<"Private extern (hidden) symbols should not be transformed "
- "into local symbols">, Group<grp_opts>;
-def order_file : Separate<["-"], "order_file">,
- MetaVarName<"<file-path>">,
- HelpText<"re-order and move specified symbols to start of their section">,
- Group<grp_opts>;
-def flat_namespace : Flag<["-"], "flat_namespace">,
- HelpText<"Resolves symbols in any (transitively) linked dynamic libraries. "
- "Source libraries are not recorded: dyld will re-search all "
- "images at runtime and use the first definition found.">,
- Group<grp_opts>;
-def twolevel_namespace : Flag<["-"], "twolevel_namespace">,
- HelpText<"Resolves symbols in listed libraries only. Source libraries are "
- "recorded in the symbol table.">,
- Group<grp_opts>;
-def undefined : Separate<["-"], "undefined">,
- MetaVarName<"<undefined>">,
- HelpText<"Determines how undefined symbols are handled.">,
- Group<grp_opts>;
-def no_objc_category_merging : Flag<["-"], "no_objc_category_merging">,
- HelpText<"Disables the optimisation which merges Objective-C categories "
- "on a class in to the class itself.">,
- Group<grp_opts>;
-
-// main executable options
-def grp_main : OptionGroup<"opts">, HelpText<"MAIN EXECUTABLE OPTIONS">;
-def entry : Separate<["-"], "e">,
- MetaVarName<"<entry-name>">,
- HelpText<"entry symbol name">,Group<grp_main>;
-def pie : Flag<["-"], "pie">,
- HelpText<"Create Position Independent Executable (for ASLR)">,
- Group<grp_main>;
-def no_pie : Flag<["-"], "no_pie">,
- HelpText<"Do not create Position Independent Executable">,
- Group<grp_main>;
-def stack_size : Separate<["-"], "stack_size">,
- HelpText<"Specifies the maximum stack size for the main thread in a program. "
- "Must be a page-size multiple. (default=8Mb)">,
- Group<grp_main>;
-def export_dynamic : Flag<["-"], "export_dynamic">,
- HelpText<"Preserves all global symbols in main executables during LTO">,
- Group<grp_main>;
-
-// dylib executable options
-def grp_dylib : OptionGroup<"opts">, HelpText<"DYLIB EXECUTABLE OPTIONS">;
-def install_name : Separate<["-"], "install_name">,
- MetaVarName<"<path>">,
- HelpText<"The dylib's install name">, Group<grp_dylib>;
-def mark_dead_strippable_dylib : Flag<["-"], "mark_dead_strippable_dylib">,
- HelpText<"Marks the dylib as having no side effects during initialization">,
- Group<grp_dylib>;
-def compatibility_version : Separate<["-"], "compatibility_version">,
- MetaVarName<"<version>">,
- HelpText<"The dylib's compatibility version">, Group<grp_dylib>;
-def current_version : Separate<["-"], "current_version">,
- MetaVarName<"<version>">,
- HelpText<"The dylib's current version">, Group<grp_dylib>;
-
-// dylib executable options - compatibility aliases
-def dylib_install_name : Separate<["-"], "dylib_install_name">,
- Alias<install_name>;
-def dylib_compatibility_version : Separate<["-"], "dylib_compatibility_version">,
- MetaVarName<"<version>">, Alias<compatibility_version>;
-def dylib_current_version : Separate<["-"], "dylib_current_version">,
- MetaVarName<"<version>">, Alias<current_version>;
-
-// bundle executable options
-def grp_bundle : OptionGroup<"opts">, HelpText<"BUNDLE EXECUTABLE OPTIONS">;
-def bundle_loader : Separate<["-"], "bundle_loader">,
- MetaVarName<"<path>">,
- HelpText<"The executable that will be loading this Mach-O bundle">,
- Group<grp_bundle>;
-
-// library options
-def grp_libs : OptionGroup<"libs">, HelpText<"LIBRARY OPTIONS">;
-def L : JoinedOrSeparate<["-"], "L">,
- MetaVarName<"<dir>">,
- HelpText<"Add directory to library search path">, Group<grp_libs>;
-def F : JoinedOrSeparate<["-"], "F">,
- MetaVarName<"<dir>">,
- HelpText<"Add directory to framework search path">, Group<grp_libs>;
-def Z : Flag<["-"], "Z">,
- HelpText<"Do not search standard directories for libraries or frameworks">;
-def all_load : Flag<["-"], "all_load">,
- HelpText<"Forces all members of all static libraries to be loaded">,
- Group<grp_libs>;
-def force_load : Separate<["-"], "force_load">,
- MetaVarName<"<library-path>">,
- HelpText<"Forces all members of specified static libraries to be loaded">,
- Group<grp_libs>;
-def syslibroot : Separate<["-"], "syslibroot">, MetaVarName<"<dir>">,
- HelpText<"Add path to SDK to all absolute library search paths">,
- Group<grp_libs>;
-
-// Input options
-def l : Joined<["-"], "l">,
- MetaVarName<"<libname>">,
- HelpText<"Base name of library searched for in -L directories">;
-def upward_l : Joined<["-"], "upward-l">,
- MetaVarName<"<libname>">,
- HelpText<"Base name of upward library searched for in -L directories">;
-def framework : Separate<["-"], "framework">,
- MetaVarName<"<name>">,
- HelpText<"Base name of framework searched for in -F directories">;
-def upward_framework : Separate<["-"], "upward_framework">,
- MetaVarName<"<name>">,
- HelpText<"Base name of upward framework searched for in -F directories">;
-def upward_library : Separate<["-"], "upward_library">,
- MetaVarName<"<path>">,
- HelpText<"path to upward dylib to link with">;
-def filelist : Separate<["-"], "filelist">,
- MetaVarName<"<path>">,
- HelpText<"file containing paths to input files">;
-
-
-// test case options
-def print_atoms : Flag<["-"], "print_atoms">,
- HelpText<"Emit output as yaml atoms">;
-def test_file_usage : Flag<["-"], "test_file_usage">,
- HelpText<"Only files specified by -file_exists are considered to exist. "
- "Print which files would be used">;
-def path_exists : Separate<["-"], "path_exists">,
- MetaVarName<"<path>">,
- HelpText<"Used with -test_file_usage to declare a path">;
-
-
-// general options
-def output : Separate<["-"], "o">,
- MetaVarName<"<path>">,
- HelpText<"Output file path">;
-def arch : Separate<["-"], "arch">,
- MetaVarName<"<arch-name>">,
- HelpText<"Architecture to link">;
-def sectalign : MultiArg<["-"], "sectalign", 3>,
- MetaVarName<"<segname> <sectname> <alignment>">,
- HelpText<"Alignment for segment/section">;
-def sectcreate : MultiArg<["-"], "sectcreate", 3>,
- MetaVarName<"<segname> <sectname> <file>">,
- HelpText<"Create section <segname>/<sectname> from contents of <file>">;
-def image_base : Separate<["-"], "image_base">;
-def seg1addr : Separate<["-"], "seg1addr">, Alias<image_base>;
-def demangle : Flag<["-"], "demangle">,
- HelpText<"Demangles symbol names in errors and warnings">;
-def dependency_info : Separate<["-"], "dependency_info">,
- MetaVarName<"<file>">,
- HelpText<"Write binary list of files used during link">;
-def S : Flag<["-"], "S">,
- HelpText<"Remove debug information (STABS or DWARF) from the output file">;
-def rpath : Separate<["-"], "rpath">,
- MetaVarName<"<path>">,
- HelpText<"Add path to the runpath search path list for image being created">;
-
-def t : Flag<["-"], "t">,
- HelpText<"Print the names of the input files as ld processes them">;
-def v : Flag<["-"], "v">,
- HelpText<"Print linker information">;
-def error_limit : Separate<["-", "--"], "error-limit">,
- MetaVarName<"<number>">,
- HelpText<"Maximum number of errors to emit before stopping (0 = no limit)">;
-
-// Ignored options
-def lto_library : Separate<["-"], "lto_library">,
- MetaVarName<"<path>">,
- HelpText<"Ignored for compatibility with other linkers">;
-
-// Obsolete options
-def grp_obsolete : OptionGroup<"obsolete">, HelpText<"OBSOLETE OPTIONS">;
-def single_module : Flag<["-"], "single_module">,
- HelpText<"Default for dylibs">, Group<grp_obsolete>;
-def multi_module : Flag<["-"], "multi_module">,
- HelpText<"Unsupported way to build dylibs">, Group<grp_obsolete>;
-def objc_gc_compaction : Flag<["-"], "objc_gc_compaction">,
- HelpText<"Unsupported ObjC GC option">, Group<grp_obsolete>;
-def objc_gc : Flag<["-"], "objc_gc">,
- HelpText<"Unsupported ObjC GC option">, Group<grp_obsolete>;
-def objc_gc_only : Flag<["-"], "objc_gc_only">,
- HelpText<"Unsupported ObjC GC option">, Group<grp_obsolete>;
+++ /dev/null
-add_subdirectory(MachO)
-add_subdirectory(YAML)
-
-if (MSVC)
- add_definitions(-wd4062) # Suppress 'warning C4062: Enumerator has no associated handler in a switch statement.'
-endif()
-
-add_lld_library(lldReaderWriter
- FileArchive.cpp
-
- ADDITIONAL_HEADER_DIRS
- ${LLD_INCLUDE_DIR}/lld/ReaderWriter
-
- LINK_COMPONENTS
- Object
- Support
-
- LINK_LIBS
- lldCore
- )
+++ /dev/null
-//===- lib/ReaderWriter/FileArchive.cpp -----------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "lld/Common/LLVM.h"
-#include "lld/Core/ArchiveLibraryFile.h"
-#include "lld/Core/File.h"
-#include "lld/Core/Reader.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/BinaryFormat/Magic.h"
-#include "llvm/Object/Archive.h"
-#include "llvm/Object/Error.h"
-#include "llvm/Support/Debug.h"
-#include "llvm/Support/ErrorOr.h"
-#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/Format.h"
-#include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/raw_ostream.h"
-#include <memory>
-#include <set>
-#include <string>
-#include <system_error>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
-using llvm::object::Archive;
-using llvm::file_magic;
-using llvm::identify_magic;
-
-namespace lld {
-
-namespace {
-
-/// The FileArchive class represents an Archive Library file
-class FileArchive : public lld::ArchiveLibraryFile {
-public:
- FileArchive(std::unique_ptr<MemoryBuffer> mb, const Registry ®,
- StringRef path, bool logLoading)
- : ArchiveLibraryFile(path), _mb(std::shared_ptr<MemoryBuffer>(mb.release())),
- _registry(reg), _logLoading(logLoading) {}
-
- /// Check if any member of the archive contains an Atom with the
- /// specified name and return the File object for that member, or nullptr.
- File *find(StringRef name) override {
- auto member = _symbolMemberMap.find(name);
- if (member == _symbolMemberMap.end())
- return nullptr;
- Archive::Child c = member->second;
-
- // Don't return a member already returned
- Expected<StringRef> buf = c.getBuffer();
- if (!buf) {
- // TODO: Actually report errors helpfully.
- consumeError(buf.takeError());
- return nullptr;
- }
- const char *memberStart = buf->data();
- if (_membersInstantiated.count(memberStart))
- return nullptr;
- _membersInstantiated.insert(memberStart);
-
- std::unique_ptr<File> result;
- if (instantiateMember(c, result))
- return nullptr;
-
- File *file = result.get();
- _filesReturned.push_back(std::move(result));
-
- // Give up the file pointer. It was stored and will be destroyed with destruction of FileArchive
- return file;
- }
-
- /// parse each member
- std::error_code
- parseAllMembers(std::vector<std::unique_ptr<File>> &result) override {
- if (std::error_code ec = parse())
- return ec;
- llvm::Error err = llvm::Error::success();
- for (auto mf = _archive->child_begin(err), me = _archive->child_end();
- mf != me; ++mf) {
- std::unique_ptr<File> file;
- if (std::error_code ec = instantiateMember(*mf, file)) {
- // err is Success (or we wouldn't be in the loop body) but we can't
- // return without testing or consuming it.
- consumeError(std::move(err));
- return ec;
- }
- result.push_back(std::move(file));
- }
- if (err)
- return errorToErrorCode(std::move(err));
- return std::error_code();
- }
-
- const AtomRange<DefinedAtom> defined() const override {
- return _noDefinedAtoms;
- }
-
- const AtomRange<UndefinedAtom> undefined() const override {
- return _noUndefinedAtoms;
- }
-
- const AtomRange<SharedLibraryAtom> sharedLibrary() const override {
- return _noSharedLibraryAtoms;
- }
-
- const AtomRange<AbsoluteAtom> absolute() const override {
- return _noAbsoluteAtoms;
- }
-
- void clearAtoms() override {
- _noDefinedAtoms.clear();
- _noUndefinedAtoms.clear();
- _noSharedLibraryAtoms.clear();
- _noAbsoluteAtoms.clear();
- }
-
-protected:
- std::error_code doParse() override {
- // Make Archive object which will be owned by FileArchive object.
- llvm::Error Err = llvm::Error::success();
- _archive.reset(new Archive(_mb->getMemBufferRef(), Err));
- if (Err)
- return errorToErrorCode(std::move(Err));
- std::error_code ec;
- if ((ec = buildTableOfContents()))
- return ec;
- return std::error_code();
- }
-
-private:
- std::error_code instantiateMember(Archive::Child member,
- std::unique_ptr<File> &result) const {
- Expected<llvm::MemoryBufferRef> mbOrErr = member.getMemoryBufferRef();
- if (!mbOrErr)
- return errorToErrorCode(mbOrErr.takeError());
- llvm::MemoryBufferRef mb = mbOrErr.get();
- std::string memberPath = (_archive->getFileName() + "("
- + mb.getBufferIdentifier() + ")").str();
-
- if (_logLoading)
- llvm::errs() << memberPath << "\n";
-
- std::unique_ptr<MemoryBuffer> memberMB(MemoryBuffer::getMemBuffer(
- mb.getBuffer(), mb.getBufferIdentifier(), false));
-
- ErrorOr<std::unique_ptr<File>> fileOrErr =
- _registry.loadFile(std::move(memberMB));
- if (std::error_code ec = fileOrErr.getError())
- return ec;
- result = std::move(fileOrErr.get());
- if (std::error_code ec = result->parse())
- return ec;
- result->setArchivePath(_archive->getFileName());
-
- // The memory buffer is co-owned by the archive file and the children,
- // so that the bufffer is deallocated when all the members are destructed.
- result->setSharedMemoryBuffer(_mb);
- return std::error_code();
- }
-
- std::error_code buildTableOfContents() {
- DEBUG_WITH_TYPE("FileArchive", llvm::dbgs()
- << "Table of contents for archive '"
- << _archive->getFileName() << "':\n");
- for (const Archive::Symbol &sym : _archive->symbols()) {
- StringRef name = sym.getName();
- Expected<Archive::Child> memberOrErr = sym.getMember();
- if (!memberOrErr)
- return errorToErrorCode(memberOrErr.takeError());
- Archive::Child member = memberOrErr.get();
- DEBUG_WITH_TYPE("FileArchive",
- llvm::dbgs()
- << llvm::format("0x%08llX ",
- member.getBuffer()->data())
- << "'" << name << "'\n");
- _symbolMemberMap.insert(std::make_pair(name, member));
- }
- return std::error_code();
- }
-
- typedef std::unordered_map<StringRef, Archive::Child> MemberMap;
- typedef std::set<const char *> InstantiatedSet;
-
- std::shared_ptr<MemoryBuffer> _mb;
- const Registry &_registry;
- std::unique_ptr<Archive> _archive;
- MemberMap _symbolMemberMap;
- InstantiatedSet _membersInstantiated;
- bool _logLoading;
- std::vector<std::unique_ptr<MemoryBuffer>> _memberBuffers;
- std::vector<std::unique_ptr<File>> _filesReturned;
-};
-
-class ArchiveReader : public Reader {
-public:
- ArchiveReader(bool logLoading) : _logLoading(logLoading) {}
-
- bool canParse(file_magic magic, MemoryBufferRef) const override {
- return magic == file_magic::archive;
- }
-
- ErrorOr<std::unique_ptr<File>> loadFile(std::unique_ptr<MemoryBuffer> mb,
- const Registry ®) const override {
- StringRef path = mb->getBufferIdentifier();
- std::unique_ptr<File> ret =
- std::make_unique<FileArchive>(std::move(mb), reg, path, _logLoading);
- return std::move(ret);
- }
-
-private:
- bool _logLoading;
-};
-
-} // anonymous namespace
-
-void Registry::addSupportArchives(bool logLoading) {
- add(std::unique_ptr<Reader>(new ArchiveReader(logLoading)));
-}
-
-} // namespace lld
+++ /dev/null
-//===- lib/FileFormat/MachO/ArchHandler.cpp -------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-
-#include "ArchHandler.h"
-#include "Atoms.h"
-#include "MachONormalizedFileBinaryUtils.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/ADT/StringSwitch.h"
-#include "llvm/ADT/Triple.h"
-#include "llvm/Support/ErrorHandling.h"
-
-using namespace llvm::MachO;
-using namespace lld::mach_o::normalized;
-
-namespace lld {
-namespace mach_o {
-
-
-ArchHandler::ArchHandler() {
-}
-
-ArchHandler::~ArchHandler() {
-}
-
-std::unique_ptr<mach_o::ArchHandler> ArchHandler::create(
- MachOLinkingContext::Arch arch) {
- switch (arch) {
- case MachOLinkingContext::arch_x86_64:
- return create_x86_64();
- case MachOLinkingContext::arch_x86:
- return create_x86();
- case MachOLinkingContext::arch_armv6:
- case MachOLinkingContext::arch_armv7:
- case MachOLinkingContext::arch_armv7s:
- return create_arm();
- case MachOLinkingContext::arch_arm64:
- return create_arm64();
- default:
- llvm_unreachable("Unknown arch");
- }
-}
-
-
-bool ArchHandler::isLazyPointer(const Reference &ref) {
- // A lazy bind entry is needed for a lazy pointer.
- const StubInfo &info = stubInfo();
- if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
- return false;
- if (ref.kindArch() != info.lazyPointerReferenceToFinal.arch)
- return false;
- return (ref.kindValue() == info.lazyPointerReferenceToFinal.kind);
-}
-
-
-ArchHandler::RelocPattern ArchHandler::relocPattern(const Relocation &reloc) {
- assert((reloc.type & 0xFFF0) == 0);
- uint16_t result = reloc.type;
- if (reloc.scattered)
- result |= rScattered;
- if (reloc.pcRel)
- result |= rPcRel;
- if (reloc.isExtern)
- result |= rExtern;
- switch(reloc.length) {
- case 0:
- break;
- case 1:
- result |= rLength2;
- break;
- case 2:
- result |= rLength4;
- break;
- case 3:
- result |= rLength8;
- break;
- default:
- llvm_unreachable("bad r_length");
- }
- return result;
-}
-
-normalized::Relocation
-ArchHandler::relocFromPattern(ArchHandler::RelocPattern pattern) {
- normalized::Relocation result;
- result.offset = 0;
- result.scattered = (pattern & rScattered);
- result.type = (RelocationInfoType)(pattern & 0xF);
- result.pcRel = (pattern & rPcRel);
- result.isExtern = (pattern & rExtern);
- result.value = 0;
- result.symbol = 0;
- switch (pattern & 0x300) {
- case rLength1:
- result.length = 0;
- break;
- case rLength2:
- result.length = 1;
- break;
- case rLength4:
- result.length = 2;
- break;
- case rLength8:
- result.length = 3;
- break;
- }
- return result;
-}
-
-void ArchHandler::appendReloc(normalized::Relocations &relocs, uint32_t offset,
- uint32_t symbol, uint32_t value,
- RelocPattern pattern) {
- normalized::Relocation reloc = relocFromPattern(pattern);
- reloc.offset = offset;
- reloc.symbol = symbol;
- reloc.value = value;
- relocs.push_back(reloc);
-}
-
-
-int16_t ArchHandler::readS16(const uint8_t *addr, bool isBig) {
- return read16(addr, isBig);
-}
-
-int32_t ArchHandler::readS32(const uint8_t *addr, bool isBig) {
- return read32(addr, isBig);
-}
-
-uint32_t ArchHandler::readU32(const uint8_t *addr, bool isBig) {
- return read32(addr, isBig);
-}
-
- int64_t ArchHandler::readS64(const uint8_t *addr, bool isBig) {
- return read64(addr, isBig);
-}
-
-bool ArchHandler::isDwarfCIE(bool isBig, const DefinedAtom *atom) {
- assert(atom->contentType() == DefinedAtom::typeCFI);
- if (atom->rawContent().size() < sizeof(uint32_t))
- return false;
- uint32_t size = read32(atom->rawContent().data(), isBig);
-
- uint32_t idOffset = sizeof(uint32_t);
- if (size == 0xffffffffU)
- idOffset += sizeof(uint64_t);
-
- return read32(atom->rawContent().data() + idOffset, isBig) == 0;
-}
-
-const Atom *ArchHandler::fdeTargetFunction(const DefinedAtom *fde) {
- for (auto ref : *fde) {
- if (ref->kindNamespace() == Reference::KindNamespace::mach_o &&
- ref->kindValue() == unwindRefToFunctionKind()) {
- assert(ref->kindArch() == kindArch() && "unexpected Reference arch");
- return ref->target();
- }
- }
-
- return nullptr;
-}
-
-} // namespace mach_o
-} // namespace lld
-
-
-
+++ /dev/null
-//===- lib/FileFormat/MachO/ArchHandler.h ---------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_READER_WRITER_MACHO_ARCH_HANDLER_H
-#define LLD_READER_WRITER_MACHO_ARCH_HANDLER_H
-
-#include "Atoms.h"
-#include "File.h"
-#include "MachONormalizedFile.h"
-#include "lld/Common/LLVM.h"
-#include "lld/Core/Error.h"
-#include "lld/Core/Reference.h"
-#include "lld/Core/Simple.h"
-#include "lld/ReaderWriter/MachOLinkingContext.h"
-#include "llvm/ADT/Triple.h"
-
-namespace lld {
-namespace mach_o {
-
-///
-/// The ArchHandler class handles all architecture specific aspects of
-/// mach-o linking.
-///
-class ArchHandler {
-public:
- virtual ~ArchHandler();
-
- /// There is no public interface to subclasses of ArchHandler, so this
- /// is the only way to instantiate an ArchHandler.
- static std::unique_ptr<ArchHandler> create(MachOLinkingContext::Arch arch);
-
- /// Get (arch specific) kind strings used by Registry.
- virtual const Registry::KindStrings *kindStrings() = 0;
-
- /// Convert mach-o Arch to Reference::KindArch.
- virtual Reference::KindArch kindArch() = 0;
-
- /// Used by StubPass to update References to shared library functions
- /// to be references to a stub.
- virtual bool isCallSite(const Reference &) = 0;
-
- /// Used by GOTPass to locate GOT References
- virtual bool isGOTAccess(const Reference &, bool &canBypassGOT) {
- return false;
- }
-
- /// Used by TLVPass to locate TLV References.
- virtual bool isTLVAccess(const Reference &) const { return false; }
-
- /// Used by the TLVPass to update TLV References.
- virtual void updateReferenceToTLV(const Reference *) {}
-
- /// Used by ShimPass to insert shims in branches that switch mode.
- virtual bool isNonCallBranch(const Reference &) = 0;
-
- /// Used by GOTPass to update GOT References
- virtual void updateReferenceToGOT(const Reference *, bool targetIsNowGOT) {}
-
- /// Does this architecture make use of __unwind_info sections for exception
- /// handling? If so, it will need a separate pass to create them.
- virtual bool needsCompactUnwind() = 0;
-
- /// Returns the kind of reference to use to synthesize a 32-bit image-offset
- /// value, used in the __unwind_info section.
- virtual Reference::KindValue imageOffsetKind() = 0;
-
- /// Returns the kind of reference to use to synthesize a 32-bit image-offset
- /// indirect value. Used for personality functions in the __unwind_info
- /// section.
- virtual Reference::KindValue imageOffsetKindIndirect() = 0;
-
- /// Architecture specific compact unwind type that signals __eh_frame should
- /// actually be used.
- virtual uint32_t dwarfCompactUnwindType() = 0;
-
- /// Reference from an __eh_frame CIE atom to its personality function it's
- /// describing. Usually pointer-sized and PC-relative, but differs in whether
- /// it needs to be in relocatable objects.
- virtual Reference::KindValue unwindRefToPersonalityFunctionKind() = 0;
-
- /// Reference from an __eh_frame FDE to the CIE it's based on.
- virtual Reference::KindValue unwindRefToCIEKind() = 0;
-
- /// Reference from an __eh_frame FDE atom to the function it's
- /// describing. Usually pointer-sized and PC-relative, but differs in whether
- /// it needs to be in relocatable objects.
- virtual Reference::KindValue unwindRefToFunctionKind() = 0;
-
- /// Reference from an __unwind_info entry of dwarfCompactUnwindType to the
- /// required __eh_frame entry. On current architectures, the low 24 bits
- /// represent the offset of the function's FDE entry from the start of
- /// __eh_frame.
- virtual Reference::KindValue unwindRefToEhFrameKind() = 0;
-
- /// Returns a pointer sized reference kind. On 64-bit targets this will
- /// likely be something like pointer64, and pointer32 on 32-bit targets.
- virtual Reference::KindValue pointerKind() = 0;
-
- virtual const Atom *fdeTargetFunction(const DefinedAtom *fde);
-
- /// Used by normalizedFromAtoms() to know where to generated rebasing and
- /// binding info in final executables.
- virtual bool isPointer(const Reference &) = 0;
-
- /// Used by normalizedFromAtoms() to know where to generated lazy binding
- /// info in final executables.
- virtual bool isLazyPointer(const Reference &);
-
- /// Reference from an __stub_helper entry to the required offset of the
- /// lazy bind commands.
- virtual Reference::KindValue lazyImmediateLocationKind() = 0;
-
- /// Returns true if the specified relocation is paired to the next relocation.
- virtual bool isPairedReloc(const normalized::Relocation &) = 0;
-
- /// Prototype for a helper function. Given a sectionIndex and address,
- /// finds the atom and offset with that atom of that address.
- typedef std::function<llvm::Error (uint32_t sectionIndex, uint64_t addr,
- const lld::Atom **, Reference::Addend *)>
- FindAtomBySectionAndAddress;
-
- /// Prototype for a helper function. Given a symbolIndex, finds the atom
- /// representing that symbol.
- typedef std::function<llvm::Error (uint32_t symbolIndex,
- const lld::Atom **)> FindAtomBySymbolIndex;
-
- /// Analyzes a relocation from a .o file and returns the info
- /// (kind, target, addend) needed to instantiate a Reference.
- /// Two helper functions are passed as parameters to find the target atom
- /// given a symbol index or address.
- virtual llvm::Error
- getReferenceInfo(const normalized::Relocation &reloc,
- const DefinedAtom *inAtom,
- uint32_t offsetInAtom,
- uint64_t fixupAddress, bool isBigEndian,
- FindAtomBySectionAndAddress atomFromAddress,
- FindAtomBySymbolIndex atomFromSymbolIndex,
- Reference::KindValue *kind,
- const lld::Atom **target,
- Reference::Addend *addend) = 0;
-
- /// Analyzes a pair of relocations from a .o file and returns the info
- /// (kind, target, addend) needed to instantiate a Reference.
- /// Two helper functions are passed as parameters to find the target atom
- /// given a symbol index or address.
- virtual llvm::Error
- getPairReferenceInfo(const normalized::Relocation &reloc1,
- const normalized::Relocation &reloc2,
- const DefinedAtom *inAtom,
- uint32_t offsetInAtom,
- uint64_t fixupAddress, bool isBig, bool scatterable,
- FindAtomBySectionAndAddress atomFromAddress,
- FindAtomBySymbolIndex atomFromSymbolIndex,
- Reference::KindValue *kind,
- const lld::Atom **target,
- Reference::Addend *addend) = 0;
-
- /// Prototype for a helper function. Given an atom, finds the symbol table
- /// index for it in the output file.
- typedef std::function<uint32_t (const Atom &atom)> FindSymbolIndexForAtom;
-
- /// Prototype for a helper function. Given an atom, finds the index
- /// of the section that will contain the atom.
- typedef std::function<uint32_t (const Atom &atom)> FindSectionIndexForAtom;
-
- /// Prototype for a helper function. Given an atom, finds the address
- /// assigned to it in the output file.
- typedef std::function<uint64_t (const Atom &atom)> FindAddressForAtom;
-
- /// Some architectures require local symbols on anonymous atoms.
- virtual bool needsLocalSymbolInRelocatableFile(const DefinedAtom *atom) {
- return false;
- }
-
- /// Copy raw content then apply all fixup References on an Atom.
- virtual void generateAtomContent(const DefinedAtom &atom, bool relocatable,
- FindAddressForAtom findAddress,
- FindAddressForAtom findSectionAddress,
- uint64_t imageBaseAddress,
- llvm::MutableArrayRef<uint8_t> atomContentBuffer) = 0;
-
- /// Used in -r mode to convert a Reference to a mach-o relocation.
- virtual void appendSectionRelocations(const DefinedAtom &atom,
- uint64_t atomSectionOffset,
- const Reference &ref,
- FindSymbolIndexForAtom,
- FindSectionIndexForAtom,
- FindAddressForAtom,
- normalized::Relocations&) = 0;
-
- /// Add arch-specific References.
- virtual void addAdditionalReferences(MachODefinedAtom &atom) { }
-
- // Add Reference for data-in-code marker.
- virtual void addDataInCodeReference(MachODefinedAtom &atom, uint32_t atomOff,
- uint16_t length, uint16_t kind) { }
-
- /// Returns true if the specificed Reference value marks the start or end
- /// of a data-in-code range in an atom.
- virtual bool isDataInCodeTransition(Reference::KindValue refKind) {
- return false;
- }
-
- /// Returns the Reference value for a Reference that marks that start of
- /// a data-in-code range.
- virtual Reference::KindValue dataInCodeTransitionStart(
- const MachODefinedAtom &atom) {
- return 0;
- }
-
- /// Returns the Reference value for a Reference that marks that end of
- /// a data-in-code range.
- virtual Reference::KindValue dataInCodeTransitionEnd(
- const MachODefinedAtom &atom) {
- return 0;
- }
-
- /// Only relevant for 32-bit arm archs.
- virtual bool isThumbFunction(const DefinedAtom &atom) { return false; }
-
- /// Only relevant for 32-bit arm archs.
- virtual const DefinedAtom *createShim(MachOFile &file, bool thumbToArm,
- const DefinedAtom &) {
- llvm_unreachable("shims only support on arm");
- }
-
- /// Does a given unwind-cfi atom represent a CIE (as opposed to an FDE).
- static bool isDwarfCIE(bool isBig, const DefinedAtom *atom);
-
- struct ReferenceInfo {
- Reference::KindArch arch;
- uint16_t kind;
- uint32_t offset;
- int32_t addend;
- };
-
- struct OptionalRefInfo {
- bool used;
- uint16_t kind;
- uint32_t offset;
- int32_t addend;
- };
-
- /// Table of architecture specific information for creating stubs.
- struct StubInfo {
- const char* binderSymbolName;
- ReferenceInfo lazyPointerReferenceToHelper;
- ReferenceInfo lazyPointerReferenceToFinal;
- ReferenceInfo nonLazyPointerReferenceToBinder;
- uint8_t codeAlignment;
-
- uint32_t stubSize;
- uint8_t stubBytes[16];
- ReferenceInfo stubReferenceToLP;
- OptionalRefInfo optStubReferenceToLP;
-
- uint32_t stubHelperSize;
- uint8_t stubHelperBytes[16];
- ReferenceInfo stubHelperReferenceToImm;
- ReferenceInfo stubHelperReferenceToHelperCommon;
-
- DefinedAtom::ContentType stubHelperImageCacheContentType;
-
- uint32_t stubHelperCommonSize;
- uint8_t stubHelperCommonAlignment;
- uint8_t stubHelperCommonBytes[36];
- ReferenceInfo stubHelperCommonReferenceToCache;
- OptionalRefInfo optStubHelperCommonReferenceToCache;
- ReferenceInfo stubHelperCommonReferenceToBinder;
- OptionalRefInfo optStubHelperCommonReferenceToBinder;
- };
-
- virtual const StubInfo &stubInfo() = 0;
-
-protected:
- ArchHandler();
-
- static std::unique_ptr<mach_o::ArchHandler> create_x86_64();
- static std::unique_ptr<mach_o::ArchHandler> create_x86();
- static std::unique_ptr<mach_o::ArchHandler> create_arm();
- static std::unique_ptr<mach_o::ArchHandler> create_arm64();
-
- // Handy way to pack mach-o r_type and other bit fields into one 16-bit value.
- typedef uint16_t RelocPattern;
- enum {
- rScattered = 0x8000,
- rPcRel = 0x4000,
- rExtern = 0x2000,
- rLength1 = 0x0000,
- rLength2 = 0x0100,
- rLength4 = 0x0200,
- rLength8 = 0x0300,
- rLenArmLo = rLength1,
- rLenArmHi = rLength2,
- rLenThmbLo = rLength4,
- rLenThmbHi = rLength8
- };
- /// Extract RelocPattern from normalized mach-o relocation.
- static RelocPattern relocPattern(const normalized::Relocation &reloc);
- /// Create normalized Relocation initialized from pattern.
- static normalized::Relocation relocFromPattern(RelocPattern pattern);
- /// One liner to add a relocation.
- static void appendReloc(normalized::Relocations &relocs, uint32_t offset,
- uint32_t symbol, uint32_t value,
- RelocPattern pattern);
-
-
- static int16_t readS16(const uint8_t *addr, bool isBig);
- static int32_t readS32(const uint8_t *addr, bool isBig);
- static uint32_t readU32(const uint8_t *addr, bool isBig);
- static int64_t readS64(const uint8_t *addr, bool isBig);
-};
-
-} // namespace mach_o
-} // namespace lld
-
-#endif // LLD_READER_WRITER_MACHO_ARCH_HANDLER_H
+++ /dev/null
-//===- lib/FileFormat/MachO/ArchHandler_arm.cpp ---------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "ArchHandler.h"
-#include "Atoms.h"
-#include "MachONormalizedFileBinaryUtils.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/ADT/StringSwitch.h"
-#include "llvm/ADT/Triple.h"
-#include "llvm/Support/Endian.h"
-#include "llvm/Support/ErrorHandling.h"
-
-using namespace llvm::MachO;
-using namespace lld::mach_o::normalized;
-
-namespace lld {
-namespace mach_o {
-
-using llvm::support::ulittle32_t;
-using llvm::support::little32_t;
-
-
-class ArchHandler_arm : public ArchHandler {
-public:
- ArchHandler_arm() = default;
- ~ArchHandler_arm() override = default;
-
- const Registry::KindStrings *kindStrings() override { return _sKindStrings; }
-
- Reference::KindArch kindArch() override { return Reference::KindArch::ARM; }
-
- const ArchHandler::StubInfo &stubInfo() override;
- bool isCallSite(const Reference &) override;
- bool isPointer(const Reference &) override;
- bool isPairedReloc(const normalized::Relocation &) override;
- bool isNonCallBranch(const Reference &) override;
-
- bool needsCompactUnwind() override {
- return false;
- }
- Reference::KindValue imageOffsetKind() override {
- return invalid;
- }
- Reference::KindValue imageOffsetKindIndirect() override {
- return invalid;
- }
-
- Reference::KindValue unwindRefToPersonalityFunctionKind() override {
- return invalid;
- }
-
- Reference::KindValue unwindRefToCIEKind() override {
- return invalid;
- }
-
- Reference::KindValue unwindRefToFunctionKind() override {
- return invalid;
- }
-
- Reference::KindValue unwindRefToEhFrameKind() override {
- return invalid;
- }
-
- Reference::KindValue lazyImmediateLocationKind() override {
- return lazyImmediateLocation;
- }
-
- Reference::KindValue pointerKind() override {
- return invalid;
- }
-
- uint32_t dwarfCompactUnwindType() override {
- // FIXME
- return -1;
- }
-
- llvm::Error getReferenceInfo(const normalized::Relocation &reloc,
- const DefinedAtom *inAtom,
- uint32_t offsetInAtom,
- uint64_t fixupAddress, bool swap,
- FindAtomBySectionAndAddress atomFromAddress,
- FindAtomBySymbolIndex atomFromSymbolIndex,
- Reference::KindValue *kind,
- const lld::Atom **target,
- Reference::Addend *addend) override;
- llvm::Error
- getPairReferenceInfo(const normalized::Relocation &reloc1,
- const normalized::Relocation &reloc2,
- const DefinedAtom *inAtom,
- uint32_t offsetInAtom,
- uint64_t fixupAddress, bool swap, bool scatterable,
- FindAtomBySectionAndAddress atomFromAddress,
- FindAtomBySymbolIndex atomFromSymbolIndex,
- Reference::KindValue *kind,
- const lld::Atom **target,
- Reference::Addend *addend) override;
-
- void generateAtomContent(const DefinedAtom &atom, bool relocatable,
- FindAddressForAtom findAddress,
- FindAddressForAtom findSectionAddress,
- uint64_t imageBaseAddress,
- llvm::MutableArrayRef<uint8_t> atomContentBuffer) override;
-
- void appendSectionRelocations(const DefinedAtom &atom,
- uint64_t atomSectionOffset,
- const Reference &ref,
- FindSymbolIndexForAtom,
- FindSectionIndexForAtom,
- FindAddressForAtom,
- normalized::Relocations &) override;
-
- void addAdditionalReferences(MachODefinedAtom &atom) override;
-
- bool isDataInCodeTransition(Reference::KindValue refKind) override {
- switch (refKind) {
- case modeThumbCode:
- case modeArmCode:
- case modeData:
- return true;
- default:
- return false;
- break;
- }
- }
-
- Reference::KindValue dataInCodeTransitionStart(
- const MachODefinedAtom &atom) override {
- return modeData;
- }
-
- Reference::KindValue dataInCodeTransitionEnd(
- const MachODefinedAtom &atom) override {
- return atom.isThumb() ? modeThumbCode : modeArmCode;
- }
-
- bool isThumbFunction(const DefinedAtom &atom) override;
- const DefinedAtom *createShim(MachOFile &file, bool thumbToArm,
- const DefinedAtom &) override;
-
-private:
- friend class Thumb2ToArmShimAtom;
- friend class ArmToThumbShimAtom;
-
- static const Registry::KindStrings _sKindStrings[];
- static const StubInfo _sStubInfoArmPIC;
-
- enum ArmKind : Reference::KindValue {
- invalid, /// for error condition
-
- modeThumbCode, /// Content starting at this offset is thumb.
- modeArmCode, /// Content starting at this offset is arm.
- modeData, /// Content starting at this offset is data.
-
- // Kinds found in mach-o .o files:
- thumb_bl22, /// ex: bl _foo
- thumb_b22, /// ex: b _foo
- thumb_movw, /// ex: movw r1, :lower16:_foo
- thumb_movt, /// ex: movt r1, :lower16:_foo
- thumb_movw_funcRel, /// ex: movw r1, :lower16:(_foo-(L1+4))
- thumb_movt_funcRel, /// ex: movt r1, :upper16:(_foo-(L1+4))
- arm_bl24, /// ex: bl _foo
- arm_b24, /// ex: b _foo
- arm_movw, /// ex: movw r1, :lower16:_foo
- arm_movt, /// ex: movt r1, :lower16:_foo
- arm_movw_funcRel, /// ex: movw r1, :lower16:(_foo-(L1+4))
- arm_movt_funcRel, /// ex: movt r1, :upper16:(_foo-(L1+4))
- pointer32, /// ex: .long _foo
- delta32, /// ex: .long _foo - .
-
- // Kinds introduced by Passes:
- lazyPointer, /// Location contains a lazy pointer.
- lazyImmediateLocation, /// Location contains immediate value used in stub.
- };
-
- // Utility functions for inspecting/updating instructions.
- static bool isThumbMovw(uint32_t instruction);
- static bool isThumbMovt(uint32_t instruction);
- static bool isArmMovw(uint32_t instruction);
- static bool isArmMovt(uint32_t instruction);
- static int32_t getDisplacementFromThumbBranch(uint32_t instruction, uint32_t);
- static int32_t getDisplacementFromArmBranch(uint32_t instruction);
- static uint16_t getWordFromThumbMov(uint32_t instruction);
- static uint16_t getWordFromArmMov(uint32_t instruction);
- static uint32_t clearThumbBit(uint32_t value, const Atom *target);
- static uint32_t setDisplacementInArmBranch(uint32_t instr, int32_t disp,
- bool targetIsThumb);
- static uint32_t setDisplacementInThumbBranch(uint32_t instr, uint32_t ia,
- int32_t disp, bool targetThumb);
- static uint32_t setWordFromThumbMov(uint32_t instruction, uint16_t word);
- static uint32_t setWordFromArmMov(uint32_t instruction, uint16_t word);
-
- StringRef stubName(const DefinedAtom &);
- bool useExternalRelocationTo(const Atom &target);
-
- void applyFixupFinal(const Reference &ref, uint8_t *location,
- uint64_t fixupAddress, uint64_t targetAddress,
- uint64_t inAtomAddress, bool &thumbMode,
- bool targetIsThumb);
-
- void applyFixupRelocatable(const Reference &ref, uint8_t *location,
- uint64_t fixupAddress,
- uint64_t targetAddress,
- uint64_t inAtomAddress, bool &thumbMode,
- bool targetIsThumb);
-};
-
-//===----------------------------------------------------------------------===//
-// ArchHandler_arm
-//===----------------------------------------------------------------------===//
-
-const Registry::KindStrings ArchHandler_arm::_sKindStrings[] = {
- LLD_KIND_STRING_ENTRY(invalid),
- LLD_KIND_STRING_ENTRY(modeThumbCode),
- LLD_KIND_STRING_ENTRY(modeArmCode),
- LLD_KIND_STRING_ENTRY(modeData),
- LLD_KIND_STRING_ENTRY(thumb_bl22),
- LLD_KIND_STRING_ENTRY(thumb_b22),
- LLD_KIND_STRING_ENTRY(thumb_movw),
- LLD_KIND_STRING_ENTRY(thumb_movt),
- LLD_KIND_STRING_ENTRY(thumb_movw_funcRel),
- LLD_KIND_STRING_ENTRY(thumb_movt_funcRel),
- LLD_KIND_STRING_ENTRY(arm_bl24),
- LLD_KIND_STRING_ENTRY(arm_b24),
- LLD_KIND_STRING_ENTRY(arm_movw),
- LLD_KIND_STRING_ENTRY(arm_movt),
- LLD_KIND_STRING_ENTRY(arm_movw_funcRel),
- LLD_KIND_STRING_ENTRY(arm_movt_funcRel),
- LLD_KIND_STRING_ENTRY(pointer32),
- LLD_KIND_STRING_ENTRY(delta32),
- LLD_KIND_STRING_ENTRY(lazyPointer),
- LLD_KIND_STRING_ENTRY(lazyImmediateLocation),
- LLD_KIND_STRING_END
-};
-
-const ArchHandler::StubInfo ArchHandler_arm::_sStubInfoArmPIC = {
- "dyld_stub_binder",
-
- // References in lazy pointer
- { Reference::KindArch::ARM, pointer32, 0, 0 },
- { Reference::KindArch::ARM, lazyPointer, 0, 0 },
-
- // GOT pointer to dyld_stub_binder
- { Reference::KindArch::ARM, pointer32, 0, 0 },
-
- // arm code alignment 2^2
- 2,
-
- // Stub size and code
- 16,
- { 0x04, 0xC0, 0x9F, 0xE5, // ldr ip, pc + 12
- 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip
- 0x00, 0xF0, 0x9C, 0xE5, // ldr pc, [ip]
- 0x00, 0x00, 0x00, 0x00 }, // .long L_foo$lazy_ptr - (L1$scv + 8)
- { Reference::KindArch::ARM, delta32, 12, 0 },
- { false, 0, 0, 0 },
-
- // Stub Helper size and code
- 12,
- { 0x00, 0xC0, 0x9F, 0xE5, // ldr ip, [pc, #0]
- 0x00, 0x00, 0x00, 0xEA, // b _helperhelper
- 0x00, 0x00, 0x00, 0x00 }, // .long lazy-info-offset
- { Reference::KindArch::ARM, lazyImmediateLocation, 8, 0 },
- { Reference::KindArch::ARM, arm_b24, 4, 0 },
-
- // Stub helper image cache content type
- DefinedAtom::typeGOT,
-
- // Stub Helper-Common size and code
- 36,
- // Stub helper alignment
- 2,
- { // push lazy-info-offset
- 0x04, 0xC0, 0x2D, 0xE5, // str ip, [sp, #-4]!
- // push address of dyld_mageLoaderCache
- 0x10, 0xC0, 0x9F, 0xE5, // ldr ip, L1
- 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip
- 0x04, 0xC0, 0x2D, 0xE5, // str ip, [sp, #-4]!
- // jump through dyld_stub_binder
- 0x08, 0xC0, 0x9F, 0xE5, // ldr ip, L2
- 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip
- 0x00, 0xF0, 0x9C, 0xE5, // ldr pc, [ip]
- 0x00, 0x00, 0x00, 0x00, // L1: .long fFastStubGOTAtom - (helper+16)
- 0x00, 0x00, 0x00, 0x00 }, // L2: .long dyld_stub_binder - (helper+28)
- { Reference::KindArch::ARM, delta32, 28, 0xC },
- { false, 0, 0, 0 },
- { Reference::KindArch::ARM, delta32, 32, 0x04 },
- { false, 0, 0, 0 }
-};
-
-const ArchHandler::StubInfo &ArchHandler_arm::stubInfo() {
- // If multiple kinds of stubs are supported, select which StubInfo here.
- return _sStubInfoArmPIC;
-}
-
-bool ArchHandler_arm::isCallSite(const Reference &ref) {
- switch (ref.kindValue()) {
- case thumb_b22:
- case thumb_bl22:
- case arm_b24:
- case arm_bl24:
- return true;
- default:
- return false;
- }
-}
-
-bool ArchHandler_arm::isPointer(const Reference &ref) {
- return (ref.kindValue() == pointer32);
-}
-
-bool ArchHandler_arm::isNonCallBranch(const Reference &ref) {
- switch (ref.kindValue()) {
- case thumb_b22:
- case arm_b24:
- return true;
- default:
- return false;
- }
-}
-
-bool ArchHandler_arm::isPairedReloc(const Relocation &reloc) {
- switch (reloc.type) {
- case ARM_RELOC_SECTDIFF:
- case ARM_RELOC_LOCAL_SECTDIFF:
- case ARM_RELOC_HALF_SECTDIFF:
- case ARM_RELOC_HALF:
- return true;
- default:
- return false;
- }
-}
-
-/// Trace references from stub atom to lazy pointer to target and get its name.
-StringRef ArchHandler_arm::stubName(const DefinedAtom &stubAtom) {
- assert(stubAtom.contentType() == DefinedAtom::typeStub);
- for (const Reference *ref : stubAtom) {
- if (const DefinedAtom* lp = dyn_cast<DefinedAtom>(ref->target())) {
- if (lp->contentType() != DefinedAtom::typeLazyPointer)
- continue;
- for (const Reference *ref2 : *lp) {
- if (ref2->kindValue() != lazyPointer)
- continue;
- return ref2->target()->name();
- }
- }
- }
- return "stub";
-}
-
-/// Extract displacement from an ARM b/bl/blx instruction.
-int32_t ArchHandler_arm::getDisplacementFromArmBranch(uint32_t instruction) {
- // Sign-extend imm24
- int32_t displacement = (instruction & 0x00FFFFFF) << 2;
- if ((displacement & 0x02000000) != 0)
- displacement |= 0xFC000000;
- // If this is BLX and H bit set, add 2.
- if ((instruction & 0xFF000000) == 0xFB000000)
- displacement += 2;
- return displacement;
-}
-
-/// Update an ARM b/bl/blx instruction, switching bl <-> blx as needed.
-uint32_t ArchHandler_arm::setDisplacementInArmBranch(uint32_t instruction,
- int32_t displacement,
- bool targetIsThumb) {
- assert((displacement <= 33554428) && (displacement > (-33554432))
- && "arm branch out of range");
- bool is_blx = ((instruction & 0xF0000000) == 0xF0000000);
- uint32_t newInstruction = (instruction & 0xFF000000);
- uint32_t h = 0;
- if (targetIsThumb) {
- // Force use of BLX.
- newInstruction = 0xFA000000;
- if (!is_blx) {
- assert(((instruction & 0xF0000000) == 0xE0000000)
- && "no conditional arm blx");
- assert(((instruction & 0xFF000000) == 0xEB000000)
- && "no arm pc-rel BX instruction");
- }
- if (displacement & 2)
- h = 1;
- }
- else {
- // Force use of B/BL.
- if (is_blx)
- newInstruction = 0xEB000000;
- }
- newInstruction |= (h << 24) | ((displacement >> 2) & 0x00FFFFFF);
- return newInstruction;
-}
-
-/// Extract displacement from a thumb b/bl/blx instruction.
-int32_t ArchHandler_arm::getDisplacementFromThumbBranch(uint32_t instruction,
- uint32_t instrAddr) {
- bool is_blx = ((instruction & 0xD000F800) == 0xC000F000);
- uint32_t s = (instruction >> 10) & 0x1;
- uint32_t j1 = (instruction >> 29) & 0x1;
- uint32_t j2 = (instruction >> 27) & 0x1;
- uint32_t imm10 = instruction & 0x3FF;
- uint32_t imm11 = (instruction >> 16) & 0x7FF;
- uint32_t i1 = (j1 == s);
- uint32_t i2 = (j2 == s);
- uint32_t dis =
- (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1);
- int32_t sdis = dis;
- int32_t result = s ? (sdis | 0xFE000000) : sdis;
- if (is_blx && (instrAddr & 0x2)) {
- // The thumb blx instruction always has low bit of imm11 as zero. The way
- // a 2-byte aligned blx can branch to a 4-byte aligned ARM target is that
- // the blx instruction always 4-byte aligns the pc before adding the
- // displacement from the blx. We must emulate that when decoding this.
- result -= 2;
- }
- return result;
-}
-
-/// Update a thumb b/bl/blx instruction, switching bl <-> blx as needed.
-uint32_t ArchHandler_arm::setDisplacementInThumbBranch(uint32_t instruction,
- uint32_t instrAddr,
- int32_t displacement,
- bool targetIsThumb) {
- assert((displacement <= 16777214) && (displacement > (-16777216))
- && "thumb branch out of range");
- bool is_bl = ((instruction & 0xD000F800) == 0xD000F000);
- bool is_blx = ((instruction & 0xD000F800) == 0xC000F000);
- bool is_b = ((instruction & 0xD000F800) == 0x9000F000);
- uint32_t newInstruction = (instruction & 0xD000F800);
- if (is_bl || is_blx) {
- if (targetIsThumb) {
- newInstruction = 0xD000F000; // Use bl
- } else {
- newInstruction = 0xC000F000; // Use blx
- // See note in getDisplacementFromThumbBranch() about blx.
- if (instrAddr & 0x2)
- displacement += 2;
- }
- } else if (is_b) {
- assert(targetIsThumb && "no pc-rel thumb branch instruction that "
- "switches to arm mode");
- }
- else {
- llvm_unreachable("thumb branch22 reloc on a non-branch instruction");
- }
- uint32_t s = (uint32_t)(displacement >> 24) & 0x1;
- uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1;
- uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1;
- uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF;
- uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF;
- uint32_t j1 = (i1 == s);
- uint32_t j2 = (i2 == s);
- uint32_t nextDisp = (j1 << 13) | (j2 << 11) | imm11;
- uint32_t firstDisp = (s << 10) | imm10;
- newInstruction |= (nextDisp << 16) | firstDisp;
- return newInstruction;
-}
-
-bool ArchHandler_arm::isThumbMovw(uint32_t instruction) {
- return (instruction & 0x8000FBF0) == 0x0000F240;
-}
-
-bool ArchHandler_arm::isThumbMovt(uint32_t instruction) {
- return (instruction & 0x8000FBF0) == 0x0000F2C0;
-}
-
-bool ArchHandler_arm::isArmMovw(uint32_t instruction) {
- return (instruction & 0x0FF00000) == 0x03000000;
-}
-
-bool ArchHandler_arm::isArmMovt(uint32_t instruction) {
- return (instruction & 0x0FF00000) == 0x03400000;
-}
-
-uint16_t ArchHandler_arm::getWordFromThumbMov(uint32_t instruction) {
- assert(isThumbMovw(instruction) || isThumbMovt(instruction));
- uint32_t i = ((instruction & 0x00000400) >> 10);
- uint32_t imm4 = (instruction & 0x0000000F);
- uint32_t imm3 = ((instruction & 0x70000000) >> 28);
- uint32_t imm8 = ((instruction & 0x00FF0000) >> 16);
- return (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8;
-}
-
-uint16_t ArchHandler_arm::getWordFromArmMov(uint32_t instruction) {
- assert(isArmMovw(instruction) || isArmMovt(instruction));
- uint32_t imm4 = ((instruction & 0x000F0000) >> 16);
- uint32_t imm12 = (instruction & 0x00000FFF);
- return (imm4 << 12) | imm12;
-}
-
-uint32_t ArchHandler_arm::setWordFromThumbMov(uint32_t instr, uint16_t word) {
- assert(isThumbMovw(instr) || isThumbMovt(instr));
- uint32_t imm4 = (word & 0xF000) >> 12;
- uint32_t i = (word & 0x0800) >> 11;
- uint32_t imm3 = (word & 0x0700) >> 8;
- uint32_t imm8 = word & 0x00FF;
- return (instr & 0x8F00FBF0) | imm4 | (i << 10) | (imm3 << 28) | (imm8 << 16);
-}
-
-uint32_t ArchHandler_arm::setWordFromArmMov(uint32_t instr, uint16_t word) {
- assert(isArmMovw(instr) || isArmMovt(instr));
- uint32_t imm4 = (word & 0xF000) >> 12;
- uint32_t imm12 = word & 0x0FFF;
- return (instr & 0xFFF0F000) | (imm4 << 16) | imm12;
-}
-
-uint32_t ArchHandler_arm::clearThumbBit(uint32_t value, const Atom *target) {
- // The assembler often adds one to the address of a thumb function.
- // We need to undo that so it does not look like an addend.
- if (value & 1) {
- if (isa<DefinedAtom>(target)) {
- const MachODefinedAtom *machoTarget =
- reinterpret_cast<const MachODefinedAtom *>(target);
- if (machoTarget->isThumb())
- value &= -2; // mask off thumb-bit
- }
- }
- return value;
-}
-
-llvm::Error ArchHandler_arm::getReferenceInfo(
- const Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom,
- uint64_t fixupAddress, bool isBig,
- FindAtomBySectionAndAddress atomFromAddress,
- FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind,
- const lld::Atom **target, Reference::Addend *addend) {
- const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
- uint64_t targetAddress;
- uint32_t instruction = *(const ulittle32_t *)fixupContent;
- int32_t displacement;
- switch (relocPattern(reloc)) {
- case ARM_THUMB_RELOC_BR22 | rPcRel | rExtern | rLength4:
- // ex: bl _foo (and _foo is undefined)
- if ((instruction & 0xD000F800) == 0x9000F000)
- *kind = thumb_b22;
- else
- *kind = thumb_bl22;
- if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
- return ec;
- // Instruction contains branch to addend.
- displacement = getDisplacementFromThumbBranch(instruction, fixupAddress);
- *addend = fixupAddress + 4 + displacement;
- return llvm::Error::success();
- case ARM_THUMB_RELOC_BR22 | rPcRel | rLength4:
- // ex: bl _foo (and _foo is defined)
- if ((instruction & 0xD000F800) == 0x9000F000)
- *kind = thumb_b22;
- else
- *kind = thumb_bl22;
- displacement = getDisplacementFromThumbBranch(instruction, fixupAddress);
- targetAddress = fixupAddress + 4 + displacement;
- return atomFromAddress(reloc.symbol, targetAddress, target, addend);
- case ARM_THUMB_RELOC_BR22 | rScattered | rPcRel | rLength4:
- // ex: bl _foo+4 (and _foo is defined)
- if ((instruction & 0xD000F800) == 0x9000F000)
- *kind = thumb_b22;
- else
- *kind = thumb_bl22;
- displacement = getDisplacementFromThumbBranch(instruction, fixupAddress);
- targetAddress = fixupAddress + 4 + displacement;
- if (auto ec = atomFromAddress(0, reloc.value, target, addend))
- return ec;
- // reloc.value is target atom's address. Instruction contains branch
- // to atom+addend.
- *addend += (targetAddress - reloc.value);
- return llvm::Error::success();
- case ARM_RELOC_BR24 | rPcRel | rExtern | rLength4:
- // ex: bl _foo (and _foo is undefined)
- if (((instruction & 0x0F000000) == 0x0A000000)
- && ((instruction & 0xF0000000) != 0xF0000000))
- *kind = arm_b24;
- else
- *kind = arm_bl24;
- if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
- return ec;
- // Instruction contains branch to addend.
- displacement = getDisplacementFromArmBranch(instruction);
- *addend = fixupAddress + 8 + displacement;
- return llvm::Error::success();
- case ARM_RELOC_BR24 | rPcRel | rLength4:
- // ex: bl _foo (and _foo is defined)
- if (((instruction & 0x0F000000) == 0x0A000000)
- && ((instruction & 0xF0000000) != 0xF0000000))
- *kind = arm_b24;
- else
- *kind = arm_bl24;
- displacement = getDisplacementFromArmBranch(instruction);
- targetAddress = fixupAddress + 8 + displacement;
- return atomFromAddress(reloc.symbol, targetAddress, target, addend);
- case ARM_RELOC_BR24 | rScattered | rPcRel | rLength4:
- // ex: bl _foo+4 (and _foo is defined)
- if (((instruction & 0x0F000000) == 0x0A000000)
- && ((instruction & 0xF0000000) != 0xF0000000))
- *kind = arm_b24;
- else
- *kind = arm_bl24;
- displacement = getDisplacementFromArmBranch(instruction);
- targetAddress = fixupAddress + 8 + displacement;
- if (auto ec = atomFromAddress(0, reloc.value, target, addend))
- return ec;
- // reloc.value is target atom's address. Instruction contains branch
- // to atom+addend.
- *addend += (targetAddress - reloc.value);
- return llvm::Error::success();
- case ARM_RELOC_VANILLA | rExtern | rLength4:
- // ex: .long _foo (and _foo is undefined)
- *kind = pointer32;
- if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
- return ec;
- *addend = instruction;
- return llvm::Error::success();
- case ARM_RELOC_VANILLA | rLength4:
- // ex: .long _foo (and _foo is defined)
- *kind = pointer32;
- if (auto ec = atomFromAddress(reloc.symbol, instruction, target, addend))
- return ec;
- *addend = clearThumbBit((uint32_t) * addend, *target);
- return llvm::Error::success();
- case ARM_RELOC_VANILLA | rScattered | rLength4:
- // ex: .long _foo+a (and _foo is defined)
- *kind = pointer32;
- if (auto ec = atomFromAddress(0, reloc.value, target, addend))
- return ec;
- *addend += (clearThumbBit(instruction, *target) - reloc.value);
- return llvm::Error::success();
- default:
- return llvm::make_error<GenericError>("unsupported arm relocation type");
- }
- return llvm::Error::success();
-}
-
-llvm::Error
-ArchHandler_arm::getPairReferenceInfo(const normalized::Relocation &reloc1,
- const normalized::Relocation &reloc2,
- const DefinedAtom *inAtom,
- uint32_t offsetInAtom,
- uint64_t fixupAddress, bool isBig,
- bool scatterable,
- FindAtomBySectionAndAddress atomFromAddr,
- FindAtomBySymbolIndex atomFromSymbolIndex,
- Reference::KindValue *kind,
- const lld::Atom **target,
- Reference::Addend *addend) {
- bool pointerDiff = false;
- bool funcRel;
- bool top;
- bool thumbReloc;
- switch(relocPattern(reloc1) << 16 | relocPattern(reloc2)) {
- case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbLo) << 16 |
- ARM_RELOC_PAIR | rScattered | rLenThmbLo):
- // ex: movw r1, :lower16:(_x-L1) [thumb mode]
- *kind = thumb_movw_funcRel;
- funcRel = true;
- top = false;
- thumbReloc = true;
- break;
- case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbHi) << 16 |
- ARM_RELOC_PAIR | rScattered | rLenThmbHi):
- // ex: movt r1, :upper16:(_x-L1) [thumb mode]
- *kind = thumb_movt_funcRel;
- funcRel = true;
- top = true;
- thumbReloc = true;
- break;
- case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmLo) << 16 |
- ARM_RELOC_PAIR | rScattered | rLenArmLo):
- // ex: movw r1, :lower16:(_x-L1) [arm mode]
- *kind = arm_movw_funcRel;
- funcRel = true;
- top = false;
- thumbReloc = false;
- break;
- case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmHi) << 16 |
- ARM_RELOC_PAIR | rScattered | rLenArmHi):
- // ex: movt r1, :upper16:(_x-L1) [arm mode]
- *kind = arm_movt_funcRel;
- funcRel = true;
- top = true;
- thumbReloc = false;
- break;
- case ((ARM_RELOC_HALF | rLenThmbLo) << 16 |
- ARM_RELOC_PAIR | rLenThmbLo):
- // ex: movw r1, :lower16:_x [thumb mode]
- *kind = thumb_movw;
- funcRel = false;
- top = false;
- thumbReloc = true;
- break;
- case ((ARM_RELOC_HALF | rLenThmbHi) << 16 |
- ARM_RELOC_PAIR | rLenThmbHi):
- // ex: movt r1, :upper16:_x [thumb mode]
- *kind = thumb_movt;
- funcRel = false;
- top = true;
- thumbReloc = true;
- break;
- case ((ARM_RELOC_HALF | rLenArmLo) << 16 |
- ARM_RELOC_PAIR | rLenArmLo):
- // ex: movw r1, :lower16:_x [arm mode]
- *kind = arm_movw;
- funcRel = false;
- top = false;
- thumbReloc = false;
- break;
- case ((ARM_RELOC_HALF | rLenArmHi) << 16 |
- ARM_RELOC_PAIR | rLenArmHi):
- // ex: movt r1, :upper16:_x [arm mode]
- *kind = arm_movt;
- funcRel = false;
- top = true;
- thumbReloc = false;
- break;
- case ((ARM_RELOC_HALF | rScattered | rLenThmbLo) << 16 |
- ARM_RELOC_PAIR | rLenThmbLo):
- // ex: movw r1, :lower16:_x+a [thumb mode]
- *kind = thumb_movw;
- funcRel = false;
- top = false;
- thumbReloc = true;
- break;
- case ((ARM_RELOC_HALF | rScattered | rLenThmbHi) << 16 |
- ARM_RELOC_PAIR | rLenThmbHi):
- // ex: movt r1, :upper16:_x+a [thumb mode]
- *kind = thumb_movt;
- funcRel = false;
- top = true;
- thumbReloc = true;
- break;
- case ((ARM_RELOC_HALF | rScattered | rLenArmLo) << 16 |
- ARM_RELOC_PAIR | rLenArmLo):
- // ex: movw r1, :lower16:_x+a [arm mode]
- *kind = arm_movw;
- funcRel = false;
- top = false;
- thumbReloc = false;
- break;
- case ((ARM_RELOC_HALF | rScattered | rLenArmHi) << 16 |
- ARM_RELOC_PAIR | rLenArmHi):
- // ex: movt r1, :upper16:_x+a [arm mode]
- *kind = arm_movt;
- funcRel = false;
- top = true;
- thumbReloc = false;
- break;
- case ((ARM_RELOC_HALF | rExtern | rLenThmbLo) << 16 |
- ARM_RELOC_PAIR | rLenThmbLo):
- // ex: movw r1, :lower16:_undef [thumb mode]
- *kind = thumb_movw;
- funcRel = false;
- top = false;
- thumbReloc = true;
- break;
- case ((ARM_RELOC_HALF | rExtern | rLenThmbHi) << 16 |
- ARM_RELOC_PAIR | rLenThmbHi):
- // ex: movt r1, :upper16:_undef [thumb mode]
- *kind = thumb_movt;
- funcRel = false;
- top = true;
- thumbReloc = true;
- break;
- case ((ARM_RELOC_HALF | rExtern | rLenArmLo) << 16 |
- ARM_RELOC_PAIR | rLenArmLo):
- // ex: movw r1, :lower16:_undef [arm mode]
- *kind = arm_movw;
- funcRel = false;
- top = false;
- thumbReloc = false;
- break;
- case ((ARM_RELOC_HALF | rExtern | rLenArmHi) << 16 |
- ARM_RELOC_PAIR | rLenArmHi):
- // ex: movt r1, :upper16:_undef [arm mode]
- *kind = arm_movt;
- funcRel = false;
- top = true;
- thumbReloc = false;
- break;
- case ((ARM_RELOC_SECTDIFF | rScattered | rLength4) << 16 |
- ARM_RELOC_PAIR | rScattered | rLength4):
- case ((ARM_RELOC_LOCAL_SECTDIFF | rScattered | rLength4) << 16 |
- ARM_RELOC_PAIR | rScattered | rLength4):
- // ex: .long _foo - .
- pointerDiff = true;
- break;
- default:
- return llvm::make_error<GenericError>("unsupported arm relocation pair");
- }
- const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
- uint32_t instruction = *(const ulittle32_t *)fixupContent;
- uint32_t value;
- uint32_t fromAddress;
- uint32_t toAddress;
- uint16_t instruction16;
- uint16_t other16;
- const lld::Atom *fromTarget;
- Reference::Addend offsetInTo;
- Reference::Addend offsetInFrom;
- if (pointerDiff) {
- toAddress = reloc1.value;
- fromAddress = reloc2.value;
- if (auto ec = atomFromAddr(0, toAddress, target, &offsetInTo))
- return ec;
- if (auto ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom))
- return ec;
- if (scatterable && (fromTarget != inAtom))
- return llvm::make_error<GenericError>(
- "SECTDIFF relocation where subtrahend label is not in atom");
- *kind = delta32;
- value = clearThumbBit(instruction, *target);
- *addend = (int32_t)(value - (toAddress - fixupAddress));
- } else if (funcRel) {
- toAddress = reloc1.value;
- fromAddress = reloc2.value;
- if (auto ec = atomFromAddr(0, toAddress, target, &offsetInTo))
- return ec;
- if (auto ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom))
- return ec;
- if (fromTarget != inAtom)
- return llvm::make_error<GenericError>("ARM_RELOC_HALF_SECTDIFF relocation"
- " where subtrahend label is not in atom");
- other16 = (reloc2.offset & 0xFFFF);
- if (thumbReloc) {
- if (top) {
- if (!isThumbMovt(instruction))
- return llvm::make_error<GenericError>("expected movt instruction");
- }
- else {
- if (!isThumbMovw(instruction))
- return llvm::make_error<GenericError>("expected movw instruction");
- }
- instruction16 = getWordFromThumbMov(instruction);
- }
- else {
- if (top) {
- if (!isArmMovt(instruction))
- return llvm::make_error<GenericError>("expected movt instruction");
- }
- else {
- if (!isArmMovw(instruction))
- return llvm::make_error<GenericError>("expected movw instruction");
- }
- instruction16 = getWordFromArmMov(instruction);
- }
- if (top)
- value = (instruction16 << 16) | other16;
- else
- value = (other16 << 16) | instruction16;
- value = clearThumbBit(value, *target);
- int64_t ta = (int64_t) value - (toAddress - fromAddress);
- *addend = ta - offsetInFrom;
- return llvm::Error::success();
- } else {
- uint32_t sectIndex;
- if (thumbReloc) {
- if (top) {
- if (!isThumbMovt(instruction))
- return llvm::make_error<GenericError>("expected movt instruction");
- }
- else {
- if (!isThumbMovw(instruction))
- return llvm::make_error<GenericError>("expected movw instruction");
- }
- instruction16 = getWordFromThumbMov(instruction);
- }
- else {
- if (top) {
- if (!isArmMovt(instruction))
- return llvm::make_error<GenericError>("expected movt instruction");
- }
- else {
- if (!isArmMovw(instruction))
- return llvm::make_error<GenericError>("expected movw instruction");
- }
- instruction16 = getWordFromArmMov(instruction);
- }
- other16 = (reloc2.offset & 0xFFFF);
- if (top)
- value = (instruction16 << 16) | other16;
- else
- value = (other16 << 16) | instruction16;
- if (reloc1.isExtern) {
- if (auto ec = atomFromSymbolIndex(reloc1.symbol, target))
- return ec;
- *addend = value;
- } else {
- if (reloc1.scattered) {
- toAddress = reloc1.value;
- sectIndex = 0;
- } else {
- toAddress = value;
- sectIndex = reloc1.symbol;
- }
- if (auto ec = atomFromAddr(sectIndex, toAddress, target, &offsetInTo))
- return ec;
- *addend = value - toAddress;
- }
- }
-
- return llvm::Error::success();
-}
-
-void ArchHandler_arm::applyFixupFinal(const Reference &ref, uint8_t *loc,
- uint64_t fixupAddress,
- uint64_t targetAddress,
- uint64_t inAtomAddress,
- bool &thumbMode, bool targetIsThumb) {
- if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
- return;
- assert(ref.kindArch() == Reference::KindArch::ARM);
- ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc);
- int32_t displacement;
- uint16_t value16;
- uint32_t value32;
- switch (static_cast<ArmKind>(ref.kindValue())) {
- case modeThumbCode:
- thumbMode = true;
- break;
- case modeArmCode:
- thumbMode = false;
- break;
- case modeData:
- break;
- case thumb_b22:
- case thumb_bl22:
- assert(thumbMode);
- displacement = (targetAddress - (fixupAddress + 4)) + ref.addend();
- value32 = setDisplacementInThumbBranch(*loc32, fixupAddress,
- displacement, targetIsThumb);
- *loc32 = value32;
- break;
- case thumb_movw:
- assert(thumbMode);
- value16 = (targetAddress + ref.addend()) & 0xFFFF;
- if (targetIsThumb)
- value16 |= 1;
- *loc32 = setWordFromThumbMov(*loc32, value16);
- break;
- case thumb_movt:
- assert(thumbMode);
- value16 = (targetAddress + ref.addend()) >> 16;
- *loc32 = setWordFromThumbMov(*loc32, value16);
- break;
- case thumb_movw_funcRel:
- assert(thumbMode);
- value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF;
- if (targetIsThumb)
- value16 |= 1;
- *loc32 = setWordFromThumbMov(*loc32, value16);
- break;
- case thumb_movt_funcRel:
- assert(thumbMode);
- value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16;
- *loc32 = setWordFromThumbMov(*loc32, value16);
- break;
- case arm_b24:
- case arm_bl24:
- assert(!thumbMode);
- displacement = (targetAddress - (fixupAddress + 8)) + ref.addend();
- value32 = setDisplacementInArmBranch(*loc32, displacement, targetIsThumb);
- *loc32 = value32;
- break;
- case arm_movw:
- assert(!thumbMode);
- value16 = (targetAddress + ref.addend()) & 0xFFFF;
- if (targetIsThumb)
- value16 |= 1;
- *loc32 = setWordFromArmMov(*loc32, value16);
- break;
- case arm_movt:
- assert(!thumbMode);
- value16 = (targetAddress + ref.addend()) >> 16;
- *loc32 = setWordFromArmMov(*loc32, value16);
- break;
- case arm_movw_funcRel:
- assert(!thumbMode);
- value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF;
- if (targetIsThumb)
- value16 |= 1;
- *loc32 = setWordFromArmMov(*loc32, value16);
- break;
- case arm_movt_funcRel:
- assert(!thumbMode);
- value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16;
- *loc32 = setWordFromArmMov(*loc32, value16);
- break;
- case pointer32:
- if (targetIsThumb)
- *loc32 = targetAddress + ref.addend() + 1;
- else
- *loc32 = targetAddress + ref.addend();
- break;
- case delta32:
- if (targetIsThumb)
- *loc32 = targetAddress - fixupAddress + ref.addend() + 1;
- else
- *loc32 = targetAddress - fixupAddress + ref.addend();
- break;
- case lazyPointer:
- // do nothing
- break;
- case lazyImmediateLocation:
- *loc32 = ref.addend();
- break;
- case invalid:
- llvm_unreachable("invalid ARM Reference Kind");
- break;
- }
-}
-
-void ArchHandler_arm::generateAtomContent(const DefinedAtom &atom,
- bool relocatable,
- FindAddressForAtom findAddress,
- FindAddressForAtom findSectionAddress,
- uint64_t imageBaseAddress,
- llvm::MutableArrayRef<uint8_t> atomContentBuffer) {
- // Copy raw bytes.
- std::copy(atom.rawContent().begin(), atom.rawContent().end(),
- atomContentBuffer.begin());
- // Apply fix-ups.
- bool thumbMode = false;
- for (const Reference *ref : atom) {
- uint32_t offset = ref->offsetInAtom();
- const Atom *target = ref->target();
- uint64_t targetAddress = 0;
- bool targetIsThumb = false;
- if (const DefinedAtom *defTarg = dyn_cast<DefinedAtom>(target)) {
- targetAddress = findAddress(*target);
- targetIsThumb = isThumbFunction(*defTarg);
- }
- uint64_t atomAddress = findAddress(atom);
- uint64_t fixupAddress = atomAddress + offset;
- if (relocatable) {
- applyFixupRelocatable(*ref, &atomContentBuffer[offset], fixupAddress,
- targetAddress, atomAddress, thumbMode,
- targetIsThumb);
- } else {
- applyFixupFinal(*ref, &atomContentBuffer[offset], fixupAddress,
- targetAddress, atomAddress, thumbMode, targetIsThumb);
- }
- }
-}
-
-bool ArchHandler_arm::useExternalRelocationTo(const Atom &target) {
- // Undefined symbols are referenced via external relocations.
- if (isa<UndefinedAtom>(&target))
- return true;
- if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(&target)) {
- switch (defAtom->merge()) {
- case DefinedAtom::mergeAsTentative:
- // Tentative definitions are referenced via external relocations.
- return true;
- case DefinedAtom::mergeAsWeak:
- case DefinedAtom::mergeAsWeakAndAddressUsed:
- // Global weak-defs are referenced via external relocations.
- return (defAtom->scope() == DefinedAtom::scopeGlobal);
- default:
- break;
- }
- }
- // Everything else is reference via an internal relocation.
- return false;
-}
-
-void ArchHandler_arm::applyFixupRelocatable(const Reference &ref, uint8_t *loc,
- uint64_t fixupAddress,
- uint64_t targetAddress,
- uint64_t inAtomAddress,
- bool &thumbMode,
- bool targetIsThumb) {
- if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
- return;
- assert(ref.kindArch() == Reference::KindArch::ARM);
- bool useExternalReloc = useExternalRelocationTo(*ref.target());
- ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc);
- int32_t displacement;
- uint16_t value16;
- uint32_t value32;
- bool targetIsUndef = isa<UndefinedAtom>(ref.target());
- switch (static_cast<ArmKind>(ref.kindValue())) {
- case modeThumbCode:
- thumbMode = true;
- break;
- case modeArmCode:
- thumbMode = false;
- break;
- case modeData:
- break;
- case thumb_b22:
- case thumb_bl22:
- assert(thumbMode);
- if (useExternalReloc)
- displacement = (ref.addend() - (fixupAddress + 4));
- else
- displacement = (targetAddress - (fixupAddress + 4)) + ref.addend();
- value32 = setDisplacementInThumbBranch(*loc32, fixupAddress,
- displacement,
- targetIsUndef || targetIsThumb);
- *loc32 = value32;
- break;
- case thumb_movw:
- assert(thumbMode);
- if (useExternalReloc)
- value16 = ref.addend() & 0xFFFF;
- else
- value16 = (targetAddress + ref.addend()) & 0xFFFF;
- *loc32 = setWordFromThumbMov(*loc32, value16);
- break;
- case thumb_movt:
- assert(thumbMode);
- if (useExternalReloc)
- value16 = ref.addend() >> 16;
- else
- value16 = (targetAddress + ref.addend()) >> 16;
- *loc32 = setWordFromThumbMov(*loc32, value16);
- break;
- case thumb_movw_funcRel:
- assert(thumbMode);
- value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF;
- *loc32 = setWordFromThumbMov(*loc32, value16);
- break;
- case thumb_movt_funcRel:
- assert(thumbMode);
- value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16;
- *loc32 = setWordFromThumbMov(*loc32, value16);
- break;
- case arm_b24:
- case arm_bl24:
- assert(!thumbMode);
- if (useExternalReloc)
- displacement = (ref.addend() - (fixupAddress + 8));
- else
- displacement = (targetAddress - (fixupAddress + 8)) + ref.addend();
- value32 = setDisplacementInArmBranch(*loc32, displacement,
- targetIsThumb);
- *loc32 = value32;
- break;
- case arm_movw:
- assert(!thumbMode);
- if (useExternalReloc)
- value16 = ref.addend() & 0xFFFF;
- else
- value16 = (targetAddress + ref.addend()) & 0xFFFF;
- *loc32 = setWordFromArmMov(*loc32, value16);
- break;
- case arm_movt:
- assert(!thumbMode);
- if (useExternalReloc)
- value16 = ref.addend() >> 16;
- else
- value16 = (targetAddress + ref.addend()) >> 16;
- *loc32 = setWordFromArmMov(*loc32, value16);
- break;
- case arm_movw_funcRel:
- assert(!thumbMode);
- value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF;
- *loc32 = setWordFromArmMov(*loc32, value16);
- break;
- case arm_movt_funcRel:
- assert(!thumbMode);
- value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16;
- *loc32 = setWordFromArmMov(*loc32, value16);
- break;
- case pointer32:
- *loc32 = targetAddress + ref.addend();
- break;
- case delta32:
- *loc32 = targetAddress - fixupAddress + ref.addend();
- break;
- case lazyPointer:
- case lazyImmediateLocation:
- // do nothing
- break;
- case invalid:
- llvm_unreachable("invalid ARM Reference Kind");
- break;
- }
-}
-
-void ArchHandler_arm::appendSectionRelocations(
- const DefinedAtom &atom,
- uint64_t atomSectionOffset,
- const Reference &ref,
- FindSymbolIndexForAtom symbolIndexForAtom,
- FindSectionIndexForAtom sectionIndexForAtom,
- FindAddressForAtom addressForAtom,
- normalized::Relocations &relocs) {
- if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
- return;
- assert(ref.kindArch() == Reference::KindArch::ARM);
- uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom();
- bool useExternalReloc = useExternalRelocationTo(*ref.target());
- uint32_t targetAtomAddress;
- uint32_t fromAtomAddress;
- uint16_t other16;
- switch (static_cast<ArmKind>(ref.kindValue())) {
- case modeThumbCode:
- case modeArmCode:
- case modeData:
- // Do nothing.
- break;
- case thumb_b22:
- case thumb_bl22:
- if (useExternalReloc) {
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- ARM_THUMB_RELOC_BR22 | rExtern | rPcRel | rLength4);
- } else {
- if (ref.addend() != 0)
- appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
- ARM_THUMB_RELOC_BR22 | rScattered | rPcRel | rLength4);
- else
- appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
- ARM_THUMB_RELOC_BR22 | rPcRel | rLength4);
- }
- break;
- case thumb_movw:
- if (useExternalReloc) {
- other16 = ref.addend() >> 16;
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- ARM_RELOC_HALF | rExtern | rLenThmbLo);
- appendReloc(relocs, other16, 0, 0,
- ARM_RELOC_PAIR | rLenThmbLo);
- } else {
- targetAtomAddress = addressForAtom(*ref.target());
- if (ref.addend() != 0) {
- other16 = (targetAtomAddress + ref.addend()) >> 16;
- appendReloc(relocs, sectionOffset, 0, targetAtomAddress,
- ARM_RELOC_HALF | rScattered | rLenThmbLo);
- appendReloc(relocs, other16, 0, 0,
- ARM_RELOC_PAIR | rLenThmbLo);
- } else {
- other16 = (targetAtomAddress + ref.addend()) >> 16;
- appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
- ARM_RELOC_HALF | rLenThmbLo);
- appendReloc(relocs, other16, 0, 0,
- ARM_RELOC_PAIR | rLenThmbLo);
- }
- }
- break;
- case thumb_movt:
- if (useExternalReloc) {
- other16 = ref.addend() & 0xFFFF;
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- ARM_RELOC_HALF | rExtern | rLenThmbHi);
- appendReloc(relocs, other16, 0, 0,
- ARM_RELOC_PAIR | rLenThmbHi);
- } else {
- targetAtomAddress = addressForAtom(*ref.target());
- if (ref.addend() != 0) {
- other16 = (targetAtomAddress + ref.addend()) & 0xFFFF;
- appendReloc(relocs, sectionOffset, 0, targetAtomAddress,
- ARM_RELOC_HALF | rScattered | rLenThmbHi);
- appendReloc(relocs, other16, 0, 0,
- ARM_RELOC_PAIR | rLenThmbHi);
- } else {
- other16 = (targetAtomAddress + ref.addend()) & 0xFFFF;
- appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
- ARM_RELOC_HALF | rLenThmbHi);
- appendReloc(relocs, other16, 0, 0,
- ARM_RELOC_PAIR | rLenThmbHi);
- }
- }
- break;
- case thumb_movw_funcRel:
- fromAtomAddress = addressForAtom(atom);
- targetAtomAddress = addressForAtom(*ref.target());
- other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) >> 16;
- appendReloc(relocs, sectionOffset, 0, targetAtomAddress,
- ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbLo);
- appendReloc(relocs, other16, 0, fromAtomAddress,
- ARM_RELOC_PAIR | rScattered | rLenThmbLo);
- break;
- case thumb_movt_funcRel:
- fromAtomAddress = addressForAtom(atom);
- targetAtomAddress = addressForAtom(*ref.target());
- other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) & 0xFFFF;
- appendReloc(relocs, sectionOffset, 0, targetAtomAddress,
- ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbHi);
- appendReloc(relocs, other16, 0, fromAtomAddress,
- ARM_RELOC_PAIR | rScattered | rLenThmbHi);
- break;
- case arm_b24:
- case arm_bl24:
- if (useExternalReloc) {
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- ARM_RELOC_BR24 | rExtern | rPcRel | rLength4);
- } else {
- if (ref.addend() != 0)
- appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
- ARM_RELOC_BR24 | rScattered | rPcRel | rLength4);
- else
- appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
- ARM_RELOC_BR24 | rPcRel | rLength4);
- }
- break;
- case arm_movw:
- if (useExternalReloc) {
- other16 = ref.addend() >> 16;
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- ARM_RELOC_HALF | rExtern | rLenArmLo);
- appendReloc(relocs, other16, 0, 0,
- ARM_RELOC_PAIR | rLenArmLo);
- } else {
- targetAtomAddress = addressForAtom(*ref.target());
- if (ref.addend() != 0) {
- other16 = (targetAtomAddress + ref.addend()) >> 16;
- appendReloc(relocs, sectionOffset, 0, targetAtomAddress,
- ARM_RELOC_HALF | rScattered | rLenArmLo);
- appendReloc(relocs, other16, 0, 0,
- ARM_RELOC_PAIR | rLenArmLo);
- } else {
- other16 = (targetAtomAddress + ref.addend()) >> 16;
- appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
- ARM_RELOC_HALF | rLenArmLo);
- appendReloc(relocs, other16, 0, 0,
- ARM_RELOC_PAIR | rLenArmLo);
- }
- }
- break;
- case arm_movt:
- if (useExternalReloc) {
- other16 = ref.addend() & 0xFFFF;
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- ARM_RELOC_HALF | rExtern | rLenArmHi);
- appendReloc(relocs, other16, 0, 0,
- ARM_RELOC_PAIR | rLenArmHi);
- } else {
- targetAtomAddress = addressForAtom(*ref.target());
- if (ref.addend() != 0) {
- other16 = (targetAtomAddress + ref.addend()) & 0xFFFF;
- appendReloc(relocs, sectionOffset, 0, targetAtomAddress,
- ARM_RELOC_HALF | rScattered | rLenArmHi);
- appendReloc(relocs, other16, 0, 0,
- ARM_RELOC_PAIR | rLenArmHi);
- } else {
- other16 = (targetAtomAddress + ref.addend()) & 0xFFFF;
- appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
- ARM_RELOC_HALF | rLenArmHi);
- appendReloc(relocs, other16, 0, 0,
- ARM_RELOC_PAIR | rLenArmHi);
- }
- }
- break;
- case arm_movw_funcRel:
- fromAtomAddress = addressForAtom(atom);
- targetAtomAddress = addressForAtom(*ref.target());
- other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) >> 16;
- appendReloc(relocs, sectionOffset, 0, targetAtomAddress,
- ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmLo);
- appendReloc(relocs, other16, 0, fromAtomAddress,
- ARM_RELOC_PAIR | rScattered | rLenArmLo);
- break;
- case arm_movt_funcRel:
- fromAtomAddress = addressForAtom(atom);
- targetAtomAddress = addressForAtom(*ref.target());
- other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) & 0xFFFF;
- appendReloc(relocs, sectionOffset, 0, targetAtomAddress,
- ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmHi);
- appendReloc(relocs, other16, 0, fromAtomAddress,
- ARM_RELOC_PAIR | rScattered | rLenArmHi);
- break;
- case pointer32:
- if (useExternalReloc) {
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- ARM_RELOC_VANILLA | rExtern | rLength4);
- }
- else {
- if (ref.addend() != 0)
- appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
- ARM_RELOC_VANILLA | rScattered | rLength4);
- else
- appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
- ARM_RELOC_VANILLA | rLength4);
- }
- break;
- case delta32:
- appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
- ARM_RELOC_SECTDIFF | rScattered | rLength4);
- appendReloc(relocs, sectionOffset, 0, addressForAtom(atom) +
- ref.offsetInAtom(),
- ARM_RELOC_PAIR | rScattered | rLength4);
- break;
- case lazyPointer:
- case lazyImmediateLocation:
- // do nothing
- break;
- case invalid:
- llvm_unreachable("invalid ARM Reference Kind");
- break;
- }
-}
-
-void ArchHandler_arm::addAdditionalReferences(MachODefinedAtom &atom) {
- if (atom.isThumb()) {
- atom.addReference(Reference::KindNamespace::mach_o,
- Reference::KindArch::ARM, modeThumbCode, 0, &atom, 0);
- }
-}
-
-bool ArchHandler_arm::isThumbFunction(const DefinedAtom &atom) {
- for (const Reference *ref : atom) {
- if (ref->offsetInAtom() != 0)
- return false;
- if (ref->kindNamespace() != Reference::KindNamespace::mach_o)
- continue;
- assert(ref->kindArch() == Reference::KindArch::ARM);
- if (ref->kindValue() == modeThumbCode)
- return true;
- }
- return false;
-}
-
-class Thumb2ToArmShimAtom : public SimpleDefinedAtom {
-public:
- Thumb2ToArmShimAtom(MachOFile &file, StringRef targetName,
- const DefinedAtom &target)
- : SimpleDefinedAtom(file) {
- addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM,
- ArchHandler_arm::modeThumbCode, 0, this, 0);
- addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM,
- ArchHandler_arm::delta32, 8, &target, 0);
- std::string name = std::string(targetName) + "$shim";
- StringRef tmp(name);
- _name = tmp.copy(file.allocator());
- }
-
- ~Thumb2ToArmShimAtom() override = default;
-
- StringRef name() const override {
- return _name;
- }
-
- ContentType contentType() const override {
- return DefinedAtom::typeCode;
- }
-
- Alignment alignment() const override { return 4; }
-
- uint64_t size() const override {
- return 12;
- }
-
- ContentPermissions permissions() const override {
- return DefinedAtom::permR_X;
- }
-
- ArrayRef<uint8_t> rawContent() const override {
- static const uint8_t bytes[] =
- { 0xDF, 0xF8, 0x04, 0xC0, // ldr ip, pc + 4
- 0xFF, 0x44, // add ip, pc, ip
- 0x60, 0x47, // ldr pc, [ip]
- 0x00, 0x00, 0x00, 0x00 }; // .long target - this
- assert(sizeof(bytes) == size());
- return llvm::makeArrayRef(bytes, sizeof(bytes));
- }
-private:
- StringRef _name;
-};
-
-class ArmToThumbShimAtom : public SimpleDefinedAtom {
-public:
- ArmToThumbShimAtom(MachOFile &file, StringRef targetName,
- const DefinedAtom &target)
- : SimpleDefinedAtom(file) {
- addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM,
- ArchHandler_arm::delta32, 12, &target, 0);
- std::string name = std::string(targetName) + "$shim";
- StringRef tmp(name);
- _name = tmp.copy(file.allocator());
- }
-
- ~ArmToThumbShimAtom() override = default;
-
- StringRef name() const override {
- return _name;
- }
-
- ContentType contentType() const override {
- return DefinedAtom::typeCode;
- }
-
- Alignment alignment() const override { return 4; }
-
- uint64_t size() const override {
- return 16;
- }
-
- ContentPermissions permissions() const override {
- return DefinedAtom::permR_X;
- }
-
- ArrayRef<uint8_t> rawContent() const override {
- static const uint8_t bytes[] =
- { 0x04, 0xC0, 0x9F, 0xE5, // ldr ip, pc + 4
- 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip
- 0x1C, 0xFF, 0x2F, 0xE1, // ldr pc, [ip]
- 0x00, 0x00, 0x00, 0x00 }; // .long target - this
- assert(sizeof(bytes) == size());
- return llvm::makeArrayRef(bytes, sizeof(bytes));
- }
-private:
- StringRef _name;
-};
-
-const DefinedAtom *ArchHandler_arm::createShim(MachOFile &file,
- bool thumbToArm,
- const DefinedAtom &target) {
- bool isStub = (target.contentType() == DefinedAtom::typeStub);
- StringRef targetName = isStub ? stubName(target) : target.name();
- if (thumbToArm)
- return new (file.allocator()) Thumb2ToArmShimAtom(file, targetName, target);
- else
- return new (file.allocator()) ArmToThumbShimAtom(file, targetName, target);
-}
-
-std::unique_ptr<mach_o::ArchHandler> ArchHandler::create_arm() {
- return std::unique_ptr<mach_o::ArchHandler>(new ArchHandler_arm());
-}
-
-} // namespace mach_o
-} // namespace lld
+++ /dev/null
-//===- lib/FileFormat/MachO/ArchHandler_arm64.cpp -------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "ArchHandler.h"
-#include "Atoms.h"
-#include "MachONormalizedFileBinaryUtils.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/ADT/StringSwitch.h"
-#include "llvm/ADT/Triple.h"
-#include "llvm/Support/Endian.h"
-#include "llvm/Support/ErrorHandling.h"
-#include "llvm/Support/Format.h"
-
-using namespace llvm::MachO;
-using namespace lld::mach_o::normalized;
-
-namespace lld {
-namespace mach_o {
-
-using llvm::support::ulittle32_t;
-using llvm::support::ulittle64_t;
-
-using llvm::support::little32_t;
-using llvm::support::little64_t;
-
-class ArchHandler_arm64 : public ArchHandler {
-public:
- ArchHandler_arm64() = default;
- ~ArchHandler_arm64() override = default;
-
- const Registry::KindStrings *kindStrings() override { return _sKindStrings; }
-
- Reference::KindArch kindArch() override {
- return Reference::KindArch::AArch64;
- }
-
- /// Used by GOTPass to locate GOT References
- bool isGOTAccess(const Reference &ref, bool &canBypassGOT) override {
- if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
- return false;
- assert(ref.kindArch() == Reference::KindArch::AArch64);
- switch (ref.kindValue()) {
- case gotPage21:
- case gotOffset12:
- canBypassGOT = true;
- return true;
- case delta32ToGOT:
- case unwindCIEToPersonalityFunction:
- case imageOffsetGot:
- canBypassGOT = false;
- return true;
- default:
- return false;
- }
- }
-
- /// Used by GOTPass to update GOT References.
- void updateReferenceToGOT(const Reference *ref, bool targetNowGOT) override {
- // If GOT slot was instantiated, transform:
- // gotPage21/gotOffset12 -> page21/offset12scale8
- // If GOT slot optimized away, transform:
- // gotPage21/gotOffset12 -> page21/addOffset12
- assert(ref->kindNamespace() == Reference::KindNamespace::mach_o);
- assert(ref->kindArch() == Reference::KindArch::AArch64);
- switch (ref->kindValue()) {
- case gotPage21:
- const_cast<Reference *>(ref)->setKindValue(page21);
- break;
- case gotOffset12:
- const_cast<Reference *>(ref)->setKindValue(targetNowGOT ?
- offset12scale8 : addOffset12);
- break;
- case delta32ToGOT:
- const_cast<Reference *>(ref)->setKindValue(delta32);
- break;
- case imageOffsetGot:
- const_cast<Reference *>(ref)->setKindValue(imageOffset);
- break;
- default:
- llvm_unreachable("Not a GOT reference");
- }
- }
-
- const StubInfo &stubInfo() override { return _sStubInfo; }
-
- bool isCallSite(const Reference &) override;
- bool isNonCallBranch(const Reference &) override {
- return false;
- }
-
- bool isPointer(const Reference &) override;
- bool isPairedReloc(const normalized::Relocation &) override;
-
- bool needsCompactUnwind() override {
- return true;
- }
- Reference::KindValue imageOffsetKind() override {
- return imageOffset;
- }
- Reference::KindValue imageOffsetKindIndirect() override {
- return imageOffsetGot;
- }
-
- Reference::KindValue unwindRefToPersonalityFunctionKind() override {
- return unwindCIEToPersonalityFunction;
- }
-
- Reference::KindValue unwindRefToCIEKind() override {
- return negDelta32;
- }
-
- Reference::KindValue unwindRefToFunctionKind() override {
- return unwindFDEToFunction;
- }
-
- Reference::KindValue unwindRefToEhFrameKind() override {
- return unwindInfoToEhFrame;
- }
-
- Reference::KindValue pointerKind() override {
- return pointer64;
- }
-
- Reference::KindValue lazyImmediateLocationKind() override {
- return lazyImmediateLocation;
- }
-
- uint32_t dwarfCompactUnwindType() override {
- return 0x03000000;
- }
-
- llvm::Error getReferenceInfo(const normalized::Relocation &reloc,
- const DefinedAtom *inAtom,
- uint32_t offsetInAtom,
- uint64_t fixupAddress, bool isBig,
- FindAtomBySectionAndAddress atomFromAddress,
- FindAtomBySymbolIndex atomFromSymbolIndex,
- Reference::KindValue *kind,
- const lld::Atom **target,
- Reference::Addend *addend) override;
- llvm::Error
- getPairReferenceInfo(const normalized::Relocation &reloc1,
- const normalized::Relocation &reloc2,
- const DefinedAtom *inAtom,
- uint32_t offsetInAtom,
- uint64_t fixupAddress, bool isBig, bool scatterable,
- FindAtomBySectionAndAddress atomFromAddress,
- FindAtomBySymbolIndex atomFromSymbolIndex,
- Reference::KindValue *kind,
- const lld::Atom **target,
- Reference::Addend *addend) override;
-
- bool needsLocalSymbolInRelocatableFile(const DefinedAtom *atom) override {
- return (atom->contentType() == DefinedAtom::typeCString);
- }
-
- void generateAtomContent(const DefinedAtom &atom, bool relocatable,
- FindAddressForAtom findAddress,
- FindAddressForAtom findSectionAddress,
- uint64_t imageBaseAddress,
- llvm::MutableArrayRef<uint8_t> atomContentBuffer) override;
-
- void appendSectionRelocations(const DefinedAtom &atom,
- uint64_t atomSectionOffset,
- const Reference &ref,
- FindSymbolIndexForAtom symbolIndexForAtom,
- FindSectionIndexForAtom sectionIndexForAtom,
- FindAddressForAtom addressForAtom,
- normalized::Relocations &relocs) override;
-
-private:
- static const Registry::KindStrings _sKindStrings[];
- static const StubInfo _sStubInfo;
-
- enum Arm64Kind : Reference::KindValue {
- invalid, /// for error condition
-
- // Kinds found in mach-o .o files:
- branch26, /// ex: bl _foo
- page21, /// ex: adrp x1, _foo@PAGE
- offset12, /// ex: ldrb w0, [x1, _foo@PAGEOFF]
- offset12scale2, /// ex: ldrs w0, [x1, _foo@PAGEOFF]
- offset12scale4, /// ex: ldr w0, [x1, _foo@PAGEOFF]
- offset12scale8, /// ex: ldr x0, [x1, _foo@PAGEOFF]
- offset12scale16, /// ex: ldr q0, [x1, _foo@PAGEOFF]
- gotPage21, /// ex: adrp x1, _foo@GOTPAGE
- gotOffset12, /// ex: ldr w0, [x1, _foo@GOTPAGEOFF]
- tlvPage21, /// ex: adrp x1, _foo@TLVPAGE
- tlvOffset12, /// ex: ldr w0, [x1, _foo@TLVPAGEOFF]
-
- pointer64, /// ex: .quad _foo
- delta64, /// ex: .quad _foo - .
- delta32, /// ex: .long _foo - .
- negDelta32, /// ex: .long . - _foo
- pointer64ToGOT, /// ex: .quad _foo@GOT
- delta32ToGOT, /// ex: .long _foo@GOT - .
-
- // Kinds introduced by Passes:
- addOffset12, /// Location contains LDR to change into ADD.
- lazyPointer, /// Location contains a lazy pointer.
- lazyImmediateLocation, /// Location contains immediate value used in stub.
- imageOffset, /// Location contains offset of atom in final image
- imageOffsetGot, /// Location contains offset of GOT entry for atom in
- /// final image (typically personality function).
- unwindCIEToPersonalityFunction, /// Nearly delta32ToGOT, but cannot be
- /// rematerialized in relocatable object
- /// (yay for implicit contracts!).
- unwindFDEToFunction, /// Nearly delta64, but cannot be rematerialized in
- /// relocatable object (yay for implicit contracts!).
- unwindInfoToEhFrame, /// Fix low 24 bits of compact unwind encoding to
- /// refer to __eh_frame entry.
- };
-
- void applyFixupFinal(const Reference &ref, uint8_t *location,
- uint64_t fixupAddress, uint64_t targetAddress,
- uint64_t inAtomAddress, uint64_t imageBaseAddress,
- FindAddressForAtom findSectionAddress);
-
- void applyFixupRelocatable(const Reference &ref, uint8_t *location,
- uint64_t fixupAddress, uint64_t targetAddress,
- uint64_t inAtomAddress, bool targetUnnamed);
-
- // Utility functions for inspecting/updating instructions.
- static uint32_t setDisplacementInBranch26(uint32_t instr, int32_t disp);
- static uint32_t setDisplacementInADRP(uint32_t instr, int64_t disp);
- static Arm64Kind offset12KindFromInstruction(uint32_t instr);
- static uint32_t setImm12(uint32_t instr, uint32_t offset);
-};
-
-const Registry::KindStrings ArchHandler_arm64::_sKindStrings[] = {
- LLD_KIND_STRING_ENTRY(invalid),
- LLD_KIND_STRING_ENTRY(branch26),
- LLD_KIND_STRING_ENTRY(page21),
- LLD_KIND_STRING_ENTRY(offset12),
- LLD_KIND_STRING_ENTRY(offset12scale2),
- LLD_KIND_STRING_ENTRY(offset12scale4),
- LLD_KIND_STRING_ENTRY(offset12scale8),
- LLD_KIND_STRING_ENTRY(offset12scale16),
- LLD_KIND_STRING_ENTRY(gotPage21),
- LLD_KIND_STRING_ENTRY(gotOffset12),
- LLD_KIND_STRING_ENTRY(tlvPage21),
- LLD_KIND_STRING_ENTRY(tlvOffset12),
- LLD_KIND_STRING_ENTRY(pointer64),
- LLD_KIND_STRING_ENTRY(delta64),
- LLD_KIND_STRING_ENTRY(delta32),
- LLD_KIND_STRING_ENTRY(negDelta32),
- LLD_KIND_STRING_ENTRY(pointer64ToGOT),
- LLD_KIND_STRING_ENTRY(delta32ToGOT),
-
- LLD_KIND_STRING_ENTRY(addOffset12),
- LLD_KIND_STRING_ENTRY(lazyPointer),
- LLD_KIND_STRING_ENTRY(lazyImmediateLocation),
- LLD_KIND_STRING_ENTRY(imageOffset),
- LLD_KIND_STRING_ENTRY(imageOffsetGot),
- LLD_KIND_STRING_ENTRY(unwindCIEToPersonalityFunction),
- LLD_KIND_STRING_ENTRY(unwindFDEToFunction),
- LLD_KIND_STRING_ENTRY(unwindInfoToEhFrame),
-
- LLD_KIND_STRING_END
-};
-
-const ArchHandler::StubInfo ArchHandler_arm64::_sStubInfo = {
- "dyld_stub_binder",
-
- // Lazy pointer references
- { Reference::KindArch::AArch64, pointer64, 0, 0 },
- { Reference::KindArch::AArch64, lazyPointer, 0, 0 },
-
- // GOT pointer to dyld_stub_binder
- { Reference::KindArch::AArch64, pointer64, 0, 0 },
-
- // arm64 code alignment 2^1
- 1,
-
- // Stub size and code
- 12,
- { 0x10, 0x00, 0x00, 0x90, // ADRP X16, lazy_pointer@page
- 0x10, 0x02, 0x40, 0xF9, // LDR X16, [X16, lazy_pointer@pageoff]
- 0x00, 0x02, 0x1F, 0xD6 }, // BR X16
- { Reference::KindArch::AArch64, page21, 0, 0 },
- { true, offset12scale8, 4, 0 },
-
- // Stub Helper size and code
- 12,
- { 0x50, 0x00, 0x00, 0x18, // LDR W16, L0
- 0x00, 0x00, 0x00, 0x14, // LDR B helperhelper
- 0x00, 0x00, 0x00, 0x00 }, // L0: .long 0
- { Reference::KindArch::AArch64, lazyImmediateLocation, 8, 0 },
- { Reference::KindArch::AArch64, branch26, 4, 0 },
-
- // Stub helper image cache content type
- DefinedAtom::typeGOT,
-
- // Stub Helper-Common size and code
- 24,
- // Stub helper alignment
- 2,
- { 0x11, 0x00, 0x00, 0x90, // ADRP X17, dyld_ImageLoaderCache@page
- 0x31, 0x02, 0x00, 0x91, // ADD X17, X17, dyld_ImageLoaderCache@pageoff
- 0xF0, 0x47, 0xBF, 0xA9, // STP X16/X17, [SP, #-16]!
- 0x10, 0x00, 0x00, 0x90, // ADRP X16, _fast_lazy_bind@page
- 0x10, 0x02, 0x40, 0xF9, // LDR X16, [X16,_fast_lazy_bind@pageoff]
- 0x00, 0x02, 0x1F, 0xD6 }, // BR X16
- { Reference::KindArch::AArch64, page21, 0, 0 },
- { true, offset12, 4, 0 },
- { Reference::KindArch::AArch64, page21, 12, 0 },
- { true, offset12scale8, 16, 0 }
-};
-
-bool ArchHandler_arm64::isCallSite(const Reference &ref) {
- if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
- return false;
- assert(ref.kindArch() == Reference::KindArch::AArch64);
- return (ref.kindValue() == branch26);
-}
-
-bool ArchHandler_arm64::isPointer(const Reference &ref) {
- if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
- return false;
- assert(ref.kindArch() == Reference::KindArch::AArch64);
- Reference::KindValue kind = ref.kindValue();
- return (kind == pointer64);
-}
-
-bool ArchHandler_arm64::isPairedReloc(const Relocation &r) {
- return ((r.type == ARM64_RELOC_ADDEND) || (r.type == ARM64_RELOC_SUBTRACTOR));
-}
-
-uint32_t ArchHandler_arm64::setDisplacementInBranch26(uint32_t instr,
- int32_t displacement) {
- assert((displacement <= 134217727) && (displacement > (-134217728)) &&
- "arm64 branch out of range");
- return (instr & 0xFC000000) | ((uint32_t)(displacement >> 2) & 0x03FFFFFF);
-}
-
-uint32_t ArchHandler_arm64::setDisplacementInADRP(uint32_t instruction,
- int64_t displacement) {
- assert((displacement <= 0x100000000LL) && (displacement > (-0x100000000LL)) &&
- "arm64 ADRP out of range");
- assert(((instruction & 0x9F000000) == 0x90000000) &&
- "reloc not on ADRP instruction");
- uint32_t immhi = (displacement >> 9) & (0x00FFFFE0);
- uint32_t immlo = (displacement << 17) & (0x60000000);
- return (instruction & 0x9F00001F) | immlo | immhi;
-}
-
-ArchHandler_arm64::Arm64Kind
-ArchHandler_arm64::offset12KindFromInstruction(uint32_t instruction) {
- if (instruction & 0x08000000) {
- switch ((instruction >> 30) & 0x3) {
- case 0:
- if ((instruction & 0x04800000) == 0x04800000)
- return offset12scale16;
- return offset12;
- case 1:
- return offset12scale2;
- case 2:
- return offset12scale4;
- case 3:
- return offset12scale8;
- }
- }
- return offset12;
-}
-
-uint32_t ArchHandler_arm64::setImm12(uint32_t instruction, uint32_t offset) {
- assert(((offset & 0xFFFFF000) == 0) && "imm12 offset out of range");
- uint32_t imm12 = offset << 10;
- return (instruction & 0xFFC003FF) | imm12;
-}
-
-llvm::Error ArchHandler_arm64::getReferenceInfo(
- const Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom,
- uint64_t fixupAddress, bool isBig,
- FindAtomBySectionAndAddress atomFromAddress,
- FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind,
- const lld::Atom **target, Reference::Addend *addend) {
- const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
- switch (relocPattern(reloc)) {
- case ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4:
- // ex: bl _foo
- *kind = branch26;
- if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
- return ec;
- *addend = 0;
- return llvm::Error::success();
- case ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4:
- // ex: adrp x1, _foo@PAGE
- *kind = page21;
- if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
- return ec;
- *addend = 0;
- return llvm::Error::success();
- case ARM64_RELOC_PAGEOFF12 | rExtern | rLength4:
- // ex: ldr x0, [x1, _foo@PAGEOFF]
- *kind = offset12KindFromInstruction(*(const little32_t *)fixupContent);
- if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
- return ec;
- *addend = 0;
- return llvm::Error::success();
- case ARM64_RELOC_GOT_LOAD_PAGE21 | rPcRel | rExtern | rLength4:
- // ex: adrp x1, _foo@GOTPAGE
- *kind = gotPage21;
- if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
- return ec;
- *addend = 0;
- return llvm::Error::success();
- case ARM64_RELOC_GOT_LOAD_PAGEOFF12 | rExtern | rLength4:
- // ex: ldr x0, [x1, _foo@GOTPAGEOFF]
- *kind = gotOffset12;
- if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
- return ec;
- *addend = 0;
- return llvm::Error::success();
- case ARM64_RELOC_TLVP_LOAD_PAGE21 | rPcRel | rExtern | rLength4:
- // ex: adrp x1, _foo@TLVPAGE
- *kind = tlvPage21;
- if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
- return ec;
- *addend = 0;
- return llvm::Error::success();
- case ARM64_RELOC_TLVP_LOAD_PAGEOFF12 | rExtern | rLength4:
- // ex: ldr x0, [x1, _foo@TLVPAGEOFF]
- *kind = tlvOffset12;
- if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
- return ec;
- *addend = 0;
- return llvm::Error::success();
- case ARM64_RELOC_UNSIGNED | rExtern | rLength8:
- // ex: .quad _foo + N
- *kind = pointer64;
- if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
- return ec;
- *addend = *(const little64_t *)fixupContent;
- return llvm::Error::success();
- case ARM64_RELOC_UNSIGNED | rLength8:
- // ex: .quad Lfoo + N
- *kind = pointer64;
- return atomFromAddress(reloc.symbol, *(const little64_t *)fixupContent,
- target, addend);
- case ARM64_RELOC_POINTER_TO_GOT | rExtern | rLength8:
- // ex: .quad _foo@GOT
- *kind = pointer64ToGOT;
- if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
- return ec;
- *addend = 0;
- return llvm::Error::success();
- case ARM64_RELOC_POINTER_TO_GOT | rPcRel | rExtern | rLength4:
- // ex: .long _foo@GOT - .
-
- // If we are in an .eh_frame section, then the kind of the relocation should
- // not be delta32ToGOT. It may instead be unwindCIEToPersonalityFunction.
- if (inAtom->contentType() == DefinedAtom::typeCFI)
- *kind = unwindCIEToPersonalityFunction;
- else
- *kind = delta32ToGOT;
-
- if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
- return ec;
- *addend = 0;
- return llvm::Error::success();
- default:
- return llvm::make_error<GenericError>("unsupported arm64 relocation type");
- }
-}
-
-llvm::Error ArchHandler_arm64::getPairReferenceInfo(
- const normalized::Relocation &reloc1, const normalized::Relocation &reloc2,
- const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress,
- bool swap, bool scatterable, FindAtomBySectionAndAddress atomFromAddress,
- FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind,
- const lld::Atom **target, Reference::Addend *addend) {
- const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
- switch (relocPattern(reloc1) << 16 | relocPattern(reloc2)) {
- case ((ARM64_RELOC_ADDEND | rLength4) << 16 |
- ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4):
- // ex: bl _foo+8
- *kind = branch26;
- if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
- return ec;
- *addend = reloc1.symbol;
- return llvm::Error::success();
- case ((ARM64_RELOC_ADDEND | rLength4) << 16 |
- ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4):
- // ex: adrp x1, _foo@PAGE
- *kind = page21;
- if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
- return ec;
- *addend = reloc1.symbol;
- return llvm::Error::success();
- case ((ARM64_RELOC_ADDEND | rLength4) << 16 |
- ARM64_RELOC_PAGEOFF12 | rExtern | rLength4): {
- // ex: ldr w0, [x1, _foo@PAGEOFF]
- uint32_t cont32 = (int32_t)*(const little32_t *)fixupContent;
- *kind = offset12KindFromInstruction(cont32);
- if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
- return ec;
- *addend = reloc1.symbol;
- return llvm::Error::success();
- }
- case ((ARM64_RELOC_SUBTRACTOR | rExtern | rLength8) << 16 |
- ARM64_RELOC_UNSIGNED | rExtern | rLength8):
- // ex: .quad _foo - .
- if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
- return ec;
-
- // If we are in an .eh_frame section, then the kind of the relocation should
- // not be delta64. It may instead be unwindFDEToFunction.
- if (inAtom->contentType() == DefinedAtom::typeCFI)
- *kind = unwindFDEToFunction;
- else
- *kind = delta64;
-
- // The offsets of the 2 relocations must match
- if (reloc1.offset != reloc2.offset)
- return llvm::make_error<GenericError>(
- "paired relocs must have the same offset");
- *addend = (int64_t)*(const little64_t *)fixupContent + offsetInAtom;
- return llvm::Error::success();
- case ((ARM64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 |
- ARM64_RELOC_UNSIGNED | rExtern | rLength4):
- // ex: .quad _foo - .
- *kind = delta32;
- if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
- return ec;
- *addend = (int32_t)*(const little32_t *)fixupContent + offsetInAtom;
- return llvm::Error::success();
- default:
- return llvm::make_error<GenericError>("unsupported arm64 relocation pair");
- }
-}
-
-void ArchHandler_arm64::generateAtomContent(
- const DefinedAtom &atom, bool relocatable, FindAddressForAtom findAddress,
- FindAddressForAtom findSectionAddress, uint64_t imageBaseAddress,
- llvm::MutableArrayRef<uint8_t> atomContentBuffer) {
- // Copy raw bytes.
- std::copy(atom.rawContent().begin(), atom.rawContent().end(),
- atomContentBuffer.begin());
- // Apply fix-ups.
-#ifndef NDEBUG
- if (atom.begin() != atom.end()) {
- DEBUG_WITH_TYPE("atom-content", llvm::dbgs()
- << "Applying fixups to atom:\n"
- << " address="
- << llvm::format(" 0x%09lX", &atom)
- << ", file=#"
- << atom.file().ordinal()
- << ", atom=#"
- << atom.ordinal()
- << ", name="
- << atom.name()
- << ", type="
- << atom.contentType()
- << "\n");
- }
-#endif
- for (const Reference *ref : atom) {
- uint32_t offset = ref->offsetInAtom();
- const Atom *target = ref->target();
- bool targetUnnamed = target->name().empty();
- uint64_t targetAddress = 0;
- if (isa<DefinedAtom>(target))
- targetAddress = findAddress(*target);
- uint64_t atomAddress = findAddress(atom);
- uint64_t fixupAddress = atomAddress + offset;
- if (relocatable) {
- applyFixupRelocatable(*ref, &atomContentBuffer[offset], fixupAddress,
- targetAddress, atomAddress, targetUnnamed);
- } else {
- applyFixupFinal(*ref, &atomContentBuffer[offset], fixupAddress,
- targetAddress, atomAddress, imageBaseAddress,
- findSectionAddress);
- }
- }
-}
-
-void ArchHandler_arm64::applyFixupFinal(const Reference &ref, uint8_t *loc,
- uint64_t fixupAddress,
- uint64_t targetAddress,
- uint64_t inAtomAddress,
- uint64_t imageBaseAddress,
- FindAddressForAtom findSectionAddress) {
- if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
- return;
- assert(ref.kindArch() == Reference::KindArch::AArch64);
- ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc);
- ulittle64_t *loc64 = reinterpret_cast<ulittle64_t *>(loc);
- int32_t displacement;
- uint32_t instruction;
- uint32_t value32;
- uint32_t value64;
- switch (static_cast<Arm64Kind>(ref.kindValue())) {
- case branch26:
- displacement = (targetAddress - fixupAddress) + ref.addend();
- *loc32 = setDisplacementInBranch26(*loc32, displacement);
- return;
- case page21:
- case gotPage21:
- case tlvPage21:
- displacement =
- ((targetAddress + ref.addend()) & (-4096)) - (fixupAddress & (-4096));
- *loc32 = setDisplacementInADRP(*loc32, displacement);
- return;
- case offset12:
- case gotOffset12:
- case tlvOffset12:
- displacement = (targetAddress + ref.addend()) & 0x00000FFF;
- *loc32 = setImm12(*loc32, displacement);
- return;
- case offset12scale2:
- displacement = (targetAddress + ref.addend()) & 0x00000FFF;
- assert(((displacement & 0x1) == 0) &&
- "scaled imm12 not accessing 2-byte aligneds");
- *loc32 = setImm12(*loc32, displacement >> 1);
- return;
- case offset12scale4:
- displacement = (targetAddress + ref.addend()) & 0x00000FFF;
- assert(((displacement & 0x3) == 0) &&
- "scaled imm12 not accessing 4-byte aligned");
- *loc32 = setImm12(*loc32, displacement >> 2);
- return;
- case offset12scale8:
- displacement = (targetAddress + ref.addend()) & 0x00000FFF;
- assert(((displacement & 0x7) == 0) &&
- "scaled imm12 not accessing 8-byte aligned");
- *loc32 = setImm12(*loc32, displacement >> 3);
- return;
- case offset12scale16:
- displacement = (targetAddress + ref.addend()) & 0x00000FFF;
- assert(((displacement & 0xF) == 0) &&
- "scaled imm12 not accessing 16-byte aligned");
- *loc32 = setImm12(*loc32, displacement >> 4);
- return;
- case addOffset12:
- instruction = *loc32;
- assert(((instruction & 0xFFC00000) == 0xF9400000) &&
- "GOT reloc is not an LDR instruction");
- displacement = (targetAddress + ref.addend()) & 0x00000FFF;
- value32 = 0x91000000 | (instruction & 0x000003FF);
- instruction = setImm12(value32, displacement);
- *loc32 = instruction;
- return;
- case pointer64:
- case pointer64ToGOT:
- *loc64 = targetAddress + ref.addend();
- return;
- case delta64:
- case unwindFDEToFunction:
- *loc64 = (targetAddress - fixupAddress) + ref.addend();
- return;
- case delta32:
- case delta32ToGOT:
- case unwindCIEToPersonalityFunction:
- *loc32 = (targetAddress - fixupAddress) + ref.addend();
- return;
- case negDelta32:
- *loc32 = fixupAddress - targetAddress + ref.addend();
- return;
- case lazyPointer:
- // Do nothing
- return;
- case lazyImmediateLocation:
- *loc32 = ref.addend();
- return;
- case imageOffset:
- *loc32 = (targetAddress - imageBaseAddress) + ref.addend();
- return;
- case imageOffsetGot:
- llvm_unreachable("imageOffsetGot should have been changed to imageOffset");
- break;
- case unwindInfoToEhFrame:
- value64 = targetAddress - findSectionAddress(*ref.target()) + ref.addend();
- assert(value64 < 0xffffffU && "offset in __eh_frame too large");
- *loc32 = (*loc32 & 0xff000000U) | value64;
- return;
- case invalid:
- // Fall into llvm_unreachable().
- break;
- }
- llvm_unreachable("invalid arm64 Reference Kind");
-}
-
-void ArchHandler_arm64::applyFixupRelocatable(const Reference &ref,
- uint8_t *loc,
- uint64_t fixupAddress,
- uint64_t targetAddress,
- uint64_t inAtomAddress,
- bool targetUnnamed) {
- if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
- return;
- assert(ref.kindArch() == Reference::KindArch::AArch64);
- ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc);
- ulittle64_t *loc64 = reinterpret_cast<ulittle64_t *>(loc);
- switch (static_cast<Arm64Kind>(ref.kindValue())) {
- case branch26:
- *loc32 = setDisplacementInBranch26(*loc32, 0);
- return;
- case page21:
- case gotPage21:
- case tlvPage21:
- *loc32 = setDisplacementInADRP(*loc32, 0);
- return;
- case offset12:
- case offset12scale2:
- case offset12scale4:
- case offset12scale8:
- case offset12scale16:
- case gotOffset12:
- case tlvOffset12:
- *loc32 = setImm12(*loc32, 0);
- return;
- case pointer64:
- if (targetUnnamed)
- *loc64 = targetAddress + ref.addend();
- else
- *loc64 = ref.addend();
- return;
- case delta64:
- *loc64 = ref.addend() + inAtomAddress - fixupAddress;
- return;
- case unwindFDEToFunction:
- // We don't emit unwindFDEToFunction in -r mode as they are implicitly
- // generated from the data in the __eh_frame section. So here we need
- // to use the targetAddress so that we can generate the full relocation
- // when we parse again later.
- *loc64 = targetAddress - fixupAddress;
- return;
- case delta32:
- *loc32 = ref.addend() + inAtomAddress - fixupAddress;
- return;
- case negDelta32:
- // We don't emit negDelta32 in -r mode as they are implicitly
- // generated from the data in the __eh_frame section. So here we need
- // to use the targetAddress so that we can generate the full relocation
- // when we parse again later.
- *loc32 = fixupAddress - targetAddress + ref.addend();
- return;
- case pointer64ToGOT:
- *loc64 = 0;
- return;
- case delta32ToGOT:
- *loc32 = inAtomAddress - fixupAddress;
- return;
- case unwindCIEToPersonalityFunction:
- // We don't emit unwindCIEToPersonalityFunction in -r mode as they are
- // implicitly generated from the data in the __eh_frame section. So here we
- // need to use the targetAddress so that we can generate the full relocation
- // when we parse again later.
- *loc32 = targetAddress - fixupAddress;
- return;
- case addOffset12:
- llvm_unreachable("lazy reference kind implies GOT pass was run");
- case lazyPointer:
- case lazyImmediateLocation:
- llvm_unreachable("lazy reference kind implies Stubs pass was run");
- case imageOffset:
- case imageOffsetGot:
- case unwindInfoToEhFrame:
- llvm_unreachable("fixup implies __unwind_info");
- return;
- case invalid:
- // Fall into llvm_unreachable().
- break;
- }
- llvm_unreachable("unknown arm64 Reference Kind");
-}
-
-void ArchHandler_arm64::appendSectionRelocations(
- const DefinedAtom &atom, uint64_t atomSectionOffset, const Reference &ref,
- FindSymbolIndexForAtom symbolIndexForAtom,
- FindSectionIndexForAtom sectionIndexForAtom,
- FindAddressForAtom addressForAtom, normalized::Relocations &relocs) {
- if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
- return;
- assert(ref.kindArch() == Reference::KindArch::AArch64);
- uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom();
- switch (static_cast<Arm64Kind>(ref.kindValue())) {
- case branch26:
- if (ref.addend()) {
- appendReloc(relocs, sectionOffset, ref.addend(), 0,
- ARM64_RELOC_ADDEND | rLength4);
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4);
- } else {
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4);
- }
- return;
- case page21:
- if (ref.addend()) {
- appendReloc(relocs, sectionOffset, ref.addend(), 0,
- ARM64_RELOC_ADDEND | rLength4);
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4);
- } else {
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4);
- }
- return;
- case offset12:
- case offset12scale2:
- case offset12scale4:
- case offset12scale8:
- case offset12scale16:
- if (ref.addend()) {
- appendReloc(relocs, sectionOffset, ref.addend(), 0,
- ARM64_RELOC_ADDEND | rLength4);
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- ARM64_RELOC_PAGEOFF12 | rExtern | rLength4);
- } else {
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- ARM64_RELOC_PAGEOFF12 | rExtern | rLength4);
- }
- return;
- case gotPage21:
- assert(ref.addend() == 0);
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- ARM64_RELOC_GOT_LOAD_PAGE21 | rPcRel | rExtern | rLength4);
- return;
- case gotOffset12:
- assert(ref.addend() == 0);
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- ARM64_RELOC_GOT_LOAD_PAGEOFF12 | rExtern | rLength4);
- return;
- case tlvPage21:
- assert(ref.addend() == 0);
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- ARM64_RELOC_TLVP_LOAD_PAGE21 | rPcRel | rExtern | rLength4);
- return;
- case tlvOffset12:
- assert(ref.addend() == 0);
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- ARM64_RELOC_TLVP_LOAD_PAGEOFF12 | rExtern | rLength4);
- return;
- case pointer64:
- if (ref.target()->name().empty())
- appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
- ARM64_RELOC_UNSIGNED | rLength8);
- else
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- ARM64_RELOC_UNSIGNED | rExtern | rLength8);
- return;
- case delta64:
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
- ARM64_RELOC_SUBTRACTOR | rExtern | rLength8);
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- ARM64_RELOC_UNSIGNED | rExtern | rLength8);
- return;
- case delta32:
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
- ARM64_RELOC_SUBTRACTOR | rExtern | rLength4 );
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- ARM64_RELOC_UNSIGNED | rExtern | rLength4 );
- return;
- case pointer64ToGOT:
- assert(ref.addend() == 0);
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- ARM64_RELOC_POINTER_TO_GOT | rExtern | rLength8);
- return;
- case delta32ToGOT:
- assert(ref.addend() == 0);
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- ARM64_RELOC_POINTER_TO_GOT | rPcRel | rExtern | rLength4);
- return;
- case addOffset12:
- llvm_unreachable("lazy reference kind implies GOT pass was run");
- case lazyPointer:
- case lazyImmediateLocation:
- llvm_unreachable("lazy reference kind implies Stubs pass was run");
- case imageOffset:
- case imageOffsetGot:
- llvm_unreachable("deltas from mach_header can only be in final images");
- case unwindCIEToPersonalityFunction:
- case unwindFDEToFunction:
- case unwindInfoToEhFrame:
- case negDelta32:
- // Do nothing.
- return;
- case invalid:
- // Fall into llvm_unreachable().
- break;
- }
- llvm_unreachable("unknown arm64 Reference Kind");
-}
-
-std::unique_ptr<mach_o::ArchHandler> ArchHandler::create_arm64() {
- return std::unique_ptr<mach_o::ArchHandler>(new ArchHandler_arm64());
-}
-
-} // namespace mach_o
-} // namespace lld
+++ /dev/null
-//===- lib/FileFormat/MachO/ArchHandler_x86.cpp ---------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "ArchHandler.h"
-#include "Atoms.h"
-#include "MachONormalizedFileBinaryUtils.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/ADT/StringSwitch.h"
-#include "llvm/ADT/Triple.h"
-#include "llvm/Support/Endian.h"
-#include "llvm/Support/ErrorHandling.h"
-
-using namespace llvm::MachO;
-using namespace lld::mach_o::normalized;
-
-namespace lld {
-namespace mach_o {
-
-using llvm::support::ulittle16_t;
-using llvm::support::ulittle32_t;
-
-using llvm::support::little16_t;
-using llvm::support::little32_t;
-
-class ArchHandler_x86 : public ArchHandler {
-public:
- ArchHandler_x86() = default;
- ~ArchHandler_x86() override = default;
-
- const Registry::KindStrings *kindStrings() override { return _sKindStrings; }
-
- Reference::KindArch kindArch() override { return Reference::KindArch::x86; }
-
- const StubInfo &stubInfo() override { return _sStubInfo; }
- bool isCallSite(const Reference &) override;
- bool isNonCallBranch(const Reference &) override {
- return false;
- }
-
- bool isPointer(const Reference &) override;
- bool isPairedReloc(const normalized::Relocation &) override;
-
- bool needsCompactUnwind() override {
- return false;
- }
-
- Reference::KindValue imageOffsetKind() override {
- return invalid;
- }
-
- Reference::KindValue imageOffsetKindIndirect() override {
- return invalid;
- }
-
- Reference::KindValue unwindRefToPersonalityFunctionKind() override {
- return invalid;
- }
-
- Reference::KindValue unwindRefToCIEKind() override {
- return negDelta32;
- }
-
- Reference::KindValue unwindRefToFunctionKind() override{
- return delta32;
- }
-
- Reference::KindValue lazyImmediateLocationKind() override {
- return lazyImmediateLocation;
- }
-
- Reference::KindValue unwindRefToEhFrameKind() override {
- return invalid;
- }
-
- Reference::KindValue pointerKind() override {
- return invalid;
- }
-
- uint32_t dwarfCompactUnwindType() override {
- return 0x04000000U;
- }
-
- llvm::Error getReferenceInfo(const normalized::Relocation &reloc,
- const DefinedAtom *inAtom,
- uint32_t offsetInAtom,
- uint64_t fixupAddress, bool swap,
- FindAtomBySectionAndAddress atomFromAddress,
- FindAtomBySymbolIndex atomFromSymbolIndex,
- Reference::KindValue *kind,
- const lld::Atom **target,
- Reference::Addend *addend) override;
- llvm::Error
- getPairReferenceInfo(const normalized::Relocation &reloc1,
- const normalized::Relocation &reloc2,
- const DefinedAtom *inAtom,
- uint32_t offsetInAtom,
- uint64_t fixupAddress, bool swap, bool scatterable,
- FindAtomBySectionAndAddress atomFromAddress,
- FindAtomBySymbolIndex atomFromSymbolIndex,
- Reference::KindValue *kind,
- const lld::Atom **target,
- Reference::Addend *addend) override;
-
- void generateAtomContent(const DefinedAtom &atom, bool relocatable,
- FindAddressForAtom findAddress,
- FindAddressForAtom findSectionAddress,
- uint64_t imageBaseAddress,
- llvm::MutableArrayRef<uint8_t> atomContentBuffer) override;
-
- void appendSectionRelocations(const DefinedAtom &atom,
- uint64_t atomSectionOffset,
- const Reference &ref,
- FindSymbolIndexForAtom symbolIndexForAtom,
- FindSectionIndexForAtom sectionIndexForAtom,
- FindAddressForAtom addressForAtom,
- normalized::Relocations &relocs) override;
-
- bool isDataInCodeTransition(Reference::KindValue refKind) override {
- return refKind == modeCode || refKind == modeData;
- }
-
- Reference::KindValue dataInCodeTransitionStart(
- const MachODefinedAtom &atom) override {
- return modeData;
- }
-
- Reference::KindValue dataInCodeTransitionEnd(
- const MachODefinedAtom &atom) override {
- return modeCode;
- }
-
-private:
- static const Registry::KindStrings _sKindStrings[];
- static const StubInfo _sStubInfo;
-
- enum X86Kind : Reference::KindValue {
- invalid, /// for error condition
-
- modeCode, /// Content starting at this offset is code.
- modeData, /// Content starting at this offset is data.
-
- // Kinds found in mach-o .o files:
- branch32, /// ex: call _foo
- branch16, /// ex: callw _foo
- abs32, /// ex: movl _foo, %eax
- funcRel32, /// ex: movl _foo-L1(%eax), %eax
- pointer32, /// ex: .long _foo
- delta32, /// ex: .long _foo - .
- negDelta32, /// ex: .long . - _foo
-
- // Kinds introduced by Passes:
- lazyPointer, /// Location contains a lazy pointer.
- lazyImmediateLocation, /// Location contains immediate value used in stub.
- };
-
- static bool useExternalRelocationTo(const Atom &target);
-
- void applyFixupFinal(const Reference &ref, uint8_t *location,
- uint64_t fixupAddress, uint64_t targetAddress,
- uint64_t inAtomAddress);
-
- void applyFixupRelocatable(const Reference &ref, uint8_t *location,
- uint64_t fixupAddress,
- uint64_t targetAddress,
- uint64_t inAtomAddress);
-};
-
-//===----------------------------------------------------------------------===//
-// ArchHandler_x86
-//===----------------------------------------------------------------------===//
-
-const Registry::KindStrings ArchHandler_x86::_sKindStrings[] = {
- LLD_KIND_STRING_ENTRY(invalid),
- LLD_KIND_STRING_ENTRY(modeCode),
- LLD_KIND_STRING_ENTRY(modeData),
- LLD_KIND_STRING_ENTRY(branch32),
- LLD_KIND_STRING_ENTRY(branch16),
- LLD_KIND_STRING_ENTRY(abs32),
- LLD_KIND_STRING_ENTRY(funcRel32),
- LLD_KIND_STRING_ENTRY(pointer32),
- LLD_KIND_STRING_ENTRY(delta32),
- LLD_KIND_STRING_ENTRY(negDelta32),
- LLD_KIND_STRING_ENTRY(lazyPointer),
- LLD_KIND_STRING_ENTRY(lazyImmediateLocation),
- LLD_KIND_STRING_END
-};
-
-const ArchHandler::StubInfo ArchHandler_x86::_sStubInfo = {
- "dyld_stub_binder",
-
- // Lazy pointer references
- { Reference::KindArch::x86, pointer32, 0, 0 },
- { Reference::KindArch::x86, lazyPointer, 0, 0 },
-
- // GOT pointer to dyld_stub_binder
- { Reference::KindArch::x86, pointer32, 0, 0 },
-
- // x86 code alignment
- 1,
-
- // Stub size and code
- 6,
- { 0xff, 0x25, 0x00, 0x00, 0x00, 0x00 }, // jmp *lazyPointer
- { Reference::KindArch::x86, abs32, 2, 0 },
- { false, 0, 0, 0 },
-
- // Stub Helper size and code
- 10,
- { 0x68, 0x00, 0x00, 0x00, 0x00, // pushl $lazy-info-offset
- 0xE9, 0x00, 0x00, 0x00, 0x00 }, // jmp helperhelper
- { Reference::KindArch::x86, lazyImmediateLocation, 1, 0 },
- { Reference::KindArch::x86, branch32, 6, 0 },
-
- // Stub helper image cache content type
- DefinedAtom::typeNonLazyPointer,
-
- // Stub Helper-Common size and code
- 12,
- // Stub helper alignment
- 2,
- { 0x68, 0x00, 0x00, 0x00, 0x00, // pushl $dyld_ImageLoaderCache
- 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *_fast_lazy_bind
- 0x90 }, // nop
- { Reference::KindArch::x86, abs32, 1, 0 },
- { false, 0, 0, 0 },
- { Reference::KindArch::x86, abs32, 7, 0 },
- { false, 0, 0, 0 }
-};
-
-bool ArchHandler_x86::isCallSite(const Reference &ref) {
- return (ref.kindValue() == branch32);
-}
-
-bool ArchHandler_x86::isPointer(const Reference &ref) {
- return (ref.kindValue() == pointer32);
-}
-
-bool ArchHandler_x86::isPairedReloc(const Relocation &reloc) {
- if (!reloc.scattered)
- return false;
- return (reloc.type == GENERIC_RELOC_LOCAL_SECTDIFF) ||
- (reloc.type == GENERIC_RELOC_SECTDIFF);
-}
-
-llvm::Error
-ArchHandler_x86::getReferenceInfo(const Relocation &reloc,
- const DefinedAtom *inAtom,
- uint32_t offsetInAtom,
- uint64_t fixupAddress, bool swap,
- FindAtomBySectionAndAddress atomFromAddress,
- FindAtomBySymbolIndex atomFromSymbolIndex,
- Reference::KindValue *kind,
- const lld::Atom **target,
- Reference::Addend *addend) {
- DefinedAtom::ContentPermissions perms;
- const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
- uint64_t targetAddress;
- switch (relocPattern(reloc)) {
- case GENERIC_RELOC_VANILLA | rPcRel | rExtern | rLength4:
- // ex: call _foo (and _foo undefined)
- *kind = branch32;
- if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
- return ec;
- *addend = fixupAddress + 4 + (int32_t)*(const little32_t *)fixupContent;
- break;
- case GENERIC_RELOC_VANILLA | rPcRel | rLength4:
- // ex: call _foo (and _foo defined)
- *kind = branch32;
- targetAddress =
- fixupAddress + 4 + (int32_t) * (const little32_t *)fixupContent;
- return atomFromAddress(reloc.symbol, targetAddress, target, addend);
- break;
- case GENERIC_RELOC_VANILLA | rScattered | rPcRel | rLength4:
- // ex: call _foo+n (and _foo defined)
- *kind = branch32;
- targetAddress =
- fixupAddress + 4 + (int32_t) * (const little32_t *)fixupContent;
- if (auto ec = atomFromAddress(0, reloc.value, target, addend))
- return ec;
- *addend = targetAddress - reloc.value;
- break;
- case GENERIC_RELOC_VANILLA | rPcRel | rExtern | rLength2:
- // ex: callw _foo (and _foo undefined)
- *kind = branch16;
- if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
- return ec;
- *addend = fixupAddress + 2 + (int16_t)*(const little16_t *)fixupContent;
- break;
- case GENERIC_RELOC_VANILLA | rPcRel | rLength2:
- // ex: callw _foo (and _foo defined)
- *kind = branch16;
- targetAddress =
- fixupAddress + 2 + (int16_t) * (const little16_t *)fixupContent;
- return atomFromAddress(reloc.symbol, targetAddress, target, addend);
- break;
- case GENERIC_RELOC_VANILLA | rScattered | rPcRel | rLength2:
- // ex: callw _foo+n (and _foo defined)
- *kind = branch16;
- targetAddress =
- fixupAddress + 2 + (int16_t) * (const little16_t *)fixupContent;
- if (auto ec = atomFromAddress(0, reloc.value, target, addend))
- return ec;
- *addend = targetAddress - reloc.value;
- break;
- case GENERIC_RELOC_VANILLA | rExtern | rLength4:
- // ex: movl _foo, %eax (and _foo undefined)
- // ex: .long _foo (and _foo undefined)
- perms = inAtom->permissions();
- *kind =
- ((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) ? abs32
- : pointer32;
- if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
- return ec;
- *addend = *(const ulittle32_t *)fixupContent;
- break;
- case GENERIC_RELOC_VANILLA | rLength4:
- // ex: movl _foo, %eax (and _foo defined)
- // ex: .long _foo (and _foo defined)
- perms = inAtom->permissions();
- *kind =
- ((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) ? abs32
- : pointer32;
- targetAddress = *(const ulittle32_t *)fixupContent;
- return atomFromAddress(reloc.symbol, targetAddress, target, addend);
- break;
- case GENERIC_RELOC_VANILLA | rScattered | rLength4:
- // ex: .long _foo+n (and _foo defined)
- perms = inAtom->permissions();
- *kind =
- ((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) ? abs32
- : pointer32;
- if (auto ec = atomFromAddress(0, reloc.value, target, addend))
- return ec;
- *addend = *(const ulittle32_t *)fixupContent - reloc.value;
- break;
- default:
- return llvm::make_error<GenericError>("unsupported i386 relocation type");
- }
- return llvm::Error::success();
-}
-
-llvm::Error
-ArchHandler_x86::getPairReferenceInfo(const normalized::Relocation &reloc1,
- const normalized::Relocation &reloc2,
- const DefinedAtom *inAtom,
- uint32_t offsetInAtom,
- uint64_t fixupAddress, bool swap,
- bool scatterable,
- FindAtomBySectionAndAddress atomFromAddr,
- FindAtomBySymbolIndex atomFromSymbolIndex,
- Reference::KindValue *kind,
- const lld::Atom **target,
- Reference::Addend *addend) {
- const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
- DefinedAtom::ContentPermissions perms = inAtom->permissions();
- uint32_t fromAddress;
- uint32_t toAddress;
- uint32_t value;
- const lld::Atom *fromTarget;
- Reference::Addend offsetInTo;
- Reference::Addend offsetInFrom;
- switch (relocPattern(reloc1) << 16 | relocPattern(reloc2)) {
- case ((GENERIC_RELOC_SECTDIFF | rScattered | rLength4) << 16 |
- GENERIC_RELOC_PAIR | rScattered | rLength4):
- case ((GENERIC_RELOC_LOCAL_SECTDIFF | rScattered | rLength4) << 16 |
- GENERIC_RELOC_PAIR | rScattered | rLength4):
- toAddress = reloc1.value;
- fromAddress = reloc2.value;
- value = *(const little32_t *)fixupContent;
- if (auto ec = atomFromAddr(0, toAddress, target, &offsetInTo))
- return ec;
- if (auto ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom))
- return ec;
- if (fromTarget != inAtom) {
- if (*target != inAtom)
- return llvm::make_error<GenericError>(
- "SECTDIFF relocation where neither target is in atom");
- *kind = negDelta32;
- *addend = toAddress - value - fromAddress;
- *target = fromTarget;
- } else {
- if ((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) {
- // SECTDIFF relocations are used in i386 codegen where the function
- // prolog does a CALL to the next instruction which POPs the return
- // address into EBX which becomes the pic-base register. The POP
- // instruction is label the used for the subtrahend in expressions.
- // The funcRel32 kind represents the 32-bit delta to some symbol from
- // the start of the function (atom) containing the funcRel32.
- *kind = funcRel32;
- uint32_t ta = fromAddress + value - toAddress;
- *addend = ta - offsetInFrom;
- } else {
- *kind = delta32;
- *addend = fromAddress + value - toAddress;
- }
- }
- return llvm::Error::success();
- break;
- default:
- return llvm::make_error<GenericError>("unsupported i386 relocation type");
- }
-}
-
-void ArchHandler_x86::generateAtomContent(const DefinedAtom &atom,
- bool relocatable,
- FindAddressForAtom findAddress,
- FindAddressForAtom findSectionAddress,
- uint64_t imageBaseAddress,
- llvm::MutableArrayRef<uint8_t> atomContentBuffer) {
- // Copy raw bytes.
- std::copy(atom.rawContent().begin(), atom.rawContent().end(),
- atomContentBuffer.begin());
- // Apply fix-ups.
- for (const Reference *ref : atom) {
- uint32_t offset = ref->offsetInAtom();
- const Atom *target = ref->target();
- uint64_t targetAddress = 0;
- if (isa<DefinedAtom>(target))
- targetAddress = findAddress(*target);
- uint64_t atomAddress = findAddress(atom);
- uint64_t fixupAddress = atomAddress + offset;
- if (relocatable) {
- applyFixupRelocatable(*ref, &atomContentBuffer[offset],
- fixupAddress, targetAddress,
- atomAddress);
- } else {
- applyFixupFinal(*ref, &atomContentBuffer[offset],
- fixupAddress, targetAddress,
- atomAddress);
- }
- }
-}
-
-void ArchHandler_x86::applyFixupFinal(const Reference &ref, uint8_t *loc,
- uint64_t fixupAddress,
- uint64_t targetAddress,
- uint64_t inAtomAddress) {
- if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
- return;
- assert(ref.kindArch() == Reference::KindArch::x86);
- ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc);
- switch (static_cast<X86Kind>(ref.kindValue())) {
- case branch32:
- *loc32 = (targetAddress - (fixupAddress + 4)) + ref.addend();
- break;
- case branch16:
- *loc32 = (targetAddress - (fixupAddress + 2)) + ref.addend();
- break;
- case pointer32:
- case abs32:
- *loc32 = targetAddress + ref.addend();
- break;
- case funcRel32:
- *loc32 = targetAddress - inAtomAddress + ref.addend();
- break;
- case delta32:
- *loc32 = targetAddress - fixupAddress + ref.addend();
- break;
- case negDelta32:
- *loc32 = fixupAddress - targetAddress + ref.addend();
- break;
- case modeCode:
- case modeData:
- case lazyPointer:
- // do nothing
- break;
- case lazyImmediateLocation:
- *loc32 = ref.addend();
- break;
- case invalid:
- llvm_unreachable("invalid x86 Reference Kind");
- break;
- }
-}
-
-void ArchHandler_x86::applyFixupRelocatable(const Reference &ref,
- uint8_t *loc,
- uint64_t fixupAddress,
- uint64_t targetAddress,
- uint64_t inAtomAddress) {
- if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
- return;
- assert(ref.kindArch() == Reference::KindArch::x86);
- bool useExternalReloc = useExternalRelocationTo(*ref.target());
- ulittle16_t *loc16 = reinterpret_cast<ulittle16_t *>(loc);
- ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc);
- switch (static_cast<X86Kind>(ref.kindValue())) {
- case branch32:
- if (useExternalReloc)
- *loc32 = ref.addend() - (fixupAddress + 4);
- else
- *loc32 =(targetAddress - (fixupAddress+4)) + ref.addend();
- break;
- case branch16:
- if (useExternalReloc)
- *loc16 = ref.addend() - (fixupAddress + 2);
- else
- *loc16 = (targetAddress - (fixupAddress+2)) + ref.addend();
- break;
- case pointer32:
- case abs32:
- *loc32 = targetAddress + ref.addend();
- break;
- case funcRel32:
- *loc32 = targetAddress - inAtomAddress + ref.addend(); // FIXME
- break;
- case delta32:
- *loc32 = targetAddress - fixupAddress + ref.addend();
- break;
- case negDelta32:
- *loc32 = fixupAddress - targetAddress + ref.addend();
- break;
- case modeCode:
- case modeData:
- case lazyPointer:
- case lazyImmediateLocation:
- // do nothing
- break;
- case invalid:
- llvm_unreachable("invalid x86 Reference Kind");
- break;
- }
-}
-
-bool ArchHandler_x86::useExternalRelocationTo(const Atom &target) {
- // Undefined symbols are referenced via external relocations.
- if (isa<UndefinedAtom>(&target))
- return true;
- if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(&target)) {
- switch (defAtom->merge()) {
- case DefinedAtom::mergeAsTentative:
- // Tentative definitions are referenced via external relocations.
- return true;
- case DefinedAtom::mergeAsWeak:
- case DefinedAtom::mergeAsWeakAndAddressUsed:
- // Global weak-defs are referenced via external relocations.
- return (defAtom->scope() == DefinedAtom::scopeGlobal);
- default:
- break;
- }
- }
- // Everything else is reference via an internal relocation.
- return false;
-}
-
-void ArchHandler_x86::appendSectionRelocations(
- const DefinedAtom &atom,
- uint64_t atomSectionOffset,
- const Reference &ref,
- FindSymbolIndexForAtom symbolIndexForAtom,
- FindSectionIndexForAtom sectionIndexForAtom,
- FindAddressForAtom addressForAtom,
- normalized::Relocations &relocs) {
- if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
- return;
- assert(ref.kindArch() == Reference::KindArch::x86);
- uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom();
- bool useExternalReloc = useExternalRelocationTo(*ref.target());
- switch (static_cast<X86Kind>(ref.kindValue())) {
- case modeCode:
- case modeData:
- break;
- case branch32:
- if (useExternalReloc) {
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- GENERIC_RELOC_VANILLA | rExtern | rPcRel | rLength4);
- } else {
- if (ref.addend() != 0)
- appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
- GENERIC_RELOC_VANILLA | rScattered | rPcRel | rLength4);
- else
- appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
- GENERIC_RELOC_VANILLA | rPcRel | rLength4);
- }
- break;
- case branch16:
- if (useExternalReloc) {
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- GENERIC_RELOC_VANILLA | rExtern | rPcRel | rLength2);
- } else {
- if (ref.addend() != 0)
- appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
- GENERIC_RELOC_VANILLA | rScattered | rPcRel | rLength2);
- else
- appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
- GENERIC_RELOC_VANILLA | rPcRel | rLength2);
- }
- break;
- case pointer32:
- case abs32:
- if (useExternalReloc)
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- GENERIC_RELOC_VANILLA | rExtern | rLength4);
- else {
- if (ref.addend() != 0)
- appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
- GENERIC_RELOC_VANILLA | rScattered | rLength4);
- else
- appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
- GENERIC_RELOC_VANILLA | rLength4);
- }
- break;
- case funcRel32:
- appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
- GENERIC_RELOC_SECTDIFF | rScattered | rLength4);
- appendReloc(relocs, sectionOffset, 0, addressForAtom(atom) - ref.addend(),
- GENERIC_RELOC_PAIR | rScattered | rLength4);
- break;
- case delta32:
- appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
- GENERIC_RELOC_SECTDIFF | rScattered | rLength4);
- appendReloc(relocs, sectionOffset, 0, addressForAtom(atom) +
- ref.offsetInAtom(),
- GENERIC_RELOC_PAIR | rScattered | rLength4);
- break;
- case negDelta32:
- appendReloc(relocs, sectionOffset, 0, addressForAtom(atom) +
- ref.offsetInAtom(),
- GENERIC_RELOC_SECTDIFF | rScattered | rLength4);
- appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
- GENERIC_RELOC_PAIR | rScattered | rLength4);
- break;
- case lazyPointer:
- case lazyImmediateLocation:
- llvm_unreachable("lazy reference kind implies Stubs pass was run");
- break;
- case invalid:
- llvm_unreachable("unknown x86 Reference Kind");
- break;
- }
-}
-
-std::unique_ptr<mach_o::ArchHandler> ArchHandler::create_x86() {
- return std::unique_ptr<mach_o::ArchHandler>(new ArchHandler_x86());
-}
-
-} // namespace mach_o
-} // namespace lld
+++ /dev/null
-//===- lib/FileFormat/MachO/ArchHandler_x86_64.cpp ------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "ArchHandler.h"
-#include "Atoms.h"
-#include "MachONormalizedFileBinaryUtils.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/ADT/StringSwitch.h"
-#include "llvm/ADT/Triple.h"
-#include "llvm/Support/Endian.h"
-#include "llvm/Support/ErrorHandling.h"
-
-using namespace llvm::MachO;
-using namespace lld::mach_o::normalized;
-
-namespace lld {
-namespace mach_o {
-
-using llvm::support::ulittle32_t;
-using llvm::support::ulittle64_t;
-
-using llvm::support::little32_t;
-using llvm::support::little64_t;
-
-class ArchHandler_x86_64 : public ArchHandler {
-public:
- ArchHandler_x86_64() = default;
- ~ArchHandler_x86_64() override = default;
-
- const Registry::KindStrings *kindStrings() override { return _sKindStrings; }
-
- Reference::KindArch kindArch() override {
- return Reference::KindArch::x86_64;
- }
-
- /// Used by GOTPass to locate GOT References
- bool isGOTAccess(const Reference &ref, bool &canBypassGOT) override {
- if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
- return false;
- assert(ref.kindArch() == Reference::KindArch::x86_64);
- switch (ref.kindValue()) {
- case ripRel32GotLoad:
- canBypassGOT = true;
- return true;
- case ripRel32Got:
- canBypassGOT = false;
- return true;
- case imageOffsetGot:
- canBypassGOT = false;
- return true;
- default:
- return false;
- }
- }
-
- bool isTLVAccess(const Reference &ref) const override {
- assert(ref.kindNamespace() == Reference::KindNamespace::mach_o);
- assert(ref.kindArch() == Reference::KindArch::x86_64);
- return ref.kindValue() == ripRel32Tlv;
- }
-
- void updateReferenceToTLV(const Reference *ref) override {
- assert(ref->kindNamespace() == Reference::KindNamespace::mach_o);
- assert(ref->kindArch() == Reference::KindArch::x86_64);
- assert(ref->kindValue() == ripRel32Tlv);
- const_cast<Reference*>(ref)->setKindValue(ripRel32);
- }
-
- /// Used by GOTPass to update GOT References
- void updateReferenceToGOT(const Reference *ref, bool targetNowGOT) override {
- assert(ref->kindNamespace() == Reference::KindNamespace::mach_o);
- assert(ref->kindArch() == Reference::KindArch::x86_64);
-
- switch (ref->kindValue()) {
- case ripRel32Got:
- assert(targetNowGOT && "target must be GOT");
- LLVM_FALLTHROUGH;
- case ripRel32GotLoad:
- const_cast<Reference *>(ref)
- ->setKindValue(targetNowGOT ? ripRel32 : ripRel32GotLoadNowLea);
- break;
- case imageOffsetGot:
- const_cast<Reference *>(ref)->setKindValue(imageOffset);
- break;
- default:
- llvm_unreachable("unknown GOT reference kind");
- }
- }
-
- bool needsCompactUnwind() override {
- return true;
- }
-
- Reference::KindValue imageOffsetKind() override {
- return imageOffset;
- }
-
- Reference::KindValue imageOffsetKindIndirect() override {
- return imageOffsetGot;
- }
-
- Reference::KindValue unwindRefToPersonalityFunctionKind() override {
- return ripRel32Got;
- }
-
- Reference::KindValue unwindRefToCIEKind() override {
- return negDelta32;
- }
-
- Reference::KindValue unwindRefToFunctionKind() override{
- return unwindFDEToFunction;
- }
-
- Reference::KindValue lazyImmediateLocationKind() override {
- return lazyImmediateLocation;
- }
-
- Reference::KindValue unwindRefToEhFrameKind() override {
- return unwindInfoToEhFrame;
- }
-
- Reference::KindValue pointerKind() override {
- return pointer64;
- }
-
- uint32_t dwarfCompactUnwindType() override {
- return 0x04000000U;
- }
-
- const StubInfo &stubInfo() override { return _sStubInfo; }
-
- bool isNonCallBranch(const Reference &) override {
- return false;
- }
-
- bool isCallSite(const Reference &) override;
- bool isPointer(const Reference &) override;
- bool isPairedReloc(const normalized::Relocation &) override;
-
- llvm::Error getReferenceInfo(const normalized::Relocation &reloc,
- const DefinedAtom *inAtom,
- uint32_t offsetInAtom,
- uint64_t fixupAddress, bool swap,
- FindAtomBySectionAndAddress atomFromAddress,
- FindAtomBySymbolIndex atomFromSymbolIndex,
- Reference::KindValue *kind,
- const lld::Atom **target,
- Reference::Addend *addend) override;
- llvm::Error
- getPairReferenceInfo(const normalized::Relocation &reloc1,
- const normalized::Relocation &reloc2,
- const DefinedAtom *inAtom,
- uint32_t offsetInAtom,
- uint64_t fixupAddress, bool swap, bool scatterable,
- FindAtomBySectionAndAddress atomFromAddress,
- FindAtomBySymbolIndex atomFromSymbolIndex,
- Reference::KindValue *kind,
- const lld::Atom **target,
- Reference::Addend *addend) override;
-
- bool needsLocalSymbolInRelocatableFile(const DefinedAtom *atom) override {
- return (atom->contentType() == DefinedAtom::typeCString);
- }
-
- void generateAtomContent(const DefinedAtom &atom, bool relocatable,
- FindAddressForAtom findAddress,
- FindAddressForAtom findSectionAddress,
- uint64_t imageBase,
- llvm::MutableArrayRef<uint8_t> atomContentBuffer) override;
-
- void appendSectionRelocations(const DefinedAtom &atom,
- uint64_t atomSectionOffset,
- const Reference &ref,
- FindSymbolIndexForAtom symbolIndexForAtom,
- FindSectionIndexForAtom sectionIndexForAtom,
- FindAddressForAtom addressForAtom,
- normalized::Relocations &relocs) override;
-
- bool isDataInCodeTransition(Reference::KindValue refKind) override {
- return refKind == modeCode || refKind == modeData;
- }
-
- Reference::KindValue dataInCodeTransitionStart(
- const MachODefinedAtom &atom) override {
- return modeData;
- }
-
- Reference::KindValue dataInCodeTransitionEnd(
- const MachODefinedAtom &atom) override {
- return modeCode;
- }
-
-private:
- static const Registry::KindStrings _sKindStrings[];
- static const StubInfo _sStubInfo;
-
- enum X86_64Kind: Reference::KindValue {
- invalid, /// for error condition
-
- modeCode, /// Content starting at this offset is code.
- modeData, /// Content starting at this offset is data.
-
- // Kinds found in mach-o .o files:
- branch32, /// ex: call _foo
- ripRel32, /// ex: movq _foo(%rip), %rax
- ripRel32Minus1, /// ex: movb $0x12, _foo(%rip)
- ripRel32Minus2, /// ex: movw $0x1234, _foo(%rip)
- ripRel32Minus4, /// ex: movl $0x12345678, _foo(%rip)
- ripRel32Anon, /// ex: movq L1(%rip), %rax
- ripRel32Minus1Anon, /// ex: movb $0x12, L1(%rip)
- ripRel32Minus2Anon, /// ex: movw $0x1234, L1(%rip)
- ripRel32Minus4Anon, /// ex: movw $0x12345678, L1(%rip)
- ripRel32GotLoad, /// ex: movq _foo@GOTPCREL(%rip), %rax
- ripRel32Got, /// ex: pushq _foo@GOTPCREL(%rip)
- ripRel32Tlv, /// ex: movq _foo@TLVP(%rip), %rdi
- pointer64, /// ex: .quad _foo
- pointer64Anon, /// ex: .quad L1
- delta64, /// ex: .quad _foo - .
- delta32, /// ex: .long _foo - .
- delta64Anon, /// ex: .quad L1 - .
- delta32Anon, /// ex: .long L1 - .
- negDelta64, /// ex: .quad . - _foo
- negDelta32, /// ex: .long . - _foo
-
- // Kinds introduced by Passes:
- ripRel32GotLoadNowLea, /// Target of GOT load is in linkage unit so
- /// "movq _foo@GOTPCREL(%rip), %rax" can be changed
- /// to "leaq _foo(%rip), %rax
- lazyPointer, /// Location contains a lazy pointer.
- lazyImmediateLocation, /// Location contains immediate value used in stub.
-
- imageOffset, /// Location contains offset of atom in final image
- imageOffsetGot, /// Location contains offset of GOT entry for atom in
- /// final image (typically personality function).
- unwindFDEToFunction, /// Nearly delta64, but cannot be rematerialized in
- /// relocatable object (yay for implicit contracts!).
- unwindInfoToEhFrame, /// Fix low 24 bits of compact unwind encoding to
- /// refer to __eh_frame entry.
- tlvInitSectionOffset /// Location contains offset tlv init-value atom
- /// within the __thread_data section.
- };
-
- Reference::KindValue kindFromReloc(const normalized::Relocation &reloc);
-
- void applyFixupFinal(const Reference &ref, uint8_t *location,
- uint64_t fixupAddress, uint64_t targetAddress,
- uint64_t inAtomAddress, uint64_t imageBaseAddress,
- FindAddressForAtom findSectionAddress);
-
- void applyFixupRelocatable(const Reference &ref, uint8_t *location,
- uint64_t fixupAddress,
- uint64_t targetAddress,
- uint64_t inAtomAddress);
-};
-
-const Registry::KindStrings ArchHandler_x86_64::_sKindStrings[] = {
- LLD_KIND_STRING_ENTRY(invalid),
- LLD_KIND_STRING_ENTRY(modeCode),
- LLD_KIND_STRING_ENTRY(modeData),
- LLD_KIND_STRING_ENTRY(branch32),
- LLD_KIND_STRING_ENTRY(ripRel32),
- LLD_KIND_STRING_ENTRY(ripRel32Minus1),
- LLD_KIND_STRING_ENTRY(ripRel32Minus2),
- LLD_KIND_STRING_ENTRY(ripRel32Minus4),
- LLD_KIND_STRING_ENTRY(ripRel32Anon),
- LLD_KIND_STRING_ENTRY(ripRel32Minus1Anon),
- LLD_KIND_STRING_ENTRY(ripRel32Minus2Anon),
- LLD_KIND_STRING_ENTRY(ripRel32Minus4Anon),
- LLD_KIND_STRING_ENTRY(ripRel32GotLoad),
- LLD_KIND_STRING_ENTRY(ripRel32GotLoadNowLea),
- LLD_KIND_STRING_ENTRY(ripRel32Got),
- LLD_KIND_STRING_ENTRY(ripRel32Tlv),
- LLD_KIND_STRING_ENTRY(lazyPointer),
- LLD_KIND_STRING_ENTRY(lazyImmediateLocation),
- LLD_KIND_STRING_ENTRY(pointer64),
- LLD_KIND_STRING_ENTRY(pointer64Anon),
- LLD_KIND_STRING_ENTRY(delta32),
- LLD_KIND_STRING_ENTRY(delta64),
- LLD_KIND_STRING_ENTRY(delta32Anon),
- LLD_KIND_STRING_ENTRY(delta64Anon),
- LLD_KIND_STRING_ENTRY(negDelta64),
- LLD_KIND_STRING_ENTRY(negDelta32),
- LLD_KIND_STRING_ENTRY(imageOffset),
- LLD_KIND_STRING_ENTRY(imageOffsetGot),
- LLD_KIND_STRING_ENTRY(unwindFDEToFunction),
- LLD_KIND_STRING_ENTRY(unwindInfoToEhFrame),
- LLD_KIND_STRING_ENTRY(tlvInitSectionOffset),
- LLD_KIND_STRING_END
-};
-
-const ArchHandler::StubInfo ArchHandler_x86_64::_sStubInfo = {
- "dyld_stub_binder",
-
- // Lazy pointer references
- { Reference::KindArch::x86_64, pointer64, 0, 0 },
- { Reference::KindArch::x86_64, lazyPointer, 0, 0 },
-
- // GOT pointer to dyld_stub_binder
- { Reference::KindArch::x86_64, pointer64, 0, 0 },
-
- // x86_64 code alignment 2^1
- 1,
-
- // Stub size and code
- 6,
- { 0xff, 0x25, 0x00, 0x00, 0x00, 0x00 }, // jmp *lazyPointer
- { Reference::KindArch::x86_64, ripRel32, 2, 0 },
- { false, 0, 0, 0 },
-
- // Stub Helper size and code
- 10,
- { 0x68, 0x00, 0x00, 0x00, 0x00, // pushq $lazy-info-offset
- 0xE9, 0x00, 0x00, 0x00, 0x00 }, // jmp helperhelper
- { Reference::KindArch::x86_64, lazyImmediateLocation, 1, 0 },
- { Reference::KindArch::x86_64, branch32, 6, 0 },
-
- // Stub helper image cache content type
- DefinedAtom::typeNonLazyPointer,
-
- // Stub Helper-Common size and code
- 16,
- // Stub helper alignment
- 2,
- { 0x4C, 0x8D, 0x1D, 0x00, 0x00, 0x00, 0x00, // leaq cache(%rip),%r11
- 0x41, 0x53, // push %r11
- 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *binder(%rip)
- 0x90 }, // nop
- { Reference::KindArch::x86_64, ripRel32, 3, 0 },
- { false, 0, 0, 0 },
- { Reference::KindArch::x86_64, ripRel32, 11, 0 },
- { false, 0, 0, 0 }
-
-};
-
-bool ArchHandler_x86_64::isCallSite(const Reference &ref) {
- if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
- return false;
- assert(ref.kindArch() == Reference::KindArch::x86_64);
- return (ref.kindValue() == branch32);
-}
-
-bool ArchHandler_x86_64::isPointer(const Reference &ref) {
- if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
- return false;
- assert(ref.kindArch() == Reference::KindArch::x86_64);
- Reference::KindValue kind = ref.kindValue();
- return (kind == pointer64 || kind == pointer64Anon);
-}
-
-bool ArchHandler_x86_64::isPairedReloc(const Relocation &reloc) {
- return (reloc.type == X86_64_RELOC_SUBTRACTOR);
-}
-
-Reference::KindValue
-ArchHandler_x86_64::kindFromReloc(const Relocation &reloc) {
- switch(relocPattern(reloc)) {
- case X86_64_RELOC_BRANCH | rPcRel | rExtern | rLength4:
- return branch32;
- case X86_64_RELOC_SIGNED | rPcRel | rExtern | rLength4:
- return ripRel32;
- case X86_64_RELOC_SIGNED | rPcRel | rLength4:
- return ripRel32Anon;
- case X86_64_RELOC_SIGNED_1 | rPcRel | rExtern | rLength4:
- return ripRel32Minus1;
- case X86_64_RELOC_SIGNED_1 | rPcRel | rLength4:
- return ripRel32Minus1Anon;
- case X86_64_RELOC_SIGNED_2 | rPcRel | rExtern | rLength4:
- return ripRel32Minus2;
- case X86_64_RELOC_SIGNED_2 | rPcRel | rLength4:
- return ripRel32Minus2Anon;
- case X86_64_RELOC_SIGNED_4 | rPcRel | rExtern | rLength4:
- return ripRel32Minus4;
- case X86_64_RELOC_SIGNED_4 | rPcRel | rLength4:
- return ripRel32Minus4Anon;
- case X86_64_RELOC_GOT_LOAD | rPcRel | rExtern | rLength4:
- return ripRel32GotLoad;
- case X86_64_RELOC_GOT | rPcRel | rExtern | rLength4:
- return ripRel32Got;
- case X86_64_RELOC_TLV | rPcRel | rExtern | rLength4:
- return ripRel32Tlv;
- case X86_64_RELOC_UNSIGNED | rExtern | rLength8:
- return pointer64;
- case X86_64_RELOC_UNSIGNED | rLength8:
- return pointer64Anon;
- default:
- return invalid;
- }
-}
-
-llvm::Error
-ArchHandler_x86_64::getReferenceInfo(const Relocation &reloc,
- const DefinedAtom *inAtom,
- uint32_t offsetInAtom,
- uint64_t fixupAddress, bool swap,
- FindAtomBySectionAndAddress atomFromAddress,
- FindAtomBySymbolIndex atomFromSymbolIndex,
- Reference::KindValue *kind,
- const lld::Atom **target,
- Reference::Addend *addend) {
- *kind = kindFromReloc(reloc);
- if (*kind == invalid)
- return llvm::make_error<GenericError>("unknown type");
- const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
- uint64_t targetAddress;
- switch (*kind) {
- case branch32:
- case ripRel32:
- if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
- return ec;
- *addend = *(const little32_t *)fixupContent;
- return llvm::Error::success();
- case ripRel32Minus1:
- if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
- return ec;
- *addend = (int32_t)*(const little32_t *)fixupContent + 1;
- return llvm::Error::success();
- case ripRel32Minus2:
- if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
- return ec;
- *addend = (int32_t)*(const little32_t *)fixupContent + 2;
- return llvm::Error::success();
- case ripRel32Minus4:
- if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
- return ec;
- *addend = (int32_t)*(const little32_t *)fixupContent + 4;
- return llvm::Error::success();
- case ripRel32Anon:
- targetAddress = fixupAddress + 4 + *(const little32_t *)fixupContent;
- return atomFromAddress(reloc.symbol, targetAddress, target, addend);
- case ripRel32Minus1Anon:
- targetAddress = fixupAddress + 5 + *(const little32_t *)fixupContent;
- return atomFromAddress(reloc.symbol, targetAddress, target, addend);
- case ripRel32Minus2Anon:
- targetAddress = fixupAddress + 6 + *(const little32_t *)fixupContent;
- return atomFromAddress(reloc.symbol, targetAddress, target, addend);
- case ripRel32Minus4Anon:
- targetAddress = fixupAddress + 8 + *(const little32_t *)fixupContent;
- return atomFromAddress(reloc.symbol, targetAddress, target, addend);
- case ripRel32GotLoad:
- case ripRel32Got:
- case ripRel32Tlv:
- if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
- return ec;
- *addend = *(const little32_t *)fixupContent;
- return llvm::Error::success();
- case tlvInitSectionOffset:
- case pointer64:
- if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
- return ec;
- // If this is the 3rd pointer of a tlv-thunk (i.e. the pointer to the TLV's
- // initial value) we need to handle it specially.
- if (inAtom->contentType() == DefinedAtom::typeThunkTLV &&
- offsetInAtom == 16) {
- *kind = tlvInitSectionOffset;
- assert(*addend == 0 && "TLV-init has non-zero addend?");
- } else
- *addend = *(const little64_t *)fixupContent;
- return llvm::Error::success();
- case pointer64Anon:
- targetAddress = *(const little64_t *)fixupContent;
- return atomFromAddress(reloc.symbol, targetAddress, target, addend);
- default:
- llvm_unreachable("bad reloc kind");
- }
-}
-
-llvm::Error
-ArchHandler_x86_64::getPairReferenceInfo(const normalized::Relocation &reloc1,
- const normalized::Relocation &reloc2,
- const DefinedAtom *inAtom,
- uint32_t offsetInAtom,
- uint64_t fixupAddress, bool swap,
- bool scatterable,
- FindAtomBySectionAndAddress atomFromAddress,
- FindAtomBySymbolIndex atomFromSymbolIndex,
- Reference::KindValue *kind,
- const lld::Atom **target,
- Reference::Addend *addend) {
- const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
- uint64_t targetAddress;
- const lld::Atom *fromTarget;
- if (auto ec = atomFromSymbolIndex(reloc1.symbol, &fromTarget))
- return ec;
-
- switch(relocPattern(reloc1) << 16 | relocPattern(reloc2)) {
- case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength8) << 16 |
- X86_64_RELOC_UNSIGNED | rExtern | rLength8): {
- if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
- return ec;
- uint64_t encodedAddend = (int64_t)*(const little64_t *)fixupContent;
- if (inAtom == fromTarget) {
- if (inAtom->contentType() == DefinedAtom::typeCFI)
- *kind = unwindFDEToFunction;
- else
- *kind = delta64;
- *addend = encodedAddend + offsetInAtom;
- } else if (inAtom == *target) {
- *kind = negDelta64;
- *addend = encodedAddend - offsetInAtom;
- *target = fromTarget;
- } else
- return llvm::make_error<GenericError>("Invalid pointer diff");
- return llvm::Error::success();
- }
- case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 |
- X86_64_RELOC_UNSIGNED | rExtern | rLength4): {
- if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
- return ec;
- uint32_t encodedAddend = (int32_t)*(const little32_t *)fixupContent;
- if (inAtom == fromTarget) {
- *kind = delta32;
- *addend = encodedAddend + offsetInAtom;
- } else if (inAtom == *target) {
- *kind = negDelta32;
- *addend = encodedAddend - offsetInAtom;
- *target = fromTarget;
- } else
- return llvm::make_error<GenericError>("Invalid pointer diff");
- return llvm::Error::success();
- }
- case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength8) << 16 |
- X86_64_RELOC_UNSIGNED | rLength8):
- if (fromTarget != inAtom)
- return llvm::make_error<GenericError>("pointer diff not in base atom");
- *kind = delta64Anon;
- targetAddress = offsetInAtom + (int64_t)*(const little64_t *)fixupContent;
- return atomFromAddress(reloc2.symbol, targetAddress, target, addend);
- case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 |
- X86_64_RELOC_UNSIGNED | rLength4):
- if (fromTarget != inAtom)
- return llvm::make_error<GenericError>("pointer diff not in base atom");
- *kind = delta32Anon;
- targetAddress = offsetInAtom + (int32_t)*(const little32_t *)fixupContent;
- return atomFromAddress(reloc2.symbol, targetAddress, target, addend);
- default:
- return llvm::make_error<GenericError>("unknown pair");
- }
-}
-
-void ArchHandler_x86_64::generateAtomContent(
- const DefinedAtom &atom, bool relocatable, FindAddressForAtom findAddress,
- FindAddressForAtom findSectionAddress, uint64_t imageBaseAddress,
- llvm::MutableArrayRef<uint8_t> atomContentBuffer) {
- // Copy raw bytes.
- std::copy(atom.rawContent().begin(), atom.rawContent().end(),
- atomContentBuffer.begin());
- // Apply fix-ups.
- for (const Reference *ref : atom) {
- uint32_t offset = ref->offsetInAtom();
- const Atom *target = ref->target();
- uint64_t targetAddress = 0;
- if (isa<DefinedAtom>(target))
- targetAddress = findAddress(*target);
- uint64_t atomAddress = findAddress(atom);
- uint64_t fixupAddress = atomAddress + offset;
- if (relocatable) {
- applyFixupRelocatable(*ref, &atomContentBuffer[offset],
- fixupAddress, targetAddress,
- atomAddress);
- } else {
- applyFixupFinal(*ref, &atomContentBuffer[offset],
- fixupAddress, targetAddress,
- atomAddress, imageBaseAddress, findSectionAddress);
- }
- }
-}
-
-void ArchHandler_x86_64::applyFixupFinal(
- const Reference &ref, uint8_t *loc, uint64_t fixupAddress,
- uint64_t targetAddress, uint64_t inAtomAddress, uint64_t imageBaseAddress,
- FindAddressForAtom findSectionAddress) {
- if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
- return;
- assert(ref.kindArch() == Reference::KindArch::x86_64);
- ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc);
- ulittle64_t *loc64 = reinterpret_cast<ulittle64_t *>(loc);
- switch (static_cast<X86_64Kind>(ref.kindValue())) {
- case branch32:
- case ripRel32:
- case ripRel32Anon:
- case ripRel32Got:
- case ripRel32GotLoad:
- case ripRel32Tlv:
- *loc32 = targetAddress - (fixupAddress + 4) + ref.addend();
- return;
- case pointer64:
- case pointer64Anon:
- *loc64 = targetAddress + ref.addend();
- return;
- case tlvInitSectionOffset:
- *loc64 = targetAddress - findSectionAddress(*ref.target()) + ref.addend();
- return;
- case ripRel32Minus1:
- case ripRel32Minus1Anon:
- *loc32 = targetAddress - (fixupAddress + 5) + ref.addend();
- return;
- case ripRel32Minus2:
- case ripRel32Minus2Anon:
- *loc32 = targetAddress - (fixupAddress + 6) + ref.addend();
- return;
- case ripRel32Minus4:
- case ripRel32Minus4Anon:
- *loc32 = targetAddress - (fixupAddress + 8) + ref.addend();
- return;
- case delta32:
- case delta32Anon:
- *loc32 = targetAddress - fixupAddress + ref.addend();
- return;
- case delta64:
- case delta64Anon:
- case unwindFDEToFunction:
- *loc64 = targetAddress - fixupAddress + ref.addend();
- return;
- case ripRel32GotLoadNowLea:
- // Change MOVQ to LEA
- assert(loc[-2] == 0x8B);
- loc[-2] = 0x8D;
- *loc32 = targetAddress - (fixupAddress + 4) + ref.addend();
- return;
- case negDelta64:
- *loc64 = fixupAddress - targetAddress + ref.addend();
- return;
- case negDelta32:
- *loc32 = fixupAddress - targetAddress + ref.addend();
- return;
- case modeCode:
- case modeData:
- case lazyPointer:
- // Do nothing
- return;
- case lazyImmediateLocation:
- *loc32 = ref.addend();
- return;
- case imageOffset:
- case imageOffsetGot:
- *loc32 = (targetAddress - imageBaseAddress) + ref.addend();
- return;
- case unwindInfoToEhFrame: {
- uint64_t val = targetAddress - findSectionAddress(*ref.target()) + ref.addend();
- assert(val < 0xffffffU && "offset in __eh_frame too large");
- *loc32 = (*loc32 & 0xff000000U) | val;
- return;
- }
- case invalid:
- // Fall into llvm_unreachable().
- break;
- }
- llvm_unreachable("invalid x86_64 Reference Kind");
-}
-
-void ArchHandler_x86_64::applyFixupRelocatable(const Reference &ref,
- uint8_t *loc,
- uint64_t fixupAddress,
- uint64_t targetAddress,
- uint64_t inAtomAddress) {
- if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
- return;
- assert(ref.kindArch() == Reference::KindArch::x86_64);
- ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc);
- ulittle64_t *loc64 = reinterpret_cast<ulittle64_t *>(loc);
- switch (static_cast<X86_64Kind>(ref.kindValue())) {
- case branch32:
- case ripRel32:
- case ripRel32Got:
- case ripRel32GotLoad:
- case ripRel32Tlv:
- *loc32 = ref.addend();
- return;
- case ripRel32Anon:
- *loc32 = (targetAddress - (fixupAddress + 4)) + ref.addend();
- return;
- case tlvInitSectionOffset:
- case pointer64:
- *loc64 = ref.addend();
- return;
- case pointer64Anon:
- *loc64 = targetAddress + ref.addend();
- return;
- case ripRel32Minus1:
- *loc32 = ref.addend() - 1;
- return;
- case ripRel32Minus1Anon:
- *loc32 = (targetAddress - (fixupAddress + 5)) + ref.addend();
- return;
- case ripRel32Minus2:
- *loc32 = ref.addend() - 2;
- return;
- case ripRel32Minus2Anon:
- *loc32 = (targetAddress - (fixupAddress + 6)) + ref.addend();
- return;
- case ripRel32Minus4:
- *loc32 = ref.addend() - 4;
- return;
- case ripRel32Minus4Anon:
- *loc32 = (targetAddress - (fixupAddress + 8)) + ref.addend();
- return;
- case delta32:
- *loc32 = ref.addend() + inAtomAddress - fixupAddress;
- return;
- case delta32Anon:
- // The value we write here should be the delta to the target
- // after taking in to account the difference from the fixup back to the
- // last defined label
- // ie, if we have:
- // _base: ...
- // Lfixup: .quad Ltarget - .
- // ...
- // Ltarget:
- //
- // Then we want to encode the value (Ltarget + addend) - (LFixup - _base)
- *loc32 = (targetAddress + ref.addend()) - (fixupAddress - inAtomAddress);
- return;
- case delta64:
- *loc64 = ref.addend() + inAtomAddress - fixupAddress;
- return;
- case delta64Anon:
- // The value we write here should be the delta to the target
- // after taking in to account the difference from the fixup back to the
- // last defined label
- // ie, if we have:
- // _base: ...
- // Lfixup: .quad Ltarget - .
- // ...
- // Ltarget:
- //
- // Then we want to encode the value (Ltarget + addend) - (LFixup - _base)
- *loc64 = (targetAddress + ref.addend()) - (fixupAddress - inAtomAddress);
- return;
- case negDelta64:
- *loc64 = ref.addend() + fixupAddress - inAtomAddress;
- return;
- case negDelta32:
- *loc32 = ref.addend() + fixupAddress - inAtomAddress;
- return;
- case ripRel32GotLoadNowLea:
- llvm_unreachable("ripRel32GotLoadNowLea implies GOT pass was run");
- return;
- case lazyPointer:
- case lazyImmediateLocation:
- llvm_unreachable("lazy reference kind implies Stubs pass was run");
- return;
- case imageOffset:
- case imageOffsetGot:
- case unwindInfoToEhFrame:
- llvm_unreachable("fixup implies __unwind_info");
- return;
- case modeCode:
- case modeData:
- case unwindFDEToFunction:
- // Do nothing for now
- return;
- case invalid:
- // Fall into llvm_unreachable().
- break;
- }
- llvm_unreachable("unknown x86_64 Reference Kind");
-}
-
-void ArchHandler_x86_64::appendSectionRelocations(
- const DefinedAtom &atom,
- uint64_t atomSectionOffset,
- const Reference &ref,
- FindSymbolIndexForAtom symbolIndexForAtom,
- FindSectionIndexForAtom sectionIndexForAtom,
- FindAddressForAtom addressForAtom,
- normalized::Relocations &relocs) {
- if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
- return;
- assert(ref.kindArch() == Reference::KindArch::x86_64);
- uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom();
- switch (static_cast<X86_64Kind>(ref.kindValue())) {
- case modeCode:
- case modeData:
- return;
- case branch32:
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- X86_64_RELOC_BRANCH | rPcRel | rExtern | rLength4);
- return;
- case ripRel32:
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- X86_64_RELOC_SIGNED | rPcRel | rExtern | rLength4 );
- return;
- case ripRel32Anon:
- appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
- X86_64_RELOC_SIGNED | rPcRel | rLength4 );
- return;
- case ripRel32Got:
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- X86_64_RELOC_GOT | rPcRel | rExtern | rLength4 );
- return;
- case ripRel32GotLoad:
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- X86_64_RELOC_GOT_LOAD | rPcRel | rExtern | rLength4 );
- return;
- case ripRel32Tlv:
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- X86_64_RELOC_TLV | rPcRel | rExtern | rLength4 );
- return;
- case tlvInitSectionOffset:
- case pointer64:
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- X86_64_RELOC_UNSIGNED | rExtern | rLength8);
- return;
- case pointer64Anon:
- appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
- X86_64_RELOC_UNSIGNED | rLength8);
- return;
- case ripRel32Minus1:
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- X86_64_RELOC_SIGNED_1 | rPcRel | rExtern | rLength4 );
- return;
- case ripRel32Minus1Anon:
- appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
- X86_64_RELOC_SIGNED_1 | rPcRel | rLength4 );
- return;
- case ripRel32Minus2:
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- X86_64_RELOC_SIGNED_2 | rPcRel | rExtern | rLength4 );
- return;
- case ripRel32Minus2Anon:
- appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
- X86_64_RELOC_SIGNED_2 | rPcRel | rLength4 );
- return;
- case ripRel32Minus4:
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- X86_64_RELOC_SIGNED_4 | rPcRel | rExtern | rLength4 );
- return;
- case ripRel32Minus4Anon:
- appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
- X86_64_RELOC_SIGNED_4 | rPcRel | rLength4 );
- return;
- case delta32:
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
- X86_64_RELOC_SUBTRACTOR | rExtern | rLength4 );
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- X86_64_RELOC_UNSIGNED | rExtern | rLength4 );
- return;
- case delta32Anon:
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
- X86_64_RELOC_SUBTRACTOR | rExtern | rLength4 );
- appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
- X86_64_RELOC_UNSIGNED | rLength4 );
- return;
- case delta64:
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
- X86_64_RELOC_SUBTRACTOR | rExtern | rLength8 );
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- X86_64_RELOC_UNSIGNED | rExtern | rLength8 );
- return;
- case delta64Anon:
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
- X86_64_RELOC_SUBTRACTOR | rExtern | rLength8 );
- appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
- X86_64_RELOC_UNSIGNED | rLength8 );
- return;
- case unwindFDEToFunction:
- case unwindInfoToEhFrame:
- return;
- case negDelta32:
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- X86_64_RELOC_SUBTRACTOR | rExtern | rLength4 );
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
- X86_64_RELOC_UNSIGNED | rExtern | rLength4 );
- return;
- case negDelta64:
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
- X86_64_RELOC_SUBTRACTOR | rExtern | rLength8 );
- appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
- X86_64_RELOC_UNSIGNED | rExtern | rLength8 );
- return;
- case ripRel32GotLoadNowLea:
- llvm_unreachable("ripRel32GotLoadNowLea implies GOT pass was run");
- return;
- case lazyPointer:
- case lazyImmediateLocation:
- llvm_unreachable("lazy reference kind implies Stubs pass was run");
- return;
- case imageOffset:
- case imageOffsetGot:
- llvm_unreachable("__unwind_info references should have been resolved");
- return;
- case invalid:
- // Fall into llvm_unreachable().
- break;
- }
- llvm_unreachable("unknown x86_64 Reference Kind");
-}
-
-std::unique_ptr<mach_o::ArchHandler> ArchHandler::create_x86_64() {
- return std::unique_ptr<mach_o::ArchHandler>(new ArchHandler_x86_64());
-}
-
-} // namespace mach_o
-} // namespace lld
+++ /dev/null
-//===- lib/ReaderWriter/MachO/Atoms.h ---------------------------*- C++ -*-===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_READER_WRITER_MACHO_ATOMS_H
-#define LLD_READER_WRITER_MACHO_ATOMS_H
-
-#include "lld/Core/Atom.h"
-#include "lld/Core/DefinedAtom.h"
-#include "lld/Core/SharedLibraryAtom.h"
-#include "lld/Core/Simple.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/StringRef.h"
-#include <cstdint>
-#include <string>
-
-namespace lld {
-
-class File;
-
-namespace mach_o {
-
-class MachODefinedAtom : public SimpleDefinedAtom {
-public:
- MachODefinedAtom(const File &f, const StringRef name, Scope scope,
- ContentType type, Merge merge, bool thumb, bool noDeadStrip,
- const ArrayRef<uint8_t> content, Alignment align)
- : SimpleDefinedAtom(f), _name(name), _content(content),
- _align(align), _contentType(type), _scope(scope), _merge(merge),
- _thumb(thumb), _noDeadStrip(noDeadStrip) {}
-
- // Constructor for zero-fill content
- MachODefinedAtom(const File &f, const StringRef name, Scope scope,
- ContentType type, uint64_t size, bool noDeadStrip,
- Alignment align)
- : SimpleDefinedAtom(f), _name(name),
- _content(ArrayRef<uint8_t>(nullptr, size)), _align(align),
- _contentType(type), _scope(scope), _merge(mergeNo), _thumb(false),
- _noDeadStrip(noDeadStrip) {}
-
- ~MachODefinedAtom() override = default;
-
- uint64_t size() const override { return _content.size(); }
-
- ContentType contentType() const override { return _contentType; }
-
- Alignment alignment() const override { return _align; }
-
- StringRef name() const override { return _name; }
-
- Scope scope() const override { return _scope; }
-
- Merge merge() const override { return _merge; }
-
- DeadStripKind deadStrip() const override {
- if (_contentType == DefinedAtom::typeInitializerPtr)
- return deadStripNever;
- if (_contentType == DefinedAtom::typeTerminatorPtr)
- return deadStripNever;
- if (_noDeadStrip)
- return deadStripNever;
- return deadStripNormal;
- }
-
- ArrayRef<uint8_t> rawContent() const override {
- // Note: Zerofill atoms have a content pointer which is null.
- return _content;
- }
-
- bool isThumb() const { return _thumb; }
-
-private:
- const StringRef _name;
- const ArrayRef<uint8_t> _content;
- const DefinedAtom::Alignment _align;
- const ContentType _contentType;
- const Scope _scope;
- const Merge _merge;
- const bool _thumb;
- const bool _noDeadStrip;
-};
-
-class MachODefinedCustomSectionAtom : public MachODefinedAtom {
-public:
- MachODefinedCustomSectionAtom(const File &f, const StringRef name,
- Scope scope, ContentType type, Merge merge,
- bool thumb, bool noDeadStrip,
- const ArrayRef<uint8_t> content,
- StringRef sectionName, Alignment align)
- : MachODefinedAtom(f, name, scope, type, merge, thumb, noDeadStrip,
- content, align),
- _sectionName(sectionName) {}
-
- ~MachODefinedCustomSectionAtom() override = default;
-
- SectionChoice sectionChoice() const override {
- return DefinedAtom::sectionCustomRequired;
- }
-
- StringRef customSectionName() const override {
- return _sectionName;
- }
-private:
- StringRef _sectionName;
-};
-
-class MachOTentativeDefAtom : public SimpleDefinedAtom {
-public:
- MachOTentativeDefAtom(const File &f, const StringRef name, Scope scope,
- uint64_t size, DefinedAtom::Alignment align)
- : SimpleDefinedAtom(f), _name(std::string(name)), _scope(scope),
- _size(size), _align(align) {}
-
- ~MachOTentativeDefAtom() override = default;
-
- uint64_t size() const override { return _size; }
-
- Merge merge() const override { return DefinedAtom::mergeAsTentative; }
-
- ContentType contentType() const override { return DefinedAtom::typeZeroFill; }
-
- Alignment alignment() const override { return _align; }
-
- StringRef name() const override { return _name; }
-
- Scope scope() const override { return _scope; }
-
- ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(); }
-
-private:
- const std::string _name;
- const Scope _scope;
- const uint64_t _size;
- const DefinedAtom::Alignment _align;
-};
-
-class MachOSharedLibraryAtom : public SharedLibraryAtom {
-public:
- MachOSharedLibraryAtom(const File &file, StringRef name,
- StringRef dylibInstallName, bool weakDef)
- : SharedLibraryAtom(), _file(file), _name(name),
- _dylibInstallName(dylibInstallName) {}
- ~MachOSharedLibraryAtom() override = default;
-
- StringRef loadName() const override { return _dylibInstallName; }
-
- bool canBeNullAtRuntime() const override {
- // FIXME: this may actually be changeable. For now, all symbols are strongly
- // defined though.
- return false;
- }
-
- const File &file() const override { return _file; }
-
- StringRef name() const override { return _name; }
-
- Type type() const override {
- // Unused in MachO (I think).
- return Type::Unknown;
- }
-
- uint64_t size() const override {
- // Unused in MachO (I think)
- return 0;
- }
-
-private:
- const File &_file;
- StringRef _name;
- StringRef _dylibInstallName;
-};
-
-} // end namespace mach_o
-} // end namespace lld
-
-#endif // LLD_READER_WRITER_MACHO_ATOMS_H
+++ /dev/null
-add_lld_library(lldMachO
- ArchHandler.cpp
- ArchHandler_arm.cpp
- ArchHandler_arm64.cpp
- ArchHandler_x86.cpp
- ArchHandler_x86_64.cpp
- CompactUnwindPass.cpp
- GOTPass.cpp
- LayoutPass.cpp
- MachOLinkingContext.cpp
- MachONormalizedFileBinaryReader.cpp
- MachONormalizedFileBinaryWriter.cpp
- MachONormalizedFileFromAtoms.cpp
- MachONormalizedFileToAtoms.cpp
- MachONormalizedFileYAML.cpp
- ObjCPass.cpp
- ShimPass.cpp
- StubsPass.cpp
- TLVPass.cpp
- WriterMachO.cpp
-
- LINK_COMPONENTS
- DebugInfoDWARF
- Demangle
- Object
- Support
- TextAPI
-
- LINK_LIBS
- lldCommon
- lldCore
- lldYAML
- ${LLVM_PTHREAD_LIB}
- )
-
-include_directories(.)
+++ /dev/null
-//===- lib/ReaderWriter/MachO/CompactUnwindPass.cpp -------------*- C++ -*-===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file A pass to convert MachO's __compact_unwind sections into the final
-/// __unwind_info format used during runtime. See
-/// mach-o/compact_unwind_encoding.h for more details on the formats involved.
-///
-//===----------------------------------------------------------------------===//
-
-#include "ArchHandler.h"
-#include "File.h"
-#include "MachONormalizedFileBinaryUtils.h"
-#include "MachOPasses.h"
-#include "lld/Common/LLVM.h"
-#include "lld/Core/DefinedAtom.h"
-#include "lld/Core/File.h"
-#include "lld/Core/Reference.h"
-#include "lld/Core/Simple.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/Support/Debug.h"
-#include "llvm/Support/Format.h"
-
-#define DEBUG_TYPE "macho-compact-unwind"
-
-namespace lld {
-namespace mach_o {
-
-namespace {
-struct CompactUnwindEntry {
- const Atom *rangeStart;
- const Atom *personalityFunction;
- const Atom *lsdaLocation;
- const Atom *ehFrame;
-
- uint32_t rangeLength;
-
- // There are 3 types of compact unwind entry, distinguished by the encoding
- // value: 0 indicates a function with no unwind info;
- // _archHandler.dwarfCompactUnwindType() indicates that the entry defers to
- // __eh_frame, and that the ehFrame entry will be valid; any other value is a
- // real compact unwind entry -- personalityFunction will be set and
- // lsdaLocation may be.
- uint32_t encoding;
-
- CompactUnwindEntry(const DefinedAtom *function)
- : rangeStart(function), personalityFunction(nullptr),
- lsdaLocation(nullptr), ehFrame(nullptr), rangeLength(function->size()),
- encoding(0) {}
-
- CompactUnwindEntry()
- : rangeStart(nullptr), personalityFunction(nullptr),
- lsdaLocation(nullptr), ehFrame(nullptr), rangeLength(0), encoding(0) {}
-};
-
-struct UnwindInfoPage {
- ArrayRef<CompactUnwindEntry> entries;
-};
-}
-
-class UnwindInfoAtom : public SimpleDefinedAtom {
-public:
- UnwindInfoAtom(ArchHandler &archHandler, const File &file, bool isBig,
- std::vector<const Atom *> &personalities,
- std::vector<uint32_t> &commonEncodings,
- std::vector<UnwindInfoPage> &pages, uint32_t numLSDAs)
- : SimpleDefinedAtom(file), _archHandler(archHandler),
- _commonEncodingsOffset(7 * sizeof(uint32_t)),
- _personalityArrayOffset(_commonEncodingsOffset +
- commonEncodings.size() * sizeof(uint32_t)),
- _topLevelIndexOffset(_personalityArrayOffset +
- personalities.size() * sizeof(uint32_t)),
- _lsdaIndexOffset(_topLevelIndexOffset +
- 3 * (pages.size() + 1) * sizeof(uint32_t)),
- _firstPageOffset(_lsdaIndexOffset + 2 * numLSDAs * sizeof(uint32_t)),
- _isBig(isBig) {
-
- addHeader(commonEncodings.size(), personalities.size(), pages.size());
- addCommonEncodings(commonEncodings);
- addPersonalityFunctions(personalities);
- addTopLevelIndexes(pages);
- addLSDAIndexes(pages, numLSDAs);
- addSecondLevelPages(pages);
- }
-
- ~UnwindInfoAtom() override = default;
-
- ContentType contentType() const override {
- return DefinedAtom::typeProcessedUnwindInfo;
- }
-
- Alignment alignment() const override { return 4; }
-
- uint64_t size() const override { return _contents.size(); }
-
- ContentPermissions permissions() const override {
- return DefinedAtom::permR__;
- }
-
- ArrayRef<uint8_t> rawContent() const override { return _contents; }
-
- void addHeader(uint32_t numCommon, uint32_t numPersonalities,
- uint32_t numPages) {
- using normalized::write32;
-
- uint32_t headerSize = 7 * sizeof(uint32_t);
- _contents.resize(headerSize);
-
- uint8_t *headerEntries = _contents.data();
- // version
- write32(headerEntries, 1, _isBig);
- // commonEncodingsArraySectionOffset
- write32(headerEntries + sizeof(uint32_t), _commonEncodingsOffset, _isBig);
- // commonEncodingsArrayCount
- write32(headerEntries + 2 * sizeof(uint32_t), numCommon, _isBig);
- // personalityArraySectionOffset
- write32(headerEntries + 3 * sizeof(uint32_t), _personalityArrayOffset,
- _isBig);
- // personalityArrayCount
- write32(headerEntries + 4 * sizeof(uint32_t), numPersonalities, _isBig);
- // indexSectionOffset
- write32(headerEntries + 5 * sizeof(uint32_t), _topLevelIndexOffset, _isBig);
- // indexCount
- write32(headerEntries + 6 * sizeof(uint32_t), numPages + 1, _isBig);
- }
-
- /// Add the list of common encodings to the section; this is simply an array
- /// of uint32_t compact values. Size has already been specified in the header.
- void addCommonEncodings(std::vector<uint32_t> &commonEncodings) {
- using normalized::write32;
-
- _contents.resize(_commonEncodingsOffset +
- commonEncodings.size() * sizeof(uint32_t));
- uint8_t *commonEncodingsArea =
- reinterpret_cast<uint8_t *>(_contents.data() + _commonEncodingsOffset);
-
- for (uint32_t encoding : commonEncodings) {
- write32(commonEncodingsArea, encoding, _isBig);
- commonEncodingsArea += sizeof(uint32_t);
- }
- }
-
- void addPersonalityFunctions(std::vector<const Atom *> personalities) {
- _contents.resize(_personalityArrayOffset +
- personalities.size() * sizeof(uint32_t));
-
- for (unsigned i = 0; i < personalities.size(); ++i)
- addImageReferenceIndirect(_personalityArrayOffset + i * sizeof(uint32_t),
- personalities[i]);
- }
-
- void addTopLevelIndexes(std::vector<UnwindInfoPage> &pages) {
- using normalized::write32;
-
- uint32_t numIndexes = pages.size() + 1;
- _contents.resize(_topLevelIndexOffset + numIndexes * 3 * sizeof(uint32_t));
-
- uint32_t pageLoc = _firstPageOffset;
-
- // The most difficult job here is calculating the LSDAs; everything else
- // follows fairly naturally, but we can't state where the first
- uint8_t *indexData = &_contents[_topLevelIndexOffset];
- uint32_t numLSDAs = 0;
- for (unsigned i = 0; i < pages.size(); ++i) {
- // functionOffset
- addImageReference(_topLevelIndexOffset + 3 * i * sizeof(uint32_t),
- pages[i].entries[0].rangeStart);
- // secondLevelPagesSectionOffset
- write32(indexData + (3 * i + 1) * sizeof(uint32_t), pageLoc, _isBig);
- write32(indexData + (3 * i + 2) * sizeof(uint32_t),
- _lsdaIndexOffset + numLSDAs * 2 * sizeof(uint32_t), _isBig);
-
- for (auto &entry : pages[i].entries)
- if (entry.lsdaLocation)
- ++numLSDAs;
- }
-
- // Finally, write out the final sentinel index
- auto &finalEntry = pages[pages.size() - 1].entries.back();
- addImageReference(_topLevelIndexOffset +
- 3 * pages.size() * sizeof(uint32_t),
- finalEntry.rangeStart, finalEntry.rangeLength);
- // secondLevelPagesSectionOffset => 0
- write32(indexData + (3 * pages.size() + 2) * sizeof(uint32_t),
- _lsdaIndexOffset + numLSDAs * 2 * sizeof(uint32_t), _isBig);
- }
-
- void addLSDAIndexes(std::vector<UnwindInfoPage> &pages, uint32_t numLSDAs) {
- _contents.resize(_lsdaIndexOffset + numLSDAs * 2 * sizeof(uint32_t));
-
- uint32_t curOffset = _lsdaIndexOffset;
- for (auto &page : pages) {
- for (auto &entry : page.entries) {
- if (!entry.lsdaLocation)
- continue;
-
- addImageReference(curOffset, entry.rangeStart);
- addImageReference(curOffset + sizeof(uint32_t), entry.lsdaLocation);
- curOffset += 2 * sizeof(uint32_t);
- }
- }
- }
-
- void addSecondLevelPages(std::vector<UnwindInfoPage> &pages) {
- for (auto &page : pages) {
- addRegularSecondLevelPage(page);
- }
- }
-
- void addRegularSecondLevelPage(const UnwindInfoPage &page) {
- uint32_t curPageOffset = _contents.size();
- const int16_t headerSize = sizeof(uint32_t) + 2 * sizeof(uint16_t);
- uint32_t curPageSize =
- headerSize + 2 * page.entries.size() * sizeof(uint32_t);
- _contents.resize(curPageOffset + curPageSize);
-
- using normalized::write32;
- using normalized::write16;
- // 2 => regular page
- write32(&_contents[curPageOffset], 2, _isBig);
- // offset of 1st entry
- write16(&_contents[curPageOffset + 4], headerSize, _isBig);
- write16(&_contents[curPageOffset + 6], page.entries.size(), _isBig);
-
- uint32_t pagePos = curPageOffset + headerSize;
- for (auto &entry : page.entries) {
- addImageReference(pagePos, entry.rangeStart);
-
- write32(_contents.data() + pagePos + sizeof(uint32_t), entry.encoding,
- _isBig);
- if ((entry.encoding & 0x0f000000U) ==
- _archHandler.dwarfCompactUnwindType())
- addEhFrameReference(pagePos + sizeof(uint32_t), entry.ehFrame);
-
- pagePos += 2 * sizeof(uint32_t);
- }
- }
-
- void addEhFrameReference(uint32_t offset, const Atom *dest,
- Reference::Addend addend = 0) {
- addReference(Reference::KindNamespace::mach_o, _archHandler.kindArch(),
- _archHandler.unwindRefToEhFrameKind(), offset, dest, addend);
- }
-
- void addImageReference(uint32_t offset, const Atom *dest,
- Reference::Addend addend = 0) {
- addReference(Reference::KindNamespace::mach_o, _archHandler.kindArch(),
- _archHandler.imageOffsetKind(), offset, dest, addend);
- }
-
- void addImageReferenceIndirect(uint32_t offset, const Atom *dest) {
- addReference(Reference::KindNamespace::mach_o, _archHandler.kindArch(),
- _archHandler.imageOffsetKindIndirect(), offset, dest, 0);
- }
-
-private:
- mach_o::ArchHandler &_archHandler;
- std::vector<uint8_t> _contents;
- uint32_t _commonEncodingsOffset;
- uint32_t _personalityArrayOffset;
- uint32_t _topLevelIndexOffset;
- uint32_t _lsdaIndexOffset;
- uint32_t _firstPageOffset;
- bool _isBig;
-};
-
-/// Pass for instantiating and optimizing GOT slots.
-///
-class CompactUnwindPass : public Pass {
-public:
- CompactUnwindPass(const MachOLinkingContext &context)
- : _ctx(context), _archHandler(_ctx.archHandler()),
- _file(*_ctx.make_file<MachOFile>("<mach-o Compact Unwind Pass>")),
- _isBig(MachOLinkingContext::isBigEndian(_ctx.arch())) {
- _file.setOrdinal(_ctx.getNextOrdinalAndIncrement());
- }
-
-private:
- llvm::Error perform(SimpleFile &mergedFile) override {
- LLVM_DEBUG(llvm::dbgs() << "MachO Compact Unwind pass\n");
-
- std::map<const Atom *, CompactUnwindEntry> unwindLocs;
- std::map<const Atom *, const Atom *> dwarfFrames;
- std::vector<const Atom *> personalities;
- uint32_t numLSDAs = 0;
-
- // First collect all __compact_unwind and __eh_frame entries, addressable by
- // the function referred to.
- collectCompactUnwindEntries(mergedFile, unwindLocs, personalities,
- numLSDAs);
-
- collectDwarfFrameEntries(mergedFile, dwarfFrames);
-
- // Skip rest of pass if no unwind info.
- if (unwindLocs.empty() && dwarfFrames.empty())
- return llvm::Error::success();
-
- // FIXME: if there are more than 4 personality functions then we need to
- // defer to DWARF info for the ones we don't put in the list. They should
- // also probably be sorted by frequency.
- assert(personalities.size() <= 4);
-
- // TODO: Find common encodings for use by compressed pages.
- std::vector<uint32_t> commonEncodings;
-
- // Now sort the entries by final address and fixup the compact encoding to
- // its final form (i.e. set personality function bits & create DWARF
- // references where needed).
- std::vector<CompactUnwindEntry> unwindInfos = createUnwindInfoEntries(
- mergedFile, unwindLocs, personalities, dwarfFrames);
-
- // Remove any unused eh-frame atoms.
- pruneUnusedEHFrames(mergedFile, unwindInfos, unwindLocs, dwarfFrames);
-
- // Finally, we can start creating pages based on these entries.
-
- LLVM_DEBUG(llvm::dbgs() << " Splitting entries into pages\n");
- // FIXME: we split the entries into pages naively: lots of 4k pages followed
- // by a small one. ld64 tried to minimize space and align them to real 4k
- // boundaries. That might be worth doing, or perhaps we could perform some
- // minor balancing for expected number of lookups.
- std::vector<UnwindInfoPage> pages;
- auto remainingInfos = llvm::makeArrayRef(unwindInfos);
- do {
- pages.push_back(UnwindInfoPage());
-
- // FIXME: we only create regular pages at the moment. These can hold up to
- // 1021 entries according to the documentation.
- unsigned entriesInPage = std::min(1021U, (unsigned)remainingInfos.size());
-
- pages.back().entries = remainingInfos.slice(0, entriesInPage);
- remainingInfos = remainingInfos.slice(entriesInPage);
-
- LLVM_DEBUG(llvm::dbgs()
- << " Page from "
- << pages.back().entries[0].rangeStart->name() << " to "
- << pages.back().entries.back().rangeStart->name() << " + "
- << llvm::format("0x%x",
- pages.back().entries.back().rangeLength)
- << " has " << entriesInPage << " entries\n");
- } while (!remainingInfos.empty());
-
- auto *unwind = new (_file.allocator())
- UnwindInfoAtom(_archHandler, _file, _isBig, personalities,
- commonEncodings, pages, numLSDAs);
- mergedFile.addAtom(*unwind);
-
- // Finally, remove all __compact_unwind atoms now that we've processed them.
- mergedFile.removeDefinedAtomsIf([](const DefinedAtom *atom) {
- return atom->contentType() == DefinedAtom::typeCompactUnwindInfo;
- });
-
- return llvm::Error::success();
- }
-
- void collectCompactUnwindEntries(
- const SimpleFile &mergedFile,
- std::map<const Atom *, CompactUnwindEntry> &unwindLocs,
- std::vector<const Atom *> &personalities, uint32_t &numLSDAs) {
- LLVM_DEBUG(llvm::dbgs() << " Collecting __compact_unwind entries\n");
-
- for (const DefinedAtom *atom : mergedFile.defined()) {
- if (atom->contentType() != DefinedAtom::typeCompactUnwindInfo)
- continue;
-
- auto unwindEntry = extractCompactUnwindEntry(atom);
- unwindLocs.insert(std::make_pair(unwindEntry.rangeStart, unwindEntry));
-
- LLVM_DEBUG(llvm::dbgs() << " Entry for "
- << unwindEntry.rangeStart->name() << ", encoding="
- << llvm::format("0x%08x", unwindEntry.encoding));
- if (unwindEntry.personalityFunction)
- LLVM_DEBUG(llvm::dbgs()
- << ", personality="
- << unwindEntry.personalityFunction->name()
- << ", lsdaLoc=" << unwindEntry.lsdaLocation->name());
- LLVM_DEBUG(llvm::dbgs() << '\n');
-
- // Count number of LSDAs we see, since we need to know how big the index
- // will be while laying out the section.
- if (unwindEntry.lsdaLocation)
- ++numLSDAs;
-
- // Gather the personality functions now, so that they're in deterministic
- // order (derived from the DefinedAtom order).
- if (unwindEntry.personalityFunction &&
- !llvm::count(personalities, unwindEntry.personalityFunction))
- personalities.push_back(unwindEntry.personalityFunction);
- }
- }
-
- CompactUnwindEntry extractCompactUnwindEntry(const DefinedAtom *atom) {
- CompactUnwindEntry entry;
-
- for (const Reference *ref : *atom) {
- switch (ref->offsetInAtom()) {
- case 0:
- // FIXME: there could legitimately be functions with multiple encoding
- // entries. However, nothing produces them at the moment.
- assert(ref->addend() == 0 && "unexpected offset into function");
- entry.rangeStart = ref->target();
- break;
- case 0x10:
- assert(ref->addend() == 0 && "unexpected offset into personality fn");
- entry.personalityFunction = ref->target();
- break;
- case 0x18:
- assert(ref->addend() == 0 && "unexpected offset into LSDA atom");
- entry.lsdaLocation = ref->target();
- break;
- }
- }
-
- if (atom->rawContent().size() < 4 * sizeof(uint32_t))
- return entry;
-
- using normalized::read32;
- entry.rangeLength =
- read32(atom->rawContent().data() + 2 * sizeof(uint32_t), _isBig);
- entry.encoding =
- read32(atom->rawContent().data() + 3 * sizeof(uint32_t), _isBig);
- return entry;
- }
-
- void
- collectDwarfFrameEntries(const SimpleFile &mergedFile,
- std::map<const Atom *, const Atom *> &dwarfFrames) {
- for (const DefinedAtom *ehFrameAtom : mergedFile.defined()) {
- if (ehFrameAtom->contentType() != DefinedAtom::typeCFI)
- continue;
- if (ArchHandler::isDwarfCIE(_isBig, ehFrameAtom))
- continue;
-
- if (const Atom *function = _archHandler.fdeTargetFunction(ehFrameAtom))
- dwarfFrames[function] = ehFrameAtom;
- }
- }
-
- /// Every atom defined in __TEXT,__text needs an entry in the final
- /// __unwind_info section (in order). These comes from two sources:
- /// + Input __compact_unwind sections where possible (after adding the
- /// personality function offset which is only known now).
- /// + A synthesised reference to __eh_frame if there's no __compact_unwind
- /// or too many personality functions to be accommodated.
- std::vector<CompactUnwindEntry> createUnwindInfoEntries(
- const SimpleFile &mergedFile,
- const std::map<const Atom *, CompactUnwindEntry> &unwindLocs,
- const std::vector<const Atom *> &personalities,
- const std::map<const Atom *, const Atom *> &dwarfFrames) {
- std::vector<CompactUnwindEntry> unwindInfos;
-
- LLVM_DEBUG(llvm::dbgs() << " Creating __unwind_info entries\n");
- // The final order in the __unwind_info section must be derived from the
- // order of typeCode atoms, since that's how they'll be put into the object
- // file eventually (yuck!).
- for (const DefinedAtom *atom : mergedFile.defined()) {
- if (atom->contentType() != DefinedAtom::typeCode)
- continue;
-
- unwindInfos.push_back(finalizeUnwindInfoEntryForAtom(
- atom, unwindLocs, personalities, dwarfFrames));
-
- LLVM_DEBUG(llvm::dbgs()
- << " Entry for " << atom->name() << ", final encoding="
- << llvm::format("0x%08x", unwindInfos.back().encoding)
- << '\n');
- }
-
- return unwindInfos;
- }
-
- /// Remove unused EH frames.
- ///
- /// An EH frame is considered unused if there is a corresponding compact
- /// unwind atom that doesn't require the EH frame.
- void pruneUnusedEHFrames(
- SimpleFile &mergedFile,
- const std::vector<CompactUnwindEntry> &unwindInfos,
- const std::map<const Atom *, CompactUnwindEntry> &unwindLocs,
- const std::map<const Atom *, const Atom *> &dwarfFrames) {
-
- // Worklist of all 'used' FDEs.
- std::vector<const DefinedAtom *> usedDwarfWorklist;
-
- // We have to check two conditions when building the worklist:
- // (1) EH frames used by compact unwind entries.
- for (auto &entry : unwindInfos)
- if (entry.ehFrame)
- usedDwarfWorklist.push_back(cast<DefinedAtom>(entry.ehFrame));
-
- // (2) EH frames that reference functions with no corresponding compact
- // unwind info.
- for (auto &entry : dwarfFrames)
- if (!unwindLocs.count(entry.first))
- usedDwarfWorklist.push_back(cast<DefinedAtom>(entry.second));
-
- // Add all transitively referenced CFI atoms by processing the worklist.
- std::set<const Atom *> usedDwarfFrames;
- while (!usedDwarfWorklist.empty()) {
- const DefinedAtom *cfiAtom = usedDwarfWorklist.back();
- usedDwarfWorklist.pop_back();
- usedDwarfFrames.insert(cfiAtom);
- for (const auto *ref : *cfiAtom) {
- const DefinedAtom *cfiTarget = dyn_cast<DefinedAtom>(ref->target());
- if (cfiTarget->contentType() == DefinedAtom::typeCFI)
- usedDwarfWorklist.push_back(cfiTarget);
- }
- }
-
- // Finally, delete all unreferenced CFI atoms.
- mergedFile.removeDefinedAtomsIf([&](const DefinedAtom *atom) {
- if ((atom->contentType() == DefinedAtom::typeCFI) &&
- !usedDwarfFrames.count(atom))
- return true;
- return false;
- });
- }
-
- CompactUnwindEntry finalizeUnwindInfoEntryForAtom(
- const DefinedAtom *function,
- const std::map<const Atom *, CompactUnwindEntry> &unwindLocs,
- const std::vector<const Atom *> &personalities,
- const std::map<const Atom *, const Atom *> &dwarfFrames) {
- auto unwindLoc = unwindLocs.find(function);
-
- CompactUnwindEntry entry;
- if (unwindLoc == unwindLocs.end()) {
- // Default entry has correct encoding (0 => no unwind), but we need to
- // synthesise the function.
- entry.rangeStart = function;
- entry.rangeLength = function->size();
- } else
- entry = unwindLoc->second;
-
-
- // If there's no __compact_unwind entry, or it explicitly says to use
- // __eh_frame, we need to try and fill in the correct DWARF atom.
- if (entry.encoding == _archHandler.dwarfCompactUnwindType() ||
- entry.encoding == 0) {
- auto dwarfFrame = dwarfFrames.find(function);
- if (dwarfFrame != dwarfFrames.end()) {
- entry.encoding = _archHandler.dwarfCompactUnwindType();
- entry.ehFrame = dwarfFrame->second;
- }
- }
-
- auto personality = llvm::find(personalities, entry.personalityFunction);
- uint32_t personalityIdx = personality == personalities.end()
- ? 0
- : personality - personalities.begin() + 1;
-
- // FIXME: We should also use DWARF when there isn't enough room for the
- // personality function in the compact encoding.
- assert(personalityIdx < 4 && "too many personality functions");
-
- entry.encoding |= personalityIdx << 28;
-
- if (entry.lsdaLocation)
- entry.encoding |= 1U << 30;
-
- return entry;
- }
-
- const MachOLinkingContext &_ctx;
- mach_o::ArchHandler &_archHandler;
- MachOFile &_file;
- bool _isBig;
-};
-
-void addCompactUnwindPass(PassManager &pm, const MachOLinkingContext &ctx) {
- assert(ctx.needsCompactUnwindPass());
- pm.add(std::make_unique<CompactUnwindPass>(ctx));
-}
-
-} // end namespace mach_o
-} // end namespace lld
+++ /dev/null
-//===- lib/ReaderWriter/MachO/File.h ----------------------------*- C++ -*-===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_READER_WRITER_MACHO_DEBUGINFO_H
-#define LLD_READER_WRITER_MACHO_DEBUGINFO_H
-
-#include "lld/Core/Atom.h"
-#include <vector>
-
-#include "llvm/Support/Allocator.h"
-#include "llvm/Support/Format.h"
-#include "llvm/Support/raw_ostream.h"
-
-
-namespace lld {
-namespace mach_o {
-
-class DebugInfo {
-public:
- enum class Kind {
- Dwarf,
- Stabs
- };
-
- Kind kind() const { return _kind; }
-
- void setAllocator(std::unique_ptr<llvm::BumpPtrAllocator> allocator) {
- _allocator = std::move(allocator);
- }
-
-protected:
- DebugInfo(Kind kind) : _kind(kind) {}
-
-private:
- std::unique_ptr<llvm::BumpPtrAllocator> _allocator;
- Kind _kind;
-};
-
-struct TranslationUnitSource {
- StringRef name;
- StringRef path;
-};
-
-class DwarfDebugInfo : public DebugInfo {
-public:
- DwarfDebugInfo(TranslationUnitSource tu)
- : DebugInfo(Kind::Dwarf), _tu(std::move(tu)) {}
-
- static inline bool classof(const DebugInfo *di) {
- return di->kind() == Kind::Dwarf;
- }
-
- const TranslationUnitSource &translationUnitSource() const { return _tu; }
-
-private:
- TranslationUnitSource _tu;
-};
-
-struct Stab {
- Stab(const Atom* atom, uint8_t type, uint8_t other, uint16_t desc,
- uint32_t value, StringRef str)
- : atom(atom), type(type), other(other), desc(desc), value(value),
- str(str) {}
-
- const class Atom* atom;
- uint8_t type;
- uint8_t other;
- uint16_t desc;
- uint32_t value;
- StringRef str;
-};
-
-inline raw_ostream& operator<<(raw_ostream &os, Stab &s) {
- os << "Stab -- atom: " << llvm::format("%p", s.atom) << ", type: " << (uint32_t)s.type
- << ", other: " << (uint32_t)s.other << ", desc: " << s.desc << ", value: " << s.value
- << ", str: '" << s.str << "'";
- return os;
-}
-
-class StabsDebugInfo : public DebugInfo {
-public:
-
- typedef std::vector<Stab> StabsList;
-
- StabsDebugInfo(StabsList stabs)
- : DebugInfo(Kind::Stabs), _stabs(std::move(stabs)) {}
-
- static inline bool classof(const DebugInfo *di) {
- return di->kind() == Kind::Stabs;
- }
-
- const StabsList& stabs() const { return _stabs; }
-
-public:
- StabsList _stabs;
-};
-
-} // end namespace mach_o
-} // end namespace lld
-
-#endif // LLD_READER_WRITER_MACHO_DEBUGINFO_H
+++ /dev/null
-//===- lib/ReaderWriter/MachO/ExecutableAtoms.h ---------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_READER_WRITER_MACHO_EXECUTABLE_ATOMS_H
-#define LLD_READER_WRITER_MACHO_EXECUTABLE_ATOMS_H
-
-#include "Atoms.h"
-#include "File.h"
-
-#include "llvm/BinaryFormat/MachO.h"
-
-#include "lld/Core/DefinedAtom.h"
-#include "lld/Core/File.h"
-#include "lld/Core/LinkingContext.h"
-#include "lld/Core/Reference.h"
-#include "lld/Core/Simple.h"
-#include "lld/Core/UndefinedAtom.h"
-#include "lld/ReaderWriter/MachOLinkingContext.h"
-
-namespace lld {
-namespace mach_o {
-
-
-//
-// CEntryFile adds an UndefinedAtom for "_main" so that the Resolving
-// phase will fail if "_main" is undefined.
-//
-class CEntryFile : public SimpleFile {
-public:
- CEntryFile(const MachOLinkingContext &context)
- : SimpleFile("C entry", kindCEntryObject),
- _undefMain(*this, context.entrySymbolName()) {
- this->addAtom(_undefMain);
- }
-
-private:
- SimpleUndefinedAtom _undefMain;
-};
-
-
-//
-// StubHelperFile adds an UndefinedAtom for "dyld_stub_binder" so that
-// the Resolveing phase will fail if "dyld_stub_binder" is undefined.
-//
-class StubHelperFile : public SimpleFile {
-public:
- StubHelperFile(const MachOLinkingContext &context)
- : SimpleFile("stub runtime", kindStubHelperObject),
- _undefBinder(*this, context.binderSymbolName()) {
- this->addAtom(_undefBinder);
- }
-
-private:
- SimpleUndefinedAtom _undefBinder;
-};
-
-
-//
-// MachHeaderAliasFile lazily instantiates the magic symbols that mark the start
-// of the mach_header for final linked images.
-//
-class MachHeaderAliasFile : public SimpleFile {
-public:
- MachHeaderAliasFile(const MachOLinkingContext &context)
- : SimpleFile("mach_header symbols", kindHeaderObject) {
- StringRef machHeaderSymbolName;
- DefinedAtom::Scope symbolScope = DefinedAtom::scopeLinkageUnit;
- StringRef dsoHandleName;
- switch (context.outputMachOType()) {
- case llvm::MachO::MH_OBJECT:
- machHeaderSymbolName = "__mh_object_header";
- break;
- case llvm::MachO::MH_EXECUTE:
- machHeaderSymbolName = "__mh_execute_header";
- symbolScope = DefinedAtom::scopeGlobal;
- dsoHandleName = "___dso_handle";
- break;
- case llvm::MachO::MH_FVMLIB:
- llvm_unreachable("no mach_header symbol for file type");
- case llvm::MachO::MH_CORE:
- llvm_unreachable("no mach_header symbol for file type");
- case llvm::MachO::MH_PRELOAD:
- llvm_unreachable("no mach_header symbol for file type");
- case llvm::MachO::MH_DYLIB:
- machHeaderSymbolName = "__mh_dylib_header";
- dsoHandleName = "___dso_handle";
- break;
- case llvm::MachO::MH_DYLINKER:
- machHeaderSymbolName = "__mh_dylinker_header";
- dsoHandleName = "___dso_handle";
- break;
- case llvm::MachO::MH_BUNDLE:
- machHeaderSymbolName = "__mh_bundle_header";
- dsoHandleName = "___dso_handle";
- break;
- case llvm::MachO::MH_DYLIB_STUB:
- llvm_unreachable("no mach_header symbol for file type");
- case llvm::MachO::MH_DSYM:
- llvm_unreachable("no mach_header symbol for file type");
- case llvm::MachO::MH_KEXT_BUNDLE:
- dsoHandleName = "___dso_handle";
- break;
- }
- if (!machHeaderSymbolName.empty())
- _definedAtoms.push_back(new (allocator()) MachODefinedAtom(
- *this, machHeaderSymbolName, symbolScope,
- DefinedAtom::typeMachHeader, DefinedAtom::mergeNo, false,
- true /* noDeadStrip */,
- ArrayRef<uint8_t>(), DefinedAtom::Alignment(4096)));
-
- if (!dsoHandleName.empty())
- _definedAtoms.push_back(new (allocator()) MachODefinedAtom(
- *this, dsoHandleName, DefinedAtom::scopeLinkageUnit,
- DefinedAtom::typeDSOHandle, DefinedAtom::mergeNo, false,
- true /* noDeadStrip */,
- ArrayRef<uint8_t>(), DefinedAtom::Alignment(1)));
- }
-
- const AtomRange<DefinedAtom> defined() const override {
- return _definedAtoms;
- }
- const AtomRange<UndefinedAtom> undefined() const override {
- return _noUndefinedAtoms;
- }
-
- const AtomRange<SharedLibraryAtom> sharedLibrary() const override {
- return _noSharedLibraryAtoms;
- }
-
- const AtomRange<AbsoluteAtom> absolute() const override {
- return _noAbsoluteAtoms;
- }
-
- void clearAtoms() override {
- _definedAtoms.clear();
- _noUndefinedAtoms.clear();
- _noSharedLibraryAtoms.clear();
- _noAbsoluteAtoms.clear();
- }
-
-
-private:
- mutable AtomVector<DefinedAtom> _definedAtoms;
-};
-
-} // namespace mach_o
-} // namespace lld
-
-#endif // LLD_READER_WRITER_MACHO_EXECUTABLE_ATOMS_H
+++ /dev/null
-//===- lib/ReaderWriter/MachO/File.h ----------------------------*- C++ -*-===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_READER_WRITER_MACHO_FILE_H
-#define LLD_READER_WRITER_MACHO_FILE_H
-
-#include "Atoms.h"
-#include "DebugInfo.h"
-#include "MachONormalizedFile.h"
-#include "lld/Core/SharedLibraryFile.h"
-#include "lld/Core/Simple.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/StringMap.h"
-#include "llvm/Support/Format.h"
-#include "llvm/TextAPI/InterfaceFile.h"
-#include "llvm/TextAPI/TextAPIReader.h"
-#include <unordered_map>
-
-namespace lld {
-namespace mach_o {
-
-using lld::mach_o::normalized::Section;
-
-class MachOFile : public SimpleFile {
-public:
-
- /// Real file constructor - for on-disk files.
- MachOFile(std::unique_ptr<MemoryBuffer> mb, MachOLinkingContext *ctx)
- : SimpleFile(mb->getBufferIdentifier(), File::kindMachObject),
- _mb(std::move(mb)), _ctx(ctx) {}
-
- /// Dummy file constructor - for virtual files.
- MachOFile(StringRef path)
- : SimpleFile(path, File::kindMachObject) {}
-
- void addDefinedAtom(StringRef name, Atom::Scope scope,
- DefinedAtom::ContentType type, DefinedAtom::Merge merge,
- uint64_t sectionOffset, uint64_t contentSize, bool thumb,
- bool noDeadStrip, bool copyRefs,
- const Section *inSection) {
- assert(sectionOffset+contentSize <= inSection->content.size());
- ArrayRef<uint8_t> content = inSection->content.slice(sectionOffset,
- contentSize);
- if (copyRefs) {
- // Make a copy of the atom's name and content that is owned by this file.
- name = name.copy(allocator());
- content = content.copy(allocator());
- }
- DefinedAtom::Alignment align(
- inSection->alignment,
- sectionOffset % inSection->alignment);
- auto *atom =
- new (allocator()) MachODefinedAtom(*this, name, scope, type, merge,
- thumb, noDeadStrip, content, align);
- addAtomForSection(inSection, atom, sectionOffset);
- }
-
- void addDefinedAtomInCustomSection(StringRef name, Atom::Scope scope,
- DefinedAtom::ContentType type, DefinedAtom::Merge merge,
- bool thumb, bool noDeadStrip, uint64_t sectionOffset,
- uint64_t contentSize, StringRef sectionName,
- bool copyRefs, const Section *inSection) {
- assert(sectionOffset+contentSize <= inSection->content.size());
- ArrayRef<uint8_t> content = inSection->content.slice(sectionOffset,
- contentSize);
- if (copyRefs) {
- // Make a copy of the atom's name and content that is owned by this file.
- name = name.copy(allocator());
- content = content.copy(allocator());
- sectionName = sectionName.copy(allocator());
- }
- DefinedAtom::Alignment align(
- inSection->alignment,
- sectionOffset % inSection->alignment);
- auto *atom =
- new (allocator()) MachODefinedCustomSectionAtom(*this, name, scope, type,
- merge, thumb,
- noDeadStrip, content,
- sectionName, align);
- addAtomForSection(inSection, atom, sectionOffset);
- }
-
- void addZeroFillDefinedAtom(StringRef name, Atom::Scope scope,
- uint64_t sectionOffset, uint64_t size,
- bool noDeadStrip, bool copyRefs,
- const Section *inSection) {
- if (copyRefs) {
- // Make a copy of the atom's name and content that is owned by this file.
- name = name.copy(allocator());
- }
- DefinedAtom::Alignment align(
- inSection->alignment,
- sectionOffset % inSection->alignment);
-
- DefinedAtom::ContentType type = DefinedAtom::typeUnknown;
- switch (inSection->type) {
- case llvm::MachO::S_ZEROFILL:
- type = DefinedAtom::typeZeroFill;
- break;
- case llvm::MachO::S_THREAD_LOCAL_ZEROFILL:
- type = DefinedAtom::typeTLVInitialZeroFill;
- break;
- default:
- llvm_unreachable("Unrecognized zero-fill section");
- }
-
- auto *atom =
- new (allocator()) MachODefinedAtom(*this, name, scope, type, size,
- noDeadStrip, align);
- addAtomForSection(inSection, atom, sectionOffset);
- }
-
- void addUndefinedAtom(StringRef name, bool copyRefs) {
- if (copyRefs) {
- // Make a copy of the atom's name that is owned by this file.
- name = name.copy(allocator());
- }
- auto *atom = new (allocator()) SimpleUndefinedAtom(*this, name);
- addAtom(*atom);
- _undefAtoms[name] = atom;
- }
-
- void addTentativeDefAtom(StringRef name, Atom::Scope scope, uint64_t size,
- DefinedAtom::Alignment align, bool copyRefs) {
- if (copyRefs) {
- // Make a copy of the atom's name that is owned by this file.
- name = name.copy(allocator());
- }
- auto *atom =
- new (allocator()) MachOTentativeDefAtom(*this, name, scope, size, align);
- addAtom(*atom);
- _undefAtoms[name] = atom;
- }
-
- /// Search this file for the atom from 'section' that covers
- /// 'offsetInSect'. Returns nullptr is no atom found.
- MachODefinedAtom *findAtomCoveringAddress(const Section §ion,
- uint64_t offsetInSect,
- uint32_t *foundOffsetAtom=nullptr) {
- const auto &pos = _sectionAtoms.find(§ion);
- if (pos == _sectionAtoms.end())
- return nullptr;
- const auto &vec = pos->second;
- assert(offsetInSect < section.content.size());
- // Vector of atoms for section are already sorted, so do binary search.
- const auto &atomPos = std::lower_bound(vec.begin(), vec.end(), offsetInSect,
- [offsetInSect](const SectionOffsetAndAtom &ao,
- uint64_t targetAddr) -> bool {
- // Each atom has a start offset of its slice of the
- // section's content. This compare function must return true
- // iff the atom's range is before the offset being searched for.
- uint64_t atomsEndOffset = ao.offset+ao.atom->rawContent().size();
- return (atomsEndOffset <= offsetInSect);
- });
- if (atomPos == vec.end())
- return nullptr;
- if (foundOffsetAtom)
- *foundOffsetAtom = offsetInSect - atomPos->offset;
- return atomPos->atom;
- }
-
- /// Searches this file for an UndefinedAtom named 'name'. Returns
- /// nullptr is no such atom found.
- const lld::Atom *findUndefAtom(StringRef name) {
- auto pos = _undefAtoms.find(name);
- if (pos == _undefAtoms.end())
- return nullptr;
- return pos->second;
- }
-
- typedef std::function<void (MachODefinedAtom* atom)> DefinedAtomVisitor;
-
- void eachDefinedAtom(DefinedAtomVisitor vistor) {
- for (auto §AndAtoms : _sectionAtoms) {
- for (auto &offAndAtom : sectAndAtoms.second) {
- vistor(offAndAtom.atom);
- }
- }
- }
-
- typedef std::function<void(MachODefinedAtom *atom, uint64_t offset)>
- SectionAtomVisitor;
-
- void eachAtomInSection(const Section §ion, SectionAtomVisitor visitor) {
- auto pos = _sectionAtoms.find(§ion);
- if (pos == _sectionAtoms.end())
- return;
- auto vec = pos->second;
-
- for (auto &offAndAtom : vec)
- visitor(offAndAtom.atom, offAndAtom.offset);
- }
-
- MachOLinkingContext::Arch arch() const { return _arch; }
- void setArch(MachOLinkingContext::Arch arch) { _arch = arch; }
-
- MachOLinkingContext::OS OS() const { return _os; }
- void setOS(MachOLinkingContext::OS os) { _os = os; }
-
- MachOLinkingContext::ObjCConstraint objcConstraint() const {
- return _objcConstraint;
- }
- void setObjcConstraint(MachOLinkingContext::ObjCConstraint v) {
- _objcConstraint = v;
- }
-
- uint32_t minVersion() const { return _minVersion; }
- void setMinVersion(uint32_t v) { _minVersion = v; }
-
- LoadCommandType minVersionLoadCommandKind() const {
- return _minVersionLoadCommandKind;
- }
- void setMinVersionLoadCommandKind(LoadCommandType v) {
- _minVersionLoadCommandKind = v;
- }
-
- uint32_t swiftVersion() const { return _swiftVersion; }
- void setSwiftVersion(uint32_t v) { _swiftVersion = v; }
-
- bool subsectionsViaSymbols() const {
- return _flags & llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS;
- }
- void setFlags(normalized::FileFlags v) { _flags = v; }
-
- /// Methods for support type inquiry through isa, cast, and dyn_cast:
- static inline bool classof(const File *F) {
- return F->kind() == File::kindMachObject;
- }
-
- void setDebugInfo(std::unique_ptr<DebugInfo> debugInfo) {
- _debugInfo = std::move(debugInfo);
- }
-
- DebugInfo* debugInfo() const { return _debugInfo.get(); }
- std::unique_ptr<DebugInfo> takeDebugInfo() { return std::move(_debugInfo); }
-
-protected:
- std::error_code doParse() override {
- // Convert binary file to normalized mach-o.
- auto normFile = normalized::readBinary(_mb, _ctx->arch());
- if (auto ec = normFile.takeError())
- return llvm::errorToErrorCode(std::move(ec));
- // Convert normalized mach-o to atoms.
- if (auto ec = normalized::normalizedObjectToAtoms(this, **normFile, false))
- return llvm::errorToErrorCode(std::move(ec));
- return std::error_code();
- }
-
-private:
- struct SectionOffsetAndAtom { uint64_t offset; MachODefinedAtom *atom; };
-
- void addAtomForSection(const Section *inSection, MachODefinedAtom* atom,
- uint64_t sectionOffset) {
- SectionOffsetAndAtom offAndAtom;
- offAndAtom.offset = sectionOffset;
- offAndAtom.atom = atom;
- _sectionAtoms[inSection].push_back(offAndAtom);
- addAtom(*atom);
- }
-
- typedef llvm::DenseMap<const normalized::Section *,
- std::vector<SectionOffsetAndAtom>> SectionToAtoms;
- typedef llvm::StringMap<const lld::Atom *> NameToAtom;
-
- std::unique_ptr<MemoryBuffer> _mb;
- MachOLinkingContext *_ctx;
- SectionToAtoms _sectionAtoms;
- NameToAtom _undefAtoms;
- MachOLinkingContext::Arch _arch = MachOLinkingContext::arch_unknown;
- MachOLinkingContext::OS _os = MachOLinkingContext::OS::unknown;
- uint32_t _minVersion = 0;
- LoadCommandType _minVersionLoadCommandKind = (LoadCommandType)0;
- MachOLinkingContext::ObjCConstraint _objcConstraint =
- MachOLinkingContext::objc_unknown;
- uint32_t _swiftVersion = 0;
- normalized::FileFlags _flags = llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS;
- std::unique_ptr<DebugInfo> _debugInfo;
-};
-
-class MachODylibFile : public SharedLibraryFile {
-public:
- MachODylibFile(std::unique_ptr<MemoryBuffer> mb, MachOLinkingContext *ctx)
- : SharedLibraryFile(mb->getBufferIdentifier()),
- _mb(std::move(mb)), _ctx(ctx) {}
-
- MachODylibFile(StringRef path) : SharedLibraryFile(path) {}
-
- OwningAtomPtr<SharedLibraryAtom> exports(StringRef name) const override {
- // Pass down _installName so that if this requested symbol
- // is re-exported through this dylib, the SharedLibraryAtom's loadName()
- // is this dylib installName and not the implementation dylib's.
- // NOTE: isData is not needed for dylibs (it matters for static libs).
- return exports(name, _installName);
- }
-
- /// Adds symbol name that this dylib exports. The corresponding
- /// SharedLibraryAtom is created lazily (since most symbols are not used).
- void addExportedSymbol(StringRef name, bool weakDef, bool copyRefs) {
- if (copyRefs) {
- name = name.copy(allocator());
- }
- AtomAndFlags info(weakDef);
- _nameToAtom[name] = info;
- }
-
- void addReExportedDylib(StringRef dylibPath) {
- _reExportedDylibs.emplace_back(dylibPath);
- }
-
- StringRef installName() const { return _installName; }
- uint32_t currentVersion() { return _currentVersion; }
- uint32_t compatVersion() { return _compatVersion; }
-
- void setInstallName(StringRef name) { _installName = name; }
- void setCompatVersion(uint32_t version) { _compatVersion = version; }
- void setCurrentVersion(uint32_t version) { _currentVersion = version; }
-
- typedef std::function<MachODylibFile *(StringRef)> FindDylib;
-
- void loadReExportedDylibs(FindDylib find) {
- for (ReExportedDylib &entry : _reExportedDylibs) {
- if (!entry.file)
- entry.file = find(entry.path);
- }
- }
-
- StringRef getDSOName() const override { return _installName; }
-
- std::error_code doParse() override {
- // Convert binary file to normalized mach-o.
- auto normFile = normalized::readBinary(_mb, _ctx->arch());
- if (auto ec = normFile.takeError())
- return llvm::errorToErrorCode(std::move(ec));
- // Convert normalized mach-o to atoms.
- if (auto ec = normalized::normalizedDylibToAtoms(this, **normFile, false))
- return llvm::errorToErrorCode(std::move(ec));
- return std::error_code();
- }
-
-protected:
- OwningAtomPtr<SharedLibraryAtom> exports(StringRef name,
- StringRef installName) const {
- // First, check if requested symbol is directly implemented by this dylib.
- auto entry = _nameToAtom.find(name);
- if (entry != _nameToAtom.end()) {
- // FIXME: Make this map a set and only used in assert builds.
- // Note, its safe to assert here as the resolver is the only client of
- // this API and it only requests exports for undefined symbols.
- // If we return from here we are no longer undefined so we should never
- // get here again.
- assert(!entry->second.atom && "Duplicate shared library export");
- bool weakDef = entry->second.weakDef;
- auto *atom = new (allocator()) MachOSharedLibraryAtom(*this, name,
- installName,
- weakDef);
- entry->second.atom = atom;
- return atom;
- }
-
- // Next, check if symbol is implemented in some re-exported dylib.
- for (const ReExportedDylib &dylib : _reExportedDylibs) {
- assert(dylib.file);
- auto atom = dylib.file->exports(name, installName);
- if (atom.get())
- return atom;
- }
-
- // Symbol not exported or re-exported by this dylib.
- return nullptr;
- }
-
- struct ReExportedDylib {
- ReExportedDylib(StringRef p) : path(p), file(nullptr) { }
- ReExportedDylib(StringRef p, MachODylibFile *file) : path(p), file(file) { }
- StringRef path;
- MachODylibFile *file;
- };
-
- struct AtomAndFlags {
- AtomAndFlags() : atom(nullptr), weakDef(false) { }
- AtomAndFlags(bool weak) : atom(nullptr), weakDef(weak) { }
- const SharedLibraryAtom *atom;
- bool weakDef;
- };
-
- std::unique_ptr<MemoryBuffer> _mb;
- MachOLinkingContext *_ctx;
- StringRef _installName;
- uint32_t _currentVersion;
- uint32_t _compatVersion;
- std::vector<ReExportedDylib> _reExportedDylibs;
- mutable std::unordered_map<StringRef, AtomAndFlags> _nameToAtom;
-};
-
-class TAPIFile : public MachODylibFile {
-public:
-
- TAPIFile(std::unique_ptr<MemoryBuffer> mb, MachOLinkingContext *ctx)
- : MachODylibFile(std::move(mb), ctx) {}
-
- std::error_code doParse() override {
-
- llvm::Expected<std::unique_ptr<llvm::MachO::InterfaceFile>> result =
- llvm::MachO::TextAPIReader::get(*_mb);
- if (!result)
- return std::make_error_code(std::errc::invalid_argument);
-
- std::unique_ptr<llvm::MachO::InterfaceFile> interface{std::move(*result)};
- return loadFromInterface(*interface);
- }
-
-private:
- std::error_code loadFromInterface(llvm::MachO::InterfaceFile &interface) {
- llvm::MachO::Architecture arch;
- switch(_ctx->arch()) {
- case MachOLinkingContext::arch_x86:
- arch = llvm::MachO::AK_i386;
- break;
- case MachOLinkingContext::arch_x86_64:
- arch = llvm::MachO::AK_x86_64;
- break;
- case MachOLinkingContext::arch_arm64:
- arch = llvm::MachO::AK_arm64;
- break;
- default:
- return std::make_error_code(std::errc::invalid_argument);
- }
-
- setInstallName(interface.getInstallName().copy(allocator()));
- // TODO(compnerd) filter out symbols based on the target platform
- for (const auto symbol : interface.symbols())
- if (symbol->getArchitectures().has(arch))
- addExportedSymbol(symbol->getName(), symbol->isWeakDefined(), true);
-
- for (const llvm::MachO::InterfaceFileRef &reexport :
- interface.reexportedLibraries())
- addReExportedDylib(reexport.getInstallName().copy(allocator()));
-
- for (const auto& document : interface.documents()) {
- for (auto& reexport : _reExportedDylibs) {
- if (reexport.path != document->getInstallName())
- continue;
- assert(!reexport.file);
- _ownedFiles.push_back(std::make_unique<TAPIFile>(
- MemoryBuffer::getMemBuffer("", _mb->getBufferIdentifier()), _ctx));
- reexport.file = _ownedFiles.back().get();
- std::error_code err = _ownedFiles.back()->loadFromInterface(*document);
- if (err)
- return err;
- }
- }
-
- return std::error_code();
- }
-
- std::vector<std::unique_ptr<TAPIFile>> _ownedFiles;
-};
-
-} // end namespace mach_o
-} // end namespace lld
-
-#endif // LLD_READER_WRITER_MACHO_FILE_H
+++ /dev/null
-//===- lib/ReaderWriter/MachO/FlatNamespaceFile.h -------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_READER_WRITER_MACHO_FLAT_NAMESPACE_FILE_H
-#define LLD_READER_WRITER_MACHO_FLAT_NAMESPACE_FILE_H
-
-#include "Atoms.h"
-#include "lld/Core/SharedLibraryFile.h"
-#include "lld/ReaderWriter/MachOLinkingContext.h"
-#include "llvm/Support/Debug.h"
-
-namespace lld {
-namespace mach_o {
-
-//
-// A FlateNamespaceFile instance may be added as a resolution source of last
-// resort, depending on how -flat_namespace and -undefined are set.
-//
-class FlatNamespaceFile : public SharedLibraryFile {
-public:
- FlatNamespaceFile(const MachOLinkingContext &context)
- : SharedLibraryFile("flat namespace") { }
-
- OwningAtomPtr<SharedLibraryAtom> exports(StringRef name) const override {
- return new (allocator()) MachOSharedLibraryAtom(*this, name, getDSOName(),
- false);
- }
-
- StringRef getDSOName() const override { return "flat-namespace"; }
-
- const AtomRange<DefinedAtom> defined() const override {
- return _noDefinedAtoms;
- }
- const AtomRange<UndefinedAtom> undefined() const override {
- return _noUndefinedAtoms;
- }
-
- const AtomRange<SharedLibraryAtom> sharedLibrary() const override {
- return _noSharedLibraryAtoms;
- }
-
- const AtomRange<AbsoluteAtom> absolute() const override {
- return _noAbsoluteAtoms;
- }
-
- void clearAtoms() override {
- _noDefinedAtoms.clear();
- _noUndefinedAtoms.clear();
- _noSharedLibraryAtoms.clear();
- _noAbsoluteAtoms.clear();
- }
-};
-
-} // namespace mach_o
-} // namespace lld
-
-#endif // LLD_READER_WRITER_MACHO_FLAT_NAMESPACE_FILE_H
+++ /dev/null
-//===- lib/ReaderWriter/MachO/GOTPass.cpp -----------------------*- C++ -*-===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// This linker pass transforms all GOT kind references to real references.
-/// That is, in assembly you can write something like:
-/// movq foo@GOTPCREL(%rip), %rax
-/// which means you want to load a pointer to "foo" out of the GOT (global
-/// Offsets Table). In the object file, the Atom containing this instruction
-/// has a Reference whose target is an Atom named "foo" and the Reference
-/// kind is a GOT load. The linker needs to instantiate a pointer sized
-/// GOT entry. This is done be creating a GOT Atom to represent that pointer
-/// sized data in this pass, and altering the Atom graph so the Reference now
-/// points to the GOT Atom entry (corresponding to "foo") and changing the
-/// Reference Kind to reflect it is now pointing to a GOT entry (rather
-/// then needing a GOT entry).
-///
-/// There is one optimization the linker can do here. If the target of the GOT
-/// is in the same linkage unit and does not need to be interposable, and
-/// the GOT use is just a load (not some other operation), this pass can
-/// transform that load into an LEA (add). This optimizes away one memory load
-/// which at runtime that could stall the pipeline. This optimization only
-/// works for architectures in which a (GOT) load instruction can be change to
-/// an LEA instruction that is the same size. The method isGOTAccess() should
-/// only return true for "canBypassGOT" if this optimization is supported.
-///
-//===----------------------------------------------------------------------===//
-
-#include "ArchHandler.h"
-#include "File.h"
-#include "MachOPasses.h"
-#include "lld/Common/LLVM.h"
-#include "lld/Core/DefinedAtom.h"
-#include "lld/Core/File.h"
-#include "lld/Core/Reference.h"
-#include "lld/Core/Simple.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/STLExtras.h"
-
-namespace lld {
-namespace mach_o {
-
-//
-// GOT Entry Atom created by the GOT pass.
-//
-class GOTEntryAtom : public SimpleDefinedAtom {
-public:
- GOTEntryAtom(const File &file, bool is64, StringRef name)
- : SimpleDefinedAtom(file), _is64(is64), _name(name) { }
-
- ~GOTEntryAtom() override = default;
-
- ContentType contentType() const override {
- return DefinedAtom::typeGOT;
- }
-
- Alignment alignment() const override {
- return _is64 ? 8 : 4;
- }
-
- uint64_t size() const override {
- return _is64 ? 8 : 4;
- }
-
- ContentPermissions permissions() const override {
- return DefinedAtom::permRW_;
- }
-
- ArrayRef<uint8_t> rawContent() const override {
- static const uint8_t zeros[] =
- { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
- return llvm::makeArrayRef(zeros, size());
- }
-
- StringRef slotName() const {
- return _name;
- }
-
-private:
- const bool _is64;
- StringRef _name;
-};
-
-/// Pass for instantiating and optimizing GOT slots.
-///
-class GOTPass : public Pass {
-public:
- GOTPass(const MachOLinkingContext &context)
- : _ctx(context), _archHandler(_ctx.archHandler()),
- _file(*_ctx.make_file<MachOFile>("<mach-o GOT Pass>")) {
- _file.setOrdinal(_ctx.getNextOrdinalAndIncrement());
- }
-
-private:
- llvm::Error perform(SimpleFile &mergedFile) override {
- // Scan all references in all atoms.
- for (const DefinedAtom *atom : mergedFile.defined()) {
- for (const Reference *ref : *atom) {
- // Look at instructions accessing the GOT.
- bool canBypassGOT;
- if (!_archHandler.isGOTAccess(*ref, canBypassGOT))
- continue;
- const Atom *target = ref->target();
- assert(target != nullptr);
-
- if (!shouldReplaceTargetWithGOTAtom(target, canBypassGOT)) {
- // Update reference kind to reflect that target is a direct access.
- _archHandler.updateReferenceToGOT(ref, false);
- } else {
- // Replace the target with a reference to a GOT entry.
- const DefinedAtom *gotEntry = makeGOTEntry(target);
- const_cast<Reference *>(ref)->setTarget(gotEntry);
- // Update reference kind to reflect that target is now a GOT entry.
- _archHandler.updateReferenceToGOT(ref, true);
- }
- }
- }
-
- // Sort and add all created GOT Atoms to master file
- std::vector<const GOTEntryAtom *> entries;
- entries.reserve(_targetToGOT.size());
- for (auto &it : _targetToGOT)
- entries.push_back(it.second);
- std::sort(entries.begin(), entries.end(),
- [](const GOTEntryAtom *left, const GOTEntryAtom *right) {
- return (left->slotName().compare(right->slotName()) < 0);
- });
- for (const GOTEntryAtom *slot : entries)
- mergedFile.addAtom(*slot);
-
- return llvm::Error::success();
- }
-
- bool shouldReplaceTargetWithGOTAtom(const Atom *target, bool canBypassGOT) {
- // Accesses to shared library symbols must go through GOT.
- if (isa<SharedLibraryAtom>(target))
- return true;
- // Accesses to interposable symbols in same linkage unit must also go
- // through GOT.
- const DefinedAtom *defTarget = dyn_cast<DefinedAtom>(target);
- if (defTarget != nullptr &&
- defTarget->interposable() != DefinedAtom::interposeNo) {
- assert(defTarget->scope() != DefinedAtom::scopeTranslationUnit);
- return true;
- }
- // Target does not require indirection. So, if instruction allows GOT to be
- // by-passed, do that optimization and don't create GOT entry.
- return !canBypassGOT;
- }
-
- const DefinedAtom *makeGOTEntry(const Atom *target) {
- auto pos = _targetToGOT.find(target);
- if (pos == _targetToGOT.end()) {
- auto *gotEntry = new (_file.allocator())
- GOTEntryAtom(_file, _ctx.is64Bit(), target->name());
- _targetToGOT[target] = gotEntry;
- const ArchHandler::ReferenceInfo &nlInfo = _archHandler.stubInfo().
- nonLazyPointerReferenceToBinder;
- gotEntry->addReference(Reference::KindNamespace::mach_o, nlInfo.arch,
- nlInfo.kind, 0, target, 0);
- return gotEntry;
- }
- return pos->second;
- }
-
- const MachOLinkingContext &_ctx;
- mach_o::ArchHandler &_archHandler;
- MachOFile &_file;
- llvm::DenseMap<const Atom*, const GOTEntryAtom*> _targetToGOT;
-};
-
-void addGOTPass(PassManager &pm, const MachOLinkingContext &ctx) {
- assert(ctx.needsGOTPass());
- pm.add(std::make_unique<GOTPass>(ctx));
-}
-
-} // end namespace mach_o
-} // end namespace lld
+++ /dev/null
-//===-- ReaderWriter/MachO/LayoutPass.cpp - Layout atoms ------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "LayoutPass.h"
-#include "lld/Core/Instrumentation.h"
-#include "lld/Core/PassManager.h"
-#include "lld/ReaderWriter/MachOLinkingContext.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/Twine.h"
-#include "llvm/Support/Debug.h"
-#include "llvm/Support/Parallel.h"
-#include <algorithm>
-#include <set>
-#include <utility>
-
-using namespace lld;
-
-#define DEBUG_TYPE "LayoutPass"
-
-namespace lld {
-namespace mach_o {
-
-static bool compareAtoms(const LayoutPass::SortKey &,
- const LayoutPass::SortKey &,
- LayoutPass::SortOverride customSorter);
-
-#ifndef NDEBUG
-// Return "reason (leftval, rightval)"
-static std::string formatReason(StringRef reason, int leftVal, int rightVal) {
- return (Twine(reason) + " (" + Twine(leftVal) + ", " + Twine(rightVal) + ")")
- .str();
-}
-
-// Less-than relationship of two atoms must be transitive, which is, if a < b
-// and b < c, a < c must be true. This function checks the transitivity by
-// checking the sort results.
-static void checkTransitivity(std::vector<LayoutPass::SortKey> &vec,
- LayoutPass::SortOverride customSorter) {
- for (auto i = vec.begin(), e = vec.end(); (i + 1) != e; ++i) {
- for (auto j = i + 1; j != e; ++j) {
- assert(compareAtoms(*i, *j, customSorter));
- assert(!compareAtoms(*j, *i, customSorter));
- }
- }
-}
-
-// Helper functions to check follow-on graph.
-typedef llvm::DenseMap<const DefinedAtom *, const DefinedAtom *> AtomToAtomT;
-
-static std::string atomToDebugString(const Atom *atom) {
- const DefinedAtom *definedAtom = dyn_cast<DefinedAtom>(atom);
- std::string str;
- llvm::raw_string_ostream s(str);
- if (definedAtom->name().empty())
- s << "<anonymous " << definedAtom << ">";
- else
- s << definedAtom->name();
- s << " in ";
- if (definedAtom->customSectionName().empty())
- s << "<anonymous>";
- else
- s << definedAtom->customSectionName();
- s.flush();
- return str;
-}
-
-static void showCycleDetectedError(const Registry ®istry,
- AtomToAtomT &followOnNexts,
- const DefinedAtom *atom) {
- const DefinedAtom *start = atom;
- llvm::dbgs() << "There's a cycle in a follow-on chain!\n";
- do {
- llvm::dbgs() << " " << atomToDebugString(atom) << "\n";
- for (const Reference *ref : *atom) {
- StringRef kindValStr;
- if (!registry.referenceKindToString(ref->kindNamespace(), ref->kindArch(),
- ref->kindValue(), kindValStr)) {
- kindValStr = "<unknown>";
- }
- llvm::dbgs() << " " << kindValStr
- << ": " << atomToDebugString(ref->target()) << "\n";
- }
- atom = followOnNexts[atom];
- } while (atom != start);
- llvm::report_fatal_error("Cycle detected");
-}
-
-/// Exit if there's a cycle in a followon chain reachable from the
-/// given root atom. Uses the tortoise and hare algorithm to detect a
-/// cycle.
-static void checkNoCycleInFollowonChain(const Registry ®istry,
- AtomToAtomT &followOnNexts,
- const DefinedAtom *root) {
- const DefinedAtom *tortoise = root;
- const DefinedAtom *hare = followOnNexts[root];
- while (true) {
- if (!tortoise || !hare)
- return;
- if (tortoise == hare)
- showCycleDetectedError(registry, followOnNexts, tortoise);
- tortoise = followOnNexts[tortoise];
- hare = followOnNexts[followOnNexts[hare]];
- }
-}
-
-static void checkReachabilityFromRoot(AtomToAtomT &followOnRoots,
- const DefinedAtom *atom) {
- if (!atom) return;
- auto i = followOnRoots.find(atom);
- if (i == followOnRoots.end()) {
- llvm_unreachable(((Twine("Atom <") + atomToDebugString(atom) +
- "> has no follow-on root!"))
- .str()
- .c_str());
- }
- const DefinedAtom *ap = i->second;
- while (true) {
- const DefinedAtom *next = followOnRoots[ap];
- if (!next) {
- llvm_unreachable((Twine("Atom <" + atomToDebugString(atom) +
- "> is not reachable from its root!"))
- .str()
- .c_str());
- }
- if (next == ap)
- return;
- ap = next;
- }
-}
-
-static void printDefinedAtoms(const File::AtomRange<DefinedAtom> &atomRange) {
- for (const DefinedAtom *atom : atomRange) {
- llvm::dbgs() << " file=" << atom->file().path()
- << ", name=" << atom->name()
- << ", size=" << atom->size()
- << ", type=" << atom->contentType()
- << ", ordinal=" << atom->ordinal()
- << "\n";
- }
-}
-
-/// Verify that the followon chain is sane. Should not be called in
-/// release binary.
-void LayoutPass::checkFollowonChain(const File::AtomRange<DefinedAtom> &range) {
- ScopedTask task(getDefaultDomain(), "LayoutPass::checkFollowonChain");
-
- // Verify that there's no cycle in follow-on chain.
- std::set<const DefinedAtom *> roots;
- for (const auto &ai : _followOnRoots)
- roots.insert(ai.second);
- for (const DefinedAtom *root : roots)
- checkNoCycleInFollowonChain(_registry, _followOnNexts, root);
-
- // Verify that all the atoms in followOnNexts have references to
- // their roots.
- for (const auto &ai : _followOnNexts) {
- checkReachabilityFromRoot(_followOnRoots, ai.first);
- checkReachabilityFromRoot(_followOnRoots, ai.second);
- }
-}
-#endif // #ifndef NDEBUG
-
-/// The function compares atoms by sorting atoms in the following order
-/// a) Sorts atoms by their ordinal overrides (layout-after/ingroup)
-/// b) Sorts atoms by their permissions
-/// c) Sorts atoms by their content
-/// d) Sorts atoms by custom sorter
-/// e) Sorts atoms on how they appear using File Ordinality
-/// f) Sorts atoms on how they appear within the File
-static bool compareAtomsSub(const LayoutPass::SortKey &lc,
- const LayoutPass::SortKey &rc,
- LayoutPass::SortOverride customSorter,
- std::string &reason) {
- const DefinedAtom *left = lc._atom.get();
- const DefinedAtom *right = rc._atom.get();
- if (left == right) {
- reason = "same";
- return false;
- }
-
- // Find the root of the chain if it is a part of a follow-on chain.
- const DefinedAtom *leftRoot = lc._root;
- const DefinedAtom *rightRoot = rc._root;
-
- // Sort atoms by their ordinal overrides only if they fall in the same
- // chain.
- if (leftRoot == rightRoot) {
- LLVM_DEBUG(reason = formatReason("override", lc._override, rc._override));
- return lc._override < rc._override;
- }
-
- // Sort same permissions together.
- DefinedAtom::ContentPermissions leftPerms = leftRoot->permissions();
- DefinedAtom::ContentPermissions rightPerms = rightRoot->permissions();
-
- if (leftPerms != rightPerms) {
- LLVM_DEBUG(
- reason = formatReason("contentPerms", (int)leftPerms, (int)rightPerms));
- return leftPerms < rightPerms;
- }
-
- // Sort same content types together.
- DefinedAtom::ContentType leftType = leftRoot->contentType();
- DefinedAtom::ContentType rightType = rightRoot->contentType();
-
- if (leftType != rightType) {
- LLVM_DEBUG(reason =
- formatReason("contentType", (int)leftType, (int)rightType));
- return leftType < rightType;
- }
-
- // Use custom sorter if supplied.
- if (customSorter) {
- bool leftBeforeRight;
- if (customSorter(leftRoot, rightRoot, leftBeforeRight))
- return leftBeforeRight;
- }
-
- // Sort by .o order.
- const File *leftFile = &leftRoot->file();
- const File *rightFile = &rightRoot->file();
-
- if (leftFile != rightFile) {
- LLVM_DEBUG(reason = formatReason(".o order", (int)leftFile->ordinal(),
- (int)rightFile->ordinal()));
- return leftFile->ordinal() < rightFile->ordinal();
- }
-
- // Sort by atom order with .o file.
- uint64_t leftOrdinal = leftRoot->ordinal();
- uint64_t rightOrdinal = rightRoot->ordinal();
-
- if (leftOrdinal != rightOrdinal) {
- LLVM_DEBUG(reason = formatReason("ordinal", (int)leftRoot->ordinal(),
- (int)rightRoot->ordinal()));
- return leftOrdinal < rightOrdinal;
- }
-
- llvm::errs() << "Unordered: <" << left->name() << "> <" << right->name()
- << ">\n";
- llvm_unreachable("Atoms with Same Ordinal!");
-}
-
-static bool compareAtoms(const LayoutPass::SortKey &lc,
- const LayoutPass::SortKey &rc,
- LayoutPass::SortOverride customSorter) {
- std::string reason;
- bool result = compareAtomsSub(lc, rc, customSorter, reason);
- LLVM_DEBUG({
- StringRef comp = result ? "<" : ">=";
- llvm::dbgs() << "Layout: '" << lc._atom.get()->name()
- << "' " << comp << " '"
- << rc._atom.get()->name() << "' (" << reason << ")\n";
- });
- return result;
-}
-
-LayoutPass::LayoutPass(const Registry ®istry, SortOverride sorter)
- : _registry(registry), _customSorter(std::move(sorter)) {}
-
-// Returns the atom immediately followed by the given atom in the followon
-// chain.
-const DefinedAtom *LayoutPass::findAtomFollowedBy(
- const DefinedAtom *targetAtom) {
- // Start from the beginning of the chain and follow the chain until
- // we find the targetChain.
- const DefinedAtom *atom = _followOnRoots[targetAtom];
- while (true) {
- const DefinedAtom *prevAtom = atom;
- AtomToAtomT::iterator targetFollowOnAtomsIter = _followOnNexts.find(atom);
- // The target atom must be in the chain of its root.
- assert(targetFollowOnAtomsIter != _followOnNexts.end());
- atom = targetFollowOnAtomsIter->second;
- if (atom == targetAtom)
- return prevAtom;
- }
-}
-
-// Check if all the atoms followed by the given target atom are of size zero.
-// When this method is called, an atom being added is not of size zero and
-// will be added to the head of the followon chain. All the atoms between the
-// atom and the targetAtom (specified by layout-after) need to be of size zero
-// in this case. Otherwise the desired layout is impossible.
-bool LayoutPass::checkAllPrevAtomsZeroSize(const DefinedAtom *targetAtom) {
- const DefinedAtom *atom = _followOnRoots[targetAtom];
- while (true) {
- if (atom == targetAtom)
- return true;
- if (atom->size() != 0)
- // TODO: print warning that an impossible layout is being desired by the
- // user.
- return false;
- AtomToAtomT::iterator targetFollowOnAtomsIter = _followOnNexts.find(atom);
- // The target atom must be in the chain of its root.
- assert(targetFollowOnAtomsIter != _followOnNexts.end());
- atom = targetFollowOnAtomsIter->second;
- }
-}
-
-// Set the root of all atoms in targetAtom's chain to the given root.
-void LayoutPass::setChainRoot(const DefinedAtom *targetAtom,
- const DefinedAtom *root) {
- // Walk through the followon chain and override each node's root.
- while (true) {
- _followOnRoots[targetAtom] = root;
- AtomToAtomT::iterator targetFollowOnAtomsIter =
- _followOnNexts.find(targetAtom);
- if (targetFollowOnAtomsIter == _followOnNexts.end())
- return;
- targetAtom = targetFollowOnAtomsIter->second;
- }
-}
-
-/// This pass builds the followon tables described by two DenseMaps
-/// followOnRoots and followonNexts.
-/// The followOnRoots map contains a mapping of a DefinedAtom to its root
-/// The followOnNexts map contains a mapping of what DefinedAtom follows the
-/// current Atom
-/// The algorithm follows a very simple approach
-/// a) If the atom is first seen, then make that as the root atom
-/// b) The targetAtom which this Atom contains, has the root thats set to the
-/// root of the current atom
-/// c) If the targetAtom is part of a different tree and the root of the
-/// targetAtom is itself, Chain all the atoms that are contained in the tree
-/// to the current Tree
-/// d) If the targetAtom is part of a different chain and the root of the
-/// targetAtom until the targetAtom has all atoms of size 0, then chain the
-/// targetAtoms and its tree to the current chain
-void LayoutPass::buildFollowOnTable(const File::AtomRange<DefinedAtom> &range) {
- ScopedTask task(getDefaultDomain(), "LayoutPass::buildFollowOnTable");
- // Set the initial size of the followon and the followonNext hash to the
- // number of atoms that we have.
- _followOnRoots.reserve(range.size());
- _followOnNexts.reserve(range.size());
- for (const DefinedAtom *ai : range) {
- for (const Reference *r : *ai) {
- if (r->kindNamespace() != lld::Reference::KindNamespace::all ||
- r->kindValue() != lld::Reference::kindLayoutAfter)
- continue;
- const DefinedAtom *targetAtom = dyn_cast<DefinedAtom>(r->target());
- _followOnNexts[ai] = targetAtom;
-
- // If we find a followon for the first time, let's make that atom as the
- // root atom.
- if (_followOnRoots.count(ai) == 0)
- _followOnRoots[ai] = ai;
-
- auto iter = _followOnRoots.find(targetAtom);
- if (iter == _followOnRoots.end()) {
- // If the targetAtom is not a root of any chain, let's make the root of
- // the targetAtom to the root of the current chain.
-
- // The expression m[i] = m[j] where m is a DenseMap and i != j is not
- // safe. m[j] returns a reference, which would be invalidated when a
- // rehashing occurs. If rehashing occurs to make room for m[i], m[j]
- // becomes invalid, and that invalid reference would be used as the RHS
- // value of the expression.
- // Copy the value to workaround.
- const DefinedAtom *tmp = _followOnRoots[ai];
- _followOnRoots[targetAtom] = tmp;
- continue;
- }
- if (iter->second == targetAtom) {
- // If the targetAtom is the root of a chain, the chain becomes part of
- // the current chain. Rewrite the subchain's root to the current
- // chain's root.
- setChainRoot(targetAtom, _followOnRoots[ai]);
- continue;
- }
- // The targetAtom is already a part of a chain. If the current atom is
- // of size zero, we can insert it in the middle of the chain just
- // before the target atom, while not breaking other atom's followon
- // relationships. If it's not, we can only insert the current atom at
- // the beginning of the chain. All the atoms followed by the target
- // atom must be of size zero in that case to satisfy the followon
- // relationships.
- size_t currentAtomSize = ai->size();
- if (currentAtomSize == 0) {
- const DefinedAtom *targetPrevAtom = findAtomFollowedBy(targetAtom);
- _followOnNexts[targetPrevAtom] = ai;
- const DefinedAtom *tmp = _followOnRoots[targetPrevAtom];
- _followOnRoots[ai] = tmp;
- continue;
- }
- if (!checkAllPrevAtomsZeroSize(targetAtom))
- break;
- _followOnNexts[ai] = _followOnRoots[targetAtom];
- setChainRoot(_followOnRoots[targetAtom], _followOnRoots[ai]);
- }
- }
-}
-
-/// Build an ordinal override map by traversing the followon chain, and
-/// assigning ordinals to each atom, if the atoms have their ordinals
-/// already assigned skip the atom and move to the next. This is the
-/// main map thats used to sort the atoms while comparing two atoms together
-void
-LayoutPass::buildOrdinalOverrideMap(const File::AtomRange<DefinedAtom> &range) {
- ScopedTask task(getDefaultDomain(), "LayoutPass::buildOrdinalOverrideMap");
- uint64_t index = 0;
- for (const DefinedAtom *ai : range) {
- const DefinedAtom *atom = ai;
- if (_ordinalOverrideMap.find(atom) != _ordinalOverrideMap.end())
- continue;
- AtomToAtomT::iterator start = _followOnRoots.find(atom);
- if (start == _followOnRoots.end())
- continue;
- for (const DefinedAtom *nextAtom = start->second; nextAtom;
- nextAtom = _followOnNexts[nextAtom]) {
- AtomToOrdinalT::iterator pos = _ordinalOverrideMap.find(nextAtom);
- if (pos == _ordinalOverrideMap.end())
- _ordinalOverrideMap[nextAtom] = index++;
- }
- }
-}
-
-std::vector<LayoutPass::SortKey>
-LayoutPass::decorate(File::AtomRange<DefinedAtom> &atomRange) const {
- std::vector<SortKey> ret;
- for (OwningAtomPtr<DefinedAtom> &atom : atomRange.owning_ptrs()) {
- auto ri = _followOnRoots.find(atom.get());
- auto oi = _ordinalOverrideMap.find(atom.get());
- const auto *root = (ri == _followOnRoots.end()) ? atom.get() : ri->second;
- uint64_t override = (oi == _ordinalOverrideMap.end()) ? 0 : oi->second;
- ret.push_back(SortKey(std::move(atom), root, override));
- }
- return ret;
-}
-
-void LayoutPass::undecorate(File::AtomRange<DefinedAtom> &atomRange,
- std::vector<SortKey> &keys) const {
- size_t i = 0;
- for (SortKey &k : keys)
- atomRange[i++] = std::move(k._atom);
-}
-
-/// Perform the actual pass
-llvm::Error LayoutPass::perform(SimpleFile &mergedFile) {
- LLVM_DEBUG(llvm::dbgs() << "******** Laying out atoms:\n");
- // sort the atoms
- ScopedTask task(getDefaultDomain(), "LayoutPass");
- File::AtomRange<DefinedAtom> atomRange = mergedFile.defined();
-
- // Build follow on tables
- buildFollowOnTable(atomRange);
-
- // Check the structure of followon graph if running in debug mode.
- LLVM_DEBUG(checkFollowonChain(atomRange));
-
- // Build override maps
- buildOrdinalOverrideMap(atomRange);
-
- LLVM_DEBUG({
- llvm::dbgs() << "unsorted atoms:\n";
- printDefinedAtoms(atomRange);
- });
-
- std::vector<LayoutPass::SortKey> vec = decorate(atomRange);
- llvm::parallelSort(
- vec,
- [&](const LayoutPass::SortKey &l, const LayoutPass::SortKey &r) -> bool {
- return compareAtoms(l, r, _customSorter);
- });
- LLVM_DEBUG(checkTransitivity(vec, _customSorter));
- undecorate(atomRange, vec);
-
- LLVM_DEBUG({
- llvm::dbgs() << "sorted atoms:\n";
- printDefinedAtoms(atomRange);
- });
-
- LLVM_DEBUG(llvm::dbgs() << "******** Finished laying out atoms\n");
- return llvm::Error::success();
-}
-
-void addLayoutPass(PassManager &pm, const MachOLinkingContext &ctx) {
- pm.add(std::make_unique<LayoutPass>(
- ctx.registry(), [&](const DefinedAtom * left, const DefinedAtom * right,
- bool & leftBeforeRight) ->bool {
- return ctx.customAtomOrderer(left, right, leftBeforeRight);
- }));
-}
-
-} // namespace mach_o
-} // namespace lld
+++ /dev/null
-//===------ lib/ReaderWriter/MachO/LayoutPass.h - Handles Layout of atoms -===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_READER_WRITER_MACHO_LAYOUT_PASS_H
-#define LLD_READER_WRITER_MACHO_LAYOUT_PASS_H
-
-#include "lld/Core/File.h"
-#include "lld/Core/Pass.h"
-#include "lld/Core/Reader.h"
-#include "lld/Core/Simple.h"
-#include "llvm/ADT/DenseMap.h"
-#include <map>
-#include <string>
-#include <vector>
-
-namespace lld {
-class DefinedAtom;
-class SimpleFile;
-
-namespace mach_o {
-
-/// This linker pass does the layout of the atoms. The pass is done after the
-/// order their .o files were found on the command line, then by order of the
-/// atoms (address) in the .o file. But some atoms have a preferred location
-/// in their section (such as pinned to the start or end of the section), so
-/// the sort must take that into account too.
-class LayoutPass : public Pass {
-public:
- struct SortKey {
- SortKey(OwningAtomPtr<DefinedAtom> &&atom,
- const DefinedAtom *root, uint64_t override)
- : _atom(std::move(atom)), _root(root), _override(override) {}
- OwningAtomPtr<DefinedAtom> _atom;
- const DefinedAtom *_root;
- uint64_t _override;
-
- // Note, these are only here to appease MSVC bots which didn't like
- // the same methods being implemented/deleted in OwningAtomPtr.
- SortKey(SortKey &&key) : _atom(std::move(key._atom)), _root(key._root),
- _override(key._override) {
- key._root = nullptr;
- }
-
- SortKey &operator=(SortKey &&key) {
- _atom = std::move(key._atom);
- _root = key._root;
- key._root = nullptr;
- _override = key._override;
- return *this;
- }
-
- private:
- SortKey(const SortKey &) = delete;
- void operator=(const SortKey&) = delete;
- };
-
- typedef std::function<bool (const DefinedAtom *left, const DefinedAtom *right,
- bool &leftBeforeRight)> SortOverride;
-
- LayoutPass(const Registry ®istry, SortOverride sorter);
-
- /// Sorts atoms in mergedFile by content type then by command line order.
- llvm::Error perform(SimpleFile &mergedFile) override;
-
- ~LayoutPass() override = default;
-
-private:
- // Build the followOn atoms chain as specified by the kindLayoutAfter
- // reference type
- void buildFollowOnTable(const File::AtomRange<DefinedAtom> &range);
-
- // Build a map of Atoms to ordinals for sorting the atoms
- void buildOrdinalOverrideMap(const File::AtomRange<DefinedAtom> &range);
-
- const Registry &_registry;
- SortOverride _customSorter;
-
- typedef llvm::DenseMap<const DefinedAtom *, const DefinedAtom *> AtomToAtomT;
- typedef llvm::DenseMap<const DefinedAtom *, uint64_t> AtomToOrdinalT;
-
- // A map to be used to sort atoms. It represents the order of atoms in the
- // result; if Atom X is mapped to atom Y in this map, X will be located
- // immediately before Y in the output file. Y might be mapped to another
- // atom, constructing a follow-on chain. An atom cannot be mapped to more
- // than one atom unless all but one atom are of size zero.
- AtomToAtomT _followOnNexts;
-
- // A map to be used to sort atoms. It's a map from an atom to its root of
- // follow-on chain. A root atom is mapped to itself. If an atom is not in
- // _followOnNexts, the atom is not in this map, and vice versa.
- AtomToAtomT _followOnRoots;
-
- AtomToOrdinalT _ordinalOverrideMap;
-
- // Helper methods for buildFollowOnTable().
- const DefinedAtom *findAtomFollowedBy(const DefinedAtom *targetAtom);
- bool checkAllPrevAtomsZeroSize(const DefinedAtom *targetAtom);
-
- void setChainRoot(const DefinedAtom *targetAtom, const DefinedAtom *root);
-
- std::vector<SortKey> decorate(File::AtomRange<DefinedAtom> &atomRange) const;
-
- void undecorate(File::AtomRange<DefinedAtom> &atomRange,
- std::vector<SortKey> &keys) const;
-
- // Check if the follow-on graph is a correct structure. For debugging only.
- void checkFollowonChain(const File::AtomRange<DefinedAtom> &range);
-};
-
-} // namespace mach_o
-} // namespace lld
-
-#endif // LLD_READER_WRITER_MACHO_LAYOUT_PASS_H
+++ /dev/null
-//===- lib/ReaderWriter/MachO/MachOLinkingContext.cpp ---------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "lld/Common/ErrorHandler.h"
-#include "lld/ReaderWriter/MachOLinkingContext.h"
-#include "ArchHandler.h"
-#include "File.h"
-#include "FlatNamespaceFile.h"
-#include "MachONormalizedFile.h"
-#include "MachOPasses.h"
-#include "SectCreateFile.h"
-#include "lld/Common/Driver.h"
-#include "lld/Core/ArchiveLibraryFile.h"
-#include "lld/Core/PassManager.h"
-#include "lld/Core/Reader.h"
-#include "lld/Core/Writer.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/StringExtras.h"
-#include "llvm/ADT/Triple.h"
-#include "llvm/BinaryFormat/MachO.h"
-#include "llvm/Demangle/Demangle.h"
-#include "llvm/Support/Debug.h"
-#include "llvm/Support/Errc.h"
-#include "llvm/Support/Host.h"
-#include "llvm/Support/Path.h"
-#include <algorithm>
-
-using lld::mach_o::ArchHandler;
-using lld::mach_o::MachOFile;
-using lld::mach_o::MachODylibFile;
-using namespace llvm::MachO;
-
-namespace lld {
-
-bool MachOLinkingContext::parsePackedVersion(StringRef str, uint32_t &result) {
- result = 0;
-
- if (str.empty())
- return false;
-
- SmallVector<StringRef, 3> parts;
- llvm::SplitString(str, parts, ".");
-
- unsigned long long num;
- if (llvm::getAsUnsignedInteger(parts[0], 10, num))
- return true;
- if (num > 65535)
- return true;
- result = num << 16;
-
- if (parts.size() > 1) {
- if (llvm::getAsUnsignedInteger(parts[1], 10, num))
- return true;
- if (num > 255)
- return true;
- result |= (num << 8);
- }
-
- if (parts.size() > 2) {
- if (llvm::getAsUnsignedInteger(parts[2], 10, num))
- return true;
- if (num > 255)
- return true;
- result |= num;
- }
-
- return false;
-}
-
-bool MachOLinkingContext::parsePackedVersion(StringRef str, uint64_t &result) {
- result = 0;
-
- if (str.empty())
- return false;
-
- SmallVector<StringRef, 5> parts;
- llvm::SplitString(str, parts, ".");
-
- unsigned long long num;
- if (llvm::getAsUnsignedInteger(parts[0], 10, num))
- return true;
- if (num > 0xFFFFFF)
- return true;
- result = num << 40;
-
- unsigned Shift = 30;
- for (StringRef str : llvm::makeArrayRef(parts).slice(1)) {
- if (llvm::getAsUnsignedInteger(str, 10, num))
- return true;
- if (num > 0x3FF)
- return true;
- result |= (num << Shift);
- Shift -= 10;
- }
-
- return false;
-}
-
-MachOLinkingContext::ArchInfo MachOLinkingContext::_s_archInfos[] = {
- { "x86_64", arch_x86_64, true, CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL },
- { "i386", arch_x86, true, CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL },
- { "ppc", arch_ppc, false, CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL },
- { "armv6", arch_armv6, true, CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6 },
- { "armv7", arch_armv7, true, CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7 },
- { "armv7s", arch_armv7s, true, CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S },
- { "arm64", arch_arm64, true, CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL },
- { "", arch_unknown,false, 0, 0 }
-};
-
-MachOLinkingContext::Arch
-MachOLinkingContext::archFromCpuType(uint32_t cputype, uint32_t cpusubtype) {
- for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) {
- if ((info->cputype == cputype) && (info->cpusubtype == cpusubtype))
- return info->arch;
- }
- return arch_unknown;
-}
-
-MachOLinkingContext::Arch
-MachOLinkingContext::archFromName(StringRef archName) {
- for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) {
- if (info->archName.equals(archName))
- return info->arch;
- }
- return arch_unknown;
-}
-
-StringRef MachOLinkingContext::nameFromArch(Arch arch) {
- for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) {
- if (info->arch == arch)
- return info->archName;
- }
- return "<unknown>";
-}
-
-uint32_t MachOLinkingContext::cpuTypeFromArch(Arch arch) {
- assert(arch != arch_unknown);
- for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) {
- if (info->arch == arch)
- return info->cputype;
- }
- llvm_unreachable("Unknown arch type");
-}
-
-uint32_t MachOLinkingContext::cpuSubtypeFromArch(Arch arch) {
- assert(arch != arch_unknown);
- for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) {
- if (info->arch == arch)
- return info->cpusubtype;
- }
- llvm_unreachable("Unknown arch type");
-}
-
-bool MachOLinkingContext::isThinObjectFile(StringRef path, Arch &arch) {
- return mach_o::normalized::isThinObjectFile(path, arch);
-}
-
-bool MachOLinkingContext::sliceFromFatFile(MemoryBufferRef mb, uint32_t &offset,
- uint32_t &size) {
- return mach_o::normalized::sliceFromFatFile(mb, _arch, offset, size);
-}
-
-MachOLinkingContext::MachOLinkingContext() {}
-
-MachOLinkingContext::~MachOLinkingContext() {
- // Atoms are allocated on BumpPtrAllocator's on File's.
- // As we transfer atoms from one file to another, we need to clear all of the
- // atoms before we remove any of the BumpPtrAllocator's.
- auto &nodes = getNodes();
- for (unsigned i = 0, e = nodes.size(); i != e; ++i) {
- FileNode *node = dyn_cast<FileNode>(nodes[i].get());
- if (!node)
- continue;
- File *file = node->getFile();
- file->clearAtoms();
- }
-}
-
-void MachOLinkingContext::configure(HeaderFileType type, Arch arch, OS os,
- uint32_t minOSVersion,
- bool exportDynamicSymbols) {
- _outputMachOType = type;
- _arch = arch;
- _os = os;
- _osMinVersion = minOSVersion;
-
- // If min OS not specified on command line, use reasonable defaults.
- // Note that we only do sensible defaults when emitting something other than
- // object and preload.
- if (_outputMachOType != llvm::MachO::MH_OBJECT &&
- _outputMachOType != llvm::MachO::MH_PRELOAD) {
- if (minOSVersion == 0) {
- switch (_arch) {
- case arch_x86_64:
- case arch_x86:
- parsePackedVersion("10.8", _osMinVersion);
- _os = MachOLinkingContext::OS::macOSX;
- break;
- case arch_armv6:
- case arch_armv7:
- case arch_armv7s:
- case arch_arm64:
- parsePackedVersion("7.0", _osMinVersion);
- _os = MachOLinkingContext::OS::iOS;
- break;
- default:
- break;
- }
- }
- }
-
- switch (_outputMachOType) {
- case llvm::MachO::MH_EXECUTE:
- // If targeting newer OS, use _main
- if (minOS("10.8", "6.0")) {
- _entrySymbolName = "_main";
- } else {
- // If targeting older OS, use start (in crt1.o)
- _entrySymbolName = "start";
- }
-
- // __PAGEZERO defaults to 4GB on 64-bit (except for PP64 which lld does not
- // support) and 4KB on 32-bit.
- if (is64Bit(_arch)) {
- _pageZeroSize = 0x100000000;
- } else {
- _pageZeroSize = 0x1000;
- }
-
- // Initial base address is __PAGEZERO size.
- _baseAddress = _pageZeroSize;
-
- // Make PIE by default when targetting newer OSs.
- switch (os) {
- case OS::macOSX:
- if (minOSVersion >= 0x000A0700) // MacOSX 10.7
- _pie = true;
- break;
- case OS::iOS:
- if (minOSVersion >= 0x00040300) // iOS 4.3
- _pie = true;
- break;
- case OS::iOS_simulator:
- _pie = true;
- break;
- case OS::unknown:
- break;
- }
- setGlobalsAreDeadStripRoots(exportDynamicSymbols);
- break;
- case llvm::MachO::MH_DYLIB:
- setGlobalsAreDeadStripRoots(exportDynamicSymbols);
- break;
- case llvm::MachO::MH_BUNDLE:
- break;
- case llvm::MachO::MH_OBJECT:
- _printRemainingUndefines = false;
- _allowRemainingUndefines = true;
- break;
- default:
- break;
- }
-
- // Set default segment page sizes based on arch.
- if (arch == arch_arm64)
- _pageSize = 4*4096;
-}
-
-uint32_t MachOLinkingContext::getCPUType() const {
- return cpuTypeFromArch(_arch);
-}
-
-uint32_t MachOLinkingContext::getCPUSubType() const {
- return cpuSubtypeFromArch(_arch);
-}
-
-bool MachOLinkingContext::is64Bit(Arch arch) {
- for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) {
- if (info->arch == arch) {
- return (info->cputype & CPU_ARCH_ABI64);
- }
- }
- // unknown archs are not 64-bit.
- return false;
-}
-
-bool MachOLinkingContext::isHostEndian(Arch arch) {
- assert(arch != arch_unknown);
- for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) {
- if (info->arch == arch) {
- return (info->littleEndian == llvm::sys::IsLittleEndianHost);
- }
- }
- llvm_unreachable("Unknown arch type");
-}
-
-bool MachOLinkingContext::isBigEndian(Arch arch) {
- assert(arch != arch_unknown);
- for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) {
- if (info->arch == arch) {
- return ! info->littleEndian;
- }
- }
- llvm_unreachable("Unknown arch type");
-}
-
-bool MachOLinkingContext::is64Bit() const {
- return is64Bit(_arch);
-}
-
-bool MachOLinkingContext::outputTypeHasEntry() const {
- switch (_outputMachOType) {
- case MH_EXECUTE:
- case MH_DYLINKER:
- case MH_PRELOAD:
- return true;
- default:
- return false;
- }
-}
-
-bool MachOLinkingContext::needsStubsPass() const {
- switch (_outputMachOType) {
- case MH_EXECUTE:
- return !_outputMachOTypeStatic;
- case MH_DYLIB:
- case MH_BUNDLE:
- return true;
- default:
- return false;
- }
-}
-
-bool MachOLinkingContext::needsGOTPass() const {
- // GOT pass not used in -r mode.
- if (_outputMachOType == MH_OBJECT)
- return false;
- // Only some arches use GOT pass.
- switch (_arch) {
- case arch_x86_64:
- case arch_arm64:
- return true;
- default:
- return false;
- }
-}
-
-bool MachOLinkingContext::needsCompactUnwindPass() const {
- switch (_outputMachOType) {
- case MH_EXECUTE:
- case MH_DYLIB:
- case MH_BUNDLE:
- return archHandler().needsCompactUnwind();
- default:
- return false;
- }
-}
-
-bool MachOLinkingContext::needsObjCPass() const {
- // ObjC pass is only needed if any of the inputs were ObjC.
- return _objcConstraint != objc_unknown;
-}
-
-bool MachOLinkingContext::needsShimPass() const {
- // Shim pass only used in final executables.
- if (_outputMachOType == MH_OBJECT)
- return false;
- // Only 32-bit arm arches use Shim pass.
- switch (_arch) {
- case arch_armv6:
- case arch_armv7:
- case arch_armv7s:
- return true;
- default:
- return false;
- }
-}
-
-bool MachOLinkingContext::needsTLVPass() const {
- switch (_outputMachOType) {
- case MH_BUNDLE:
- case MH_EXECUTE:
- case MH_DYLIB:
- return true;
- default:
- return false;
- }
-}
-
-StringRef MachOLinkingContext::binderSymbolName() const {
- return archHandler().stubInfo().binderSymbolName;
-}
-
-bool MachOLinkingContext::minOS(StringRef mac, StringRef iOS) const {
- uint32_t parsedVersion;
- switch (_os) {
- case OS::macOSX:
- if (parsePackedVersion(mac, parsedVersion))
- return false;
- return _osMinVersion >= parsedVersion;
- case OS::iOS:
- case OS::iOS_simulator:
- if (parsePackedVersion(iOS, parsedVersion))
- return false;
- return _osMinVersion >= parsedVersion;
- case OS::unknown:
- // If we don't know the target, then assume that we don't meet the min OS.
- // This matches the ld64 behaviour
- return false;
- }
- llvm_unreachable("invalid OS enum");
-}
-
-bool MachOLinkingContext::addEntryPointLoadCommand() const {
- if ((_outputMachOType == MH_EXECUTE) && !_outputMachOTypeStatic) {
- return minOS("10.8", "6.0");
- }
- return false;
-}
-
-bool MachOLinkingContext::addUnixThreadLoadCommand() const {
- switch (_outputMachOType) {
- case MH_EXECUTE:
- if (_outputMachOTypeStatic)
- return true;
- else
- return !minOS("10.8", "6.0");
- break;
- case MH_DYLINKER:
- case MH_PRELOAD:
- return true;
- default:
- return false;
- }
-}
-
-bool MachOLinkingContext::pathExists(StringRef path) const {
- if (!_testingFileUsage)
- return llvm::sys::fs::exists(path.str());
-
- // Otherwise, we're in test mode: only files explicitly provided on the
- // command-line exist.
- std::string key = path.str();
- std::replace(key.begin(), key.end(), '\\', '/');
- return _existingPaths.find(key) != _existingPaths.end();
-}
-
-bool MachOLinkingContext::fileExists(StringRef path) const {
- bool found = pathExists(path);
- // Log search misses.
- if (!found)
- addInputFileNotFound(path);
-
- // When testing, file is never opened, so logging is done here.
- if (_testingFileUsage && found)
- addInputFileDependency(path);
-
- return found;
-}
-
-void MachOLinkingContext::setSysLibRoots(const StringRefVector &paths) {
- _syslibRoots = paths;
-}
-
-void MachOLinkingContext::addRpath(StringRef rpath) {
- _rpaths.push_back(rpath);
-}
-
-void MachOLinkingContext::addModifiedSearchDir(StringRef libPath,
- bool isSystemPath) {
- bool addedModifiedPath = false;
-
- // -syslibroot only applies to absolute paths.
- if (libPath.startswith("/")) {
- for (auto syslibRoot : _syslibRoots) {
- SmallString<256> path(syslibRoot);
- llvm::sys::path::append(path, libPath);
- if (pathExists(path)) {
- _searchDirs.push_back(path.str().copy(_allocator));
- addedModifiedPath = true;
- }
- }
- }
-
- if (addedModifiedPath)
- return;
-
- // Finally, if only one -syslibroot is given, system paths which aren't in it
- // get suppressed.
- if (_syslibRoots.size() != 1 || !isSystemPath) {
- if (pathExists(libPath)) {
- _searchDirs.push_back(libPath);
- }
- }
-}
-
-void MachOLinkingContext::addFrameworkSearchDir(StringRef fwPath,
- bool isSystemPath) {
- bool pathAdded = false;
-
- // -syslibroot only used with to absolute framework search paths.
- if (fwPath.startswith("/")) {
- for (auto syslibRoot : _syslibRoots) {
- SmallString<256> path(syslibRoot);
- llvm::sys::path::append(path, fwPath);
- if (pathExists(path)) {
- _frameworkDirs.push_back(path.str().copy(_allocator));
- pathAdded = true;
- }
- }
- }
- // If fwPath found in any -syslibroot, then done.
- if (pathAdded)
- return;
-
- // If only one -syslibroot, system paths not in that SDK are suppressed.
- if (isSystemPath && (_syslibRoots.size() == 1))
- return;
-
- // Only use raw fwPath if that directory exists.
- if (pathExists(fwPath))
- _frameworkDirs.push_back(fwPath);
-}
-
-llvm::Optional<StringRef>
-MachOLinkingContext::searchDirForLibrary(StringRef path,
- StringRef libName) const {
- SmallString<256> fullPath;
- if (libName.endswith(".o")) {
- // A request ending in .o is special: just search for the file directly.
- fullPath.assign(path);
- llvm::sys::path::append(fullPath, libName);
- if (fileExists(fullPath))
- return fullPath.str().copy(_allocator);
- return llvm::None;
- }
-
- // Search for stub library
- fullPath.assign(path);
- llvm::sys::path::append(fullPath, Twine("lib") + libName + ".tbd");
- if (fileExists(fullPath))
- return fullPath.str().copy(_allocator);
-
- // Search for dynamic library
- fullPath.assign(path);
- llvm::sys::path::append(fullPath, Twine("lib") + libName + ".dylib");
- if (fileExists(fullPath))
- return fullPath.str().copy(_allocator);
-
- // If not, try for a static library
- fullPath.assign(path);
- llvm::sys::path::append(fullPath, Twine("lib") + libName + ".a");
- if (fileExists(fullPath))
- return fullPath.str().copy(_allocator);
-
- return llvm::None;
-}
-
-llvm::Optional<StringRef>
-MachOLinkingContext::searchLibrary(StringRef libName) const {
- SmallString<256> path;
- for (StringRef dir : searchDirs()) {
- llvm::Optional<StringRef> searchDir = searchDirForLibrary(dir, libName);
- if (searchDir)
- return searchDir;
- }
-
- return llvm::None;
-}
-
-llvm::Optional<StringRef>
-MachOLinkingContext::findPathForFramework(StringRef fwName) const{
- SmallString<256> fullPath;
- for (StringRef dir : frameworkDirs()) {
- fullPath.assign(dir);
- llvm::sys::path::append(fullPath, Twine(fwName) + ".framework", fwName);
- if (fileExists(fullPath))
- return fullPath.str().copy(_allocator);
- }
-
- return llvm::None;
-}
-
-bool MachOLinkingContext::validateImpl() {
- // TODO: if -arch not specified, look at arch of first .o file.
-
- if (_currentVersion && _outputMachOType != MH_DYLIB) {
- error("-current_version can only be used with dylibs");
- return false;
- }
-
- if (_compatibilityVersion && _outputMachOType != MH_DYLIB) {
- error("-compatibility_version can only be used with dylibs");
- return false;
- }
-
- if (_deadStrippableDylib && _outputMachOType != MH_DYLIB) {
- error("-mark_dead_strippable_dylib can only be used with dylibs");
- return false;
- }
-
- if (!_bundleLoader.empty() && outputMachOType() != MH_BUNDLE) {
- error("-bundle_loader can only be used with Mach-O bundles");
- return false;
- }
-
- // If -exported_symbols_list used, all exported symbols must be defined.
- if (_exportMode == ExportMode::exported) {
- for (const auto &symbol : _exportedSymbols)
- addInitialUndefinedSymbol(symbol.getKey());
- }
-
- // If -dead_strip, set up initial live symbols.
- if (deadStrip()) {
- // Entry point is live.
- if (outputTypeHasEntry())
- addDeadStripRoot(entrySymbolName());
- // Lazy binding helper is live.
- if (needsStubsPass())
- addDeadStripRoot(binderSymbolName());
- // If using -exported_symbols_list, make all exported symbols live.
- if (_exportMode == ExportMode::exported) {
- setGlobalsAreDeadStripRoots(false);
- for (const auto &symbol : _exportedSymbols)
- addDeadStripRoot(symbol.getKey());
- }
- }
-
- addOutputFileDependency(outputPath());
-
- return true;
-}
-
-void MachOLinkingContext::addPasses(PassManager &pm) {
- // objc pass should be before layout pass. Otherwise test cases may contain
- // no atoms which confuses the layout pass.
- if (needsObjCPass())
- mach_o::addObjCPass(pm, *this);
- mach_o::addLayoutPass(pm, *this);
- if (needsStubsPass())
- mach_o::addStubsPass(pm, *this);
- if (needsCompactUnwindPass())
- mach_o::addCompactUnwindPass(pm, *this);
- if (needsGOTPass())
- mach_o::addGOTPass(pm, *this);
- if (needsTLVPass())
- mach_o::addTLVPass(pm, *this);
- if (needsShimPass())
- mach_o::addShimPass(pm, *this); // Shim pass must run after stubs pass.
-}
-
-Writer &MachOLinkingContext::writer() const {
- if (!_writer)
- _writer = createWriterMachO(*this);
- return *_writer;
-}
-
-ErrorOr<std::unique_ptr<MemoryBuffer>>
-MachOLinkingContext::getMemoryBuffer(StringRef path) {
- addInputFileDependency(path);
-
- ErrorOr<std::unique_ptr<MemoryBuffer>> mbOrErr =
- MemoryBuffer::getFileOrSTDIN(path);
- if (std::error_code ec = mbOrErr.getError())
- return ec;
- std::unique_ptr<MemoryBuffer> mb = std::move(mbOrErr.get());
-
- // If buffer contains a fat file, find required arch in fat buffer
- // and switch buffer to point to just that required slice.
- uint32_t offset;
- uint32_t size;
- if (sliceFromFatFile(mb->getMemBufferRef(), offset, size))
- return MemoryBuffer::getFileSlice(path, size, offset);
- return std::move(mb);
-}
-
-MachODylibFile* MachOLinkingContext::loadIndirectDylib(StringRef path) {
- ErrorOr<std::unique_ptr<MemoryBuffer>> mbOrErr = getMemoryBuffer(path);
- if (mbOrErr.getError())
- return nullptr;
-
- ErrorOr<std::unique_ptr<File>> fileOrErr =
- registry().loadFile(std::move(mbOrErr.get()));
- if (!fileOrErr)
- return nullptr;
- std::unique_ptr<File> &file = fileOrErr.get();
- file->parse();
- MachODylibFile *result = reinterpret_cast<MachODylibFile *>(file.get());
- // Node object now owned by _indirectDylibs vector.
- _indirectDylibs.push_back(std::move(file));
- return result;
-}
-
-MachODylibFile* MachOLinkingContext::findIndirectDylib(StringRef path) {
- // See if already loaded.
- auto pos = _pathToDylibMap.find(path);
- if (pos != _pathToDylibMap.end())
- return pos->second;
-
- // Search -L paths if of the form "libXXX.dylib"
- std::pair<StringRef, StringRef> split = path.rsplit('/');
- StringRef leafName = split.second;
- if (leafName.startswith("lib") && leafName.endswith(".dylib")) {
- // FIXME: Need to enhance searchLibrary() to only look for .dylib
- auto libPath = searchLibrary(leafName);
- if (libPath)
- return loadIndirectDylib(libPath.getValue());
- }
-
- // Try full path with sysroot.
- for (StringRef sysPath : _syslibRoots) {
- SmallString<256> fullPath;
- fullPath.assign(sysPath);
- llvm::sys::path::append(fullPath, path);
- if (pathExists(fullPath))
- return loadIndirectDylib(fullPath);
- }
-
- // Try full path.
- if (pathExists(path)) {
- return loadIndirectDylib(path);
- }
-
- return nullptr;
-}
-
-uint32_t MachOLinkingContext::dylibCurrentVersion(StringRef installName) const {
- auto pos = _pathToDylibMap.find(installName);
- if (pos != _pathToDylibMap.end())
- return pos->second->currentVersion();
- else
- return 0x10000; // 1.0
-}
-
-uint32_t MachOLinkingContext::dylibCompatVersion(StringRef installName) const {
- auto pos = _pathToDylibMap.find(installName);
- if (pos != _pathToDylibMap.end())
- return pos->second->compatVersion();
- else
- return 0x10000; // 1.0
-}
-
-void MachOLinkingContext::createImplicitFiles(
- std::vector<std::unique_ptr<File> > &result) {
- // Add indirect dylibs by asking each linked dylib to add its indirects.
- // Iterate until no more dylibs get loaded.
- size_t dylibCount = 0;
- while (dylibCount != _allDylibs.size()) {
- dylibCount = _allDylibs.size();
- for (MachODylibFile *dylib : _allDylibs) {
- dylib->loadReExportedDylibs([this] (StringRef path) -> MachODylibFile* {
- return findIndirectDylib(path); });
- }
- }
-
- // Let writer add output type specific extras.
- writer().createImplicitFiles(result);
-
- // If undefinedMode is != error, add a FlatNamespaceFile instance. This will
- // provide a SharedLibraryAtom for symbols that aren't defined elsewhere.
- if (undefinedMode() != UndefinedMode::error) {
- result.emplace_back(new mach_o::FlatNamespaceFile(*this));
- _flatNamespaceFile = result.back().get();
- }
-}
-
-void MachOLinkingContext::registerDylib(MachODylibFile *dylib,
- bool upward) const {
- std::lock_guard<std::mutex> lock(_dylibsMutex);
-
- if (!llvm::count(_allDylibs, dylib))
- _allDylibs.push_back(dylib);
- _pathToDylibMap[dylib->installName()] = dylib;
- // If path is different than install name, register path too.
- if (!dylib->path().equals(dylib->installName()))
- _pathToDylibMap[dylib->path()] = dylib;
- if (upward)
- _upwardDylibs.insert(dylib);
-}
-
-bool MachOLinkingContext::isUpwardDylib(StringRef installName) const {
- for (MachODylibFile *dylib : _upwardDylibs) {
- if (dylib->installName().equals(installName))
- return true;
- }
- return false;
-}
-
-ArchHandler &MachOLinkingContext::archHandler() const {
- if (!_archHandler)
- _archHandler = ArchHandler::create(_arch);
- return *_archHandler;
-}
-
-void MachOLinkingContext::addSectionAlignment(StringRef seg, StringRef sect,
- uint16_t align) {
- SectionAlign entry = { seg, sect, align };
- _sectAligns.push_back(entry);
-}
-
-void MachOLinkingContext::addSectCreateSection(
- StringRef seg, StringRef sect,
- std::unique_ptr<MemoryBuffer> content) {
-
- if (!_sectCreateFile) {
- auto sectCreateFile = std::make_unique<mach_o::SectCreateFile>();
- _sectCreateFile = sectCreateFile.get();
- getNodes().push_back(std::make_unique<FileNode>(std::move(sectCreateFile)));
- }
-
- assert(_sectCreateFile && "sectcreate file does not exist.");
- _sectCreateFile->addSection(seg, sect, std::move(content));
-}
-
-bool MachOLinkingContext::sectionAligned(StringRef seg, StringRef sect,
- uint16_t &align) const {
- for (const SectionAlign &entry : _sectAligns) {
- if (seg.equals(entry.segmentName) && sect.equals(entry.sectionName)) {
- align = entry.align;
- return true;
- }
- }
- return false;
-}
-
-void MachOLinkingContext::addExportSymbol(StringRef sym) {
- // Support old crufty export lists with bogus entries.
- if (sym.endswith(".eh") || sym.startswith(".objc_category_name_")) {
- llvm::errs() << "warning: ignoring " << sym << " in export list\n";
- return;
- }
- // Only i386 MacOSX uses old ABI, so don't change those.
- if ((_os != OS::macOSX) || (_arch != arch_x86)) {
- // ObjC has two different ABIs. Be nice and allow one export list work for
- // both ABIs by renaming symbols.
- if (sym.startswith(".objc_class_name_")) {
- std::string abi2className("_OBJC_CLASS_$_");
- abi2className += sym.substr(17);
- _exportedSymbols.insert(copy(abi2className));
- std::string abi2metaclassName("_OBJC_METACLASS_$_");
- abi2metaclassName += sym.substr(17);
- _exportedSymbols.insert(copy(abi2metaclassName));
- return;
- }
- }
-
- // FIXME: Support wildcards.
- _exportedSymbols.insert(sym);
-}
-
-bool MachOLinkingContext::exportSymbolNamed(StringRef sym) const {
- switch (_exportMode) {
- case ExportMode::globals:
- llvm_unreachable("exportSymbolNamed() should not be called in this mode");
- break;
- case ExportMode::exported:
- return _exportedSymbols.count(sym);
- case ExportMode::unexported:
- return !_exportedSymbols.count(sym);
- }
- llvm_unreachable("_exportMode unknown enum value");
-}
-
-std::string MachOLinkingContext::demangle(StringRef symbolName) const {
- // Only try to demangle symbols if -demangle on command line
- if (!demangleSymbols())
- return std::string(symbolName);
-
- // Only try to demangle symbols that look like C++ symbols
- if (!symbolName.startswith("__Z"))
- return std::string(symbolName);
-
- SmallString<256> symBuff;
- StringRef nullTermSym = Twine(symbolName).toNullTerminatedStringRef(symBuff);
- // Mach-O has extra leading underscore that needs to be removed.
- const char *cstr = nullTermSym.data() + 1;
- int status;
- char *demangled = llvm::itaniumDemangle(cstr, nullptr, nullptr, &status);
- if (demangled) {
- std::string result(demangled);
- // __cxa_demangle() always uses a malloc'ed buffer to return the result.
- free(demangled);
- return result;
- }
-
- return std::string(symbolName);
-}
-
-static void addDependencyInfoHelper(llvm::raw_fd_ostream *DepInfo,
- char Opcode, StringRef Path) {
- if (!DepInfo)
- return;
-
- *DepInfo << Opcode;
- *DepInfo << Path;
- *DepInfo << '\0';
-}
-
-std::error_code MachOLinkingContext::createDependencyFile(StringRef path) {
- std::error_code ec;
- _dependencyInfo = std::unique_ptr<llvm::raw_fd_ostream>(
- new llvm::raw_fd_ostream(path, ec, llvm::sys::fs::OF_None));
- if (ec) {
- _dependencyInfo.reset();
- return ec;
- }
-
- addDependencyInfoHelper(_dependencyInfo.get(), 0x00, "lld" /*FIXME*/);
- return std::error_code();
-}
-
-void MachOLinkingContext::addInputFileDependency(StringRef path) const {
- addDependencyInfoHelper(_dependencyInfo.get(), 0x10, path);
-}
-
-void MachOLinkingContext::addInputFileNotFound(StringRef path) const {
- addDependencyInfoHelper(_dependencyInfo.get(), 0x11, path);
-}
-
-void MachOLinkingContext::addOutputFileDependency(StringRef path) const {
- addDependencyInfoHelper(_dependencyInfo.get(), 0x40, path);
-}
-
-void MachOLinkingContext::appendOrderedSymbol(StringRef symbol,
- StringRef filename) {
- // To support sorting static functions which may have the same name in
- // multiple .o files, _orderFiles maps the symbol name to a vector
- // of OrderFileNode each of which can specify a file prefix.
- OrderFileNode info;
- if (!filename.empty())
- info.fileFilter = copy(filename);
- info.order = _orderFileEntries++;
- _orderFiles[symbol].push_back(info);
-}
-
-bool
-MachOLinkingContext::findOrderOrdinal(const std::vector<OrderFileNode> &nodes,
- const DefinedAtom *atom,
- unsigned &ordinal) {
- const File *objFile = &atom->file();
- assert(objFile);
- StringRef objName = objFile->path();
- std::pair<StringRef, StringRef> dirAndLeaf = objName.rsplit('/');
- if (!dirAndLeaf.second.empty())
- objName = dirAndLeaf.second;
- for (const OrderFileNode &info : nodes) {
- if (info.fileFilter.empty()) {
- // Have unprefixed symbol name in order file that matches this atom.
- ordinal = info.order;
- return true;
- }
- if (info.fileFilter.equals(objName)) {
- // Have prefixed symbol name in order file that matches atom's path.
- ordinal = info.order;
- return true;
- }
- }
- return false;
-}
-
-bool MachOLinkingContext::customAtomOrderer(const DefinedAtom *left,
- const DefinedAtom *right,
- bool &leftBeforeRight) const {
- // No custom sorting if no order file entries.
- if (!_orderFileEntries)
- return false;
-
- // Order files can only order named atoms.
- StringRef leftName = left->name();
- StringRef rightName = right->name();
- if (leftName.empty() || rightName.empty())
- return false;
-
- // If neither is in order file list, no custom sorter.
- auto leftPos = _orderFiles.find(leftName);
- auto rightPos = _orderFiles.find(rightName);
- bool leftIsOrdered = (leftPos != _orderFiles.end());
- bool rightIsOrdered = (rightPos != _orderFiles.end());
- if (!leftIsOrdered && !rightIsOrdered)
- return false;
-
- // There could be multiple symbols with same name but different file prefixes.
- unsigned leftOrder;
- unsigned rightOrder;
- bool foundLeft =
- leftIsOrdered && findOrderOrdinal(leftPos->getValue(), left, leftOrder);
- bool foundRight = rightIsOrdered &&
- findOrderOrdinal(rightPos->getValue(), right, rightOrder);
- if (!foundLeft && !foundRight)
- return false;
-
- // If only one is in order file list, ordered one goes first.
- if (foundLeft != foundRight)
- leftBeforeRight = foundLeft;
- else
- leftBeforeRight = (leftOrder < rightOrder);
-
- return true;
-}
-
-static bool isLibrary(const std::unique_ptr<Node> &elem) {
- if (FileNode *node = dyn_cast<FileNode>(const_cast<Node *>(elem.get()))) {
- File *file = node->getFile();
- return isa<SharedLibraryFile>(file) || isa<ArchiveLibraryFile>(file);
- }
- return false;
-}
-
-// The darwin linker processes input files in two phases. The first phase
-// links in all object (.o) files in command line order. The second phase
-// links in libraries in command line order.
-// In this function we reorder the input files so that all the object files
-// comes before any library file. We also make a group for the library files
-// so that the Resolver will reiterate over the libraries as long as we find
-// new undefines from libraries.
-void MachOLinkingContext::finalizeInputFiles() {
- std::vector<std::unique_ptr<Node>> &elements = getNodes();
- llvm::stable_sort(elements, [](const std::unique_ptr<Node> &a,
- const std::unique_ptr<Node> &b) {
- return !isLibrary(a) && isLibrary(b);
- });
- size_t numLibs = std::count_if(elements.begin(), elements.end(), isLibrary);
- elements.push_back(std::make_unique<GroupEnd>(numLibs));
-}
-
-llvm::Error MachOLinkingContext::handleLoadedFile(File &file) {
- auto *machoFile = dyn_cast<MachOFile>(&file);
- if (!machoFile)
- return llvm::Error::success();
-
- // Check that the arch of the context matches that of the file.
- // Also set the arch of the context if it didn't have one.
- if (_arch == arch_unknown) {
- _arch = machoFile->arch();
- } else if (machoFile->arch() != arch_unknown && machoFile->arch() != _arch) {
- // Archs are different.
- return llvm::make_error<GenericError>(file.path() +
- Twine(" cannot be linked due to incompatible architecture"));
- }
-
- // Check that the OS of the context matches that of the file.
- // Also set the OS of the context if it didn't have one.
- if (_os == OS::unknown) {
- _os = machoFile->OS();
- } else if (machoFile->OS() != OS::unknown && machoFile->OS() != _os) {
- // OSes are different.
- return llvm::make_error<GenericError>(file.path() +
- Twine(" cannot be linked due to incompatible operating systems"));
- }
-
- // Check that if the objc info exists, that it is compatible with the target
- // OS.
- switch (machoFile->objcConstraint()) {
- case objc_unknown:
- // The file is not compiled with objc, so skip the checks.
- break;
- case objc_gc_only:
- case objc_supports_gc:
- llvm_unreachable("GC support should already have thrown an error");
- case objc_retainReleaseForSimulator:
- // The file is built with simulator objc, so make sure that the context
- // is also building with simulator support.
- if (_os != OS::iOS_simulator)
- return llvm::make_error<GenericError>(file.path() +
- Twine(" cannot be linked. It contains ObjC built for the simulator"
- " while we are linking a non-simulator target"));
- assert((_objcConstraint == objc_unknown ||
- _objcConstraint == objc_retainReleaseForSimulator) &&
- "Must be linking with retain/release for the simulator");
- _objcConstraint = objc_retainReleaseForSimulator;
- break;
- case objc_retainRelease:
- // The file is built without simulator objc, so make sure that the
- // context is also building without simulator support.
- if (_os == OS::iOS_simulator)
- return llvm::make_error<GenericError>(file.path() +
- Twine(" cannot be linked. It contains ObjC built for a non-simulator"
- " target while we are linking a simulator target"));
- assert((_objcConstraint == objc_unknown ||
- _objcConstraint == objc_retainRelease) &&
- "Must be linking with retain/release for a non-simulator target");
- _objcConstraint = objc_retainRelease;
- break;
- }
-
- // Check that the swift version of the context matches that of the file.
- // Also set the swift version of the context if it didn't have one.
- if (!_swiftVersion) {
- _swiftVersion = machoFile->swiftVersion();
- } else if (machoFile->swiftVersion() &&
- machoFile->swiftVersion() != _swiftVersion) {
- // Swift versions are different.
- return llvm::make_error<GenericError>("different swift versions");
- }
-
- return llvm::Error::success();
-}
-
-} // end namespace lld
+++ /dev/null
-//===- lib/ReaderWriter/MachO/MachONormalizedFile.h -----------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-///
-/// \file These data structures comprise the "normalized" view of
-/// mach-o object files. The normalized view is an in-memory only data structure
-/// which is always in native endianness and pointer size.
-///
-/// The normalized view easily converts to and from YAML using YAML I/O.
-///
-/// The normalized view converts to and from binary mach-o object files using
-/// the writeBinary() and readBinary() functions.
-///
-/// The normalized view converts to and from lld::Atoms using the
-/// normalizedToAtoms() and normalizedFromAtoms().
-///
-/// Overall, the conversion paths available look like:
-///
-/// +---------------+
-/// | binary mach-o |
-/// +---------------+
-/// ^
-/// |
-/// v
-/// +------------+ +------+
-/// | normalized | <-> | yaml |
-/// +------------+ +------+
-/// ^
-/// |
-/// v
-/// +-------+
-/// | Atoms |
-/// +-------+
-///
-
-#ifndef LLD_READER_WRITER_MACHO_NORMALIZE_FILE_H
-#define LLD_READER_WRITER_MACHO_NORMALIZE_FILE_H
-
-#include "DebugInfo.h"
-#include "lld/Common/LLVM.h"
-#include "lld/Core/Error.h"
-#include "lld/ReaderWriter/MachOLinkingContext.h"
-#include "llvm/ADT/SmallString.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/BinaryFormat/MachO.h"
-#include "llvm/Support/Allocator.h"
-#include "llvm/Support/Debug.h"
-#include "llvm/Support/ErrorOr.h"
-#include "llvm/Support/YAMLTraits.h"
-
-using llvm::BumpPtrAllocator;
-using llvm::yaml::Hex64;
-using llvm::yaml::Hex32;
-using llvm::yaml::Hex16;
-using llvm::yaml::Hex8;
-using llvm::yaml::SequenceTraits;
-using llvm::MachO::HeaderFileType;
-using llvm::MachO::BindType;
-using llvm::MachO::RebaseType;
-using llvm::MachO::NListType;
-using llvm::MachO::RelocationInfoType;
-using llvm::MachO::SectionType;
-using llvm::MachO::LoadCommandType;
-using llvm::MachO::ExportSymbolKind;
-using llvm::MachO::DataRegionType;
-
-namespace lld {
-namespace mach_o {
-namespace normalized {
-
-
-/// The real mach-o relocation record is 8-bytes on disk and is
-/// encoded in one of two different bit-field patterns. This
-/// normalized form has the union of all possible fields.
-struct Relocation {
- Relocation() : offset(0), scattered(false),
- type(llvm::MachO::GENERIC_RELOC_VANILLA),
- length(0), pcRel(false), isExtern(false), value(0),
- symbol(0) { }
-
- Hex32 offset;
- bool scattered;
- RelocationInfoType type;
- uint8_t length;
- bool pcRel;
- bool isExtern;
- Hex32 value;
- uint32_t symbol;
-};
-
-/// A typedef so that YAML I/O can treat this vector as a sequence.
-typedef std::vector<Relocation> Relocations;
-
-/// A typedef so that YAML I/O can process the raw bytes in a section.
-typedef std::vector<Hex8> ContentBytes;
-
-/// A typedef so that YAML I/O can treat indirect symbols as a flow sequence.
-typedef std::vector<uint32_t> IndirectSymbols;
-
-/// A typedef so that YAML I/O can encode/decode section attributes.
-LLVM_YAML_STRONG_TYPEDEF(uint32_t, SectionAttr)
-
-/// A typedef so that YAML I/O can encode/decode section alignment.
-LLVM_YAML_STRONG_TYPEDEF(uint16_t, SectionAlignment)
-
-/// Mach-O has a 32-bit and 64-bit section record. This normalized form
-/// can support either kind.
-struct Section {
- Section() : type(llvm::MachO::S_REGULAR),
- attributes(0), alignment(1), address(0) { }
-
- StringRef segmentName;
- StringRef sectionName;
- SectionType type;
- SectionAttr attributes;
- SectionAlignment alignment;
- Hex64 address;
- ArrayRef<uint8_t> content;
- Relocations relocations;
- IndirectSymbols indirectSymbols;
-};
-
-
-/// A typedef so that YAML I/O can encode/decode the scope bits of an nlist.
-LLVM_YAML_STRONG_TYPEDEF(uint8_t, SymbolScope)
-
-/// A typedef so that YAML I/O can encode/decode the desc bits of an nlist.
-LLVM_YAML_STRONG_TYPEDEF(uint16_t, SymbolDesc)
-
-/// Mach-O has a 32-bit and 64-bit symbol table entry (nlist), and the symbol
-/// type and scope and mixed in the same n_type field. This normalized form
-/// works for any pointer size and separates out the type and scope.
-struct Symbol {
- Symbol() : type(llvm::MachO::N_UNDF), scope(0), sect(0), desc(0), value(0) { }
-
- StringRef name;
- NListType type;
- SymbolScope scope;
- uint8_t sect;
- SymbolDesc desc;
- Hex64 value;
-};
-
-/// Check whether the given section type indicates a zero-filled section.
-// FIXME: Utility functions of this kind should probably be moved into
-// llvm/Support.
-inline bool isZeroFillSection(SectionType T) {
- return (T == llvm::MachO::S_ZEROFILL ||
- T == llvm::MachO::S_THREAD_LOCAL_ZEROFILL);
-}
-
-/// A typedef so that YAML I/O can (de/en)code the protection bits of a segment.
-LLVM_YAML_STRONG_TYPEDEF(uint32_t, VMProtect)
-
-/// A typedef to hold verions X.Y.X packed into 32-bit xxxx.yy.zz
-LLVM_YAML_STRONG_TYPEDEF(uint32_t, PackedVersion)
-
-/// Segments are only used in normalized final linked images (not in relocatable
-/// object files). They specify how a range of the file is loaded.
-struct Segment {
- StringRef name;
- Hex64 address;
- Hex64 size;
- VMProtect init_access;
- VMProtect max_access;
-};
-
-/// Only used in normalized final linked images to specify on which dylibs
-/// it depends.
-struct DependentDylib {
- StringRef path;
- LoadCommandType kind;
- PackedVersion compatVersion;
- PackedVersion currentVersion;
-};
-
-/// A normalized rebasing entry. Only used in normalized final linked images.
-struct RebaseLocation {
- Hex32 segOffset;
- uint8_t segIndex;
- RebaseType kind;
-};
-
-/// A normalized binding entry. Only used in normalized final linked images.
-struct BindLocation {
- Hex32 segOffset;
- uint8_t segIndex;
- BindType kind;
- bool canBeNull;
- int ordinal;
- StringRef symbolName;
- Hex64 addend;
-};
-
-/// A typedef so that YAML I/O can encode/decode export flags.
-LLVM_YAML_STRONG_TYPEDEF(uint32_t, ExportFlags)
-
-/// A normalized export entry. Only used in normalized final linked images.
-struct Export {
- StringRef name;
- Hex64 offset;
- ExportSymbolKind kind;
- ExportFlags flags;
- Hex32 otherOffset;
- StringRef otherName;
-};
-
-/// A normalized data-in-code entry.
-struct DataInCode {
- Hex32 offset;
- Hex16 length;
- DataRegionType kind;
-};
-
-/// A typedef so that YAML I/O can encode/decode mach_header.flags.
-LLVM_YAML_STRONG_TYPEDEF(uint32_t, FileFlags)
-
-///
-struct NormalizedFile {
- MachOLinkingContext::Arch arch = MachOLinkingContext::arch_unknown;
- HeaderFileType fileType = llvm::MachO::MH_OBJECT;
- FileFlags flags = 0;
- std::vector<Segment> segments; // Not used in object files.
- std::vector<Section> sections;
-
- // Symbols sorted by kind.
- std::vector<Symbol> localSymbols;
- std::vector<Symbol> globalSymbols;
- std::vector<Symbol> undefinedSymbols;
- std::vector<Symbol> stabsSymbols;
-
- // Maps to load commands with no LINKEDIT content (final linked images only).
- std::vector<DependentDylib> dependentDylibs;
- StringRef installName; // dylibs only
- PackedVersion compatVersion = 0; // dylibs only
- PackedVersion currentVersion = 0; // dylibs only
- bool hasUUID = false;
- bool hasMinVersionLoadCommand = false;
- bool generateDataInCodeLoadCommand = false;
- std::vector<StringRef> rpaths;
- Hex64 entryAddress = 0;
- Hex64 stackSize = 0;
- MachOLinkingContext::OS os = MachOLinkingContext::OS::unknown;
- Hex64 sourceVersion = 0;
- PackedVersion minOSverson = 0;
- PackedVersion sdkVersion = 0;
- LoadCommandType minOSVersionKind = (LoadCommandType)0;
-
- // Maps to load commands with LINKEDIT content (final linked images only).
- Hex32 pageSize = 0;
- std::vector<RebaseLocation> rebasingInfo;
- std::vector<BindLocation> bindingInfo;
- std::vector<BindLocation> weakBindingInfo;
- std::vector<BindLocation> lazyBindingInfo;
- std::vector<Export> exportInfo;
- std::vector<uint8_t> functionStarts;
- std::vector<DataInCode> dataInCode;
-
- // TODO:
- // code-signature
- // split-seg-info
- // function-starts
-
- // For any allocations in this struct which need to be owned by this struct.
- BumpPtrAllocator ownedAllocations;
-};
-
-/// Tests if a file is a non-fat mach-o object file.
-bool isThinObjectFile(StringRef path, MachOLinkingContext::Arch &arch);
-
-/// If the buffer is a fat file with the request arch, then this function
-/// returns true with 'offset' and 'size' set to location of the arch slice
-/// within the buffer. Otherwise returns false;
-bool sliceFromFatFile(MemoryBufferRef mb, MachOLinkingContext::Arch arch,
- uint32_t &offset, uint32_t &size);
-
-/// Reads a mach-o file and produces an in-memory normalized view.
-llvm::Expected<std::unique_ptr<NormalizedFile>>
-readBinary(std::unique_ptr<MemoryBuffer> &mb,
- const MachOLinkingContext::Arch arch);
-
-/// Takes in-memory normalized view and writes a mach-o object file.
-llvm::Error writeBinary(const NormalizedFile &file, StringRef path);
-
-size_t headerAndLoadCommandsSize(const NormalizedFile &file,
- bool includeFunctionStarts);
-
-
-/// Parses a yaml encoded mach-o file to produce an in-memory normalized view.
-llvm::Expected<std::unique_ptr<NormalizedFile>>
-readYaml(std::unique_ptr<MemoryBuffer> &mb);
-
-/// Writes a yaml encoded mach-o files given an in-memory normalized view.
-std::error_code writeYaml(const NormalizedFile &file, raw_ostream &out);
-
-llvm::Error
-normalizedObjectToAtoms(MachOFile *file,
- const NormalizedFile &normalizedFile,
- bool copyRefs);
-
-llvm::Error
-normalizedDylibToAtoms(MachODylibFile *file,
- const NormalizedFile &normalizedFile,
- bool copyRefs);
-
-/// Takes in-memory normalized dylib or object and parses it into lld::File
-llvm::Expected<std::unique_ptr<lld::File>>
-normalizedToAtoms(const NormalizedFile &normalizedFile, StringRef path,
- bool copyRefs);
-
-/// Takes atoms and generates a normalized macho-o view.
-llvm::Expected<std::unique_ptr<NormalizedFile>>
-normalizedFromAtoms(const lld::File &atomFile, const MachOLinkingContext &ctxt);
-
-
-} // namespace normalized
-
-/// Class for interfacing mach-o yaml files into generic yaml parsing
-class MachOYamlIOTaggedDocumentHandler : public YamlIOTaggedDocumentHandler {
-public:
- MachOYamlIOTaggedDocumentHandler(MachOLinkingContext::Arch arch)
- : _arch(arch) { }
- bool handledDocTag(llvm::yaml::IO &io, const lld::File *&file) const override;
-private:
- const MachOLinkingContext::Arch _arch;
-};
-
-} // namespace mach_o
-} // namespace lld
-
-#endif // LLD_READER_WRITER_MACHO_NORMALIZE_FILE_H
+++ /dev/null
-//===- lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp ---------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-///
-/// \file For mach-o object files, this implementation converts from
-/// mach-o on-disk binary format to in-memory normalized mach-o.
-///
-/// +---------------+
-/// | binary mach-o |
-/// +---------------+
-/// |
-/// |
-/// v
-/// +------------+
-/// | normalized |
-/// +------------+
-
-#include "ArchHandler.h"
-#include "MachONormalizedFile.h"
-#include "MachONormalizedFileBinaryUtils.h"
-#include "lld/Common/LLVM.h"
-#include "lld/Core/Error.h"
-#include "lld/Core/SharedLibraryFile.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/SmallString.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/ADT/StringSwitch.h"
-#include "llvm/ADT/Twine.h"
-#include "llvm/BinaryFormat/MachO.h"
-#include "llvm/BinaryFormat/Magic.h"
-#include "llvm/Object/MachO.h"
-#include "llvm/Support/Casting.h"
-#include "llvm/Support/Errc.h"
-#include "llvm/Support/ErrorHandling.h"
-#include "llvm/Support/FileOutputBuffer.h"
-#include "llvm/Support/Host.h"
-#include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/raw_ostream.h"
-#include <functional>
-#include <system_error>
-
-using namespace llvm::MachO;
-using llvm::object::ExportEntry;
-using llvm::file_magic;
-using llvm::object::MachOObjectFile;
-
-namespace lld {
-namespace mach_o {
-namespace normalized {
-
-// Utility to call a lambda expression on each load command.
-static llvm::Error forEachLoadCommand(
- StringRef lcRange, unsigned lcCount, bool isBig, bool is64,
- std::function<bool(uint32_t cmd, uint32_t size, const char *lc)> func) {
- const char* p = lcRange.begin();
- for (unsigned i=0; i < lcCount; ++i) {
- const load_command *lc = reinterpret_cast<const load_command*>(p);
- load_command lcCopy;
- const load_command *slc = lc;
- if (isBig != llvm::sys::IsBigEndianHost) {
- memcpy(&lcCopy, lc, sizeof(load_command));
- swapStruct(lcCopy);
- slc = &lcCopy;
- }
- if ( (p + slc->cmdsize) > lcRange.end() )
- return llvm::make_error<GenericError>("Load command exceeds range");
-
- if (func(slc->cmd, slc->cmdsize, p))
- return llvm::Error::success();
-
- p += slc->cmdsize;
- }
-
- return llvm::Error::success();
-}
-
-static std::error_code appendRelocations(Relocations &relocs, StringRef buffer,
- bool bigEndian,
- uint32_t reloff, uint32_t nreloc) {
- if ((reloff + nreloc*8) > buffer.size())
- return make_error_code(llvm::errc::executable_format_error);
- const any_relocation_info* relocsArray =
- reinterpret_cast<const any_relocation_info*>(buffer.begin()+reloff);
-
- for(uint32_t i=0; i < nreloc; ++i) {
- relocs.push_back(unpackRelocation(relocsArray[i], bigEndian));
- }
- return std::error_code();
-}
-
-static std::error_code
-appendIndirectSymbols(IndirectSymbols &isyms, StringRef buffer, bool isBig,
- uint32_t istOffset, uint32_t istCount,
- uint32_t startIndex, uint32_t count) {
- if ((istOffset + istCount*4) > buffer.size())
- return make_error_code(llvm::errc::executable_format_error);
- if (startIndex+count > istCount)
- return make_error_code(llvm::errc::executable_format_error);
- const uint8_t *indirectSymbolArray = (const uint8_t *)buffer.data();
-
- for(uint32_t i=0; i < count; ++i) {
- isyms.push_back(read32(
- indirectSymbolArray + (startIndex + i) * sizeof(uint32_t), isBig));
- }
- return std::error_code();
-}
-
-
-template <typename T> static T readBigEndian(T t) {
- if (llvm::sys::IsLittleEndianHost)
- llvm::sys::swapByteOrder(t);
- return t;
-}
-
-
-static bool isMachOHeader(const mach_header *mh, bool &is64, bool &isBig) {
- switch (read32(&mh->magic, false)) {
- case llvm::MachO::MH_MAGIC:
- is64 = false;
- isBig = false;
- return true;
- case llvm::MachO::MH_MAGIC_64:
- is64 = true;
- isBig = false;
- return true;
- case llvm::MachO::MH_CIGAM:
- is64 = false;
- isBig = true;
- return true;
- case llvm::MachO::MH_CIGAM_64:
- is64 = true;
- isBig = true;
- return true;
- default:
- return false;
- }
-}
-
-
-bool isThinObjectFile(StringRef path, MachOLinkingContext::Arch &arch) {
- // Try opening and mapping file at path.
- ErrorOr<std::unique_ptr<MemoryBuffer>> b = MemoryBuffer::getFileOrSTDIN(path);
- if (b.getError())
- return false;
-
- // If file length < 32 it is too small to be mach-o object file.
- StringRef fileBuffer = b->get()->getBuffer();
- if (fileBuffer.size() < 32)
- return false;
-
- // If file buffer does not start with MH_MAGIC (and variants), not obj file.
- const mach_header *mh = reinterpret_cast<const mach_header *>(
- fileBuffer.begin());
- bool is64, isBig;
- if (!isMachOHeader(mh, is64, isBig))
- return false;
-
- // If not MH_OBJECT, not object file.
- if (read32(&mh->filetype, isBig) != MH_OBJECT)
- return false;
-
- // Lookup up arch from cpu/subtype pair.
- arch = MachOLinkingContext::archFromCpuType(
- read32(&mh->cputype, isBig),
- read32(&mh->cpusubtype, isBig));
- return true;
-}
-
-bool sliceFromFatFile(MemoryBufferRef mb, MachOLinkingContext::Arch arch,
- uint32_t &offset, uint32_t &size) {
- const char *start = mb.getBufferStart();
- const llvm::MachO::fat_header *fh =
- reinterpret_cast<const llvm::MachO::fat_header *>(start);
- if (readBigEndian(fh->magic) != llvm::MachO::FAT_MAGIC)
- return false;
- uint32_t nfat_arch = readBigEndian(fh->nfat_arch);
- const fat_arch *fstart =
- reinterpret_cast<const fat_arch *>(start + sizeof(fat_header));
- const fat_arch *fend =
- reinterpret_cast<const fat_arch *>(start + sizeof(fat_header) +
- sizeof(fat_arch) * nfat_arch);
- const uint32_t reqCpuType = MachOLinkingContext::cpuTypeFromArch(arch);
- const uint32_t reqCpuSubtype = MachOLinkingContext::cpuSubtypeFromArch(arch);
- for (const fat_arch *fa = fstart; fa < fend; ++fa) {
- if ((readBigEndian(fa->cputype) == reqCpuType) &&
- (readBigEndian(fa->cpusubtype) == reqCpuSubtype)) {
- offset = readBigEndian(fa->offset);
- size = readBigEndian(fa->size);
- if ((offset + size) > mb.getBufferSize())
- return false;
- return true;
- }
- }
- return false;
-}
-
-/// Reads a mach-o file and produces an in-memory normalized view.
-llvm::Expected<std::unique_ptr<NormalizedFile>>
-readBinary(std::unique_ptr<MemoryBuffer> &mb,
- const MachOLinkingContext::Arch arch) {
- // Make empty NormalizedFile.
- std::unique_ptr<NormalizedFile> f(new NormalizedFile());
-
- const char *start = mb->getBufferStart();
- size_t objSize = mb->getBufferSize();
- const mach_header *mh = reinterpret_cast<const mach_header *>(start);
-
- uint32_t sliceOffset;
- uint32_t sliceSize;
- if (sliceFromFatFile(mb->getMemBufferRef(), arch, sliceOffset, sliceSize)) {
- start = &start[sliceOffset];
- objSize = sliceSize;
- mh = reinterpret_cast<const mach_header *>(start);
- }
-
- // Determine endianness and pointer size for mach-o file.
- bool is64, isBig;
- if (!isMachOHeader(mh, is64, isBig))
- return llvm::make_error<GenericError>("File is not a mach-o");
-
- // Endian swap header, if needed.
- mach_header headerCopy;
- const mach_header *smh = mh;
- if (isBig != llvm::sys::IsBigEndianHost) {
- memcpy(&headerCopy, mh, sizeof(mach_header));
- swapStruct(headerCopy);
- smh = &headerCopy;
- }
-
- // Validate head and load commands fit in buffer.
- const uint32_t lcCount = smh->ncmds;
- const char *lcStart =
- start + (is64 ? sizeof(mach_header_64) : sizeof(mach_header));
- StringRef lcRange(lcStart, smh->sizeofcmds);
- if (lcRange.end() > (start + objSize))
- return llvm::make_error<GenericError>("Load commands exceed file size");
-
- // Get architecture from mach_header.
- f->arch = MachOLinkingContext::archFromCpuType(smh->cputype, smh->cpusubtype);
- if (f->arch != arch) {
- return llvm::make_error<GenericError>(
- Twine("file is wrong architecture. Expected "
- "(" + MachOLinkingContext::nameFromArch(arch)
- + ") found ("
- + MachOLinkingContext::nameFromArch(f->arch)
- + ")" ));
- }
- // Copy file type and flags
- f->fileType = HeaderFileType(smh->filetype);
- f->flags = smh->flags;
-
-
- // Pre-scan load commands looking for indirect symbol table.
- uint32_t indirectSymbolTableOffset = 0;
- uint32_t indirectSymbolTableCount = 0;
- auto ec = forEachLoadCommand(lcRange, lcCount, isBig, is64,
- [&](uint32_t cmd, uint32_t size,
- const char *lc) -> bool {
- if (cmd == LC_DYSYMTAB) {
- const dysymtab_command *d = reinterpret_cast<const dysymtab_command*>(lc);
- indirectSymbolTableOffset = read32(&d->indirectsymoff, isBig);
- indirectSymbolTableCount = read32(&d->nindirectsyms, isBig);
- return true;
- }
- return false;
- });
- if (ec)
- return std::move(ec);
-
- // Walk load commands looking for segments/sections and the symbol table.
- const data_in_code_entry *dataInCode = nullptr;
- const dyld_info_command *dyldInfo = nullptr;
- uint32_t dataInCodeSize = 0;
- ec = forEachLoadCommand(lcRange, lcCount, isBig, is64,
- [&] (uint32_t cmd, uint32_t size, const char* lc) -> bool {
- switch(cmd) {
- case LC_SEGMENT_64:
- if (is64) {
- const segment_command_64 *seg =
- reinterpret_cast<const segment_command_64*>(lc);
- const unsigned sectionCount = read32(&seg->nsects, isBig);
- const section_64 *sects = reinterpret_cast<const section_64*>
- (lc + sizeof(segment_command_64));
- const unsigned lcSize = sizeof(segment_command_64)
- + sectionCount*sizeof(section_64);
- // Verify sections don't extend beyond end of segment load command.
- if (lcSize > size)
- return true;
- for (unsigned i=0; i < sectionCount; ++i) {
- const section_64 *sect = §s[i];
- Section section;
- section.segmentName = getString16(sect->segname);
- section.sectionName = getString16(sect->sectname);
- section.type = (SectionType)(read32(§->flags, isBig) &
- SECTION_TYPE);
- section.attributes = read32(§->flags, isBig) & SECTION_ATTRIBUTES;
- section.alignment = 1 << read32(§->align, isBig);
- section.address = read64(§->addr, isBig);
- const uint8_t *content =
- (const uint8_t *)start + read32(§->offset, isBig);
- size_t contentSize = read64(§->size, isBig);
- // Note: this assign() is copying the content bytes. Ideally,
- // we can use a custom allocator for vector to avoid the copy.
- section.content = llvm::makeArrayRef(content, contentSize);
- appendRelocations(section.relocations, mb->getBuffer(), isBig,
- read32(§->reloff, isBig),
- read32(§->nreloc, isBig));
- if (section.type == S_NON_LAZY_SYMBOL_POINTERS) {
- appendIndirectSymbols(section.indirectSymbols, mb->getBuffer(),
- isBig,
- indirectSymbolTableOffset,
- indirectSymbolTableCount,
- read32(§->reserved1, isBig),
- contentSize/4);
- }
- f->sections.push_back(section);
- }
- }
- break;
- case LC_SEGMENT:
- if (!is64) {
- const segment_command *seg =
- reinterpret_cast<const segment_command*>(lc);
- const unsigned sectionCount = read32(&seg->nsects, isBig);
- const section *sects = reinterpret_cast<const section*>
- (lc + sizeof(segment_command));
- const unsigned lcSize = sizeof(segment_command)
- + sectionCount*sizeof(section);
- // Verify sections don't extend beyond end of segment load command.
- if (lcSize > size)
- return true;
- for (unsigned i=0; i < sectionCount; ++i) {
- const section *sect = §s[i];
- Section section;
- section.segmentName = getString16(sect->segname);
- section.sectionName = getString16(sect->sectname);
- section.type = (SectionType)(read32(§->flags, isBig) &
- SECTION_TYPE);
- section.attributes =
- read32((const uint8_t *)§->flags, isBig) & SECTION_ATTRIBUTES;
- section.alignment = 1 << read32(§->align, isBig);
- section.address = read32(§->addr, isBig);
- const uint8_t *content =
- (const uint8_t *)start + read32(§->offset, isBig);
- size_t contentSize = read32(§->size, isBig);
- // Note: this assign() is copying the content bytes. Ideally,
- // we can use a custom allocator for vector to avoid the copy.
- section.content = llvm::makeArrayRef(content, contentSize);
- appendRelocations(section.relocations, mb->getBuffer(), isBig,
- read32(§->reloff, isBig),
- read32(§->nreloc, isBig));
- if (section.type == S_NON_LAZY_SYMBOL_POINTERS) {
- appendIndirectSymbols(
- section.indirectSymbols, mb->getBuffer(), isBig,
- indirectSymbolTableOffset, indirectSymbolTableCount,
- read32(§->reserved1, isBig), contentSize / 4);
- }
- f->sections.push_back(section);
- }
- }
- break;
- case LC_SYMTAB: {
- const symtab_command *st = reinterpret_cast<const symtab_command*>(lc);
- const char *strings = start + read32(&st->stroff, isBig);
- const uint32_t strSize = read32(&st->strsize, isBig);
- // Validate string pool and symbol table all in buffer.
- if (read32((const uint8_t *)&st->stroff, isBig) +
- read32((const uint8_t *)&st->strsize, isBig) >
- objSize)
- return true;
- if (is64) {
- const uint32_t symOffset = read32(&st->symoff, isBig);
- const uint32_t symCount = read32(&st->nsyms, isBig);
- if ( symOffset+(symCount*sizeof(nlist_64)) > objSize)
- return true;
- const nlist_64 *symbols =
- reinterpret_cast<const nlist_64 *>(start + symOffset);
- // Convert each nlist_64 to a lld::mach_o::normalized::Symbol.
- for(uint32_t i=0; i < symCount; ++i) {
- nlist_64 tempSym;
- memcpy(&tempSym, &symbols[i], sizeof(nlist_64));
- const nlist_64 *sin = &tempSym;
- if (isBig != llvm::sys::IsBigEndianHost)
- swapStruct(tempSym);
- Symbol sout;
- if (sin->n_strx > strSize)
- return true;
- sout.name = &strings[sin->n_strx];
- sout.type = static_cast<NListType>(sin->n_type & (N_STAB|N_TYPE));
- sout.scope = (sin->n_type & (N_PEXT|N_EXT));
- sout.sect = sin->n_sect;
- sout.desc = sin->n_desc;
- sout.value = sin->n_value;
- if (sin->n_type & N_STAB)
- f->stabsSymbols.push_back(sout);
- else if (sout.type == N_UNDF)
- f->undefinedSymbols.push_back(sout);
- else if (sin->n_type & N_EXT)
- f->globalSymbols.push_back(sout);
- else
- f->localSymbols.push_back(sout);
- }
- } else {
- const uint32_t symOffset = read32(&st->symoff, isBig);
- const uint32_t symCount = read32(&st->nsyms, isBig);
- if ( symOffset+(symCount*sizeof(nlist)) > objSize)
- return true;
- const nlist *symbols =
- reinterpret_cast<const nlist *>(start + symOffset);
- // Convert each nlist to a lld::mach_o::normalized::Symbol.
- for(uint32_t i=0; i < symCount; ++i) {
- const nlist *sin = &symbols[i];
- nlist tempSym;
- if (isBig != llvm::sys::IsBigEndianHost) {
- tempSym = *sin; swapStruct(tempSym); sin = &tempSym;
- }
- Symbol sout;
- if (sin->n_strx > strSize)
- return true;
- sout.name = &strings[sin->n_strx];
- sout.type = (NListType)(sin->n_type & N_TYPE);
- sout.scope = (sin->n_type & (N_PEXT|N_EXT));
- sout.sect = sin->n_sect;
- sout.desc = sin->n_desc;
- sout.value = sin->n_value;
- if (sout.type == N_UNDF)
- f->undefinedSymbols.push_back(sout);
- else if (sout.scope == (SymbolScope)N_EXT)
- f->globalSymbols.push_back(sout);
- else if (sin->n_type & N_STAB)
- f->stabsSymbols.push_back(sout);
- else
- f->localSymbols.push_back(sout);
- }
- }
- }
- break;
- case LC_ID_DYLIB: {
- const dylib_command *dl = reinterpret_cast<const dylib_command*>(lc);
- f->installName = lc + read32(&dl->dylib.name, isBig);
- f->currentVersion = read32(&dl->dylib.current_version, isBig);
- f->compatVersion = read32(&dl->dylib.compatibility_version, isBig);
- }
- break;
- case LC_DATA_IN_CODE: {
- const linkedit_data_command *ldc =
- reinterpret_cast<const linkedit_data_command*>(lc);
- dataInCode = reinterpret_cast<const data_in_code_entry *>(
- start + read32(&ldc->dataoff, isBig));
- dataInCodeSize = read32(&ldc->datasize, isBig);
- }
- break;
- case LC_LOAD_DYLIB:
- case LC_LOAD_WEAK_DYLIB:
- case LC_REEXPORT_DYLIB:
- case LC_LOAD_UPWARD_DYLIB: {
- const dylib_command *dl = reinterpret_cast<const dylib_command*>(lc);
- DependentDylib entry;
- entry.path = lc + read32(&dl->dylib.name, isBig);
- entry.kind = LoadCommandType(cmd);
- entry.compatVersion = read32(&dl->dylib.compatibility_version, isBig);
- entry.currentVersion = read32(&dl->dylib.current_version, isBig);
- f->dependentDylibs.push_back(entry);
- }
- break;
- case LC_RPATH: {
- const rpath_command *rpc = reinterpret_cast<const rpath_command *>(lc);
- f->rpaths.push_back(lc + read32(&rpc->path, isBig));
- }
- break;
- case LC_DYLD_INFO:
- case LC_DYLD_INFO_ONLY:
- dyldInfo = reinterpret_cast<const dyld_info_command*>(lc);
- break;
- case LC_VERSION_MIN_MACOSX:
- case LC_VERSION_MIN_IPHONEOS:
- case LC_VERSION_MIN_WATCHOS:
- case LC_VERSION_MIN_TVOS:
- // If we are emitting an object file, then we may take the load command
- // kind from these commands and pass it on to the output
- // file.
- f->minOSVersionKind = (LoadCommandType)cmd;
- break;
- }
- return false;
- });
- if (ec)
- return std::move(ec);
-
- if (dataInCode) {
- // Convert on-disk data_in_code_entry array to DataInCode vector.
- for (unsigned i=0; i < dataInCodeSize/sizeof(data_in_code_entry); ++i) {
- DataInCode entry;
- entry.offset = read32(&dataInCode[i].offset, isBig);
- entry.length = read16(&dataInCode[i].length, isBig);
- entry.kind =
- (DataRegionType)read16((const uint8_t *)&dataInCode[i].kind, isBig);
- f->dataInCode.push_back(entry);
- }
- }
-
- if (dyldInfo) {
- // If any exports, extract and add to normalized exportInfo vector.
- if (dyldInfo->export_size) {
- const uint8_t *trieStart = reinterpret_cast<const uint8_t *>(
- start + read32(&dyldInfo->export_off, isBig));
- ArrayRef<uint8_t> trie(trieStart, read32(&dyldInfo->export_size, isBig));
- Error Err = Error::success();
- for (const ExportEntry &trieExport : MachOObjectFile::exports(Err, trie)) {
- Export normExport;
- normExport.name = trieExport.name().copy(f->ownedAllocations);
- normExport.offset = trieExport.address();
- normExport.kind = ExportSymbolKind(trieExport.flags() & EXPORT_SYMBOL_FLAGS_KIND_MASK);
- normExport.flags = trieExport.flags() & ~EXPORT_SYMBOL_FLAGS_KIND_MASK;
- normExport.otherOffset = trieExport.other();
- if (!trieExport.otherName().empty())
- normExport.otherName = trieExport.otherName().copy(f->ownedAllocations);
- f->exportInfo.push_back(normExport);
- }
- if (Err)
- return std::move(Err);
- }
- }
-
- return std::move(f);
-}
-
-class MachOObjectReader : public Reader {
-public:
- MachOObjectReader(MachOLinkingContext &ctx) : _ctx(ctx) {}
-
- bool canParse(file_magic magic, MemoryBufferRef mb) const override {
- return (magic == file_magic::macho_object && mb.getBufferSize() > 32);
- }
-
- ErrorOr<std::unique_ptr<File>>
- loadFile(std::unique_ptr<MemoryBuffer> mb,
- const Registry ®istry) const override {
- std::unique_ptr<File> ret =
- std::make_unique<MachOFile>(std::move(mb), &_ctx);
- return std::move(ret);
- }
-
-private:
- MachOLinkingContext &_ctx;
-};
-
-class MachODylibReader : public Reader {
-public:
- MachODylibReader(MachOLinkingContext &ctx) : _ctx(ctx) {}
-
- bool canParse(file_magic magic, MemoryBufferRef mb) const override {
- switch (magic) {
- case file_magic::macho_dynamically_linked_shared_lib:
- case file_magic::macho_dynamically_linked_shared_lib_stub:
- return mb.getBufferSize() > 32;
- default:
- return false;
- }
- }
-
- ErrorOr<std::unique_ptr<File>>
- loadFile(std::unique_ptr<MemoryBuffer> mb,
- const Registry ®istry) const override {
- std::unique_ptr<File> ret =
- std::make_unique<MachODylibFile>(std::move(mb), &_ctx);
- return std::move(ret);
- }
-
-private:
- MachOLinkingContext &_ctx;
-};
-
-class MachOTAPIReader : public Reader {
-public:
- MachOTAPIReader(MachOLinkingContext &ctx) : _ctx(ctx) {}
-
- bool canParse(file_magic magic, MemoryBufferRef mb) const override {
- return magic == file_magic::tapi_file;
- }
-
- ErrorOr<std::unique_ptr<File>>
- loadFile(std::unique_ptr<MemoryBuffer> mb,
- const Registry ®istry) const override {
- std::unique_ptr<File> ret =
- std::make_unique<TAPIFile>(std::move(mb), &_ctx);
- return std::move(ret);
- }
-
-private:
- MachOLinkingContext &_ctx;
-};
-
-} // namespace normalized
-} // namespace mach_o
-
-void Registry::addSupportMachOObjects(MachOLinkingContext &ctx) {
- MachOLinkingContext::Arch arch = ctx.arch();
- add(std::unique_ptr<Reader>(new mach_o::normalized::MachOObjectReader(ctx)));
- add(std::unique_ptr<Reader>(new mach_o::normalized::MachODylibReader(ctx)));
- add(std::unique_ptr<Reader>(new mach_o::normalized::MachOTAPIReader(ctx)));
- addKindTable(Reference::KindNamespace::mach_o, ctx.archHandler().kindArch(),
- ctx.archHandler().kindStrings());
- add(std::unique_ptr<YamlIOTaggedDocumentHandler>(
- new mach_o::MachOYamlIOTaggedDocumentHandler(arch)));
-}
-
-
-} // namespace lld
+++ /dev/null
-//===- lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h ------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_READER_WRITER_MACHO_NORMALIZED_FILE_BINARY_UTILS_H
-#define LLD_READER_WRITER_MACHO_NORMALIZED_FILE_BINARY_UTILS_H
-
-#include "MachONormalizedFile.h"
-#include "lld/Common/LLVM.h"
-#include "lld/Core/Error.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/BinaryFormat/MachO.h"
-#include "llvm/Support/Casting.h"
-#include "llvm/Support/Endian.h"
-#include "llvm/Support/ErrorHandling.h"
-#include "llvm/Support/Host.h"
-#include "llvm/Support/LEB128.h"
-#include <system_error>
-
-namespace lld {
-namespace mach_o {
-namespace normalized {
-
-class ByteBuffer {
-public:
- ByteBuffer() : _ostream(_bytes) { }
-
- void append_byte(uint8_t b) {
- _ostream << b;
- }
- void append_uleb128(uint64_t value) {
- llvm::encodeULEB128(value, _ostream);
- }
- void append_uleb128Fixed(uint64_t value, unsigned byteCount) {
- unsigned min = llvm::getULEB128Size(value);
- assert(min <= byteCount);
- unsigned pad = byteCount - min;
- llvm::encodeULEB128(value, _ostream, pad);
- }
- void append_sleb128(int64_t value) {
- llvm::encodeSLEB128(value, _ostream);
- }
- void append_string(StringRef str) {
- _ostream << str;
- append_byte(0);
- }
- void align(unsigned alignment) {
- while ( (_ostream.tell() % alignment) != 0 )
- append_byte(0);
- }
- size_t size() {
- return _ostream.tell();
- }
- const uint8_t *bytes() {
- return reinterpret_cast<const uint8_t*>(_ostream.str().data());
- }
-
-private:
- SmallVector<char, 128> _bytes;
- // Stream ivar must be after SmallVector ivar to construct properly.
- llvm::raw_svector_ostream _ostream;
-};
-
-using namespace llvm::support::endian;
-using llvm::sys::getSwappedBytes;
-
-template<typename T>
-static inline uint16_t read16(const T *loc, bool isBig) {
- assert((uint64_t)loc % alignof(T) == 0 && "invalid pointer alignment");
- return isBig ? read16be(loc) : read16le(loc);
-}
-
-template<typename T>
-static inline uint32_t read32(const T *loc, bool isBig) {
- assert((uint64_t)loc % alignof(T) == 0 && "invalid pointer alignment");
- return isBig ? read32be(loc) : read32le(loc);
-}
-
-template<typename T>
-static inline uint64_t read64(const T *loc, bool isBig) {
- assert((uint64_t)loc % alignof(T) == 0 && "invalid pointer alignment");
- return isBig ? read64be(loc) : read64le(loc);
-}
-
-inline void write16(uint8_t *loc, uint16_t value, bool isBig) {
- if (isBig)
- write16be(loc, value);
- else
- write16le(loc, value);
-}
-
-inline void write32(uint8_t *loc, uint32_t value, bool isBig) {
- if (isBig)
- write32be(loc, value);
- else
- write32le(loc, value);
-}
-
-inline void write64(uint8_t *loc, uint64_t value, bool isBig) {
- if (isBig)
- write64be(loc, value);
- else
- write64le(loc, value);
-}
-
-inline uint32_t
-bitFieldExtract(uint32_t value, bool isBigEndianBigField, uint8_t firstBit,
- uint8_t bitCount) {
- const uint32_t mask = ((1<<bitCount)-1);
- const uint8_t shift = isBigEndianBigField ? (32-firstBit-bitCount) : firstBit;
- return (value >> shift) & mask;
-}
-
-inline void
-bitFieldSet(uint32_t &bits, bool isBigEndianBigField, uint32_t newBits,
- uint8_t firstBit, uint8_t bitCount) {
- const uint32_t mask = ((1<<bitCount)-1);
- assert((newBits & mask) == newBits);
- const uint8_t shift = isBigEndianBigField ? (32-firstBit-bitCount) : firstBit;
- bits &= ~(mask << shift);
- bits |= (newBits << shift);
-}
-
-inline Relocation unpackRelocation(const llvm::MachO::any_relocation_info &r,
- bool isBigEndian) {
- uint32_t r0 = read32(&r.r_word0, isBigEndian);
- uint32_t r1 = read32(&r.r_word1, isBigEndian);
-
- Relocation result;
- if (r0 & llvm::MachO::R_SCATTERED) {
- // scattered relocation record always laid out like big endian bit field
- result.offset = bitFieldExtract(r0, true, 8, 24);
- result.scattered = true;
- result.type = (RelocationInfoType)
- bitFieldExtract(r0, true, 4, 4);
- result.length = bitFieldExtract(r0, true, 2, 2);
- result.pcRel = bitFieldExtract(r0, true, 1, 1);
- result.isExtern = false;
- result.value = r1;
- result.symbol = 0;
- } else {
- result.offset = r0;
- result.scattered = false;
- result.type = (RelocationInfoType)
- bitFieldExtract(r1, isBigEndian, 28, 4);
- result.length = bitFieldExtract(r1, isBigEndian, 25, 2);
- result.pcRel = bitFieldExtract(r1, isBigEndian, 24, 1);
- result.isExtern = bitFieldExtract(r1, isBigEndian, 27, 1);
- result.value = 0;
- result.symbol = bitFieldExtract(r1, isBigEndian, 0, 24);
- }
- return result;
-}
-
-
-inline llvm::MachO::any_relocation_info
-packRelocation(const Relocation &r, bool swap, bool isBigEndian) {
- uint32_t r0 = 0;
- uint32_t r1 = 0;
-
- if (r.scattered) {
- r1 = r.value;
- bitFieldSet(r0, true, r.offset, 8, 24);
- bitFieldSet(r0, true, r.type, 4, 4);
- bitFieldSet(r0, true, r.length, 2, 2);
- bitFieldSet(r0, true, r.pcRel, 1, 1);
- bitFieldSet(r0, true, r.scattered, 0, 1); // R_SCATTERED
- } else {
- r0 = r.offset;
- bitFieldSet(r1, isBigEndian, r.type, 28, 4);
- bitFieldSet(r1, isBigEndian, r.isExtern, 27, 1);
- bitFieldSet(r1, isBigEndian, r.length, 25, 2);
- bitFieldSet(r1, isBigEndian, r.pcRel, 24, 1);
- bitFieldSet(r1, isBigEndian, r.symbol, 0, 24);
- }
-
- llvm::MachO::any_relocation_info result;
- result.r_word0 = swap ? getSwappedBytes(r0) : r0;
- result.r_word1 = swap ? getSwappedBytes(r1) : r1;
- return result;
-}
-
-inline StringRef getString16(const char s[16]) {
- // The StringRef(const char *) constructor passes the const char * to
- // strlen(), so we can't use this constructor here, because if there is no
- // null terminator in s, then strlen() will read past the end of the array.
- return StringRef(s, strnlen(s, 16));
-}
-
-inline void setString16(StringRef str, char s[16]) {
- memset(s, 0, 16);
- memcpy(s, str.begin(), (str.size() > 16) ? 16: str.size());
-}
-
-// Implemented in normalizedToAtoms() and used by normalizedFromAtoms() so
-// that the same table can be used to map mach-o sections to and from
-// DefinedAtom::ContentType.
-void relocatableSectionInfoForContentType(DefinedAtom::ContentType atomType,
- StringRef &segmentName,
- StringRef §ionName,
- SectionType §ionType,
- SectionAttr §ionAttrs,
- bool &relocsToDefinedCanBeImplicit);
-
-} // namespace normalized
-} // namespace mach_o
-} // namespace lld
-
-#endif // LLD_READER_WRITER_MACHO_NORMALIZED_FILE_BINARY_UTILS_H
+++ /dev/null
-//===- lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp ---------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-///
-/// \file For mach-o object files, this implementation converts normalized
-/// mach-o in memory to mach-o binary on disk.
-///
-/// +---------------+
-/// | binary mach-o |
-/// +---------------+
-/// ^
-/// |
-/// |
-/// +------------+
-/// | normalized |
-/// +------------+
-
-#include "MachONormalizedFile.h"
-#include "MachONormalizedFileBinaryUtils.h"
-#include "lld/Common/LLVM.h"
-#include "lld/Core/Error.h"
-#include "llvm/ADT/SmallString.h"
-#include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/ADT/ilist.h"
-#include "llvm/ADT/ilist_node.h"
-#include "llvm/BinaryFormat/MachO.h"
-#include "llvm/Support/Casting.h"
-#include "llvm/Support/Debug.h"
-#include "llvm/Support/Errc.h"
-#include "llvm/Support/ErrorHandling.h"
-#include "llvm/Support/FileOutputBuffer.h"
-#include "llvm/Support/Format.h"
-#include "llvm/Support/Host.h"
-#include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/raw_ostream.h"
-#include <functional>
-#include <list>
-#include <map>
-#include <system_error>
-
-using namespace llvm::MachO;
-
-namespace lld {
-namespace mach_o {
-namespace normalized {
-
-struct TrieNode; // Forward declaration.
-
-struct TrieEdge : public llvm::ilist_node<TrieEdge> {
- TrieEdge(StringRef s, TrieNode *node) : _subString(s), _child(node) {}
-
- StringRef _subString;
- struct TrieNode *_child;
-};
-
-} // namespace normalized
-} // namespace mach_o
-} // namespace lld
-
-
-namespace llvm {
-using lld::mach_o::normalized::TrieEdge;
-template <>
-struct ilist_alloc_traits<TrieEdge> : ilist_noalloc_traits<TrieEdge> {};
-} // namespace llvm
-
-
-namespace lld {
-namespace mach_o {
-namespace normalized {
-
-struct TrieNode {
- typedef llvm::ilist<TrieEdge> TrieEdgeList;
-
- TrieNode(StringRef s)
- : _cummulativeString(s), _address(0), _flags(0), _other(0),
- _trieOffset(0), _hasExportInfo(false) {}
- ~TrieNode() = default;
-
- void addSymbol(const Export &entry, BumpPtrAllocator &allocator,
- std::vector<TrieNode *> &allNodes);
-
- void addOrderedNodes(const Export &entry,
- std::vector<TrieNode *> &allNodes);
- bool updateOffset(uint32_t &offset);
- void appendToByteBuffer(ByteBuffer &out);
-
-private:
- StringRef _cummulativeString;
- TrieEdgeList _children;
- uint64_t _address;
- uint64_t _flags;
- uint64_t _other;
- StringRef _importedName;
- uint32_t _trieOffset;
- bool _hasExportInfo;
- bool _ordered = false;
-};
-
-/// Utility class for writing a mach-o binary file given an in-memory
-/// normalized file.
-class MachOFileLayout {
-public:
- /// All layout computation is done in the constructor.
- MachOFileLayout(const NormalizedFile &file, bool alwaysIncludeFunctionStarts);
-
- /// Returns the final file size as computed in the constructor.
- size_t size() const;
-
- // Returns size of the mach_header and load commands.
- size_t headerAndLoadCommandsSize() const;
-
- /// Writes the normalized file as a binary mach-o file to the specified
- /// path. This does not have a stream interface because the generated
- /// file may need the 'x' bit set.
- llvm::Error writeBinary(StringRef path);
-
-private:
- uint32_t loadCommandsSize(uint32_t &count,
- bool alwaysIncludeFunctionStarts);
- void buildFileOffsets();
- void writeMachHeader();
- llvm::Error writeLoadCommands();
- void writeSectionContent();
- void writeRelocations();
- void writeSymbolTable();
- void writeRebaseInfo();
- void writeBindingInfo();
- void writeLazyBindingInfo();
- void writeExportInfo();
- void writeFunctionStartsInfo();
- void writeDataInCodeInfo();
- void writeLinkEditContent();
- void buildLinkEditInfo();
- void buildRebaseInfo();
- void buildBindInfo();
- void buildLazyBindInfo();
- void buildExportTrie();
- void computeFunctionStartsSize();
- void computeDataInCodeSize();
- void computeSymbolTableSizes();
- void buildSectionRelocations();
- void appendSymbols(const std::vector<Symbol> &symbols,
- uint32_t &symOffset, uint32_t &strOffset);
- uint32_t indirectSymbolIndex(const Section §, uint32_t &index);
- uint32_t indirectSymbolElementSize(const Section §);
-
- // For use as template parameter to load command methods.
- struct MachO64Trait {
- typedef llvm::MachO::segment_command_64 command;
- typedef llvm::MachO::section_64 section;
- enum { LC = llvm::MachO::LC_SEGMENT_64 };
- };
-
- // For use as template parameter to load command methods.
- struct MachO32Trait {
- typedef llvm::MachO::segment_command command;
- typedef llvm::MachO::section section;
- enum { LC = llvm::MachO::LC_SEGMENT };
- };
-
- template <typename T>
- llvm::Error writeSingleSegmentLoadCommand(uint8_t *&lc);
- template <typename T> llvm::Error writeSegmentLoadCommands(uint8_t *&lc);
-
- uint32_t pointerAlign(uint32_t value);
- static StringRef dyldPath();
-
- struct SegExtraInfo {
- uint32_t fileOffset;
- uint32_t fileSize;
- std::vector<const Section*> sections;
- };
- typedef std::map<const Segment*, SegExtraInfo> SegMap;
- struct SectionExtraInfo {
- uint32_t fileOffset;
- };
- typedef std::map<const Section*, SectionExtraInfo> SectionMap;
-
- const NormalizedFile &_file;
- std::error_code _ec;
- uint8_t *_buffer;
- const bool _is64;
- const bool _swap;
- const bool _bigEndianArch;
- uint64_t _seg1addr;
- uint32_t _startOfLoadCommands;
- uint32_t _countOfLoadCommands;
- uint32_t _endOfLoadCommands;
- uint32_t _startOfRelocations;
- uint32_t _startOfFunctionStarts;
- uint32_t _startOfDataInCode;
- uint32_t _startOfSymbols;
- uint32_t _startOfIndirectSymbols;
- uint32_t _startOfSymbolStrings;
- uint32_t _endOfSymbolStrings;
- uint32_t _symbolTableLocalsStartIndex;
- uint32_t _symbolTableGlobalsStartIndex;
- uint32_t _symbolTableUndefinesStartIndex;
- uint32_t _symbolStringPoolSize;
- uint32_t _symbolTableSize;
- uint32_t _functionStartsSize;
- uint32_t _dataInCodeSize;
- uint32_t _indirectSymbolTableCount;
- // Used in object file creation only
- uint32_t _startOfSectionsContent;
- uint32_t _endOfSectionsContent;
- // Used in final linked image only
- uint32_t _startOfLinkEdit;
- uint32_t _startOfRebaseInfo;
- uint32_t _endOfRebaseInfo;
- uint32_t _startOfBindingInfo;
- uint32_t _endOfBindingInfo;
- uint32_t _startOfLazyBindingInfo;
- uint32_t _endOfLazyBindingInfo;
- uint32_t _startOfExportTrie;
- uint32_t _endOfExportTrie;
- uint32_t _endOfLinkEdit;
- uint64_t _addressOfLinkEdit;
- SegMap _segInfo;
- SectionMap _sectInfo;
- ByteBuffer _rebaseInfo;
- ByteBuffer _bindingInfo;
- ByteBuffer _lazyBindingInfo;
- ByteBuffer _weakBindingInfo;
- ByteBuffer _exportTrie;
-};
-
-size_t headerAndLoadCommandsSize(const NormalizedFile &file,
- bool includeFunctionStarts) {
- MachOFileLayout layout(file, includeFunctionStarts);
- return layout.headerAndLoadCommandsSize();
-}
-
-StringRef MachOFileLayout::dyldPath() {
- return "/usr/lib/dyld";
-}
-
-uint32_t MachOFileLayout::pointerAlign(uint32_t value) {
- return llvm::alignTo(value, _is64 ? 8 : 4);
-}
-
-
-size_t MachOFileLayout::headerAndLoadCommandsSize() const {
- return _endOfLoadCommands;
-}
-
-MachOFileLayout::MachOFileLayout(const NormalizedFile &file,
- bool alwaysIncludeFunctionStarts)
- : _file(file),
- _is64(MachOLinkingContext::is64Bit(file.arch)),
- _swap(!MachOLinkingContext::isHostEndian(file.arch)),
- _bigEndianArch(MachOLinkingContext::isBigEndian(file.arch)),
- _seg1addr(INT64_MAX) {
- _startOfLoadCommands = _is64 ? sizeof(mach_header_64) : sizeof(mach_header);
- const size_t segCommandBaseSize =
- (_is64 ? sizeof(segment_command_64) : sizeof(segment_command));
- const size_t sectsSize = (_is64 ? sizeof(section_64) : sizeof(section));
- if (file.fileType == llvm::MachO::MH_OBJECT) {
- // object files have just one segment load command containing all sections
- _endOfLoadCommands = _startOfLoadCommands
- + segCommandBaseSize
- + file.sections.size() * sectsSize
- + sizeof(symtab_command);
- _countOfLoadCommands = 2;
- if (file.hasMinVersionLoadCommand) {
- _endOfLoadCommands += sizeof(version_min_command);
- _countOfLoadCommands++;
- }
- if (!_file.functionStarts.empty() || alwaysIncludeFunctionStarts) {
- _endOfLoadCommands += sizeof(linkedit_data_command);
- _countOfLoadCommands++;
- }
- if (_file.generateDataInCodeLoadCommand) {
- _endOfLoadCommands += sizeof(linkedit_data_command);
- _countOfLoadCommands++;
- }
- // Assign file offsets to each section.
- _startOfSectionsContent = _endOfLoadCommands;
- unsigned relocCount = 0;
- uint64_t offset = _startOfSectionsContent;
- for (const Section § : file.sections) {
- if (isZeroFillSection(sect.type))
- _sectInfo[§].fileOffset = 0;
- else {
- offset = llvm::alignTo(offset, sect.alignment);
- _sectInfo[§].fileOffset = offset;
- offset += sect.content.size();
- }
- relocCount += sect.relocations.size();
- }
- _endOfSectionsContent = offset;
-
- computeSymbolTableSizes();
- computeFunctionStartsSize();
- computeDataInCodeSize();
-
- // Align start of relocations.
- _startOfRelocations = pointerAlign(_endOfSectionsContent);
- _startOfFunctionStarts = _startOfRelocations + relocCount * 8;
- _startOfDataInCode = _startOfFunctionStarts + _functionStartsSize;
- _startOfSymbols = _startOfDataInCode + _dataInCodeSize;
- // Add Indirect symbol table.
- _startOfIndirectSymbols = _startOfSymbols + _symbolTableSize;
- // Align start of symbol table and symbol strings.
- _startOfSymbolStrings = _startOfIndirectSymbols
- + pointerAlign(_indirectSymbolTableCount * sizeof(uint32_t));
- _endOfSymbolStrings = _startOfSymbolStrings
- + pointerAlign(_symbolStringPoolSize);
- _endOfLinkEdit = _endOfSymbolStrings;
- DEBUG_WITH_TYPE("MachOFileLayout",
- llvm::dbgs() << "MachOFileLayout()\n"
- << " startOfLoadCommands=" << _startOfLoadCommands << "\n"
- << " countOfLoadCommands=" << _countOfLoadCommands << "\n"
- << " endOfLoadCommands=" << _endOfLoadCommands << "\n"
- << " startOfRelocations=" << _startOfRelocations << "\n"
- << " startOfSymbols=" << _startOfSymbols << "\n"
- << " startOfSymbolStrings=" << _startOfSymbolStrings << "\n"
- << " endOfSymbolStrings=" << _endOfSymbolStrings << "\n"
- << " startOfSectionsContent=" << _startOfSectionsContent << "\n"
- << " endOfSectionsContent=" << _endOfSectionsContent << "\n");
- } else {
- // Final linked images have one load command per segment.
- _endOfLoadCommands = _startOfLoadCommands
- + loadCommandsSize(_countOfLoadCommands,
- alwaysIncludeFunctionStarts);
-
- // Assign section file offsets.
- buildFileOffsets();
- buildLinkEditInfo();
-
- // LINKEDIT of final linked images has in order:
- // rebase info, binding info, lazy binding info, weak binding info,
- // data-in-code, symbol table, indirect symbol table, symbol table strings.
- _startOfRebaseInfo = _startOfLinkEdit;
- _endOfRebaseInfo = _startOfRebaseInfo + _rebaseInfo.size();
- _startOfBindingInfo = _endOfRebaseInfo;
- _endOfBindingInfo = _startOfBindingInfo + _bindingInfo.size();
- _startOfLazyBindingInfo = _endOfBindingInfo;
- _endOfLazyBindingInfo = _startOfLazyBindingInfo + _lazyBindingInfo.size();
- _startOfExportTrie = _endOfLazyBindingInfo;
- _endOfExportTrie = _startOfExportTrie + _exportTrie.size();
- _startOfFunctionStarts = _endOfExportTrie;
- _startOfDataInCode = _startOfFunctionStarts + _functionStartsSize;
- _startOfSymbols = _startOfDataInCode + _dataInCodeSize;
- _startOfIndirectSymbols = _startOfSymbols + _symbolTableSize;
- _startOfSymbolStrings = _startOfIndirectSymbols
- + pointerAlign(_indirectSymbolTableCount * sizeof(uint32_t));
- _endOfSymbolStrings = _startOfSymbolStrings
- + pointerAlign(_symbolStringPoolSize);
- _endOfLinkEdit = _endOfSymbolStrings;
- DEBUG_WITH_TYPE("MachOFileLayout",
- llvm::dbgs() << "MachOFileLayout()\n"
- << " startOfLoadCommands=" << _startOfLoadCommands << "\n"
- << " countOfLoadCommands=" << _countOfLoadCommands << "\n"
- << " endOfLoadCommands=" << _endOfLoadCommands << "\n"
- << " startOfLinkEdit=" << _startOfLinkEdit << "\n"
- << " startOfRebaseInfo=" << _startOfRebaseInfo << "\n"
- << " endOfRebaseInfo=" << _endOfRebaseInfo << "\n"
- << " startOfBindingInfo=" << _startOfBindingInfo << "\n"
- << " endOfBindingInfo=" << _endOfBindingInfo << "\n"
- << " startOfLazyBindingInfo=" << _startOfLazyBindingInfo << "\n"
- << " endOfLazyBindingInfo=" << _endOfLazyBindingInfo << "\n"
- << " startOfExportTrie=" << _startOfExportTrie << "\n"
- << " endOfExportTrie=" << _endOfExportTrie << "\n"
- << " startOfFunctionStarts=" << _startOfFunctionStarts << "\n"
- << " startOfDataInCode=" << _startOfDataInCode << "\n"
- << " startOfSymbols=" << _startOfSymbols << "\n"
- << " startOfSymbolStrings=" << _startOfSymbolStrings << "\n"
- << " endOfSymbolStrings=" << _endOfSymbolStrings << "\n"
- << " addressOfLinkEdit=" << _addressOfLinkEdit << "\n");
- }
-}
-
-uint32_t MachOFileLayout::loadCommandsSize(uint32_t &count,
- bool alwaysIncludeFunctionStarts) {
- uint32_t size = 0;
- count = 0;
-
- const size_t segCommandSize =
- (_is64 ? sizeof(segment_command_64) : sizeof(segment_command));
- const size_t sectionSize = (_is64 ? sizeof(section_64) : sizeof(section));
-
- // Add LC_SEGMENT for each segment.
- size += _file.segments.size() * segCommandSize;
- count += _file.segments.size();
- // Add section record for each section.
- size += _file.sections.size() * sectionSize;
-
- // If creating a dylib, add LC_ID_DYLIB.
- if (_file.fileType == llvm::MachO::MH_DYLIB) {
- size += sizeof(dylib_command) + pointerAlign(_file.installName.size() + 1);
- ++count;
- }
-
- // Add LC_DYLD_INFO
- size += sizeof(dyld_info_command);
- ++count;
-
- // Add LC_SYMTAB
- size += sizeof(symtab_command);
- ++count;
-
- // Add LC_DYSYMTAB
- if (_file.fileType != llvm::MachO::MH_PRELOAD) {
- size += sizeof(dysymtab_command);
- ++count;
- }
-
- // If main executable add LC_LOAD_DYLINKER
- if (_file.fileType == llvm::MachO::MH_EXECUTE) {
- size += pointerAlign(sizeof(dylinker_command) + dyldPath().size()+1);
- ++count;
- }
-
- // Add LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS, LC_VERSION_MIN_WATCHOS,
- // LC_VERSION_MIN_TVOS
- if (_file.hasMinVersionLoadCommand) {
- size += sizeof(version_min_command);
- ++count;
- }
-
- // Add LC_SOURCE_VERSION
- size += sizeof(source_version_command);
- ++count;
-
- // If main executable add LC_MAIN
- if (_file.fileType == llvm::MachO::MH_EXECUTE) {
- size += sizeof(entry_point_command);
- ++count;
- }
-
- // Add LC_LOAD_DYLIB for each dependent dylib.
- for (const DependentDylib &dep : _file.dependentDylibs) {
- size += sizeof(dylib_command) + pointerAlign(dep.path.size()+1);
- ++count;
- }
-
- // Add LC_RPATH
- for (const StringRef &path : _file.rpaths) {
- size += pointerAlign(sizeof(rpath_command) + path.size() + 1);
- ++count;
- }
-
- // Add LC_FUNCTION_STARTS if needed
- if (!_file.functionStarts.empty() || alwaysIncludeFunctionStarts) {
- size += sizeof(linkedit_data_command);
- ++count;
- }
-
- // Add LC_DATA_IN_CODE if requested. Note, we do encode zero length entries.
- // FIXME: Zero length entries is only to match ld64. Should we change this?
- if (_file.generateDataInCodeLoadCommand) {
- size += sizeof(linkedit_data_command);
- ++count;
- }
-
- return size;
-}
-
-static bool overlaps(const Segment &s1, const Segment &s2) {
- if (s2.address >= s1.address+s1.size)
- return false;
- if (s1.address >= s2.address+s2.size)
- return false;
- return true;
-}
-
-static bool overlaps(const Section &s1, const Section &s2) {
- if (s2.address >= s1.address+s1.content.size())
- return false;
- if (s1.address >= s2.address+s2.content.size())
- return false;
- return true;
-}
-
-void MachOFileLayout::buildFileOffsets() {
- // Verify no segments overlap
- for (const Segment &sg1 : _file.segments) {
- for (const Segment &sg2 : _file.segments) {
- if (&sg1 == &sg2)
- continue;
- if (overlaps(sg1,sg2)) {
- _ec = make_error_code(llvm::errc::executable_format_error);
- return;
- }
- }
- }
-
- // Verify no sections overlap
- for (const Section &s1 : _file.sections) {
- for (const Section &s2 : _file.sections) {
- if (&s1 == &s2)
- continue;
- if (overlaps(s1,s2)) {
- _ec = make_error_code(llvm::errc::executable_format_error);
- return;
- }
- }
- }
-
- // Build side table of extra info about segments and sections.
- SegExtraInfo t;
- t.fileOffset = 0;
- for (const Segment &sg : _file.segments) {
- _segInfo[&sg] = t;
- }
- SectionExtraInfo t2;
- t2.fileOffset = 0;
- // Assign sections to segments.
- for (const Section &s : _file.sections) {
- _sectInfo[&s] = t2;
- bool foundSegment = false;
- for (const Segment &sg : _file.segments) {
- if (sg.name.equals(s.segmentName)) {
- if ((s.address >= sg.address)
- && (s.address+s.content.size() <= sg.address+sg.size)) {
- _segInfo[&sg].sections.push_back(&s);
- foundSegment = true;
- break;
- }
- }
- }
- if (!foundSegment) {
- _ec = make_error_code(llvm::errc::executable_format_error);
- return;
- }
- }
-
- // Assign file offsets.
- uint32_t fileOffset = 0;
- DEBUG_WITH_TYPE("MachOFileLayout",
- llvm::dbgs() << "buildFileOffsets()\n");
- for (const Segment &sg : _file.segments) {
- _segInfo[&sg].fileOffset = fileOffset;
- if ((_seg1addr == INT64_MAX) && sg.init_access)
- _seg1addr = sg.address;
- DEBUG_WITH_TYPE("MachOFileLayout",
- llvm::dbgs() << " segment=" << sg.name
- << ", fileOffset=" << _segInfo[&sg].fileOffset << "\n");
-
- uint32_t segFileSize = 0;
- // A segment that is not zero-fill must use a least one page of disk space.
- if (sg.init_access)
- segFileSize = _file.pageSize;
- for (const Section *s : _segInfo[&sg].sections) {
- uint32_t sectOffset = s->address - sg.address;
- uint32_t sectFileSize =
- isZeroFillSection(s->type) ? 0 : s->content.size();
- segFileSize = std::max(segFileSize, sectOffset + sectFileSize);
-
- _sectInfo[s].fileOffset = _segInfo[&sg].fileOffset + sectOffset;
- DEBUG_WITH_TYPE("MachOFileLayout",
- llvm::dbgs() << " section=" << s->sectionName
- << ", fileOffset=" << fileOffset << "\n");
- }
-
- // round up all segments to page aligned, except __LINKEDIT
- if (!sg.name.equals("__LINKEDIT")) {
- _segInfo[&sg].fileSize = llvm::alignTo(segFileSize, _file.pageSize);
- fileOffset = llvm::alignTo(fileOffset + segFileSize, _file.pageSize);
- }
- _addressOfLinkEdit = sg.address + sg.size;
- }
- _startOfLinkEdit = fileOffset;
-}
-
-size_t MachOFileLayout::size() const {
- return _endOfSymbolStrings;
-}
-
-void MachOFileLayout::writeMachHeader() {
- auto cpusubtype = MachOLinkingContext::cpuSubtypeFromArch(_file.arch);
- // dynamic x86 executables on newer OS version should also set the
- // CPU_SUBTYPE_LIB64 mask in the CPU subtype.
- // FIXME: Check that this is a dynamic executable, not a static one.
- if (_file.fileType == llvm::MachO::MH_EXECUTE &&
- cpusubtype == CPU_SUBTYPE_X86_64_ALL &&
- _file.os == MachOLinkingContext::OS::macOSX) {
- uint32_t version;
- bool failed = MachOLinkingContext::parsePackedVersion("10.5", version);
- if (!failed && _file.minOSverson >= version)
- cpusubtype |= CPU_SUBTYPE_LIB64;
- }
-
- mach_header *mh = reinterpret_cast<mach_header*>(_buffer);
- mh->magic = _is64 ? llvm::MachO::MH_MAGIC_64 : llvm::MachO::MH_MAGIC;
- mh->cputype = MachOLinkingContext::cpuTypeFromArch(_file.arch);
- mh->cpusubtype = cpusubtype;
- mh->filetype = _file.fileType;
- mh->ncmds = _countOfLoadCommands;
- mh->sizeofcmds = _endOfLoadCommands - _startOfLoadCommands;
- mh->flags = _file.flags;
- if (_swap)
- swapStruct(*mh);
-}
-
-uint32_t MachOFileLayout::indirectSymbolIndex(const Section §,
- uint32_t &index) {
- if (sect.indirectSymbols.empty())
- return 0;
- uint32_t result = index;
- index += sect.indirectSymbols.size();
- return result;
-}
-
-uint32_t MachOFileLayout::indirectSymbolElementSize(const Section §) {
- if (sect.indirectSymbols.empty())
- return 0;
- if (sect.type != S_SYMBOL_STUBS)
- return 0;
- return sect.content.size() / sect.indirectSymbols.size();
-}
-
-template <typename T>
-llvm::Error MachOFileLayout::writeSingleSegmentLoadCommand(uint8_t *&lc) {
- typename T::command* seg = reinterpret_cast<typename T::command*>(lc);
- seg->cmd = T::LC;
- seg->cmdsize = sizeof(typename T::command)
- + _file.sections.size() * sizeof(typename T::section);
- uint8_t *next = lc + seg->cmdsize;
- memset(seg->segname, 0, 16);
- seg->flags = 0;
- seg->vmaddr = 0;
- seg->fileoff = _endOfLoadCommands;
- seg->maxprot = VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE;
- seg->initprot = VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE;
- seg->nsects = _file.sections.size();
- if (seg->nsects) {
- seg->vmsize = _file.sections.back().address
- + _file.sections.back().content.size();
- seg->filesize = _sectInfo[&_file.sections.back()].fileOffset +
- _file.sections.back().content.size() -
- _sectInfo[&_file.sections.front()].fileOffset;
- }
- if (_swap)
- swapStruct(*seg);
- typename T::section *sout = reinterpret_cast<typename T::section*>
- (lc+sizeof(typename T::command));
- uint32_t relOffset = _startOfRelocations;
- uint32_t indirectSymRunningIndex = 0;
- for (const Section &sin : _file.sections) {
- setString16(sin.sectionName, sout->sectname);
- setString16(sin.segmentName, sout->segname);
- sout->addr = sin.address;
- sout->size = sin.content.size();
- sout->offset = _sectInfo[&sin].fileOffset;
- sout->align = llvm::Log2_32(sin.alignment);
- sout->reloff = sin.relocations.empty() ? 0 : relOffset;
- sout->nreloc = sin.relocations.size();
- sout->flags = sin.type | sin.attributes;
- sout->reserved1 = indirectSymbolIndex(sin, indirectSymRunningIndex);
- sout->reserved2 = indirectSymbolElementSize(sin);
- relOffset += sin.relocations.size() * sizeof(any_relocation_info);
- if (_swap)
- swapStruct(*sout);
- ++sout;
- }
- lc = next;
- return llvm::Error::success();
-}
-
-template <typename T>
-llvm::Error MachOFileLayout::writeSegmentLoadCommands(uint8_t *&lc) {
- uint32_t indirectSymRunningIndex = 0;
- for (const Segment &seg : _file.segments) {
- // Link edit has no sections and a custom range of address, so handle it
- // specially.
- SegExtraInfo &segInfo = _segInfo[&seg];
- if (seg.name.equals("__LINKEDIT")) {
- size_t linkeditSize = _endOfLinkEdit - _startOfLinkEdit;
- typename T::command* cmd = reinterpret_cast<typename T::command*>(lc);
- cmd->cmd = T::LC;
- cmd->cmdsize = sizeof(typename T::command);
- uint8_t *next = lc + cmd->cmdsize;
- setString16("__LINKEDIT", cmd->segname);
- cmd->vmaddr = _addressOfLinkEdit;
- cmd->vmsize = llvm::alignTo(linkeditSize, _file.pageSize);
- cmd->fileoff = _startOfLinkEdit;
- cmd->filesize = linkeditSize;
- cmd->initprot = seg.init_access;
- cmd->maxprot = seg.max_access;
- cmd->nsects = 0;
- cmd->flags = 0;
- if (_swap)
- swapStruct(*cmd);
- lc = next;
- continue;
- }
- // Write segment command with trailing sections.
- typename T::command* cmd = reinterpret_cast<typename T::command*>(lc);
- cmd->cmd = T::LC;
- cmd->cmdsize = sizeof(typename T::command)
- + segInfo.sections.size() * sizeof(typename T::section);
- uint8_t *next = lc + cmd->cmdsize;
- setString16(seg.name, cmd->segname);
- cmd->vmaddr = seg.address;
- cmd->vmsize = seg.size;
- cmd->fileoff = segInfo.fileOffset;
- cmd->filesize = segInfo.fileSize;
- cmd->initprot = seg.init_access;
- cmd->maxprot = seg.max_access;
- cmd->nsects = segInfo.sections.size();
- cmd->flags = 0;
- if (_swap)
- swapStruct(*cmd);
- typename T::section *sect = reinterpret_cast<typename T::section*>
- (lc+sizeof(typename T::command));
- for (const Section *section : segInfo.sections) {
- setString16(section->sectionName, sect->sectname);
- setString16(section->segmentName, sect->segname);
- sect->addr = section->address;
- sect->size = section->content.size();
- if (isZeroFillSection(section->type))
- sect->offset = 0;
- else
- sect->offset = section->address - seg.address + segInfo.fileOffset;
- sect->align = llvm::Log2_32(section->alignment);
- sect->reloff = 0;
- sect->nreloc = 0;
- sect->flags = section->type | section->attributes;
- sect->reserved1 = indirectSymbolIndex(*section, indirectSymRunningIndex);
- sect->reserved2 = indirectSymbolElementSize(*section);
- if (_swap)
- swapStruct(*sect);
- ++sect;
- }
- lc = reinterpret_cast<uint8_t*>(next);
- }
- return llvm::Error::success();
-}
-
-static void writeVersionMinLoadCommand(const NormalizedFile &_file,
- bool _swap,
- uint8_t *&lc) {
- if (!_file.hasMinVersionLoadCommand)
- return;
- version_min_command *vm = reinterpret_cast<version_min_command*>(lc);
- switch (_file.os) {
- case MachOLinkingContext::OS::unknown:
- vm->cmd = _file.minOSVersionKind;
- vm->cmdsize = sizeof(version_min_command);
- vm->version = _file.minOSverson;
- vm->sdk = 0;
- break;
- case MachOLinkingContext::OS::macOSX:
- vm->cmd = LC_VERSION_MIN_MACOSX;
- vm->cmdsize = sizeof(version_min_command);
- vm->version = _file.minOSverson;
- vm->sdk = _file.sdkVersion;
- break;
- case MachOLinkingContext::OS::iOS:
- case MachOLinkingContext::OS::iOS_simulator:
- vm->cmd = LC_VERSION_MIN_IPHONEOS;
- vm->cmdsize = sizeof(version_min_command);
- vm->version = _file.minOSverson;
- vm->sdk = _file.sdkVersion;
- break;
- }
- if (_swap)
- swapStruct(*vm);
- lc += sizeof(version_min_command);
-}
-
-llvm::Error MachOFileLayout::writeLoadCommands() {
- uint8_t *lc = &_buffer[_startOfLoadCommands];
- if (_file.fileType == llvm::MachO::MH_OBJECT) {
- // Object files have one unnamed segment which holds all sections.
- if (_is64) {
- if (auto ec = writeSingleSegmentLoadCommand<MachO64Trait>(lc))
- return ec;
- } else {
- if (auto ec = writeSingleSegmentLoadCommand<MachO32Trait>(lc))
- return ec;
- }
- // Add LC_SYMTAB with symbol table info
- symtab_command* st = reinterpret_cast<symtab_command*>(lc);
- st->cmd = LC_SYMTAB;
- st->cmdsize = sizeof(symtab_command);
- st->symoff = _startOfSymbols;
- st->nsyms = _file.stabsSymbols.size() + _file.localSymbols.size() +
- _file.globalSymbols.size() + _file.undefinedSymbols.size();
- st->stroff = _startOfSymbolStrings;
- st->strsize = _endOfSymbolStrings - _startOfSymbolStrings;
- if (_swap)
- swapStruct(*st);
- lc += sizeof(symtab_command);
-
- // Add LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS,
- // LC_VERSION_MIN_WATCHOS, LC_VERSION_MIN_TVOS
- writeVersionMinLoadCommand(_file, _swap, lc);
-
- // Add LC_FUNCTION_STARTS if needed.
- if (_functionStartsSize != 0) {
- linkedit_data_command* dl = reinterpret_cast<linkedit_data_command*>(lc);
- dl->cmd = LC_FUNCTION_STARTS;
- dl->cmdsize = sizeof(linkedit_data_command);
- dl->dataoff = _startOfFunctionStarts;
- dl->datasize = _functionStartsSize;
- if (_swap)
- swapStruct(*dl);
- lc += sizeof(linkedit_data_command);
- }
-
- // Add LC_DATA_IN_CODE if requested.
- if (_file.generateDataInCodeLoadCommand) {
- linkedit_data_command* dl = reinterpret_cast<linkedit_data_command*>(lc);
- dl->cmd = LC_DATA_IN_CODE;
- dl->cmdsize = sizeof(linkedit_data_command);
- dl->dataoff = _startOfDataInCode;
- dl->datasize = _dataInCodeSize;
- if (_swap)
- swapStruct(*dl);
- lc += sizeof(linkedit_data_command);
- }
- } else {
- // Final linked images have sections under segments.
- if (_is64) {
- if (auto ec = writeSegmentLoadCommands<MachO64Trait>(lc))
- return ec;
- } else {
- if (auto ec = writeSegmentLoadCommands<MachO32Trait>(lc))
- return ec;
- }
-
- // Add LC_ID_DYLIB command for dynamic libraries.
- if (_file.fileType == llvm::MachO::MH_DYLIB) {
- dylib_command *dc = reinterpret_cast<dylib_command*>(lc);
- StringRef path = _file.installName;
- uint32_t size = sizeof(dylib_command) + pointerAlign(path.size() + 1);
- dc->cmd = LC_ID_DYLIB;
- dc->cmdsize = size;
- dc->dylib.name = sizeof(dylib_command); // offset
- // needs to be some constant value different than the one in LC_LOAD_DYLIB
- dc->dylib.timestamp = 1;
- dc->dylib.current_version = _file.currentVersion;
- dc->dylib.compatibility_version = _file.compatVersion;
- if (_swap)
- swapStruct(*dc);
- memcpy(lc + sizeof(dylib_command), path.begin(), path.size());
- lc[sizeof(dylib_command) + path.size()] = '\0';
- lc += size;
- }
-
- // Add LC_DYLD_INFO_ONLY.
- dyld_info_command* di = reinterpret_cast<dyld_info_command*>(lc);
- di->cmd = LC_DYLD_INFO_ONLY;
- di->cmdsize = sizeof(dyld_info_command);
- di->rebase_off = _rebaseInfo.size() ? _startOfRebaseInfo : 0;
- di->rebase_size = _rebaseInfo.size();
- di->bind_off = _bindingInfo.size() ? _startOfBindingInfo : 0;
- di->bind_size = _bindingInfo.size();
- di->weak_bind_off = 0;
- di->weak_bind_size = 0;
- di->lazy_bind_off = _lazyBindingInfo.size() ? _startOfLazyBindingInfo : 0;
- di->lazy_bind_size = _lazyBindingInfo.size();
- di->export_off = _exportTrie.size() ? _startOfExportTrie : 0;
- di->export_size = _exportTrie.size();
- if (_swap)
- swapStruct(*di);
- lc += sizeof(dyld_info_command);
-
- // Add LC_SYMTAB with symbol table info.
- symtab_command* st = reinterpret_cast<symtab_command*>(lc);
- st->cmd = LC_SYMTAB;
- st->cmdsize = sizeof(symtab_command);
- st->symoff = _startOfSymbols;
- st->nsyms = _file.stabsSymbols.size() + _file.localSymbols.size() +
- _file.globalSymbols.size() + _file.undefinedSymbols.size();
- st->stroff = _startOfSymbolStrings;
- st->strsize = _endOfSymbolStrings - _startOfSymbolStrings;
- if (_swap)
- swapStruct(*st);
- lc += sizeof(symtab_command);
-
- // Add LC_DYSYMTAB
- if (_file.fileType != llvm::MachO::MH_PRELOAD) {
- dysymtab_command* dst = reinterpret_cast<dysymtab_command*>(lc);
- dst->cmd = LC_DYSYMTAB;
- dst->cmdsize = sizeof(dysymtab_command);
- dst->ilocalsym = _symbolTableLocalsStartIndex;
- dst->nlocalsym = _file.stabsSymbols.size() +
- _file.localSymbols.size();
- dst->iextdefsym = _symbolTableGlobalsStartIndex;
- dst->nextdefsym = _file.globalSymbols.size();
- dst->iundefsym = _symbolTableUndefinesStartIndex;
- dst->nundefsym = _file.undefinedSymbols.size();
- dst->tocoff = 0;
- dst->ntoc = 0;
- dst->modtaboff = 0;
- dst->nmodtab = 0;
- dst->extrefsymoff = 0;
- dst->nextrefsyms = 0;
- dst->indirectsymoff = _startOfIndirectSymbols;
- dst->nindirectsyms = _indirectSymbolTableCount;
- dst->extreloff = 0;
- dst->nextrel = 0;
- dst->locreloff = 0;
- dst->nlocrel = 0;
- if (_swap)
- swapStruct(*dst);
- lc += sizeof(dysymtab_command);
- }
-
- // If main executable, add LC_LOAD_DYLINKER
- if (_file.fileType == llvm::MachO::MH_EXECUTE) {
- // Build LC_LOAD_DYLINKER load command.
- uint32_t size=pointerAlign(sizeof(dylinker_command)+dyldPath().size()+1);
- dylinker_command* dl = reinterpret_cast<dylinker_command*>(lc);
- dl->cmd = LC_LOAD_DYLINKER;
- dl->cmdsize = size;
- dl->name = sizeof(dylinker_command); // offset
- if (_swap)
- swapStruct(*dl);
- memcpy(lc+sizeof(dylinker_command), dyldPath().data(), dyldPath().size());
- lc[sizeof(dylinker_command)+dyldPath().size()] = '\0';
- lc += size;
- }
-
- // Add LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS, LC_VERSION_MIN_WATCHOS,
- // LC_VERSION_MIN_TVOS
- writeVersionMinLoadCommand(_file, _swap, lc);
-
- // Add LC_SOURCE_VERSION
- {
- // Note, using a temporary here to appease UB as we may not be aligned
- // enough for a struct containing a uint64_t when emitting a 32-bit binary
- source_version_command sv;
- sv.cmd = LC_SOURCE_VERSION;
- sv.cmdsize = sizeof(source_version_command);
- sv.version = _file.sourceVersion;
- if (_swap)
- swapStruct(sv);
- memcpy(lc, &sv, sizeof(source_version_command));
- lc += sizeof(source_version_command);
- }
-
- // If main executable, add LC_MAIN.
- if (_file.fileType == llvm::MachO::MH_EXECUTE) {
- // Build LC_MAIN load command.
- // Note, using a temporary here to appease UB as we may not be aligned
- // enough for a struct containing a uint64_t when emitting a 32-bit binary
- entry_point_command ep;
- ep.cmd = LC_MAIN;
- ep.cmdsize = sizeof(entry_point_command);
- ep.entryoff = _file.entryAddress - _seg1addr;
- ep.stacksize = _file.stackSize;
- if (_swap)
- swapStruct(ep);
- memcpy(lc, &ep, sizeof(entry_point_command));
- lc += sizeof(entry_point_command);
- }
-
- // Add LC_LOAD_DYLIB commands
- for (const DependentDylib &dep : _file.dependentDylibs) {
- dylib_command* dc = reinterpret_cast<dylib_command*>(lc);
- uint32_t size = sizeof(dylib_command) + pointerAlign(dep.path.size()+1);
- dc->cmd = dep.kind;
- dc->cmdsize = size;
- dc->dylib.name = sizeof(dylib_command); // offset
- // needs to be some constant value different than the one in LC_ID_DYLIB
- dc->dylib.timestamp = 2;
- dc->dylib.current_version = dep.currentVersion;
- dc->dylib.compatibility_version = dep.compatVersion;
- if (_swap)
- swapStruct(*dc);
- memcpy(lc+sizeof(dylib_command), dep.path.begin(), dep.path.size());
- lc[sizeof(dylib_command)+dep.path.size()] = '\0';
- lc += size;
- }
-
- // Add LC_RPATH
- for (const StringRef &path : _file.rpaths) {
- rpath_command *rpc = reinterpret_cast<rpath_command *>(lc);
- uint32_t size = pointerAlign(sizeof(rpath_command) + path.size() + 1);
- rpc->cmd = LC_RPATH;
- rpc->cmdsize = size;
- rpc->path = sizeof(rpath_command); // offset
- if (_swap)
- swapStruct(*rpc);
- memcpy(lc+sizeof(rpath_command), path.begin(), path.size());
- lc[sizeof(rpath_command)+path.size()] = '\0';
- lc += size;
- }
-
- // Add LC_FUNCTION_STARTS if needed.
- if (_functionStartsSize != 0) {
- linkedit_data_command* dl = reinterpret_cast<linkedit_data_command*>(lc);
- dl->cmd = LC_FUNCTION_STARTS;
- dl->cmdsize = sizeof(linkedit_data_command);
- dl->dataoff = _startOfFunctionStarts;
- dl->datasize = _functionStartsSize;
- if (_swap)
- swapStruct(*dl);
- lc += sizeof(linkedit_data_command);
- }
-
- // Add LC_DATA_IN_CODE if requested.
- if (_file.generateDataInCodeLoadCommand) {
- linkedit_data_command* dl = reinterpret_cast<linkedit_data_command*>(lc);
- dl->cmd = LC_DATA_IN_CODE;
- dl->cmdsize = sizeof(linkedit_data_command);
- dl->dataoff = _startOfDataInCode;
- dl->datasize = _dataInCodeSize;
- if (_swap)
- swapStruct(*dl);
- lc += sizeof(linkedit_data_command);
- }
- }
- assert(lc == &_buffer[_endOfLoadCommands]);
- return llvm::Error::success();
-}
-
-void MachOFileLayout::writeSectionContent() {
- for (const Section &s : _file.sections) {
- // Copy all section content to output buffer.
- if (isZeroFillSection(s.type))
- continue;
- if (s.content.empty())
- continue;
- uint32_t offset = _sectInfo[&s].fileOffset;
- assert(offset >= _endOfLoadCommands);
- uint8_t *p = &_buffer[offset];
- memcpy(p, &s.content[0], s.content.size());
- p += s.content.size();
- }
-}
-
-void MachOFileLayout::writeRelocations() {
- uint32_t relOffset = _startOfRelocations;
- for (Section sect : _file.sections) {
- for (Relocation r : sect.relocations) {
- any_relocation_info* rb = reinterpret_cast<any_relocation_info*>(
- &_buffer[relOffset]);
- *rb = packRelocation(r, _swap, _bigEndianArch);
- relOffset += sizeof(any_relocation_info);
- }
- }
-}
-
-void MachOFileLayout::appendSymbols(const std::vector<Symbol> &symbols,
- uint32_t &symOffset, uint32_t &strOffset) {
- for (const Symbol &sym : symbols) {
- if (_is64) {
- nlist_64* nb = reinterpret_cast<nlist_64*>(&_buffer[symOffset]);
- nb->n_strx = strOffset - _startOfSymbolStrings;
- nb->n_type = sym.type | sym.scope;
- nb->n_sect = sym.sect;
- nb->n_desc = sym.desc;
- nb->n_value = sym.value;
- if (_swap)
- swapStruct(*nb);
- symOffset += sizeof(nlist_64);
- } else {
- nlist* nb = reinterpret_cast<nlist*>(&_buffer[symOffset]);
- nb->n_strx = strOffset - _startOfSymbolStrings;
- nb->n_type = sym.type | sym.scope;
- nb->n_sect = sym.sect;
- nb->n_desc = sym.desc;
- nb->n_value = sym.value;
- if (_swap)
- swapStruct(*nb);
- symOffset += sizeof(nlist);
- }
- memcpy(&_buffer[strOffset], sym.name.begin(), sym.name.size());
- strOffset += sym.name.size();
- _buffer[strOffset++] ='\0'; // Strings in table have nul terminator.
- }
-}
-
-void MachOFileLayout::writeFunctionStartsInfo() {
- if (!_functionStartsSize)
- return;
- memcpy(&_buffer[_startOfFunctionStarts], _file.functionStarts.data(),
- _functionStartsSize);
-}
-
-void MachOFileLayout::writeDataInCodeInfo() {
- uint32_t offset = _startOfDataInCode;
- for (const DataInCode &entry : _file.dataInCode) {
- data_in_code_entry *dst = reinterpret_cast<data_in_code_entry*>(
- &_buffer[offset]);
- dst->offset = entry.offset;
- dst->length = entry.length;
- dst->kind = entry.kind;
- if (_swap)
- swapStruct(*dst);
- offset += sizeof(data_in_code_entry);
- }
-}
-
-void MachOFileLayout::writeSymbolTable() {
- // Write symbol table and symbol strings in parallel.
- uint32_t symOffset = _startOfSymbols;
- uint32_t strOffset = _startOfSymbolStrings;
- // Reserve n_strx offset of zero to mean no name.
- _buffer[strOffset++] = ' ';
- _buffer[strOffset++] = '\0';
- appendSymbols(_file.stabsSymbols, symOffset, strOffset);
- appendSymbols(_file.localSymbols, symOffset, strOffset);
- appendSymbols(_file.globalSymbols, symOffset, strOffset);
- appendSymbols(_file.undefinedSymbols, symOffset, strOffset);
- // Write indirect symbol table array.
- uint32_t *indirects = reinterpret_cast<uint32_t*>
- (&_buffer[_startOfIndirectSymbols]);
- if (_file.fileType == llvm::MachO::MH_OBJECT) {
- // Object files have sections in same order as input normalized file.
- for (const Section §ion : _file.sections) {
- for (uint32_t index : section.indirectSymbols) {
- if (_swap)
- *indirects++ = llvm::sys::getSwappedBytes(index);
- else
- *indirects++ = index;
- }
- }
- } else {
- // Final linked images must sort sections from normalized file.
- for (const Segment &seg : _file.segments) {
- SegExtraInfo &segInfo = _segInfo[&seg];
- for (const Section *section : segInfo.sections) {
- for (uint32_t index : section->indirectSymbols) {
- if (_swap)
- *indirects++ = llvm::sys::getSwappedBytes(index);
- else
- *indirects++ = index;
- }
- }
- }
- }
-}
-
-void MachOFileLayout::writeRebaseInfo() {
- memcpy(&_buffer[_startOfRebaseInfo], _rebaseInfo.bytes(), _rebaseInfo.size());
-}
-
-void MachOFileLayout::writeBindingInfo() {
- memcpy(&_buffer[_startOfBindingInfo],
- _bindingInfo.bytes(), _bindingInfo.size());
-}
-
-void MachOFileLayout::writeLazyBindingInfo() {
- memcpy(&_buffer[_startOfLazyBindingInfo],
- _lazyBindingInfo.bytes(), _lazyBindingInfo.size());
-}
-
-void MachOFileLayout::writeExportInfo() {
- memcpy(&_buffer[_startOfExportTrie], _exportTrie.bytes(), _exportTrie.size());
-}
-
-void MachOFileLayout::buildLinkEditInfo() {
- buildRebaseInfo();
- buildBindInfo();
- buildLazyBindInfo();
- buildExportTrie();
- computeSymbolTableSizes();
- computeFunctionStartsSize();
- computeDataInCodeSize();
-}
-
-void MachOFileLayout::buildSectionRelocations() {
-
-}
-
-void MachOFileLayout::buildRebaseInfo() {
- // TODO: compress rebasing info.
- for (const RebaseLocation& entry : _file.rebasingInfo) {
- _rebaseInfo.append_byte(REBASE_OPCODE_SET_TYPE_IMM | entry.kind);
- _rebaseInfo.append_byte(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
- | entry.segIndex);
- _rebaseInfo.append_uleb128(entry.segOffset);
- _rebaseInfo.append_uleb128(REBASE_OPCODE_DO_REBASE_IMM_TIMES | 1);
- }
- _rebaseInfo.append_byte(REBASE_OPCODE_DONE);
- _rebaseInfo.align(_is64 ? 8 : 4);
-}
-
-void MachOFileLayout::buildBindInfo() {
- // TODO: compress bind info.
- uint64_t lastAddend = 0;
- int lastOrdinal = 0x80000000;
- StringRef lastSymbolName;
- BindType lastType = (BindType)0;
- Hex32 lastSegOffset = ~0U;
- uint8_t lastSegIndex = (uint8_t)~0U;
- for (const BindLocation& entry : _file.bindingInfo) {
- if (entry.ordinal != lastOrdinal) {
- if (entry.ordinal <= 0)
- _bindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM |
- (entry.ordinal & BIND_IMMEDIATE_MASK));
- else if (entry.ordinal <= BIND_IMMEDIATE_MASK)
- _bindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM |
- entry.ordinal);
- else {
- _bindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
- _bindingInfo.append_uleb128(entry.ordinal);
- }
- lastOrdinal = entry.ordinal;
- }
-
- if (lastSymbolName != entry.symbolName) {
- _bindingInfo.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM);
- _bindingInfo.append_string(entry.symbolName);
- lastSymbolName = entry.symbolName;
- }
-
- if (lastType != entry.kind) {
- _bindingInfo.append_byte(BIND_OPCODE_SET_TYPE_IMM | entry.kind);
- lastType = entry.kind;
- }
-
- if (lastSegIndex != entry.segIndex || lastSegOffset != entry.segOffset) {
- _bindingInfo.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
- | entry.segIndex);
- _bindingInfo.append_uleb128(entry.segOffset);
- lastSegIndex = entry.segIndex;
- lastSegOffset = entry.segOffset;
- }
- if (entry.addend != lastAddend) {
- _bindingInfo.append_byte(BIND_OPCODE_SET_ADDEND_SLEB);
- _bindingInfo.append_sleb128(entry.addend);
- lastAddend = entry.addend;
- }
- _bindingInfo.append_byte(BIND_OPCODE_DO_BIND);
- }
- _bindingInfo.append_byte(BIND_OPCODE_DONE);
- _bindingInfo.align(_is64 ? 8 : 4);
-}
-
-void MachOFileLayout::buildLazyBindInfo() {
- for (const BindLocation& entry : _file.lazyBindingInfo) {
- _lazyBindingInfo.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
- | entry.segIndex);
- _lazyBindingInfo.append_uleb128(entry.segOffset);
- if (entry.ordinal <= 0)
- _lazyBindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM |
- (entry.ordinal & BIND_IMMEDIATE_MASK));
- else if (entry.ordinal <= BIND_IMMEDIATE_MASK)
- _lazyBindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM |
- entry.ordinal);
- else {
- _lazyBindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
- _lazyBindingInfo.append_uleb128(entry.ordinal);
- }
- // FIXME: We need to | the opcode here with flags.
- _lazyBindingInfo.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM);
- _lazyBindingInfo.append_string(entry.symbolName);
- _lazyBindingInfo.append_byte(BIND_OPCODE_DO_BIND);
- _lazyBindingInfo.append_byte(BIND_OPCODE_DONE);
- }
- _lazyBindingInfo.align(_is64 ? 8 : 4);
-}
-
-void TrieNode::addSymbol(const Export& entry,
- BumpPtrAllocator &allocator,
- std::vector<TrieNode*> &allNodes) {
- StringRef partialStr = entry.name.drop_front(_cummulativeString.size());
- for (TrieEdge &edge : _children) {
- StringRef edgeStr = edge._subString;
- if (partialStr.startswith(edgeStr)) {
- // Already have matching edge, go down that path.
- edge._child->addSymbol(entry, allocator, allNodes);
- return;
- }
- // See if string has common prefix with existing edge.
- for (int n=edgeStr.size()-1; n > 0; --n) {
- if (partialStr.substr(0, n).equals(edgeStr.substr(0, n))) {
- // Splice in new node: was A -> C, now A -> B -> C
- StringRef bNodeStr = edge._child->_cummulativeString;
- bNodeStr = bNodeStr.drop_back(edgeStr.size()-n).copy(allocator);
- auto *bNode = new (allocator) TrieNode(bNodeStr);
- allNodes.push_back(bNode);
- TrieNode* cNode = edge._child;
- StringRef abEdgeStr = edgeStr.substr(0,n).copy(allocator);
- StringRef bcEdgeStr = edgeStr.substr(n).copy(allocator);
- DEBUG_WITH_TYPE("trie-builder", llvm::dbgs()
- << "splice in TrieNode('" << bNodeStr
- << "') between edge '"
- << abEdgeStr << "' and edge='"
- << bcEdgeStr<< "'\n");
- TrieEdge& abEdge = edge;
- abEdge._subString = abEdgeStr;
- abEdge._child = bNode;
- auto *bcEdge = new (allocator) TrieEdge(bcEdgeStr, cNode);
- bNode->_children.insert(bNode->_children.end(), bcEdge);
- bNode->addSymbol(entry, allocator, allNodes);
- return;
- }
- }
- }
- if (entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT) {
- assert(entry.otherOffset != 0);
- }
- if (entry.flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) {
- assert(entry.otherOffset != 0);
- }
- // No commonality with any existing child, make a new edge.
- auto *newNode = new (allocator) TrieNode(entry.name.copy(allocator));
- auto *newEdge = new (allocator) TrieEdge(partialStr, newNode);
- _children.insert(_children.end(), newEdge);
- DEBUG_WITH_TYPE("trie-builder", llvm::dbgs()
- << "new TrieNode('" << entry.name << "') with edge '"
- << partialStr << "' from node='"
- << _cummulativeString << "'\n");
- newNode->_address = entry.offset;
- newNode->_flags = entry.flags | entry.kind;
- newNode->_other = entry.otherOffset;
- if ((entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT) && !entry.otherName.empty())
- newNode->_importedName = entry.otherName.copy(allocator);
- newNode->_hasExportInfo = true;
- allNodes.push_back(newNode);
-}
-
-void TrieNode::addOrderedNodes(const Export& entry,
- std::vector<TrieNode*> &orderedNodes) {
- if (!_ordered) {
- orderedNodes.push_back(this);
- _ordered = true;
- }
-
- StringRef partialStr = entry.name.drop_front(_cummulativeString.size());
- for (TrieEdge &edge : _children) {
- StringRef edgeStr = edge._subString;
- if (partialStr.startswith(edgeStr)) {
- // Already have matching edge, go down that path.
- edge._child->addOrderedNodes(entry, orderedNodes);
- return;
- }
- }
-}
-
-bool TrieNode::updateOffset(uint32_t& offset) {
- uint32_t nodeSize = 1; // Length when no export info
- if (_hasExportInfo) {
- if (_flags & EXPORT_SYMBOL_FLAGS_REEXPORT) {
- nodeSize = llvm::getULEB128Size(_flags);
- nodeSize += llvm::getULEB128Size(_other); // Other contains ordinal.
- nodeSize += _importedName.size();
- ++nodeSize; // Trailing zero in imported name.
- } else {
- nodeSize = llvm::getULEB128Size(_flags) + llvm::getULEB128Size(_address);
- if (_flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER)
- nodeSize += llvm::getULEB128Size(_other);
- }
- // Overall node size so far is uleb128 of export info + actual export info.
- nodeSize += llvm::getULEB128Size(nodeSize);
- }
- // Compute size of all child edges.
- ++nodeSize; // Byte for number of children.
- for (TrieEdge &edge : _children) {
- nodeSize += edge._subString.size() + 1 // String length.
- + llvm::getULEB128Size(edge._child->_trieOffset); // Offset len.
- }
- // On input, 'offset' is new prefered location for this node.
- bool result = (_trieOffset != offset);
- // Store new location in node object for use by parents.
- _trieOffset = offset;
- // Update offset for next iteration.
- offset += nodeSize;
- // Return true if _trieOffset was changed.
- return result;
-}
-
-void TrieNode::appendToByteBuffer(ByteBuffer &out) {
- if (_hasExportInfo) {
- if (_flags & EXPORT_SYMBOL_FLAGS_REEXPORT) {
- if (!_importedName.empty()) {
- // nodes with re-export info: size, flags, ordinal, import-name
- uint32_t nodeSize = llvm::getULEB128Size(_flags)
- + llvm::getULEB128Size(_other)
- + _importedName.size() + 1;
- assert(nodeSize < 256);
- out.append_byte(nodeSize);
- out.append_uleb128(_flags);
- out.append_uleb128(_other);
- out.append_string(_importedName);
- } else {
- // nodes without re-export info: size, flags, ordinal, empty-string
- uint32_t nodeSize = llvm::getULEB128Size(_flags)
- + llvm::getULEB128Size(_other) + 1;
- assert(nodeSize < 256);
- out.append_byte(nodeSize);
- out.append_uleb128(_flags);
- out.append_uleb128(_other);
- out.append_byte(0);
- }
- } else if ( _flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) {
- // Nodes with export info: size, flags, address, other
- uint32_t nodeSize = llvm::getULEB128Size(_flags)
- + llvm::getULEB128Size(_address)
- + llvm::getULEB128Size(_other);
- assert(nodeSize < 256);
- out.append_byte(nodeSize);
- out.append_uleb128(_flags);
- out.append_uleb128(_address);
- out.append_uleb128(_other);
- } else {
- // Nodes with export info: size, flags, address
- uint32_t nodeSize = llvm::getULEB128Size(_flags)
- + llvm::getULEB128Size(_address);
- assert(nodeSize < 256);
- out.append_byte(nodeSize);
- out.append_uleb128(_flags);
- out.append_uleb128(_address);
- }
- } else {
- // Node with no export info.
- uint32_t nodeSize = 0;
- out.append_byte(nodeSize);
- }
- // Add number of children.
- assert(_children.size() < 256);
- out.append_byte(_children.size());
- // Append each child edge substring and node offset.
- for (TrieEdge &edge : _children) {
- out.append_string(edge._subString);
- out.append_uleb128(edge._child->_trieOffset);
- }
-}
-
-void MachOFileLayout::buildExportTrie() {
- if (_file.exportInfo.empty())
- return;
-
- // For all temporary strings and objects used building trie.
- BumpPtrAllocator allocator;
-
- // Build trie of all exported symbols.
- auto *rootNode = new (allocator) TrieNode(StringRef());
- std::vector<TrieNode*> allNodes;
- allNodes.reserve(_file.exportInfo.size()*2);
- allNodes.push_back(rootNode);
- for (const Export& entry : _file.exportInfo) {
- rootNode->addSymbol(entry, allocator, allNodes);
- }
-
- std::vector<TrieNode*> orderedNodes;
- orderedNodes.reserve(allNodes.size());
-
- for (const Export& entry : _file.exportInfo)
- rootNode->addOrderedNodes(entry, orderedNodes);
-
- // Assign each node in the vector an offset in the trie stream, iterating
- // until all uleb128 sizes have stabilized.
- bool more;
- do {
- uint32_t offset = 0;
- more = false;
- for (TrieNode* node : orderedNodes) {
- if (node->updateOffset(offset))
- more = true;
- }
- } while (more);
-
- // Serialize trie to ByteBuffer.
- for (TrieNode* node : orderedNodes) {
- node->appendToByteBuffer(_exportTrie);
- }
- _exportTrie.align(_is64 ? 8 : 4);
-}
-
-void MachOFileLayout::computeSymbolTableSizes() {
- // MachO symbol tables have three ranges: locals, globals, and undefines
- const size_t nlistSize = (_is64 ? sizeof(nlist_64) : sizeof(nlist));
- _symbolTableSize = nlistSize * (_file.stabsSymbols.size()
- + _file.localSymbols.size()
- + _file.globalSymbols.size()
- + _file.undefinedSymbols.size());
- // Always reserve 1-byte for the empty string and 1-byte for its terminator.
- _symbolStringPoolSize = 2;
- for (const Symbol &sym : _file.stabsSymbols) {
- _symbolStringPoolSize += (sym.name.size()+1);
- }
- for (const Symbol &sym : _file.localSymbols) {
- _symbolStringPoolSize += (sym.name.size()+1);
- }
- for (const Symbol &sym : _file.globalSymbols) {
- _symbolStringPoolSize += (sym.name.size()+1);
- }
- for (const Symbol &sym : _file.undefinedSymbols) {
- _symbolStringPoolSize += (sym.name.size()+1);
- }
- _symbolTableLocalsStartIndex = 0;
- _symbolTableGlobalsStartIndex = _file.stabsSymbols.size() +
- _file.localSymbols.size();
- _symbolTableUndefinesStartIndex = _symbolTableGlobalsStartIndex
- + _file.globalSymbols.size();
-
- _indirectSymbolTableCount = 0;
- for (const Section § : _file.sections) {
- _indirectSymbolTableCount += sect.indirectSymbols.size();
- }
-}
-
-void MachOFileLayout::computeFunctionStartsSize() {
- _functionStartsSize = _file.functionStarts.size();
-}
-
-void MachOFileLayout::computeDataInCodeSize() {
- _dataInCodeSize = _file.dataInCode.size() * sizeof(data_in_code_entry);
-}
-
-void MachOFileLayout::writeLinkEditContent() {
- if (_file.fileType == llvm::MachO::MH_OBJECT) {
- writeRelocations();
- writeFunctionStartsInfo();
- writeDataInCodeInfo();
- writeSymbolTable();
- } else {
- writeRebaseInfo();
- writeBindingInfo();
- writeLazyBindingInfo();
- // TODO: add weak binding info
- writeExportInfo();
- writeFunctionStartsInfo();
- writeDataInCodeInfo();
- writeSymbolTable();
- }
-}
-
-llvm::Error MachOFileLayout::writeBinary(StringRef path) {
- // Check for pending error from constructor.
- if (_ec)
- return llvm::errorCodeToError(_ec);
- // Create FileOutputBuffer with calculated size.
- unsigned flags = 0;
- if (_file.fileType != llvm::MachO::MH_OBJECT)
- flags = llvm::FileOutputBuffer::F_executable;
- Expected<std::unique_ptr<llvm::FileOutputBuffer>> fobOrErr =
- llvm::FileOutputBuffer::create(path, size(), flags);
- if (Error E = fobOrErr.takeError())
- return E;
- std::unique_ptr<llvm::FileOutputBuffer> &fob = *fobOrErr;
- // Write content.
- _buffer = fob->getBufferStart();
- writeMachHeader();
- if (auto ec = writeLoadCommands())
- return ec;
- writeSectionContent();
- writeLinkEditContent();
- if (Error E = fob->commit())
- return E;
-
- return llvm::Error::success();
-}
-
-/// Takes in-memory normalized view and writes a mach-o object file.
-llvm::Error writeBinary(const NormalizedFile &file, StringRef path) {
- MachOFileLayout layout(file, false);
- return layout.writeBinary(path);
-}
-
-} // namespace normalized
-} // namespace mach_o
-} // namespace lld
+++ /dev/null
-//===- lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp ------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-///
-/// \file Converts from in-memory Atoms to in-memory normalized mach-o.
-///
-/// +------------+
-/// | normalized |
-/// +------------+
-/// ^
-/// |
-/// |
-/// +-------+
-/// | Atoms |
-/// +-------+
-
-#include "ArchHandler.h"
-#include "DebugInfo.h"
-#include "MachONormalizedFile.h"
-#include "MachONormalizedFileBinaryUtils.h"
-#include "lld/Common/LLVM.h"
-#include "lld/Core/Error.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/ADT/StringSwitch.h"
-#include "llvm/BinaryFormat/MachO.h"
-#include "llvm/Support/Casting.h"
-#include "llvm/Support/Debug.h"
-#include "llvm/Support/ErrorHandling.h"
-#include "llvm/Support/Format.h"
-#include <map>
-#include <system_error>
-#include <unordered_set>
-
-using llvm::StringRef;
-using llvm::isa;
-using namespace llvm::MachO;
-using namespace lld::mach_o::normalized;
-using namespace lld;
-
-namespace {
-
-struct AtomInfo {
- const DefinedAtom *atom;
- uint64_t offsetInSection;
-};
-
-struct SectionInfo {
- SectionInfo(StringRef seg, StringRef sect, SectionType type,
- const MachOLinkingContext &ctxt, uint32_t attr,
- bool relocsToDefinedCanBeImplicit);
-
- StringRef segmentName;
- StringRef sectionName;
- SectionType type;
- uint32_t attributes;
- uint64_t address;
- uint64_t size;
- uint16_t alignment;
-
- /// If this is set, the any relocs in this section which point to defined
- /// addresses can be implicitly generated. This is the case for the
- /// __eh_frame section where references to the function can be implicit if the
- /// function is defined.
- bool relocsToDefinedCanBeImplicit;
-
-
- std::vector<AtomInfo> atomsAndOffsets;
- uint32_t normalizedSectionIndex;
- uint32_t finalSectionIndex;
-};
-
-SectionInfo::SectionInfo(StringRef sg, StringRef sct, SectionType t,
- const MachOLinkingContext &ctxt, uint32_t attrs,
- bool relocsToDefinedCanBeImplicit)
- : segmentName(sg), sectionName(sct), type(t), attributes(attrs),
- address(0), size(0), alignment(1),
- relocsToDefinedCanBeImplicit(relocsToDefinedCanBeImplicit),
- normalizedSectionIndex(0), finalSectionIndex(0) {
- uint16_t align = 1;
- if (ctxt.sectionAligned(segmentName, sectionName, align)) {
- alignment = align;
- }
-}
-
-struct SegmentInfo {
- SegmentInfo(StringRef name);
-
- StringRef name;
- uint64_t address;
- uint64_t size;
- uint32_t init_access;
- uint32_t max_access;
- std::vector<SectionInfo*> sections;
- uint32_t normalizedSegmentIndex;
-};
-
-SegmentInfo::SegmentInfo(StringRef n)
- : name(n), address(0), size(0), init_access(0), max_access(0),
- normalizedSegmentIndex(0) {
-}
-
-class Util {
-public:
- Util(const MachOLinkingContext &ctxt)
- : _ctx(ctxt), _archHandler(ctxt.archHandler()), _entryAtom(nullptr),
- _hasTLVDescriptors(false), _subsectionsViaSymbols(true) {}
- ~Util();
-
- void processDefinedAtoms(const lld::File &atomFile);
- void processAtomAttributes(const DefinedAtom *atom);
- void assignAtomToSection(const DefinedAtom *atom);
- void organizeSections();
- void assignAddressesToSections(const NormalizedFile &file);
- uint32_t fileFlags();
- void copySegmentInfo(NormalizedFile &file);
- void copySectionInfo(NormalizedFile &file);
- void updateSectionInfo(NormalizedFile &file);
- void buildAtomToAddressMap();
- llvm::Error synthesizeDebugNotes(NormalizedFile &file);
- llvm::Error addSymbols(const lld::File &atomFile, NormalizedFile &file);
- void addIndirectSymbols(const lld::File &atomFile, NormalizedFile &file);
- void addRebaseAndBindingInfo(const lld::File &, NormalizedFile &file);
- void addExportInfo(const lld::File &, NormalizedFile &file);
- void addSectionRelocs(const lld::File &, NormalizedFile &file);
- void addFunctionStarts(const lld::File &, NormalizedFile &file);
- void buildDataInCodeArray(const lld::File &, NormalizedFile &file);
- void addDependentDylibs(const lld::File &, NormalizedFile &file);
- void copyEntryPointAddress(NormalizedFile &file);
- void copySectionContent(NormalizedFile &file);
-
- bool allSourceFilesHaveMinVersions() const {
- return _allSourceFilesHaveMinVersions;
- }
-
- uint32_t minVersion() const {
- return _minVersion;
- }
-
- LoadCommandType minVersionCommandType() const {
- return _minVersionCommandType;
- }
-
-private:
- typedef std::map<DefinedAtom::ContentType, SectionInfo*> TypeToSection;
- typedef llvm::DenseMap<const Atom*, uint64_t> AtomToAddress;
-
- struct DylibInfo { int ordinal; bool hasWeak; bool hasNonWeak; };
- typedef llvm::StringMap<DylibInfo> DylibPathToInfo;
-
- SectionInfo *sectionForAtom(const DefinedAtom*);
- SectionInfo *getRelocatableSection(DefinedAtom::ContentType type);
- SectionInfo *getFinalSection(DefinedAtom::ContentType type);
- void appendAtom(SectionInfo *sect, const DefinedAtom *atom);
- SegmentInfo *segmentForName(StringRef segName);
- void layoutSectionsInSegment(SegmentInfo *seg, uint64_t &addr);
- void layoutSectionsInTextSegment(size_t, SegmentInfo *, uint64_t &);
- void copySectionContent(SectionInfo *si, ContentBytes &content);
- uint16_t descBits(const DefinedAtom* atom);
- int dylibOrdinal(const SharedLibraryAtom *sa);
- void segIndexForSection(const SectionInfo *sect,
- uint8_t &segmentIndex, uint64_t &segmentStartAddr);
- const Atom *targetOfLazyPointer(const DefinedAtom *lpAtom);
- const Atom *targetOfStub(const DefinedAtom *stubAtom);
- llvm::Error getSymbolTableRegion(const DefinedAtom* atom,
- bool &inGlobalsRegion,
- SymbolScope &symbolScope);
- void appendSection(SectionInfo *si, NormalizedFile &file);
- uint32_t sectionIndexForAtom(const Atom *atom);
- void fixLazyReferenceImm(const DefinedAtom *atom, uint32_t offset,
- NormalizedFile &file);
-
- typedef llvm::DenseMap<const Atom*, uint32_t> AtomToIndex;
- struct AtomAndIndex { const Atom *atom; uint32_t index; SymbolScope scope; };
- struct AtomSorter {
- bool operator()(const AtomAndIndex &left, const AtomAndIndex &right);
- };
- struct SegmentSorter {
- bool operator()(const SegmentInfo *left, const SegmentInfo *right);
- static unsigned weight(const SegmentInfo *);
- };
- struct TextSectionSorter {
- bool operator()(const SectionInfo *left, const SectionInfo *right);
- static unsigned weight(const SectionInfo *);
- };
-
- const MachOLinkingContext &_ctx;
- mach_o::ArchHandler &_archHandler;
- llvm::BumpPtrAllocator _allocator;
- std::vector<SectionInfo*> _sectionInfos;
- std::vector<SegmentInfo*> _segmentInfos;
- TypeToSection _sectionMap;
- std::vector<SectionInfo*> _customSections;
- AtomToAddress _atomToAddress;
- DylibPathToInfo _dylibInfo;
- const DefinedAtom *_entryAtom;
- AtomToIndex _atomToSymbolIndex;
- std::vector<const Atom *> _machHeaderAliasAtoms;
- bool _hasTLVDescriptors;
- bool _subsectionsViaSymbols;
- bool _allSourceFilesHaveMinVersions = true;
- LoadCommandType _minVersionCommandType = (LoadCommandType)0;
- uint32_t _minVersion = 0;
- std::vector<lld::mach_o::Stab> _stabs;
-};
-
-Util::~Util() {
- // The SectionInfo structs are BumpPtr allocated, but atomsAndOffsets needs
- // to be deleted.
- for (SectionInfo *si : _sectionInfos) {
- // clear() destroys vector elements, but does not deallocate.
- // Instead use swap() to deallocate vector buffer.
- std::vector<AtomInfo> empty;
- si->atomsAndOffsets.swap(empty);
- }
- // The SegmentInfo structs are BumpPtr allocated, but sections needs
- // to be deleted.
- for (SegmentInfo *sgi : _segmentInfos) {
- std::vector<SectionInfo*> empty2;
- sgi->sections.swap(empty2);
- }
-}
-
-SectionInfo *Util::getRelocatableSection(DefinedAtom::ContentType type) {
- StringRef segmentName;
- StringRef sectionName;
- SectionType sectionType;
- SectionAttr sectionAttrs;
- bool relocsToDefinedCanBeImplicit;
-
- // Use same table used by when parsing .o files.
- relocatableSectionInfoForContentType(type, segmentName, sectionName,
- sectionType, sectionAttrs,
- relocsToDefinedCanBeImplicit);
- // If we already have a SectionInfo with this name, re-use it.
- // This can happen if two ContentType map to the same mach-o section.
- for (auto sect : _sectionMap) {
- if (sect.second->sectionName.equals(sectionName) &&
- sect.second->segmentName.equals(segmentName)) {
- return sect.second;
- }
- }
- // Otherwise allocate new SectionInfo object.
- auto *sect = new (_allocator)
- SectionInfo(segmentName, sectionName, sectionType, _ctx, sectionAttrs,
- relocsToDefinedCanBeImplicit);
- _sectionInfos.push_back(sect);
- _sectionMap[type] = sect;
- return sect;
-}
-
-#define ENTRY(seg, sect, type, atomType) \
- {seg, sect, type, DefinedAtom::atomType }
-
-struct MachOFinalSectionFromAtomType {
- StringRef segmentName;
- StringRef sectionName;
- SectionType sectionType;
- DefinedAtom::ContentType atomType;
-};
-
-const MachOFinalSectionFromAtomType sectsToAtomType[] = {
- ENTRY("__TEXT", "__text", S_REGULAR, typeCode),
- ENTRY("__TEXT", "__text", S_REGULAR, typeMachHeader),
- ENTRY("__TEXT", "__cstring", S_CSTRING_LITERALS, typeCString),
- ENTRY("__TEXT", "__ustring", S_REGULAR, typeUTF16String),
- ENTRY("__TEXT", "__const", S_REGULAR, typeConstant),
- ENTRY("__TEXT", "__const", S_4BYTE_LITERALS, typeLiteral4),
- ENTRY("__TEXT", "__const", S_8BYTE_LITERALS, typeLiteral8),
- ENTRY("__TEXT", "__const", S_16BYTE_LITERALS, typeLiteral16),
- ENTRY("__TEXT", "__stubs", S_SYMBOL_STUBS, typeStub),
- ENTRY("__TEXT", "__stub_helper", S_REGULAR, typeStubHelper),
- ENTRY("__TEXT", "__gcc_except_tab", S_REGULAR, typeLSDA),
- ENTRY("__TEXT", "__eh_frame", S_COALESCED, typeCFI),
- ENTRY("__TEXT", "__unwind_info", S_REGULAR, typeProcessedUnwindInfo),
- ENTRY("__DATA", "__data", S_REGULAR, typeData),
- ENTRY("__DATA", "__const", S_REGULAR, typeConstData),
- ENTRY("__DATA", "__cfstring", S_REGULAR, typeCFString),
- ENTRY("__DATA", "__la_symbol_ptr", S_LAZY_SYMBOL_POINTERS,
- typeLazyPointer),
- ENTRY("__DATA", "__mod_init_func", S_MOD_INIT_FUNC_POINTERS,
- typeInitializerPtr),
- ENTRY("__DATA", "__mod_term_func", S_MOD_TERM_FUNC_POINTERS,
- typeTerminatorPtr),
- ENTRY("__DATA", "__got", S_NON_LAZY_SYMBOL_POINTERS,
- typeGOT),
- ENTRY("__DATA", "__nl_symbol_ptr", S_NON_LAZY_SYMBOL_POINTERS,
- typeNonLazyPointer),
- ENTRY("__DATA", "__thread_vars", S_THREAD_LOCAL_VARIABLES,
- typeThunkTLV),
- ENTRY("__DATA", "__thread_data", S_THREAD_LOCAL_REGULAR,
- typeTLVInitialData),
- ENTRY("__DATA", "__thread_ptrs", S_THREAD_LOCAL_VARIABLE_POINTERS,
- typeTLVInitializerPtr),
- ENTRY("__DATA", "__thread_bss", S_THREAD_LOCAL_ZEROFILL,
- typeTLVInitialZeroFill),
- ENTRY("__DATA", "__bss", S_ZEROFILL, typeZeroFill),
- ENTRY("__DATA", "__interposing", S_INTERPOSING, typeInterposingTuples),
-};
-#undef ENTRY
-
-SectionInfo *Util::getFinalSection(DefinedAtom::ContentType atomType) {
- for (auto &p : sectsToAtomType) {
- if (p.atomType != atomType)
- continue;
- SectionAttr sectionAttrs = 0;
- switch (atomType) {
- case DefinedAtom::typeMachHeader:
- case DefinedAtom::typeCode:
- case DefinedAtom::typeStub:
- case DefinedAtom::typeStubHelper:
- sectionAttrs = S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS;
- break;
- case DefinedAtom::typeThunkTLV:
- _hasTLVDescriptors = true;
- break;
- default:
- break;
- }
- // If we already have a SectionInfo with this name, re-use it.
- // This can happen if two ContentType map to the same mach-o section.
- for (auto sect : _sectionMap) {
- if (sect.second->sectionName.equals(p.sectionName) &&
- sect.second->segmentName.equals(p.segmentName)) {
- return sect.second;
- }
- }
- // Otherwise allocate new SectionInfo object.
- auto *sect = new (_allocator) SectionInfo(
- p.segmentName, p.sectionName, p.sectionType, _ctx, sectionAttrs,
- /* relocsToDefinedCanBeImplicit */ false);
- _sectionInfos.push_back(sect);
- _sectionMap[atomType] = sect;
- return sect;
- }
- llvm_unreachable("content type not yet supported");
-}
-
-SectionInfo *Util::sectionForAtom(const DefinedAtom *atom) {
- if (atom->sectionChoice() == DefinedAtom::sectionBasedOnContent) {
- // Section for this atom is derived from content type.
- DefinedAtom::ContentType type = atom->contentType();
- auto pos = _sectionMap.find(type);
- if ( pos != _sectionMap.end() )
- return pos->second;
- bool rMode = (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT);
- return rMode ? getRelocatableSection(type) : getFinalSection(type);
- } else {
- // This atom needs to be in a custom section.
- StringRef customName = atom->customSectionName();
- // Look to see if we have already allocated the needed custom section.
- for(SectionInfo *sect : _customSections) {
- const DefinedAtom *firstAtom = sect->atomsAndOffsets.front().atom;
- if (firstAtom->customSectionName().equals(customName)) {
- return sect;
- }
- }
- // Not found, so need to create a new custom section.
- size_t seperatorIndex = customName.find('/');
- assert(seperatorIndex != StringRef::npos);
- StringRef segName = customName.slice(0, seperatorIndex);
- StringRef sectName = customName.drop_front(seperatorIndex + 1);
- auto *sect =
- new (_allocator) SectionInfo(segName, sectName, S_REGULAR, _ctx,
- 0, /* relocsToDefinedCanBeImplicit */ false);
- _customSections.push_back(sect);
- _sectionInfos.push_back(sect);
- return sect;
- }
-}
-
-void Util::appendAtom(SectionInfo *sect, const DefinedAtom *atom) {
- // Figure out offset for atom in this section given alignment constraints.
- uint64_t offset = sect->size;
- DefinedAtom::Alignment atomAlign = atom->alignment();
- uint64_t align = atomAlign.value;
- uint64_t requiredModulus = atomAlign.modulus;
- uint64_t currentModulus = (offset % align);
- if ( currentModulus != requiredModulus ) {
- if ( requiredModulus > currentModulus )
- offset += requiredModulus-currentModulus;
- else
- offset += align+requiredModulus-currentModulus;
- }
- // Record max alignment of any atom in this section.
- if (align > sect->alignment)
- sect->alignment = atomAlign.value;
- // Assign atom to this section with this offset.
- AtomInfo ai = {atom, offset};
- sect->atomsAndOffsets.push_back(ai);
- // Update section size to include this atom.
- sect->size = offset + atom->size();
-}
-
-void Util::processDefinedAtoms(const lld::File &atomFile) {
- for (const DefinedAtom *atom : atomFile.defined()) {
- processAtomAttributes(atom);
- assignAtomToSection(atom);
- }
-}
-
-void Util::processAtomAttributes(const DefinedAtom *atom) {
- if (auto *machoFile = dyn_cast<mach_o::MachOFile>(&atom->file())) {
- // If the file doesn't use subsections via symbols, then make sure we don't
- // add that flag to the final output file if we have a relocatable file.
- if (!machoFile->subsectionsViaSymbols())
- _subsectionsViaSymbols = false;
-
- // All the source files must have min versions for us to output an object
- // file with a min version.
- if (auto v = machoFile->minVersion())
- _minVersion = std::max(_minVersion, v);
- else
- _allSourceFilesHaveMinVersions = false;
-
- // If we don't have a platform load command, but one of the source files
- // does, then take the one from the file.
- if (!_minVersionCommandType)
- if (auto v = machoFile->minVersionLoadCommandKind())
- _minVersionCommandType = v;
- }
-}
-
-void Util::assignAtomToSection(const DefinedAtom *atom) {
- if (atom->contentType() == DefinedAtom::typeMachHeader) {
- _machHeaderAliasAtoms.push_back(atom);
- // Assign atom to this section with this offset.
- AtomInfo ai = {atom, 0};
- sectionForAtom(atom)->atomsAndOffsets.push_back(ai);
- } else if (atom->contentType() == DefinedAtom::typeDSOHandle)
- _machHeaderAliasAtoms.push_back(atom);
- else
- appendAtom(sectionForAtom(atom), atom);
-}
-
-SegmentInfo *Util::segmentForName(StringRef segName) {
- for (SegmentInfo *si : _segmentInfos) {
- if ( si->name.equals(segName) )
- return si;
- }
- auto *info = new (_allocator) SegmentInfo(segName);
-
- // Set the initial segment protection.
- if (segName.equals("__TEXT"))
- info->init_access = VM_PROT_READ | VM_PROT_EXECUTE;
- else if (segName.equals("__PAGEZERO"))
- info->init_access = 0;
- else if (segName.equals("__LINKEDIT"))
- info->init_access = VM_PROT_READ;
- else {
- // All others default to read-write
- info->init_access = VM_PROT_READ | VM_PROT_WRITE;
- }
-
- // Set max segment protection
- // Note, its overkill to use a switch statement here, but makes it so much
- // easier to use switch coverage to catch new cases.
- switch (_ctx.os()) {
- case lld::MachOLinkingContext::OS::unknown:
- case lld::MachOLinkingContext::OS::macOSX:
- case lld::MachOLinkingContext::OS::iOS_simulator:
- if (segName.equals("__PAGEZERO")) {
- info->max_access = 0;
- break;
- }
- // All others default to all
- info->max_access = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
- break;
- case lld::MachOLinkingContext::OS::iOS:
- // iPhoneOS always uses same protection for max and initial
- info->max_access = info->init_access;
- break;
- }
- _segmentInfos.push_back(info);
- return info;
-}
-
-unsigned Util::SegmentSorter::weight(const SegmentInfo *seg) {
- return llvm::StringSwitch<unsigned>(seg->name)
- .Case("__PAGEZERO", 1)
- .Case("__TEXT", 2)
- .Case("__DATA", 3)
- .Default(100);
-}
-
-bool Util::SegmentSorter::operator()(const SegmentInfo *left,
- const SegmentInfo *right) {
- return (weight(left) < weight(right));
-}
-
-unsigned Util::TextSectionSorter::weight(const SectionInfo *sect) {
- return llvm::StringSwitch<unsigned>(sect->sectionName)
- .Case("__text", 1)
- .Case("__stubs", 2)
- .Case("__stub_helper", 3)
- .Case("__const", 4)
- .Case("__cstring", 5)
- .Case("__unwind_info", 98)
- .Case("__eh_frame", 99)
- .Default(10);
-}
-
-bool Util::TextSectionSorter::operator()(const SectionInfo *left,
- const SectionInfo *right) {
- return (weight(left) < weight(right));
-}
-
-void Util::organizeSections() {
- // NOTE!: Keep this in sync with assignAddressesToSections.
- switch (_ctx.outputMachOType()) {
- case llvm::MachO::MH_EXECUTE:
- // Main executables, need a zero-page segment
- segmentForName("__PAGEZERO");
- // Fall into next case.
- LLVM_FALLTHROUGH;
- case llvm::MachO::MH_DYLIB:
- case llvm::MachO::MH_BUNDLE:
- // All dynamic code needs TEXT segment to hold the load commands.
- segmentForName("__TEXT");
- break;
- default:
- break;
- }
- segmentForName("__LINKEDIT");
-
- // Group sections into segments.
- for (SectionInfo *si : _sectionInfos) {
- SegmentInfo *seg = segmentForName(si->segmentName);
- seg->sections.push_back(si);
- }
- // Sort segments.
- std::sort(_segmentInfos.begin(), _segmentInfos.end(), SegmentSorter());
-
- // Sort sections within segments.
- for (SegmentInfo *seg : _segmentInfos) {
- if (seg->name.equals("__TEXT")) {
- std::sort(seg->sections.begin(), seg->sections.end(),
- TextSectionSorter());
- }
- }
-
- // Record final section indexes.
- uint32_t segmentIndex = 0;
- uint32_t sectionIndex = 1;
- for (SegmentInfo *seg : _segmentInfos) {
- seg->normalizedSegmentIndex = segmentIndex++;
- for (SectionInfo *sect : seg->sections)
- sect->finalSectionIndex = sectionIndex++;
- }
-}
-
-void Util::layoutSectionsInSegment(SegmentInfo *seg, uint64_t &addr) {
- seg->address = addr;
- for (SectionInfo *sect : seg->sections) {
- sect->address = llvm::alignTo(addr, sect->alignment);
- addr = sect->address + sect->size;
- }
- seg->size = llvm::alignTo(addr - seg->address, _ctx.pageSize());
-}
-
-// __TEXT segment lays out backwards so padding is at front after load commands.
-void Util::layoutSectionsInTextSegment(size_t hlcSize, SegmentInfo *seg,
- uint64_t &addr) {
- seg->address = addr;
- // Walks sections starting at end to calculate padding for start.
- int64_t taddr = 0;
- for (auto it = seg->sections.rbegin(); it != seg->sections.rend(); ++it) {
- SectionInfo *sect = *it;
- taddr -= sect->size;
- taddr = taddr & (0 - sect->alignment);
- }
- int64_t padding = taddr - hlcSize;
- while (padding < 0)
- padding += _ctx.pageSize();
- // Start assigning section address starting at padded offset.
- addr += (padding + hlcSize);
- for (SectionInfo *sect : seg->sections) {
- sect->address = llvm::alignTo(addr, sect->alignment);
- addr = sect->address + sect->size;
- }
- seg->size = llvm::alignTo(addr - seg->address, _ctx.pageSize());
-}
-
-void Util::assignAddressesToSections(const NormalizedFile &file) {
- // NOTE!: Keep this in sync with organizeSections.
- size_t hlcSize = headerAndLoadCommandsSize(file,
- _ctx.generateFunctionStartsLoadCommand());
- uint64_t address = 0;
- for (SegmentInfo *seg : _segmentInfos) {
- if (seg->name.equals("__PAGEZERO")) {
- seg->size = _ctx.pageZeroSize();
- address += seg->size;
- }
- else if (seg->name.equals("__TEXT")) {
- // _ctx.baseAddress() == 0 implies it was either unspecified or
- // pageZeroSize is also 0. In either case resetting address is safe.
- address = _ctx.baseAddress() ? _ctx.baseAddress() : address;
- layoutSectionsInTextSegment(hlcSize, seg, address);
- } else
- layoutSectionsInSegment(seg, address);
-
- address = llvm::alignTo(address, _ctx.pageSize());
- }
- DEBUG_WITH_TYPE("WriterMachO-norm",
- llvm::dbgs() << "assignAddressesToSections()\n";
- for (SegmentInfo *sgi : _segmentInfos) {
- llvm::dbgs() << " address=" << llvm::format("0x%08llX", sgi->address)
- << ", size=" << llvm::format("0x%08llX", sgi->size)
- << ", segment-name='" << sgi->name
- << "'\n";
- for (SectionInfo *si : sgi->sections) {
- llvm::dbgs()<< " addr=" << llvm::format("0x%08llX", si->address)
- << ", size=" << llvm::format("0x%08llX", si->size)
- << ", section-name='" << si->sectionName
- << "\n";
- }
- }
- );
-}
-
-void Util::copySegmentInfo(NormalizedFile &file) {
- for (SegmentInfo *sgi : _segmentInfos) {
- Segment seg;
- seg.name = sgi->name;
- seg.address = sgi->address;
- seg.size = sgi->size;
- seg.init_access = sgi->init_access;
- seg.max_access = sgi->max_access;
- file.segments.push_back(seg);
- }
-}
-
-void Util::appendSection(SectionInfo *si, NormalizedFile &file) {
- // Add new empty section to end of file.sections.
- Section temp;
- file.sections.push_back(std::move(temp));
- Section* normSect = &file.sections.back();
- // Copy fields to normalized section.
- normSect->segmentName = si->segmentName;
- normSect->sectionName = si->sectionName;
- normSect->type = si->type;
- normSect->attributes = si->attributes;
- normSect->address = si->address;
- normSect->alignment = si->alignment;
- // Record where normalized section is.
- si->normalizedSectionIndex = file.sections.size()-1;
-}
-
-void Util::copySectionContent(NormalizedFile &file) {
- const bool r = (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT);
-
- // Utility function for ArchHandler to find address of atom in output file.
- auto addrForAtom = [&] (const Atom &atom) -> uint64_t {
- auto pos = _atomToAddress.find(&atom);
- assert(pos != _atomToAddress.end());
- return pos->second;
- };
-
- auto sectionAddrForAtom = [&] (const Atom &atom) -> uint64_t {
- for (const SectionInfo *sectInfo : _sectionInfos)
- for (const AtomInfo &atomInfo : sectInfo->atomsAndOffsets)
- if (atomInfo.atom == &atom)
- return sectInfo->address;
- llvm_unreachable("atom not assigned to section");
- };
-
- for (SectionInfo *si : _sectionInfos) {
- Section *normSect = &file.sections[si->normalizedSectionIndex];
- if (isZeroFillSection(si->type)) {
- const uint8_t *empty = nullptr;
- normSect->content = llvm::makeArrayRef(empty, si->size);
- continue;
- }
- // Copy content from atoms to content buffer for section.
- llvm::MutableArrayRef<uint8_t> sectionContent;
- if (si->size) {
- uint8_t *sectContent = file.ownedAllocations.Allocate<uint8_t>(si->size);
- sectionContent = llvm::MutableArrayRef<uint8_t>(sectContent, si->size);
- normSect->content = sectionContent;
- }
- for (AtomInfo &ai : si->atomsAndOffsets) {
- if (!ai.atom->size()) {
- assert(ai.atom->begin() == ai.atom->end() &&
- "Cannot have references without content");
- continue;
- }
- auto atomContent = sectionContent.slice(ai.offsetInSection,
- ai.atom->size());
- _archHandler.generateAtomContent(*ai.atom, r, addrForAtom,
- sectionAddrForAtom, _ctx.baseAddress(),
- atomContent);
- }
- }
-}
-
-void Util::copySectionInfo(NormalizedFile &file) {
- file.sections.reserve(_sectionInfos.size());
- // Write sections grouped by segment.
- for (SegmentInfo *sgi : _segmentInfos) {
- for (SectionInfo *si : sgi->sections) {
- appendSection(si, file);
- }
- }
-}
-
-void Util::updateSectionInfo(NormalizedFile &file) {
- file.sections.reserve(_sectionInfos.size());
- // sections grouped by segment.
- for (SegmentInfo *sgi : _segmentInfos) {
- Segment *normSeg = &file.segments[sgi->normalizedSegmentIndex];
- normSeg->address = sgi->address;
- normSeg->size = sgi->size;
- for (SectionInfo *si : sgi->sections) {
- Section *normSect = &file.sections[si->normalizedSectionIndex];
- normSect->address = si->address;
- }
- }
-}
-
-void Util::copyEntryPointAddress(NormalizedFile &nFile) {
- if (!_entryAtom) {
- nFile.entryAddress = 0;
- return;
- }
-
- if (_ctx.outputTypeHasEntry()) {
- if (_archHandler.isThumbFunction(*_entryAtom))
- nFile.entryAddress = (_atomToAddress[_entryAtom] | 1);
- else
- nFile.entryAddress = _atomToAddress[_entryAtom];
- }
-}
-
-void Util::buildAtomToAddressMap() {
- DEBUG_WITH_TYPE("WriterMachO-address", llvm::dbgs()
- << "assign atom addresses:\n");
- const bool lookForEntry = _ctx.outputTypeHasEntry();
- for (SectionInfo *sect : _sectionInfos) {
- for (const AtomInfo &info : sect->atomsAndOffsets) {
- _atomToAddress[info.atom] = sect->address + info.offsetInSection;
- if (lookForEntry && (info.atom->contentType() == DefinedAtom::typeCode) &&
- (info.atom->size() != 0) &&
- info.atom->name() == _ctx.entrySymbolName()) {
- _entryAtom = info.atom;
- }
- DEBUG_WITH_TYPE("WriterMachO-address", llvm::dbgs()
- << " address="
- << llvm::format("0x%016X", _atomToAddress[info.atom])
- << llvm::format(" 0x%09lX", info.atom)
- << ", file=#"
- << info.atom->file().ordinal()
- << ", atom=#"
- << info.atom->ordinal()
- << ", name="
- << info.atom->name()
- << ", type="
- << info.atom->contentType()
- << "\n");
- }
- }
- DEBUG_WITH_TYPE("WriterMachO-address", llvm::dbgs()
- << "assign header alias atom addresses:\n");
- for (const Atom *atom : _machHeaderAliasAtoms) {
- _atomToAddress[atom] = _ctx.baseAddress();
-#ifndef NDEBUG
- if (auto *definedAtom = dyn_cast<DefinedAtom>(atom)) {
- DEBUG_WITH_TYPE("WriterMachO-address", llvm::dbgs()
- << " address="
- << llvm::format("0x%016X", _atomToAddress[atom])
- << llvm::format(" 0x%09lX", atom)
- << ", file=#"
- << definedAtom->file().ordinal()
- << ", atom=#"
- << definedAtom->ordinal()
- << ", name="
- << definedAtom->name()
- << ", type="
- << definedAtom->contentType()
- << "\n");
- } else {
- DEBUG_WITH_TYPE("WriterMachO-address", llvm::dbgs()
- << " address="
- << llvm::format("0x%016X", _atomToAddress[atom])
- << " atom=" << atom
- << " name=" << atom->name() << "\n");
- }
-#endif
- }
-}
-
-llvm::Error Util::synthesizeDebugNotes(NormalizedFile &file) {
-
- // Bail out early if we don't need to generate a debug map.
- if (_ctx.debugInfoMode() == MachOLinkingContext::DebugInfoMode::noDebugMap)
- return llvm::Error::success();
-
- std::vector<const DefinedAtom*> atomsNeedingDebugNotes;
- std::set<const mach_o::MachOFile*> filesWithStabs;
- bool objFileHasDwarf = false;
- const File *objFile = nullptr;
-
- for (SectionInfo *sect : _sectionInfos) {
- for (const AtomInfo &info : sect->atomsAndOffsets) {
- if (const DefinedAtom *atom = dyn_cast<DefinedAtom>(info.atom)) {
-
- // FIXME: No stabs/debug-notes for symbols that wouldn't be in the
- // symbol table.
- // FIXME: No stabs/debug-notes for kernel dtrace probes.
-
- if (atom->contentType() == DefinedAtom::typeCFI ||
- atom->contentType() == DefinedAtom::typeCString)
- continue;
-
- // Whenever we encounter a new file, update the 'objfileHasDwarf' flag.
- if (&info.atom->file() != objFile) {
- objFileHasDwarf = false;
- if (const mach_o::MachOFile *atomFile =
- dyn_cast<mach_o::MachOFile>(&info.atom->file())) {
- if (atomFile->debugInfo()) {
- if (isa<mach_o::DwarfDebugInfo>(atomFile->debugInfo()))
- objFileHasDwarf = true;
- else if (isa<mach_o::StabsDebugInfo>(atomFile->debugInfo()))
- filesWithStabs.insert(atomFile);
- }
- }
- }
-
- // If this atom is from a file that needs dwarf, add it to the list.
- if (objFileHasDwarf)
- atomsNeedingDebugNotes.push_back(info.atom);
- }
- }
- }
-
- // Sort atoms needing debug notes by file ordinal, then atom ordinal.
- std::sort(atomsNeedingDebugNotes.begin(), atomsNeedingDebugNotes.end(),
- [](const DefinedAtom *lhs, const DefinedAtom *rhs) {
- if (lhs->file().ordinal() != rhs->file().ordinal())
- return (lhs->file().ordinal() < rhs->file().ordinal());
- return (lhs->ordinal() < rhs->ordinal());
- });
-
- // FIXME: Handle <rdar://problem/17689030>: Add -add_ast_path option to \
- // linker which add N_AST stab entry to output
- // See OutputFile::synthesizeDebugNotes in ObjectFile.cpp in ld64.
-
- StringRef oldFileName = "";
- StringRef oldDirPath = "";
- bool wroteStartSO = false;
- std::unordered_set<std::string> seenFiles;
- for (const DefinedAtom *atom : atomsNeedingDebugNotes) {
- const auto &atomFile = cast<mach_o::MachOFile>(atom->file());
- assert(dyn_cast_or_null<lld::mach_o::DwarfDebugInfo>(atomFile.debugInfo())
- && "file for atom needing debug notes does not contain dwarf");
- auto &dwarf = cast<lld::mach_o::DwarfDebugInfo>(*atomFile.debugInfo());
-
- auto &tu = dwarf.translationUnitSource();
- StringRef newFileName = tu.name;
- StringRef newDirPath = tu.path;
-
- // Add an SO whenever the TU source file changes.
- if (newFileName != oldFileName || newDirPath != oldDirPath) {
- // Translation unit change, emit ending SO
- if (oldFileName != "")
- _stabs.push_back(mach_o::Stab(nullptr, N_SO, 1, 0, 0, ""));
-
- oldFileName = newFileName;
- oldDirPath = newDirPath;
-
- // If newDirPath doesn't end with a '/' we need to add one:
- if (newDirPath.back() != '/') {
- char *p =
- file.ownedAllocations.Allocate<char>(newDirPath.size() + 2);
- memcpy(p, newDirPath.data(), newDirPath.size());
- p[newDirPath.size()] = '/';
- p[newDirPath.size() + 1] = '\0';
- newDirPath = p;
- }
-
- // New translation unit, emit start SOs:
- _stabs.push_back(mach_o::Stab(nullptr, N_SO, 0, 0, 0, newDirPath));
- _stabs.push_back(mach_o::Stab(nullptr, N_SO, 0, 0, 0, newFileName));
-
- // Synthesize OSO for start of file.
- char *fullPath = nullptr;
- {
- SmallString<1024> pathBuf(atomFile.path());
- if (auto EC = llvm::sys::fs::make_absolute(pathBuf))
- return llvm::errorCodeToError(EC);
- fullPath = file.ownedAllocations.Allocate<char>(pathBuf.size() + 1);
- memcpy(fullPath, pathBuf.c_str(), pathBuf.size() + 1);
- }
-
- // Get mod time.
- uint32_t modTime = 0;
- llvm::sys::fs::file_status stat;
- if (!llvm::sys::fs::status(fullPath, stat))
- if (llvm::sys::fs::exists(stat))
- modTime = llvm::sys::toTimeT(stat.getLastModificationTime());
-
- _stabs.push_back(mach_o::Stab(nullptr, N_OSO, _ctx.getCPUSubType(), 1,
- modTime, fullPath));
- // <rdar://problem/6337329> linker should put cpusubtype in n_sect field
- // of nlist entry for N_OSO debug note entries.
- wroteStartSO = true;
- }
-
- if (atom->contentType() == DefinedAtom::typeCode) {
- // Synthesize BNSYM and start FUN stabs.
- _stabs.push_back(mach_o::Stab(atom, N_BNSYM, 1, 0, 0, ""));
- _stabs.push_back(mach_o::Stab(atom, N_FUN, 1, 0, 0, atom->name()));
- // Synthesize any SOL stabs needed
- // FIXME: add SOL stabs.
- _stabs.push_back(mach_o::Stab(nullptr, N_FUN, 0, 0,
- atom->rawContent().size(), ""));
- _stabs.push_back(mach_o::Stab(nullptr, N_ENSYM, 1, 0,
- atom->rawContent().size(), ""));
- } else {
- if (atom->scope() == Atom::scopeTranslationUnit)
- _stabs.push_back(mach_o::Stab(atom, N_STSYM, 1, 0, 0, atom->name()));
- else
- _stabs.push_back(mach_o::Stab(nullptr, N_GSYM, 1, 0, 0, atom->name()));
- }
- }
-
- // Emit ending SO if necessary.
- if (wroteStartSO)
- _stabs.push_back(mach_o::Stab(nullptr, N_SO, 1, 0, 0, ""));
-
- // Copy any stabs from .o file.
- for (const auto *objFile : filesWithStabs) {
- const auto &stabsList =
- cast<mach_o::StabsDebugInfo>(objFile->debugInfo())->stabs();
- for (auto &stab : stabsList) {
- // FIXME: Drop stabs whose atoms have been dead-stripped.
- _stabs.push_back(stab);
- }
- }
-
- return llvm::Error::success();
-}
-
-uint16_t Util::descBits(const DefinedAtom* atom) {
- uint16_t desc = 0;
- switch (atom->merge()) {
- case lld::DefinedAtom::mergeNo:
- case lld::DefinedAtom::mergeAsTentative:
- break;
- case lld::DefinedAtom::mergeAsWeak:
- case lld::DefinedAtom::mergeAsWeakAndAddressUsed:
- desc |= N_WEAK_DEF;
- break;
- case lld::DefinedAtom::mergeSameNameAndSize:
- case lld::DefinedAtom::mergeByLargestSection:
- case lld::DefinedAtom::mergeByContent:
- llvm_unreachable("Unsupported DefinedAtom::merge()");
- break;
- }
- if (atom->contentType() == lld::DefinedAtom::typeResolver)
- desc |= N_SYMBOL_RESOLVER;
- if (atom->contentType() == lld::DefinedAtom::typeMachHeader)
- desc |= REFERENCED_DYNAMICALLY;
- if (_archHandler.isThumbFunction(*atom))
- desc |= N_ARM_THUMB_DEF;
- if (atom->deadStrip() == DefinedAtom::deadStripNever &&
- _ctx.outputMachOType() == llvm::MachO::MH_OBJECT) {
- if ((atom->contentType() != DefinedAtom::typeInitializerPtr)
- && (atom->contentType() != DefinedAtom::typeTerminatorPtr))
- desc |= N_NO_DEAD_STRIP;
- }
- return desc;
-}
-
-bool Util::AtomSorter::operator()(const AtomAndIndex &left,
- const AtomAndIndex &right) {
- return (left.atom->name().compare(right.atom->name()) < 0);
-}
-
-llvm::Error Util::getSymbolTableRegion(const DefinedAtom* atom,
- bool &inGlobalsRegion,
- SymbolScope &scope) {
- bool rMode = (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT);
- switch (atom->scope()) {
- case Atom::scopeTranslationUnit:
- scope = 0;
- inGlobalsRegion = false;
- return llvm::Error::success();
- case Atom::scopeLinkageUnit:
- if ((_ctx.exportMode() == MachOLinkingContext::ExportMode::exported) &&
- _ctx.exportSymbolNamed(atom->name())) {
- return llvm::make_error<GenericError>(
- Twine("cannot export hidden symbol ") + atom->name());
- }
- if (rMode) {
- if (_ctx.keepPrivateExterns()) {
- // -keep_private_externs means keep in globals region as N_PEXT.
- scope = N_PEXT | N_EXT;
- inGlobalsRegion = true;
- return llvm::Error::success();
- }
- }
- // scopeLinkageUnit symbols are no longer global once linked.
- scope = N_PEXT;
- inGlobalsRegion = false;
- return llvm::Error::success();
- case Atom::scopeGlobal:
- if (_ctx.exportRestrictMode()) {
- if (_ctx.exportSymbolNamed(atom->name())) {
- scope = N_EXT;
- inGlobalsRegion = true;
- return llvm::Error::success();
- } else {
- scope = N_PEXT;
- inGlobalsRegion = false;
- return llvm::Error::success();
- }
- } else {
- scope = N_EXT;
- inGlobalsRegion = true;
- return llvm::Error::success();
- }
- break;
- }
- llvm_unreachable("atom->scope() unknown enum value");
-}
-
-
-
-llvm::Error Util::addSymbols(const lld::File &atomFile,
- NormalizedFile &file) {
- bool rMode = (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT);
- // Mach-O symbol table has four regions: stabs, locals, globals, undefs.
-
- // Add all stabs.
- for (auto &stab : _stabs) {
- lld::mach_o::normalized::Symbol sym;
- sym.type = static_cast<NListType>(stab.type);
- sym.scope = 0;
- sym.sect = stab.other;
- sym.desc = stab.desc;
- if (stab.atom)
- sym.value = _atomToAddress[stab.atom];
- else
- sym.value = stab.value;
- sym.name = stab.str;
- file.stabsSymbols.push_back(sym);
- }
-
- // Add all local (non-global) symbols in address order
- std::vector<AtomAndIndex> globals;
- globals.reserve(512);
- for (SectionInfo *sect : _sectionInfos) {
- for (const AtomInfo &info : sect->atomsAndOffsets) {
- const DefinedAtom *atom = info.atom;
- if (!atom->name().empty()) {
- SymbolScope symbolScope;
- bool inGlobalsRegion;
- if (auto ec = getSymbolTableRegion(atom, inGlobalsRegion, symbolScope)){
- return ec;
- }
- if (inGlobalsRegion) {
- AtomAndIndex ai = { atom, sect->finalSectionIndex, symbolScope };
- globals.push_back(ai);
- } else {
- lld::mach_o::normalized::Symbol sym;
- sym.name = atom->name();
- sym.type = N_SECT;
- sym.scope = symbolScope;
- sym.sect = sect->finalSectionIndex;
- sym.desc = descBits(atom);
- sym.value = _atomToAddress[atom];
- _atomToSymbolIndex[atom] = file.localSymbols.size();
- file.localSymbols.push_back(sym);
- }
- } else if (rMode && _archHandler.needsLocalSymbolInRelocatableFile(atom)){
- // Create 'Lxxx' labels for anonymous atoms if archHandler says so.
- static unsigned tempNum = 1;
- char tmpName[16];
- sprintf(tmpName, "L%04u", tempNum++);
- StringRef tempRef(tmpName);
- lld::mach_o::normalized::Symbol sym;
- sym.name = tempRef.copy(file.ownedAllocations);
- sym.type = N_SECT;
- sym.scope = 0;
- sym.sect = sect->finalSectionIndex;
- sym.desc = 0;
- sym.value = _atomToAddress[atom];
- _atomToSymbolIndex[atom] = file.localSymbols.size();
- file.localSymbols.push_back(sym);
- }
- }
- }
-
- // Sort global symbol alphabetically, then add to symbol table.
- std::sort(globals.begin(), globals.end(), AtomSorter());
- const uint32_t globalStartIndex = file.localSymbols.size();
- for (AtomAndIndex &ai : globals) {
- lld::mach_o::normalized::Symbol sym;
- sym.name = ai.atom->name();
- sym.type = N_SECT;
- sym.scope = ai.scope;
- sym.sect = ai.index;
- sym.desc = descBits(static_cast<const DefinedAtom*>(ai.atom));
- sym.value = _atomToAddress[ai.atom];
- _atomToSymbolIndex[ai.atom] = globalStartIndex + file.globalSymbols.size();
- file.globalSymbols.push_back(sym);
- }
-
- // Sort undefined symbol alphabetically, then add to symbol table.
- std::vector<AtomAndIndex> undefs;
- undefs.reserve(128);
- for (const UndefinedAtom *atom : atomFile.undefined()) {
- AtomAndIndex ai = { atom, 0, N_EXT };
- undefs.push_back(ai);
- }
- for (const SharedLibraryAtom *atom : atomFile.sharedLibrary()) {
- AtomAndIndex ai = { atom, 0, N_EXT };
- undefs.push_back(ai);
- }
- std::sort(undefs.begin(), undefs.end(), AtomSorter());
- const uint32_t start = file.globalSymbols.size() + file.localSymbols.size();
- for (AtomAndIndex &ai : undefs) {
- lld::mach_o::normalized::Symbol sym;
- uint16_t desc = 0;
- if (!rMode) {
- uint8_t ordinal = 0;
- if (!_ctx.useFlatNamespace())
- ordinal = dylibOrdinal(dyn_cast<SharedLibraryAtom>(ai.atom));
- llvm::MachO::SET_LIBRARY_ORDINAL(desc, ordinal);
- }
- sym.name = ai.atom->name();
- sym.type = N_UNDF;
- sym.scope = ai.scope;
- sym.sect = 0;
- sym.desc = desc;
- sym.value = 0;
- _atomToSymbolIndex[ai.atom] = file.undefinedSymbols.size() + start;
- file.undefinedSymbols.push_back(sym);
- }
-
- return llvm::Error::success();
-}
-
-const Atom *Util::targetOfLazyPointer(const DefinedAtom *lpAtom) {
- for (const Reference *ref : *lpAtom) {
- if (_archHandler.isLazyPointer(*ref)) {
- return ref->target();
- }
- }
- return nullptr;
-}
-
-const Atom *Util::targetOfStub(const DefinedAtom *stubAtom) {
- for (const Reference *ref : *stubAtom) {
- if (const Atom *ta = ref->target()) {
- if (const DefinedAtom *lpAtom = dyn_cast<DefinedAtom>(ta)) {
- const Atom *target = targetOfLazyPointer(lpAtom);
- if (target)
- return target;
- }
- }
- }
- return nullptr;
-}
-
-void Util::addIndirectSymbols(const lld::File &atomFile, NormalizedFile &file) {
- for (SectionInfo *si : _sectionInfos) {
- Section &normSect = file.sections[si->normalizedSectionIndex];
- switch (si->type) {
- case llvm::MachO::S_NON_LAZY_SYMBOL_POINTERS:
- for (const AtomInfo &info : si->atomsAndOffsets) {
- bool foundTarget = false;
- for (const Reference *ref : *info.atom) {
- const Atom *target = ref->target();
- if (target) {
- if (isa<const SharedLibraryAtom>(target)) {
- uint32_t index = _atomToSymbolIndex[target];
- normSect.indirectSymbols.push_back(index);
- foundTarget = true;
- } else {
- normSect.indirectSymbols.push_back(
- llvm::MachO::INDIRECT_SYMBOL_LOCAL);
- }
- }
- }
- if (!foundTarget) {
- normSect.indirectSymbols.push_back(
- llvm::MachO::INDIRECT_SYMBOL_ABS);
- }
- }
- break;
- case llvm::MachO::S_LAZY_SYMBOL_POINTERS:
- for (const AtomInfo &info : si->atomsAndOffsets) {
- const Atom *target = targetOfLazyPointer(info.atom);
- if (target) {
- uint32_t index = _atomToSymbolIndex[target];
- normSect.indirectSymbols.push_back(index);
- }
- }
- break;
- case llvm::MachO::S_SYMBOL_STUBS:
- for (const AtomInfo &info : si->atomsAndOffsets) {
- const Atom *target = targetOfStub(info.atom);
- if (target) {
- uint32_t index = _atomToSymbolIndex[target];
- normSect.indirectSymbols.push_back(index);
- }
- }
- break;
- default:
- break;
- }
- }
-}
-
-void Util::addDependentDylibs(const lld::File &atomFile,
- NormalizedFile &nFile) {
- // Scan all imported symbols and build up list of dylibs they are from.
- int ordinal = 1;
- for (const auto *dylib : _ctx.allDylibs()) {
- DylibPathToInfo::iterator pos = _dylibInfo.find(dylib->installName());
- if (pos == _dylibInfo.end()) {
- DylibInfo info;
- bool flatNamespaceAtom = dylib == _ctx.flatNamespaceFile();
-
- // If we're in -flat_namespace mode (or this atom came from the flat
- // namespace file under -undefined dynamic_lookup) then use the flat
- // lookup ordinal.
- if (flatNamespaceAtom || _ctx.useFlatNamespace())
- info.ordinal = BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
- else
- info.ordinal = ordinal++;
- info.hasWeak = false;
- info.hasNonWeak = !info.hasWeak;
- _dylibInfo[dylib->installName()] = info;
-
- // Unless this was a flat_namespace atom, record the source dylib.
- if (!flatNamespaceAtom) {
- DependentDylib depInfo;
- depInfo.path = dylib->installName();
- depInfo.kind = llvm::MachO::LC_LOAD_DYLIB;
- depInfo.currentVersion = _ctx.dylibCurrentVersion(dylib->path());
- depInfo.compatVersion = _ctx.dylibCompatVersion(dylib->path());
- nFile.dependentDylibs.push_back(depInfo);
- }
- } else {
- pos->second.hasWeak = false;
- pos->second.hasNonWeak = !pos->second.hasWeak;
- }
- }
- // Automatically weak link dylib in which all symbols are weak (canBeNull).
- for (DependentDylib &dep : nFile.dependentDylibs) {
- DylibInfo &info = _dylibInfo[dep.path];
- if (info.hasWeak && !info.hasNonWeak)
- dep.kind = llvm::MachO::LC_LOAD_WEAK_DYLIB;
- else if (_ctx.isUpwardDylib(dep.path))
- dep.kind = llvm::MachO::LC_LOAD_UPWARD_DYLIB;
- }
-}
-
-int Util::dylibOrdinal(const SharedLibraryAtom *sa) {
- return _dylibInfo[sa->loadName()].ordinal;
-}
-
-void Util::segIndexForSection(const SectionInfo *sect, uint8_t &segmentIndex,
- uint64_t &segmentStartAddr) {
- segmentIndex = 0;
- for (const SegmentInfo *seg : _segmentInfos) {
- if ((seg->address <= sect->address)
- && (seg->address+seg->size >= sect->address+sect->size)) {
- segmentStartAddr = seg->address;
- return;
- }
- ++segmentIndex;
- }
- llvm_unreachable("section not in any segment");
-}
-
-uint32_t Util::sectionIndexForAtom(const Atom *atom) {
- uint64_t address = _atomToAddress[atom];
- for (const SectionInfo *si : _sectionInfos) {
- if ((si->address <= address) && (address < si->address+si->size))
- return si->finalSectionIndex;
- }
- llvm_unreachable("atom not in any section");
-}
-
-void Util::addSectionRelocs(const lld::File &, NormalizedFile &file) {
- if (_ctx.outputMachOType() != llvm::MachO::MH_OBJECT)
- return;
-
- // Utility function for ArchHandler to find symbol index for an atom.
- auto symIndexForAtom = [&] (const Atom &atom) -> uint32_t {
- auto pos = _atomToSymbolIndex.find(&atom);
- assert(pos != _atomToSymbolIndex.end());
- return pos->second;
- };
-
- // Utility function for ArchHandler to find section index for an atom.
- auto sectIndexForAtom = [&] (const Atom &atom) -> uint32_t {
- return sectionIndexForAtom(&atom);
- };
-
- // Utility function for ArchHandler to find address of atom in output file.
- auto addressForAtom = [&] (const Atom &atom) -> uint64_t {
- auto pos = _atomToAddress.find(&atom);
- assert(pos != _atomToAddress.end());
- return pos->second;
- };
-
- for (SectionInfo *si : _sectionInfos) {
- Section &normSect = file.sections[si->normalizedSectionIndex];
- for (const AtomInfo &info : si->atomsAndOffsets) {
- const DefinedAtom *atom = info.atom;
- for (const Reference *ref : *atom) {
- // Skip emitting relocs for sections which are always able to be
- // implicitly regenerated and where the relocation targets an address
- // which is defined.
- if (si->relocsToDefinedCanBeImplicit && isa<DefinedAtom>(ref->target()))
- continue;
- _archHandler.appendSectionRelocations(*atom, info.offsetInSection, *ref,
- symIndexForAtom,
- sectIndexForAtom,
- addressForAtom,
- normSect.relocations);
- }
- }
- }
-}
-
-void Util::addFunctionStarts(const lld::File &, NormalizedFile &file) {
- if (!_ctx.generateFunctionStartsLoadCommand())
- return;
- file.functionStarts.reserve(8192);
- // Delta compress function starts, starting with the mach header symbol.
- const uint64_t badAddress = ~0ULL;
- uint64_t addr = badAddress;
- for (SectionInfo *si : _sectionInfos) {
- for (const AtomInfo &info : si->atomsAndOffsets) {
- auto type = info.atom->contentType();
- if (type == DefinedAtom::typeMachHeader) {
- addr = _atomToAddress[info.atom];
- continue;
- }
- if (type != DefinedAtom::typeCode)
- continue;
- assert(addr != badAddress && "Missing mach header symbol");
- // Skip atoms which have 0 size. This is so that LC_FUNCTION_STARTS
- // can't spill in to the next section.
- if (!info.atom->size())
- continue;
- uint64_t nextAddr = _atomToAddress[info.atom];
- if (_archHandler.isThumbFunction(*info.atom))
- nextAddr |= 1;
- uint64_t delta = nextAddr - addr;
- if (delta) {
- ByteBuffer buffer;
- buffer.append_uleb128(delta);
- file.functionStarts.insert(file.functionStarts.end(), buffer.bytes(),
- buffer.bytes() + buffer.size());
- }
- addr = nextAddr;
- }
- }
-
- // Null terminate, and pad to pointer size for this arch.
- file.functionStarts.push_back(0);
-
- auto size = file.functionStarts.size();
- for (unsigned i = size, e = llvm::alignTo(size, _ctx.is64Bit() ? 8 : 4);
- i != e; ++i)
- file.functionStarts.push_back(0);
-}
-
-void Util::buildDataInCodeArray(const lld::File &, NormalizedFile &file) {
- if (!_ctx.generateDataInCodeLoadCommand())
- return;
- for (SectionInfo *si : _sectionInfos) {
- for (const AtomInfo &info : si->atomsAndOffsets) {
- // Atoms that contain data-in-code have "transition" references
- // which mark a point where the embedded data starts of ends.
- // This needs to be converted to the mach-o format which is an array
- // of data-in-code ranges.
- uint32_t startOffset = 0;
- DataRegionType mode = DataRegionType(0);
- for (const Reference *ref : *info.atom) {
- if (ref->kindNamespace() != Reference::KindNamespace::mach_o)
- continue;
- if (_archHandler.isDataInCodeTransition(ref->kindValue())) {
- DataRegionType nextMode = (DataRegionType)ref->addend();
- if (mode != nextMode) {
- if (mode != 0) {
- // Found end data range, so make range entry.
- DataInCode entry;
- entry.offset = si->address + info.offsetInSection + startOffset;
- entry.length = ref->offsetInAtom() - startOffset;
- entry.kind = mode;
- file.dataInCode.push_back(entry);
- }
- }
- mode = nextMode;
- startOffset = ref->offsetInAtom();
- }
- }
- if (mode != 0) {
- // Function ends with data (no end transition).
- DataInCode entry;
- entry.offset = si->address + info.offsetInSection + startOffset;
- entry.length = info.atom->size() - startOffset;
- entry.kind = mode;
- file.dataInCode.push_back(entry);
- }
- }
- }
-}
-
-void Util::addRebaseAndBindingInfo(const lld::File &atomFile,
- NormalizedFile &nFile) {
- if (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT)
- return;
-
- uint8_t segmentIndex;
- uint64_t segmentStartAddr;
- uint32_t offsetInBindInfo = 0;
-
- for (SectionInfo *sect : _sectionInfos) {
- segIndexForSection(sect, segmentIndex, segmentStartAddr);
- for (const AtomInfo &info : sect->atomsAndOffsets) {
- const DefinedAtom *atom = info.atom;
- for (const Reference *ref : *atom) {
- uint64_t segmentOffset = _atomToAddress[atom] + ref->offsetInAtom()
- - segmentStartAddr;
- const Atom* targ = ref->target();
- if (_archHandler.isPointer(*ref)) {
- // A pointer to a DefinedAtom requires rebasing.
- if (isa<DefinedAtom>(targ)) {
- RebaseLocation rebase;
- rebase.segIndex = segmentIndex;
- rebase.segOffset = segmentOffset;
- rebase.kind = llvm::MachO::REBASE_TYPE_POINTER;
- nFile.rebasingInfo.push_back(rebase);
- }
- // A pointer to an SharedLibraryAtom requires binding.
- if (const SharedLibraryAtom *sa = dyn_cast<SharedLibraryAtom>(targ)) {
- BindLocation bind;
- bind.segIndex = segmentIndex;
- bind.segOffset = segmentOffset;
- bind.kind = llvm::MachO::BIND_TYPE_POINTER;
- bind.canBeNull = sa->canBeNullAtRuntime();
- bind.ordinal = dylibOrdinal(sa);
- bind.symbolName = targ->name();
- bind.addend = ref->addend();
- nFile.bindingInfo.push_back(bind);
- }
- }
- else if (_archHandler.isLazyPointer(*ref)) {
- BindLocation bind;
- if (const SharedLibraryAtom *sa = dyn_cast<SharedLibraryAtom>(targ)) {
- bind.ordinal = dylibOrdinal(sa);
- } else {
- bind.ordinal = llvm::MachO::BIND_SPECIAL_DYLIB_SELF;
- }
- bind.segIndex = segmentIndex;
- bind.segOffset = segmentOffset;
- bind.kind = llvm::MachO::BIND_TYPE_POINTER;
- bind.canBeNull = false; //sa->canBeNullAtRuntime();
- bind.symbolName = targ->name();
- bind.addend = ref->addend();
- nFile.lazyBindingInfo.push_back(bind);
-
- // Now that we know the segmentOffset and the ordinal attribute,
- // we can fix the helper's code
-
- fixLazyReferenceImm(atom, offsetInBindInfo, nFile);
-
- // 5 bytes for opcodes + variable sizes (target name + \0 and offset
- // encode's size)
- offsetInBindInfo +=
- 6 + targ->name().size() + llvm::getULEB128Size(bind.segOffset);
- if (bind.ordinal > BIND_IMMEDIATE_MASK)
- offsetInBindInfo += llvm::getULEB128Size(bind.ordinal);
- }
- }
- }
- }
-}
-
-void Util::fixLazyReferenceImm(const DefinedAtom *atom, uint32_t offset,
- NormalizedFile &file) {
- for (const Reference *ref : *atom) {
- const DefinedAtom *da = dyn_cast<DefinedAtom>(ref->target());
- if (da == nullptr)
- return;
-
- const Reference *helperRef = nullptr;
- for (const Reference *hr : *da) {
- if (hr->kindValue() == _archHandler.lazyImmediateLocationKind()) {
- helperRef = hr;
- break;
- }
- }
- if (helperRef == nullptr)
- continue;
-
- // TODO: maybe get the fixed atom content from _archHandler ?
- for (SectionInfo *sectInfo : _sectionInfos) {
- for (const AtomInfo &atomInfo : sectInfo->atomsAndOffsets) {
- if (atomInfo.atom == helperRef->target()) {
- auto sectionContent =
- file.sections[sectInfo->normalizedSectionIndex].content;
- uint8_t *rawb =
- file.ownedAllocations.Allocate<uint8_t>(sectionContent.size());
- llvm::MutableArrayRef<uint8_t> newContent{rawb,
- sectionContent.size()};
- std::copy(sectionContent.begin(), sectionContent.end(),
- newContent.begin());
- llvm::support::ulittle32_t *loc =
- reinterpret_cast<llvm::support::ulittle32_t *>(
- &newContent[atomInfo.offsetInSection +
- helperRef->offsetInAtom()]);
- *loc = offset;
- file.sections[sectInfo->normalizedSectionIndex].content = newContent;
- }
- }
- }
- }
-}
-
-void Util::addExportInfo(const lld::File &atomFile, NormalizedFile &nFile) {
- if (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT)
- return;
-
- for (SectionInfo *sect : _sectionInfos) {
- for (const AtomInfo &info : sect->atomsAndOffsets) {
- const DefinedAtom *atom = info.atom;
- if (atom->scope() != Atom::scopeGlobal)
- continue;
- if (_ctx.exportRestrictMode()) {
- if (!_ctx.exportSymbolNamed(atom->name()))
- continue;
- }
- Export exprt;
- exprt.name = atom->name();
- exprt.offset = _atomToAddress[atom] - _ctx.baseAddress();
- exprt.kind = EXPORT_SYMBOL_FLAGS_KIND_REGULAR;
- if (atom->merge() == DefinedAtom::mergeAsWeak)
- exprt.flags = EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION;
- else
- exprt.flags = 0;
- exprt.otherOffset = 0;
- exprt.otherName = StringRef();
- nFile.exportInfo.push_back(exprt);
- }
- }
-}
-
-uint32_t Util::fileFlags() {
- // FIXME: these need to determined at runtime.
- if (_ctx.outputMachOType() == MH_OBJECT) {
- return _subsectionsViaSymbols ? (uint32_t)MH_SUBSECTIONS_VIA_SYMBOLS : 0;
- } else {
- uint32_t flags = MH_DYLDLINK;
- if (!_ctx.useFlatNamespace())
- flags |= MH_TWOLEVEL | MH_NOUNDEFS;
- if ((_ctx.outputMachOType() == MH_EXECUTE) && _ctx.PIE())
- flags |= MH_PIE;
- if (_hasTLVDescriptors)
- flags |= (MH_PIE | MH_HAS_TLV_DESCRIPTORS);
- return flags;
- }
-}
-
-} // end anonymous namespace
-
-namespace lld {
-namespace mach_o {
-namespace normalized {
-
-/// Convert a set of Atoms into a normalized mach-o file.
-llvm::Expected<std::unique_ptr<NormalizedFile>>
-normalizedFromAtoms(const lld::File &atomFile,
- const MachOLinkingContext &context) {
- // The util object buffers info until the normalized file can be made.
- Util util(context);
- util.processDefinedAtoms(atomFile);
- util.organizeSections();
-
- std::unique_ptr<NormalizedFile> f(new NormalizedFile());
- NormalizedFile &normFile = *f.get();
- normFile.arch = context.arch();
- normFile.fileType = context.outputMachOType();
- normFile.flags = util.fileFlags();
- normFile.stackSize = context.stackSize();
- normFile.installName = context.installName();
- normFile.currentVersion = context.currentVersion();
- normFile.compatVersion = context.compatibilityVersion();
- normFile.os = context.os();
-
- // If we are emitting an object file, then the min version is the maximum
- // of the min's of all the source files and the cmdline.
- if (normFile.fileType == llvm::MachO::MH_OBJECT)
- normFile.minOSverson = std::max(context.osMinVersion(), util.minVersion());
- else
- normFile.minOSverson = context.osMinVersion();
-
- normFile.minOSVersionKind = util.minVersionCommandType();
-
- normFile.sdkVersion = context.sdkVersion();
- normFile.sourceVersion = context.sourceVersion();
-
- if (context.generateVersionLoadCommand() &&
- context.os() != MachOLinkingContext::OS::unknown)
- normFile.hasMinVersionLoadCommand = true;
- else if (normFile.fileType == llvm::MachO::MH_OBJECT &&
- util.allSourceFilesHaveMinVersions() &&
- ((normFile.os != MachOLinkingContext::OS::unknown) ||
- util.minVersionCommandType())) {
- // If we emit an object file, then it should contain a min version load
- // command if all of the source files also contained min version commands.
- // Also, we either need to have a platform, or found a platform from the
- // source object files.
- normFile.hasMinVersionLoadCommand = true;
- }
- normFile.generateDataInCodeLoadCommand =
- context.generateDataInCodeLoadCommand();
- normFile.pageSize = context.pageSize();
- normFile.rpaths = context.rpaths();
- util.addDependentDylibs(atomFile, normFile);
- util.copySegmentInfo(normFile);
- util.copySectionInfo(normFile);
- util.assignAddressesToSections(normFile);
- util.buildAtomToAddressMap();
- if (auto err = util.synthesizeDebugNotes(normFile))
- return std::move(err);
- util.updateSectionInfo(normFile);
- util.copySectionContent(normFile);
- if (auto ec = util.addSymbols(atomFile, normFile)) {
- return std::move(ec);
- }
- util.addIndirectSymbols(atomFile, normFile);
- util.addRebaseAndBindingInfo(atomFile, normFile);
- util.addExportInfo(atomFile, normFile);
- util.addSectionRelocs(atomFile, normFile);
- util.addFunctionStarts(atomFile, normFile);
- util.buildDataInCodeArray(atomFile, normFile);
- util.copyEntryPointAddress(normFile);
-
- return std::move(f);
-}
-
-} // namespace normalized
-} // namespace mach_o
-} // namespace lld
+++ /dev/null
-//===- lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp --------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-///
-/// \file Converts from in-memory normalized mach-o to in-memory Atoms.
-///
-/// +------------+
-/// | normalized |
-/// +------------+
-/// |
-/// |
-/// v
-/// +-------+
-/// | Atoms |
-/// +-------+
-
-#include "ArchHandler.h"
-#include "Atoms.h"
-#include "File.h"
-#include "MachONormalizedFile.h"
-#include "MachONormalizedFileBinaryUtils.h"
-#include "lld/Common/LLVM.h"
-#include "lld/Core/Error.h"
-#include "llvm/BinaryFormat/Dwarf.h"
-#include "llvm/BinaryFormat/MachO.h"
-#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
-#include "llvm/Support/DataExtractor.h"
-#include "llvm/Support/Debug.h"
-#include "llvm/Support/Error.h"
-#include "llvm/Support/Format.h"
-#include "llvm/Support/LEB128.h"
-#include "llvm/Support/raw_ostream.h"
-
-using namespace llvm::MachO;
-using namespace lld::mach_o::normalized;
-
-#define DEBUG_TYPE "normalized-file-to-atoms"
-
-namespace lld {
-namespace mach_o {
-
-
-namespace { // anonymous
-
-
-#define ENTRY(seg, sect, type, atomType) \
- {seg, sect, type, DefinedAtom::atomType }
-
-struct MachORelocatableSectionToAtomType {
- StringRef segmentName;
- StringRef sectionName;
- SectionType sectionType;
- DefinedAtom::ContentType atomType;
-};
-
-const MachORelocatableSectionToAtomType sectsToAtomType[] = {
- ENTRY("__TEXT", "__text", S_REGULAR, typeCode),
- ENTRY("__TEXT", "__text", S_REGULAR, typeResolver),
- ENTRY("__TEXT", "__cstring", S_CSTRING_LITERALS, typeCString),
- ENTRY("", "", S_CSTRING_LITERALS, typeCString),
- ENTRY("__TEXT", "__ustring", S_REGULAR, typeUTF16String),
- ENTRY("__TEXT", "__const", S_REGULAR, typeConstant),
- ENTRY("__TEXT", "__const_coal", S_COALESCED, typeConstant),
- ENTRY("__TEXT", "__eh_frame", S_COALESCED, typeCFI),
- ENTRY("__TEXT", "__eh_frame", S_REGULAR, typeCFI),
- ENTRY("__TEXT", "__literal4", S_4BYTE_LITERALS, typeLiteral4),
- ENTRY("__TEXT", "__literal8", S_8BYTE_LITERALS, typeLiteral8),
- ENTRY("__TEXT", "__literal16", S_16BYTE_LITERALS, typeLiteral16),
- ENTRY("__TEXT", "__gcc_except_tab", S_REGULAR, typeLSDA),
- ENTRY("__DATA", "__data", S_REGULAR, typeData),
- ENTRY("__DATA", "__datacoal_nt", S_COALESCED, typeData),
- ENTRY("__DATA", "__const", S_REGULAR, typeConstData),
- ENTRY("__DATA", "__cfstring", S_REGULAR, typeCFString),
- ENTRY("__DATA", "__mod_init_func", S_MOD_INIT_FUNC_POINTERS,
- typeInitializerPtr),
- ENTRY("__DATA", "__mod_term_func", S_MOD_TERM_FUNC_POINTERS,
- typeTerminatorPtr),
- ENTRY("__DATA", "__got", S_NON_LAZY_SYMBOL_POINTERS,
- typeGOT),
- ENTRY("__DATA", "__bss", S_ZEROFILL, typeZeroFill),
- ENTRY("", "", S_NON_LAZY_SYMBOL_POINTERS,
- typeGOT),
- ENTRY("__DATA", "__interposing", S_INTERPOSING, typeInterposingTuples),
- ENTRY("__DATA", "__thread_vars", S_THREAD_LOCAL_VARIABLES,
- typeThunkTLV),
- ENTRY("__DATA", "__thread_data", S_THREAD_LOCAL_REGULAR, typeTLVInitialData),
- ENTRY("__DATA", "__thread_bss", S_THREAD_LOCAL_ZEROFILL,
- typeTLVInitialZeroFill),
- ENTRY("__DATA", "__objc_imageinfo", S_REGULAR, typeObjCImageInfo),
- ENTRY("__DATA", "__objc_catlist", S_REGULAR, typeObjC2CategoryList),
- ENTRY("", "", S_INTERPOSING, typeInterposingTuples),
- ENTRY("__LD", "__compact_unwind", S_REGULAR,
- typeCompactUnwindInfo),
- ENTRY("", "", S_REGULAR, typeUnknown)
-};
-#undef ENTRY
-
-
-/// Figures out ContentType of a mach-o section.
-DefinedAtom::ContentType atomTypeFromSection(const Section §ion,
- bool &customSectionName) {
- // First look for match of name and type. Empty names in table are wildcards.
- customSectionName = false;
- for (const MachORelocatableSectionToAtomType *p = sectsToAtomType ;
- p->atomType != DefinedAtom::typeUnknown; ++p) {
- if (p->sectionType != section.type)
- continue;
- if (!p->segmentName.equals(section.segmentName) && !p->segmentName.empty())
- continue;
- if (!p->sectionName.equals(section.sectionName) && !p->sectionName.empty())
- continue;
- customSectionName = p->segmentName.empty() && p->sectionName.empty();
- return p->atomType;
- }
- // Look for code denoted by section attributes
- if (section.attributes & S_ATTR_PURE_INSTRUCTIONS)
- return DefinedAtom::typeCode;
-
- return DefinedAtom::typeUnknown;
-}
-
-enum AtomizeModel {
- atomizeAtSymbols,
- atomizeFixedSize,
- atomizePointerSize,
- atomizeUTF8,
- atomizeUTF16,
- atomizeCFI,
- atomizeCU,
- atomizeCFString
-};
-
-/// Returns info on how to atomize a section of the specified ContentType.
-void sectionParseInfo(DefinedAtom::ContentType atomType,
- unsigned int &sizeMultiple,
- DefinedAtom::Scope &scope,
- DefinedAtom::Merge &merge,
- AtomizeModel &atomizeModel) {
- struct ParseInfo {
- DefinedAtom::ContentType atomType;
- unsigned int sizeMultiple;
- DefinedAtom::Scope scope;
- DefinedAtom::Merge merge;
- AtomizeModel atomizeModel;
- };
-
- #define ENTRY(type, size, scope, merge, model) \
- {DefinedAtom::type, size, DefinedAtom::scope, DefinedAtom::merge, model }
-
- static const ParseInfo parseInfo[] = {
- ENTRY(typeCode, 1, scopeGlobal, mergeNo,
- atomizeAtSymbols),
- ENTRY(typeData, 1, scopeGlobal, mergeNo,
- atomizeAtSymbols),
- ENTRY(typeConstData, 1, scopeGlobal, mergeNo,
- atomizeAtSymbols),
- ENTRY(typeZeroFill, 1, scopeGlobal, mergeNo,
- atomizeAtSymbols),
- ENTRY(typeConstant, 1, scopeGlobal, mergeNo,
- atomizeAtSymbols),
- ENTRY(typeCString, 1, scopeLinkageUnit, mergeByContent,
- atomizeUTF8),
- ENTRY(typeUTF16String, 1, scopeLinkageUnit, mergeByContent,
- atomizeUTF16),
- ENTRY(typeCFI, 4, scopeTranslationUnit, mergeNo,
- atomizeCFI),
- ENTRY(typeLiteral4, 4, scopeLinkageUnit, mergeByContent,
- atomizeFixedSize),
- ENTRY(typeLiteral8, 8, scopeLinkageUnit, mergeByContent,
- atomizeFixedSize),
- ENTRY(typeLiteral16, 16, scopeLinkageUnit, mergeByContent,
- atomizeFixedSize),
- ENTRY(typeCFString, 4, scopeLinkageUnit, mergeByContent,
- atomizeCFString),
- ENTRY(typeInitializerPtr, 4, scopeTranslationUnit, mergeNo,
- atomizePointerSize),
- ENTRY(typeTerminatorPtr, 4, scopeTranslationUnit, mergeNo,
- atomizePointerSize),
- ENTRY(typeCompactUnwindInfo, 4, scopeTranslationUnit, mergeNo,
- atomizeCU),
- ENTRY(typeGOT, 4, scopeLinkageUnit, mergeByContent,
- atomizePointerSize),
- ENTRY(typeObjC2CategoryList, 4, scopeTranslationUnit, mergeByContent,
- atomizePointerSize),
- ENTRY(typeUnknown, 1, scopeGlobal, mergeNo,
- atomizeAtSymbols)
- };
- #undef ENTRY
- const int tableLen = sizeof(parseInfo) / sizeof(ParseInfo);
- for (int i=0; i < tableLen; ++i) {
- if (parseInfo[i].atomType == atomType) {
- sizeMultiple = parseInfo[i].sizeMultiple;
- scope = parseInfo[i].scope;
- merge = parseInfo[i].merge;
- atomizeModel = parseInfo[i].atomizeModel;
- return;
- }
- }
-
- // Unknown type is atomized by symbols.
- sizeMultiple = 1;
- scope = DefinedAtom::scopeGlobal;
- merge = DefinedAtom::mergeNo;
- atomizeModel = atomizeAtSymbols;
-}
-
-
-Atom::Scope atomScope(uint8_t scope) {
- switch (scope) {
- case N_EXT:
- return Atom::scopeGlobal;
- case N_PEXT:
- case N_PEXT | N_EXT:
- return Atom::scopeLinkageUnit;
- case 0:
- return Atom::scopeTranslationUnit;
- }
- llvm_unreachable("unknown scope value!");
-}
-
-void appendSymbolsInSection(
- const std::vector<lld::mach_o::normalized::Symbol> &inSymbols,
- uint32_t sectionIndex,
- SmallVector<const lld::mach_o::normalized::Symbol *, 64> &outSyms) {
- for (const lld::mach_o::normalized::Symbol &sym : inSymbols) {
- // Only look at definition symbols.
- if ((sym.type & N_TYPE) != N_SECT)
- continue;
- if (sym.sect != sectionIndex)
- continue;
- outSyms.push_back(&sym);
- }
-}
-
-void atomFromSymbol(DefinedAtom::ContentType atomType, const Section §ion,
- MachOFile &file, uint64_t symbolAddr, StringRef symbolName,
- uint16_t symbolDescFlags, Atom::Scope symbolScope,
- uint64_t nextSymbolAddr, bool scatterable, bool copyRefs) {
- // Mach-O symbol table does have size in it. Instead the size is the
- // difference between this and the next symbol.
- uint64_t size = nextSymbolAddr - symbolAddr;
- uint64_t offset = symbolAddr - section.address;
- bool noDeadStrip = (symbolDescFlags & N_NO_DEAD_STRIP) || !scatterable;
- if (isZeroFillSection(section.type)) {
- file.addZeroFillDefinedAtom(symbolName, symbolScope, offset, size,
- noDeadStrip, copyRefs, §ion);
- } else {
- DefinedAtom::Merge merge = (symbolDescFlags & N_WEAK_DEF)
- ? DefinedAtom::mergeAsWeak : DefinedAtom::mergeNo;
- bool thumb = (symbolDescFlags & N_ARM_THUMB_DEF);
- if (atomType == DefinedAtom::typeUnknown) {
- // Mach-O needs a segment and section name. Concatenate those two
- // with a / separator (e.g. "seg/sect") to fit into the lld model
- // of just a section name.
- std::string segSectName = section.segmentName.str()
- + "/" + section.sectionName.str();
- file.addDefinedAtomInCustomSection(symbolName, symbolScope, atomType,
- merge, thumb, noDeadStrip, offset,
- size, segSectName, true, §ion);
- } else {
- if ((atomType == lld::DefinedAtom::typeCode) &&
- (symbolDescFlags & N_SYMBOL_RESOLVER)) {
- atomType = lld::DefinedAtom::typeResolver;
- }
- file.addDefinedAtom(symbolName, symbolScope, atomType, merge,
- offset, size, thumb, noDeadStrip, copyRefs, §ion);
- }
- }
-}
-
-llvm::Error processSymboledSection(DefinedAtom::ContentType atomType,
- const Section §ion,
- const NormalizedFile &normalizedFile,
- MachOFile &file, bool scatterable,
- bool copyRefs) {
- // Find section's index.
- uint32_t sectIndex = 1;
- for (auto § : normalizedFile.sections) {
- if (§ == §ion)
- break;
- ++sectIndex;
- }
-
- // Find all symbols in this section.
- SmallVector<const lld::mach_o::normalized::Symbol *, 64> symbols;
- appendSymbolsInSection(normalizedFile.globalSymbols, sectIndex, symbols);
- appendSymbolsInSection(normalizedFile.localSymbols, sectIndex, symbols);
-
- // Sort symbols.
- std::sort(symbols.begin(), symbols.end(),
- [](const lld::mach_o::normalized::Symbol *lhs,
- const lld::mach_o::normalized::Symbol *rhs) -> bool {
- if (lhs == rhs)
- return false;
- // First by address.
- uint64_t lhsAddr = lhs->value;
- uint64_t rhsAddr = rhs->value;
- if (lhsAddr != rhsAddr)
- return lhsAddr < rhsAddr;
- // If same address, one is an alias so sort by scope.
- Atom::Scope lScope = atomScope(lhs->scope);
- Atom::Scope rScope = atomScope(rhs->scope);
- if (lScope != rScope)
- return lScope < rScope;
- // If same address and scope, see if one might be better as
- // the alias.
- bool lPrivate = (lhs->name.front() == 'l');
- bool rPrivate = (rhs->name.front() == 'l');
- if (lPrivate != rPrivate)
- return lPrivate;
- // If same address and scope, sort by name.
- return lhs->name < rhs->name;
- });
-
- // Debug logging of symbols.
- // for (const Symbol *sym : symbols)
- // llvm::errs() << " sym: "
- // << llvm::format("0x%08llx ", (uint64_t)sym->value)
- // << ", " << sym->name << "\n";
-
- // If section has no symbols and no content, there are no atoms.
- if (symbols.empty() && section.content.empty())
- return llvm::Error::success();
-
- if (symbols.empty()) {
- // Section has no symbols, put all content in one anonymous atom.
- atomFromSymbol(atomType, section, file, section.address, StringRef(),
- 0, Atom::scopeTranslationUnit,
- section.address + section.content.size(),
- scatterable, copyRefs);
- }
- else if (symbols.front()->value != section.address) {
- // Section has anonymous content before first symbol.
- atomFromSymbol(atomType, section, file, section.address, StringRef(),
- 0, Atom::scopeTranslationUnit, symbols.front()->value,
- scatterable, copyRefs);
- }
-
- const lld::mach_o::normalized::Symbol *lastSym = nullptr;
- for (const lld::mach_o::normalized::Symbol *sym : symbols) {
- if (lastSym != nullptr) {
- // Ignore any assembler added "ltmpNNN" symbol at start of section
- // if there is another symbol at the start.
- if ((lastSym->value != sym->value)
- || lastSym->value != section.address
- || !lastSym->name.startswith("ltmp")) {
- atomFromSymbol(atomType, section, file, lastSym->value, lastSym->name,
- lastSym->desc, atomScope(lastSym->scope), sym->value,
- scatterable, copyRefs);
- }
- }
- lastSym = sym;
- }
- if (lastSym != nullptr) {
- atomFromSymbol(atomType, section, file, lastSym->value, lastSym->name,
- lastSym->desc, atomScope(lastSym->scope),
- section.address + section.content.size(),
- scatterable, copyRefs);
- }
-
- // If object built without .subsections_via_symbols, add reference chain.
- if (!scatterable) {
- MachODefinedAtom *prevAtom = nullptr;
- file.eachAtomInSection(section,
- [&](MachODefinedAtom *atom, uint64_t offset)->void {
- if (prevAtom)
- prevAtom->addReference(Reference::KindNamespace::all,
- Reference::KindArch::all,
- Reference::kindLayoutAfter, 0, atom, 0);
- prevAtom = atom;
- });
- }
-
- return llvm::Error::success();
-}
-
-llvm::Error processSection(DefinedAtom::ContentType atomType,
- const Section §ion,
- bool customSectionName,
- const NormalizedFile &normalizedFile,
- MachOFile &file, bool scatterable,
- bool copyRefs) {
- const bool is64 = MachOLinkingContext::is64Bit(normalizedFile.arch);
- const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch);
-
- // Get info on how to atomize section.
- unsigned int sizeMultiple;
- DefinedAtom::Scope scope;
- DefinedAtom::Merge merge;
- AtomizeModel atomizeModel;
- sectionParseInfo(atomType, sizeMultiple, scope, merge, atomizeModel);
-
- // Validate section size.
- if ((section.content.size() % sizeMultiple) != 0)
- return llvm::make_error<GenericError>(Twine("Section ")
- + section.segmentName
- + "/" + section.sectionName
- + " has size ("
- + Twine(section.content.size())
- + ") which is not a multiple of "
- + Twine(sizeMultiple));
-
- if (atomizeModel == atomizeAtSymbols) {
- // Break section up into atoms each with a fixed size.
- return processSymboledSection(atomType, section, normalizedFile, file,
- scatterable, copyRefs);
- } else {
- unsigned int size;
- for (unsigned int offset = 0, e = section.content.size(); offset != e;) {
- switch (atomizeModel) {
- case atomizeFixedSize:
- // Break section up into atoms each with a fixed size.
- size = sizeMultiple;
- break;
- case atomizePointerSize:
- // Break section up into atoms each the size of a pointer.
- size = is64 ? 8 : 4;
- break;
- case atomizeUTF8:
- // Break section up into zero terminated c-strings.
- size = 0;
- for (unsigned int i = offset; i < e; ++i) {
- if (section.content[i] == 0) {
- size = i + 1 - offset;
- break;
- }
- }
- break;
- case atomizeUTF16:
- // Break section up into zero terminated UTF16 strings.
- size = 0;
- for (unsigned int i = offset; i < e; i += 2) {
- if ((section.content[i] == 0) && (section.content[i + 1] == 0)) {
- size = i + 2 - offset;
- break;
- }
- }
- break;
- case atomizeCFI:
- // Break section up into dwarf unwind CFIs (FDE or CIE).
- size = read32(§ion.content[offset], isBig) + 4;
- if (offset+size > section.content.size()) {
- return llvm::make_error<GenericError>(Twine("Section ")
- + section.segmentName
- + "/" + section.sectionName
- + " is malformed. Size of CFI "
- "starting at offset ("
- + Twine(offset)
- + ") is past end of section.");
- }
- break;
- case atomizeCU:
- // Break section up into compact unwind entries.
- size = is64 ? 32 : 20;
- break;
- case atomizeCFString:
- // Break section up into NS/CFString objects.
- size = is64 ? 32 : 16;
- break;
- case atomizeAtSymbols:
- break;
- }
- if (size == 0) {
- return llvm::make_error<GenericError>(Twine("Section ")
- + section.segmentName
- + "/" + section.sectionName
- + " is malformed. The last atom "
- "is not zero terminated.");
- }
- if (customSectionName) {
- // Mach-O needs a segment and section name. Concatenate those two
- // with a / separator (e.g. "seg/sect") to fit into the lld model
- // of just a section name.
- std::string segSectName = section.segmentName.str()
- + "/" + section.sectionName.str();
- file.addDefinedAtomInCustomSection(StringRef(), scope, atomType,
- merge, false, false, offset,
- size, segSectName, true, §ion);
- } else {
- file.addDefinedAtom(StringRef(), scope, atomType, merge, offset, size,
- false, false, copyRefs, §ion);
- }
- offset += size;
- }
- }
- return llvm::Error::success();
-}
-
-const Section* findSectionCoveringAddress(const NormalizedFile &normalizedFile,
- uint64_t address) {
- for (const Section &s : normalizedFile.sections) {
- uint64_t sAddr = s.address;
- if ((sAddr <= address) && (address < sAddr+s.content.size())) {
- return &s;
- }
- }
- return nullptr;
-}
-
-const MachODefinedAtom *
-findAtomCoveringAddress(const NormalizedFile &normalizedFile, MachOFile &file,
- uint64_t addr, Reference::Addend &addend) {
- const Section *sect = nullptr;
- sect = findSectionCoveringAddress(normalizedFile, addr);
- if (!sect)
- return nullptr;
-
- uint32_t offsetInTarget;
- uint64_t offsetInSect = addr - sect->address;
- auto atom =
- file.findAtomCoveringAddress(*sect, offsetInSect, &offsetInTarget);
- addend = offsetInTarget;
- return atom;
-}
-
-// Walks all relocations for a section in a normalized .o file and
-// creates corresponding lld::Reference objects.
-llvm::Error convertRelocs(const Section §ion,
- const NormalizedFile &normalizedFile,
- bool scatterable,
- MachOFile &file,
- ArchHandler &handler) {
- // Utility function for ArchHandler to find atom by its address.
- auto atomByAddr = [&] (uint32_t sectIndex, uint64_t addr,
- const lld::Atom **atom, Reference::Addend *addend)
- -> llvm::Error {
- if (sectIndex > normalizedFile.sections.size())
- return llvm::make_error<GenericError>(Twine("out of range section "
- "index (") + Twine(sectIndex) + ")");
- const Section *sect = nullptr;
- if (sectIndex == 0) {
- sect = findSectionCoveringAddress(normalizedFile, addr);
- if (!sect)
- return llvm::make_error<GenericError>(Twine("address (" + Twine(addr)
- + ") is not in any section"));
- } else {
- sect = &normalizedFile.sections[sectIndex-1];
- }
- uint32_t offsetInTarget;
- uint64_t offsetInSect = addr - sect->address;
- *atom = file.findAtomCoveringAddress(*sect, offsetInSect, &offsetInTarget);
- *addend = offsetInTarget;
- return llvm::Error::success();
- };
-
- // Utility function for ArchHandler to find atom by its symbol index.
- auto atomBySymbol = [&] (uint32_t symbolIndex, const lld::Atom **result)
- -> llvm::Error {
- // Find symbol from index.
- const lld::mach_o::normalized::Symbol *sym = nullptr;
- uint32_t numStabs = normalizedFile.stabsSymbols.size();
- uint32_t numLocal = normalizedFile.localSymbols.size();
- uint32_t numGlobal = normalizedFile.globalSymbols.size();
- uint32_t numUndef = normalizedFile.undefinedSymbols.size();
- assert(symbolIndex >= numStabs && "Searched for stab via atomBySymbol?");
- if (symbolIndex < numStabs+numLocal) {
- sym = &normalizedFile.localSymbols[symbolIndex-numStabs];
- } else if (symbolIndex < numStabs+numLocal+numGlobal) {
- sym = &normalizedFile.globalSymbols[symbolIndex-numStabs-numLocal];
- } else if (symbolIndex < numStabs+numLocal+numGlobal+numUndef) {
- sym = &normalizedFile.undefinedSymbols[symbolIndex-numStabs-numLocal-
- numGlobal];
- } else {
- return llvm::make_error<GenericError>(Twine("symbol index (")
- + Twine(symbolIndex) + ") out of range");
- }
-
- // Find atom from symbol.
- if ((sym->type & N_TYPE) == N_SECT) {
- if (sym->sect > normalizedFile.sections.size())
- return llvm::make_error<GenericError>(Twine("symbol section index (")
- + Twine(sym->sect) + ") out of range ");
- const Section &symSection = normalizedFile.sections[sym->sect-1];
- uint64_t targetOffsetInSect = sym->value - symSection.address;
- MachODefinedAtom *target = file.findAtomCoveringAddress(symSection,
- targetOffsetInSect);
- if (target) {
- *result = target;
- return llvm::Error::success();
- }
- return llvm::make_error<GenericError>("no atom found for defined symbol");
- } else if ((sym->type & N_TYPE) == N_UNDF) {
- const lld::Atom *target = file.findUndefAtom(sym->name);
- if (target) {
- *result = target;
- return llvm::Error::success();
- }
- return llvm::make_error<GenericError>("no undefined atom found for sym");
- } else {
- // Search undefs
- return llvm::make_error<GenericError>("no atom found for symbol");
- }
- };
-
- const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch);
- // Use old-school iterator so that paired relocations can be grouped.
- for (auto it=section.relocations.begin(), e=section.relocations.end();
- it != e; ++it) {
- const Relocation &reloc = *it;
- // Find atom this relocation is in.
- if (reloc.offset > section.content.size())
- return llvm::make_error<GenericError>(
- Twine("r_address (") + Twine(reloc.offset)
- + ") is larger than section size ("
- + Twine(section.content.size()) + ")");
- uint32_t offsetInAtom;
- MachODefinedAtom *inAtom = file.findAtomCoveringAddress(section,
- reloc.offset,
- &offsetInAtom);
- assert(inAtom && "r_address in range, should have found atom");
- uint64_t fixupAddress = section.address + reloc.offset;
-
- const lld::Atom *target = nullptr;
- Reference::Addend addend = 0;
- Reference::KindValue kind;
- if (handler.isPairedReloc(reloc)) {
- // Handle paired relocations together.
- const Relocation &reloc2 = *++it;
- auto relocErr = handler.getPairReferenceInfo(
- reloc, reloc2, inAtom, offsetInAtom, fixupAddress, isBig, scatterable,
- atomByAddr, atomBySymbol, &kind, &target, &addend);
- if (relocErr) {
- return handleErrors(std::move(relocErr),
- [&](std::unique_ptr<GenericError> GE) {
- return llvm::make_error<GenericError>(
- Twine("bad relocation (") + GE->getMessage()
- + ") in section "
- + section.segmentName + "/" + section.sectionName
- + " (r1_address=" + Twine::utohexstr(reloc.offset)
- + ", r1_type=" + Twine(reloc.type)
- + ", r1_extern=" + Twine(reloc.isExtern)
- + ", r1_length=" + Twine((int)reloc.length)
- + ", r1_pcrel=" + Twine(reloc.pcRel)
- + (!reloc.scattered ? (Twine(", r1_symbolnum=")
- + Twine(reloc.symbol))
- : (Twine(", r1_scattered=1, r1_value=")
- + Twine(reloc.value)))
- + ")"
- + ", (r2_address=" + Twine::utohexstr(reloc2.offset)
- + ", r2_type=" + Twine(reloc2.type)
- + ", r2_extern=" + Twine(reloc2.isExtern)
- + ", r2_length=" + Twine((int)reloc2.length)
- + ", r2_pcrel=" + Twine(reloc2.pcRel)
- + (!reloc2.scattered ? (Twine(", r2_symbolnum=")
- + Twine(reloc2.symbol))
- : (Twine(", r2_scattered=1, r2_value=")
- + Twine(reloc2.value)))
- + ")" );
- });
- }
- }
- else {
- // Use ArchHandler to convert relocation record into information
- // needed to instantiate an lld::Reference object.
- auto relocErr = handler.getReferenceInfo(
- reloc, inAtom, offsetInAtom, fixupAddress, isBig, atomByAddr,
- atomBySymbol, &kind, &target, &addend);
- if (relocErr) {
- return handleErrors(std::move(relocErr),
- [&](std::unique_ptr<GenericError> GE) {
- return llvm::make_error<GenericError>(
- Twine("bad relocation (") + GE->getMessage()
- + ") in section "
- + section.segmentName + "/" + section.sectionName
- + " (r_address=" + Twine::utohexstr(reloc.offset)
- + ", r_type=" + Twine(reloc.type)
- + ", r_extern=" + Twine(reloc.isExtern)
- + ", r_length=" + Twine((int)reloc.length)
- + ", r_pcrel=" + Twine(reloc.pcRel)
- + (!reloc.scattered ? (Twine(", r_symbolnum=") + Twine(reloc.symbol))
- : (Twine(", r_scattered=1, r_value=")
- + Twine(reloc.value)))
- + ")" );
- });
- }
- }
- // Instantiate an lld::Reference object and add to its atom.
- inAtom->addReference(Reference::KindNamespace::mach_o,
- handler.kindArch(),
- kind, offsetInAtom, target, addend);
- }
-
- return llvm::Error::success();
-}
-
-bool isDebugInfoSection(const Section §ion) {
- if ((section.attributes & S_ATTR_DEBUG) == 0)
- return false;
- return section.segmentName.equals("__DWARF");
-}
-
-static const Atom* findDefinedAtomByName(MachOFile &file, Twine name) {
- std::string strName = name.str();
- for (auto *atom : file.defined())
- if (atom->name() == strName)
- return atom;
- return nullptr;
-}
-
-static StringRef copyDebugString(StringRef str, BumpPtrAllocator &alloc) {
- char *strCopy = alloc.Allocate<char>(str.size() + 1);
- memcpy(strCopy, str.data(), str.size());
- strCopy[str.size()] = '\0';
- return strCopy;
-}
-
-llvm::Error parseStabs(MachOFile &file,
- const NormalizedFile &normalizedFile,
- bool copyRefs) {
-
- if (normalizedFile.stabsSymbols.empty())
- return llvm::Error::success();
-
- // FIXME: Kill this off when we can move to sane yaml parsing.
- std::unique_ptr<BumpPtrAllocator> allocator;
- if (copyRefs)
- allocator = std::make_unique<BumpPtrAllocator>();
-
- enum { start, inBeginEnd } state = start;
-
- const Atom *currentAtom = nullptr;
- uint64_t currentAtomAddress = 0;
- StabsDebugInfo::StabsList stabsList;
- for (const auto &stabSym : normalizedFile.stabsSymbols) {
- Stab stab(nullptr, stabSym.type, stabSym.sect, stabSym.desc,
- stabSym.value, stabSym.name);
- switch (state) {
- case start:
- switch (static_cast<StabType>(stabSym.type)) {
- case N_BNSYM:
- state = inBeginEnd;
- currentAtomAddress = stabSym.value;
- Reference::Addend addend;
- currentAtom = findAtomCoveringAddress(normalizedFile, file,
- currentAtomAddress, addend);
- if (addend != 0)
- return llvm::make_error<GenericError>(
- "Non-zero addend for BNSYM '" + stabSym.name + "' in " +
- file.path());
- if (currentAtom)
- stab.atom = currentAtom;
- else {
- // FIXME: ld64 just issues a warning here - should we match that?
- return llvm::make_error<GenericError>(
- "can't find atom for stabs BNSYM at " +
- Twine::utohexstr(stabSym.value) + " in " + file.path());
- }
- break;
- case N_SO:
- case N_OSO:
- // Not associated with an atom, just copy.
- if (copyRefs)
- stab.str = copyDebugString(stabSym.name, *allocator);
- else
- stab.str = stabSym.name;
- break;
- case N_GSYM: {
- auto colonIdx = stabSym.name.find(':');
- if (colonIdx != StringRef::npos) {
- StringRef name = stabSym.name.substr(0, colonIdx);
- currentAtom = findDefinedAtomByName(file, "_" + name);
- stab.atom = currentAtom;
- if (copyRefs)
- stab.str = copyDebugString(stabSym.name, *allocator);
- else
- stab.str = stabSym.name;
- } else {
- currentAtom = findDefinedAtomByName(file, stabSym.name);
- stab.atom = currentAtom;
- if (copyRefs)
- stab.str = copyDebugString(stabSym.name, *allocator);
- else
- stab.str = stabSym.name;
- }
- if (stab.atom == nullptr)
- return llvm::make_error<GenericError>(
- "can't find atom for N_GSYM stabs" + stabSym.name +
- " in " + file.path());
- break;
- }
- case N_FUN:
- return llvm::make_error<GenericError>(
- "old-style N_FUN stab '" + stabSym.name + "' unsupported");
- default:
- return llvm::make_error<GenericError>(
- "unrecognized stab symbol '" + stabSym.name + "'");
- }
- break;
- case inBeginEnd:
- stab.atom = currentAtom;
- switch (static_cast<StabType>(stabSym.type)) {
- case N_ENSYM:
- state = start;
- currentAtom = nullptr;
- break;
- case N_FUN:
- // Just copy the string.
- if (copyRefs)
- stab.str = copyDebugString(stabSym.name, *allocator);
- else
- stab.str = stabSym.name;
- break;
- default:
- return llvm::make_error<GenericError>(
- "unrecognized stab symbol '" + stabSym.name + "'");
- }
- }
- llvm::dbgs() << "Adding to stabsList: " << stab << "\n";
- stabsList.push_back(stab);
- }
-
- file.setDebugInfo(std::make_unique<StabsDebugInfo>(std::move(stabsList)));
-
- // FIXME: Kill this off when we fix YAML memory ownership.
- file.debugInfo()->setAllocator(std::move(allocator));
-
- return llvm::Error::success();
-}
-
-static llvm::DataExtractor
-dataExtractorFromSection(const NormalizedFile &normalizedFile,
- const Section &S) {
- const bool is64 = MachOLinkingContext::is64Bit(normalizedFile.arch);
- const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch);
- StringRef SecData(reinterpret_cast<const char*>(S.content.data()),
- S.content.size());
- return llvm::DataExtractor(SecData, !isBig, is64 ? 8 : 4);
-}
-
-// FIXME: Cribbed from llvm-dwp -- should share "lightweight CU DIE
-// inspection" code if possible.
-static uint64_t getCUAbbrevOffset(llvm::DataExtractor abbrevData,
- uint64_t abbrCode) {
- uint64_t offset = 0;
- while (abbrevData.getULEB128(&offset) != abbrCode) {
- // Tag
- abbrevData.getULEB128(&offset);
- // DW_CHILDREN
- abbrevData.getU8(&offset);
- // Attributes
- while (abbrevData.getULEB128(&offset) | abbrevData.getULEB128(&offset))
- ;
- }
- return offset;
-}
-
-// FIXME: Cribbed from llvm-dwp -- should share "lightweight CU DIE
-// inspection" code if possible.
-static Expected<const char *>
-getIndexedString(const NormalizedFile &normalizedFile,
- llvm::dwarf::Form form, llvm::DataExtractor infoData,
- uint64_t &infoOffset, const Section &stringsSection) {
- if (form == llvm::dwarf::DW_FORM_string)
- return infoData.getCStr(&infoOffset);
- if (form != llvm::dwarf::DW_FORM_strp)
- return llvm::make_error<GenericError>(
- "string field encoded without DW_FORM_strp");
- uint64_t stringOffset = infoData.getU32(&infoOffset);
- llvm::DataExtractor stringsData =
- dataExtractorFromSection(normalizedFile, stringsSection);
- return stringsData.getCStr(&stringOffset);
-}
-
-// FIXME: Cribbed from llvm-dwp -- should share "lightweight CU DIE
-// inspection" code if possible.
-static llvm::Expected<TranslationUnitSource>
-readCompUnit(const NormalizedFile &normalizedFile,
- const Section &info,
- const Section &abbrev,
- const Section &strings,
- StringRef path) {
- // FIXME: Cribbed from llvm-dwp -- should share "lightweight CU DIE
- // inspection" code if possible.
- uint64_t offset = 0;
- llvm::dwarf::DwarfFormat Format = llvm::dwarf::DwarfFormat::DWARF32;
- auto infoData = dataExtractorFromSection(normalizedFile, info);
- uint32_t length = infoData.getU32(&offset);
- if (length == llvm::dwarf::DW_LENGTH_DWARF64) {
- Format = llvm::dwarf::DwarfFormat::DWARF64;
- infoData.getU64(&offset);
- }
- else if (length >= llvm::dwarf::DW_LENGTH_lo_reserved)
- return llvm::make_error<GenericError>("Malformed DWARF in " + path);
-
- uint16_t version = infoData.getU16(&offset);
-
- if (version < 2 || version > 4)
- return llvm::make_error<GenericError>("Unsupported DWARF version in " +
- path);
-
- infoData.getU32(&offset); // Abbrev offset (should be zero)
- uint8_t addrSize = infoData.getU8(&offset);
-
- uint32_t abbrCode = infoData.getULEB128(&offset);
- auto abbrevData = dataExtractorFromSection(normalizedFile, abbrev);
- uint64_t abbrevOffset = getCUAbbrevOffset(abbrevData, abbrCode);
- uint64_t tag = abbrevData.getULEB128(&abbrevOffset);
- if (tag != llvm::dwarf::DW_TAG_compile_unit)
- return llvm::make_error<GenericError>("top level DIE is not a compile unit");
- // DW_CHILDREN
- abbrevData.getU8(&abbrevOffset);
- uint32_t name;
- llvm::dwarf::Form form;
- llvm::dwarf::FormParams formParams = {version, addrSize, Format};
- TranslationUnitSource tu;
- while ((name = abbrevData.getULEB128(&abbrevOffset)) |
- (form = static_cast<llvm::dwarf::Form>(
- abbrevData.getULEB128(&abbrevOffset))) &&
- (name != 0 || form != 0)) {
- switch (name) {
- case llvm::dwarf::DW_AT_name: {
- if (auto eName = getIndexedString(normalizedFile, form, infoData, offset,
- strings))
- tu.name = *eName;
- else
- return eName.takeError();
- break;
- }
- case llvm::dwarf::DW_AT_comp_dir: {
- if (auto eName = getIndexedString(normalizedFile, form, infoData, offset,
- strings))
- tu.path = *eName;
- else
- return eName.takeError();
- break;
- }
- default:
- llvm::DWARFFormValue::skipValue(form, infoData, &offset, formParams);
- }
- }
- return tu;
-}
-
-llvm::Error parseDebugInfo(MachOFile &file,
- const NormalizedFile &normalizedFile, bool copyRefs) {
-
- // Find the interesting debug info sections.
- const Section *debugInfo = nullptr;
- const Section *debugAbbrev = nullptr;
- const Section *debugStrings = nullptr;
-
- for (auto &s : normalizedFile.sections) {
- if (s.segmentName == "__DWARF") {
- if (s.sectionName == "__debug_info")
- debugInfo = &s;
- else if (s.sectionName == "__debug_abbrev")
- debugAbbrev = &s;
- else if (s.sectionName == "__debug_str")
- debugStrings = &s;
- }
- }
-
- if (!debugInfo)
- return parseStabs(file, normalizedFile, copyRefs);
-
- if (debugInfo->content.size() == 0)
- return llvm::Error::success();
-
- if (debugInfo->content.size() < 12)
- return llvm::make_error<GenericError>("Malformed __debug_info section in " +
- file.path() + ": too small");
-
- if (!debugAbbrev)
- return llvm::make_error<GenericError>("Missing __dwarf_abbrev section in " +
- file.path());
-
- if (auto tuOrErr = readCompUnit(normalizedFile, *debugInfo, *debugAbbrev,
- *debugStrings, file.path())) {
- // FIXME: Kill of allocator and code under 'copyRefs' when we fix YAML
- // memory ownership.
- std::unique_ptr<BumpPtrAllocator> allocator;
- if (copyRefs) {
- allocator = std::make_unique<BumpPtrAllocator>();
- tuOrErr->name = copyDebugString(tuOrErr->name, *allocator);
- tuOrErr->path = copyDebugString(tuOrErr->path, *allocator);
- }
- file.setDebugInfo(std::make_unique<DwarfDebugInfo>(std::move(*tuOrErr)));
- if (copyRefs)
- file.debugInfo()->setAllocator(std::move(allocator));
- } else
- return tuOrErr.takeError();
-
- return llvm::Error::success();
-}
-
-static int64_t readSPtr(bool is64, bool isBig, const uint8_t *addr) {
- if (is64)
- return read64(addr, isBig);
-
- int32_t res = read32(addr, isBig);
- return res;
-}
-
-/// --- Augmentation String Processing ---
-
-struct CIEInfo {
- bool _augmentationDataPresent = false;
- bool _mayHaveEH = false;
- uint32_t _offsetOfLSDA = ~0U;
- uint32_t _offsetOfPersonality = ~0U;
- uint32_t _offsetOfFDEPointerEncoding = ~0U;
- uint32_t _augmentationDataLength = ~0U;
-};
-
-typedef llvm::DenseMap<const MachODefinedAtom*, CIEInfo> CIEInfoMap;
-
-static llvm::Error processAugmentationString(const uint8_t *augStr,
- CIEInfo &cieInfo,
- unsigned &len) {
-
- if (augStr[0] == '\0') {
- len = 1;
- return llvm::Error::success();
- }
-
- if (augStr[0] != 'z')
- return llvm::make_error<GenericError>("expected 'z' at start of "
- "augmentation string");
-
- cieInfo._augmentationDataPresent = true;
- uint64_t idx = 1;
-
- uint32_t offsetInAugmentationData = 0;
- while (augStr[idx] != '\0') {
- if (augStr[idx] == 'L') {
- cieInfo._offsetOfLSDA = offsetInAugmentationData;
- // This adds a single byte to the augmentation data.
- ++offsetInAugmentationData;
- ++idx;
- continue;
- }
- if (augStr[idx] == 'P') {
- cieInfo._offsetOfPersonality = offsetInAugmentationData;
- // This adds a single byte to the augmentation data for the encoding,
- // then a number of bytes for the pointer data.
- // FIXME: We are assuming 4 is correct here for the pointer size as we
- // always currently use delta32ToGOT.
- offsetInAugmentationData += 5;
- ++idx;
- continue;
- }
- if (augStr[idx] == 'R') {
- cieInfo._offsetOfFDEPointerEncoding = offsetInAugmentationData;
- // This adds a single byte to the augmentation data.
- ++offsetInAugmentationData;
- ++idx;
- continue;
- }
- if (augStr[idx] == 'e') {
- if (augStr[idx + 1] != 'h')
- return llvm::make_error<GenericError>("expected 'eh' in "
- "augmentation string");
- cieInfo._mayHaveEH = true;
- idx += 2;
- continue;
- }
- ++idx;
- }
-
- cieInfo._augmentationDataLength = offsetInAugmentationData;
-
- len = idx + 1;
- return llvm::Error::success();
-}
-
-static llvm::Error processCIE(const NormalizedFile &normalizedFile,
- MachOFile &file,
- mach_o::ArchHandler &handler,
- const Section *ehFrameSection,
- MachODefinedAtom *atom,
- uint64_t offset,
- CIEInfoMap &cieInfos) {
- const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch);
- const uint8_t *frameData = atom->rawContent().data();
-
- CIEInfo cieInfo;
-
- uint32_t size = read32(frameData, isBig);
- uint64_t cieIDField = size == 0xffffffffU
- ? sizeof(uint32_t) + sizeof(uint64_t)
- : sizeof(uint32_t);
- uint64_t versionField = cieIDField + sizeof(uint32_t);
- uint64_t augmentationStringField = versionField + sizeof(uint8_t);
-
- unsigned augmentationStringLength = 0;
- if (auto err = processAugmentationString(frameData + augmentationStringField,
- cieInfo, augmentationStringLength))
- return err;
-
- if (cieInfo._offsetOfPersonality != ~0U) {
- // If we have augmentation data for the personality function, then we may
- // need to implicitly generate its relocation.
-
- // Parse the EH Data field which is pointer sized.
- uint64_t EHDataField = augmentationStringField + augmentationStringLength;
- const bool is64 = MachOLinkingContext::is64Bit(normalizedFile.arch);
- unsigned EHDataFieldSize = (cieInfo._mayHaveEH ? (is64 ? 8 : 4) : 0);
-
- // Parse Code Align Factor which is a ULEB128.
- uint64_t CodeAlignField = EHDataField + EHDataFieldSize;
- unsigned lengthFieldSize = 0;
- llvm::decodeULEB128(frameData + CodeAlignField, &lengthFieldSize);
-
- // Parse Data Align Factor which is a SLEB128.
- uint64_t DataAlignField = CodeAlignField + lengthFieldSize;
- llvm::decodeSLEB128(frameData + DataAlignField, &lengthFieldSize);
-
- // Parse Return Address Register which is a byte.
- uint64_t ReturnAddressField = DataAlignField + lengthFieldSize;
-
- // Parse the augmentation length which is a ULEB128.
- uint64_t AugmentationLengthField = ReturnAddressField + 1;
- uint64_t AugmentationLength =
- llvm::decodeULEB128(frameData + AugmentationLengthField,
- &lengthFieldSize);
-
- if (AugmentationLength != cieInfo._augmentationDataLength)
- return llvm::make_error<GenericError>("CIE augmentation data length "
- "mismatch");
-
- // Get the start address of the augmentation data.
- uint64_t AugmentationDataField = AugmentationLengthField + lengthFieldSize;
-
- // Parse the personality function from the augmentation data.
- uint64_t PersonalityField =
- AugmentationDataField + cieInfo._offsetOfPersonality;
-
- // Parse the personality encoding.
- // FIXME: Verify that this is a 32-bit pcrel offset.
- uint64_t PersonalityFunctionField = PersonalityField + 1;
-
- if (atom->begin() != atom->end()) {
- // If we have an explicit relocation, then make sure it matches this
- // offset as this is where we'd expect it to be applied to.
- DefinedAtom::reference_iterator CurrentRef = atom->begin();
- if (CurrentRef->offsetInAtom() != PersonalityFunctionField)
- return llvm::make_error<GenericError>("CIE personality reloc at "
- "wrong offset");
-
- if (++CurrentRef != atom->end())
- return llvm::make_error<GenericError>("CIE contains too many relocs");
- } else {
- // Implicitly generate the personality function reloc. It's assumed to
- // be a delta32 offset to a GOT entry.
- // FIXME: Parse the encoding and check this.
- int32_t funcDelta = read32(frameData + PersonalityFunctionField, isBig);
- uint64_t funcAddress = ehFrameSection->address + offset +
- PersonalityFunctionField;
- funcAddress += funcDelta;
-
- const MachODefinedAtom *func = nullptr;
- Reference::Addend addend;
- func = findAtomCoveringAddress(normalizedFile, file, funcAddress,
- addend);
- atom->addReference(Reference::KindNamespace::mach_o, handler.kindArch(),
- handler.unwindRefToPersonalityFunctionKind(),
- PersonalityFunctionField, func, addend);
- }
- } else if (atom->begin() != atom->end()) {
- // Otherwise, we expect there to be no relocations in this atom as the only
- // relocation would have been to the personality function.
- return llvm::make_error<GenericError>("unexpected relocation in CIE");
- }
-
-
- cieInfos[atom] = std::move(cieInfo);
-
- return llvm::Error::success();
-}
-
-static llvm::Error processFDE(const NormalizedFile &normalizedFile,
- MachOFile &file,
- mach_o::ArchHandler &handler,
- const Section *ehFrameSection,
- MachODefinedAtom *atom,
- uint64_t offset,
- const CIEInfoMap &cieInfos) {
-
- const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch);
- const bool is64 = MachOLinkingContext::is64Bit(normalizedFile.arch);
-
- // Compiler wasn't lazy and actually told us what it meant.
- // Unfortunately, the compiler may not have generated references for all of
- // [cie, func, lsda] and so we still need to parse the FDE and add references
- // for any the compiler didn't generate.
- if (atom->begin() != atom->end())
- atom->sortReferences();
-
- DefinedAtom::reference_iterator CurrentRef = atom->begin();
-
- // This helper returns the reference (if one exists) at the offset we are
- // currently processing. It automatically increments the ref iterator if we
- // do return a ref, and throws an error if we pass over a ref without
- // comsuming it.
- auto currentRefGetter = [&CurrentRef,
- &atom](uint64_t Offset)->const Reference* {
- // If there are no more refs found, then we are done.
- if (CurrentRef == atom->end())
- return nullptr;
-
- const Reference *Ref = *CurrentRef;
-
- // If we haven't reached the offset for this reference, then return that
- // we don't yet have a reference to process.
- if (Offset < Ref->offsetInAtom())
- return nullptr;
-
- // If the offset is equal, then we want to process this ref.
- if (Offset == Ref->offsetInAtom()) {
- ++CurrentRef;
- return Ref;
- }
-
- // The current ref is at an offset which is earlier than the current
- // offset, then we failed to consume it when we should have. In this case
- // throw an error.
- llvm::report_fatal_error("Skipped reference when processing FDE");
- };
-
- // Helper to either get the reference at this current location, and verify
- // that it is of the expected type, or add a reference of that type.
- // Returns the reference target.
- auto verifyOrAddReference = [&](uint64_t targetAddress,
- Reference::KindValue refKind,
- uint64_t refAddress,
- bool allowsAddend)->const Atom* {
- if (auto *ref = currentRefGetter(refAddress)) {
- // The compiler already emitted a relocation for the CIE ref. This should
- // have been converted to the correct type of reference in
- // get[Pair]ReferenceInfo().
- assert(ref->kindValue() == refKind &&
- "Incorrect EHFrame reference kind");
- return ref->target();
- }
- Reference::Addend addend;
- auto *target = findAtomCoveringAddress(normalizedFile, file,
- targetAddress, addend);
- atom->addReference(Reference::KindNamespace::mach_o, handler.kindArch(),
- refKind, refAddress, target, addend);
-
- if (!allowsAddend)
- assert(!addend && "EHFrame reference cannot have addend");
- return target;
- };
-
- const uint8_t *startFrameData = atom->rawContent().data();
- const uint8_t *frameData = startFrameData;
-
- uint32_t size = read32(frameData, isBig);
- uint64_t cieFieldInFDE = size == 0xffffffffU
- ? sizeof(uint32_t) + sizeof(uint64_t)
- : sizeof(uint32_t);
-
- // Linker needs to fixup a reference from the FDE to its parent CIE (a
- // 32-bit byte offset backwards in the __eh_frame section).
- uint32_t cieDelta = read32(frameData + cieFieldInFDE, isBig);
- uint64_t cieAddress = ehFrameSection->address + offset + cieFieldInFDE;
- cieAddress -= cieDelta;
-
- auto *cieRefTarget = verifyOrAddReference(cieAddress,
- handler.unwindRefToCIEKind(),
- cieFieldInFDE, false);
- const MachODefinedAtom *cie = dyn_cast<MachODefinedAtom>(cieRefTarget);
- assert(cie && cie->contentType() == DefinedAtom::typeCFI &&
- "FDE's CIE field does not point at the start of a CIE.");
-
- const CIEInfo &cieInfo = cieInfos.find(cie)->second;
-
- // Linker needs to fixup reference from the FDE to the function it's
- // describing. FIXME: there are actually different ways to do this, and the
- // particular method used is specified in the CIE's augmentation fields
- // (hopefully)
- uint64_t rangeFieldInFDE = cieFieldInFDE + sizeof(uint32_t);
-
- int64_t functionFromFDE = readSPtr(is64, isBig,
- frameData + rangeFieldInFDE);
- uint64_t rangeStart = ehFrameSection->address + offset + rangeFieldInFDE;
- rangeStart += functionFromFDE;
-
- verifyOrAddReference(rangeStart,
- handler.unwindRefToFunctionKind(),
- rangeFieldInFDE, true);
-
- // Handle the augmentation data if there is any.
- if (cieInfo._augmentationDataPresent) {
- // First process the augmentation data length field.
- uint64_t augmentationDataLengthFieldInFDE =
- rangeFieldInFDE + 2 * (is64 ? sizeof(uint64_t) : sizeof(uint32_t));
- unsigned lengthFieldSize = 0;
- uint64_t augmentationDataLength =
- llvm::decodeULEB128(frameData + augmentationDataLengthFieldInFDE,
- &lengthFieldSize);
-
- if (cieInfo._offsetOfLSDA != ~0U && augmentationDataLength > 0) {
-
- // Look at the augmentation data field.
- uint64_t augmentationDataFieldInFDE =
- augmentationDataLengthFieldInFDE + lengthFieldSize;
-
- int64_t lsdaFromFDE = readSPtr(is64, isBig,
- frameData + augmentationDataFieldInFDE);
- uint64_t lsdaStart =
- ehFrameSection->address + offset + augmentationDataFieldInFDE +
- lsdaFromFDE;
-
- verifyOrAddReference(lsdaStart,
- handler.unwindRefToFunctionKind(),
- augmentationDataFieldInFDE, true);
- }
- }
-
- return llvm::Error::success();
-}
-
-llvm::Error addEHFrameReferences(const NormalizedFile &normalizedFile,
- MachOFile &file,
- mach_o::ArchHandler &handler) {
-
- const Section *ehFrameSection = nullptr;
- for (auto §ion : normalizedFile.sections)
- if (section.segmentName == "__TEXT" &&
- section.sectionName == "__eh_frame") {
- ehFrameSection = §ion;
- break;
- }
-
- // No __eh_frame so nothing to do.
- if (!ehFrameSection)
- return llvm::Error::success();
-
- llvm::Error ehFrameErr = llvm::Error::success();
- CIEInfoMap cieInfos;
-
- file.eachAtomInSection(*ehFrameSection,
- [&](MachODefinedAtom *atom, uint64_t offset) -> void {
- assert(atom->contentType() == DefinedAtom::typeCFI);
-
- // Bail out if we've encountered an error.
- if (ehFrameErr)
- return;
-
- const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch);
- if (ArchHandler::isDwarfCIE(isBig, atom))
- ehFrameErr = processCIE(normalizedFile, file, handler, ehFrameSection,
- atom, offset, cieInfos);
- else
- ehFrameErr = processFDE(normalizedFile, file, handler, ehFrameSection,
- atom, offset, cieInfos);
- });
-
- return ehFrameErr;
-}
-
-llvm::Error parseObjCImageInfo(const Section §,
- const NormalizedFile &normalizedFile,
- MachOFile &file) {
-
- // struct objc_image_info {
- // uint32_t version; // initially 0
- // uint32_t flags;
- // };
-
- ArrayRef<uint8_t> content = sect.content;
- if (content.size() != 8)
- return llvm::make_error<GenericError>(sect.segmentName + "/" +
- sect.sectionName +
- " in file " + file.path() +
- " should be 8 bytes in size");
-
- const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch);
- uint32_t version = read32(content.data(), isBig);
- if (version)
- return llvm::make_error<GenericError>(sect.segmentName + "/" +
- sect.sectionName +
- " in file " + file.path() +
- " should have version=0");
-
- uint32_t flags = read32(content.data() + 4, isBig);
- if (flags & (MachOLinkingContext::objc_supports_gc |
- MachOLinkingContext::objc_gc_only))
- return llvm::make_error<GenericError>(sect.segmentName + "/" +
- sect.sectionName +
- " in file " + file.path() +
- " uses GC. This is not supported");
-
- if (flags & MachOLinkingContext::objc_retainReleaseForSimulator)
- file.setObjcConstraint(MachOLinkingContext::objc_retainReleaseForSimulator);
- else
- file.setObjcConstraint(MachOLinkingContext::objc_retainRelease);
-
- file.setSwiftVersion((flags >> 8) & 0xFF);
-
- return llvm::Error::success();
-}
-
-/// Converts normalized mach-o file into an lld::File and lld::Atoms.
-llvm::Expected<std::unique_ptr<lld::File>>
-objectToAtoms(const NormalizedFile &normalizedFile, StringRef path,
- bool copyRefs) {
- auto file = std::make_unique<MachOFile>(path);
- if (auto ec = normalizedObjectToAtoms(file.get(), normalizedFile, copyRefs))
- return std::move(ec);
- return std::unique_ptr<File>(std::move(file));
-}
-
-llvm::Expected<std::unique_ptr<lld::File>>
-dylibToAtoms(const NormalizedFile &normalizedFile, StringRef path,
- bool copyRefs) {
- // Instantiate SharedLibraryFile object.
- auto file = std::make_unique<MachODylibFile>(path);
- if (auto ec = normalizedDylibToAtoms(file.get(), normalizedFile, copyRefs))
- return std::move(ec);
- return std::unique_ptr<File>(std::move(file));
-}
-
-} // anonymous namespace
-
-namespace normalized {
-
-static bool isObjCImageInfo(const Section §) {
- return (sect.segmentName == "__OBJC" && sect.sectionName == "__image_info") ||
- (sect.segmentName == "__DATA" && sect.sectionName == "__objc_imageinfo");
-}
-
-llvm::Error
-normalizedObjectToAtoms(MachOFile *file,
- const NormalizedFile &normalizedFile,
- bool copyRefs) {
- LLVM_DEBUG(llvm::dbgs() << "******** Normalizing file to atoms: "
- << file->path() << "\n");
- bool scatterable = ((normalizedFile.flags & MH_SUBSECTIONS_VIA_SYMBOLS) != 0);
-
- // Create atoms from each section.
- for (auto § : normalizedFile.sections) {
-
- // If this is a debug-info section parse it specially.
- if (isDebugInfoSection(sect))
- continue;
-
- // If the file contains an objc_image_info struct, then we should parse the
- // ObjC flags and Swift version.
- if (isObjCImageInfo(sect)) {
- if (auto ec = parseObjCImageInfo(sect, normalizedFile, *file))
- return ec;
- // We then skip adding atoms for this section as we use the ObjCPass to
- // re-emit this data after it has been aggregated for all files.
- continue;
- }
-
- bool customSectionName;
- DefinedAtom::ContentType atomType = atomTypeFromSection(sect,
- customSectionName);
- if (auto ec = processSection(atomType, sect, customSectionName,
- normalizedFile, *file, scatterable, copyRefs))
- return ec;
- }
- // Create atoms from undefined symbols.
- for (auto &sym : normalizedFile.undefinedSymbols) {
- // Undefined symbols with n_value != 0 are actually tentative definitions.
- if (sym.value == Hex64(0)) {
- file->addUndefinedAtom(sym.name, copyRefs);
- } else {
- file->addTentativeDefAtom(sym.name, atomScope(sym.scope), sym.value,
- DefinedAtom::Alignment(1 << (sym.desc >> 8)),
- copyRefs);
- }
- }
-
- // Convert mach-o relocations to References
- std::unique_ptr<mach_o::ArchHandler> handler
- = ArchHandler::create(normalizedFile.arch);
- for (auto § : normalizedFile.sections) {
- if (isDebugInfoSection(sect))
- continue;
- if (llvm::Error ec = convertRelocs(sect, normalizedFile, scatterable,
- *file, *handler))
- return ec;
- }
-
- // Add additional arch-specific References
- file->eachDefinedAtom([&](MachODefinedAtom* atom) -> void {
- handler->addAdditionalReferences(*atom);
- });
-
- // Each __eh_frame section needs references to both __text (the function we're
- // providing unwind info for) and itself (FDE -> CIE). These aren't
- // represented in the relocations on some architectures, so we have to add
- // them back in manually there.
- if (auto ec = addEHFrameReferences(normalizedFile, *file, *handler))
- return ec;
-
- // Process mach-o data-in-code regions array. That information is encoded in
- // atoms as References at each transition point.
- unsigned nextIndex = 0;
- for (const DataInCode &entry : normalizedFile.dataInCode) {
- ++nextIndex;
- const Section* s = findSectionCoveringAddress(normalizedFile, entry.offset);
- if (!s) {
- return llvm::make_error<GenericError>(Twine("LC_DATA_IN_CODE address ("
- + Twine(entry.offset)
- + ") is not in any section"));
- }
- uint64_t offsetInSect = entry.offset - s->address;
- uint32_t offsetInAtom;
- MachODefinedAtom *atom = file->findAtomCoveringAddress(*s, offsetInSect,
- &offsetInAtom);
- if (offsetInAtom + entry.length > atom->size()) {
- return llvm::make_error<GenericError>(Twine("LC_DATA_IN_CODE entry "
- "(offset="
- + Twine(entry.offset)
- + ", length="
- + Twine(entry.length)
- + ") crosses atom boundary."));
- }
- // Add reference that marks start of data-in-code.
- atom->addReference(Reference::KindNamespace::mach_o, handler->kindArch(),
- handler->dataInCodeTransitionStart(*atom),
- offsetInAtom, atom, entry.kind);
-
- // Peek at next entry, if it starts where this one ends, skip ending ref.
- if (nextIndex < normalizedFile.dataInCode.size()) {
- const DataInCode &nextEntry = normalizedFile.dataInCode[nextIndex];
- if (nextEntry.offset == (entry.offset + entry.length))
- continue;
- }
-
- // If data goes to end of function, skip ending ref.
- if ((offsetInAtom + entry.length) == atom->size())
- continue;
-
- // Add reference that marks end of data-in-code.
- atom->addReference(Reference::KindNamespace::mach_o, handler->kindArch(),
- handler->dataInCodeTransitionEnd(*atom),
- offsetInAtom+entry.length, atom, 0);
- }
-
- // Cache some attributes on the file for use later.
- file->setFlags(normalizedFile.flags);
- file->setArch(normalizedFile.arch);
- file->setOS(normalizedFile.os);
- file->setMinVersion(normalizedFile.minOSverson);
- file->setMinVersionLoadCommandKind(normalizedFile.minOSVersionKind);
-
- // Sort references in each atom to their canonical order.
- for (const DefinedAtom* defAtom : file->defined()) {
- reinterpret_cast<const SimpleDefinedAtom*>(defAtom)->sortReferences();
- }
-
- if (auto err = parseDebugInfo(*file, normalizedFile, copyRefs))
- return err;
-
- return llvm::Error::success();
-}
-
-llvm::Error
-normalizedDylibToAtoms(MachODylibFile *file,
- const NormalizedFile &normalizedFile,
- bool copyRefs) {
- file->setInstallName(normalizedFile.installName);
- file->setCompatVersion(normalizedFile.compatVersion);
- file->setCurrentVersion(normalizedFile.currentVersion);
-
- // Tell MachODylibFile object about all symbols it exports.
- if (!normalizedFile.exportInfo.empty()) {
- // If exports trie exists, use it instead of traditional symbol table.
- for (const Export &exp : normalizedFile.exportInfo) {
- bool weakDef = (exp.flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION);
- // StringRefs from export iterator are ephemeral, so force copy.
- file->addExportedSymbol(exp.name, weakDef, true);
- }
- } else {
- for (auto &sym : normalizedFile.globalSymbols) {
- assert((sym.scope & N_EXT) && "only expect external symbols here");
- bool weakDef = (sym.desc & N_WEAK_DEF);
- file->addExportedSymbol(sym.name, weakDef, copyRefs);
- }
- }
- // Tell MachODylibFile object about all dylibs it re-exports.
- for (const DependentDylib &dep : normalizedFile.dependentDylibs) {
- if (dep.kind == llvm::MachO::LC_REEXPORT_DYLIB)
- file->addReExportedDylib(dep.path);
- }
- return llvm::Error::success();
-}
-
-void relocatableSectionInfoForContentType(DefinedAtom::ContentType atomType,
- StringRef &segmentName,
- StringRef §ionName,
- SectionType §ionType,
- SectionAttr §ionAttrs,
- bool &relocsToDefinedCanBeImplicit) {
-
- for (const MachORelocatableSectionToAtomType *p = sectsToAtomType ;
- p->atomType != DefinedAtom::typeUnknown; ++p) {
- if (p->atomType != atomType)
- continue;
- // Wild carded entries are ignored for reverse lookups.
- if (p->segmentName.empty() || p->sectionName.empty())
- continue;
- segmentName = p->segmentName;
- sectionName = p->sectionName;
- sectionType = p->sectionType;
- sectionAttrs = 0;
- relocsToDefinedCanBeImplicit = false;
- if (atomType == DefinedAtom::typeCode)
- sectionAttrs = S_ATTR_PURE_INSTRUCTIONS;
- if (atomType == DefinedAtom::typeCFI)
- relocsToDefinedCanBeImplicit = true;
- return;
- }
- llvm_unreachable("content type not yet supported");
-}
-
-llvm::Expected<std::unique_ptr<lld::File>>
-normalizedToAtoms(const NormalizedFile &normalizedFile, StringRef path,
- bool copyRefs) {
- switch (normalizedFile.fileType) {
- case MH_DYLIB:
- case MH_DYLIB_STUB:
- return dylibToAtoms(normalizedFile, path, copyRefs);
- case MH_OBJECT:
- return objectToAtoms(normalizedFile, path, copyRefs);
- default:
- llvm_unreachable("unhandled MachO file type!");
- }
-}
-
-} // namespace normalized
-} // namespace mach_o
-} // namespace lld
+++ /dev/null
-//===- lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp -----------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-///
-/// \file For mach-o object files, this implementation uses YAML I/O to
-/// provide the convert between YAML and the normalized mach-o (NM).
-///
-/// +------------+ +------+
-/// | normalized | <-> | yaml |
-/// +------------+ +------+
-
-#include "MachONormalizedFile.h"
-#include "lld/Common/LLVM.h"
-#include "lld/Core/Error.h"
-#include "lld/ReaderWriter/YamlContext.h"
-#include "llvm/ADT/SmallString.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/ADT/StringSwitch.h"
-#include "llvm/ADT/Twine.h"
-#include "llvm/BinaryFormat/MachO.h"
-#include "llvm/Support/Casting.h"
-#include "llvm/Support/ErrorHandling.h"
-#include "llvm/Support/Format.h"
-#include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/SourceMgr.h"
-#include "llvm/Support/YAMLTraits.h"
-#include "llvm/Support/raw_ostream.h"
-#include <system_error>
-
-using llvm::StringRef;
-using namespace llvm::yaml;
-using namespace llvm::MachO;
-using namespace lld::mach_o::normalized;
-using lld::YamlContext;
-
-LLVM_YAML_IS_SEQUENCE_VECTOR(Segment)
-LLVM_YAML_IS_SEQUENCE_VECTOR(DependentDylib)
-LLVM_YAML_IS_SEQUENCE_VECTOR(RebaseLocation)
-LLVM_YAML_IS_SEQUENCE_VECTOR(BindLocation)
-LLVM_YAML_IS_SEQUENCE_VECTOR(Export)
-LLVM_YAML_IS_SEQUENCE_VECTOR(DataInCode)
-
-
-// for compatibility with gcc-4.7 in C++11 mode, add extra namespace
-namespace llvm {
-namespace yaml {
-
-// A vector of Sections is a sequence.
-template<>
-struct SequenceTraits< std::vector<Section> > {
- static size_t size(IO &io, std::vector<Section> &seq) {
- return seq.size();
- }
- static Section& element(IO &io, std::vector<Section> &seq, size_t index) {
- if ( index >= seq.size() )
- seq.resize(index+1);
- return seq[index];
- }
-};
-
-template<>
-struct SequenceTraits< std::vector<Symbol> > {
- static size_t size(IO &io, std::vector<Symbol> &seq) {
- return seq.size();
- }
- static Symbol& element(IO &io, std::vector<Symbol> &seq, size_t index) {
- if ( index >= seq.size() )
- seq.resize(index+1);
- return seq[index];
- }
-};
-
-// A vector of Relocations is a sequence.
-template<>
-struct SequenceTraits< Relocations > {
- static size_t size(IO &io, Relocations &seq) {
- return seq.size();
- }
- static Relocation& element(IO &io, Relocations &seq, size_t index) {
- if ( index >= seq.size() )
- seq.resize(index+1);
- return seq[index];
- }
-};
-
-// The content for a section is represented as a flow sequence of hex bytes.
-template<>
-struct SequenceTraits< ContentBytes > {
- static size_t size(IO &io, ContentBytes &seq) {
- return seq.size();
- }
- static Hex8& element(IO &io, ContentBytes &seq, size_t index) {
- if ( index >= seq.size() )
- seq.resize(index+1);
- return seq[index];
- }
- static const bool flow = true;
-};
-
-// The indirect symbols for a section is represented as a flow sequence
-// of numbers (symbol table indexes).
-template<>
-struct SequenceTraits< IndirectSymbols > {
- static size_t size(IO &io, IndirectSymbols &seq) {
- return seq.size();
- }
- static uint32_t& element(IO &io, IndirectSymbols &seq, size_t index) {
- if ( index >= seq.size() )
- seq.resize(index+1);
- return seq[index];
- }
- static const bool flow = true;
-};
-
-template <>
-struct ScalarEnumerationTraits<lld::MachOLinkingContext::Arch> {
- static void enumeration(IO &io, lld::MachOLinkingContext::Arch &value) {
- io.enumCase(value, "unknown",lld::MachOLinkingContext::arch_unknown);
- io.enumCase(value, "ppc", lld::MachOLinkingContext::arch_ppc);
- io.enumCase(value, "x86", lld::MachOLinkingContext::arch_x86);
- io.enumCase(value, "x86_64", lld::MachOLinkingContext::arch_x86_64);
- io.enumCase(value, "armv6", lld::MachOLinkingContext::arch_armv6);
- io.enumCase(value, "armv7", lld::MachOLinkingContext::arch_armv7);
- io.enumCase(value, "armv7s", lld::MachOLinkingContext::arch_armv7s);
- io.enumCase(value, "arm64", lld::MachOLinkingContext::arch_arm64);
- }
-};
-
-template <>
-struct ScalarEnumerationTraits<lld::MachOLinkingContext::OS> {
- static void enumeration(IO &io, lld::MachOLinkingContext::OS &value) {
- io.enumCase(value, "unknown",
- lld::MachOLinkingContext::OS::unknown);
- io.enumCase(value, "Mac OS X",
- lld::MachOLinkingContext::OS::macOSX);
- io.enumCase(value, "iOS",
- lld::MachOLinkingContext::OS::iOS);
- io.enumCase(value, "iOS Simulator",
- lld::MachOLinkingContext::OS::iOS_simulator);
- }
-};
-
-
-template <>
-struct ScalarEnumerationTraits<HeaderFileType> {
- static void enumeration(IO &io, HeaderFileType &value) {
- io.enumCase(value, "MH_OBJECT", llvm::MachO::MH_OBJECT);
- io.enumCase(value, "MH_DYLIB", llvm::MachO::MH_DYLIB);
- io.enumCase(value, "MH_EXECUTE", llvm::MachO::MH_EXECUTE);
- io.enumCase(value, "MH_BUNDLE", llvm::MachO::MH_BUNDLE);
- }
-};
-
-
-template <>
-struct ScalarBitSetTraits<FileFlags> {
- static void bitset(IO &io, FileFlags &value) {
- io.bitSetCase(value, "MH_TWOLEVEL",
- llvm::MachO::MH_TWOLEVEL);
- io.bitSetCase(value, "MH_SUBSECTIONS_VIA_SYMBOLS",
- llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
- }
-};
-
-
-template <>
-struct ScalarEnumerationTraits<SectionType> {
- static void enumeration(IO &io, SectionType &value) {
- io.enumCase(value, "S_REGULAR",
- llvm::MachO::S_REGULAR);
- io.enumCase(value, "S_ZEROFILL",
- llvm::MachO::S_ZEROFILL);
- io.enumCase(value, "S_CSTRING_LITERALS",
- llvm::MachO::S_CSTRING_LITERALS);
- io.enumCase(value, "S_4BYTE_LITERALS",
- llvm::MachO::S_4BYTE_LITERALS);
- io.enumCase(value, "S_8BYTE_LITERALS",
- llvm::MachO::S_8BYTE_LITERALS);
- io.enumCase(value, "S_LITERAL_POINTERS",
- llvm::MachO::S_LITERAL_POINTERS);
- io.enumCase(value, "S_NON_LAZY_SYMBOL_POINTERS",
- llvm::MachO::S_NON_LAZY_SYMBOL_POINTERS);
- io.enumCase(value, "S_LAZY_SYMBOL_POINTERS",
- llvm::MachO::S_LAZY_SYMBOL_POINTERS);
- io.enumCase(value, "S_SYMBOL_STUBS",
- llvm::MachO::S_SYMBOL_STUBS);
- io.enumCase(value, "S_MOD_INIT_FUNC_POINTERS",
- llvm::MachO::S_MOD_INIT_FUNC_POINTERS);
- io.enumCase(value, "S_MOD_TERM_FUNC_POINTERS",
- llvm::MachO::S_MOD_TERM_FUNC_POINTERS);
- io.enumCase(value, "S_COALESCED",
- llvm::MachO::S_COALESCED);
- io.enumCase(value, "S_GB_ZEROFILL",
- llvm::MachO::S_GB_ZEROFILL);
- io.enumCase(value, "S_INTERPOSING",
- llvm::MachO::S_INTERPOSING);
- io.enumCase(value, "S_16BYTE_LITERALS",
- llvm::MachO::S_16BYTE_LITERALS);
- io.enumCase(value, "S_DTRACE_DOF",
- llvm::MachO::S_DTRACE_DOF);
- io.enumCase(value, "S_LAZY_DYLIB_SYMBOL_POINTERS",
- llvm::MachO::S_LAZY_DYLIB_SYMBOL_POINTERS);
- io.enumCase(value, "S_THREAD_LOCAL_REGULAR",
- llvm::MachO::S_THREAD_LOCAL_REGULAR);
- io.enumCase(value, "S_THREAD_LOCAL_ZEROFILL",
- llvm::MachO::S_THREAD_LOCAL_ZEROFILL);
- io.enumCase(value, "S_THREAD_LOCAL_VARIABLES",
- llvm::MachO::S_THREAD_LOCAL_VARIABLES);
- io.enumCase(value, "S_THREAD_LOCAL_VARIABLE_POINTERS",
- llvm::MachO::S_THREAD_LOCAL_VARIABLE_POINTERS);
- io.enumCase(value, "S_THREAD_LOCAL_INIT_FUNCTION_POINTERS",
- llvm::MachO::S_THREAD_LOCAL_INIT_FUNCTION_POINTERS);
- }
-};
-
-template <>
-struct ScalarBitSetTraits<SectionAttr> {
- static void bitset(IO &io, SectionAttr &value) {
- io.bitSetCase(value, "S_ATTR_PURE_INSTRUCTIONS",
- llvm::MachO::S_ATTR_PURE_INSTRUCTIONS);
- io.bitSetCase(value, "S_ATTR_SOME_INSTRUCTIONS",
- llvm::MachO::S_ATTR_SOME_INSTRUCTIONS);
- io.bitSetCase(value, "S_ATTR_NO_DEAD_STRIP",
- llvm::MachO::S_ATTR_NO_DEAD_STRIP);
- io.bitSetCase(value, "S_ATTR_EXT_RELOC",
- llvm::MachO::S_ATTR_EXT_RELOC);
- io.bitSetCase(value, "S_ATTR_LOC_RELOC",
- llvm::MachO::S_ATTR_LOC_RELOC);
- io.bitSetCase(value, "S_ATTR_DEBUG",
- llvm::MachO::S_ATTR_DEBUG);
- }
-};
-
-/// This is a custom formatter for SectionAlignment. Values are
-/// the power to raise by, ie, the n in 2^n.
-template <> struct ScalarTraits<SectionAlignment> {
- static void output(const SectionAlignment &value, void *ctxt,
- raw_ostream &out) {
- out << llvm::format("%d", (uint32_t)value);
- }
-
- static StringRef input(StringRef scalar, void *ctxt,
- SectionAlignment &value) {
- uint32_t alignment;
- if (scalar.getAsInteger(0, alignment)) {
- return "malformed alignment value";
- }
- if (!llvm::isPowerOf2_32(alignment))
- return "alignment must be a power of 2";
- value = alignment;
- return StringRef(); // returning empty string means success
- }
-
- static QuotingType mustQuote(StringRef) { return QuotingType::None; }
-};
-
-template <>
-struct ScalarEnumerationTraits<NListType> {
- static void enumeration(IO &io, NListType &value) {
- io.enumCase(value, "N_UNDF", llvm::MachO::N_UNDF);
- io.enumCase(value, "N_ABS", llvm::MachO::N_ABS);
- io.enumCase(value, "N_SECT", llvm::MachO::N_SECT);
- io.enumCase(value, "N_PBUD", llvm::MachO::N_PBUD);
- io.enumCase(value, "N_INDR", llvm::MachO::N_INDR);
- }
-};
-
-template <>
-struct ScalarBitSetTraits<SymbolScope> {
- static void bitset(IO &io, SymbolScope &value) {
- io.bitSetCase(value, "N_EXT", llvm::MachO::N_EXT);
- io.bitSetCase(value, "N_PEXT", llvm::MachO::N_PEXT);
- }
-};
-
-template <>
-struct ScalarBitSetTraits<SymbolDesc> {
- static void bitset(IO &io, SymbolDesc &value) {
- io.bitSetCase(value, "N_NO_DEAD_STRIP", llvm::MachO::N_NO_DEAD_STRIP);
- io.bitSetCase(value, "N_WEAK_REF", llvm::MachO::N_WEAK_REF);
- io.bitSetCase(value, "N_WEAK_DEF", llvm::MachO::N_WEAK_DEF);
- io.bitSetCase(value, "N_ARM_THUMB_DEF", llvm::MachO::N_ARM_THUMB_DEF);
- io.bitSetCase(value, "N_SYMBOL_RESOLVER", llvm::MachO::N_SYMBOL_RESOLVER);
- }
-};
-
-
-template <>
-struct MappingTraits<Section> {
- struct NormalizedContentBytes;
- static void mapping(IO &io, Section §) {
- io.mapRequired("segment", sect.segmentName);
- io.mapRequired("section", sect.sectionName);
- io.mapRequired("type", sect.type);
- io.mapOptional("attributes", sect.attributes);
- io.mapOptional("alignment", sect.alignment, (SectionAlignment)1);
- io.mapRequired("address", sect.address);
- if (isZeroFillSection(sect.type)) {
- // S_ZEROFILL sections use "size:" instead of "content:"
- uint64_t size = sect.content.size();
- io.mapOptional("size", size);
- if (!io.outputting()) {
- uint8_t *bytes = nullptr;
- sect.content = makeArrayRef(bytes, size);
- }
- } else {
- MappingNormalization<NormalizedContent, ArrayRef<uint8_t>> content(
- io, sect.content);
- io.mapOptional("content", content->_normalizedContent);
- }
- io.mapOptional("relocations", sect.relocations);
- io.mapOptional("indirect-syms", sect.indirectSymbols);
- }
-
- struct NormalizedContent {
- NormalizedContent(IO &io) : _io(io) {}
- NormalizedContent(IO &io, ArrayRef<uint8_t> content) : _io(io) {
- // When writing yaml, copy content byte array to Hex8 vector.
- for (auto &c : content) {
- _normalizedContent.push_back(c);
- }
- }
- ArrayRef<uint8_t> denormalize(IO &io) {
- // When reading yaml, allocate byte array owned by NormalizedFile and
- // copy Hex8 vector to byte array.
- YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
- assert(info != nullptr);
- NormalizedFile *file = info->_normalizeMachOFile;
- assert(file != nullptr);
- size_t size = _normalizedContent.size();
- if (!size)
- return None;
- uint8_t *bytes = file->ownedAllocations.Allocate<uint8_t>(size);
- std::copy(_normalizedContent.begin(), _normalizedContent.end(), bytes);
- return makeArrayRef(bytes, size);
- }
-
- IO &_io;
- ContentBytes _normalizedContent;
- };
-};
-
-
-template <>
-struct MappingTraits<Relocation> {
- static void mapping(IO &io, Relocation &reloc) {
- io.mapRequired("offset", reloc.offset);
- io.mapOptional("scattered", reloc.scattered, false);
- io.mapRequired("type", reloc.type);
- io.mapRequired("length", reloc.length);
- io.mapRequired("pc-rel", reloc.pcRel);
- if ( !reloc.scattered )
- io.mapRequired("extern", reloc.isExtern);
- if ( reloc.scattered )
- io.mapRequired("value", reloc.value);
- if ( !reloc.scattered )
- io.mapRequired("symbol", reloc.symbol);
- }
-};
-
-
-template <>
-struct ScalarEnumerationTraits<RelocationInfoType> {
- static void enumeration(IO &io, RelocationInfoType &value) {
- YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
- assert(info != nullptr);
- NormalizedFile *file = info->_normalizeMachOFile;
- assert(file != nullptr);
- switch (file->arch) {
- case lld::MachOLinkingContext::arch_x86_64:
- io.enumCase(value, "X86_64_RELOC_UNSIGNED",
- llvm::MachO::X86_64_RELOC_UNSIGNED);
- io.enumCase(value, "X86_64_RELOC_SIGNED",
- llvm::MachO::X86_64_RELOC_SIGNED);
- io.enumCase(value, "X86_64_RELOC_BRANCH",
- llvm::MachO::X86_64_RELOC_BRANCH);
- io.enumCase(value, "X86_64_RELOC_GOT_LOAD",
- llvm::MachO::X86_64_RELOC_GOT_LOAD);
- io.enumCase(value, "X86_64_RELOC_GOT",
- llvm::MachO::X86_64_RELOC_GOT);
- io.enumCase(value, "X86_64_RELOC_SUBTRACTOR",
- llvm::MachO::X86_64_RELOC_SUBTRACTOR);
- io.enumCase(value, "X86_64_RELOC_SIGNED_1",
- llvm::MachO::X86_64_RELOC_SIGNED_1);
- io.enumCase(value, "X86_64_RELOC_SIGNED_2",
- llvm::MachO::X86_64_RELOC_SIGNED_2);
- io.enumCase(value, "X86_64_RELOC_SIGNED_4",
- llvm::MachO::X86_64_RELOC_SIGNED_4);
- io.enumCase(value, "X86_64_RELOC_TLV",
- llvm::MachO::X86_64_RELOC_TLV);
- break;
- case lld::MachOLinkingContext::arch_x86:
- io.enumCase(value, "GENERIC_RELOC_VANILLA",
- llvm::MachO::GENERIC_RELOC_VANILLA);
- io.enumCase(value, "GENERIC_RELOC_PAIR",
- llvm::MachO::GENERIC_RELOC_PAIR);
- io.enumCase(value, "GENERIC_RELOC_SECTDIFF",
- llvm::MachO::GENERIC_RELOC_SECTDIFF);
- io.enumCase(value, "GENERIC_RELOC_LOCAL_SECTDIFF",
- llvm::MachO::GENERIC_RELOC_LOCAL_SECTDIFF);
- io.enumCase(value, "GENERIC_RELOC_TLV",
- llvm::MachO::GENERIC_RELOC_TLV);
- break;
- case lld::MachOLinkingContext::arch_armv6:
- case lld::MachOLinkingContext::arch_armv7:
- case lld::MachOLinkingContext::arch_armv7s:
- io.enumCase(value, "ARM_RELOC_VANILLA",
- llvm::MachO::ARM_RELOC_VANILLA);
- io.enumCase(value, "ARM_RELOC_PAIR",
- llvm::MachO::ARM_RELOC_PAIR);
- io.enumCase(value, "ARM_RELOC_SECTDIFF",
- llvm::MachO::ARM_RELOC_SECTDIFF);
- io.enumCase(value, "ARM_RELOC_LOCAL_SECTDIFF",
- llvm::MachO::ARM_RELOC_LOCAL_SECTDIFF);
- io.enumCase(value, "ARM_RELOC_BR24",
- llvm::MachO::ARM_RELOC_BR24);
- io.enumCase(value, "ARM_THUMB_RELOC_BR22",
- llvm::MachO::ARM_THUMB_RELOC_BR22);
- io.enumCase(value, "ARM_RELOC_HALF",
- llvm::MachO::ARM_RELOC_HALF);
- io.enumCase(value, "ARM_RELOC_HALF_SECTDIFF",
- llvm::MachO::ARM_RELOC_HALF_SECTDIFF);
- break;
- case lld::MachOLinkingContext::arch_arm64:
- io.enumCase(value, "ARM64_RELOC_UNSIGNED",
- llvm::MachO::ARM64_RELOC_UNSIGNED);
- io.enumCase(value, "ARM64_RELOC_SUBTRACTOR",
- llvm::MachO::ARM64_RELOC_SUBTRACTOR);
- io.enumCase(value, "ARM64_RELOC_BRANCH26",
- llvm::MachO::ARM64_RELOC_BRANCH26);
- io.enumCase(value, "ARM64_RELOC_PAGE21",
- llvm::MachO::ARM64_RELOC_PAGE21);
- io.enumCase(value, "ARM64_RELOC_PAGEOFF12",
- llvm::MachO::ARM64_RELOC_PAGEOFF12);
- io.enumCase(value, "ARM64_RELOC_GOT_LOAD_PAGE21",
- llvm::MachO::ARM64_RELOC_GOT_LOAD_PAGE21);
- io.enumCase(value, "ARM64_RELOC_GOT_LOAD_PAGEOFF12",
- llvm::MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12);
- io.enumCase(value, "ARM64_RELOC_POINTER_TO_GOT",
- llvm::MachO::ARM64_RELOC_POINTER_TO_GOT);
- io.enumCase(value, "ARM64_RELOC_TLVP_LOAD_PAGE21",
- llvm::MachO::ARM64_RELOC_TLVP_LOAD_PAGE21);
- io.enumCase(value, "ARM64_RELOC_TLVP_LOAD_PAGEOFF12",
- llvm::MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12);
- io.enumCase(value, "ARM64_RELOC_ADDEND",
- llvm::MachO::ARM64_RELOC_ADDEND);
- break;
- default:
- llvm_unreachable("unknown architecture");
- }
- }
-};
-
-
-template <>
-struct MappingTraits<Symbol> {
- static void mapping(IO &io, Symbol& sym) {
- io.mapRequired("name", sym.name);
- io.mapRequired("type", sym.type);
- io.mapOptional("scope", sym.scope, SymbolScope(0));
- io.mapOptional("sect", sym.sect, (uint8_t)0);
- if (sym.type == llvm::MachO::N_UNDF) {
- // In undef symbols, desc field contains alignment/ordinal info
- // which is better represented as a hex vaule.
- uint16_t t1 = sym.desc;
- Hex16 t2 = t1;
- io.mapOptional("desc", t2, Hex16(0));
- sym.desc = t2;
- } else {
- // In defined symbols, desc fit is a set of option bits.
- io.mapOptional("desc", sym.desc, SymbolDesc(0));
- }
- io.mapRequired("value", sym.value);
- }
-};
-
-// Custom mapping for VMProtect (e.g. "r-x").
-template <>
-struct ScalarTraits<VMProtect> {
- static void output(const VMProtect &value, void*, raw_ostream &out) {
- out << ( (value & llvm::MachO::VM_PROT_READ) ? 'r' : '-');
- out << ( (value & llvm::MachO::VM_PROT_WRITE) ? 'w' : '-');
- out << ( (value & llvm::MachO::VM_PROT_EXECUTE) ? 'x' : '-');
- }
- static StringRef input(StringRef scalar, void*, VMProtect &value) {
- value = 0;
- if (scalar.size() != 3)
- return "segment access protection must be three chars (e.g. \"r-x\")";
- switch (scalar[0]) {
- case 'r':
- value = llvm::MachO::VM_PROT_READ;
- break;
- case '-':
- break;
- default:
- return "segment access protection first char must be 'r' or '-'";
- }
- switch (scalar[1]) {
- case 'w':
- value = value | llvm::MachO::VM_PROT_WRITE;
- break;
- case '-':
- break;
- default:
- return "segment access protection second char must be 'w' or '-'";
- }
- switch (scalar[2]) {
- case 'x':
- value = value | llvm::MachO::VM_PROT_EXECUTE;
- break;
- case '-':
- break;
- default:
- return "segment access protection third char must be 'x' or '-'";
- }
- // Return the empty string on success,
- return StringRef();
- }
- static QuotingType mustQuote(StringRef) { return QuotingType::None; }
-};
-
-
-template <>
-struct MappingTraits<Segment> {
- static void mapping(IO &io, Segment& seg) {
- io.mapRequired("name", seg.name);
- io.mapRequired("address", seg.address);
- io.mapRequired("size", seg.size);
- io.mapRequired("init-access", seg.init_access);
- io.mapRequired("max-access", seg.max_access);
- }
-};
-
-template <>
-struct ScalarEnumerationTraits<LoadCommandType> {
- static void enumeration(IO &io, LoadCommandType &value) {
- io.enumCase(value, "LC_LOAD_DYLIB",
- llvm::MachO::LC_LOAD_DYLIB);
- io.enumCase(value, "LC_LOAD_WEAK_DYLIB",
- llvm::MachO::LC_LOAD_WEAK_DYLIB);
- io.enumCase(value, "LC_REEXPORT_DYLIB",
- llvm::MachO::LC_REEXPORT_DYLIB);
- io.enumCase(value, "LC_LOAD_UPWARD_DYLIB",
- llvm::MachO::LC_LOAD_UPWARD_DYLIB);
- io.enumCase(value, "LC_LAZY_LOAD_DYLIB",
- llvm::MachO::LC_LAZY_LOAD_DYLIB);
- io.enumCase(value, "LC_VERSION_MIN_MACOSX",
- llvm::MachO::LC_VERSION_MIN_MACOSX);
- io.enumCase(value, "LC_VERSION_MIN_IPHONEOS",
- llvm::MachO::LC_VERSION_MIN_IPHONEOS);
- io.enumCase(value, "LC_VERSION_MIN_TVOS",
- llvm::MachO::LC_VERSION_MIN_TVOS);
- io.enumCase(value, "LC_VERSION_MIN_WATCHOS",
- llvm::MachO::LC_VERSION_MIN_WATCHOS);
- }
-};
-
-template <>
-struct MappingTraits<DependentDylib> {
- static void mapping(IO &io, DependentDylib& dylib) {
- io.mapRequired("path", dylib.path);
- io.mapOptional("kind", dylib.kind,
- llvm::MachO::LC_LOAD_DYLIB);
- io.mapOptional("compat-version", dylib.compatVersion,
- PackedVersion(0x10000));
- io.mapOptional("current-version", dylib.currentVersion,
- PackedVersion(0x10000));
- }
-};
-
-template <>
-struct ScalarEnumerationTraits<RebaseType> {
- static void enumeration(IO &io, RebaseType &value) {
- io.enumCase(value, "REBASE_TYPE_POINTER",
- llvm::MachO::REBASE_TYPE_POINTER);
- io.enumCase(value, "REBASE_TYPE_TEXT_PCREL32",
- llvm::MachO::REBASE_TYPE_TEXT_PCREL32);
- io.enumCase(value, "REBASE_TYPE_TEXT_ABSOLUTE32",
- llvm::MachO::REBASE_TYPE_TEXT_ABSOLUTE32);
- }
-};
-
-
-template <>
-struct MappingTraits<RebaseLocation> {
- static void mapping(IO &io, RebaseLocation& rebase) {
- io.mapRequired("segment-index", rebase.segIndex);
- io.mapRequired("segment-offset", rebase.segOffset);
- io.mapOptional("kind", rebase.kind,
- llvm::MachO::REBASE_TYPE_POINTER);
- }
-};
-
-
-
-template <>
-struct ScalarEnumerationTraits<BindType> {
- static void enumeration(IO &io, BindType &value) {
- io.enumCase(value, "BIND_TYPE_POINTER",
- llvm::MachO::BIND_TYPE_POINTER);
- io.enumCase(value, "BIND_TYPE_TEXT_ABSOLUTE32",
- llvm::MachO::BIND_TYPE_TEXT_ABSOLUTE32);
- io.enumCase(value, "BIND_TYPE_TEXT_PCREL32",
- llvm::MachO::BIND_TYPE_TEXT_PCREL32);
- }
-};
-
-template <>
-struct MappingTraits<BindLocation> {
- static void mapping(IO &io, BindLocation &bind) {
- io.mapRequired("segment-index", bind.segIndex);
- io.mapRequired("segment-offset", bind.segOffset);
- io.mapOptional("kind", bind.kind,
- llvm::MachO::BIND_TYPE_POINTER);
- io.mapOptional("can-be-null", bind.canBeNull, false);
- io.mapRequired("ordinal", bind.ordinal);
- io.mapRequired("symbol-name", bind.symbolName);
- io.mapOptional("addend", bind.addend, Hex64(0));
- }
-};
-
-
-template <>
-struct ScalarEnumerationTraits<ExportSymbolKind> {
- static void enumeration(IO &io, ExportSymbolKind &value) {
- io.enumCase(value, "EXPORT_SYMBOL_FLAGS_KIND_REGULAR",
- llvm::MachO::EXPORT_SYMBOL_FLAGS_KIND_REGULAR);
- io.enumCase(value, "EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL",
- llvm::MachO::EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL);
- io.enumCase(value, "EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE",
- llvm::MachO::EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE);
- }
-};
-
-template <>
-struct ScalarBitSetTraits<ExportFlags> {
- static void bitset(IO &io, ExportFlags &value) {
- io.bitSetCase(value, "EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION",
- llvm::MachO::EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION);
- io.bitSetCase(value, "EXPORT_SYMBOL_FLAGS_REEXPORT",
- llvm::MachO::EXPORT_SYMBOL_FLAGS_REEXPORT);
- io.bitSetCase(value, "EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER",
- llvm::MachO::EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER);
- }
-};
-
-
-template <>
-struct MappingTraits<Export> {
- static void mapping(IO &io, Export &exp) {
- io.mapRequired("name", exp.name);
- io.mapOptional("offset", exp.offset);
- io.mapOptional("kind", exp.kind,
- llvm::MachO::EXPORT_SYMBOL_FLAGS_KIND_REGULAR);
- if (!io.outputting() || exp.flags)
- io.mapOptional("flags", exp.flags);
- io.mapOptional("other", exp.otherOffset, Hex32(0));
- io.mapOptional("other-name", exp.otherName, StringRef());
- }
-};
-
-template <>
-struct ScalarEnumerationTraits<DataRegionType> {
- static void enumeration(IO &io, DataRegionType &value) {
- io.enumCase(value, "DICE_KIND_DATA",
- llvm::MachO::DICE_KIND_DATA);
- io.enumCase(value, "DICE_KIND_JUMP_TABLE8",
- llvm::MachO::DICE_KIND_JUMP_TABLE8);
- io.enumCase(value, "DICE_KIND_JUMP_TABLE16",
- llvm::MachO::DICE_KIND_JUMP_TABLE16);
- io.enumCase(value, "DICE_KIND_JUMP_TABLE32",
- llvm::MachO::DICE_KIND_JUMP_TABLE32);
- io.enumCase(value, "DICE_KIND_ABS_JUMP_TABLE32",
- llvm::MachO::DICE_KIND_ABS_JUMP_TABLE32);
- }
-};
-
-template <>
-struct MappingTraits<DataInCode> {
- static void mapping(IO &io, DataInCode &entry) {
- io.mapRequired("offset", entry.offset);
- io.mapRequired("length", entry.length);
- io.mapRequired("kind", entry.kind);
- }
-};
-
-template <>
-struct ScalarTraits<PackedVersion> {
- static void output(const PackedVersion &value, void*, raw_ostream &out) {
- out << llvm::format("%d.%d", (value >> 16), (value >> 8) & 0xFF);
- if (value & 0xFF) {
- out << llvm::format(".%d", (value & 0xFF));
- }
- }
- static StringRef input(StringRef scalar, void*, PackedVersion &result) {
- uint32_t value;
- if (lld::MachOLinkingContext::parsePackedVersion(scalar, value))
- return "malformed version number";
- result = value;
- // Return the empty string on success,
- return StringRef();
- }
- static QuotingType mustQuote(StringRef) { return QuotingType::None; }
-};
-
-template <>
-struct MappingTraits<NormalizedFile> {
- static void mapping(IO &io, NormalizedFile &file) {
- io.mapRequired("arch", file.arch);
- io.mapRequired("file-type", file.fileType);
- io.mapOptional("flags", file.flags);
- io.mapOptional("dependents", file.dependentDylibs);
- io.mapOptional("install-name", file.installName, StringRef());
- io.mapOptional("compat-version", file.compatVersion, PackedVersion(0x10000));
- io.mapOptional("current-version", file.currentVersion, PackedVersion(0x10000));
- io.mapOptional("has-UUID", file.hasUUID, true);
- io.mapOptional("rpaths", file.rpaths);
- io.mapOptional("entry-point", file.entryAddress, Hex64(0));
- io.mapOptional("stack-size", file.stackSize, Hex64(0));
- io.mapOptional("source-version", file.sourceVersion, Hex64(0));
- io.mapOptional("OS", file.os);
- io.mapOptional("min-os-version", file.minOSverson, PackedVersion(0));
- io.mapOptional("min-os-version-kind", file.minOSVersionKind, (LoadCommandType)0);
- io.mapOptional("sdk-version", file.sdkVersion, PackedVersion(0));
- io.mapOptional("segments", file.segments);
- io.mapOptional("sections", file.sections);
- io.mapOptional("local-symbols", file.localSymbols);
- io.mapOptional("global-symbols", file.globalSymbols);
- io.mapOptional("undefined-symbols",file.undefinedSymbols);
- io.mapOptional("page-size", file.pageSize, Hex32(4096));
- io.mapOptional("rebasings", file.rebasingInfo);
- io.mapOptional("bindings", file.bindingInfo);
- io.mapOptional("weak-bindings", file.weakBindingInfo);
- io.mapOptional("lazy-bindings", file.lazyBindingInfo);
- io.mapOptional("exports", file.exportInfo);
- io.mapOptional("dataInCode", file.dataInCode);
- }
- static std::string validate(IO &io, NormalizedFile &file) { return {}; }
-};
-
-} // namespace llvm
-} // namespace yaml
-
-
-namespace lld {
-namespace mach_o {
-
-/// Handles !mach-o tagged yaml documents.
-bool MachOYamlIOTaggedDocumentHandler::handledDocTag(llvm::yaml::IO &io,
- const lld::File *&file) const {
- if (!io.mapTag("!mach-o"))
- return false;
- // Step 1: parse yaml into normalized mach-o struct.
- NormalizedFile nf;
- YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
- assert(info != nullptr);
- assert(info->_normalizeMachOFile == nullptr);
- info->_normalizeMachOFile = &nf;
- MappingTraits<NormalizedFile>::mapping(io, nf);
- // Step 2: parse normalized mach-o struct into atoms.
- auto fileOrError = normalizedToAtoms(nf, info->_path, true);
-
- // Check that we parsed successfully.
- if (!fileOrError) {
- std::string buffer;
- llvm::raw_string_ostream stream(buffer);
- handleAllErrors(fileOrError.takeError(),
- [&](const llvm::ErrorInfoBase &EI) {
- EI.log(stream);
- stream << "\n";
- });
- io.setError(stream.str());
- return false;
- }
-
- if (nf.arch != _arch) {
- io.setError(Twine("file is wrong architecture. Expected ("
- + MachOLinkingContext::nameFromArch(_arch)
- + ") found ("
- + MachOLinkingContext::nameFromArch(nf.arch)
- + ")"));
- return false;
- }
- info->_normalizeMachOFile = nullptr;
- file = fileOrError->release();
- return true;
-}
-
-
-
-namespace normalized {
-
-/// Parses a yaml encoded mach-o file to produce an in-memory normalized view.
-llvm::Expected<std::unique_ptr<NormalizedFile>>
-readYaml(std::unique_ptr<MemoryBuffer> &mb) {
- // Make empty NormalizedFile.
- std::unique_ptr<NormalizedFile> f(new NormalizedFile());
-
- // Create YAML Input parser.
- YamlContext yamlContext;
- yamlContext._normalizeMachOFile = f.get();
- llvm::yaml::Input yin(mb->getBuffer(), &yamlContext);
-
- // Fill NormalizedFile by parsing yaml.
- yin >> *f;
-
- // Return error if there were parsing problems.
- if (auto ec = yin.error())
- return llvm::make_error<GenericError>(Twine("YAML parsing error: ")
- + ec.message());
-
- // Hand ownership of instantiated NormalizedFile to caller.
- return std::move(f);
-}
-
-
-/// Writes a yaml encoded mach-o files from an in-memory normalized view.
-std::error_code writeYaml(const NormalizedFile &file, raw_ostream &out) {
- // YAML I/O is not const aware, so need to cast away ;-(
- NormalizedFile *f = const_cast<NormalizedFile*>(&file);
-
- // Create yaml Output writer, using yaml options for context.
- YamlContext yamlContext;
- yamlContext._normalizeMachOFile = f;
- llvm::yaml::Output yout(out, &yamlContext);
-
- // Stream out yaml.
- yout << *f;
-
- return std::error_code();
-}
-
-} // namespace normalized
-} // namespace mach_o
-} // namespace lld
+++ /dev/null
-//===- lib/ReaderWriter/MachO/MachOPasses.h -------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_READER_WRITER_MACHO_PASSES_H
-#define LLD_READER_WRITER_MACHO_PASSES_H
-
-#include "lld/Core/PassManager.h"
-#include "lld/ReaderWriter/MachOLinkingContext.h"
-
-namespace lld {
-namespace mach_o {
-
-void addLayoutPass(PassManager &pm, const MachOLinkingContext &ctx);
-void addStubsPass(PassManager &pm, const MachOLinkingContext &ctx);
-void addGOTPass(PassManager &pm, const MachOLinkingContext &ctx);
-void addTLVPass(PassManager &pm, const MachOLinkingContext &ctx);
-void addCompactUnwindPass(PassManager &pm, const MachOLinkingContext &ctx);
-void addObjCPass(PassManager &pm, const MachOLinkingContext &ctx);
-void addShimPass(PassManager &pm, const MachOLinkingContext &ctx);
-
-} // namespace mach_o
-} // namespace lld
-
-#endif // LLD_READER_WRITER_MACHO_PASSES_H
+++ /dev/null
-//===- lib/ReaderWriter/MachO/ObjCPass.cpp -------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-//
-//===----------------------------------------------------------------------===//
-
-#include "ArchHandler.h"
-#include "File.h"
-#include "MachONormalizedFileBinaryUtils.h"
-#include "MachOPasses.h"
-#include "lld/Common/LLVM.h"
-#include "lld/Core/DefinedAtom.h"
-#include "lld/Core/File.h"
-#include "lld/Core/Reference.h"
-#include "lld/Core/Simple.h"
-#include "lld/ReaderWriter/MachOLinkingContext.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/STLExtras.h"
-
-namespace lld {
-namespace mach_o {
-
-///
-/// ObjC Image Info Atom created by the ObjC pass.
-///
-class ObjCImageInfoAtom : public SimpleDefinedAtom {
-public:
- ObjCImageInfoAtom(const File &file, bool isBig,
- MachOLinkingContext::ObjCConstraint objCConstraint,
- uint32_t swiftVersion)
- : SimpleDefinedAtom(file) {
-
- Data.info.version = 0;
-
- switch (objCConstraint) {
- case MachOLinkingContext::objc_unknown:
- llvm_unreachable("Shouldn't run the objc pass without a constraint");
- case MachOLinkingContext::objc_supports_gc:
- case MachOLinkingContext::objc_gc_only:
- llvm_unreachable("GC is not supported");
- case MachOLinkingContext::objc_retainReleaseForSimulator:
- // The retain/release for simulator flag is already the correct
- // encoded value for the data so just set it here.
- Data.info.flags = (uint32_t)objCConstraint;
- break;
- case MachOLinkingContext::objc_retainRelease:
- // We don't need to encode this flag, so just leave the flags as 0.
- Data.info.flags = 0;
- break;
- }
-
- Data.info.flags |= (swiftVersion << 8);
-
- normalized::write32(Data.bytes + 4, Data.info.flags, isBig);
- }
-
- ~ObjCImageInfoAtom() override = default;
-
- ContentType contentType() const override {
- return DefinedAtom::typeObjCImageInfo;
- }
-
- Alignment alignment() const override {
- return 4;
- }
-
- uint64_t size() const override {
- return 8;
- }
-
- ContentPermissions permissions() const override {
- return DefinedAtom::permR__;
- }
-
- ArrayRef<uint8_t> rawContent() const override {
- return llvm::makeArrayRef(Data.bytes, size());
- }
-
-private:
-
- struct objc_image_info {
- uint32_t version;
- uint32_t flags;
- };
-
- union {
- objc_image_info info;
- uint8_t bytes[8];
- } Data;
-};
-
-class ObjCPass : public Pass {
-public:
- ObjCPass(const MachOLinkingContext &context)
- : _ctx(context),
- _file(*_ctx.make_file<MachOFile>("<mach-o objc pass>")) {
- _file.setOrdinal(_ctx.getNextOrdinalAndIncrement());
- }
-
- llvm::Error perform(SimpleFile &mergedFile) override {
- // Add the image info.
- mergedFile.addAtom(*getImageInfo());
-
- return llvm::Error::success();
- }
-
-private:
-
- const DefinedAtom* getImageInfo() {
- bool IsBig = MachOLinkingContext::isBigEndian(_ctx.arch());
- return new (_file.allocator()) ObjCImageInfoAtom(_file, IsBig,
- _ctx.objcConstraint(),
- _ctx.swiftVersion());
- }
-
- const MachOLinkingContext &_ctx;
- MachOFile &_file;
-};
-
-
-
-void addObjCPass(PassManager &pm, const MachOLinkingContext &ctx) {
- pm.add(std::make_unique<ObjCPass>(ctx));
-}
-
-} // end namespace mach_o
-} // end namespace lld
+++ /dev/null
-//===---- lib/ReaderWriter/MachO/SectCreateFile.h ---------------*- c++ -*-===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_READER_WRITER_MACHO_SECTCREATE_FILE_H
-#define LLD_READER_WRITER_MACHO_SECTCREATE_FILE_H
-
-#include "lld/Core/DefinedAtom.h"
-#include "lld/Core/Simple.h"
-#include "lld/ReaderWriter/MachOLinkingContext.h"
-
-namespace lld {
-namespace mach_o {
-
-//
-// A FlateNamespaceFile instance may be added as a resolution source of last
-// resort, depending on how -flat_namespace and -undefined are set.
-//
-class SectCreateFile : public File {
-public:
- class SectCreateAtom : public SimpleDefinedAtom {
- public:
- SectCreateAtom(const File &file, StringRef segName, StringRef sectName,
- std::unique_ptr<MemoryBuffer> content)
- : SimpleDefinedAtom(file),
- _combinedName((segName + "/" + sectName).str()),
- _content(std::move(content)) {}
-
- ~SectCreateAtom() override = default;
-
- uint64_t size() const override { return _content->getBufferSize(); }
-
- Scope scope() const override { return scopeGlobal; }
-
- ContentType contentType() const override { return typeSectCreate; }
-
- SectionChoice sectionChoice() const override { return sectionCustomRequired; }
-
- StringRef customSectionName() const override { return _combinedName; }
-
- DeadStripKind deadStrip() const override { return deadStripNever; }
-
- ArrayRef<uint8_t> rawContent() const override {
- const uint8_t *data =
- reinterpret_cast<const uint8_t*>(_content->getBufferStart());
- return ArrayRef<uint8_t>(data, _content->getBufferSize());
- }
-
- StringRef segmentName() const { return _segName; }
- StringRef sectionName() const { return _sectName; }
-
- private:
- std::string _combinedName;
- StringRef _segName;
- StringRef _sectName;
- std::unique_ptr<MemoryBuffer> _content;
- };
-
- SectCreateFile() : File("sectcreate", kindSectCreateObject) {}
-
- void addSection(StringRef seg, StringRef sect,
- std::unique_ptr<MemoryBuffer> content) {
- _definedAtoms.push_back(
- new (allocator()) SectCreateAtom(*this, seg, sect, std::move(content)));
- }
-
- const AtomRange<DefinedAtom> defined() const override {
- return _definedAtoms;
- }
-
- const AtomRange<UndefinedAtom> undefined() const override {
- return _noUndefinedAtoms;
- }
-
- const AtomRange<SharedLibraryAtom> sharedLibrary() const override {
- return _noSharedLibraryAtoms;
- }
-
- const AtomRange<AbsoluteAtom> absolute() const override {
- return _noAbsoluteAtoms;
- }
-
- void clearAtoms() override {
- _definedAtoms.clear();
- _noUndefinedAtoms.clear();
- _noSharedLibraryAtoms.clear();
- _noAbsoluteAtoms.clear();
- }
-
-private:
- AtomVector<DefinedAtom> _definedAtoms;
-};
-
-} // namespace mach_o
-} // namespace lld
-
-#endif // LLD_READER_WRITER_MACHO_SECTCREATE_FILE_H
+++ /dev/null
-//===- lib/ReaderWriter/MachO/ShimPass.cpp -------------------------------===//
-//
-// 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 linker pass updates branch-sites whose target is a different mode
-// (thumb vs arm).
-//
-// Arm code has two instruction encodings thumb and arm. When branching from
-// one code encoding to another, you need to use an instruction that switches
-// the instruction mode. Usually the transition only happens at call sites, and
-// the linker can transform a BL instruction in BLX (or vice versa). But if the
-// compiler did a tail call optimization and a function ends with a branch (not
-// branch and link), there is no pc-rel BX instruction.
-//
-// The ShimPass looks for pc-rel B instructions that will need to switch mode.
-// For those cases it synthesizes a shim which does the transition, then
-// modifies the original atom with the B instruction to target to the shim atom.
-//
-//===----------------------------------------------------------------------===//
-
-#include "ArchHandler.h"
-#include "File.h"
-#include "MachOPasses.h"
-#include "lld/Common/LLVM.h"
-#include "lld/Core/DefinedAtom.h"
-#include "lld/Core/File.h"
-#include "lld/Core/Reference.h"
-#include "lld/Core/Simple.h"
-#include "lld/ReaderWriter/MachOLinkingContext.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/STLExtras.h"
-
-namespace lld {
-namespace mach_o {
-
-class ShimPass : public Pass {
-public:
- ShimPass(const MachOLinkingContext &context)
- : _ctx(context), _archHandler(_ctx.archHandler()),
- _stubInfo(_archHandler.stubInfo()),
- _file(*_ctx.make_file<MachOFile>("<mach-o shim pass>")) {
- _file.setOrdinal(_ctx.getNextOrdinalAndIncrement());
- }
-
- llvm::Error perform(SimpleFile &mergedFile) override {
- // Scan all references in all atoms.
- for (const DefinedAtom *atom : mergedFile.defined()) {
- for (const Reference *ref : *atom) {
- // Look at non-call branches.
- if (!_archHandler.isNonCallBranch(*ref))
- continue;
- const Atom *target = ref->target();
- assert(target != nullptr);
- if (const lld::DefinedAtom *daTarget = dyn_cast<DefinedAtom>(target)) {
- bool atomIsThumb = _archHandler.isThumbFunction(*atom);
- bool targetIsThumb = _archHandler.isThumbFunction(*daTarget);
- if (atomIsThumb != targetIsThumb)
- updateBranchToUseShim(atomIsThumb, *daTarget, ref);
- }
- }
- }
- // Exit early if no shims needed.
- if (_targetToShim.empty())
- return llvm::Error::success();
-
- // Sort shim atoms so the layout order is stable.
- std::vector<const DefinedAtom *> shims;
- shims.reserve(_targetToShim.size());
- for (auto element : _targetToShim) {
- shims.push_back(element.second);
- }
- std::sort(shims.begin(), shims.end(),
- [](const DefinedAtom *l, const DefinedAtom *r) {
- return (l->name() < r->name());
- });
-
- // Add all shims to master file.
- for (const DefinedAtom *shim : shims)
- mergedFile.addAtom(*shim);
-
- return llvm::Error::success();
- }
-
-private:
-
- void updateBranchToUseShim(bool thumbToArm, const DefinedAtom& target,
- const Reference *ref) {
- // Make file-format specific stub and other support atoms.
- const DefinedAtom *shim = this->getShim(thumbToArm, target);
- assert(shim != nullptr);
- // Switch branch site to target shim atom.
- const_cast<Reference *>(ref)->setTarget(shim);
- }
-
- const DefinedAtom* getShim(bool thumbToArm, const DefinedAtom& target) {
- auto pos = _targetToShim.find(&target);
- if ( pos != _targetToShim.end() ) {
- // Reuse an existing shim.
- assert(pos->second != nullptr);
- return pos->second;
- } else {
- // There is no existing shim, so create a new one.
- const DefinedAtom *shim = _archHandler.createShim(_file, thumbToArm,
- target);
- _targetToShim[&target] = shim;
- return shim;
- }
- }
-
- const MachOLinkingContext &_ctx;
- mach_o::ArchHandler &_archHandler;
- const ArchHandler::StubInfo &_stubInfo;
- MachOFile &_file;
- llvm::DenseMap<const Atom*, const DefinedAtom*> _targetToShim;
-};
-
-
-
-void addShimPass(PassManager &pm, const MachOLinkingContext &ctx) {
- pm.add(std::make_unique<ShimPass>(ctx));
-}
-
-} // end namespace mach_o
-} // end namespace lld
+++ /dev/null
-//===- lib/ReaderWriter/MachO/StubsPass.cpp ---------------------*- C++ -*-===//
-//
-// 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 linker pass updates call-sites which have references to shared library
-// atoms to instead have a reference to a stub (PLT entry) for the specified
-// symbol. Each file format defines a subclass of StubsPass which implements
-// the abstract methods for creating the file format specific StubAtoms.
-//
-//===----------------------------------------------------------------------===//
-
-#include "ArchHandler.h"
-#include "File.h"
-#include "MachOPasses.h"
-#include "lld/Common/LLVM.h"
-#include "lld/Core/DefinedAtom.h"
-#include "lld/Core/File.h"
-#include "lld/Core/Reference.h"
-#include "lld/Core/Simple.h"
-#include "lld/ReaderWriter/MachOLinkingContext.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/SmallVector.h"
-
-namespace lld {
-namespace mach_o {
-
-//
-// Lazy Pointer Atom created by the stubs pass.
-//
-class LazyPointerAtom : public SimpleDefinedAtom {
-public:
- LazyPointerAtom(const File &file, bool is64)
- : SimpleDefinedAtom(file), _is64(is64) { }
-
- ~LazyPointerAtom() override = default;
-
- ContentType contentType() const override {
- return DefinedAtom::typeLazyPointer;
- }
-
- Alignment alignment() const override {
- return _is64 ? 8 : 4;
- }
-
- uint64_t size() const override {
- return _is64 ? 8 : 4;
- }
-
- ContentPermissions permissions() const override {
- return DefinedAtom::permRW_;
- }
-
- ArrayRef<uint8_t> rawContent() const override {
- static const uint8_t zeros[] =
- { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
- return llvm::makeArrayRef(zeros, size());
- }
-
-private:
- const bool _is64;
-};
-
-//
-// NonLazyPointer (GOT) Atom created by the stubs pass.
-//
-class NonLazyPointerAtom : public SimpleDefinedAtom {
-public:
- NonLazyPointerAtom(const File &file, bool is64, ContentType contentType)
- : SimpleDefinedAtom(file), _is64(is64), _contentType(contentType) { }
-
- ~NonLazyPointerAtom() override = default;
-
- ContentType contentType() const override {
- return _contentType;
- }
-
- Alignment alignment() const override {
- return _is64 ? 8 : 4;
- }
-
- uint64_t size() const override {
- return _is64 ? 8 : 4;
- }
-
- ContentPermissions permissions() const override {
- return DefinedAtom::permRW_;
- }
-
- ArrayRef<uint8_t> rawContent() const override {
- static const uint8_t zeros[] =
- { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
- return llvm::makeArrayRef(zeros, size());
- }
-
-private:
- const bool _is64;
- const ContentType _contentType;
-};
-
-//
-// Stub Atom created by the stubs pass.
-//
-class StubAtom : public SimpleDefinedAtom {
-public:
- StubAtom(const File &file, const ArchHandler::StubInfo &stubInfo)
- : SimpleDefinedAtom(file), _stubInfo(stubInfo){ }
-
- ~StubAtom() override = default;
-
- ContentType contentType() const override {
- return DefinedAtom::typeStub;
- }
-
- Alignment alignment() const override {
- return 1 << _stubInfo.codeAlignment;
- }
-
- uint64_t size() const override {
- return _stubInfo.stubSize;
- }
-
- ContentPermissions permissions() const override {
- return DefinedAtom::permR_X;
- }
-
- ArrayRef<uint8_t> rawContent() const override {
- return llvm::makeArrayRef(_stubInfo.stubBytes, _stubInfo.stubSize);
- }
-
-private:
- const ArchHandler::StubInfo &_stubInfo;
-};
-
-//
-// Stub Helper Atom created by the stubs pass.
-//
-class StubHelperAtom : public SimpleDefinedAtom {
-public:
- StubHelperAtom(const File &file, const ArchHandler::StubInfo &stubInfo)
- : SimpleDefinedAtom(file), _stubInfo(stubInfo) { }
-
- ~StubHelperAtom() override = default;
-
- ContentType contentType() const override {
- return DefinedAtom::typeStubHelper;
- }
-
- Alignment alignment() const override {
- return 1 << _stubInfo.codeAlignment;
- }
-
- uint64_t size() const override {
- return _stubInfo.stubHelperSize;
- }
-
- ContentPermissions permissions() const override {
- return DefinedAtom::permR_X;
- }
-
- ArrayRef<uint8_t> rawContent() const override {
- return llvm::makeArrayRef(_stubInfo.stubHelperBytes,
- _stubInfo.stubHelperSize);
- }
-
-private:
- const ArchHandler::StubInfo &_stubInfo;
-};
-
-//
-// Stub Helper Common Atom created by the stubs pass.
-//
-class StubHelperCommonAtom : public SimpleDefinedAtom {
-public:
- StubHelperCommonAtom(const File &file, const ArchHandler::StubInfo &stubInfo)
- : SimpleDefinedAtom(file), _stubInfo(stubInfo) { }
-
- ~StubHelperCommonAtom() override = default;
-
- ContentType contentType() const override {
- return DefinedAtom::typeStubHelper;
- }
-
- Alignment alignment() const override {
- return 1 << _stubInfo.stubHelperCommonAlignment;
- }
-
- uint64_t size() const override {
- return _stubInfo.stubHelperCommonSize;
- }
-
- ContentPermissions permissions() const override {
- return DefinedAtom::permR_X;
- }
-
- ArrayRef<uint8_t> rawContent() const override {
- return llvm::makeArrayRef(_stubInfo.stubHelperCommonBytes,
- _stubInfo.stubHelperCommonSize);
- }
-
-private:
- const ArchHandler::StubInfo &_stubInfo;
-};
-
-class StubsPass : public Pass {
-public:
- StubsPass(const MachOLinkingContext &context)
- : _ctx(context), _archHandler(_ctx.archHandler()),
- _stubInfo(_archHandler.stubInfo()),
- _file(*_ctx.make_file<MachOFile>("<mach-o Stubs pass>")) {
- _file.setOrdinal(_ctx.getNextOrdinalAndIncrement());
- }
-
- llvm::Error perform(SimpleFile &mergedFile) override {
- // Skip this pass if output format uses text relocations instead of stubs.
- if (!this->noTextRelocs())
- return llvm::Error::success();
-
- // Scan all references in all atoms.
- for (const DefinedAtom *atom : mergedFile.defined()) {
- for (const Reference *ref : *atom) {
- // Look at call-sites.
- if (!this->isCallSite(*ref))
- continue;
- const Atom *target = ref->target();
- assert(target != nullptr);
- if (isa<SharedLibraryAtom>(target)) {
- // Calls to shared libraries go through stubs.
- _targetToUses[target].push_back(ref);
- continue;
- }
- const DefinedAtom *defTarget = dyn_cast<DefinedAtom>(target);
- if (defTarget && defTarget->interposable() != DefinedAtom::interposeNo){
- // Calls to interposable functions in same linkage unit must also go
- // through a stub.
- assert(defTarget->scope() != DefinedAtom::scopeTranslationUnit);
- _targetToUses[target].push_back(ref);
- }
- }
- }
-
- // Exit early if no stubs needed.
- if (_targetToUses.empty())
- return llvm::Error::success();
-
- // First add help-common and GOT slots used by lazy binding.
- SimpleDefinedAtom *helperCommonAtom =
- new (_file.allocator()) StubHelperCommonAtom(_file, _stubInfo);
- SimpleDefinedAtom *helperCacheNLPAtom =
- new (_file.allocator()) NonLazyPointerAtom(_file, _ctx.is64Bit(),
- _stubInfo.stubHelperImageCacheContentType);
- SimpleDefinedAtom *helperBinderNLPAtom =
- new (_file.allocator()) NonLazyPointerAtom(_file, _ctx.is64Bit(),
- _stubInfo.stubHelperImageCacheContentType);
- addReference(helperCommonAtom, _stubInfo.stubHelperCommonReferenceToCache,
- helperCacheNLPAtom);
- addOptReference(
- helperCommonAtom, _stubInfo.stubHelperCommonReferenceToCache,
- _stubInfo.optStubHelperCommonReferenceToCache, helperCacheNLPAtom);
- addReference(helperCommonAtom, _stubInfo.stubHelperCommonReferenceToBinder,
- helperBinderNLPAtom);
- addOptReference(
- helperCommonAtom, _stubInfo.stubHelperCommonReferenceToBinder,
- _stubInfo.optStubHelperCommonReferenceToBinder, helperBinderNLPAtom);
- mergedFile.addAtom(*helperCommonAtom);
- mergedFile.addAtom(*helperBinderNLPAtom);
- mergedFile.addAtom(*helperCacheNLPAtom);
-
- // Add reference to dyld_stub_binder in libSystem.dylib
- auto I = llvm::find_if(
- mergedFile.sharedLibrary(), [&](const SharedLibraryAtom *atom) {
- return atom->name().equals(_stubInfo.binderSymbolName);
- });
- assert(I != mergedFile.sharedLibrary().end() &&
- "dyld_stub_binder not found");
- addReference(helperBinderNLPAtom, _stubInfo.nonLazyPointerReferenceToBinder, *I);
-
- // Sort targets by name, so stubs and lazy pointers are consistent
- std::vector<const Atom *> targetsNeedingStubs;
- for (auto it : _targetToUses)
- targetsNeedingStubs.push_back(it.first);
- std::sort(targetsNeedingStubs.begin(), targetsNeedingStubs.end(),
- [](const Atom * left, const Atom * right) {
- return (left->name().compare(right->name()) < 0);
- });
-
- // Make and append stubs, lazy pointers, and helpers in alphabetical order.
- unsigned lazyOffset = 0;
- for (const Atom *target : targetsNeedingStubs) {
- auto *stub = new (_file.allocator()) StubAtom(_file, _stubInfo);
- auto *lp =
- new (_file.allocator()) LazyPointerAtom(_file, _ctx.is64Bit());
- auto *helper = new (_file.allocator()) StubHelperAtom(_file, _stubInfo);
-
- addReference(stub, _stubInfo.stubReferenceToLP, lp);
- addOptReference(stub, _stubInfo.stubReferenceToLP,
- _stubInfo.optStubReferenceToLP, lp);
- addReference(lp, _stubInfo.lazyPointerReferenceToHelper, helper);
- addReference(lp, _stubInfo.lazyPointerReferenceToFinal, target);
- addReference(helper, _stubInfo.stubHelperReferenceToImm, helper);
- addReferenceAddend(helper, _stubInfo.stubHelperReferenceToImm, helper,
- lazyOffset);
- addReference(helper, _stubInfo.stubHelperReferenceToHelperCommon,
- helperCommonAtom);
-
- mergedFile.addAtom(*stub);
- mergedFile.addAtom(*lp);
- mergedFile.addAtom(*helper);
-
- // Update each reference to use stub.
- for (const Reference *ref : _targetToUses[target]) {
- assert(ref->target() == target);
- // Switch call site to reference stub atom instead.
- const_cast<Reference *>(ref)->setTarget(stub);
- }
-
- // Calculate new offset
- lazyOffset += target->name().size() + 12;
- }
-
- return llvm::Error::success();
- }
-
-private:
- bool noTextRelocs() {
- return true;
- }
-
- bool isCallSite(const Reference &ref) {
- return _archHandler.isCallSite(ref);
- }
-
- void addReference(SimpleDefinedAtom* atom,
- const ArchHandler::ReferenceInfo &refInfo,
- const lld::Atom* target) {
- atom->addReference(Reference::KindNamespace::mach_o,
- refInfo.arch, refInfo.kind, refInfo.offset,
- target, refInfo.addend);
- }
-
- void addReferenceAddend(SimpleDefinedAtom *atom,
- const ArchHandler::ReferenceInfo &refInfo,
- const lld::Atom *target, uint64_t addend) {
- atom->addReference(Reference::KindNamespace::mach_o, refInfo.arch,
- refInfo.kind, refInfo.offset, target, addend);
- }
-
- void addOptReference(SimpleDefinedAtom* atom,
- const ArchHandler::ReferenceInfo &refInfo,
- const ArchHandler::OptionalRefInfo &optRef,
- const lld::Atom* target) {
- if (!optRef.used)
- return;
- atom->addReference(Reference::KindNamespace::mach_o,
- refInfo.arch, optRef.kind, optRef.offset,
- target, optRef.addend);
- }
-
- typedef llvm::DenseMap<const Atom*,
- llvm::SmallVector<const Reference *, 8>> TargetToUses;
-
- const MachOLinkingContext &_ctx;
- mach_o::ArchHandler &_archHandler;
- const ArchHandler::StubInfo &_stubInfo;
- MachOFile &_file;
- TargetToUses _targetToUses;
-};
-
-void addStubsPass(PassManager &pm, const MachOLinkingContext &ctx) {
- pm.add(std::unique_ptr<Pass>(new StubsPass(ctx)));
-}
-
-} // end namespace mach_o
-} // end namespace lld
+++ /dev/null
-//===- lib/ReaderWriter/MachO/TLVPass.cpp -----------------------*- C++ -*-===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// This linker pass transforms all TLV references to real references.
-///
-//===----------------------------------------------------------------------===//
-
-#include "ArchHandler.h"
-#include "File.h"
-#include "MachOPasses.h"
-#include "lld/Core/Simple.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/Support/Debug.h"
-
-namespace lld {
-namespace mach_o {
-
-//
-// TLVP Entry Atom created by the TLV pass.
-//
-class TLVPEntryAtom : public SimpleDefinedAtom {
-public:
- TLVPEntryAtom(const File &file, bool is64, StringRef name)
- : SimpleDefinedAtom(file), _is64(is64), _name(name) {}
-
- ~TLVPEntryAtom() override = default;
-
- ContentType contentType() const override {
- return DefinedAtom::typeTLVInitializerPtr;
- }
-
- Alignment alignment() const override {
- return _is64 ? 8 : 4;
- }
-
- uint64_t size() const override {
- return _is64 ? 8 : 4;
- }
-
- ContentPermissions permissions() const override {
- return DefinedAtom::permRW_;
- }
-
- ArrayRef<uint8_t> rawContent() const override {
- static const uint8_t zeros[] =
- { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
- return llvm::makeArrayRef(zeros, size());
- }
-
- StringRef slotName() const {
- return _name;
- }
-
-private:
- const bool _is64;
- StringRef _name;
-};
-
-class TLVPass : public Pass {
-public:
- TLVPass(const MachOLinkingContext &context)
- : _ctx(context), _archHandler(_ctx.archHandler()),
- _file(*_ctx.make_file<MachOFile>("<mach-o TLV pass>")) {
- _file.setOrdinal(_ctx.getNextOrdinalAndIncrement());
- }
-
-private:
- llvm::Error perform(SimpleFile &mergedFile) override {
- bool allowTLV = _ctx.minOS("10.7", "1.0");
-
- for (const DefinedAtom *atom : mergedFile.defined()) {
- for (const Reference *ref : *atom) {
- if (!_archHandler.isTLVAccess(*ref))
- continue;
-
- if (!allowTLV)
- return llvm::make_error<GenericError>(
- "targeted OS version does not support use of thread local "
- "variables in " + atom->name() + " for architecture " +
- _ctx.archName());
-
- const Atom *target = ref->target();
- assert(target != nullptr);
-
- const DefinedAtom *tlvpEntry = makeTLVPEntry(target);
- const_cast<Reference*>(ref)->setTarget(tlvpEntry);
- _archHandler.updateReferenceToTLV(ref);
- }
- }
-
- std::vector<const TLVPEntryAtom*> entries;
- entries.reserve(_targetToTLVP.size());
- for (auto &it : _targetToTLVP)
- entries.push_back(it.second);
- std::sort(entries.begin(), entries.end(),
- [](const TLVPEntryAtom *lhs, const TLVPEntryAtom *rhs) {
- return (lhs->slotName().compare(rhs->slotName()) < 0);
- });
-
- for (const TLVPEntryAtom *slot : entries)
- mergedFile.addAtom(*slot);
-
- return llvm::Error::success();
- }
-
- const DefinedAtom *makeTLVPEntry(const Atom *target) {
- auto pos = _targetToTLVP.find(target);
-
- if (pos != _targetToTLVP.end())
- return pos->second;
-
- auto *tlvpEntry = new (_file.allocator())
- TLVPEntryAtom(_file, _ctx.is64Bit(), target->name());
- _targetToTLVP[target] = tlvpEntry;
- const ArchHandler::ReferenceInfo &nlInfo =
- _archHandler.stubInfo().nonLazyPointerReferenceToBinder;
- tlvpEntry->addReference(Reference::KindNamespace::mach_o, nlInfo.arch,
- nlInfo.kind, 0, target, 0);
- return tlvpEntry;
- }
-
- const MachOLinkingContext &_ctx;
- mach_o::ArchHandler &_archHandler;
- MachOFile &_file;
- llvm::DenseMap<const Atom*, const TLVPEntryAtom*> _targetToTLVP;
-};
-
-void addTLVPass(PassManager &pm, const MachOLinkingContext &ctx) {
- assert(ctx.needsTLVPass());
- pm.add(std::make_unique<TLVPass>(ctx));
-}
-
-} // end namespace mach_o
-} // end namespace lld
+++ /dev/null
-//===- lib/ReaderWriter/MachO/WriterMachO.cpp -----------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "ExecutableAtoms.h"
-#include "MachONormalizedFile.h"
-#include "lld/Core/File.h"
-#include "lld/Core/Writer.h"
-#include "lld/ReaderWriter/MachOLinkingContext.h"
-#include "llvm/BinaryFormat/MachO.h"
-#include "llvm/Support/Debug.h"
-#include "llvm/Support/ErrorHandling.h"
-#include "llvm/Support/FileOutputBuffer.h"
-#include "llvm/Support/raw_ostream.h"
-#include <system_error>
-
-using lld::mach_o::normalized::NormalizedFile;
-
-namespace lld {
-namespace mach_o {
-
-class MachOWriter : public Writer {
-public:
- MachOWriter(const MachOLinkingContext &ctxt) : _ctx(ctxt) {}
-
- llvm::Error writeFile(const lld::File &file, StringRef path) override {
- // Construct empty normalized file from atoms.
- llvm::Expected<std::unique_ptr<NormalizedFile>> nFile =
- normalized::normalizedFromAtoms(file, _ctx);
- if (auto ec = nFile.takeError())
- return ec;
-
- // For testing, write out yaml form of normalized file.
- if (_ctx.printAtoms()) {
- std::unique_ptr<Writer> yamlWriter = createWriterYAML(_ctx);
- if (auto ec = yamlWriter->writeFile(file, "-"))
- return ec;
- }
-
- // Write normalized file as mach-o binary.
- return writeBinary(*nFile->get(), path);
- }
-
- void createImplicitFiles(std::vector<std::unique_ptr<File>> &r) override {
- // When building main executables, add _main as required entry point.
- if (_ctx.outputTypeHasEntry())
- r.emplace_back(new CEntryFile(_ctx));
- // If this can link with dylibs, need helper function (dyld_stub_binder).
- if (_ctx.needsStubsPass())
- r.emplace_back(new StubHelperFile(_ctx));
- // Final linked images can access a symbol for their mach_header.
- if (_ctx.outputMachOType() != llvm::MachO::MH_OBJECT)
- r.emplace_back(new MachHeaderAliasFile(_ctx));
- }
-private:
- const MachOLinkingContext &_ctx;
- };
-
-
-} // namespace mach_o
-
-std::unique_ptr<Writer> createWriterMachO(const MachOLinkingContext &context) {
- return std::unique_ptr<Writer>(new lld::mach_o::MachOWriter(context));
-}
-
-} // namespace lld
+++ /dev/null
-add_lld_library(lldYAML
- ReaderWriterYAML.cpp
-
- LINK_COMPONENTS
- Support
-
- LINK_LIBS
- lldCore
- )
+++ /dev/null
-//===- lib/ReaderWriter/YAML/ReaderWriterYAML.cpp -------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "lld/Core/AbsoluteAtom.h"
-#include "lld/Core/ArchiveLibraryFile.h"
-#include "lld/Core/Atom.h"
-#include "lld/Core/DefinedAtom.h"
-#include "lld/Core/Error.h"
-#include "lld/Core/File.h"
-#include "lld/Core/LinkingContext.h"
-#include "lld/Core/Reader.h"
-#include "lld/Core/Reference.h"
-#include "lld/Core/SharedLibraryAtom.h"
-#include "lld/Core/Simple.h"
-#include "lld/Core/UndefinedAtom.h"
-#include "lld/Core/Writer.h"
-#include "lld/ReaderWriter/YamlContext.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/StringMap.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/ADT/Twine.h"
-#include "llvm/BinaryFormat/Magic.h"
-#include "llvm/Support/Allocator.h"
-#include "llvm/Support/Debug.h"
-#include "llvm/Support/Error.h"
-#include "llvm/Support/ErrorOr.h"
-#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/Format.h"
-#include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/YAMLTraits.h"
-#include "llvm/Support/raw_ostream.h"
-#include <cassert>
-#include <cstdint>
-#include <cstring>
-#include <memory>
-#include <string>
-#include <system_error>
-#include <vector>
-
-using llvm::file_magic;
-using llvm::yaml::MappingTraits;
-using llvm::yaml::ScalarEnumerationTraits;
-using llvm::yaml::ScalarTraits;
-using llvm::yaml::IO;
-using llvm::yaml::SequenceTraits;
-using llvm::yaml::DocumentListTraits;
-
-using namespace lld;
-
-/// The conversion of Atoms to and from YAML uses LLVM's YAML I/O. This
-/// file just defines template specializations on the lld types which control
-/// how the mapping is done to and from YAML.
-
-namespace {
-
-/// Used when writing yaml files.
-/// In most cases, atoms names are unambiguous, so references can just
-/// use the atom name as the target (e.g. target: foo). But in a few
-/// cases that does not work, so ref-names are added. These are labels
-/// used only in yaml. The labels do not exist in the Atom model.
-///
-/// One need for ref-names are when atoms have no user supplied name
-/// (e.g. c-string literal). Another case is when two object files with
-/// identically named static functions are merged (ld -r) into one object file.
-/// In that case referencing the function by name is ambiguous, so a unique
-/// ref-name is added.
-class RefNameBuilder {
-public:
- RefNameBuilder(const lld::File &file)
- : _collisionCount(0), _unnamedCounter(0) {
- // visit all atoms
- for (const lld::DefinedAtom *atom : file.defined()) {
- // Build map of atoms names to detect duplicates
- if (!atom->name().empty())
- buildDuplicateNameMap(*atom);
-
- // Find references to unnamed atoms and create ref-names for them.
- for (const lld::Reference *ref : *atom) {
- // create refname for any unnamed reference target
- const lld::Atom *target = ref->target();
- if ((target != nullptr) && target->name().empty()) {
- std::string storage;
- llvm::raw_string_ostream buffer(storage);
- buffer << llvm::format("L%03d", _unnamedCounter++);
- StringRef newName = copyString(buffer.str());
- _refNames[target] = std::string(newName);
- DEBUG_WITH_TYPE("WriterYAML",
- llvm::dbgs() << "unnamed atom: creating ref-name: '"
- << newName << "' ("
- << (const void *)newName.data() << ", "
- << newName.size() << ")\n");
- }
- }
- }
- for (const lld::UndefinedAtom *undefAtom : file.undefined()) {
- buildDuplicateNameMap(*undefAtom);
- }
- for (const lld::SharedLibraryAtom *shlibAtom : file.sharedLibrary()) {
- buildDuplicateNameMap(*shlibAtom);
- }
- for (const lld::AbsoluteAtom *absAtom : file.absolute()) {
- if (!absAtom->name().empty())
- buildDuplicateNameMap(*absAtom);
- }
- }
-
- void buildDuplicateNameMap(const lld::Atom &atom) {
- assert(!atom.name().empty());
- NameToAtom::iterator pos = _nameMap.find(atom.name());
- if (pos != _nameMap.end()) {
- // Found name collision, give each a unique ref-name.
- std::string Storage;
- llvm::raw_string_ostream buffer(Storage);
- buffer << atom.name() << llvm::format(".%03d", ++_collisionCount);
- StringRef newName = copyString(buffer.str());
- _refNames[&atom] = std::string(newName);
- DEBUG_WITH_TYPE("WriterYAML",
- llvm::dbgs() << "name collision: creating ref-name: '"
- << newName << "' ("
- << (const void *)newName.data()
- << ", " << newName.size() << ")\n");
- const lld::Atom *prevAtom = pos->second;
- AtomToRefName::iterator pos2 = _refNames.find(prevAtom);
- if (pos2 == _refNames.end()) {
- // Only create ref-name for previous if none already created.
- std::string Storage2;
- llvm::raw_string_ostream buffer2(Storage2);
- buffer2 << prevAtom->name() << llvm::format(".%03d", ++_collisionCount);
- StringRef newName2 = copyString(buffer2.str());
- _refNames[prevAtom] = std::string(newName2);
- DEBUG_WITH_TYPE("WriterYAML",
- llvm::dbgs() << "name collision: creating ref-name: '"
- << newName2 << "' ("
- << (const void *)newName2.data() << ", "
- << newName2.size() << ")\n");
- }
- } else {
- // First time we've seen this name, just add it to map.
- _nameMap[atom.name()] = &atom;
- DEBUG_WITH_TYPE("WriterYAML", llvm::dbgs()
- << "atom name seen for first time: '"
- << atom.name() << "' ("
- << (const void *)atom.name().data()
- << ", " << atom.name().size() << ")\n");
- }
- }
-
- bool hasRefName(const lld::Atom *atom) { return _refNames.count(atom); }
-
- StringRef refName(const lld::Atom *atom) {
- return _refNames.find(atom)->second;
- }
-
-private:
- typedef llvm::StringMap<const lld::Atom *> NameToAtom;
- typedef llvm::DenseMap<const lld::Atom *, std::string> AtomToRefName;
-
- // Allocate a new copy of this string in _storage, so the strings
- // can be freed when RefNameBuilder is destroyed.
- StringRef copyString(StringRef str) {
- char *s = _storage.Allocate<char>(str.size());
- memcpy(s, str.data(), str.size());
- return StringRef(s, str.size());
- }
-
- unsigned int _collisionCount;
- unsigned int _unnamedCounter;
- NameToAtom _nameMap;
- AtomToRefName _refNames;
- llvm::BumpPtrAllocator _storage;
-};
-
-/// Used when reading yaml files to find the target of a reference
-/// that could be a name or ref-name.
-class RefNameResolver {
-public:
- RefNameResolver(const lld::File *file, IO &io);
-
- const lld::Atom *lookup(StringRef name) const {
- NameToAtom::const_iterator pos = _nameMap.find(name);
- if (pos != _nameMap.end())
- return pos->second;
- _io.setError(Twine("no such atom name: ") + name);
- return nullptr;
- }
-
-private:
- typedef llvm::StringMap<const lld::Atom *> NameToAtom;
-
- void add(StringRef name, const lld::Atom *atom) {
- if (_nameMap.count(name)) {
- _io.setError(Twine("duplicate atom name: ") + name);
- } else {
- _nameMap[name] = atom;
- }
- }
-
- IO &_io;
- NameToAtom _nameMap;
-};
-
-/// Mapping of Atoms.
-template <typename T> class AtomList {
- using Ty = std::vector<OwningAtomPtr<T>>;
-
-public:
- typename Ty::iterator begin() { return _atoms.begin(); }
- typename Ty::iterator end() { return _atoms.end(); }
- Ty _atoms;
-};
-
-/// Mapping of kind: field in yaml files.
-enum FileKinds {
- fileKindObjectAtoms, // atom based object file encoded in yaml
- fileKindArchive, // static archive library encoded in yaml
- fileKindObjectMachO // mach-o object files encoded in yaml
-};
-
-struct ArchMember {
- FileKinds _kind;
- StringRef _name;
- const lld::File *_content;
-};
-
-// The content bytes in a DefinedAtom are just uint8_t but we want
-// special formatting, so define a strong type.
-LLVM_YAML_STRONG_TYPEDEF(uint8_t, ImplicitHex8)
-
-// SharedLibraryAtoms have a bool canBeNull() method which we'd like to be
-// more readable than just true/false.
-LLVM_YAML_STRONG_TYPEDEF(bool, ShlibCanBeNull)
-
-// lld::Reference::Kind is a tuple of <namespace, arch, value>.
-// For yaml, we just want one string that encapsulates the tuple.
-struct RefKind {
- Reference::KindNamespace ns;
- Reference::KindArch arch;
- Reference::KindValue value;
-};
-
-} // end anonymous namespace
-
-LLVM_YAML_IS_SEQUENCE_VECTOR(ArchMember)
-LLVM_YAML_IS_SEQUENCE_VECTOR(const lld::Reference *)
-// Always write DefinedAtoms content bytes as a flow sequence.
-LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(ImplicitHex8)
-
-// for compatibility with gcc-4.7 in C++11 mode, add extra namespace
-namespace llvm {
-namespace yaml {
-
-// This is a custom formatter for RefKind
-template <> struct ScalarTraits<RefKind> {
- static void output(const RefKind &kind, void *ctxt, raw_ostream &out) {
- assert(ctxt != nullptr);
- YamlContext *info = reinterpret_cast<YamlContext *>(ctxt);
- assert(info->_registry);
- StringRef str;
- if (info->_registry->referenceKindToString(kind.ns, kind.arch, kind.value,
- str))
- out << str;
- else
- out << (int)(kind.ns) << "-" << (int)(kind.arch) << "-" << kind.value;
- }
-
- static StringRef input(StringRef scalar, void *ctxt, RefKind &kind) {
- assert(ctxt != nullptr);
- YamlContext *info = reinterpret_cast<YamlContext *>(ctxt);
- assert(info->_registry);
- if (info->_registry->referenceKindFromString(scalar, kind.ns, kind.arch,
- kind.value))
- return StringRef();
- return StringRef("unknown reference kind");
- }
-
- static QuotingType mustQuote(StringRef) { return QuotingType::None; }
-};
-
-template <> struct ScalarEnumerationTraits<lld::File::Kind> {
- static void enumeration(IO &io, lld::File::Kind &value) {
- io.enumCase(value, "error-object", lld::File::kindErrorObject);
- io.enumCase(value, "object", lld::File::kindMachObject);
- io.enumCase(value, "shared-library", lld::File::kindSharedLibrary);
- io.enumCase(value, "static-library", lld::File::kindArchiveLibrary);
- }
-};
-
-template <> struct ScalarEnumerationTraits<lld::Atom::Scope> {
- static void enumeration(IO &io, lld::Atom::Scope &value) {
- io.enumCase(value, "global", lld::Atom::scopeGlobal);
- io.enumCase(value, "hidden", lld::Atom::scopeLinkageUnit);
- io.enumCase(value, "static", lld::Atom::scopeTranslationUnit);
- }
-};
-
-template <> struct ScalarEnumerationTraits<lld::DefinedAtom::SectionChoice> {
- static void enumeration(IO &io, lld::DefinedAtom::SectionChoice &value) {
- io.enumCase(value, "content", lld::DefinedAtom::sectionBasedOnContent);
- io.enumCase(value, "custom", lld::DefinedAtom::sectionCustomPreferred);
- io.enumCase(value, "custom-required",
- lld::DefinedAtom::sectionCustomRequired);
- }
-};
-
-template <> struct ScalarEnumerationTraits<lld::DefinedAtom::Interposable> {
- static void enumeration(IO &io, lld::DefinedAtom::Interposable &value) {
- io.enumCase(value, "no", DefinedAtom::interposeNo);
- io.enumCase(value, "yes", DefinedAtom::interposeYes);
- io.enumCase(value, "yes-and-weak", DefinedAtom::interposeYesAndRuntimeWeak);
- }
-};
-
-template <> struct ScalarEnumerationTraits<lld::DefinedAtom::Merge> {
- static void enumeration(IO &io, lld::DefinedAtom::Merge &value) {
- io.enumCase(value, "no", lld::DefinedAtom::mergeNo);
- io.enumCase(value, "as-tentative", lld::DefinedAtom::mergeAsTentative);
- io.enumCase(value, "as-weak", lld::DefinedAtom::mergeAsWeak);
- io.enumCase(value, "as-addressed-weak",
- lld::DefinedAtom::mergeAsWeakAndAddressUsed);
- io.enumCase(value, "by-content", lld::DefinedAtom::mergeByContent);
- io.enumCase(value, "same-name-and-size",
- lld::DefinedAtom::mergeSameNameAndSize);
- io.enumCase(value, "largest", lld::DefinedAtom::mergeByLargestSection);
- }
-};
-
-template <> struct ScalarEnumerationTraits<lld::DefinedAtom::DeadStripKind> {
- static void enumeration(IO &io, lld::DefinedAtom::DeadStripKind &value) {
- io.enumCase(value, "normal", lld::DefinedAtom::deadStripNormal);
- io.enumCase(value, "never", lld::DefinedAtom::deadStripNever);
- io.enumCase(value, "always", lld::DefinedAtom::deadStripAlways);
- }
-};
-
-template <> struct ScalarEnumerationTraits<lld::DefinedAtom::DynamicExport> {
- static void enumeration(IO &io, lld::DefinedAtom::DynamicExport &value) {
- io.enumCase(value, "normal", lld::DefinedAtom::dynamicExportNormal);
- io.enumCase(value, "always", lld::DefinedAtom::dynamicExportAlways);
- }
-};
-
-template <> struct ScalarEnumerationTraits<lld::DefinedAtom::CodeModel> {
- static void enumeration(IO &io, lld::DefinedAtom::CodeModel &value) {
- io.enumCase(value, "none", lld::DefinedAtom::codeNA);
- io.enumCase(value, "mips-pic", lld::DefinedAtom::codeMipsPIC);
- io.enumCase(value, "mips-micro", lld::DefinedAtom::codeMipsMicro);
- io.enumCase(value, "mips-micro-pic", lld::DefinedAtom::codeMipsMicroPIC);
- io.enumCase(value, "mips-16", lld::DefinedAtom::codeMips16);
- io.enumCase(value, "arm-thumb", lld::DefinedAtom::codeARMThumb);
- io.enumCase(value, "arm-a", lld::DefinedAtom::codeARM_a);
- io.enumCase(value, "arm-d", lld::DefinedAtom::codeARM_d);
- io.enumCase(value, "arm-t", lld::DefinedAtom::codeARM_t);
- }
-};
-
-template <>
-struct ScalarEnumerationTraits<lld::DefinedAtom::ContentPermissions> {
- static void enumeration(IO &io, lld::DefinedAtom::ContentPermissions &value) {
- io.enumCase(value, "---", lld::DefinedAtom::perm___);
- io.enumCase(value, "r--", lld::DefinedAtom::permR__);
- io.enumCase(value, "r-x", lld::DefinedAtom::permR_X);
- io.enumCase(value, "rw-", lld::DefinedAtom::permRW_);
- io.enumCase(value, "rwx", lld::DefinedAtom::permRWX);
- io.enumCase(value, "rw-l", lld::DefinedAtom::permRW_L);
- io.enumCase(value, "unknown", lld::DefinedAtom::permUnknown);
- }
-};
-
-template <> struct ScalarEnumerationTraits<lld::DefinedAtom::ContentType> {
- static void enumeration(IO &io, lld::DefinedAtom::ContentType &value) {
- io.enumCase(value, "unknown", DefinedAtom::typeUnknown);
- io.enumCase(value, "code", DefinedAtom::typeCode);
- io.enumCase(value, "stub", DefinedAtom::typeStub);
- io.enumCase(value, "constant", DefinedAtom::typeConstant);
- io.enumCase(value, "data", DefinedAtom::typeData);
- io.enumCase(value, "quick-data", DefinedAtom::typeDataFast);
- io.enumCase(value, "zero-fill", DefinedAtom::typeZeroFill);
- io.enumCase(value, "zero-fill-quick", DefinedAtom::typeZeroFillFast);
- io.enumCase(value, "const-data", DefinedAtom::typeConstData);
- io.enumCase(value, "got", DefinedAtom::typeGOT);
- io.enumCase(value, "resolver", DefinedAtom::typeResolver);
- io.enumCase(value, "branch-island", DefinedAtom::typeBranchIsland);
- io.enumCase(value, "branch-shim", DefinedAtom::typeBranchShim);
- io.enumCase(value, "stub-helper", DefinedAtom::typeStubHelper);
- io.enumCase(value, "c-string", DefinedAtom::typeCString);
- io.enumCase(value, "utf16-string", DefinedAtom::typeUTF16String);
- io.enumCase(value, "unwind-cfi", DefinedAtom::typeCFI);
- io.enumCase(value, "unwind-lsda", DefinedAtom::typeLSDA);
- io.enumCase(value, "const-4-byte", DefinedAtom::typeLiteral4);
- io.enumCase(value, "const-8-byte", DefinedAtom::typeLiteral8);
- io.enumCase(value, "const-16-byte", DefinedAtom::typeLiteral16);
- io.enumCase(value, "lazy-pointer", DefinedAtom::typeLazyPointer);
- io.enumCase(value, "lazy-dylib-pointer",
- DefinedAtom::typeLazyDylibPointer);
- io.enumCase(value, "cfstring", DefinedAtom::typeCFString);
- io.enumCase(value, "initializer-pointer",
- DefinedAtom::typeInitializerPtr);
- io.enumCase(value, "terminator-pointer",
- DefinedAtom::typeTerminatorPtr);
- io.enumCase(value, "c-string-pointer",DefinedAtom::typeCStringPtr);
- io.enumCase(value, "objc-class-pointer",
- DefinedAtom::typeObjCClassPtr);
- io.enumCase(value, "objc-category-list",
- DefinedAtom::typeObjC2CategoryList);
- io.enumCase(value, "objc-image-info",
- DefinedAtom::typeObjCImageInfo);
- io.enumCase(value, "objc-method-list",
- DefinedAtom::typeObjCMethodList);
- io.enumCase(value, "objc-class1", DefinedAtom::typeObjC1Class);
- io.enumCase(value, "dtraceDOF", DefinedAtom::typeDTraceDOF);
- io.enumCase(value, "interposing-tuples",
- DefinedAtom::typeInterposingTuples);
- io.enumCase(value, "lto-temp", DefinedAtom::typeTempLTO);
- io.enumCase(value, "compact-unwind", DefinedAtom::typeCompactUnwindInfo);
- io.enumCase(value, "unwind-info", DefinedAtom::typeProcessedUnwindInfo);
- io.enumCase(value, "tlv-thunk", DefinedAtom::typeThunkTLV);
- io.enumCase(value, "tlv-data", DefinedAtom::typeTLVInitialData);
- io.enumCase(value, "tlv-zero-fill", DefinedAtom::typeTLVInitialZeroFill);
- io.enumCase(value, "tlv-initializer-ptr",
- DefinedAtom::typeTLVInitializerPtr);
- io.enumCase(value, "mach_header", DefinedAtom::typeMachHeader);
- io.enumCase(value, "dso_handle", DefinedAtom::typeDSOHandle);
- io.enumCase(value, "sectcreate", DefinedAtom::typeSectCreate);
- }
-};
-
-template <> struct ScalarEnumerationTraits<lld::UndefinedAtom::CanBeNull> {
- static void enumeration(IO &io, lld::UndefinedAtom::CanBeNull &value) {
- io.enumCase(value, "never", lld::UndefinedAtom::canBeNullNever);
- io.enumCase(value, "at-runtime", lld::UndefinedAtom::canBeNullAtRuntime);
- io.enumCase(value, "at-buildtime",lld::UndefinedAtom::canBeNullAtBuildtime);
- }
-};
-
-template <> struct ScalarEnumerationTraits<ShlibCanBeNull> {
- static void enumeration(IO &io, ShlibCanBeNull &value) {
- io.enumCase(value, "never", false);
- io.enumCase(value, "at-runtime", true);
- }
-};
-
-template <>
-struct ScalarEnumerationTraits<lld::SharedLibraryAtom::Type> {
- static void enumeration(IO &io, lld::SharedLibraryAtom::Type &value) {
- io.enumCase(value, "code", lld::SharedLibraryAtom::Type::Code);
- io.enumCase(value, "data", lld::SharedLibraryAtom::Type::Data);
- io.enumCase(value, "unknown", lld::SharedLibraryAtom::Type::Unknown);
- }
-};
-
-/// This is a custom formatter for lld::DefinedAtom::Alignment. Values look
-/// like:
-/// 8 # 8-byte aligned
-/// 7 mod 16 # 16-byte aligned plus 7 bytes
-template <> struct ScalarTraits<lld::DefinedAtom::Alignment> {
- static void output(const lld::DefinedAtom::Alignment &value, void *ctxt,
- raw_ostream &out) {
- if (value.modulus == 0) {
- out << llvm::format("%d", value.value);
- } else {
- out << llvm::format("%d mod %d", value.modulus, value.value);
- }
- }
-
- static StringRef input(StringRef scalar, void *ctxt,
- lld::DefinedAtom::Alignment &value) {
- value.modulus = 0;
- size_t modStart = scalar.find("mod");
- if (modStart != StringRef::npos) {
- StringRef modStr = scalar.slice(0, modStart);
- modStr = modStr.rtrim();
- unsigned int modulus;
- if (modStr.getAsInteger(0, modulus)) {
- return "malformed alignment modulus";
- }
- value.modulus = modulus;
- scalar = scalar.drop_front(modStart + 3);
- scalar = scalar.ltrim();
- }
- unsigned int power;
- if (scalar.getAsInteger(0, power)) {
- return "malformed alignment power";
- }
- value.value = power;
- if (value.modulus >= power) {
- return "malformed alignment, modulus too large for power";
- }
- return StringRef(); // returning empty string means success
- }
-
- static QuotingType mustQuote(StringRef) { return QuotingType::None; }
-};
-
-template <> struct ScalarEnumerationTraits<FileKinds> {
- static void enumeration(IO &io, FileKinds &value) {
- io.enumCase(value, "object", fileKindObjectAtoms);
- io.enumCase(value, "archive", fileKindArchive);
- io.enumCase(value, "object-mach-o", fileKindObjectMachO);
- }
-};
-
-template <> struct MappingTraits<ArchMember> {
- static void mapping(IO &io, ArchMember &member) {
- io.mapOptional("kind", member._kind, fileKindObjectAtoms);
- io.mapOptional("name", member._name);
- io.mapRequired("content", member._content);
- }
-};
-
-// Declare that an AtomList is a yaml sequence.
-template <typename T> struct SequenceTraits<AtomList<T> > {
- static size_t size(IO &io, AtomList<T> &seq) { return seq._atoms.size(); }
- static T *&element(IO &io, AtomList<T> &seq, size_t index) {
- if (index >= seq._atoms.size())
- seq._atoms.resize(index + 1);
- return seq._atoms[index].get();
- }
-};
-
-// Declare that an AtomRange is a yaml sequence.
-template <typename T> struct SequenceTraits<File::AtomRange<T> > {
- static size_t size(IO &io, File::AtomRange<T> &seq) { return seq.size(); }
- static T *&element(IO &io, File::AtomRange<T> &seq, size_t index) {
- assert(io.outputting() && "AtomRange only used when outputting");
- assert(index < seq.size() && "Out of range access");
- return seq[index].get();
- }
-};
-
-// Used to allow DefinedAtom content bytes to be a flow sequence of
-// two-digit hex numbers without the leading 0x (e.g. FF, 04, 0A)
-template <> struct ScalarTraits<ImplicitHex8> {
- static void output(const ImplicitHex8 &val, void *, raw_ostream &out) {
- uint8_t num = val;
- out << llvm::format("%02X", num);
- }
-
- static StringRef input(StringRef str, void *, ImplicitHex8 &val) {
- unsigned long long n;
- if (getAsUnsignedInteger(str, 16, n))
- return "invalid two-digit-hex number";
- if (n > 0xFF)
- return "out of range two-digit-hex number";
- val = n;
- return StringRef(); // returning empty string means success
- }
-
- static QuotingType mustQuote(StringRef) { return QuotingType::None; }
-};
-
-// YAML conversion for std::vector<const lld::File*>
-template <> struct DocumentListTraits<std::vector<const lld::File *> > {
- static size_t size(IO &io, std::vector<const lld::File *> &seq) {
- return seq.size();
- }
- static const lld::File *&element(IO &io, std::vector<const lld::File *> &seq,
- size_t index) {
- if (index >= seq.size())
- seq.resize(index + 1);
- return seq[index];
- }
-};
-
-// YAML conversion for const lld::File*
-template <> struct MappingTraits<const lld::File *> {
- class NormArchiveFile : public lld::ArchiveLibraryFile {
- public:
- NormArchiveFile(IO &io) : ArchiveLibraryFile("") {}
-
- NormArchiveFile(IO &io, const lld::File *file)
- : ArchiveLibraryFile(file->path()), _path(file->path()) {
- // If we want to support writing archives, this constructor would
- // need to populate _members.
- }
-
- const lld::File *denormalize(IO &io) { return this; }
-
- const AtomRange<lld::DefinedAtom> defined() const override {
- return _noDefinedAtoms;
- }
-
- const AtomRange<lld::UndefinedAtom> undefined() const override {
- return _noUndefinedAtoms;
- }
-
- const AtomRange<lld::SharedLibraryAtom> sharedLibrary() const override {
- return _noSharedLibraryAtoms;
- }
-
- const AtomRange<lld::AbsoluteAtom> absolute() const override {
- return _noAbsoluteAtoms;
- }
-
- void clearAtoms() override {
- _noDefinedAtoms.clear();
- _noUndefinedAtoms.clear();
- _noSharedLibraryAtoms.clear();
- _noAbsoluteAtoms.clear();
- }
-
- File *find(StringRef name) override {
- for (const ArchMember &member : _members)
- for (const lld::DefinedAtom *atom : member._content->defined())
- if (name == atom->name())
- return const_cast<File *>(member._content);
- return nullptr;
- }
-
- std::error_code
- parseAllMembers(std::vector<std::unique_ptr<File>> &result) override {
- return std::error_code();
- }
-
- StringRef _path;
- std::vector<ArchMember> _members;
- };
-
- class NormalizedFile : public lld::File {
- public:
- NormalizedFile(IO &io)
- : File("", kindNormalizedObject), _io(io), _rnb(nullptr),
- _definedAtomsRef(_definedAtoms._atoms),
- _undefinedAtomsRef(_undefinedAtoms._atoms),
- _sharedLibraryAtomsRef(_sharedLibraryAtoms._atoms),
- _absoluteAtomsRef(_absoluteAtoms._atoms) {}
-
- NormalizedFile(IO &io, const lld::File *file)
- : File(file->path(), kindNormalizedObject), _io(io),
- _rnb(new RefNameBuilder(*file)), _path(file->path()),
- _definedAtomsRef(file->defined()),
- _undefinedAtomsRef(file->undefined()),
- _sharedLibraryAtomsRef(file->sharedLibrary()),
- _absoluteAtomsRef(file->absolute()) {
- }
-
- ~NormalizedFile() override {
- }
-
- const lld::File *denormalize(IO &io);
-
- const AtomRange<lld::DefinedAtom> defined() const override {
- return _definedAtomsRef;
- }
-
- const AtomRange<lld::UndefinedAtom> undefined() const override {
- return _undefinedAtomsRef;
- }
-
- const AtomRange<lld::SharedLibraryAtom> sharedLibrary() const override {
- return _sharedLibraryAtomsRef;
- }
-
- const AtomRange<lld::AbsoluteAtom> absolute() const override {
- return _absoluteAtomsRef;
- }
-
- void clearAtoms() override {
- _definedAtoms._atoms.clear();
- _undefinedAtoms._atoms.clear();
- _sharedLibraryAtoms._atoms.clear();
- _absoluteAtoms._atoms.clear();
- }
-
- // Allocate a new copy of this string in _storage, so the strings
- // can be freed when File is destroyed.
- StringRef copyString(StringRef str) {
- char *s = _storage.Allocate<char>(str.size());
- memcpy(s, str.data(), str.size());
- return StringRef(s, str.size());
- }
-
- IO &_io;
- std::unique_ptr<RefNameBuilder> _rnb;
- StringRef _path;
- AtomList<lld::DefinedAtom> _definedAtoms;
- AtomList<lld::UndefinedAtom> _undefinedAtoms;
- AtomList<lld::SharedLibraryAtom> _sharedLibraryAtoms;
- AtomList<lld::AbsoluteAtom> _absoluteAtoms;
- AtomRange<lld::DefinedAtom> _definedAtomsRef;
- AtomRange<lld::UndefinedAtom> _undefinedAtomsRef;
- AtomRange<lld::SharedLibraryAtom> _sharedLibraryAtomsRef;
- AtomRange<lld::AbsoluteAtom> _absoluteAtomsRef;
- llvm::BumpPtrAllocator _storage;
- };
-
- static void mapping(IO &io, const lld::File *&file) {
- YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
- assert(info != nullptr);
- // Let any register tag handler process this.
- if (info->_registry && info->_registry->handleTaggedDoc(io, file))
- return;
- // If no registered handler claims this tag and there is no tag,
- // grandfather in as "!native".
- if (io.mapTag("!native", true) || io.mapTag("tag:yaml.org,2002:map"))
- mappingAtoms(io, file);
- }
-
- static void mappingAtoms(IO &io, const lld::File *&file) {
- YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
- MappingNormalizationHeap<NormalizedFile, const lld::File *>
- keys(io, file, nullptr);
- assert(info != nullptr);
- info->_file = keys.operator->();
-
- io.mapOptional("path", keys->_path);
-
- if (io.outputting()) {
- io.mapOptional("defined-atoms", keys->_definedAtomsRef);
- io.mapOptional("undefined-atoms", keys->_undefinedAtomsRef);
- io.mapOptional("shared-library-atoms", keys->_sharedLibraryAtomsRef);
- io.mapOptional("absolute-atoms", keys->_absoluteAtomsRef);
- } else {
- io.mapOptional("defined-atoms", keys->_definedAtoms);
- io.mapOptional("undefined-atoms", keys->_undefinedAtoms);
- io.mapOptional("shared-library-atoms", keys->_sharedLibraryAtoms);
- io.mapOptional("absolute-atoms", keys->_absoluteAtoms);
- }
- }
-
- static void mappingArchive(IO &io, const lld::File *&file) {
- YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
- MappingNormalizationHeap<NormArchiveFile, const lld::File *>
- keys(io, file, &info->_file->allocator());
-
- io.mapOptional("path", keys->_path);
- io.mapOptional("members", keys->_members);
- }
-};
-
-// YAML conversion for const lld::Reference*
-template <> struct MappingTraits<const lld::Reference *> {
- class NormalizedReference : public lld::Reference {
- public:
- NormalizedReference(IO &io)
- : lld::Reference(lld::Reference::KindNamespace::all,
- lld::Reference::KindArch::all, 0),
- _target(nullptr), _offset(0), _addend(0), _tag(0) {}
-
- NormalizedReference(IO &io, const lld::Reference *ref)
- : lld::Reference(ref->kindNamespace(), ref->kindArch(),
- ref->kindValue()),
- _target(nullptr), _targetName(targetName(io, ref)),
- _offset(ref->offsetInAtom()), _addend(ref->addend()),
- _tag(ref->tag()) {
- _mappedKind.ns = ref->kindNamespace();
- _mappedKind.arch = ref->kindArch();
- _mappedKind.value = ref->kindValue();
- }
-
- const lld::Reference *denormalize(IO &io) {
- YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
- assert(info != nullptr);
- typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
- NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
- if (!_targetName.empty())
- _targetName = f->copyString(_targetName);
- DEBUG_WITH_TYPE("WriterYAML", llvm::dbgs()
- << "created Reference to name: '"
- << _targetName << "' ("
- << (const void *)_targetName.data()
- << ", " << _targetName.size() << ")\n");
- setKindNamespace(_mappedKind.ns);
- setKindArch(_mappedKind.arch);
- setKindValue(_mappedKind.value);
- return this;
- }
-
- void bind(const RefNameResolver &);
- static StringRef targetName(IO &io, const lld::Reference *ref);
-
- uint64_t offsetInAtom() const override { return _offset; }
- const lld::Atom *target() const override { return _target; }
- Addend addend() const override { return _addend; }
- void setAddend(Addend a) override { _addend = a; }
- void setTarget(const lld::Atom *a) override { _target = a; }
-
- const lld::Atom *_target;
- StringRef _targetName;
- uint32_t _offset;
- Addend _addend;
- RefKind _mappedKind;
- uint32_t _tag;
- };
-
- static void mapping(IO &io, const lld::Reference *&ref) {
- YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
- MappingNormalizationHeap<NormalizedReference, const lld::Reference *> keys(
- io, ref, &info->_file->allocator());
-
- io.mapRequired("kind", keys->_mappedKind);
- io.mapOptional("offset", keys->_offset);
- io.mapOptional("target", keys->_targetName);
- io.mapOptional("addend", keys->_addend, (lld::Reference::Addend)0);
- io.mapOptional("tag", keys->_tag, 0u);
- }
-};
-
-// YAML conversion for const lld::DefinedAtom*
-template <> struct MappingTraits<const lld::DefinedAtom *> {
-
- class NormalizedAtom : public lld::DefinedAtom {
- public:
- NormalizedAtom(IO &io)
- : _file(fileFromContext(io)), _contentType(), _alignment(1) {
- static uint32_t ordinalCounter = 1;
- _ordinal = ordinalCounter++;
- }
-
- NormalizedAtom(IO &io, const lld::DefinedAtom *atom)
- : _file(fileFromContext(io)), _name(atom->name()),
- _scope(atom->scope()), _interpose(atom->interposable()),
- _merge(atom->merge()), _contentType(atom->contentType()),
- _alignment(atom->alignment()), _sectionChoice(atom->sectionChoice()),
- _deadStrip(atom->deadStrip()), _dynamicExport(atom->dynamicExport()),
- _codeModel(atom->codeModel()),
- _permissions(atom->permissions()), _size(atom->size()),
- _sectionName(atom->customSectionName()),
- _sectionSize(atom->sectionSize()) {
- for (const lld::Reference *r : *atom)
- _references.push_back(r);
- if (!atom->occupiesDiskSpace())
- return;
- ArrayRef<uint8_t> cont = atom->rawContent();
- _content.reserve(cont.size());
- for (uint8_t x : cont)
- _content.push_back(x);
- }
-
- ~NormalizedAtom() override = default;
-
- const lld::DefinedAtom *denormalize(IO &io) {
- YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
- assert(info != nullptr);
- typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
- NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
- if (!_name.empty())
- _name = f->copyString(_name);
- if (!_refName.empty())
- _refName = f->copyString(_refName);
- if (!_sectionName.empty())
- _sectionName = f->copyString(_sectionName);
- DEBUG_WITH_TYPE("WriterYAML",
- llvm::dbgs() << "created DefinedAtom named: '" << _name
- << "' (" << (const void *)_name.data()
- << ", " << _name.size() << ")\n");
- return this;
- }
-
- void bind(const RefNameResolver &);
-
- // Extract current File object from YAML I/O parsing context
- const lld::File &fileFromContext(IO &io) {
- YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
- assert(info != nullptr);
- assert(info->_file != nullptr);
- return *info->_file;
- }
-
- const lld::File &file() const override { return _file; }
- StringRef name() const override { return _name; }
- uint64_t size() const override { return _size; }
- Scope scope() const override { return _scope; }
- Interposable interposable() const override { return _interpose; }
- Merge merge() const override { return _merge; }
- ContentType contentType() const override { return _contentType; }
- Alignment alignment() const override { return _alignment; }
- SectionChoice sectionChoice() const override { return _sectionChoice; }
- StringRef customSectionName() const override { return _sectionName; }
- uint64_t sectionSize() const override { return _sectionSize; }
- DeadStripKind deadStrip() const override { return _deadStrip; }
- DynamicExport dynamicExport() const override { return _dynamicExport; }
- CodeModel codeModel() const override { return _codeModel; }
- ContentPermissions permissions() const override { return _permissions; }
- ArrayRef<uint8_t> rawContent() const override {
- if (!occupiesDiskSpace())
- return ArrayRef<uint8_t>();
- return ArrayRef<uint8_t>(
- reinterpret_cast<const uint8_t *>(_content.data()), _content.size());
- }
-
- uint64_t ordinal() const override { return _ordinal; }
-
- reference_iterator begin() const override {
- uintptr_t index = 0;
- const void *it = reinterpret_cast<const void *>(index);
- return reference_iterator(*this, it);
- }
- reference_iterator end() const override {
- uintptr_t index = _references.size();
- const void *it = reinterpret_cast<const void *>(index);
- return reference_iterator(*this, it);
- }
- const lld::Reference *derefIterator(const void *it) const override {
- uintptr_t index = reinterpret_cast<uintptr_t>(it);
- assert(index < _references.size());
- return _references[index];
- }
- void incrementIterator(const void *&it) const override {
- uintptr_t index = reinterpret_cast<uintptr_t>(it);
- ++index;
- it = reinterpret_cast<const void *>(index);
- }
-
- void addReference(Reference::KindNamespace ns,
- Reference::KindArch arch,
- Reference::KindValue kindValue, uint64_t off,
- const Atom *target, Reference::Addend a) override {
- assert(target && "trying to create reference to nothing");
- auto node = new (file().allocator()) SimpleReference(ns, arch, kindValue,
- off, target, a);
- _references.push_back(node);
- }
-
- const lld::File &_file;
- StringRef _name;
- StringRef _refName;
- Scope _scope;
- Interposable _interpose;
- Merge _merge;
- ContentType _contentType;
- Alignment _alignment;
- SectionChoice _sectionChoice;
- DeadStripKind _deadStrip;
- DynamicExport _dynamicExport;
- CodeModel _codeModel;
- ContentPermissions _permissions;
- uint32_t _ordinal;
- std::vector<ImplicitHex8> _content;
- uint64_t _size;
- StringRef _sectionName;
- uint64_t _sectionSize;
- std::vector<const lld::Reference *> _references;
- };
-
- static void mapping(IO &io, const lld::DefinedAtom *&atom) {
- YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
- MappingNormalizationHeap<NormalizedAtom, const lld::DefinedAtom *> keys(
- io, atom, &info->_file->allocator());
- if (io.outputting()) {
- // If writing YAML, check if atom needs a ref-name.
- typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
- assert(info != nullptr);
- NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
- assert(f);
- assert(f->_rnb);
- if (f->_rnb->hasRefName(atom)) {
- keys->_refName = f->_rnb->refName(atom);
- }
- }
-
- io.mapOptional("name", keys->_name, StringRef());
- io.mapOptional("ref-name", keys->_refName, StringRef());
- io.mapOptional("scope", keys->_scope,
- DefinedAtom::scopeTranslationUnit);
- io.mapOptional("type", keys->_contentType,
- DefinedAtom::typeCode);
- io.mapOptional("content", keys->_content);
- io.mapOptional("size", keys->_size, (uint64_t)keys->_content.size());
- io.mapOptional("interposable", keys->_interpose,
- DefinedAtom::interposeNo);
- io.mapOptional("merge", keys->_merge, DefinedAtom::mergeNo);
- io.mapOptional("alignment", keys->_alignment,
- DefinedAtom::Alignment(1));
- io.mapOptional("section-choice", keys->_sectionChoice,
- DefinedAtom::sectionBasedOnContent);
- io.mapOptional("section-name", keys->_sectionName, StringRef());
- io.mapOptional("section-size", keys->_sectionSize, (uint64_t)0);
- io.mapOptional("dead-strip", keys->_deadStrip,
- DefinedAtom::deadStripNormal);
- io.mapOptional("dynamic-export", keys->_dynamicExport,
- DefinedAtom::dynamicExportNormal);
- io.mapOptional("code-model", keys->_codeModel, DefinedAtom::codeNA);
- // default permissions based on content type
- io.mapOptional("permissions", keys->_permissions,
- DefinedAtom::permissions(
- keys->_contentType));
- io.mapOptional("references", keys->_references);
- }
-};
-
-template <> struct MappingTraits<lld::DefinedAtom *> {
- static void mapping(IO &io, lld::DefinedAtom *&atom) {
- const lld::DefinedAtom *atomPtr = atom;
- MappingTraits<const lld::DefinedAtom *>::mapping(io, atomPtr);
- atom = const_cast<lld::DefinedAtom *>(atomPtr);
- }
-};
-
-// YAML conversion for const lld::UndefinedAtom*
-template <> struct MappingTraits<const lld::UndefinedAtom *> {
- class NormalizedAtom : public lld::UndefinedAtom {
- public:
- NormalizedAtom(IO &io)
- : _file(fileFromContext(io)), _canBeNull(canBeNullNever) {}
-
- NormalizedAtom(IO &io, const lld::UndefinedAtom *atom)
- : _file(fileFromContext(io)), _name(atom->name()),
- _canBeNull(atom->canBeNull()) {}
-
- ~NormalizedAtom() override = default;
-
- const lld::UndefinedAtom *denormalize(IO &io) {
- YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
- assert(info != nullptr);
- typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
- NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
- if (!_name.empty())
- _name = f->copyString(_name);
-
- DEBUG_WITH_TYPE("WriterYAML",
- llvm::dbgs() << "created UndefinedAtom named: '" << _name
- << "' (" << (const void *)_name.data() << ", "
- << _name.size() << ")\n");
- return this;
- }
-
- // Extract current File object from YAML I/O parsing context
- const lld::File &fileFromContext(IO &io) {
- YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
- assert(info != nullptr);
- assert(info->_file != nullptr);
- return *info->_file;
- }
-
- const lld::File &file() const override { return _file; }
- StringRef name() const override { return _name; }
- CanBeNull canBeNull() const override { return _canBeNull; }
-
- const lld::File &_file;
- StringRef _name;
- CanBeNull _canBeNull;
- };
-
- static void mapping(IO &io, const lld::UndefinedAtom *&atom) {
- YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
- MappingNormalizationHeap<NormalizedAtom, const lld::UndefinedAtom *> keys(
- io, atom, &info->_file->allocator());
-
- io.mapRequired("name", keys->_name);
- io.mapOptional("can-be-null", keys->_canBeNull,
- lld::UndefinedAtom::canBeNullNever);
- }
-};
-
-template <> struct MappingTraits<lld::UndefinedAtom *> {
- static void mapping(IO &io, lld::UndefinedAtom *&atom) {
- const lld::UndefinedAtom *atomPtr = atom;
- MappingTraits<const lld::UndefinedAtom *>::mapping(io, atomPtr);
- atom = const_cast<lld::UndefinedAtom *>(atomPtr);
- }
-};
-
-// YAML conversion for const lld::SharedLibraryAtom*
-template <> struct MappingTraits<const lld::SharedLibraryAtom *> {
- class NormalizedAtom : public lld::SharedLibraryAtom {
- public:
- NormalizedAtom(IO &io)
- : _file(fileFromContext(io)), _canBeNull(false),
- _type(Type::Unknown), _size(0) {}
-
- NormalizedAtom(IO &io, const lld::SharedLibraryAtom *atom)
- : _file(fileFromContext(io)), _name(atom->name()),
- _loadName(atom->loadName()), _canBeNull(atom->canBeNullAtRuntime()),
- _type(atom->type()), _size(atom->size()) {}
-
- ~NormalizedAtom() override = default;
-
- const lld::SharedLibraryAtom *denormalize(IO &io) {
- YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
- assert(info != nullptr);
- typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
- NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
- if (!_name.empty())
- _name = f->copyString(_name);
- if (!_loadName.empty())
- _loadName = f->copyString(_loadName);
-
- DEBUG_WITH_TYPE("WriterYAML",
- llvm::dbgs() << "created SharedLibraryAtom named: '"
- << _name << "' ("
- << (const void *)_name.data()
- << ", " << _name.size() << ")\n");
- return this;
- }
-
- // Extract current File object from YAML I/O parsing context
- const lld::File &fileFromContext(IO &io) {
- YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
- assert(info != nullptr);
- assert(info->_file != nullptr);
- return *info->_file;
- }
-
- const lld::File &file() const override { return _file; }
- StringRef name() const override { return _name; }
- StringRef loadName() const override { return _loadName; }
- bool canBeNullAtRuntime() const override { return _canBeNull; }
- Type type() const override { return _type; }
- uint64_t size() const override { return _size; }
-
- const lld::File &_file;
- StringRef _name;
- StringRef _loadName;
- ShlibCanBeNull _canBeNull;
- Type _type;
- uint64_t _size;
- };
-
- static void mapping(IO &io, const lld::SharedLibraryAtom *&atom) {
-
- YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
- MappingNormalizationHeap<NormalizedAtom, const lld::SharedLibraryAtom *>
- keys(io, atom, &info->_file->allocator());
-
- io.mapRequired("name", keys->_name);
- io.mapOptional("load-name", keys->_loadName);
- io.mapOptional("can-be-null", keys->_canBeNull, (ShlibCanBeNull) false);
- io.mapOptional("type", keys->_type, SharedLibraryAtom::Type::Code);
- io.mapOptional("size", keys->_size, uint64_t(0));
- }
-};
-
-template <> struct MappingTraits<lld::SharedLibraryAtom *> {
- static void mapping(IO &io, lld::SharedLibraryAtom *&atom) {
- const lld::SharedLibraryAtom *atomPtr = atom;
- MappingTraits<const lld::SharedLibraryAtom *>::mapping(io, atomPtr);
- atom = const_cast<lld::SharedLibraryAtom *>(atomPtr);
- }
-};
-
-// YAML conversion for const lld::AbsoluteAtom*
-template <> struct MappingTraits<const lld::AbsoluteAtom *> {
- class NormalizedAtom : public lld::AbsoluteAtom {
- public:
- NormalizedAtom(IO &io)
- : _file(fileFromContext(io)), _scope(), _value(0) {}
-
- NormalizedAtom(IO &io, const lld::AbsoluteAtom *atom)
- : _file(fileFromContext(io)), _name(atom->name()),
- _scope(atom->scope()), _value(atom->value()) {}
-
- ~NormalizedAtom() override = default;
-
- const lld::AbsoluteAtom *denormalize(IO &io) {
- YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
- assert(info != nullptr);
- typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
- NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
- if (!_name.empty())
- _name = f->copyString(_name);
-
- DEBUG_WITH_TYPE("WriterYAML",
- llvm::dbgs() << "created AbsoluteAtom named: '" << _name
- << "' (" << (const void *)_name.data()
- << ", " << _name.size() << ")\n");
- return this;
- }
-
- // Extract current File object from YAML I/O parsing context
- const lld::File &fileFromContext(IO &io) {
- YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
- assert(info != nullptr);
- assert(info->_file != nullptr);
- return *info->_file;
- }
-
- const lld::File &file() const override { return _file; }
- StringRef name() const override { return _name; }
- uint64_t value() const override { return _value; }
- Scope scope() const override { return _scope; }
-
- const lld::File &_file;
- StringRef _name;
- StringRef _refName;
- Scope _scope;
- Hex64 _value;
- };
-
- static void mapping(IO &io, const lld::AbsoluteAtom *&atom) {
- YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
- MappingNormalizationHeap<NormalizedAtom, const lld::AbsoluteAtom *> keys(
- io, atom, &info->_file->allocator());
-
- if (io.outputting()) {
- typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
- YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
- assert(info != nullptr);
- NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
- assert(f);
- assert(f->_rnb);
- if (f->_rnb->hasRefName(atom)) {
- keys->_refName = f->_rnb->refName(atom);
- }
- }
-
- io.mapRequired("name", keys->_name);
- io.mapOptional("ref-name", keys->_refName, StringRef());
- io.mapOptional("scope", keys->_scope);
- io.mapRequired("value", keys->_value);
- }
-};
-
-template <> struct MappingTraits<lld::AbsoluteAtom *> {
- static void mapping(IO &io, lld::AbsoluteAtom *&atom) {
- const lld::AbsoluteAtom *atomPtr = atom;
- MappingTraits<const lld::AbsoluteAtom *>::mapping(io, atomPtr);
- atom = const_cast<lld::AbsoluteAtom *>(atomPtr);
- }
-};
-
-} // end namespace llvm
-} // end namespace yaml
-
-RefNameResolver::RefNameResolver(const lld::File *file, IO &io) : _io(io) {
- typedef MappingTraits<const lld::DefinedAtom *>::NormalizedAtom
- NormalizedAtom;
- for (const lld::DefinedAtom *a : file->defined()) {
- const auto *na = (const NormalizedAtom *)a;
- if (!na->_refName.empty())
- add(na->_refName, a);
- else if (!na->_name.empty())
- add(na->_name, a);
- }
-
- for (const lld::UndefinedAtom *a : file->undefined())
- add(a->name(), a);
-
- for (const lld::SharedLibraryAtom *a : file->sharedLibrary())
- add(a->name(), a);
-
- typedef MappingTraits<const lld::AbsoluteAtom *>::NormalizedAtom NormAbsAtom;
- for (const lld::AbsoluteAtom *a : file->absolute()) {
- const auto *na = (const NormAbsAtom *)a;
- if (na->_refName.empty())
- add(na->_name, a);
- else
- add(na->_refName, a);
- }
-}
-
-inline const lld::File *
-MappingTraits<const lld::File *>::NormalizedFile::denormalize(IO &io) {
- typedef MappingTraits<const lld::DefinedAtom *>::NormalizedAtom
- NormalizedAtom;
-
- RefNameResolver nameResolver(this, io);
- // Now that all atoms are parsed, references can be bound.
- for (const lld::DefinedAtom *a : this->defined()) {
- auto *normAtom = (NormalizedAtom *)const_cast<DefinedAtom *>(a);
- normAtom->bind(nameResolver);
- }
-
- return this;
-}
-
-inline void MappingTraits<const lld::DefinedAtom *>::NormalizedAtom::bind(
- const RefNameResolver &resolver) {
- typedef MappingTraits<const lld::Reference *>::NormalizedReference
- NormalizedReference;
- for (const lld::Reference *ref : _references) {
- auto *normRef = (NormalizedReference *)const_cast<Reference *>(ref);
- normRef->bind(resolver);
- }
-}
-
-inline void MappingTraits<const lld::Reference *>::NormalizedReference::bind(
- const RefNameResolver &resolver) {
- _target = resolver.lookup(_targetName);
-}
-
-inline StringRef
-MappingTraits<const lld::Reference *>::NormalizedReference::targetName(
- IO &io, const lld::Reference *ref) {
- if (ref->target() == nullptr)
- return StringRef();
- YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
- assert(info != nullptr);
- typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
- NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
- RefNameBuilder &rnb = *f->_rnb;
- if (rnb.hasRefName(ref->target()))
- return rnb.refName(ref->target());
- return ref->target()->name();
-}
-
-namespace lld {
-namespace yaml {
-
-class Writer : public lld::Writer {
-public:
- Writer(const LinkingContext &context) : _ctx(context) {}
-
- llvm::Error writeFile(const lld::File &file, StringRef outPath) override {
- // Create stream to path.
- std::error_code ec;
- llvm::raw_fd_ostream out(outPath, ec, llvm::sys::fs::OF_TextWithCRLF);
- if (ec)
- return llvm::errorCodeToError(ec);
-
- // Create yaml Output writer, using yaml options for context.
- YamlContext yamlContext;
- yamlContext._ctx = &_ctx;
- yamlContext._registry = &_ctx.registry();
- llvm::yaml::Output yout(out, &yamlContext);
-
- // Write yaml output.
- const lld::File *fileRef = &file;
- yout << fileRef;
-
- return llvm::Error::success();
- }
-
-private:
- const LinkingContext &_ctx;
-};
-
-} // end namespace yaml
-
-namespace {
-
-/// Handles !native tagged yaml documents.
-class NativeYamlIOTaggedDocumentHandler : public YamlIOTaggedDocumentHandler {
- bool handledDocTag(llvm::yaml::IO &io, const lld::File *&file) const override {
- if (io.mapTag("!native")) {
- MappingTraits<const lld::File *>::mappingAtoms(io, file);
- return true;
- }
- return false;
- }
-};
-
-/// Handles !archive tagged yaml documents.
-class ArchiveYamlIOTaggedDocumentHandler : public YamlIOTaggedDocumentHandler {
- bool handledDocTag(llvm::yaml::IO &io, const lld::File *&file) const override {
- if (io.mapTag("!archive")) {
- MappingTraits<const lld::File *>::mappingArchive(io, file);
- return true;
- }
- return false;
- }
-};
-
-class YAMLReader : public Reader {
-public:
- YAMLReader(const Registry ®istry) : _registry(registry) {}
-
- bool canParse(file_magic magic, MemoryBufferRef mb) const override {
- StringRef name = mb.getBufferIdentifier();
- return name.endswith(".objtxt") || name.endswith(".yaml");
- }
-
- ErrorOr<std::unique_ptr<File>>
- loadFile(std::unique_ptr<MemoryBuffer> mb,
- const class Registry &) const override {
- // Create YAML Input Reader.
- YamlContext yamlContext;
- yamlContext._registry = &_registry;
- yamlContext._path = mb->getBufferIdentifier();
- llvm::yaml::Input yin(mb->getBuffer(), &yamlContext);
-
- // Fill vector with File objects created by parsing yaml.
- std::vector<const lld::File *> createdFiles;
- yin >> createdFiles;
- assert(createdFiles.size() == 1);
-
- // Error out now if there were parsing errors.
- if (yin.error())
- return make_error_code(lld::YamlReaderError::illegal_value);
-
- std::shared_ptr<MemoryBuffer> smb(mb.release());
- const File *file = createdFiles[0];
- // Note: loadFile() should return vector of *const* File
- File *f = const_cast<File *>(file);
- f->setLastError(std::error_code());
- f->setSharedMemoryBuffer(smb);
- return std::unique_ptr<File>(f);
- }
-
-private:
- const Registry &_registry;
-};
-
-} // end anonymous namespace
-
-void Registry::addSupportYamlFiles() {
- add(std::unique_ptr<Reader>(new YAMLReader(*this)));
- add(std::unique_ptr<YamlIOTaggedDocumentHandler>(
- new NativeYamlIOTaggedDocumentHandler()));
- add(std::unique_ptr<YamlIOTaggedDocumentHandler>(
- new ArchiveYamlIOTaggedDocumentHandler()));
-}
-
-std::unique_ptr<Writer> createWriterYAML(const LinkingContext &context) {
- return std::unique_ptr<Writer>(new lld::yaml::Writer(context));
-}
-
-} // end namespace lld
lldCommon
lldCOFF
lldELF
- lldMachO
lldMinGW
lldWasm
)
+if (LLD_ENABLE_MACHO)
+ target_link_libraries(lld
+ lldMachO
+ )
+endif()
+
install(TARGETS lld
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
//
//===----------------------------------------------------------------------===//
+#include "lld/Common/CommonLinkerContext.h"
#include "lld/Common/Driver.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/PluginLoader.h"
+#include "llvm/Support/Process.h"
#include <cstdlib>
+#include <optional>
using namespace lld;
using namespace llvm;
enum Flavor {
Invalid,
- Gnu, // -flavor gnu
- WinLink, // -flavor link
- Darwin, // -flavor darwin
- DarwinOld, // -flavor darwinold
- Wasm, // -flavor wasm
+ Gnu, // -flavor gnu
+ WinLink, // -flavor link
+ Darwin, // -flavor darwin
+ Wasm, // -flavor wasm
};
[[noreturn]] static void die(const Twine &s) {
.CasesLower("ld", "ld.lld", "gnu", Gnu)
.CasesLower("wasm", "ld-wasm", Wasm)
.CaseLower("link", WinLink)
- .CasesLower("ld64", "ld64.lld", "darwin", "darwinnew",
- "ld64.lld.darwinnew", Darwin)
- .CasesLower("darwinold", "ld64.lld.darwinold", DarwinOld)
+ .CasesLower("ld64", "ld64.lld", "darwin", Darwin)
.Default(Invalid);
}
-#ifndef __OpenBSD__
+#ifdef LLD_ENABLE_MINGW
static cl::TokenizerCallback getDefaultQuotingStyle() {
if (Triple(sys::getProcessTriple()).getOS() == Triple::Win32)
return cl::TokenizeWindowsCommandLine;
// Expand response files (arguments in the form of @<filename>)
// to allow detecting the -m argument from arguments in them.
SmallVector<const char *, 256> expandedArgs(v.data(), v.data() + v.size());
- cl::ExpandResponseFiles(saver, getDefaultQuotingStyle(), expandedArgs);
+ BumpPtrAllocator a;
+ StringSaver saver(a);
+ cl::ExpansionContext ECtx(saver.getAllocator(), getDefaultQuotingStyle());
+ if (Error Err = ECtx.expandResponseFiles(expandedArgs))
+ die(toString(std::move(Err)));
for (auto it = expandedArgs.begin(); it + 1 != expandedArgs.end(); ++it) {
if (StringRef(*it) != "-m")
continue;
return parseProgname(arg0);
}
+bool inTestOutputDisabled = false;
+
/// Universal linker main(). This linker emulates the gnu, darwin, or
/// windows linker based on the argv[0] or -flavor option.
static int lldMain(int argc, const char **argv, llvm::raw_ostream &stdoutOS,
llvm::raw_ostream &stderrOS, bool exitEarly = true) {
std::vector<const char *> args(argv, argv + argc);
- switch (parseFlavor(args)) {
- case Gnu:
-#ifndef __OpenBSD__
- if (isPETarget(args))
- return !mingw::link(args, exitEarly, stdoutOS, stderrOS);
+ auto link = [&args]() {
+ Flavor f = parseFlavor(args);
+#ifdef LLD_ENABLE_MINGW
+ if (f == Gnu && isPETarget(args))
+ return mingw::link;
+ else if (f == Gnu)
+#else
+ if (f == Gnu)
#endif
- return !elf::link(args, exitEarly, stdoutOS, stderrOS);
-#ifndef __OpenBSD__
- case WinLink:
- return !coff::link(args, exitEarly, stdoutOS, stderrOS);
- case Darwin:
- return !macho::link(args, exitEarly, stdoutOS, stderrOS);
- case DarwinOld:
- return !mach_o::link(args, exitEarly, stdoutOS, stderrOS);
- case Wasm:
- return !lld::wasm::link(args, exitEarly, stdoutOS, stderrOS);
+ return elf::link;
+#ifdef LLD_ENABLE_COFF
+ else if (f == WinLink)
+ return coff::link;
#endif
- default:
- die("lld is a generic driver.\n"
- "Invoke ld.lld (Unix), ld64.lld (macOS), lld-link (Windows), wasm-ld"
- " (WebAssembly) instead");
- }
+#ifdef LLD_ENABLE_MACHO
+ else if (f == Darwin)
+ return macho::link;
+#endif
+#ifdef LLD_ENABLE_WASM
+ else if (f == Wasm)
+ return lld::wasm::link;
+#endif
+ else
+ die("lld is a generic driver.\n"
+ "Invoke ld.lld (Unix), ld64.lld (macOS), lld-link (Windows), wasm-ld"
+ " (WebAssembly) instead");
+ }();
+ // Run the driver. If an error occurs, false will be returned.
+ bool r = link(args, stdoutOS, stderrOS, exitEarly, inTestOutputDisabled);
+
+ // Call exit() if we can to avoid calling destructors.
+ if (exitEarly)
+ exitLld(!r ? 1 : 0);
+
+ // Delete the global context and clear the global context pointer, so that it
+ // cannot be accessed anymore.
+ CommonLinkerContext::destroy();
+
+ return !r ? 1 : 0;
}
// Similar to lldMain except that exceptions are caught.
// Cleanup memory and reset everything back in pristine condition. This path
// is only taken when LLD is in test, or when it is used as a library.
llvm::CrashRecoveryContext crc;
- if (!crc.RunSafely([&]() { errorHandler().reset(); })) {
+ if (!crc.RunSafely([&]() { CommonLinkerContext::destroy(); })) {
// The memory is corrupted beyond any possible recovery.
return {r, /*canRunAgain=*/false};
}
return v;
}
-int main(int argc, const char **argv) {
+int lld_main(int argc, char **argv) {
InitLLVM x(argc, argv);
+ sys::Process::UseANSIEscapeCodes(true);
+
+ if (::getenv("FORCE_LLD_DIAGNOSTICS_CRASH")) {
+ llvm::errs()
+ << "crashing due to environment variable FORCE_LLD_DIAGNOSTICS_CRASH\n";
+ LLVM_BUILTIN_TRAP;
+ }
// Not running in lit tests, just take the shortest codepath with global
// exception handling and no memory cleanup on exit.
if (!inTestVerbosity())
- return lldMain(argc, argv, llvm::outs(), llvm::errs());
+ return lldMain(argc, const_cast<const char **>(argv), llvm::outs(),
+ llvm::errs());
- Optional<int> mainRet;
+ std::optional<int> mainRet;
CrashRecoveryContext::Enable();
for (unsigned i = inTestVerbosity(); i > 0; --i) {
// Disable stdout/stderr for all iterations but the last one.
- if (i != 1)
- errorHandler().disableOutput = true;
+ inTestOutputDisabled = (i != 1);
// Execute one iteration.
- auto r = safeLldMain(argc, argv, llvm::outs(), llvm::errs());
+ auto r = safeLldMain(argc, const_cast<const char **>(argv), llvm::outs(),
+ llvm::errs());
if (!r.canRunAgain)
exitLld(r.ret); // Exit now, can't re-execute again.
+++ /dev/null
-add_custom_target(LLDUnitTests)
-set_target_properties(LLDUnitTests PROPERTIES FOLDER "lld tests")
-
-set(CMAKE_BUILD_WITH_INSTALL_RPATH OFF)
-
-# add_lld_unittest(test_dirname file1.cpp file2.cpp)
-#
-# Will compile the list of files together and link against lld
-# Produces a binary named 'basename(test_dirname)'.
-function(add_lld_unittest test_dirname)
- add_unittest(LLDUnitTests ${test_dirname} ${ARGN})
- target_link_libraries(${test_dirname} ${LLVM_COMMON_LIBS})
-endfunction()
-
-add_subdirectory(DriverTests)
-add_subdirectory(MachOTests)
+++ /dev/null
-add_lld_unittest(DriverTests
- DarwinLdDriverTest.cpp
- )
-
-target_link_libraries(DriverTests
- PRIVATE
- lldDriver
- lldMachO
- )
+++ /dev/null
-//===- lld/unittest/DarwinLdDriverTest.cpp --------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// Darwin's ld driver tests.
-///
-//===----------------------------------------------------------------------===//
-
-#include "lld/Common/Driver.h"
-#include "lld/ReaderWriter/MachOLinkingContext.h"
-#include "llvm/BinaryFormat/MachO.h"
-#include "llvm/Support/raw_ostream.h"
-#include "gtest/gtest.h"
-
-using namespace llvm;
-using namespace lld;
-
-namespace lld {
-namespace mach_o {
-bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx);
-}
-}
-
-namespace {
-class DarwinLdParserTest : public testing::Test {
-protected:
- int inputFileCount() { return _ctx.getNodes().size(); }
-
- StringRef inputFile(int index) {
- Node &node = *_ctx.getNodes()[index];
- if (node.kind() == Node::Kind::File)
- return cast<FileNode>(&node)->getFile()->path();
- llvm_unreachable("not handling other types of input files");
- }
-
- bool parse(std::vector<const char *> args) {
- args.insert(args.begin(), "ld");
- return mach_o::parse(args, _ctx);
- }
-
- MachOLinkingContext _ctx;
-};
-}
-
-TEST_F(DarwinLdParserTest, Basic) {
- EXPECT_TRUE(parse({"foo.o", "bar.o", "-arch", "i386"}));
- EXPECT_FALSE(_ctx.allowRemainingUndefines());
- EXPECT_FALSE(_ctx.deadStrip());
- EXPECT_EQ(2, inputFileCount());
- EXPECT_EQ("foo.o", inputFile(0));
- EXPECT_EQ("bar.o", inputFile(1));
-}
-
-TEST_F(DarwinLdParserTest, Output) {
- EXPECT_TRUE(parse({"-o", "my.out", "foo.o", "-arch", "i386"}));
- EXPECT_EQ("my.out", _ctx.outputPath());
-}
-
-TEST_F(DarwinLdParserTest, Dylib) {
- EXPECT_TRUE(parse({"-dylib", "foo.o", "-arch", "i386"}));
- EXPECT_EQ(llvm::MachO::MH_DYLIB, _ctx.outputMachOType());
-}
-
-TEST_F(DarwinLdParserTest, Relocatable) {
- EXPECT_TRUE(parse({"-r", "foo.o", "-arch", "i386"}));
- EXPECT_EQ(llvm::MachO::MH_OBJECT, _ctx.outputMachOType());
-}
-
-TEST_F(DarwinLdParserTest, Bundle) {
- EXPECT_TRUE(parse({"-bundle", "foo.o", "-arch", "i386"}));
- EXPECT_EQ(llvm::MachO::MH_BUNDLE, _ctx.outputMachOType());
-}
-
-TEST_F(DarwinLdParserTest, Preload) {
- EXPECT_TRUE(parse({"-preload", "foo.o", "-arch", "i386"}));
- EXPECT_EQ(llvm::MachO::MH_PRELOAD, _ctx.outputMachOType());
-}
-
-TEST_F(DarwinLdParserTest, Static) {
- EXPECT_TRUE(parse({"-static", "foo.o", "-arch", "i386"}));
- EXPECT_EQ(llvm::MachO::MH_EXECUTE, _ctx.outputMachOType());
-}
-
-TEST_F(DarwinLdParserTest, Entry) {
- EXPECT_TRUE(parse({"-e", "entryFunc", "foo.o", "-arch", "i386"}));
- EXPECT_EQ("entryFunc", _ctx.entrySymbolName());
-}
-
-TEST_F(DarwinLdParserTest, DeadStrip) {
- EXPECT_TRUE(parse({"-arch", "x86_64", "-dead_strip", "foo.o"}));
- EXPECT_TRUE(_ctx.deadStrip());
-}
-
-TEST_F(DarwinLdParserTest, DeadStripRootsExe) {
- EXPECT_TRUE(parse({"-arch", "x86_64", "-dead_strip", "foo.o"}));
- EXPECT_FALSE(_ctx.globalsAreDeadStripRoots());
-}
-
-TEST_F(DarwinLdParserTest, DeadStripRootsDylib) {
- EXPECT_TRUE(parse({"-arch", "x86_64", "-dylib", "-dead_strip", "foo.o"}));
- EXPECT_FALSE(_ctx.globalsAreDeadStripRoots());
-}
-
-TEST_F(DarwinLdParserTest, DeadStripRootsRelocatable) {
- EXPECT_TRUE(parse({"-arch", "x86_64", "-r", "-dead_strip", "foo.o"}));
- EXPECT_FALSE(_ctx.globalsAreDeadStripRoots());
-}
-
-TEST_F(DarwinLdParserTest, DeadStripRootsExportDynamicExe) {
- EXPECT_TRUE(
- parse({"-arch", "x86_64", "-dead_strip", "-export_dynamic", "foo.o"}));
- EXPECT_TRUE(_ctx.globalsAreDeadStripRoots());
-}
-
-TEST_F(DarwinLdParserTest, DeadStripRootsExportDynamicDylib) {
- EXPECT_TRUE(parse({"-arch", "x86_64", "-dylib", "-dead_strip",
- "-export_dynamic", "foo.o"}));
- EXPECT_TRUE(_ctx.globalsAreDeadStripRoots());
-}
-
-TEST_F(DarwinLdParserTest, DeadStripRootsExportDynamicRelocatable) {
- EXPECT_TRUE(parse(
- {"-arch", "x86_64", "-r", "-dead_strip", "-export_dynamic", "foo.o"}));
- EXPECT_FALSE(_ctx.globalsAreDeadStripRoots());
-}
-
-TEST_F(DarwinLdParserTest, Arch) {
- EXPECT_TRUE(parse({"-arch", "x86_64", "foo.o"}));
- EXPECT_EQ(MachOLinkingContext::arch_x86_64, _ctx.arch());
- EXPECT_EQ((uint32_t)llvm::MachO::CPU_TYPE_X86_64, _ctx.getCPUType());
- EXPECT_EQ(llvm::MachO::CPU_SUBTYPE_X86_64_ALL, _ctx.getCPUSubType());
-}
-
-TEST_F(DarwinLdParserTest, Arch_x86) {
- EXPECT_TRUE(parse({"-arch", "i386", "foo.o"}));
- EXPECT_EQ(MachOLinkingContext::arch_x86, _ctx.arch());
- EXPECT_EQ((uint32_t)llvm::MachO::CPU_TYPE_I386, _ctx.getCPUType());
- EXPECT_EQ(llvm::MachO::CPU_SUBTYPE_X86_ALL, _ctx.getCPUSubType());
-}
-
-TEST_F(DarwinLdParserTest, Arch_armv6) {
- EXPECT_TRUE(parse({"-arch", "armv6", "foo.o"}));
- EXPECT_EQ(MachOLinkingContext::arch_armv6, _ctx.arch());
- EXPECT_EQ((uint32_t)llvm::MachO::CPU_TYPE_ARM, _ctx.getCPUType());
- EXPECT_EQ(llvm::MachO::CPU_SUBTYPE_ARM_V6, _ctx.getCPUSubType());
-}
-
-TEST_F(DarwinLdParserTest, Arch_armv7) {
- EXPECT_TRUE(parse({"-arch", "armv7", "foo.o"}));
- EXPECT_EQ(MachOLinkingContext::arch_armv7, _ctx.arch());
- EXPECT_EQ((uint32_t)llvm::MachO::CPU_TYPE_ARM, _ctx.getCPUType());
- EXPECT_EQ(llvm::MachO::CPU_SUBTYPE_ARM_V7, _ctx.getCPUSubType());
-}
-
-TEST_F(DarwinLdParserTest, Arch_armv7s) {
- EXPECT_TRUE(parse({"-arch", "armv7s", "foo.o"}));
- EXPECT_EQ(MachOLinkingContext::arch_armv7s, _ctx.arch());
- EXPECT_EQ((uint32_t)llvm::MachO::CPU_TYPE_ARM, _ctx.getCPUType());
- EXPECT_EQ(llvm::MachO::CPU_SUBTYPE_ARM_V7S, _ctx.getCPUSubType());
-}
-
-TEST_F(DarwinLdParserTest, MinMacOSX10_7) {
- EXPECT_TRUE(
- parse({"-macosx_version_min", "10.7", "foo.o", "-arch", "x86_64"}));
- EXPECT_EQ(MachOLinkingContext::OS::macOSX, _ctx.os());
- EXPECT_TRUE(_ctx.minOS("10.7", ""));
- EXPECT_FALSE(_ctx.minOS("10.8", ""));
-}
-
-TEST_F(DarwinLdParserTest, MinMacOSX10_8) {
- EXPECT_TRUE(
- parse({"-macosx_version_min", "10.8.3", "foo.o", "-arch", "x86_64"}));
- EXPECT_EQ(MachOLinkingContext::OS::macOSX, _ctx.os());
- EXPECT_TRUE(_ctx.minOS("10.7", ""));
- EXPECT_TRUE(_ctx.minOS("10.8", ""));
-}
-
-TEST_F(DarwinLdParserTest, iOS5) {
- EXPECT_TRUE(parse({"-ios_version_min", "5.0", "foo.o", "-arch", "armv7"}));
- EXPECT_EQ(MachOLinkingContext::OS::iOS, _ctx.os());
- EXPECT_TRUE(_ctx.minOS("", "5.0"));
- EXPECT_FALSE(_ctx.minOS("", "6.0"));
-}
-
-TEST_F(DarwinLdParserTest, iOS6) {
- EXPECT_TRUE(parse({"-ios_version_min", "6.0", "foo.o", "-arch", "armv7"}));
- EXPECT_EQ(MachOLinkingContext::OS::iOS, _ctx.os());
- EXPECT_TRUE(_ctx.minOS("", "5.0"));
- EXPECT_TRUE(_ctx.minOS("", "6.0"));
-}
-
-TEST_F(DarwinLdParserTest, iOS_Simulator5) {
- EXPECT_TRUE(
- parse({"-ios_simulator_version_min", "5.0", "a.o", "-arch", "i386"}));
- EXPECT_EQ(MachOLinkingContext::OS::iOS_simulator, _ctx.os());
- EXPECT_TRUE(_ctx.minOS("", "5.0"));
- EXPECT_FALSE(_ctx.minOS("", "6.0"));
-}
-
-TEST_F(DarwinLdParserTest, iOS_Simulator6) {
- EXPECT_TRUE(
- parse({"-ios_simulator_version_min", "6.0", "a.o", "-arch", "i386"}));
- EXPECT_EQ(MachOLinkingContext::OS::iOS_simulator, _ctx.os());
- EXPECT_TRUE(_ctx.minOS("", "5.0"));
- EXPECT_TRUE(_ctx.minOS("", "6.0"));
-}
-
-TEST_F(DarwinLdParserTest, compatibilityVersion) {
- EXPECT_TRUE(parse(
- {"-dylib", "-compatibility_version", "1.2.3", "a.o", "-arch", "i386"}));
- EXPECT_EQ(_ctx.compatibilityVersion(), 0x10203U);
-}
-
-TEST_F(DarwinLdParserTest, compatibilityVersionInvalidType) {
- EXPECT_FALSE(parse(
- {"-bundle", "-compatibility_version", "1.2.3", "a.o", "-arch", "i386"}));
-}
-
-TEST_F(DarwinLdParserTest, compatibilityVersionInvalidValue) {
- EXPECT_FALSE(parse(
- {"-bundle", "-compatibility_version", "1,2,3", "a.o", "-arch", "i386"}));
-}
-
-TEST_F(DarwinLdParserTest, currentVersion) {
- EXPECT_TRUE(
- parse({"-dylib", "-current_version", "1.2.3", "a.o", "-arch", "i386"}));
- EXPECT_EQ(_ctx.currentVersion(), 0x10203U);
-}
-
-TEST_F(DarwinLdParserTest, currentVersionInvalidType) {
- EXPECT_FALSE(
- parse({"-bundle", "-current_version", "1.2.3", "a.o", "-arch", "i386"}));
-}
-
-TEST_F(DarwinLdParserTest, currentVersionInvalidValue) {
- EXPECT_FALSE(
- parse({"-bundle", "-current_version", "1,2,3", "a.o", "-arch", "i386"}));
-}
-
-TEST_F(DarwinLdParserTest, bundleLoader) {
- EXPECT_TRUE(
- parse({"-bundle", "-bundle_loader", "/bin/ls", "a.o", "-arch", "i386"}));
- EXPECT_EQ(_ctx.bundleLoader(), "/bin/ls");
-}
-
-TEST_F(DarwinLdParserTest, bundleLoaderInvalidType) {
- EXPECT_FALSE(parse({"-bundle_loader", "/bin/ls", "a.o", "-arch", "i386"}));
-}
-
-TEST_F(DarwinLdParserTest, deadStrippableDylib) {
- EXPECT_TRUE(
- parse({"-dylib", "-mark_dead_strippable_dylib", "a.o", "-arch", "i386"}));
- EXPECT_EQ(true, _ctx.deadStrippableDylib());
-}
-
-TEST_F(DarwinLdParserTest, deadStrippableDylibInvalidType) {
- EXPECT_FALSE(parse({"-mark_dead_strippable_dylib", "a.o", "-arch", "i386"}));
-}
+++ /dev/null
-
-add_lld_unittest(lldMachOTests
- MachONormalizedFileBinaryReaderTests.cpp
- MachONormalizedFileBinaryWriterTests.cpp
- MachONormalizedFileToAtomsTests.cpp
- MachONormalizedFileYAMLTests.cpp
- )
-
-target_link_libraries(lldMachOTests
- PRIVATE
- lldDriver
- lldMachO
- lldYAML
- )
+++ /dev/null
-//===- lld/unittest/MachOTests/MachONormalizedFileBinaryReaderTests.cpp ---===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "../../lib/ReaderWriter/MachO/MachONormalizedFile.h"
-#include "lld/ReaderWriter/MachOLinkingContext.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/BinaryFormat/MachO.h"
-#include "llvm/Support/Error.h"
-#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/YAMLTraits.h"
-#include "gtest/gtest.h"
-#include <cstdint>
-#include <memory>
-
-using llvm::SmallString;
-using llvm::StringRef;
-using llvm::MemoryBuffer;
-using llvm::Twine;
-
-using namespace lld::mach_o::normalized;
-using namespace llvm::MachO;
-
-static std::unique_ptr<NormalizedFile>
-fromBinary(const uint8_t bytes[], unsigned length, StringRef archStr) {
- StringRef sr((const char*)bytes, length);
- std::unique_ptr<MemoryBuffer> mb(MemoryBuffer::getMemBuffer(sr, "", false));
- llvm::Expected<std::unique_ptr<NormalizedFile>> r =
- lld::mach_o::normalized::readBinary(
- mb, lld::MachOLinkingContext::archFromName(archStr));
- EXPECT_FALSE(!r);
- return std::move(*r);
-}
-
-// The Mach-O object reader uses functions such as read32 or read64
-// which don't allow unaligned access. Our in-memory object file
-// needs to be aligned to a larger boundary than uint8_t's.
-#if _MSC_VER
-#define FILEBYTES __declspec(align(64)) const uint8_t fileBytes[]
-#else
-#define FILEBYTES const uint8_t fileBytes[] __attribute__((aligned(64)))
-#endif
-
-TEST(BinaryReaderTest, empty_obj_x86_64) {
- FILEBYTES = {
- 0xcf, 0xfa, 0xed, 0xfe, 0x07, 0x00, 0x00, 0x01,
- 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00,
- 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x19, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x5f, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
- std::unique_ptr<NormalizedFile> f =
- fromBinary(fileBytes, sizeof(fileBytes), "x86_64");
- EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86_64);
- EXPECT_EQ((int)(f->fileType), MH_OBJECT);
- EXPECT_EQ((int)(f->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
- EXPECT_TRUE(f->localSymbols.empty());
- EXPECT_TRUE(f->globalSymbols.empty());
- EXPECT_TRUE(f->undefinedSymbols.empty());
-}
-
-TEST(BinaryReaderTest, empty_obj_x86) {
- FILEBYTES = {
- 0xce, 0xfa, 0xed, 0xfe, 0x07, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00,
- 0x00, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
- 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x74, 0x65,
- 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x54, 0x45,
- 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
- std::unique_ptr<NormalizedFile> f =
- fromBinary(fileBytes, sizeof(fileBytes), "i386");
- EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86);
- EXPECT_EQ((int)(f->fileType), MH_OBJECT);
- EXPECT_EQ((int)(f->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
- EXPECT_TRUE(f->localSymbols.empty());
- EXPECT_TRUE(f->globalSymbols.empty());
- EXPECT_TRUE(f->undefinedSymbols.empty());
-}
-
-TEST(BinaryReaderTest, empty_obj_ppc) {
- FILEBYTES = {
- 0xfe, 0xed, 0xfa, 0xce, 0x00, 0x00, 0x00, 0x12,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7c,
- 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
- 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x74, 0x65,
- 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x54, 0x45,
- 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
- std::unique_ptr<NormalizedFile> f =
- fromBinary(fileBytes, sizeof(fileBytes), "ppc");
- EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_ppc);
- EXPECT_EQ((int)(f->fileType), MH_OBJECT);
- EXPECT_EQ((int)(f->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
- EXPECT_TRUE(f->localSymbols.empty());
- EXPECT_TRUE(f->globalSymbols.empty());
- EXPECT_TRUE(f->undefinedSymbols.empty());
-}
-
-TEST(BinaryReaderTest, empty_obj_armv7) {
- FILEBYTES = {
- 0xce, 0xfa, 0xed, 0xfe, 0x0c, 0x00, 0x00, 0x00,
- 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00,
- 0x00, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
- 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x74, 0x65,
- 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x54, 0x45,
- 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
- std::unique_ptr<NormalizedFile> f =
- fromBinary(fileBytes, sizeof(fileBytes), "armv7");
- EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_armv7);
- EXPECT_EQ((int)(f->fileType), MH_OBJECT);
- EXPECT_EQ((int)(f->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
- EXPECT_TRUE(f->localSymbols.empty());
- EXPECT_TRUE(f->globalSymbols.empty());
- EXPECT_TRUE(f->undefinedSymbols.empty());
-}
-
-TEST(BinaryReaderTest, empty_obj_x86_64_arm7) {
- FILEBYTES = {
-#include "empty_obj_x86_armv7.txt"
- };
- std::unique_ptr<NormalizedFile> f =
- fromBinary(fileBytes, sizeof(fileBytes), "x86_64");
- EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86_64);
- EXPECT_EQ((int)(f->fileType), MH_OBJECT);
- EXPECT_EQ((int)(f->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
- EXPECT_TRUE(f->localSymbols.empty());
- EXPECT_TRUE(f->globalSymbols.empty());
- EXPECT_TRUE(f->undefinedSymbols.empty());
-
- std::unique_ptr<NormalizedFile> f2 =
- fromBinary(fileBytes, sizeof(fileBytes), "armv7");
- EXPECT_EQ(f2->arch, lld::MachOLinkingContext::arch_armv7);
- EXPECT_EQ((int)(f2->fileType), MH_OBJECT);
- EXPECT_EQ((int)(f2->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
- EXPECT_TRUE(f2->localSymbols.empty());
- EXPECT_TRUE(f2->globalSymbols.empty());
- EXPECT_TRUE(f2->undefinedSymbols.empty());
-}
-
-TEST(BinaryReaderTest, hello_obj_x86_64) {
- FILEBYTES = {
- 0xCF, 0xFA, 0xED, 0xFE, 0x07, 0x00, 0x00, 0x01,
- 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x50, 0x01, 0x00, 0x00,
- 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x19, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x70, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x5F, 0x5F, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x5F, 0x5F, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x2D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x70, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
- 0xA4, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x5F, 0x5F, 0x63, 0x73, 0x74, 0x72, 0x69, 0x6E,
- 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x5F, 0x5F, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x2D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x9D, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
- 0xB4, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0xE4, 0x01, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
- 0x0B, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x55, 0x48, 0x89, 0xE5, 0x48, 0x83, 0xEC, 0x10,
- 0x48, 0x8D, 0x3D, 0x00, 0x00, 0x00, 0x00, 0xC7,
- 0x45, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xB0, 0x00,
- 0xE8, 0x00, 0x00, 0x00, 0x00, 0xB9, 0x00, 0x00,
- 0x00, 0x00, 0x89, 0x45, 0xF8, 0x89, 0xC8, 0x48,
- 0x83, 0xC4, 0x10, 0x5D, 0xC3, 0x68, 0x65, 0x6C,
- 0x6C, 0x6F, 0x0A, 0x00, 0x19, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x2D, 0x0B, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x1D, 0x0F, 0x00, 0x00, 0x00,
- 0x0E, 0x02, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x0F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x6D, 0x61,
- 0x69, 0x6E, 0x00, 0x5F, 0x70, 0x72, 0x69, 0x6E,
- 0x74, 0x66, 0x00, 0x4C, 0x5F, 0x2E, 0x73, 0x74,
- 0x72, 0x00, 0x00, 0x00 };
- std::unique_ptr<NormalizedFile> f =
- fromBinary(fileBytes, sizeof(fileBytes), "x86_64");
-
- EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86_64);
- EXPECT_EQ((int)(f->fileType), MH_OBJECT);
- EXPECT_EQ((int)(f->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
- EXPECT_EQ(f->sections.size(), 2UL);
- const Section& text = f->sections[0];
- EXPECT_TRUE(text.segmentName.equals("__TEXT"));
- EXPECT_TRUE(text.sectionName.equals("__text"));
- EXPECT_EQ(text.type, S_REGULAR);
- EXPECT_EQ(text.attributes,SectionAttr(S_ATTR_PURE_INSTRUCTIONS
- | S_ATTR_SOME_INSTRUCTIONS));
- EXPECT_EQ((uint16_t)text.alignment, 16U);
- EXPECT_EQ(text.address, Hex64(0x0));
- EXPECT_EQ(text.content.size(), 45UL);
- EXPECT_EQ((int)(text.content[0]), 0x55);
- EXPECT_EQ((int)(text.content[1]), 0x48);
- EXPECT_TRUE(text.indirectSymbols.empty());
- EXPECT_EQ(text.relocations.size(), 2UL);
- const Relocation& call = text.relocations[0];
- EXPECT_EQ(call.offset, Hex32(0x19));
- EXPECT_EQ(call.type, X86_64_RELOC_BRANCH);
- EXPECT_EQ(call.length, 2);
- EXPECT_EQ(call.isExtern, true);
- EXPECT_EQ(call.symbol, 2U);
- const Relocation& str = text.relocations[1];
- EXPECT_EQ(str.offset, Hex32(0xB));
- EXPECT_EQ(str.type, X86_64_RELOC_SIGNED);
- EXPECT_EQ(str.length, 2);
- EXPECT_EQ(str.isExtern, true);
- EXPECT_EQ(str.symbol, 0U);
-
- const Section& cstring = f->sections[1];
- EXPECT_TRUE(cstring.segmentName.equals("__TEXT"));
- EXPECT_TRUE(cstring.sectionName.equals("__cstring"));
- EXPECT_EQ(cstring.type, S_CSTRING_LITERALS);
- EXPECT_EQ(cstring.attributes, SectionAttr(0));
- EXPECT_EQ((uint16_t)cstring.alignment, 1U);
- EXPECT_EQ(cstring.address, Hex64(0x02D));
- EXPECT_EQ(cstring.content.size(), 7UL);
- EXPECT_EQ((int)(cstring.content[0]), 0x68);
- EXPECT_EQ((int)(cstring.content[1]), 0x65);
- EXPECT_EQ((int)(cstring.content[2]), 0x6c);
- EXPECT_TRUE(cstring.indirectSymbols.empty());
- EXPECT_TRUE(cstring.relocations.empty());
-
- EXPECT_EQ(f->localSymbols.size(), 1UL);
- const Symbol& strLabel = f->localSymbols[0];
- EXPECT_EQ(strLabel.type, N_SECT);
- EXPECT_EQ(strLabel.sect, 2);
- EXPECT_EQ(strLabel.value, Hex64(0x2D));
- EXPECT_EQ(f->globalSymbols.size(), 1UL);
- const Symbol& mainLabel = f->globalSymbols[0];
- EXPECT_TRUE(mainLabel.name.equals("_main"));
- EXPECT_EQ(mainLabel.type, N_SECT);
- EXPECT_EQ(mainLabel.sect, 1);
- EXPECT_EQ(mainLabel.scope, SymbolScope(N_EXT));
- EXPECT_EQ(mainLabel.value, Hex64(0x0));
- EXPECT_EQ(f->undefinedSymbols.size(), 1UL);
- const Symbol& printfLabel = f->undefinedSymbols[0];
- EXPECT_TRUE(printfLabel.name.equals("_printf"));
- EXPECT_EQ(printfLabel.type, N_UNDF);
- EXPECT_EQ(printfLabel.scope, SymbolScope(N_EXT));
-}
-
-TEST(BinaryReaderTest, hello_obj_x86) {
- FILEBYTES = {
- 0xCE, 0xFA, 0xED, 0xFE, 0x07, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x28, 0x01, 0x00, 0x00,
- 0x00, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x37, 0x00, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00,
- 0x37, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
- 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x5F, 0x5F, 0x74, 0x65,
- 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x5F, 0x5F, 0x54, 0x45,
- 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x30, 0x00, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x7C, 0x01, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x80,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x5F, 0x5F, 0x63, 0x73, 0x74, 0x72, 0x69, 0x6E,
- 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x5F, 0x5F, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x30, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
- 0x74, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x18, 0x00, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0xAC, 0x01, 0x00, 0x00,
- 0x10, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
- 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x55, 0x89, 0xE5, 0x83,
- 0xEC, 0x18, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x58,
- 0x8D, 0x80, 0x25, 0x00, 0x00, 0x00, 0xC7, 0x45,
- 0xFC, 0x00, 0x00, 0x00, 0x00, 0x89, 0x04, 0x24,
- 0xE8, 0xDF, 0xFF, 0xFF, 0xFF, 0xB9, 0x00, 0x00,
- 0x00, 0x00, 0x89, 0x45, 0xF8, 0x89, 0xC8, 0x83,
- 0xC4, 0x18, 0x5D, 0xC3, 0x68, 0x65, 0x6C, 0x6C,
- 0x6F, 0x0A, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x0D, 0x0E, 0x00, 0x00, 0xA4,
- 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA1,
- 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x0F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x6D, 0x61,
- 0x69, 0x6E, 0x00, 0x5F, 0x70, 0x72, 0x69, 0x6E,
- 0x74, 0x66, 0x00, 0x00
- };
- std::unique_ptr<NormalizedFile> f =
- fromBinary(fileBytes, sizeof(fileBytes), "i386");
-
- EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86);
- EXPECT_EQ((int)(f->fileType), MH_OBJECT);
- EXPECT_EQ((int)(f->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
- EXPECT_EQ(f->sections.size(), 2UL);
- const Section& text = f->sections[0];
- EXPECT_TRUE(text.segmentName.equals("__TEXT"));
- EXPECT_TRUE(text.sectionName.equals("__text"));
- EXPECT_EQ(text.type, S_REGULAR);
- EXPECT_EQ(text.attributes,SectionAttr(S_ATTR_PURE_INSTRUCTIONS
- | S_ATTR_SOME_INSTRUCTIONS));
- EXPECT_EQ((uint16_t)text.alignment, 16U);
- EXPECT_EQ(text.address, Hex64(0x0));
- EXPECT_EQ(text.content.size(), 48UL);
- EXPECT_EQ((int)(text.content[0]), 0x55);
- EXPECT_EQ((int)(text.content[1]), 0x89);
- EXPECT_TRUE(text.indirectSymbols.empty());
- EXPECT_EQ(text.relocations.size(), 3UL);
- const Relocation& call = text.relocations[0];
- EXPECT_EQ(call.offset, Hex32(0x1D));
- EXPECT_EQ(call.scattered, false);
- EXPECT_EQ(call.type, GENERIC_RELOC_VANILLA);
- EXPECT_EQ(call.pcRel, true);
- EXPECT_EQ(call.length, 2);
- EXPECT_EQ(call.isExtern, true);
- EXPECT_EQ(call.symbol, 1U);
- const Relocation& sectDiff = text.relocations[1];
- EXPECT_EQ(sectDiff.offset, Hex32(0xE));
- EXPECT_EQ(sectDiff.scattered, true);
- EXPECT_EQ(sectDiff.type, GENERIC_RELOC_LOCAL_SECTDIFF);
- EXPECT_EQ(sectDiff.pcRel, false);
- EXPECT_EQ(sectDiff.length, 2);
- EXPECT_EQ(sectDiff.value, 0x30U);
- const Relocation& pair = text.relocations[2];
- EXPECT_EQ(pair.offset, Hex32(0x0));
- EXPECT_EQ(pair.scattered, true);
- EXPECT_EQ(pair.type, GENERIC_RELOC_PAIR);
- EXPECT_EQ(pair.pcRel, false);
- EXPECT_EQ(pair.length, 2);
- EXPECT_EQ(pair.value, 0x0BU);
-
- const Section& cstring = f->sections[1];
- EXPECT_TRUE(cstring.segmentName.equals("__TEXT"));
- EXPECT_TRUE(cstring.sectionName.equals("__cstring"));
- EXPECT_EQ(cstring.type, S_CSTRING_LITERALS);
- EXPECT_EQ(cstring.attributes, SectionAttr(0));
- EXPECT_EQ((uint16_t)cstring.alignment, 1U);
- EXPECT_EQ(cstring.address, Hex64(0x030));
- EXPECT_EQ(cstring.content.size(), 7UL);
- EXPECT_EQ((int)(cstring.content[0]), 0x68);
- EXPECT_EQ((int)(cstring.content[1]), 0x65);
- EXPECT_EQ((int)(cstring.content[2]), 0x6c);
- EXPECT_TRUE(cstring.indirectSymbols.empty());
- EXPECT_TRUE(cstring.relocations.empty());
-
- EXPECT_EQ(f->localSymbols.size(), 0UL);
- EXPECT_EQ(f->globalSymbols.size(), 1UL);
- const Symbol& mainLabel = f->globalSymbols[0];
- EXPECT_TRUE(mainLabel.name.equals("_main"));
- EXPECT_EQ(mainLabel.type, N_SECT);
- EXPECT_EQ(mainLabel.sect, 1);
- EXPECT_EQ(mainLabel.scope, SymbolScope(N_EXT));
- EXPECT_EQ(mainLabel.value, Hex64(0x0));
- EXPECT_EQ(f->undefinedSymbols.size(), 1UL);
- const Symbol& printfLabel = f->undefinedSymbols[0];
- EXPECT_TRUE(printfLabel.name.equals("_printf"));
- EXPECT_EQ(printfLabel.type, N_UNDF);
- EXPECT_EQ(printfLabel.scope, SymbolScope(N_EXT));
-}
-
-TEST(BinaryReaderTest, hello_obj_armv7) {
- FILEBYTES = {
- 0xCE, 0xFA, 0xED, 0xFE, 0x0C, 0x00, 0x00, 0x00,
- 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x28, 0x01, 0x00, 0x00,
- 0x00, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x31, 0x00, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00,
- 0x31, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
- 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x5F, 0x5F, 0x74, 0x65,
- 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x5F, 0x5F, 0x54, 0x45,
- 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x2A, 0x00, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00,
- 0x05, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x80,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x5F, 0x5F, 0x63, 0x73, 0x74, 0x72, 0x69, 0x6E,
- 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x5F, 0x5F, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x2A, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
- 0x6E, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x18, 0x00, 0x00, 0x00, 0xA0, 0x01, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0xB8, 0x01, 0x00, 0x00,
- 0x10, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
- 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x80, 0xB5, 0x6F, 0x46,
- 0x82, 0xB0, 0x40, 0xF2, 0x18, 0x00, 0xC0, 0xF2,
- 0x00, 0x00, 0x78, 0x44, 0x00, 0x21, 0xC0, 0xF2,
- 0x00, 0x01, 0x01, 0x91, 0xFF, 0xF7, 0xF2, 0xFF,
- 0x00, 0x21, 0xC0, 0xF2, 0x00, 0x01, 0x00, 0x90,
- 0x08, 0x46, 0x02, 0xB0, 0x80, 0xBD, 0x68, 0x65,
- 0x6C, 0x6C, 0x6F, 0x0A, 0x00, 0x00, 0x00, 0x00,
- 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x6D,
- 0x0A, 0x00, 0x00, 0xB9, 0x2A, 0x00, 0x00, 0x00,
- 0x18, 0x00, 0x00, 0xB1, 0x0E, 0x00, 0x00, 0x00,
- 0x06, 0x00, 0x00, 0xA9, 0x2A, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0xA1, 0x0E, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x0F, 0x01, 0x08, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x5F, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x5F,
- 0x70, 0x72, 0x69, 0x6E, 0x74, 0x66, 0x00, 0x00
- };
- std::unique_ptr<NormalizedFile> f =
- fromBinary(fileBytes, sizeof(fileBytes), "armv7");
-
- EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_armv7);
- EXPECT_EQ((int)(f->fileType), MH_OBJECT);
- EXPECT_EQ((int)(f->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
- EXPECT_EQ(f->sections.size(), 2UL);
- const Section& text = f->sections[0];
- EXPECT_TRUE(text.segmentName.equals("__TEXT"));
- EXPECT_TRUE(text.sectionName.equals("__text"));
- EXPECT_EQ(text.type, S_REGULAR);
- EXPECT_EQ(text.attributes,SectionAttr(S_ATTR_PURE_INSTRUCTIONS
- | S_ATTR_SOME_INSTRUCTIONS));
- EXPECT_EQ((uint16_t)text.alignment, 4U);
- EXPECT_EQ(text.address, Hex64(0x0));
- EXPECT_EQ(text.content.size(), 42UL);
- EXPECT_EQ((int)(text.content[0]), 0x80);
- EXPECT_EQ((int)(text.content[1]), 0xB5);
- EXPECT_TRUE(text.indirectSymbols.empty());
- EXPECT_EQ(text.relocations.size(), 5UL);
- const Relocation& call = text.relocations[0];
- EXPECT_EQ(call.offset, Hex32(0x18));
- EXPECT_EQ(call.scattered, false);
- EXPECT_EQ(call.type, ARM_THUMB_RELOC_BR22);
- EXPECT_EQ(call.length, 2);
- EXPECT_EQ(call.isExtern, true);
- EXPECT_EQ(call.symbol, 1U);
- const Relocation& movt = text.relocations[1];
- EXPECT_EQ(movt.offset, Hex32(0xA));
- EXPECT_EQ(movt.scattered, true);
- EXPECT_EQ(movt.type, ARM_RELOC_HALF_SECTDIFF);
- EXPECT_EQ(movt.length, 3);
- EXPECT_EQ(movt.value, Hex32(0x2A));
- const Relocation& movtPair = text.relocations[2];
- EXPECT_EQ(movtPair.offset, Hex32(0x18));
- EXPECT_EQ(movtPair.scattered, true);
- EXPECT_EQ(movtPair.type, ARM_RELOC_PAIR);
- EXPECT_EQ(movtPair.length, 3);
- EXPECT_EQ(movtPair.value, Hex32(0xE));
- const Relocation& movw = text.relocations[3];
- EXPECT_EQ(movw.offset, Hex32(0x6));
- EXPECT_EQ(movw.scattered, true);
- EXPECT_EQ(movw.type, ARM_RELOC_HALF_SECTDIFF);
- EXPECT_EQ(movw.length, 2);
- EXPECT_EQ(movw.value, Hex32(0x2A));
- const Relocation& movwPair = text.relocations[4];
- EXPECT_EQ(movwPair.offset, Hex32(0x0));
- EXPECT_EQ(movwPair.scattered, true);
- EXPECT_EQ(movwPair.type, ARM_RELOC_PAIR);
- EXPECT_EQ(movwPair.length, 2);
- EXPECT_EQ(movwPair.value, Hex32(0xE));
-
- const Section& cstring = f->sections[1];
- EXPECT_TRUE(cstring.segmentName.equals("__TEXT"));
- EXPECT_TRUE(cstring.sectionName.equals("__cstring"));
- EXPECT_EQ(cstring.type, S_CSTRING_LITERALS);
- EXPECT_EQ(cstring.attributes, SectionAttr(0));
- EXPECT_EQ((uint16_t)cstring.alignment, 1U);
- EXPECT_EQ(cstring.address, Hex64(0x02A));
- EXPECT_EQ(cstring.content.size(), 7UL);
- EXPECT_EQ((int)(cstring.content[0]), 0x68);
- EXPECT_EQ((int)(cstring.content[1]), 0x65);
- EXPECT_EQ((int)(cstring.content[2]), 0x6c);
- EXPECT_TRUE(cstring.indirectSymbols.empty());
- EXPECT_TRUE(cstring.relocations.empty());
-
- EXPECT_EQ(f->localSymbols.size(), 0UL);
- EXPECT_EQ(f->globalSymbols.size(), 1UL);
- const Symbol& mainLabel = f->globalSymbols[0];
- EXPECT_TRUE(mainLabel.name.equals("_main"));
- EXPECT_EQ(mainLabel.type, N_SECT);
- EXPECT_EQ(mainLabel.sect, 1);
- EXPECT_EQ(mainLabel.scope, SymbolScope(N_EXT));
- EXPECT_EQ(mainLabel.value, Hex64(0x0));
- EXPECT_EQ(f->undefinedSymbols.size(), 1UL);
- const Symbol& printfLabel = f->undefinedSymbols[0];
- EXPECT_TRUE(printfLabel.name.equals("_printf"));
- EXPECT_EQ(printfLabel.type, N_UNDF);
- EXPECT_EQ(printfLabel.scope, SymbolScope(N_EXT));
-}
-
-TEST(BinaryReaderTest, hello_obj_ppc) {
- FILEBYTES = {
- 0xFE, 0xED, 0xFA, 0xCE, 0x00, 0x00, 0x00, 0x12,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x28,
- 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x01, 0x44,
- 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x07,
- 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x02,
- 0x00, 0x00, 0x00, 0x00, 0x5F, 0x5F, 0x74, 0x65,
- 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x5F, 0x5F, 0x54, 0x45,
- 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x01, 0x44,
- 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x90,
- 0x00, 0x00, 0x00, 0x05, 0x80, 0x00, 0x04, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x5F, 0x5F, 0x63, 0x73, 0x74, 0x72, 0x69, 0x6E,
- 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x5F, 0x5F, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x07,
- 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x00, 0x02,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
- 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x01, 0xB8,
- 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0xD0,
- 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0B,
- 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x7C, 0x08, 0x02, 0xA6,
- 0xBF, 0xC1, 0xFF, 0xF8, 0x90, 0x01, 0x00, 0x08,
- 0x94, 0x21, 0xFF, 0xB0, 0x7C, 0x3E, 0x0B, 0x78,
- 0x42, 0x9F, 0x00, 0x05, 0x7F, 0xE8, 0x02, 0xA6,
- 0x3C, 0x5F, 0x00, 0x00, 0x38, 0x62, 0x00, 0x2C,
- 0x4B, 0xFF, 0xFF, 0xDD, 0x38, 0x00, 0x00, 0x00,
- 0x7C, 0x03, 0x03, 0x78, 0x80, 0x21, 0x00, 0x00,
- 0x80, 0x01, 0x00, 0x08, 0x7C, 0x08, 0x03, 0xA6,
- 0xBB, 0xC1, 0xFF, 0xF8, 0x4E, 0x80, 0x00, 0x20,
- 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x01, 0xD3,
- 0xAB, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x44,
- 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
- 0xAC, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x44,
- 0xA1, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x18,
- 0x00, 0x00, 0x00, 0x01, 0x0F, 0x01, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x5F, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x5F,
- 0x70, 0x72, 0x69, 0x6E, 0x74, 0x66, 0x00, 0x00
- };
- std::unique_ptr<NormalizedFile> f =
- fromBinary(fileBytes, sizeof(fileBytes), "ppc");
-
- EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_ppc);
- EXPECT_EQ((int)(f->fileType), MH_OBJECT);
- EXPECT_EQ((int)(f->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
- EXPECT_EQ(f->sections.size(), 2UL);
- const Section& text = f->sections[0];
- EXPECT_TRUE(text.segmentName.equals("__TEXT"));
- EXPECT_TRUE(text.sectionName.equals("__text"));
- EXPECT_EQ(text.type, S_REGULAR);
- EXPECT_EQ(text.attributes,SectionAttr(S_ATTR_PURE_INSTRUCTIONS
- | S_ATTR_SOME_INSTRUCTIONS));
- EXPECT_EQ((uint16_t)text.alignment, 4U);
- EXPECT_EQ(text.address, Hex64(0x0));
- EXPECT_EQ(text.content.size(), 68UL);
- EXPECT_EQ((int)(text.content[0]), 0x7C);
- EXPECT_EQ((int)(text.content[1]), 0x08);
- EXPECT_TRUE(text.indirectSymbols.empty());
- EXPECT_EQ(text.relocations.size(), 5UL);
- const Relocation& bl = text.relocations[0];
- EXPECT_EQ(bl.offset, Hex32(0x24));
- EXPECT_EQ(bl.type, PPC_RELOC_BR24);
- EXPECT_EQ(bl.length, 2);
- EXPECT_EQ(bl.isExtern, true);
- EXPECT_EQ(bl.symbol, 1U);
- const Relocation& lo = text.relocations[1];
- EXPECT_EQ(lo.offset, Hex32(0x20));
- EXPECT_EQ(lo.scattered, true);
- EXPECT_EQ(lo.type, PPC_RELOC_LO16_SECTDIFF);
- EXPECT_EQ(lo.length, 2);
- EXPECT_EQ(lo.value, Hex32(0x44));
- const Relocation& loPair = text.relocations[2];
- EXPECT_EQ(loPair.offset, Hex32(0x0));
- EXPECT_EQ(loPair.scattered, true);
- EXPECT_EQ(loPair.type, PPC_RELOC_PAIR);
- EXPECT_EQ(loPair.length, 2);
- EXPECT_EQ(loPair.value, Hex32(0x18));
- const Relocation& ha = text.relocations[3];
- EXPECT_EQ(ha.offset, Hex32(0x1C));
- EXPECT_EQ(ha.scattered, true);
- EXPECT_EQ(ha.type, PPC_RELOC_HA16_SECTDIFF);
- EXPECT_EQ(ha.length, 2);
- EXPECT_EQ(ha.value, Hex32(0x44));
- const Relocation& haPair = text.relocations[4];
- EXPECT_EQ(haPair.offset, Hex32(0x2c));
- EXPECT_EQ(haPair.scattered, true);
- EXPECT_EQ(haPair.type, PPC_RELOC_PAIR);
- EXPECT_EQ(haPair.length, 2);
- EXPECT_EQ(haPair.value, Hex32(0x18));
-
- const Section& cstring = f->sections[1];
- EXPECT_TRUE(cstring.segmentName.equals("__TEXT"));
- EXPECT_TRUE(cstring.sectionName.equals("__cstring"));
- EXPECT_EQ(cstring.type, S_CSTRING_LITERALS);
- EXPECT_EQ(cstring.attributes, SectionAttr(0));
- EXPECT_EQ((uint16_t)cstring.alignment, 4U);
- EXPECT_EQ(cstring.address, Hex64(0x044));
- EXPECT_EQ(cstring.content.size(), 7UL);
- EXPECT_EQ((int)(cstring.content[0]), 0x68);
- EXPECT_EQ((int)(cstring.content[1]), 0x65);
- EXPECT_EQ((int)(cstring.content[2]), 0x6c);
- EXPECT_TRUE(cstring.indirectSymbols.empty());
- EXPECT_TRUE(cstring.relocations.empty());
-
- EXPECT_EQ(f->localSymbols.size(), 0UL);
- EXPECT_EQ(f->globalSymbols.size(), 1UL);
- const Symbol& mainLabel = f->globalSymbols[0];
- EXPECT_TRUE(mainLabel.name.equals("_main"));
- EXPECT_EQ(mainLabel.type, N_SECT);
- EXPECT_EQ(mainLabel.sect, 1);
- EXPECT_EQ(mainLabel.scope, SymbolScope(N_EXT));
- EXPECT_EQ(mainLabel.value, Hex64(0x0));
- EXPECT_EQ(f->undefinedSymbols.size(), 1UL);
- const Symbol& printfLabel = f->undefinedSymbols[0];
- EXPECT_TRUE(printfLabel.name.equals("_printf"));
- EXPECT_EQ(printfLabel.type, N_UNDF);
- EXPECT_EQ(printfLabel.scope, SymbolScope(N_EXT));
-
- SmallString<128> tmpFl;
- std::error_code ec =
- llvm::sys::fs::createTemporaryFile(Twine("xx"), "o", tmpFl);
- EXPECT_FALSE(ec);
- llvm::Error ec2 = writeBinary(*f, tmpFl);
- EXPECT_FALSE(ec2);
- llvm::sys::fs::remove(tmpFl);
-}
+++ /dev/null
-//===- lld/unittest/MachOTests/MachONormalizedFileBinaryWriterTests.cpp ---===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "../../lib/ReaderWriter/MachO/MachONormalizedFile.h"
-#include "llvm/ADT/Twine.h"
-#include "llvm/BinaryFormat/MachO.h"
-#include "llvm/Support/FileSystem.h"
-#include "gtest/gtest.h"
-#include <cassert>
-#include <memory>
-#include <system_error>
-#include <vector>
-
-using llvm::StringRef;
-using llvm::MemoryBuffer;
-using llvm::SmallString;
-using llvm::Twine;
-using llvm::ErrorOr;
-using namespace llvm::MachO;
-using namespace lld::mach_o::normalized;
-
-// Parses binary mach-o file at specified path and returns
-// ownership of buffer to mb parameter and ownership of
-// Normalized file to nf parameter.
-static void fromBinary(StringRef path, std::unique_ptr<MemoryBuffer> &mb,
- std::unique_ptr<NormalizedFile> &nf, StringRef archStr) {
- ErrorOr<std::unique_ptr<MemoryBuffer>> mbOrErr = MemoryBuffer::getFile(path);
- std::error_code ec = mbOrErr.getError();
- EXPECT_FALSE(ec);
- mb = std::move(mbOrErr.get());
-
- llvm::Expected<std::unique_ptr<NormalizedFile>> r =
- lld::mach_o::normalized::readBinary(
- mb, lld::MachOLinkingContext::archFromName(archStr));
- EXPECT_FALSE(!r);
- nf.reset(r->release());
-}
-
-static Relocation
-makeReloc(unsigned addr, bool rel, bool ext, RelocationInfoType type,
- unsigned sym) {
- Relocation result;
- result.offset = addr;
- result.scattered = false;
- result.type = type;
- result.length = 2;
- result.pcRel = rel;
- result.isExtern = ext;
- result.value = 0;
- result.symbol = sym;
- return result;
-}
-
-static Relocation
-makeScatReloc(unsigned addr, RelocationInfoType type, unsigned value) {
- Relocation result;
- result.offset = addr;
- result.scattered = true;
- result.type = type;
- result.length = 2;
- result.pcRel = false;
- result.isExtern = true;
- result.value = value;
- result.symbol = 0;
- return result;
-}
-
-static Symbol
-makeUndefSymbol(StringRef name) {
- Symbol sym;
- sym.name = name;
- sym.type = N_UNDF;
- sym.scope = N_EXT;
- sym.sect = NO_SECT;
- sym.desc = 0;
- sym.value = 0;
- return sym;
-}
-
-
-static Symbol
-makeSymbol(StringRef name, unsigned addr) {
- Symbol sym;
- sym.name = name;
- sym.type = N_SECT;
- sym.scope = N_EXT;
- sym.sect = 1;
- sym.desc = 0;
- sym.value = addr;
- return sym;
-}
-
-static Symbol
-makeThumbSymbol(StringRef name, unsigned addr) {
- Symbol sym;
- sym.name = name;
- sym.type = N_SECT;
- sym.scope = N_EXT;
- sym.sect = 1;
- sym.desc = N_ARM_THUMB_DEF;
- sym.value = addr;
- return sym;
-}
-
-TEST(BinaryWriterTest, obj_relocs_x86_64) {
- SmallString<128> tmpFl;
- {
- NormalizedFile f;
- f.arch = lld::MachOLinkingContext::arch_x86_64;
- f.fileType = MH_OBJECT;
- f.flags = MH_SUBSECTIONS_VIA_SYMBOLS;
- f.os = lld::MachOLinkingContext::OS::macOSX;
- f.sections.resize(1);
- Section& text = f.sections.front();
- text.segmentName = "__TEXT";
- text.sectionName = "__text";
- text.type = S_REGULAR;
- text.attributes = SectionAttr(S_ATTR_PURE_INSTRUCTIONS
- | S_ATTR_SOME_INSTRUCTIONS);
- text.alignment = 16;
- text.address = 0;
- const uint8_t textBytes[] = {
- 0xe8, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x05,
- 0x00, 0x00, 0x00, 0x00, 0xff, 0x35, 0x00, 0x00,
- 0x00, 0x00, 0x8b, 0x05, 0x00, 0x00, 0x00, 0x00,
- 0xc6, 0x05, 0xff, 0xff, 0xff, 0xff, 0x12, 0xc7,
- 0x05, 0xfc, 0xff, 0xff, 0xff, 0x78, 0x56, 0x34,
- 0x12, 0x48, 0x8b, 0x3d, 0x00, 0x00, 0x00, 0x00 };
-
- text.content = llvm::makeArrayRef(textBytes, sizeof(textBytes));
- text.relocations.push_back(makeReloc(0x01, false, true, X86_64_RELOC_BRANCH, 1));
- text.relocations.push_back(makeReloc(0x08, false, true, X86_64_RELOC_GOT_LOAD, 1));
- text.relocations.push_back(makeReloc(0x0E, false, true, X86_64_RELOC_GOT, 1));
- text.relocations.push_back(makeReloc(0x14, false, true, X86_64_RELOC_SIGNED, 1));
- text.relocations.push_back(makeReloc(0x1A, false, true, X86_64_RELOC_SIGNED_1, 1));
- text.relocations.push_back(makeReloc(0x21, false, true, X86_64_RELOC_SIGNED_4, 1));
- text.relocations.push_back(makeReloc(0x2C, false, true, X86_64_RELOC_TLV, 2));
-
- f.undefinedSymbols.push_back(makeUndefSymbol("_bar"));
- f.undefinedSymbols.push_back(makeUndefSymbol("_tbar"));
-
- std::error_code ec =
- llvm::sys::fs::createTemporaryFile(Twine("xx"), "o", tmpFl);
- EXPECT_FALSE(ec);
- llvm::Error ec2 = writeBinary(f, tmpFl);
- EXPECT_FALSE(ec2);
- }
-
- std::unique_ptr<MemoryBuffer> bufferOwner;
- std::unique_ptr<NormalizedFile> f2;
- fromBinary(tmpFl, bufferOwner, f2, "x86_64");
-
- EXPECT_EQ(lld::MachOLinkingContext::arch_x86_64, f2->arch);
- EXPECT_EQ(MH_OBJECT, f2->fileType);
- EXPECT_EQ(FileFlags(MH_SUBSECTIONS_VIA_SYMBOLS), f2->flags);
-
- EXPECT_TRUE(f2->localSymbols.empty());
- EXPECT_TRUE(f2->globalSymbols.empty());
- EXPECT_EQ(2UL, f2->undefinedSymbols.size());
- const Symbol& barUndef = f2->undefinedSymbols[0];
- EXPECT_TRUE(barUndef.name.equals("_bar"));
- EXPECT_EQ(N_UNDF, barUndef.type);
- EXPECT_EQ(SymbolScope(N_EXT), barUndef.scope);
- const Symbol& tbarUndef = f2->undefinedSymbols[1];
- EXPECT_TRUE(tbarUndef.name.equals("_tbar"));
- EXPECT_EQ(N_UNDF, tbarUndef.type);
- EXPECT_EQ(SymbolScope(N_EXT), tbarUndef.scope);
-
- EXPECT_EQ(1UL, f2->sections.size());
- const Section& text = f2->sections[0];
- EXPECT_TRUE(text.segmentName.equals("__TEXT"));
- EXPECT_TRUE(text.sectionName.equals("__text"));
- EXPECT_EQ(S_REGULAR, text.type);
- EXPECT_EQ(text.attributes,SectionAttr(S_ATTR_PURE_INSTRUCTIONS
- | S_ATTR_SOME_INSTRUCTIONS));
- EXPECT_EQ((uint16_t)text.alignment, 16U);
- EXPECT_EQ(text.address, Hex64(0x0));
- EXPECT_EQ(48UL, text.content.size());
- const Relocation& call = text.relocations[0];
- EXPECT_EQ(call.offset, Hex32(0x1));
- EXPECT_EQ(call.type, X86_64_RELOC_BRANCH);
- EXPECT_EQ(call.length, 2);
- EXPECT_EQ(call.isExtern, true);
- EXPECT_EQ(call.symbol, 1U);
- const Relocation& gotLoad = text.relocations[1];
- EXPECT_EQ(gotLoad.offset, Hex32(0x8));
- EXPECT_EQ(gotLoad.type, X86_64_RELOC_GOT_LOAD);
- EXPECT_EQ(gotLoad.length, 2);
- EXPECT_EQ(gotLoad.isExtern, true);
- EXPECT_EQ(gotLoad.symbol, 1U);
- const Relocation& gotUse = text.relocations[2];
- EXPECT_EQ(gotUse.offset, Hex32(0xE));
- EXPECT_EQ(gotUse.type, X86_64_RELOC_GOT);
- EXPECT_EQ(gotUse.length, 2);
- EXPECT_EQ(gotUse.isExtern, true);
- EXPECT_EQ(gotUse.symbol, 1U);
- const Relocation& signed0 = text.relocations[3];
- EXPECT_EQ(signed0.offset, Hex32(0x14));
- EXPECT_EQ(signed0.type, X86_64_RELOC_SIGNED);
- EXPECT_EQ(signed0.length, 2);
- EXPECT_EQ(signed0.isExtern, true);
- EXPECT_EQ(signed0.symbol, 1U);
- const Relocation& signed1 = text.relocations[4];
- EXPECT_EQ(signed1.offset, Hex32(0x1A));
- EXPECT_EQ(signed1.type, X86_64_RELOC_SIGNED_1);
- EXPECT_EQ(signed1.length, 2);
- EXPECT_EQ(signed1.isExtern, true);
- EXPECT_EQ(signed1.symbol, 1U);
- const Relocation& signed4 = text.relocations[5];
- EXPECT_EQ(signed4.offset, Hex32(0x21));
- EXPECT_EQ(signed4.type, X86_64_RELOC_SIGNED_4);
- EXPECT_EQ(signed4.length, 2);
- EXPECT_EQ(signed4.isExtern, true);
- EXPECT_EQ(signed4.symbol, 1U);
-
- bufferOwner.reset(nullptr);
- std::error_code ec = llvm::sys::fs::remove(Twine(tmpFl));
- EXPECT_FALSE(ec);
-}
-
-
-
-TEST(BinaryWriterTest, obj_relocs_x86) {
- SmallString<128> tmpFl;
- {
- NormalizedFile f;
- f.arch = lld::MachOLinkingContext::arch_x86;
- f.fileType = MH_OBJECT;
- f.flags = MH_SUBSECTIONS_VIA_SYMBOLS;
- f.os = lld::MachOLinkingContext::OS::macOSX;
- f.sections.resize(1);
- Section& text = f.sections.front();
- text.segmentName = "__TEXT";
- text.sectionName = "__text";
- text.type = S_REGULAR;
- text.attributes = SectionAttr(S_ATTR_PURE_INSTRUCTIONS
- | S_ATTR_SOME_INSTRUCTIONS);
- text.alignment = 16;
- text.address = 0;
- const uint8_t textBytes[] = {
- 0xe8, 0xfb, 0xff, 0xff, 0xff, 0xa1, 0x00, 0x00,
- 0x00, 0x00, 0x8b, 0xb0, 0xfb, 0xff, 0xff, 0xff,
- 0x8b, 0x80, 0x11, 0x00, 0x00, 0x00 };
-
- text.content = llvm::makeArrayRef(textBytes, sizeof(textBytes));
- text.relocations.push_back(makeReloc(0x01, true, true, GENERIC_RELOC_VANILLA, 0));
- text.relocations.push_back(makeReloc(0x06, false, true, GENERIC_RELOC_VANILLA, 0));
- text.relocations.push_back(makeScatReloc(0x0c, GENERIC_RELOC_LOCAL_SECTDIFF, 0));
- text.relocations.push_back(makeScatReloc(0x0, GENERIC_RELOC_PAIR, 5));
- text.relocations.push_back(makeReloc(0x12, true, true, GENERIC_RELOC_TLV, 1));
-
- f.undefinedSymbols.push_back(makeUndefSymbol("_bar"));
- f.undefinedSymbols.push_back(makeUndefSymbol("_tbar"));
-
- std::error_code ec =
- llvm::sys::fs::createTemporaryFile(Twine("xx"), "o", tmpFl);
- EXPECT_FALSE(ec);
- llvm::Error ec2 = writeBinary(f, tmpFl);
- EXPECT_FALSE(ec2);
- }
- std::unique_ptr<MemoryBuffer> bufferOwner;
- std::unique_ptr<NormalizedFile> f2;
- fromBinary(tmpFl, bufferOwner, f2, "i386");
-
- EXPECT_EQ(lld::MachOLinkingContext::arch_x86, f2->arch);
- EXPECT_EQ(MH_OBJECT, f2->fileType);
- EXPECT_EQ(FileFlags(MH_SUBSECTIONS_VIA_SYMBOLS), f2->flags);
-
- EXPECT_TRUE(f2->localSymbols.empty());
- EXPECT_TRUE(f2->globalSymbols.empty());
- EXPECT_EQ(2UL, f2->undefinedSymbols.size());
- const Symbol& barUndef = f2->undefinedSymbols[0];
- EXPECT_TRUE(barUndef.name.equals("_bar"));
- EXPECT_EQ(N_UNDF, barUndef.type);
- EXPECT_EQ(SymbolScope(N_EXT), barUndef.scope);
- const Symbol& tbarUndef = f2->undefinedSymbols[1];
- EXPECT_TRUE(tbarUndef.name.equals("_tbar"));
- EXPECT_EQ(N_UNDF, tbarUndef.type);
- EXPECT_EQ(SymbolScope(N_EXT), tbarUndef.scope);
-
- EXPECT_EQ(1UL, f2->sections.size());
- const Section& text = f2->sections[0];
- EXPECT_TRUE(text.segmentName.equals("__TEXT"));
- EXPECT_TRUE(text.sectionName.equals("__text"));
- EXPECT_EQ(S_REGULAR, text.type);
- EXPECT_EQ(text.attributes,SectionAttr(S_ATTR_PURE_INSTRUCTIONS
- | S_ATTR_SOME_INSTRUCTIONS));
- EXPECT_EQ((uint16_t)text.alignment, 16U);
- EXPECT_EQ(text.address, Hex64(0x0));
- EXPECT_EQ(22UL, text.content.size());
- const Relocation& call = text.relocations[0];
- EXPECT_EQ(call.offset, Hex32(0x1));
- EXPECT_EQ(call.scattered, false);
- EXPECT_EQ(call.type, GENERIC_RELOC_VANILLA);
- EXPECT_EQ(call.pcRel, true);
- EXPECT_EQ(call.length, 2);
- EXPECT_EQ(call.isExtern, true);
- EXPECT_EQ(call.symbol, 0U);
- const Relocation& absLoad = text.relocations[1];
- EXPECT_EQ(absLoad.offset, Hex32(0x6));
- EXPECT_EQ(absLoad.scattered, false);
- EXPECT_EQ(absLoad.type, GENERIC_RELOC_VANILLA);
- EXPECT_EQ(absLoad.pcRel, false);
- EXPECT_EQ(absLoad.length, 2);
- EXPECT_EQ(absLoad.isExtern, true);
- EXPECT_EQ(absLoad.symbol,0U);
- const Relocation& pic1 = text.relocations[2];
- EXPECT_EQ(pic1.offset, Hex32(0xc));
- EXPECT_EQ(pic1.scattered, true);
- EXPECT_EQ(pic1.type, GENERIC_RELOC_LOCAL_SECTDIFF);
- EXPECT_EQ(pic1.length, 2);
- EXPECT_EQ(pic1.value, 0U);
- const Relocation& pic2 = text.relocations[3];
- EXPECT_EQ(pic2.offset, Hex32(0x0));
- EXPECT_EQ(pic1.scattered, true);
- EXPECT_EQ(pic2.type, GENERIC_RELOC_PAIR);
- EXPECT_EQ(pic2.length, 2);
- EXPECT_EQ(pic2.value, 5U);
- const Relocation& tlv = text.relocations[4];
- EXPECT_EQ(tlv.offset, Hex32(0x12));
- EXPECT_EQ(tlv.type, GENERIC_RELOC_TLV);
- EXPECT_EQ(tlv.length, 2);
- EXPECT_EQ(tlv.isExtern, true);
- EXPECT_EQ(tlv.symbol, 1U);
-
- // lld::errs() << "temp = " << tmpFl << "\n";
- bufferOwner.reset(nullptr);
- std::error_code ec = llvm::sys::fs::remove(Twine(tmpFl));
- EXPECT_FALSE(ec);
-}
-
-
-
-TEST(BinaryWriterTest, obj_relocs_armv7) {
- SmallString<128> tmpFl;
- {
- NormalizedFile f;
- f.arch = lld::MachOLinkingContext::arch_armv7;
- f.fileType = MH_OBJECT;
- f.flags = MH_SUBSECTIONS_VIA_SYMBOLS;
- f.os = lld::MachOLinkingContext::OS::macOSX;
- f.sections.resize(1);
- Section& text = f.sections.front();
- text.segmentName = "__TEXT";
- text.sectionName = "__text";
- text.type = S_REGULAR;
- text.attributes = SectionAttr(S_ATTR_PURE_INSTRUCTIONS
- | S_ATTR_SOME_INSTRUCTIONS);
- text.alignment = 4;
- text.address = 0;
- const uint8_t textBytes[] = {
- 0xff, 0xf7, 0xfe, 0xef, 0x40, 0xf2, 0x05, 0x01,
- 0xc0, 0xf2, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0xbf };
-
- text.content = llvm::makeArrayRef(textBytes, sizeof(textBytes));
- text.relocations.push_back(makeReloc(0x00, true, true,
- ARM_THUMB_RELOC_BR22, 2));
- text.relocations.push_back(makeScatReloc(0x04,
- ARM_RELOC_HALF_SECTDIFF, 0x10));
- text.relocations.push_back(makeScatReloc(0x00,
- ARM_RELOC_PAIR, 0xC));
- text.relocations.push_back(makeScatReloc(0x08,
- ARM_RELOC_HALF_SECTDIFF, 0x10));
- text.relocations.push_back(makeScatReloc(0x00,
- ARM_RELOC_PAIR, 0xC));
- text.relocations.push_back(makeReloc(0x0C, false, true,
- ARM_RELOC_VANILLA, 2));
-
- f.globalSymbols.push_back(makeThumbSymbol("_foo", 0x00));
- f.globalSymbols.push_back(makeThumbSymbol("_foo2", 0x10));
- f.undefinedSymbols.push_back(makeUndefSymbol("_bar"));
-
- std::error_code ec =
- llvm::sys::fs::createTemporaryFile(Twine("xx"), "o", tmpFl);
- EXPECT_FALSE(ec);
- llvm::Error ec2 = writeBinary(f, tmpFl);
- EXPECT_FALSE(ec2);
- }
- std::unique_ptr<MemoryBuffer> bufferOwner;
- std::unique_ptr<NormalizedFile> f2;
- fromBinary(tmpFl, bufferOwner, f2, "armv7");
-
- EXPECT_EQ(lld::MachOLinkingContext::arch_armv7, f2->arch);
- EXPECT_EQ(MH_OBJECT, f2->fileType);
- EXPECT_EQ(FileFlags(MH_SUBSECTIONS_VIA_SYMBOLS), f2->flags);
-
- EXPECT_TRUE(f2->localSymbols.empty());
- EXPECT_EQ(2UL, f2->globalSymbols.size());
- const Symbol& fooDef = f2->globalSymbols[0];
- EXPECT_TRUE(fooDef.name.equals("_foo"));
- EXPECT_EQ(N_SECT, fooDef.type);
- EXPECT_EQ(1, fooDef.sect);
- EXPECT_EQ(SymbolScope(N_EXT), fooDef.scope);
- const Symbol& foo2Def = f2->globalSymbols[1];
- EXPECT_TRUE(foo2Def.name.equals("_foo2"));
- EXPECT_EQ(N_SECT, foo2Def.type);
- EXPECT_EQ(1, foo2Def.sect);
- EXPECT_EQ(SymbolScope(N_EXT), foo2Def.scope);
-
- EXPECT_EQ(1UL, f2->undefinedSymbols.size());
- const Symbol& barUndef = f2->undefinedSymbols[0];
- EXPECT_TRUE(barUndef.name.equals("_bar"));
- EXPECT_EQ(N_UNDF, barUndef.type);
- EXPECT_EQ(SymbolScope(N_EXT), barUndef.scope);
-
- EXPECT_EQ(1UL, f2->sections.size());
- const Section& text = f2->sections[0];
- EXPECT_TRUE(text.segmentName.equals("__TEXT"));
- EXPECT_TRUE(text.sectionName.equals("__text"));
- EXPECT_EQ(S_REGULAR, text.type);
- EXPECT_EQ(text.attributes,SectionAttr(S_ATTR_PURE_INSTRUCTIONS
- | S_ATTR_SOME_INSTRUCTIONS));
- EXPECT_EQ((uint16_t)text.alignment, 4U);
- EXPECT_EQ(text.address, Hex64(0x0));
- EXPECT_EQ(18UL, text.content.size());
- const Relocation& blx = text.relocations[0];
- EXPECT_EQ(blx.offset, Hex32(0x0));
- EXPECT_EQ(blx.scattered, false);
- EXPECT_EQ(blx.type, ARM_THUMB_RELOC_BR22);
- EXPECT_EQ(blx.pcRel, true);
- EXPECT_EQ(blx.length, 2);
- EXPECT_EQ(blx.isExtern, true);
- EXPECT_EQ(blx.symbol, 2U);
- const Relocation& movw1 = text.relocations[1];
- EXPECT_EQ(movw1.offset, Hex32(0x4));
- EXPECT_EQ(movw1.scattered, true);
- EXPECT_EQ(movw1.type, ARM_RELOC_HALF_SECTDIFF);
- EXPECT_EQ(movw1.length, 2);
- EXPECT_EQ(movw1.value, 0x10U);
- const Relocation& movw2 = text.relocations[2];
- EXPECT_EQ(movw2.offset, Hex32(0x0));
- EXPECT_EQ(movw2.scattered, true);
- EXPECT_EQ(movw2.type, ARM_RELOC_PAIR);
- EXPECT_EQ(movw2.length, 2);
- EXPECT_EQ(movw2.value, Hex32(0xC));
- const Relocation& movt1 = text.relocations[3];
- EXPECT_EQ(movt1.offset, Hex32(0x8));
- EXPECT_EQ(movt1.scattered, true);
- EXPECT_EQ(movt1.type, ARM_RELOC_HALF_SECTDIFF);
- EXPECT_EQ(movt1.length, 2);
- EXPECT_EQ(movt1.value, Hex32(0x10));
- const Relocation& movt2 = text.relocations[4];
- EXPECT_EQ(movt2.offset, Hex32(0x0));
- EXPECT_EQ(movt2.scattered, true);
- EXPECT_EQ(movt2.type, ARM_RELOC_PAIR);
- EXPECT_EQ(movt2.length, 2);
- EXPECT_EQ(movt2.value, Hex32(0xC));
- const Relocation& absPointer = text.relocations[5];
- EXPECT_EQ(absPointer.offset, Hex32(0xC));
- EXPECT_EQ(absPointer.type, ARM_RELOC_VANILLA);
- EXPECT_EQ(absPointer.length, 2);
- EXPECT_EQ(absPointer.isExtern, true);
- EXPECT_EQ(absPointer.symbol, 2U);
-
- // lld::errs() << "temp = " << tmpFl << "\n";
- bufferOwner.reset(nullptr);
- std::error_code ec = llvm::sys::fs::remove(Twine(tmpFl));
- EXPECT_FALSE(ec);
-}
-
-
-
-TEST(BinaryWriterTest, obj_relocs_ppc) {
- SmallString<128> tmpFl;
- {
- NormalizedFile f;
- f.arch = lld::MachOLinkingContext::arch_ppc;
- f.fileType = MH_OBJECT;
- f.flags = MH_SUBSECTIONS_VIA_SYMBOLS;
- f.os = lld::MachOLinkingContext::OS::macOSX;
- f.sections.resize(1);
- Section& text = f.sections.front();
- text.segmentName = "__TEXT";
- text.sectionName = "__text";
- text.type = S_REGULAR;
- text.attributes = SectionAttr(S_ATTR_PURE_INSTRUCTIONS
- | S_ATTR_SOME_INSTRUCTIONS);
- text.alignment = 4;
- text.address = 0;
- const uint8_t textBytes[] = {
- 0x48, 0x00, 0x00, 0x01, 0x40, 0x82, 0xff, 0xfc,
- 0x3c, 0x62, 0x00, 0x00, 0x3c, 0x62, 0x00, 0x00,
- 0x80, 0x63, 0x00, 0x24, 0x80, 0x63, 0x00, 0x24,
- 0x3c, 0x40, 0x00, 0x00, 0x3c, 0x60, 0x00, 0x00,
- 0x80, 0x42, 0x00, 0x28, 0x80, 0x63, 0x00, 0x28,
- 0x60, 0x00, 0x00, 0x00 };
-
- text.content = llvm::makeArrayRef(textBytes, sizeof(textBytes));
- text.relocations.push_back(makeReloc(0x00, true, true,
- PPC_RELOC_BR24, 2));
- text.relocations.push_back(makeReloc(0x04, true, true,
- PPC_RELOC_BR14, 2));
- text.relocations.push_back(makeScatReloc(0x08,
- PPC_RELOC_HI16_SECTDIFF, 0x28));
- text.relocations.push_back(makeScatReloc(0x24,
- PPC_RELOC_PAIR, 0x4));
- text.relocations.push_back(makeScatReloc(0x0C,
- PPC_RELOC_HA16_SECTDIFF, 0x28));
- text.relocations.push_back(makeScatReloc(0x24,
- PPC_RELOC_PAIR, 0x4));
- text.relocations.push_back(makeScatReloc(0x10,
- PPC_RELOC_LO16_SECTDIFF, 0x28));
- text.relocations.push_back(makeScatReloc(0x00,
- PPC_RELOC_PAIR, 0x4));
- text.relocations.push_back(makeScatReloc(0x14,
- PPC_RELOC_LO14_SECTDIFF, 0x28));
- text.relocations.push_back(makeScatReloc(0x00,
- PPC_RELOC_PAIR, 0x4));
- text.relocations.push_back(makeReloc(0x18, false, false,
- PPC_RELOC_HI16, 1));
- text.relocations.push_back(makeReloc(0x28, false, false,
- PPC_RELOC_PAIR, 0));
- text.relocations.push_back(makeReloc(0x1C, false, false,
- PPC_RELOC_HA16, 1));
- text.relocations.push_back(makeReloc(0x28, false, false,
- PPC_RELOC_PAIR, 0));
- text.relocations.push_back(makeReloc(0x20, false, false,
- PPC_RELOC_LO16, 1));
- text.relocations.push_back(makeReloc(0x00, false, false,
- PPC_RELOC_PAIR, 0));
- text.relocations.push_back(makeReloc(0x24, false, false,
- PPC_RELOC_LO14, 1));
- text.relocations.push_back(makeReloc(0x00, false, false,
- PPC_RELOC_PAIR, 0));
-
- f.globalSymbols.push_back(makeSymbol("_foo", 0x00));
- f.globalSymbols.push_back(makeSymbol("_foo2", 0x28));
- f.undefinedSymbols.push_back(makeUndefSymbol("_bar"));
-
- std::error_code ec =
- llvm::sys::fs::createTemporaryFile(Twine("xx"), "o", tmpFl);
- EXPECT_FALSE(ec);
- llvm::Error ec2 = writeBinary(f, tmpFl);
- EXPECT_FALSE(ec2);
- }
- std::unique_ptr<MemoryBuffer> bufferOwner;
- std::unique_ptr<NormalizedFile> f2;
- fromBinary(tmpFl, bufferOwner, f2, "ppc");
-
- EXPECT_EQ(lld::MachOLinkingContext::arch_ppc, f2->arch);
- EXPECT_EQ(MH_OBJECT, f2->fileType);
- EXPECT_EQ(FileFlags(MH_SUBSECTIONS_VIA_SYMBOLS), f2->flags);
-
- EXPECT_TRUE(f2->localSymbols.empty());
- EXPECT_EQ(2UL, f2->globalSymbols.size());
- const Symbol& fooDef = f2->globalSymbols[0];
- EXPECT_TRUE(fooDef.name.equals("_foo"));
- EXPECT_EQ(N_SECT, fooDef.type);
- EXPECT_EQ(1, fooDef.sect);
- EXPECT_EQ(SymbolScope(N_EXT), fooDef.scope);
- const Symbol& foo2Def = f2->globalSymbols[1];
- EXPECT_TRUE(foo2Def.name.equals("_foo2"));
- EXPECT_EQ(N_SECT, foo2Def.type);
- EXPECT_EQ(1, foo2Def.sect);
- EXPECT_EQ(SymbolScope(N_EXT), foo2Def.scope);
-
- EXPECT_EQ(1UL, f2->undefinedSymbols.size());
- const Symbol& barUndef = f2->undefinedSymbols[0];
- EXPECT_TRUE(barUndef.name.equals("_bar"));
- EXPECT_EQ(N_UNDF, barUndef.type);
- EXPECT_EQ(SymbolScope(N_EXT), barUndef.scope);
-
- EXPECT_EQ(1UL, f2->sections.size());
- const Section& text = f2->sections[0];
- EXPECT_TRUE(text.segmentName.equals("__TEXT"));
- EXPECT_TRUE(text.sectionName.equals("__text"));
- EXPECT_EQ(S_REGULAR, text.type);
- EXPECT_EQ(text.attributes,SectionAttr(S_ATTR_PURE_INSTRUCTIONS
- | S_ATTR_SOME_INSTRUCTIONS));
- EXPECT_EQ((uint16_t)text.alignment, 4U);
- EXPECT_EQ(text.address, Hex64(0x0));
- EXPECT_EQ(44UL, text.content.size());
- const Relocation& br24 = text.relocations[0];
- EXPECT_EQ(br24.offset, Hex32(0x0));
- EXPECT_EQ(br24.scattered, false);
- EXPECT_EQ(br24.type, PPC_RELOC_BR24);
- EXPECT_EQ(br24.pcRel, true);
- EXPECT_EQ(br24.length, 2);
- EXPECT_EQ(br24.isExtern, true);
- EXPECT_EQ(br24.symbol, 2U);
- const Relocation& br14 = text.relocations[1];
- EXPECT_EQ(br14.offset, Hex32(0x4));
- EXPECT_EQ(br14.scattered, false);
- EXPECT_EQ(br14.type, PPC_RELOC_BR14);
- EXPECT_EQ(br14.pcRel, true);
- EXPECT_EQ(br14.length, 2);
- EXPECT_EQ(br14.isExtern, true);
- EXPECT_EQ(br14.symbol, 2U);
- const Relocation& pichi1 = text.relocations[2];
- EXPECT_EQ(pichi1.offset, Hex32(0x8));
- EXPECT_EQ(pichi1.scattered, true);
- EXPECT_EQ(pichi1.type, PPC_RELOC_HI16_SECTDIFF);
- EXPECT_EQ(pichi1.length, 2);
- EXPECT_EQ(pichi1.value, 0x28U);
- const Relocation& pichi2 = text.relocations[3];
- EXPECT_EQ(pichi2.offset, Hex32(0x24));
- EXPECT_EQ(pichi2.scattered, true);
- EXPECT_EQ(pichi2.type, PPC_RELOC_PAIR);
- EXPECT_EQ(pichi2.length, 2);
- EXPECT_EQ(pichi2.value, 0x4U);
- const Relocation& picha1 = text.relocations[4];
- EXPECT_EQ(picha1.offset, Hex32(0xC));
- EXPECT_EQ(picha1.scattered, true);
- EXPECT_EQ(picha1.type, PPC_RELOC_HA16_SECTDIFF);
- EXPECT_EQ(picha1.length, 2);
- EXPECT_EQ(picha1.value, 0x28U);
- const Relocation& picha2 = text.relocations[5];
- EXPECT_EQ(picha2.offset, Hex32(0x24));
- EXPECT_EQ(picha2.scattered, true);
- EXPECT_EQ(picha2.type, PPC_RELOC_PAIR);
- EXPECT_EQ(picha2.length, 2);
- EXPECT_EQ(picha2.value, 0x4U);
- const Relocation& piclo1 = text.relocations[6];
- EXPECT_EQ(piclo1.offset, Hex32(0x10));
- EXPECT_EQ(piclo1.scattered, true);
- EXPECT_EQ(piclo1.type, PPC_RELOC_LO16_SECTDIFF);
- EXPECT_EQ(piclo1.length, 2);
- EXPECT_EQ(piclo1.value, 0x28U);
- const Relocation& piclo2 = text.relocations[7];
- EXPECT_EQ(piclo2.offset, Hex32(0x0));
- EXPECT_EQ(piclo2.scattered, true);
- EXPECT_EQ(piclo2.type, PPC_RELOC_PAIR);
- EXPECT_EQ(piclo2.length, 2);
- EXPECT_EQ(piclo2.value, 0x4U);
- const Relocation& picloa1 = text.relocations[8];
- EXPECT_EQ(picloa1.offset, Hex32(0x14));
- EXPECT_EQ(picloa1.scattered, true);
- EXPECT_EQ(picloa1.type, PPC_RELOC_LO14_SECTDIFF);
- EXPECT_EQ(picloa1.length, 2);
- EXPECT_EQ(picloa1.value, 0x28U);
- const Relocation& picloa2 = text.relocations[9];
- EXPECT_EQ(picloa2.offset, Hex32(0x0));
- EXPECT_EQ(picloa2.scattered, true);
- EXPECT_EQ(picloa2.type, PPC_RELOC_PAIR);
- EXPECT_EQ(picloa2.length, 2);
- EXPECT_EQ(picloa2.value, 0x4U);
- const Relocation& abshi1 = text.relocations[10];
- EXPECT_EQ(abshi1.offset, Hex32(0x18));
- EXPECT_EQ(abshi1.scattered, false);
- EXPECT_EQ(abshi1.type, PPC_RELOC_HI16);
- EXPECT_EQ(abshi1.length, 2);
- EXPECT_EQ(abshi1.symbol, 1U);
- const Relocation& abshi2 = text.relocations[11];
- EXPECT_EQ(abshi2.offset, Hex32(0x28));
- EXPECT_EQ(abshi2.scattered, false);
- EXPECT_EQ(abshi2.type, PPC_RELOC_PAIR);
- EXPECT_EQ(abshi2.length, 2);
- EXPECT_EQ(abshi2.symbol, 0U);
- const Relocation& absha1 = text.relocations[12];
- EXPECT_EQ(absha1.offset, Hex32(0x1C));
- EXPECT_EQ(absha1.scattered, false);
- EXPECT_EQ(absha1.type, PPC_RELOC_HA16);
- EXPECT_EQ(absha1.length, 2);
- EXPECT_EQ(absha1.symbol, 1U);
- const Relocation& absha2 = text.relocations[13];
- EXPECT_EQ(absha2.offset, Hex32(0x28));
- EXPECT_EQ(absha2.scattered, false);
- EXPECT_EQ(absha2.type, PPC_RELOC_PAIR);
- EXPECT_EQ(absha2.length, 2);
- EXPECT_EQ(absha2.symbol, 0U);
- const Relocation& abslo1 = text.relocations[14];
- EXPECT_EQ(abslo1.offset, Hex32(0x20));
- EXPECT_EQ(abslo1.scattered, false);
- EXPECT_EQ(abslo1.type, PPC_RELOC_LO16);
- EXPECT_EQ(abslo1.length, 2);
- EXPECT_EQ(abslo1.symbol, 1U);
- const Relocation& abslo2 = text.relocations[15];
- EXPECT_EQ(abslo2.offset, Hex32(0x00));
- EXPECT_EQ(abslo2.scattered, false);
- EXPECT_EQ(abslo2.type, PPC_RELOC_PAIR);
- EXPECT_EQ(abslo2.length, 2);
- EXPECT_EQ(abslo2.symbol, 0U);
- const Relocation& absloa1 = text.relocations[16];
- EXPECT_EQ(absloa1.offset, Hex32(0x24));
- EXPECT_EQ(absloa1.scattered, false);
- EXPECT_EQ(absloa1.type, PPC_RELOC_LO14);
- EXPECT_EQ(absloa1.length, 2);
- EXPECT_EQ(absloa1.symbol, 1U);
- const Relocation& absloa2 = text.relocations[17];
- EXPECT_EQ(absloa2.offset, Hex32(0x00));
- EXPECT_EQ(absloa2.scattered, false);
- EXPECT_EQ(absloa2.type, PPC_RELOC_PAIR);
- EXPECT_EQ(absloa2.length, 2);
- EXPECT_EQ(absloa2.symbol, 0U);
-
- bufferOwner.reset(nullptr);
- std::error_code ec = llvm::sys::fs::remove(Twine(tmpFl));
- EXPECT_FALSE(ec);
-}
+++ /dev/null
-//===- lld/unittest/MachOTests/MachONormalizedFileToAtomsTests.cpp --------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "../../lib/ReaderWriter/MachO/MachONormalizedFile.h"
-#include "lld/Core/Atom.h"
-#include "lld/Core/DefinedAtom.h"
-#include "lld/Core/File.h"
-#include "lld/Core/UndefinedAtom.h"
-#include "lld/ReaderWriter/MachOLinkingContext.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/BinaryFormat/MachO.h"
-#include "llvm/Support/Error.h"
-#include "llvm/Support/YAMLTraits.h"
-#include "gtest/gtest.h"
-#include <cstdint>
-#include <memory>
-
-using namespace lld::mach_o::normalized;
-using namespace llvm::MachO;
-
-TEST(ToAtomsTest, empty_obj_x86_64) {
- NormalizedFile f;
- f.arch = lld::MachOLinkingContext::arch_x86_64;
- llvm::Expected<std::unique_ptr<const lld::File>> atom_f =
- normalizedToAtoms(f, "", false);
- EXPECT_FALSE(!atom_f);
- EXPECT_EQ(0U, (*atom_f)->defined().size());
-}
-
-TEST(ToAtomsTest, basic_obj_x86_64) {
- NormalizedFile f;
- f.arch = lld::MachOLinkingContext::arch_x86_64;
- Section textSection;
- static const uint8_t contentBytes[] = { 0x90, 0xC3, 0xC3, 0xC4 };
- const unsigned contentSize = sizeof(contentBytes) / sizeof(contentBytes[0]);
- textSection.content = llvm::makeArrayRef(contentBytes, contentSize);
- f.sections.push_back(textSection);
- Symbol fooSymbol;
- fooSymbol.name = "_foo";
- fooSymbol.type = N_SECT;
- fooSymbol.scope = N_EXT;
- fooSymbol.sect = 1;
- fooSymbol.value = 0;
- f.globalSymbols.push_back(fooSymbol);
- Symbol barSymbol;
- barSymbol.name = "_bar";
- barSymbol.type = N_SECT;
- barSymbol.scope = N_EXT;
- barSymbol.sect = 1;
- barSymbol.value = 2;
- f.globalSymbols.push_back(barSymbol);
- Symbol undefSym;
- undefSym.name = "_undef";
- undefSym.type = N_UNDF;
- f.undefinedSymbols.push_back(undefSym);
- Symbol bazSymbol;
- bazSymbol.name = "_baz";
- bazSymbol.type = N_SECT;
- bazSymbol.scope = N_EXT | N_PEXT;
- bazSymbol.sect = 1;
- bazSymbol.value = 3;
- f.localSymbols.push_back(bazSymbol);
-
- llvm::Expected<std::unique_ptr<const lld::File>> atom_f =
- normalizedToAtoms(f, "", false);
- EXPECT_FALSE(!atom_f);
- const lld::File &file = **atom_f;
- EXPECT_EQ(3U, file.defined().size());
- auto it = file.defined().begin();
- const lld::DefinedAtom *atom1 = *it;
- ++it;
- const lld::DefinedAtom *atom2 = *it;
- ++it;
- const lld::DefinedAtom *atom3 = *it;
- const lld::UndefinedAtom *atom4 = *file.undefined().begin();
- EXPECT_TRUE(atom1->name().equals("_foo"));
- EXPECT_EQ(2U, atom1->rawContent().size());
- EXPECT_EQ(0x90, atom1->rawContent()[0]);
- EXPECT_EQ(0xC3, atom1->rawContent()[1]);
- EXPECT_EQ(lld::Atom::scopeGlobal, atom1->scope());
-
- EXPECT_TRUE(atom2->name().equals("_bar"));
- EXPECT_EQ(1U, atom2->rawContent().size());
- EXPECT_EQ(0xC3, atom2->rawContent()[0]);
- EXPECT_EQ(lld::Atom::scopeGlobal, atom2->scope());
-
- EXPECT_TRUE(atom3->name().equals("_baz"));
- EXPECT_EQ(1U, atom3->rawContent().size());
- EXPECT_EQ(0xC4, atom3->rawContent()[0]);
- EXPECT_EQ(lld::Atom::scopeLinkageUnit, atom3->scope());
-
- EXPECT_TRUE(atom4->name().equals("_undef"));
- EXPECT_EQ(lld::Atom::definitionUndefined, atom4->definition());
-}
-
-TEST(ToAtomsTest, reservedUnitLength) {
- static const uint8_t debugInfoWithReservedLengthContent[12] = {
- 0xf0, 0xff, 0xff, 0xff // Reserved length value
- };
- static const uint8_t debugInfoWithValidBigLengthContent[12] = {
- 0xef, 0xff, 0xff, 0xff, // The maximum valid length value for DWARF32
- 0x00, 0x00 // Wrong version
- };
- static const uint8_t dummyContent[] = {0x00};
-
- NormalizedFile fReservedLength, fValidBigLength;
- fReservedLength.arch = lld::MachOLinkingContext::arch_x86;
- fValidBigLength.arch = lld::MachOLinkingContext::arch_x86;
- Section section;
- section.segmentName = "__DWARF";
- section.sectionName = "__debug_info";
- section.content = llvm::makeArrayRef(debugInfoWithReservedLengthContent);
- fReservedLength.sections.push_back(section);
- section.content = llvm::makeArrayRef(debugInfoWithValidBigLengthContent);
- fValidBigLength.sections.push_back(section);
- section.sectionName = "__debug_abbrev";
- section.content = llvm::makeArrayRef(dummyContent);
- fReservedLength.sections.push_back(section);
- fValidBigLength.sections.push_back(section);
- section.sectionName = "__debug_str";
- fReservedLength.sections.push_back(section);
- fValidBigLength.sections.push_back(section);
-
- auto resultReservedLength = normalizedToAtoms(fReservedLength, "foo", false);
- auto resultValidBigLength = normalizedToAtoms(fValidBigLength, "foo", false);
-
- // Both cases should return errors, but different.
- ASSERT_FALSE(resultReservedLength);
- ASSERT_FALSE(resultValidBigLength);
-
- EXPECT_STREQ("Malformed DWARF in foo",
- toString(resultReservedLength.takeError()).c_str());
- EXPECT_STREQ("Unsupported DWARF version in foo",
- toString(resultValidBigLength.takeError()).c_str());
-}
+++ /dev/null
-//===- lld/unittest/MachOTests/MachONormalizedFileYAMLTests.cpp -----------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "../../lib/ReaderWriter/MachO/MachONormalizedFile.h"
-#include "lld/ReaderWriter/MachOLinkingContext.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/BinaryFormat/MachO.h"
-#include "llvm/Support/Error.h"
-#include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/raw_ostream.h"
-#include "gtest/gtest.h"
-#include <cstdint>
-#include <memory>
-#include <string>
-#include <system_error>
-
-using llvm::StringRef;
-using llvm::MemoryBuffer;
-using lld::mach_o::normalized::NormalizedFile;
-using lld::mach_o::normalized::Symbol;
-using lld::mach_o::normalized::Section;
-using lld::mach_o::normalized::Relocation;
-
-static std::unique_ptr<NormalizedFile> fromYAML(StringRef str) {
- std::unique_ptr<MemoryBuffer> mb(MemoryBuffer::getMemBuffer(str));
- llvm::Expected<std::unique_ptr<NormalizedFile>> r
- = lld::mach_o::normalized::readYaml(mb);
- EXPECT_FALSE(!r);
- return std::move(*r);
-}
-
-static void toYAML(const NormalizedFile &f, std::string &out) {
- llvm::raw_string_ostream ostr(out);
- std::error_code ec = lld::mach_o::normalized::writeYaml(f, ostr);
- EXPECT_TRUE(!ec);
-}
-
-// ppc is no longer supported, but it is here to test endianness handling.
-TEST(ObjectFileYAML, empty_ppc) {
- std::unique_ptr<NormalizedFile> f = fromYAML(
- "---\n"
- "arch: ppc\n"
- "file-type: MH_OBJECT\n"
- "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n"
- "...\n");
- EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_ppc);
- EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
- EXPECT_EQ((int)(f->flags), (int)(int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
- EXPECT_TRUE(f->sections.empty());
- EXPECT_TRUE(f->localSymbols.empty());
- EXPECT_TRUE(f->globalSymbols.empty());
- EXPECT_TRUE(f->undefinedSymbols.empty());
-}
-
-TEST(ObjectFileYAML, empty_x86_64) {
- std::unique_ptr<NormalizedFile> f = fromYAML(
- "---\n"
- "arch: x86_64\n"
- "file-type: MH_OBJECT\n"
- "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n"
- "...\n");
- EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86_64);
- EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
- EXPECT_EQ((int)(f->flags), (int)(int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
- EXPECT_TRUE(f->sections.empty());
- EXPECT_TRUE(f->localSymbols.empty());
- EXPECT_TRUE(f->globalSymbols.empty());
- EXPECT_TRUE(f->undefinedSymbols.empty());
-}
-
-TEST(ObjectFileYAML, empty_x86) {
- std::unique_ptr<NormalizedFile> f = fromYAML(
- "---\n"
- "arch: x86\n"
- "file-type: MH_OBJECT\n"
- "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n"
- "...\n");
- EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86);
- EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
- EXPECT_EQ((int)(f->flags), (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
- EXPECT_TRUE(f->sections.empty());
- EXPECT_TRUE(f->localSymbols.empty());
- EXPECT_TRUE(f->globalSymbols.empty());
- EXPECT_TRUE(f->undefinedSymbols.empty());
-}
-
-TEST(ObjectFileYAML, empty_armv6) {
- std::unique_ptr<NormalizedFile> f = fromYAML(
- "---\n"
- "arch: armv6\n"
- "file-type: MH_OBJECT\n"
- "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n"
- "...\n");
- EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_armv6);
- EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
- EXPECT_EQ((int)(f->flags), (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
- EXPECT_TRUE(f->sections.empty());
- EXPECT_TRUE(f->localSymbols.empty());
- EXPECT_TRUE(f->globalSymbols.empty());
- EXPECT_TRUE(f->undefinedSymbols.empty());
-}
-
-TEST(ObjectFileYAML, empty_armv7) {
- std::unique_ptr<NormalizedFile> f = fromYAML(
- "---\n"
- "arch: armv7\n"
- "file-type: MH_OBJECT\n"
- "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n"
- "...\n");
- EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_armv7);
- EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
- EXPECT_EQ((int)(f->flags), (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
- EXPECT_TRUE(f->sections.empty());
- EXPECT_TRUE(f->localSymbols.empty());
- EXPECT_TRUE(f->globalSymbols.empty());
- EXPECT_TRUE(f->undefinedSymbols.empty());
-}
-
-TEST(ObjectFileYAML, empty_armv7s) {
- std::unique_ptr<NormalizedFile> f = fromYAML(
- "---\n"
- "arch: armv7s\n"
- "file-type: MH_OBJECT\n"
- "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n"
- "...\n");
- EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_armv7s);
- EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
- EXPECT_EQ((int)(f->flags), (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
- EXPECT_TRUE(f->sections.empty());
- EXPECT_TRUE(f->localSymbols.empty());
- EXPECT_TRUE(f->globalSymbols.empty());
- EXPECT_TRUE(f->undefinedSymbols.empty());
-}
-
-TEST(ObjectFileYAML, roundTrip) {
- std::string intermediate;
- {
- NormalizedFile f;
- f.arch = lld::MachOLinkingContext::arch_x86_64;
- f.fileType = llvm::MachO::MH_OBJECT;
- f.flags = (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS;
- f.os = lld::MachOLinkingContext::OS::macOSX;
- toYAML(f, intermediate);
- }
- {
- std::unique_ptr<NormalizedFile> f2 = fromYAML(intermediate);
- EXPECT_EQ(f2->arch, lld::MachOLinkingContext::arch_x86_64);
- EXPECT_EQ((int)(f2->fileType), llvm::MachO::MH_OBJECT);
- EXPECT_EQ((int)(f2->flags), (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
- EXPECT_TRUE(f2->sections.empty());
- EXPECT_TRUE(f2->localSymbols.empty());
- EXPECT_TRUE(f2->globalSymbols.empty());
- EXPECT_TRUE(f2->undefinedSymbols.empty());
- }
-}
-
-TEST(ObjectFileYAML, oneSymbol) {
- std::unique_ptr<NormalizedFile> f = fromYAML(
- "---\n"
- "arch: x86_64\n"
- "file-type: MH_OBJECT\n"
- "global-symbols:\n"
- " - name: _main\n"
- " type: N_SECT\n"
- " scope: [ N_EXT ]\n"
- " sect: 1\n"
- " desc: [ ]\n"
- " value: 0x100\n"
- "...\n");
- EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86_64);
- EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
- EXPECT_TRUE(f->sections.empty());
- EXPECT_TRUE(f->localSymbols.empty());
- EXPECT_TRUE(f->undefinedSymbols.empty());
- EXPECT_EQ(f->globalSymbols.size(), 1UL);
- const Symbol& sym = f->globalSymbols[0];
- EXPECT_TRUE(sym.name.equals("_main"));
- EXPECT_EQ((int)(sym.type), llvm::MachO::N_SECT);
- EXPECT_EQ((int)(sym.scope), llvm::MachO::N_EXT);
- EXPECT_EQ(sym.sect, 1);
- EXPECT_EQ((int)(sym.desc), 0);
- EXPECT_EQ((uint64_t)sym.value, 0x100ULL);
-}
-
-TEST(ObjectFileYAML, oneSection) {
- std::unique_ptr<NormalizedFile> f = fromYAML(
- "---\n"
- "arch: x86_64\n"
- "file-type: MH_OBJECT\n"
- "sections:\n"
- " - segment: __TEXT\n"
- " section: __text\n"
- " type: S_REGULAR\n"
- " attributes: [ S_ATTR_PURE_INSTRUCTIONS ]\n"
- " alignment: 2\n"
- " address: 0x12345678\n"
- " content: [ 0x90, 0x90 ]\n"
- "...\n");
- EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86_64);
- EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
- EXPECT_TRUE(f->localSymbols.empty());
- EXPECT_TRUE(f->globalSymbols.empty());
- EXPECT_TRUE(f->undefinedSymbols.empty());
- EXPECT_EQ(f->sections.size(), 1UL);
- const Section& sect = f->sections[0];
- EXPECT_TRUE(sect.segmentName.equals("__TEXT"));
- EXPECT_TRUE(sect.sectionName.equals("__text"));
- EXPECT_EQ((uint32_t)(sect.type), (uint32_t)(llvm::MachO::S_REGULAR));
- EXPECT_EQ((uint32_t)(sect.attributes),
- (uint32_t)(llvm::MachO::S_ATTR_PURE_INSTRUCTIONS));
- EXPECT_EQ((uint16_t)sect.alignment, 2U);
- EXPECT_EQ((uint64_t)sect.address, 0x12345678ULL);
- EXPECT_EQ(sect.content.size(), 2UL);
- EXPECT_EQ((int)(sect.content[0]), 0x90);
- EXPECT_EQ((int)(sect.content[1]), 0x90);
-}
-
-TEST(ObjectFileYAML, hello_x86_64) {
- std::unique_ptr<NormalizedFile> f = fromYAML(
- "---\n"
- "arch: x86_64\n"
- "file-type: MH_OBJECT\n"
- "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n"
- "sections:\n"
- " - segment: __TEXT\n"
- " section: __text\n"
- " type: S_REGULAR\n"
- " attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS]\n"
- " alignment: 1\n"
- " address: 0x0000\n"
- " content: [ 0x55, 0x48, 0x89, 0xe5, 0x48, 0x8d, 0x3d, 0x00,\n"
- " 0x00, 0x00, 0x00, 0x30, 0xc0, 0xe8, 0x00, 0x00,\n"
- " 0x00, 0x00, 0x31, 0xc0, 0x5d, 0xc3 ]\n"
- " relocations:\n"
- " - offset: 0x0e\n"
- " type: X86_64_RELOC_BRANCH\n"
- " length: 2\n"
- " pc-rel: true\n"
- " extern: true\n"
- " symbol: 2\n"
- " - offset: 0x07\n"
- " type: X86_64_RELOC_SIGNED\n"
- " length: 2\n"
- " pc-rel: true\n"
- " extern: true\n"
- " symbol: 1\n"
- " - segment: __TEXT\n"
- " section: __cstring\n"
- " type: S_CSTRING_LITERALS\n"
- " attributes: [ ]\n"
- " alignment: 1\n"
- " address: 0x0016\n"
- " content: [ 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a, 0x00 ]\n"
- "global-symbols:\n"
- " - name: _main\n"
- " type: N_SECT\n"
- " scope: [ N_EXT ]\n"
- " sect: 1\n"
- " value: 0x0\n"
- "local-symbols:\n"
- " - name: L_.str\n"
- " type: N_SECT\n"
- " scope: [ ]\n"
- " sect: 2\n"
- " value: 0x16\n"
- "undefined-symbols:\n"
- " - name: _printf\n"
- " type: N_UNDF\n"
- " value: 0x0\n"
- "...\n");
- EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86_64);
- EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
- EXPECT_EQ((int)(f->flags), (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
- EXPECT_EQ(f->sections.size(), 2UL);
-
- const Section& sect1 = f->sections[0];
- EXPECT_TRUE(sect1.segmentName.equals("__TEXT"));
- EXPECT_TRUE(sect1.sectionName.equals("__text"));
- EXPECT_EQ((uint32_t)(sect1.type), (uint32_t)(llvm::MachO::S_REGULAR));
- EXPECT_EQ((uint32_t)(sect1.attributes),
- (uint32_t)(llvm::MachO::S_ATTR_PURE_INSTRUCTIONS
- | llvm::MachO::S_ATTR_SOME_INSTRUCTIONS));
- EXPECT_EQ((uint16_t)sect1.alignment, 1U);
- EXPECT_EQ((uint64_t)sect1.address, 0x0ULL);
- EXPECT_EQ(sect1.content.size(), 22UL);
- EXPECT_EQ((int)(sect1.content[0]), 0x55);
- EXPECT_EQ((int)(sect1.content[1]), 0x48);
- EXPECT_EQ(sect1.relocations.size(), 2UL);
- const Relocation& reloc1 = sect1.relocations[0];
- EXPECT_EQ(reloc1.offset, 0x0eU);
- EXPECT_FALSE(reloc1.scattered);
- EXPECT_EQ((int)reloc1.type, (int)llvm::MachO::X86_64_RELOC_BRANCH);
- EXPECT_EQ(reloc1.length, 2);
- EXPECT_TRUE(reloc1.pcRel);
- EXPECT_TRUE(reloc1.isExtern);
- EXPECT_EQ(reloc1.symbol, 2U);
- EXPECT_EQ((int)(reloc1.value), 0);
- const Relocation& reloc2 = sect1.relocations[1];
- EXPECT_EQ(reloc2.offset, 0x07U);
- EXPECT_FALSE(reloc2.scattered);
- EXPECT_EQ((int)reloc2.type, (int)llvm::MachO::X86_64_RELOC_SIGNED);
- EXPECT_EQ(reloc2.length, 2);
- EXPECT_TRUE(reloc2.pcRel);
- EXPECT_TRUE(reloc2.isExtern);
- EXPECT_EQ(reloc2.symbol, 1U);
- EXPECT_EQ((int)(reloc2.value), 0);
-
- const Section& sect2 = f->sections[1];
- EXPECT_TRUE(sect2.segmentName.equals("__TEXT"));
- EXPECT_TRUE(sect2.sectionName.equals("__cstring"));
- EXPECT_EQ((uint32_t)(sect2.type), (uint32_t)(llvm::MachO::S_CSTRING_LITERALS));
- EXPECT_EQ((uint32_t)(sect2.attributes), 0U);
- EXPECT_EQ((uint16_t)sect2.alignment, 1U);
- EXPECT_EQ((uint64_t)sect2.address, 0x016ULL);
- EXPECT_EQ(sect2.content.size(), 7UL);
- EXPECT_EQ((int)(sect2.content[0]), 0x68);
- EXPECT_EQ((int)(sect2.content[1]), 0x65);
- EXPECT_EQ((int)(sect2.content[2]), 0x6c);
-
- EXPECT_EQ(f->globalSymbols.size(), 1UL);
- const Symbol& sym1 = f->globalSymbols[0];
- EXPECT_TRUE(sym1.name.equals("_main"));
- EXPECT_EQ((int)(sym1.type), llvm::MachO::N_SECT);
- EXPECT_EQ((int)(sym1.scope), llvm::MachO::N_EXT);
- EXPECT_EQ(sym1.sect, 1);
- EXPECT_EQ((int)(sym1.desc), 0);
- EXPECT_EQ((uint64_t)sym1.value, 0x0ULL);
- EXPECT_EQ(f->localSymbols.size(), 1UL);
- const Symbol& sym2 = f->localSymbols[0];
- EXPECT_TRUE(sym2.name.equals("L_.str"));
- EXPECT_EQ((int)(sym2.type), llvm::MachO::N_SECT);
- EXPECT_EQ((int)(sym2.scope), 0);
- EXPECT_EQ(sym2.sect, 2);
- EXPECT_EQ((int)(sym2.desc), 0);
- EXPECT_EQ((uint64_t)sym2.value, 0x16ULL);
- EXPECT_EQ(f->undefinedSymbols.size(), 1UL);
- const Symbol& sym3 = f->undefinedSymbols[0];
- EXPECT_TRUE(sym3.name.equals("_printf"));
- EXPECT_EQ((int)(sym3.type), llvm::MachO::N_UNDF);
- EXPECT_EQ((int)(sym3.scope), 0);
- EXPECT_EQ(sym3.sect, 0);
- EXPECT_EQ((int)(sym3.desc), 0);
- EXPECT_EQ((uint64_t)sym3.value, 0x0ULL);
-}
-
-TEST(ObjectFileYAML, hello_x86) {
- std::unique_ptr<NormalizedFile> f = fromYAML(
- "---\n"
- "arch: x86\n"
- "file-type: MH_OBJECT\n"
- "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n"
- "sections:\n"
- " - segment: __TEXT\n"
- " section: __text\n"
- " type: S_REGULAR\n"
- " attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS]\n"
- " alignment: 1\n"
- " address: 0x0000\n"
- " content: [ 0x55, 0x89, 0xe5, 0x83, 0xec, 0x08, 0xe8, 0x00,\n"
- " 0x00, 0x00, 0x00, 0x58, 0x8d, 0x80, 0x16, 0x00,\n"
- " 0x00, 0x00, 0x89, 0x04, 0x24, 0xe8, 0xe6, 0xff,\n"
- " 0xff, 0xff, 0x31, 0xc0, 0x83, 0xc4, 0x08, 0x5d,\n"
- " 0xc3 ]\n"
- " relocations:\n"
- " - offset: 0x16\n"
- " type: GENERIC_RELOC_VANILLA\n"
- " length: 2\n"
- " pc-rel: true\n"
- " extern: true\n"
- " symbol: 1\n"
- " - offset: 0x0e\n"
- " scattered: true\n"
- " type: GENERIC_RELOC_LOCAL_SECTDIFF\n"
- " length: 2\n"
- " pc-rel: false\n"
- " value: 0x21\n"
- " - offset: 0x0\n"
- " scattered: true\n"
- " type: GENERIC_RELOC_PAIR\n"
- " length: 2\n"
- " pc-rel: false\n"
- " value: 0xb\n"
- " - segment: __TEXT\n"
- " section: __cstring\n"
- " type: S_CSTRING_LITERALS\n"
- " attributes: [ ]\n"
- " alignment: 1\n"
- " address: 0x0021\n"
- " content: [ 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a, 0x00 ]\n"
- "global-symbols:\n"
- " - name: _main\n"
- " type: N_SECT\n"
- " scope: [ N_EXT ]\n"
- " sect: 1\n"
- " value: 0x0\n"
- "undefined-symbols:\n"
- " - name: _printf\n"
- " type: N_UNDF\n"
- " value: 0x0\n"
- "...\n");
- EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86);
- EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
- EXPECT_EQ((int)(f->flags), (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
- EXPECT_EQ(f->sections.size(), 2UL);
-
- const Section& sect1 = f->sections[0];
- EXPECT_TRUE(sect1.segmentName.equals("__TEXT"));
- EXPECT_TRUE(sect1.sectionName.equals("__text"));
- EXPECT_EQ((uint32_t)(sect1.type), (uint32_t)(llvm::MachO::S_REGULAR));
- EXPECT_EQ((uint32_t)(sect1.attributes),
- (uint32_t)(llvm::MachO::S_ATTR_PURE_INSTRUCTIONS
- | llvm::MachO::S_ATTR_SOME_INSTRUCTIONS));
- EXPECT_EQ((uint16_t)sect1.alignment, 1U);
- EXPECT_EQ((uint64_t)sect1.address, 0x0ULL);
- EXPECT_EQ(sect1.content.size(), 33UL);
- EXPECT_EQ((int)(sect1.content[0]), 0x55);
- EXPECT_EQ((int)(sect1.content[1]), 0x89);
- EXPECT_EQ(sect1.relocations.size(), 3UL);
- const Relocation& reloc1 = sect1.relocations[0];
- EXPECT_EQ(reloc1.offset, 0x16U);
- EXPECT_FALSE(reloc1.scattered);
- EXPECT_EQ((int)reloc1.type, (int)llvm::MachO::GENERIC_RELOC_VANILLA);
- EXPECT_EQ(reloc1.length, 2);
- EXPECT_TRUE(reloc1.pcRel);
- EXPECT_TRUE(reloc1.isExtern);
- EXPECT_EQ(reloc1.symbol, 1U);
- EXPECT_EQ((int)(reloc1.value), 0);
- const Relocation& reloc2 = sect1.relocations[1];
- EXPECT_EQ(reloc2.offset, 0x0eU);
- EXPECT_TRUE(reloc2.scattered);
- EXPECT_EQ((int)reloc2.type, (int)llvm::MachO::GENERIC_RELOC_LOCAL_SECTDIFF);
- EXPECT_EQ(reloc2.length, 2);
- EXPECT_FALSE(reloc2.pcRel);
- EXPECT_EQ(reloc2.symbol, 0U);
- EXPECT_EQ((int)(reloc2.value), 0x21);
- const Relocation& reloc3 = sect1.relocations[2];
- EXPECT_EQ(reloc3.offset, 0U);
- EXPECT_TRUE(reloc3.scattered);
- EXPECT_EQ((int)reloc3.type, (int)llvm::MachO::GENERIC_RELOC_PAIR);
- EXPECT_EQ(reloc3.length, 2);
- EXPECT_FALSE(reloc3.pcRel);
- EXPECT_EQ(reloc3.symbol, 0U);
- EXPECT_EQ((int)(reloc3.value), 0xb);
-
- const Section& sect2 = f->sections[1];
- EXPECT_TRUE(sect2.segmentName.equals("__TEXT"));
- EXPECT_TRUE(sect2.sectionName.equals("__cstring"));
- EXPECT_EQ((uint32_t)(sect2.type), (uint32_t)(llvm::MachO::S_CSTRING_LITERALS));
- EXPECT_EQ((uint32_t)(sect2.attributes), 0U);
- EXPECT_EQ((uint16_t)sect2.alignment, 1U);
- EXPECT_EQ((uint64_t)sect2.address, 0x021ULL);
- EXPECT_EQ(sect2.content.size(), 7UL);
- EXPECT_EQ((int)(sect2.content[0]), 0x68);
- EXPECT_EQ((int)(sect2.content[1]), 0x65);
- EXPECT_EQ((int)(sect2.content[2]), 0x6c);
-
- EXPECT_EQ(f->globalSymbols.size(), 1UL);
- const Symbol& sym1 = f->globalSymbols[0];
- EXPECT_TRUE(sym1.name.equals("_main"));
- EXPECT_EQ((int)(sym1.type), llvm::MachO::N_SECT);
- EXPECT_EQ((int)(sym1.scope), llvm::MachO::N_EXT);
- EXPECT_EQ(sym1.sect, 1);
- EXPECT_EQ((int)(sym1.desc), 0);
- EXPECT_EQ((uint64_t)sym1.value, 0x0ULL);
- EXPECT_EQ(f->undefinedSymbols.size(), 1UL);
- const Symbol& sym2 = f->undefinedSymbols[0];
- EXPECT_TRUE(sym2.name.equals("_printf"));
- EXPECT_EQ((int)(sym2.type), llvm::MachO::N_UNDF);
- EXPECT_EQ((int)(sym2.scope), 0);
- EXPECT_EQ(sym2.sect, 0);
- EXPECT_EQ((int)(sym2.desc), 0);
- EXPECT_EQ((uint64_t)sym2.value, 0x0ULL);
-}
-
-TEST(ObjectFileYAML, hello_armv6) {
- std::unique_ptr<NormalizedFile> f = fromYAML(
- "---\n"
- "arch: armv6\n"
- "file-type: MH_OBJECT\n"
- "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n"
- "sections:\n"
- " - segment: __TEXT\n"
- " section: __text\n"
- " type: S_REGULAR\n"
- " attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS]\n"
- " alignment: 4\n"
- " address: 0x0000\n"
- " content: [ 0x80, 0x40, 0x2d, 0xe9, 0x10, 0x00, 0x9f, 0xe5,\n"
- " 0x0d, 0x70, 0xa0, 0xe1, 0x00, 0x00, 0x8f, 0xe0,\n"
- " 0xfa, 0xff, 0xff, 0xeb, 0x00, 0x00, 0xa0, 0xe3,\n"
- " 0x80, 0x80, 0xbd, 0xe8, 0x0c, 0x00, 0x00, 0x00 ]\n"
- " relocations:\n"
- " - offset: 0x1c\n"
- " scattered: true\n"
- " type: ARM_RELOC_SECTDIFF\n"
- " length: 2\n"
- " pc-rel: false\n"
- " value: 0x20\n"
- " - offset: 0x0\n"
- " scattered: true\n"
- " type: ARM_RELOC_PAIR\n"
- " length: 2\n"
- " pc-rel: false\n"
- " value: 0xc\n"
- " - offset: 0x10\n"
- " type: ARM_RELOC_BR24\n"
- " length: 2\n"
- " pc-rel: true\n"
- " extern: true\n"
- " symbol: 1\n"
- " - segment: __TEXT\n"
- " section: __cstring\n"
- " type: S_CSTRING_LITERALS\n"
- " attributes: [ ]\n"
- " alignment: 1\n"
- " address: 0x0020\n"
- " content: [ 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a, 0x00 ]\n"
- "global-symbols:\n"
- " - name: _main\n"
- " type: N_SECT\n"
- " scope: [ N_EXT ]\n"
- " sect: 1\n"
- " value: 0x0\n"
- "undefined-symbols:\n"
- " - name: _printf\n"
- " type: N_UNDF\n"
- " value: 0x0\n"
- "...\n");
- EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_armv6);
- EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
- EXPECT_EQ((int)(f->flags), (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
- EXPECT_EQ(f->sections.size(), 2UL);
-
- const Section& sect1 = f->sections[0];
- EXPECT_TRUE(sect1.segmentName.equals("__TEXT"));
- EXPECT_TRUE(sect1.sectionName.equals("__text"));
- EXPECT_EQ((uint32_t)(sect1.type), (uint32_t)(llvm::MachO::S_REGULAR));
- EXPECT_EQ((uint32_t)(sect1.attributes),
- (uint32_t)(llvm::MachO::S_ATTR_PURE_INSTRUCTIONS
- | llvm::MachO::S_ATTR_SOME_INSTRUCTIONS));
- EXPECT_EQ((uint16_t)sect1.alignment, 4U);
- EXPECT_EQ((uint64_t)sect1.address, 0x0ULL);
- EXPECT_EQ(sect1.content.size(), 32UL);
- EXPECT_EQ((int)(sect1.content[0]), 0x80);
- EXPECT_EQ((int)(sect1.content[1]), 0x40);
- EXPECT_EQ(sect1.relocations.size(), 3UL);
- const Relocation& reloc1 = sect1.relocations[0];
- EXPECT_EQ(reloc1.offset, 0x1cU);
- EXPECT_TRUE(reloc1.scattered);
- EXPECT_EQ((int)reloc1.type, (int)llvm::MachO::ARM_RELOC_SECTDIFF);
- EXPECT_EQ(reloc1.length, 2);
- EXPECT_FALSE(reloc1.pcRel);
- EXPECT_EQ(reloc1.symbol, 0U);
- EXPECT_EQ((int)(reloc1.value), 0x20);
- const Relocation& reloc2 = sect1.relocations[1];
- EXPECT_EQ(reloc2.offset, 0x0U);
- EXPECT_TRUE(reloc2.scattered);
- EXPECT_EQ((int)reloc2.type, (int)llvm::MachO::ARM_RELOC_PAIR);
- EXPECT_EQ(reloc2.length, 2);
- EXPECT_FALSE(reloc2.pcRel);
- EXPECT_EQ(reloc2.symbol, 0U);
- EXPECT_EQ((int)(reloc2.value), 0xc);
- const Relocation& reloc3 = sect1.relocations[2];
- EXPECT_EQ(reloc3.offset, 0x10U);
- EXPECT_FALSE(reloc3.scattered);
- EXPECT_EQ((int)reloc3.type, (int)llvm::MachO::ARM_RELOC_BR24);
- EXPECT_EQ(reloc3.length, 2);
- EXPECT_TRUE(reloc3.pcRel);
- EXPECT_TRUE(reloc3.isExtern);
- EXPECT_EQ(reloc3.symbol, 1U);
- EXPECT_EQ((int)(reloc3.value), 0);
-
- const Section& sect2 = f->sections[1];
- EXPECT_TRUE(sect2.segmentName.equals("__TEXT"));
- EXPECT_TRUE(sect2.sectionName.equals("__cstring"));
- EXPECT_EQ((uint32_t)(sect2.type), (uint32_t)(llvm::MachO::S_CSTRING_LITERALS));
- EXPECT_EQ((uint32_t)(sect2.attributes), 0U);
- EXPECT_EQ((uint16_t)sect2.alignment, 1U);
- EXPECT_EQ((uint64_t)sect2.address, 0x020ULL);
- EXPECT_EQ(sect2.content.size(), 7UL);
- EXPECT_EQ((int)(sect2.content[0]), 0x68);
- EXPECT_EQ((int)(sect2.content[1]), 0x65);
- EXPECT_EQ((int)(sect2.content[2]), 0x6c);
-
- EXPECT_EQ(f->globalSymbols.size(), 1UL);
- const Symbol& sym1 = f->globalSymbols[0];
- EXPECT_TRUE(sym1.name.equals("_main"));
- EXPECT_EQ((int)(sym1.type), llvm::MachO::N_SECT);
- EXPECT_EQ((int)(sym1.scope), llvm::MachO::N_EXT);
- EXPECT_EQ(sym1.sect, 1);
- EXPECT_EQ((int)(sym1.desc), 0);
- EXPECT_EQ((uint64_t)sym1.value, 0x0ULL);
- EXPECT_EQ(f->undefinedSymbols.size(), 1UL);
- const Symbol& sym2 = f->undefinedSymbols[0];
- EXPECT_TRUE(sym2.name.equals("_printf"));
- EXPECT_EQ((int)(sym2.type), llvm::MachO::N_UNDF);
- EXPECT_EQ((int)(sym2.scope), 0);
- EXPECT_EQ(sym2.sect, 0);
- EXPECT_EQ((int)(sym2.desc), 0);
- EXPECT_EQ((uint64_t)sym2.value, 0x0ULL);
-}
-
-TEST(ObjectFileYAML, hello_armv7) {
- std::unique_ptr<NormalizedFile> f = fromYAML(
- "---\n"
- "arch: armv7\n"
- "file-type: MH_OBJECT\n"
- "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n"
- "sections:\n"
- " - segment: __TEXT\n"
- " section: __text\n"
- " type: S_REGULAR\n"
- " attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS]\n"
- " alignment: 2\n"
- " address: 0x0000\n"
- " content: [ 0x80, 0xb5, 0x40, 0xf2, 0x06, 0x00, 0x6f, 0x46,\n"
- " 0xc0, 0xf2, 0x00, 0x00, 0x78, 0x44, 0xff, 0xf7,\n"
- " 0xf8, 0xef, 0x00, 0x20, 0x80, 0xbd ]\n"
- " relocations:\n"
- " - offset: 0x0e\n"
- " type: ARM_THUMB_RELOC_BR22\n"
- " length: 2\n"
- " pc-rel: true\n"
- " extern: true\n"
- " symbol: 1\n"
- " - offset: 0x08\n"
- " scattered: true\n"
- " type: ARM_RELOC_HALF_SECTDIFF\n"
- " length: 3\n"
- " pc-rel: false\n"
- " value: 0x16\n"
- " - offset: 0x06\n"
- " scattered: true\n"
- " type: ARM_RELOC_PAIR\n"
- " length: 3\n"
- " pc-rel: false\n"
- " value: 0xc\n"
- " - offset: 0x02\n"
- " scattered: true\n"
- " type: ARM_RELOC_HALF_SECTDIFF\n"
- " length: 2\n"
- " pc-rel: false\n"
- " value: 0x16\n"
- " - offset: 0x0\n"
- " scattered: true\n"
- " type: ARM_RELOC_PAIR\n"
- " length: 2\n"
- " pc-rel: false\n"
- " value: 0xc\n"
- " - segment: __TEXT\n"
- " section: __cstring\n"
- " type: S_CSTRING_LITERALS\n"
- " attributes: [ ]\n"
- " alignment: 1\n"
- " address: 0x0016\n"
- " content: [ 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a, 0x00 ]\n"
- "global-symbols:\n"
- " - name: _main\n"
- " type: N_SECT\n"
- " scope: [ N_EXT ]\n"
- " sect: 1\n"
- " desc: [ N_ARM_THUMB_DEF ]\n"
- " value: 0x0\n"
- "undefined-symbols:\n"
- " - name: _printf\n"
- " type: N_UNDF\n"
- " value: 0x0\n"
- "...\n");
- EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_armv7);
- EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
- EXPECT_EQ((int)(f->flags), (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
- EXPECT_EQ(f->sections.size(), 2UL);
-
- const Section& sect1 = f->sections[0];
- EXPECT_TRUE(sect1.segmentName.equals("__TEXT"));
- EXPECT_TRUE(sect1.sectionName.equals("__text"));
- EXPECT_EQ((uint32_t)(sect1.type), (uint32_t)(llvm::MachO::S_REGULAR));
- EXPECT_EQ((uint32_t)(sect1.attributes),
- (uint32_t)(llvm::MachO::S_ATTR_PURE_INSTRUCTIONS
- | llvm::MachO::S_ATTR_SOME_INSTRUCTIONS));
- EXPECT_EQ((uint16_t)sect1.alignment, 2U);
- EXPECT_EQ((uint64_t)sect1.address, 0x0ULL);
- EXPECT_EQ(sect1.content.size(), 22UL);
- EXPECT_EQ((int)(sect1.content[0]), 0x80);
- EXPECT_EQ((int)(sect1.content[1]), 0xb5);
- EXPECT_EQ(sect1.relocations.size(), 5UL);
- const Relocation& reloc1 = sect1.relocations[0];
- EXPECT_EQ(reloc1.offset, 0x0eU);
- EXPECT_FALSE(reloc1.scattered);
- EXPECT_EQ((int)reloc1.type, (int)llvm::MachO::ARM_THUMB_RELOC_BR22);
- EXPECT_EQ(reloc1.length, 2);
- EXPECT_TRUE(reloc1.pcRel);
- EXPECT_TRUE(reloc1.isExtern);
- EXPECT_EQ(reloc1.symbol, 1U);
- EXPECT_EQ((int)(reloc1.value), 0);
- const Relocation& reloc2 = sect1.relocations[1];
- EXPECT_EQ(reloc2.offset, 0x8U);
- EXPECT_TRUE(reloc2.scattered);
- EXPECT_EQ((int)reloc2.type, (int)llvm::MachO::ARM_RELOC_HALF_SECTDIFF);
- EXPECT_EQ(reloc2.length, 3);
- EXPECT_FALSE(reloc2.pcRel);
- EXPECT_EQ(reloc2.symbol, 0U);
- EXPECT_EQ((int)(reloc2.value), 0x16);
- const Relocation& reloc3 = sect1.relocations[2];
- EXPECT_EQ(reloc3.offset, 0x6U);
- EXPECT_TRUE(reloc3.scattered);
- EXPECT_EQ((int)reloc3.type, (int)llvm::MachO::ARM_RELOC_PAIR);
- EXPECT_EQ(reloc3.length, 3);
- EXPECT_FALSE(reloc3.pcRel);
- EXPECT_EQ(reloc3.symbol, 0U);
- EXPECT_EQ((int)(reloc3.value), 0xc);
- const Relocation& reloc4 = sect1.relocations[3];
- EXPECT_EQ(reloc4.offset, 0x2U);
- EXPECT_TRUE(reloc4.scattered);
- EXPECT_EQ((int)reloc4.type, (int)llvm::MachO::ARM_RELOC_HALF_SECTDIFF);
- EXPECT_EQ(reloc4.length, 2);
- EXPECT_FALSE(reloc4.pcRel);
- EXPECT_EQ(reloc4.symbol, 0U);
- EXPECT_EQ((int)(reloc4.value), 0x16);
- const Relocation& reloc5 = sect1.relocations[4];
- EXPECT_EQ(reloc5.offset, 0x0U);
- EXPECT_TRUE(reloc5.scattered);
- EXPECT_EQ((int)reloc5.type, (int)llvm::MachO::ARM_RELOC_PAIR);
- EXPECT_EQ(reloc5.length, 2);
- EXPECT_FALSE(reloc5.pcRel);
- EXPECT_EQ(reloc5.symbol, 0U);
- EXPECT_EQ((int)(reloc5.value), 0xc);
-
- const Section& sect2 = f->sections[1];
- EXPECT_TRUE(sect2.segmentName.equals("__TEXT"));
- EXPECT_TRUE(sect2.sectionName.equals("__cstring"));
- EXPECT_EQ((uint32_t)(sect2.type), (uint32_t)(llvm::MachO::S_CSTRING_LITERALS));
- EXPECT_EQ((uint32_t)(sect2.attributes), 0U);
- EXPECT_EQ((uint16_t)sect2.alignment, 1U);
- EXPECT_EQ((uint64_t)sect2.address, 0x016ULL);
- EXPECT_EQ(sect2.content.size(), 7UL);
- EXPECT_EQ((int)(sect2.content[0]), 0x68);
- EXPECT_EQ((int)(sect2.content[1]), 0x65);
- EXPECT_EQ((int)(sect2.content[2]), 0x6c);
-
- EXPECT_EQ(f->globalSymbols.size(), 1UL);
- const Symbol& sym1 = f->globalSymbols[0];
- EXPECT_TRUE(sym1.name.equals("_main"));
- EXPECT_EQ((int)(sym1.type), llvm::MachO::N_SECT);
- EXPECT_EQ((int)(sym1.scope), llvm::MachO::N_EXT);
- EXPECT_EQ(sym1.sect, 1);
- EXPECT_EQ((int)(sym1.desc), (int)(llvm::MachO::N_ARM_THUMB_DEF));
- EXPECT_EQ((uint64_t)sym1.value, 0x0ULL);
- EXPECT_EQ(f->undefinedSymbols.size(), 1UL);
- const Symbol& sym2 = f->undefinedSymbols[0];
- EXPECT_TRUE(sym2.name.equals("_printf"));
- EXPECT_EQ((int)(sym2.type), llvm::MachO::N_UNDF);
- EXPECT_EQ((int)(sym2.scope), 0);
- EXPECT_EQ(sym2.sect, 0);
- EXPECT_EQ((int)(sym2.desc), 0);
- EXPECT_EQ((uint64_t)sym2.value, 0x0ULL);
-}
+++ /dev/null
-0xca, 0xfe, 0xba, 0xbe, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x07, 0x00,
-0x00, 0x00, 0x03, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00,
-0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x40,
-0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0xcf, 0xfa, 0xed, 0xfe, 0x07, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00,
-0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00,
-0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x98, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00,
-0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x5f, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0xce, 0xfa, 0xed, 0xfe, 0x0c, 0x00, 0x00, 0x00, 0x09,
-0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7c, 0x00,
-0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00,
-0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x74,
-0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00