From e130b681c43ee2215512ffcf6cadcbc4487e96e6 Mon Sep 17 00:00:00 2001 From: Carmelo Amoroso Date: Fri, 16 Nov 2007 14:26:46 +0000 Subject: Added AVR32 support to uClibc. Signed-off-by: Hans-Christian Egtvedt --- ldso/include/dl-string.h | 3 +- ldso/ldso/avr32/dl-debug.h | 45 ++++++++++ ldso/ldso/avr32/dl-startup.h | 112 ++++++++++++++++++++++++ ldso/ldso/avr32/dl-syscalls.h | 6 ++ ldso/ldso/avr32/dl-sysdep.h | 105 +++++++++++++++++++++++ ldso/ldso/avr32/elfinterp.c | 193 ++++++++++++++++++++++++++++++++++++++++++ ldso/ldso/avr32/resolve.S | 28 ++++++ ldso/ldso/dl-startup.c | 4 +- 8 files changed, 494 insertions(+), 2 deletions(-) create mode 100644 ldso/ldso/avr32/dl-debug.h create mode 100644 ldso/ldso/avr32/dl-startup.h create mode 100644 ldso/ldso/avr32/dl-syscalls.h create mode 100644 ldso/ldso/avr32/dl-sysdep.h create mode 100644 ldso/ldso/avr32/elfinterp.c create mode 100644 ldso/ldso/avr32/resolve.S (limited to 'ldso') diff --git a/ldso/include/dl-string.h b/ldso/include/dl-string.h index 32c5bf83a..4a35a1c8d 100644 --- a/ldso/include/dl-string.h +++ b/ldso/include/dl-string.h @@ -285,7 +285,8 @@ static __always_inline char * _dl_simple_ltoahex(char * local, unsigned long i) /* On some arches constant strings are referenced through the GOT. * This requires that load_addr must already be defined... */ #if defined(mc68000) || defined(__arm__) || defined(__thumb__) || \ - defined(__mips__) || defined(__sh__) || defined(__powerpc__) + defined(__mips__) || defined(__sh__) || defined(__powerpc__) || \ + defined(__avr32__) # define CONSTANT_STRING_GOT_FIXUP(X) \ if ((X) < (const char *) load_addr) (X) += load_addr # define NO_EARLY_SEND_STDERR diff --git a/ldso/ldso/avr32/dl-debug.h b/ldso/ldso/avr32/dl-debug.h new file mode 100644 index 000000000..fe35539f6 --- /dev/null +++ b/ldso/ldso/avr32/dl-debug.h @@ -0,0 +1,45 @@ +/* + * AVR32 ELF shared libary loader support + * + * Copyright (C) 2005-2007 Atmel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the above contributors may not be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +static const char *_dl_reltypes_tab[] = { + "R_AVR32_NONE", + "R_AVR32_32", "R_AVR32_16", "R_AVR32_8", + "R_AVR32_32_PCREL", "R_AVR32_16_PCREL", "R_AVR32_8_PCREL", + "R_AVR32_DIFF32", "R_AVR32_DIFF16", "R_AVR32_DIFF8", + "R_AVR32_GOT32", "R_AVR32_GOT16", "R_AVR32_GOT8", + "R_AVR32_21S", "R_AVR32_16U", "R_AVR32_16S", "R_AVR32_8S", "R_AVR32_8S_EXT", + "R_AVR32_22H_PCREL", "R_AVR32_18W_PCREL", "R_AVR32_16B_PCREL", + "R_AVR32_16N_PCREL", "R_AVR32_14UW_PCREL", "R_AVR32_11H_PCREL", + "R_AVR32_10UW_PCREL", "R_AVR32_9H_PCREL", "R_AVR32_9UW_PCREL", + "R_AVR32_HI16", "R_AVR32_LO16", + "R_AVR32_GOTPC", "R_AVR32_GOTCALL", "R_AVR32_LDA_GOT", + "R_AVR32_GOT21S", "R_AVR32_GOT18SW", "R_AVR32_GOT16S", "R_AVR32_GOT7UW", + "R_AVR32_32_CPENT", "R_AVR32_CPCALL", "R_AVR32_16_CP", "R_AVR32_9W_CP", + "R_AVR32_RELATIVE", "R_AVR32_GLOB_DAT", "R_AVR32_JMP_SLOT", + "R_AVR32_ALIGN", +}; diff --git a/ldso/ldso/avr32/dl-startup.h b/ldso/ldso/avr32/dl-startup.h new file mode 100644 index 000000000..2280b2629 --- /dev/null +++ b/ldso/ldso/avr32/dl-startup.h @@ -0,0 +1,112 @@ +/* + * Architecture specific code used by dl-startup.c + * + * Copyright (C) 2005-2007 Atmel Corporation + * + * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. + */ + +/* This is the library loader's main entry point. Let _dl_boot2 do its + * initializations and jump to the application's entry point + * afterwards. */ +asm( " .text\n" + " .global _start\n" + " .type _start,@function\n" + "_start:\n" + /* All arguments are on the stack initially */ + " mov r12, sp\n" + " rcall _dl_start\n" + /* Returns user entry point in r12. Save it. */ + " mov r0, r12\n" + /* We're PIC, so get the Global Offset Table */ + " lddpc r6, .L_GOT\n" + ".L_RGOT:\n" + " rsub r6, pc\n" + /* Adjust argc and argv according to _dl_skip_args */ + " ld.w r1, r6[_dl_skip_args@got]\n" + " ld.w r1, r1[0]\n" + " ld.w r2, sp++\n" + " sub r2, r1\n" + " add sp, sp, r1 << 2\n" + " st.w --sp, r2\n" + /* Load the finalizer function */ + " ld.w r12, r6[_dl_fini@got]\n" + /* Jump to the user's entry point */ + " mov pc, r0\n\n" + + " .align 2\n" + ".L_GOT:" + " .long .L_RGOT - _GLOBAL_OFFSET_TABLE_\n" + " .size _start, . - _start\n" + " .previous\n"); + +/* Get a pointer to the argv array. On many platforms this can be just + * the address if the first argument, on other platforms we need to + * do something a little more subtle here. */ +#define GET_ARGV(ARGVP, ARGS) ARGVP = ((unsigned long *)ARGS + 1) + + +/* We can't call functions before the GOT has been initialized */ +#define NO_FUNCS_BEFORE_BOOTSTRAP + +/* + * Relocate the GOT during dynamic loader bootstrap. This will add + * the load address to all entries in the GOT, which is necessary + * because the linker doesn't generate R_AVR32_RELATIVE relocs for the + * GOT. + */ +static __always_inline +void PERFORM_BOOTSTRAP_GOT(struct elf_resolve *tpnt) +{ + Elf32_Addr i, nr_got; + register Elf32_Addr *__r6 __asm__("r6"); + Elf32_Addr *got = __r6; + + nr_got = tpnt->dynamic_info[DT_AVR32_GOTSZ_IDX] / sizeof(*got); + for (i = 2; i < nr_got; i++) + got[i] += tpnt->loadaddr; +} + +#define PERFORM_BOOTSTRAP_GOT(tpnt) PERFORM_BOOTSTRAP_GOT(tpnt) + +/* Handle relocation of the symbols in the dynamic loader. */ +static __always_inline +void PERFORM_BOOTSTRAP_RELOC(ELF_RELOC *rpnt, unsigned long *reloc_addr, + unsigned long symbol_addr, + unsigned long load_addr, Elf32_Sym *symtab) +{ + switch(ELF32_R_TYPE(rpnt->r_info)) { + case R_AVR32_NONE: + break; + case R_AVR32_GLOB_DAT: + case R_AVR32_JMP_SLOT: + *reloc_addr = symbol_addr; + break; + case R_AVR32_RELATIVE: + SEND_STDERR_DEBUG("Applying RELATIVE relocation: "); + SEND_ADDRESS_STDERR_DEBUG(load_addr, 0); + SEND_STDERR_DEBUG(" + "); + SEND_ADDRESS_STDERR_DEBUG(rpnt->r_addend, 1); + *reloc_addr = load_addr + rpnt->r_addend; + break; + default: + SEND_STDERR("BOOTSTRAP_RELOC: unhandled reloc_type "); + SEND_NUMBER_STDERR(ELF32_R_TYPE(rpnt->r_info), 1); + SEND_STDERR("REL, SYMBOL, LOAD: "); + SEND_ADDRESS_STDERR(reloc_addr, 0); + SEND_STDERR(", "); + SEND_ADDRESS_STDERR(symbol_addr, 0); + SEND_STDERR(", "); + SEND_ADDRESS_STDERR(load_addr, 1); + _dl_exit(1); + } +} + +/* Transfer control to the user's application, once the dynamic loader + * is done. This routine has to exit the current function, then call + * the _dl_elf_main function. + * + * Since our _dl_boot will simply call whatever is returned by + * _dl_boot2, we can just return the address we're supposed to + * call. */ +#define START() return _dl_elf_main; diff --git a/ldso/ldso/avr32/dl-syscalls.h b/ldso/ldso/avr32/dl-syscalls.h new file mode 100644 index 000000000..996bb87c6 --- /dev/null +++ b/ldso/ldso/avr32/dl-syscalls.h @@ -0,0 +1,6 @@ +/* We can't use the real errno in ldso, since it has not yet + * been dynamicly linked in yet. */ +#include "sys/syscall.h" +extern int _dl_errno; +#undef __set_errno +#define __set_errno(X) {(_dl_errno) = (X);} diff --git a/ldso/ldso/avr32/dl-sysdep.h b/ldso/ldso/avr32/dl-sysdep.h new file mode 100644 index 000000000..270800ade --- /dev/null +++ b/ldso/ldso/avr32/dl-sysdep.h @@ -0,0 +1,105 @@ +/* + * Various assembly language/system dependent hacks that are required + * so that we can minimize the amount of platform specific code. + * + * Copyright (C) 2004-2007 Atmel Corporation + * + * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. + */ + +/* Define this if the system uses RELOCA. */ +#define ELF_USES_RELOCA + +#include + +#define ARCH_NUM 1 +#define DT_AVR32_GOTSZ_IDX (DT_NUM + OS_NUM) + +#define ARCH_DYNAMIC_INFO(dpnt, dynamic, debug_addr) \ + do { \ + if (dpnt->d_tag == DT_AVR32_GOTSZ) \ + dynamic[DT_AVR32_GOTSZ_IDX] = dpnt->d_un.d_val; \ + } while (0) + +/* Initialization sequence for the application/library GOT. */ +#define INIT_GOT(GOT_BASE,MODULE) \ + do { \ + unsigned long i, nr_got; \ + \ + GOT_BASE[0] = (unsigned long) _dl_linux_resolve; \ + GOT_BASE[1] = (unsigned long) MODULE; \ + \ + /* Add load address displacement to all GOT entries */ \ + nr_got = MODULE->dynamic_info[DT_AVR32_GOTSZ_IDX] / 4; \ + for (i = 2; i < nr_got; i++) \ + GOT_BASE[i] += (unsigned long)MODULE->loadaddr; \ + } while (0) + +#define do_rem(result, n, base) ((result) = (n) % (base)) + +/* Here we define the magic numbers that this dynamic loader should accept */ +#define MAGIC1 EM_AVR32 +#undef MAGIC2 + +/* Used for error messages */ +#define ELF_TARGET "AVR32" + +unsigned long _dl_linux_resolver(unsigned long got_offset, unsigned long *got); + +/* 4096 bytes alignment */ +#define PAGE_ALIGN 0xfffff000 +#define ADDR_ALIGN 0xfff +#define OFFS_ALIGN 0x7ffff000 + +#define elf_machine_type_class(type) \ + ((type == R_AVR32_JMP_SLOT) * ELF_RTYPE_CLASS_PLT) + +/* AVR32 doesn't need any COPY relocs */ +#define DL_NO_COPY_RELOCS + +/* Return the link-time address of _DYNAMIC. Conveniently, this is the + first element of the GOT. This must be inlined in a function which + uses global data. */ +static inline Elf32_Addr +elf_machine_dynamic (void) +{ + register Elf32_Addr *got asm ("r6"); + return *got; +} + +/* Return the run-time load address of the shared object. */ +static inline Elf32_Addr +elf_machine_load_address (void) +{ + extern void __dl_start asm("_dl_start"); + Elf32_Addr got_addr = (Elf32_Addr) &__dl_start; + Elf32_Addr pcrel_addr; + + asm (" lddpc %0, 2f\n" + "1: add %0, pc\n" + " rjmp 3f\n" + " .align 2\n" + "2: .long _dl_start - 1b\n" + "3:\n" + : "=r"(pcrel_addr) : : "cc"); + + return pcrel_addr - got_addr; +} + +/* + * Perform any RELATIVE relocations specified by DT_RELCOUNT. + * Currently, we don't use that tag, but we might in the future as + * this would reduce the startup time somewhat (although probably not by much). + */ +static inline void +elf_machine_relative (Elf32_Addr load_off, const Elf32_Addr rel_addr, + Elf32_Word relative_count) +{ + Elf32_Rela *rpnt = (void *)rel_addr; + + do { + Elf32_Addr *reloc_addr; + reloc_addr = (void *)(load_off + (rpnt++)->r_offset); + *reloc_addr = load_off + rpnt->r_addend; + } while (--relative_count); +} diff --git a/ldso/ldso/avr32/elfinterp.c b/ldso/ldso/avr32/elfinterp.c new file mode 100644 index 000000000..e236e4825 --- /dev/null +++ b/ldso/ldso/avr32/elfinterp.c @@ -0,0 +1,193 @@ +/* + * AVR32 ELF shared library loader suppport + * + * Copyright (C) 2004-2006 Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the above contributors may not be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +unsigned long _dl_linux_resolver(unsigned long got_offset, unsigned long *got) +{ + /* + * AVR32 currently does not do lazy relocation. + */ +#if 0 + struct elf_resolve *tpnt = (struct elf_resolve *)got[1]; + Elf32_Sym *sym; + unsigned long local_gotno; + unsigned long gotsym; + unsigned long new_addr; + char *strtab, *symname; + unsigned long *entry; + unsigned long sym_index = got_offset / 4; + + local_gotno = tpnt->dynamic_info[DT_AVR32_LOCAL_GOTNO]; + gotsym = tpnt->dynamic_info[DT_AVR32_GOTSYM]; + + sym = ((Elf32_Sym *)(tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr)) + + sym_index; + strtab = (char *)(tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr); + symname = strtab + sym->st_name; + + new_addr = (unsigned long) _dl_find_hash(strtab + sym->st_name, + tpnt->symbol_scope, tpnt, + resolver); + + entry = (unsigned long *)(got + local_gotno + sym_index - gotsym); + *entry = new_addr; + + return new_addr; +#endif + return 0; +} + +static int +_dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope, + unsigned long rel_addr, unsigned long rel_size, + int (*reloc_func)(struct elf_resolve *tpnt, struct dyn_elf *scope, + Elf32_Rela *rpnt, Elf32_Sym *symtab, char *strtab)) +{ + Elf32_Sym *symtab; + Elf32_Rela *rpnt; + char *strtab; + int i; + + rpnt = (Elf32_Rela *)rel_addr; + rel_size /= sizeof(Elf32_Rela); + symtab = (Elf32_Sym *)tpnt->dynamic_info[DT_SYMTAB]; + strtab = (char *)tpnt->dynamic_info[DT_STRTAB]; + + for (i = 0; i < rel_size; i++, rpnt++) { + int symtab_index, res; + + symtab_index = ELF32_R_SYM(rpnt->r_info); + + debug_sym(symtab, strtab, symtab_index); + debug_reloc(symtab, strtab, rpnt); + + res = reloc_func(tpnt, scope, rpnt, symtab, strtab); + + if (res == 0) + continue; + + _dl_dprintf(2, "\n%s: ", _dl_progname); + + if (symtab_index) + _dl_dprintf(2, "symbol '%s': ", + strtab + symtab[symtab_index].st_name); + + if (res < 0) { + int reloc_type = ELF32_R_TYPE(rpnt->r_info); +#if defined(__SUPPORT_LD_DEBUG__) + _dl_dprintf(2, "can't handle reloc type %s\n", + _dl_reltypes(reloc_type)); +#else + _dl_dprintf(2, "can't handle reloc type %x\n", + reloc_type); +#endif + _dl_exit(-res); + } else { + _dl_dprintf(2, "can't resolve symbol\n"); + return res; + } + } + + return 0; +} + +static int _dl_do_reloc(struct elf_resolve *tpnt, struct dyn_elf *scope, + Elf32_Rela *rpnt, Elf32_Sym *symtab, char *strtab) +{ + int reloc_type; + int symtab_index; + char *symname; + unsigned long *reloc_addr; + unsigned long symbol_addr; +#if defined(__SUPPORT_LD_DEBUG__) + unsigned long old_val; +#endif + + reloc_addr = (unsigned long *)(tpnt->loadaddr + rpnt->r_offset); + reloc_type = ELF32_R_TYPE(rpnt->r_info); + symtab_index = ELF32_R_SYM(rpnt->r_info); + symbol_addr = 0; + symname = strtab + symtab[symtab_index].st_name; + + if (symtab_index) { + symbol_addr = (unsigned long) + _dl_find_hash(strtab + symtab[symtab_index].st_name, + tpnt->symbol_scope, tpnt, + elf_machine_type_class(reloc_type)); + + /* Allow undefined references to weak symbols */ + if (!symbol_addr && + ELF32_ST_BIND(symtab[symtab_index].st_info) != STB_WEAK) { + _dl_dprintf(2, "%s: can't resolve symbol '%s'\n", + _dl_progname, symname); + return 0; + } + } + +#if defined(__SUPPORT_LD_DEBUG__) + old_val = *reloc_addr; +#endif + switch (reloc_type) { + case R_AVR32_NONE: + break; + case R_AVR32_GLOB_DAT: + case R_AVR32_JMP_SLOT: + *reloc_addr = symbol_addr + rpnt->r_addend; + break; + case R_AVR32_RELATIVE: + *reloc_addr = (unsigned long)tpnt->loadaddr + + rpnt->r_addend; + break; + default: + return -1; + } + +#if defined(__SUPPORT_LD_DEBUG__) + if (_dl_debug_reloc && _dl_debug_detail) + _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x\n", + old_val, *reloc_addr); +#endif + + return 0; +} + +void _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt, + unsigned long rel_addr, + unsigned long rel_size) +{ + /* TODO: Might want to support this in order to get faster + * startup times... */ +} + +int _dl_parse_relocation_information(struct dyn_elf *rpnt, + unsigned long rel_addr, + unsigned long rel_size) +{ + return _dl_parse(rpnt->dyn, rpnt->dyn->symbol_scope, rel_addr, rel_size, + _dl_do_reloc); +} diff --git a/ldso/ldso/avr32/resolve.S b/ldso/ldso/avr32/resolve.S new file mode 100644 index 000000000..ad90c2548 --- /dev/null +++ b/ldso/ldso/avr32/resolve.S @@ -0,0 +1,28 @@ +/* + * Linux dynamic resolving code for AVR32. Fixes up the GOT entry as + * indicated in register r12 and jumps to the resolved address. + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License. See the file "COPYING.LIB" in the main directory of this + * archive for more details. + * + * Copyright (C) 2004-2007 Atmel Corporation + */ + +#define ip r5 + + .text + .global _dl_linux_resolve + .type _dl_linux_resolve,@function +_dl_linux_resolve: + /* The PLT code pushed r8 for us. It contains the address of this + function's GOT entry, that is entry 0. ip contains the address + of the GOT entry of the function we wanted to call. */ + stm --sp, r9-r12, lr + mov r11, r8 + sub r12, ip, r8 + rcall _dl_linux_resolver + mov ip, r12 + popm r8-r12,lr + mov pc, ip + .size _dl_linux_resolve, . - _dl_linux_resolve diff --git a/ldso/ldso/dl-startup.c b/ldso/ldso/dl-startup.c index 5cf1d049e..d4294ec3a 100644 --- a/ldso/ldso/dl-startup.c +++ b/ldso/ldso/dl-startup.c @@ -217,7 +217,9 @@ DL_START(unsigned long args) /* some arches (like MIPS) we have to tweak the GOT before relocations */ PERFORM_BOOTSTRAP_GOT(tpnt); -#else +#endif + +#if !defined(PERFORM_BOOTSTRAP_GOT) || defined(__avr32__) /* OK, now do the relocations. We do not do a lazy binding here, so that once we are done, we have considerably more flexibility. */ -- cgit v1.2.3