musl/arch/mips/reloc.h
Rich Felker 2d8cc92a7c fix regression in mips dynamic linker
this issue caused the address of functions in shared libraries to
resolve to their PLT thunks in the main program rather than their
correct addresses. it was observed causing crashes, though the
mechanism of the crash was not thoroughly investigated. since the
issue is very subtle, it calls for some explanation:

on all well-behaved archs, GOT entries that belong to the PLT use a
special relocation type, typically called JMP_SLOT, so that the
dynamic linker can avoid having the jump destinations for the PLT
resolve to PLT thunks themselves (they also provide a definition for
the symbol, which must be used whenever the address of the function is
taken so that all DSOs see the same address).

however, the traditional mips PIC ABI lacked such a JMP_SLOT
relocation type, presumably because, due to the way PIC works, the
address of the PLT thunk was never needed and could always be ignored.

prior to commit adf94c1966, the mips
version of reloc.h contained a hack that caused all symbol lookups to
be treated like JMP_SLOT, inhibiting undefined symbols from ever being
used to resolve symbolic relocations. this hack goes all the way back
to commit babf820180, when the mips
dynamic linker was first made usable.

during the recent refactoring to eliminate arch-specific relocation
processing (commit adf94c1966), this
hack was overlooked and no equivalent functionality was provided in
the new code.

fixing the problem is not as simple as adding back an equivalent hack,
since there is now also a "non-PIC ABI" that can be used for the main
executable, which actually does use a PLT. the closest thing to
official documentation I could find for this ABI is nonpic.txt,
attached to Message-ID: 20080701202236.GA1534@caradoc.them.org, which
can be found in the gcc mailing list archives and elsewhere. per this
document, undefined symbols corresponding to PLT thunks have the
STO_MIPS_PLT bit set in the symbol's st_other field. thus, I have
added an arch-specific rule for mips, applied at the find_sym level
rather than the relocation level, to reject undefined symbols with the
STO_MIPS_PLT bit clear.

the previous hack of treating all mips relocations as JMP_SLOT-like,
rather than rejecting the unwanted symbols in find_sym, probably also
caused dlsym to wrongly return PLT thunks in place of the correct
address of a function under at least some conditions. this should now
be fixed, at least for global-scope symbol lookups.
2014-06-30 01:18:14 -04:00

90 lines
2.1 KiB
C

#include <string.h>
#include <elf.h>
#include <endian.h>
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define ENDIAN_SUFFIX "el"
#else
#define ENDIAN_SUFFIX ""
#endif
#ifdef __mips_soft_float
#define FP_SUFFIX "-sf"
#else
#define FP_SUFFIX ""
#endif
#define LDSO_ARCH "mips" ENDIAN_SUFFIX FP_SUFFIX
#define TPOFF_K (-0x7000)
static int remap_rel(int type)
{
switch(type) {
case R_MIPS_REL32:
return REL_SYM_OR_REL;
case R_MIPS_JUMP_SLOT:
return REL_PLT;
case R_MIPS_COPY:
return REL_COPY;
case R_MIPS_TLS_DTPMOD32:
return REL_DTPMOD;
case R_MIPS_TLS_DTPREL32:
return REL_DTPOFF;
case R_MIPS_TLS_TPREL32:
return REL_TPOFF;
}
return 0;
}
void __reloc_self(int c, size_t *a, size_t *dynv, size_t *got)
{
char *base;
size_t t[20], n;
for (a+=c+1; *a; a++);
for (a++; *a; a+=2) if (*a<20) t[*a] = a[1];
base = (char *)t[AT_BASE];
if (!base) base = (char *)(t[AT_PHDR] & -t[AT_PAGESZ]);
for (a=dynv; *a; a+=2) if (*a-0x70000000UL<20) t[*a&31] = a[1];
n = t[DT_MIPS_LOCAL_GOTNO - 0x70000000];
for (a=got; n; a++, n--) *a += (size_t)base;
}
static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stride);
static void do_arch_relocs(struct dso *this, struct dso *head)
{
unsigned char *base = this->base;
size_t *dynv = this->dynv;
size_t dyn[20] = {0};
size_t i;
size_t rel[2], got=0;
Sym *sym;
for (i=0; dynv[i]; i+=2) {
if (dynv[i]-0x70000000UL<20)
dyn[dynv[i]&31] = dynv[i+1];
else if (dynv[i] == DT_PLTGOT)
got = dynv[i+1];
}
i = dyn[DT_MIPS_LOCAL_GOTNO-0x70000000];
if (this->shortname && !strcmp(this->shortname, "libc.so")) {
got += sizeof(size_t) * i;
} else {
for (; i; i--, got+=sizeof(size_t))
*(size_t *)(base+got) += (size_t)base;
}
sym = this->syms + dyn[DT_MIPS_GOTSYM-0x70000000];
i = dyn[DT_MIPS_SYMTABNO-0x70000000] - dyn[DT_MIPS_GOTSYM-0x70000000];
for (; i; i--, got+=sizeof(size_t), sym++) {
rel[0] = got;
rel[1] = sym-this->syms << 8 | R_MIPS_JUMP_SLOT;
*(size_t *)(base+got) = 0;
do_relocs(this, rel, sizeof rel, 2);
}
}
#define NEED_ARCH_RELOCS 1
#define DYNAMIC_IS_RO 1
#define ARCH_SYM_REJECT_UND(s) (!((s)->st_other & STO_MIPS_PLT))