summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authornwhitehorn <nwhitehorn@FreeBSD.org>2010-02-22 16:49:45 +0000
committernwhitehorn <nwhitehorn@FreeBSD.org>2010-02-22 16:49:45 +0000
commit911ef3e63a10a36b09b7bd36839f79e0e8dd5758 (patch)
tree1b6081d62ab5d0156c8ca6a59ffbe97bebf44e45
parentece1c414fe52f1415ecb1a5135353ee93c0395d6 (diff)
downloadFreeBSD-src-911ef3e63a10a36b09b7bd36839f79e0e8dd5758.zip
FreeBSD-src-911ef3e63a10a36b09b7bd36839f79e0e8dd5758.tar.gz
Support the extended PLT format used when objects have more than 8192
PLT relocations on PPC32.
-rw-r--r--libexec/rtld-elf/powerpc/reloc.c85
-rw-r--r--libexec/rtld-elf/powerpc/rtld_machdep.h1
-rw-r--r--libexec/rtld-elf/powerpc/rtld_start.S6
3 files changed, 67 insertions, 25 deletions
diff --git a/libexec/rtld-elf/powerpc/reloc.c b/libexec/rtld-elf/powerpc/reloc.c
index 2252428..ccdcd90 100644
--- a/libexec/rtld-elf/powerpc/reloc.c
+++ b/libexec/rtld-elf/powerpc/reloc.c
@@ -47,6 +47,13 @@
((u_int32_t)(x) + 0x10000) : (u_int32_t)(x)) >> 16)
#define _ppc_la(x) ((u_int32_t)(x) & 0xffff)
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+
+#define PLT_EXTENDED_BEGIN (1 << 13)
+#define JMPTAB_BASE(N) (18 + N*2 + ((N > PLT_EXTENDED_BEGIN) ? \
+ (N - PLT_EXTENDED_BEGIN)*2 : 0))
+
/*
* Process the R_PPC_COPY relocations
*/
@@ -313,7 +320,6 @@ done:
return (r);
}
-
/*
* Initialise a PLT slot to the resolving trampoline
*/
@@ -321,27 +327,43 @@ static int
reloc_plt_object(Obj_Entry *obj, const Elf_Rela *rela)
{
Elf_Word *where = (Elf_Word *)(obj->relocbase + rela->r_offset);
- Elf_Addr *pltresolve;
+ Elf_Addr *pltresolve, *pltlongresolve, *jmptab;
Elf_Addr distance;
+ int N = obj->pltrelasize / sizeof(Elf_Rela);
int reloff;
reloff = rela - obj->pltrela;
- if ((reloff < 0) || (reloff >= 0x8000)) {
+ if (reloff < 0)
return (-1);
- }
- pltresolve = obj->pltgot + 8;
+ pltlongresolve = obj->pltgot + 5;
+ pltresolve = pltlongresolve + 5;
distance = (Elf_Addr)pltresolve - (Elf_Addr)(where + 1);
dbg(" reloc_plt_object: where=%p,pltres=%p,reloff=%x,distance=%x",
(void *)where, (void *)pltresolve, reloff, distance);
- /* li r11,reloff */
- /* b pltresolve */
- where[0] = 0x39600000 | reloff;
- where[1] = 0x48000000 | (distance & 0x03fffffc);
+ if (reloff < PLT_EXTENDED_BEGIN) {
+ /* li r11,reloff */
+ /* b pltresolve */
+ where[0] = 0x39600000 | reloff;
+ where[1] = 0x48000000 | (distance & 0x03fffffc);
+ } else {
+ jmptab = obj->pltgot + JMPTAB_BASE(N);
+ jmptab[reloff] = (u_int)pltlongresolve;
+
+ /* lis r11,jmptab[reloff]@ha */
+ /* lwzu r12,jmptab[reloff]@l(r11) */
+ /* mtctr r12 */
+ /* bctr */
+ where[0] = 0x3d600000 | _ppc_ha(&jmptab[reloff]);
+ where[1] = 0x858b0000 | _ppc_la(&jmptab[reloff]);
+ where[2] = 0x7d8903a6;
+ where[3] = 0x4e800420;
+ }
+
/*
* The icache will be sync'd in init_pltgot, which is called
@@ -453,25 +475,28 @@ reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target, const Obj_Entry *defobj,
int N = obj->pltrelasize / sizeof(Elf_Rela);
int reloff = rela - obj->pltrela;
- if ((reloff < 0) || (reloff >= 0x8000)) {
+ if (reloff < 0)
return (-1);
- }
pltcall = obj->pltgot;
- dbg(" reloc_jmpslot: indir, reloff=%d, N=%d\n",
+ dbg(" reloc_jmpslot: indir, reloff=%x, N=%x\n",
reloff, N);
- jmptab = obj->pltgot + 18 + N * 2;
+ jmptab = obj->pltgot + JMPTAB_BASE(N);
jmptab[reloff] = target;
- distance = (Elf_Addr)pltcall - (Elf_Addr)(wherep + 1);
+ if (reloff < PLT_EXTENDED_BEGIN) {
+ /* for extended PLT entries, we keep the old code */
+
+ distance = (Elf_Addr)pltcall - (Elf_Addr)(wherep + 1);
- /* li r11,reloff */
- /* b pltcall # use indirect pltcall routine */
- wherep[0] = 0x39600000 | reloff;
- wherep[1] = 0x48000000 | (distance & 0x03fffffc);
- __syncicache(wherep, 8);
+ /* li r11,reloff */
+ /* b pltcall # use indirect pltcall routine */
+ wherep[0] = 0x39600000 | reloff;
+ wherep[1] = 0x48000000 | (distance & 0x03fffffc);
+ __syncicache(wherep, 8);
+ }
}
return (target);
@@ -481,13 +506,14 @@ reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target, const Obj_Entry *defobj,
/*
* Setup the plt glue routines.
*/
-#define PLTCALL_SIZE 20
-#define PLTRESOLVE_SIZE 24
+#define PLTCALL_SIZE 20
+#define PLTLONGRESOLVE_SIZE 20
+#define PLTRESOLVE_SIZE 24
void
init_pltgot(Obj_Entry *obj)
{
- Elf_Word *pltcall, *pltresolve;
+ Elf_Word *pltcall, *pltresolve, *pltlongresolve;
Elf_Word *jmptab;
int N = obj->pltrelasize / sizeof(Elf_Rela);
@@ -524,18 +550,27 @@ init_pltgot(Obj_Entry *obj)
* of the jumptable into the absolute-call assembler code so it
* can determine this address.
*/
- jmptab = pltcall + 18 + N * 2;
+ jmptab = obj->pltgot + JMPTAB_BASE(N);
pltcall[1] |= _ppc_ha(jmptab); /* addis 11,11,jmptab@ha */
pltcall[2] |= _ppc_la(jmptab); /* lwz 11,jmptab@l(11) */
/*
- * Skip down 32 bytes into the initial reserved area and copy
+ * Skip down 20 bytes into the initial reserved area and copy
* in the standard resolving assembler call. Into this assembler,
* insert the absolute address of the _rtld_bind_start routine
* and the address of the relocation object.
+ *
+ * We place pltlongresolve first, so it can fix up its arguments
+ * and then fall through to the regular PLT resolver.
*/
- pltresolve = obj->pltgot + 8;
+ pltlongresolve = obj->pltgot + 5;
+
+ memcpy(pltlongresolve, _rtld_powerpc_pltlongresolve,
+ PLTLONGRESOLVE_SIZE);
+ pltlongresolve[0] |= _ppc_ha(jmptab); /* lis 12,jmptab@ha */
+ pltlongresolve[1] |= _ppc_la(jmptab); /* addi 12,12,jmptab@l */
+ pltresolve = pltlongresolve + PLTLONGRESOLVE_SIZE/sizeof(uint32_t);
memcpy(pltresolve, _rtld_powerpc_pltresolve, PLTRESOLVE_SIZE);
pltresolve[0] |= _ppc_ha(_rtld_bind_start);
pltresolve[1] |= _ppc_la(_rtld_bind_start);
diff --git a/libexec/rtld-elf/powerpc/rtld_machdep.h b/libexec/rtld-elf/powerpc/rtld_machdep.h
index f5f21a4..bf589c5 100644
--- a/libexec/rtld-elf/powerpc/rtld_machdep.h
+++ b/libexec/rtld-elf/powerpc/rtld_machdep.h
@@ -57,6 +57,7 @@ void _rtld_bind_start(void);
* PLT functions. Not really correct prototypes, but the
* symbol values are needed.
*/
+void _rtld_powerpc_pltlongresolve(void);
void _rtld_powerpc_pltresolve(void);
void _rtld_powerpc_pltcall(void);
diff --git a/libexec/rtld-elf/powerpc/rtld_start.S b/libexec/rtld-elf/powerpc/rtld_start.S
index 86f76e6..00692d2 100644
--- a/libexec/rtld-elf/powerpc/rtld_start.S
+++ b/libexec/rtld-elf/powerpc/rtld_start.S
@@ -163,6 +163,12 @@ _ENTRY(_rtld_bind_start)
* The ELF object is shifted into %r11, and _rtld_bind_start is called
* to complete the binding.
*/
+_ENTRY(_rtld_powerpc_pltlongresolve)
+ lis %r12,0 # lis 12,jmptab@ha
+ addi %r12,%r12,0 # addi 12,12,jmptab@l
+ subf %r11,%r12,%r11 # reloff
+ li %r12,2
+ srw %r11,%r11,%r12 # index = reloff/sizeof(Elf_Addr)
_ENTRY(_rtld_powerpc_pltresolve)
lis %r12,0 # lis 12,_rtld_bind_start@ha
addi %r12,%r12,0 # addi 12,12,_rtld_bind_start@l
OpenPOWER on IntegriCloud