From: Julian Seward Date: Sat, 12 Feb 2005 18:59:31 +0000 (+0000) Subject: Resuscitate a linker that knows how to do relocations, so we can X-Git-Tag: svn/VALGRIND_3_0_1^2~442 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=322bf368f7edb42baf22e8d93d403338f637c7f9;p=thirdparty%2Fvalgrind.git Resuscitate a linker that knows how to do relocations, so we can actually run real programs. git-svn-id: svn://svn.valgrind.org/vex/trunk@892 --- diff --git a/VEX/switchback/Makefile b/VEX/switchback/Makefile index 545dcde05b..b8072b5950 100644 --- a/VEX/switchback/Makefile +++ b/VEX/switchback/Makefile @@ -1,6 +1,6 @@ -all: switchback.c - gcc -Wall -O -g -o switchback switchback.c ../libvex.a +all: switchback.c linker.c linker.h + gcc -Wall -O -g -o switchback switchback.c linker.c ../libvex.a clean: - rm -f switchback switchback.o + rm -f switchback switchback.o linker.o diff --git a/VEX/switchback/linker.c b/VEX/switchback/linker.c new file mode 100644 index 0000000000..7bf44686a0 --- /dev/null +++ b/VEX/switchback/linker.c @@ -0,0 +1,1313 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "linker.h" + +#define IF_DEBUG(x,y) /* */ +static int debug_linker = 0; + +#define i386_TARGET_ARCH +// #define arm_TARGET_ARCH + +#if !defined(i386_TARGET_ARCH) && !defined(arm_TARGET_ARCH) +# error "Must #define i386_TARGET_ARCH or arm_TARGET_ARCH" +#endif + + +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// +// +// TYPES + +#define FALSE 0 +#define TRUE 1 + +typedef enum { OBJECT_LOADED, OBJECT_RESOLVED } OStatus; + + +#define N_FIXUP_PAGES 1 + + +/* Indication of section kinds for loaded objects. Needed by + the GC for deciding whether or not a pointer on the stack + is a code pointer. +*/ +typedef + enum { SECTIONKIND_CODE_OR_RODATA, + SECTIONKIND_RWDATA, + SECTIONKIND_OTHER, + SECTIONKIND_NOINFOAVAIL } + SectionKind; + +typedef + struct _Section { + void* start; + void* end; + SectionKind kind; + struct _Section* next; + } + Section; + +typedef + struct _ProddableBlock { + void* start; + int size; + struct _ProddableBlock* next; + } + ProddableBlock; + +/* Top-level structure for an object module. One of these is allocated + * for each object file in use. + */ +typedef struct _ObjectCode { + OStatus status; + char* fileName; + int fileSize; + char* formatName; /* eg "ELF32", "DLL", "COFF", etc. */ + + /* An array containing ptrs to all the symbol names copied from + this object into the global symbol hash table. This is so that + we know which parts of the latter mapping to nuke when this + object is removed from the system. */ + char** symbols; + int n_symbols; + + /* ptr to malloc'd lump of memory holding the obj file */ + void* image; + + /* Fixup area for long-distance jumps. */ + char* fixup; + int fixup_used; + int fixup_size; + + /* The section-kind entries for this object module. Linked + list. */ + Section* sections; + + /* A private hash table for local symbols. */ + /* HashTable* */ void* lochash; + + /* Allow a chain of these things */ + struct _ObjectCode * next; + + /* SANITY CHECK ONLY: a list of the only memory regions which may + safely be prodded during relocation. Any attempt to prod + outside one of these is an error in the linker. */ + ProddableBlock* proddables; + +} ObjectCode; + +/* + * Define a set of types which can be used for both ELF32 and ELF64 + */ + +#ifdef ELF_64BIT +#define ELFCLASS ELFCLASS64 +#define Elf_Addr Elf64_Addr +#define Elf_Word Elf64_Word +#define Elf_Sword Elf64_Sword +#define Elf_Ehdr Elf64_Ehdr +#define Elf_Phdr Elf64_Phdr +#define Elf_Shdr Elf64_Shdr +#define Elf_Sym Elf64_Sym +#define Elf_Rel Elf64_Rel +#define Elf_Rela Elf64_Rela +#define ELF_ST_TYPE ELF64_ST_TYPE +#define ELF_ST_BIND ELF64_ST_BIND +#define ELF_R_TYPE ELF64_R_TYPE +#define ELF_R_SYM ELF64_R_SYM +#else +#define ELFCLASS ELFCLASS32 +#define Elf_Addr Elf32_Addr +#define Elf_Word Elf32_Word +#define Elf_Sword Elf32_Sword +#define Elf_Ehdr Elf32_Ehdr +#define Elf_Phdr Elf32_Phdr +#define Elf_Shdr Elf32_Shdr +#define Elf_Sym Elf32_Sym +#define Elf_Rel Elf32_Rel +#define Elf_Rela Elf32_Rela +#ifndef ELF_ST_TYPE +#define ELF_ST_TYPE ELF32_ST_TYPE +#endif +#ifndef ELF_ST_BIND +#define ELF_ST_BIND ELF32_ST_BIND +#endif +#ifndef ELF_R_TYPE +#define ELF_R_TYPE ELF32_R_TYPE +#endif +#ifndef ELF_R_SYM +#define ELF_R_SYM ELF32_R_SYM +#endif +#endif + + + + +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// +// +// PARANOIA + +/* ----------------------------------------------------------------------- + * Sanity checking. For each ObjectCode, maintain a list of address ranges + * which may be prodded during relocation, and abort if we try and write + * outside any of these. + */ +static void addProddableBlock ( ObjectCode* oc, void* start, int size ) +{ + ProddableBlock* pb + = malloc(sizeof(ProddableBlock)); + if (debug_linker) + fprintf(stderr, "aPB oc=%p %p %d (%p .. %p)\n", oc, start, size, + start, ((char*)start)+size-1 ); + assert(size > 0); + pb->start = start; + pb->size = size; + pb->next = oc->proddables; + oc->proddables = pb; +} + +static void checkProddableBlock ( ObjectCode* oc, void* addr ) +{ + ProddableBlock* pb; + for (pb = oc->proddables; pb != NULL; pb = pb->next) { + char* s = (char*)(pb->start); + char* e = s + pb->size - 1; + char* a = (char*)addr; + /* Assumes that the biggest fixup involves a 4-byte write. This + probably needs to be changed to 8 (ie, +7) on 64-bit + plats. */ + if (a >= s && (a+3) <= e) return; + } + fprintf(stderr, + "checkProddableBlock: invalid fixup %p in runtime linker\n", + addr); + exit(1); +} + + + +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// +// +// String->Addr mappings + +typedef + struct { char* mp_name; void* mp_addr; } + Maplet; + +typedef + struct { + int sm_size; + int sm_used; + Maplet* maplets; + } + StringMap; + +static StringMap* new_StringMap ( void ) +{ + StringMap* sm = malloc(sizeof(StringMap)); + sm->sm_size = 10; + sm->sm_used = 0; + sm->maplets = malloc(10 * sizeof(Maplet)); + return sm; +} + +static void delete_StringMap ( StringMap* sm ) +{ + assert(sm->maplets != NULL); + free(sm->maplets); + sm->maplets = NULL; + free(sm); +} + +static void ensure_StringMap ( StringMap* sm ) +{ + int i; + Maplet* mp2; + assert(sm->maplets != NULL); + if (sm->sm_used < sm->sm_size) + return; + sm->sm_size *= 2; + mp2 = malloc(sm->sm_size * sizeof(Maplet)); + for (i = 0; i < sm->sm_used; i++) + mp2[i] = sm->maplets[i]; + free(sm->maplets); + sm->maplets = mp2; +} + +static void* search_StringMap ( StringMap* sm, char* name ) +{ + int i; + for (i = 0; i < sm->sm_used; i++) + if (0 == strcmp(name, sm->maplets[i].mp_name)) + return sm->maplets[i].mp_addr; + return NULL; +} + +static void addto_StringMap ( StringMap* sm, char* name, void* addr ) +{ + ensure_StringMap(sm); + sm->maplets[sm->sm_used].mp_name = name; + sm->maplets[sm->sm_used].mp_addr = addr; + sm->sm_used++; +} + +static void paranoid_addto_StringMap ( StringMap* sm, char* name, void* addr ) +{ + if (search_StringMap(sm,name) != NULL) { + fprintf(stderr, "paranoid_addto_StringMap(%s,%p)\n", name, addr); + exit(1); + } + addto_StringMap(sm,name,addr); +} + + +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// +// +// Top-level linker control. + +StringMap* global_symbol_table = NULL; +ObjectCode* global_object_list = NULL; + +static void initLinker ( void ) +{ + if (global_symbol_table != NULL) + return; + global_symbol_table = new_StringMap(); +} + + + +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// +// +// SYMBOL TABLE(s) + +/* ----------------------------------------------------------------- + * lookup a symbol in the global symbol table + */ +static +void * lookupSymbol( char *lbl ) +{ + void *val; + initLinker() ; + assert(global_symbol_table != NULL); + val = search_StringMap(global_symbol_table, lbl); + return val; +} + + +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// +// +// HELPERS + +/* + * Generic ELF functions + */ + +static char * +findElfSection ( void* objImage, Elf_Word sh_type ) +{ + char* ehdrC = (char*)objImage; + Elf_Ehdr* ehdr = (Elf_Ehdr*)ehdrC; + Elf_Shdr* shdr = (Elf_Shdr*)(ehdrC + ehdr->e_shoff); + char* sh_strtab = ehdrC + shdr[ehdr->e_shstrndx].sh_offset; + char* ptr = NULL; + int i; + + for (i = 0; i < ehdr->e_shnum; i++) { + if (shdr[i].sh_type == sh_type + /* Ignore the section header's string table. */ + && i != ehdr->e_shstrndx + /* Ignore string tables named .stabstr, as they contain + debugging info. */ + && 0 != memcmp(".stabstr", sh_strtab + shdr[i].sh_name, 8) + ) { + ptr = ehdrC + shdr[i].sh_offset; + break; + } + } + return ptr; +} + +#ifdef arm_TARGET_ARCH +static +char* alloc_fixup_bytes ( ObjectCode* oc, int nbytes ) +{ + char* res; + assert(nbytes % 4 == 0); + assert(nbytes > 0); + res = &(oc->fixup[oc->fixup_used]); + oc->fixup_used += nbytes; + if (oc->fixup_used >= oc->fixup_size) { + fprintf(stderr, "fixup area too small for %s\n", oc->fileName); + exit(1); + } + return res; +} +#endif + + +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// +// +// RESOLVE + +static +void* lookup_magic_hacks ( char* sym ) +{ + if (0==strcmp(sym, "printf")) return (void*)(&printf); + return NULL; +} + +#ifdef arm_TARGET_ARCH +static +void arm_notify_new_code ( char* start, int length ) +{ + __asm __volatile ("mov r1, %0\n\t" + "mov r2, %1\n\t" + "mov r3, %2\n\t" + "swi 0x9f0002\n\t" + : + : "ir" (start), "ir" (length), "ir" (0) ); +} + + +static +void gen_armle_goto ( char* fixup, char* dstP ) +{ + Elf_Word w = (Elf_Word)dstP; + /* + 2 .text + 3 0000 04F01FE5 ldr pc, value + 4 0004 44332211 value: .word 0x11223344 + */ + fprintf(stderr,"at %p generating jump to %p\n", fixup, dstP ); + fixup[0] = 0x04; fixup[1] = 0xF0; fixup[2] = 0x1F; fixup[3] = 0xE5; + fixup[4] = w & 0xFF; w >>= 8; + fixup[5] = w & 0xFF; w >>= 8; + fixup[6] = w & 0xFF; w >>= 8; + fixup[7] = w & 0xFF; w >>= 8; + arm_notify_new_code(fixup, 8); +} +#endif /* arm_TARGET_ARCH */ + + + +/* Do ELF relocations which lack an explicit addend. All x86-linux + relocations appear to be of this form. */ +static int +do_Elf_Rel_relocations ( ObjectCode* oc, char* ehdrC, + Elf_Shdr* shdr, int shnum, + Elf_Sym* stab, char* strtab ) +{ + int j; + char *symbol = NULL; + Elf_Word* targ; + Elf_Rel* rtab = (Elf_Rel*) (ehdrC + shdr[shnum].sh_offset); + int nent = shdr[shnum].sh_size / sizeof(Elf_Rel); + int target_shndx = shdr[shnum].sh_info; + int symtab_shndx = shdr[shnum].sh_link; + + stab = (Elf_Sym*) (ehdrC + shdr[ symtab_shndx ].sh_offset); + targ = (Elf_Word*)(ehdrC + shdr[ target_shndx ].sh_offset); + IF_DEBUG(linker,belch( "relocations for section %d using symtab %d", + target_shndx, symtab_shndx )); + + for (j = 0; j < nent; j++) { + Elf_Addr offset = rtab[j].r_offset; + Elf_Addr info = rtab[j].r_info; + + Elf_Addr P = ((Elf_Addr)targ) + offset; + Elf_Word* pP = (Elf_Word*)P; + Elf_Addr A = *pP; + Elf_Addr S; + Elf_Addr value; + + IF_DEBUG(linker,belch( "Rel entry %3d is raw(%6p %6p)", + j, (void*)offset, (void*)info )); + if (!info) { + IF_DEBUG(linker,belch( " ZERO" )); + S = 0; + } else { + Elf_Sym sym = stab[ELF_R_SYM(info)]; + /* First see if it is a local symbol. */ + if (ELF_ST_BIND(sym.st_info) == STB_LOCAL) { + /* Yes, so we can get the address directly from the ELF symbol + table. */ + symbol = sym.st_name==0 ? "(noname)" : strtab+sym.st_name; + S = (Elf_Addr) + (ehdrC + shdr[ sym.st_shndx ].sh_offset + + stab[ELF_R_SYM(info)].st_value); + + } else { + /* No, so look up the name in our global table. */ + symbol = strtab + sym.st_name; + S = (Elf_Addr)lookupSymbol( symbol ); + } + if (!S) { + S = (Elf_Addr)lookup_magic_hacks(symbol); + } + if (!S) { + fprintf(stderr,"%s: unknown symbol `%s'\n", + oc->fileName, symbol); + return 0; + } + if (debug_linker>1) + fprintf(stderr, "\n`%s' resolves to %p\n", symbol, (void*)S ); + } + + if (debug_linker>1) + fprintf(stderr, "Reloc: P = %p S = %p A = %p\n", + (void*)P, (void*)S, (void*)A ); + checkProddableBlock ( oc, pP ); + + value = S + A; + + switch (ELF_R_TYPE(info)) { +# ifdef i386_TARGET_ARCH + case R_386_32: *pP = value; break; + case R_386_PC32: *pP = value - P; break; +# endif +# ifdef arm_TARGET_ARCH + case R_ARM_PC24: { + Elf_Word w, delta, deltaTop8; + /* Generate a jump sequence into the fixup area + and branch to that instead. */ + char* fixup = alloc_fixup_bytes(oc, 8); + /* First of all, figure out where we're really trying to + jump to. */ + // compensate for pc+8 bias + Elf_Word real_dst = (A & 0x00FFFFFF) + 2; + // sign-extend 24-to-32 of real_dst + if (real_dst & 0x00800000) + real_dst |= 0xFF000000; + else + real_dst &= 0x00FFFFFF; + + real_dst <<= 2; + real_dst += S; + + gen_armle_goto(fixup, (char*)real_dst); + + /* Delta is in bytes .. */ + delta = (((Elf_Word)fixup) - ((Elf_Word)pP) - 8); + deltaTop8 = (delta >> 24) & 0xFF; + if (deltaTop8 != 0 && deltaTop8 != 0xFF) { + fprintf(stderr,"R_ARM_PC24: out of range delta 0x%x for %s\n", + delta, symbol); + exit(1); + } + delta >>= 2; + w = *pP; + w &= 0xFF000000; + w |= (0x00FFFFFF & delta ); + *pP = w; + break; + } + case R_ARM_ABS32: + *pP = value; + break; +# endif + default: + fprintf(stderr, + "%s: unhandled ELF relocation(Rel) type %d\n\n", + oc->fileName, ELF_R_TYPE(info)); + return 0; + } + + } + return 1; +} + +/* Do ELF relocations for which explicit addends are supplied. + sparc-solaris relocations appear to be of this form. */ +static int +do_Elf_Rela_relocations ( ObjectCode* oc, char* ehdrC, + Elf_Shdr* shdr, int shnum, + Elf_Sym* stab, char* strtab ) +{ + int j; + char *symbol; + Elf_Addr targ; + Elf_Rela* rtab = (Elf_Rela*) (ehdrC + shdr[shnum].sh_offset); + int nent = shdr[shnum].sh_size / sizeof(Elf_Rela); + int target_shndx = shdr[shnum].sh_info; + int symtab_shndx = shdr[shnum].sh_link; + + stab = (Elf_Sym*) (ehdrC + shdr[ symtab_shndx ].sh_offset); + targ = (Elf_Addr) (ehdrC + shdr[ target_shndx ].sh_offset); + IF_DEBUG(linker,belch( "relocations for section %d using symtab %d", + target_shndx, symtab_shndx )); + + for (j = 0; j < nent; j++) { +#if defined(DEBUG) || defined(sparc_TARGET_ARCH) || defined(ia64_TARGET_ARCH) + /* This #ifdef only serves to avoid unused-var warnings. */ + Elf_Addr offset = rtab[j].r_offset; + Elf_Addr P = targ + offset; +#endif + Elf_Addr info = rtab[j].r_info; + Elf_Addr A = rtab[j].r_addend; + Elf_Addr S; + Elf_Addr value; +# if defined(sparc_TARGET_ARCH) + Elf_Word* pP = (Elf_Word*)P; + Elf_Word w1, w2; +# elif defined(ia64_TARGET_ARCH) + Elf64_Xword *pP = (Elf64_Xword *)P; + Elf_Addr addr; +# endif + + IF_DEBUG(linker,belch( "Rel entry %3d is raw(%6p %6p %6p) ", + j, (void*)offset, (void*)info, + (void*)A )); + if (!info) { + IF_DEBUG(linker,belch( " ZERO" )); + S = 0; + } else { + Elf_Sym sym = stab[ELF_R_SYM(info)]; + /* First see if it is a local symbol. */ + if (ELF_ST_BIND(sym.st_info) == STB_LOCAL) { + /* Yes, so we can get the address directly from the ELF symbol + table. */ + symbol = sym.st_name==0 ? "(noname)" : strtab+sym.st_name; + S = (Elf_Addr) + (ehdrC + shdr[ sym.st_shndx ].sh_offset + + stab[ELF_R_SYM(info)].st_value); +#ifdef ELF_FUNCTION_DESC + /* Make a function descriptor for this function */ + if (S && ELF_ST_TYPE(sym.st_info) == STT_FUNC) { + S = allocateFunctionDesc(S + A); + A = 0; + } +#endif + } else { + /* No, so look up the name in our global table. */ + symbol = strtab + sym.st_name; + S = (Elf_Addr)lookupSymbol( symbol ); + +#ifdef ELF_FUNCTION_DESC + /* If a function, already a function descriptor - we would + have to copy it to add an offset. */ + if (S && (ELF_ST_TYPE(sym.st_info) == STT_FUNC) && (A != 0)) + belch("%s: function %s with addend %p", oc->fileName, symbol, (void *)A); +#endif + } + if (!S) { + fprintf(stderr,"%s: unknown symbol `%s'\n", oc->fileName, symbol); + return 0; + } + IF_DEBUG(linker,belch( "`%s' resolves to %p\n", symbol, (void*)S )); + } + + IF_DEBUG(linker,fprintf ( stderr, "Reloc: P = %p S = %p A = %p\n", + (void*)P, (void*)S, (void*)A )); + /* checkProddableBlock ( oc, (void*)P ); */ + + value = S + A; + + switch (ELF_R_TYPE(info)) { +# if defined(sparc_TARGET_ARCH) + case R_SPARC_WDISP30: + w1 = *pP & 0xC0000000; + w2 = (Elf_Word)((value - P) >> 2); + ASSERT((w2 & 0xC0000000) == 0); + w1 |= w2; + *pP = w1; + break; + case R_SPARC_HI22: + w1 = *pP & 0xFFC00000; + w2 = (Elf_Word)(value >> 10); + ASSERT((w2 & 0xFFC00000) == 0); + w1 |= w2; + *pP = w1; + break; + case R_SPARC_LO10: + w1 = *pP & ~0x3FF; + w2 = (Elf_Word)(value & 0x3FF); + ASSERT((w2 & ~0x3FF) == 0); + w1 |= w2; + *pP = w1; + break; + /* According to the Sun documentation: + R_SPARC_UA32 + This relocation type resembles R_SPARC_32, except it refers to an + unaligned word. That is, the word to be relocated must be treated + as four separate bytes with arbitrary alignment, not as a word + aligned according to the architecture requirements. + + (JRS: which means that freeloading on the R_SPARC_32 case + is probably wrong, but hey ...) + */ + case R_SPARC_UA32: + case R_SPARC_32: + w2 = (Elf_Word)value; + *pP = w2; + break; +# elif defined(ia64_TARGET_ARCH) + case R_IA64_DIR64LSB: + case R_IA64_FPTR64LSB: + *pP = value; + break; + case R_IA64_PCREL64LSB: + *pP = value - P; + break; + case R_IA64_SEGREL64LSB: + addr = findElfSegment(ehdrC, value); + *pP = value - addr; + break; + case R_IA64_GPREL22: + ia64_reloc_gprel22(P, value); + break; + case R_IA64_LTOFF22: + case R_IA64_LTOFF22X: + case R_IA64_LTOFF_FPTR22: + addr = allocateGOTEntry(value); + ia64_reloc_gprel22(P, addr); + break; + case R_IA64_PCREL21B: + ia64_reloc_pcrel21(P, S, oc); + break; + case R_IA64_LDXMOV: + /* This goes with R_IA64_LTOFF22X and points to the load to + * convert into a move. We don't implement relaxation. */ + break; +# endif + default: + fprintf(stderr, + "%s: unhandled ELF relocation(RelA) type %d\n", + oc->fileName, ELF_R_TYPE(info)); + return 0; + } + + } + return 1; +} + + +static int +ocResolve_ELF ( ObjectCode* oc ) +{ + char *strtab; + int shnum, ok; + Elf_Sym* stab = NULL; + char* ehdrC = (char*)(oc->image); + Elf_Ehdr* ehdr = (Elf_Ehdr*) ehdrC; + Elf_Shdr* shdr = (Elf_Shdr*) (ehdrC + ehdr->e_shoff); + char* sh_strtab = ehdrC + shdr[ehdr->e_shstrndx].sh_offset; + + /* first find "the" symbol table */ + stab = (Elf_Sym*) findElfSection ( ehdrC, SHT_SYMTAB ); + + /* also go find the string table */ + strtab = findElfSection ( ehdrC, SHT_STRTAB ); + + if (stab == NULL || strtab == NULL) { + fprintf(stderr,"%s: can't find string or symbol table\n", oc->fileName); + return 0; + } + + /* Process the relocation sections. */ + for (shnum = 0; shnum < ehdr->e_shnum; shnum++) { + + /* Skip sections called ".rel.stab". These appear to contain + relocation entries that, when done, make the stabs debugging + info point at the right places. We ain't interested in all + dat jazz, mun. */ + if (0 == memcmp(".rel.stab", sh_strtab + shdr[shnum].sh_name, 9)) + continue; + + if (shdr[shnum].sh_type == SHT_REL ) { + ok = do_Elf_Rel_relocations ( oc, ehdrC, shdr, + shnum, stab, strtab ); + if (!ok) return ok; + } + else + if (shdr[shnum].sh_type == SHT_RELA) { + ok = do_Elf_Rela_relocations ( oc, ehdrC, shdr, + shnum, stab, strtab ); + if (!ok) return ok; + } + } + + /* Free the local symbol table; we won't need it again. */ + delete_StringMap(oc->lochash); + oc->lochash = NULL; + + return 1; +} + + +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// +// +// VERIFY + +static int +ocVerifyImage_ELF ( ObjectCode* oc ) +{ + Elf_Shdr* shdr; + Elf_Sym* stab; + int i, j, nent, nstrtab, nsymtabs; + char* sh_strtab; + char* strtab; + + char* ehdrC = (char*)(oc->image); + Elf_Ehdr* ehdr = (Elf_Ehdr*)ehdrC; + + if (ehdr->e_ident[EI_MAG0] != ELFMAG0 || + ehdr->e_ident[EI_MAG1] != ELFMAG1 || + ehdr->e_ident[EI_MAG2] != ELFMAG2 || + ehdr->e_ident[EI_MAG3] != ELFMAG3) { + fprintf(stderr,"%s: not an ELF object\n", oc->fileName); + return 0; + } + + if (ehdr->e_ident[EI_CLASS] != ELFCLASS) { + fprintf(stderr,"%s: unsupported ELF format\n", oc->fileName); + return 0; + } + + if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB) { + if (debug_linker) + fprintf(stderr, "Is little-endian\n" ); + } else + if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB) { + if (debug_linker) + fprintf(stderr, "Is big-endian\n" ); + } else { + fprintf(stderr,"%s: unknown endiannness\n", oc->fileName); + return 0; + } + + if (ehdr->e_type != ET_REL) { + fprintf(stderr,"%s: not a relocatable object (.o) file\n", oc->fileName); + return 0; + } + if (debug_linker) + fprintf(stderr, "Is a relocatable object (.o) file\n" ); + + if (debug_linker) + fprintf(stderr, "Architecture is " ); + switch (ehdr->e_machine) { + case EM_386: if (debug_linker) fprintf(stderr, "x86\n" ); break; + case EM_SPARC: if (debug_linker) fprintf(stderr, "sparc\n" ); break; + case EM_ARM: if (debug_linker) fprintf(stderr, "arm\n" ); break; +#ifdef EM_IA_64 + case EM_IA_64: if (debug_linker) fprintf(stderr, "ia64\n" ); break; +#endif + default: if (debug_linker) fprintf(stderr, "unknown\n" ); + fprintf(stderr,"%s: unknown architecture\n", oc->fileName); + return 0; + } + + if (debug_linker>1) fprintf(stderr, + "\nSection header table: start %d, n_entries %d, ent_size %d\n", + ehdr->e_shoff, ehdr->e_shnum, ehdr->e_shentsize ); + + assert (ehdr->e_shentsize == sizeof(Elf_Shdr)); + + shdr = (Elf_Shdr*) (ehdrC + ehdr->e_shoff); + + if (ehdr->e_shstrndx == SHN_UNDEF) { + fprintf(stderr,"%s: no section header string table\n", oc->fileName); + return 0; + } else { + if (debug_linker>1) + fprintf(stderr, "Section header string table is section %d\n", + ehdr->e_shstrndx); + sh_strtab = ehdrC + shdr[ehdr->e_shstrndx].sh_offset; + } + + for (i = 0; i < ehdr->e_shnum; i++) { + if (debug_linker>1) fprintf(stderr, "%2d: ", i ); + if (debug_linker>1) fprintf(stderr, "type=%2d ", (int)shdr[i].sh_type ); + if (debug_linker>1) fprintf(stderr, "size=%4d ", (int)shdr[i].sh_size ); + if (debug_linker>1) fprintf(stderr, "offs=%4d ", (int)shdr[i].sh_offset ); + if (debug_linker>1) fprintf(stderr, " (%p .. %p) ", + ehdrC + shdr[i].sh_offset, + ehdrC + shdr[i].sh_offset + shdr[i].sh_size - 1); + + if (shdr[i].sh_type == SHT_REL) { + if (debug_linker>1) fprintf(stderr, "Rel " ); + } else if (shdr[i].sh_type == SHT_RELA) { + if (debug_linker>1) fprintf(stderr, "RelA " ); + } else { + if (debug_linker>1) fprintf(stderr," "); + } + if (sh_strtab) { + if (debug_linker>1) fprintf(stderr, "sname=%s\n", + sh_strtab + shdr[i].sh_name ); + } + } + + if (debug_linker>1) fprintf(stderr, "\nString tables\n" ); + strtab = NULL; + nstrtab = 0; + for (i = 0; i < ehdr->e_shnum; i++) { + if (shdr[i].sh_type == SHT_STRTAB + /* Ignore the section header's string table. */ + && i != ehdr->e_shstrndx + /* Ignore string tables named .stabstr, as they contain + debugging info. */ + && 0 != memcmp(".stabstr", sh_strtab + shdr[i].sh_name, 8) + ) { + if (debug_linker>1) + fprintf(stderr," section %d is a normal string table\n", i ); + strtab = ehdrC + shdr[i].sh_offset; + nstrtab++; + } + } + if (nstrtab != 1) { + fprintf(stderr,"%s: no string tables, or too many\n", oc->fileName); + return 0; + } + + nsymtabs = 0; + if (debug_linker>1) fprintf(stderr, "\nSymbol tables\n" ); + for (i = 0; i < ehdr->e_shnum; i++) { + if (shdr[i].sh_type != SHT_SYMTAB) continue; + if (debug_linker>1) fprintf(stderr, "section %d is a symbol table\n", i ); + nsymtabs++; + stab = (Elf_Sym*) (ehdrC + shdr[i].sh_offset); + nent = shdr[i].sh_size / sizeof(Elf_Sym); + if (debug_linker>1) fprintf(stderr, + " number of entries is apparently %d (%d rem)\n", + nent, + shdr[i].sh_size % sizeof(Elf_Sym) + ); + if (0 != shdr[i].sh_size % sizeof(Elf_Sym)) { + fprintf(stderr,"%s: non-integral number of symbol table entries\n", + oc->fileName); + return 0; + } + for (j = 0; j < nent; j++) { + if (debug_linker>1) fprintf(stderr, " %2d ", j ); + if (debug_linker>1) fprintf(stderr, " sec=%-5d size=%-3d val=%5p ", + (int)stab[j].st_shndx, + (int)stab[j].st_size, + (char*)stab[j].st_value ); + + if (debug_linker>1) fprintf(stderr, "type=" ); + switch (ELF_ST_TYPE(stab[j].st_info)) { + case STT_NOTYPE: if (debug_linker>1) fprintf(stderr, "notype " ); break; + case STT_OBJECT: if (debug_linker>1) fprintf(stderr, "object " ); break; + case STT_FUNC : if (debug_linker>1) fprintf(stderr, "func " ); break; + case STT_SECTION: if (debug_linker>1) fprintf(stderr, "section" ); break; + case STT_FILE: if (debug_linker>1) fprintf(stderr, "file " ); break; + default: if (debug_linker>1) fprintf(stderr, "? " ); break; + } + if (debug_linker>1) fprintf(stderr, " " ); + + if (debug_linker>1) fprintf(stderr, "bind=" ); + switch (ELF_ST_BIND(stab[j].st_info)) { + case STB_LOCAL : if (debug_linker>1) fprintf(stderr, "local " ); break; + case STB_GLOBAL: if (debug_linker>1) fprintf(stderr, "global" ); break; + case STB_WEAK : if (debug_linker>1) fprintf(stderr, "weak " ); break; + default: if (debug_linker>1) fprintf(stderr, "? " ); break; + } + if (debug_linker>1) fprintf(stderr, " " ); + + if (debug_linker>1) fprintf(stderr, "name=%s\n", strtab + stab[j].st_name ); + } + } + + if (nsymtabs == 0) { + fprintf(stderr,"%s: didn't find any symbol tables\n", oc->fileName); + return 0; + } + + return 1; +} + + +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// +// +// GETNAMES + +static int +ocGetNames_ELF ( ObjectCode* oc ) +{ + int i, j, k, nent; + Elf_Sym* stab; + + char* ehdrC = (char*)(oc->image); + Elf_Ehdr* ehdr = (Elf_Ehdr*)ehdrC; + char* strtab = findElfSection ( ehdrC, SHT_STRTAB ); + Elf_Shdr* shdr = (Elf_Shdr*) (ehdrC + ehdr->e_shoff); + + char* sh_strtab = ehdrC + shdr[ehdr->e_shstrndx].sh_offset; + char* sec_name; + + assert(global_symbol_table != NULL); + + if (!strtab) { + fprintf(stderr,"%s: no strtab\n", oc->fileName); + return 0; + } + + k = 0; + for (i = 0; i < ehdr->e_shnum; i++) { + /* Figure out what kind of section it is. Logic derived from + Figure 1.14 ("Special Sections") of the ELF document + ("Portable Formats Specification, Version 1.1"). */ + Elf_Shdr hdr = shdr[i]; + SectionKind kind = SECTIONKIND_OTHER; + int is_bss = FALSE; + + if (hdr.sh_type == SHT_PROGBITS + && (hdr.sh_flags & SHF_ALLOC) && (hdr.sh_flags & SHF_EXECINSTR)) { + /* .text-style section */ + kind = SECTIONKIND_CODE_OR_RODATA; + } + else + if (hdr.sh_type == SHT_PROGBITS + && (hdr.sh_flags & SHF_ALLOC) && (hdr.sh_flags & SHF_WRITE)) { + /* .data-style section */ + kind = SECTIONKIND_RWDATA; + } + else + if (hdr.sh_type == SHT_PROGBITS + && (hdr.sh_flags & SHF_ALLOC) && !(hdr.sh_flags & SHF_WRITE)) { + /* .rodata-style section */ + kind = SECTIONKIND_CODE_OR_RODATA; + } + else + if (hdr.sh_type == SHT_NOBITS + && (hdr.sh_flags & SHF_ALLOC) && (hdr.sh_flags & SHF_WRITE)) { + /* .bss-style section */ + kind = SECTIONKIND_RWDATA; + is_bss = TRUE; + } + + if (is_bss && shdr[i].sh_size > 0) { + /* This is a non-empty .bss section. Allocate zeroed space for + it, and set its .sh_offset field such that + ehdrC + .sh_offset == addr_of_zeroed_space. */ + char* zspace = calloc(1, shdr[i].sh_size); + shdr[i].sh_offset = ((char*)zspace) - ((char*)ehdrC); + /* + fprintf(stderr, "BSS section at 0x%x, size %d\n", + zspace, shdr[i].sh_size); + */ + } + + /* When loading objects compiled with -g, it seems there are + relocations in various debug-info sections. So we'd better + tell addProddableBlock to allow those bits to be prodded. */ + //fprintf(stderr, "ZZZZZZZZZZ %s\n", sh_strtab + hdr.sh_name); + sec_name = sh_strtab + shdr[i].sh_name; + if (kind == SECTIONKIND_OTHER + && (0 == strcmp(".debug_info", sec_name) + || 0 == strcmp(".debug_line", sec_name) + || 0 == strcmp(".debug_pubnames", sec_name) + || 0 == strcmp(".debug_aranges", sec_name) + || 0 == strcmp(".debug_frame", sec_name))) { + kind = SECTIONKIND_CODE_OR_RODATA; + } + + /* fill in the section info */ + if (kind != SECTIONKIND_OTHER && shdr[i].sh_size > 0) { + addProddableBlock(oc, ehdrC + shdr[i].sh_offset, shdr[i].sh_size); + //addSection(oc, kind, ehdrC + shdr[i].sh_offset, + // ehdrC + shdr[i].sh_offset + shdr[i].sh_size - 1); + } + + if (shdr[i].sh_type != SHT_SYMTAB) continue; + + /* copy stuff into this module's object symbol table */ + stab = (Elf_Sym*) (ehdrC + shdr[i].sh_offset); + nent = shdr[i].sh_size / sizeof(Elf_Sym); + + oc->n_symbols = nent; + oc->symbols = malloc(oc->n_symbols * sizeof(char*)); + + for (j = 0; j < nent; j++) { + + char isLocal = FALSE; /* avoids uninit-var warning */ + char* ad = NULL; + char* nm = strtab + stab[j].st_name; + int secno = stab[j].st_shndx; + + /* Figure out if we want to add it; if so, set ad to its + address. Otherwise leave ad == NULL. */ + + if (secno == SHN_COMMON) { + isLocal = FALSE; + ad = calloc(1, stab[j].st_size); + /* + fprintf(stderr, "COMMON symbol, size %d name %s\n", + stab[j].st_size, nm); + */ + /* Pointless to do addProddableBlock() for this area, + since the linker should never poke around in it. */ + } + else + if ( ( ELF_ST_BIND(stab[j].st_info)==STB_GLOBAL + || ELF_ST_BIND(stab[j].st_info)==STB_LOCAL + ) + /* and not an undefined symbol */ + && stab[j].st_shndx != SHN_UNDEF + /* and not in a "special section" */ + && stab[j].st_shndx < SHN_LORESERVE + && + /* and it's a not a section or string table or anything silly */ + ( ELF_ST_TYPE(stab[j].st_info)==STT_FUNC || + ELF_ST_TYPE(stab[j].st_info)==STT_OBJECT || + ELF_ST_TYPE(stab[j].st_info)==STT_NOTYPE + ) + ) { + /* Section 0 is the undefined section, hence > and not >=. */ + assert(secno > 0 && secno < ehdr->e_shnum); + /* + if (shdr[secno].sh_type == SHT_NOBITS) { + fprintf(stderr, " BSS symbol, size %d off %d name %s\n", + stab[j].st_size, stab[j].st_value, nm); + } + */ + ad = ehdrC + shdr[ secno ].sh_offset + stab[j].st_value; + if (ELF_ST_BIND(stab[j].st_info)==STB_LOCAL) { + isLocal = TRUE; + } else { +#ifdef ELF_FUNCTION_DESC + /* dlsym() and the initialisation table both give us function + * descriptors, so to be consistent we store function descriptors + * in the symbol table */ + if (ELF_ST_TYPE(stab[j].st_info) == STT_FUNC) + ad = (char *)allocateFunctionDesc((Elf_Addr)ad); +#endif + if (debug_linker) + fprintf(stderr, "addOTabName(GLOB): %10p %s %s\n", + ad, oc->fileName, nm ); + isLocal = FALSE; + } + } + + /* And the decision is ... */ + + if (ad != NULL) { + assert(nm != NULL); + oc->symbols[j] = nm; + /* Acquire! */ + if (isLocal) { + /* Ignore entirely. */ + } else { + //ghciInsertStrHashTable(oc->fileName, global_symbol_table, nm, ad); + paranoid_addto_StringMap(global_symbol_table, nm, ad); + } + } else { + /* Skip. */ + if (debug_linker>1) fprintf(stderr, "skipping `%s'\n", + strtab + stab[j].st_name ); + /* + fprintf(stderr, + "skipping bind = %d, type = %d, shndx = %d `%s'\n", + (int)ELF_ST_BIND(stab[j].st_info), + (int)ELF_ST_TYPE(stab[j].st_info), + (int)stab[j].st_shndx, + strtab + stab[j].st_name + ); + */ + oc->symbols[j] = NULL; + } + + } + } + + return 1; +} + + +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// +// +// TOP-LEVEL CONTROL OF THE LINKER + + +/* --------------------------------------------------------------------- + * Load an obj (populate the global symbol table, but don't resolve yet) + * + * Returns: 1 if ok, 0 on error. + */ +static +int loadObj( char *path ) +{ + ObjectCode* oc; + struct stat st; + int r; + int fd, pagesize; + char* p; + + initLinker(); + + fprintf(stderr, "==== loadObj %s ====\n", path ); + + /* Check that we haven't already loaded this object. */ + { + ObjectCode *o; + int is_dup = 0; + for (o = global_object_list; o; o = o->next) { + if (0 == strcmp(o->fileName, path)) + is_dup = 1; + } + if (is_dup) { + fprintf(stderr, + "\n\n" + "GHCi runtime linker: warning: looks like you're trying to load the\n" + "same object file twice:\n" + " %s\n" + , path); + exit(1); + } + } + + oc = malloc(sizeof(ObjectCode)); + + oc->formatName = "ELF"; + + r = stat(path, &st); + if (r == -1) { return 0; } + + /* sigh, strdup() isn't a POSIX function, so do it the long way */ + oc->fileName = malloc( strlen(path)+1 ); + strcpy(oc->fileName, path); + + oc->fileSize = st.st_size; + oc->symbols = NULL; + oc->sections = NULL; + oc->lochash = new_StringMap(); + oc->proddables = NULL; + oc->fixup = NULL; + oc->fixup_used = 0; + oc->fixup_size = 0; + + /* chain it onto the list of objects */ + oc->next = global_object_list; + global_object_list = oc; + + fd = open(path, O_RDONLY); + if (fd == -1) { + fprintf(stderr,"loadObj: can't open `%s'\n", path); + exit(1); + } + + /* Allocate a 1-page area just prior to the image, so we can put + fixup code fragments there. Used for doing R_ARM_PC24 + relocations for jump distances > 64M. */ + + pagesize = getpagesize(); + p = memalign(pagesize, N_FIXUP_PAGES * pagesize + + oc->fileSize); + + if (p == NULL) { + fprintf(stderr,"loadObj: failed to allocate space for `%s'\n", path); + exit(1); + } + + oc->fixup = p; + oc->fixup_size = N_FIXUP_PAGES * pagesize; + oc->fixup_used = 0; + oc->image = &(p[ oc->fixup_size ]); + + r = read(fd, oc->image, oc->fileSize); + if (r != oc->fileSize) { + fprintf(stderr,"loadObj: failed to read `%s'\n", path); + exit(1); + } + + fprintf(stderr, "loaded %s at %p (fixup = %p)\n", + oc->fileName, oc->image, oc->fixup ); + + close(fd); + + /* verify the in-memory image */ + r = ocVerifyImage_ELF ( oc ); + if (!r) { return r; } + + /* build the symbol list for this image */ + r = ocGetNames_ELF ( oc ); + if (!r) { return r; } + + /* loaded, but not resolved yet */ + oc->status = OBJECT_LOADED; + + return 1; +} + + + +/* --------------------------------------------------------------------------- + * resolve all the currently unlinked objects in memory + * + * Returns: 1 if ok, 0 on error. + */ +static +int resolveObjs( void ) +{ + ObjectCode *oc; + int r; + + initLinker(); + + for (oc = global_object_list; oc; oc = oc->next) { + if (oc->status != OBJECT_RESOLVED) { + r = ocResolve_ELF ( oc ); + if (!r) { return r; } + oc->status = OBJECT_RESOLVED; + } + } + return 1; +} + + +/* --------------------------------------------------------------------------- + * Top-level linker. + */ + +/* Load and link a bunch of .o's, and return the address of + 'entry'. Or NULL if something borks. +*/ +void* linker_top_level_LINK ( int n_object_names, char** object_names ) +{ + int r, i; + void* mainp; + + initLinker(); + for (i = 0; i < n_object_names; i++) { + //fprintf(stderr, "linkloop %d %s\n", i, object_names[i] ); + r = loadObj( object_names[i] ); + if (r != 1) return NULL; + } + r = resolveObjs(); + if (r != 1) return NULL; + mainp = search_StringMap ( global_symbol_table, "entry" ); + if (mainp == NULL) return NULL; + printf("switchback: Linker: success!\n"); + return mainp; +} + + diff --git a/VEX/switchback/linker.h b/VEX/switchback/linker.h new file mode 100644 index 0000000000..6b6c54b22b --- /dev/null +++ b/VEX/switchback/linker.h @@ -0,0 +1,3 @@ + +extern +void* linker_top_level_LINK ( int n_object_names, char** object_names ); diff --git a/VEX/switchback/switchback.c b/VEX/switchback/switchback.c index 82ff8e248d..126ebb9169 100644 --- a/VEX/switchback/switchback.c +++ b/VEX/switchback/switchback.c @@ -3,11 +3,10 @@ Compile test file (eg test_hello.c) to a .o -It must have an entry point called "entry" immediately preceded by the -bytes 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88, so the entry point can -be found. +It must have an entry point called "entry", which expects to +take a single argument which is a function pointer (to "serviceFn"). -It must have ***NO RELOCATIONS*** - objdump -r file.o shows none +Test file may not reference any other symbols. */ @@ -22,7 +21,7 @@ It must have ***NO RELOCATIONS*** - objdump -r file.o shows none #include "../pub/libvex_guest_x86.h" #include "../pub/libvex_guest_amd64.h" #include "../pub/libvex.h" - +#include "linker.h" static Int n_bbs_done = 0; @@ -77,8 +76,8 @@ HWord sb_helper1 = 0; HWord sb_helper2 = 0; /* translation cache */ -#define N_TRANS_CACHE 100000 -#define N_TRANS_TABLE 1000 +#define N_TRANS_CACHE 1000000 +#define N_TRANS_TABLE 10000 ULong trans_cache[N_TRANS_CACHE]; VexGuestExtents trans_table [N_TRANS_TABLE]; @@ -394,48 +393,15 @@ int main ( Int argc, HChar** argv ) return 1; } - imageSz = (Int)buf.st_size; - printf("stopAfter = %d\n", stopAfter); - printf("file size is %d\n", imageSz); - - /* Get the file aboard and find the entry point */ - image = malloc(imageSz); - assert(image); - - f = fopen(oname, "r"); - assert(f); - n = fread(image, 1, imageSz, f); - assert(n == imageSz); - fclose(f); - - assert(imageSz > 12); - entry = NULL; - for (n = 0; n < imageSz - 8; n++) { - if (image[n+0] == 0x11 - && image[n+1] == 0x22 - && image[n+2] == 0x33 - && image[n+3] == 0x44 - && image[n+4] == 0x55 - && image[n+5] == 0x66 - && image[n+6] == 0x77 - && image[n+7] == 0x88 - ) { - entry = &image[n+8]; - break; - } - } + entry = linker_top_level_LINK( 1, &argv[1] ); if (!entry) { printf("switchback: can't find entry point\n"); exit(1); } - printf("switchback: entry bytes are: "); - for (i = 0; i < 4; i++) - printf("0x%02x ", (Int)entry[i]); - LibVEX_default_VexControl(&vcon); - vcon.guest_max_insns=1; //50; + vcon.guest_max_insns=1; vcon.guest_chase_thresh=0; LibVEX_Init( failure_exit, log_bytes, 1, False, &vcon ); LibVEX_Guest_initialise(&gst);