aboutsummaryrefslogtreecommitdiff
path: root/lld
diff options
context:
space:
mode:
Diffstat (limited to 'lld')
-rw-r--r--lld/COFF/Chunks.cpp22
-rw-r--r--lld/COFF/Chunks.h32
-rw-r--r--lld/COFF/Config.h3
-rw-r--r--lld/COFF/Driver.cpp88
-rw-r--r--lld/COFF/Driver.h2
-rw-r--r--lld/COFF/DriverUtils.cpp16
-rw-r--r--lld/COFF/MarkLive.cpp5
-rw-r--r--lld/COFF/Options.td4
-rw-r--r--lld/COFF/SymbolTable.cpp42
-rw-r--r--lld/COFF/SymbolTable.h6
-rw-r--r--lld/COFF/Symbols.cpp8
-rw-r--r--lld/COFF/Symbols.h4
-rw-r--r--lld/COFF/Writer.cpp29
-rw-r--r--lld/ELF/Arch/LoongArch.cpp9
-rw-r--r--lld/ELF/Arch/Mips.cpp4
-rw-r--r--lld/ELF/Config.h2
-rw-r--r--lld/ELF/Driver.cpp1
-rw-r--r--lld/ELF/InputSection.cpp5
-rw-r--r--lld/ELF/Relocations.cpp29
-rw-r--r--lld/ELF/Relocations.h1
-rw-r--r--lld/ELF/SyntheticSections.cpp56
-rw-r--r--lld/ELF/SyntheticSections.h71
-rw-r--r--lld/ELF/Target.cpp3
-rw-r--r--lld/ELF/Target.h1
-rw-r--r--lld/ELF/Writer.cpp5
-rw-r--r--lld/test/COFF/arm64x-sameaddress.test107
-rw-r--r--lld/test/ELF/loongarch-relax-pc-hi20-lo12.s59
27 files changed, 426 insertions, 188 deletions
diff --git a/lld/COFF/Chunks.cpp b/lld/COFF/Chunks.cpp
index 01752cd..39fc250 100644
--- a/lld/COFF/Chunks.cpp
+++ b/lld/COFF/Chunks.cpp
@@ -422,12 +422,6 @@ void SectionChunk::writeTo(uint8_t *buf) const {
applyRelocation(buf + rel.VirtualAddress, rel);
}
-
- // Write the offset to EC entry thunk preceding section contents. The low bit
- // is always set, so it's effectively an offset from the last byte of the
- // offset.
- if (Defined *entryThunk = getEntryThunk())
- write32le(buf - sizeof(uint32_t), entryThunk->getRVA() - rva + 1);
}
void SectionChunk::applyRelocation(uint8_t *off,
@@ -881,6 +875,19 @@ void RangeExtensionThunkARM64::writeTo(uint8_t *buf) const {
applyArm64Imm(buf + 4, target->getRVA() & 0xfff, 0);
}
+void SameAddressThunkARM64EC::setDynamicRelocs(COFFLinkerContext &ctx) const {
+ // Add ARM64X relocations replacing adrp/add instructions with a version using
+ // the hybrid target.
+ RangeExtensionThunkARM64 hybridView(ARM64EC, hybridTarget);
+ uint8_t buf[sizeof(arm64Thunk)];
+ hybridView.setRVA(rva);
+ hybridView.writeTo(buf);
+ uint32_t addrp = *reinterpret_cast<ulittle32_t *>(buf);
+ uint32_t add = *reinterpret_cast<ulittle32_t *>(buf + sizeof(uint32_t));
+ ctx.dynamicRelocs->set(this, addrp);
+ ctx.dynamicRelocs->set(Arm64XRelocVal(this, sizeof(uint32_t)), add);
+}
+
LocalImportChunk::LocalImportChunk(COFFLinkerContext &c, Defined *s)
: sym(s), ctx(c) {
setAlignment(ctx.config.wordsize);
@@ -1264,7 +1271,8 @@ void DynamicRelocsChunk::finalize() {
}
// Set the reloc value. The reloc entry must be allocated beforehand.
-void DynamicRelocsChunk::set(uint32_t rva, Arm64XRelocVal value) {
+void DynamicRelocsChunk::set(Arm64XRelocVal offset, Arm64XRelocVal value) {
+ uint32_t rva = offset.get();
auto entry =
llvm::find_if(arm64xRelocs, [rva](const Arm64XDynamicRelocEntry &e) {
return e.offset.get() == rva;
diff --git a/lld/COFF/Chunks.h b/lld/COFF/Chunks.h
index d03a64c..6d88f5e 100644
--- a/lld/COFF/Chunks.h
+++ b/lld/COFF/Chunks.h
@@ -193,6 +193,8 @@ public:
// allowed ranges. Return the additional space required for the extension.
virtual uint32_t extendRanges() { return 0; };
+ virtual Defined *getEntryThunk() const { return nullptr; };
+
static bool classof(const Chunk *c) { return c->kind() >= OtherKind; }
protected:
@@ -633,7 +635,7 @@ public:
bool verifyRanges() override;
uint32_t extendRanges() override;
- Defined *exitThunk;
+ Defined *exitThunk = nullptr;
Defined *sym = nullptr;
bool extended = false;
@@ -675,6 +677,26 @@ private:
MachineTypes machine;
};
+// A chunk used to guarantee the same address for a function in both views of
+// a hybrid image. Similar to RangeExtensionThunkARM64 chunks, it calls the
+// target symbol using a BR instruction. It also contains an entry thunk for EC
+// compatibility and additional ARM64X relocations that swap targets between
+// views.
+class SameAddressThunkARM64EC : public RangeExtensionThunkARM64 {
+public:
+ explicit SameAddressThunkARM64EC(Defined *t, Defined *hybridTarget,
+ Defined *entryThunk)
+ : RangeExtensionThunkARM64(ARM64EC, t), hybridTarget(hybridTarget),
+ entryThunk(entryThunk) {}
+
+ Defined *getEntryThunk() const override { return entryThunk; }
+ void setDynamicRelocs(COFFLinkerContext &ctx) const;
+
+private:
+ Defined *hybridTarget;
+ Defined *entryThunk;
+};
+
// Windows-specific.
// See comments for DefinedLocalImport class.
class LocalImportChunk : public NonSectionChunk {
@@ -843,13 +865,13 @@ class Arm64XRelocVal {
public:
Arm64XRelocVal(uint64_t value = 0) : value(value) {}
Arm64XRelocVal(Defined *sym, int32_t offset = 0) : sym(sym), value(offset) {}
- Arm64XRelocVal(Chunk *chunk, int32_t offset = 0)
+ Arm64XRelocVal(const Chunk *chunk, int32_t offset = 0)
: chunk(chunk), value(offset) {}
uint64_t get() const;
private:
Defined *sym = nullptr;
- Chunk *chunk = nullptr;
+ const Chunk *chunk = nullptr;
uint64_t value;
};
@@ -884,7 +906,7 @@ public:
arm64xRelocs.emplace_back(type, size, offset, value);
}
- void set(uint32_t rva, Arm64XRelocVal value);
+ void set(Arm64XRelocVal offset, Arm64XRelocVal value);
private:
std::vector<Arm64XDynamicRelocEntry> arm64xRelocs;
@@ -940,6 +962,8 @@ inline bool Chunk::isHotPatchable() const {
inline Defined *Chunk::getEntryThunk() const {
if (auto *c = dyn_cast<const SectionChunkEC>(this))
return c->entryThunk;
+ if (auto *c = dyn_cast<const NonSectionChunk>(this))
+ return c->getEntryThunk();
return nullptr;
}
diff --git a/lld/COFF/Config.h b/lld/COFF/Config.h
index 91b6e63..a03bb57 100644
--- a/lld/COFF/Config.h
+++ b/lld/COFF/Config.h
@@ -223,6 +223,9 @@ struct Configuration {
StringRef manifestUIAccess = "'false'";
StringRef manifestFile;
+ // used for /arm64xsameaddress
+ std::vector<std::pair<Symbol *, Symbol *>> sameAddresses;
+
// used for /dwodir
StringRef dwoDir;
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 570b8f9..7580b469 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -500,7 +500,9 @@ void LinkerDriver::parseDirectives(InputFile *file) {
file->symtab.parseAlternateName(arg->getValue());
break;
case OPT_arm64xsameaddress:
- if (!file->symtab.isEC())
+ if (file->symtab.isEC())
+ parseSameAddress(arg->getValue());
+ else
Warn(ctx) << arg->getSpelling()
<< " is not allowed in non-ARM64EC files (" << toString(file)
<< ")";
@@ -1318,13 +1320,9 @@ void LinkerDriver::convertResources() {
}
void LinkerDriver::maybeCreateECExportThunk(StringRef name, Symbol *&sym) {
- Defined *def;
if (!sym)
return;
- if (auto undef = dyn_cast<Undefined>(sym))
- def = undef->getDefinedWeakAlias();
- else
- def = dyn_cast<Defined>(sym);
+ Defined *def = sym->getDefined();
if (!def)
return;
@@ -1356,11 +1354,7 @@ void LinkerDriver::createECExportThunks() {
Symbol *sym = ctx.symtab.find(targetName);
if (!sym)
continue;
- Defined *targetSym;
- if (auto undef = dyn_cast<Undefined>(sym))
- targetSym = undef->getDefinedWeakAlias();
- else
- targetSym = dyn_cast<Defined>(sym);
+ Defined *targetSym = sym->getDefined();
if (!targetSym)
continue;
@@ -2303,6 +2297,13 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
args.filtered(OPT_dependentloadflag, OPT_dependentloadflag_opt))
parseDependentLoadFlags(arg);
+ for (auto *arg : args.filtered(OPT_arm64xsameaddress)) {
+ if (ctx.hybridSymtab)
+ parseSameAddress(arg->getValue());
+ else
+ Warn(ctx) << arg->getSpelling() << " is allowed only on EC targets";
+ }
+
if (tar) {
llvm::TimeTraceScope timeScope("Reproducer: response file");
tar->append(
@@ -2676,12 +2677,46 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
createECExportThunks();
// Resolve remaining undefined symbols and warn about imported locals.
+ std::vector<Undefined *> aliases;
ctx.forEachSymtab(
- [&](SymbolTable &symtab) { symtab.resolveRemainingUndefines(); });
+ [&](SymbolTable &symtab) { symtab.resolveRemainingUndefines(aliases); });
if (errorCount())
return;
+ ctx.forEachActiveSymtab([](SymbolTable &symtab) {
+ symtab.initializeECThunks();
+ symtab.initializeLoadConfig();
+ });
+
+ // Identify unreferenced COMDAT sections.
+ if (config->doGC) {
+ if (config->mingw) {
+ // markLive doesn't traverse .eh_frame, but the personality function is
+ // only reached that way. The proper solution would be to parse and
+ // traverse the .eh_frame section, like the ELF linker does.
+ // For now, just manually try to retain the known possible personality
+ // functions. This doesn't bring in more object files, but only marks
+ // functions that already have been included to be retained.
+ ctx.forEachSymtab([&](SymbolTable &symtab) {
+ for (const char *n : {"__gxx_personality_v0", "__gcc_personality_v0",
+ "rust_eh_personality"}) {
+ Defined *d = dyn_cast_or_null<Defined>(symtab.findUnderscore(n));
+ if (d && !d->isGCRoot) {
+ d->isGCRoot = true;
+ config->gcroot.push_back(d);
+ }
+ }
+ });
+ }
+
+ markLive(ctx);
+ }
+
+ ctx.symtab.initializeSameAddressThunks();
+ for (auto alias : aliases)
+ alias->resolveWeakAlias();
+
if (config->mingw) {
// Make sure the crtend.o object is the last object file. This object
// file can contain terminating section chunks that need to be placed
@@ -2773,35 +2808,6 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
if (auto *arg = args.getLastArg(OPT_print_symbol_order))
config->printSymbolOrder = arg->getValue();
- if (ctx.symtab.isEC())
- ctx.symtab.initializeECThunks();
- ctx.forEachActiveSymtab(
- [](SymbolTable &symtab) { symtab.initializeLoadConfig(); });
-
- // Identify unreferenced COMDAT sections.
- if (config->doGC) {
- if (config->mingw) {
- // markLive doesn't traverse .eh_frame, but the personality function is
- // only reached that way. The proper solution would be to parse and
- // traverse the .eh_frame section, like the ELF linker does.
- // For now, just manually try to retain the known possible personality
- // functions. This doesn't bring in more object files, but only marks
- // functions that already have been included to be retained.
- ctx.forEachSymtab([&](SymbolTable &symtab) {
- for (const char *n : {"__gxx_personality_v0", "__gcc_personality_v0",
- "rust_eh_personality"}) {
- Defined *d = dyn_cast_or_null<Defined>(symtab.findUnderscore(n));
- if (d && !d->isGCRoot) {
- d->isGCRoot = true;
- config->gcroot.push_back(d);
- }
- }
- });
- }
-
- markLive(ctx);
- }
-
// Needs to happen after the last call to addFile().
convertResources();
diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h
index 5a9bd5c..b500ac8 100644
--- a/lld/COFF/Driver.h
+++ b/lld/COFF/Driver.h
@@ -214,6 +214,8 @@ private:
void parsePDBPageSize(StringRef);
void parseSection(StringRef);
+ void parseSameAddress(StringRef);
+
// Parses a MS-DOS stub file
void parseDosStub(StringRef path);
diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp
index d8b41c7..dc4039f 100644
--- a/lld/COFF/DriverUtils.cpp
+++ b/lld/COFF/DriverUtils.cpp
@@ -328,6 +328,22 @@ void LinkerDriver::parseSwaprun(StringRef arg) {
} while (!arg.empty());
}
+void LinkerDriver::parseSameAddress(StringRef arg) {
+ auto mangledName = getArm64ECMangledFunctionName(arg);
+ Symbol *sym = ctx.symtab.addUndefined(mangledName ? *mangledName : arg);
+
+ // MSVC appears to generate thunks even for non-hybrid ARM64EC images.
+ // As a side effect, the native symbol is pulled in. Since this is used
+ // in the CRT for thread-local constructors, it results in the image
+ // containing unnecessary native code. As these thunks don't appear to
+ // be useful, we limit this behavior to actual hybrid targets. This may
+ // change if compatibility becomes necessary.
+ if (ctx.config.machine != ARM64X)
+ return;
+ Symbol *nativeSym = ctx.hybridSymtab->addUndefined(arg);
+ ctx.config.sameAddresses.emplace_back(sym, nativeSym);
+}
+
// An RAII temporary file class that automatically removes a temporary file.
namespace {
class TemporaryFile {
diff --git a/lld/COFF/MarkLive.cpp b/lld/COFF/MarkLive.cpp
index f40810c..78f5030 100644
--- a/lld/COFF/MarkLive.cpp
+++ b/lld/COFF/MarkLive.cpp
@@ -49,7 +49,10 @@ void markLive(COFFLinkerContext &ctx) {
addSym(file->impchkThunk->exitThunk);
};
- addSym = [&](Symbol *b) {
+ addSym = [&](Symbol *s) {
+ Defined *b = s->getDefined();
+ if (!b)
+ return;
if (auto *sym = dyn_cast<DefinedRegular>(b)) {
enqueue(sym->getChunk());
} else if (auto *sym = dyn_cast<DefinedImportData>(b)) {
diff --git a/lld/COFF/Options.td b/lld/COFF/Options.td
index 0d66b49..2c393cc 100644
--- a/lld/COFF/Options.td
+++ b/lld/COFF/Options.td
@@ -31,6 +31,9 @@ multiclass B_priv<string name> {
def align : P<"align", "Section alignment">;
def aligncomm : P<"aligncomm", "Set common symbol alignment">;
def alternatename : P<"alternatename", "Define weak alias">;
+def arm64xsameaddress
+ : P<"arm64xsameaddress", "Generate a thunk for the symbol with the same "
+ "address in both native and EC views on ARM64X.">;
def base : P<"base", "Base address of the program">;
def color_diagnostics: Flag<["--"], "color-diagnostics">,
HelpText<"Alias for --color-diagnostics=always">;
@@ -373,4 +376,3 @@ def tlbid : P_priv<"tlbid">;
def tlbout : P_priv<"tlbout">;
def verbose_all : P_priv<"verbose">;
def guardsym : P_priv<"guardsym">;
-def arm64xsameaddress : P_priv<"arm64xsameaddress">;
diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index 189e75d..de04cdf 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -452,7 +452,7 @@ void SymbolTable::reportUnresolvable() {
reportProblemSymbols(undefs, /*localImports=*/nullptr, true);
}
-void SymbolTable::resolveRemainingUndefines() {
+void SymbolTable::resolveRemainingUndefines(std::vector<Undefined *> &aliases) {
llvm::TimeTraceScope timeScope("Resolve remaining undefined symbols");
SmallPtrSet<Symbol *, 8> undefs;
DenseMap<Symbol *, Symbol *> localImports;
@@ -468,8 +468,10 @@ void SymbolTable::resolveRemainingUndefines() {
StringRef name = undef->getName();
// A weak alias may have been resolved, so check for that.
- if (undef->resolveWeakAlias())
+ if (undef->getWeakAlias()) {
+ aliases.push_back(undef);
continue;
+ }
// If we can resolve a symbol by removing __imp_ prefix, do that.
// This odd rule is for compatibility with MSVC linker.
@@ -620,10 +622,10 @@ void SymbolTable::initializeECThunks() {
return;
for (auto it : entryThunks) {
- auto *to = dyn_cast<Defined>(it.second);
+ Defined *to = it.second->getDefined();
if (!to)
continue;
- auto *from = dyn_cast<DefinedRegular>(it.first);
+ auto *from = dyn_cast_or_null<DefinedRegular>(it.first->getDefined());
// We need to be able to add padding to the function and fill it with an
// offset to its entry thunks. To ensure that padding the function is
// feasible, functions are required to be COMDAT symbols with no offset.
@@ -642,7 +644,8 @@ void SymbolTable::initializeECThunks() {
Symbol *sym = exitThunks.lookup(file->thunkSym);
if (!sym)
sym = exitThunks.lookup(file->impECSym);
- file->impchkThunk->exitThunk = dyn_cast_or_null<Defined>(sym);
+ if (sym)
+ file->impchkThunk->exitThunk = sym->getDefined();
}
// On ARM64EC, the __imp_ symbol references the auxiliary IAT, while the
@@ -659,6 +662,35 @@ void SymbolTable::initializeECThunks() {
});
}
+void SymbolTable::initializeSameAddressThunks() {
+ for (auto iter : ctx.config.sameAddresses) {
+ auto sym = dyn_cast_or_null<DefinedRegular>(iter.first->getDefined());
+ if (!sym || !sym->isLive())
+ continue;
+ auto nativeSym =
+ dyn_cast_or_null<DefinedRegular>(iter.second->getDefined());
+ if (!nativeSym || !nativeSym->isLive())
+ continue;
+ Defined *entryThunk = sym->getChunk()->getEntryThunk();
+ if (!entryThunk)
+ continue;
+
+ // Replace symbols with symbols referencing the thunk. Store the original
+ // symbol as equivalent DefinedSynthetic instances for use in the thunk
+ // itself.
+ auto symClone = make<DefinedSynthetic>(sym->getName(), sym->getChunk(),
+ sym->getValue());
+ auto nativeSymClone = make<DefinedSynthetic>(
+ nativeSym->getName(), nativeSym->getChunk(), nativeSym->getValue());
+ SameAddressThunkARM64EC *thunk =
+ make<SameAddressThunkARM64EC>(nativeSymClone, symClone, entryThunk);
+ sameAddressThunks.push_back(thunk);
+
+ replaceSymbol<DefinedSynthetic>(sym, sym->getName(), thunk);
+ replaceSymbol<DefinedSynthetic>(nativeSym, nativeSym->getName(), thunk);
+ }
+}
+
Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
bool overrideLazy) {
auto [s, wasInserted] = insert(name, f);
diff --git a/lld/COFF/SymbolTable.h b/lld/COFF/SymbolTable.h
index 7eb0676..aadd366 100644
--- a/lld/COFF/SymbolTable.h
+++ b/lld/COFF/SymbolTable.h
@@ -31,6 +31,7 @@ class DefinedAbsolute;
class DefinedRegular;
class ImportThunkChunk;
class LazyArchive;
+class SameAddressThunkARM64EC;
class SectionChunk;
class Symbol;
@@ -67,7 +68,7 @@ public:
// Try to resolve any undefined symbols and update the symbol table
// accordingly, then print an error message for any remaining undefined
// symbols and warn about imported local symbols.
- void resolveRemainingUndefines();
+ void resolveRemainingUndefines(std::vector<Undefined *> &aliases);
// Try to resolve undefined symbols with alternate names.
void resolveAlternateNames();
@@ -140,6 +141,7 @@ public:
void addEntryThunk(Symbol *from, Symbol *to);
void addExitThunk(Symbol *from, Symbol *to);
void initializeECThunks();
+ void initializeSameAddressThunks();
void reportDuplicate(Symbol *existing, InputFile *newFile,
SectionChunk *newSc = nullptr,
@@ -159,6 +161,8 @@ public:
// A list of EC EXP+ symbols.
std::vector<Symbol *> expSymbols;
+ std::vector<SameAddressThunkARM64EC *> sameAddressThunks;
+
// A list of DLL exports.
std::vector<Export> exports;
llvm::DenseSet<StringRef> directivesExports;
diff --git a/lld/COFF/Symbols.cpp b/lld/COFF/Symbols.cpp
index b571ce9..ba4f95d1 100644
--- a/lld/COFF/Symbols.cpp
+++ b/lld/COFF/Symbols.cpp
@@ -91,6 +91,14 @@ bool Symbol::isLive() const {
return true;
}
+Defined *Symbol::getDefined() {
+ if (auto d = dyn_cast<Defined>(this))
+ return d;
+ if (auto u = dyn_cast<Undefined>(this))
+ return u->getDefinedWeakAlias();
+ return nullptr;
+}
+
void Symbol::replaceKeepingName(Symbol *other, size_t size) {
StringRef origName = getName();
memcpy(this, other, size);
diff --git a/lld/COFF/Symbols.h b/lld/COFF/Symbols.h
index fd3d8ce..c86ded8 100644
--- a/lld/COFF/Symbols.h
+++ b/lld/COFF/Symbols.h
@@ -95,6 +95,10 @@ public:
symbolKind == LazyDLLSymbolKind;
}
+ // Get the Defined symbol associated with this symbol, either itself or its
+ // weak alias.
+ Defined *getDefined();
+
private:
void computeName();
diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index 0765618..21ab9d1 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -314,6 +314,7 @@ private:
uint32_t dataDirOffset64;
OutputSection *textSec;
+ OutputSection *wowthkSec;
OutputSection *hexpthkSec;
OutputSection *bssSec;
OutputSection *rdataSec;
@@ -1076,8 +1077,10 @@ void Writer::createSections() {
// Try to match the section order used by link.exe.
textSec = createSection(".text", code | r | x);
- if (isArm64EC(ctx.config.machine))
+ if (isArm64EC(ctx.config.machine)) {
+ wowthkSec = createSection(".wowthk", code | r | x);
hexpthkSec = createSection(".hexpthk", code | r | x);
+ }
bssSec = createSection(".bss", bss | r | w);
rdataSec = createSection(".rdata", data | r);
buildidSec = createSection(".buildid", data | r);
@@ -1129,6 +1132,9 @@ void Writer::createSections() {
if (hasIdata)
locateImportTables();
+ for (auto thunk : ctx.symtab.sameAddressThunks)
+ wowthkSec->addChunk(thunk);
+
// Then create an OutputSection for each section.
// '$' and all following characters in input section names are
// discarded when determining output section. So, .text$foo
@@ -2310,6 +2316,14 @@ void Writer::createECChunks() {
ctx.symtab.findUnderscore("__arm64x_redirection_metadata");
replaceSymbol<DefinedSynthetic>(entryPointsSym, entryPointsSym->getName(),
entryPoints);
+
+ for (auto thunk : ctx.symtab.sameAddressThunks) {
+ // Relocation values are set later in setECSymbols.
+ ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),
+ thunk);
+ ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),
+ Arm64XRelocVal(thunk, sizeof(uint32_t)));
+ }
}
// MinGW specific. Gather all relocations that are imported from a DLL even
@@ -2519,6 +2533,9 @@ void Writer::setECSymbols() {
chpeSym->getRVA() + offsetof(chpe_metadata, ExtraRFETableSize),
pdata.last->getRVA() + pdata.last->getSize() - pdata.first->getRVA());
}
+
+ for (SameAddressThunkARM64EC *thunk : ctx.symtab.sameAddressThunks)
+ thunk->setDynamicRelocs(ctx);
}
// Write section contents to a mmap'ed file.
@@ -2544,7 +2561,15 @@ void Writer::writeSections() {
}
parallelForEach(sec->chunks, [&](Chunk *c) {
- c->writeTo(secBuf + c->getRVA() - sec->getRVA());
+ uint8_t *buf = secBuf + c->getRVA() - sec->getRVA();
+ c->writeTo(buf);
+
+ // Write the offset to EC entry thunk preceding section contents. The low
+ // bit is always set, so it's effectively an offset from the last byte of
+ // the offset.
+ if (Defined *entryThunk = c->getEntryThunk())
+ write32le(buf - sizeof(uint32_t),
+ entryThunk->getRVA() - c->getRVA() + 1);
});
}
}
diff --git a/lld/ELF/Arch/LoongArch.cpp b/lld/ELF/Arch/LoongArch.cpp
index 15dcddb..a145530 100644
--- a/lld/ELF/Arch/LoongArch.cpp
+++ b/lld/ELF/Arch/LoongArch.cpp
@@ -809,10 +809,13 @@ static void relaxPCHi20Lo12(Ctx &ctx, const InputSection &sec, size_t i,
// address.
// Meanwhile skip undefined, preemptible and STT_GNU_IFUNC symbols, because
// these symbols may be resolve in runtime.
+ // Moreover, relaxation can only occur if the addends of both relocations are
+ // zero for GOT references.
if (rHi20.type == R_LARCH_GOT_PC_HI20 &&
- (!rHi20.sym->isDefined() || rHi20.sym->isPreemptible ||
- rHi20.sym->isGnuIFunc() ||
- (ctx.arg.isPic && !cast<Defined>(*rHi20.sym).section)))
+ (!rHi20.sym || rHi20.sym != rLo12.sym || !rHi20.sym->isDefined() ||
+ rHi20.sym->isPreemptible || rHi20.sym->isGnuIFunc() ||
+ (ctx.arg.isPic && !cast<Defined>(*rHi20.sym).section) ||
+ rHi20.addend != 0 || rLo12.addend != 0))
return;
uint64_t dest = 0;
diff --git a/lld/ELF/Arch/Mips.cpp b/lld/ELF/Arch/Mips.cpp
index 91c7f15..f88b021c 100644
--- a/lld/ELF/Arch/Mips.cpp
+++ b/lld/ELF/Arch/Mips.cpp
@@ -40,6 +40,10 @@ public:
};
} // namespace
+uint64_t elf::getMipsPageAddr(uint64_t addr) {
+ return (addr + 0x8000) & ~0xffff;
+}
+
template <class ELFT> MIPS<ELFT>::MIPS(Ctx &ctx) : TargetInfo(ctx) {
gotPltHeaderEntriesNum = 2;
defaultMaxPageSize = 65536;
diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index e7622dd..a83a4c1 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -702,6 +702,8 @@ struct Ctx : CommonLinkerContext {
std::unique_ptr<llvm::TarWriter> tar;
// InputFile for linker created symbols with no source location.
InputFile *internalFile = nullptr;
+ // Dummy Undefined for relocations without a symbol.
+ Undefined *dummySym = nullptr;
// True if symbols can be exported (isExported) or preemptible.
bool hasDynsym = false;
// True if SHT_LLVM_SYMPART is used.
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 91e11bc..6c2f318 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -3140,6 +3140,7 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
ctx.symtab->insert(arg->getValue())->traced = true;
ctx.internalFile = createInternalFile(ctx, "<internal>");
+ ctx.dummySym = make<Undefined>(ctx.internalFile, "", STB_LOCAL, 0, 0);
// Handle -u/--undefined before input files. If both a.a and b.so define foo,
// -u foo a.a b.so will extract a.a.
diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp
index 68e3feb..784ff7c 100644
--- a/lld/ELF/InputSection.cpp
+++ b/lld/ELF/InputSection.cpp
@@ -861,6 +861,11 @@ uint64_t InputSectionBase::getRelocTargetVA(Ctx &ctx, const Relocation &r,
return ctx.in.mipsGot->getVA() +
ctx.in.mipsGot->getPageEntryOffset(file, *r.sym, a) -
ctx.in.mipsGot->getGp(file);
+ case RE_MIPS_OSEC_LOCAL_PAGE:
+ // This is used by the MIPS multi-GOT implementation. It relocates
+ // addresses of 64kb pages that lie inside the output section that sym is
+ // a representative for.
+ return getMipsPageAddr(r.sym->getOutputSection()->addr) + a;
case RE_MIPS_GOT_OFF:
case RE_MIPS_GOT_OFF32:
// In case of MIPS if a GOT relocation has non-zero addend this addend
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index bd22fe2..32ac28d6 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -885,10 +885,12 @@ static void addPltEntry(Ctx &ctx, PltSection &plt, GotPltSection &gotPlt,
RelocationBaseSection &rel, RelType type, Symbol &sym) {
plt.addEntry(sym);
gotPlt.addEntry(sym);
- rel.addReloc({type, &gotPlt, sym.getGotPltOffset(ctx),
- sym.isPreemptible ? DynamicReloc::AgainstSymbol
- : DynamicReloc::AddendOnlyWithTargetVA,
- sym, 0, R_ABS});
+ if (sym.isPreemptible)
+ rel.addReloc(
+ {type, &gotPlt, sym.getGotPltOffset(ctx), true, sym, 0, R_ADDEND});
+ else
+ rel.addReloc(
+ {type, &gotPlt, sym.getGotPltOffset(ctx), false, sym, 0, R_ABS});
}
void elf::addGotEntry(Ctx &ctx, Symbol &sym) {
@@ -897,9 +899,8 @@ void elf::addGotEntry(Ctx &ctx, Symbol &sym) {
// If preemptible, emit a GLOB_DAT relocation.
if (sym.isPreemptible) {
- ctx.mainPart->relaDyn->addReloc({ctx.target->gotRel, ctx.in.got.get(), off,
- DynamicReloc::AgainstSymbol, sym, 0,
- R_ABS});
+ ctx.mainPart->relaDyn->addReloc(
+ {ctx.target->gotRel, ctx.in.got.get(), off, true, sym, 0, R_ADDEND});
return;
}
@@ -920,15 +921,13 @@ static void addGotAuthEntry(Ctx &ctx, Symbol &sym) {
// If preemptible, emit a GLOB_DAT relocation.
if (sym.isPreemptible) {
ctx.mainPart->relaDyn->addReloc({R_AARCH64_AUTH_GLOB_DAT, ctx.in.got.get(),
- off, DynamicReloc::AgainstSymbol, sym, 0,
- R_ABS});
+ off, true, sym, 0, R_ADDEND});
return;
}
// Signed GOT requires dynamic relocation.
ctx.in.got->getPartition(ctx).relaDyn->addReloc(
- {R_AARCH64_AUTH_RELATIVE, ctx.in.got.get(), off,
- DynamicReloc::AddendOnlyWithTargetVA, sym, 0, R_ABS});
+ {R_AARCH64_AUTH_RELATIVE, ctx.in.got.get(), off, false, sym, 0, R_ABS});
}
static void addTpOffsetGotEntry(Ctx &ctx, Symbol &sym) {
@@ -1159,9 +1158,8 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset,
sec->addReloc({expr, type, offset, addend, &sym});
part.relrAuthDyn->relocs.push_back({sec, sec->relocs().size() - 1});
} else {
- part.relaDyn->addReloc({R_AARCH64_AUTH_RELATIVE, sec, offset,
- DynamicReloc::AddendOnlyWithTargetVA, sym,
- addend, R_ABS});
+ part.relaDyn->addReloc({R_AARCH64_AUTH_RELATIVE, sec, offset, false,
+ sym, addend, R_ABS});
}
return;
}
@@ -1948,13 +1946,12 @@ void elf::postScanRelocations(Ctx &ctx) {
GotSection *got = ctx.in.got.get();
if (ctx.needsTlsLd.load(std::memory_order_relaxed) && got->addTlsIndex()) {
- static Undefined dummy(ctx.internalFile, "", STB_LOCAL, 0, 0);
if (ctx.arg.shared)
ctx.mainPart->relaDyn->addReloc(
{ctx.target->tlsModuleIndexRel, got, got->getTlsIndexOff()});
else
got->addConstant({R_ADDEND, ctx.target->symbolicRel,
- got->getTlsIndexOff(), 1, &dummy});
+ got->getTlsIndexOff(), 1, ctx.dummySym});
}
assert(ctx.symAux.size() == 1);
diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h
index 02ddf70..c1c4860 100644
--- a/lld/ELF/Relocations.h
+++ b/lld/ELF/Relocations.h
@@ -110,6 +110,7 @@ enum RelExpr {
RE_MIPS_GOT_LOCAL_PAGE,
RE_MIPS_GOT_OFF,
RE_MIPS_GOT_OFF32,
+ RE_MIPS_OSEC_LOCAL_PAGE,
RE_MIPS_TLSGD,
RE_MIPS_TLSLD,
RE_PPC32_PLTREL,
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index efec41a..0d87f9a 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -769,10 +769,6 @@ void GotSection::writeTo(uint8_t *buf) {
}
}
-static uint64_t getMipsPageAddr(uint64_t addr) {
- return (addr + 0x8000) & ~0xffff;
-}
-
static uint64_t getMipsPageCount(uint64_t size) {
return (size + 0xfffe) / 0xffff + 1;
}
@@ -786,7 +782,7 @@ void MipsGotSection::addEntry(InputFile &file, Symbol &sym, int64_t addend,
FileGot &g = getGot(file);
if (expr == RE_MIPS_GOT_LOCAL_PAGE) {
if (const OutputSection *os = sym.getOutputSection())
- g.pagesMap.insert({os, {}});
+ g.pagesMap.insert({os, {&sym}});
else
g.local16.insert({{nullptr, getMipsPageAddr(sym.getVA(ctx, addend))}, 0});
} else if (sym.isTls())
@@ -1066,8 +1062,7 @@ void MipsGotSection::build() {
// be allocated before us in the static TLS block.
if (s->isPreemptible || ctx.arg.shared)
ctx.mainPart->relaDyn->addReloc(
- {ctx.target->tlsGotRel, this, offset,
- DynamicReloc::AgainstSymbolWithTargetVA, *s, 0, R_ABS});
+ {ctx.target->tlsGotRel, this, offset, true, *s, 0, R_ABS});
}
for (std::pair<Symbol *, size_t> &p : got.dynTlsSymbols) {
Symbol *s = p.first;
@@ -1115,15 +1110,16 @@ void MipsGotSection::build() {
size_t pageCount = l.second.count;
for (size_t pi = 0; pi < pageCount; ++pi) {
uint64_t offset = (l.second.firstIndex + pi) * ctx.arg.wordsize;
- ctx.mainPart->relaDyn->addReloc({ctx.target->relativeRel, this, offset,
- l.first, int64_t(pi * 0x10000)});
+ ctx.mainPart->relaDyn->addReloc(
+ {ctx.target->relativeRel, this, offset, false, *l.second.repSym,
+ int64_t(pi * 0x10000), RE_MIPS_OSEC_LOCAL_PAGE});
}
}
for (const std::pair<GotEntry, size_t> &p : got.local16) {
uint64_t offset = p.second * ctx.arg.wordsize;
ctx.mainPart->relaDyn->addReloc({ctx.target->relativeRel, this, offset,
- DynamicReloc::AddendOnlyWithTargetVA,
- *p.first.first, p.first.second, R_ABS});
+ false, *p.first.first, p.first.second,
+ R_ABS});
}
}
}
@@ -1646,24 +1642,10 @@ uint64_t DynamicReloc::getOffset() const {
}
int64_t DynamicReloc::computeAddend(Ctx &ctx) const {
- switch (kind) {
- case AddendOnly:
- assert(sym == nullptr);
- return addend;
- case AgainstSymbol:
- assert(sym != nullptr);
- return addend;
- case AddendOnlyWithTargetVA:
- case AgainstSymbolWithTargetVA: {
- uint64_t ca = inputSec->getRelocTargetVA(
- ctx, Relocation{expr, type, 0, addend, sym}, getOffset());
- return ctx.arg.is64 ? ca : SignExtend64<32>(ca);
- }
- case MipsMultiGotPage:
- assert(sym == nullptr);
- return getMipsPageAddr(outputSec->addr) + addend;
- }
- llvm_unreachable("Unknown DynamicReloc::Kind enum");
+ assert(!isFinal && "addend already computed");
+ uint64_t ca = inputSec->getRelocTargetVA(
+ ctx, Relocation{expr, type, 0, addend, sym}, getOffset());
+ return ctx.arg.is64 ? ca : SignExtend64<32>(ca);
}
uint32_t DynamicReloc::getSymIndex(SymbolTableBaseSection *symTab) const {
@@ -1691,8 +1673,8 @@ RelocationBaseSection::RelocationBaseSection(Ctx &ctx, StringRef name,
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 : ctx.target->noneRel);
+ addReloc(true, dynType, isec, offsetInSec, sym, addend, R_ADDEND,
+ addendRelType ? *addendRelType : ctx.target->noneRel);
}
void RelocationBaseSection::addAddendOnlyRelocIfNonPreemptible(
@@ -1700,11 +1682,9 @@ void RelocationBaseSection::addAddendOnlyRelocIfNonPreemptible(
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,
- R_ABS});
+ addReloc({dynType, &isec, offsetInSec, true, sym, 0, R_ADDEND});
else
- addReloc(DynamicReloc::AddendOnlyWithTargetVA, dynType, isec, offsetInSec,
- sym, 0, R_ABS, addendRelType);
+ addReloc(false, dynType, isec, offsetInSec, sym, 0, R_ABS, addendRelType);
}
void RelocationBaseSection::mergeRels() {
@@ -1744,17 +1724,17 @@ void RelocationBaseSection::finalizeContents() {
}
}
-void DynamicReloc::computeRaw(Ctx &ctx, SymbolTableBaseSection *symt) {
+void DynamicReloc::finalize(Ctx &ctx, SymbolTableBaseSection *symt) {
r_offset = getOffset();
r_sym = getSymIndex(symt);
addend = computeAddend(ctx);
- kind = AddendOnly; // Catch errors
+ isFinal = true; // Catch errors
}
void RelocationBaseSection::computeRels() {
SymbolTableBaseSection *symTab = getPartition(ctx).dynSymTab.get();
parallelForEach(relocs, [&ctx = ctx, symTab](DynamicReloc &rel) {
- rel.computeRaw(ctx, symTab);
+ rel.finalize(ctx, symTab);
});
auto irelative = std::stable_partition(
diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h
index 5f01513..223dfe3 100644
--- a/lld/ELF/SyntheticSections.h
+++ b/lld/ELF/SyntheticSections.h
@@ -327,9 +327,11 @@ private:
size_t startIndex = 0;
struct PageBlock {
+ Symbol *repSym; // Representative symbol for the OutputSection
size_t firstIndex;
size_t count;
- PageBlock() : firstIndex(0), count(0) {}
+ PageBlock(Symbol *repSym = nullptr)
+ : repSym(repSym), firstIndex(0), count(0) {}
};
// Map output sections referenced by MIPS GOT relocations
@@ -418,61 +420,31 @@ private:
class DynamicReloc {
public:
- enum Kind {
- /// The resulting dynamic relocation does not reference a symbol (#sym must
- /// be nullptr) and uses #addend as the result of computeAddend(ctx).
- AddendOnly,
- /// The resulting dynamic relocation will not reference a symbol: #sym is
- /// only used to compute the addend with InputSection::getRelocTargetVA().
- /// Useful for various relative and TLS relocations (e.g. R_X86_64_TPOFF64).
- AddendOnlyWithTargetVA,
- /// The resulting dynamic relocation references symbol #sym from the dynamic
- /// symbol table and uses #addend as the value of computeAddend(ctx).
- AgainstSymbol,
- /// The resulting dynamic relocation references symbol #sym from the dynamic
- /// symbol table and uses InputSection::getRelocTargetVA() + #addend for the
- /// final addend. It can be used for relocations that write the symbol VA as
- // the addend (e.g. R_MIPS_TLS_TPREL64) but still reference the symbol.
- AgainstSymbolWithTargetVA,
- /// This is used by the MIPS multi-GOT implementation. It relocates
- /// addresses of 64kb pages that lie inside the output section.
- MipsMultiGotPage,
- };
- /// This constructor records a relocation against a symbol.
+ /// This constructor records a normal relocation.
DynamicReloc(RelType type, const InputSectionBase *inputSec,
- uint64_t offsetInSec, Kind kind, Symbol &sym, int64_t addend,
- RelExpr expr)
+ uint64_t offsetInSec, bool isAgainstSymbol, Symbol &sym,
+ int64_t addend, RelExpr expr)
: sym(&sym), inputSec(inputSec), offsetInSec(offsetInSec), type(type),
- addend(addend), kind(kind), expr(expr) {}
+ addend(addend), isAgainstSymbol(isAgainstSymbol), isFinal(false),
+ expr(expr) {}
/// This constructor records a relative relocation with no symbol.
DynamicReloc(RelType type, const InputSectionBase *inputSec,
uint64_t offsetInSec, int64_t addend = 0)
- : 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)
- : sym(nullptr), outputSec(outputSec), inputSec(inputSec),
- offsetInSec(offsetInSec), type(type), addend(addend),
- kind(MipsMultiGotPage), expr(R_ADDEND) {}
+ : DynamicReloc(type, inputSec, offsetInSec, false,
+ *inputSec->getCtx().dummySym, addend, R_ADDEND) {}
uint64_t getOffset() const;
uint32_t getSymIndex(SymbolTableBaseSection *symTab) const;
- bool needsDynSymIndex() const {
- return kind == AgainstSymbol || kind == AgainstSymbolWithTargetVA;
- }
+ bool needsDynSymIndex() const { return isAgainstSymbol; }
/// Computes the addend of the dynamic relocation. Note that this is not the
/// same as the #addend member variable as it may also include the symbol
/// address/the address of the corresponding GOT entry/etc.
int64_t computeAddend(Ctx &) const;
- void computeRaw(Ctx &, SymbolTableBaseSection *symt);
+ void finalize(Ctx &, SymbolTableBaseSection *symt);
Symbol *sym;
- const OutputSection *outputSec = nullptr;
const InputSectionBase *inputSec;
uint64_t offsetInSec;
uint64_t r_offset;
@@ -483,7 +455,15 @@ public:
int64_t addend;
private:
- Kind kind;
+ /// Whether this was constructed with a Kind of AgainstSymbol.
+ LLVM_PREFERRED_TYPE(bool)
+ uint8_t isAgainstSymbol : 1;
+
+ /// The resulting dynamic relocation has already had its addend computed.
+ /// Calling computeAddend() is an error.
+ LLVM_PREFERRED_TYPE(bool)
+ uint8_t isFinal : 1;
+
// The kind of expression used to calculate the added (required e.g. for
// relative GOT relocations).
RelExpr expr;
@@ -528,8 +508,8 @@ public:
uint64_t offsetInSec, Symbol &sym, int64_t addend,
RelType addendRelType, RelExpr expr) {
assert(expr != R_ADDEND && "expected non-addend relocation expression");
- addReloc<shard>(DynamicReloc::AddendOnlyWithTargetVA, dynType, isec,
- offsetInSec, sym, addend, expr, addendRelType);
+ addReloc<shard>(false, 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.
@@ -538,14 +518,15 @@ public:
uint64_t offsetInSec, Symbol &sym,
RelType addendRelType);
template <bool shard = false>
- void addReloc(DynamicReloc::Kind kind, RelType dynType, InputSectionBase &sec,
+ void addReloc(bool isAgainstSymbol, 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 (ctx.arg.writeAddends && (expr != R_ADDEND || addend != 0))
sec.addReloc({expr, addendRelType, offsetInSec, addend, &sym});
- addReloc<shard>({dynType, &sec, offsetInSec, kind, sym, addend, expr});
+ addReloc<shard>(
+ {dynType, &sec, offsetInSec, isAgainstSymbol, sym, addend, expr});
}
bool isNeeded() const override {
return !relocs.empty() ||
diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp
index ad7d57d..4946484 100644
--- a/lld/ELF/Target.cpp
+++ b/lld/ELF/Target.cpp
@@ -105,10 +105,9 @@ ErrorPlace elf::getErrorPlace(Ctx &ctx, const uint8_t *loc) {
if (isecLoc <= loc && loc < isecLoc + isec->getSize()) {
std::string objLoc = isec->getLocation(loc - isecLoc);
// Return object file location and source file location.
- Undefined dummy(ctx.internalFile, "", STB_LOCAL, 0, 0);
ELFSyncStream msg(ctx, DiagLevel::None);
if (isec->file)
- msg << isec->getSrcMsg(dummy, loc - isecLoc);
+ msg << isec->getSrcMsg(*ctx.dummySym, loc - isecLoc);
return {isec, objLoc + ": ", std::string(msg.str())};
}
}
diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index f4a6d83..fdc0c20 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -214,6 +214,7 @@ void processArmCmseSymbols(Ctx &);
template <class ELFT> uint32_t calcMipsEFlags(Ctx &);
uint8_t getMipsFpAbiFlag(Ctx &, InputFile *file, uint8_t oldFlag,
uint8_t newFlag);
+uint64_t getMipsPageAddr(uint64_t addr);
bool isMipsN32Abi(Ctx &, const InputFile &f);
bool isMicroMips(Ctx &);
bool isMipsR6(Ctx &);
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index c3c0578..2b0e097 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -1586,9 +1586,8 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
if (isInt<32>(reloc.sym->getVA(ctx, reloc.addend)))
return false;
part.relaDyn->addReloc({R_AARCH64_AUTH_RELATIVE, elem.inputSec,
- reloc.offset,
- DynamicReloc::AddendOnlyWithTargetVA,
- *reloc.sym, reloc.addend, R_ABS});
+ reloc.offset, false, *reloc.sym,
+ reloc.addend, R_ABS});
return true;
});
changed |= (it != part.relrAuthDyn->relocs.end());
diff --git a/lld/test/COFF/arm64x-sameaddress.test b/lld/test/COFF/arm64x-sameaddress.test
index c69be9d..819d19b 100644
--- a/lld/test/COFF/arm64x-sameaddress.test
+++ b/lld/test/COFF/arm64x-sameaddress.test
@@ -3,16 +3,103 @@ RUN: split-file %s %t.dir && cd %t.dir
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows func-arm64ec.s -o func-arm64ec.obj
RUN: llvm-mc -filetype=obj -triple=aarch64-windows func-arm64.s -o func-arm64.obj
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows ref-arm64ec.s -o ref-arm64ec.obj
+RUN: llvm-mc -filetype=obj -triple=aarch64-windows ref-arm64.s -o ref-arm64.obj
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows drectve.s -o drectve.obj
RUN: llvm-mc -filetype=obj -triple=aarch64-windows drectve.s -o drectve-arm64.obj
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
RUN: llvm-mc -filetype=obj -triple=aarch64-windows %S/Inputs/loadconfig-arm64.s -o loadconfig-arm64.obj
RUN: lld-link -machine:arm64x -dll -noentry -out:out.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \
-RUN: func-arm64.obj func-arm64ec.obj drectve.obj
+RUN: func-arm64.obj func-arm64ec.obj ref-arm64.obj ref-arm64ec.obj drectve.obj
+
+RUN: llvm-objdump -d out.dll | FileCheck --check-prefix=DISASM %s
+DISASM: 000000180001000 <.text>:
+DISASM-NEXT: 180001000: d2800020 mov x0, #0x1 // =1
+DISASM-NEXT: 180001004: d65f03c0 ret
+DISASM-NEXT: ...
+DISASM-NEXT: 180002000: 00000019 udf #0x19
+DISASM-NEXT: 180002004: d2800040 mov x0, #0x2 // =2
+DISASM-NEXT: 180002008: d65f03c0 ret
+DISASM-NEXT: 18000200c: 0000000d udf #0xd
+DISASM-NEXT: 180002010: f0fffff0 adrp x16, 0x180001000 <.text>
+DISASM-NEXT: 180002014: 91000210 add x16, x16, #0x0
+DISASM-NEXT: 180002018: d61f0200 br x16
+DISASM-NEXT: 18000201c: d2800060 mov x0, #0x3 // =3
+DISASM-NEXT: 180002020: d65f03c0 ret
+
+RUN: llvm-readobj --hex-dump=.test out.dll | FileCheck --check-prefix=TESTSEC %s
+TESTSEC: 10200000 10200000 10200000
+
+RUN: llvm-readobj --coff-load-config out.dll | FileCheck --check-prefix=DYNRELOCS %s
+DYNRELOCS: DynamicRelocations [
+DYNRELOCS-NEXT: Version: 0x1
+DYNRELOCS-NEXT: Arm64X [
+DYNRELOCS-NEXT: Entry [
+DYNRELOCS-NEXT: RVA: 0x7C
+DYNRELOCS-NEXT: Type: VALUE
+DYNRELOCS-NEXT: Size: 0x2
+DYNRELOCS-NEXT: Value: 0x8664
+DYNRELOCS-NEXT: ]
+DYNRELOCS-NEXT: Entry [
+DYNRELOCS-NEXT: RVA: 0x150
+DYNRELOCS-NEXT: Type: VALUE
+DYNRELOCS-NEXT: Size: 0x4
+DYNRELOCS-NEXT: Value: 0x3150
+DYNRELOCS-NEXT: ]
+DYNRELOCS-NEXT: Entry [
+DYNRELOCS-NEXT: RVA: 0x154
+DYNRELOCS-NEXT: Type: VALUE
+DYNRELOCS-NEXT: Size: 0x4
+DYNRELOCS-NEXT: Value: 0x140
+DYNRELOCS-NEXT: ]
+DYNRELOCS-NEXT: Entry [
+DYNRELOCS-NEXT: RVA: 0x2010
+DYNRELOCS-NEXT: Type: VALUE
+DYNRELOCS-NEXT: Size: 0x4
+DYNRELOCS-NEXT: Value: 0x90000010
+DYNRELOCS-NEXT: ]
+DYNRELOCS-NEXT: Entry [
+DYNRELOCS-NEXT: RVA: 0x2014
+DYNRELOCS-NEXT: Type: VALUE
+DYNRELOCS-NEXT: Size: 0x4
+DYNRELOCS-NEXT: Value: 0x91001210
+DYNRELOCS-NEXT: ]
+DYNRELOCS-NEXT: ]
+DYNRELOCS-NEXT: ]
RUN: lld-link -machine:arm64x -dll -noentry -out:out-cmd.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \
-RUN: func-arm64.obj func-arm64ec.obj -arm64xsameaddress:func
+RUN: func-arm64.obj func-arm64ec.obj ref-arm64.obj ref-arm64ec.obj -arm64xsameaddress:func
+RUN: llvm-objdump -d out-cmd.dll | FileCheck --check-prefix=DISASM %s
+RUN: llvm-readobj --hex-dump=.test out-cmd.dll | FileCheck --check-prefix=TESTSEC %s
+RUN: llvm-readobj --coff-load-config out-cmd.dll | FileCheck --check-prefix=DYNRELOCS %s
+
+RUN: lld-link -machine:arm64x -dll -noentry -out:out-both.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \
+RUN: func-arm64.obj func-arm64ec.obj ref-arm64.obj ref-arm64ec.obj drectve.obj -arm64xsameaddress:func
+RUN: llvm-objdump -d out-both.dll | FileCheck --check-prefix=DISASM %s
+RUN: llvm-readobj --hex-dump=.test out-both.dll | FileCheck --check-prefix=TESTSEC %s
+RUN: llvm-readobj --coff-load-config out-both.dll | FileCheck --check-prefix=DYNRELOCS %s
+
+Check that if any of the sameaddress symbols is not alive, the thunk is not generated.
+
+RUN: lld-link -machine:arm64x -dll -noentry -out:out-live1.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \
+RUN: func-arm64.obj func-arm64ec.obj ref-arm64ec.obj drectve.obj
+RUN: llvm-objdump -d out-live1.dll | FileCheck --check-prefix=DISASM-LIVE1 %s
+DISASM-LIVE1: 0000000180001000 <.text>:
+DISASM-LIVE1-NEXT: 180001000: 00000009 udf #0x9
+DISASM-LIVE1-NEXT: 180001004: d2800040 mov x0, #0x2 // =2
+DISASM-LIVE1-NEXT: 180001008: d65f03c0 ret
+DISASM-LIVE1-NEXT: 18000100c: d2800060 mov x0, #0x3 // =3
+DISASM-LIVE1-NEXT: 180001010: d65f03c0 ret
+DISASM-LIVE1-NOT: br
+
+RUN: lld-link -machine:arm64x -dll -noentry -out:out-live2.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \
+RUN: func-arm64.obj func-arm64ec.obj ref-arm64.obj drectve.obj
+RUN: llvm-objdump -d out-live2.dll | FileCheck --check-prefix=DISASM-LIVE2 %s
+DISASM-LIVE2: 0000000180001000 <.text>:
+DISASM-LIVE2-NEXT: 180001000: d2800020 mov x0, #0x1 // =1
+DISASM-LIVE2-NEXT: 180001004: d65f03c0 ret
+DISASM-LIVE2-NOT: br
RUN: lld-link -machine:arm64ec -dll -noentry -out:out-ec.dll loadconfig-arm64ec.obj func-arm64ec.obj drectve.obj
@@ -20,6 +107,10 @@ RUN: lld-link -machine:arm64x -dll -noentry -out:out-warn.dll loadconfig-arm64.o
RUN: func-arm64.obj func-arm64ec.obj drectve-arm64.obj 2>&1 | FileCheck --check-prefix=WARN %s
WARN: lld-link: warning: -arm64xsameaddress: is not allowed in non-ARM64EC files (drectve-arm64.obj)
+RUN: lld-link -machine:arm64 -dll -noentry -out:out-warn2.dll loadconfig-arm64.obj \
+RUN: func-arm64.obj -arm64xsameaddress:func 2>&1 | FileCheck --check-prefix=WARN2 %s
+WARN2: lld-link: warning: -arm64xsameaddress: is allowed only on EC targets
+
#--- func-arm64.s
.section .text,"xr",discard,func
.globl func
@@ -27,6 +118,10 @@ func:
mov x0, #1
ret
+#--- ref-arm64.s
+ .section .test,"dr"
+ .rva func
+
#--- func-arm64ec.s
.section .text,"xr",discard,"#func"
.globl "#func"
@@ -43,14 +138,16 @@ entry_thunk:
mov x0, #3
ret
- .section .test,"dr"
- .rva func
-
.section .hybmp$x,"yi"
.symidx "#func"
.symidx entry_thunk
.word 1
+#--- ref-arm64ec.s
+ .section .test,"dr"
+ .rva func
+ .rva "#func"
+
#--- drectve.s
.section .drectve, "yn"
.ascii " -arm64xsameaddress:func"
diff --git a/lld/test/ELF/loongarch-relax-pc-hi20-lo12.s b/lld/test/ELF/loongarch-relax-pc-hi20-lo12.s
index a417d89..a33f866 100644
--- a/lld/test/ELF/loongarch-relax-pc-hi20-lo12.s
+++ b/lld/test/ELF/loongarch-relax-pc-hi20-lo12.s
@@ -1,22 +1,23 @@
# REQUIRES: loongarch
+# RUN: rm -rf %t && split-file %s %t && cd %t
-# RUN: llvm-mc --filetype=obj --triple=loongarch32 -mattr=+relax %s -o %t.32.o
-# RUN: llvm-mc --filetype=obj --triple=loongarch64 -mattr=+relax %s -o %t.64.o
+# RUN: llvm-mc --filetype=obj --triple=loongarch32 -mattr=+relax a.s -o a.32.o
+# RUN: llvm-mc --filetype=obj --triple=loongarch64 -mattr=+relax a.s -o a.64.o
-# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x14000 %t.32.o -o %t.32
-# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x14000 %t.64.o -o %t.64
-# RUN: llvm-objdump -td --no-show-raw-insn %t.32 | FileCheck --check-prefixes=RELAX %s
-# RUN: llvm-objdump -td --no-show-raw-insn %t.64 | FileCheck --check-prefixes=RELAX %s
+# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x14000 a.32.o -o a.32
+# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x14000 a.64.o -o a.64
+# RUN: llvm-objdump -td --no-show-raw-insn a.32 | FileCheck --check-prefixes=RELAX %s
+# RUN: llvm-objdump -td --no-show-raw-insn a.64 | FileCheck --check-prefixes=RELAX %s
-# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x14000 %t.32.o -shared -o %t.32s
-# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x14000 %t.64.o -shared -o %t.64s
-# RUN: llvm-objdump -td --no-show-raw-insn %t.32s | FileCheck --check-prefixes=RELAX %s
-# RUN: llvm-objdump -td --no-show-raw-insn %t.64s | FileCheck --check-prefixes=RELAX %s
+# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x14000 a.32.o -shared -o a.32s
+# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x14000 a.64.o -shared -o a.64s
+# RUN: llvm-objdump -td --no-show-raw-insn a.32s | FileCheck --check-prefixes=RELAX %s
+# RUN: llvm-objdump -td --no-show-raw-insn a.64s | FileCheck --check-prefixes=RELAX %s
-# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x410000 %t.32.o -o %t.32o
-# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x410000 %t.64.o -o %t.64o
-# RUN: llvm-objdump -td --no-show-raw-insn %t.32o | FileCheck --check-prefixes=NORELAX32 %s
-# RUN: llvm-objdump -td --no-show-raw-insn %t.64o | FileCheck --check-prefixes=NORELAX64 %s
+# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x410000 a.32.o -o a.32o
+# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x410000 a.64.o -o a.64o
+# RUN: llvm-objdump -td --no-show-raw-insn a.32o | FileCheck --check-prefixes=NORELAX32 %s
+# RUN: llvm-objdump -td --no-show-raw-insn a.64o | FileCheck --check-prefixes=NORELAX64 %s
# RELAX-LABEL: <_start>:
## offset = 0x14000 - 0x10000 = 4096<<2
@@ -49,6 +50,25 @@
# NORELAX64-NEXT: pcalau12i $a0, 1024
# NORELAX64-NEXT: ld.d $a0, $a0, 8
+
+## GOT references with non-zero addends. No relaxation.
+# RUN: llvm-mc --filetype=obj --triple=loongarch32 -mattr=+relax nonzero.s -o nonzero.32.o
+# RUN: llvm-mc --filetype=obj --triple=loongarch64 -mattr=+relax nonzero.s -o nonzero.64.o
+# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x14000 nonzero.32.o -o nonzero.32
+# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x14000 nonzero.64.o -o nonzero.64
+# RUN: llvm-objdump -td --no-show-raw-insn nonzero.32 | FileCheck --check-prefixes=NONZERO32 %s
+# RUN: llvm-objdump -td --no-show-raw-insn nonzero.64 | FileCheck --check-prefixes=NONZERO64 %s
+
+# NONZERO32-LABEL: <_start>:
+# NONZERO32-NEXT: 10000: pcalau12i $a0, 4
+# NONZERO32-NEXT: ld.w $a0, $a0, 8
+
+# NONZERO64-LABEL: <_start>:
+# NONZERO64-NEXT: 10000: pcalau12i $a0, 4
+# NONZERO64-NEXT: ld.d $a0, $a0, 12
+
+
+#--- a.s
.section .text
.global _start
_start:
@@ -60,3 +80,14 @@ _start:
.section .data
sym:
.zero 4
+
+
+#--- nonzero.s
+.section .text
+.global _start
+_start:
+ la.got $a0, sym+4
+
+.section .data
+sym:
+ .zero 4