aboutsummaryrefslogtreecommitdiff
path: root/ELF/Relocations.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ELF/Relocations.cpp')
-rw-r--r--ELF/Relocations.cpp112
1 files changed, 61 insertions, 51 deletions
diff --git a/ELF/Relocations.cpp b/ELF/Relocations.cpp
index 96e409578f5c..94ea3e1557c4 100644
--- a/ELF/Relocations.cpp
+++ b/ELF/Relocations.cpp
@@ -70,12 +70,11 @@ using namespace lld::elf;
// >>> defined in /home/alice/src/foo.o
// >>> referenced by bar.c:12 (/home/alice/src/bar.c:12)
// >>> /home/alice/src/bar.o:(.text+0x1)
-template <class ELFT>
static std::string getLocation(InputSectionBase &S, const Symbol &Sym,
uint64_t Off) {
std::string Msg =
"\n>>> defined in " + toString(Sym.File) + "\n>>> referenced by ";
- std::string Src = S.getSrcMsg<ELFT>(Sym, Off);
+ std::string Src = S.getSrcMsg(Sym, Off);
if (!Src.empty())
Msg += Src + "\n>>> ";
return Msg + S.getObjMsg(Off);
@@ -365,7 +364,6 @@ static bool isRelExpr(RelExpr Expr) {
//
// If this function returns false, that means we need to emit a
// dynamic relocation so that the relocation will be fixed at load-time.
-template <class ELFT>
static bool isStaticLinkTimeConstant(RelExpr E, RelType Type, const Symbol &Sym,
InputSectionBase &S, uint64_t RelOff) {
// These expressions always compute a constant
@@ -410,7 +408,7 @@ static bool isStaticLinkTimeConstant(RelExpr E, RelType Type, const Symbol &Sym,
return true;
error("relocation " + toString(Type) + " cannot refer to absolute symbol: " +
- toString(Sym) + getLocation<ELFT>(S, Sym, RelOff));
+ toString(Sym) + getLocation(S, Sym, RelOff));
return true;
}
@@ -443,8 +441,8 @@ template <class ELFT> static bool isReadOnly(SharedSymbol *SS) {
typedef typename ELFT::Phdr Elf_Phdr;
// Determine if the symbol is read-only by scanning the DSO's program headers.
- const SharedFile<ELFT> *File = SS->getFile<ELFT>();
- for (const Elf_Phdr &Phdr : check(File->getObj().program_headers()))
+ const SharedFile<ELFT> &File = SS->getFile<ELFT>();
+ for (const Elf_Phdr &Phdr : check(File.getObj().program_headers()))
if ((Phdr.p_type == ELF::PT_LOAD || Phdr.p_type == ELF::PT_GNU_RELRO) &&
!(Phdr.p_flags & ELF::PF_W) && SS->Value >= Phdr.p_vaddr &&
SS->Value < Phdr.p_vaddr + Phdr.p_memsz)
@@ -461,14 +459,14 @@ template <class ELFT>
static std::vector<SharedSymbol *> getSymbolsAt(SharedSymbol *SS) {
typedef typename ELFT::Sym Elf_Sym;
- SharedFile<ELFT> *File = SS->getFile<ELFT>();
+ SharedFile<ELFT> &File = SS->getFile<ELFT>();
std::vector<SharedSymbol *> Ret;
- for (const Elf_Sym &S : File->getGlobalELFSyms()) {
+ for (const Elf_Sym &S : File.getGlobalELFSyms()) {
if (S.st_shndx == SHN_UNDEF || S.st_shndx == SHN_ABS ||
S.st_value != SS->Value)
continue;
- StringRef Name = check(S.getName(File->getStringTable()));
+ StringRef Name = check(S.getName(File.getStringTable()));
Symbol *Sym = Symtab->find(Name);
if (auto *Alias = dyn_cast_or_null<SharedSymbol>(Sym))
Ret.push_back(Alias);
@@ -554,22 +552,57 @@ static void errorOrWarn(const Twine &Msg) {
warn(Msg);
}
+// Returns PLT relocation expression.
+//
+// This handles a non PIC program call to function in a shared library. In
+// an ideal world, we could just report an error saying the relocation can
+// overflow at runtime. In the real world with glibc, crt1.o has a
+// R_X86_64_PC32 pointing to libc.so.
+//
+// The general idea on how to handle such cases is to create a PLT entry and
+// use that as the function value.
+//
+// For the static linking part, we just return a plt expr and everything
+// else will use the the PLT entry as the address.
+//
+// The remaining problem is making sure pointer equality still works. We
+// need the help of the dynamic linker for that. We let it know that we have
+// a direct reference to a so symbol by creating an undefined symbol with a
+// non zero st_value. Seeing that, the dynamic linker resolves the symbol to
+// the value of the symbol we created. This is true even for got entries, so
+// pointer equality is maintained. To avoid an infinite loop, the only entry
+// that points to the real function is a dedicated got entry used by the
+// plt. That is identified by special relocation types (R_X86_64_JUMP_SLOT,
+// R_386_JMP_SLOT, etc).
+static RelExpr getPltExpr(Symbol &Sym, RelExpr Expr, bool &IsConstant) {
+ Sym.NeedsPltAddr = true;
+ Sym.IsPreemptible = false;
+ IsConstant = true;
+ return toPlt(Expr);
+}
+
template <class ELFT>
static RelExpr adjustExpr(Symbol &Sym, RelExpr Expr, RelType Type,
- InputSectionBase &S, uint64_t RelOff) {
+ InputSectionBase &S, uint64_t RelOff,
+ bool &IsConstant) {
// We can create any dynamic relocation if a section is simply writable.
if (S.Flags & SHF_WRITE)
return Expr;
// Or, if we are allowed to create dynamic relocations against
- // read-only sections (i.e. unless "-z notext" is given),
+ // read-only sections (i.e. when "-z notext" is given),
// we can create a dynamic relocation as we want, too.
- if (!Config->ZText)
+ if (!Config->ZText) {
+ // We use PLT for relocations that may overflow in runtime,
+ // see comment for getPltExpr().
+ if (Sym.isFunc() && !Target->isPicRel(Type))
+ return getPltExpr(Sym, Expr, IsConstant);
return Expr;
+ }
// If a relocation can be applied at link-time, we don't need to
// create a dynamic relocation in the first place.
- if (isStaticLinkTimeConstant<ELFT>(Expr, Type, Sym, S, RelOff))
+ if (IsConstant)
return Expr;
// If we got here we know that this relocation would require the dynamic
@@ -579,6 +612,7 @@ static RelExpr adjustExpr(Symbol &Sym, RelExpr Expr, RelType Type,
// non preemptible 0.
if (Sym.isUndefWeak()) {
Sym.IsPreemptible = false;
+ IsConstant = true;
return Expr;
}
@@ -589,13 +623,13 @@ static RelExpr adjustExpr(Symbol &Sym, RelExpr Expr, RelType Type,
"can't create dynamic relocation " + toString(Type) + " against " +
(Sym.getName().empty() ? "local symbol" : "symbol: " + toString(Sym)) +
" in readonly segment; recompile object files with -fPIC" +
- getLocation<ELFT>(S, Sym, RelOff));
+ getLocation(S, Sym, RelOff));
return Expr;
}
if (Sym.getVisibility() != STV_DEFAULT) {
error("cannot preempt symbol: " + toString(Sym) +
- getLocation<ELFT>(S, Sym, RelOff));
+ getLocation(S, Sym, RelOff));
return Expr;
}
@@ -607,38 +641,16 @@ static RelExpr adjustExpr(Symbol &Sym, RelExpr Expr, RelType Type,
error("unresolvable relocation " + toString(Type) +
" against symbol '" + toString(*B) +
"'; recompile with -fPIC or remove '-z nocopyreloc'" +
- getLocation<ELFT>(S, Sym, RelOff));
+ getLocation(S, Sym, RelOff));
addCopyRelSymbol<ELFT>(B);
}
+ IsConstant = true;
return Expr;
}
- if (Sym.isFunc()) {
- // This handles a non PIC program call to function in a shared library. In
- // an ideal world, we could just report an error saying the relocation can
- // overflow at runtime. In the real world with glibc, crt1.o has a
- // R_X86_64_PC32 pointing to libc.so.
- //
- // The general idea on how to handle such cases is to create a PLT entry and
- // use that as the function value.
- //
- // For the static linking part, we just return a plt expr and everything
- // else will use the the PLT entry as the address.
- //
- // The remaining problem is making sure pointer equality still works. We
- // need the help of the dynamic linker for that. We let it know that we have
- // a direct reference to a so symbol by creating an undefined symbol with a
- // non zero st_value. Seeing that, the dynamic linker resolves the symbol to
- // the value of the symbol we created. This is true even for got entries, so
- // pointer equality is maintained. To avoid an infinite loop, the only entry
- // that points to the real function is a dedicated got entry used by the
- // plt. That is identified by special relocation types (R_X86_64_JUMP_SLOT,
- // R_386_JMP_SLOT, etc).
- Sym.NeedsPltAddr = true;
- Sym.IsPreemptible = false;
- return toPlt(Expr);
- }
+ if (Sym.isFunc())
+ return getPltExpr(Sym, Expr, IsConstant);
errorOrWarn("symbol '" + toString(Sym) + "' defined in " +
toString(Sym.File) + " has no type");
@@ -708,7 +720,6 @@ static int64_t computeAddend(const RelTy &Rel, const RelTy *End,
// Report an undefined symbol if necessary.
// Returns true if this function printed out an error message.
-template <class ELFT>
static bool maybeReportUndefined(Symbol &Sym, InputSectionBase &Sec,
uint64_t Offset) {
if (Config->UnresolvedSymbols == UnresolvedPolicy::IgnoreAll)
@@ -725,7 +736,7 @@ static bool maybeReportUndefined(Symbol &Sym, InputSectionBase &Sec,
std::string Msg =
"undefined symbol: " + toString(Sym) + "\n>>> referenced by ";
- std::string Src = Sec.getSrcMsg<ELFT>(Sym, Offset);
+ std::string Src = Sec.getSrcMsg(Sym, Offset);
if (!Src.empty())
Msg += Src + "\n>>> ";
Msg += Sec.getObjMsg(Offset);
@@ -846,7 +857,7 @@ template <class ELFT> static void addGotEntry(Symbol &Sym, bool Preemptible) {
//
// This is ugly -- the difference between REL and RELA should be
// handled in a better way. It's a TODO.
- if (!Config->IsRela)
+ if (!Config->IsRela && !Preemptible)
InX::Got->Relocations.push_back({R_ABS, Target->GotRel, Off, 0, &Sym});
}
@@ -885,7 +896,7 @@ static void scanRelocs(InputSectionBase &Sec, ArrayRef<RelTy> Rels) {
continue;
// Skip if the target symbol is an erroneous undefined symbol.
- if (maybeReportUndefined<ELFT>(Sym, Sec, Rel.r_offset))
+ if (maybeReportUndefined(Sym, Sec, Rel.r_offset))
continue;
RelExpr Expr =
@@ -923,7 +934,10 @@ static void scanRelocs(InputSectionBase &Sec, ArrayRef<RelTy> Rels) {
else if (!Preemptible)
Expr = fromPlt(Expr);
- Expr = adjustExpr<ELFT>(Sym, Expr, Type, Sec, Rel.r_offset);
+ bool IsConstant =
+ isStaticLinkTimeConstant(Expr, Type, Sym, Sec, Rel.r_offset);
+
+ Expr = adjustExpr<ELFT>(Sym, Expr, Type, Sec, Rel.r_offset, IsConstant);
if (errorCount())
continue;
@@ -980,7 +994,7 @@ static void scanRelocs(InputSectionBase &Sec, ArrayRef<RelTy> Rels) {
errorOrWarn(
"relocation " + toString(Type) +
" cannot be used against shared object; recompile with -fPIC" +
- getLocation<ELFT>(Sec, Sym, Offset));
+ getLocation(Sec, Sym, Offset));
InX::RelaDyn->addReloc(
{Target->getDynRel(Type), &Sec, Offset, false, &Sym, Addend});
@@ -1005,10 +1019,6 @@ static void scanRelocs(InputSectionBase &Sec, ArrayRef<RelTy> Rels) {
continue;
}
- // If the relocation points to something in the file, we can process it.
- bool IsConstant =
- isStaticLinkTimeConstant<ELFT>(Expr, Type, Sym, Sec, Rel.r_offset);
-
// The size is not going to change, so we fold it in here.
if (Expr == R_SIZE)
Addend += Sym.getSize();