aboutsummaryrefslogtreecommitdiff
path: root/libexec
diff options
context:
space:
mode:
authorJohn Baldwin <jhb@FreeBSD.org>2017-09-12 17:46:30 +0000
committerJohn Baldwin <jhb@FreeBSD.org>2017-09-12 17:46:30 +0000
commitb4e9a36bf77882e1c8c20e08fb852ab284e83d19 (patch)
treeca1ec9ab3e449df447876425e6194292037badcc /libexec
parent08d1c5b15284f2046c06b90974c05a2529f6d78e (diff)
downloadsrc-b4e9a36bf77882e1c8c20e08fb852ab284e83d19.tar.gz
src-b4e9a36bf77882e1c8c20e08fb852ab284e83d19.zip
Handle relocations for newer non-PIC MIPS ABI.
Newer binutils supports extensions to the MIPS ABI for non-PIC code that is used when compiling O32 binaries with clang 5 (but not used for N64 oddly enough). These extensions require support for R_MIPS_COPY relocations as well as a second PLT GOT using R_MIPS_JUMP_SLOT relocations. For R_MIPS_COPY, use the same approach as on other architectures where fixups are deferred to the MD do_copy_relocations. The additional PLT GOT for jump slots is located in a .got.plt section which is identified by a DT_MIPS_PLTGOT dynamic entry. This GOT also requires fixups for the first two GOT entries just as the normal GOT. However, the entry point for this second GOT uses a different calling convention. Rather than passing an offset into the GOT, it passes an offset into the .rel.plt section. This requires a second entry point (_rtld_pltbind_start) which calls the normal _rtld_bind() rather than _mips_rtld_bind(). This also means providing a real version of reloc_jmpslot() which is used by _rtld_bind(). In addition, add real implementions of reloc_plt() and reloc_jmpslots() which walk .rel.plt handling R_MIPS_JUMP_SLOT relocations. Reviewed by: kib Sponsored by: DARPA / AFRL Differential Revision: https://reviews.freebsd.org/D12326
Notes
Notes: svn path=/head/; revision=323501
Diffstat (limited to 'libexec')
-rw-r--r--libexec/rtld-elf/mips/reloc.c178
-rw-r--r--libexec/rtld-elf/mips/rtld_start.S94
-rw-r--r--libexec/rtld-elf/rtld.c6
-rw-r--r--libexec/rtld-elf/rtld.h1
4 files changed, 240 insertions, 39 deletions
diff --git a/libexec/rtld-elf/mips/reloc.c b/libexec/rtld-elf/mips/reloc.c
index f816a89912ea..c50611d061d1 100644
--- a/libexec/rtld-elf/mips/reloc.c
+++ b/libexec/rtld-elf/mips/reloc.c
@@ -67,21 +67,98 @@ __FBSDID("$FreeBSD$");
#define GOT1_RESERVED_FOR_RTLD(got) ((got)[1] & GOT1_MASK)
#endif
+#ifdef __mips_n64
+/*
+ * ELF64 MIPS encodes the relocs uniquely. The first 32-bits of info contain
+ * the symbol index. The top 32-bits contain three relocation types encoded
+ * in big-endian integer with first relocation in LSB. This means for little
+ * endian we have to byte swap that integer (r_type).
+ */
+#define Elf_Sxword Elf64_Sxword
+#define ELF_R_NXTTYPE_64_P(r_type) ((((r_type) >> 8) & 0xff) == R_TYPE(64))
+#if BYTE_ORDER == LITTLE_ENDIAN
+#undef ELF_R_SYM
+#undef ELF_R_TYPE
+#define ELF_R_SYM(r_info) ((r_info) & 0xffffffff)
+#define ELF_R_TYPE(r_info) bswap32((r_info) >> 32)
+#endif
+#else
+#define ELF_R_NXTTYPE_64_P(r_type) (0)
+#define Elf_Sxword Elf32_Sword
+#endif
+
+void _rtld_pltbind_start(void);
+
void
init_pltgot(Obj_Entry *obj)
{
+
if (obj->pltgot != NULL) {
obj->pltgot[0] = (Elf_Addr) &_rtld_bind_start;
if (GOT1_RESERVED_FOR_RTLD(obj->pltgot))
obj->pltgot[1] = (Elf_Addr) obj | GOT1_MASK;
}
+ if (obj->mips_pltgot != NULL) {
+ obj->mips_pltgot[0] = (Elf_Addr) &_rtld_pltbind_start;
+ obj->mips_pltgot[1] = (Elf_Addr) obj;
+ }
}
int
do_copy_relocations(Obj_Entry *dstobj)
{
- /* Do nothing */
- return 0;
+ const Obj_Entry *srcobj, *defobj;
+ const Elf_Rel *rellim;
+ const Elf_Rel *rel;
+ const Elf_Sym *srcsym;
+ const Elf_Sym *dstsym;
+ const void *srcaddr;
+ const char *name;
+ void *dstaddr;
+ SymLook req;
+ size_t size;
+ int res;
+
+ /*
+ * COPY relocs are invalid outside of the main program
+ */
+ assert(dstobj->mainprog);
+
+ rellim = (const Elf_Rel *)((caddr_t)dstobj->rel + dstobj->relsize);
+ for (rel = dstobj->rel; rel < rellim; rel++) {
+ if (ELF_R_TYPE(rel->r_info) != R_MIPS_COPY)
+ continue;
+
+ dstaddr = (void *)(dstobj->relocbase + rel->r_offset);
+ dstsym = dstobj->symtab + ELF_R_SYM(rel->r_info);
+ name = dstobj->strtab + dstsym->st_name;
+ size = dstsym->st_size;
+
+ symlook_init(&req, name);
+ req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rel->r_info));
+ req.flags = SYMLOOK_EARLY;
+
+ for (srcobj = globallist_next(dstobj); srcobj != NULL;
+ srcobj = globallist_next(srcobj)) {
+ res = symlook_obj(&req, srcobj);
+ if (res == 0) {
+ srcsym = req.sym_out;
+ defobj = req.defobj_out;
+ break;
+ }
+ }
+ if (srcobj == NULL) {
+ _rtld_error(
+"Undefined symbol \"%s\" referenced from COPY relocation in %s",
+ name, dstobj->path);
+ return (-1);
+ }
+
+ srcaddr = (const void *)(defobj->relocbase + srcsym->st_value);
+ memcpy(dstaddr, srcaddr, size);
+ }
+
+ return (0);
}
void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr);
@@ -90,26 +167,6 @@ void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr);
* It is possible for the compiler to emit relocations for unaligned data.
* We handle this situation with these inlines.
*/
-#ifdef __mips_n64
-/*
- * ELF64 MIPS encodes the relocs uniquely. The first 32-bits of info contain
- * the symbol index. The top 32-bits contain three relocation types encoded
- * in big-endian integer with first relocation in LSB. This means for little
- * endian we have to byte swap that integer (r_type).
- */
-#define Elf_Sxword Elf64_Sxword
-#define ELF_R_NXTTYPE_64_P(r_type) ((((r_type) >> 8) & 0xff) == R_TYPE(64))
-#if BYTE_ORDER == LITTLE_ENDIAN
-#undef ELF_R_SYM
-#undef ELF_R_TYPE
-#define ELF_R_SYM(r_info) ((r_info) & 0xffffffff)
-#define ELF_R_TYPE(r_info) bswap32((r_info) >> 32)
-#endif
-#else
-#define ELF_R_NXTTYPE_64_P(r_type) (0)
-#define Elf_Sxword Elf32_Sword
-#endif
-
static __inline Elf_Sxword
load_ptr(void *where, size_t len)
{
@@ -475,6 +532,20 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
break;
}
+ case R_TYPE(COPY):
+ /*
+ * These are deferred until all other relocations have
+ * been done. All we do here is make sure that the
+ * COPY relocation is not in a shared library. They
+ * are allowed only in executable files.
+ */
+ if (!obj->mainprog) {
+ _rtld_error("%s: Unexpected R_MIPS_COPY "
+ "relocation in shared library", obj->path);
+ return (-1);
+ }
+ break;
+
#ifdef __mips_n64
case R_TYPE(TLS_DTPMOD64):
#else
@@ -581,23 +652,25 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
int
reloc_plt(Obj_Entry *obj)
{
-#if 0
const Elf_Rel *rellim;
const Elf_Rel *rel;
-
- dbg("reloc_plt obj:%p pltrel:%p sz:%s", obj, obj->pltrel, (int)obj->pltrelsize);
- dbg("gottable %p num syms:%s", obj->pltgot, obj->symtabno );
- dbg("*****************************************************");
- rellim = (const Elf_Rel *)((char *)obj->pltrel +
- obj->pltrelsize);
- for (rel = obj->pltrel; rel < rellim; rel++) {
+
+ rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize);
+ for (rel = obj->pltrel; rel < rellim; rel++) {
Elf_Addr *where;
- where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
- *where += (Elf_Addr )obj->relocbase;
+
+ switch (ELF_R_TYPE(rel->r_info)) {
+ case R_MIPS_JUMP_SLOT:
+ where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+ *where += (Elf_Addr )obj->relocbase;
+ break;
+ default:
+ _rtld_error("Unknown relocation type %u in PLT",
+ (unsigned int)ELF_R_TYPE(rel->r_info));
+ return (-1);
+ }
}
-#endif
- /* PLT fixups were done above in the GOT relocation. */
return (0);
}
@@ -607,9 +680,34 @@ reloc_plt(Obj_Entry *obj)
int
reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate)
{
- /* Do nothing */
- obj->jmpslots_done = true;
-
+ const Obj_Entry *defobj;
+ const Elf_Rel *rellim;
+ const Elf_Rel *rel;
+ const Elf_Sym *def;
+
+ rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize);
+ for (rel = obj->pltrel; rel < rellim; rel++) {
+ Elf_Addr *where;
+
+ switch (ELF_R_TYPE(rel->r_info)) {
+ case R_MIPS_JUMP_SLOT:
+ def = find_symdef(ELF_R_SYM(rel->r_info), obj,
+ &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate);
+ if (def == NULL) {
+ dbg("reloc_jmpslots: sym not found");
+ return (-1);
+ }
+
+ where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+ *where = (Elf_Addr)(defobj->relocbase + def->st_value);
+ break;
+ default:
+ _rtld_error("Unknown relocation type %u in PLT",
+ (unsigned int)ELF_R_TYPE(rel->r_info));
+ return (-1);
+ }
+ }
+
return (0);
}
@@ -635,9 +733,11 @@ reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const Obj_Entry *defobj,
const Obj_Entry *obj, const Elf_Rel *rel)
{
- /* Do nothing */
+ assert(ELF_R_TYPE(rel->r_info) == R_MIPS_JUMP_SLOT);
- return target;
+ if (*where != target && !ld_bind_not)
+ *where = target;
+ return (target);
}
void
diff --git a/libexec/rtld-elf/mips/rtld_start.S b/libexec/rtld-elf/mips/rtld_start.S
index 9ec131fd38bf..64d51eed1b63 100644
--- a/libexec/rtld-elf/mips/rtld_start.S
+++ b/libexec/rtld-elf/mips/rtld_start.S
@@ -100,6 +100,9 @@ END(rtld_start)
#define XCALLFRAME_A4 (0*SZREG)
#endif
+/*
+ * Trampoline for "old" PLT stubs which use .got entries.
+ */
.globl _rtld_bind_start
.ent _rtld_bind_start
_rtld_bind_start:
@@ -188,3 +191,94 @@ _rtld_bind_start:
nop
.cfi_endproc
END(_rtld_bind_start)
+
+
+/*
+ * Trampoline for PLT stubs using .pltrel entries and .got.plt.
+ */
+ .globl _rtld_pltbind_start
+ .ent _rtld_pltbind_start
+_rtld_pltbind_start:
+ .frame sp, XCALLFRAME_SIZ, $15
+ .cfi_startproc simple
+ .cfi_register ra, $15
+#if defined(__mips_o32)
+ move v1, gp /* save pointer to .got.plt */
+#else
+ move v1, t2 /* save pointer to .got.plt */
+#endif
+#if defined(__mips_o32) || defined(__mips_o64)
+ PTR_ADDU t9, 8 /* modify T9 to point at .cpload */
+#endif
+ SETUP_GP
+ PTR_SUBU sp, XCALLFRAME_SIZ /* save arguments and sp value in stack */
+ .cfi_def_cfa sp, XCALLFRAME_SIZ
+ SETUP_GP64(XCALLFRAME_GP, _rtld_pltbind_start)
+ SAVE_GP(XCALLFRAME_GP)
+#if defined(__mips_n32) || defined(__mips_n64)
+ REG_S a4, XCALLFRAME_A4(sp)
+ .cfi_rel_offset a4, XCALLFRAME_A4
+ REG_S a5, XCALLFRAME_A5(sp)
+ .cfi_rel_offset a5, XCALLFRAME_A5
+ REG_S a6, XCALLFRAME_A6(sp)
+ .cfi_rel_offset a6, XCALLFRAME_A6
+ REG_S a7, XCALLFRAME_A7(sp)
+ .cfi_rel_offset a7, XCALLFRAME_A7
+#endif
+ REG_S a0, XCALLFRAME_A0(sp)
+ .cfi_rel_offset a0, XCALLFRAME_A0
+ REG_S a1, XCALLFRAME_A1(sp)
+ .cfi_rel_offset a1, XCALLFRAME_A1
+ REG_S a2, XCALLFRAME_A2(sp)
+ .cfi_rel_offset a2, XCALLFRAME_A2
+ REG_S a3, XCALLFRAME_A3(sp)
+ .cfi_rel_offset a3, XCALLFRAME_A3
+ REG_S $15, XCALLFRAME_RA(sp) /* ra is in t7/t3 */
+ .cfi_rel_offset ra, XCALLFRAME_RA
+ REG_S s0, XCALLFRAME_S0(sp)
+ .cfi_rel_offset s0, XCALLFRAME_S0
+ move s0, sp
+
+ move a0, v1 /* .got.plt */
+#if defined(__mips_n64)
+ ld a0, 8(a0) /* object = .got.plt[1] */
+ sll a1, t8, 4 /* PLT entry index to .rel.plt offset */
+#else
+ lw a0, 4(a0) /* object = .got.plt[1] */
+ sll a1, t8, 3 /* PLT entry index to .rel.plt offset */
+#endif
+
+ PTR_LA t9, _C_LABEL(_rtld_bind)
+ jalr t9
+ nop
+
+ move sp, s0
+ REG_L ra, XCALLFRAME_RA(sp)
+ .cfi_restore ra
+ REG_L s0, XCALLFRAME_S0(sp)
+ .cfi_restore s0
+ REG_L a0, XCALLFRAME_A0(sp)
+ .cfi_restore a0
+ REG_L a1, XCALLFRAME_A1(sp)
+ .cfi_restore a1
+ REG_L a2, XCALLFRAME_A2(sp)
+ .cfi_restore a2
+ REG_L a3, XCALLFRAME_A3(sp)
+ .cfi_restore a3
+#if defined(__mips_n32) || defined(__mips_n64)
+ REG_L a4, XCALLFRAME_A4(sp)
+ .cfi_restore a4
+ REG_L a5, XCALLFRAME_A5(sp)
+ .cfi_restore a5
+ REG_L a6, XCALLFRAME_A6(sp)
+ .cfi_restore a6
+ REG_L a7, XCALLFRAME_A7(sp)
+ .cfi_restore a7
+#endif
+ RESTORE_GP64
+ PTR_ADDU sp, XCALLFRAME_SIZ
+ move t9, v0
+ jr t9
+ nop
+ .cfi_endproc
+END(_rtld_pltbind_start)
diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c
index 76fbbf6c3f2d..78e6299a9fdf 100644
--- a/libexec/rtld-elf/rtld.c
+++ b/libexec/rtld-elf/rtld.c
@@ -1257,6 +1257,12 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath,
case DT_MIPS_RLD_MAP:
*((Elf_Addr *)(dynp->d_un.d_ptr)) = (Elf_Addr) &r_debug;
break;
+
+ case DT_MIPS_PLTGOT:
+ obj->mips_pltgot = (Elf_Addr *) (obj->relocbase +
+ dynp->d_un.d_ptr);
+ break;
+
#endif
#ifdef __powerpc64__
diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h
index 19fb8fc51a19..e6c2c3a70752 100644
--- a/libexec/rtld-elf/rtld.h
+++ b/libexec/rtld-elf/rtld.h
@@ -187,6 +187,7 @@ typedef struct Struct_Obj_Entry {
Elf_Word local_gotno; /* Number of local GOT entries */
Elf_Word symtabno; /* Number of dynamic symbols */
Elf_Word gotsym; /* First dynamic symbol in GOT */
+ Elf_Addr *mips_pltgot; /* Second PLT GOT */
#endif
#ifdef __powerpc64__
Elf_Addr glink; /* GLINK PLT call stub section */