rtld_machine.c revision 1.1
154359Sroberto/* $OpenBSD: rtld_machine.c,v 1.1 2017/01/11 14:11:27 patrick Exp $ */ 2132451Sroberto 354359Sroberto/* 454359Sroberto * Copyright (c) 2004 Dale Rahn 582498Sroberto * 654359Sroberto * Redistribution and use in source and binary forms, with or without 754359Sroberto * modification, are permitted provided that the following conditions 854359Sroberto * are met: 954359Sroberto * 1. Redistributions of source code must retain the above copyright 1054359Sroberto * notice, this list of conditions and the following disclaimer. 1154359Sroberto * 2. Redistributions in binary form must reproduce the above copyright 12182007Sroberto * notice, this list of conditions and the following disclaimer in the 1354359Sroberto * documentation and/or other materials provided with the distribution. 1482498Sroberto * 1582498Sroberto * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 1682498Sroberto * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 1782498Sroberto * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1882498Sroberto * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 1982498Sroberto * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2054359Sroberto * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21132451Sroberto * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2254359Sroberto * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2354359Sroberto * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2454359Sroberto * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25132451Sroberto * SUCH DAMAGE. 26132451Sroberto * 27132451Sroberto */ 2854359Sroberto 2954359Sroberto#define _DYN_LOADER 30132451Sroberto 31132451Sroberto#include <sys/types.h> 32132451Sroberto#include <sys/mman.h> 33132451Sroberto#include <sys/syscall.h> 34132451Sroberto#include <sys/unistd.h> 3554359Sroberto 36132451Sroberto#include <nlist.h> 37132451Sroberto#include <link.h> 38132451Sroberto 39132451Sroberto#include "syscall.h" 4054359Sroberto#include "archdep.h" 4154359Sroberto#include "resolve.h" 4254359Sroberto 4354359Srobertoint64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden; 4454359Sroberto 4554359Srobertovoid _dl_bind_start(void); /* XXX */ 4654359SrobertoElf_Addr _dl_bind(elf_object_t *object, int index); 4754359Sroberto#define _RF_S 0x80000000 /* Resolve symbol */ 4854359Sroberto#define _RF_A 0x40000000 /* Use addend */ 4954359Sroberto#define _RF_P 0x20000000 /* Location relative */ 5054359Sroberto#define _RF_G 0x10000000 /* GOT offset */ 5154359Sroberto#define _RF_B 0x08000000 /* Load address relative */ 5254359Sroberto#define _RF_U 0x04000000 /* Unaligned */ 5354359Sroberto#define _RF_V 0x02000000 /* ERROR */ 5454359Sroberto#define _RF_SZ(s) (((s) & 0xff) << 8) /* memory target size */ 5554359Sroberto#define _RF_RS(s) ((s) & 0xff) /* right shift */ 5654359Srobertostatic int reloc_target_flags[] = { 5754359Sroberto 0, /* 0 NONE */ 5854359Sroberto [ R_AARCH64_ABS64 ] = 5954359Sroberto _RF_V|_RF_S|_RF_A| _RF_SZ(64) | _RF_RS(0), /* ABS64 */ 6054359Sroberto [ R_AARCH64_GLOB_DAT ] = 6154359Sroberto _RF_V|_RF_S|_RF_A| _RF_SZ(64) | _RF_RS(0), /* GLOB_DAT */ 6254359Sroberto [ R_AARCH64_JUMP_SLOT ] = 6354359Sroberto _RF_V|_RF_S| _RF_SZ(64) | _RF_RS(0), /* JUMP_SLOT */ 64132451Sroberto [ R_AARCH64_RELATIVE ] = 6554359Sroberto _RF_V|_RF_B|_RF_A| _RF_SZ(64) | _RF_RS(0), /* REL64 */ 6654359Sroberto [ R_AARCH64_TLSDESC ] = 6754359Sroberto _RF_V|_RF_S, 68132451Sroberto [ R_AARCH64_TLS_TPREL64 ] = 69132451Sroberto _RF_V|_RF_S, 7054359Sroberto [ R_AARCH64_COPY ] = 71132451Sroberto _RF_V|_RF_S| _RF_SZ(32) | _RF_RS(0), /* 20 COPY */ 72132451Sroberto 73132451Sroberto}; 7454359Sroberto 7554359Sroberto#define RELOC_RESOLVE_SYMBOL(t) ((reloc_target_flags[t] & _RF_S) != 0) 7654359Sroberto#define RELOC_PC_RELATIVE(t) ((reloc_target_flags[t] & _RF_P) != 0) 7754359Sroberto#define RELOC_BASE_RELATIVE(t) ((reloc_target_flags[t] & _RF_B) != 0) 78132451Sroberto#define RELOC_UNALIGNED(t) ((reloc_target_flags[t] & _RF_U) != 0) 79132451Sroberto#define RELOC_USE_ADDEND(t) ((reloc_target_flags[t] & _RF_A) != 0) 80132451Sroberto#define RELOC_TARGET_SIZE(t) ((reloc_target_flags[t] >> 8) & 0xff) 8154359Sroberto#define RELOC_VALUE_RIGHTSHIFT(t) (reloc_target_flags[t] & 0xff) 8254359Srobertostatic Elf_Addr reloc_target_bitmask[] = { 8354359Sroberto#define _BM(x) (~(Elf_Addr)0 >> ((8*sizeof(reloc_target_bitmask[0])) - (x))) 8454359Sroberto 0, /* 0 NONE */ 8554359Sroberto [ R_AARCH64_ABS64 ] = _BM(64), 86132451Sroberto [ R_AARCH64_GLOB_DAT ] = _BM(64), 87132451Sroberto [ R_AARCH64_JUMP_SLOT ] = _BM(64), 8854359Sroberto [ R_AARCH64_RELATIVE ] = _BM(64), 8954359Sroberto [ R_AARCH64_TLSDESC ] = _BM(64), 9054359Sroberto [ R_AARCH64_TLS_TPREL64 ] = _BM(64), 9154359Sroberto [ R_AARCH64_COPY ] = _BM(64), 9254359Sroberto#undef _BM 9354359Sroberto}; 9454359Sroberto#define RELOC_VALUE_BITMASK(t) (reloc_target_bitmask[t]) 9554359Sroberto 9654359Sroberto#define R_TYPE(x) R_AARCH64_ ## x 9754359Sroberto 9854359Srobertovoid _dl_reloc_plt(Elf_Word *where, Elf_Addr value, Elf_RelA *rel); 9954359Sroberto 10054359Sroberto#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) 10154359Sroberto 10254359Srobertoint 10354359Sroberto_dl_md_reloc(elf_object_t *object, int rel, int relsz) 10454359Sroberto{ 10554359Sroberto long i; 10654359Sroberto long numrel; 10754359Sroberto long relrel; 108132451Sroberto int fails = 0; 109132451Sroberto Elf_Addr loff; 11054359Sroberto Elf_Addr prev_value = 0; 11154359Sroberto const Elf_Sym *prev_sym = NULL; 11254359Sroberto Elf_RelA *rels; 11354359Sroberto struct load_list *llist; 11454359Sroberto 11554359Sroberto loff = object->obj_base; 11654359Sroberto numrel = object->Dyn.info[relsz] / sizeof(Elf_RelA); 11754359Sroberto relrel = rel == DT_RELA ? object->relcount : 0; 11854359Sroberto rels = (Elf_RelA *)(object->Dyn.info[rel]); 11954359Sroberto 12054359Sroberto if (rels == NULL) 12154359Sroberto return(0); 12254359Sroberto 12354359Sroberto if (relrel > numrel) { 12454359Sroberto _dl_printf("relcount > numrel: %ld > %ld\n", relrel, numrel); 12554359Sroberto _dl_exit(20); 12654359Sroberto } 127132451Sroberto 12854359Sroberto /* 12954359Sroberto * unprotect some segments if we need it. 13054359Sroberto */ 13154359Sroberto if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) { 13254359Sroberto for (llist = object->load_list; 13354359Sroberto llist != NULL; 13454359Sroberto llist = llist->next) { 13554359Sroberto if (!(llist->prot & PROT_WRITE)) 13654359Sroberto _dl_mprotect(llist->start, llist->size, 13754359Sroberto llist->prot|PROT_WRITE); 13854359Sroberto } 13954359Sroberto } 14054359Sroberto 14154359Sroberto /* tight loop for leading RELATIVE relocs */ 14254359Sroberto for (i = 0; i < relrel; i++, rels++) { 14354359Sroberto Elf_Addr *where; 14454359Sroberto 14554359Sroberto#ifdef DEBUG 14654359Sroberto if (ELF_R_TYPE(rels->r_info) != R_TYPE(RELATIVE)) { 14754359Sroberto _dl_printf("RELCOUNT wrong\n"); 14854359Sroberto _dl_exit(20); 14954359Sroberto } 15054359Sroberto#endif 15154359Sroberto where = (Elf_Addr *)(rels->r_offset + loff); 15254359Sroberto *where += loff; 15354359Sroberto } 15454359Sroberto for (; i < numrel; i++, rels++) { 15554359Sroberto Elf_Addr *where, value, ooff, mask; 15654359Sroberto Elf_Word type; 15754359Sroberto const Elf_Sym *sym, *this; 15854359Sroberto const char *symn; 15954359Sroberto 16054359Sroberto type = ELF_R_TYPE(rels->r_info); 16154359Sroberto 16254359Sroberto if (type >= nitems(reloc_target_flags)) { 16354359Sroberto _dl_printf(" bad relocation %d %d\n", i, type); 16454359Sroberto _dl_exit(1); 16554359Sroberto } 16654359Sroberto if ((reloc_target_flags[type] & _RF_V)==0) { 16754359Sroberto _dl_printf(" bad relocation %d %d\n", i, type); 16854359Sroberto _dl_exit(1); 16954359Sroberto } 17054359Sroberto if (type == R_TYPE(NONE)) 17154359Sroberto continue; 17254359Sroberto 17354359Sroberto if (type == R_TYPE(JUMP_SLOT) && rel != DT_JMPREL) 17454359Sroberto continue; 17554359Sroberto 17654359Sroberto where = (Elf_Addr *)(rels->r_offset + loff); 17754359Sroberto 17854359Sroberto if (RELOC_USE_ADDEND(type)) 17954359Sroberto value = rels->r_addend; 18054359Sroberto else 181182007Sroberto value = 0; 182182007Sroberto 183182007Sroberto sym = NULL; 184182007Sroberto symn = NULL; 18554359Sroberto if (RELOC_RESOLVE_SYMBOL(type)) { 186182007Sroberto sym = object->dyn.symtab; 187182007Sroberto sym += ELF_R_SYM(rels->r_info); 188182007Sroberto symn = object->dyn.strtab + sym->st_name; 189182007Sroberto 190182007Sroberto if (sym->st_shndx != SHN_UNDEF && 191182007Sroberto ELF_ST_BIND(sym->st_info) == STB_LOCAL) { 192182007Sroberto value += loff; 193182007Sroberto } else if (sym == prev_sym) { 194182007Sroberto value += prev_value; 195182007Sroberto } else { 196182007Sroberto this = NULL; 197182007Sroberto ooff = _dl_find_symbol_bysym(object, 198182007Sroberto ELF_R_SYM(rels->r_info), &this, 199182007Sroberto SYM_SEARCH_ALL|SYM_WARNNOTFOUND| 20054359Sroberto ((type == R_TYPE(JUMP_SLOT)) ? 20154359Sroberto SYM_PLT : SYM_NOTPLT), 202182007Sroberto sym, NULL); 203182007Sroberto if (this == NULL) { 20454359Srobertoresolve_failed: 205182007Sroberto if (ELF_ST_BIND(sym->st_info) != 20654359Sroberto STB_WEAK) 20754359Sroberto fails++; 20854359Sroberto continue; 20954359Sroberto } 21054359Sroberto prev_sym = sym; 21154359Sroberto prev_value = (Elf_Addr)(ooff + this->st_value); 212132451Sroberto value += prev_value; 21354359Sroberto } 21454359Sroberto } 21554359Sroberto 21654359Sroberto if (type == R_TYPE(JUMP_SLOT)) { 217182007Sroberto /* 21854359Sroberto _dl_reloc_plt((Elf_Word *)where, value, rels); 21954359Sroberto */ 220132451Sroberto *where = value; 221132451Sroberto continue; 222132451Sroberto } 22354359Sroberto 22454359Sroberto if (type == R_TYPE(COPY)) { 22554359Sroberto void *dstaddr = where; 226132451Sroberto const void *srcaddr; 227132451Sroberto const Elf_Sym *dstsym = sym, *srcsym = NULL; 228132451Sroberto Elf_Addr soff; 229132451Sroberto 230132451Sroberto soff = _dl_find_symbol(symn, &srcsym, 231132451Sroberto SYM_SEARCH_OTHER|SYM_WARNNOTFOUND|SYM_NOTPLT, 23254359Sroberto dstsym, object, NULL); 23354359Sroberto if (srcsym == NULL) 234132451Sroberto goto resolve_failed; 235132451Sroberto 23654359Sroberto srcaddr = (void *)(soff + srcsym->st_value); 23754359Sroberto _dl_bcopy(srcaddr, dstaddr, dstsym->st_size); 23854359Sroberto continue; 239132451Sroberto } 24054359Sroberto 24154359Sroberto if (RELOC_PC_RELATIVE(type)) 24254359Sroberto value -= (Elf_Addr)where; 24354359Sroberto if (RELOC_BASE_RELATIVE(type)) 24454359Sroberto value += loff; 24554359Sroberto 24654359Sroberto mask = RELOC_VALUE_BITMASK(type); 247182007Sroberto value >>= RELOC_VALUE_RIGHTSHIFT(type); 24854359Sroberto value &= mask; 24954359Sroberto 25054359Sroberto if (RELOC_UNALIGNED(type)) { 25154359Sroberto /* Handle unaligned relocations. */ 25254359Sroberto Elf_Addr tmp = 0; 25354359Sroberto char *ptr = (char *)where; 25454359Sroberto int i, size = RELOC_TARGET_SIZE(type)/8; 25554359Sroberto 25654359Sroberto /* Read it in one byte at a time. */ 25754359Sroberto for (i=0; i<size; i++) 258132451Sroberto tmp = (tmp << 8) | ptr[i]; 25954359Sroberto 260132451Sroberto tmp &= ~mask; 26154359Sroberto tmp |= value; 26254359Sroberto 263182007Sroberto /* Write it back out. */ 264182007Sroberto for (i=0; i<size; i++) 265132451Sroberto ptr[i] = ((tmp >> (8*i)) & 0xff); 266182007Sroberto } else { 267132451Sroberto *where &= ~mask; 26854359Sroberto *where |= value; 26954359Sroberto } 27054359Sroberto } 27154359Sroberto 272132451Sroberto /* reprotect the unprotected segments */ 273132451Sroberto if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) { 27454359Sroberto for (llist = object->load_list; 27554359Sroberto llist != NULL; 27654359Sroberto llist = llist->next) { 27754359Sroberto if (!(llist->prot & PROT_WRITE)) 27854359Sroberto _dl_mprotect(llist->start, llist->size, 27954359Sroberto llist->prot); 28054359Sroberto } 281132451Sroberto } 282132451Sroberto 28354359Sroberto return (fails); 284132451Sroberto} 285132451Sroberto 286132451Sroberto/* 28754359Sroberto * Relocate the Global Offset Table (GOT). 28854359Sroberto * This is done by calling _dl_md_reloc on DT_JMPREL for DL_BIND_NOW, 28954359Sroberto * otherwise the lazy binding plt initialization is performed. 29054359Sroberto */ 291182007Srobertoint 292132451Sroberto_dl_md_reloc_got(elf_object_t *object, int lazy) 293132451Sroberto{ 29454359Sroberto int fails = 0; 29554359Sroberto Elf_Addr *pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT]; 296132451Sroberto int i, num; 297132451Sroberto Elf_RelA *rel; 29854359Sroberto 29954359Sroberto if (object->Dyn.info[DT_PLTREL] != DT_RELA) 30054359Sroberto return (0); 30154359Sroberto 30254359Sroberto if (object->traced) 30354359Sroberto lazy = 1; 30454359Sroberto 305182007Sroberto lazy = 0; // until support is written. 30654359Sroberto 30754359Sroberto if (!lazy) { 30854359Sroberto fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ); 30954359Sroberto } else { 31054359Sroberto rel = (Elf_RelA *)(object->Dyn.info[DT_JMPREL]); 31154359Sroberto num = (object->Dyn.info[DT_PLTRELSZ]); 31254359Sroberto 31354359Sroberto for (i = 0; i < num/sizeof(Elf_RelA); i++, rel++) { 31454359Sroberto Elf_Addr *where; 31554359Sroberto where = (Elf_Addr *)(rel->r_offset + object->obj_base); 31654359Sroberto *where += object->obj_base; 31754359Sroberto } 31854359Sroberto 319132451Sroberto pltgot[1] = (Elf_Addr)object; 320132451Sroberto pltgot[2] = (Elf_Addr)_dl_bind_start; 32154359Sroberto } 32254359Sroberto 32354359Sroberto /* mprotect the GOT */ 32454359Sroberto _dl_protect_segment(object, 0, "__got_start", "__got_end", PROT_READ); 32554359Sroberto 32654359Sroberto return (fails); 32754359Sroberto} 32854359Sroberto 32954359SrobertoElf_Addr 33054359Sroberto_dl_bind(elf_object_t *object, int relidx) 33154359Sroberto{ 33254359Sroberto Elf_RelA *rel; 33354359Sroberto const Elf_Sym *sym, *this; 33454359Sroberto const char *symn; 33554359Sroberto const elf_object_t *sobj; 33654359Sroberto Elf_Addr ooff; 33754359Sroberto int64_t cookie = pcookie; 33854359Sroberto struct { 33954359Sroberto struct __kbind param; 34054359Sroberto Elf_Addr newval; 34154359Sroberto } buf; 34254359Sroberto 34354359Sroberto rel = ((Elf_RelA *)object->Dyn.info[DT_JMPREL]) + (relidx); 344132451Sroberto 34554359Sroberto sym = object->dyn.symtab; 34654359Sroberto sym += ELF_R_SYM(rel->r_info); 34754359Sroberto symn = object->dyn.strtab + sym->st_name; 34854359Sroberto 34954359Sroberto this = NULL; 35054359Sroberto ooff = _dl_find_symbol(symn, &this, 35154359Sroberto SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym, object, &sobj); 35254359Sroberto if (this == NULL) { 35354359Sroberto _dl_printf("lazy binding failed!\n"); 35454359Sroberto *(volatile int *)0 = 0; /* XXX */ 35554359Sroberto } 35654359Sroberto 35754359Sroberto buf.newval = ooff + this->st_value; 35854359Sroberto 359 if (sobj->traced && _dl_trace_plt(sobj, symn)) 360 return buf.newval; 361 362 buf.param.kb_addr = (Elf_Word *)(object->obj_base + rel->r_offset); 363 buf.param.kb_size = sizeof(Elf_Addr); 364 365 /* directly code the syscall, so that it's actually inline here */ 366 { 367 register long syscall_num __asm("x8") = SYS_kbind; 368 register void *arg1 __asm("x0") = &buf; 369 register long arg2 __asm("x1") = sizeof(buf); 370 register long arg3 __asm("x2") = cookie; 371 372 __asm volatile("svc 0" : "+r" (arg1), "+r" (arg2) 373 : "r" (syscall_num), "r" (arg3) 374 : "cc", "memory"); 375 } 376 377 return buf.newval; 378} 379