From ecb79165c15958e69bff0285b1b8de852dfd2488 Mon Sep 17 00:00:00 2001 From: Eric Andersen Date: Tue, 3 Jul 2001 18:28:09 +0000 Subject: Merge the arm port into the main tree. The final version (the one that actually works) is the most excellent work of Shane Nay , who took what I had been doing and fixed it. --- ldso/ldso/arm/dl-syscalls.h | 124 +++++++++++++++ ldso/ldso/arm/dl-sysdep.h | 120 ++++++++++++++ ldso/ldso/arm/elfinterp.c | 374 ++++++++++++++++++++++++++++++++++++++++++++ ldso/ldso/arm/ld_syscalls.h | 124 +++++++++++++++ ldso/ldso/arm/ld_sysdep.h | 120 ++++++++++++++ ldso/ldso/arm/resolve.S | 41 +++++ ldso/ldso/arm/syscalls.h | 124 +++++++++++++++ ldso/ldso/arm/sysdep.h | 120 ++++++++++++++ 8 files changed, 1147 insertions(+) create mode 100644 ldso/ldso/arm/dl-syscalls.h create mode 100644 ldso/ldso/arm/dl-sysdep.h create mode 100644 ldso/ldso/arm/elfinterp.c create mode 100644 ldso/ldso/arm/ld_syscalls.h create mode 100644 ldso/ldso/arm/ld_sysdep.h create mode 100644 ldso/ldso/arm/resolve.S create mode 100644 ldso/ldso/arm/syscalls.h create mode 100644 ldso/ldso/arm/sysdep.h (limited to 'ldso') diff --git a/ldso/ldso/arm/dl-syscalls.h b/ldso/ldso/arm/dl-syscalls.h new file mode 100644 index 000000000..aa08421a5 --- /dev/null +++ b/ldso/ldso/arm/dl-syscalls.h @@ -0,0 +1,124 @@ +#include + +/* + * This file contains the system call macros and syscall + * numbers used by the shared library loader. + */ + +#define __NR_SYSCALL_BASE 0x900000 + +#define __NR_exit (__NR_SYSCALL_BASE+ 1) +#define __NR_read (__NR_SYSCALL_BASE+ 3) +#define __NR_write (__NR_SYSCALL_BASE+ 4) +#define __NR_open (__NR_SYSCALL_BASE+ 5) +#define __NR_close (__NR_SYSCALL_BASE+ 6) +#define __NR_getuid (__NR_SYSCALL_BASE+ 24) +#define __NR_geteuid (__NR_SYSCALL_BASE+ 49) +#define __NR_getgid (__NR_SYSCALL_BASE+ 47) +#define __NR_getegid (__NR_SYSCALL_BASE+ 50) +#define __NR_mmap (__NR_SYSCALL_BASE+ 90) +#define __NR_munmap (__NR_SYSCALL_BASE+ 91) +#define __NR_stat (__NR_SYSCALL_BASE+106) +#define __NR_mprotect (__NR_SYSCALL_BASE+125) + + +/* Here are the macros which define how this platform makes + * system calls. This particular variant does _not_ set + * errno (note how it is disabled in __syscall_return) since + * these will get called before the errno symbol is dynamicly + * linked. */ + +/* These are Erik's versions of the syscall routines. His were + * cleaner than mine, so I adopted them instead with some + * reformating. Shane Nay. + */ + +#define __sys2(x) #x +#define __sys1(x) __sys2(x) + +#ifndef __syscall +#define __syscall(name) "swi\t" __sys1(__NR_##name) "\n\t" +#endif + +#undef __syscall_return +#define __syscall_return(type, res) \ +do { \ + if ((unsigned long)(res) >= (unsigned long)(-125)) { \ + /*errno = -(res);*/ \ + res = -1; \ + } \ + return (type) (res); \ +} while (0) + +#define _syscall0(type,name) \ +type name(void) { \ + long __res; \ + __asm__ __volatile__ ( \ + __syscall(name) \ + "mov %0,r0" \ + :"=r" (__res) : : "r0","lr"); \ + __syscall_return(type,__res); \ +} + +#define _syscall1(type,name,type1,arg1) \ +type name(type1 arg1) { \ + long __res; \ + __asm__ __volatile__ ( \ + "mov\tr0,%1\n\t" \ + __syscall(name) \ + "mov %0,r0" \ + : "=r" (__res) \ + : "r" ((long)(arg1)) \ + : "r0","lr"); \ + __syscall_return(type,__res); \ +} + +#define _syscall2(type,name,type1,arg1,type2,arg2) \ +type name(type1 arg1,type2 arg2) { \ + long __res; \ + __asm__ __volatile__ ( \ + "mov\tr0,%1\n\t" \ + "mov\tr1,%2\n\t" \ + __syscall(name) \ + "mov\t%0,r0" \ + : "=r" (__res) \ + : "r" ((long)(arg1)),"r" ((long)(arg2)) \ + : "r0","r1","lr"); \ + __syscall_return(type,__res); \ +} + + +#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \ +type name(type1 arg1,type2 arg2,type3 arg3) { \ + long __res; \ + __asm__ __volatile__ ( \ + "mov\tr0,%1\n\t" \ + "mov\tr1,%2\n\t" \ + "mov\tr2,%3\n\t" \ + __syscall(name) \ + "mov\t%0,r0" \ + : "=r" (__res) \ + : "r" ((long)(arg1)),"r" ((long)(arg2)),"r" ((long)(arg3)) \ + : "r0","r1","r2","lr"); \ + __syscall_return(type,__res); \ +} + +#undef _syscall4 +#define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4)\ +type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + long __res; \ + __asm__ __volatile__ ( \ + "mov\tr0,%1\n\t" \ + "mov\tr1,%2\n\t" \ + "mov\tr2,%3\n\t" \ + "mov\tr3,%4\n\t" \ + __syscall(name) \ + "mov\t%0,r0" \ + : "=r" (__res) \ + : "r" ((long)(arg1)),"r" ((long)(arg2)), \ + "r" ((long)(arg3)),"r" ((long)(arg4)) \ + : "r0","r1","r2","r3","lr"); \ + __syscall_return(type,__res); \ +} + + diff --git a/ldso/ldso/arm/dl-sysdep.h b/ldso/ldso/arm/dl-sysdep.h new file mode 100644 index 000000000..b3d430519 --- /dev/null +++ b/ldso/ldso/arm/dl-sysdep.h @@ -0,0 +1,120 @@ +/* + * Various assmbly language/system dependent hacks that are required + * so that we can minimize the amount of platform specific code. + */ + +/* + * Define this if the system uses RELOCA. + */ +#undef ELF_USES_RELOCA + +/* + * 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) + +/* + * Initialization sequence for a GOT. + */ +#define INIT_GOT(GOT_BASE,MODULE) \ +{ \ + GOT_BASE[2] = (unsigned long) _dl_linux_resolve; \ + GOT_BASE[1] = (unsigned long) MODULE; \ +} + +/* + * Here is a macro to perform a relocation. This is only used when + * bootstrapping the dynamic loader. RELP is the relocation that we + * are performing, REL is the pointer to the address we are relocating. + * SYMBOL is the symbol involved in the relocation, and LOAD is the + * load address. + */ +#define PERFORM_BOOTSTRAP_RELOC(RELP,REL,SYMBOL,LOAD) \ + switch(ELF32_R_TYPE((RELP)->r_info)){ \ + case R_ARM_ABS32: \ + *REL += SYMBOL; \ + break; \ + case R_ARM_PC24: \ + { \ + unsigned long newval, topbits; \ + long addend=*REL & 0x00ffffff; \ + if(addend & 0x00800000) \ + addend|=0xff000000; \ + newval=SYMBOL- ((unsigned long)REL) + (addend<<2); \ + topbits=newval & 0xfe000000; \ + if (topbits != 0xfe000000 && topbits != 0x00000000) {/* \ + newval=fix_bad_pc24(REL,value) - \ + ((unsigned long)REL) + (addend << 2); \ + topbits=newval & 0xfe000000; \ + if(topbits != 0xfe000000 && topbits != 0x00000000)*/ \ + _dl_exit(1); \ + } \ + newval>>=2; \ + SYMBOL= (*REL & 0xff000000)|(newval & 0x00ffffff); \ + *REL=SYMBOL; \ + } \ + break; \ + case R_ARM_GLOB_DAT: \ + case R_ARM_JUMP_SLOT: \ + *REL = SYMBOL; \ + break; \ + case R_ARM_RELATIVE: \ + *REL += (unsigned long) LOAD; \ + break; \ + case R_ARM_NONE: \ + break; \ + default: \ + _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. + */ + +#define START() return _dl_elf_main; + + + +/* Here we define the magic numbers that this dynamic loader should accept */ + +#define MAGIC1 EM_ARM +#undef MAGIC2 +/* Used for error messages */ +#define ELF_TARGET "ARM" + +struct elf_resolve; +extern unsigned long _dl_linux_resolver(struct elf_resolve * tpnt, int reloc_entry); + +static inline unsigned long arm_modulus(unsigned long m, unsigned long p) { + unsigned long i,t,inc; + i=p; t=0; + while(!(i&(1<<31))) { + i<<=1; + t++; + } + t--; + for(inc=t;inc>2;inc--) { + i=p<=i) { + m-=i; + i<<=1; + if(i&(1<<31)) + break; + if(i=p) { + m-=p; + } + return m; +} + +#define do_rem(result, n, base) result=arm_modulus(n,base); diff --git a/ldso/ldso/arm/elfinterp.c b/ldso/ldso/arm/elfinterp.c new file mode 100644 index 000000000..397610bd6 --- /dev/null +++ b/ldso/ldso/arm/elfinterp.c @@ -0,0 +1,374 @@ +/* Run an ELF binary on a linux system. + + Copyright (C) 1993, Eric Youngdale. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef VERBOSE_DLINKER +#define VERBOSE_DLINKER +#endif +#ifdef VERBOSE_DLINKER +static char *_dl_reltypes[] = + { "R_ARM_NONE", "R_ARM_PC24", "R_ARM_ABS32", "R_ARM_REL32", + "R_ARM_PC13", "R_ARM_ABS16", "R_ARM_ABS12", "R_ARM_THM_ABS5", + "R_ARM_ABS8", "R_ARM_SBREL32", "R_ARM_THM_PC22", "R_ARM_THM_PC8", + "R_ARM_AMP_VCALL9", "R_ARM_SWI24", "R_ARM_THM_SWI8", "R_ARM_XPC25", + "R_ARM_THM_XPC22", "R_ARM_COPY", "R_ARM_GLOB_DAT", "R_ARM_JUMP_SLOT", + "R_ARM_RELATIVE", "R_ARM_GOTOFF", "R_ARM_GOTPC", "R_ARM_GOT32", + "R_ARM_PLT32", "R_ARM_ALU_PCREL_7_0", "R_ARM_ALU_PCREL_15_8", + "R_ARM_ALU_PCREL_23_15", "R_ARM_LDR_SBREL_11_0", "R_ARM_ALU_SBREL_19_12", + "R_ARM_ALU_SBREL_27_20", "R_ARM_GNU_VTENTRY", "R_ARM_GNU_VTINHERIT", + "R_ARM_THM_PC11", "R_ARM_THM_PC9", "R_ARM_RXPC25", "R_ARM_RSBREL32", + "R_ARM_THM_RPC22", "R_ARM_RREL32", "R_ARM_RABS22", "R_ARM_RPC24", + "R_ARM_RBASE", "R_ARM_NUM" +}; +#endif + +/* Program to load an ELF binary on a linux system, and run it. + References to symbols in sharable libraries can be resolved by either + an ELF sharable library or a linux style of shared library. */ + +/* Disclaimer: I have never seen any AT&T source code for SVr4, nor have + I ever taken any courses on internals. This program was developed using + information available through the book "UNIX SYSTEM V RELEASE 4, + Programmers guide: Ansi C and Programming Support Tools", which did + a more than adequate job of explaining everything required to get this + working. */ + +#include +#include +#include "elf.h" +#include "hash.h" +#include "syscall.h" +#include "string.h" +#include "sysdep.h" + +extern char *_dl_progname; + +extern int _dl_linux_resolve(void); + +unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry) +{ + int reloc_type; + Elf32_Rel *this_reloc; + char *strtab; + Elf32_Sym *symtab; + Elf32_Rel *rel_addr; + int symtab_index; + char *new_addr; + char **got_addr; + unsigned long instr_addr; + + rel_addr = (Elf32_Rel *) (tpnt->dynamic_info[DT_JMPREL] + tpnt->loadaddr); + + this_reloc = rel_addr + (reloc_entry >> 3); + reloc_type = ELF32_R_TYPE(this_reloc->r_info); + symtab_index = ELF32_R_SYM(this_reloc->r_info); + + symtab = (Elf32_Sym *) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr); + strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr); + + if (reloc_type != R_ARM_JUMP_SLOT) { + _dl_fdprintf(2, "%s: Incorrect relocation type in jump relocations\n", + _dl_progname); + _dl_exit(1); + }; + + /* Address of jump instruction to fix up */ + instr_addr = ((unsigned long) this_reloc->r_offset + + (unsigned long) tpnt->loadaddr); + got_addr = (char **) instr_addr; + +#ifdef DL_DEBUG + _dl_fdprintf(2, "Resolving symbol %s\n", + strtab + symtab[symtab_index].st_name); +#endif + + /* Get the address of the GOT entry */ + new_addr = _dl_find_hash(strtab + symtab[symtab_index].st_name, + tpnt->symbol_scope, (unsigned long) got_addr, tpnt, 0); + if (!new_addr) { + _dl_fdprintf(2, "%s: can't resolve symbol '%s'\n", + _dl_progname, strtab + symtab[symtab_index].st_name); + _dl_exit(1); + }; +#ifdef DL_DEBUG + if ((unsigned long) got_addr < 0x40000000) { + _dl_fdprintf(2, "Calling library function: %s\n", + strtab + symtab[symtab_index].st_name); + } else { + *got_addr = new_addr; + } +#else + *got_addr = new_addr; +#endif + return (unsigned long) new_addr; +} + +void _dl_parse_lazy_relocation_information(struct elf_resolve *tpnt, + unsigned long rel_addr, unsigned long rel_size, int type) +{ + int i; + char *strtab; + int reloc_type; + int symtab_index; + Elf32_Sym *symtab; + Elf32_Rel *rpnt; + unsigned long *reloc_addr; + + /* Now parse the relocation information */ + rpnt = (Elf32_Rel *) (rel_addr + tpnt->loadaddr); + rel_size = rel_size / sizeof(Elf32_Rel); + + symtab = + (Elf32_Sym *) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr); + strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr); + + for (i = 0; i < rel_size; i++, rpnt++) { + reloc_addr = (unsigned long *) (tpnt->loadaddr + (unsigned long) rpnt->r_offset); + reloc_type = ELF32_R_TYPE(rpnt->r_info); + symtab_index = ELF32_R_SYM(rpnt->r_info); + + /* When the dynamic linker bootstrapped itself, it resolved some symbols. + Make sure we do not do them again */ + if (!symtab_index && tpnt->libtype == program_interpreter) + continue; + if (symtab_index && tpnt->libtype == program_interpreter && + _dl_symbol(strtab + symtab[symtab_index].st_name)) + continue; + + switch (reloc_type) { + case R_ARM_NONE: + break; + case R_ARM_JUMP_SLOT: + *reloc_addr += (unsigned long) tpnt->loadaddr; + break; + default: + _dl_fdprintf(2, "%s: (LAZY) can't handle reloc type ", + _dl_progname); +#ifdef VERBOSE_DLINKER + _dl_fdprintf(2, "%s ", _dl_reltypes[reloc_type]); +#endif + if (symtab_index) + _dl_fdprintf(2, "'%s'\n", strtab + symtab[symtab_index].st_name); + _dl_exit(1); + }; + }; +} + +static unsigned long +fix_bad_pc24 (unsigned long *const reloc_addr, unsigned long value) +{ + static void *fix_page; + static unsigned int fix_offset; + unsigned int *fix_address; + if (! fix_page) + { + fix_page = _dl_mmap (NULL, 4096 , PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + fix_offset = 0; + } + + fix_address = (unsigned int *)(fix_page + fix_offset); + fix_address[0] = 0xe51ff004; /* ldr pc, [pc, #-4] */ + fix_address[1] = value; + + fix_offset += 8; + if (fix_offset >= 4096) + fix_page = NULL; + + return (unsigned long)fix_address; +} + + +int _dl_parse_relocation_information(struct elf_resolve *tpnt, + unsigned long rel_addr, unsigned long rel_size, int type) +{ + int i; + char *strtab; + int reloc_type; + int goof = 0; + Elf32_Sym *symtab; + Elf32_Rel *rpnt; + unsigned long *reloc_addr; + unsigned long symbol_addr; + int symtab_index; + + /* Now parse the relocation information */ + + rpnt = (Elf32_Rel *) (rel_addr + tpnt->loadaddr); + rel_size = rel_size / sizeof(Elf32_Rel); + + symtab = (Elf32_Sym *) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr); + strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr); + + for (i = 0; i < rel_size; i++, rpnt++) { + reloc_addr = (unsigned long *) (tpnt->loadaddr + (unsigned long) rpnt->r_offset); + reloc_type = ELF32_R_TYPE(rpnt->r_info); + symtab_index = ELF32_R_SYM(rpnt->r_info); + symbol_addr = 0; + + if (!symtab_index && tpnt->libtype == program_interpreter) + continue; + + if (symtab_index) { + + if (tpnt->libtype == program_interpreter && + _dl_symbol(strtab + symtab[symtab_index].st_name)) + continue; + + symbol_addr = (unsigned long) _dl_find_hash(strtab + symtab[symtab_index].st_name, + tpnt->symbol_scope, (unsigned long) reloc_addr, + (reloc_type == R_ARM_JUMP_SLOT ? tpnt : NULL), 0); + + /* + * We want to allow undefined references to weak symbols - this might + * have been intentional. We should not be linking local symbols + * here, so all bases should be covered. + */ + if (!symbol_addr && ELF32_ST_BIND(symtab[symtab_index].st_info) == STB_GLOBAL) { + _dl_fdprintf(2, "%s: can't resolve symbol '%s'\n", + _dl_progname, strtab + symtab[symtab_index].st_name); + goof++; + } + } + switch (reloc_type) { + case R_ARM_NONE: + break; + case R_ARM_ABS32: + *reloc_addr += symbol_addr; + break; + case R_ARM_PC24: + { + unsigned long addend; + long newvalue, topbits; + + addend = *reloc_addr & 0x00ffffff; + if (addend & 0x00800000) addend |= 0xff000000; + + newvalue = symbol_addr - (unsigned long)reloc_addr + (addend << 2); + topbits = newvalue & 0xfe000000; + if (topbits != 0xfe000000 && topbits != 0x00000000) + { + newvalue = fix_bad_pc24(reloc_addr, symbol_addr) + - (unsigned long)reloc_addr + (addend << 2); + topbits = newvalue & 0xfe000000; + if (topbits != 0xfe000000 && topbits != 0x00000000) + { + _dl_fdprintf(2, "R_ARM_PC24 relocation out of range "); + _dl_exit(1); + } + } + newvalue >>= 2; + symbol_addr = (*reloc_addr & 0xff000000) | (newvalue & 0x00ffffff); + *reloc_addr = symbol_addr; + break; + } + case R_ARM_GLOB_DAT: + case R_ARM_JUMP_SLOT: + *reloc_addr = symbol_addr; + break; + case R_ARM_RELATIVE: + *reloc_addr += (unsigned long) tpnt->loadaddr; + break; + case R_ARM_COPY: +#if 0 + /* Do this later */ + _dl_fdprintf(2, "Doing copy for symbol "); + if (symtab_index) _dl_fdprintf(2, strtab + symtab[symtab_index].st_name); + _dl_fdprintf(2, "\n"); + _dl_memcpy((void *) symtab[symtab_index].st_value, + (void *) symbol_addr, symtab[symtab_index].st_size); +#endif + break; + default: + _dl_fdprintf(2, "%s: can't handle reloc type ", _dl_progname); +#ifdef VERBOSE_DLINKER + _dl_fdprintf(2, "%s ", _dl_reltypes[reloc_type]); +#endif + if (symtab_index) + _dl_fdprintf(2, "'%s'\n", strtab + symtab[symtab_index].st_name); + _dl_exit(1); + }; + + }; + return goof; +} + + +/* This is done as a separate step, because there are cases where + information is first copied and later initialized. This results in + the wrong information being copied. Someone at Sun was complaining about + a bug in the handling of _COPY by SVr4, and this may in fact be what he + was talking about. Sigh. */ + +/* No, there are cases where the SVr4 linker fails to emit COPY relocs + at all */ + +int _dl_parse_copy_information(struct dyn_elf *xpnt, unsigned long rel_addr, + unsigned long rel_size, int type) +{ + int i; + char *strtab; + int reloc_type; + int goof = 0; + Elf32_Sym *symtab; + Elf32_Rel *rpnt; + unsigned long *reloc_addr; + unsigned long symbol_addr; + struct elf_resolve *tpnt; + int symtab_index; + + /* Now parse the relocation information */ + + tpnt = xpnt->dyn; + + rpnt = (Elf32_Rel *) (rel_addr + tpnt->loadaddr); + rel_size = rel_size / sizeof(Elf32_Rel); + + symtab = (Elf32_Sym *) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr); + strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr); + + for (i = 0; i < rel_size; i++, rpnt++) { + reloc_addr = (unsigned long *) (tpnt->loadaddr + (unsigned long) rpnt->r_offset); + reloc_type = ELF32_R_TYPE(rpnt->r_info); + if (reloc_type != R_ARM_COPY) + continue; + symtab_index = ELF32_R_SYM(rpnt->r_info); + symbol_addr = 0; + if (!symtab_index && tpnt->libtype == program_interpreter) + continue; + if (symtab_index) { + + if (tpnt->libtype == program_interpreter && + _dl_symbol(strtab + symtab[symtab_index].st_name)) + continue; + + symbol_addr = (unsigned long) _dl_find_hash(strtab + + symtab[symtab_index].st_name, xpnt->next, + (unsigned long) reloc_addr, NULL, 1); + if (!symbol_addr) { + _dl_fdprintf(2, "%s: can't resolve symbol '%s'\n", + _dl_progname, strtab + symtab[symtab_index].st_name); + goof++; + }; + }; + if (!goof) { + _dl_memcpy((char *) symtab[symtab_index].st_value, + (char *) symbol_addr, symtab[symtab_index].st_size); + } + }; + return goof; +} diff --git a/ldso/ldso/arm/ld_syscalls.h b/ldso/ldso/arm/ld_syscalls.h new file mode 100644 index 000000000..aa08421a5 --- /dev/null +++ b/ldso/ldso/arm/ld_syscalls.h @@ -0,0 +1,124 @@ +#include + +/* + * This file contains the system call macros and syscall + * numbers used by the shared library loader. + */ + +#define __NR_SYSCALL_BASE 0x900000 + +#define __NR_exit (__NR_SYSCALL_BASE+ 1) +#define __NR_read (__NR_SYSCALL_BASE+ 3) +#define __NR_write (__NR_SYSCALL_BASE+ 4) +#define __NR_open (__NR_SYSCALL_BASE+ 5) +#define __NR_close (__NR_SYSCALL_BASE+ 6) +#define __NR_getuid (__NR_SYSCALL_BASE+ 24) +#define __NR_geteuid (__NR_SYSCALL_BASE+ 49) +#define __NR_getgid (__NR_SYSCALL_BASE+ 47) +#define __NR_getegid (__NR_SYSCALL_BASE+ 50) +#define __NR_mmap (__NR_SYSCALL_BASE+ 90) +#define __NR_munmap (__NR_SYSCALL_BASE+ 91) +#define __NR_stat (__NR_SYSCALL_BASE+106) +#define __NR_mprotect (__NR_SYSCALL_BASE+125) + + +/* Here are the macros which define how this platform makes + * system calls. This particular variant does _not_ set + * errno (note how it is disabled in __syscall_return) since + * these will get called before the errno symbol is dynamicly + * linked. */ + +/* These are Erik's versions of the syscall routines. His were + * cleaner than mine, so I adopted them instead with some + * reformating. Shane Nay. + */ + +#define __sys2(x) #x +#define __sys1(x) __sys2(x) + +#ifndef __syscall +#define __syscall(name) "swi\t" __sys1(__NR_##name) "\n\t" +#endif + +#undef __syscall_return +#define __syscall_return(type, res) \ +do { \ + if ((unsigned long)(res) >= (unsigned long)(-125)) { \ + /*errno = -(res);*/ \ + res = -1; \ + } \ + return (type) (res); \ +} while (0) + +#define _syscall0(type,name) \ +type name(void) { \ + long __res; \ + __asm__ __volatile__ ( \ + __syscall(name) \ + "mov %0,r0" \ + :"=r" (__res) : : "r0","lr"); \ + __syscall_return(type,__res); \ +} + +#define _syscall1(type,name,type1,arg1) \ +type name(type1 arg1) { \ + long __res; \ + __asm__ __volatile__ ( \ + "mov\tr0,%1\n\t" \ + __syscall(name) \ + "mov %0,r0" \ + : "=r" (__res) \ + : "r" ((long)(arg1)) \ + : "r0","lr"); \ + __syscall_return(type,__res); \ +} + +#define _syscall2(type,name,type1,arg1,type2,arg2) \ +type name(type1 arg1,type2 arg2) { \ + long __res; \ + __asm__ __volatile__ ( \ + "mov\tr0,%1\n\t" \ + "mov\tr1,%2\n\t" \ + __syscall(name) \ + "mov\t%0,r0" \ + : "=r" (__res) \ + : "r" ((long)(arg1)),"r" ((long)(arg2)) \ + : "r0","r1","lr"); \ + __syscall_return(type,__res); \ +} + + +#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \ +type name(type1 arg1,type2 arg2,type3 arg3) { \ + long __res; \ + __asm__ __volatile__ ( \ + "mov\tr0,%1\n\t" \ + "mov\tr1,%2\n\t" \ + "mov\tr2,%3\n\t" \ + __syscall(name) \ + "mov\t%0,r0" \ + : "=r" (__res) \ + : "r" ((long)(arg1)),"r" ((long)(arg2)),"r" ((long)(arg3)) \ + : "r0","r1","r2","lr"); \ + __syscall_return(type,__res); \ +} + +#undef _syscall4 +#define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4)\ +type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + long __res; \ + __asm__ __volatile__ ( \ + "mov\tr0,%1\n\t" \ + "mov\tr1,%2\n\t" \ + "mov\tr2,%3\n\t" \ + "mov\tr3,%4\n\t" \ + __syscall(name) \ + "mov\t%0,r0" \ + : "=r" (__res) \ + : "r" ((long)(arg1)),"r" ((long)(arg2)), \ + "r" ((long)(arg3)),"r" ((long)(arg4)) \ + : "r0","r1","r2","r3","lr"); \ + __syscall_return(type,__res); \ +} + + diff --git a/ldso/ldso/arm/ld_sysdep.h b/ldso/ldso/arm/ld_sysdep.h new file mode 100644 index 000000000..b3d430519 --- /dev/null +++ b/ldso/ldso/arm/ld_sysdep.h @@ -0,0 +1,120 @@ +/* + * Various assmbly language/system dependent hacks that are required + * so that we can minimize the amount of platform specific code. + */ + +/* + * Define this if the system uses RELOCA. + */ +#undef ELF_USES_RELOCA + +/* + * 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) + +/* + * Initialization sequence for a GOT. + */ +#define INIT_GOT(GOT_BASE,MODULE) \ +{ \ + GOT_BASE[2] = (unsigned long) _dl_linux_resolve; \ + GOT_BASE[1] = (unsigned long) MODULE; \ +} + +/* + * Here is a macro to perform a relocation. This is only used when + * bootstrapping the dynamic loader. RELP is the relocation that we + * are performing, REL is the pointer to the address we are relocating. + * SYMBOL is the symbol involved in the relocation, and LOAD is the + * load address. + */ +#define PERFORM_BOOTSTRAP_RELOC(RELP,REL,SYMBOL,LOAD) \ + switch(ELF32_R_TYPE((RELP)->r_info)){ \ + case R_ARM_ABS32: \ + *REL += SYMBOL; \ + break; \ + case R_ARM_PC24: \ + { \ + unsigned long newval, topbits; \ + long addend=*REL & 0x00ffffff; \ + if(addend & 0x00800000) \ + addend|=0xff000000; \ + newval=SYMBOL- ((unsigned long)REL) + (addend<<2); \ + topbits=newval & 0xfe000000; \ + if (topbits != 0xfe000000 && topbits != 0x00000000) {/* \ + newval=fix_bad_pc24(REL,value) - \ + ((unsigned long)REL) + (addend << 2); \ + topbits=newval & 0xfe000000; \ + if(topbits != 0xfe000000 && topbits != 0x00000000)*/ \ + _dl_exit(1); \ + } \ + newval>>=2; \ + SYMBOL= (*REL & 0xff000000)|(newval & 0x00ffffff); \ + *REL=SYMBOL; \ + } \ + break; \ + case R_ARM_GLOB_DAT: \ + case R_ARM_JUMP_SLOT: \ + *REL = SYMBOL; \ + break; \ + case R_ARM_RELATIVE: \ + *REL += (unsigned long) LOAD; \ + break; \ + case R_ARM_NONE: \ + break; \ + default: \ + _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. + */ + +#define START() return _dl_elf_main; + + + +/* Here we define the magic numbers that this dynamic loader should accept */ + +#define MAGIC1 EM_ARM +#undef MAGIC2 +/* Used for error messages */ +#define ELF_TARGET "ARM" + +struct elf_resolve; +extern unsigned long _dl_linux_resolver(struct elf_resolve * tpnt, int reloc_entry); + +static inline unsigned long arm_modulus(unsigned long m, unsigned long p) { + unsigned long i,t,inc; + i=p; t=0; + while(!(i&(1<<31))) { + i<<=1; + t++; + } + t--; + for(inc=t;inc>2;inc--) { + i=p<=i) { + m-=i; + i<<=1; + if(i&(1<<31)) + break; + if(i=p) { + m-=p; + } + return m; +} + +#define do_rem(result, n, base) result=arm_modulus(n,base); diff --git a/ldso/ldso/arm/resolve.S b/ldso/ldso/arm/resolve.S new file mode 100644 index 000000000..514ecb64e --- /dev/null +++ b/ldso/ldso/arm/resolve.S @@ -0,0 +1,41 @@ +/* + * This function is _not_ called directly. It is jumped to (so no return + * address is on the stack) when attempting to use a symbol that has not yet + * been resolved. The first time a jump symbol (such as a function call inside + * a shared library) is used (before it gets resolved) it will jump here to + * _dl_linux_resolve. When we get called the stack looks like this: + * reloc_entry + * tpnt + * + * This function saves all the registers, puts a copy of reloc_entry and tpnt + * on the stack (as function arguments) then make the function call + * _dl_linux_resolver(tpnt, reloc_entry). _dl_linux_resolver() figures out + * where the jump symbol is _really_ supposed to have jumped to and returns + * that to us. Once we have that, we overwrite tpnt with this fixed up + * address. We then clean up after ourselves, put all the registers back how we + * found them, then we jump to where the fixed up address, which is where the + * jump symbol that got us here really wanted to jump to in the first place. + * found them, then we jump to the fixed up address, which is where the jump + * symbol that got us here really wanted to jump to in the first place. + * -Erik Andersen + */ +.text +.globl _dl_linux_resolve +.type _dl_linux_resolve,#function +.align 2 +_dl_linux_resolve: + stmdb sp!,{r0-r3,sl,fp} + sub r1, ip, lr + sub r1, r1, #4 + add r1, r1, r1 + ldr r0, [lr, #-4] + mov r3,r0 + + bl _dl_linux_resolver + +// str r0, [lr, #-4] + mov ip, r0 + ldmia sp!,{r0-r3,sl,fp,lr} + mov pc,ip +.size _dl_linux_resolve, .-_dl_linux_resolve + diff --git a/ldso/ldso/arm/syscalls.h b/ldso/ldso/arm/syscalls.h new file mode 100644 index 000000000..aa08421a5 --- /dev/null +++ b/ldso/ldso/arm/syscalls.h @@ -0,0 +1,124 @@ +#include + +/* + * This file contains the system call macros and syscall + * numbers used by the shared library loader. + */ + +#define __NR_SYSCALL_BASE 0x900000 + +#define __NR_exit (__NR_SYSCALL_BASE+ 1) +#define __NR_read (__NR_SYSCALL_BASE+ 3) +#define __NR_write (__NR_SYSCALL_BASE+ 4) +#define __NR_open (__NR_SYSCALL_BASE+ 5) +#define __NR_close (__NR_SYSCALL_BASE+ 6) +#define __NR_getuid (__NR_SYSCALL_BASE+ 24) +#define __NR_geteuid (__NR_SYSCALL_BASE+ 49) +#define __NR_getgid (__NR_SYSCALL_BASE+ 47) +#define __NR_getegid (__NR_SYSCALL_BASE+ 50) +#define __NR_mmap (__NR_SYSCALL_BASE+ 90) +#define __NR_munmap (__NR_SYSCALL_BASE+ 91) +#define __NR_stat (__NR_SYSCALL_BASE+106) +#define __NR_mprotect (__NR_SYSCALL_BASE+125) + + +/* Here are the macros which define how this platform makes + * system calls. This particular variant does _not_ set + * errno (note how it is disabled in __syscall_return) since + * these will get called before the errno symbol is dynamicly + * linked. */ + +/* These are Erik's versions of the syscall routines. His were + * cleaner than mine, so I adopted them instead with some + * reformating. Shane Nay. + */ + +#define __sys2(x) #x +#define __sys1(x) __sys2(x) + +#ifndef __syscall +#define __syscall(name) "swi\t" __sys1(__NR_##name) "\n\t" +#endif + +#undef __syscall_return +#define __syscall_return(type, res) \ +do { \ + if ((unsigned long)(res) >= (unsigned long)(-125)) { \ + /*errno = -(res);*/ \ + res = -1; \ + } \ + return (type) (res); \ +} while (0) + +#define _syscall0(type,name) \ +type name(void) { \ + long __res; \ + __asm__ __volatile__ ( \ + __syscall(name) \ + "mov %0,r0" \ + :"=r" (__res) : : "r0","lr"); \ + __syscall_return(type,__res); \ +} + +#define _syscall1(type,name,type1,arg1) \ +type name(type1 arg1) { \ + long __res; \ + __asm__ __volatile__ ( \ + "mov\tr0,%1\n\t" \ + __syscall(name) \ + "mov %0,r0" \ + : "=r" (__res) \ + : "r" ((long)(arg1)) \ + : "r0","lr"); \ + __syscall_return(type,__res); \ +} + +#define _syscall2(type,name,type1,arg1,type2,arg2) \ +type name(type1 arg1,type2 arg2) { \ + long __res; \ + __asm__ __volatile__ ( \ + "mov\tr0,%1\n\t" \ + "mov\tr1,%2\n\t" \ + __syscall(name) \ + "mov\t%0,r0" \ + : "=r" (__res) \ + : "r" ((long)(arg1)),"r" ((long)(arg2)) \ + : "r0","r1","lr"); \ + __syscall_return(type,__res); \ +} + + +#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \ +type name(type1 arg1,type2 arg2,type3 arg3) { \ + long __res; \ + __asm__ __volatile__ ( \ + "mov\tr0,%1\n\t" \ + "mov\tr1,%2\n\t" \ + "mov\tr2,%3\n\t" \ + __syscall(name) \ + "mov\t%0,r0" \ + : "=r" (__res) \ + : "r" ((long)(arg1)),"r" ((long)(arg2)),"r" ((long)(arg3)) \ + : "r0","r1","r2","lr"); \ + __syscall_return(type,__res); \ +} + +#undef _syscall4 +#define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4)\ +type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + long __res; \ + __asm__ __volatile__ ( \ + "mov\tr0,%1\n\t" \ + "mov\tr1,%2\n\t" \ + "mov\tr2,%3\n\t" \ + "mov\tr3,%4\n\t" \ + __syscall(name) \ + "mov\t%0,r0" \ + : "=r" (__res) \ + : "r" ((long)(arg1)),"r" ((long)(arg2)), \ + "r" ((long)(arg3)),"r" ((long)(arg4)) \ + : "r0","r1","r2","r3","lr"); \ + __syscall_return(type,__res); \ +} + + diff --git a/ldso/ldso/arm/sysdep.h b/ldso/ldso/arm/sysdep.h new file mode 100644 index 000000000..b3d430519 --- /dev/null +++ b/ldso/ldso/arm/sysdep.h @@ -0,0 +1,120 @@ +/* + * Various assmbly language/system dependent hacks that are required + * so that we can minimize the amount of platform specific code. + */ + +/* + * Define this if the system uses RELOCA. + */ +#undef ELF_USES_RELOCA + +/* + * 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) + +/* + * Initialization sequence for a GOT. + */ +#define INIT_GOT(GOT_BASE,MODULE) \ +{ \ + GOT_BASE[2] = (unsigned long) _dl_linux_resolve; \ + GOT_BASE[1] = (unsigned long) MODULE; \ +} + +/* + * Here is a macro to perform a relocation. This is only used when + * bootstrapping the dynamic loader. RELP is the relocation that we + * are performing, REL is the pointer to the address we are relocating. + * SYMBOL is the symbol involved in the relocation, and LOAD is the + * load address. + */ +#define PERFORM_BOOTSTRAP_RELOC(RELP,REL,SYMBOL,LOAD) \ + switch(ELF32_R_TYPE((RELP)->r_info)){ \ + case R_ARM_ABS32: \ + *REL += SYMBOL; \ + break; \ + case R_ARM_PC24: \ + { \ + unsigned long newval, topbits; \ + long addend=*REL & 0x00ffffff; \ + if(addend & 0x00800000) \ + addend|=0xff000000; \ + newval=SYMBOL- ((unsigned long)REL) + (addend<<2); \ + topbits=newval & 0xfe000000; \ + if (topbits != 0xfe000000 && topbits != 0x00000000) {/* \ + newval=fix_bad_pc24(REL,value) - \ + ((unsigned long)REL) + (addend << 2); \ + topbits=newval & 0xfe000000; \ + if(topbits != 0xfe000000 && topbits != 0x00000000)*/ \ + _dl_exit(1); \ + } \ + newval>>=2; \ + SYMBOL= (*REL & 0xff000000)|(newval & 0x00ffffff); \ + *REL=SYMBOL; \ + } \ + break; \ + case R_ARM_GLOB_DAT: \ + case R_ARM_JUMP_SLOT: \ + *REL = SYMBOL; \ + break; \ + case R_ARM_RELATIVE: \ + *REL += (unsigned long) LOAD; \ + break; \ + case R_ARM_NONE: \ + break; \ + default: \ + _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. + */ + +#define START() return _dl_elf_main; + + + +/* Here we define the magic numbers that this dynamic loader should accept */ + +#define MAGIC1 EM_ARM +#undef MAGIC2 +/* Used for error messages */ +#define ELF_TARGET "ARM" + +struct elf_resolve; +extern unsigned long _dl_linux_resolver(struct elf_resolve * tpnt, int reloc_entry); + +static inline unsigned long arm_modulus(unsigned long m, unsigned long p) { + unsigned long i,t,inc; + i=p; t=0; + while(!(i&(1<<31))) { + i<<=1; + t++; + } + t--; + for(inc=t;inc>2;inc--) { + i=p<=i) { + m-=i; + i<<=1; + if(i&(1<<31)) + break; + if(i=p) { + m-=p; + } + return m; +} + +#define do_rem(result, n, base) result=arm_modulus(n,base); -- cgit v1.2.3