From f62da49b2f17f279ddba946bf4bc7ba7247273a5 Mon Sep 17 00:00:00 2001 From: Justin Hibbits Date: Tue, 25 Jun 2019 00:40:44 +0000 Subject: [PATCH] powerpc: Transition to Secure-PLT, like most other OSs Summary: PowerPC has two PLT models: BSS-PLT and Secure-PLT. BSS-PLT uses runtime code generation to generate the PLT stubs. Secure-PLT was introduced with GCC 4.1 and Binutils 2.17 (base has GCC 4.2.1 and Binutils 2.17), and is a more secure PLT format, using a read-only linkage table, with the dynamic linker populating a non-executable index table. This is the libc, rtld, and kernel support only. The toolchain and build parts will be updated separately. Reviewed By: nwhitehorn, bdragon, pfg Differential Revision: https://reviews.freebsd.org/D20598 MFC after: 1 month --- contrib/gcc/config/rs6000/tramp.asm | 3 +- lib/libc/powerpc/SYS.h | 7 ++- lib/libc/powerpc/gen/_ctx_start.S | 9 +++- lib/libc/powerpc/sys/cerror.S | 23 +++++++--- libexec/rtld-elf/powerpc/reloc.c | 27 +++++++++++- libexec/rtld-elf/powerpc/rtld_start.S | 63 ++++++++++++++++----------- libexec/rtld-elf/rtld.c | 6 +++ libexec/rtld-elf/rtld.h | 4 ++ sys/powerpc/powerpc/machdep.c | 13 ++++++ 9 files changed, 116 insertions(+), 39 deletions(-) diff --git a/contrib/gcc/config/rs6000/tramp.asm b/contrib/gcc/config/rs6000/tramp.asm index 013cac363b6..fe05401e039 100644 --- a/contrib/gcc/config/rs6000/tramp.asm +++ b/contrib/gcc/config/rs6000/tramp.asm @@ -38,6 +38,7 @@ .file "tramp.asm" .section ".text" #include "ppc-asm.h" + #include "auto-host.h" #ifndef __powerpc64__ .type trampoline_initial,@object @@ -105,7 +106,7 @@ FUNC_START(__trampoline_setup) blr .Labort: -#if defined SHARED && defined HAVE_AS_REL16 +#if (defined(__PIC__) || defined(__pic__)) && defined HAVE_AS_REL16 bcl 20,31,1f 1: mflr r30 addis r30,r30,_GLOBAL_OFFSET_TABLE_-1b@ha diff --git a/lib/libc/powerpc/SYS.h b/lib/libc/powerpc/SYS.h index 63a5ec47c4b..e9b6d3226c9 100644 --- a/lib/libc/powerpc/SYS.h +++ b/lib/libc/powerpc/SYS.h @@ -44,7 +44,7 @@ #define SYSCALL(name) \ .text; \ .align 2; \ -2: b PIC_PLT(CNAME(HIDENAME(cerror))); \ +2: b CNAME(HIDENAME(cerror)); \ ENTRY(__sys_##name); \ WEAK_REFERENCE(__sys_##name, name); \ WEAK_REFERENCE(__sys_##name, _##name); \ @@ -58,15 +58,14 @@ ENTRY(__sys_##name); \ WEAK_REFERENCE(__sys_##name, _##name); \ _SYSCALL(name); \ bnslr; \ - b PIC_PLT(CNAME(HIDENAME(cerror))) + b CNAME(HIDENAME(cerror)) #define RSYSCALL(name) \ .text; \ .align 2; \ -2: b PIC_PLT(CNAME(HIDENAME(cerror))); \ ENTRY(__sys_##name); \ WEAK_REFERENCE(__sys_##name, name); \ WEAK_REFERENCE(__sys_##name, _##name); \ _SYSCALL(name); \ bnslr; \ - b PIC_PLT(CNAME(HIDENAME(cerror))) + b CNAME(HIDENAME(cerror)) diff --git a/lib/libc/powerpc/gen/_ctx_start.S b/lib/libc/powerpc/gen/_ctx_start.S index 4b9fc1d53ae..70b4a9d91c8 100644 --- a/lib/libc/powerpc/gen/_ctx_start.S +++ b/lib/libc/powerpc/gen/_ctx_start.S @@ -35,11 +35,18 @@ mtlr %r14 blrl /* branch to start function */ mr %r3,%r15 /* pass pointer to ucontext as argument */ - bl PIC_PLT(CNAME(_ctx_done)) /* branch to ctxt completion func */ + bl CNAME(_ctx_done) /* branch to ctxt completion func */ + /* * we should never return from the * above branch. */ + /* Don't bother saving off %r30, we're already in a bad state. */ + bcl 20,31,1f +1: mflr %r30 + mr %r3,%r30 # save for _DYNAMIC + addis %r30,%r30,_GLOBAL_OFFSET_TABLE_-1b@ha + addi %r30,%r30,_GLOBAL_OFFSET_TABLE_-1b@l bl PIC_PLT(CNAME(abort)) /* abort */ END(_cts_start) diff --git a/lib/libc/powerpc/sys/cerror.S b/lib/libc/powerpc/sys/cerror.S index 7667cb8361d..c2bc994b9c3 100644 --- a/lib/libc/powerpc/sys/cerror.S +++ b/lib/libc/powerpc/sys/cerror.S @@ -40,16 +40,27 @@ __FBSDID("$FreeBSD$"); */ HIDENAME(cerror): mflr %r0 - stwu %r1,-16(%r1) /* allocate new stack frame */ - stw %r0,20(%r1) /* and save lr, r31 */ - stw %r31,8(%r1) + stwu %r1,-20(%r1) /* allocate new stack frame */ + stw %r0,24(%r1) /* and save lr, r31 */ + stw %r31,12(%r1) +#ifdef __PIC__ + stw %r30,8(%r1) + bcl 20,31,1f +1: + mflr %r30 + addis %r30,%r30,_GLOBAL_OFFSET_TABLE_-1b@ha + addi %r30,%r30,_GLOBAL_OFFSET_TABLE_-1b@l +#endif mr %r31,%r3 /* stash errval in callee-saved register */ bl PIC_PLT(CNAME(__error)) stw %r31,0(%r3) /* store errval into &errno */ - lwz %r0,20(%r1) - lwz %r31,8(%r1) + lwz %r0,24(%r1) + lwz %r31,12(%r1) +#ifdef __PIC__ + lwz %r30,8(%r1) +#endif mtlr %r0 - la %r1,16(%r1) + la %r1,20(%r1) li %r3,-1 li %r4,-1 blr /* return to callers caller */ diff --git a/libexec/rtld-elf/powerpc/reloc.c b/libexec/rtld-elf/powerpc/reloc.c index 382417ca349..c923c732607 100644 --- a/libexec/rtld-elf/powerpc/reloc.c +++ b/libexec/rtld-elf/powerpc/reloc.c @@ -57,6 +57,8 @@ #define JMPTAB_BASE(N) (18 + N*2 + ((N > PLT_EXTENDED_BEGIN) ? \ (N - PLT_EXTENDED_BEGIN)*2 : 0)) +void _rtld_bind_secureplt_start(void); + /* * Process the R_PPC_COPY relocations */ @@ -361,6 +363,11 @@ reloc_plt_object(Obj_Entry *obj, const Elf_Rela *rela) if (reloff < 0) return (-1); + if (obj->gotptr != NULL) { + *where += (Elf_Addr)obj->relocbase; + return (0); + } + pltlongresolve = obj->pltgot + 5; pltresolve = pltlongresolve + 5; @@ -425,7 +432,7 @@ reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused) * Sync the icache for the byte range represented by the * trampoline routines and call slots. */ - if (obj->pltgot != NULL) + if (obj->pltgot != NULL && obj->gotptr == NULL) __syncicache(obj->pltgot, JMPTAB_BASE(N)*4); return (0); @@ -501,6 +508,14 @@ reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target, */ offset = target - (Elf_Addr)wherep; + if (obj->gotptr != NULL) { + assert(wherep >= (Elf_Word *)obj->pltgot); + assert(wherep < + (Elf_Word *)obj->pltgot + obj->pltrelasize); + *wherep = target; + goto out; + } + if (abs((int)offset) < 32*1024*1024) { /* inside 32MB? */ /* b value # branch directly */ *wherep = 0x48000000 | (offset & 0x03fffffc); @@ -579,6 +594,16 @@ init_pltgot(Obj_Entry *obj) return; } + /* Handle Secure-PLT first, if applicable. */ + if (obj->gotptr != NULL) { + obj->gotptr[1] = (Elf_Addr)_rtld_bind_secureplt_start; + obj->gotptr[2] = (Elf_Addr)obj; + dbg("obj %s secure-plt gotptr=%p start=%p obj=%p", + obj->path, obj->gotptr, + (void *)obj->gotptr[1], (void *)obj->gotptr[2]); + return; + } + /* * From the SVR4 PPC ABI: * diff --git a/libexec/rtld-elf/powerpc/rtld_start.S b/libexec/rtld-elf/powerpc/rtld_start.S index 28ec19bad11..5a2cd7bd265 100644 --- a/libexec/rtld-elf/powerpc/rtld_start.S +++ b/libexec/rtld-elf/powerpc/rtld_start.S @@ -52,35 +52,22 @@ _ENTRY(.rtld_start) * - use link-time constants to determine offset to the * _DYNAMIC section and the GOT. Add these to the PC to * convert to absolute addresses. - * - sync icache to allow execution of the SVR4 ABI-specified - * blrl instruction preceding the GOT - * - Use this instruction to determine the GOT absolute address * - read GOT[0], which is the SVR4 ABI-specified link-time * value of _DYNAMIC. Subtract this value from the absolute * value to determine the load address * - call reloc_non_plt_self() to fix up ld-elf.so's relocations */ - bl 1f - .long _DYNAMIC-. - .long _GLOBAL_OFFSET_TABLE_-. /* branch lr + 4 */ -1: - mflr %r3 /* PC value at .long */ - lwz %r4,4(%r3) - add %r4,%r4,%r3 /* &_GLOBAL_OFFSET_TABLE-4, blrl insn. */ - dcbst %r0,%r4 /* sync i-cache with d-cache */ - sync - icbi %r0,%r4 - isync - - lwz %r4,0(%r3) /* offset to _DYNAMIC */ - add %r3,%r4,%r3 /* r3 = &_DYNAMIC, absolute value */ - - bl _GLOBAL_OFFSET_TABLE_@local-4 - mflr %r4 /* &_GLOBAL_OFFSET_TABLE_, absolute value */ - lwz %r4,0(%r4) /* linker &_DYNAMIC, from got[0] */ - subf %r4,%r4,%r3 /* subtract to calculate relocbase */ - - bl reloc_non_plt_self@plt /* reloc_non_plt_self(&_DYNAMIC,base) */ + bcl 20,31,1f +1: mflr %r30 + mr %r3,%r30 # save for _DYNAMIC + addis %r30,%r30,_GLOBAL_OFFSET_TABLE_-1b@ha + addi %r30,%r30,_GLOBAL_OFFSET_TABLE_-1b@l + addis %r3,%r3,_DYNAMIC-1b@ha # get _DYNAMIC actual address + addi %r3,%r3,_DYNAMIC-1b@l + lwz %r28,0(%r30) # get base-relative &_DYNAMIC + sub %r28,%r3,%r28 # r28 = relocbase + mr %r4,%r28 # r4 = relocbase + bl reloc_non_plt_self /* reloc_non_plt_self(&_DYNAMIC,base) */ /* * The _rtld() function likes to see a stack layout containing @@ -95,7 +82,7 @@ _ENTRY(.rtld_start) addi %r4,%r1,8 /* &exit_proc on stack */ addi %r5,%r1,12 /* &obj_main on stack */ - bl _rtld@plt /* &_start = _rtld(sp, &exit_proc, &obj_main)*/ + bl _rtld /* &_start = _rtld(sp, &exit_proc, &obj_main)*/ mtlr %r3 /* @@ -114,6 +101,29 @@ _ENTRY(.rtld_start) li %r0,1 /* _exit() */ sc +/* + * _rtld_bind_secureplt_start() + * + * Call into the MI binder (Secure-PLT stub). + * secure-plt expects %r11 to be the offset to the rela entry. + * bss-plt expects %r11 to be index of the rela entry. + * So for bss-plt, we multiply the index by 12 to get the offset. + */ +_ENTRY(_rtld_bind_secureplt_start) + stwu %r1,-160(%r1) # stack space for 29 regs + r0/lr/cr + stw %r0,20(%r1) # save r0 + + /* + * Instead of division which is costly we will use multiplicative + * inverse. a / n = ((a * inv(n)) >> 32) + * where inv(n) = (0x100000000 + n - 1) / n + */ + mr %r0,%r11 + lis %r11,0x15555556@h # load multiplicative inverse of 12 + ori %r11,%r11,0x15555556@l + mulhwu %r11,%r11,%r0 # get high half of multiplication + b 1f + /* * _rtld_bind_start() * @@ -129,6 +139,7 @@ _ENTRY(.rtld_start) _ENTRY(_rtld_bind_start) stwu %r1,-160(%r1) # stack space for 29 regs + r0/lr/cr stw %r0,20(%r1) # save r0 +1: mflr %r0 stw %r0,16(%r1) # save lr mfcr %r0 @@ -137,7 +148,7 @@ _ENTRY(_rtld_bind_start) mr %r3,%r12 # obj mulli %r4,%r11,12 # rela index * sizeof(Elf_Rela) - bl _rtld_bind@PLT # target addr = _rtld_bind(obj, reloff) + bl _rtld_bind # target addr = _rtld_bind(obj, reloff) mtctr %r3 # move absolute target addr into ctr lmw %r3,24(%r1) # restore r3-r31 diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index 7cdd5c58c5d..ece58e13823 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -1286,10 +1286,16 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath, #endif +#ifdef __powerpc__ #ifdef __powerpc64__ case DT_PPC64_GLINK: obj->glink = (Elf_Addr)(obj->relocbase + dynp->d_un.d_ptr); break; +#else + case DT_PPC_GOT: + obj->gotptr = (Elf_Addr *)(obj->relocbase + dynp->d_un.d_ptr); + break; +#endif #endif case DT_FLAGS_1: diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index d7ef9d56b8d..fc9f1025581 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -190,8 +190,12 @@ typedef struct Struct_Obj_Entry { Elf_Word gotsym; /* First dynamic symbol in GOT */ Elf_Addr *mips_pltgot; /* Second PLT GOT */ #endif +#ifdef __powerpc__ #ifdef __powerpc64__ Elf_Addr glink; /* GLINK PLT call stub section */ +#else + Elf_Addr *gotptr; /* GOT pointer (secure-plt only) */ +#endif #endif const Elf_Verneed *verneed; /* Required versions. */ diff --git a/sys/powerpc/powerpc/machdep.c b/sys/powerpc/powerpc/machdep.c index 6fb874e31d9..0bd6a11197a 100644 --- a/sys/powerpc/powerpc/machdep.c +++ b/sys/powerpc/powerpc/machdep.c @@ -595,3 +595,16 @@ bzero(void *buf, size_t len) len--; } } + +/* __stack_chk_fail_local() is called in secure-plt (32-bit). */ +#if !defined(__powerpc64__) +extern void __stack_chk_fail(void); +void __stack_chk_fail_local(void); + +void +__stack_chk_fail_local(void) +{ + + __stack_chk_fail(); +} +#endif -- 2.45.0