From 9af6ea0bc9db91e151fb7c34862c667b7acb584b Mon Sep 17 00:00:00 2001 From: Mark Salter Date: Wed, 6 Jun 2012 16:44:45 -0400 Subject: Update C6X support This patch updates the C6X support to work with latest uClibc code and uses reworked DSBT support to allow using kernel FDPIC loader. Signed-off-by: Mark Salter Signed-off-by: Bernhard Reutner-Fischer --- ldso/ldso/c6x/dl-startup.h | 72 ++++++++++++++++++++++++++++++++-- ldso/ldso/c6x/dl-sysdep.h | 51 +++++++++++++----------- ldso/ldso/c6x/elfinterp.c | 32 ++++++++------- libc/sysdeps/linux/c6x/bits/elf-dsbt.h | 9 +---- 4 files changed, 118 insertions(+), 46 deletions(-) diff --git a/ldso/ldso/c6x/dl-startup.h b/ldso/ldso/c6x/dl-startup.h index 70a8b89a7..c83e33cb3 100644 --- a/ldso/ldso/c6x/dl-startup.h +++ b/ldso/ldso/c6x/dl-startup.h @@ -6,10 +6,9 @@ * * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. */ - #undef DL_START #define DL_START(X) \ -int \ +static void * __attribute_used__ \ _dl_start (unsigned placeholder, \ struct elf32_dsbt_loadmap *dl_boot_progmap, \ struct elf32_dsbt_loadmap *dl_boot_ldsomap, \ @@ -34,7 +33,6 @@ _dl_start (unsigned placeholder, \ * B4 --> executable loadmap address * A6 --> interpreter loadmap address * B6 --> dynamic section address - * B14 --> our DP setup by kernel * * NB: DSBT index is always 0 for the executable * and 1 for the interpreter @@ -44,6 +42,74 @@ __asm__(" .text\n" ".globl _start\n" ".hidden _start\n" "_start:\n" + /* Find interpreter DSBT base in dynamic section */ + " MV .S2 B6,B2\n" + " || ADD .D1X B6,4,A2\n" + " LDW .D2T2 *B2++[2],B0\n" + " || LDW .D1T1 *A2++[2],A0\n" + " MVKL .S2 " __stringify(DT_C6000_DSBT_BASE) ",B7\n" + " MVKH .S2 " __stringify(DT_C6000_DSBT_BASE) ",B7\n" + " NOP\n" + " NOP\n" + /* + * B0 now holds dynamic tag and A0 holds tag value. + * Loop through looking for DSBT base tag + */ + "0:\n" + " [B0] CMPEQ .L2 B0,B7,B1\n" + " || [!B0] MVK .S2 1,B1\n" + " [!B1] BNOP .S1 0b,5\n" + " ||[!B1] LDW .D2T2 *B2++[2],B0\n" + " ||[!B1] LDW .D1T1 *A2++[2],A0\n" + /* + * DSBT base in A0 needs to be relocated. + * Search through our loadmap to find where it got loaded. + * + * struct elf32_dsbt_loadmap { + * Elf32_Half version; + * Elf32_Half nsegs; + * struct { + * Elf32_Addr addr; + * Elf32_Addr p_vaddr; + * Elf32_Word p_memsz; + * } segments[]; + * } + * + */ + " MV .S1 A6,A1\n" + " [!A1] MV .S1X B4,A1\n" + " ADD .D1 A1,2,A3\n" + " LDHU .D1T2 *A3++[1],B0\n" /* nsegs */ + " LDW .D1T1 *A3++[1],A10\n" /* addr */ + " LDW .D1T1 *A3++[1],A11\n" /* p_vaddr */ + " LDW .D1T1 *A3++[1],A12\n" /* p_memsz */ + " NOP\n" + " NOP\n" + /* + * Here we have: + * B0 -> number of segments to search. + * A3 -> pointer to next segment to check + * A10 -> segment load address + * A11 -> ELF segment virt address + * A12 -> ELF segment size + */ + "0:\n" + " [!B0] B .S2 0f\n" + " SUB .D2 B0,1,B0\n" + " CMPLTU .L1 A0,A11,A13\n" + " || SUB .S1 A12,1,A12\n" + " ADD .D1 A11,A12,A12\n" + " CMPGTU .L1 A0,A12,A14\n" + " OR .L1 A13,A14,A2\n" + " [A2] B .S2 0b\n" + " || [!A2] SUB .L1 A0,A11,A0\n" + " [B0] LDW .D1T1 *A3++[1],A10\n" /* addr */ + " || [!A2] ADD .L1 A0,A10,A0\n" + " [B0] LDW .D1T1 *A3++[1],A11\n" /* p_vaddr */ + " [B0] LDW .D1T1 *A3++[1],A12\n" /* p_memsz */ + " MV .S2X A0,B14\n" + " NOP\n" + "0:\n" " B .S2 _dl_start\n" " STW .D2T2 B14, *+B14[1]\n" " ADD .D1X B15,8,A8\n" diff --git a/ldso/ldso/c6x/dl-sysdep.h b/ldso/ldso/c6x/dl-sysdep.h index 0dbe8bf90..c2e91d2f0 100644 --- a/ldso/ldso/c6x/dl-sysdep.h +++ b/ldso/ldso/c6x/dl-sysdep.h @@ -52,13 +52,13 @@ extern int _dl_linux_resolve(void) attribute_hidden; struct funcdesc_ht; struct elf32_dsbt_loadaddr; -/* We must force strings used early in the bootstrap into the text - segment (const data), such that they are referenced relative to - the DP register rather than through the GOT which will not have - been relocated when these are used. */ +/* Current toolchains access constant strings via unrelocated GOT + entries. Fortunately, we have enough in place to just call the + relocation function early on. */ #undef SEND_EARLY_STDERR #define SEND_EARLY_STDERR(S) \ - do { static char __s[] = (S); SEND_STDERR (__s); } while (0) + do { char *__p = __reloc_pointer((S), dl_boot_ldsomap?:dl_boot_progmap);\ + SEND_STDERR (__p); } while (0) #define DL_LOADADDR_TYPE struct elf32_dsbt_loadaddr @@ -114,7 +114,7 @@ struct elf32_dsbt_loadaddr; (__dl_loadaddr_unmap ((LIB)->loadaddr)) #define DL_LOADADDR_BASE(LOADADDR) \ - ((LOADADDR).map->dsbt_table) + ((LOADADDR).map) #define DL_ADDR_IN_LOADADDR(ADDR, TPNT, TFROM) \ (! (TFROM) && __dl_addr_in_loadaddr ((void*)(ADDR), (TPNT)->loadaddr)) @@ -150,18 +150,28 @@ while (0) /* - * Compute the GOT address. - * Also setup program and interpreter DSBT table entries. + * C6X doesn't really need the GOT here. + * The GOT is placed just past the DSBT table, so we could find it by + * using the DSBT register + table size found in the dynamic section. + * + * do { \ + * unsigned long *ldso_dsbt; \ + * ElfW(Dyn) *d = dl_boot_ldso_dyn_pointer; \ + * while (d->d_tag != DT_NULL) { \ + * if (d->d_tag == DT_C6000_DSBT_SIZE) { \ + * __asm__ (" MV .S2 B14,%0\n" \ + * : "=b" (ldso_dsbt)); \ + * (GOT) = ldso_dsbt + d->d_un.d_val; \ + * break; \ + * } \ + * d++; \ + * } \ + * } while(0) + * + * Instead, just point it to the DSBT table to avoid unused variable warning. */ #define DL_BOOT_COMPUTE_GOT(GOT) \ - do { \ - unsigned long *ldso_dsbt, *prog_dsbt; \ - ldso_dsbt = dl_boot_ldsomap->dsbt_table; \ - prog_dsbt = dl_boot_progmap->dsbt_table; \ - ldso_dsbt[0] = prog_dsbt[0] = (unsigned long)prog_dsbt; \ - ldso_dsbt[1] = prog_dsbt[1] = (unsigned long)ldso_dsbt; \ - (GOT) = ldso_dsbt + dl_boot_ldsomap->dsbt_size; \ - } while(0) + __asm__ (" MV .S2 B14,%0\n" : "=b" (GOT)) #define DL_BOOT_COMPUTE_DYN(dpnt, got, load_addr) \ ((dpnt) = dl_boot_ldso_dyn_pointer) @@ -186,12 +196,9 @@ while (0) # undef __USE_GNU #endif -static __always_inline Elf32_Addr -elf_machine_load_address (void) -{ - /* this is never an issue on DSBT systems */ - return 0; -} +/* we need this for __LDSO_STANDALONE_SUPPORT__ */ +#define elf_machine_load_address() \ + (dl_boot_ldsomap ?: dl_boot_progmap)->segs[0].addr static __always_inline void elf_machine_relative (DL_LOADADDR_TYPE load_off, const Elf32_Addr rel_addr, diff --git a/ldso/ldso/c6x/elfinterp.c b/ldso/ldso/c6x/elfinterp.c index 3772f90b3..f0e05b9d0 100644 --- a/ldso/ldso/c6x/elfinterp.c +++ b/ldso/ldso/c6x/elfinterp.c @@ -69,14 +69,12 @@ _dl_linux_resolver (struct elf_resolve *tpnt, int reloc_entry) got_addr = (char **) DL_RELOC_ADDR(tpnt->loadaddr, this_reloc->r_offset); /* Get the address to be used to fill in the GOT entry. */ - new_addr = _dl_find_hash(symname, tpnt->symbol_scope, tpnt, - ELF_RTYPE_CLASS_PLT, NULL); + new_addr = _dl_find_hash(symname, &_dl_loaded_modules->symbol_scope, tpnt, ELF_RTYPE_CLASS_PLT, NULL); if (unlikely(!new_addr)) { _dl_dprintf(2, "%s: can't resolve symbol '%s' in lib '%s'.\n", _dl_progname, symname, tpnt->libname); _dl_exit(1); } - #if defined (__SUPPORT_LD_DEBUG__) if (_dl_debug_bindings) { _dl_dprintf(_dl_debug_file, "\nresolve function: %s", symname); @@ -96,9 +94,9 @@ _dl_linux_resolver (struct elf_resolve *tpnt, int reloc_entry) } static int -_dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope, +_dl_parse(struct elf_resolve *tpnt, struct r_scope_elem *scope, unsigned long rel_addr, unsigned long rel_size, - int (*reloc_fnc) (struct elf_resolve *tpnt, struct dyn_elf *scope, + int (*reloc_fnc) (struct elf_resolve *tpnt, struct r_scope_elem *scope, ELF_RELOC *rpnt, ElfW(Sym) *symtab, char *strtab)) { unsigned int i; @@ -148,7 +146,7 @@ _dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope, } static int -_dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope, +_dl_do_reloc (struct elf_resolve *tpnt,struct r_scope_elem *scope, ELF_RELOC *rpnt, ElfW(Sym) *symtab, char *strtab) { int reloc_type; @@ -157,7 +155,9 @@ _dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope, unsigned long *reloc_addr; unsigned long symbol_addr, sym_val; long reloc_addend; - unsigned long old_val, new_val; + unsigned long old_val, new_val = 0; + struct symbol_ref sym_ref; + struct elf_resolve *symbol_tpnt; reloc_addr = (unsigned long *)(intptr_t) DL_RELOC_ADDR (tpnt->loadaddr, rpnt->r_offset); @@ -167,14 +167,17 @@ _dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope, symtab_index = ELF_R_SYM(rpnt->r_info); symbol_addr = 0; symname = strtab + symtab[symtab_index].st_name; + sym_ref.sym = &symtab[symtab_index]; + sym_ref.tpnt = NULL; if (ELF_ST_BIND (symtab[symtab_index].st_info) == STB_LOCAL) { symbol_addr = (unsigned long) DL_RELOC_ADDR (tpnt->loadaddr, symtab[symtab_index].st_value); + symbol_tpnt = tpnt; } else { - symbol_addr = (unsigned long) _dl_find_hash(strtab + symtab[symtab_index].st_name, - scope, tpnt, elf_machine_type_class(reloc_type), - NULL); + symbol_addr = (unsigned long) _dl_find_hash(symname, + scope, NULL, elf_machine_type_class(reloc_type), + &sym_ref); /* * We want to allow undefined references to weak symbols - this might * have been intentional. We should not be linking local symbols @@ -186,6 +189,7 @@ _dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope, _dl_progname, strtab + symtab[symtab_index].st_name); _dl_exit (1); } + symbol_tpnt = sym_ref.tpnt; } old_val = *reloc_addr; sym_val = symbol_addr + reloc_addend; @@ -199,7 +203,7 @@ _dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope, *reloc_addr = sym_val; break; case R_C6000_DSBT_INDEX: - new_val = (old_val & ~0x007fff00) | ((tpnt->loadaddr.map->dsbt_index & 0x7fff) << 8); + new_val = (old_val & ~0x007fff00) | ((symbol_tpnt->dsbt_index & 0x7fff) << 8); *reloc_addr = new_val; break; case R_C6000_ABS_L16: @@ -242,7 +246,7 @@ _dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope, static int _dl_do_lazy_reloc (struct elf_resolve *tpnt, - struct dyn_elf *scope attribute_unused, + struct r_scope_elem *scope attribute_unused, ELF_RELOC *rpnt, ElfW(Sym) *symtab attribute_unused, char *strtab attribute_unused) { @@ -283,9 +287,9 @@ _dl_parse_lazy_relocation_information int _dl_parse_relocation_information -(struct dyn_elf *rpnt, unsigned long rel_addr, unsigned long rel_size) +(struct dyn_elf *rpnt, struct r_scope_elem *scope, unsigned long rel_addr, unsigned long rel_size) { - return _dl_parse(rpnt->dyn, rpnt->dyn->symbol_scope, rel_addr, rel_size, _dl_do_reloc); + return _dl_parse(rpnt->dyn, scope, rel_addr, rel_size, _dl_do_reloc); } /* We don't have copy relocs. */ diff --git a/libc/sysdeps/linux/c6x/bits/elf-dsbt.h b/libc/sysdeps/linux/c6x/bits/elf-dsbt.h index ff8b24bd7..5ad8bb3b0 100644 --- a/libc/sysdeps/linux/c6x/bits/elf-dsbt.h +++ b/libc/sysdeps/linux/c6x/bits/elf-dsbt.h @@ -59,15 +59,10 @@ struct elf32_dsbt_loadseg struct elf32_dsbt_loadmap { /* Protocol version number, must be zero. */ - Elf32_Word version; - - /* Pointer to DSBT */ - unsigned *dsbt_table; - unsigned dsbt_size; - unsigned dsbt_index; + Elf32_Half version; /* number of segments */ - Elf32_Word nsegs; + Elf32_Half nsegs; /* The actual memory map. */ struct elf32_dsbt_loadseg segs[0]; -- cgit v1.2.3