X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=modutils%2Finsmod.c;h=0ca39911c6a9f2a002562ba06e62e639103cc879;hb=17d355cb3c97d50b9ffe39442bd2896abddd92f0;hp=7af135902a9e9e49a0af0534bee265355343b076;hpb=90fe7fea10b69d85c4d53e7061aaf85657136530;p=oweals%2Fbusybox.git diff --git a/modutils/insmod.c b/modutils/insmod.c index 7af135902..0ca39911c 100644 --- a/modutils/insmod.c +++ b/modutils/insmod.c @@ -2,10 +2,17 @@ /* * Mini insmod implementation for busybox * - * Copyright (C) 1999,2000,2001 by Lineo, inc. - * Written by Erik Andersen + * This version of insmod supports ARM, CRIS, H8/300, x86, ia64, x86_64, + * m68k, MIPS, PowerPC, S390, SH3/4/5, Sparc, v850e, and x86_64. + * + * Copyright (C) 1999-2004 by Erik Andersen * and Ron Alder * + * Rodney Radford 17-Aug-2004. + * Added x86_64 support. + * + * Miles Bader added NEC V850E support. + * * Modified by Bryan Rittmeyer to support SH4 * and (theoretically) SH3. I have only tested SH4 in little endian mode. * @@ -14,11 +21,32 @@ * very minor changes required to also work with StrongArm and presumably * all ARM based systems. * + * Yoshinori Sato 19-May-2004. + * added Renesas H8/300 support. + * + * Paul Mundt 08-Aug-2003. + * Integrated support for sh64 (SH-5), from preliminary modutils + * patches from Benedict Gaster . + * Currently limited to support for 32bit ABI. + * + * Magnus Damm 22-May-2002. + * The plt and got code are now using the same structs. + * Added generic linked list code to fully support PowerPC. + * Replaced the mess in arch_apply_relocation() with architecture blocks. + * The arch_create_got() function got cleaned up with architecture blocks. + * These blocks should be easy maintain and sync with obj_xxx.c in modutils. + * * Magnus Damm added PowerPC support 20-Feb-2001. - * PowerPC specific code stolen from modutils-2.3.16, + * PowerPC specific code stolen from modutils-2.3.16, * written by Paul Mackerras, Copyright 1996, 1997 Linux International. * I've only tested the code on mpc8xx platforms in big-endian mode. - * Did some cleanup and added BB_USE_xxx_ENTRIES... + * Did some cleanup and added CONFIG_USE_xxx_ENTRIES... + * + * Quinn Jensen added MIPS support 23-Feb-2001. + * based on modutils-2.4.2 + * MIPS specific support for Elf loading and relocation. + * Copyright 1996, 1997 Linux International. + * Contributed by Ralf Baechle * * Based almost entirely on the Linux modutils-2.3.11 implementation. * Copyright 1996, 1997 Linux International. @@ -53,33 +81,233 @@ #include #include #include +#include #include -#include -#include #include "busybox.h" -#if defined(__powerpc__) -#define BB_USE_PLT_ENTRIES -#define BB_PLT_ENTRY_SIZE 16 +#if !defined(CONFIG_FEATURE_2_4_MODULES) && \ + !defined(CONFIG_FEATURE_2_6_MODULES) +#define CONFIG_FEATURE_2_4_MODULES +#endif + +#if !defined(CONFIG_FEATURE_2_4_MODULES) +#define insmod_ng_main insmod_main +#endif + +#if defined(CONFIG_FEATURE_2_6_MODULES) +extern int insmod_ng_main( int argc, char **argv); +#endif + + +#if defined(CONFIG_FEATURE_2_4_MODULES) + + +#ifdef CONFIG_FEATURE_INSMOD_LOADINKMEM +#define LOADBITS 0 +#else +#define LOADBITS 1 #endif + +/* Alpha */ +#if defined(__alpha__) +#define MATCH_MACHINE(x) (x == EM_ALPHA) +#define SHT_RELM SHT_RELA +#define Elf64_RelM Elf64_Rela +#define ELFCLASSM ELFCLASS64 +#endif + +/* ARM support */ #if defined(__arm__) -#define BB_USE_PLT_ENTRIES -#define BB_PLT_ENTRY_SIZE 8 -#define BB_USE_GOT_ENTRIES -#define BB_GOT_ENTRY_SIZE 8 +#define MATCH_MACHINE(x) (x == EM_ARM) +#define SHT_RELM SHT_REL +#define Elf32_RelM Elf32_Rel +#define ELFCLASSM ELFCLASS32 +#define CONFIG_USE_PLT_ENTRIES +#define CONFIG_PLT_ENTRY_SIZE 8 +#define CONFIG_USE_GOT_ENTRIES +#define CONFIG_GOT_ENTRY_SIZE 8 +#define CONFIG_USE_SINGLE #endif -#if defined(__sh__) -#define BB_USE_GOT_ENTRIES -#define BB_GOT_ENTRY_SIZE 4 +/* CRIS */ +#if defined(__cris__) +#define MATCH_MACHINE(x) (x == EM_CRIS) +#define SHT_RELM SHT_RELA +#define Elf32_RelM Elf32_Rela +#define ELFCLASSM ELFCLASS32 +#ifndef EM_CRIS +#define EM_CRIS 76 +#define R_CRIS_NONE 0 +#define R_CRIS_32 3 +#endif #endif +/* H8/300 */ +#if defined(__H8300H__) || defined(__H8300S__) +#define MATCH_MACHINE(x) (x == EM_H8_300) +#define SHT_RELM SHT_RELA +#define Elf32_RelM Elf32_Rela +#define ELFCLASSM ELFCLASS32 +#define CONFIG_USE_SINGLE +#define SYMBOL_PREFIX "_" +#endif + +/* PA-RISC / HP-PA */ +#if defined(__hppa__) +#define MATCH_MACHINE(x) (x == EM_PARISC) +#define SHT_RELM SHT_RELA +#if defined(__LP64__) +#define Elf64_RelM Elf64_Rela +#define ELFCLASSM ELFCLASS64 +#else +#define Elf32_RelM Elf32_Rela +#define ELFCLASSM ELFCLASS32 +#endif +#endif + +/* x86 */ #if defined(__i386__) -#define BB_USE_GOT_ENTRIES -#define BB_GOT_ENTRY_SIZE 4 +#ifndef EM_486 +#define MATCH_MACHINE(x) (x == EM_386) +#else +#define MATCH_MACHINE(x) (x == EM_386 || x == EM_486) +#endif +#define SHT_RELM SHT_REL +#define Elf32_RelM Elf32_Rel +#define ELFCLASSM ELFCLASS32 +#define CONFIG_USE_GOT_ENTRIES +#define CONFIG_GOT_ENTRY_SIZE 4 +#define CONFIG_USE_SINGLE +#endif + +/* IA64, aka Itanium */ +#if defined(__ia64__) +#define MATCH_MACHINE(x) (x == EM_IA_64) +#define SHT_RELM SHT_RELA +#define Elf64_RelM Elf64_Rela +#define ELFCLASSM ELFCLASS64 +#endif + +/* m68k */ +#if defined(__mc68000__) +#define MATCH_MACHINE(x) (x == EM_68K) +#define SHT_RELM SHT_RELA +#define Elf32_RelM Elf32_Rela +#define ELFCLASSM ELFCLASS32 +#define CONFIG_USE_GOT_ENTRIES +#define CONFIG_GOT_ENTRY_SIZE 4 +#define CONFIG_USE_SINGLE +#endif + +/* MIPS */ +#if defined(__mips__) +#define MATCH_MACHINE(x) (x == EM_MIPS || x == EM_MIPS_RS3_LE) +#define SHT_RELM SHT_REL +#define Elf32_RelM Elf32_Rel +#define ELFCLASSM ELFCLASS32 +/* Account for ELF spec changes. */ +#ifndef EM_MIPS_RS3_LE +#ifdef EM_MIPS_RS4_BE +#define EM_MIPS_RS3_LE EM_MIPS_RS4_BE +#else +#define EM_MIPS_RS3_LE 10 +#endif +#endif /* !EM_MIPS_RS3_LE */ +#define ARCHDATAM "__dbe_table" +#endif + +/* PowerPC */ +#if defined(__powerpc64__) +#define MATCH_MACHINE(x) (x == EM_PPC64) +#define SHT_RELM SHT_RELA +#define Elf64_RelM Elf64_Rela +#define ELFCLASSM ELFCLASS64 +#elif defined(__powerpc__) +#define MATCH_MACHINE(x) (x == EM_PPC) +#define SHT_RELM SHT_RELA +#define Elf32_RelM Elf32_Rela +#define ELFCLASSM ELFCLASS32 +#define CONFIG_USE_PLT_ENTRIES +#define CONFIG_PLT_ENTRY_SIZE 16 +#define CONFIG_USE_PLT_LIST +#define CONFIG_LIST_ARCHTYPE ElfW(Addr) +#define CONFIG_USE_LIST +#define ARCHDATAM "__ftr_fixup" +#endif + +/* S390 */ +#if defined(__s390__) +#define MATCH_MACHINE(x) (x == EM_S390) +#define SHT_RELM SHT_RELA +#define Elf32_RelM Elf32_Rela +#define ELFCLASSM ELFCLASS32 +#define CONFIG_USE_PLT_ENTRIES +#define CONFIG_PLT_ENTRY_SIZE 8 +#define CONFIG_USE_GOT_ENTRIES +#define CONFIG_GOT_ENTRY_SIZE 8 +#define CONFIG_USE_SINGLE +#endif + +/* SuperH */ +#if defined(__sh__) +#define MATCH_MACHINE(x) (x == EM_SH) +#define SHT_RELM SHT_RELA +#define Elf32_RelM Elf32_Rela +#define ELFCLASSM ELFCLASS32 +#define CONFIG_USE_GOT_ENTRIES +#define CONFIG_GOT_ENTRY_SIZE 4 +#define CONFIG_USE_SINGLE +/* the SH changes have only been tested in =little endian= mode */ +/* I'm not sure about big endian, so let's warn: */ +#if defined(__sh__) && defined(__BIG_ENDIAN__) +#error insmod.c may require changes for use on big endian SH +#endif +/* it may or may not work on the SH1/SH2... Error on those also */ +#if ((!(defined(__SH3__) || defined(__SH4__) || defined(__SH5__)))) && (defined(__sh__)) +#error insmod.c may require changes for SH1 or SH2 use +#endif +#endif + +/* Sparc */ +#if defined(__sparc__) +#define MATCH_MACHINE(x) (x == EM_SPARC) +#define SHT_RELM SHT_RELA +#define Elf32_RelM Elf32_Rela +#define ELFCLASSM ELFCLASS32 +#endif + +/* v850e */ +#if defined (__v850e__) +#define MATCH_MACHINE(x) ((x) == EM_V850 || (x) == EM_CYGNUS_V850) +#define SHT_RELM SHT_RELA +#define Elf32_RelM Elf32_Rela +#define ELFCLASSM ELFCLASS32 +#define CONFIG_USE_PLT_ENTRIES +#define CONFIG_PLT_ENTRY_SIZE 8 +#define CONFIG_USE_SINGLE +#ifndef EM_CYGNUS_V850 /* grumble */ +#define EM_CYGNUS_V850 0x9080 +#endif +#define SYMBOL_PREFIX "_" +#endif + +/* X86_64 */ +#if defined(__x86_64__) +#define MATCH_MACHINE(x) (x == EM_X86_64) +#define SHT_RELM SHT_RELA +#define CONFIG_USE_GOT_ENTRIES +#define CONFIG_GOT_ENTRY_SIZE 8 +#define CONFIG_USE_SINGLE +#define Elf64_RelM Elf64_Rela +#define ELFCLASSM ELFCLASS64 +#endif + +#ifndef SHT_RELM +#error Sorry, but insmod.c does not yet support this architecture... #endif + //---------------------------------------------------------------------------- //--------modutils module.h, lines 45-242 //---------------------------------------------------------------------------- @@ -109,68 +337,7 @@ #ifndef MODUTILS_MODULE_H static const int MODUTILS_MODULE_H = 1; -#ident "$Id: insmod.c,v 1.49 2001/02/20 20:47:08 andersen Exp $" - -/* This file contains the structures used by the 2.0 and 2.1 kernels. - We do not use the kernel headers directly because we do not wish - to be dependant on a particular kernel version to compile insmod. */ - - -/*======================================================================*/ -/* The structures used by Linux 2.0. */ - -/* The symbol format used by get_kernel_syms(2). */ -struct old_kernel_sym -{ - unsigned long value; - char name[60]; -}; - -struct old_module_ref -{ - unsigned long module; /* kernel addresses */ - unsigned long next; -}; - -struct old_module_symbol -{ - unsigned long addr; - unsigned long name; -}; - -struct old_symbol_table -{ - int size; /* total, including string table!!! */ - int n_symbols; - int n_refs; - struct old_module_symbol symbol[0]; /* actual size defined by n_symbols */ - struct old_module_ref ref[0]; /* actual size defined by n_refs */ -}; - -struct old_mod_routines -{ - unsigned long init; - unsigned long cleanup; -}; - -struct old_module -{ - unsigned long next; - unsigned long ref; /* the list of modules that refer to me */ - unsigned long symtab; - unsigned long name; - int size; /* size of module in pages */ - unsigned long addr; /* address of module */ - int state; - unsigned long cleanup; /* cleanup routine */ -}; - -/* Sent to init_module(2) or'ed into the code size parameter. */ -static const int OLD_MOD_AUTOCLEAN = 0x40000000; /* big enough, but no sign problems... */ - -int get_kernel_syms(struct old_kernel_sym *); -int old_sys_init_module(const char *name, char *code, unsigned codesize, - struct old_mod_routines *, struct old_symbol_table *); +#ident "$Id: insmod.c,v 1.126 2004/12/26 09:13:32 vapier Exp $" /*======================================================================*/ /* For sizeof() which are related to the module platform and not to the @@ -201,55 +368,68 @@ static const int tgt_sizeof_void_p = 8; /* Note: new_module_symbol does not use tgt_long intentionally */ struct new_module_symbol { - unsigned long value; - unsigned long name; + unsigned long value; + unsigned long name; }; struct new_module_persist; struct new_module_ref { - unsigned tgt_long dep; /* kernel addresses */ - unsigned tgt_long ref; - unsigned tgt_long next_ref; + unsigned tgt_long dep; /* kernel addresses */ + unsigned tgt_long ref; + unsigned tgt_long next_ref; }; struct new_module { - unsigned tgt_long size_of_struct; /* == sizeof(module) */ - unsigned tgt_long next; - unsigned tgt_long name; - unsigned tgt_long size; - - tgt_long usecount; - unsigned tgt_long flags; /* AUTOCLEAN et al */ - - unsigned nsyms; - unsigned ndeps; - - unsigned tgt_long syms; - unsigned tgt_long deps; - unsigned tgt_long refs; - unsigned tgt_long init; - unsigned tgt_long cleanup; - unsigned tgt_long ex_table_start; - unsigned tgt_long ex_table_end; + unsigned tgt_long size_of_struct; /* == sizeof(module) */ + unsigned tgt_long next; + unsigned tgt_long name; + unsigned tgt_long size; + + tgt_long usecount; + unsigned tgt_long flags; /* AUTOCLEAN et al */ + + unsigned nsyms; + unsigned ndeps; + + unsigned tgt_long syms; + unsigned tgt_long deps; + unsigned tgt_long refs; + unsigned tgt_long init; + unsigned tgt_long cleanup; + unsigned tgt_long ex_table_start; + unsigned tgt_long ex_table_end; #ifdef __alpha__ - unsigned tgt_long gp; + unsigned tgt_long gp; #endif - /* Everything after here is extension. */ - unsigned tgt_long persist_start; - unsigned tgt_long persist_end; - unsigned tgt_long can_unload; - unsigned tgt_long runsize; + /* Everything after here is extension. */ + unsigned tgt_long persist_start; + unsigned tgt_long persist_end; + unsigned tgt_long can_unload; + unsigned tgt_long runsize; + const char *kallsyms_start; /* All symbols for kernel debugging */ + const char *kallsyms_end; + const char *archdata_start; /* arch specific data for module */ + const char *archdata_end; + const char *kernel_data; /* Reserved for kernel internal use */ }; +#ifdef ARCHDATAM +#define ARCHDATA_SEC_NAME ARCHDATAM +#else +#define ARCHDATA_SEC_NAME "__archdata" +#endif +#define KALLSYMS_SEC_NAME "__kallsyms" + + struct new_module_info { - unsigned long addr; - unsigned long size; - unsigned long flags; - long usecount; + unsigned long addr; + unsigned long size; + unsigned long flags; + long usecount; }; /* Bits of module.flags. */ @@ -259,9 +439,9 @@ static const int NEW_MOD_AUTOCLEAN = 4; static const int NEW_MOD_VISITED = 8; static const int NEW_MOD_USED_ONCE = 16; -int new_sys_init_module(const char *name, const struct new_module *); -int query_module(const char *name, int which, void *buf, size_t bufsize, - size_t *ret); +int init_module(const char *name, const struct new_module *); +int query_module(const char *name, int which, void *buf, + size_t bufsize, size_t *ret); /* Values for query_module's which. */ @@ -315,69 +495,18 @@ int delete_module(const char *); #ifndef MODUTILS_OBJ_H static const int MODUTILS_OBJ_H = 1; -#ident "$Id: insmod.c,v 1.49 2001/02/20 20:47:08 andersen Exp $" +#ident "$Id: insmod.c,v 1.126 2004/12/26 09:13:32 vapier Exp $" /* The relocatable object is manipulated using elfin types. */ #include #include +#include - -/* Machine-specific elf macros for i386 et al. */ - -/* the SH changes have only been tested on the SH4 in =little endian= mode */ -/* I'm not sure about big endian, so let's warn: */ - -#if (defined(__SH4__) || defined(__SH3__)) && defined(__BIG_ENDIAN__) -#error insmod.c may require changes for use on big endian SH4/SH3 -#endif - -/* it may or may not work on the SH1/SH2... So let's error on those - also */ -#if (defined(__sh__) && (!(defined(__SH3__) || defined(__SH4__)))) -#error insmod.c may require changes for non-SH3/SH4 use -#endif - -#define ELFCLASSM ELFCLASS32 - -#if defined(__sh__) - -#define MATCH_MACHINE(x) (x == EM_SH) -#define SHT_RELM SHT_RELA -#define Elf32_RelM Elf32_Rela -#define ELFDATAM ELFDATA2LSB - -#elif defined(__arm__) - -#define MATCH_MACHINE(x) (x == EM_ARM) -#define SHT_RELM SHT_REL -#define Elf32_RelM Elf32_Rel -#define ELFDATAM ELFDATA2LSB - -#elif defined(__powerpc__) - -#define MATCH_MACHINE(x) (x == EM_PPC) -#define SHT_RELM SHT_RELA -#define Elf32_RelM Elf32_Rela -#define ELFDATAM ELFDATA2MSB - -#elif defined(__i386__) - -/* presumably we can use these for anything but the SH and ARM*/ -/* this is the previous behavior, but it does result in - insmod.c being broken on anything except i386 */ -#ifndef EM_486 -#define MATCH_MACHINE(x) (x == EM_386) -#else -#define MATCH_MACHINE(x) (x == EM_386 || x == EM_486) -#endif - -#define SHT_RELM SHT_REL -#define Elf32_RelM Elf32_Rel +#if __BYTE_ORDER == __LITTLE_ENDIAN #define ELFDATAM ELFDATA2LSB - -#else -#error Sorry, but insmod.c does not yet support this architecture... +#elif __BYTE_ORDER == __BIG_ENDIAN +#define ELFDATAM ELFDATA2MSB #endif #ifndef ElfW @@ -390,7 +519,7 @@ static const int MODUTILS_OBJ_H = 1; # endif #endif -/* For some reason this is missing from libc5. */ +/* For some reason this is missing from some ancient C libraries.... */ #ifndef ELF32_ST_INFO # define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) #endif @@ -399,28 +528,34 @@ static const int MODUTILS_OBJ_H = 1; # define ELF64_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) #endif +#define ELF_ST_BIND(info) ELFW(ST_BIND)(info) +#define ELF_ST_TYPE(info) ELFW(ST_TYPE)(info) +#define ELF_ST_INFO(bind, type) ELFW(ST_INFO)(bind, type) +#define ELF_R_TYPE(val) ELFW(R_TYPE)(val) +#define ELF_R_SYM(val) ELFW(R_SYM)(val) + struct obj_string_patch; struct obj_symbol_patch; struct obj_section { - ElfW(Shdr) header; - const char *name; - char *contents; - struct obj_section *load_next; - int idx; + ElfW(Shdr) header; + const char *name; + char *contents; + struct obj_section *load_next; + int idx; }; struct obj_symbol { - struct obj_symbol *next; /* hash table link */ - const char *name; - unsigned long value; - unsigned long size; - int secidx; /* the defining section index/module */ - int info; - int ksymidx; /* for export to the kernel symtab */ - int referenced; /* actually used in the link */ + struct obj_symbol *next; /* hash table link */ + const char *name; + unsigned long value; + unsigned long size; + int secidx; /* the defining section index/module */ + int info; + int ksymidx; /* for export to the kernel symtab */ + int referenced; /* actually used in the link */ }; /* Hardcode the hash table size. We shouldn't be needing so many @@ -431,119 +566,116 @@ struct obj_symbol struct obj_file { - ElfW(Ehdr) header; - ElfW(Addr) baseaddr; - struct obj_section **sections; - struct obj_section *load_order; - struct obj_section **load_order_search_start; - struct obj_string_patch *string_patches; - struct obj_symbol_patch *symbol_patches; - int (*symbol_cmp)(const char *, const char *); - unsigned long (*symbol_hash)(const char *); - unsigned long local_symtab_size; - struct obj_symbol **local_symtab; - struct obj_symbol *symtab[HASH_BUCKETS]; + ElfW(Ehdr) header; + ElfW(Addr) baseaddr; + struct obj_section **sections; + struct obj_section *load_order; + struct obj_section **load_order_search_start; + struct obj_string_patch *string_patches; + struct obj_symbol_patch *symbol_patches; + int (*symbol_cmp)(const char *, const char *); + unsigned long (*symbol_hash)(const char *); + unsigned long local_symtab_size; + struct obj_symbol **local_symtab; + struct obj_symbol *symtab[HASH_BUCKETS]; }; enum obj_reloc { - obj_reloc_ok, - obj_reloc_overflow, - obj_reloc_dangerous, - obj_reloc_unhandled + obj_reloc_ok, + obj_reloc_overflow, + obj_reloc_dangerous, + obj_reloc_unhandled }; struct obj_string_patch { - struct obj_string_patch *next; - int reloc_secidx; - ElfW(Addr) reloc_offset; - ElfW(Addr) string_offset; + struct obj_string_patch *next; + int reloc_secidx; + ElfW(Addr) reloc_offset; + ElfW(Addr) string_offset; }; struct obj_symbol_patch { - struct obj_symbol_patch *next; - int reloc_secidx; - ElfW(Addr) reloc_offset; - struct obj_symbol *sym; + struct obj_symbol_patch *next; + int reloc_secidx; + ElfW(Addr) reloc_offset; + struct obj_symbol *sym; }; /* Generic object manipulation routines. */ -unsigned long obj_elf_hash(const char *); +static unsigned long obj_elf_hash(const char *); -unsigned long obj_elf_hash_n(const char *, unsigned long len); +static unsigned long obj_elf_hash_n(const char *, unsigned long len); -struct obj_symbol *obj_add_symbol (struct obj_file *f, const char *name, - unsigned long symidx, int info, int secidx, - ElfW(Addr) value, unsigned long size); - -struct obj_symbol *obj_find_symbol (struct obj_file *f, +static struct obj_symbol *obj_find_symbol (struct obj_file *f, const char *name); -ElfW(Addr) obj_symbol_final_value(struct obj_file *f, +static ElfW(Addr) obj_symbol_final_value(struct obj_file *f, struct obj_symbol *sym); -void obj_set_symbol_compare(struct obj_file *f, +#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING +static void obj_set_symbol_compare(struct obj_file *f, int (*cmp)(const char *, const char *), unsigned long (*hash)(const char *)); +#endif -struct obj_section *obj_find_section (struct obj_file *f, +static struct obj_section *obj_find_section (struct obj_file *f, const char *name); -void obj_insert_section_load_order (struct obj_file *f, +static void obj_insert_section_load_order (struct obj_file *f, struct obj_section *sec); -struct obj_section *obj_create_alloced_section (struct obj_file *f, +static struct obj_section *obj_create_alloced_section (struct obj_file *f, const char *name, unsigned long align, unsigned long size); -struct obj_section *obj_create_alloced_section_first (struct obj_file *f, +static struct obj_section *obj_create_alloced_section_first (struct obj_file *f, const char *name, unsigned long align, unsigned long size); -void *obj_extend_section (struct obj_section *sec, unsigned long more); +static void *obj_extend_section (struct obj_section *sec, unsigned long more); -int obj_string_patch(struct obj_file *f, int secidx, ElfW(Addr) offset, +static int obj_string_patch(struct obj_file *f, int secidx, ElfW(Addr) offset, const char *string); -int obj_symbol_patch(struct obj_file *f, int secidx, ElfW(Addr) offset, +static int obj_symbol_patch(struct obj_file *f, int secidx, ElfW(Addr) offset, struct obj_symbol *sym); -int obj_check_undefineds(struct obj_file *f); +static int obj_check_undefineds(struct obj_file *f); -void obj_allocate_commons(struct obj_file *f); +static void obj_allocate_commons(struct obj_file *f); -unsigned long obj_load_size (struct obj_file *f); +static unsigned long obj_load_size (struct obj_file *f); -int obj_relocate (struct obj_file *f, ElfW(Addr) base); +static int obj_relocate (struct obj_file *f, ElfW(Addr) base); -struct obj_file *obj_load(FILE *f); +static struct obj_file *obj_load(FILE *f, int loadprogbits); -int obj_create_image (struct obj_file *f, char *image); +static int obj_create_image (struct obj_file *f, char *image); /* Architecture specific manipulation routines. */ -struct obj_file *arch_new_file (void); +static struct obj_file *arch_new_file (void); -struct obj_section *arch_new_section (void); +static struct obj_section *arch_new_section (void); -struct obj_symbol *arch_new_symbol (void); +static struct obj_symbol *arch_new_symbol (void); -enum obj_reloc arch_apply_relocation (struct obj_file *f, +static enum obj_reloc arch_apply_relocation (struct obj_file *f, struct obj_section *targsec, struct obj_section *symsec, struct obj_symbol *sym, ElfW(RelM) *rel, ElfW(Addr) value); -int arch_create_got (struct obj_file *f); +static void arch_create_got (struct obj_file *f); -struct new_module; -int arch_init_module (struct obj_file *f, struct new_module *); +static int obj_gpl_license(struct obj_file *f, const char **license); #endif /* obj.h */ //---------------------------------------------------------------------------- @@ -551,7 +683,12 @@ int arch_init_module (struct obj_file *f, struct new_module *); //---------------------------------------------------------------------------- - +/* SPFX is always a string, so it can be concatenated to string constants. */ +#ifdef SYMBOL_PREFIX +#define SPFX SYMBOL_PREFIX +#else +#define SPFX "" +#endif #define _PATH_MODULES "/lib/modules" @@ -559,58 +696,71 @@ static const int STRVERSIONLEN = 32; /*======================================================================*/ -int flag_force_load = 0; -int flag_autoclean = 0; -int flag_verbose = 0; -int flag_export = 1; +static int flag_force_load = 0; +static int flag_autoclean = 0; +static int flag_verbose = 0; +static int flag_quiet = 0; +static int flag_export = 1; /*======================================================================*/ -/* previously, these were named i386_* but since we could be - compiling for the sh, I've renamed them to the more general - arch_* These structures are the same between the x86 and SH, - and we can't support anything else right now anyway. In the - future maybe they should be #if defined'd */ +#if defined(CONFIG_USE_LIST) -/* Done ;-) */ +struct arch_list_entry +{ + struct arch_list_entry *next; + CONFIG_LIST_ARCHTYPE addend; + int offset; + int inited : 1; +}; +#endif +#if defined(CONFIG_USE_SINGLE) -#if defined(BB_USE_PLT_ENTRIES) -struct arch_plt_entry +struct arch_single_entry { - int offset; - int allocated:1; - int inited:1; /* has been set up */ + int offset; + int inited : 1; + int allocated : 1; }; + #endif -#if defined(BB_USE_GOT_ENTRIES) -struct arch_got_entry { - int offset; - unsigned offset_done:1; - unsigned reloc_done:1; +#if defined(__mips__) +struct mips_hi16 +{ + struct mips_hi16 *next; + ElfW(Addr) *addr; + ElfW(Addr) value; }; #endif struct arch_file { struct obj_file root; -#if defined(BB_USE_PLT_ENTRIES) +#if defined(CONFIG_USE_PLT_ENTRIES) struct obj_section *plt; #endif -#if defined(BB_USE_GOT_ENTRIES) +#if defined(CONFIG_USE_GOT_ENTRIES) struct obj_section *got; #endif +#if defined(__mips__) + struct mips_hi16 *mips_hi16_list; +#endif }; struct arch_symbol { struct obj_symbol root; -#if defined(BB_USE_PLT_ENTRIES) - struct arch_plt_entry pltent; +#if defined(CONFIG_USE_PLT_ENTRIES) +#if defined(CONFIG_USE_PLT_LIST) + struct arch_list_entry *pltent; +#else + struct arch_single_entry pltent; +#endif #endif -#if defined(BB_USE_GOT_ENTRIES) - struct arch_got_entry gotent; +#if defined(CONFIG_USE_GOT_ENTRIES) + struct arch_single_entry gotent; #endif }; @@ -623,89 +773,39 @@ struct external_module { struct new_module_symbol *syms; }; -struct new_module_symbol *ksyms; -size_t nksyms; - -struct external_module *ext_modules; -int n_ext_modules; -int n_ext_modules_used; - +static struct new_module_symbol *ksyms; +static size_t nksyms; - -/* Some firendly syscalls to cheer everyone's day... */ -#define __NR_new_sys_init_module __NR_init_module -_syscall2(int, new_sys_init_module, const char *, name, - const struct new_module *, info) -#define __NR_old_sys_init_module __NR_init_module -_syscall5(int, old_sys_init_module, const char *, name, char *, code, - unsigned, codesize, struct old_mod_routines *, routines, - struct old_symbol_table *, symtab) -#ifndef BB_RMMOD -_syscall1(int, delete_module, const char *, name) -#else +static struct external_module *ext_modules; +static int n_ext_modules; +static int n_ext_modules_used; extern int delete_module(const char *); -#endif - -/* This is kind of troublesome. See, we don't actually support - the m68k or the arm the same way we support i386 and (now) - sh. In doing my SH patch, I just assumed that whatever works - for i386 also works for m68k and arm since currently insmod.c - does nothing special for them. If this isn't true, the below - line is rather misleading IMHO, and someone should either - change it or add more proper architecture-dependent support - for these boys. - -- Bryan Rittmeyer */ +static char *m_filename; +static char *m_fullName; -#ifdef BB_FEATURE_OLD_MODULE_INTERFACE -_syscall1(int, get_kernel_syms, struct old_kernel_sym *, ks) -#endif - -#if defined(__i386__) || defined(__m68k__) || defined(__arm__) \ - || defined(__powerpc__) -/* Jump through hoops to fixup error return codes */ -#define __NR__create_module __NR_create_module -static inline _syscall2(long, _create_module, const char *, name, size_t, - size) -unsigned long create_module(const char *name, size_t size) -{ - long ret = _create_module(name, size); - if (ret == -1 && errno > 125) { - ret = -errno; - errno = 0; - } - return ret; -} -#else -_syscall2(unsigned long, create_module, const char *, name, size_t, size) -#endif -static char m_filename[BUFSIZ + 1] = "\0"; -static char m_fullName[BUFSIZ + 1] = "\0"; /*======================================================================*/ -static int findNamedModule(const char *fileName, struct stat *statbuf, - void *userDate) +static int check_module_name_match(const char *filename, struct stat *statbuf, + void *userdata) { - char *fullName = (char *) userDate; + char *fullname = (char *) userdata; - - if (fullName[0] == '\0') + if (fullname[0] == '\0') return (FALSE); else { - char *tmp = strrchr((char *) fileName, '/'); - - if (tmp == NULL) - tmp = (char *) fileName; - else - tmp++; - if (check_wildcard_match(tmp, fullName) == TRUE) { + char *tmp, *tmp1 = bb_xstrdup(filename); + tmp = bb_get_last_path_component(tmp1); + if (strcmp(tmp, fullname) == 0) { + free(tmp1); /* Stop searching if we find a match */ - memcpy(m_filename, fileName, strlen(fileName)+1); + m_filename = bb_xstrdup(filename); return (FALSE); } + free(tmp1); } return (TRUE); } @@ -713,279 +813,793 @@ static int findNamedModule(const char *fileName, struct stat *statbuf, /*======================================================================*/ -struct obj_file *arch_new_file(void) +static struct obj_file *arch_new_file(void) { struct arch_file *f; f = xmalloc(sizeof(*f)); -#if defined(BB_USE_PLT_ENTRIES) - f->plt = NULL; -#endif -#if defined(BB_USE_GOT_ENTRIES) - f->got = NULL; -#endif + memset(f, 0, sizeof(*f)); return &f->root; } -struct obj_section *arch_new_section(void) +static struct obj_section *arch_new_section(void) { return xmalloc(sizeof(struct obj_section)); } -struct obj_symbol *arch_new_symbol(void) +static struct obj_symbol *arch_new_symbol(void) { struct arch_symbol *sym; sym = xmalloc(sizeof(*sym)); -#if defined(BB_USE_PLT_ENTRIES) - memset(&sym->pltent, 0, sizeof(sym->pltent)); -#endif -#if defined(BB_USE_GOT_ENTRIES) - memset(&sym->gotent, 0, sizeof(sym->gotent)); -#endif + memset(sym, 0, sizeof(*sym)); return &sym->root; } -enum obj_reloc +static enum obj_reloc arch_apply_relocation(struct obj_file *f, struct obj_section *targsec, struct obj_section *symsec, struct obj_symbol *sym, - ElfW(RelM) *rel, ElfW(Addr) v) + ElfW(RelM) *rel, ElfW(Addr) v) { struct arch_file *ifile = (struct arch_file *) f; - struct arch_symbol *isym = (struct arch_symbol *) sym; - + enum obj_reloc ret = obj_reloc_ok; ElfW(Addr) *loc = (ElfW(Addr) *) (targsec->contents + rel->r_offset); ElfW(Addr) dot = targsec->header.sh_addr + rel->r_offset; -#if defined(BB_USE_GOT_ENTRIES) +#if defined(CONFIG_USE_GOT_ENTRIES) || defined(CONFIG_USE_PLT_ENTRIES) + struct arch_symbol *isym = (struct arch_symbol *) sym; +#endif +#if defined(CONFIG_USE_GOT_ENTRIES) ElfW(Addr) got = ifile->got ? ifile->got->header.sh_addr : 0; #endif -#if defined(BB_USE_PLT_ENTRIES) +#if defined(CONFIG_USE_PLT_ENTRIES) ElfW(Addr) plt = ifile->plt ? ifile->plt->header.sh_addr : 0; - struct arch_plt_entry *pe; unsigned long *ip; +# if defined(CONFIG_USE_PLT_LIST) + struct arch_list_entry *pe; +# else + struct arch_single_entry *pe; +# endif #endif - enum obj_reloc ret = obj_reloc_ok; - - switch (ELF32_R_TYPE(rel->r_info)) { -/* even though these constants seem to be the same for - the i386 and the sh, we "#if define" them for clarity - and in case that ever changes */ -#if defined(__sh__) - case R_SH_NONE: -#elif defined(__arm__) - case R_ARM_NONE: -#elif defined(__i386__) - case R_386_NONE: -#elif defined(__powerpc__) - case R_PPC_NONE: -#endif - break; + switch (ELF_R_TYPE(rel->r_info)) { -#if defined(__sh__) - case R_SH_DIR32: -#elif defined(__arm__) - case R_ARM_ABS32: -#elif defined(__i386__) - case R_386_32: -#elif defined(__powerpc__) - case R_PPC_ADDR32: -#endif - *loc += v; - break; +#if defined(__arm__) -#if defined(__powerpc__) - case R_PPC_ADDR16_HA: - *(unsigned short *)loc = (v + 0x8000) >> 16; - break; + case R_ARM_NONE: + break; - case R_PPC_ADDR16_HI: - *(unsigned short *)loc = v >> 16; - break; + case R_ARM_ABS32: + *loc += v; + break; - case R_PPC_ADDR16_LO: - *(unsigned short *)loc = v; - break; -#endif + case R_ARM_GOT32: + goto bb_use_got; -#if defined(__arm__) -#elif defined(__sh__) - case R_SH_REL32: - *loc += v - dot; - break; -#elif defined(__i386__) - case R_386_PLT32: - case R_386_PC32: - *loc += v - dot; - break; -#elif defined(__powerpc__) - case R_PPC_REL32: - *loc = v - dot; - break; -#endif + case R_ARM_GOTPC: + /* relative reloc, always to _GLOBAL_OFFSET_TABLE_ + * (which is .got) similar to branch, + * but is full 32 bits relative */ -#if defined(__sh__) - case R_SH_PLT32: - *loc = v - dot; - break; -#elif defined(__i386__) -#endif + assert(got); + *loc += got - dot; + break; -#if defined(BB_USE_PLT_ENTRIES) + case R_ARM_PC24: + case R_ARM_PLT32: + goto bb_use_plt; -#if defined(__arm__) - case R_ARM_PC24: - case R_ARM_PLT32: -#endif -#if defined(__powerpc__) - case R_PPC_REL24: -#endif - /* find the plt entry and initialize it if necessary */ - assert(isym != NULL); + case R_ARM_GOTOFF: /* address relative to the got */ + assert(got); + *loc += v - got; + break; - pe = (struct arch_plt_entry*) &isym->pltent; +#elif defined(__cris__) - if (! pe->inited) { - ip = (unsigned long *) (ifile->plt->contents + pe->offset); + case R_CRIS_NONE: + break; - /* generate some machine code */ + case R_CRIS_32: + /* CRIS keeps the relocation value in the r_addend field and + * should not use whats in *loc at all + */ + *loc = v; + break; -#if defined(__arm__) - ip[0] = 0xe51ff004; /* ldr pc,[pc,#-4] */ - ip[1] = v; /* sym@ */ -#endif -#if defined(__powerpc__) - ip[0] = 0x3d600000 + ((v + 0x8000) >> 16); /* lis r11,sym@ha */ - ip[1] = 0x396b0000 + (v & 0xffff); /* addi r11,r11,sym@l */ - ip[2] = 0x7d6903a6; /* mtctr r11 */ - ip[3] = 0x4e800420; /* bctr */ -#endif - pe->inited = 1; - } - - /* relative distance to target */ - v -= dot; - /* if the target is too far away.... */ - if ((int)v < -0x02000000 || (int)v >= 0x02000000) { - /* go via the plt */ - v = plt + pe->offset - dot; - } - if (v & 3) - ret = obj_reloc_dangerous; - - /* merge the offset into the instruction. */ -#if defined(__arm__) - /* Convert to words. */ - v >>= 2; +#elif defined(__H8300H__) || defined(__H8300S__) - *loc = (*loc & ~0x00ffffff) | ((v + *loc) & 0x00ffffff); -#endif -#if defined(__powerpc__) - *loc = (*loc & ~0x03fffffc) | (v & 0x03fffffc); -#endif - break; -#endif /* BB_USE_PLT_ENTRIES */ + case R_H8_DIR24R8: + loc = (ElfW(Addr) *)((ElfW(Addr))loc - 1); + *loc = (*loc & 0xff000000) | ((*loc & 0xffffff) + v); + break; + case R_H8_DIR24A8: + *loc += v; + break; + case R_H8_DIR32: + case R_H8_DIR32A16: + *loc += v; + break; + case R_H8_PCREL16: + v -= dot + 2; + if ((ElfW(Sword))v > 0x7fff || + (ElfW(Sword))v < -(ElfW(Sword))0x8000) + ret = obj_reloc_overflow; + else + *(unsigned short *)loc = v; + break; + case R_H8_PCREL8: + v -= dot + 1; + if ((ElfW(Sword))v > 0x7f || + (ElfW(Sword))v < -(ElfW(Sword))0x80) + ret = obj_reloc_overflow; + else + *(unsigned char *)loc = v; + break; -#if defined(__arm__) -#elif defined(__sh__) - case R_SH_GLOB_DAT: - case R_SH_JMP_SLOT: - *loc = v; - break; #elif defined(__i386__) - case R_386_GLOB_DAT: - case R_386_JMP_SLOT: - *loc = v; - break; + + case R_386_NONE: + break; + + case R_386_32: + *loc += v; + break; + + case R_386_PLT32: + case R_386_PC32: + *loc += v - dot; + break; + + case R_386_GLOB_DAT: + case R_386_JMP_SLOT: + *loc = v; + break; + + case R_386_RELATIVE: + *loc += f->baseaddr; + break; + + case R_386_GOTPC: + assert(got != 0); + *loc += got - dot; + break; + + case R_386_GOT32: + goto bb_use_got; + + case R_386_GOTOFF: + assert(got != 0); + *loc += v - got; + break; + +#elif defined(__mc68000__) + + case R_68K_NONE: + break; + + case R_68K_32: + *loc += v; + break; + + case R_68K_8: + if (v > 0xff) { + ret = obj_reloc_overflow; + } + *(char *)loc = v; + break; + + case R_68K_16: + if (v > 0xffff) { + ret = obj_reloc_overflow; + } + *(short *)loc = v; + break; + + case R_68K_PC8: + v -= dot; + if ((ElfW(Sword))v > 0x7f || + (ElfW(Sword))v < -(ElfW(Sword))0x80) { + ret = obj_reloc_overflow; + } + *(char *)loc = v; + break; + + case R_68K_PC16: + v -= dot; + if ((ElfW(Sword))v > 0x7fff || + (ElfW(Sword))v < -(ElfW(Sword))0x8000) { + ret = obj_reloc_overflow; + } + *(short *)loc = v; + break; + + case R_68K_PC32: + *(int *)loc = v - dot; + break; + + case R_68K_GLOB_DAT: + case R_68K_JMP_SLOT: + *loc = v; + break; + + case R_68K_RELATIVE: + *(int *)loc += f->baseaddr; + break; + + case R_68K_GOT32: + goto bb_use_got; + +# ifdef R_68K_GOTOFF + case R_68K_GOTOFF: + assert(got != 0); + *loc += v - got; + break; +# endif + +#elif defined(__mips__) + + case R_MIPS_NONE: + break; + + case R_MIPS_32: + *loc += v; + break; + + case R_MIPS_26: + if (v % 4) + ret = obj_reloc_dangerous; + if ((v & 0xf0000000) != ((dot + 4) & 0xf0000000)) + ret = obj_reloc_overflow; + *loc = + (*loc & ~0x03ffffff) | ((*loc + (v >> 2)) & + 0x03ffffff); + break; + + case R_MIPS_HI16: + { + struct mips_hi16 *n; + + /* We cannot relocate this one now because we don't know the value + of the carry we need to add. Save the information, and let LO16 + do the actual relocation. */ + n = (struct mips_hi16 *) xmalloc(sizeof *n); + n->addr = loc; + n->value = v; + n->next = ifile->mips_hi16_list; + ifile->mips_hi16_list = n; + break; + } + + case R_MIPS_LO16: + { + unsigned long insnlo = *loc; + ElfW(Addr) val, vallo; + + /* Sign extend the addend we extract from the lo insn. */ + vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000; + + if (ifile->mips_hi16_list != NULL) { + struct mips_hi16 *l; + + l = ifile->mips_hi16_list; + while (l != NULL) { + struct mips_hi16 *next; + unsigned long insn; + + /* The value for the HI16 had best be the same. */ + assert(v == l->value); + + /* Do the HI16 relocation. Note that we actually don't + need to know anything about the LO16 itself, except where + to find the low 16 bits of the addend needed by the LO16. */ + insn = *l->addr; + val = + ((insn & 0xffff) << 16) + + vallo; + val += v; + + /* Account for the sign extension that will happen in the + low bits. */ + val = + ((val >> 16) + + ((val & 0x8000) != + 0)) & 0xffff; + + insn = (insn & ~0xffff) | val; + *l->addr = insn; + + next = l->next; + free(l); + l = next; + } + + ifile->mips_hi16_list = NULL; + } + + /* Ok, we're done with the HI16 relocs. Now deal with the LO16. */ + val = v + vallo; + insnlo = (insnlo & ~0xffff) | (val & 0xffff); + *loc = insnlo; + break; + } + +#elif defined(__powerpc__) + + case R_PPC_ADDR16_HA: + *(unsigned short *)loc = (v + 0x8000) >> 16; + break; + + case R_PPC_ADDR16_HI: + *(unsigned short *)loc = v >> 16; + break; + + case R_PPC_ADDR16_LO: + *(unsigned short *)loc = v; + break; + + case R_PPC_REL24: + goto bb_use_plt; + + case R_PPC_REL32: + *loc = v - dot; + break; + + case R_PPC_ADDR32: + *loc = v; + break; + +#elif defined(__s390__) + + case R_390_32: + *(unsigned int *) loc += v; + break; + case R_390_16: + *(unsigned short *) loc += v; + break; + case R_390_8: + *(unsigned char *) loc += v; + break; + + case R_390_PC32: + *(unsigned int *) loc += v - dot; + break; + case R_390_PC16DBL: + *(unsigned short *) loc += (v - dot) >> 1; + break; + case R_390_PC16: + *(unsigned short *) loc += v - dot; + break; + + case R_390_PLT32: + case R_390_PLT16DBL: + /* find the plt entry and initialize it. */ + assert(isym != NULL); + pe = (struct arch_single_entry *) &isym->pltent; + assert(pe->allocated); + if (pe->inited == 0) { + ip = (unsigned long *)(ifile->plt->contents + pe->offset); + ip[0] = 0x0d105810; /* basr 1,0; lg 1,10(1); br 1 */ + ip[1] = 0x100607f1; + if (ELF_R_TYPE(rel->r_info) == R_390_PLT16DBL) + ip[2] = v - 2; + else + ip[2] = v; + pe->inited = 1; + } + + /* Insert relative distance to target. */ + v = plt + pe->offset - dot; + if (ELF_R_TYPE(rel->r_info) == R_390_PLT32) + *(unsigned int *) loc = (unsigned int) v; + else if (ELF_R_TYPE(rel->r_info) == R_390_PLT16DBL) + *(unsigned short *) loc = (unsigned short) ((v + 2) >> 1); + break; + + case R_390_GLOB_DAT: + case R_390_JMP_SLOT: + *loc = v; + break; + + case R_390_RELATIVE: + *loc += f->baseaddr; + break; + + case R_390_GOTPC: + assert(got != 0); + *(unsigned long *) loc += got - dot; + break; + + case R_390_GOT12: + case R_390_GOT16: + case R_390_GOT32: + assert(isym != NULL); + assert(got != 0); + if (!isym->gotent.inited) + { + isym->gotent.inited = 1; + *(ElfW(Addr) *)(ifile->got->contents + isym->gotent.offset) = v; + } + if (ELF_R_TYPE(rel->r_info) == R_390_GOT12) + *(unsigned short *) loc |= (*(unsigned short *) loc + isym->gotent.offset) & 0xfff; + else if (ELF_R_TYPE(rel->r_info) == R_390_GOT16) + *(unsigned short *) loc += isym->gotent.offset; + else if (ELF_R_TYPE(rel->r_info) == R_390_GOT32) + *(unsigned int *) loc += isym->gotent.offset; + break; + +# ifndef R_390_GOTOFF32 +# define R_390_GOTOFF32 R_390_GOTOFF +# endif + case R_390_GOTOFF32: + assert(got != 0); + *loc += v - got; + break; + +#elif defined(__sh__) + + case R_SH_NONE: + break; + + case R_SH_DIR32: + *loc += v; + break; + + case R_SH_REL32: + *loc += v - dot; + break; + + case R_SH_PLT32: + *loc = v - dot; + break; + + case R_SH_GLOB_DAT: + case R_SH_JMP_SLOT: + *loc = v; + break; + + case R_SH_RELATIVE: + *loc = f->baseaddr + rel->r_addend; + break; + + case R_SH_GOTPC: + assert(got != 0); + *loc = got - dot + rel->r_addend; + break; + + case R_SH_GOT32: + goto bb_use_got; + + case R_SH_GOTOFF: + assert(got != 0); + *loc = v - got; + break; + +# if defined(__SH5__) + case R_SH_IMM_MEDLOW16: + case R_SH_IMM_LOW16: + { + ElfW(Addr) word; + + if (ELF_R_TYPE(rel->r_info) == R_SH_IMM_MEDLOW16) + v >>= 16; + + /* + * movi and shori have the format: + * + * | op | imm | reg | reserved | + * 31..26 25..10 9.. 4 3 .. 0 + * + * so we simply mask and or in imm. + */ + word = *loc & ~0x3fffc00; + word |= (v & 0xffff) << 10; + + *loc = word; + + break; + } + + case R_SH_IMM_MEDLOW16_PCREL: + case R_SH_IMM_LOW16_PCREL: + { + ElfW(Addr) word; + + word = *loc & ~0x3fffc00; + + v -= dot; + + if (ELF_R_TYPE(rel->r_info) == R_SH_IMM_MEDLOW16_PCREL) + v >>= 16; + + word |= (v & 0xffff) << 10; + + *loc = word; + + break; + } +# endif /* __SH5__ */ + +#elif defined (__v850e__) + + case R_V850_NONE: + break; + + case R_V850_32: + /* We write two shorts instead of a long because even + 32-bit insns only need half-word alignment, but + 32-bit data needs to be long-word aligned. */ + v += ((unsigned short *)loc)[0]; + v += ((unsigned short *)loc)[1] << 16; + ((unsigned short *)loc)[0] = v & 0xffff; + ((unsigned short *)loc)[1] = (v >> 16) & 0xffff; + break; + + case R_V850_22_PCREL: + goto bb_use_plt; + +#elif defined(__x86_64__) + + case R_X86_64_NONE: + break; + + case R_X86_64_64: + *loc += v; + break; + + case R_X86_64_32: + *(unsigned int *) loc += v; + if (v > 0xffffffff) + { + ret = obj_reloc_overflow; /* Kernel module compiled without -mcmodel=kernel. */ + /* error("Possibly is module compiled without -mcmodel=kernel!"); */ + } + break; + + case R_X86_64_32S: + *(signed int *) loc += v; + break; + + case R_X86_64_16: + *(unsigned short *) loc += v; + break; + + case R_X86_64_8: + *(unsigned char *) loc += v; + break; + + case R_X86_64_PC32: + *(unsigned int *) loc += v - dot; + break; + + case R_X86_64_PC16: + *(unsigned short *) loc += v - dot; + break; + + case R_X86_64_PC8: + *(unsigned char *) loc += v - dot; + break; + + case R_X86_64_GLOB_DAT: + case R_X86_64_JUMP_SLOT: + *loc = v; + break; + + case R_X86_64_RELATIVE: + *loc += f->baseaddr; + break; + + case R_X86_64_GOT32: + case R_X86_64_GOTPCREL: + goto bb_use_got; +# if 0 + assert(isym != NULL); + if (!isym->gotent.reloc_done) + { + isym->gotent.reloc_done = 1; + *(Elf64_Addr *)(ifile->got->contents + isym->gotent.offset) = v; + } + /* XXX are these really correct? */ + if (ELF64_R_TYPE(rel->r_info) == R_X86_64_GOTPCREL) + *(unsigned int *) loc += v + isym->gotent.offset; + else + *loc += isym->gotent.offset; + break; +# endif + #endif + default: + printf("Warning: unhandled reloc %d\n",(int)ELF_R_TYPE(rel->r_info)); + ret = obj_reloc_unhandled; + break; + +#if defined(CONFIG_USE_PLT_ENTRIES) + +bb_use_plt: + + /* find the plt entry and initialize it if necessary */ + assert(isym != NULL); + +#if defined(CONFIG_USE_PLT_LIST) + for (pe = isym->pltent; pe != NULL && pe->addend != rel->r_addend;) + pe = pe->next; + assert(pe != NULL); +#else + pe = &isym->pltent; +#endif + + if (! pe->inited) { + ip = (unsigned long *) (ifile->plt->contents + pe->offset); + + /* generate some machine code */ + #if defined(__arm__) -#elif defined(__sh__) - case R_SH_RELATIVE: - *loc += f->baseaddr + rel->r_addend; - break; -#elif defined(__i386__) - case R_386_RELATIVE: - *loc += f->baseaddr; - break; + ip[0] = 0xe51ff004; /* ldr pc,[pc,#-4] */ + ip[1] = v; /* sym@ */ +#endif +#if defined(__powerpc__) + ip[0] = 0x3d600000 + ((v + 0x8000) >> 16); /* lis r11,sym@ha */ + ip[1] = 0x396b0000 + (v & 0xffff); /* addi r11,r11,sym@l */ + ip[2] = 0x7d6903a6; /* mtctr r11 */ + ip[3] = 0x4e800420; /* bctr */ #endif +#if defined (__v850e__) + /* We have to trash a register, so we assume that any control + transfer more than 21-bits away must be a function call + (so we can use a call-clobbered register). */ + ip[0] = 0x0621 + ((v & 0xffff) << 16); /* mov sym, r1 ... */ + ip[1] = ((v >> 16) & 0xffff) + 0x610000; /* ...; jmp r1 */ +#endif + pe->inited = 1; + } -#if defined(BB_USE_GOT_ENTRIES) + /* relative distance to target */ + v -= dot; + /* if the target is too far away.... */ +#if defined (__arm__) || defined (__powerpc__) + if ((int)v < -0x02000000 || (int)v >= 0x02000000) +#elif defined (__v850e__) + if ((ElfW(Sword))v > 0x1fffff || (ElfW(Sword))v < (ElfW(Sword))-0x200000) +#endif + /* go via the plt */ + v = plt + pe->offset - dot; -#if defined(__sh__) - case R_SH_GOTPC: -#elif defined(__arm__) - case R_ARM_GOTPC: -#elif defined(__i386__) - case R_386_GOTPC: +#if defined (__v850e__) + if (v & 1) +#else + if (v & 3) #endif - assert(got != 0); -#if defined(__sh__) - *loc += got - dot + rel->r_addend;; -#elif defined(__i386__) || defined(__arm__) - *loc += got - dot; + ret = obj_reloc_dangerous; + + /* merge the offset into the instruction. */ +#if defined(__arm__) + /* Convert to words. */ + v >>= 2; + + *loc = (*loc & ~0x00ffffff) | ((v + *loc) & 0x00ffffff); #endif - break; +#if defined(__powerpc__) + *loc = (*loc & ~0x03fffffc) | (v & 0x03fffffc); +#endif +#if defined (__v850e__) + /* We write two shorts instead of a long because even 32-bit insns + only need half-word alignment, but the 32-bit data write needs + to be long-word aligned. */ + ((unsigned short *)loc)[0] = + (*(unsigned short *)loc & 0xffc0) /* opcode + reg */ + | ((v >> 16) & 0x3f); /* offs high part */ + ((unsigned short *)loc)[1] = + (v & 0xffff); /* offs low part */ +#endif + break; +#endif /* CONFIG_USE_PLT_ENTRIES */ +#if defined(CONFIG_USE_GOT_ENTRIES) +bb_use_got: + + assert(isym != NULL); + /* needs an entry in the .got: set it, once */ + if (!isym->gotent.inited) { + isym->gotent.inited = 1; + *(ElfW(Addr) *) (ifile->got->contents + isym->gotent.offset) = v; + } + /* make the reloc with_respect_to_.got */ #if defined(__sh__) - case R_SH_GOT32: -#elif defined(__arm__) - case R_ARM_GOT32: -#elif defined(__i386__) - case R_386_GOT32: + *loc += isym->gotent.offset + rel->r_addend; +#elif defined(__i386__) || defined(__arm__) || defined(__mc68000__) + *loc += isym->gotent.offset; #endif - assert(isym != NULL); - /* needs an entry in the .got: set it, once */ - if (!isym->gotent.reloc_done) { - isym->gotent.reloc_done = 1; - *(ElfW(Addr) *) (ifile->got->contents + isym->gotent.offset) = v; + break; + +#endif /* CONFIG_USE_GOT_ENTRIES */ + } + + return ret; +} + + +#if defined(CONFIG_USE_LIST) + +static int arch_list_add(ElfW(RelM) *rel, struct arch_list_entry **list, + int offset, int size) +{ + struct arch_list_entry *pe; + + for (pe = *list; pe != NULL; pe = pe->next) { + if (pe->addend == rel->r_addend) { + break; } - /* make the reloc with_respect_to_.got */ -#if defined(__sh__) - *loc += isym->gotent.offset + rel->r_addend; -#elif defined(__i386__) || defined(__arm__) - *loc += isym->gotent.offset; + } + + if (pe == NULL) { + pe = xmalloc(sizeof(struct arch_list_entry)); + pe->next = *list; + pe->addend = rel->r_addend; + pe->offset = offset; + pe->inited = 0; + *list = pe; + return size; + } + return 0; +} + #endif - break; - /* address relative to the got */ -#if defined(__sh__) - case R_SH_GOTOFF: -#elif defined(__arm__) - case R_ARM_GOTOFF: -#elif defined(__i386__) - case R_386_GOTOFF: +#if defined(CONFIG_USE_SINGLE) + +static int arch_single_init(ElfW(RelM) *rel, struct arch_single_entry *single, + int offset, int size) +{ + if (single->allocated == 0) { + single->allocated = 1; + single->offset = offset; + single->inited = 0; + return size; + } + return 0; +} + #endif - assert(got != 0); - *loc += v - got; - break; -#endif /* BB_USE_GOT_ENTRIES */ +#if defined(CONFIG_USE_GOT_ENTRIES) || defined(CONFIG_USE_PLT_ENTRIES) + +static struct obj_section *arch_xsect_init(struct obj_file *f, char *name, + int offset, int size) +{ + struct obj_section *myrelsec = obj_find_section(f, name); + + if (offset == 0) { + offset += size; + } - default: - printf("Warning: unhandled reloc %d\n",(int)ELF32_R_TYPE(rel->r_info)); - ret = obj_reloc_unhandled; - break; + if (myrelsec) { + obj_extend_section(myrelsec, offset); + } else { + myrelsec = obj_create_alloced_section(f, name, + size, offset); + assert(myrelsec); } - return ret; + return myrelsec; } -int arch_create_got(struct obj_file *f) +#endif + +static void arch_create_got(struct obj_file *f) { +#if defined(CONFIG_USE_GOT_ENTRIES) || defined(CONFIG_USE_PLT_ENTRIES) struct arch_file *ifile = (struct arch_file *) f; int i; -#if defined(BB_USE_GOT_ENTRIES) - int got_offset = 0, gotneeded = 0; +#if defined(CONFIG_USE_GOT_ENTRIES) + int got_offset = 0, got_needed = 0, got_allocate; #endif -#if defined(BB_USE_PLT_ENTRIES) - int plt_offset = 0, pltneeded = 0; +#if defined(CONFIG_USE_PLT_ENTRIES) + int plt_offset = 0, plt_needed = 0, plt_allocate; #endif - struct obj_section *relsec, *symsec, *strsec; + struct obj_section *relsec, *symsec, *strsec; ElfW(RelM) *rel, *relend; ElfW(Sym) *symtab, *extsym; const char *strtab, *name; @@ -1005,49 +1619,75 @@ int arch_create_got(struct obj_file *f) strtab = (const char *) strsec->contents; for (; rel < relend; ++rel) { - extsym = &symtab[ELF32_R_SYM(rel->r_info)]; + extsym = &symtab[ELF_R_SYM(rel->r_info)]; - switch (ELF32_R_TYPE(rel->r_info)) { -#if defined(__arm__) - case R_ARM_GOT32: - break; -#elif defined(__sh__) - case R_SH_GOT32: - break; -#elif defined(__i386__) - case R_386_GOT32: - break; +#if defined(CONFIG_USE_GOT_ENTRIES) + got_allocate = 0; #endif - -#if defined(__powerpc__) - case R_PPC_REL24: - pltneeded = 1; - break; +#if defined(CONFIG_USE_PLT_ENTRIES) + plt_allocate = 0; #endif + switch (ELF_R_TYPE(rel->r_info)) { #if defined(__arm__) - case R_ARM_PC24: - case R_ARM_PLT32: - pltneeded = 1; - break; + case R_ARM_PC24: + case R_ARM_PLT32: + plt_allocate = 1; + break; + + case R_ARM_GOTOFF: + case R_ARM_GOTPC: + got_needed = 1; + continue; + + case R_ARM_GOT32: + got_allocate = 1; + break; - case R_ARM_GOTPC: - case R_ARM_GOTOFF: - gotneeded = 1; - if (got_offset == 0) - got_offset = 4; -#elif defined(__sh__) - case R_SH_GOTPC: - case R_SH_GOTOFF: - gotneeded = 1; #elif defined(__i386__) - case R_386_GOTPC: - case R_386_GOTOFF: - gotneeded = 1; + case R_386_GOTPC: + case R_386_GOTOFF: + got_needed = 1; + continue; + + case R_386_GOT32: + got_allocate = 1; + break; + +#elif defined(__powerpc__) + case R_PPC_REL24: + plt_allocate = 1; + break; + +#elif defined(__mc68000__) + case R_68K_GOT32: + got_allocate = 1; + break; + +#ifdef R_68K_GOTOFF + case R_68K_GOTOFF: + got_needed = 1; + continue; #endif - default: - continue; +#elif defined(__sh__) + case R_SH_GOT32: + got_allocate = 1; + break; + + case R_SH_GOTPC: + case R_SH_GOTOFF: + got_needed = 1; + continue; + +#elif defined (__v850e__) + case R_V850_22_PCREL: + plt_needed = 1; + break; + +#endif + default: + continue; } if (extsym->st_name != 0) { @@ -1056,61 +1696,53 @@ int arch_create_got(struct obj_file *f) name = f->sections[extsym->st_shndx]->name; } intsym = (struct arch_symbol *) obj_find_symbol(f, name); -#if defined(BB_USE_GOT_ENTRIES) - if (!intsym->gotent.offset_done) { - intsym->gotent.offset_done = 1; - intsym->gotent.offset = got_offset; - got_offset += BB_GOT_ENTRY_SIZE; +#if defined(CONFIG_USE_GOT_ENTRIES) + if (got_allocate) { + got_offset += arch_single_init( + rel, &intsym->gotent, + got_offset, CONFIG_GOT_ENTRY_SIZE); + + got_needed = 1; } #endif -#if defined(BB_USE_PLT_ENTRIES) - if (pltneeded && intsym->pltent.allocated == 0) { - intsym->pltent.allocated = 1; - intsym->pltent.offset = plt_offset; - plt_offset += BB_PLT_ENTRY_SIZE; - intsym->pltent.inited = 0; - pltneeded = 0; - } +#if defined(CONFIG_USE_PLT_ENTRIES) + if (plt_allocate) { +#if defined(CONFIG_USE_PLT_LIST) + plt_offset += arch_list_add( + rel, &intsym->pltent, + plt_offset, CONFIG_PLT_ENTRY_SIZE); +#else + plt_offset += arch_single_init( + rel, &intsym->pltent, + plt_offset, CONFIG_PLT_ENTRY_SIZE); #endif + plt_needed = 1; } +#endif } + } -#if defined(BB_USE_GOT_ENTRIES) - if (got_offset) { - struct obj_section* relsec = obj_find_section(f, ".got"); - - if (relsec) { - obj_extend_section(relsec, got_offset); - } else { - relsec = obj_create_alloced_section(f, ".got", - BB_GOT_ENTRY_SIZE, - got_offset); - assert(relsec); - } - - ifile->got = relsec; +#if defined(CONFIG_USE_GOT_ENTRIES) + if (got_needed) { + ifile->got = arch_xsect_init(f, ".got", got_offset, + CONFIG_GOT_ENTRY_SIZE); } #endif -#if defined(BB_USE_PLT_ENTRIES) - if (plt_offset) - ifile->plt = obj_create_alloced_section(f, ".plt", - BB_PLT_ENTRY_SIZE, - plt_offset); +#if defined(CONFIG_USE_PLT_ENTRIES) + if (plt_needed) { + ifile->plt = arch_xsect_init(f, ".plt", plt_offset, + CONFIG_PLT_ENTRY_SIZE); + } #endif - return 1; -} -int arch_init_module(struct obj_file *f, struct new_module *mod) -{ - return 1; +#endif /* defined(CONFIG_USE_GOT_ENTRIES) || defined(CONFIG_USE_PLT_ENTRIES) */ } - /*======================================================================*/ /* Standard ELF hash function. */ -inline unsigned long obj_elf_hash_n(const char *name, unsigned long n) +static inline unsigned long obj_elf_hash_n(const char *name, unsigned long n) { unsigned long h = 0; unsigned long g; @@ -1128,38 +1760,12 @@ inline unsigned long obj_elf_hash_n(const char *name, unsigned long n) return h; } -unsigned long obj_elf_hash(const char *name) -{ - return obj_elf_hash_n(name, strlen(name)); -} - -#ifdef BB_FEATURE_INSMOD_VERSION_CHECKING -/* Get the kernel version in the canonical integer form. */ - -static int get_kernel_version(char str[STRVERSIONLEN]) -{ - struct utsname uts_info; - char *p, *q; - int a, b, c; - - if (uname(&uts_info) < 0) - return -1; - strncpy(str, uts_info.release, STRVERSIONLEN); - p = uts_info.release; - - a = strtoul(p, &p, 10); - if (*p != '.') - return -1; - b = strtoul(p + 1, &p, 10); - if (*p != '.') - return -1; - c = strtoul(p + 1, &q, 10); - if (p + 1 == q) - return -1; - - return a << 16 | b << 8 | c; +static unsigned long obj_elf_hash(const char *name) +{ + return obj_elf_hash_n(name, strlen(name)); } +#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING /* String comparison for non-co-versioned kernel and module. */ static int ncv_strcmp(const char *a, const char *b) @@ -1185,7 +1791,7 @@ static unsigned long ncv_symbol_hash(const char *str) return obj_elf_hash_n(str, len); } -void +static void obj_set_symbol_compare(struct obj_file *f, int (*cmp) (const char *, const char *), unsigned long (*hash) (const char *)) @@ -1211,25 +1817,25 @@ obj_set_symbol_compare(struct obj_file *f, } } -#endif /* BB_FEATURE_INSMOD_VERSION_CHECKING */ +#endif /* CONFIG_FEATURE_INSMOD_VERSION_CHECKING */ - -struct obj_symbol *obj_add_symbol(struct obj_file *f, const char *name, +static struct obj_symbol * +obj_add_symbol(struct obj_file *f, const char *name, unsigned long symidx, int info, int secidx, ElfW(Addr) value, unsigned long size) { struct obj_symbol *sym; unsigned long hash = f->symbol_hash(name) % HASH_BUCKETS; - int n_type = ELFW(ST_TYPE) (info); - int n_binding = ELFW(ST_BIND) (info); + int n_type = ELF_ST_TYPE(info); + int n_binding = ELF_ST_BIND(info); for (sym = f->symtab[hash]; sym; sym = sym->next) if (f->symbol_cmp(sym->name, name) == 0) { int o_secidx = sym->secidx; int o_info = sym->info; - int o_type = ELFW(ST_TYPE) (o_info); - int o_binding = ELFW(ST_BIND) (o_info); + int o_type = ELF_ST_TYPE(o_info); + int o_binding = ELF_ST_BIND(o_info); /* A redefinition! Is it legal? */ @@ -1270,16 +1876,16 @@ struct obj_symbol *obj_add_symbol(struct obj_file *f, const char *name, /* Don't unify COMMON symbols with object types the programmer doesn't expect. */ else if (secidx == SHN_COMMON - && (o_type == STT_NOTYPE || o_type == STT_OBJECT)) + && (o_type == STT_NOTYPE || o_type == STT_OBJECT)) return sym; else if (o_secidx == SHN_COMMON - && (n_type == STT_NOTYPE || n_type == STT_OBJECT)) + && (n_type == STT_NOTYPE || n_type == STT_OBJECT)) goto found; else { /* Don't report an error if the symbol is coming from the kernel or some external module. */ if (secidx <= SHN_HIRESERVE) - error_msg("%s multiply defined", name); + bb_error_msg("%s multiply defined", name); return sym; } } @@ -1290,10 +1896,15 @@ struct obj_symbol *obj_add_symbol(struct obj_file *f, const char *name, f->symtab[hash] = sym; sym->ksymidx = -1; - if (ELFW(ST_BIND) (info) == STB_LOCAL) - f->local_symtab[symidx] = sym; + if (ELF_ST_BIND(info) == STB_LOCAL && symidx != -1) { + if (symidx >= f->local_symtab_size) + bb_error_msg("local symbol %s with index %ld exceeds local_symtab_size %ld", + name, (long) symidx, (long) f->local_symtab_size); + else + f->local_symtab[symidx] = sym; + } - found: +found: sym->name = name; sym->value = value; sym->size = size; @@ -1303,7 +1914,8 @@ struct obj_symbol *obj_add_symbol(struct obj_file *f, const char *name, return sym; } -struct obj_symbol *obj_find_symbol(struct obj_file *f, const char *name) +static struct obj_symbol * +obj_find_symbol(struct obj_file *f, const char *name) { struct obj_symbol *sym; unsigned long hash = f->symbol_hash(name) % HASH_BUCKETS; @@ -1315,7 +1927,7 @@ struct obj_symbol *obj_find_symbol(struct obj_file *f, const char *name) return NULL; } -ElfW(Addr) +static ElfW(Addr) obj_symbol_final_value(struct obj_file * f, struct obj_symbol * sym) { if (sym) { @@ -1329,7 +1941,7 @@ ElfW(Addr) } } -struct obj_section *obj_find_section(struct obj_file *f, const char *name) +static struct obj_section *obj_find_section(struct obj_file *f, const char *name) { int i, n = f->header.e_shnum; @@ -1348,7 +1960,7 @@ static int obj_load_order_prio(struct obj_section *a) ac = 0; if (a->name[0] != '.' || strlen(a->name) != 10 || - strcmp(a->name + 5, ".init")) + strcmp(a->name + 5, ".init")) ac |= 32; if (af & SHF_ALLOC) ac |= 16; @@ -1362,7 +1974,7 @@ static int obj_load_order_prio(struct obj_section *a) return ac; } -void +static void obj_insert_section_load_order(struct obj_file *f, struct obj_section *sec) { struct obj_section **p; @@ -1374,7 +1986,7 @@ obj_insert_section_load_order(struct obj_file *f, struct obj_section *sec) *p = sec; } -struct obj_section *obj_create_alloced_section(struct obj_file *f, +static struct obj_section *obj_create_alloced_section(struct obj_file *f, const char *name, unsigned long align, unsigned long size) @@ -1400,7 +2012,7 @@ struct obj_section *obj_create_alloced_section(struct obj_file *f, return sec; } -struct obj_section *obj_create_alloced_section_first(struct obj_file *f, +static struct obj_section *obj_create_alloced_section_first(struct obj_file *f, const char *name, unsigned long align, unsigned long size) @@ -1429,39 +2041,85 @@ struct obj_section *obj_create_alloced_section_first(struct obj_file *f, return sec; } -void *obj_extend_section(struct obj_section *sec, unsigned long more) +static void *obj_extend_section(struct obj_section *sec, unsigned long more) { unsigned long oldsize = sec->header.sh_size; - sec->contents = xrealloc(sec->contents, sec->header.sh_size += more); + if (more) { + sec->contents = xrealloc(sec->contents, sec->header.sh_size += more); + } return sec->contents + oldsize; } - /* Conditionally add the symbols from the given symbol set to the new module. */ static int -add_symbols_from( - struct obj_file *f, +add_symbols_from( struct obj_file *f, int idx, struct new_module_symbol *syms, size_t nsyms) { struct new_module_symbol *s; size_t i; int used = 0; +#ifdef SYMBOL_PREFIX + char *name_buf = 0; + size_t name_alloced_size = 0; +#endif +#ifdef CONFIG_FEATURE_CHECK_TAINTED_MODULE + int gpl; + gpl = obj_gpl_license(f, NULL) == 0; +#endif for (i = 0, s = syms; i < nsyms; ++i, ++s) { - - /* Only add symbols that are already marked external. If we - override locals we may cause problems for argument initialization. - We will also create a false dependency on the module. */ + /* Only add symbols that are already marked external. + If we override locals we may cause problems for + argument initialization. We will also create a false + dependency on the module. */ struct obj_symbol *sym; - - sym = obj_find_symbol(f, (char *) s->name); - if (sym && !ELFW(ST_BIND) (sym->info) == STB_LOCAL) { - sym = obj_add_symbol(f, (char *) s->name, -1, - ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE), - idx, s->value, 0); + char *name; + + /* GPL licensed modules can use symbols exported with + * EXPORT_SYMBOL_GPL, so ignore any GPLONLY_ prefix on the + * exported names. Non-GPL modules never see any GPLONLY_ + * symbols so they cannot fudge it by adding the prefix on + * their references. + */ + if (strncmp((char *)s->name, "GPLONLY_", 8) == 0) { +#ifdef CONFIG_FEATURE_CHECK_TAINTED_MODULE + if (gpl) + s->name += 8; + else +#endif + continue; + } + name = (char *)s->name; + +#ifdef SYMBOL_PREFIX + /* Prepend SYMBOL_PREFIX to the symbol's name (the + kernel exports `C names', but module object files + reference `linker names'). */ + size_t extra = sizeof SYMBOL_PREFIX; + size_t name_size = strlen (name) + extra; + if (name_size > name_alloced_size) { + name_alloced_size = name_size * 2; + name_buf = alloca (name_alloced_size); + } + strcpy (name_buf, SYMBOL_PREFIX); + strcpy (name_buf + extra - 1, name); + name = name_buf; +#endif /* SYMBOL_PREFIX */ + + sym = obj_find_symbol(f, name); + if (sym && !(ELF_ST_BIND(sym->info) == STB_LOCAL)) { +#ifdef SYMBOL_PREFIX + /* Put NAME_BUF into more permanent storage. */ + name = xmalloc (name_size); + strcpy (name, name_buf); +#endif + sym = obj_add_symbol(f, name, -1, + ELF_ST_INFO(STB_GLOBAL, + STT_NOTYPE), + idx, s->value, 0); /* Did our symbol just get installed? If so, mark the module as "used". */ if (sym->secidx == idx) @@ -1481,8 +2139,8 @@ static void add_kernel_symbols(struct obj_file *f) for (i = 0, m = ext_modules; i < n_ext_modules; ++i, ++m) if (m->nsyms - && add_symbols_from(f, SHN_HIRESERVE + 2 + i, m->syms, - m->nsyms)) m->used = 1, ++nused; + && add_symbols_from(f, SHN_HIRESERVE + 2 + i, m->syms, + m->nsyms)) m->used = 1, ++nused; n_ext_modules_used = nused; @@ -1520,369 +2178,6 @@ static char *get_modinfo_value(struct obj_file *f, const char *key) } -/*======================================================================*/ -/* Functions relating to module loading in pre 2.1 kernels. */ - -static int -old_process_module_arguments(struct obj_file *f, int argc, char **argv) -{ - while (argc > 0) { - char *p, *q; - struct obj_symbol *sym; - int *loc; - - p = *argv; - if ((q = strchr(p, '=')) == NULL) { - argc--; - continue; - } - *q++ = '\0'; - - sym = obj_find_symbol(f, p); - - /* Also check that the parameter was not resolved from the kernel. */ - if (sym == NULL || sym->secidx > SHN_HIRESERVE) { - error_msg("symbol for parameter %s not found", p); - return 0; - } - - loc = (int *) (f->sections[sym->secidx]->contents + sym->value); - - /* Do C quoting if we begin with a ". */ - if (*q == '"') { - char *r, *str; - - str = alloca(strlen(q)); - for (r = str, q++; *q != '"'; ++q, ++r) { - if (*q == '\0') { - error_msg("improperly terminated string argument for %s", p); - return 0; - } else if (*q == '\\') - switch (*++q) { - case 'a': - *r = '\a'; - break; - case 'b': - *r = '\b'; - break; - case 'e': - *r = '\033'; - break; - case 'f': - *r = '\f'; - break; - case 'n': - *r = '\n'; - break; - case 'r': - *r = '\r'; - break; - case 't': - *r = '\t'; - break; - - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - { - int c = *q - '0'; - if (q[1] >= '0' && q[1] <= '7') { - c = (c * 8) + *++q - '0'; - if (q[1] >= '0' && q[1] <= '7') - c = (c * 8) + *++q - '0'; - } - *r = c; - } - break; - - default: - *r = *q; - break; - } else - *r = *q; - } - *r = '\0'; - obj_string_patch(f, sym->secidx, sym->value, str); - } else if (*q >= '0' && *q <= '9') { - do - *loc++ = strtoul(q, &q, 0); - while (*q++ == ','); - } else { - char *contents = f->sections[sym->secidx]->contents; - char *loc = contents + sym->value; - char *r; /* To search for commas */ - - /* Break the string with comas */ - while ((r = strchr(q, ',')) != (char *) NULL) { - *r++ = '\0'; - obj_string_patch(f, sym->secidx, loc - contents, q); - loc += sizeof(char *); - q = r; - } - - /* last part */ - obj_string_patch(f, sym->secidx, loc - contents, q); - } - - argc--, argv++; - } - - return 1; -} - -#ifdef BB_FEATURE_INSMOD_VERSION_CHECKING -static int old_is_module_checksummed(struct obj_file *f) -{ - return obj_find_symbol(f, "Using_Versions") != NULL; -} -/* Get the module's kernel version in the canonical integer form. */ - -static int -old_get_module_version(struct obj_file *f, char str[STRVERSIONLEN]) -{ - struct obj_symbol *sym; - char *p, *q; - int a, b, c; - - sym = obj_find_symbol(f, "kernel_version"); - if (sym == NULL) - return -1; - - p = f->sections[sym->secidx]->contents + sym->value; - strncpy(str, p, STRVERSIONLEN); - - a = strtoul(p, &p, 10); - if (*p != '.') - return -1; - b = strtoul(p + 1, &p, 10); - if (*p != '.') - return -1; - c = strtoul(p + 1, &q, 10); - if (p + 1 == q) - return -1; - - return a << 16 | b << 8 | c; -} - -#endif /* BB_FEATURE_INSMOD_VERSION_CHECKING */ - -#ifdef BB_FEATURE_OLD_MODULE_INTERFACE - -/* Fetch all the symbols and divvy them up as appropriate for the modules. */ - -static int old_get_kernel_symbols(const char *m_name) -{ - struct old_kernel_sym *ks, *k; - struct new_module_symbol *s; - struct external_module *mod; - int nks, nms, nmod, i; - - nks = get_kernel_syms(NULL); - if (nks < 0) { - perror_msg("get_kernel_syms: %s", m_name); - return 0; - } - - ks = k = xmalloc(nks * sizeof(*ks)); - - if (get_kernel_syms(ks) != nks) { - perror("inconsistency with get_kernel_syms -- is someone else " - "playing with modules?"); - free(ks); - return 0; - } - - /* Collect the module information. */ - - mod = NULL; - nmod = -1; - - while (k->name[0] == '#' && k->name[1]) { - struct old_kernel_sym *k2; - struct new_module_symbol *s; - - /* Find out how many symbols this module has. */ - for (k2 = k + 1; k2->name[0] != '#'; ++k2) - continue; - nms = k2 - k - 1; - - mod = xrealloc(mod, (++nmod + 1) * sizeof(*mod)); - mod[nmod].name = k->name + 1; - mod[nmod].addr = k->value; - mod[nmod].used = 0; - mod[nmod].nsyms = nms; - mod[nmod].syms = s = (nms ? xmalloc(nms * sizeof(*s)) : NULL); - - for (i = 0, ++k; i < nms; ++i, ++s, ++k) { - s->name = (unsigned long) k->name; - s->value = k->value; - } - - k = k2; - } - - ext_modules = mod; - n_ext_modules = nmod + 1; - - /* Now collect the symbols for the kernel proper. */ - - if (k->name[0] == '#') - ++k; - - nksyms = nms = nks - (k - ks); - ksyms = s = (nms ? xmalloc(nms * sizeof(*s)) : NULL); - - for (i = 0; i < nms; ++i, ++s, ++k) { - s->name = (unsigned long) k->name; - s->value = k->value; - } - - return 1; -} - -/* Return the kernel symbol checksum version, or zero if not used. */ - -static int old_is_kernel_checksummed(void) -{ - /* Using_Versions is the first symbol. */ - if (nksyms > 0 - && strcmp((char *) ksyms[0].name, - "Using_Versions") == 0) return ksyms[0].value; - else - return 0; -} - - -static int old_create_mod_use_count(struct obj_file *f) -{ - struct obj_section *sec; - - sec = obj_create_alloced_section_first(f, ".moduse", sizeof(long), - sizeof(long)); - - obj_add_symbol(f, "mod_use_count_", -1, - ELFW(ST_INFO) (STB_LOCAL, STT_OBJECT), sec->idx, 0, - sizeof(long)); - - return 1; -} - -static int -old_init_module(const char *m_name, struct obj_file *f, - unsigned long m_size) -{ - char *image; - struct old_mod_routines routines; - struct old_symbol_table *symtab; - int ret; - - /* Create the symbol table */ - { - int nsyms = 0, strsize = 0, total; - - /* Size things first... */ - if (flag_export) { - int i; - for (i = 0; i < HASH_BUCKETS; ++i) { - struct obj_symbol *sym; - for (sym = f->symtab[i]; sym; sym = sym->next) - if (ELFW(ST_BIND) (sym->info) != STB_LOCAL - && sym->secidx <= SHN_HIRESERVE) - { - sym->ksymidx = nsyms++; - strsize += strlen(sym->name) + 1; - } - } - } - - total = (sizeof(struct old_symbol_table) - + nsyms * sizeof(struct old_module_symbol) - + n_ext_modules_used * sizeof(struct old_module_ref) - + strsize); - symtab = xmalloc(total); - symtab->size = total; - symtab->n_symbols = nsyms; - symtab->n_refs = n_ext_modules_used; - - if (flag_export && nsyms) { - struct old_module_symbol *ksym; - char *str; - int i; - - ksym = symtab->symbol; - str = ((char *) ksym + nsyms * sizeof(struct old_module_symbol) - + n_ext_modules_used * sizeof(struct old_module_ref)); - - for (i = 0; i < HASH_BUCKETS; ++i) { - struct obj_symbol *sym; - for (sym = f->symtab[i]; sym; sym = sym->next) - if (sym->ksymidx >= 0) { - ksym->addr = obj_symbol_final_value(f, sym); - ksym->name = - (unsigned long) str - (unsigned long) symtab; - - strcpy(str, sym->name); - str += strlen(sym->name) + 1; - ksym++; - } - } - } - - if (n_ext_modules_used) { - struct old_module_ref *ref; - int i; - - ref = (struct old_module_ref *) - ((char *) symtab->symbol + nsyms * sizeof(struct old_module_symbol)); - - for (i = 0; i < n_ext_modules; ++i) - if (ext_modules[i].used) - ref++->module = ext_modules[i].addr; - } - } - - /* Fill in routines. */ - - routines.init = - obj_symbol_final_value(f, obj_find_symbol(f, "init_module")); - routines.cleanup = - obj_symbol_final_value(f, obj_find_symbol(f, "cleanup_module")); - - /* Whew! All of the initialization is complete. Collect the final - module image and give it to the kernel. */ - - image = xmalloc(m_size); - obj_create_image(f, image); - - /* image holds the complete relocated module, accounting correctly for - mod_use_count. However the old module kernel support assume that - it is receiving something which does not contain mod_use_count. */ - ret = old_sys_init_module(m_name, image + sizeof(long), - m_size | (flag_autoclean ? OLD_MOD_AUTOCLEAN - : 0), &routines, symtab); - if (ret) - perror_msg("init_module: %s", m_name); - - free(image); - free(symtab); - - return ret == 0; -} - -#else - -#define old_create_mod_use_count(x) TRUE -#define old_init_module(x, y, z) TRUE - -#endif /* BB_FEATURE_OLD_MODULE_INTERFACE */ - - - /*======================================================================*/ /* Functions relating to module loading after 2.1.18. */ @@ -1890,7 +2185,7 @@ static int new_process_module_arguments(struct obj_file *f, int argc, char **argv) { while (argc > 0) { - char *p, *q, *key; + char *p, *q, *key, *sym_name; struct obj_symbol *sym; char *contents, *loc; int min, max, n; @@ -1899,7 +2194,7 @@ new_process_module_arguments(struct obj_file *f, int argc, char **argv) if ((q = strchr(p, '=')) == NULL) { argc--; continue; - } + } key = alloca(q - p + 6); memcpy(key, "parm_", 5); @@ -1909,15 +2204,22 @@ new_process_module_arguments(struct obj_file *f, int argc, char **argv) p = get_modinfo_value(f, key); key += 5; if (p == NULL) { - error_msg("invalid parameter %s", key); + bb_error_msg("invalid parameter %s", key); return 0; } - sym = obj_find_symbol(f, key); +#ifdef SYMBOL_PREFIX + sym_name = alloca (strlen (key) + sizeof SYMBOL_PREFIX); + strcpy (sym_name, SYMBOL_PREFIX); + strcat (sym_name, key); +#else + sym_name = key; +#endif + sym = obj_find_symbol(f, sym_name); /* Also check that the parameter was not resolved from the kernel. */ if (sym == NULL || sym->secidx > SHN_HIRESERVE) { - error_msg("symbol for parameter %s not found", key); + bb_error_msg("symbol for parameter %s not found", key); return 0; } @@ -1945,57 +2247,57 @@ new_process_module_arguments(struct obj_file *f, int argc, char **argv) str = alloca(strlen(q)); for (r = str, q++; *q != '"'; ++q, ++r) { if (*q == '\0') { - error_msg("improperly terminated string argument for %s", + bb_error_msg("improperly terminated string argument for %s", key); return 0; } else if (*q == '\\') switch (*++q) { - case 'a': - *r = '\a'; - break; - case 'b': - *r = '\b'; - break; - case 'e': - *r = '\033'; - break; - case 'f': - *r = '\f'; - break; - case 'n': - *r = '\n'; - break; - case 'r': - *r = '\r'; - break; - case 't': - *r = '\t'; - break; - - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - { - int c = *q - '0'; - if (q[1] >= '0' && q[1] <= '7') { - c = (c * 8) + *++q - '0'; - if (q[1] >= '0' && q[1] <= '7') + case 'a': + *r = '\a'; + break; + case 'b': + *r = '\b'; + break; + case 'e': + *r = '\033'; + break; + case 'f': + *r = '\f'; + break; + case 'n': + *r = '\n'; + break; + case 'r': + *r = '\r'; + break; + case 't': + *r = '\t'; + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + int c = *q - '0'; + if (q[1] >= '0' && q[1] <= '7') { c = (c * 8) + *++q - '0'; + if (q[1] >= '0' && q[1] <= '7') + c = (c * 8) + *++q - '0'; + } + *r = c; } - *r = c; - } - break; + break; - default: + default: + *r = *q; + break; + } else *r = *q; - break; - } else - *r = *q; } *r = '\0'; ++q; @@ -2015,8 +2317,8 @@ new_process_module_arguments(struct obj_file *f, int argc, char **argv) str = alloca(r - q + 1); memcpy(str, q, r - q); - /* I don't know if it is usefull, as the previous case - doesn't null terminate the string ??? */ + /* I don't know if it is useful, as the previous case + doesn't nul terminate the string ??? */ str[r - q] = '\0'; /* Keep next fields */ @@ -2039,7 +2341,7 @@ new_process_module_arguments(struct obj_file *f, int argc, char **argv) /* Get the size of each member */ /* Probably we should do that outside the loop ? */ if (!isdigit(*(p + 1))) { - error_msg("parameter type 'c' for %s must be followed by" + bb_error_msg("parameter type 'c' for %s must be followed by" " the maximum size", key); return 0; } @@ -2047,7 +2349,7 @@ new_process_module_arguments(struct obj_file *f, int argc, char **argv) /* Check length */ if (strlen(str) >= charssize) { - error_msg("string too long for %s (max %ld)", key, + bb_error_msg("string too long for %s (max %ld)", key, charssize - 1); return 0; } @@ -2059,57 +2361,57 @@ new_process_module_arguments(struct obj_file *f, int argc, char **argv) } else { long v = strtoul(q, &q, 0); switch (*p) { - case 'b': - *loc++ = v; - break; - case 'h': - *(short *) loc = v; - loc += tgt_sizeof_short; - break; - case 'i': - *(int *) loc = v; - loc += tgt_sizeof_int; - break; - case 'l': - *(long *) loc = v; - loc += tgt_sizeof_long; - break; + case 'b': + *loc++ = v; + break; + case 'h': + *(short *) loc = v; + loc += tgt_sizeof_short; + break; + case 'i': + *(int *) loc = v; + loc += tgt_sizeof_int; + break; + case 'l': + *(long *) loc = v; + loc += tgt_sizeof_long; + break; - default: - error_msg("unknown parameter type '%c' for %s", *p, key); - return 0; + default: + bb_error_msg("unknown parameter type '%c' for %s", *p, key); + return 0; } } - retry_end_of_value: +retry_end_of_value: switch (*q) { - case '\0': - goto end_of_arg; - - case ' ': - case '\t': - case '\n': - case '\r': - ++q; - goto retry_end_of_value; - - case ',': - if (++n > max) { - error_msg("too many values for %s (max %d)", key, max); - return 0; - } - ++q; - break; + case '\0': + goto end_of_arg; - default: - error_msg("invalid argument syntax for %s", key); - return 0; + case ' ': + case '\t': + case '\n': + case '\r': + ++q; + goto retry_end_of_value; + + case ',': + if (++n > max) { + bb_error_msg("too many values for %s (max %d)", key, max); + return 0; + } + ++q; + break; + + default: + bb_error_msg("invalid argument syntax for %s", key); + return 0; } } - end_of_arg: +end_of_arg: if (n < min) { - error_msg("too few values for %s (min %d)", key, min); + bb_error_msg("too few values for %s (min %d)", key, min); return 0; } @@ -2119,7 +2421,7 @@ new_process_module_arguments(struct obj_file *f, int argc, char **argv) return 1; } -#ifdef BB_FEATURE_INSMOD_VERSION_CHECKING +#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING static int new_is_module_checksummed(struct obj_file *f) { const char *p = get_modinfo_value(f, "using_checksums"); @@ -2140,7 +2442,7 @@ new_get_module_version(struct obj_file *f, char str[STRVERSIONLEN]) p = get_modinfo_value(f, "kernel_version"); if (p == NULL) return -1; - strncpy(str, p, STRVERSIONLEN); + safe_strncpy(str, p, STRVERSIONLEN); a = strtoul(p, &p, 10); if (*p != '.') @@ -2155,11 +2457,9 @@ new_get_module_version(struct obj_file *f, char str[STRVERSIONLEN]) return a << 16 | b << 8 | c; } -#endif /* BB_FEATURE_INSMOD_VERSION_CHECKING */ +#endif /* CONFIG_FEATURE_INSMOD_VERSION_CHECKING */ -#ifdef BB_FEATURE_NEW_MODULE_INTERFACE - /* Fetch the loaded modules, and all currently exported symbols. */ static int new_get_kernel_symbols(void) @@ -2172,72 +2472,74 @@ static int new_get_kernel_symbols(void) /* Collect the loaded modules. */ module_names = xmalloc(bufsize = 256); - retry_modules_load: +retry_modules_load: if (query_module(NULL, QM_MODULES, module_names, bufsize, &ret)) { - if (errno == ENOSPC) { + if (errno == ENOSPC && bufsize < ret) { module_names = xrealloc(module_names, bufsize = ret); goto retry_modules_load; } - perror_msg("QM_MODULES"); + bb_perror_msg("QM_MODULES"); return 0; } n_ext_modules = nmod = ret; - ext_modules = modules = xmalloc(nmod * sizeof(*modules)); - memset(modules, 0, nmod * sizeof(*modules)); /* Collect the modules' symbols. */ - for (i = 0, mn = module_names, m = modules; - i < nmod; ++i, ++m, mn += strlen(mn) + 1) { - struct new_module_info info; + if (nmod){ + ext_modules = modules = xmalloc(nmod * sizeof(*modules)); + memset(modules, 0, nmod * sizeof(*modules)); + for (i = 0, mn = module_names, m = modules; + i < nmod; ++i, ++m, mn += strlen(mn) + 1) { + struct new_module_info info; - if (query_module(mn, QM_INFO, &info, sizeof(info), &ret)) { - if (errno == ENOENT) { - /* The module was removed out from underneath us. */ - continue; + if (query_module(mn, QM_INFO, &info, sizeof(info), &ret)) { + if (errno == ENOENT) { + /* The module was removed out from underneath us. */ + continue; + } + bb_perror_msg("query_module: QM_INFO: %s", mn); + return 0; } - perror_msg("query_module: QM_INFO: %s", mn); - return 0; - } - syms = xmalloc(bufsize = 1024); - retry_mod_sym_load: - if (query_module(mn, QM_SYMBOLS, syms, bufsize, &ret)) { - switch (errno) { - case ENOSPC: - syms = xrealloc(syms, bufsize = ret); - goto retry_mod_sym_load; - case ENOENT: - /* The module was removed out from underneath us. */ - continue; - default: - perror_msg("query_module: QM_SYMBOLS: %s", mn); - return 0; + syms = xmalloc(bufsize = 1024); +retry_mod_sym_load: + if (query_module(mn, QM_SYMBOLS, syms, bufsize, &ret)) { + switch (errno) { + case ENOSPC: + syms = xrealloc(syms, bufsize = ret); + goto retry_mod_sym_load; + case ENOENT: + /* The module was removed out from underneath us. */ + continue; + default: + bb_perror_msg("query_module: QM_SYMBOLS: %s", mn); + return 0; + } } - } - nsyms = ret; + nsyms = ret; - m->name = mn; - m->addr = info.addr; - m->nsyms = nsyms; - m->syms = syms; + m->name = mn; + m->addr = info.addr; + m->nsyms = nsyms; + m->syms = syms; - for (j = 0, s = syms; j < nsyms; ++j, ++s) { - s->name += (unsigned long) syms; + for (j = 0, s = syms; j < nsyms; ++j, ++s) { + s->name += (unsigned long) syms; + } } } /* Collect the kernel's symbols. */ syms = xmalloc(bufsize = 16 * 1024); - retry_kern_sym_load: +retry_kern_sym_load: if (query_module(NULL, QM_SYMBOLS, syms, bufsize, &ret)) { - if (errno == ENOSPC) { + if (errno == ENOSPC && bufsize < ret) { syms = xrealloc(syms, bufsize = ret); goto retry_kern_sym_load; } - perror_msg("kernel: QM_SYMBOLS"); + bb_perror_msg("kernel: QM_SYMBOLS"); return 0; } nksyms = nsyms = ret; @@ -2272,19 +2574,50 @@ static int new_create_this_module(struct obj_file *f, const char *m_name) struct obj_section *sec; sec = obj_create_alloced_section_first(f, ".this", tgt_sizeof_long, - sizeof(struct new_module)); + sizeof(struct new_module)); memset(sec->contents, 0, sizeof(struct new_module)); - obj_add_symbol(f, "__this_module", -1, - ELFW(ST_INFO) (STB_LOCAL, STT_OBJECT), sec->idx, 0, - sizeof(struct new_module)); + obj_add_symbol(f, SPFX "__this_module", -1, + ELF_ST_INFO(STB_LOCAL, STT_OBJECT), sec->idx, 0, + sizeof(struct new_module)); obj_string_patch(f, sec->idx, offsetof(struct new_module, name), - m_name); + m_name); return 1; } +#ifdef CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS +/* add an entry to the __ksymtab section, creating it if necessary */ +static void new_add_ksymtab(struct obj_file *f, struct obj_symbol *sym) +{ + struct obj_section *sec; + ElfW(Addr) ofs; + + /* ensure __ksymtab is allocated, EXPORT_NOSYMBOLS creates a non-alloc section. + * If __ksymtab is defined but not marked alloc, x out the first character + * (no obj_delete routine) and create a new __ksymtab with the correct + * characteristics. + */ + sec = obj_find_section(f, "__ksymtab"); + if (sec && !(sec->header.sh_flags & SHF_ALLOC)) { + *((char *)(sec->name)) = 'x'; /* override const */ + sec = NULL; + } + if (!sec) + sec = obj_create_alloced_section(f, "__ksymtab", + tgt_sizeof_void_p, 0); + if (!sec) + return; + sec->header.sh_flags |= SHF_ALLOC; + sec->header.sh_addralign = tgt_sizeof_void_p; /* Empty section might + be byte-aligned */ + ofs = sec->header.sh_size; + obj_symbol_patch(f, sec->idx, ofs, sym); + obj_string_patch(f, sec->idx, ofs + tgt_sizeof_void_p, sym->name); + obj_extend_section(sec, 2 * tgt_sizeof_char_p); +} +#endif /* CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS */ static int new_create_module_ksymtab(struct obj_file *f) { @@ -2298,18 +2631,18 @@ static int new_create_module_ksymtab(struct obj_file *f) struct obj_symbol *tm; sec = obj_create_alloced_section(f, ".kmodtab", tgt_sizeof_void_p, - (sizeof(struct new_module_ref) - * n_ext_modules_used)); + (sizeof(struct new_module_ref) + * n_ext_modules_used)); if (!sec) return 0; - tm = obj_find_symbol(f, "__this_module"); + tm = obj_find_symbol(f, SPFX "__this_module"); dep = (struct new_module_ref *) sec->contents; for (i = 0; i < n_ext_modules; ++i) if (ext_modules[i].used) { dep->dep = ext_modules[i].addr; obj_symbol_patch(f, sec->idx, - (char *) &dep->ref - sec->contents, tm); + (char *) &dep->ref - sec->contents, tm); dep->next_ref = 0; ++dep; } @@ -2321,7 +2654,7 @@ static int new_create_module_ksymtab(struct obj_file *f) sec = obj_create_alloced_section(f, "__ksymtab", tgt_sizeof_void_p, - 0); + 0); /* We don't want to export symbols residing in sections that aren't loaded. There are a number of these created so that @@ -2334,15 +2667,15 @@ static int new_create_module_ksymtab(struct obj_file *f) for (nsyms = i = 0; i < HASH_BUCKETS; ++i) { struct obj_symbol *sym; for (sym = f->symtab[i]; sym; sym = sym->next) - if (ELFW(ST_BIND) (sym->info) != STB_LOCAL - && sym->secidx <= SHN_HIRESERVE - && (sym->secidx >= SHN_LORESERVE - || loaded[sym->secidx])) { + if (ELF_ST_BIND(sym->info) != STB_LOCAL + && sym->secidx <= SHN_HIRESERVE + && (sym->secidx >= SHN_LORESERVE + || loaded[sym->secidx])) { ElfW(Addr) ofs = nsyms * 2 * tgt_sizeof_void_p; obj_symbol_patch(f, sec->idx, ofs, sym); obj_string_patch(f, sec->idx, ofs + tgt_sizeof_void_p, - sym->name); + sym->name); nsyms++; } @@ -2356,8 +2689,7 @@ static int new_create_module_ksymtab(struct obj_file *f) static int -new_init_module(const char *m_name, struct obj_file *f, - unsigned long m_size) +new_init_module(const char *m_name, struct obj_file *f, unsigned long m_size) { struct new_module *module; struct obj_section *sec; @@ -2366,6 +2698,9 @@ new_init_module(const char *m_name, struct obj_file *f, tgt_long m_addr; sec = obj_find_section(f, ".this"); + if (!sec || !sec->contents) { + bb_perror_msg_and_die("corrupt module %s?",m_name); + } module = (struct new_module *) sec->contents; m_addr = sec->header.sh_addr; @@ -2386,9 +2721,9 @@ new_init_module(const char *m_name, struct obj_file *f, } module->init = - obj_symbol_final_value(f, obj_find_symbol(f, "init_module")); + obj_symbol_final_value(f, obj_find_symbol(f, SPFX "init_module")); module->cleanup = - obj_symbol_final_value(f, obj_find_symbol(f, "cleanup_module")); + obj_symbol_final_value(f, obj_find_symbol(f, SPFX "cleanup_module")); sec = obj_find_section(f, "__ex_table"); if (sec) { @@ -2403,12 +2738,19 @@ new_init_module(const char *m_name, struct obj_file *f, sec = obj_find_section(f, ".data.init"); if (sec) { if (!module->runsize || - module->runsize > sec->header.sh_addr - m_addr) - module->runsize = sec->header.sh_addr - m_addr; + module->runsize > sec->header.sh_addr - m_addr) + module->runsize = sec->header.sh_addr - m_addr; + } + sec = obj_find_section(f, ARCHDATA_SEC_NAME); + if (sec && sec->header.sh_size) { + module->archdata_start = (void*)sec->header.sh_addr; + module->archdata_end = module->archdata_start + sec->header.sh_size; + } + sec = obj_find_section(f, KALLSYMS_SEC_NAME); + if (sec && sec->header.sh_size) { + module->kallsyms_start = (void*)sec->header.sh_addr; + module->kallsyms_end = module->kallsyms_start + sec->header.sh_size; } - - if (!arch_init_module(f, module)) - return 0; /* Whew! All of the initialization is complete. Collect the final module image and give it to the kernel. */ @@ -2416,28 +2758,19 @@ new_init_module(const char *m_name, struct obj_file *f, image = xmalloc(m_size); obj_create_image(f, image); - ret = new_sys_init_module(m_name, (struct new_module *) image); + ret = init_module(m_name, (struct new_module *) image); if (ret) - perror_msg("init_module: %s", m_name); + bb_perror_msg("init_module: %s", m_name); free(image); return ret == 0; } -#else - -#define new_init_module(x, y, z) TRUE -#define new_create_this_module(x, y) 0 -#define new_create_module_ksymtab(x) -#define query_module(v, w, x, y, z) -1 - -#endif /* BB_FEATURE_NEW_MODULE_INTERFACE */ - /*======================================================================*/ -int +static int obj_string_patch(struct obj_file *f, int secidx, ElfW(Addr) offset, const char *string) { @@ -2466,7 +2799,7 @@ obj_string_patch(struct obj_file *f, int secidx, ElfW(Addr) offset, return 1; } -int +static int obj_symbol_patch(struct obj_file *f, int secidx, ElfW(Addr) offset, struct obj_symbol *sym) { @@ -2482,7 +2815,7 @@ obj_symbol_patch(struct obj_file *f, int secidx, ElfW(Addr) offset, return 1; } -int obj_check_undefineds(struct obj_file *f) +static int obj_check_undefineds(struct obj_file *f) { unsigned long i; int ret = 1; @@ -2491,11 +2824,13 @@ int obj_check_undefineds(struct obj_file *f) struct obj_symbol *sym; for (sym = f->symtab[i]; sym; sym = sym->next) if (sym->secidx == SHN_UNDEF) { - if (ELFW(ST_BIND) (sym->info) == STB_WEAK) { + if (ELF_ST_BIND(sym->info) == STB_WEAK) { sym->secidx = SHN_ABS; sym->value = 0; } else { - error_msg("unresolved symbol %s", sym->name); + if (!flag_quiet) { + bb_error_msg("unresolved symbol %s", sym->name); + } ret = 0; } } @@ -2504,7 +2839,7 @@ int obj_check_undefineds(struct obj_file *f) return ret; } -void obj_allocate_commons(struct obj_file *f) +static void obj_allocate_commons(struct obj_file *f) { struct common_entry { struct common_entry *next; @@ -2603,8 +2938,8 @@ void obj_allocate_commons(struct obj_file *f) struct obj_section *s = f->sections[i]; if (s->header.sh_type == SHT_NOBITS) { if (s->header.sh_size != 0) - s->contents = memset(xmalloc(s->header.sh_size), - 0, s->header.sh_size); + s->contents = memset(xmalloc(s->header.sh_size), + 0, s->header.sh_size); else s->contents = NULL; @@ -2613,7 +2948,7 @@ void obj_allocate_commons(struct obj_file *f) } } -unsigned long obj_load_size(struct obj_file *f) +static unsigned long obj_load_size(struct obj_file *f) { unsigned long dot = 0; struct obj_section *sec; @@ -2634,7 +2969,7 @@ unsigned long obj_load_size(struct obj_file *f) return dot; } -int obj_relocate(struct obj_file *f, ElfW(Addr) base) +static int obj_relocate(struct obj_file *f, ElfW(Addr) base) { int i, n = f->header.e_shnum; int ret = 1; @@ -2675,12 +3010,12 @@ int obj_relocate(struct obj_file *f, ElfW(Addr) base) /* Attempt to find a value to use for this relocation. */ - symndx = ELFW(R_SYM) (rel->r_info); + symndx = ELF_R_SYM(rel->r_info); if (symndx) { /* Note we've already checked for undefined symbols. */ extsym = &symtab[symndx]; - if (ELFW(ST_BIND) (extsym->st_info) == STB_LOCAL) { + if (ELF_ST_BIND(extsym->st_info) == STB_LOCAL) { /* Local symbols we look up in the local table to be sure we get the one that is really intended. */ intsym = f->local_symtab[symndx]; @@ -2701,7 +3036,7 @@ int obj_relocate(struct obj_file *f, ElfW(Addr) base) #if defined(__alpha__) && defined(AXP_BROKEN_GAS) /* Work around a nasty GAS bug, that is fixed as of 2.7.0.9. */ if (!extsym || !extsym->st_name || - ELFW(ST_BIND) (extsym->st_info) != STB_LOCAL) + ELF_ST_BIND(extsym->st_info) != STB_LOCAL) #endif value += rel->r_addend; #endif @@ -2709,28 +3044,28 @@ int obj_relocate(struct obj_file *f, ElfW(Addr) base) /* Do it! */ switch (arch_apply_relocation (f, targsec, symsec, intsym, rel, value)) { - case obj_reloc_ok: - break; + case obj_reloc_ok: + break; - case obj_reloc_overflow: - errmsg = "Relocation overflow"; - goto bad_reloc; - case obj_reloc_dangerous: - errmsg = "Dangerous relocation"; - goto bad_reloc; - case obj_reloc_unhandled: - errmsg = "Unhandled relocation"; - bad_reloc: - if (extsym) { - error_msg("%s of type %ld for %s", errmsg, - (long) ELFW(R_TYPE) (rel->r_info), - strtab + extsym->st_name); - } else { - error_msg("%s of type %ld", errmsg, - (long) ELFW(R_TYPE) (rel->r_info)); - } - ret = 0; - break; + case obj_reloc_overflow: + errmsg = "Relocation overflow"; + goto bad_reloc; + case obj_reloc_dangerous: + errmsg = "Dangerous relocation"; + goto bad_reloc; + case obj_reloc_unhandled: + errmsg = "Unhandled relocation"; +bad_reloc: + if (extsym) { + bb_error_msg("%s of type %ld for %s", errmsg, + (long) ELF_R_TYPE(rel->r_info), + strtab + extsym->st_name); + } else { + bb_error_msg("%s of type %ld", errmsg, + (long) ELF_R_TYPE(rel->r_info)); + } + ret = 0; + break; } } } @@ -2764,7 +3099,7 @@ int obj_relocate(struct obj_file *f, ElfW(Addr) base) return ret; } -int obj_create_image(struct obj_file *f, char *image) +static int obj_create_image(struct obj_file *f, char *image) { struct obj_section *sec; ElfW(Addr) base = f->baseaddr; @@ -2772,7 +3107,7 @@ int obj_create_image(struct obj_file *f, char *image) for (sec = f->load_order; sec; sec = sec->load_next) { char *secimg; - if (sec->header.sh_size == 0) + if (sec->contents == 0 || sec->header.sh_size == 0) continue; secimg = image + (sec->header.sh_addr - base); @@ -2786,7 +3121,7 @@ int obj_create_image(struct obj_file *f, char *image) /*======================================================================*/ -struct obj_file *obj_load(FILE * fp) +static struct obj_file *obj_load(FILE * fp, int loadprogbits) { struct obj_file *f; ElfW(Shdr) * section_headers; @@ -2803,33 +3138,33 @@ struct obj_file *obj_load(FILE * fp) fseek(fp, 0, SEEK_SET); if (fread(&f->header, sizeof(f->header), 1, fp) != 1) { - perror_msg("error reading ELF header"); + bb_perror_msg("error reading ELF header"); return NULL; } if (f->header.e_ident[EI_MAG0] != ELFMAG0 - || f->header.e_ident[EI_MAG1] != ELFMAG1 - || f->header.e_ident[EI_MAG2] != ELFMAG2 - || f->header.e_ident[EI_MAG3] != ELFMAG3) { - error_msg("not an ELF file"); + || f->header.e_ident[EI_MAG1] != ELFMAG1 + || f->header.e_ident[EI_MAG2] != ELFMAG2 + || f->header.e_ident[EI_MAG3] != ELFMAG3) { + bb_error_msg("not an ELF file"); return NULL; } if (f->header.e_ident[EI_CLASS] != ELFCLASSM - || f->header.e_ident[EI_DATA] != ELFDATAM - || f->header.e_ident[EI_VERSION] != EV_CURRENT - || !MATCH_MACHINE(f->header.e_machine)) { - error_msg("ELF file not for this architecture"); + || f->header.e_ident[EI_DATA] != ELFDATAM + || f->header.e_ident[EI_VERSION] != EV_CURRENT + || !MATCH_MACHINE(f->header.e_machine)) { + bb_error_msg("ELF file not for this architecture"); return NULL; } if (f->header.e_type != ET_REL) { - error_msg("ELF file not a relocatable object"); + bb_error_msg("ELF file not a relocatable object"); return NULL; } /* Read the section headers. */ if (f->header.e_shentsize != sizeof(ElfW(Shdr))) { - error_msg("section header size mismatch: %lu != %lu", + bb_error_msg("section header size mismatch: %lu != %lu", (unsigned long) f->header.e_shentsize, (unsigned long) sizeof(ElfW(Shdr))); return NULL; @@ -2842,7 +3177,7 @@ struct obj_file *obj_load(FILE * fp) section_headers = alloca(sizeof(ElfW(Shdr)) * shnum); fseek(fp, f->header.e_shoff, SEEK_SET); if (fread(section_headers, sizeof(ElfW(Shdr)), shnum, fp) != shnum) { - perror_msg("error reading ELF section headers"); + bb_perror_msg("error reading ELF section headers"); return NULL; } @@ -2857,51 +3192,57 @@ struct obj_file *obj_load(FILE * fp) sec->header = section_headers[i]; sec->idx = i; - switch (sec->header.sh_type) { - case SHT_NULL: - case SHT_NOTE: - case SHT_NOBITS: - /* ignore */ - break; - - case SHT_PROGBITS: - case SHT_SYMTAB: - case SHT_STRTAB: - case SHT_RELM: - if (sec->header.sh_size > 0) { - sec->contents = xmalloc(sec->header.sh_size); - fseek(fp, sec->header.sh_offset, SEEK_SET); - if (fread(sec->contents, sec->header.sh_size, 1, fp) != 1) { - perror_msg("error reading ELF section data"); - return NULL; + if(sec->header.sh_size) switch (sec->header.sh_type) { + case SHT_NULL: + case SHT_NOTE: + case SHT_NOBITS: + /* ignore */ + break; + + case SHT_PROGBITS: +#if LOADBITS + if (!loadprogbits) { + sec->contents = NULL; + break; + } +#endif + case SHT_SYMTAB: + case SHT_STRTAB: + case SHT_RELM: + if (sec->header.sh_size > 0) { + sec->contents = xmalloc(sec->header.sh_size); + fseek(fp, sec->header.sh_offset, SEEK_SET); + if (fread(sec->contents, sec->header.sh_size, 1, fp) != 1) { + bb_perror_msg("error reading ELF section data"); + return NULL; + } + } else { + sec->contents = NULL; } - } else { - sec->contents = NULL; - } - break; + break; #if SHT_RELM == SHT_REL - case SHT_RELA: - error_msg("RELA relocations not supported on this architecture"); - return NULL; + case SHT_RELA: + bb_error_msg("RELA relocations not supported on this architecture"); + return NULL; #else - case SHT_REL: - error_msg("REL relocations not supported on this architecture"); - return NULL; + case SHT_REL: + bb_error_msg("REL relocations not supported on this architecture"); + return NULL; #endif - default: - if (sec->header.sh_type >= SHT_LOPROC) { - /* Assume processor specific section types are debug - info and can safely be ignored. If this is ever not - the case (Hello MIPS?), don't put ifdefs here but - create an arch_load_proc_section(). */ - break; - } + default: + if (sec->header.sh_type >= SHT_LOPROC) { + /* Assume processor specific section types are debug + info and can safely be ignored. If this is ever not + the case (Hello MIPS?), don't put ifdefs here but + create an arch_load_proc_section(). */ + break; + } - error_msg("can't handle sections of type %ld", - (long) sec->header.sh_type); - return NULL; + bb_error_msg("can't handle sections of type %ld", + (long) sec->header.sh_type); + return NULL; } } @@ -2917,67 +3258,121 @@ struct obj_file *obj_load(FILE * fp) for (i = 0; i < shnum; ++i) { struct obj_section *sec = f->sections[i]; + /* .modinfo should be contents only but gcc has no attribute for that. + * The kernel may have marked .modinfo as ALLOC, ignore this bit. + */ + if (strcmp(sec->name, ".modinfo") == 0) + sec->header.sh_flags &= ~SHF_ALLOC; + if (sec->header.sh_flags & SHF_ALLOC) obj_insert_section_load_order(f, sec); switch (sec->header.sh_type) { - case SHT_SYMTAB: - { - unsigned long nsym, j; - char *strtab; - ElfW(Sym) * sym; + case SHT_SYMTAB: + { + unsigned long nsym, j; + char *strtab; + ElfW(Sym) * sym; + + if (sec->header.sh_entsize != sizeof(ElfW(Sym))) { + bb_error_msg("symbol size mismatch: %lu != %lu", + (unsigned long) sec->header.sh_entsize, + (unsigned long) sizeof(ElfW(Sym))); + return NULL; + } + + nsym = sec->header.sh_size / sizeof(ElfW(Sym)); + strtab = f->sections[sec->header.sh_link]->contents; + sym = (ElfW(Sym) *) sec->contents; + + /* Allocate space for a table of local symbols. */ + j = f->local_symtab_size = sec->header.sh_info; + f->local_symtab = xcalloc(j, sizeof(struct obj_symbol *)); + + /* Insert all symbols into the hash table. */ + for (j = 1, ++sym; j < nsym; ++j, ++sym) { + ElfW(Addr) val = sym->st_value; + const char *name; + if (sym->st_name) + name = strtab + sym->st_name; + else if (sym->st_shndx < shnum) + name = f->sections[sym->st_shndx]->name; + else + continue; + +#if defined(__SH5__) + /* + * For sh64 it is possible that the target of a branch + * requires a mode switch (32 to 16 and back again). + * + * This is implied by the lsb being set in the target + * address for SHmedia mode and clear for SHcompact. + */ + val |= sym->st_other & 4; +#endif + + obj_add_symbol(f, name, j, sym->st_info, sym->st_shndx, + val, sym->st_size); + } + } + break; - if (sec->header.sh_entsize != sizeof(ElfW(Sym))) { - error_msg("symbol size mismatch: %lu != %lu", + case SHT_RELM: + if (sec->header.sh_entsize != sizeof(ElfW(RelM))) { + bb_error_msg("relocation entry size mismatch: %lu != %lu", (unsigned long) sec->header.sh_entsize, - (unsigned long) sizeof(ElfW(Sym))); + (unsigned long) sizeof(ElfW(RelM))); return NULL; } + break; + /* XXX Relocation code from modutils-2.3.19 is not here. + * Why? That's about 20 lines of code from obj/obj_load.c, + * which gets done in a second pass through the sections. + * This BusyBox insmod does similar work in obj_relocate(). */ + } + } + + return f; +} + +#ifdef CONFIG_FEATURE_INSMOD_LOADINKMEM +/* + * load the unloaded sections directly into the memory allocated by + * kernel for the module + */ - nsym = sec->header.sh_size / sizeof(ElfW(Sym)); - strtab = f->sections[sec->header.sh_link]->contents; - sym = (ElfW(Sym) *) sec->contents; +static int obj_load_progbits(FILE * fp, struct obj_file* f, char* imagebase) +{ + ElfW(Addr) base = f->baseaddr; + struct obj_section* sec; - /* Allocate space for a table of local symbols. */ - j = f->local_symtab_size = sec->header.sh_info; - f->local_symtab = xmalloc(j *= - sizeof(struct obj_symbol *)); - memset(f->local_symtab, 0, j); + for (sec = f->load_order; sec; sec = sec->load_next) { - /* Insert all symbols into the hash table. */ - for (j = 1, ++sym; j < nsym; ++j, ++sym) { - const char *name; - if (sym->st_name) - name = strtab + sym->st_name; - else - name = f->sections[sym->st_shndx]->name; + /* section already loaded? */ + if (sec->contents != NULL) + continue; - obj_add_symbol(f, name, j, sym->st_info, sym->st_shndx, - sym->st_value, sym->st_size); - } - } - break; + if (sec->header.sh_size == 0) + continue; - case SHT_RELM: - if (sec->header.sh_entsize != sizeof(ElfW(RelM))) { - error_msg("relocation entry size mismatch: %lu != %lu", - (unsigned long) sec->header.sh_entsize, - (unsigned long) sizeof(ElfW(RelM))); - return NULL; - } - break; + sec->contents = imagebase + (sec->header.sh_addr - base); + fseek(fp, sec->header.sh_offset, SEEK_SET); + if (fread(sec->contents, sec->header.sh_size, 1, fp) != 1) { + bb_error_msg("error reading ELF section data: %s\n", strerror(errno)); + return 0; } - } - return f; + } + return 1; } +#endif static void hide_special_symbols(struct obj_file *f) { static const char *const specials[] = { - "cleanup_module", - "init_module", - "kernel_version", + SPFX "cleanup_module", + SPFX "init_module", + SPFX "kernel_version", NULL }; @@ -2987,182 +3382,599 @@ static void hide_special_symbols(struct obj_file *f) for (p = specials; *p; ++p) if ((sym = obj_find_symbol(f, *p)) != NULL) sym->info = - ELFW(ST_INFO) (STB_LOCAL, ELFW(ST_TYPE) (sym->info)); + ELF_ST_INFO(STB_LOCAL, ELF_ST_TYPE(sym->info)); +} + + +#ifdef CONFIG_FEATURE_CHECK_TAINTED_MODULE +static int obj_gpl_license(struct obj_file *f, const char **license) +{ + struct obj_section *sec; + /* This list must match *exactly* the list of allowable licenses in + * linux/include/linux/module.h. Checking for leading "GPL" will not + * work, somebody will use "GPL sucks, this is proprietary". + */ + static const char * const gpl_licenses[] = { + "GPL", + "GPL v2", + "GPL and additional rights", + "Dual BSD/GPL", + "Dual MPL/GPL", + }; + + if ((sec = obj_find_section(f, ".modinfo"))) { + const char *value, *ptr, *endptr; + ptr = sec->contents; + endptr = ptr + sec->header.sh_size; + while (ptr < endptr) { + if ((value = strchr(ptr, '=')) && strncmp(ptr, "license", value-ptr) == 0) { + int i; + if (license) + *license = value+1; + for (i = 0; i < sizeof(gpl_licenses)/sizeof(gpl_licenses[0]); ++i) { + if (strcmp(value+1, gpl_licenses[i]) == 0) + return(0); + } + return(2); + } + if (strchr(ptr, '\0')) + ptr = strchr(ptr, '\0') + 1; + else + ptr = endptr; + } + } + return(1); +} + +#define TAINT_FILENAME "/proc/sys/kernel/tainted" +#define TAINT_PROPRIETORY_MODULE (1<<0) +#define TAINT_FORCED_MODULE (1<<1) +#define TAINT_UNSAFE_SMP (1<<2) +#define TAINT_URL "http://www.tux.org/lkml/#export-tainted" + +static void set_tainted(struct obj_file *f, int fd, char *m_name, + int kernel_has_tainted, int taint, const char *text1, const char *text2) +{ + char buf[80]; + int oldval; + static int first = 1; + if (fd < 0 && !kernel_has_tainted) + return; /* New modutils on old kernel */ + printf("Warning: loading %s will taint the kernel: %s%s\n", + m_name, text1, text2); + if (first) { + printf(" See %s for information about tainted modules\n", TAINT_URL); + first = 0; + } + if (fd >= 0) { + read(fd, buf, sizeof(buf)-1); + buf[sizeof(buf)-1] = '\0'; + oldval = strtoul(buf, NULL, 10); + sprintf(buf, "%d\n", oldval | taint); + write(fd, buf, strlen(buf)); + } +} + +/* Check if loading this module will taint the kernel. */ +static void check_tainted_module(struct obj_file *f, char *m_name) +{ + static const char tainted_file[] = TAINT_FILENAME; + int fd, kernel_has_tainted; + const char *ptr; + + kernel_has_tainted = 1; + if ((fd = open(tainted_file, O_RDWR)) < 0) { + if (errno == ENOENT) + kernel_has_tainted = 0; + else if (errno == EACCES) + kernel_has_tainted = 1; + else { + perror(tainted_file); + kernel_has_tainted = 0; + } + } + + switch (obj_gpl_license(f, &ptr)) { + case 0: + break; + case 1: + set_tainted(f, fd, m_name, kernel_has_tainted, TAINT_PROPRIETORY_MODULE, "no license", ""); + break; + case 2: + /* The module has a non-GPL license so we pretend that the + * kernel always has a taint flag to get a warning even on + * kernels without the proc flag. + */ + set_tainted(f, fd, m_name, 1, TAINT_PROPRIETORY_MODULE, "non-GPL license - ", ptr); + break; + default: + set_tainted(f, fd, m_name, 1, TAINT_PROPRIETORY_MODULE, "Unexpected return from obj_gpl_license", ""); + break; + } + + if (flag_force_load) + set_tainted(f, fd, m_name, 1, TAINT_FORCED_MODULE, "forced load", ""); + + if (fd >= 0) + close(fd); +} +#else /* CONFIG_FEATURE_CHECK_TAINTED_MODULE */ +#define check_tainted_module(x, y) do { } while(0); +#endif /* CONFIG_FEATURE_CHECK_TAINTED_MODULE */ + +#ifdef CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS +/* add module source, timestamp, kernel version and a symbol for the + * start of some sections. this info is used by ksymoops to do better + * debugging. + */ +static int +get_module_version(struct obj_file *f, char str[STRVERSIONLEN]) +{ +#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING + return new_get_module_version(f, str); +#else /* CONFIG_FEATURE_INSMOD_VERSION_CHECKING */ + strncpy(str, "???", sizeof(str)); + return -1; +#endif /* CONFIG_FEATURE_INSMOD_VERSION_CHECKING */ +} + +/* add module source, timestamp, kernel version and a symbol for the + * start of some sections. this info is used by ksymoops to do better + * debugging. + */ +static void +add_ksymoops_symbols(struct obj_file *f, const char *filename, + const char *m_name) +{ + static const char symprefix[] = "__insmod_"; + struct obj_section *sec; + struct obj_symbol *sym; + char *name, *absolute_filename; + char str[STRVERSIONLEN], real[PATH_MAX]; + int i, l, lm_name, lfilename, use_ksymtab, version; + struct stat statbuf; + + static const char *section_names[] = { + ".text", + ".rodata", + ".data", + ".bss", + ".sbss" + }; + + if (realpath(filename, real)) { + absolute_filename = bb_xstrdup(real); + } + else { + int save_errno = errno; + bb_error_msg("cannot get realpath for %s", filename); + errno = save_errno; + perror(""); + absolute_filename = bb_xstrdup(filename); + } + + lm_name = strlen(m_name); + lfilename = strlen(absolute_filename); + + /* add to ksymtab if it already exists or there is no ksymtab and other symbols + * are not to be exported. otherwise leave ksymtab alone for now, the + * "export all symbols" compatibility code will export these symbols later. + */ + use_ksymtab = obj_find_section(f, "__ksymtab") || !flag_export; + + if ((sec = obj_find_section(f, ".this"))) { + /* tag the module header with the object name, last modified + * timestamp and module version. worst case for module version + * is 0xffffff, decimal 16777215. putting all three fields in + * one symbol is less readable but saves kernel space. + */ + l = sizeof(symprefix)+ /* "__insmod_" */ + lm_name+ /* module name */ + 2+ /* "_O" */ + lfilename+ /* object filename */ + 2+ /* "_M" */ + 2*sizeof(statbuf.st_mtime)+ /* mtime in hex */ + 2+ /* "_V" */ + 8+ /* version in dec */ + 1; /* nul */ + name = xmalloc(l); + if (stat(absolute_filename, &statbuf) != 0) + statbuf.st_mtime = 0; + version = get_module_version(f, str); /* -1 if not found */ + snprintf(name, l, "%s%s_O%s_M%0*lX_V%d", + symprefix, m_name, absolute_filename, + (int)(2*sizeof(statbuf.st_mtime)), statbuf.st_mtime, + version); + sym = obj_add_symbol(f, name, -1, + ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE), + sec->idx, sec->header.sh_addr, 0); + if (use_ksymtab) + new_add_ksymtab(f, sym); + } + free(absolute_filename); +#ifdef _NOT_SUPPORTED_ + /* record where the persistent data is going, same address as previous symbol */ + + if (f->persist) { + l = sizeof(symprefix)+ /* "__insmod_" */ + lm_name+ /* module name */ + 2+ /* "_P" */ + strlen(f->persist)+ /* data store */ + 1; /* nul */ + name = xmalloc(l); + snprintf(name, l, "%s%s_P%s", + symprefix, m_name, f->persist); + sym = obj_add_symbol(f, name, -1, ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE), + sec->idx, sec->header.sh_addr, 0); + if (use_ksymtab) + new_add_ksymtab(f, sym); + } +#endif /* _NOT_SUPPORTED_ */ + /* tag the desired sections if size is non-zero */ + + for (i = 0; i < sizeof(section_names)/sizeof(section_names[0]); ++i) { + if ((sec = obj_find_section(f, section_names[i])) && + sec->header.sh_size) { + l = sizeof(symprefix)+ /* "__insmod_" */ + lm_name+ /* module name */ + 2+ /* "_S" */ + strlen(sec->name)+ /* section name */ + 2+ /* "_L" */ + 8+ /* length in dec */ + 1; /* nul */ + name = xmalloc(l); + snprintf(name, l, "%s%s_S%s_L%ld", + symprefix, m_name, sec->name, + (long)sec->header.sh_size); + sym = obj_add_symbol(f, name, -1, ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE), + sec->idx, sec->header.sh_addr, 0); + if (use_ksymtab) + new_add_ksymtab(f, sym); + } + } } +#endif /* CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS */ + +#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP +static void print_load_map(struct obj_file *f) +{ + struct obj_symbol *sym; + struct obj_symbol **all, **p; + struct obj_section *sec; + int i, nsyms, *loaded; + + /* Report on the section layout. */ + + printf("Sections: Size %-*s Align\n", + (int) (2 * sizeof(void *)), "Address"); + for (sec = f->load_order; sec; sec = sec->load_next) { + int a; + unsigned long tmp; + + for (a = -1, tmp = sec->header.sh_addralign; tmp; ++a) + tmp >>= 1; + if (a == -1) + a = 0; + + printf("%-15s %08lx %0*lx 2**%d\n", + sec->name, + (long)sec->header.sh_size, + (int) (2 * sizeof(void *)), + (long)sec->header.sh_addr, + a); + } +#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL + /* Quick reference which section indicies are loaded. */ + + loaded = alloca(sizeof(int) * (i = f->header.e_shnum)); + while (--i >= 0) + loaded[i] = (f->sections[i]->header.sh_flags & SHF_ALLOC) != 0; + + /* Collect the symbols we'll be listing. */ + + for (nsyms = i = 0; i < HASH_BUCKETS; ++i) + for (sym = f->symtab[i]; sym; sym = sym->next) + if (sym->secidx <= SHN_HIRESERVE + && (sym->secidx >= SHN_LORESERVE || loaded[sym->secidx])) + ++nsyms; + + all = alloca(nsyms * sizeof(struct obj_symbol *)); + + for (i = 0, p = all; i < HASH_BUCKETS; ++i) + for (sym = f->symtab[i]; sym; sym = sym->next) + if (sym->secidx <= SHN_HIRESERVE + && (sym->secidx >= SHN_LORESERVE || loaded[sym->secidx])) + *p++ = sym; + + /* And list them. */ + printf("\nSymbols:\n"); + for (p = all; p < all + nsyms; ++p) { + char type = '?'; + unsigned long value; + + sym = *p; + if (sym->secidx == SHN_ABS) { + type = 'A'; + value = sym->value; + } else if (sym->secidx == SHN_UNDEF) { + type = 'U'; + value = 0; + } else { + sec = f->sections[sym->secidx]; + + if (sec->header.sh_type == SHT_NOBITS) + type = 'B'; + else if (sec->header.sh_flags & SHF_ALLOC) { + if (sec->header.sh_flags & SHF_EXECINSTR) + type = 'T'; + else if (sec->header.sh_flags & SHF_WRITE) + type = 'D'; + else + type = 'R'; + } + value = sym->value + sec->header.sh_addr; + } + + if (ELF_ST_BIND(sym->info) == STB_LOCAL) + type = tolower(type); + + printf("%0*lx %c %s\n", (int) (2 * sizeof(void *)), value, + type, sym->name); + } +#endif +} +#endif extern int insmod_main( int argc, char **argv) { int opt; - int k_crcs; - int k_new_syscalls; int len; - char *tmp; + int k_crcs; + char *tmp, *tmp1; unsigned long m_size; ElfW(Addr) m_addr; - FILE *fp; struct obj_file *f; struct stat st; - char m_name[BUFSIZ + 1] = "\0"; + char *m_name = 0; int exit_status = EXIT_FAILURE; int m_has_modinfo; -#ifdef BB_FEATURE_INSMOD_VERSION_CHECKING - int k_version; - char k_strversion[STRVERSIONLEN]; +#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING + struct utsname uts_info; char m_strversion[STRVERSIONLEN]; - int m_version; - int m_crcs; + int m_version, m_crcs; +#endif +#ifdef CONFIG_FEATURE_CLEAN_UP + FILE *fp = 0; +#else + FILE *fp; +#endif +#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP + int flag_print_load_map = 0; #endif + int k_version = 0; + struct utsname myuname; /* Parse any options */ - while ((opt = getopt(argc, argv, "fkvxLo:")) > 0) { - switch (opt) { - case 'f': /* force loading */ - flag_force_load = 1; - break; - case 'k': /* module loaded by kerneld, auto-cleanable */ - flag_autoclean = 1; - break; - case 'v': /* verbose output */ - flag_verbose = 1; - break; - case 'x': /* do not export externs */ - flag_export = 0; - break; - case 'o': /* name the output module */ - strncpy(m_name, optarg, BUFSIZ); - break; - case 'L': /* Stub warning */ - /* This is needed for compatibility with modprobe. - * In theory, this does locking, but we don't do - * that. So be careful and plan your life around not - * loading the same module 50 times concurrently. */ - break; - default: - show_usage(); +#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP + while ((opt = getopt(argc, argv, "fkqsvxmLo:")) > 0) +#else + while ((opt = getopt(argc, argv, "fkqsvxLo:")) > 0) +#endif + { + switch (opt) { + case 'f': /* force loading */ + flag_force_load = 1; + break; + case 'k': /* module loaded by kerneld, auto-cleanable */ + flag_autoclean = 1; + break; + case 's': /* log to syslog */ + /* log to syslog -- not supported */ + /* but kernel needs this for request_module(), */ + /* as this calls: modprobe -k -s -- */ + /* so silently ignore this flag */ + break; + case 'v': /* verbose output */ + flag_verbose = 1; + break; + case 'q': /* silent */ + flag_quiet = 1; + break; + case 'x': /* do not export externs */ + flag_export = 0; + break; + case 'o': /* name the output module */ + free(m_name); + m_name = bb_xstrdup(optarg); + break; + case 'L': /* Stub warning */ + /* This is needed for compatibility with modprobe. + * In theory, this does locking, but we don't do + * that. So be careful and plan your life around not + * loading the same module 50 times concurrently. */ + break; +#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP + case 'm': /* print module load map */ + flag_print_load_map = 1; + break; +#endif + default: + bb_show_usage(); + } } - } - + if (argv[optind] == NULL) { - show_usage(); + bb_show_usage(); } /* Grab the module name */ - if ((tmp = strrchr(argv[optind], '/')) != NULL) { - tmp++; - } else { - tmp = argv[optind]; - } + tmp1 = bb_xstrdup(argv[optind]); + tmp = basename(tmp1); len = strlen(tmp); - if (len > 2 && tmp[len - 2] == '.' && tmp[len - 1] == 'o') - len -= 2; - strncpy(m_fullName, tmp, len); - if (*m_name == '\0') { - strcpy(m_name, m_fullName); + if (uname(&myuname) == 0) { + if (myuname.release[0] == '2') { + k_version = myuname.release[2] - '0'; + } + } + +#if defined(CONFIG_FEATURE_2_6_MODULES) + if (k_version > 4 && len > 3 && tmp[len - 3] == '.' && + tmp[len - 2] == 'k' && tmp[len - 1] == 'o') { + len-=3; + tmp[len] = '\0'; + } + else +#endif + if (len > 2 && tmp[len - 2] == '.' && tmp[len - 1] == 'o') { + len-=2; + tmp[len] = '\0'; + } + + +#if defined(CONFIG_FEATURE_2_6_MODULES) + if (k_version > 4) + m_fullName = bb_xasprintf("%s.ko", tmp); + else +#endif + m_fullName = bb_xasprintf("%s.o", tmp); + + if (!m_name) { + m_name = tmp; + } else { + free(tmp1); + tmp1 = 0; /* flag for free(m_name) before exit() */ } - strcat(m_fullName, ".o"); - /* Get a filedesc for the module */ + /* Get a filedesc for the module. Check we we have a complete path */ if (stat(argv[optind], &st) < 0 || !S_ISREG(st.st_mode) || (fp = fopen(argv[optind], "r")) == NULL) { - /* Hmpf. Could not open it. Search through _PATH_MODULES to find a module named m_name */ - if (recursive_action(_PATH_MODULES, TRUE, FALSE, FALSE, - findNamedModule, 0, m_fullName) == FALSE) + /* Hmm. Could not open it. First search under /lib/modules/`uname -r`, + * but do not error out yet if we fail to find it... */ + if (k_version) { /* uname succeedd */ + char *module_dir; + char *tmdn; + char real_module_dir[FILENAME_MAX]; + + tmdn = concat_path_file(_PATH_MODULES, myuname.release); + /* Jump through hoops in case /lib/modules/`uname -r` + * is a symlink. We do not want recursive_action to + * follow symlinks, but we do want to follow the + * /lib/modules/`uname -r` dir, So resolve it ourselves + * if it is a link... */ + if (realpath (tmdn, real_module_dir) == NULL) + module_dir = tmdn; + else + module_dir = real_module_dir; + recursive_action(module_dir, TRUE, FALSE, FALSE, + check_module_name_match, 0, m_fullName); + free(tmdn); + } + + /* Check if we have found anything yet */ + if (m_filename == 0 || ((fp = fopen(m_filename, "r")) == NULL)) { - if (m_filename[0] == '\0' - || ((fp = fopen(m_filename, "r")) == NULL)) + char module_dir[FILENAME_MAX]; + + free(m_filename); + m_filename = 0; + if (realpath (_PATH_MODULES, module_dir) == NULL) + strcpy(module_dir, _PATH_MODULES); + /* No module found under /lib/modules/`uname -r`, this + * time cast the net a bit wider. Search /lib/modules/ */ + if (! recursive_action(module_dir, TRUE, FALSE, FALSE, + check_module_name_match, 0, m_fullName)) { - error_msg("No module named '%s' found in '%s'", m_fullName, _PATH_MODULES); - return EXIT_FAILURE; - } - } else - error_msg_and_die("No module named '%s' found in '%s'", m_fullName, _PATH_MODULES); + if (m_filename == 0 + || ((fp = fopen(m_filename, "r")) == NULL)) + { + bb_error_msg("%s: no module by that name found", m_fullName); + goto out; + } + } else + bb_error_msg_and_die("%s: no module by that name found", m_fullName); + } } else - memcpy(m_filename, argv[optind], strlen(argv[optind])); + m_filename = bb_xstrdup(argv[optind]); + + if (flag_verbose) + printf("Using %s\n", m_filename); +#ifdef CONFIG_FEATURE_2_6_MODULES + if (k_version > 4) + { + optind--; + argv[optind + 1] = m_filename; + return insmod_ng_main(argc - optind, argv + optind); + } +#endif - if ((f = obj_load(fp)) == NULL) - perror_msg_and_die("Could not load the module"); + if ((f = obj_load(fp, LOADBITS)) == NULL) + bb_perror_msg_and_die("Could not load the module"); if (get_modinfo_value(f, "kernel_version") == NULL) m_has_modinfo = 0; else m_has_modinfo = 1; -#ifdef BB_FEATURE_INSMOD_VERSION_CHECKING +#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING /* Version correspondence? */ - - k_version = get_kernel_version(k_strversion); - if (m_has_modinfo) { - m_version = new_get_module_version(f, m_strversion); - } else { - m_version = old_get_module_version(f, m_strversion); - if (m_version == -1) { - error_msg("couldn't find the kernel version the module was " - "compiled for"); - goto out; + if (!flag_quiet) { + if (uname(&uts_info) < 0) + uts_info.release[0] = '\0'; + if (m_has_modinfo) { + m_version = new_get_module_version(f, m_strversion); + if (m_version == -1) { + bb_error_msg("couldn't find the kernel version the module was " + "compiled for"); + goto out; + } } - } - if (strncmp(k_strversion, m_strversion, STRVERSIONLEN) != 0) { - if (flag_force_load) { - error_msg("Warning: kernel-module version mismatch\n" - "\t%s was compiled for kernel version %s\n" - "\twhile this kernel is version %s", - m_filename, m_strversion, k_strversion); - } else { - error_msg("kernel-module version mismatch\n" - "\t%s was compiled for kernel version %s\n" - "\twhile this kernel is version %s.", - m_filename, m_strversion, k_strversion); - goto out; + if (strncmp(uts_info.release, m_strversion, STRVERSIONLEN) != 0) { + if (flag_force_load) { + bb_error_msg("Warning: kernel-module version mismatch\n" + "\t%s was compiled for kernel version %s\n" + "\twhile this kernel is version %s", + m_filename, m_strversion, uts_info.release); + } else { + bb_error_msg("kernel-module version mismatch\n" + "\t%s was compiled for kernel version %s\n" + "\twhile this kernel is version %s.", + m_filename, m_strversion, uts_info.release); + goto out; + } } } k_crcs = 0; -#endif /* BB_FEATURE_INSMOD_VERSION_CHECKING */ +#endif /* CONFIG_FEATURE_INSMOD_VERSION_CHECKING */ - k_new_syscalls = !query_module(NULL, 0, NULL, 0, NULL); - - if (k_new_syscalls) { -#ifdef BB_FEATURE_NEW_MODULE_INTERFACE + if (!query_module(NULL, 0, NULL, 0, NULL)) { if (!new_get_kernel_symbols()) goto out; k_crcs = new_is_kernel_checksummed(); -#else - error_msg("Not configured to support new kernels"); - goto out; -#endif } else { -#ifdef BB_FEATURE_OLD_MODULE_INTERFACE - if (!old_get_kernel_symbols(m_name)) - goto out; - k_crcs = old_is_kernel_checksummed(); -#else - error_msg("Not configured to support old kernels"); + bb_error_msg("Not configured to support old kernels"); goto out; -#endif } -#ifdef BB_FEATURE_INSMOD_VERSION_CHECKING +#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING + m_crcs = 0; if (m_has_modinfo) m_crcs = new_is_module_checksummed(f); - else - m_crcs = old_is_module_checksummed(f); if (m_crcs != k_crcs) obj_set_symbol_compare(f, ncv_strcmp, ncv_symbol_hash); -#endif /* BB_FEATURE_INSMOD_VERSION_CHECKING */ +#endif /* CONFIG_FEATURE_INSMOD_VERSION_CHECKING */ /* Let the module know about the kernel symbols. */ add_kernel_symbols(f); /* Allocate common symbols, symbol tables, and string tables. */ - if (k_new_syscalls - ? !new_create_this_module(f, m_name) - : !old_create_mod_use_count(f)) + if (!new_create_this_module(f, m_name)) { goto out; } @@ -3171,14 +3983,13 @@ extern int insmod_main( int argc, char **argv) goto out; } obj_allocate_commons(f); + check_tainted_module(f, m_name); /* done with the module name, on to the optional var=value arguments */ ++optind; if (optind < argc) { - if (m_has_modinfo - ? !new_process_module_arguments(f, argc - optind, argv + optind) - : !old_process_module_arguments(f, argc - optind, argv + optind)) + if (!new_process_module_arguments(f, argc - optind, argv + optind)) { goto out; } @@ -3187,43 +3998,148 @@ extern int insmod_main( int argc, char **argv) arch_create_got(f); hide_special_symbols(f); - if (k_new_syscalls) - new_create_module_ksymtab(f); +#ifdef CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS + add_ksymoops_symbols(f, m_filename, m_name); +#endif /* CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS */ + + new_create_module_ksymtab(f); /* Find current size of the module */ m_size = obj_load_size(f); m_addr = create_module(m_name, m_size); - if (m_addr==-1) switch (errno) { - case EEXIST: - error_msg("A module named %s already exists", m_name); - goto out; - case ENOMEM: - error_msg("Can't allocate kernel memory for module; needed %lu bytes", - m_size); - goto out; - default: - perror_msg("create_module: %s", m_name); + if (m_addr == -1) switch (errno) { + case EEXIST: + bb_error_msg("A module named %s already exists", m_name); + goto out; + case ENOMEM: + bb_error_msg("Can't allocate kernel memory for module; needed %lu bytes", + m_size); + goto out; + default: + bb_perror_msg("create_module: %s", m_name); + goto out; + } + +#if !LOADBITS + /* + * the PROGBITS section was not loaded by the obj_load + * now we can load them directly into the kernel memory + */ + if (!obj_load_progbits(fp, f, (char*)m_addr)) { + delete_module(m_name); goto out; } +#endif if (!obj_relocate(f, m_addr)) { delete_module(m_name); goto out; } - if (k_new_syscalls - ? !new_init_module(m_name, f, m_size) - : !old_init_module(m_name, f, m_size)) + if (!new_init_module(m_name, f, m_size)) { delete_module(m_name); goto out; } +#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP + if(flag_print_load_map) + print_load_map(f); +#endif + exit_status = EXIT_SUCCESS; out: - fclose(fp); +#ifdef CONFIG_FEATURE_CLEAN_UP + if(fp) + fclose(fp); + if(tmp1) { + free(tmp1); + } else { + free(m_name); + } + free(m_filename); +#endif return(exit_status); } + + +#endif + + +#ifdef CONFIG_FEATURE_2_6_MODULES + +#include +#include +#include + +/* We use error numbers in a loose translation... */ +static const char *moderror(int err) +{ + switch (err) { + case ENOEXEC: + return "Invalid module format"; + case ENOENT: + return "Unknown symbol in module"; + case ESRCH: + return "Module has wrong symbol version"; + case EINVAL: + return "Invalid parameters"; + default: + return strerror(err); + } +} + +extern int insmod_ng_main( int argc, char **argv) +{ + int i; + int fd; + long int ret; + struct stat st; + unsigned long len; + void *map; + char *filename, *options = bb_xstrdup(""); + + filename = argv[1]; + if (!filename) { + bb_show_usage(); + return -1; + } + + /* Rest is options */ + for (i = 2; i < argc; i++) { + options = xrealloc(options, strlen(options) + 2 + strlen(argv[i]) + 2); + /* Spaces handled by "" pairs, but no way of escaping quotes */ + if (strchr(argv[i], ' ')) { + strcat(options, "\""); + strcat(options, argv[i]); + strcat(options, "\""); + } else { + strcat(options, argv[i]); + } + strcat(options, " "); + } + + if ((fd = open(filename, O_RDONLY, 0)) < 0) { + bb_perror_msg_and_die("cannot open module `%s'", filename); + } + + fstat(fd, &st); + len = st.st_size; + map = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); + if (map == MAP_FAILED) { + bb_perror_msg_and_die("cannot mmap `%s'", filename); + } + + ret = syscall(__NR_init_module, map, len, options); + if (ret != 0) { + bb_perror_msg_and_die("cannot insert `%s': %s (%li)", + filename, moderror(errno), ret); + } + + return 0; +} + +#endif