diff options
author | Guillaume Cottenceau <gc@mandriva.com> | 2001-02-22 17:21:43 +0000 |
---|---|---|
committer | Guillaume Cottenceau <gc@mandriva.com> | 2001-02-22 17:21:43 +0000 |
commit | 31d44a623579fbca300f20bc751c7278c4375980 (patch) | |
tree | e54cb0772ebc6ffce9fc7ccdcc61d094751d3b54 /mdk-stage1 | |
parent | 446293819c1c265f0799036bde77f98145187ecf (diff) | |
download | drakx-31d44a623579fbca300f20bc751c7278c4375980.tar drakx-31d44a623579fbca300f20bc751c7278c4375980.tar.gz drakx-31d44a623579fbca300f20bc751c7278c4375980.tar.bz2 drakx-31d44a623579fbca300f20bc751c7278c4375980.tar.xz drakx-31d44a623579fbca300f20bc751c7278c4375980.zip |
use modutils for non Intel arch's
Diffstat (limited to 'mdk-stage1')
67 files changed, 13421 insertions, 9 deletions
diff --git a/mdk-stage1/Makefile b/mdk-stage1/Makefile index c230d4b39..530c8b9b2 100644 --- a/mdk-stage1/Makefile +++ b/mdk-stage1/Makefile @@ -26,9 +26,6 @@ top_dir = . include $(top_dir)/Makefile.common -ARCH := $(patsubst i%86,i386,$(shell uname -m)) -ARCH := $(patsubst sparc%,sparc,$(ARCH)) - CFLAGS = -Os -pipe -Wall -Werror -fomit-frame-pointer DEFS = -DVERSION=\"$(VERSION)\" @@ -73,9 +70,14 @@ MEDIAS_FRONTEND_LINK = $(FRONTEND_LINK) endif endif +ifeq (i386, $(ARCH)) +INSMOD = insmod-busybox +else +INSMOD = insmod-modutils +endif -GLIBC_STAGE1_OWN_LIBS = insmod-busybox/libinsmod.a mar/libmar.a bzlib/libbzlib.a -DIETLIBC_STAGE1_OWN_LIBS = insmod-busybox/libinsmod-DIET.a mar/libmar-DIET.a bzlib/libbzlib-DIET.a +GLIBC_STAGE1_OWN_LIBS = $(INSMOD)/libinsmod.a mar/libmar.a bzlib/libbzlib.a +DIETLIBC_STAGE1_OWN_LIBS = $(INSMOD)/libinsmod-DIET.a mar/libmar-DIET.a bzlib/libbzlib-DIET.a STAGE1_OWN_LIBS = $($(L)_STAGE1_OWN_LIBS) @@ -116,10 +118,14 @@ INIT_LIBC = $(DIETLIBC_LIBC) endif -BINS = init stage1-cdrom stage1-disk stage1-network stage1-full +BINS = init stage1-full + +ifeq (i386, $(ARCH)) +BINS += stage1-cdrom stage1-disk stage1-network +endif -DIRS = dietlibc mar insmod-busybox pci-resource bzlib +DIRS = dietlibc mar pci-resource bzlib $(INSMOD) ifeq (i386,$(ARCH)) DIRS += pcmcia endif diff --git a/mdk-stage1/Makefile.common b/mdk-stage1/Makefile.common index 9d2d3f9f3..ec8441978 100644 --- a/mdk-stage1/Makefile.common +++ b/mdk-stage1/Makefile.common @@ -14,6 +14,8 @@ # #***************************************************************************** +ARCH := $(patsubst i%86,i386,$(shell uname -m)) +ARCH := $(patsubst sparc%,sparc,$(ARCH)) #- default lib is dietlibc (honoured by main Makefile whenever possible) L = DIETLIBC diff --git a/mdk-stage1/insmod-busybox/insmod.c b/mdk-stage1/insmod-busybox/insmod.c index 95dee8855..d1027004c 100644 --- a/mdk-stage1/insmod-busybox/insmod.c +++ b/mdk-stage1/insmod-busybox/insmod.c @@ -36,6 +36,7 @@ * */ +#include "../insmod.h" #include "busybox.h" #include <stdlib.h> #include <stdio.h> diff --git a/mdk-stage1/insmod-modutils/Makefile b/mdk-stage1/insmod-modutils/Makefile new file mode 100644 index 000000000..74ebdeafb --- /dev/null +++ b/mdk-stage1/insmod-modutils/Makefile @@ -0,0 +1,61 @@ + #****************************************************************************** + # + # insmod from modutils (generic) + # + # $Id$ + # + # Copyright 1996, 1997 Linux International. + # + #***************************************************************************** + +top_dir = .. + +include $(top_dir)/Makefile.common + + +FLAGS = -c -Wall -Os -fomit-frame-pointer -I./include -D_GNU_SOURCE -DELF_MACHINE_H='"elf_$(ARCH).h"' -DARCH_$(ARCH) + + +DIRS = util obj + +all: dirs insmod libinsmod.a #libinsmod-DIET.a + +dirs: + @for n in . $(DIRS); do \ + [ "$$n" = "." ] || make -C $$n ;\ + done + +clean: + @for n in $(DIRS); do \ + (cd $$n; make clean) \ + done + rm -rf t *.o insmod libinsmod.a libinsmod-DIET.a + + +insmod: insmod-frontend.o insmod.o ./util/libutil-STANDALONE.a ./obj/libobj.a + gcc -o $@ $^ + $(STRIPCMD) $@ + +t/.create_stuff: util/libutil.a obj/libobj.a + rm -rf t + mkdir t + cd t && for e in $^; do ar -x ../$$e; done + touch t/.create_stuff + +libinsmod.a: insmod.o t/.create_stuff + ar cru $@ insmod.o t/* + ranlib $@ + +libinsmod-DIET.a: insmod-DIET.o ./util/libutil-DIET.a ./obj/libobj-DIET.a + ar cru $@ $^ + ranlib $@ + +insmod-frontend.o: insmod-frontend.c insmod.c + gcc $(FLAGS) $(GLIBC_INCLUDES) insmod-frontend.c + +insmod.o: insmod.c + gcc $(FLAGS) $(GLIBC_INCLUDES) insmod.c + +insmod-DIET.o: insmod.c + gcc $(FLAGS) $(DIETLIBC_INCLUDES) -o $@ insmod.c + diff --git a/mdk-stage1/insmod-modutils/include/config.h b/mdk-stage1/insmod-modutils/include/config.h new file mode 100644 index 000000000..11da6a523 --- /dev/null +++ b/mdk-stage1/insmod-modutils/include/config.h @@ -0,0 +1,105 @@ +/* + * Configuration file management + * + * Copyright 1994, 1995, 1996, 1997: + * Jacques Gelinas <jack@solucorp.qc.ca> + * Björn Ekwall <bj0rn@blox.se> February, March 1999 + * + * This file is part of the Linux modutils. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _CONFIG_H +#define _CONFIG_H + +#include <stdio.h> +#include <sys/utsname.h> + +#define ETC_MODULES_CONF "/etc/modules.conf" + +#define EXEC_PRE_INSTALL 0 +#define EXEC_POST_INSTALL 1 +#define EXEC_PRE_REMOVE 2 +#define EXEC_POST_REMOVE 3 +#define EXEC_INSTALL 4 +#define EXEC_REMOVE 5 + +struct PATH_TYPE { + char *type; + char *path; +}; + +struct EXEC_TYPE { + int when; + char *module; + char *cmd; +}; + +typedef struct { + char *name; + GLOB_LIST *opts; + int autoclean; +} OPT_LIST; + +/* config.c */ +extern int flag_autoclean; +extern struct utsname uts_info; +extern char *aliaslist[]; +extern struct PATH_TYPE *modpath; +extern int nmodpath; +extern struct EXEC_TYPE *execs; +extern int nexecs; +extern char *insmod_opt; +extern char *config_file; +extern char *optlist[]; +extern char *prune[]; +extern OPT_LIST *opt_list; +extern OPT_LIST *abovelist; +extern OPT_LIST *belowlist; +extern OPT_LIST *prunelist; +extern OPT_LIST *probe_list; +extern OPT_LIST *probeall_list; +extern OPT_LIST *aliases; +extern time_t config_mtime; +extern int root_check_off; /* Check modules are owned by root? */ + +/* Information about generated files */ +struct gen_files { + char *base; /* xxx in /lib/modules/`uname -r`/modules.xxx */ + char *name; /* name actually used */ + time_t mtime; +}; + +extern struct gen_files gen_file[]; +extern const int gen_file_count; +/* The enum order must match the gen_file initialization order in config.c */ +enum gen_file_enum { + GEN_GENERIC_STRINGFILE, + GEN_PCIMAPFILE, + GEN_ISAPNPMAPFILE, + GEN_USBMAPFILE, + GEN_PARPORTMAPFILE, + GEN_DEPFILE, +}; + +extern char *persistdir; + +char *fgets_strip(char *buf, int sizebuf, FILE * fin, int *lineno); +int config_read(int all, char *force_ver, char *base_dir, char *conf_file); +GLOB_LIST *config_lstmod(const char *match, const char *type, int first_only); +char *search_module_path(const char *base); + +#endif /* _CONFIG_H */ diff --git a/mdk-stage1/insmod-modutils/include/elf_alpha.h b/mdk-stage1/insmod-modutils/include/elf_alpha.h new file mode 100644 index 000000000..f1ec66f1c --- /dev/null +++ b/mdk-stage1/insmod-modutils/include/elf_alpha.h @@ -0,0 +1,10 @@ +/* Machine-specific elf macros for the Alpha. */ +#ident "$Id$" + +#define ELFCLASSM ELFCLASS64 +#define ELFDATAM ELFDATA2LSB + +#define MATCH_MACHINE(x) (x == EM_ALPHA) + +#define SHT_RELM SHT_RELA +#define Elf64_RelM Elf64_Rela diff --git a/mdk-stage1/insmod-modutils/include/elf_arm.h b/mdk-stage1/insmod-modutils/include/elf_arm.h new file mode 100644 index 000000000..f6b531e88 --- /dev/null +++ b/mdk-stage1/insmod-modutils/include/elf_arm.h @@ -0,0 +1,10 @@ +/* Machine-specific elf macros for ARM. */ +#ident "$Id$" + +#define ELFCLASSM ELFCLASS32 +#define ELFDATAM ELFDATA2LSB + +#define MATCH_MACHINE(x) (x == EM_ARM) + +#define SHT_RELM SHT_REL +#define Elf32_RelM Elf32_Rel diff --git a/mdk-stage1/insmod-modutils/include/elf_hppa.h b/mdk-stage1/insmod-modutils/include/elf_hppa.h new file mode 100644 index 000000000..9a1bd34ce --- /dev/null +++ b/mdk-stage1/insmod-modutils/include/elf_hppa.h @@ -0,0 +1,9 @@ +/* Machine-specific elf macros for HP-PA. */ + +#define ELFCLASSM ELFCLASS32 +#define ELFDATAM ELFDATA2MSB + +#define MATCH_MACHINE(x) (x == EM_PARISC) + +#define SHT_RELM SHT_RELA +#define Elf32_RelM Elf32_Rela diff --git a/mdk-stage1/insmod-modutils/include/elf_hppa64.h b/mdk-stage1/insmod-modutils/include/elf_hppa64.h new file mode 100644 index 000000000..05b201aef --- /dev/null +++ b/mdk-stage1/insmod-modutils/include/elf_hppa64.h @@ -0,0 +1,9 @@ +/* Machine-specific elf macros for HP-PA64. */ + +#define ELFCLASSM ELFCLASS64 +#define ELFDATAM ELFDATA2MSB + +#define MATCH_MACHINE(x) (x == EM_PARISC) + +#define SHT_RELM SHT_RELA +#define Elf64_RelM Elf64_Rela diff --git a/mdk-stage1/insmod-modutils/include/elf_i386.h b/mdk-stage1/insmod-modutils/include/elf_i386.h new file mode 100644 index 000000000..c6c2d326e --- /dev/null +++ b/mdk-stage1/insmod-modutils/include/elf_i386.h @@ -0,0 +1,10 @@ +/* Machine-specific elf macros for i386 et al. */ +#ident "$Id$" + +#define ELFCLASSM ELFCLASS32 +#define ELFDATAM ELFDATA2LSB + +#define MATCH_MACHINE(x) (x == EM_386) + +#define SHT_RELM SHT_REL +#define Elf32_RelM Elf32_Rel diff --git a/mdk-stage1/insmod-modutils/include/elf_ia64.h b/mdk-stage1/insmod-modutils/include/elf_ia64.h new file mode 100644 index 000000000..b4d902956 --- /dev/null +++ b/mdk-stage1/insmod-modutils/include/elf_ia64.h @@ -0,0 +1,9 @@ +/* Machine-specific elf macros for ia64. */ + +#define ELFCLASSM ELFCLASS64 +#define ELFDATAM ELFDATA2LSB + +#define MATCH_MACHINE(x) (x == EM_IA_64) + +#define SHT_RELM SHT_RELA +#define Elf64_RelM Elf64_Rela diff --git a/mdk-stage1/insmod-modutils/include/elf_m68k.h b/mdk-stage1/insmod-modutils/include/elf_m68k.h new file mode 100644 index 000000000..817fc8f2c --- /dev/null +++ b/mdk-stage1/insmod-modutils/include/elf_m68k.h @@ -0,0 +1,10 @@ +/* Machine-specific elf macros for m68k. */ +#ident "$Id$" + +#define ELFCLASSM ELFCLASS32 +#define ELFDATAM ELFDATA2MSB + +#define MATCH_MACHINE(x) (x == EM_68K) + +#define SHT_RELM SHT_RELA +#define Elf32_RelM Elf32_Rela diff --git a/mdk-stage1/insmod-modutils/include/elf_mips.h b/mdk-stage1/insmod-modutils/include/elf_mips.h new file mode 100644 index 000000000..8a0430216 --- /dev/null +++ b/mdk-stage1/insmod-modutils/include/elf_mips.h @@ -0,0 +1,24 @@ +/* Machine-specific elf macros for MIPS. */ +#ident "$Id$" + +#define ELFCLASSM ELFCLASS32 +#ifdef __MIPSEB__ +#define ELFDATAM ELFDATA2MSB +#endif +#ifdef __MIPSEL__ +#define ELFDATAM ELFDATA2LSB +#endif + +/* 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 MATCH_MACHINE(x) (x == EM_MIPS || x == EM_MIPS_RS3_LE) + +#define SHT_RELM SHT_REL +#define Elf32_RelM Elf32_Rel diff --git a/mdk-stage1/insmod-modutils/include/elf_ppc.h b/mdk-stage1/insmod-modutils/include/elf_ppc.h new file mode 100644 index 000000000..71596de8c --- /dev/null +++ b/mdk-stage1/insmod-modutils/include/elf_ppc.h @@ -0,0 +1,10 @@ +/* Machine-specific elf macros for the PowerPC. */ +#ident "$Id$" + +#define ELFCLASSM ELFCLASS32 +#define ELFDATAM ELFDATA2MSB + +#define MATCH_MACHINE(x) (x == EM_PPC) + +#define SHT_RELM SHT_RELA +#define Elf32_RelM Elf32_Rela diff --git a/mdk-stage1/insmod-modutils/include/elf_s390.h b/mdk-stage1/insmod-modutils/include/elf_s390.h new file mode 100644 index 000000000..547d66b83 --- /dev/null +++ b/mdk-stage1/insmod-modutils/include/elf_s390.h @@ -0,0 +1,10 @@ +/* Machine-specific elf macros for i386 et al. */ +#ident "$Id$" + +#define ELFCLASSM ELFCLASS32 +#define ELFDATAM ELFDATA2MSB + +#define MATCH_MACHINE(x) (x == EM_S390) + +#define SHT_RELM SHT_RELA +#define Elf32_RelM Elf32_Rela diff --git a/mdk-stage1/insmod-modutils/include/elf_sparc.h b/mdk-stage1/insmod-modutils/include/elf_sparc.h new file mode 100644 index 000000000..9b5c348d9 --- /dev/null +++ b/mdk-stage1/insmod-modutils/include/elf_sparc.h @@ -0,0 +1,10 @@ +/* Machine-specific elf macros for the Sparc. */ +#ident "$Id$" + +#define ELFCLASSM ELFCLASS32 +#define ELFDATAM ELFDATA2MSB + +#define MATCH_MACHINE(x) (x == EM_SPARC) + +#define SHT_RELM SHT_RELA +#define Elf32_RelM Elf32_Rela diff --git a/mdk-stage1/insmod-modutils/include/elf_sparc64.h b/mdk-stage1/insmod-modutils/include/elf_sparc64.h new file mode 100644 index 000000000..158edd7e1 --- /dev/null +++ b/mdk-stage1/insmod-modutils/include/elf_sparc64.h @@ -0,0 +1,27 @@ +/* Machine-specific elf macros for the Sparc. */ +#ident "$Id$" + +#define ELFCLASSM ELFCLASS64 +#define ELFDATAM ELFDATA2MSB + +#ifndef EM_SPARCV9 +#define EM_SPARCV9 43 +#endif +#ifndef EM_SPARC64 +#define EM_SPARC64 11 +#endif +#define MATCH_MACHINE(x) ((x) == EM_SPARCV9 || (x) == EM_SPARC64) + +#define SHT_RELM SHT_RELA +#define Elf64_RelM Elf64_Rela + +#ifndef ELF64_R_SYM +#define ELF64_R_SYM(x) ((x) >> 32) +#define ELF64_R_TYPE(x) ((unsigned)(x)) +#endif + +#ifndef ELF64_ST_BIND +#define ELF64_ST_BIND(x) ((x) >> 4) +#define ELF64_ST_TYPE(x) ((x) & 0xf) +#endif + diff --git a/mdk-stage1/insmod-modutils/include/kallsyms.h b/mdk-stage1/insmod-modutils/include/kallsyms.h new file mode 100644 index 000000000..9748873cf --- /dev/null +++ b/mdk-stage1/insmod-modutils/include/kallsyms.h @@ -0,0 +1,131 @@ +/* kallsyms headers + Copyright 2000 Keith Owens <kaos@ocs.com.au> + + This file is part of the Linux modutils. It is exported to kernel + space so debuggers can access the kallsyms data. + + The kallsyms data contains all the non-stack symbols from a kernel + or a module. The kernel symbols are held between __start___kallsyms + and __stop___kallsyms. The symbols for a module are accessed via + the struct module chain which is based at module_list. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ident "$Id$" + +#ifndef MODUTILS_KALLSYMS_H +#define MODUTILS_KALLSYMS_H 1 + +/* Have to (re)define these ElfW entries here because external kallsyms + * code does not have access to modutils/include/obj.h. This code is + * included from user spaces tools (modutils) and kernel, they need + * different includes. + */ + +#ifndef ELFCLASS32 +#ifdef __KERNEL__ +#include <linux/elf.h> +#else /* __KERNEL__ */ +#include <elf.h> +#endif /* __KERNEL__ */ +#endif /* ELFCLASS32 */ + +#ifndef ELFCLASSM +#define ELFCLASSM ELF_CLASS +#endif + +#ifndef ElfW +# if ELFCLASSM == ELFCLASS32 +# define ElfW(x) Elf32_ ## x +# define ELFW(x) ELF32_ ## x +# else +# define ElfW(x) Elf64_ ## x +# define ELFW(x) ELF64_ ## x +# endif +#endif + +/* Format of data in the kallsyms section. + * Most of the fields are small numbers but the total size and all + * offsets can be large so use the 32/64 bit types for these fields. + * + * Do not use sizeof() on these structures, modutils may be using extra + * fields. Instead use the size fields in the header to access the + * other bits of data. + */ + +struct kallsyms_header { + int size; /* Size of this header */ + ElfW(Word) total_size; /* Total size of kallsyms data */ + int sections; /* Number of section entries */ + ElfW(Off) section_off; /* Offset to first section entry */ + int section_size; /* Size of one section entry */ + int symbols; /* Number of symbol entries */ + ElfW(Off) symbol_off; /* Offset to first symbol entry */ + int symbol_size; /* Size of one symbol entry */ + ElfW(Off) string_off; /* Offset to first string */ + ElfW(Addr) start; /* Start address of first section */ + ElfW(Addr) end; /* End address of last section */ +}; + +struct kallsyms_section { + ElfW(Addr) start; /* Start address of section */ + ElfW(Word) size; /* Size of this section */ + ElfW(Off) name_off; /* Offset to section name */ + ElfW(Word) flags; /* Flags from section */ +}; + +struct kallsyms_symbol { + ElfW(Off) section_off; /* Offset to section that owns this symbol */ + ElfW(Addr) symbol_addr; /* Address of symbol */ + ElfW(Off) name_off; /* Offset to symbol name */ +}; + +#define KALLSYMS_SEC_NAME "__kallsyms" +#define KALLSYMS_IDX 2 /* obj_kallsyms creates kallsyms as section 2 */ + +#define kallsyms_next_sec(h,s) \ + ((s) = (struct kallsyms_section *)((char *)(s) + (h)->section_size)) +#define kallsyms_next_sym(h,s) \ + ((s) = (struct kallsyms_symbol *)((char *)(s) + (h)->symbol_size)) + +int kallsyms_symbol_to_address( + const char *name, /* Name to lookup */ + unsigned long *token, /* Which module to start with */ + const char **mod_name, /* Set to module name or "kernel" */ + unsigned long *mod_start, /* Set to start address of module */ + unsigned long *mod_end, /* Set to end address of module */ + const char **sec_name, /* Set to section name */ + unsigned long *sec_start, /* Set to start address of section */ + unsigned long *sec_end, /* Set to end address of section */ + const char **sym_name, /* Set to full symbol name */ + unsigned long *sym_start, /* Set to start address of symbol */ + unsigned long *sym_end /* Set to end address of symbol */ + ); + +int kallsyms_address_to_symbol( + unsigned long address, /* Address to lookup */ + const char **mod_name, /* Set to module name */ + unsigned long *mod_start, /* Set to start address of module */ + unsigned long *mod_end, /* Set to end address of module */ + const char **sec_name, /* Set to section name */ + unsigned long *sec_start, /* Set to start address of section */ + unsigned long *sec_end, /* Set to end address of section */ + const char **sym_name, /* Set to full symbol name */ + unsigned long *sym_start, /* Set to start address of symbol */ + unsigned long *sym_end /* Set to end address of symbol */ + ); + +#endif /* kallsyms.h */ diff --git a/mdk-stage1/insmod-modutils/include/kerneld.h b/mdk-stage1/insmod-modutils/include/kerneld.h new file mode 100644 index 000000000..3bc2c9d42 --- /dev/null +++ b/mdk-stage1/insmod-modutils/include/kerneld.h @@ -0,0 +1,46 @@ +/* Definitions for the Linux kerneld SYSV IPC interface. + This file was part of the Linux kernel, and so is covered by the GPL. */ + +#ifndef MODUTILS_KERNELD_H +#define MODUTILS_KERNELD_H + +#ident "$Id$" + +#define KERNELD_SYSTEM 1 +#define KERNELD_REQUEST_MODULE 2 /* "insmod" */ +#define KERNELD_RELEASE_MODULE 3 /* "rmmod" */ +#define KERNELD_DELAYED_RELEASE_MODULE 4 /* "rmmod" */ +#define KERNELD_CANCEL_RELEASE_MODULE 5 /* "rmmod" */ +#define KERNELD_REQUEST_ROUTE 6 /* net/ipv4/route.c */ +#define KERNELD_BLANKER 7 /* drivers/char/console.c */ +#define KERNELD_PNP 8 /* drivers/pnp/kerneld.c */ +#define KERNELD_ARP 256 /* net/ipv4/arp.c */ + +#ifdef NEW_KERNELD_PROTOCOL +# define OLDIPC_KERNELD 00040000 /* old kerneld message channel */ +# define IPC_KERNELD 00140000 /* new kerneld message channel */ +# define KDHDR (sizeof(long) + sizeof(short) + sizeof(short)) +# define NULL_KDHDR 0, 2, 0 +#else /* NEW_KERNELD_PROTOCOL */ +# define IPC_KERNELD 00040000 +# define KDHDR (sizeof(long)) +# define NULL_KDHDR 0 +#endif /* NEW_KERNELD_PROTOCOL */ + +#define KERNELD_MAXCMD 0x7ffeffff +#define KERNELD_MINSEQ 0x7fff0000 /* "commands" legal up to 0x7ffeffff */ +#define KERNELD_WAIT 0x80000000 +#define KERNELD_NOWAIT 0 + +struct kerneld_msg + { + long mtype; + long id; +#ifdef NEW_KERNELD_PROTOCOL + short version; + short pid; +#endif /* NEW_KERNELD_PROTOCOL */ + char text[1]; + }; + +#endif /* kerneld.h */ diff --git a/mdk-stage1/insmod-modutils/include/modstat.h b/mdk-stage1/insmod-modutils/include/modstat.h new file mode 100644 index 000000000..b8a58eeae --- /dev/null +++ b/mdk-stage1/insmod-modutils/include/modstat.h @@ -0,0 +1,55 @@ +/* + * For kernel module status and information + * + * Add module_name_list and l_module_name_list. + * Keith Owens <kaos@ocs.com.au> November 1999. + * Björn Ekwall <bj0rn@blox.se> February 1999. + * + * This file is part of the Linux modutils. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef _KERNEL_H +#define _KERNEL_H + +#define K_SYMBOLS 1 /* Want info about symbols */ +#define K_INFO 2 /* Want extended module info */ +#define K_REFS 4 /* Want info about references */ + +struct module_stat { + char *name; + unsigned long addr; + unsigned long modstruct; /* COMPAT_2_0! *//* depends on architecture? */ + unsigned long size; + unsigned long flags; + long usecount; + size_t nsyms; + struct module_symbol *syms; + size_t nrefs; + struct module_stat **refs; + unsigned long status; +}; + +extern struct module_stat *module_stat; +extern size_t n_module_stat; +extern char *module_name_list; +extern size_t l_module_name_list; +extern struct module_symbol *ksyms; +extern size_t nksyms; +extern int k_new_syscalls; + +int get_kernel_info(int type); + +#endif /* _KERNEL_H */ diff --git a/mdk-stage1/insmod-modutils/include/module.h b/mdk-stage1/insmod-modutils/include/module.h new file mode 100644 index 000000000..db49ab2b2 --- /dev/null +++ b/mdk-stage1/insmod-modutils/include/module.h @@ -0,0 +1,210 @@ +/* Definitions for the Linux module syscall interface. + Copyright 1996, 1997 Linux International. + + Contributed by Richard Henderson <rth@tamu.edu> + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + + +#ifndef MODUTILS_MODULE_H +#define MODUTILS_MODULE_H 1 + +#ident "$Id$" + +/* 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. */ +#define 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 *); + +/*======================================================================*/ +/* For sizeof() which are related to the module platform and not to the + environment isnmod is running in, use sizeof_xx instead of sizeof(xx). */ + +#define tgt_sizeof_char sizeof(char) +#define tgt_sizeof_short sizeof(short) +#define tgt_sizeof_int sizeof(int) +#define tgt_sizeof_long sizeof(long) +#define tgt_sizeof_char_p sizeof(char *) +#define tgt_sizeof_void_p sizeof(void *) +#define tgt_long long +#define tgt_long_fmt "l" + +/* This assumes that long long on a 32 bit system is equivalent to long on the + * equivalent 64 bit system. Also that void and char pointers are 8 bytes on + * all 64 bit systems. Add per system tweaks if it ever becomes necessary. + */ +#if defined(COMMON_3264) && defined(ONLY_64) +#undef tgt_long +#undef tgt_long_fmt +#undef tgt_sizeof_long +#undef tgt_sizeof_char_p +#undef tgt_sizeof_void_p +#define tgt_long long long +#define tgt_long_fmt "ll" +#define tgt_sizeof_long 8 +#define tgt_sizeof_char_p 8 +#define tgt_sizeof_void_p 8 +#endif + +/*======================================================================*/ +/* The structures used in Linux 2.1 onwards. */ + +/* Note: module_symbol does not use tgt_long intentionally */ +struct module_symbol +{ + unsigned long value; + unsigned long name; +}; + +struct module_ref +{ + unsigned tgt_long dep; /* kernel addresses */ + unsigned tgt_long ref; + unsigned tgt_long next_ref; +}; + +struct 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; +#ifdef __alpha__ + unsigned tgt_long gp; +#endif + /* Everything after here is extension. */ + unsigned tgt_long read_start; /* Read data from existing module */ + unsigned tgt_long read_end; + unsigned tgt_long can_unload; + unsigned tgt_long runsize; + unsigned tgt_long kallsyms_start; + unsigned tgt_long kallsyms_end; + unsigned tgt_long archdata_start; + unsigned tgt_long archdata_end; + unsigned tgt_long kernel_data; +}; + +struct module_info +{ + unsigned long addr; + unsigned long size; + unsigned long flags; + long usecount; +}; + +/* Bits of module.flags. */ +#define NEW_MOD_RUNNING 1 +#define NEW_MOD_DELETED 2 +#define NEW_MOD_AUTOCLEAN 4 +#define NEW_MOD_VISITED 8 +#define NEW_MOD_USED_ONCE 16 +#define NEW_MOD_INITIALIZING 64 + +int sys_init_module(const char *name, const struct module *); +int query_module(const char *name, int which, void *buf, size_t bufsize, + size_t *ret); + +/* Values for query_module's which. */ + +#define QM_MODULES 1 +#define QM_DEPS 2 +#define QM_REFS 3 +#define QM_SYMBOLS 4 +#define QM_INFO 5 + +/*======================================================================*/ +/* The system calls unchanged between 2.0 and 2.1. */ + +unsigned long create_module(const char *, size_t); +int delete_module(const char *); + +/* In safe mode the last parameter is forced to be a module name and meta + * expansion is not allowed on that name. + */ +extern unsigned int safemode; + +#endif /* module.h */ diff --git a/mdk-stage1/insmod-modutils/include/obj.h b/mdk-stage1/insmod-modutils/include/obj.h new file mode 100644 index 000000000..b140c65c3 --- /dev/null +++ b/mdk-stage1/insmod-modutils/include/obj.h @@ -0,0 +1,275 @@ +/* Elf object file loading and relocation routines. + Copyright 1996, 1997 Linux International. + + Contributed by Richard Henderson <rth@tamu.edu> + obj_free() added by Björn Ekwall <bj0rn@blox.se> March 1999 + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + + +#ifndef MODUTILS_OBJ_H +#define MODUTILS_OBJ_H 1 + +#ident "$Id$" + +/* The relocatable object is manipulated using elfin types. */ + +#include <stdio.h> +#include <elf.h> +#include ELF_MACHINE_H + +#ifndef ElfW +# if ELFCLASSM == ELFCLASS32 +# define ElfW(x) Elf32_ ## x +# define ELFW(x) ELF32_ ## x +# else +# define ElfW(x) Elf64_ ## x +# define ELFW(x) ELF64_ ## x +# endif +#endif + +#if defined(COMMON_3264) && defined(ONLY_32) +# define ObjW(x) obj32_ ## x +#else +# if defined(COMMON_3264) && defined(ONLY_64) +# define ObjW(x) obj64_ ## x +# else +# define ObjW(x) obj_ ## x +# endif +#endif + +/* For some reason this is missing from lib5. */ +#ifndef ELF32_ST_INFO +# define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) +#endif + +#ifndef ELF64_ST_INFO +# define ELF64_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) +#endif + +struct obj_string_patch_struct; +struct obj_symbol_patch_struct; + +struct obj_section +{ + 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 r_type; /* relocation type */ +}; + +/* Hardcode the hash table size. We shouldn't be needing so many + symbols that we begin to degrade performance, and we get a big win + by giving the compiler a constant divisor. */ + +#define HASH_BUCKETS 521 + +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_struct *string_patches; + struct obj_symbol_patch_struct *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]; + const char *filename; + char *persist; +}; + +enum obj_reloc +{ + obj_reloc_ok, + obj_reloc_overflow, + obj_reloc_dangerous, + obj_reloc_unhandled, + obj_reloc_constant_gp +}; + +struct obj_string_patch_struct +{ + struct obj_string_patch_struct *next; + int reloc_secidx; + ElfW(Addr) reloc_offset; + ElfW(Addr) string_offset; +}; + +struct obj_symbol_patch_struct +{ + struct obj_symbol_patch_struct *next; + int reloc_secidx; + ElfW(Addr) reloc_offset; + struct obj_symbol *sym; +}; + + +/* Generic object manipulation routines. */ + +#define obj_elf_hash ObjW(elf_hash) +#define obj_elf_hash_n ObjW(elf_hash_n) +#define obj_add_symbol ObjW(add_symbol) +#define obj_find_symbol ObjW(find_symbol) +#define obj_symbol_final_value ObjW(symbol_final_value) +#define obj_set_symbol_compare ObjW(set_symbol_compare) +#define obj_find_section ObjW(find_section) +#define obj_insert_section_load_order ObjW(insert_section_load_order) +#define obj_create_alloced_section ObjW(create_alloced_section) +#define obj_create_alloced_section_first \ + ObjW(create_alloced_section_first) +#define obj_extend_section ObjW(extend_section) +#define obj_string_patch ObjW(string_patch) +#define obj_symbol_patch ObjW(symbol_patch) +#define obj_check_undefineds ObjW(check_undefineds) +#define obj_clear_undefineds ObjW(clear_undefineds) +#define obj_allocate_commons ObjW(allocate_commons) +#define obj_load_size ObjW(load_size) +#define obj_relocate ObjW(relocate) +#define obj_load ObjW(load) +#define obj_free ObjW(free) +#define obj_create_image ObjW(create_image) +#define obj_addr_to_native_ptr ObjW(addr_to_native_ptr) +#define obj_native_ptr_to_addr ObjW(native_ptr_to_addr) +#define arch_new_file ObjW(arch_new_file) +#define arch_new_section ObjW(arch_new_section) +#define arch_new_symbol ObjW(arch_new_symbol) +#define arch_apply_relocation ObjW(arch_apply_relocation) +#define arch_create_got ObjW(arch_create_got) +#define arch_init_module ObjW(arch_init_module) +#define arch_load_proc_section ObjW(arch_load_proc_section) +#define arch_finalize_section_address ObjW(arch_finalize_section_address) +#define arch_archdata ObjW(arch_archdata) + +unsigned long obj_elf_hash (const char *); + +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, + const char *name); + +ElfW(Addr) obj_symbol_final_value (struct obj_file *f, + struct obj_symbol *sym); + +void obj_set_symbol_compare (struct obj_file *f, + int (*cmp)(const char *, const char *), + unsigned long (*hash)(const char *)); + +struct obj_section *obj_find_section (struct obj_file *f, + const char *name); + +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, + const char *name, + unsigned long align, + unsigned long size); + +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); + +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, + struct obj_symbol *sym); + +int obj_check_undefineds (struct obj_file *f, int quiet); + +void obj_clear_undefineds (struct obj_file *f); + +void obj_allocate_commons (struct obj_file *f); + +unsigned long obj_load_size (struct obj_file *f); + +int obj_relocate (struct obj_file *f, ElfW(Addr) base); + +struct obj_file *obj_load (int f, Elf32_Half e_type, const char *filename); + +void obj_free (struct obj_file *f); + +int obj_create_image (struct obj_file *f, char *image); + +int obj_kallsyms (struct obj_file *fin, struct obj_file **fout); + +/* Architecture specific manipulation routines. */ + +struct obj_file *arch_new_file (void); + +struct obj_section *arch_new_section (void); + +struct obj_symbol *arch_new_symbol (void); + +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); + +struct module; +int arch_init_module (struct obj_file *f, struct module *); + +int arch_load_proc_section (struct obj_section *sec, int fp); + +int arch_finalize_section_address (struct obj_file *f, ElfW(Addr) base); + +int arch_archdata (struct obj_file *fin, struct obj_section *sec); + +#define ARCHDATA_SEC_NAME "__archdata" + +/* Pointers in objects can be 32 or 64 bit */ +union obj_ptr_4 { + Elf32_Word addr; + void *ptr; +}; +union obj_ptr_8 { + Elf64_Xword addr; + void *ptr; +}; + +void *obj_addr_to_native_ptr(ElfW(Addr)); + +ElfW(Addr) obj_native_ptr_to_addr(void *); + +#endif /* obj.h */ diff --git a/mdk-stage1/insmod-modutils/include/util.h b/mdk-stage1/insmod-modutils/include/util.h new file mode 100644 index 000000000..b2e4a67c3 --- /dev/null +++ b/mdk-stage1/insmod-modutils/include/util.h @@ -0,0 +1,101 @@ +/* Miscelaneous utility functions. + Copyright 1996, 1997 Linux International. + + Contributed by Richard Henderson <rth@tamu.edu> + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + + +#ifndef MODUTILS_UTIL_H +#define MODUTILS_UTIL_H 1 + +#ident "$Id$" + +#include <stdio.h> +#include <sys/stat.h> + +#define SHELL_META "&();|<>$`\"'\\!{}[]~=+:?*" /* Sum of bj0rn and Debian */ + +void *xmalloc(size_t); +void *xrealloc(void *, size_t); +char *xstrdup(const char *); +char *xstrcat(char *, const char *, size_t); +int xsystem(const char *, char *const[]); +int arch64(void); + +typedef int (*xftw_func_t)(const char *, const struct stat *); +extern int xftw(const char *directory, xftw_func_t); + +/* Error logging */ +extern int log; +extern int errors; +extern const char *error_file; + +extern int flag_verbose; +extern void verbose(const char *ctl,...); + +void error(const char *fmt, ...) +#ifdef __GNUC__ + __attribute__((format(printf, 1, 2))) +#endif + ; + +void lprintf(const char *fmt, ...) +#ifdef __GNUC__ + __attribute__((format(printf, 1, 2))) +#endif + ; + +void setsyslog(const char *program); + +/* + * Generic globlist <bj0rn@blox.se> + */ +typedef struct { + int pathc; /* Count of paths matched so far */ + char **pathv; /* List of matched pathnames. */ +} GLOB_LIST; +int meta_expand(char *pt, GLOB_LIST *g, char *base_dir, char *version, int type); +#define ME_BUILTIN_COMMAND 1 +#define ME_SHELL_COMMAND 2 +#define ME_GLOB 4 +#define ME_ALL (ME_GLOB|ME_SHELL_COMMAND|ME_BUILTIN_COMMAND) + +extern void snap_shot(const char *module_name, int number); +extern void snap_shot_log(const char *fmt,...); + +#ifdef CONFIG_USE_ZLIB +int gzf_open(const char *name, int mode); +int gzf_read(int fd, void *buf, size_t count); +off_t gzf_lseek(int fd, off_t offset, int whence); +void gzf_close(int fd); + +#else /* ! CONFIG_USE_ZLIB */ + +#include <unistd.h> + +#define gzf_open open +#define gzf_read read +#define gzf_lseek lseek +#define gzf_close close + +#endif /* CONFIG_USE_ZLIB */ + +#define SYMPREFIX "__insmod_"; +extern const char symprefix[10]; /* Must be sizeof(SYMPREFIX), including nul */ + +#endif /* util.h */ diff --git a/mdk-stage1/insmod-modutils/include/version.h b/mdk-stage1/insmod-modutils/include/version.h new file mode 100644 index 000000000..51ca0eb29 --- /dev/null +++ b/mdk-stage1/insmod-modutils/include/version.h @@ -0,0 +1 @@ +#define MODUTILS_VERSION "2.4.2" diff --git a/mdk-stage1/insmod-modutils/insmod-frontend.c b/mdk-stage1/insmod-modutils/insmod-frontend.c new file mode 100644 index 000000000..2b3aa1acb --- /dev/null +++ b/mdk-stage1/insmod-modutils/insmod-frontend.c @@ -0,0 +1,24 @@ +/* + * Guillaume Cottenceau (gc@mandrakesoft.com) + * + * Copyright 2000 MandrakeSoft + * + * This software may be freely redistributed under the terms of the GNU + * public license. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <stdio.h> + + +int insmod_main( int argc, char **argv); + +int main( int argc, char **argv) +{ + printf("Using insmod provided by modutils.\n"); + return insmod_main(argc, argv); +} diff --git a/mdk-stage1/insmod-modutils/insmod.c b/mdk-stage1/insmod-modutils/insmod.c new file mode 100644 index 000000000..359b28837 --- /dev/null +++ b/mdk-stage1/insmod-modutils/insmod.c @@ -0,0 +1,1964 @@ +/* Insert a module into a running kernel. + Copyright 1996, 1997 Linux International. + + New implementation contributed by Richard Henderson <rth@tamu.edu> + Based on original work by Bjorn Ekwall <bj0rn@blox.se> + Restructured (and partly rewritten) by: + Björn Ekwall <bj0rn@blox.se> February 1999 + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + /* + Fixes: + + Adjust module size for mod_use_count in old_init_module: + B. James Phillippe <bryan@terran.org> + + Merged modprobe + many fixes: Björn Ekwall <bj0rn@blox.se> February 1999 + SMP "friendliness" (and -P): Bill Zumach <zumach+@transarc.com> + + Ksymoops support: Keith Owens <kaos@ocs.com.au> August 1999. + + Add -r flag: Keith Owens <kaos@ocs.com.au> October 1999. + + More flexible recognition of the way the utility was called. + Suggested by Stepan Kasal, implemented in a different way by Keith + Owens <kaos@ocs.com.au> December 1999. + + Rationalize common code for 32/64 bit architectures. + Keith Owens <kaos@ocs.com.au> December 1999. + Add arch64(). + Keith Owens <kaos@ocs.com.au> December 1999. + kallsyms support + Keith Owens <kaos@ocs.com.au> April 2000. + archdata support + Keith Owens <kaos@ocs.com.au> August 2000. + Add insmod -O, move print map before sys_init_module. + Keith Owens <kaos@ocs.com.au> October 2000. + Add insmod -S. + Keith Owens <kaos@ocs.com.au> November 2000. + Add persistent data support. + Keith Owens <kaos@ocs.com.au> November 2000. + */ + +#ident "$Id$" + +#include "../insmod.h" +#include <sys/types.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +//#include <alloca.h> provided by stdlib +#include <limits.h> +#include <ctype.h> +#include <errno.h> +#include <stddef.h> +#include <getopt.h> +#include <sys/stat.h> +#include <sys/file.h> + +#include "module.h" +#include "obj.h" +#include "kallsyms.h" +#include "util.h" +#include "version.h" + +#include "modstat.h" +#include "config.h" + +#define STRVERSIONLEN 32 + +/*======================================================================*/ + +static int flag_force_load = 0; +static int flag_silent_probe = 0; +static int flag_export = 1; +static int flag_load_map = 0; +static int flag_ksymoops = 1; + +static int n_ext_modules_used; +static int m_has_modinfo; + +extern int insmod_main(int argc, char **argv); +extern int insmod_main_32(int argc, char **argv); +extern int insmod_main_64(int argc, char **argv); +extern int modprobe_main(int argc, char **argv); +extern int rmmod_main(int argc, char **argv); +extern int ksyms_main(int argc, char **argv); +extern int lsmod_main(int argc, char **argv); +extern int kallsyms_main(int argc, char **argv); + +/*======================================================================*/ + +/* Get the kernel version in the canonical integer form. */ + +static int get_kernel_version(char str[STRVERSIONLEN]) +{ + char *p, *q; + int a, b, c; + + 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; +} + +/* String comparison for non-co-versioned kernel and module. + * prefix should be the same as used by genksyms for this kernel. + */ +static char *ncv_prefix = NULL; /* Overridden by --prefix option */ +static int ncv_plen = 0; + +/* Only set prefix once. If set by the user, use it. If not set by the + * user, look for a well known kernel symbol and derive the prefix from + * there. Otherwise set the prefix depending on whether uts_info + * includes SMP or not for backwards compatibility. + */ +static void set_ncv_prefix(char *prefix) +{ + static char derived_prefix[256]; + static const char *well_known_symbol[] = { "get_module_symbol_R", + "inter_module_get_R", + }; + struct module_symbol *s; + int i, j, l, m, pl; + const char *name; + char *p; + + if (ncv_prefix) + return; + + if (prefix) + ncv_prefix = prefix; + else { + /* Extract the prefix (if any) from well known symbols */ + for (i = 0, s = ksyms; i < nksyms; ++i, ++s) { + name = (char *) s->name; + l = strlen(name); + for (j = 0; j < sizeof(well_known_symbol)/sizeof(well_known_symbol[0]); ++j) { + m = strlen(well_known_symbol[j]); + if (m + 8 > l || + strncmp(name, well_known_symbol[j], m)) + continue; + pl = l - m - 8; + if (pl > sizeof(derived_prefix)-1) + continue; /* Prefix is wrong length */ + /* Must end with 8 hex digits */ + (void) strtoul(name+l-8, &p, 16); + if (*p == 0) { + strncpy(derived_prefix, name+m, pl); + *(derived_prefix+pl) = '\0'; + ncv_prefix = derived_prefix; + break; + } + } + } + } + if (!ncv_prefix) { + p = strchr(uts_info.version, ' '); + if (p && *(++p) && !strncmp(p, "SMP ", 4)) + ncv_prefix = "smp_"; + else + ncv_prefix = ""; + } + ncv_plen = strlen(ncv_prefix); + if (flag_verbose) + lprintf("Symbol version prefix '%s'", ncv_prefix); +} + +static int ncv_strcmp(const char *a, const char *b) +{ + size_t alen = strlen(a), blen = strlen(b); + + if (blen == alen + 10 + ncv_plen && + b[alen] == '_' && + b[alen + 1] == 'R' && + !(ncv_plen && strncmp(b + alen + 2, ncv_prefix, ncv_plen))) { + return strncmp(a, b, alen); + } else if (alen == blen + 10 + ncv_plen && + a[blen] == '_' && a[blen + 1] == 'R' && + !(ncv_plen && strncmp(a + blen + 2, ncv_prefix, ncv_plen))) { + return strncmp(a, b, blen); + } else + return strcmp(a, b); +} + +/* + * String hashing for non-co-versioned kernel and module. + * Here we are simply forced to drop the crc from the hash. + */ +static unsigned long ncv_symbol_hash(const char *str) +{ + size_t len = strlen(str); + + if (len > 10 + ncv_plen && + str[len - 10 - ncv_plen] == '_' && + str[len - 9 - ncv_plen] == 'R' && + !( + ncv_plen && + strncmp(str + len - (8 + ncv_plen), ncv_prefix, ncv_plen) + )) + len -= 10 + ncv_plen; + return obj_elf_hash_n(str, len); +} + +/* + * Conditionally add the symbols from the given symbol set + * to the new module. + */ +static int add_symbols_from(struct obj_file *f, int idx, + struct module_symbol *syms, size_t nsyms) +{ + struct module_symbol *s; + size_t i; + int used = 0; + + 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. + */ + 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); + /* + * Did our symbol just get installed? + * If so, mark the module as "used". + */ + if (sym->secidx == idx) + used = 1; + } + } + + return used; +} + +static void add_kernel_symbols(struct obj_file *f) +{ + struct module_stat *m; + size_t i, nused = 0; + + /* Add module symbols first. */ + for (i = 0, m = module_stat; i < n_module_stat; ++i, ++m) + if (m->nsyms && + add_symbols_from(f, SHN_HIRESERVE + 2 + i, m->syms, m->nsyms)) + m->status = 1 /* used */, ++nused; + n_ext_modules_used = nused; + + /* And finally the symbols from the kernel proper. */ + if (nksyms) + add_symbols_from(f, SHN_HIRESERVE + 1, ksyms, nksyms); +} + +static void hide_special_symbols(struct obj_file *f) +{ + struct obj_symbol *sym; + const char *const *p; + static const char *const specials[] = + { + "cleanup_module", + "init_module", + "kernel_version", + NULL + }; + + 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)); +} + +static void print_load_map(struct obj_file *f) +{ + struct obj_symbol *sym; + struct obj_symbol **all, **p; + struct obj_section *sec; + int load_map_cmp(const void *a, const void *b) { + struct obj_symbol **as = (struct obj_symbol **) a; + struct obj_symbol **bs = (struct obj_symbol **) b; + unsigned long aa = obj_symbol_final_value(f, *as); + unsigned long ba = obj_symbol_final_value(f, *bs); + return aa < ba ? -1 : aa > ba ? 1 : 0; + } + int i, nsyms, *loaded; + + /* Report on the section layout. */ + + lprintf("Sections: Size %-*s Align", + (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; + + lprintf("%-16s%08lx %0*lx 2**%d", + sec->name, + (long)sec->header.sh_size, + (int) (2 * sizeof(void *)), + (long)sec->header.sh_addr, + a); + } + + /* 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; + + /* Sort them by final value. */ + qsort(all, nsyms, sizeof(struct obj_file *), load_map_cmp); + + /* And list them. */ + lprintf("\nSymbols:"); + 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 { + struct obj_section *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 (ELFW(ST_BIND) (sym->info) == STB_LOCAL) + type = tolower(type); + + lprintf("%0*lx %c %s", (int) (2 * sizeof(void *)), value, + type, sym->name); + } +} + +/************************************************************************/ +/* begin compat */ + +static char * get_modinfo_value(struct obj_file *f, const char *key) +{ + struct obj_section *sec; + char *p, *v, *n, *ep; + size_t klen = strlen(key); + + sec = obj_find_section(f, ".modinfo"); + if (sec == NULL) + return NULL; + + p = sec->contents; + ep = p + sec->header.sh_size; + while (p < ep) { + v = strchr(p, '='); + n = strchr(p, '\0'); + if (v) { + if (v - p == klen && strncmp(p, key, klen) == 0) + return v + 1; + } else { + if (n - p == klen && strcmp(p, key) == 0) + return n; + } + p = n + 1; + } + + return NULL; +} + +static int 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 module)); + memset(sec->contents, 0, sizeof(struct module)); + + obj_add_symbol(f, "__this_module", -1, ELFW(ST_INFO) (STB_LOCAL, STT_OBJECT), + sec->idx, 0, sizeof(struct module)); + + obj_string_patch(f, sec->idx, offsetof(struct module, name), m_name); + + return 1; +} + +#ifdef COMPAT_2_0 +static int old_create_mod_use_count(struct obj_file *f) +{ + struct obj_section *sec; + struct obj_symbol *got; + + 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)); + + /* + * patb: if there is a _GLOBAL_OFFSET_TABLE_, + * add .got section for PIC type modules; + * we have to do this here, because obj_* calls are not made until + * after obj_check_undefined + * is there a better place for this exception? + */ + got = obj_find_symbol(f, "_GLOBAL_OFFSET_TABLE_"); + if (got) +{ + sec = obj_create_alloced_section(f, ".got", + sizeof(long), sizeof(long)); + got->secidx = sec->idx; /* mark the symbol as defined */ + } + return 1; +} +#endif + +/* add an entry to the __ksymtab section, creating it if necessary */ +static void 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; + + 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); +} + +static int create_module_ksymtab(struct obj_file *f) +{ + struct obj_section *sec; + int i; + + /* We must always add the module references. */ + + if (n_ext_modules_used) { + struct module_ref *dep; + struct obj_symbol *tm; + + sec = obj_create_alloced_section(f, ".kmodtab", tgt_sizeof_void_p, + (sizeof(struct module_ref) + * n_ext_modules_used)); + if (!sec) + return 0; + + tm = obj_find_symbol(f, "__this_module"); + dep = (struct module_ref *) sec->contents; + for (i = 0; i < n_module_stat; ++i) + if (module_stat[i].status /* used */) { + dep->dep = module_stat[i].addr; + obj_symbol_patch(f, sec->idx, (char *) &dep->ref - sec->contents, tm); + dep->next_ref = 0; + ++dep; + } + } + if (flag_export && !obj_find_section(f, "__ksymtab")) { + int *loaded; + + /* We don't want to export symbols residing in sections that + aren't loaded. There are a number of these created so that + we make sure certain module options don't appear twice. */ + + loaded = alloca(sizeof(int) * (i = f->header.e_shnum)); + while (--i >= 0) + loaded[i] = (f->sections[i]->header.sh_flags & SHF_ALLOC) != 0; + + 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->secidx >= SHN_LORESERVE + || loaded[sym->secidx])) { + add_ksymtab(f, sym); + } + } + } + } + return 1; +} + +/* Get the module's kernel version in the canonical integer form. */ +static int get_module_version(struct obj_file *f, char str[STRVERSIONLEN]) +{ + int a, b, c; + char *p, *q; + + if ((p = get_modinfo_value(f, "kernel_version")) == NULL) { + struct obj_symbol *sym; + + m_has_modinfo = 0; + if ((sym = obj_find_symbol(f, "kernel_version")) == NULL) + sym = obj_find_symbol(f, "__module_kernel_version"); + if (sym == NULL) + return -1; + p = f->sections[sym->secidx]->contents + sym->value; + } else + m_has_modinfo = 1; + + 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; +} + +/* Return the kernel symbol checksum version, or zero if not used. */ +static int is_kernel_checksummed(void) +{ + struct module_symbol *s; + size_t i; + + /* + * Using_Versions might not be the first symbol, + * but it should be in there. + */ + for (i = 0, s = ksyms; i < nksyms; ++i, ++s) + if (strcmp((char *) s->name, "Using_Versions") == 0) + return s->value; + + return 0; +} + +static int is_module_checksummed(struct obj_file *f) +{ + if (m_has_modinfo) { + const char *p = get_modinfo_value(f, "using_checksums"); + if (p) + return atoi(p); + else + return 0; + } else + return obj_find_symbol(f, "Using_Versions") != NULL; +} + +/* 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) +{ + 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" + }; + + if (realpath(filename, real)) { + absolute_filename = xstrdup(real); + } + else { + int save_errno = errno; + error("cannot get realpath for %s", filename); + errno = save_errno; + absolute_filename = 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, + 2*sizeof(statbuf.st_mtime), statbuf.st_mtime, + version); + sym = obj_add_symbol(f, name, -1, + ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE), + sec->idx, sec->header.sh_addr, 0); + if (use_ksymtab) + add_ksymtab(f, sym); + } + free(absolute_filename); + + /* 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, ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE), + sec->idx, sec->header.sh_addr, 0); + if (use_ksymtab) + add_ksymtab(f, sym); + } + + /* 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, ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE), + sec->idx, sec->header.sh_addr, 0); + if (use_ksymtab) + add_ksymtab(f, sym); + } + } +} + +static int process_module_arguments(struct obj_file *f, int argc, char **argv, int required) +{ + for (; argc > 0; ++argv, --argc) { + struct obj_symbol *sym; + int c; + int min, max; + int n; + char *contents; + char *input; + char *fmt; + char *key; + char *loc; + + if ((input = strchr(*argv, '=')) == NULL) + continue; + + n = input - *argv; + input += 1; /* skip '=' */ + + key = alloca(n + 6); + + if (m_has_modinfo) { + memcpy(key, "parm_", 5); + memcpy(key + 5, *argv, n); + key[n + 5] = '\0'; + if ((fmt = get_modinfo_value(f, key)) == NULL) { + if (required) { + error("invalid parameter %s", key); + return 0; + } + else { + if (flag_verbose) + lprintf("ignoring %s", *argv); + continue; /* silently ignore optional parameters */ + } + } + key += 5; + + if (isdigit(*fmt)) { + min = strtoul(fmt, &fmt, 10); + if (*fmt == '-') + max = strtoul(fmt + 1, &fmt, 10); + else + max = min; + } else + min = max = 1; + } else { /* not m_has_modinfo */ + memcpy(key, *argv, n); + key[n] = '\0'; + + if (isdigit(*input)) + fmt = "i"; + else + fmt = "s"; + min = max = 0; + } + + sym = obj_find_symbol(f, key); + + /* + * Also check that the parameter was not + * resolved from the kernel. + */ + if (sym == NULL || sym->secidx > SHN_HIRESERVE) { + error("symbol for parameter %s not found", key); + return 0; + } + + contents = f->sections[sym->secidx]->contents; + loc = contents + sym->value; + n = 1; + + while (*input) { + char *str; + + switch (*fmt) { + case 's': + case 'c': + /* + * Do C quoting if we begin with a ", + * else slurp the lot. + */ + if (*input == '"') { + char *r; + + str = alloca(strlen(input)); + for (r = str, input++; *input != '"'; ++input, ++r) { + if (*input == '\0') { + error("improperly terminated string argument for %s", key); + return 0; + } + /* else */ + if (*input != '\\') { + *r = *input; + continue; + } + /* else handle \ */ + switch (*++input) { + 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': + c = *input - '0'; + if ('0' <= input[1] && input[1] <= '7') { + c = (c * 8) + *++input - '0'; + if ('0' <= input[1] && input[1] <= '7') + c = (c * 8) + *++input - '0'; + } + *r = c; + break; + + default: *r = *input; break; + } + } + *r = '\0'; + ++input; + } else { + /* + * The string is not quoted. + * We will break it using the comma + * (like for ints). + * If the user wants to include commas + * in a string, he just has to quote it + */ + char *r; + + /* Search the next comma */ + if ((r = strchr(input, ',')) != NULL) { + /* + * Found a comma + * Recopy the current field + */ + str = alloca(r - input + 1); + memcpy(str, input, r - input); + str[r - input] = '\0'; + /* Keep next fields */ + input = r; + } else { + /* last string */ + str = input; + input = ""; + } + } + + if (*fmt == 's') { + /* Normal string */ + obj_string_patch(f, sym->secidx, loc - contents, str); + loc += tgt_sizeof_char_p; + } else { + /* Array of chars (in fact, matrix !) */ + long charssize; /* size of each member */ + + /* Get the size of each member */ + /* Probably we should do that outside the loop ? */ + if (!isdigit(*(fmt + 1))) { + error("parameter type 'c' for %s must be followed by" + " the maximum size", key); + return 0; + } + charssize = strtoul(fmt + 1, (char **) NULL, 10); + + /* Check length */ + if (strlen(str) >= charssize-1) { + error("string too long for %s (max %ld)", + key, charssize - 1); + return 0; + } + /* Copy to location */ + strcpy((char *) loc, str); /* safe, see check above */ + loc += charssize; + } + /* + * End of 's' and 'c' + */ + break; + + case 'b': + *loc++ = strtoul(input, &input, 0); + break; + + case 'h': + *(short *) loc = strtoul(input, &input, 0); + loc += tgt_sizeof_short; + break; + + case 'i': + *(int *) loc = strtoul(input, &input, 0); + loc += tgt_sizeof_int; + break; + + case 'l': + *(long *) loc = strtoul(input, &input, 0); + loc += tgt_sizeof_long; + break; + + default: + error("unknown parameter type '%c' for %s", + *fmt, key); + return 0; + } + /* + * end of switch (*fmt) + */ + + while (*input && isspace(*input)) + ++input; + if (*input == '\0') + break; /* while (*input) */ + /* else */ + + if (*input == ',') { + if (max && (++n > max)) { + error("too many values for %s (max %d)", key, max); + return 0; + } + ++input; + /* continue with while (*input) */ + } else { + error("invalid argument syntax for %s: '%c'", + key, *input); + return 0; + } + } /* end of while (*input) */ + + if (min && (n < min)) { + error("too few values for %s (min %d)", key, min); + return 0; + } + } /* end of for (;argc > 0;) */ + + return 1; +} + + +/* Add a kallsyms section if the kernel supports all symbols. */ +static int add_kallsyms(struct obj_file *f, + struct obj_section **module_kallsyms, int force_kallsyms) +{ + struct module_symbol *s; + struct obj_file *f_kallsyms; + struct obj_section *sec_kallsyms; + size_t i; + int l; + const char *p, *pt_R; + unsigned long start = 0, stop = 0; + + for (i = 0, s = ksyms; i < nksyms; ++i, ++s) { + p = (char *)s->name; + pt_R = strstr(p, "_R"); + if (pt_R) + l = pt_R - p; + else + l = strlen(p); + if (strncmp(p, "__start_" KALLSYMS_SEC_NAME, l) == 0) + start = s->value; + else if (strncmp(p, "__stop_" KALLSYMS_SEC_NAME, l) == 0) + stop = s->value; + } + + if (start >= stop && !force_kallsyms) + return(0); + + /* The kernel contains all symbols, do the same for this module. */ + + /* Add an empty kallsyms section to the module if necessary */ + for (i = 0; i < f->header.e_shnum; ++i) { + if (strcmp(f->sections[i]->name, KALLSYMS_SEC_NAME) == 0) { + *module_kallsyms = f->sections[i]; + break; + } + } + if (!*module_kallsyms) + *module_kallsyms = obj_create_alloced_section(f, KALLSYMS_SEC_NAME, 0, 0); + + /* Size and populate kallsyms */ + if (obj_kallsyms(f, &f_kallsyms)) + return(1); + sec_kallsyms = f_kallsyms->sections[KALLSYMS_IDX]; + (*module_kallsyms)->header.sh_addralign = sec_kallsyms->header.sh_addralign; + (*module_kallsyms)->header.sh_size = sec_kallsyms->header.sh_size; + free((*module_kallsyms)->contents); + (*module_kallsyms)->contents = sec_kallsyms->contents; + sec_kallsyms->contents = NULL; + obj_free(f_kallsyms); + + return 0; +} + + +/* Add an arch data section if the arch wants it. */ +static int add_archdata(struct obj_file *f, + struct obj_section **sec) +{ + size_t i; + + *sec = NULL; + /* Add an empty archdata section to the module if necessary */ + for (i = 0; i < f->header.e_shnum; ++i) { + if (strcmp(f->sections[i]->name, ARCHDATA_SEC_NAME) == 0) { + *sec = f->sections[i]; + break; + } + } + if (!*sec) + *sec = obj_create_alloced_section(f, ARCHDATA_SEC_NAME, 16, 0); + + /* Size and populate archdata */ + if (arch_archdata(f, *sec)) + return(1); + return 0; +} + + +static int init_module(const char *m_name, struct obj_file *f, + unsigned long m_size, const char *blob_name, + unsigned int noload, unsigned int flag_load_map) +{ + struct module *module; + struct obj_section *sec; + void *image; + int ret = 0; + tgt_long m_addr; + + sec = obj_find_section(f, ".this"); + module = (struct module *) sec->contents; + m_addr = sec->header.sh_addr; + + module->size_of_struct = sizeof(*module); + module->size = m_size; + module->flags = flag_autoclean ? NEW_MOD_AUTOCLEAN : 0; + + sec = obj_find_section(f, "__ksymtab"); + if (sec && sec->header.sh_size) { + module->syms = sec->header.sh_addr; + module->nsyms = sec->header.sh_size / (2 * tgt_sizeof_char_p); + } + if (n_ext_modules_used) { + sec = obj_find_section(f, ".kmodtab"); + module->deps = sec->header.sh_addr; + module->ndeps = n_ext_modules_used; + } + module->init = obj_symbol_final_value(f, obj_find_symbol(f, "init_module")); + module->cleanup = obj_symbol_final_value(f, + obj_find_symbol(f, "cleanup_module")); + + sec = obj_find_section(f, "__ex_table"); + if (sec) { + module->ex_table_start = sec->header.sh_addr; + module->ex_table_end = sec->header.sh_addr + sec->header.sh_size; + } + sec = obj_find_section(f, ".text.init"); + if (sec) { + module->runsize = sec->header.sh_addr - m_addr; + } + 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; + } + sec = obj_find_section(f, ARCHDATA_SEC_NAME); + if (sec && sec->header.sh_size) { + module->archdata_start = 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 = 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. + */ + image = xmalloc(m_size); + obj_create_image(f, image); + + if (flag_load_map) + print_load_map(f); + + if (blob_name) { + int fd, l; + fd = open(blob_name, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + if (fd < 0) { + error("open %s failed %m", blob_name); + ret = -1; + } + else { + if ((l = write(fd, image, m_size)) != m_size) { + error("write %s failed %m", blob_name); + ret = -1; + } + close(fd); + } + } + + if (ret == 0 && !noload) { + fflush(stdout); /* Flush any debugging output */ + ret = sys_init_module(m_name, (struct module *) image); + if (ret) { + error("init_module: %m"); + lprintf("Hint: insmod errors can be caused by incorrect module parameters, " + "including invalid IO or IRQ parameters"); + } + } + + free(image); + + return ret == 0; +} + +#ifdef COMPAT_2_0 +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; + int nsyms = 0, strsize = 0, total; + + /* Create the symbol table */ + /* 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; + + str = stpcpy(str, 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_module_stat; ++i) { + if (module_stat[i].status /* used */) { + ref++->module = module_stat[i].modstruct; + } + } + } + + /* 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 - sizeof(long)) | + (flag_autoclean ? OLD_MOD_AUTOCLEAN : 0), + &routines, + symtab); + if (ret) + error("init_module: %m"); + + free(image); + free(symtab); + + return ret == 0; +} +#endif +/* end compat */ +/************************************************************************/ + +/* Check that a module parameter has a reasonable definition */ +static int check_module_parameter(struct obj_file *f, char *key, char *value, int *persist_flag) +{ + struct obj_symbol *sym; + int min, max; + char *p = value; + + sym = obj_find_symbol(f, key); + if (sym == NULL) { + /* FIXME: For 2.2 kernel compatibility, only issue warnings for + * most error conditions. Make these all errors in 2.5. + */ + lprintf("Warning: %s symbol for parameter %s not found", error_file, key); + return(1); + } + + if (isdigit(*p)) { + min = strtoul(p, &p, 10); + if (*p == '-') + max = strtoul(p + 1, &p, 10); + else + max = min; + } else + min = max = 1; + + if (max < min) { + lprintf("Warning: %s parameter %s has max < min!", error_file, key); + return(1); + } + + switch (*p) { + case 'c': + if (!isdigit(p[1])) { + lprintf("%s parameter %s has no size after 'c'!", error_file, key); + return(1); + } + while (isdigit(p[1])) + ++p; /* swallow c array size */ + break; + case 'b': /* drop through */ + case 'h': /* drop through */ + case 'i': /* drop through */ + case 'l': /* drop through */ + case 's': + break; + case '\0': + lprintf("%s parameter %s has no format character!", error_file, key); + return(1); + default: + lprintf("%s parameter %s has unknown format character '%c'", error_file, key, *p); + return(1); + } + switch (*++p) { + case 'p': + if (*(p-1) == 's') { + error("parameter %s is invalid persistent string", key); + return(1); + } + *persist_flag = 1; + break; + case '\0': + break; + default: + lprintf("%s parameter %s has unknown format modifier '%c'", error_file, key, *p); + return(1); + } + return(0); +} + +/* Check that all module parameters have reasonable definitions */ +static void check_module_parameters(struct obj_file *f, int *persist_flag) +{ + struct obj_section *sec; + char *ptr, *value, *n, *endptr; + int namelen, err = 0; + + sec = obj_find_section(f, ".modinfo"); + if (sec == NULL) { + /* module does not support typed parameters */ + return; + } + + ptr = sec->contents; + endptr = ptr + sec->header.sh_size; + while (ptr < endptr && !err) { + value = strchr(ptr, '='); + n = strchr(ptr, '\0'); + if (value) { + namelen = value - ptr; + if (namelen >= 5 && strncmp(ptr, "parm_", 5) == 0 + && !(namelen > 10 && strncmp(ptr, "parm_desc_", 10) == 0)) { + char *pname = xmalloc(namelen + 1); + strncpy(pname, ptr + 5, namelen - 5); + pname[namelen - 5] = '\0'; + err = check_module_parameter(f, pname, value+1, persist_flag); + free(pname); + } + } else { + if (n - ptr >= 5 && strncmp(ptr, "parm_", 5) == 0) { + error("parameter %s found with no value", ptr); + err = 1; + } + } + ptr = n + 1; + } + + if (err) + *persist_flag = 0; + return; +} + + +/* For common 3264 code, only compile the usage message once, in the 64 bit version */ +#if defined(COMMON_3264) && defined(ONLY_32) +extern void insmod_usage(void); /* Use the copy in the 64 bit version */ +#else /* Common 64 bit version or any non common code - compile usage routine */ +void insmod_usage(void) +{ + fputs("Usage:\n" + "insmod [-fhkLmnpqrsSvVxXyY] [-e persist_name] [-o module_name] [-O blob_name] [-P prefix] module [ symbol=value ... ]\n" + "\n" + " module Name of a loadable kernel module ('.o' can be omitted)\n" + " -f, --force Force loading under wrong kernel version\n" + " -h, --help Print this message\n" + " -k, --autoclean Make module autoclean-able\n" + " -L, --lock Prevent simultaneous loads of the same module\n" + " -m, --map Generate load map (so crashes can be traced)\n" + " -n, --noload Don't load, just show\n" + " -p, --probe Probe mode; check if the module matches the kernel\n" + " -q, --quiet Don't print unresolved symbols\n" + " -r, --root Allow root to load modules not owned by root\n" + " -s, --syslog Report errors via syslog\n" + " -S, --kallsyms Force kallsyms on module\n" + " -v, --verbose Verbose output\n" + " -V, --version Show version\n" + " -x, --noexport Do not export externs\n" + " -X, --export Do export externs (default)\n" + " -y, --noksymoops Do not add ksymoops symbols\n" + " -Y, --ksymoops Do add ksymoops symbols (default)\n" + " -e persist_name\n" + " --persist=persist_name Filename to hold any persistent data from the module\n" + " -o NAME, --name=NAME Set internal module name to NAME\n" + " -O NAME, --blob=NAME Save the object as a binary blob in NAME\n" + " -P PREFIX\n" + " --prefix=PREFIX Prefix for kernel or module symbols\n" + ,stderr); + exit(1); +} +#endif /* defined(COMMON_3264) && defined(ONLY_32) */ + +#if defined(COMMON_3264) && defined(ONLY_32) +#define INSMOD_MAIN insmod_main_32 /* 32 bit version */ +#elif defined(COMMON_3264) && defined(ONLY_64) +#define INSMOD_MAIN insmod_main_64 /* 64 bit version */ +#else +#define INSMOD_MAIN insmod_main /* Not common code */ +#endif + +int INSMOD_MAIN(int argc, char **argv) +{ + int k_version; + int k_crcs; + char k_strversion[STRVERSIONLEN]; + struct option long_opts[] = { + {"force", 0, 0, 'f'}, + {"help", 0, 0, 'h'}, + {"autoclean", 0, 0, 'k'}, + {"lock", 0, 0, 'L'}, + {"map", 0, 0, 'm'}, + {"noload", 0, 0, 'n'}, + {"probe", 0, 0, 'p'}, + {"poll", 0, 0, 'p'}, /* poll is deprecated, remove in 2.5 */ + {"quiet", 0, 0, 'q'}, + {"root", 0, 0, 'r'}, + {"syslog", 0, 0, 's'}, + {"kallsyms", 0, 0, 'S'}, + {"verbose", 0, 0, 'v'}, + {"version", 0, 0, 'V'}, + {"noexport", 0, 0, 'x'}, + {"export", 0, 0, 'X'}, + {"noksymoops", 0, 0, 'y'}, + {"ksymoops", 0, 0, 'Y'}, + + {"persist", 1, 0, 'e'}, + {"name", 1, 0, 'o'}, + {"blob", 1, 0, 'O'}, + {"prefix", 1, 0, 'P'}, + {0, 0, 0, 0} + }; + char *m_name = NULL; + char *blob_name = NULL; /* Save object as binary blob */ + int m_version; + ElfW(Addr) m_addr; + unsigned long m_size; + int m_crcs; + char m_strversion[STRVERSIONLEN]; + char *filename; + char *persist_name = NULL; /* filename to hold any persistent data */ + int fp; + struct obj_file *f; + struct obj_section *kallsyms = NULL, *archdata = NULL; + int o; + int noload = 0; + int dolock = 1; /*Note: was: 0; */ + int quiet = 0; + int exit_status = 1; + int force_kallsyms = 0; + int persist_parms = 0; /* does module have persistent parms? */ + int i; + + error_file = "insmod"; + + /* To handle repeated calls from combined modprobe */ + errors = optind = 0; + + /* Process the command line. */ + while ((o = getopt_long(argc, argv, "fhkLmnpqrsSvVxXyYe:o:O:P:R:", + &long_opts[0], NULL)) != EOF) + switch (o) { + case 'f': /* force loading */ + flag_force_load = 1; + break; + case 'h': /* Print the usage message. */ + insmod_usage(); + break; + case 'k': /* module loaded by kerneld, auto-cleanable */ + flag_autoclean = 1; + break; + case 'L': /* protect against recursion. */ + dolock = 1; + break; + case 'm': /* generate load map */ + flag_load_map = 1; + break; + case 'n': /* don't load, just check */ + noload = 1; + break; + case 'p': /* silent probe mode */ + flag_silent_probe = 1; + break; + case 'q': /* Don't print unresolved symbols */ + quiet = 1; + break; + case 'r': /* allow root to load non-root modules */ + root_check_off = !root_check_off; + break; + case 's': /* start syslog */ + setsyslog("insmod"); + break; + case 'S': /* Force kallsyms */ + force_kallsyms = 1; + break; + case 'v': /* verbose output */ + flag_verbose = 1; + break; + case 'V': + fputs("insmod version " MODUTILS_VERSION "\n", stderr); + break; + case 'x': /* do not export externs */ + flag_export = 0; + break; + case 'X': /* do export externs */ + flag_export = 1; + break; + case 'y': /* do not define ksymoops symbols */ + flag_ksymoops = 0; + break; + case 'Y': /* do define ksymoops symbols */ + flag_ksymoops = 1; + break; + + case 'e': /* persistent data filename */ + free(persist_name); + persist_name = xstrdup(optarg); + break; + case 'o': /* name the output module */ + m_name = optarg; + break; + case 'O': /* save the output module object */ + blob_name = optarg; + break; + case 'P': /* use prefix on crc */ + set_ncv_prefix(optarg); + break; + + default: + insmod_usage(); + break; + } + + if (optind >= argc) { + insmod_usage(); + } + filename = argv[optind++]; + + if (config_read(0, NULL, "", NULL) < 0) { + error("Failed handle configuration"); + } + + if (persist_name && !*persist_name && + (!persistdir || !*persistdir)) { + free(persist_name); + persist_name = NULL; + if (flag_verbose) + lprintf("insmod: -e \"\" ignored, no persistdir"); + } + + if (m_name == NULL) { + size_t len; + char *p; + + if ((p = strrchr(filename, '/')) != NULL) + p++; + else + p = filename; + len = strlen(p); + if (len > 2 && p[len - 2] == '.' && p[len - 1] == 'o') + len -= 2; + else if (len > 4 && p[len - 4] == '.' && p[len - 3] == 'm' + && p[len - 2] == 'o' && p[len - 1] == 'd') + len -= 4; +#ifdef CONFIG_USE_ZLIB + else if (len > 5 && !strcmp(p + len - 5, ".o.gz")) + len -= 5; +#endif + + m_name = xmalloc(len + 1); + memcpy(m_name, p, len); + m_name[len] = '\0'; + } + + /* Locate the file to be loaded. */ + if (!strchr(filename, '/') && !strchr(filename, '.')) { + char *tmp = search_module_path(filename); + if (tmp == NULL) { + error("%s: no module by that name found", filename); + return 1; + } + filename = tmp; + lprintf("Using %s", filename); + } else if (flag_verbose) + lprintf("Using %s", filename); + + /* And open it. */ + if ((fp = gzf_open(filename, O_RDONLY)) == -1) { + error("%s: %m", filename); + return 1; + } + /* Try to prevent multiple simultaneous loads. */ + if (dolock) + flock(fp, LOCK_EX); + + if (!get_kernel_info(K_SYMBOLS)) + goto out; + + /* + * Set the genksyms prefix if this is a versioned kernel + * and it's not already set. + */ + set_ncv_prefix(NULL); + + for (i = 0; i < n_module_stat; ++i) { + if (strcmp(module_stat[i].name, m_name) == 0) { + error("a module named %s already exists", m_name); + goto out; + } + } + + error_file = filename; + if ((f = obj_load(fp, ET_REL, filename)) == NULL) + goto out; + + /* Version correspondence? */ + k_version = get_kernel_version(k_strversion); + m_version = get_module_version(f, m_strversion); + if (m_version == -1) { + error("couldn't find the kernel version the module was compiled for"); + goto out; + } + + k_crcs = is_kernel_checksummed(); + m_crcs = is_module_checksummed(f); + if ((m_crcs == 0 || k_crcs == 0) && + strncmp(k_strversion, m_strversion, STRVERSIONLEN) != 0) { + if (flag_force_load) { + lprintf("Warning: kernel-module version mismatch\n" + "\t%s was compiled for kernel version %s\n" + "\twhile this kernel is version %s", + filename, m_strversion, k_strversion); + } else { + if (!quiet) + error("kernel-module version mismatch\n" + "\t%s was compiled for kernel version %s\n" + "\twhile this kernel is version %s.", + filename, m_strversion, k_strversion); + goto out; + } + } + if (m_crcs != k_crcs) + obj_set_symbol_compare(f, ncv_strcmp, ncv_symbol_hash); + + /* Let the module know about the kernel symbols. */ + add_kernel_symbols(f); + + /* Allocate common symbols, symbol tables, and string tables. + * + * The calls marked DEPMOD indicate the bits of code that depmod + * uses to do a pseudo relocation, ignoring undefined symbols. + * Any changes made to the relocation sequence here should be + * checked against depmod. + */ +#ifdef COMPAT_2_0 + if (k_new_syscalls + ? !create_this_module(f, m_name) + : !old_create_mod_use_count(f)) + goto out; +#else + if (!create_this_module(f, m_name)) + goto out; +#endif + + if (!obj_check_undefineds(f, quiet)) /* DEPMOD, obj_clear_undefineds */ + goto out; + obj_allocate_commons(f); /* DEPMOD */ + + check_module_parameters(f, &persist_parms); + + if (optind < argc) { + if (!process_module_arguments(f, argc - optind, argv + optind, 1)) + goto out; + } + arch_create_got(f); /* DEPMOD */ + hide_special_symbols(f); + + if (persist_parms && persist_name && *persist_name) { + f->persist = persist_name; + persist_name = NULL; + } + + if (persist_parms && + persist_name && !*persist_name) { + /* -e "". This is ugly. Take the filename, compare it against + * each of the module paths until we find a match on the start + * of the filename, assume the rest is the relative path. Have + * to do it this way because modprobe uses absolute filenames + * for module names in modules.dep and the format of modules.dep + * does not allow for any backwards compatible changes, so there + * is nowhere to store the relative filename. The only way this + * should fail to calculate a relative path is "insmod ./xxx", for + * that case the user has to specify -e filename. + */ + int j, l = strlen(filename); + char *relative = NULL; + char *p; + for (i = 0; i < nmodpath; ++i) { + p = modpath[i].path; + j = strlen(p); + while (j && p[j] == '/') + --j; + if (j < l && strncmp(filename, p, j) == 0 && filename[j] == '/') { + while (filename[j] == '/') + ++j; + relative = xstrdup(filename+j); + break; + } + } + if (relative) { + i = strlen(relative); + if (i > 3 && strcmp(relative+i-3, ".gz") == 0) + relative[i -= 3] = '\0'; + if (i > 2 && strcmp(relative+i-2, ".o") == 0) + relative[i -= 2] = '\0'; + else if (i > 4 && strcmp(relative+i-4, ".mod") == 0) + relative[i -= 4] = '\0'; + f->persist = xmalloc(strlen(persistdir) + 1 + i + 1); + strcpy(f->persist, persistdir); /* safe, xmalloc */ + strcat(f->persist, "/"); /* safe, xmalloc */ + strcat(f->persist, relative); /* safe, xmalloc */ + free(relative); + } + else + error("Cannot calculate persistent filename"); + } + + if (f->persist && *(f->persist) != '/') { + error("Persistent filenames must be absolute, ignoring '%s'", + f->persist); + free(f->persist); + f->persist = NULL; + } + + if (f->persist && !flag_ksymoops) { + error("has persistent data but ksymoops symbols are not available"); + free(f->persist); + f->persist = NULL; + } + + if (f->persist && !k_new_syscalls) { + error("has persistent data but the kernel is too old to support it"); + free(f->persist); + f->persist = NULL; + } + + if (persist_parms && flag_verbose) { + if (f->persist) + lprintf("Persist filename '%s'", f->persist); + else + lprintf("No persistent filename available"); + } + + if (f->persist) { + FILE *fp = fopen(f->persist, "r"); + if (!fp) { + if (flag_verbose) + lprintf("Cannot open persist file '%s' %m", f->persist); + } + else { + int pargc = 0; + char *pargv[1000]; /* hard coded but big enough */ + char line[3000]; /* hard coded but big enough */ + char *p; + while (fgets(line, sizeof(line), fp)) { + p = strchr(line, '\n'); + if (!p) { + error("Persistent data line is too long\n%s", line); + break; + } + *p = '\0'; + p = line; + while (isspace(*p)) + ++p; + if (!*p || *p == '#') + continue; + if (pargc == sizeof(pargv)/sizeof(pargv[0])) { + error("More than %d persistent parameters", pargc); + break; + } + pargv[pargc++] = xstrdup(p); + } + fclose(fp); + if (!process_module_arguments(f, pargc, pargv, 0)) + goto out; + while (pargc--) + free(pargv[pargc]); + } + } + + if (flag_ksymoops) + add_ksymoops_symbols(f, filename, m_name); + + if (k_new_syscalls) + create_module_ksymtab(f); + + /* archdata based on relocatable addresses */ + if (add_archdata(f, &archdata)) + goto out; + + /* kallsyms based on relocatable addresses */ + if (add_kallsyms(f, &kallsyms, force_kallsyms)) + goto out; + /**** No symbols or sections to be changed after kallsyms above ***/ + + if (errors) + goto out; + + /* If we were just checking, we made it. */ + if (flag_silent_probe) { + exit_status = 0; + goto out; + } + /* Module has now finished growing; find its size and install it. */ + m_size = obj_load_size(f); /* DEPMOD */ + + if (noload) { + /* Don't bother actually touching the kernel. */ + m_addr = 0x12340000; + } else { + errno = 0; + m_addr = create_module(m_name, m_size); + switch (errno) { + case 0: + break; + case EEXIST: + if (dolock) { + /* + * Assume that we were just invoked + * simultaneous with another insmod + * and return success. + */ + exit_status = 0; + goto out; + } + error("a module named %s already exists", m_name); + goto out; + case ENOMEM: + error("can't allocate kernel memory for module; needed %lu bytes", + m_size); + goto out; + default: + error("create_module: %m"); + goto out; + } + } + + /* module is already built, complete with ksymoops symbols for the + * persistent filename. If the kernel does not support persistent data + * then give an error but continue. It is too difficult to clean up at + * this stage and this error will only occur on backported modules. + * rmmod will also get an error so warn the user now. + */ + if (f->persist && !noload) { + struct { + struct module m; + int data; + } test_read; + memset(&test_read, 0, sizeof(test_read)); + test_read.m.size_of_struct = -sizeof(test_read.m); /* -ve size => read, not write */ + test_read.m.read_start = m_addr + sizeof(struct module); + test_read.m.read_end = test_read.m.read_start + sizeof(test_read.data); + if (sys_init_module(m_name, (struct module *) &test_read)) { + int old_errors = errors; + error("has persistent data but the kernel is too old to support it." + " Expect errors during rmmod as well"); + errors = old_errors; + } + } + + if (!obj_relocate(f, m_addr)) { /* DEPMOD */ + if (!noload) + delete_module(m_name); + goto out; + } + + /* Do archdata again, this time we have the final addresses */ + if (add_archdata(f, &archdata)) + goto out; + + /* Do kallsyms again, this time we have the final addresses */ + if (add_kallsyms(f, &kallsyms, force_kallsyms)) + goto out; + +#ifdef COMPAT_2_0 + if (k_new_syscalls) + init_module(m_name, f, m_size, blob_name, noload, flag_load_map); + else if (!noload) + old_init_module(m_name, f, m_size); +#else + init_module(m_name, f, m_size, blob_name, noload, flag_load_map); +#endif + if (errors) { + if (!noload) + delete_module(m_name); + goto out; + } + exit_status = 0; + + out: + if (dolock) + flock(fp, LOCK_UN); + close(fp); + if (!noload) + snap_shot(NULL, 0); + + return exit_status; +} + +/* For common 3264 code, add an overall insmod_main, in the 64 bit version. */ +#if defined(COMMON_3264) && defined(ONLY_64) +int insmod_main(int argc, char **argv) +{ + if (arch64()) + return insmod_main_64(argc, argv); + else + return insmod_main_32(argc, argv); +} +#endif /* defined(COMMON_3264) && defined(ONLY_64) */ + + + +int insmod_call(char * full_filename, char * params) +{ + int argc = 2; + char *argv[50]; + char * ptr = params; + argv[0] = "stage1"; + argv[1] = full_filename; + + while (ptr != NULL) { + argv[argc] = ptr; + argc++; + ptr = strchr(ptr, ' '); + if (ptr) { + ptr[0] = '\0'; + ptr++; + } + } + + return insmod_main(argc, argv); +} diff --git a/mdk-stage1/insmod-modutils/obj/Makefile b/mdk-stage1/insmod-modutils/obj/Makefile new file mode 100644 index 000000000..9bcf8bac8 --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/Makefile @@ -0,0 +1,31 @@ + #****************************************************************************** + # + # insmod from modutils (generic) + # + # $Id$ + # + # Copyright 1996, 1997 Linux International. + # + #***************************************************************************** + +top_dir = ../.. + +include $(top_dir)/Makefile.common + + +all: libobj.a + +clean: + rm -f *.o *.a + + +FLAGS = -c -Wall -Os -fomit-frame-pointer -I./../include -D_GNU_SOURCE -DELF_MACHINE_H='"elf_$(ARCH).h"' -DARCH_$(ARCH) -DCONFIG_ROOT_CHECK_OFF=0 + +OBJS = obj_kallsyms.o obj_common.o obj_load.o obj_reloc.o obj_$(ARCH).o + +libobj.a: $(OBJS) + ar cru $@ $^ + ranlib $@ + +.c.o: + gcc $(FLAGS) $(GLIBC_INCLUDES) -c $< diff --git a/mdk-stage1/insmod-modutils/obj/obj_alpha.c b/mdk-stage1/insmod-modutils/obj/obj_alpha.c new file mode 100644 index 000000000..4006b3442 --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_alpha.c @@ -0,0 +1,305 @@ +/* Alpha specific support for Elf loading and relocation. + Copyright 1996, 1997 Linux International. + + Contributed by Richard Henderson <rth@tamu.edu> + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ident "$Id$" + +#include <string.h> +#include <assert.h> + +#include <module.h> +#include <obj.h> +#include <util.h> + +/*======================================================================*/ + +struct alpha_got_entry +{ + struct alpha_got_entry *next; + ElfW(Addr) addend; + int offset; + int reloc_done; +}; + +struct alpha_file +{ + struct obj_file root; + struct obj_section *got; +}; + +struct alpha_symbol +{ + struct obj_symbol root; + struct alpha_got_entry *got_entries; +}; + + +/*======================================================================*/ + +struct obj_file * +arch_new_file (void) +{ + struct alpha_file *f; + f = xmalloc(sizeof(*f)); + f->got = NULL; + return &f->root; +} + +struct obj_section * +arch_new_section (void) +{ + return xmalloc(sizeof(struct obj_section)); +} + +struct obj_symbol * +arch_new_symbol (void) +{ + struct alpha_symbol *sym; + sym = xmalloc(sizeof(*sym)); + sym->got_entries = NULL; + return &sym->root; +} + +int +arch_load_proc_section(struct obj_section *sec, int fp) +{ + /* Assume it's just a debugging section that we can safely + ignore ... */ + sec->contents = NULL; + + return 0; +} + +enum obj_reloc +arch_apply_relocation (struct obj_file *f, + struct obj_section *targsec, + struct obj_section *symsec, + struct obj_symbol *sym, + Elf64_Rela *rel, + Elf64_Addr v) +{ + struct alpha_file *af = (struct alpha_file *)f; + struct alpha_symbol *asym = (struct alpha_symbol *)sym; + + unsigned long *lloc = (unsigned long *)(targsec->contents + rel->r_offset); + unsigned int *iloc = (unsigned int *)lloc; + Elf64_Addr dot = targsec->header.sh_addr + rel->r_offset; + Elf64_Addr gp = af->got->header.sh_addr + 0x8000; + + enum obj_reloc ret = obj_reloc_ok; + + switch (ELF64_R_TYPE(rel->r_info)) + { + case R_ALPHA_NONE: + case R_ALPHA_LITUSE: + break; + + case R_ALPHA_REFQUAD: + *lloc += v; + break; + + case R_ALPHA_GPREL32: + v -= gp; + if ((Elf64_Sxword)v > 0x7fffffff + || (Elf64_Sxword)v < -(Elf64_Sxword)0x80000000) + ret = obj_reloc_overflow; + *iloc = v; + break; + + case R_ALPHA_LITERAL: + { + struct alpha_got_entry *gotent; + + assert(asym != NULL); + gotent = asym->got_entries; + while (gotent->addend != rel->r_addend) + gotent = gotent->next; + + if (!gotent->reloc_done) + { + *(unsigned long *)(af->got->contents + gotent->offset) = v; + gotent->reloc_done = 1; + } + + *iloc = (*iloc & ~0xffff) | ((gotent->offset - 0x8000) & 0xffff); + } + break; + + case R_ALPHA_GPDISP: + { + unsigned int *p_ldah, *p_lda; + unsigned int i_ldah, i_lda, hi, lo; + + p_ldah = iloc; + p_lda = (unsigned int *)((char *)iloc + rel->r_addend); + i_ldah = *p_ldah; + i_lda = *p_lda; + + /* Make sure the instructions are righteous. */ + if ((i_ldah >> 26) != 9 || (i_lda >> 26) != 8) + ret = obj_reloc_dangerous; + + /* Extract the existing addend. */ + v = (i_ldah & 0xffff) << 16 | (i_lda & 0xffff); + v = (v ^ 0x80008000) - 0x80008000; + + v += gp - dot; + + if ((Elf64_Sxword)v >= 0x7fff8000 + || (Elf64_Sxword)v < -(Elf64_Sxword)0x80000000) + ret = obj_reloc_overflow; + + /* Modify the instructions and finish up. */ + lo = v & 0xffff; + hi = ((v >> 16) + ((v >> 15) & 1)) & 0xffff; + + *p_ldah = (i_ldah & 0xffff0000) | hi; + *p_lda = (i_lda & 0xffff0000) | lo; + } + break; + + case R_ALPHA_BRADDR: + v -= dot + 4; + if (v % 4) + ret = obj_reloc_dangerous; + else if ((Elf64_Sxword)v > 0x3fffff + || (Elf64_Sxword)v < -(Elf64_Sxword)0x400000) + ret = obj_reloc_overflow; + v /= 4; + + *iloc = (*iloc & ~0x1fffff) | (v & 0x1fffff); + break; + + case R_ALPHA_HINT: + v -= dot + 4; + if (v % 4) + ret = obj_reloc_dangerous; + v /= 4; + + *iloc = (*iloc & ~0x3fff) | (v & 0x3fff); + break; + + default: + ret = obj_reloc_unhandled; + break; + } + + return ret; +} + +int +arch_create_got (struct obj_file *f) +{ + struct alpha_file *af = (struct alpha_file *)f; + int i, n, offset = 0; + + n = af->root.header.e_shnum; + for (i = 0; i < n; ++i) + { + struct obj_section *relsec, *symsec, *strsec; + Elf64_Rela *rel, *relend; + Elf64_Sym *symtab; + const char *strtab; + + relsec = af->root.sections[i]; + if (relsec->header.sh_type != SHT_RELA) + continue; + + symsec = af->root.sections[relsec->header.sh_link]; + strsec = af->root.sections[symsec->header.sh_link]; + + rel = (Elf64_Rela *)relsec->contents; + relend = rel + (relsec->header.sh_size / sizeof(Elf64_Rela)); + symtab = (Elf64_Sym *)symsec->contents; + strtab = (const char *)strsec->contents; + + for (; rel < relend; ++rel) + { + struct alpha_got_entry *ent; + Elf64_Sym *extsym; + struct alpha_symbol *intsym; + const char *name; + + if (ELF64_R_TYPE(rel->r_info) != R_ALPHA_LITERAL) + continue; + + extsym = &symtab[ELF64_R_SYM(rel->r_info)]; + if (extsym->st_name) + name = strtab + extsym->st_name; + else + name = f->sections[extsym->st_shndx]->name; + intsym = (struct alpha_symbol *)obj_find_symbol(&af->root, name); + + for (ent = intsym->got_entries; ent ; ent = ent->next) + if (ent->addend == rel->r_addend) + goto found; + + ent = xmalloc(sizeof(*ent)); + ent->addend = rel->r_addend; + ent->offset = offset; + ent->reloc_done = 0; + ent->next = intsym->got_entries; + intsym->got_entries = ent; + offset += 8; + + found:; + } + } + + if (offset > 0x10000) + { + error(".got section overflow: %#x > 0x10000", offset); + return 0; + } + + /* We always want a .got section so that we always have a GP for + use with GPDISP and GPREL32 relocs. Besides, if the section + is empty we don't use up space anyway. */ + af->got = obj_create_alloced_section(&af->root, ".got", 8, offset); + + return 1; +} + +int +arch_init_module (struct obj_file *f, struct module *mod) +{ + struct alpha_file *af = (struct alpha_file *)f; + + mod->gp = af->got->header.sh_addr + 0x8000; + + return 1; +} + +int +arch_finalize_section_address(struct obj_file *f, Elf64_Addr base) +{ + int i, n = f->header.e_shnum; + + f->baseaddr = base; + for (i = 0; i < n; ++i) + f->sections[i]->header.sh_addr += base; + return 1; +} + +int +arch_archdata (struct obj_file *fin, struct obj_section *sec) +{ + return 0; +} diff --git a/mdk-stage1/insmod-modutils/obj/obj_arm.c b/mdk-stage1/insmod-modutils/obj/obj_arm.c new file mode 100644 index 000000000..7a843f947 --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_arm.c @@ -0,0 +1,318 @@ +/* ARM specific support for Elf loading and relocation. + Copyright 1996, 1997, 1998 Linux International. + + Contributed by Phil Blundell <philb@gnu.org> + and wms <woody@corelcomputer.com> + based on the i386 code by Richard Henderson <rth@tamu.edu> + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ident "$Id$" + +#include <string.h> +#include <assert.h> + +#include <module.h> +#include <obj.h> +#include <util.h> + + +/*======================================================================*/ + +struct arm_plt_entry +{ + int offset; + int allocated:1; + int inited:1; // has been set up +}; + +struct arm_got_entry +{ + int offset; + int allocated : 1; + unsigned reloc_done : 1; +}; + +struct arm_file +{ + struct obj_file root; + struct obj_section *plt; + struct obj_section *got; +}; + +struct arm_symbol +{ + struct obj_symbol root; + struct arm_plt_entry pltent; + struct arm_got_entry gotent; +}; + + +/*======================================================================*/ + +struct obj_file * +arch_new_file (void) +{ + struct arm_file *f; + f = xmalloc(sizeof(*f)); + f->got = NULL; + return &f->root; +} + +struct obj_section * +arch_new_section (void) +{ + return xmalloc(sizeof(struct obj_section)); +} + +struct obj_symbol * +arch_new_symbol (void) +{ + struct arm_symbol *sym; + sym = xmalloc(sizeof(*sym)); + memset(&sym->gotent, 0, sizeof(sym->gotent)); + memset(&sym->pltent, 0, sizeof(sym->pltent)); + return &sym->root; +} + +int +arch_load_proc_section(struct obj_section *sec, int fp) +{ + /* Assume it's just a debugging section that we can safely + ignore ... */ + sec->contents = NULL; + + return 0; +} + +enum obj_reloc +arch_apply_relocation (struct obj_file *f, + struct obj_section *targsec, + struct obj_section *symsec, + struct obj_symbol *sym, + Elf32_Rel *rel, + Elf32_Addr v) +{ + struct arm_file *afile = (struct arm_file *)f; + struct arm_symbol *asym = (struct arm_symbol *)sym; + + Elf32_Addr *loc = (Elf32_Addr *)(targsec->contents + rel->r_offset); + Elf32_Addr dot = targsec->header.sh_addr + rel->r_offset; + Elf32_Addr got = afile->got ? afile->got->header.sh_addr : 0; + Elf32_Addr plt = afile->plt ? afile->plt->header.sh_addr : 0; + + struct arm_plt_entry *pe; + unsigned long *ip; + + enum obj_reloc ret = obj_reloc_ok; + + switch (ELF32_R_TYPE(rel->r_info)) + { + case R_ARM_NONE: + break; + + case R_ARM_ABS32: + *loc += v; + break; + + case R_ARM_GOT32: + /* needs an entry in the .got: set it, once */ + if (! asym->gotent.reloc_done) + { + asym->gotent.reloc_done = 1; + *(Elf32_Addr *)(afile->got->contents + asym->gotent.offset) = v; + } + /* make the reloc with_respect_to_.got */ + *loc += asym->gotent.offset; + break; + + /* relative reloc, always to _GLOBAL_OFFSET_TABLE_ (which is .got) + similar to branch, but is full 32 bits relative */ + case R_ARM_GOTPC: + assert(got); + *loc += got - dot; + break; + + case R_ARM_PC24: + case R_ARM_PLT32: + /* find the plt entry and initialize it if necessary */ + assert(asym != NULL); + pe = (struct arm_plt_entry*) &asym->pltent; + if (! pe->inited) + { + ip = (unsigned long *) (afile->plt->contents + pe->offset); + ip[0] = 0xe51ff004; /* ldr pc,[pc,#-4] */ + ip[1] = v; /* sym@ */ + 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; + + /* Convert to words. */ + v >>= 2; + + /* merge the offset into the instruction. */ + *loc = (*loc & ~0x00ffffff) | ((v + *loc) & 0x00ffffff); + break; + + /* address relative to the got */ + case R_ARM_GOTOFF: + assert(got); + *loc += v - got; + break; + + default: + printf("Warning: unhandled reloc %d\n",ELF32_R_TYPE(rel->r_info)); + ret = obj_reloc_unhandled; + break; + } + + return ret; +} + +int +arch_create_got (struct obj_file *f) +{ + struct arm_file *afile = (struct arm_file *) f; + int i; + struct obj_section *sec, *syms, *strs; + ElfW(Rel) *rel, *relend; + ElfW(Sym) *symtab, *extsym; + const char *strtab, *name; + struct arm_symbol *intsym; + struct arm_plt_entry *pe; + struct arm_got_entry *ge; + int got_offset = 0, plt_offset = 0; + + for (i = 0; i < f->header.e_shnum; ++i) + { + sec = f->sections[i]; + if (sec->header.sh_type != SHT_RELM) + continue; + syms = f->sections[sec->header.sh_link]; + strs = f->sections[syms->header.sh_link]; + + rel = (ElfW(RelM) *) sec->contents; + relend = rel + (sec->header.sh_size / sizeof(ElfW(RelM))); + symtab = (ElfW(Sym) *) syms->contents; + strtab = (const char *) strs->contents; + + for (; rel < relend; ++rel) + { + extsym = &symtab[ELF32_R_SYM(rel->r_info)]; + + switch(ELF32_R_TYPE(rel->r_info)) { + case R_ARM_PC24: + case R_ARM_PLT32: + if (extsym->st_name) + name = strtab + extsym->st_name; + else + name = f->sections[extsym->st_shndx]->name; + intsym = (struct arm_symbol *) obj_find_symbol(f, name); + + pe = &intsym->pltent; + + if (! pe->allocated) + { + pe->allocated = 1; + pe->offset = plt_offset; + plt_offset += 8; + pe->inited = 0; + } + break; + + /* these two don_t need got entries, but they need + the .got to exist */ + case R_ARM_GOTOFF: + case R_ARM_GOTPC: + if (got_offset==0) got_offset = 4; + break; + + case R_ARM_GOT32: + if (extsym->st_name) + name = strtab + extsym->st_name; + else + name = f->sections[extsym->st_shndx]->name; + intsym = (struct arm_symbol *) obj_find_symbol(f, name); + + ge = (struct arm_got_entry *) &intsym->gotent; + if (! ge->allocated) + { + ge->allocated = 1; + ge->offset = got_offset; + got_offset += sizeof(void*); + } + break; + + default: + continue; + } + } + } + + /* if there was a _GLOBAL_OFFSET_TABLE_, then the .got section + exists already; find it and use it */ + if (got_offset) + { + struct obj_section* sec = obj_find_section(f, ".got"); + if (sec) + obj_extend_section(sec, got_offset); + else + { + sec = obj_create_alloced_section(f, ".got", 8, got_offset); + assert(sec); + } + afile->got = sec; + } + + if (plt_offset) + afile->plt = obj_create_alloced_section(f, ".plt", 8, plt_offset); + + return 1; +} + +int +arch_init_module (struct obj_file *f, struct module *mod) +{ + return 1; +} + +int +arch_finalize_section_address(struct obj_file *f, Elf32_Addr base) +{ + int i, n = f->header.e_shnum; + + f->baseaddr = base; + for (i = 0; i < n; ++i) + f->sections[i]->header.sh_addr += base; + return 1; +} + +int +arch_archdata (struct obj_file *fin, struct obj_section *sec) +{ + return 0; +} diff --git a/mdk-stage1/insmod-modutils/obj/obj_common.c b/mdk-stage1/insmod-modutils/obj/obj_common.c new file mode 100644 index 000000000..2a6606c94 --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_common.c @@ -0,0 +1,399 @@ +/* Elf file, section, and symbol manipulation routines. + Copyright 1996, 1997 Linux International. + + Contributed by Richard Henderson <rth@tamu.edu> + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ident "$Id$" + +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include <obj.h> +#include <util.h> +#include <module.h> + +/*======================================================================*/ + +/* Standard ELF hash function. */ +inline unsigned long +obj_elf_hash_n(const char *name, unsigned long n) +{ + unsigned long h = 0; + unsigned long g; + unsigned char ch; + + while (n > 0) + { + ch = *name++; + h = (h << 4) + ch; + if ((g = (h & 0xf0000000)) != 0) + { + h ^= g >> 24; + h &= ~g; + } + n--; + } + return h; +} + +unsigned long +obj_elf_hash (const char *name) +{ + return obj_elf_hash_n(name, strlen(name)); +} + +void +obj_set_symbol_compare (struct obj_file *f, + int (*cmp)(const char *, const char *), + unsigned long (*hash)(const char *)) +{ + if (cmp) + f->symbol_cmp = cmp; + if (hash) + { + struct obj_symbol *tmptab[HASH_BUCKETS], *sym, *next; + int i; + + f->symbol_hash = hash; + + memcpy(tmptab, f->symtab, sizeof(tmptab)); + memset(f->symtab, 0, sizeof(f->symtab)); + + for (i = 0; i < HASH_BUCKETS; ++i) + for (sym = tmptab[i]; sym ; sym = next) + { + unsigned long h = hash(sym->name) % HASH_BUCKETS; + next = sym->next; + sym->next = f->symtab[h]; + f->symtab[h] = sym; + } + } +} + +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); + + 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); + + /* A redefinition! Is it legal? */ + + if (secidx == SHN_UNDEF) + return sym; + else if (o_secidx == SHN_UNDEF) + goto found; + else if (n_binding == STB_GLOBAL && o_binding == STB_LOCAL) + { + /* Cope with local and global symbols of the same name + in the same object file, as might have been created + by ld -r. The only reason locals are now seen at this + level at all is so that we can do semi-sensible things + with parameters. */ + + struct obj_symbol *nsym, **p; + + nsym = arch_new_symbol(); + nsym->next = sym->next; + nsym->ksymidx = -1; + + /* Excise the old (local) symbol from the hash chain. */ + for (p = &f->symtab[hash]; *p != sym; p = &(*p)->next) + continue; + *p = sym = nsym; + goto found; + } + else if (n_binding == STB_LOCAL) + { + /* Another symbol of the same name has already been defined. + Just add this to the local table. */ + sym = arch_new_symbol(); + sym->next = NULL; + sym->ksymidx = -1; + f->local_symtab[symidx] = sym; + goto found; + } + else if (n_binding == STB_WEAK) + return sym; + else if (o_binding == STB_WEAK) + goto found; + /* 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)) + return sym; + else if (o_secidx == SHN_COMMON + && (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("%s multiply defined", name); + return sym; + } + } + + /* Completely new symbol. */ + sym = arch_new_symbol(); + sym->next = f->symtab[hash]; + f->symtab[hash] = sym; + sym->ksymidx = -1; + + if (ELFW(ST_BIND)(info) == STB_LOCAL && symidx != -1) { + if (symidx >= f->local_symtab_size) + error("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: + sym->name = name; + sym->value = value; + sym->size = size; + sym->secidx = secidx; + sym->info = info; + sym->r_type = 0; /* should be R_arch_NONE for all arch */ + + return sym; +} + +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; + + for (sym = f->symtab[hash]; sym; sym = sym->next) + if (f->symbol_cmp(sym->name, name) == 0) + return sym; + + return NULL; +} + +ElfW(Addr) +obj_symbol_final_value (struct obj_file *f, struct obj_symbol *sym) +{ + if (sym) + { + if (sym->secidx >= SHN_LORESERVE) + return sym->value; + + return sym->value + f->sections[sym->secidx]->header.sh_addr; + } + else + { + /* As a special case, a NULL sym has value zero. */ + return 0; + } +} + +struct obj_section * +obj_find_section (struct obj_file *f, const char *name) +{ + int i, n = f->header.e_shnum; + + for (i = 0; i < n; ++i) + if (strcmp(f->sections[i]->name, name) == 0) + return f->sections[i]; + + return NULL; +} + +static int +obj_load_order_prio(struct obj_section *a) +{ + unsigned long af, ac; + + af = a->header.sh_flags; + + ac = 0; + if (a->name[0] != '.' || strlen(a->name) != 10 || + strcmp(a->name + 5, ".init")) ac |= 32; + if (af & SHF_ALLOC) ac |= 16; + if (!(af & SHF_WRITE)) ac |= 8; + if (af & SHF_EXECINSTR) ac |= 4; + if (a->header.sh_type != SHT_NOBITS) ac |= 2; +#if defined(ARCH_ia64) + if (af & SHF_IA_64_SHORT) ac -= 1; +#endif + + return ac; +} + +void +obj_insert_section_load_order (struct obj_file *f, struct obj_section *sec) +{ + struct obj_section **p; + int prio = obj_load_order_prio(sec); + for (p = f->load_order_search_start; *p ; p = &(*p)->load_next) + if (obj_load_order_prio(*p) < prio) + break; + sec->load_next = *p; + *p = sec; +} + +struct obj_section * +obj_create_alloced_section (struct obj_file *f, const char *name, + unsigned long align, unsigned long size) +{ + int newidx = f->header.e_shnum++; + struct obj_section *sec; + + f->sections = xrealloc(f->sections, (newidx+1) * sizeof(sec)); + f->sections[newidx] = sec = arch_new_section(); + + memset(sec, 0, sizeof(*sec)); + sec->header.sh_type = SHT_PROGBITS; + sec->header.sh_flags = SHF_WRITE|SHF_ALLOC; + sec->header.sh_size = size; + sec->header.sh_addralign = align; + sec->name = name; + sec->idx = newidx; + if (size) + sec->contents = xmalloc(size); + + obj_insert_section_load_order(f, sec); + + return sec; +} + +struct obj_section * +obj_create_alloced_section_first (struct obj_file *f, const char *name, + unsigned long align, unsigned long size) +{ + int newidx = f->header.e_shnum++; + struct obj_section *sec; + + f->sections = xrealloc(f->sections, (newidx+1) * sizeof(sec)); + f->sections[newidx] = sec = arch_new_section(); + + memset(sec, 0, sizeof(*sec)); + sec->header.sh_type = SHT_PROGBITS; + sec->header.sh_flags = SHF_WRITE|SHF_ALLOC; + sec->header.sh_size = size; + sec->header.sh_addralign = align; + sec->name = name; + sec->idx = newidx; + if (size) + sec->contents = xmalloc(size); + + sec->load_next = f->load_order; + f->load_order = sec; + if (f->load_order_search_start == &f->load_order) + f->load_order_search_start = &sec->load_next; + + return sec; +} + +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); + return sec->contents + oldsize; +} + +/* Convert an object pointer (address) to a native pointer and vice versa. + * It gets interesting when the object has 64 bit pointers but modutils + * is running 32 bit. This is nasty code but it stops the compiler giving + * spurious warning messages. "I know what I am doing" ... + */ + +void * +obj_addr_to_native_ptr (ElfW(Addr) addr) +{ + unsigned int convert = (sizeof(void *) << 8) + sizeof(addr); /* to, from */ + union obj_ptr_4 p4; + union obj_ptr_8 p8; + switch (convert) { + case 0x0404: + p4.addr = addr; + return(p4.ptr); + break; + case 0x0408: + p4.addr = addr; + if (p4.addr != addr) { + error("obj_addr_to_native_ptr truncation %" tgt_long_fmt "x", + (tgt_long) addr); + exit(1); + } + return(p4.ptr); + break; + case 0x0804: + p8.addr = addr; + return(p8.ptr); + break; + case 0x0808: + p8.addr = addr; + return(p8.ptr); + break; + default: + error("obj_addr_to_native_ptr unknown conversion 0x%04x", convert); + exit(1); + } +} + +ElfW(Addr) +obj_native_ptr_to_addr (void *ptr) +{ + unsigned int convert = (sizeof(ElfW(Addr)) << 8) + sizeof(ptr); /* to, from */ + union obj_ptr_4 p4; + union obj_ptr_8 p8; + switch (convert) { + case 0x0404: + p4.ptr = ptr; + return(p4.addr); + break; + case 0x0408: + p8.ptr = ptr; + p4.addr = p8.addr; + if (p4.addr != p8.addr) { + error("obj_native_ptr_to_addr truncation %" tgt_long_fmt "x", + (tgt_long) p8.addr); + exit(1); + } + return(p4.addr); + break; + case 0x0804: + p4.ptr = ptr; + return(p4.addr); /* compiler expands to 8 */ + break; + case 0x0808: + p8.ptr = ptr; + return(p8.addr); + break; + default: + error("obj_native_ptr_to_addr unknown conversion 0x%04x", convert); + exit(1); + } +} diff --git a/mdk-stage1/insmod-modutils/obj/obj_hppa.c b/mdk-stage1/insmod-modutils/obj/obj_hppa.c new file mode 100644 index 000000000..4207e692e --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_hppa.c @@ -0,0 +1,668 @@ +/* + * PA-RISC specific support for Elf loading and relocation. + * Copyright 2000 David Huggins-Daines <dhd@linuxcare.com>, Linuxcare Inc. + * Copyright 2000 Richard Hirst <rhirst@linuxcare.com>, Linuxcare Inc. + * + * Based on the IA-64 support, which is: + * Copyright 2000 Mike Stephens <mike.stephens@intel.com> + * + * This file is part of the Linux modutils. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include <module.h> +#include <obj.h> +#include <util.h> +#include <modstat.h> /* For ksyms */ + + +typedef struct _hppa_stub_t +{ + struct _hppa_stub_t *next; + int offset; + int reloc_done; +} hppa_stub_t; + +typedef struct _hppa_symbol_t +{ + struct obj_symbol root; + hppa_stub_t *stub; +} hppa_symbol_t; + +typedef struct _hppa_file_t +{ + struct obj_file root; + struct obj_section *stub; + Elf32_Addr dp; +} hppa_file_t; + +/* The ABI defines various more esoteric types, but these are the only + ones we actually need. */ +enum hppa_fsel +{ + e_fsel, + e_lsel, + e_rsel, + e_lrsel, + e_rrsel +}; + +/* This could be a call to obj_create_alloced_section() followed + * by an overwrite of sec->header.sh_flags. + */ + +struct obj_section * +obj_hppa_create_alloced_section (struct obj_file *f, const char *name, + unsigned long align, unsigned long size, + unsigned long sh_flags) +{ + int newidx = f->header.e_shnum++; + struct obj_section *sec; + + f->sections = xrealloc(f->sections, (newidx+1) * sizeof(sec)); + f->sections[newidx] = sec = arch_new_section(); + + memset(sec, 0, sizeof(*sec)); + sec->header.sh_type = SHT_PROGBITS; + sec->header.sh_flags = sh_flags; + sec->header.sh_size = size; + sec->header.sh_addralign = align; + sec->name = name; + sec->idx = newidx; + if (size) + sec->contents = xmalloc(size); + + obj_insert_section_load_order(f, sec); + + return sec; +} + +struct obj_file * +arch_new_file (void) +{ + hppa_file_t *f; + f = xmalloc(sizeof(*f)); + f->stub = NULL; + return &f->root; +} + +struct obj_section * +arch_new_section (void) +{ + return xmalloc(sizeof(struct obj_section)); +} + +struct obj_symbol * +arch_new_symbol (void) +{ + hppa_symbol_t *sym; + sym = xmalloc(sizeof(*sym)); + sym->stub = NULL; + return &sym->root; +} + +/* This is called for architecture specific sections we might need to + do special things to. */ +int +arch_load_proc_section(struct obj_section *sec, int fp) +{ + /* Assume it's just a debugging section that we can safely + ignore ... */ + sec->contents = NULL; + + return 0; +} + +/* ================================================================= + + These functions are from libhppa.h in the GNU BFD library. + (c) 1990, 91, 92, 93, 94, 95, 96, 98, 99, 2000 + Free Software Foundation, Inc. + + ================================================================= */ + +/* The *sign_extend functions are used to assemble various bitfields + taken from an instruction and return the resulting immediate + value. */ + +static inline int +sign_extend (x, len) + int x, len; +{ + int signbit = (1 << (len - 1)); + int mask = (signbit << 1) - 1; + return ((x & mask) ^ signbit) - signbit; +} + +static inline int +low_sign_extend (x, len) + int x, len; +{ + return (x >> 1) - ((x & 1) << (len - 1)); +} + + +/* The re_assemble_* functions prepare an immediate value for + insertion into an opcode. pa-risc uses all sorts of weird bitfields + in the instruction to hold the value. */ + +static inline int +sign_unext (x, len) + int x, len; +{ + int len_ones; + + len_ones = (1 << len) - 1; + + return x & len_ones; +} + +static inline int +low_sign_unext (x, len) + int x, len; +{ + int temp; + int sign; + + sign = (x >> (len-1)) & 1; + + temp = sign_unext (x, len-1); + + return (temp << 1) | sign; +} + +static inline int +re_assemble_3 (as3) + int as3; +{ + return (( (as3 & 4) << (13-2)) + | ((as3 & 3) << (13+1))); +} + +static inline int +re_assemble_12 (as12) + int as12; +{ + return (( (as12 & 0x800) >> 11) + | ((as12 & 0x400) >> (10 - 2)) + | ((as12 & 0x3ff) << (1 + 2))); +} + +static inline int +re_assemble_14 (as14) + int as14; +{ + return (( (as14 & 0x1fff) << 1) + | ((as14 & 0x2000) >> 13)); +} + +static inline int +re_assemble_16 (as16) + int as16; +{ + int s, t; + + /* Unusual 16-bit encoding, for wide mode only. */ + t = (as16 << 1) & 0xffff; + s = (as16 & 0x8000); + return (t ^ s ^ (s >> 1)) | (s >> 15); +} + +static inline int +re_assemble_17 (as17) + int as17; +{ + return (( (as17 & 0x10000) >> 16) + | ((as17 & 0x0f800) << (16 - 11)) + | ((as17 & 0x00400) >> (10 - 2)) + | ((as17 & 0x003ff) << (1 + 2))); +} + +static inline int +re_assemble_21 (as21) + int as21; +{ + return (( (as21 & 0x100000) >> 20) + | ((as21 & 0x0ffe00) >> 8) + | ((as21 & 0x000180) << 7) + | ((as21 & 0x00007c) << 14) + | ((as21 & 0x000003) << 12)); +} + +static inline int +re_assemble_22 (as22) + int as22; +{ + return (( (as22 & 0x200000) >> 21) + | ((as22 & 0x1f0000) << (21 - 16)) + | ((as22 & 0x00f800) << (16 - 11)) + | ((as22 & 0x000400) >> (10 - 2)) + | ((as22 & 0x0003ff) << (1 + 2))); +} + + +/* Handle field selectors for PA instructions. + The L and R (and LS, RS etc.) selectors are used in pairs to form a + full 32 bit address. eg. + + LDIL L'start,%r1 ; put left part into r1 + LDW R'start(%r1),%r2 ; add r1 and right part to form address + + This function returns sign extended values in all cases. +*/ + +static inline unsigned int +hppa_field_adjust (value, addend, r_field) + unsigned int value; + int addend; + enum hppa_fsel r_field; +{ + unsigned int sym_val; + + sym_val = value - addend; + switch (r_field) + { + case e_fsel: + /* F: No change. */ + break; + + case e_lsel: + /* L: Select top 21 bits. */ + value = value >> 11; + break; + + case e_rsel: + /* R: Select bottom 11 bits. */ + value = value & 0x7ff; + break; + + case e_lrsel: + /* LR: L with rounding of the addend to nearest 8k. */ + value = sym_val + ((addend + 0x1000) & -0x2000); + value = value >> 11; + break; + + case e_rrsel: + /* RR: R with rounding of the addend to nearest 8k. + We need to return a value such that 2048 * LR'x + RR'x == x + ie. RR'x = s+a - (s + (((a + 0x1000) & -0x2000) & -0x800)) + . = s+a - ((s & -0x800) + ((a + 0x1000) & -0x2000)) + . = (s & 0x7ff) + a - ((a + 0x1000) & -0x2000) */ + value = (sym_val & 0x7ff) + (((addend & 0x1fff) ^ 0x1000) - 0x1000); + break; + + default: + abort(); + } + return value; +} + +/* Insert VALUE into INSN using R_FORMAT to determine exactly what + bits to change. */ + +static inline int +hppa_rebuild_insn (insn, value, r_format) + int insn; + int value; + int r_format; +{ + switch (r_format) + { + case 11: + return (insn & ~ 0x7ff) | low_sign_unext (value, 11); + + case 12: + return (insn & ~ 0x1ffd) | re_assemble_12 (value); + + + case 10: + return (insn & ~ 0x3ff1) | re_assemble_14 (value & -8); + + case -11: + return (insn & ~ 0x3ff9) | re_assemble_14 (value & -4); + + case 14: + return (insn & ~ 0x3fff) | re_assemble_14 (value); + + + case -10: + return (insn & ~ 0xfff1) | re_assemble_16 (value & -8); + + case -16: + return (insn & ~ 0xfff9) | re_assemble_16 (value & -4); + + case 16: + return (insn & ~ 0xffff) | re_assemble_16 (value); + + + case 17: + return (insn & ~ 0x1f1ffd) | re_assemble_17 (value); + + case 21: + return (insn & ~ 0x1fffff) | re_assemble_21 (value); + + case 22: + return (insn & ~ 0x3ff1ffd) | re_assemble_22 (value); + + case 32: + return value; + + default: + abort (); + } + return insn; +} + +/* ==================================================================== + + End of functions from GNU BFD. + + ==================================================================== */ + +/* This is where we get the opportunity to create any extra dynamic + sections we might need. In our case we do not need a GOT because + our code is not PIC, but we do need to create a stub section. + + This is significantly less complex than what we do for shared + libraries because, obviously, modules are not shared. Also we have + no issues related to symbol visibility, lazy linking, etc. + The kernels dp is fixed (at symbol data_start), and we can fix up any + DPREL refs in the module to use that same dp value. + All PCREL17F refs result in a stub with the following format: + + ldil L'func_addr,%r1 + be,n R'func_addr(%sr4,%r1) + + Note, all PCREL17F get a stub, regardless of whether they are + local or external. With local ones, and external ones to other + modules, there is a good chance we could manage without the stub. + I'll leave that for a future optimisation. + */ + +#define LDIL_R1 0x20200000 /* ldil L'XXX,%r1 */ +#define BE_N_SR4_R1 0xe0202002 /* be,n R'XXX(%sr4,%r1) */ + +#define STUB_SIZE 8 + +int +arch_create_got(struct obj_file *f) +{ + hppa_file_t *hfile = (hppa_file_t *)f; + int i, n; + int stub_offset = 0; + + /* Create stub section. + * XXX set flags, see obj_ia64.c + */ + hfile->stub = obj_create_alloced_section(f, ".stub", STUB_SIZE, 0); + + /* Actually this is a lot like check_relocs() in a BFD backend. We + walk all sections and all their relocations and look for ones + that need special treatment. */ + n = hfile->root.header.e_shnum; + for (i = 0; i < n; ++i) + { + struct obj_section *relsec, *symsec, *strsec; + Elf32_Rela *rel, *relend; + Elf32_Sym *symtab; + char const *strtab; + + relsec = hfile->root.sections[i]; + if (relsec->header.sh_type != SHT_RELA) + continue; + + symsec = hfile->root.sections[relsec->header.sh_link]; + strsec = hfile->root.sections[symsec->header.sh_link]; + + rel = (Elf32_Rela *)relsec->contents; + relend = rel + (relsec->header.sh_size / sizeof(Elf32_Rela)); + symtab = (Elf32_Sym *)symsec->contents; + strtab = (char const *)strsec->contents; + + for (; rel < relend; rel++) + { + int need_stub = 0; + + switch (ELF32_R_TYPE(rel->r_info)) + { + default: + continue; + + case R_PARISC_PCREL17F: + need_stub = 1; + break; + } + + if (need_stub) + { + Elf32_Sym *extsym; + hppa_symbol_t *hsym; + char const *name; + int local; + unsigned long symndx; + + symndx = ELF32_R_SYM(rel->r_info); + extsym = symtab + symndx; + if (ELF32_ST_BIND(extsym->st_info) == STB_LOCAL) + hsym = (hppa_symbol_t *) f->local_symtab[symndx]; + else + { + if (extsym->st_name) + name = strtab + extsym->st_name; + else + name = f->sections[extsym->st_shndx]->name; + hsym = (hppa_symbol_t *)obj_find_symbol(f, name); + } + local = hsym->root.secidx <= SHN_HIRESERVE; + + if (need_stub) + { + hppa_stub_t *stub; + + if (hsym->stub == NULL) + { + stub = (hppa_stub_t *) xmalloc(sizeof(hppa_stub_t)); + stub->offset = stub_offset; + stub->reloc_done = 0; + hsym->stub = stub; + stub_offset += STUB_SIZE; + need_stub = 0; + } + } + } + } + } + if (stub_offset) + { + hfile->stub->contents = xmalloc(stub_offset); + hfile->stub->header.sh_size = stub_offset; + } + return 1; +} + + +enum obj_reloc +arch_apply_relocation(struct obj_file *f, + struct obj_section *targsec, + struct obj_section *symsec, + struct obj_symbol *sym, + Elf32_Rela *rel, + Elf32_Addr v) +{ + hppa_file_t *hfile = (hppa_file_t *) f; + hppa_symbol_t *hsym = (hppa_symbol_t *) sym; + + Elf32_Addr *loc = (Elf32_Addr *)(targsec->contents + rel->r_offset); + Elf32_Addr dot = (targsec->header.sh_addr + rel->r_offset) & ~0x03; + Elf32_Addr dp = hfile->dp; + Elf32_Word r_info = ELF32_R_TYPE(rel->r_info); + + enum obj_reloc ret = obj_reloc_ok; + enum hppa_fsel fsel = e_fsel; /* Avoid compiler warning */ + unsigned int r_format; + + /* Fix up the value, and determine whether we can handle this + relocation. */ + switch (r_info) + { + case R_PARISC_PLABEL32: + case R_PARISC_DIR32: + case R_PARISC_DIR21L: + case R_PARISC_DIR14R: + /* Easy. */ + break; + + case R_PARISC_DPREL21L: + case R_PARISC_DPREL14R: + v -= dp; + break; + + case R_PARISC_PCREL17F: + /* Find an import stub. */ + assert(hsym->stub != NULL); + assert(hfile->stub != NULL); + /* XXX Optimise. We may not need a stub for short branches */ + if (!hsym->stub->reloc_done) { + /* Need to create the .stub entry */ + Elf32_Addr *pstub, stubv; + + pstub = (Elf32_Addr *)(hfile->stub->contents + hsym->stub->offset); + pstub[0] = LDIL_R1; + pstub[1] = BE_N_SR4_R1; + stubv = hppa_field_adjust(v, rel->r_addend, e_lrsel); + pstub[0] = hppa_rebuild_insn(pstub[0], stubv, 21); + stubv = hppa_field_adjust(v, rel->r_addend, e_rrsel); + stubv >>= 2; /* Branch; divide by 4 */ + pstub[1] = hppa_rebuild_insn(pstub[1], stubv, 17); + hsym->stub->reloc_done = 1; + } + v = hsym->stub->offset + hfile->stub->header.sh_addr; + break; + + default: + return obj_reloc_unhandled; + } + + /* Find the field selector. */ + switch (r_info) + { + case R_PARISC_DIR32: + case R_PARISC_PLABEL32: + case R_PARISC_PCREL17F: + fsel = e_fsel; + break; + + case R_PARISC_DPREL21L: + case R_PARISC_DIR21L: + fsel = e_lrsel; + break; + + case R_PARISC_DPREL14R: + case R_PARISC_DIR14R: + fsel = e_rrsel; + break; + } + + v = hppa_field_adjust(v, rel->r_addend, fsel); + + switch (r_info) + { + case R_PARISC_PCREL17F: + case R_PARISC_PCREL17R: + case R_PARISC_PCREL22F: + v = v - dot - 8; + case R_PARISC_DIR17F: + case R_PARISC_DIR17R: + /* This is a branch. Divide the offset by four. */ + v >>= 2; + break; + default: + break; + } + + /* Find the format. */ + switch (r_info) + { + case R_PARISC_DIR32: + case R_PARISC_PLABEL32: + r_format = 32; + break; + + case R_PARISC_DPREL21L: + case R_PARISC_DIR21L: + r_format = 21; + break; + + case R_PARISC_PCREL17F: + r_format = 17; + break; + + case R_PARISC_DPREL14R: + case R_PARISC_DIR14R: + r_format = 14; + break; + + default: + abort(); + } + + *loc = hppa_rebuild_insn(*loc, v, r_format); + + return ret; +} + +int +arch_init_module (struct obj_file *f, struct module *mod) +{ + return 1; +} + +int +arch_finalize_section_address(struct obj_file *f, Elf32_Addr base) +{ + int i, n = f->header.e_shnum; + + f->baseaddr = base; + for (i = 0; i < n; ++i) + f->sections[i]->header.sh_addr += base; + return 1; +} + +int +arch_archdata (struct obj_file *f, struct obj_section *sec) +{ + struct module_symbol *s; + int i; + hppa_file_t *hfile = (hppa_file_t *)f; + + /* Initialise dp to the kernels dp (symbol data_start) + */ + for (i = 0, s = ksyms; i < nksyms; i++, s++) + if (!strcmp((char *)s->name, "data_start")) + break; + if (i >= nksyms) { + error("Cannot initialise dp, 'data_start' not found\n"); + return 1; + } + hfile->dp = s->value; + + return 0; +} + diff --git a/mdk-stage1/insmod-modutils/obj/obj_hppa64.c b/mdk-stage1/insmod-modutils/obj/obj_hppa64.c new file mode 100644 index 000000000..fe32911ff --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_hppa64.c @@ -0,0 +1,686 @@ +/* + * hppa parisc64 specific support for Elf loading and relocation. + * Copyright 2000 Richard Hirst <rhirst@linuxcare.com>, Linuxcare Inc. + * + * Based on ia64 specific support which was + * Copyright 2000 Mike Stephens <mike.stephens@intel.com> + * + * This file is part of the Linux modutils. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ident "$Id$" + +#include <string.h> +#include <assert.h> +#include <stdlib.h> + +#include <module.h> +#include <obj.h> +#include <util.h> + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE ~FALSE +#endif + +/*======================================================================*/ + +typedef struct _hppa64_opd_t +{ + int offset; + int reloc_done; +} hppa64_opd_t; + +typedef struct _hppa64_stub_t +{ + struct _hppa64_stub_t *next; + Elf64_Addr addend; + int offset; + int reloc_done; +} hppa64_stub_t; + +typedef struct _hppa64_got_t +{ + struct _hppa64_got_t *next; + Elf64_Addr addend; + int offset; + int reloc_done; +} hppa64_got_t; + +typedef struct _hppa64_symbol_t +{ + struct obj_symbol root; + hppa64_got_t *gotent; + hppa64_opd_t *opdent; + hppa64_stub_t *stubent; +} hppa64_symbol_t; + +typedef struct _hppa64_file_t +{ + struct obj_file root; + struct obj_section *got; + struct obj_section *opd; + struct obj_section *stub; + Elf64_Addr gp; + Elf64_Addr text; + Elf64_Addr data; + Elf64_Addr bss; +} hppa64_file_t; + +/* + * XXX This stub assumes it can reach the .got entry with a +/- 8K offset + * from dp. Perhaps we should use a .plt for these entries to give a + * greater chance of that being true. + * + * 53 7b 00 00 ldd 0(dp),dp + * R_PARISC_LTOFF14R <.got entry offset from dp> + * 53 61 00 20 ldd 10(dp),r1 + * e8 20 d0 00 bve (r1) + * 53 7b 00 30 ldd 18(dp),dp + * + * We need a different stub for millicode calls, which doesn't screw + * dp: + * + * 53 61 00 00 ldd 0(dp),r1 + * R_PARISC_LTOFF14R <.got entry offset from dp> + * 50 21 00 20 ldd 10(r1),r1 + * e8 20 d0 00 bve (r1) + * 08 00 02 40 nop + * + */ + +/* NOTE: to keep the code cleaner we make all stubs the same size. + */ + +#define SIZEOF_STUB 16 + +unsigned char hppa64_stub_extern[] = +{ + 0x53, 0x7b, 0x00, 0x00, + 0x53, 0x61, 0x00, 0x20, + 0xe8, 0x20, 0xd0, 0x00, + 0x53, 0x7b, 0x00, 0x30, +}; + +unsigned char hppa64_stub_millicode[] = +{ + 0x53, 0x61, 0x00, 0x00, + 0x50, 0x21, 0x00, 0x20, + 0xe8, 0x20, 0xd0, 0x00, + 0x08, 0x00, 0x02, 0x40, +}; + +/*======================================================================*/ + +enum obj_reloc +patch_14r(Elf64_Xword v64, Elf64_Word *p) +{ + Elf64_Word i = *p; + Elf64_Word v = (Elf64_Word)v64; + + if (v & 0x80000000) + v |= ~0x7ff; + else + v &= 0x7ff; + i &= ~ 0x3fff; + i |= (v & 0x1fff) << 1 | + (v & 0x2000) >> 13; + *p = i; + + return obj_reloc_ok; +} + +enum obj_reloc +patch_21l(Elf64_Xword v64, Elf64_Word *p) +{ + Elf64_Word i = *p; + Elf64_Word v = (Elf64_Word)v64; + + v &= 0xfffff800; + if (v & 0x80000000) + v += 0x800; + i &= ~ 0x1fffff; + i |= (v & 0x80000000) >> 31 | + (v & 0x7ff00000) >> 19 | + (v & 0x000c0000) >> 4 | + (v & 0x0003e000) << 3 | + (v & 0x00001800) << 1; + *p = i; + + return obj_reloc_ok; +} + + +/* All 14 bits this time... This is used to patch the .got offset in + * a stub for PCREL22F. + */ + +enum obj_reloc +patch_14r2(Elf64_Xword v64, Elf64_Word *p) +{ + Elf64_Word i = *p; + Elf64_Word v = (Elf64_Word)v64; + + if ((Elf64_Sxword)v64 > 0x1fffL || + (Elf64_Sxword)v64 < -0x2000L) + return obj_reloc_overflow; + i &= ~ 0x3fff; + i |= (v & 0x2000) >> 13 | + (v & 0x1fff) << 1; + *p = i; + + return obj_reloc_ok; +} + + +enum obj_reloc +patch_22f(Elf64_Xword v64, Elf64_Word *p) +{ + Elf64_Word i = *p; + Elf64_Word v = (Elf64_Word)v64; + + if ((Elf64_Sxword)v64 > 0x800000-1 || + (Elf64_Sxword)v64 < -0x800000) + return obj_reloc_overflow; + + i &= ~ 0x03ff1ffd; + i |= (v & 0x00800000) >> 23 | + (v & 0x007c0000) << 3 | + (v & 0x0003e000) << 3 | + (v & 0x00001000) >> 10 | + (v & 0x00000ffc) << 1; + *p = i; + + return obj_reloc_ok; +} + + +struct obj_section * +obj_hppa64_create_alloced_section (struct obj_file *f, const char *name, + unsigned long align, unsigned long size, unsigned long sh_flags) +{ + int newidx = f->header.e_shnum++; + struct obj_section *sec; + + f->sections = xrealloc(f->sections, (newidx+1) * sizeof(sec)); + f->sections[newidx] = sec = arch_new_section(); + + memset(sec, 0, sizeof(*sec)); + sec->header.sh_type = SHT_PROGBITS; + sec->header.sh_flags = sh_flags; + sec->header.sh_size = size; + sec->header.sh_addralign = align; + sec->name = name; + sec->idx = newidx; + if (size) + sec->contents = xmalloc(size); + + obj_insert_section_load_order(f, sec); + + return sec; +} + +/*======================================================================*/ + +struct obj_file * +arch_new_file (void) +{ + hppa64_file_t *f; + f = xmalloc(sizeof(*f)); + f->got = NULL; + f->opd = NULL; + f->stub = NULL; + return &f->root; +} + +struct obj_section * +arch_new_section (void) +{ + return xmalloc(sizeof(struct obj_section)); +} + +struct obj_symbol * +arch_new_symbol (void) +{ + hppa64_symbol_t *sym; + sym = xmalloc(sizeof(*sym)); + sym->gotent = NULL; + sym->opdent = NULL; + sym->stubent = NULL; + return &sym->root; +} + +/* This may not be needed, but does no harm (copied from ia64). + */ + +int +arch_load_proc_section(struct obj_section *sec, int fp) +{ + switch (sec->header.sh_type) + { + case SHT_PARISC_EXT : + sec->contents = NULL; + break; + + case SHT_PARISC_UNWIND : + if (sec->header.sh_size > 0) + { + sec->contents = xmalloc(sec->header.sh_size); + gzf_lseek(fp, sec->header.sh_offset, SEEK_SET); + if (gzf_read(fp, sec->contents, sec->header.sh_size) != sec->header.sh_size) + { + error("error reading ELF section data: %m"); + return -1; + } + } + else + sec->contents = NULL; + break; + default: + error("Unknown section header type: %08x", sec->header.sh_type); + return -1; + } + return 0; +} + +int +arch_create_got(struct obj_file *f) +{ + hppa64_file_t *hfile = (hppa64_file_t *)f; + int i; + int n; + int got_offset = 0; + int opd_offset = 64; + int stub_offset = 0; + + n = hfile->root.header.e_shnum; + for (i = 0; i < n; ++i) + { + struct obj_section *relsec, *symsec, *strsec; + Elf64_Rela *rel, *relend; + Elf64_Sym *symtab; + const char *strtab; + + relsec = hfile->root.sections[i]; + if (relsec->header.sh_type != SHT_RELA) + continue; + + symsec = hfile->root.sections[relsec->header.sh_link]; + strsec = hfile->root.sections[symsec->header.sh_link]; + + rel = (Elf64_Rela *)relsec->contents; + relend = rel + (relsec->header.sh_size / sizeof(Elf64_Rela)); + symtab = (Elf64_Sym *)symsec->contents; + strtab = (const char *)strsec->contents; + + for (; rel < relend; ++rel) + { + int need_got = FALSE; + int need_opd = FALSE; + int need_stub = FALSE; + + switch (ELF64_R_TYPE(rel->r_info)) + { + default: + { + unsigned r_info = ELF64_R_TYPE(rel->r_info); + printf("r_info 0x%x not handled\n", r_info); + } + continue; + case R_PARISC_LTOFF14R: + case R_PARISC_LTOFF21L: + /* These are simple indirect references to symbols through the + * DLT. We need to create a DLT entry for any symbols which + * appears in a DLTIND relocation. + */ + need_got = TRUE; + break; + case R_PARISC_PCREL22F: + /* These are function calls. Depending on their precise + * target we may need to make a stub for them. The stub + * uses the dlt, so we need to create dlt entries for + * these symbols too. + */ + need_got = TRUE; + need_stub = TRUE; + break; + case R_PARISC_DIR64: + break; + case R_PARISC_FPTR64: + /* This is a simple OPD entry (only created for local symbols, + * see below). + */ + need_opd = TRUE; + break; + } + + if (need_got || need_opd || need_stub) + { + Elf64_Sym *extsym; + hppa64_symbol_t *isym; + const char *name; + int local; + unsigned long symndx; + + symndx = ELF64_R_SYM(rel->r_info); + extsym = &symtab[symndx]; + if (ELF64_ST_BIND(extsym->st_info) == STB_LOCAL) + { + isym = (hppa64_symbol_t *) f->local_symtab[symndx]; + } + else + { + if (extsym->st_name) + name = strtab + extsym->st_name; + else + name = f->sections[extsym->st_shndx]->name; + isym = (hppa64_symbol_t *)obj_find_symbol(f, name); + } + local = isym->root.secidx <= SHN_HIRESERVE; + + if (need_stub) + { + hppa64_stub_t *stub; + + for (stub = isym->stubent; stub != NULL; stub = stub->next) + if (stub->addend == rel->r_addend) + break; + if (stub == NULL) + { + stub = (hppa64_stub_t *) xmalloc(sizeof(hppa64_stub_t)); + stub->next = isym->stubent; + stub->addend = rel->r_addend; + stub->offset = stub_offset; + stub->reloc_done = FALSE; + isym->stubent = stub; + { + stub_offset += SIZEOF_STUB; + } + need_stub = FALSE; + } + } + if (need_got) + { + hppa64_got_t *got; + + for (got = isym->gotent; got != NULL; got = got->next) + if (got->addend == rel->r_addend) + break; + if (got == NULL) + { + got = (hppa64_got_t *) xmalloc(sizeof(hppa64_got_t)); + got->next = isym->gotent; + got->addend = rel->r_addend; + got->offset = got_offset; + got->reloc_done = FALSE; + isym->gotent = got; + got_offset += 8; + need_got = FALSE; + } + } + if (need_opd && local) + { + hppa64_opd_t *opd; + + if (isym->opdent == NULL) + { + opd = (hppa64_opd_t *) xmalloc(sizeof(hppa64_opd_t)); + opd->offset = opd_offset; + opd->reloc_done = FALSE; + isym->opdent = opd; + opd_offset += 32; + need_opd = FALSE; + } + } + } + } + } + + hfile->got = obj_hppa64_create_alloced_section(f, ".got", 8, got_offset, + (SHF_ALLOC | SHF_WRITE | SHF_PARISC_SHORT)); + assert(hfile->got != NULL); + + hfile->opd = obj_hppa64_create_alloced_section(f, ".opd", 16, opd_offset, + (SHF_ALLOC | SHF_WRITE | SHF_PARISC_SHORT)); + assert(hfile->opd != NULL); + + if (stub_offset > 0) + { + hfile->stub = obj_hppa64_create_alloced_section(f, ".stub", 16, + stub_offset, (SHF_ALLOC | SHF_EXECINSTR | SHF_PARISC_SHORT)); + assert(hfile->stub != NULL); + } + + return 1; +} + + +/* This is a small simple version which seems to work fine. ia64 has + * a much more complex algorithm. We point dp at the end of the .got, + * which is the start of the .opd. + */ + +int +arch_finalize_section_address(struct obj_file *f, Elf64_Addr base) +{ + hppa64_file_t *hfile = (hppa64_file_t *)f; + int n = f->header.e_shnum; + int i; + + f->baseaddr = base; + for (i = 0; i < n; ++i) + f->sections[i]->header.sh_addr += base; + + /* Pick a sensible value for gp */ + hfile->gp = hfile->got->header.sh_addr + hfile->got->header.sh_size; + + return 1; +} + + +enum obj_reloc +arch_apply_relocation(struct obj_file *f, + struct obj_section *targsec, + struct obj_section *symsec, + struct obj_symbol *sym, + Elf64_Rela *rel, + Elf64_Addr v) +{ + hppa64_file_t *hfile = (hppa64_file_t *) f; + hppa64_symbol_t *isym = (hppa64_symbol_t *) sym; + + Elf64_Word *loc = (Elf64_Word *)(targsec->contents + rel->r_offset); + Elf64_Addr dot = (targsec->header.sh_addr + rel->r_offset) & ~0x03; + + Elf64_Addr got = hfile->got->header.sh_addr; + Elf64_Addr gp = hfile->gp; + + Elf64_Xword r_info = ELF64_R_TYPE(rel->r_info); + + enum obj_reloc ret = obj_reloc_ok; + + switch (r_info) + { + default: + ret = obj_reloc_unhandled; + break; + case R_PARISC_LTOFF14R: + case R_PARISC_LTOFF21L: + { + hppa64_got_t *ge; + + assert(isym != NULL); + for (ge = isym->gotent; ge != NULL && ge->addend != rel->r_addend; ) + ge = ge->next; + assert(ge != NULL); + if (!ge->reloc_done) + { + ge->reloc_done = TRUE; + *(Elf64_Addr *)(hfile->got->contents + ge->offset) = v; + } + v = got + ge->offset - gp; + if (r_info == R_PARISC_LTOFF14R) + ret = patch_14r(v, loc); + else + ret = patch_21l(v, loc); + } + break; + case R_PARISC_PCREL22F: + { + hppa64_got_t *ge; + + assert(isym != NULL); + for (ge = isym->gotent; ge != NULL && ge->addend != rel->r_addend; ) + ge = ge->next; + assert(ge != NULL); + if (!ge->reloc_done) + { + ge->reloc_done = TRUE; + *(Elf64_Addr *)(hfile->got->contents + ge->offset) = v; + } + if ((isym->root.secidx > SHN_HIRESERVE) || + ((Elf64_Sxword) (v - dot - 8) > 0x800000-1) || + ((Elf64_Sxword) (v - dot - 8) < -0x800000)) + { + hppa64_stub_t *se; + + for (se = isym->stubent; se != NULL && se->addend != rel->r_addend; ) + se = se->next; + assert(se != NULL); + if (!se->reloc_done) + { + /* This requires that we can get from dp to the entry in +/- 8K, + * or +/- 1000 entries. patch_14r2() will check that. + * Only need these dlt entries for calls to external/far + * functions, so should probably put them in a seperate section + * before dlt and point dp at the section. Change to that + * scheme if we hit problems with big modules. + */ + unsigned char *stub; + + if (!strncmp(isym->root.name, "$$", 2)) + stub = hppa64_stub_millicode; + else + stub = hppa64_stub_extern; + se->reloc_done = TRUE; + memcpy((Elf64_Addr *)(hfile->stub->contents + se->offset), + stub, SIZEOF_STUB); + v = (Elf64_Addr)(hfile->got->header.sh_addr + ge->offset) - gp; + ret = patch_14r2(v, (Elf64_Word *)(hfile->stub->contents + se->offset)); + } + v = hfile->stub->header.sh_addr + se->offset; + } + v = v - dot - 8; + if (ret == obj_reloc_ok) + ret = patch_22f(v, loc); + } + break; + case R_PARISC_DIR64: + { + loc[0] = v >> 32; + loc[1] = v; + } + break; + case R_PARISC_FPTR64: + { + assert(isym != NULL); + if (isym->root.secidx <= SHN_HIRESERVE) /* local */ + { + assert(isym->opdent != NULL); + if (!isym->opdent->reloc_done) + { + isym->opdent->reloc_done = TRUE; + *(Elf64_Addr *)(hfile->opd->contents + isym->opdent->offset + 16) = v; + *(Elf64_Addr *)(hfile->opd->contents + isym->opdent->offset + 24) = gp; + } + v = hfile->opd->header.sh_addr + isym->opdent->offset; + } + loc[0] = v >> 32; + loc[1] = v; + } + break; + } + return ret; +} + +int +arch_init_module (struct obj_file *f, struct module *mod) +{ + hppa64_file_t *hfile = (hppa64_file_t *)f; + Elf64_Addr *opd = (Elf64_Addr *)(hfile->opd->contents); + + opd[0] = 0; + opd[1] = 0; + if ((opd[2] = mod->init) != 0) + { + opd[3] = hfile->gp; + mod->init = hfile->opd->header.sh_addr; + } + + opd[4] = 0; + opd[5] = 0; + if ((opd[6] = mod->cleanup) != 0) + { + opd[7] = hfile->gp; + mod->cleanup = hfile->opd->header.sh_addr + 32; + } + + return 1; +} + +/* XXX Is this relevant to parisc? */ + +int +arch_archdata (struct obj_file *f, struct obj_section *archdata_sec) +{ + hppa64_file_t *hfile = (hppa64_file_t *)f; + struct archdata { + unsigned tgt_long unw_table; + unsigned tgt_long segment_base; + unsigned tgt_long unw_start; + unsigned tgt_long unw_end; + unsigned tgt_long gp; + } *ad; + int i; + struct obj_section *sec; + + free(archdata_sec->contents); + archdata_sec->contents = xmalloc(sizeof(struct archdata)); + memset(archdata_sec->contents, 0, sizeof(struct archdata)); + archdata_sec->header.sh_size = sizeof(struct archdata); + + ad = (struct archdata *)(archdata_sec->contents); + ad->gp = hfile->gp; + ad->unw_start = 0; + ad->unw_end = 0; + ad->unw_table = 0; + ad->segment_base = f->sections[1]->header.sh_addr; + for (i = 0; i < f->header.e_shnum; ++i) + { + sec = f->sections[i]; + if (sec->header.sh_type == SHT_PARISC_UNWIND) + { + ad->unw_start = sec->header.sh_addr; + ad->unw_end = sec->header.sh_addr + sec->header.sh_size; + break; + } + } + + return 0; +} diff --git a/mdk-stage1/insmod-modutils/obj/obj_i386.c b/mdk-stage1/insmod-modutils/obj/obj_i386.c new file mode 100644 index 000000000..28df3448c --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_i386.c @@ -0,0 +1,245 @@ +/* i386 specific support for Elf loading and relocation. + Copyright 1996, 1997 Linux International. + + Contributed by Richard Henderson <rth@tamu.edu> + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ident "$Id$" + +#include <string.h> +#include <assert.h> + +#include <module.h> +#include <obj.h> +#include <util.h> + + +/*======================================================================*/ + +struct i386_got_entry +{ + int offset; + unsigned offset_done : 1; + unsigned reloc_done : 1; +}; + +struct i386_file +{ + struct obj_file root; + struct obj_section *got; +}; + +struct i386_symbol +{ + struct obj_symbol root; + struct i386_got_entry gotent; +}; + + +/*======================================================================*/ + +struct obj_file * +arch_new_file (void) +{ + struct i386_file *f; + f = xmalloc(sizeof(*f)); + f->got = NULL; + return &f->root; +} + +struct obj_section * +arch_new_section (void) +{ + return xmalloc(sizeof(struct obj_section)); +} + +struct obj_symbol * +arch_new_symbol (void) +{ + struct i386_symbol *sym; + sym = xmalloc(sizeof(*sym)); + memset(&sym->gotent, 0, sizeof(sym->gotent)); + return &sym->root; +} + +int +arch_load_proc_section(struct obj_section *sec, int fp) +{ + /* Assume it's just a debugging section that we can safely + ignore ... */ + sec->contents = NULL; + + return 0; +} + +enum obj_reloc +arch_apply_relocation (struct obj_file *f, + struct obj_section *targsec, + struct obj_section *symsec, + struct obj_symbol *sym, + Elf32_Rel *rel, + Elf32_Addr v) +{ + struct i386_file *ifile = (struct i386_file *)f; + struct i386_symbol *isym = (struct i386_symbol *)sym; + + Elf32_Addr *loc = (Elf32_Addr *)(targsec->contents + rel->r_offset); + Elf32_Addr dot = targsec->header.sh_addr + rel->r_offset; + Elf32_Addr got = ifile->got ? ifile->got->header.sh_addr : 0; + + enum obj_reloc ret = obj_reloc_ok; + + switch (ELF32_R_TYPE(rel->r_info)) + { + 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: + assert(isym != NULL); + if (!isym->gotent.reloc_done) + { + isym->gotent.reloc_done = 1; + *(Elf32_Addr *)(ifile->got->contents + isym->gotent.offset) = v; + } + *loc += isym->gotent.offset; + break; + + case R_386_GOTOFF: + assert(got != 0); + *loc += v - got; + break; + + default: + ret = obj_reloc_unhandled; + break; + } + + return ret; +} + +int +arch_create_got (struct obj_file *f) +{ + struct i386_file *ifile = (struct i386_file *)f; + int i, n, offset = 0, gotneeded = 0; + + n = ifile->root.header.e_shnum; + for (i = 0; i < n; ++i) + { + struct obj_section *relsec, *symsec, *strsec; + Elf32_Rel *rel, *relend; + Elf32_Sym *symtab; + const char *strtab; + + relsec = ifile->root.sections[i]; + if (relsec->header.sh_type != SHT_REL) + continue; + + symsec = ifile->root.sections[relsec->header.sh_link]; + strsec = ifile->root.sections[symsec->header.sh_link]; + + rel = (Elf32_Rel *)relsec->contents; + relend = rel + (relsec->header.sh_size / sizeof(Elf32_Rel)); + symtab = (Elf32_Sym *)symsec->contents; + strtab = (const char *)strsec->contents; + + for (; rel < relend; ++rel) + { + Elf32_Sym *extsym; + struct i386_symbol *intsym; + const char *name; + + switch (ELF32_R_TYPE(rel->r_info)) + { + case R_386_GOTPC: + case R_386_GOTOFF: + gotneeded = 1; + default: + continue; + + case R_386_GOT32: + break; + } + + extsym = &symtab[ELF32_R_SYM(rel->r_info)]; + if (extsym->st_name) + name = strtab + extsym->st_name; + else + name = f->sections[extsym->st_shndx]->name; + intsym = (struct i386_symbol *)obj_find_symbol(&ifile->root, name); + + if (!intsym->gotent.offset_done) + { + intsym->gotent.offset_done = 1; + intsym->gotent.offset = offset; + offset += 4; + } + } + } + + if (offset > 0 || gotneeded) + ifile->got = obj_create_alloced_section(&ifile->root, ".got", 4, offset); + + return 1; +} + +int +arch_init_module (struct obj_file *f, struct module *mod) +{ + return 1; +} + +int +arch_finalize_section_address(struct obj_file *f, Elf32_Addr base) +{ + int i, n = f->header.e_shnum; + + f->baseaddr = base; + for (i = 0; i < n; ++i) + f->sections[i]->header.sh_addr += base; + return 1; +} + +int +arch_archdata (struct obj_file *fin, struct obj_section *sec) +{ + return 0; +} diff --git a/mdk-stage1/insmod-modutils/obj/obj_ia64.c b/mdk-stage1/insmod-modutils/obj/obj_ia64.c new file mode 100644 index 000000000..4f92c5d27 --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_ia64.c @@ -0,0 +1,1065 @@ +/* + * ia64 specific support for Elf loading and relocation. + * Copyright 2000 Mike Stephens <mike.stephens@intel.com> + * + * This file is part of the Linux modutils. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ident "$Id$" + +#include <string.h> +#include <assert.h> +#include <stdlib.h> + +#include <module.h> +#include <obj.h> +#include <util.h> + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE ~FALSE +#endif + +/*======================================================================*/ + +typedef struct _ia64_opd_t +{ + int offset; + int reloc_done; +} ia64_opd_t; + +typedef struct _ia64_plt_t +{ + struct _ia64_plt_t *next; + Elf64_Addr addend; + int text_offset; + int data_offset; + int reloc_done; +} ia64_plt_t; + +typedef struct _ia64_got_t +{ + struct _ia64_got_t *next; + Elf64_Addr addend; + int offset; + int reloc_done; +} ia64_got_t; + +typedef struct _ia64_symbol_t +{ + struct obj_symbol root; + ia64_got_t *gotent; + ia64_opd_t *opdent; + ia64_plt_t *pltent; +} ia64_symbol_t; + +typedef struct _ia64_file_t +{ + struct obj_file root; + struct obj_section *got; + struct obj_section *opd; + struct obj_section *pltt; + struct obj_section *pltd; + Elf64_Addr gp; + Elf64_Addr text; + Elf64_Addr data; + Elf64_Addr bss; +} ia64_file_t; + +/* + * aa=gp rel address of the function descriptor in the .IA_64.pltoff section + */ +unsigned char ia64_plt_local[] = +{ + 0x0b, 0x78, 0x00, 0x02, 0x00, 0x24, /* [MMI] addl r15=aa,gp;; */ + 0x00, 0x41, 0x3c, 0x30, 0x28, 0xc0, /* ld8 r16=[r15],8 */ + 0x01, 0x08, 0x00, 0x84, /* mov r14=gp;; */ + 0x11, 0x08, 0x00, 0x1e, 0x18, 0x10, /* [MIB] ld8 gp=[r15] */ + 0x60, 0x80, 0x04, 0x80, 0x03, 0x00, /* mov b6=r16 */ + 0x60, 0x00, 0x80, 0x00 /* br.few b6;; */ +}; + +unsigned char ia64_plt_extern[] = +{ + 0x0b, 0x80, 0x00, 0x02, 0x00, 0x24, /* [MMI] addl r16=aa,gp;; */ + 0xf0, 0x00, 0x40, 0x30, 0x20, 0x00, /* ld8 r15=[r16] */ + 0x00, 0x00, 0x04, 0x00, /* nop.i 0x0;; */ + 0x0b, 0x80, 0x20, 0x1e, 0x18, 0x14, /* [MMI] ld8 r16=[r15],8;; */ + 0x10, 0x00, 0x3c, 0x30, 0x20, 0xc0, /* ld8 gp=[r15] */ + 0x00, 0x09, 0x00, 0x07, /* mov b6=r16;; */ + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, /* [MIB] nop.m 0x0 */ + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, /* nop.i 0x0 */ + 0x60, 0x00, 0x80, 0x00 /* br.few b6;; */ +}; + +/*======================================================================*/ + +/* + * return the instruction at slot in bundle + */ +Elf64_Xword +obj_ia64_ins_extract_from_bundle(Elf64_Addr *bundle, Elf64_Xword slot) +{ + switch (slot) + { + case 0 : + return (*bundle >> 5) & 0x1ffffffffff; + + case 1 : + return (((*bundle >> 46) & 0x3ffff) | + (*(bundle + 1) << 18)) & 0x1ffffffffff; + + case 2 : + return (*(bundle + 1) >> 23) & 0x1ffffffffff; + + default: + } + return (-1); +} + +/* + * insert a instruction at slot in bundle + */ +void +obj_ia64_ins_insert_in_bundle(Elf64_Addr *bundle, Elf64_Xword slot, Elf64_Xword ins) +{ + Elf64_Xword i; + Elf64_Xword in = ins & 0x1ffffffffff; + + switch (slot) + { + case 0 : + i = *bundle & 0xffffc0000000001f; + *bundle = i | (in << 5); + break; + + case 1 : + i = *bundle & 0x00003fffffffffff; + *bundle = i | (in << 46); + + ++bundle; + i = *bundle & 0xffffffffff800000; + *bundle = i | (in >> 18); + break; + + case 2 : + ++bundle; + i = *bundle & 0x00000000007fffff; + *bundle = i | (in << 23); + break; + } +} + +/* + * add a immediate 14 value to the instruction at slot in bundle + */ +enum obj_reloc +obj_ia64_ins_imm14(Elf64_Xword v, Elf64_Addr *bundle, Elf64_Xword slot) +{ + Elf64_Xword ins; + + ins = obj_ia64_ins_extract_from_bundle(bundle, slot); + ins &= 0xffffffee07f01fff; + ins |= ((v & 0x2000) << 23) | ((v & 0x1f80) << 20) | ((v & 0x007f) << 13); + obj_ia64_ins_insert_in_bundle(bundle, slot, ins); + if (((Elf64_Sxword) v > 8191) || ((Elf64_Sxword) v < -8192)) + return obj_reloc_overflow; + return obj_reloc_ok; +} + +/* + * add a immediate 22 value to the instruction at slot in bundle + */ +enum obj_reloc +obj_ia64_ins_imm22(Elf64_Xword v, Elf64_Addr *bundle, Elf64_Xword slot) +{ + Elf64_Xword ins; + + ins = obj_ia64_ins_extract_from_bundle(bundle, slot); + ins &= 0xffffffe000301fff; + ins |= ((v & 0x200000) << 15) | ((v & 0x1f0000) << 6) | + ((v & 0x00ff80) << 20) | ((v & 0x00007f) << 13); + obj_ia64_ins_insert_in_bundle(bundle, slot, ins); + if (((Elf64_Sxword) v > 2097151) || ((Elf64_Sxword) v < -2097152)) + return obj_reloc_overflow; + return obj_reloc_ok; +} + +/* + * add a immediate 21 value (form 1) to the instruction at slot in bundle + */ +enum obj_reloc +obj_ia64_ins_pcrel21b(Elf64_Xword v, Elf64_Addr *bundle, Elf64_Xword slot) +{ + Elf64_Xword ins; + + ins = obj_ia64_ins_extract_from_bundle(bundle, slot); + ins &= 0xffffffee00001fff; + ins |= ((v & 0x1000000) << 12) | ((v & 0x0fffff0) << 9); + obj_ia64_ins_insert_in_bundle(bundle, slot, ins); + return obj_reloc_ok; +} + +/* + * add a immediate 21 value (form 2) to the instruction at slot in bundle + */ +enum obj_reloc +obj_ia64_ins_pcrel21m(Elf64_Xword v, Elf64_Addr *bundle, Elf64_Xword slot) +{ + Elf64_Xword ins; + + ins = obj_ia64_ins_extract_from_bundle(bundle, slot); + ins &= 0xffffffee000fe03f; + ins |= ((v & 0x1000000) << 12) | ((v & 0x0fff800) << 9) | + ((v & 0x00007f0) << 2); + obj_ia64_ins_insert_in_bundle(bundle, slot, ins); + return obj_reloc_ok; +} + +/* + * add a immediate 21 value (form 3) to the instruction at slot in bundle + */ +enum obj_reloc +obj_ia64_ins_pcrel21f(Elf64_Xword v, Elf64_Addr *bundle, Elf64_Xword slot) +{ + Elf64_Xword ins; + + ins = obj_ia64_ins_extract_from_bundle(bundle, slot); + ins &= 0xffffffeffc00003f; + ins |= ((v & 0x1000000) << 12) | ((v & 0x0fffff0) << 2); + obj_ia64_ins_insert_in_bundle(bundle, slot, ins); + return obj_reloc_ok; +} + +/* + * add a immediate 64 value to the instruction at slot in bundle + */ +enum obj_reloc +obj_ia64_ins_imm64(Elf64_Xword v, Elf64_Addr *bundle, Elf64_Xword slot) +{ + Elf64_Xword ins; + + assert(slot == 1); + ins = obj_ia64_ins_extract_from_bundle(bundle, slot); + ins &= 0xffffffee000101ff; + ins |= ((v & 0x8000000000000000) >> 28) | ((v & 0x0000000000200000)) | + ((v & 0x00000000001f0000) << 6) | ((v & 0x000000000000ff80) << 20) | + ((v & 0x000000000000007f) << 13); + obj_ia64_ins_insert_in_bundle(bundle, slot, ins); + obj_ia64_ins_insert_in_bundle(bundle, ++slot, ((v & 0x7fffffffffc00000) >> 22)); + return obj_reloc_ok; +} + +/* + * create a plt entry + */ +enum obj_reloc +obj_ia64_generate_plt(Elf64_Addr v, + Elf64_Addr gp, + ia64_file_t *ifile, + ia64_symbol_t *isym, + ia64_plt_t *pltent) +{ + *(Elf64_Addr *)(ifile->pltd->contents + pltent->data_offset) = v; + if (isym->root.secidx <= SHN_HIRESERVE) + { + /* local entry */ + *(Elf64_Addr *)(ifile->pltd->contents + pltent->data_offset + 8) = gp; + memcpy((Elf64_Addr *)(ifile->pltt->contents + pltent->text_offset), + ia64_plt_local, sizeof(ia64_plt_local)); + } + else + { + /* external entry */ + memcpy((Elf64_Addr *)(ifile->pltt->contents + pltent->text_offset), + ia64_plt_extern, sizeof(ia64_plt_extern)); + } + return obj_ia64_ins_imm22( + (ifile->pltd->header.sh_addr + pltent->data_offset - gp), + (Elf64_Addr *)(ifile->pltt->contents + pltent->text_offset), 0); +} + +struct obj_section * +obj_ia64_create_alloced_section (struct obj_file *f, const char *name, + unsigned long align, unsigned long size, unsigned long sh_flags) +{ + int newidx = f->header.e_shnum++; + struct obj_section *sec; + + f->sections = xrealloc(f->sections, (newidx+1) * sizeof(sec)); + f->sections[newidx] = sec = arch_new_section(); + + memset(sec, 0, sizeof(*sec)); + sec->header.sh_type = SHT_PROGBITS; + sec->header.sh_flags = sh_flags; + sec->header.sh_size = size; + sec->header.sh_addralign = align; + sec->name = name; + sec->idx = newidx; + if (size) + sec->contents = xmalloc(size); + + obj_insert_section_load_order(f, sec); + + return sec; +} + +/*======================================================================*/ + +struct obj_file * +arch_new_file (void) +{ + ia64_file_t *f; + f = xmalloc(sizeof(*f)); + f->got = NULL; + f->opd = NULL; + f->pltt = NULL; + f->pltd = NULL; + return &f->root; +} + +struct obj_section * +arch_new_section (void) +{ + return xmalloc(sizeof(struct obj_section)); +} + +struct obj_symbol * +arch_new_symbol (void) +{ + ia64_symbol_t *sym; + sym = xmalloc(sizeof(*sym)); + sym->gotent = NULL; + sym->opdent = NULL; + sym->pltent = NULL; + return &sym->root; +} + +int +arch_load_proc_section(struct obj_section *sec, int fp) +{ + switch (sec->header.sh_type) + { + case SHT_IA_64_EXT : + sec->contents = NULL; + break; + + case SHT_IA_64_UNWIND : + if (sec->header.sh_size > 0) + { + sec->contents = xmalloc(sec->header.sh_size); + gzf_lseek(fp, sec->header.sh_offset, SEEK_SET); + if (gzf_read(fp, sec->contents, sec->header.sh_size) != sec->header.sh_size) + { + error("error reading ELF section data: %m"); + return -1; + } + } + else + sec->contents = NULL; + break; + + default: + error("Unknown section header type: %08x", sec->header.sh_type); + return -1; + } + return 0; +} + +int +arch_create_got(struct obj_file *f) +{ + ia64_file_t *ifile = (ia64_file_t *)f; + int i; + int n; + int got_offset = 0; + int opd_offset = 32; + int plt_text_offset = 0; + int plt_data_offset = 0; + + n = ifile->root.header.e_shnum; + for (i = 0; i < n; ++i) + { + struct obj_section *relsec, *symsec, *strsec; + Elf64_Rela *rel, *relend; + Elf64_Sym *symtab; + const char *strtab; + + relsec = ifile->root.sections[i]; + if (relsec->header.sh_type != SHT_RELA) + continue; + + symsec = ifile->root.sections[relsec->header.sh_link]; + strsec = ifile->root.sections[symsec->header.sh_link]; + + rel = (Elf64_Rela *)relsec->contents; + relend = rel + (relsec->header.sh_size / sizeof(Elf64_Rela)); + symtab = (Elf64_Sym *)symsec->contents; + strtab = (const char *)strsec->contents; + + for (; rel < relend; ++rel) + { + int need_got = FALSE; + int need_opd = FALSE; + int need_plt = FALSE; + + switch (ELF64_R_TYPE(rel->r_info)) + { + default: + continue; + + case R_IA64_FPTR64I : /* @fptr(sym + add), mov imm64 */ + case R_IA64_FPTR32LSB : /* @fptr(sym + add), data4 LSB */ + case R_IA64_FPTR64LSB : /* @fptr(sym + add), data8 LSB */ + need_opd = TRUE; + break; + + case R_IA64_LTOFF22 : /* @ltoff(sym + add), add imm22 */ + case R_IA64_LTOFF22X : + case R_IA64_LTOFF64I : /* @ltoff(sym + add), mov imm64 */ + need_got = TRUE; + break; + + case R_IA64_LTOFF_FPTR22 : /* @ltoff(@fptr(s+a)), imm22 */ + case R_IA64_LTOFF_FPTR64I : /* @ltoff(@fptr(s+a)), imm64 */ + case R_IA64_LTOFF_FPTR32LSB : + case R_IA64_LTOFF_FPTR64LSB : + need_got = TRUE; + need_opd = TRUE; + break; + + case R_IA64_PLTOFF22 : /* @pltoff(sym + add), add imm22 */ + case R_IA64_PLTOFF64I : /* @pltoff(sym + add), mov imm64 */ + case R_IA64_PLTOFF64LSB : /* @pltoff(sym + add), data8 LSB */ + + case R_IA64_PCREL21B : /* @pcrel(sym + add), ptb, call */ + case R_IA64_PCREL21M : /* @pcrel(sym + add), chk.s */ + case R_IA64_PCREL21F : /* @pcrel(sym + add), fchkf */ + need_plt = TRUE; + break; + } + + if (need_got || need_opd || need_plt) + { + Elf64_Sym *extsym; + ia64_symbol_t *isym; + const char *name; + int local; + unsigned long symndx; + + symndx = ELF64_R_SYM(rel->r_info); + extsym = &symtab[symndx]; + if (ELF64_ST_BIND(extsym->st_info) == STB_LOCAL) + { + isym = (ia64_symbol_t *) f->local_symtab[symndx]; + } + else + { + if (extsym->st_name) + name = strtab + extsym->st_name; + else + name = f->sections[extsym->st_shndx]->name; + isym = (ia64_symbol_t *)obj_find_symbol(f, name); + } + local = isym->root.secidx <= SHN_HIRESERVE; + + if (need_plt) + { + ia64_plt_t *plt; + + for (plt = isym->pltent; plt != NULL; plt = plt->next) + if (plt->addend == rel->r_addend) + break; + if (plt == NULL) + { + plt = (ia64_plt_t *) xmalloc(sizeof(ia64_plt_t)); + plt->next = isym->pltent; + plt->addend = rel->r_addend; + plt->text_offset = plt_text_offset; + plt->data_offset = plt_data_offset; + plt->reloc_done = FALSE; + isym->pltent = plt; + if (local) + { + plt_text_offset += sizeof(ia64_plt_local); + plt_data_offset += 16; + } + else + { + plt_text_offset += sizeof(ia64_plt_extern); + plt_data_offset += 8; + } + need_plt = FALSE; + } + } + if (need_got) + { + ia64_got_t *got; + + for (got = isym->gotent; got != NULL; got = got->next) + if (got->addend == rel->r_addend) + break; + if (got == NULL) + { + got = (ia64_got_t *) xmalloc(sizeof(ia64_got_t)); + got->next = isym->gotent; + got->addend = rel->r_addend; + got->offset = got_offset; + got->reloc_done = FALSE; + isym->gotent = got; + got_offset += 8; + need_got = FALSE; + } + } + if (need_opd && local) + { + ia64_opd_t *opd; + + if (isym->opdent == NULL) + { + opd = (ia64_opd_t *) xmalloc(sizeof(ia64_opd_t)); + opd->offset = opd_offset; + opd->reloc_done = FALSE; + isym->opdent = opd; + opd_offset += 16; + need_opd = FALSE; + } + } + } + } + } + + ifile->got = obj_ia64_create_alloced_section(f, ".got", 8, got_offset, + (SHF_ALLOC | SHF_WRITE | SHF_IA_64_SHORT)); + assert(ifile->got != NULL); + + ifile->opd = obj_ia64_create_alloced_section(f, ".opd", 16, opd_offset, + (SHF_ALLOC | SHF_WRITE | SHF_IA_64_SHORT)); + assert(ifile->opd != NULL); + + if (plt_text_offset > 0) + { + ifile->pltt = obj_ia64_create_alloced_section(f, ".plt", 16, + plt_text_offset, (SHF_ALLOC | SHF_EXECINSTR | SHF_IA_64_SHORT)); + ifile->pltd = obj_ia64_create_alloced_section(f, ".IA_64.pltoff", + 16, plt_data_offset, (SHF_ALLOC | SHF_WRITE | SHF_IA_64_SHORT)); + assert(ifile->pltt != NULL); + assert(ifile->pltd != NULL); + } + + return 1; +} + +int +arch_finalize_section_address(struct obj_file *f, Elf64_Addr base) +{ + ia64_file_t *ifile = (ia64_file_t *)f; + Elf64_Addr min_addr = (Elf64_Addr) -1; + Elf64_Addr max_addr = 0; + Elf64_Addr min_short_addr = (Elf64_Addr) -1; + Elf64_Addr max_short_addr = 0; + Elf64_Addr gp; + Elf64_Addr text = (Elf64_Addr) -1; + Elf64_Addr data = (Elf64_Addr) -1; + Elf64_Addr bss = (Elf64_Addr) -1; + int n = f->header.e_shnum; + int i; + + /* + * Finalize the addresses of the sections, find the min and max + * address of all sections marked short, and collect min and max + * address of any type, for use in selecting a nice gp. + * + * The algorithm used for selecting set the GP value was taken from + * the ld/bfd code contributed by David Mosberger-Tang <davidm@hpl.hp.com> + */ + f->baseaddr = base; + for (i = 0; i < n; ++i) + { + Elf64_Shdr *header = &f->sections[i]->header; + Elf64_Addr lo; + Elf64_Addr hi; + + header->sh_addr += base; + if (header->sh_flags & SHF_ALLOC) + { + lo = header->sh_addr; + hi = header->sh_addr + header->sh_size; + if (hi < lo) + hi = (Elf64_Addr) -1; + + if (min_addr > lo) + min_addr = lo; + if (max_addr < hi) + max_addr = hi; + if (header->sh_flags & SHF_IA_64_SHORT) + { + if (min_short_addr > lo) + min_short_addr = lo; + if (max_short_addr < hi) + max_short_addr = hi; + } + if ((header->sh_type & SHT_NOBITS) && (lo < bss)) + bss = lo; + else if ((header->sh_flags & SHF_EXECINSTR) && (lo < text)) + text = lo; + else if (lo < data) + data = lo; + } + } + /* Pick a sensible value for gp */ + + /* Start with just the address of the .got */ + gp = ifile->got->header.sh_addr; + + /* + * If it is possible to address the entire image, but we + * don't with the choice above, adjust. + */ + if ((max_addr - min_addr < 0x400000) && (max_addr - gp <= 0x200000) && + (gp - min_addr > 0x200000)) + { + gp = min_addr + 0x200000; + } + else if (max_short_addr != 0) + { + /* If we don't cover all the short data, adjust */ + if (max_short_addr - gp >= 0x200000) + gp = min_short_addr + 0x200000; + + /* If we're addressing stuff past the end, adjust back */ + if (gp > max_addr) + gp = max_addr - 0x200000 + 8; + } + + /* + * Validate whether all SHF_IA_64_SHORT sections are within + * range of the chosen GP. + */ + if (max_short_addr != 0) + { + if (max_short_addr - min_short_addr >= 0x400000) + { + error("short data segment overflowed (0x%lx >= 0x400000)", + (unsigned long)(max_short_addr - min_short_addr)); + return 0; + } + else if (((gp > min_short_addr) && (gp - min_short_addr > 0x200000)) || + ((gp < max_short_addr) && (max_short_addr - gp >= 0x200000))) + { + error("GP does not cover short data segment"); + return 0; + } + } + ifile->gp = gp; + ifile->text = text; + ifile->data = data; + ifile->bss = bss; + return 1; +} + +/* Targets can be unaligned, use memcpy instead of assignment */ +#define COPY_64LSB(loc, v) \ + do { \ + Elf64_Xword reloc = (v); \ + memcpy((void *)(loc), &reloc, 8); \ + } while(0) +#define COPY_32LSB(loc, v) \ + do { \ + Elf32_Xword reloc = (v); \ + memcpy((void *)(loc), &reloc, 4); \ + if ((v) != reloc) \ + ret = obj_reloc_overflow; \ + } while(0) + +enum obj_reloc +arch_apply_relocation(struct obj_file *f, + struct obj_section *targsec, + struct obj_section *symsec, + struct obj_symbol *sym, + Elf64_Rela *rel, + Elf64_Addr v) +{ + ia64_file_t *ifile = (ia64_file_t *) f; + ia64_symbol_t *isym = (ia64_symbol_t *) sym; + + Elf64_Addr loc = (Elf64_Addr)(targsec->contents + rel->r_offset); + Elf64_Addr dot = (targsec->header.sh_addr + rel->r_offset) & ~0x03; + + Elf64_Addr got = ifile->got->header.sh_addr; + Elf64_Addr gp = ifile->gp; + + Elf64_Addr *bundle = (Elf64_Addr *)(loc & ~0x03); + Elf64_Xword slot = loc & 0x03; + + Elf64_Xword r_info = ELF64_R_TYPE(rel->r_info); + + enum obj_reloc ret = obj_reloc_ok; + + /* We cannot load modules compiled with -mconstant-gp */ +#ifndef EF_IA_64_CONS_GP +#define EF_IA_64_CONS_GP 0x00000040 +#endif +#ifndef EF_IA_64_NOFUNCDESC_CONS_GP +#define EF_IA_64_NOFUNCDESC_CONS_GP 0x00000080 +#endif + if (f->header.e_flags & (EF_IA_64_CONS_GP | EF_IA_64_NOFUNCDESC_CONS_GP)) + return obj_reloc_constant_gp; + + switch (r_info) + { + case R_IA64_NONE : /* none */ + case R_IA64_LDXMOV : /* Use of LTOFF22X. */ + break; + + case R_IA64_IMM14 : /* symbol + addend, add imm14 */ + ret = obj_ia64_ins_imm14(v, bundle, slot); + break; + + case R_IA64_IMM22 : /* symbol + addend, add imm22 */ + ret = obj_ia64_ins_imm22(v, bundle, slot); + break; + + case R_IA64_IMM64 : /* symbol + addend, movl imm64 */ + ret = obj_ia64_ins_imm64(v, bundle, slot); + break; + + case R_IA64_DIR32LSB : /* symbol + addend, data4 LSB */ + COPY_32LSB(loc, v); + break; + + case R_IA64_DIR64LSB : /* symbol + addend, data8 LSB */ + COPY_64LSB(loc, v); + break; + + case R_IA64_GPREL22 : /* @gprel(sym + add), add imm22 */ + v -= gp; + ret = obj_ia64_ins_imm22(v, bundle, slot); + break; + + case R_IA64_GPREL64I : /* @gprel(sym + add), mov imm64 */ + v -= gp; + ret = obj_ia64_ins_imm64(v, bundle, slot); + break; + + case R_IA64_GPREL32LSB : /* @gprel(sym + add), data4 LSB */ + COPY_32LSB(loc, v-gp); + break; + + case R_IA64_GPREL64LSB : /* @gprel(sym + add), data8 LSB */ + COPY_64LSB(loc, v-gp); + break; + + case R_IA64_LTOFF22 : /* @ltoff(sym + add), add imm22 */ + case R_IA64_LTOFF22X : /* LTOFF22, relaxable. */ + case R_IA64_LTOFF64I : /* @ltoff(sym + add), mov imm64 */ + { + ia64_got_t *ge; + + assert(isym != NULL); + for (ge = isym->gotent; ge != NULL && ge->addend != rel->r_addend; ) + ge = ge->next; + assert(ge != NULL); + if (!ge->reloc_done) + { + ge->reloc_done = TRUE; + *(Elf64_Addr *)(ifile->got->contents + ge->offset) = v; + } + v = got + ge->offset - gp; + if (r_info == R_IA64_LTOFF64I) + ret = obj_ia64_ins_imm64(v, bundle, slot); + else + ret = obj_ia64_ins_imm22(v, bundle, slot); + } + break; + + case R_IA64_PLTOFF22 : /* @pltoff(sym + add), add imm22 */ + case R_IA64_PLTOFF64I : /* @pltoff(sym + add), mov imm64 */ + case R_IA64_PLTOFF64LSB : /* @pltoff(sym + add), data8 LSB */ + { + ia64_plt_t *pe; + + assert(isym != NULL); + for (pe = isym->pltent; pe != NULL && pe->addend != rel->r_addend; ) + pe = pe->next; + assert(pe != NULL); + if (!pe->reloc_done) + { + pe->reloc_done = TRUE; + ret = obj_ia64_generate_plt(v, gp, ifile, isym, pe); + } + v = ifile->pltt->header.sh_addr + pe->text_offset - gp; + switch (r_info) + { + case R_IA64_PLTOFF22 : + ret = obj_ia64_ins_imm22(v, bundle, slot); + break; + + case R_IA64_PLTOFF64I : + ret = obj_ia64_ins_imm64(v, bundle, slot); + break; + + case R_IA64_PLTOFF64LSB : + COPY_64LSB(loc, v); + break; + } + } + break; + + case R_IA64_FPTR64I : /* @fptr(sym + add), mov imm64 */ + case R_IA64_FPTR32LSB : /* @fptr(sym + add), data4 LSB */ + case R_IA64_FPTR64LSB : /* @fptr(sym + add), data8 LSB */ + assert(isym != NULL); + if (isym->root.secidx <= SHN_HIRESERVE) + { + assert(isym->opdent != NULL); + if (!isym->opdent->reloc_done) + { + isym->opdent->reloc_done = TRUE; + *(Elf64_Addr *)(ifile->opd->contents + isym->opdent->offset) = v; + *(Elf64_Addr *)(ifile->opd->contents + isym->opdent->offset + 8) = gp; + } + v = ifile->opd->header.sh_addr + isym->opdent->offset; + } + switch (r_info) + { + case R_IA64_FPTR64I : + ret = obj_ia64_ins_imm64(v, bundle, slot); + break; + + case R_IA64_FPTR32LSB : + COPY_32LSB(loc, v); + break; + + case R_IA64_FPTR64LSB : /* @fptr(sym + add), data8 LSB */ + /* Target can be unaligned */ + COPY_64LSB(loc, v); + break; + } + break; + + case R_IA64_PCREL21B : /* @pcrel(sym + add), ptb, call */ + case R_IA64_PCREL21M : /* @pcrel(sym + add), chk.s */ + case R_IA64_PCREL21F : /* @pcrel(sym + add), fchkf */ + assert(isym != NULL); + if ((isym->root.secidx > SHN_HIRESERVE) || + ((Elf64_Sxword) (v - dot) > 16777215) || + ((Elf64_Sxword) (v - dot) < -16777216)) + { + ia64_plt_t *pe; + + for (pe = isym->pltent; pe != NULL && pe->addend != rel->r_addend; ) + pe = pe->next; + assert(pe != NULL); + if (!pe->reloc_done) + { + pe->reloc_done = TRUE; + ret = obj_ia64_generate_plt(v, gp, ifile, isym, pe); + } + v = ifile->pltt->header.sh_addr + pe->text_offset; + } + v -= dot; + switch (r_info) + { + case R_IA64_PCREL21B : + ret = obj_ia64_ins_pcrel21b(v, bundle, slot); + break; + + case R_IA64_PCREL21M : + ret = obj_ia64_ins_pcrel21m(v, bundle, slot); + break; + + case R_IA64_PCREL21F : + ret = obj_ia64_ins_pcrel21f(v, bundle, slot); + break; + } + break; + + case R_IA64_PCREL32LSB : /* @pcrel(sym + add), data4 LSB */ + COPY_32LSB(loc, v-dot); + break; + + case R_IA64_PCREL64LSB : /* @pcrel(sym + add), data8 LSB */ + COPY_64LSB(loc, v-dot); + break; + + case R_IA64_LTOFF_FPTR22 : /* @ltoff(@fptr(s+a)), imm22 */ + case R_IA64_LTOFF_FPTR64I : /* @ltoff(@fptr(s+a)), imm64 */ + case R_IA64_LTOFF_FPTR32LSB : /* @ltoff(@fptr(s+a)), data4 */ + case R_IA64_LTOFF_FPTR64LSB : /* @ltoff(@fptr(s+a)), data8 */ + { + ia64_got_t *ge; + + assert(isym != NULL); + if (isym->root.secidx <= SHN_HIRESERVE) + { + assert(isym->opdent != NULL); + if (!isym->opdent->reloc_done) + { + isym->opdent->reloc_done = TRUE; + *(Elf64_Addr *)(ifile->opd->contents + isym->opdent->offset) = v; + *(Elf64_Addr *)(ifile->opd->contents + isym->opdent->offset + 8) = gp; + } + v = ifile->opd->header.sh_addr + isym->opdent->offset; + } + for (ge = isym->gotent; ge != NULL && ge->addend != rel->r_addend; ) + ge = ge->next; + assert(ge != NULL); + if (!ge->reloc_done) + { + ge->reloc_done = TRUE; + *(Elf64_Addr *)(ifile->got->contents + ge->offset) = v; + } + v = got + ge->offset - gp; + switch (r_info) + { + case R_IA64_LTOFF_FPTR22 : + ret = obj_ia64_ins_imm22(v, bundle, slot); + break; + + case R_IA64_LTOFF_FPTR64I : + ret = obj_ia64_ins_imm64(v, bundle, slot); + break; + + case R_IA64_LTOFF_FPTR32LSB : + COPY_32LSB(loc, v); + break; + + case R_IA64_LTOFF_FPTR64LSB : + COPY_64LSB(loc, v); + break; + } + } + break; + + case R_IA64_SEGREL32LSB : /* @segrel(sym + add), data4 LSB */ + case R_IA64_SEGREL64LSB : /* @segrel(sym + add), data8 LSB */ + if (targsec->header.sh_type & SHT_NOBITS) + v = ifile->bss - v; + else if (targsec->header.sh_flags & SHF_EXECINSTR) + v = ifile->text - v; + else + v = ifile->data - v; + if (r_info == R_IA64_SEGREL32LSB) + COPY_32LSB(loc, v); + else + COPY_64LSB(loc, v); + break; + + case R_IA64_SECREL32LSB : /* @secrel(sym + add), data4 LSB */ + COPY_32LSB(loc, targsec->header.sh_addr - v); + break; + + case R_IA64_SECREL64LSB : /* @secrel(sym + add), data8 LSB */ + COPY_64LSB(loc, targsec->header.sh_addr - v); + break; + + /* + * We don't handle the big-endian relocates + * + * R_IA64_DIR32MSB symbol + addend, data4 MSB + * R_IA64_DIR64MSB symbol + addend, data8 MSB + * R_IA64_GPREL32MSB @gprel(sym + add), data4 MSB + * R_IA64_GPREL64MSB @gprel(sym + add), data8 MSB + * R_IA64_PLTOFF64MSB @pltoff(sym + add), data8 MSB + * R_IA64_FPTR32MSB @fptr(sym + add), data4 MSB + * R_IA64_FPTR64MSB @fptr(sym + add), data8 MSB + * R_IA64_PCREL32MSB @pcrel(sym + add), data4 MSB + * R_IA64_PCREL64MSB @pcrel(sym + add), data8 MSB + * R_IA64_SEGREL32MSB @segrel(sym + add), data4 MSB + * R_IA64_SEGREL64MSB @segrel(sym + add), data8 MSB + * R_IA64_SECREL32MSB @secrel(sym + add), data4 MSB + * R_IA64_SECREL64MSB @secrel(sym + add), data8 MSB + * R_IA64_REL32MSB data 4 + REL + * R_IA64_REL64MSB data 8 + REL + * R_IA64_LTV32MSB symbol + addend, data4 MSB + * R_IA64_LTV64MSB symbol + addend, data8 MSB + * R_IA64_IPLTMSB dynamic reloc, imported PLT, MSB + */ + default: + case R_IA64_REL32LSB : /* data 4 + REL */ + case R_IA64_REL64LSB : /* data 8 + REL */ + case R_IA64_LTV32LSB : /* symbol + addend, data4 LSB */ + case R_IA64_LTV64LSB : /* symbol + addend, data8 LSB */ + case R_IA64_IPLTLSB : /* dynamic reloc, imported PLT, LSB */ + ret = obj_reloc_unhandled; + break; + } + return ret; +} + +int +arch_init_module (struct obj_file *f, struct module *mod) +{ + ia64_file_t *ifile = (ia64_file_t *)f; + Elf64_Addr *opd = (Elf64_Addr *)(ifile->opd->contents); + + if ((opd[0] = mod->init) != 0) + { + opd[1] = ifile->gp; + mod->init = ifile->opd->header.sh_addr; + } + + if ((opd[2] = mod->cleanup) != 0) + { + opd[3] = ifile->gp; + mod->cleanup = ifile->opd->header.sh_addr + 16; + } + + return 1; +} + +int +arch_archdata (struct obj_file *f, struct obj_section *archdata_sec) +{ + ia64_file_t *ifile = (ia64_file_t *)f; + struct archdata { + unsigned tgt_long unw_table; + unsigned tgt_long segment_base; + unsigned tgt_long unw_start; + unsigned tgt_long unw_end; + unsigned tgt_long gp; + } *ad; + int i; + struct obj_section *sec; + + free(archdata_sec->contents); + archdata_sec->contents = xmalloc(sizeof(struct archdata)); + memset(archdata_sec->contents, 0, sizeof(struct archdata)); + archdata_sec->header.sh_size = sizeof(struct archdata); + + ad = (struct archdata *)(archdata_sec->contents); + ad->gp = ifile->gp; + ad->unw_start = 0; + ad->unw_end = 0; + ad->unw_table = 0; + ad->segment_base = f->sections[1]->header.sh_addr; + for (i = 0; i < f->header.e_shnum; ++i) + { + sec = f->sections[i]; + if (sec->header.sh_type == SHT_IA_64_UNWIND) + { + ad->unw_start = sec->header.sh_addr; + ad->unw_end = sec->header.sh_addr + sec->header.sh_size; + break; + } + } + + return 0; +} diff --git a/mdk-stage1/insmod-modutils/obj/obj_kallsyms.c b/mdk-stage1/insmod-modutils/obj/obj_kallsyms.c new file mode 100644 index 000000000..8385fb892 --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_kallsyms.c @@ -0,0 +1,292 @@ +/* Build a section containing all non-stack symbols. + Copyright 2000 Keith Owens <kaos@ocs.com.au> + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ident "$Id$" + +#include <stdlib.h> +#include <string.h> +#include <malloc.h> + +#include "obj.h" +#include "kallsyms.h" +#include "util.h" + +/*======================================================================*/ + +#define EXPAND_BY 4096 /* Arbitrary */ + +/* Append a string to the big list of strings */ + +static void +append_string (const char *s, char **strings, + ElfW(Word) *strings_size, ElfW(Word) *strings_left) +{ + int l = strlen(s) + 1; + while (l > *strings_left) { + *strings = xrealloc(*strings, *strings_size += EXPAND_BY); + *strings_left += EXPAND_BY; + } + memcpy((char *)*strings+*strings_size-*strings_left, s, l); + *strings_left -= l; +} + + +/* Append a symbol to the big list of symbols */ + +static void +append_symbol (const struct kallsyms_symbol *s, + struct kallsyms_symbol **symbols, + ElfW(Word) *symbols_size, ElfW(Word) *symbols_left) +{ + int l = sizeof(*s); + while (l > *symbols_left) { + *symbols = xrealloc(*symbols, *symbols_size += EXPAND_BY); + *symbols_left += EXPAND_BY; + } + memcpy((char *)*symbols+*symbols_size-*symbols_left, s, l); + *symbols_left -= l; +} + +/* qsort compare routine to sort symbols */ + +static const char *sym_strings; + +static int +symbol_compare (const void *a, const void *b) +{ + struct kallsyms_symbol *c = (struct kallsyms_symbol *) a; + struct kallsyms_symbol *d = (struct kallsyms_symbol *) b; + + if (c->symbol_addr > d->symbol_addr) + return(1); + if (c->symbol_addr < d->symbol_addr) + return(-1); + return(strcmp(sym_strings+c->name_off, sym_strings+d->name_off)); +} + + +/* Extract all symbols from the input obj_file, ignore ones that are + * no use for debugging, build an output obj_file containing only the + * kallsyms section. + * + * The kallsyms section is a bit unusual. It deliberately has no + * relocatable data, all "pointers" are represented as byte offsets + * into the the section. This means it can be stored anywhere without + * relocation problems. In particular it can be stored within a kernel + * image, it can be stored separately from the kernel image, it can be + * appended to a module just before loading, it can be stored in a + * separate area etc. + * + * Format of the kallsyms section. + * + * Header: + * Size of header. + * Total size of kallsyms data, including strings. + * Number of loaded sections. + * Offset to first section entry from start of header. + * Size of each section entry, excluding the name string. + * Number of symbols. + * Offset to first symbol entry from start of header. + * Size of each symbol entry, excluding the name string. + * + * Section entry - one per loaded section. + * Start of section[1]. + * Size of section. + * Offset to name of section, from start of strings. + * Section flags. + * + * Symbol entry - one per symbol in the input file[2]. + * Offset of section that owns this symbol, from start of section data. + * Address of symbol within the real section[1]. + * Offset to name of symbol, from start of strings. + * + * Notes: [1] This is an exception to the "represent pointers as + * offsets" rule, it is a value, not an offset. The start + * address of a section or a symbol is extracted from the + * obj_file data which may contain absolute or relocatable + * addresses. If the addresses are relocatable then the + * caller must adjust the section and/or symbol entries in + * kallsyms after relocation. + * [2] Only symbols that fall within loaded sections are stored. + */ + +int +obj_kallsyms (struct obj_file *fin, struct obj_file **fout_result) +{ + struct obj_file *fout; + int i, loaded = 0, *fin_to_allsym_map; + struct obj_section *isec, *osec; + struct kallsyms_header *a_hdr; + struct kallsyms_section *a_sec; + ElfW(Off) sec_off; + struct kallsyms_symbol *symbols = NULL, a_sym; + ElfW(Word) symbols_size = 0, symbols_left = 0; + char *strings = NULL, *p; + ElfW(Word) strings_size = 0, strings_left = 0; + ElfW(Off) file_offset; + static char strtab[] = "\000" KALLSYMS_SEC_NAME; + + /* Create the kallsyms section. */ + fout = arch_new_file(); + memset(fout, 0, sizeof(*fout)); + fout->symbol_cmp = strcmp; + fout->symbol_hash = obj_elf_hash; + fout->load_order_search_start = &fout->load_order; + + /* Copy file characteristics from input file and modify to suit */ + memcpy(&fout->header, &fin->header, sizeof(fout->header)); + fout->header.e_type = ET_REL; /* Output is relocatable */ + fout->header.e_entry = 0; /* No entry point */ + fout->header.e_phoff = 0; /* No program header */ + file_offset = sizeof(fout->header); /* Step over Elf header */ + fout->header.e_shoff = file_offset; /* Section headers next */ + fout->header.e_phentsize = 0; /* No program header */ + fout->header.e_phnum = 0; /* No program header */ + fout->header.e_shnum = KALLSYMS_IDX+1; /* Initial, strtab, kallsyms */ + fout->header.e_shstrndx = KALLSYMS_IDX-1; /* strtab */ + file_offset += fout->header.e_shentsize * fout->header.e_shnum; + + /* Populate the section data for kallsyms itself */ + fout->sections = xmalloc(sizeof(*(fout->sections))*fout->header.e_shnum); + memset(fout->sections, 0, sizeof(*(fout->sections))*fout->header.e_shnum); + + fout->sections[0] = osec = arch_new_section(); + memset(osec, 0, sizeof(*osec)); + osec->header.sh_type = SHT_NULL; + osec->header.sh_link = SHN_UNDEF; + + fout->sections[KALLSYMS_IDX-1] = osec = arch_new_section(); + memset(osec, 0, sizeof(*osec)); + osec->name = ".strtab"; + osec->header.sh_type = SHT_STRTAB; + osec->header.sh_link = SHN_UNDEF; + osec->header.sh_offset = file_offset; + osec->header.sh_size = sizeof(strtab); + osec->contents = xmalloc(sizeof(strtab)); + memcpy(osec->contents, strtab, sizeof(strtab)); + file_offset += osec->header.sh_size; + + fout->sections[KALLSYMS_IDX] = osec = arch_new_section(); + memset(osec, 0, sizeof(*osec)); + osec->name = KALLSYMS_SEC_NAME; + osec->header.sh_name = 1; /* Offset in strtab */ + osec->header.sh_type = SHT_PROGBITS; /* Load it */ + osec->header.sh_flags = SHF_ALLOC; /* Read only data */ + osec->header.sh_link = SHN_UNDEF; + osec->header.sh_addralign = sizeof(ElfW(Word)); + file_offset = (file_offset + osec->header.sh_addralign - 1) + & -(osec->header.sh_addralign); + osec->header.sh_offset = file_offset; + + /* How many loaded sections are there? */ + for (i = 0; i < fin->header.e_shnum; ++i) { + if (fin->sections[i]->header.sh_flags & SHF_ALLOC) + ++loaded; + } + + /* Initial contents, header + one entry per input section. No strings. */ + osec->header.sh_size = sizeof(*a_hdr) + loaded*sizeof(*a_sec); + a_hdr = (struct kallsyms_header *) osec->contents = + xmalloc(osec->header.sh_size); + memset(osec->contents, 0, osec->header.sh_size); + a_hdr->size = sizeof(*a_hdr); + a_hdr->sections = loaded; + a_hdr->section_off = a_hdr->size; + a_hdr->section_size = sizeof(*a_sec); + a_hdr->symbol_off = osec->header.sh_size; + a_hdr->symbol_size = sizeof(a_sym); + a_hdr->start = (ElfW(Addr))(~0); + + /* Map input section numbers to kallsyms section offsets. */ + sec_off = 0; /* Offset to first kallsyms section entry */ + fin_to_allsym_map = xmalloc(sizeof(*fin_to_allsym_map)*fin->header.e_shnum); + for (i = 0; i < fin->header.e_shnum; ++i) { + isec = fin->sections[i]; + if (isec->header.sh_flags & SHF_ALLOC) { + fin_to_allsym_map[isec->idx] = sec_off; + sec_off += a_hdr->section_size; + } + else + fin_to_allsym_map[isec->idx] = -1; /* Ignore this section */ + } + + /* Copy the loaded section data. */ + a_sec = (struct kallsyms_section *) ((char *) a_hdr + a_hdr->section_off); + for (i = 0; i < fin->header.e_shnum; ++i) { + isec = fin->sections[i]; + if (!(isec->header.sh_flags & SHF_ALLOC)) + continue; + a_sec->start = isec->header.sh_addr; + a_sec->size = isec->header.sh_size; + a_sec->flags = isec->header.sh_flags; + a_sec->name_off = strings_size - strings_left; + append_string(isec->name, &strings, &strings_size, &strings_left); + if (a_sec->start < a_hdr->start) + a_hdr->start = a_sec->start; + if (a_sec->start+a_sec->size > a_hdr->end) + a_hdr->end = a_sec->start+a_sec->size; + ++a_sec; + } + + /* Build the kallsyms symbol table from the symbol hashes. */ + for (i = 0; i < HASH_BUCKETS; ++i) { + struct obj_symbol *sym = fin->symtab[i]; + for (sym = fin->symtab[i]; sym ; sym = sym->next) { + if (!sym || sym->secidx >= fin->header.e_shnum) + continue; + if ((a_sym.section_off = fin_to_allsym_map[sym->secidx]) == -1) + continue; + if (strcmp(sym->name, "gcc2_compiled.") == 0 || + strncmp(sym->name, "__insmod_", 9) == 0) + continue; + a_sym.symbol_addr = sym->value; + if (fin->header.e_type == ET_REL) + a_sym.symbol_addr += fin->sections[sym->secidx]->header.sh_addr; + a_sym.name_off = strings_size - strings_left; + append_symbol(&a_sym, &symbols, &symbols_size, &symbols_left); + append_string(sym->name, &strings, &strings_size, &strings_left); + ++a_hdr->symbols; + } + } + free(fin_to_allsym_map); + + /* Sort the symbols into ascending order by address and name */ + sym_strings = strings; /* For symbol_compare */ + qsort((char *) symbols, (unsigned) a_hdr->symbols, + sizeof(* symbols), symbol_compare); + sym_strings = NULL; + + /* Put the lot together */ + osec->header.sh_size = a_hdr->total_size = + a_hdr->symbol_off + + a_hdr->symbols*a_hdr->symbol_size + + strings_size - strings_left; + a_hdr = (struct kallsyms_header *) osec->contents = + xrealloc(a_hdr, a_hdr->total_size); + p = (char *)a_hdr + a_hdr->symbol_off; + memcpy(p, symbols, a_hdr->symbols*a_hdr->symbol_size); + free(symbols); + p += a_hdr->symbols*a_hdr->symbol_size; + a_hdr->string_off = p - (char *)a_hdr; + memcpy(p, strings, strings_size - strings_left); + free(strings); + + *fout_result = fout; + return 0; +} diff --git a/mdk-stage1/insmod-modutils/obj/obj_load.c b/mdk-stage1/insmod-modutils/obj/obj_load.c new file mode 100644 index 000000000..4db20a998 --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_load.c @@ -0,0 +1,354 @@ +/* Elf file reader. + Copyright 1996, 1997 Linux International. + + Contributed by Richard Henderson <rth@tamu.edu> + obj_free() added by Björn Ekwall <bj0rn@blox.se> March 1999 + Support for kallsyms Keith Owens <kaos@ocs.com.au> April 2000 + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ident "$Id$" + +#include <alloca.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include "obj.h" +#include "util.h" + +/*======================================================================*/ + +struct obj_file * +obj_load (int fp, Elf32_Half e_type, const char *filename) +{ + struct obj_file *f; + ElfW(Shdr) *section_headers; + int shnum, i; + char *shstrtab; + + /* Read the file header. */ + + f = arch_new_file(); + memset(f, 0, sizeof(*f)); + f->symbol_cmp = strcmp; + f->symbol_hash = obj_elf_hash; + f->load_order_search_start = &f->load_order; + + gzf_lseek(fp, 0, SEEK_SET); + if (gzf_read(fp, &f->header, sizeof(f->header)) != sizeof(f->header)) + { + error("error reading ELF header %s: %m", filename); + 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("%s is not an ELF file", filename); + 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("ELF file %s not for this architecture", filename); + return NULL; + } + if (f->header.e_type != e_type && e_type != ET_NONE) + { + switch (e_type) { + case ET_REL: + error("ELF file %s not a relocatable object", filename); + break; + case ET_EXEC: + error("ELF file %s not an executable object", filename); + break; + default: + error("ELF file %s has wrong type, expecting %d got %d", + filename, e_type, f->header.e_type); + break; + } + return NULL; + } + + /* Read the section headers. */ + + if (f->header.e_shentsize != sizeof(ElfW(Shdr))) + { + error("section header size mismatch %s: %lu != %lu", + filename, + (unsigned long)f->header.e_shentsize, + (unsigned long)sizeof(ElfW(Shdr))); + return NULL; + } + + shnum = f->header.e_shnum; + f->sections = xmalloc(sizeof(struct obj_section *) * shnum); + memset(f->sections, 0, sizeof(struct obj_section *) * shnum); + + section_headers = alloca(sizeof(ElfW(Shdr)) * shnum); + gzf_lseek(fp, f->header.e_shoff, SEEK_SET); + if (gzf_read(fp, section_headers, sizeof(ElfW(Shdr))*shnum) != sizeof(ElfW(Shdr))*shnum) + { + error("error reading ELF section headers %s: %m", filename); + return NULL; + } + + /* Read the section data. */ + + for (i = 0; i < shnum; ++i) + { + struct obj_section *sec; + + f->sections[i] = sec = arch_new_section(); + memset(sec, 0, sizeof(*sec)); + + 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); + gzf_lseek(fp, sec->header.sh_offset, SEEK_SET); + if (gzf_read(fp, sec->contents, sec->header.sh_size) != sec->header.sh_size) + { + error("error reading ELF section data %s: %m", filename); + return NULL; + } + } + else + sec->contents = NULL; + break; + +#if SHT_RELM == SHT_REL + case SHT_RELA: + if (sec->header.sh_size) { + error("RELA relocations not supported on this architecture %s", filename); + return NULL; + } + break; +#else + case SHT_REL: + if (sec->header.sh_size) { + error("REL relocations not supported on this architecture %s", filename); + return NULL; + } + break; +#endif + + default: + if (sec->header.sh_type >= SHT_LOPROC) + { + if (arch_load_proc_section(sec, fp) < 0) + return NULL; + break; + } + + error("can't handle sections of type %ld %s", + (long)sec->header.sh_type, filename); + return NULL; + } + } + + /* Do what sort of interpretation as needed by each section. */ + + shstrtab = f->sections[f->header.e_shstrndx]->contents; + + for (i = 0; i < shnum; ++i) + { + struct obj_section *sec = f->sections[i]; + sec->name = shstrtab + sec->header.sh_name; + } + + for (i = 0; i < shnum; ++i) + { + struct obj_section *sec = f->sections[i]; + + /* .modinfo and .modstring should be contents only but gcc has no + * attribute for that. The kernel may have marked these sections as + * ALLOC, ignore the allocate bit. + */ + if (strcmp(sec->name, ".modinfo") == 0 || + strcmp(sec->name, ".modstring") == 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; + + if (sec->header.sh_entsize != sizeof(ElfW(Sym))) + { + error("symbol size mismatch %s: %lu != %lu", + filename, + (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 = xmalloc(j *= sizeof(struct obj_symbol *)); + memset(f->local_symtab, 0, j); + + /* 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; + + obj_add_symbol(f, name, j, sym->st_info, sym->st_shndx, + sym->st_value, sym->st_size); + + } + } + break; + } + } + + /* second pass to add relocation data to symbols */ + for (i = 0; i < shnum; ++i) + { + struct obj_section *sec = f->sections[i]; + switch (sec->header.sh_type) + { + case SHT_RELM: + { + unsigned long nrel, j; + ElfW(RelM) *rel; + struct obj_section *symtab; + char *strtab; + if (sec->header.sh_entsize != sizeof(ElfW(RelM))) + { + error("relocation entry size mismatch %s: %lu != %lu", + filename, + (unsigned long)sec->header.sh_entsize, + (unsigned long)sizeof(ElfW(RelM))); + return NULL; + } + + nrel = sec->header.sh_size / sizeof(ElfW(RelM)); + rel = (ElfW(RelM) *) sec->contents; + symtab = f->sections[sec->header.sh_link]; + strtab = f->sections[symtab->header.sh_link]->contents; + + /* Save the relocate type in each symbol entry. */ + for (j = 0; j < nrel; ++j, ++rel) + { + ElfW(Sym) *extsym; + struct obj_symbol *intsym; + unsigned long symndx; + symndx = ELFW(R_SYM)(rel->r_info); + if (symndx) + { + extsym = ((ElfW(Sym) *) symtab->contents) + symndx; + if (ELFW(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]; + } + else + { + /* Others we look up in the hash table. */ + const char *name; + if (extsym->st_name) + name = strtab + extsym->st_name; + else + name = f->sections[extsym->st_shndx]->name; + intsym = obj_find_symbol(f, name); + } + intsym->r_type = ELFW(R_TYPE)(rel->r_info); + } + } + } + break; + } + } + + f->filename = xstrdup(filename); + + return f; +} + +void obj_free(struct obj_file *f) +{ + struct obj_section *sec; + struct obj_symbol *sym; + struct obj_symbol *next; + int i; + int n; + + if (f->sections) { + n = f->header.e_shnum; + for (i = 0; i < n; ++i) { + if ((sec = f->sections[i]) != NULL) { + if (sec->contents) + free(sec->contents); + free(sec); + } + } + free(f->sections); + } + + for (i = 0; i < HASH_BUCKETS; ++i) { + for (sym = f->symtab[i]; sym; sym = next) { + next = sym->next; + free(sym); + } + } + + if (f->local_symtab) + free(f->local_symtab); + + if (f->filename) + free((char *)(f->filename)); + + if (f->persist) + free((char *)(f->persist)); + + free(f); +} diff --git a/mdk-stage1/insmod-modutils/obj/obj_m68k.c b/mdk-stage1/insmod-modutils/obj/obj_m68k.c new file mode 100644 index 000000000..cb485aed7 --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_m68k.c @@ -0,0 +1,147 @@ +/* m68k specific support for Elf loading and relocation. + Copyright 1996, 1997 Linux International. + + Contributed by Richard Henderson <rth@tamu.edu> + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ident "$Id$" + +#include <stddef.h> +#include <module.h> +#include <obj.h> +#include <util.h> + + +/*======================================================================*/ + +struct obj_file * +arch_new_file (void) +{ + return xmalloc(sizeof(struct obj_file)); +} + +struct obj_section * +arch_new_section (void) +{ + return xmalloc(sizeof(struct obj_section)); +} + +struct obj_symbol * +arch_new_symbol (void) +{ + return xmalloc(sizeof(struct obj_symbol)); +} + +int +arch_load_proc_section(struct obj_section *sec, int fp) +{ + /* Assume it's just a debugging section that we can safely + ignore ... */ + sec->contents = NULL; + + return 0; +} + +enum obj_reloc +arch_apply_relocation (struct obj_file *ef, + struct obj_section *targsec, + struct obj_section *symsec, + struct obj_symbol *sym, + Elf32_Rela *rel, + Elf32_Addr v) +{ + char *loc = targsec->contents + rel->r_offset; + Elf32_Addr dot = targsec->header.sh_addr + rel->r_offset; + + enum obj_reloc ret = obj_reloc_ok; + + switch (ELF32_R_TYPE(rel->r_info)) + { + case R_68K_NONE: + 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_32: + *(int *)loc = v; + break; + + case R_68K_PC8: + v -= dot; + if ((Elf32_Sword)v > 0x7f || (Elf32_Sword)v < -(Elf32_Sword)0x80) + ret = obj_reloc_overflow; + *(char *)loc = v; + break; + case R_68K_PC16: + v -= dot; + if ((Elf32_Sword)v > 0x7fff || (Elf32_Sword)v < -(Elf32_Sword)0x8000) + ret = obj_reloc_overflow; + *(short *)loc = v; + break; + case R_68K_PC32: + *(int *)loc = v - dot; + break; + + case R_68K_RELATIVE: + *(int *)loc += ef->baseaddr; + break; + + default: + ret = obj_reloc_unhandled; + break; + } + + return ret; +} + +int +arch_create_got (struct obj_file *ef) +{ + return 1; +} + +int +arch_init_module (struct obj_file *f, struct module *mod) +{ + return 1; +} + +int +arch_finalize_section_address(struct obj_file *f, Elf32_Addr base) +{ + int i, n = f->header.e_shnum; + + f->baseaddr = base; + for (i = 0; i < n; ++i) + f->sections[i]->header.sh_addr += base; + return 1; +} + +int +arch_archdata (struct obj_file *fin, struct obj_section *sec) +{ + return 0; +} diff --git a/mdk-stage1/insmod-modutils/obj/obj_mips.c b/mdk-stage1/insmod-modutils/obj/obj_mips.c new file mode 100644 index 000000000..c2315b659 --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_mips.c @@ -0,0 +1,238 @@ +/* MIPS specific support for Elf loading and relocation. + Copyright 1997, 1998 Linux International. + Contributed by Ralf Baechle <ralf@gnu.ai.mit.edu> + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ident "$Id$" + +#include <string.h> +#include <stdlib.h> +#include <assert.h> + +#include <module.h> +#include <obj.h> +#include <util.h> + + +/*======================================================================*/ + +struct mips_hi16 +{ + struct mips_hi16 *next; + Elf32_Addr *addr; + Elf32_Addr value; +}; + +struct mips_file +{ + struct obj_file root; + struct mips_hi16 *mips_hi16_list; +}; + +/*======================================================================*/ + +struct obj_file * +arch_new_file (void) +{ + struct mips_file *mf; + + mf = xmalloc(sizeof(*mf)); + mf->mips_hi16_list = NULL; + + return (struct obj_file *) mf; +} + +struct obj_section * +arch_new_section (void) +{ + return xmalloc(sizeof(struct obj_section)); +} + +struct obj_symbol * +arch_new_symbol (void) +{ + return xmalloc(sizeof(struct obj_symbol)); +} + +int +arch_load_proc_section(struct obj_section *sec, int fp) +{ + switch (sec->header.sh_type) + { + case SHT_MIPS_DEBUG: + case SHT_MIPS_REGINFO: + /* Actually these two sections are as useless as something can be ... */ + sec->contents = NULL; + break; + + case SHT_MIPS_LIBLIST: + case SHT_MIPS_CONFLICT: + case SHT_MIPS_GPTAB: + case SHT_MIPS_UCODE: + case SHT_MIPS_OPTIONS: + case SHT_MIPS_DWARF: + case SHT_MIPS_EVENTS: + /* These shouldn't ever be in a module file. */ + error("Unhandled section header type: %08x", sec->header.sh_type); + + default: + /* We don't even know the type. This time it might as well be a + supernova. */ + error("Unknown section header type: %08x", sec->header.sh_type); + return -1; + } + + return 0; +} + +enum obj_reloc +arch_apply_relocation (struct obj_file *f, + struct obj_section *targsec, + struct obj_section *symsec, + struct obj_symbol *sym, + Elf32_Rel *rel, + Elf32_Addr v) +{ + struct mips_file *mf = (struct mips_file *)f; + Elf32_Addr *loc = (Elf32_Addr *)(targsec->contents + rel->r_offset); + Elf32_Addr dot = targsec->header.sh_addr + rel->r_offset; + enum obj_reloc ret = obj_reloc_ok; + + /* _gp_disp is a magic symbol for PIC which is not supported for + the kernel and loadable modules. */ + if (strcmp(sym->name, "_gp_disp") == 0) + ret = obj_reloc_unhandled; + + switch (ELF32_R_TYPE(rel->r_info)) + { + 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 = mf->mips_hi16_list; + mf->mips_hi16_list = n; + break; + } + + case R_MIPS_LO16: + { + unsigned long insnlo = *loc; + Elf32_Addr val, vallo; + + /* Sign extend the addend we extract from the lo insn. */ + vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000; + + if (mf->mips_hi16_list != NULL) + { + struct mips_hi16 *l; + + l = mf->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; + } + + mf->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; + } + + default: + ret = obj_reloc_unhandled; + break; + } + + return ret; +} + +int +arch_create_got (struct obj_file *f) +{ + return 1; +} + +int +arch_init_module (struct obj_file *f, struct module *mod) +{ + return 1; +} + +int +arch_finalize_section_address(struct obj_file *f, Elf32_Addr base) +{ + int i, n = f->header.e_shnum; + + f->baseaddr = base; + for (i = 0; i < n; ++i) + f->sections[i]->header.sh_addr += base; + return 1; +} + +int +arch_archdata (struct obj_file *fin, struct obj_section *sec) +{ + return 0; +} diff --git a/mdk-stage1/insmod-modutils/obj/obj_ppc.c b/mdk-stage1/insmod-modutils/obj/obj_ppc.c new file mode 100644 index 000000000..89bb8e46b --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_ppc.c @@ -0,0 +1,255 @@ +/* PowerPC specific support for Elf loading and relocation. + Copyright 1996, 1997 Linux International. + + Adapted by Paul Mackerras <paulus@cs.anu.edu.au> from the + obj-sparc.c and obj-alpha.c files. + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ident "$Id$" + +#include <stddef.h> +#include <module.h> +#include <obj.h> +#include <util.h> +#include <assert.h> + + +/*======================================================================*/ + +/* + * Unfortunately, the bl (branch-and-link) instruction used for + * procedure calls on the PowerPC can only reach +/- 32MB from the + * current instruction. If the module is loaded far enough away from + * the main kernel text (or other modules) that this limit is + * exceeded, we have to redirect procedure calls via a procedure + * linkage table (PLT). Each entry in the PLT contains instructions + * to put the address of the procedure in a register and jump to it. + */ + +typedef unsigned int instruction; /* a powerpc instruction (4 bytes) */ + +struct ppc_plt_entry +{ + struct ppc_plt_entry *next; + ElfW(Addr) addend; + int offset; + int inited; +}; + +struct ppc_file +{ + struct obj_file file; + struct obj_section *plt; +}; + +struct ppc_symbol +{ + struct obj_symbol sym; + struct ppc_plt_entry *plt_entries; +}; + +struct obj_file * +arch_new_file (void) +{ + struct ppc_file *f; + + f = xmalloc(sizeof(struct ppc_file)); + f->plt = NULL; + return &f->file; +} + +struct obj_section * +arch_new_section (void) +{ + return xmalloc(sizeof(struct obj_section)); +} + +struct obj_symbol * +arch_new_symbol (void) +{ + struct ppc_symbol *p; + + p = xmalloc(sizeof(struct ppc_symbol)); + p->plt_entries = NULL; + return &p->sym; +} + +int +arch_load_proc_section(struct obj_section *sec, int fp) +{ + /* Assume it's just a debugging section that we can safely + ignore ... */ + sec->contents = NULL; + + return 0; +} + +enum obj_reloc +arch_apply_relocation (struct obj_file *ef, + struct obj_section *targsec, + struct obj_section *symsec, + struct obj_symbol *sym, + Elf32_Rela *rel, + Elf32_Addr v) +{ + Elf32_Addr *loc = (Elf32_Addr *)(targsec->contents + rel->r_offset); + Elf32_Addr dot = targsec->header.sh_addr + rel->r_offset; + struct ppc_file *pf = (struct ppc_file *) ef; + struct ppc_symbol *psym = (struct ppc_symbol *) sym; + struct ppc_plt_entry *pe; + instruction *ip; + + enum obj_reloc ret = obj_reloc_ok; + + switch (ELF32_R_TYPE(rel->r_info)) + { + 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: + /* find the plt entry and initialize it if necessary */ + assert(psym != NULL); + for (pe = psym->plt_entries; pe != NULL && pe->addend != rel->r_addend; ) + pe = pe->next; + assert(pe != NULL); + if (!pe->inited) + { + ip = (instruction *) (pf->plt->contents + pe->offset); + 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 */ + pe->inited = 1; + } + + v -= dot; + if ((int)v < -0x02000000 || (int)v >= 0x02000000) + { + /* go via the plt */ + v = pf->plt->header.sh_addr + pe->offset - dot; + } + if (v & 3) + ret = obj_reloc_dangerous; + *loc = (*loc & ~0x03fffffc) | (v & 0x03fffffc); + break; + + case R_PPC_REL32: + *loc = v - dot; + break; + + case R_PPC_ADDR32: + *loc = v; + break; + + default: + ret = obj_reloc_unhandled; + break; + } + + return ret; +} + +int +arch_create_got (struct obj_file *f) +{ + struct ppc_file *pf = (struct ppc_file *) f; + int i, offset; + struct obj_section *sec, *syms, *strs; + ElfW(Rela) *rel, *relend; + ElfW(Sym) *symtab, *extsym; + const char *strtab, *name; + struct ppc_symbol *intsym; + struct ppc_plt_entry *pe; + + offset = 0; + for (i = 0; i < f->header.e_shnum; ++i) + { + sec = f->sections[i]; + if (sec->header.sh_type != SHT_RELM) + continue; + syms = f->sections[sec->header.sh_link]; + strs = f->sections[syms->header.sh_link]; + + rel = (ElfW(RelM) *) sec->contents; + relend = rel + (sec->header.sh_size / sizeof(ElfW(RelM))); + symtab = (ElfW(Sym) *) syms->contents; + strtab = (const char *) strs->contents; + + for (; rel < relend; ++rel) + { + if (ELF32_R_TYPE(rel->r_info) != R_PPC_REL24) + continue; + extsym = &symtab[ELF32_R_SYM(rel->r_info)]; + if (extsym->st_name) + name = strtab + extsym->st_name; + else + name = f->sections[extsym->st_shndx]->name; + intsym = (struct ppc_symbol *) obj_find_symbol(f, name); + + for (pe = intsym->plt_entries; pe != NULL; pe = pe->next) + if (pe->addend == rel->r_addend) + break; + if (pe == NULL) + { + pe = xmalloc(sizeof(struct ppc_plt_entry)); + pe->next = intsym->plt_entries; + pe->addend = rel->r_addend; + pe->offset = offset; + pe->inited = 0; + intsym->plt_entries = pe; + offset += 16; + } + } + } + + pf->plt = obj_create_alloced_section(f, ".plt", 16, offset); + + return 1; +} + +int +arch_init_module (struct obj_file *f, struct module *mod) +{ + return 1; +} + +int +arch_finalize_section_address(struct obj_file *f, Elf32_Addr base) +{ + int i, n = f->header.e_shnum; + + f->baseaddr = base; + for (i = 0; i < n; ++i) + f->sections[i]->header.sh_addr += base; + return 1; +} + +int +arch_archdata (struct obj_file *fin, struct obj_section *sec) +{ + return 0; +} diff --git a/mdk-stage1/insmod-modutils/obj/obj_reloc.c b/mdk-stage1/insmod-modutils/obj/obj_reloc.c new file mode 100644 index 000000000..f5f2de90d --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_reloc.c @@ -0,0 +1,435 @@ +/* Elf relocation routines. + Copyright 1996, 1997 Linux International. + + Contributed by Richard Henderson <rth@tamu.edu> + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ident "$Id$" + +#include <string.h> +#include <assert.h> +#include <alloca.h> + +#include <obj.h> +#include <util.h> + +/*======================================================================*/ + +int +obj_string_patch(struct obj_file *f, int secidx, ElfW(Addr) offset, + const char *string) +{ + struct obj_string_patch_struct *p; + struct obj_section *strsec; + size_t len = strlen(string)+1; + char *loc; + + p = xmalloc(sizeof(*p)); + p->next = f->string_patches; + p->reloc_secidx = secidx; + p->reloc_offset = offset; + f->string_patches = p; + + strsec = obj_find_section(f, ".kstrtab"); + if (strsec == NULL) + { + strsec = obj_create_alloced_section(f, ".kstrtab", 1, len); + p->string_offset = 0; + loc = strsec->contents; + } + else + { + p->string_offset = strsec->header.sh_size; + loc = obj_extend_section(strsec, len); + } + memcpy(loc, string, len); + + return 1; +} + +int +obj_symbol_patch(struct obj_file *f, int secidx, ElfW(Addr) offset, + struct obj_symbol *sym) +{ + struct obj_symbol_patch_struct *p; + + p = xmalloc(sizeof(*p)); + p->next = f->symbol_patches; + p->reloc_secidx = secidx; + p->reloc_offset = offset; + p->sym = sym; + f->symbol_patches = p; + + return 1; +} + +int +obj_check_undefineds(struct obj_file *f, int quiet) +{ + unsigned long i; + int ret = 1; + + for (i = 0; i < HASH_BUCKETS; ++i) + { + 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) + { + sym->secidx = SHN_ABS; + sym->value = 0; + } + else if (sym->r_type) /* assumes R_arch_NONE is 0 on all arch */ + { + if (!quiet) + error("unresolved symbol %s", sym->name); + ret = 0; + } + } + } + + return ret; +} + +void +obj_clear_undefineds(struct obj_file *f) +{ + unsigned long i; + struct obj_symbol *sym; + for (i = 0; i < HASH_BUCKETS; ++i) + { + for (sym = f->symtab[i]; sym ; sym = sym->next) + if (sym->secidx == SHN_UNDEF) + { + sym->secidx = SHN_ABS; + sym->value = 0; + } + } +} + +void +obj_allocate_commons(struct obj_file *f) +{ + struct common_entry + { + struct common_entry *next; + struct obj_symbol *sym; + } *common_head = NULL; + + unsigned long i; + + for (i = 0; i < HASH_BUCKETS; ++i) + { + struct obj_symbol *sym; + for (sym = f->symtab[i]; sym ; sym = sym->next) + if (sym->secidx == SHN_COMMON) + { + /* Collect all COMMON symbols and sort them by size so as to + minimize space wasted by alignment requirements. */ + { + struct common_entry **p, *n; + for (p = &common_head; *p ; p = &(*p)->next) + if (sym->size <= (*p)->sym->size) + break; + + n = alloca(sizeof(*n)); + n->next = *p; + n->sym = sym; + *p = n; + } + } + } + + for (i = 1; i < f->local_symtab_size; ++i) + { + struct obj_symbol *sym = f->local_symtab[i]; + if (sym && sym->secidx == SHN_COMMON) + { + struct common_entry **p, *n; + for (p = &common_head; *p ; p = &(*p)->next) + if (sym == (*p)->sym) + break; + else if (sym->size < (*p)->sym->size) + { + n = alloca(sizeof(*n)); + n->next = *p; + n->sym = sym; + *p = n; + break; + } + } + } + + if (common_head) + { + /* Find the bss section. */ + for (i = 0; i < f->header.e_shnum; ++i) + if (f->sections[i]->header.sh_type == SHT_NOBITS) + break; + + /* If for some reason there hadn't been one, create one. */ + if (i == f->header.e_shnum) + { + struct obj_section *sec; + + f->sections = xrealloc(f->sections, (i+1) * sizeof(sec)); + f->sections[i] = sec = arch_new_section(); + f->header.e_shnum = i+1; + + memset(sec, 0, sizeof(*sec)); + sec->header.sh_type = SHT_PROGBITS; + sec->header.sh_flags = SHF_WRITE|SHF_ALLOC; + sec->name = ".bss"; + sec->idx = i; + } + + /* Allocate the COMMONS. */ + { + ElfW(Addr) bss_size = f->sections[i]->header.sh_size; + ElfW(Addr) max_align = f->sections[i]->header.sh_addralign; + struct common_entry *c; + + for (c = common_head; c ; c = c->next) + { + ElfW(Addr) align = c->sym->value; + + if (align > max_align) + max_align = align; + if (bss_size & (align - 1)) + bss_size = (bss_size | (align - 1)) + 1; + + c->sym->secidx = i; + c->sym->value = bss_size; + + bss_size += c->sym->size; + } + + f->sections[i]->header.sh_size = bss_size; + f->sections[i]->header.sh_addralign = max_align; + } + } + + /* For the sake of patch relocation and parameter initialization, + allocate zeroed data for NOBITS sections now. Note that after + this we cannot assume NOBITS are really empty. */ + for (i = 0; i < f->header.e_shnum; ++i) + { + struct obj_section *s = f->sections[i]; + if (s->header.sh_type == SHT_NOBITS) + { + if (s->header.sh_size) + s->contents = memset(xmalloc(s->header.sh_size), + 0, s->header.sh_size); + else + s->contents = NULL; + s->header.sh_type = SHT_PROGBITS; + } + } +} + +unsigned long +obj_load_size (struct obj_file *f) +{ + unsigned long dot = 0; + struct obj_section *sec; + + /* Finalize the positions of the sections relative to one another. */ + + for (sec = f->load_order; sec ; sec = sec->load_next) + { + ElfW(Addr) align; + + align = sec->header.sh_addralign; + if (align && (dot & (align - 1))) + dot = (dot | (align - 1)) + 1; + + sec->header.sh_addr = dot; + dot += sec->header.sh_size; + } + + return dot; +} + +int +obj_relocate (struct obj_file *f, ElfW(Addr) base) +{ + int i, n = f->header.e_shnum; + int ret = 1; + + /* Finalize the addresses of the sections. */ + + arch_finalize_section_address(f, base); + + /* And iterate over all of the relocations. */ + + for (i = 0; i < n; ++i) + { + struct obj_section *relsec, *symsec, *targsec, *strsec; + ElfW(RelM) *rel, *relend; + ElfW(Sym) *symtab; + const char *strtab; + + relsec = f->sections[i]; + if (relsec->header.sh_type != SHT_RELM) + continue; + + symsec = f->sections[relsec->header.sh_link]; + targsec = f->sections[relsec->header.sh_info]; + strsec = f->sections[symsec->header.sh_link]; + + rel = (ElfW(RelM) *)relsec->contents; + relend = rel + (relsec->header.sh_size / sizeof(ElfW(RelM))); + symtab = (ElfW(Sym) *)symsec->contents; + strtab = (const char *)strsec->contents; + + for (; rel < relend; ++rel) + { + ElfW(Addr) value = 0; + struct obj_symbol *intsym = NULL; + unsigned long symndx; + ElfW(Sym) *extsym = 0; + const char *errmsg; + + /* Attempt to find a value to use for this relocation. */ + + symndx = ELFW(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) + { + /* 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]; + } + else + { + /* Others we look up in the hash table. */ + const char *name; + if (extsym->st_name) + name = strtab + extsym->st_name; + else + name = f->sections[extsym->st_shndx]->name; + intsym = obj_find_symbol(f, name); + } + + value = obj_symbol_final_value(f, intsym); + } + +#if SHT_RELM == SHT_RELA +#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) +#endif + value += rel->r_addend; +#endif + + /* Do it! */ + switch (arch_apply_relocation(f,targsec,symsec,intsym,rel,value)) + { + 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"; + goto bad_reloc; + case obj_reloc_constant_gp: + errmsg = "Modules compiled with -mconstant-gp cannot be loaded"; + goto bad_reloc; + bad_reloc: + if (extsym) + { + error("%s of type %ld for %s", errmsg, + (long)ELFW(R_TYPE)(rel->r_info), + strtab + extsym->st_name); + } + else + { + error("%s of type %ld", errmsg, + (long)ELFW(R_TYPE)(rel->r_info)); + } + ret = 0; + break; + } + } + } + + /* Finally, take care of the patches. */ + + if (f->string_patches) + { + struct obj_string_patch_struct *p; + struct obj_section *strsec; + ElfW(Addr) strsec_base; + strsec = obj_find_section(f, ".kstrtab"); + strsec_base = strsec->header.sh_addr; + + for (p = f->string_patches; p ; p = p->next) + { + struct obj_section *targsec = f->sections[p->reloc_secidx]; + *(ElfW(Addr) *)(targsec->contents + p->reloc_offset) + = strsec_base + p->string_offset; + } + } + + if (f->symbol_patches) + { + struct obj_symbol_patch_struct *p; + + for (p = f->symbol_patches; p; p = p->next) + { + struct obj_section *targsec = f->sections[p->reloc_secidx]; + *(ElfW(Addr) *)(targsec->contents + p->reloc_offset) + = obj_symbol_final_value(f, p->sym); + } + } + + return ret; +} + +int +obj_create_image (struct obj_file *f, char *image) +{ + struct obj_section *sec; + ElfW(Addr) base = f->baseaddr; + + for (sec = f->load_order; sec ; sec = sec->load_next) + { + char *secimg; + + if (sec->contents == 0) + continue; + + secimg = image + (sec->header.sh_addr - base); + + /* Note that we allocated data for NOBITS sections earlier. */ + memcpy(secimg, sec->contents, sec->header.sh_size); + } + + return 1; +} diff --git a/mdk-stage1/insmod-modutils/obj/obj_s390.c b/mdk-stage1/insmod-modutils/obj/obj_s390.c new file mode 100644 index 000000000..3da72e771 --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_s390.c @@ -0,0 +1,245 @@ +/* S/390 specific support for Elf loading and relocation. + Copyright 1996, 1997 Linux International. + + Contributed by Richard Henderson <rth@tamu.edu> + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ident "$Id$" + +#include <string.h> +#include <assert.h> + +#include <module.h> +#include <obj.h> +#include <util.h> + + +/*======================================================================*/ + +struct s390_got_entry +{ + int offset; + unsigned offset_done : 1; + unsigned reloc_done : 1; +}; + +struct s390_file +{ + struct obj_file root; + struct obj_section *got; +}; + +struct s390_symbol +{ + struct obj_symbol root; + struct s390_got_entry gotent; +}; + + +/*======================================================================*/ + +struct obj_file * +arch_new_file (void) +{ + struct s390_file *f; + f = xmalloc(sizeof(*f)); + f->got = NULL; + return &f->root; +} + +struct obj_section * +arch_new_section (void) +{ + return xmalloc(sizeof(struct obj_section)); +} + +struct obj_symbol * +arch_new_symbol (void) +{ + struct s390_symbol *sym; + sym = xmalloc(sizeof(*sym)); + memset(&sym->gotent, 0, sizeof(sym->gotent)); + return &sym->root; +} + +int +arch_load_proc_section(struct obj_section *sec, int fp) +{ + /* Assume it's just a debugging section that we can safely + ignore ... */ + sec->contents = NULL; + + return 0; +} + +enum obj_reloc +arch_apply_relocation (struct obj_file *f, + struct obj_section *targsec, + struct obj_section *symsec, + struct obj_symbol *sym, + Elf32_Rela *rel, + Elf32_Addr v) +{ + struct s390_file *ifile = (struct s390_file *)f; + struct s390_symbol *isym = (struct s390_symbol *)sym; + + Elf32_Addr *loc = (Elf32_Addr *)(targsec->contents + rel->r_offset); + Elf32_Addr dot = targsec->header.sh_addr + rel->r_offset; + Elf32_Addr got = ifile->got ? ifile->got->header.sh_addr : 0; + + enum obj_reloc ret = obj_reloc_ok; + + switch (ELF32_R_TYPE(rel->r_info)) + { + case R_390_NONE: + break; + + case R_390_32: + *loc += v; + break; + + case R_390_PLT32: + case R_390_PC32: + *loc += v - dot; + 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); + *loc += got - dot; + break; + + case R_390_GOT32: + assert(isym != NULL); + if (!isym->gotent.reloc_done) + { + isym->gotent.reloc_done = 1; + *(Elf32_Addr *)(ifile->got->contents + isym->gotent.offset) = v; + } + *loc += isym->gotent.offset; + break; + + case R_390_GOTOFF: + assert(got != 0); + *loc += v - got; + break; + + default: + ret = obj_reloc_unhandled; + break; + } + + return ret; +} + +int +arch_create_got (struct obj_file *f) +{ + struct s390_file *ifile = (struct s390_file *)f; + int i, n, offset = 0, gotneeded = 0; + + n = ifile->root.header.e_shnum; + for (i = 0; i < n; ++i) + { + struct obj_section *relsec, *symsec, *strsec; + Elf32_Rel *rel, *relend; + Elf32_Sym *symtab; + const char *strtab; + + relsec = ifile->root.sections[i]; + if (relsec->header.sh_type != SHT_REL) + continue; + + symsec = ifile->root.sections[relsec->header.sh_link]; + strsec = ifile->root.sections[symsec->header.sh_link]; + + rel = (Elf32_Rel *)relsec->contents; + relend = rel + (relsec->header.sh_size / sizeof(Elf32_Rel)); + symtab = (Elf32_Sym *)symsec->contents; + strtab = (const char *)strsec->contents; + + for (; rel < relend; ++rel) + { + Elf32_Sym *extsym; + struct s390_symbol *intsym; + const char *name; + + switch (ELF32_R_TYPE(rel->r_info)) + { + case R_390_GOTPC: + case R_390_GOTOFF: + gotneeded = 1; + default: + continue; + + case R_390_GOT32: + break; + } + + extsym = &symtab[ELF32_R_SYM(rel->r_info)]; + if (extsym->st_name) + name = strtab + extsym->st_name; + else + name = f->sections[extsym->st_shndx]->name; + intsym = (struct s390_symbol *)obj_find_symbol(&ifile->root, name); + + if (!intsym->gotent.offset_done) + { + intsym->gotent.offset_done = 1; + intsym->gotent.offset = offset; + offset += 4; + } + } + } + + if (offset > 0 || gotneeded) + ifile->got = obj_create_alloced_section(&ifile->root, ".got", 4, offset); + + return 1; +} + +int +arch_init_module (struct obj_file *f, struct module *m) +{ + return 1; +} + +int +arch_finalize_section_address(struct obj_file *f, Elf32_Addr base) +{ + int i, n = f->header.e_shnum; + + f->baseaddr = base; + for (i = 0; i < n; ++i) + f->sections[i]->header.sh_addr += base; + return 1; +} + +int +arch_archdata (struct obj_file *fin, struct obj_section *sec) +{ + return 0; +} diff --git a/mdk-stage1/insmod-modutils/obj/obj_sparc.c b/mdk-stage1/insmod-modutils/obj/obj_sparc.c new file mode 100644 index 000000000..1a03c9090 --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_sparc.c @@ -0,0 +1,226 @@ +/* Sparc specific support for Elf loading and relocation. + Copyright 1996, 1997 Linux International. + + Contributed by Richard Henderson <rth@tamu.edu> + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ident "$Id$" + +#include <stddef.h> +#include <module.h> +#include <obj.h> +#include <util.h> + + +/*======================================================================*/ + +struct obj_file * +arch_new_file (void) +{ + return xmalloc(sizeof(struct obj_file)); +} + +struct obj_section * +arch_new_section (void) +{ + return xmalloc(sizeof(struct obj_section)); +} + +struct obj_symbol * +arch_new_symbol (void) +{ + return xmalloc(sizeof(struct obj_symbol)); +} + +int +arch_load_proc_section(struct obj_section *sec, int fp) +{ + /* Assume it's just a debugging section that we can safely + ignore ... */ + sec->contents = NULL; + + return 0; +} + +enum obj_reloc +arch_apply_relocation (struct obj_file *ef, + struct obj_section *targsec, + struct obj_section *symsec, + struct obj_symbol *sym, + Elf32_Rela *rel, + Elf32_Addr v) +{ + Elf32_Addr *loc = (Elf32_Addr *)(targsec->contents + rel->r_offset); + Elf32_Addr dot = targsec->header.sh_addr + rel->r_offset; + + enum obj_reloc ret = obj_reloc_ok; + + switch (ELF32_R_TYPE(rel->r_info)) + { + case R_SPARC_NONE: + break; + case R_SPARC_8: + if (v > 0xff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0xff) | (v & 0xff); + break; + case R_SPARC_16: + if (v > 0xffff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0xffff) | (v & 0xffff); + break; + case R_SPARC_32: + *loc = v; + break; + case R_SPARC_DISP8: + v -= dot; + if (v > 0xff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0xff) | (v & 0xff); + break; + case R_SPARC_DISP16: + v -= dot; + if (v > 0xffff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0xffff) | (v & 0xffff); + break; + case R_SPARC_DISP32: + v -= dot; + *loc = v; + break; + case R_SPARC_WDISP30: + v -= dot; + if (v % 4) + ret = obj_reloc_dangerous; + *loc = (*loc & ~0x3fffffff) | (v >> 2); + break; + case R_SPARC_WDISP22: + v -= dot; + if (v % 4) + ret = obj_reloc_dangerous; + *loc = (*loc & ~0x3fffff) | ((v >> 2) & 0x3fffff); + break; + case R_SPARC_HI22: + *loc = (*loc & ~0x3fffff) | (v >> 10); + break; + case R_SPARC_22: + if (v > 0x3fffff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x3fffff) | (v & 0x3fffff); + break; + case R_SPARC_13: + if (v > 0x1fff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x1fff) | (v & 0x1fff); + break; + case R_SPARC_LO10: + *loc = (*loc & ~0x3ff) | (v & 0x3ff); + break; + + case R_SPARC_PC10: + v -= dot; + *loc = (*loc & ~0x3ff) | (v & 0x3ff); + break; + case R_SPARC_PC22: + v -= dot; + *loc = (*loc & ~0x3fffff) | (v >> 10); + break; + + case R_SPARC_UA32: + *(((char *)loc) + 0) = (char)(v >> 24); + *(((char *)loc) + 1) = (char)(v >> 16); + *(((char *)loc) + 2) = (char)(v >> 8); + *(((char *)loc) + 3) = (char)v; + break; + +#ifdef R_SPARC_10 + case R_SPARC_10: + if (v > 0x3ff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x3ff) | (v & 0x3ff); + break; + case R_SPARC_11: + if (v > 0x7ff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x7ff) | (v & 0x7ff); + break; + case R_SPARC_WDISP16: + v -= dot; + if (v % 4) + ret = obj_reloc_dangerous; + *loc = (*loc & ~0x303fff) | ((v << 4) & 0x300000) | ((v >> 2) & 0x3fff); + break; + case R_SPARC_WDISP19: + v -= dot; + if (v % 4) + ret = obj_reloc_dangerous; + *loc = (*loc & ~0x7ffff) | ((v >> 2) & 0x7ffff); + break; + case R_SPARC_7: + if (v > 0x7f) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x7f) | (v & 0x7f); + break; + case R_SPARC_5: + if (v > 0x1f) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x1f) | (v & 0x1f); + break; + case R_SPARC_6: + if (v > 0x3f) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x3f) | (v & 0x3f); + break; +#endif /* R_SPARC_10 */ + + default: + ret = obj_reloc_unhandled; + break; + } + + return ret; +} + +int +arch_create_got (struct obj_file *ef) +{ + return 1; +} + +int +arch_init_module (struct obj_file *f, struct module *mod) +{ + return 1; +} + +int +arch_finalize_section_address(struct obj_file *f, Elf32_Addr base) +{ + int i, n = f->header.e_shnum; + + f->baseaddr = base; + for (i = 0; i < n; ++i) + f->sections[i]->header.sh_addr += base; + return 1; +} + +int +arch_archdata (struct obj_file *fin, struct obj_section *sec) +{ + return 0; +} diff --git a/mdk-stage1/insmod-modutils/obj/obj_sparc64.c b/mdk-stage1/insmod-modutils/obj/obj_sparc64.c new file mode 100644 index 000000000..84e8d18a0 --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_sparc64.c @@ -0,0 +1,352 @@ +/* Sparc64 specific support for Elf loading and relocation. + Copyright 1997 Linux International. + + Contributed by Jakub Jelinek <jj@sunsite.mff.cuni.cz> + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ident "$Id$" + +#include <stddef.h> +#include <module.h> +#include <obj.h> +#include <util.h> + + +/*======================================================================*/ + +struct obj_file * +arch_new_file (void) +{ + return xmalloc(sizeof(struct obj_file)); +} + +struct obj_section * +arch_new_section (void) +{ + return xmalloc(sizeof(struct obj_section)); +} + +struct obj_symbol * +arch_new_symbol (void) +{ + return xmalloc(sizeof(struct obj_symbol)); +} + +#ifdef BROKEN_SPARC64_RELOCS + +#undef R_SPARC_PLT32 +#undef R_SPARC_HIPLT22 +#undef R_SPARC_LOPLT10 +#undef R_SPARC_PCPLT32 +#undef R_SPARC_PCPLT22 +#undef R_SPARC_PCPLT10 +#undef R_SPARC_10 +#undef R_SPARC_11 +#undef R_SPARC_64 +#undef R_SPARC_OLO10 +#undef R_SPARC_HH22 +#undef R_SPARC_HM10 +#undef R_SPARC_LM22 +#undef R_SPARC_PC_HH22 +#undef R_SPARC_PC_HM10 +#undef R_SPARC_PC_LM22 +#undef R_SPARC_WDISP16 +#undef R_SPARC_WDISP19 +#undef R_SPARC_GLOB_JMP +#undef R_SPARC_7 +#undef R_SPARC_5 +#undef R_SPARC_6 + +#define R_SPARC_10 24 +#define R_SPARC_11 25 +#define R_SPARC_64 26 +#define R_SPARC_OLO10 27 +#define R_SPARC_HH22 28 +#define R_SPARC_HM10 29 +#define R_SPARC_LM22 30 +#define R_SPARC_PC_HH22 31 +#define R_SPARC_PC_HM10 32 +#define R_SPARC_PC_LM22 33 +#define R_SPARC_WDISP16 34 +#define R_SPARC_WDISP19 35 +#define R_SPARC_GLOB_JMP 36 +#define R_SPARC_7 37 +#define R_SPARC_5 38 +#define R_SPARC_6 39 + +#else + +#ifndef R_SPARC_64 + +#define R_SPARC_64 32 +#define R_SPARC_OLO10 33 +#define R_SPARC_HH22 34 +#define R_SPARC_HM10 35 +#define R_SPARC_LM22 36 +#define R_SPARC_PC_HH22 37 +#define R_SPARC_PC_HM10 38 +#define R_SPARC_PC_LM22 39 + +#endif + +#endif + +int +arch_load_proc_section(struct obj_section *sec, int fp) +{ + /* Assume it's just a debugging section that we can safely + ignore ... */ + sec->contents = NULL; + + return 0; +} + +#define ELF64_R_TYPE_ID(info) ((info) & 0xff) +#define ELF64_R_TYPE_DATA(info) ((info) >> 8) + +enum obj_reloc +arch_apply_relocation (struct obj_file *ef, + struct obj_section *targsec, + struct obj_section *symsec, + struct obj_symbol *sym, + Elf64_Rela *rel, + Elf64_Addr v) +{ + unsigned int *loc = (unsigned int *)(targsec->contents + rel->r_offset); + unsigned int dot = targsec->header.sh_addr + rel->r_offset; + + enum obj_reloc ret = obj_reloc_ok; + + switch (ELF64_R_TYPE_ID(rel->r_info)) + { + case R_SPARC_NONE: + break; + + case R_SPARC_64: + case R_SPARC_UA64: + if (! ((long) loc & 3)) { + /* Common in .eh_frame */ + ((unsigned int *) loc) [0] = v >> 32; + ((unsigned int *) loc) [1] = v; + break; + } + ((unsigned char *) loc) [0] = v >> 56; + ((unsigned char *) loc) [1] = v >> 48; + ((unsigned char *) loc) [2] = v >> 40; + ((unsigned char *) loc) [3] = v >> 32; + ((unsigned char *) loc) [4] = v >> 24; + ((unsigned char *) loc) [5] = v >> 16; + ((unsigned char *) loc) [6] = v >> 8; + ((unsigned char *) loc) [7] = v; + break; + case R_SPARC_32: + case R_SPARC_UA32: + if (! ((long) loc & 3)) { + *loc = v; + break; + } + ((unsigned char *) loc) [0] = v >> 24; + ((unsigned char *) loc) [1] = v >> 16; + ((unsigned char *) loc) [2] = v >> 8; + ((unsigned char *) loc) [3] = v; + break; + case R_SPARC_16: + if (v > 0xffff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0xffff) | (v & 0xffff); + break; + case R_SPARC_8: + if (v > 0xff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0xff) | (v & 0xff); + break; + + case R_SPARC_DISP32: + v -= dot; + *loc = v; + break; + case R_SPARC_DISP16: + v -= dot; + if (v > 0xffff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0xffff) | (v & 0xffff); + break; + case R_SPARC_DISP8: + v -= dot; + if (v > 0xff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0xff) | (v & 0xff); + break; + case R_SPARC_WDISP30: + v -= dot; + if (v % 4) + ret = obj_reloc_dangerous; + *loc = (*loc & ~0x3fffffff) | ((v >> 2) & 0x3fffffff); + break; + + /* MEDLOW code model relocs */ + case R_SPARC_LO10: + *loc = (*loc & ~0x3ff) | (v & 0x3ff); + break; + case R_SPARC_HI22: + *loc = (*loc & ~0x3fffff) | (v >> 10); + break; + case R_SPARC_OLO10: + *loc = (*loc & ~0x1fff) | (((v & 0x3ff) + ELF64_R_TYPE_DATA (rel->r_info)) & 0x1fff); + break; + + /* MEDMID code model relocs */ + case R_SPARC_H44: + *loc = (*loc & ~0x3fffff) | (v >> 22); + break; + case R_SPARC_M44: + *loc = (*loc & ~0x3ff) | ((v >> 12) & 0x3ff); + break; + case R_SPARC_L44: + *loc = (*loc & ~0xfff) | (v & 0xfff); + break; + + /* MEDANY code model relocs */ + case R_SPARC_HH22: + *loc = (*loc & ~0x3fffff) | (v >> 42); + break; + case R_SPARC_HM10: + *loc = (*loc & ~0x3ff) | ((v >> 32) & 0x3ff); + break; + case R_SPARC_LM22: + *loc = (*loc & ~0x3fffff) | ((v >> 10) & 0x3fffff); + break; + + case R_SPARC_WDISP22: + v -= dot; + if (v % 4) + ret = obj_reloc_dangerous; + *loc = (*loc & ~0x3fffff) | ((v >> 2) & 0x3fffff); + break; + case R_SPARC_22: + if (v > 0x3fffff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x3fffff) | (v & 0x3fffff); + break; + case R_SPARC_13: + if (v > 0x1fff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x1fff) | (v & 0x1fff); + break; + + case R_SPARC_PC10: + v -= dot; + *loc = (*loc & ~0x3ff) | (v & 0x3ff); + break; + case R_SPARC_PC22: + v -= dot; + *loc = (*loc & ~0x3fffff) | ((v >> 10) & 0x3fffff); + break; + +#ifdef R_SPARC_10 + case R_SPARC_10: + if (v > 0x3ff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x3ff) | (v & 0x3ff); + break; + case R_SPARC_11: + if (v > 0x7ff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x7ff) | (v & 0x7ff); + break; + +#ifdef R_SPARC_64 + case R_SPARC_PC_HH22: + v -= dot; + *loc = (*loc & ~0x3fffff) | (v >> 42); + break; + case R_SPARC_PC_HM10: + v -= dot; + *loc = (*loc & ~0x3ff) | ((v >> 32) & 0x3ff); + break; + case R_SPARC_PC_LM22: + v -= dot; + *loc = (*loc & ~0x3fffff) | ((v >> 10) & 0x3fffff); + break; +#endif + + case R_SPARC_WDISP16: + v -= dot; + if (v % 4) + ret = obj_reloc_dangerous; + *loc = (*loc & ~0x303fff) | ((v << 4) & 0x300000) | ((v >> 2) & 0x3fff); + break; + case R_SPARC_WDISP19: + v -= dot; + if (v % 4) + ret = obj_reloc_dangerous; + *loc = (*loc & ~0x7ffff) | ((v >> 2) & 0x7ffff); + break; + case R_SPARC_7: + if (v > 0x7f) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x7f) | (v & 0x7f); + break; + case R_SPARC_5: + if (v > 0x1f) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x1f) | (v & 0x1f); + break; + case R_SPARC_6: + if (v > 0x3f) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x3f) | (v & 0x3f); + break; +#endif /* R_SPARC_10 */ + + default: + ret = obj_reloc_unhandled; + break; + } + + return ret; +} + +int +arch_create_got (struct obj_file *ef) +{ + return 1; +} + +int +arch_init_module (struct obj_file *f, struct module *mod) +{ + return 1; +} + +int +arch_finalize_section_address(struct obj_file *f, Elf64_Addr base) +{ + int i, n = f->header.e_shnum; + + f->baseaddr = base; + for (i = 0; i < n; ++i) + f->sections[i]->header.sh_addr += base; + return 1; +} + +int +arch_archdata (struct obj_file *fin, struct obj_section *sec) +{ + return 0; +} diff --git a/mdk-stage1/insmod-modutils/util/Makefile b/mdk-stage1/insmod-modutils/util/Makefile new file mode 100644 index 000000000..844f8c0dc --- /dev/null +++ b/mdk-stage1/insmod-modutils/util/Makefile @@ -0,0 +1,39 @@ + #****************************************************************************** + # + # insmod from modutils (generic) + # + # $Id$ + # + # Copyright 1996, 1997 Linux International. + # + #***************************************************************************** + +top_dir = ../.. + +include $(top_dir)/Makefile.common + + +all: libutil.a libutil-STANDALONE.a #libutil-DIET.a + +clean: + rm -f *.o *.a + + +FLAGS = -c -Wall -Os -fomit-frame-pointer -I./../include -D_GNU_SOURCE -DELF_MACHINE_H='"elf_$(ARCH).h"' -DARCH_$(ARCH) -DHAVE_WORDEXP=1 -DHAVE_GLOB=1 -DCONFIG_ROOT_CHECK_OFF=0 + +OBJS = xmalloc.o xrealloc.o xstrcat.o xstrdup.o xsystem.o xftw.o \ + modstat.o meta_expand.o config.o snap_shot.o arch64.o gzfiles.o sys_nim.o sys_oim.o + +libutil.a: $(OBJS) logger.o + ar cru $@ $^ + ranlib $@ + +libutil-STANDALONE.a: $(OBJS) logger-standalone.o + ar cru $@ $^ + ranlib $@ + +logger-standalone.o: logger.c + gcc $(FLAGS) $(GLIBC_INCLUDES) -o $@ -D_STANDALONE_ logger.c + +.c.o: + gcc $(FLAGS) $(GLIBC_INCLUDES) -c $< diff --git a/mdk-stage1/insmod-modutils/util/alias.h b/mdk-stage1/insmod-modutils/util/alias.h new file mode 100644 index 000000000..c925a04f3 --- /dev/null +++ b/mdk-stage1/insmod-modutils/util/alias.h @@ -0,0 +1,244 @@ +/* + * This file is split out from config.c for easier editing + */ + +/* + * tbpath and tbtype are used to build the complete set of paths for finding + * modules, but only when we search for individual directories, they are not + * used for [boot] and [toplevel] searches. + */ +static char *tbpath[] = +{ + "/lib/modules", + NULL /* marks the end of the list! */ +}; + +char *tbtype[] = +{ + "kernel", /* as of 2.3.14 this must be first */ + "fs", + "net", + "scsi", + "block", + "cdrom", + "ipv4", + "ipv6", + "sound", + "fc4", + "video", + "misc", + "pcmcia", + "atm", + "usb", + "ide", + "ieee1394", + "mtd", + NULL /* marks the end of the list! */ +}; + +/* + * This is the list of pre-defined aliases. + * Each entry can be overridden by an entry in /etc/modules.conf + */ +char *aliaslist[] = +{ + "binfmt-204 binfmt_aout", + "binfmt-263 binfmt_aout", + "binfmt-264 binfmt_aout", + "binfmt-267 binfmt_aout", + "binfmt-387 binfmt_aout", + "binfmt-332 iBCS", + "binfmt--310 binfmt_java", + + "block-major-1 rd", + "block-major-2 floppy", + "block-major-3 ide-probe-mod", + "block-major-7 loop", + "block-major-8 sd_mod", + "block-major-9 md", /* For modular RAID */ + "block-major-11 sr_mod", + "block-major-13 xd", + "block-major-15 cdu31a", + "block-major-16 gscd", + "block-major-17 optcd", + "block-major-18 sjcd", + "block-major-20 mcdx", + "block-major-22 ide-probe-mod", + "block-major-23 mcd", + "block-major-24 sonycd535", + "block-major-25 sbpcd", + "block-major-26 sbpcd", + "block-major-27 sbpcd", + "block-major-29 aztcd", + "block-major-32 cm206", + "block-major-33 ide-probe-mod", + "block-major-34 ide-probe-mod", + "block-major-37 ide-tape", + "block-major-44 ftl", /* from David Woodhouse <dwmw2@infradead.org> */ + "block-major-56 ide-probe-mod", + "block-major-57 ide-probe-mod", + "block-major-88 ide-probe-mod", + "block-major-89 ide-probe-mod", + "block-major-90 ide-probe-mod", + "block-major-91 ide-probe-mod", + "block-major-93 nftl", /* from David Woodhouse <dwmw2@infradead.org> */ + + "char-major-4 serial", + "char-major-5 serial", + "char-major-6 lp", + "char-major-9 st", + "char-major-10 off", /* was: mouse, was: misc */ + "char-major-10-0 busmouse", /* /dev/logibm Logitech bus mouse */ + "char-major-10-1 off", /* /dev/psaux PS/2-style mouse port */ + "char-major-10-2 msbusmouse", /* /dev/inportbm Microsoft Inport bus mouse */ + "char-major-10-3 atixlmouse", /* /dev/atibm ATI XL bus mouse */ + /* /dev/jbm J-mouse */ + /* /dev/amigamouse Amiga mouse (68k/Amiga) */ + /* /dev/atarimouse Atari mouse */ + /* /dev/sunmouse Sun mouse */ + /* /dev/beep Fancy beep device */ + /* /dev/modreq Kernel module load request */ + "char-major-10-130 wdt", /* /dev/watchdog Watchdog timer port */ + "char-major-10-131 wdt", /* /dev/temperature Machine internal temperature */ + /* /dev/hwtrap Hardware fault trap */ + /* /dev/exttrp External device trap */ + "char-major-10-135 off", /* rtc cannot be compiled as a module */ + "char-major-10-139 openprom", /* /dev/openprom Linux/Sparc interface */ + "char-major-10-144 nvram", /* from Tigran Aivazian <tigran@sco.COM> */ + "char-major-10-157 applicom", /* from David Woodhouse <dwmw2@infradead.org> */ + "char-major-10-175 agpgart", /* /dev/agpgart GART AGP mapping access */ + "char-major-10-184 microcode", /* Tigran Aivazian <tigran@veritas.com> */ + + "char-major-14 soundcore", + "char-major-19 cyclades", + "char-major-20 cyclades", + "char-major-21 sg", + "char-major-22 pcxx", /* ?? */ + "char-major-23 pcxx", /* ?? */ + "char-major-27 ftape", + "char-major-34 scc", + "char-major-35 tclmidi", + "char-major-36 netlink", + "char-major-37 ide-tape", + "char-major-48 riscom8", + "char-major-49 riscom8", + "char-major-57 esp", + "char-major-58 esp", + "char-major-63 kdebug", + "char-major-90 mtdchar", /* from David Woodhouse <dwmw2@infradead.org> */ + "char-major-99 ppdev", + "char-major-107 3dfx", /* from Tigran Aivazian <tigran@sco.COM> */ + "char-major-161 ircomm-tty", + + "dos msdos", + "dummy0 dummy", + "dummy1 dummy", + "eth0 off", + "iso9660 isofs", + "md-personality-1 linear", + "md-personality-2 raid0", + "md-personality-3 raid1", + "md-personality-4 raid5", + + "net-pf-1 unix", /* PF_UNIX 1 Unix domain sockets */ + "net-pf-2 ipv4", /* PF_INET 2 Internet IP Protocol */ + "net-pf-3 off", /* PF_AX25 3 Amateur Radio AX.25 */ + "net-pf-4 ipx", /* PF_IPX 4 Novell IPX */ + "net-pf-5 appletalk", /* PF_APPLETALK 5 Appletalk DDP */ + "net-pf-6 off", /* PF_NETROM 6 Amateur radio NetROM */ + /* PF_BRIDGE 7 Multiprotocol bridge */ + /* PF_AAL5 8 Reserved for Werner's ATM */ + /* PF_X25 9 Reserved for X.25 project */ + /* PF_INET6 10 IP version 6 */ + + /* next two from <dairiki@matthews.dairiki.org> Thanks! */ + "net-pf-17 af_packet", + "net-pf-19 off", /* acorn econet */ + + "netalias-2 ip_alias", + "plip0 plip", + "plip1 plip", + "cipcb0 cipcb", + "cipcb1 cipcb", + "cipcb2 cipcb", + "cipcb3 cipcb", + "ppp0 ppp", + "ppp1 ppp", + "scsi_hostadapter off", /* if not in config file */ + "slip0 slip", + "slip1 slip", + "tty-ldisc-1 slip", + "tty-ldisc-3 ppp", + "ppp-compress-21 bsd_comp", + "ppp-compress-24 ppp_deflate", + "ppp-compress-26 ppp_deflate", + +#ifndef __sparc__ + "parport_lowlevel parport_pc", +#else + "parport_lowlevel parport_ax", +#endif + + "tty-ldisc-11 irtty", + + "usbdevfs usbcore", + + NULL /* marks the end of the list! */ +}; + +/* + * This is the list of pre-defined options. + * Each entry can be overridden by an entry in /etc/modules.conf + */ +char *optlist[] = +{ + "dummy0 -o dummy0", + "dummy1 -o dummy1", + "sb io=0x220 irq=7 dma=1 dma16=5 mpu_io=0x330", + NULL /* marks the end of the list! */ +}; + +/* + * This is the list of pre-defined "above"s, + * used for pull-in of additional modules + * Each entry can be overridden by an entry in /etc/modules.conf + */ +char *above[] = +{ + NULL /* marks the end of the list! */ +}; + +/* + * This is the list of pre-defined "below"s, + * used for push-in of additional modules + * Each entry can be overridden by an entry in /etc/modules.conf + */ +char *below[] = +{ + NULL /* marks the end of the list! */ +}; + +/* + * This is the list of pre-defined "prune"s, + * used to exclude paths from scan of /lib/modules. + * /etc/modules.conf can add entries but not remove them. + */ +char *prune[] = +{ + ".rhkmvtag", + "modules.dep", + "modules.generic_string", + "modules.pcimap", + "modules.isapnpmap", + "modules.usbmap", + "modules.parportmap", + "System.map", + ".config", + "build", /* symlink to source tree */ + "vmlinux", + "vmlinuz", + "bzImage", + "zImage", + ".rhkmvtag", /* wish RedHat had told me before they did this */ + NULL /* marks the end of the list! */ +}; diff --git a/mdk-stage1/insmod-modutils/util/arch64.c b/mdk-stage1/insmod-modutils/util/arch64.c new file mode 100644 index 000000000..4d5ace223 --- /dev/null +++ b/mdk-stage1/insmod-modutils/util/arch64.c @@ -0,0 +1,35 @@ +/* Misc utility functions. + Copyright 1996, 1997 Linux International. + Written by Keith Owens <kaos@ocs.com.au> + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ident "$Id$" + +#include <stdlib.h> +#include <string.h> +#include <sys/utsname.h> +#include "util.h" + +/*======================================================================*/ + +/* Indicate if the current machine uses 64 bit architecture */ +int arch64(void) +{ + struct utsname u; + return(!uname(&u) && strstr(u.machine, "64")); +} diff --git a/mdk-stage1/insmod-modutils/util/config.c b/mdk-stage1/insmod-modutils/util/config.c new file mode 100644 index 000000000..a860920f6 --- /dev/null +++ b/mdk-stage1/insmod-modutils/util/config.c @@ -0,0 +1,1591 @@ +/* + * Handle the configuration, including /etc/modules.conf + * + * Copyright 1994, 1995, 1996, 1997: + * Jacques Gelinas <jack@solucorp.qc.ca> + * Björn Ekwall <bj0rn@blox.se> February 1999 + * Keith Owens <kaos@ocs.com.au> October 1999 + * + * "kernelversion" idea from the Debian release via: + * Wichert Akkerman <wakkerma@cs.leidenuniv.nl> + * + * Björn, inspired by Richard Henderson <rth@twiddle.net>, cleaned up + * the wildcard handling and started using ftw in March 1999 + * Cleanup of hardcoded arrays: Björn Ekwall <bj0rn@blox.se> March 1999 + * Many additional keywords: Björn Ekwall <bj0rn@blox.se> (C) March 1999 + * Standardize on /etc/modules.conf Keith Owens <kaos@ocs.com.au> October 1999 + * + * Alpha typecast:Michal Jaegermann <michal@ellpspace.math.ualberta.ca> + * + * This file is part of the Linux modutils. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Specification: /etc/modules.conf / format + * Modules may be located at different places in the filesystem. + * + * The file /etc/modules.conf contains different definitions to + * control the manipulation of modules. + * + * Standard Unix style comments and continuation line are supported. + * Comments begin with a # and continue until the end of the line. + * A line continues on the next one if the last non-white character + * is a \. + */ +/* #Specification: /etc/modules.conf / format / official name */ + +#include <stdio.h> +#include <stdarg.h> +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <ctype.h> +#include <sys/stat.h> +#include <signal.h> +#include <limits.h> +#include <sys/param.h> +#include <errno.h> + +#include "util.h" +#include "config.h" +#include "alias.h" + +int flag_autoclean; /* set/used by modprobe and insmod */ + +struct utsname uts_info; + +struct PATH_TYPE *modpath; +int nmodpath = 0; +static int maxpath = 0; + +struct EXEC_TYPE *execs; +int nexecs = 0; +static int maxexecs = 0; + +OPT_LIST *opt_list; +static int n_opt_list; + +OPT_LIST *abovelist; +static int n_abovelist; + +OPT_LIST *belowlist; +static int n_belowlist; + +OPT_LIST *prunelist; +static int n_prunelist; + +OPT_LIST *probe_list; +static int n_probe_list; + +OPT_LIST *probeall_list; +static int n_probeall_list; + +OPT_LIST *aliases; +static int n_aliases; + +char *persistdir = "/var/lib/modules/persist"; + +const char symprefix[] = SYMPREFIX; + +char *insmod_opt = NULL; +char *config_file = NULL; /* Which file was actually used */ +time_t config_mtime; +int root_check_off = CONFIG_ROOT_CHECK_OFF; /* Default is modules must be owned by root */ +static char *config_version; /* Hack for config_add */ +int quick = 0; /* Option -A */ + +/* The initialization order must match the gen_file_enum order in config.h */ +struct gen_files gen_file[] = { + {"generic_string", NULL, 0}, + {"pcimap", NULL, 0}, + {"isapnpmap", NULL, 0}, + {"usbmap", NULL, 0}, + {"parportmap", NULL, 0}, + {"dep", NULL, 0}, +}; + +const int gen_file_count = sizeof(gen_file)/sizeof(gen_file[0]); + +int flag_verbose; + +unsigned long safemode; + +void verbose(const char *ctl,...) +{ + if (flag_verbose) { + va_list list; + va_start(list, ctl); + vprintf(ctl, list); + va_end(list); + fflush(stdout); + } +} + + +/* + * Check to see if the existing modules.xxx files need updating, + * based on the timestamps of the modules and the config file. + */ +static int check_update (const char *file, const struct stat *sb) +{ + int len = strlen(file); + int i; + + if (!S_ISREG(sb->st_mode)) + return 0; + for (i = 0; i < gen_file_count; ++i) { + if (sb->st_mtime > gen_file[i].mtime) + break; + } + if (i == gen_file_count) + return 0; /* All generated files are up to date */ + + if (len > 2 && !strcmp(file + len - 2, ".o")) + return 1; + else if (len > 4 && !strcmp(file + len - 4, ".mod")) + return 1; +#ifdef CONFIG_USE_ZLIB + else if (len > 5 && !strcmp(file + len - 5, ".o.gz")) + return 1; +#endif + return 0; +} + +static int need_update (const char *force_ver, const char *base_dir) +{ + struct stat tmp; + char dep[PATH_MAX]; + int i; + uname (&uts_info); + if (!force_ver) + force_ver = uts_info.release; + + if (strlen (force_ver) > 50) + /* That's just silly. */ + return 1; + + for (i = 0; i < gen_file_count; ++i) { + if (stat(gen_file[i].name, &tmp)) + return 1; /* No dependency file yet, so we need to build it. */ + gen_file[i].mtime = tmp.st_mtime; + } + + if (stat ("/etc/modules.conf", &tmp) && + stat ("/etc/conf.modules", &tmp)) + return 1; + + for (i = 0; i < gen_file_count; ++i) { + if (tmp.st_mtime > gen_file[i].mtime) + return 1; /* Config file is newer. */ + } + + snprintf (dep, sizeof(dep), "%s/lib/modules/%s", base_dir, force_ver); + return xftw (dep, check_update); +} + + +/* + * Strip white char at the end of a string. + * Return the address of the last non white char + 1 (point on the '\0'). + */ +static char *strip_end(char *str) +{ + int len = strlen(str); + + for (str += len - 1; len > 0 && (isspace(*str)); --len, --str) + *str = '\0'; + return str + 1; +} + +/* + * Read a line of a configuration file and process continuation lines. + * Return buf, or NULL if EOF. + * Blank at the end of line are always stripped. + * Everything on a line following comchar is a comment. + * + * Continuation character is \ + * Comment character is # + */ +char *fgets_strip(char *buf, int sizebuf, FILE * fin, int *lineno) +{ + int nocomment = 1; /* No comments found ? */ + int contline = 0; + char *start = buf; + char *ret = NULL; + char comchar = '#'; + char contchar = '\\'; + + *buf = '\0'; + + while (fgets(buf, sizebuf, fin) != NULL) { + char *end = strip_end(buf); + char *pt = strchr(buf, comchar); + + if (pt != NULL) { + nocomment = 0; + *pt = '\0'; + end = strip_end(buf); + } + + if (lineno != NULL) + (*lineno)++; + ret = start; + if (contline) { + char *pt = buf; + + while (isspace(*pt)) + pt++; + if (pt > buf + 1) { + strcpy(buf + 1, pt); /* safe, backward copy */ + buf[0] = ' '; + end -= (int) (pt - buf) - 1; + } else if (pt == buf + 1) { + buf[0] = ' '; + } + } + if (end > buf && *(end - 1) == contchar) { + if (end == buf + 1 || *(end - 2) != contchar) { + /* Continuation */ + contline = 1; + end--; + *end = '\0'; + buf = end; + } else { + *(end - 1) = '\0'; + break; + } + } else { + break; + } + } + + return ret; +} + +static char *next_word(char *pt) +{ + char *match; + char *pt2; + + /* find end of word */ + for (pt2 = pt; *pt2 && !(isspace(*pt2)); ++pt2) { + if ((match = strchr("\"'`", *pt2)) != NULL) { + for (++pt2; *pt2 && *pt2 != *match; ++pt2) { + if (*pt2 == '\\' && *(pt2 + 1) == *match) + ++pt2; + } + } + } + + /* skip leading whitespace before next word */ + if (*pt2) { + *pt2++ = '\0'; /* terminate last word */ + while (*pt2 && isspace(*pt2)) + ++pt2; + } + return pt2; +} + +static GLOB_LIST *addlist(GLOB_LIST *orig, GLOB_LIST *add) +{ + if (!orig) + return add; + /* else */ + orig->pathv = (char **)xrealloc(orig->pathv, + (orig->pathc + add->pathc + 1) * + sizeof(char *)); + memcpy(orig->pathv + orig->pathc, add->pathv, + add->pathc * sizeof(char *)); + orig->pathc += add->pathc; + orig->pathv[orig->pathc] = NULL; + /* + free(add->pathv); + free(add); + */ + return orig; +} + +static void decode_list(int *n, OPT_LIST **list, char *arg, int adding, + char *version, int opts) +{ + GLOB_LIST *pg; + GLOB_LIST *prevlist = NULL; + int i, autoclean = 1; + int where = *n; + char *arg2 = next_word(arg); + + if (opts && !strcmp (arg, "-k")) { + if (!*arg2) + error("Missing module argument after -k\n"); + arg = arg2; + arg2 = next_word(arg); + autoclean = 0; + } + + for (i = 0; i < *n; ++i) { + if (strcmp((*list)[i].name, arg) == 0) { + if (adding) + prevlist = (*list)[i].opts; + else + free((*list)[i].opts); + (*list)[i].opts = NULL; + where = i; + break; + } + } + if (where == *n) { + (*list) = (OPT_LIST *)xrealloc((*list), + (*n + 2) * sizeof(OPT_LIST)); + (*list)[*n].name = xstrdup(arg); + (*list)[*n].autoclean = autoclean; + *n += 1; + memset(&(*list)[*n], 0, sizeof(OPT_LIST)); + } else if (!autoclean) + (*list)[where].autoclean = 0; + pg = (GLOB_LIST *)xmalloc(sizeof(GLOB_LIST)); + meta_expand(arg2, pg, NULL, version, ME_ALL); + (*list)[where].opts = addlist(prevlist, pg); +} + +static void decode_exec(char *arg, int type) +{ + char *arg2; + + execs[nexecs].when = type; + arg2 = next_word(arg); + execs[nexecs].module = xstrdup(arg); + execs[nexecs].cmd = xstrdup(arg2); + if (++nexecs >= maxexecs) { + maxexecs += 10; + execs = (struct EXEC_TYPE *)xrealloc(execs, + maxexecs * sizeof(struct EXEC_TYPE)); + } +} + +static int build_list(char **in, OPT_LIST **out, char *version, int opts) +{ + GLOB_LIST *pg; + int i; + + for (i = 0; in[i]; ++i) { + char *p = xstrdup(in[i]); + char *pt = next_word(p); + char *pn = p; + + *out = (OPT_LIST *)xrealloc(*out, (i + 2) * sizeof(OPT_LIST)); + (*out)[i].autoclean = 1; + if (opts && !strcmp (p, "-k")) { + pn = pt; + pt = next_word(pn); + (*out)[i].autoclean = 0; + } + pg = (GLOB_LIST *)xmalloc(sizeof(GLOB_LIST)); + meta_expand(pt, pg, NULL, version, ME_ALL); + (*out)[i].name = xstrdup(pn); + (*out)[i].opts = pg; + free(p); + } + memset(&(*out)[i], 0, sizeof(OPT_LIST)); + + return i; +} + +/* Environment variables can override defaults, testing only */ +static void gen_file_env(struct gen_files *gf) +{ + if (!safemode) { + char *e = xmalloc(strlen(gf->base)+5), *p1 = gf->base, *p2 = e; + while ((*p2++ = toupper(*p1++))) ; + strcpy(p2-1, "PATH"); /* safe, xmalloc */ + if ((p2 = getenv(e)) != NULL) { + free(gf->name); + gf->name = xstrdup(p2); + } + free(e); + } +} + +/* Read a config option for a generated filename */ +static int gen_file_conf(struct gen_files *gf, int assgn, const char *parm, const char *arg) +{ + + int l = strlen(gf->base); + if (assgn && + strncmp(parm, gf->base, l) == 0 && + strcmp(parm+l, "file") == 0 && + !gf->name) { + gf->name = xstrdup(arg); + return(0); + } + return(1); +} + +/* Check we have a name for a generated file */ +static int gen_file_check(struct gen_files *gf, GLOB_LIST *g, + char *base_dir, char *version) +{ + char tmp[PATH_MAX]; + int ret = 0; + if (!gf->name) { + /* + * Specification: config file / no xxxfile parameter + * The default value for generated filename xxx is: + * + * xxxfile=/lib/modules/`uname -r`/modules.xxx + * + * If the config file exists but lacks an xxxfile + * specification, the default value is used since + * the system can't work without one. + */ + snprintf(tmp, sizeof(tmp), "%s/lib/modules/%s/modules.%s", + base_dir, version, gf->base); + gf->name = xstrdup(tmp); + } else { /* xxxfile defined in modules.conf */ + /* + * If we have a xxxfile definition in the configuration file + * we must resolve any shell meta-chars in its value. + */ + if (meta_expand(gf->name, g, base_dir, version, ME_ALL)) + ret = -1; + else if (!g->pathv || g->pathv[0] == NULL) + ret = -1; + else { + free(gf->name); + gf->name = xstrdup(g->pathv[0]); + } + } + return(ret); +} + +/* + * Read the configuration file. + * If parameter "all" == 0 then ignore everything except path info + * Return -1 if any error. + * Error messages generated. + */ +static int do_read(int all, char *force_ver, char *base_dir, char *conf_file, int depth) +{ + #define MAX_LEVEL 20 + FILE *fin; + GLOB_LIST g; + int i; + int assgn; + int drop_default_paths = 1; + int lineno = 0; + int ret = 0; + int state[MAX_LEVEL + 1]; /* nested "if" */ + int level = 0; + char buf[3000]; + char tmpline[100]; + char **pathp; + char *envpath; + char *version; + char *type; + char **glb; + char old_name[] = "/etc/conf.modules"; + int conf_file_specified = 0; + + /* + * The configuration file is optional. + * No error is printed if it is missing. + * If it is missing the following content is assumed. + * + * path[boot]=/lib/modules/boot + * + * path[toplevel]=/lib/modules/`uname -r` + * + * path[toplevel]=/lib/modules/`kernelversion` + * (where kernelversion gives the major kernel version: "2.0", "2.2"...) + * + * path[toplevel]=/lib/modules/default + * + * path[kernel]=/lib/modules/kernel + * path[fs]=/lib/modules/fs + * path[net]=/lib/modules/net + * path[scsi]=/lib/modules/scsi + * path[block]=/lib/modules/block + * path[cdrom]=/lib/modules/cdrom + * path[ipv4]=/lib/modules/ipv4 + * path[ipv6]=/lib/modules/ipv6 + * path[sound]=/lib/modules/sound + * path[fc4]=/lib/modules/fc4 + * path[video]=/lib/modules/video + * path[misc]=/lib/modules/misc + * path[pcmcia]=/lib/modules/pcmcia + * path[atm]=/lib/modules/atm + * path[usb]=/lib/modules/usb + * path[ide]=/lib/modules/ide + * path[ieee1394]=/lib/modules/ieee1394 + * path[mtd]=/lib/modules/mtd + * + * The idea is that modprobe will look first if the + * modules are compiled for the current release of the kernel. + * If not found, it will look for modules that fit for the + * general kernelversion (2.0, 2.2 and so on). + * If still not found, it will look into the default release. + * And if still not found, it will look in the other directories. + * + * The strategy should be like this: + * When you install a new linux kernel, the modules should go + * into a directory related to the release (version) of the kernel. + * Then you can do a symlink "default" to this directory. + * + * Each time you compile a new kernel, the make modules_install + * will create a new directory, but it won't change thee default. + * + * When you get a module unrelated to the kernel distribution + * you can place it in one of the last three directory types. + * + * This is the default strategy. Of course you can overide + * this in /etc/modules.conf. + * + * 2.3.15 added a new file tree walk algorithm which made it possible to + * point at a top level directory and get the same behaviour as earlier + * versions of modutils. 2.3.16 takes this one stage further, it + * removes all the individual directory names from most of the scans, + * only pointing at the top level directory. The only exception is the + * last ditch scan, scanning all of /lib/modules would be a bad idea(TM) + * so the last ditch scan still runs individual directory names under + * /lib/modules. + * + * Additional syntax: + * + * [add] above module module1 ... + * Specify additional modules to pull in on top of a module + * + * [add] below module module1 ... + * Specify additional modules needed to be able to load a module + * + * [add] prune filename ... + * + * [add] probe name module1 ... + * When "name" is requested, modprobe tries to install each + * module in the list until it succeeds. + * + * [add] probeall name module1 ... + * When "name" is requested, modprobe tries to install all + * modules in the list. + * If any module is installed, the command has succeeded. + * + * [add] options module option_list + * + * For all of the above, the optional "add" prefix is used to + * add to a list instead of replacing the contents. + * + * include FILE_TO_INCLUDE + * This does what you expect. No limitation on include levels. + * + * persistdir=persist_directory + * Name the directory to save persistent data from modules. + * + * In the following WORD is a sequence if non-white characters. + * If ' " or ` is found in the string, all characters up to the + * matching ' " or ` will also be included, even whitespace. + * Every WORD will then be expanded w.r.t. meta-characters. + * If the expanded result gives more than one word, then only + * the first word of the result will be used. + * + * + * define CODE WORD + * Do a putenv("CODE=WORD") + * + * EXPRESSION below can be: + * WORD compare_op WORD + * where compare_op is one of == != < <= >= > + * The string values of the WORDs are compared + * or + * -n WORD compare_op WORD + * where compare_op is one of == != < <= >= > + * The numeric values of the WORDs are compared + * or + * WORD + * if the expansion of WORD fails, or if the + * expansion is "0" (zero), "false" or "" (empty) + * then the expansion has the value FALSE. + * Otherwise the expansion has the value TRUE + * or + * -f FILENAME + * Test if the file FILENAME exists + * or + * -k + * Test if "autoclean" (i.e. called from the kernel) + * or + * ! EXPRESSION + * A negated expression is also an expression + * + * if EXPRESSION + * any config line + * ... + * elseif EXPRESSION + * any config line + * ... + * else + * any config line + * ... + * endif + * + * The else and elseif keywords are optional. + * "if"-statements nest up to 20 levels. + */ + + state[0] = 1; + + if (force_ver) + version = force_ver; + else + version = uts_info.release; + + config_version = xstrdup(version); + + /* Only read the default entries on the first file */ + if (depth == 0) { + maxpath = 100; + modpath = (struct PATH_TYPE *)xmalloc(maxpath * sizeof(struct PATH_TYPE)); + nmodpath = 0; + + maxexecs = 10; + execs = (struct EXEC_TYPE *)xmalloc(maxexecs * sizeof(struct EXEC_TYPE)); + nexecs = 0; + + /* + * Build predef options + */ + if (all && optlist[0]) + n_opt_list = build_list(optlist, &opt_list, version, 1); + + /* + * Build predef above + */ + if (all && above[0]) + n_abovelist = build_list(above, &abovelist, version, 0); + + /* + * Build predef below + */ + if (all && below[0]) + n_belowlist = build_list(below, &belowlist, version, 0); + + /* + * Build predef prune list + */ + if (prune[0]) + n_prunelist = build_list(prune, &prunelist, version, 0); + + /* + * Build predef aliases + */ + if (all && aliaslist[0]) + n_aliases = build_list(aliaslist, &aliases, version, 0); + + /* Order and priority is now: (MODPATH + modules.conf) || (predefs + modules.conf) */ + if ((envpath = getenv("MODPATH")) != NULL && !safemode) { + size_t len; + char *p; + char *path; + + /* Make a copy so's we can mung it with strtok. */ + len = strlen(envpath) + 1; + p = alloca(len); + memcpy(p, envpath, len); + path = alloca(PATH_MAX); + + for (p = strtok(p, ":"); p != NULL; p = strtok(NULL, ":")) { + len = snprintf(path, PATH_MAX, p, version); + modpath[nmodpath].path = xstrdup(path); + if ((type = strrchr(path, '/')) != NULL) + type += 1; + else + type = "misc"; + modpath[nmodpath].type = xstrdup(type); + if (++nmodpath >= maxpath) { + maxpath += 100; + modpath = (struct PATH_TYPE *)xrealloc(modpath, + maxpath * sizeof(struct PATH_TYPE)); + } + + } + } else { + /* + * Build the default "path[type]" configuration + */ + int n; + char *k; + + /* The first entry in the path list */ + modpath[nmodpath].type = xstrdup("boot"); + snprintf(tmpline, sizeof(tmpline), "%s/lib/modules/boot", base_dir); + modpath[nmodpath].path = xstrdup(tmpline); + ++nmodpath; + + /* The second entry in the path list, `uname -r` */ + modpath[nmodpath].type = xstrdup("toplevel"); + snprintf(tmpline, sizeof(tmpline), "%s/lib/modules/%s", base_dir, version); + modpath[nmodpath].path = xstrdup(tmpline); + ++nmodpath; + + /* The third entry in the path list, `kernelversion` */ + modpath[nmodpath].type = xstrdup("toplevel"); + for (n = 0, k = version; *k; ++k) { + if (*k == '.' && ++n == 2) + break; + } + snprintf(tmpline, sizeof(tmpline), "%s/lib/modules/%.*s", base_dir, + (/* typecast for Alpha */ int)(k - version), version); + modpath[nmodpath].path = xstrdup(tmpline); + ++nmodpath; + + /* The rest of the entries in the path list */ + for (pathp = tbpath; *pathp; ++pathp) { + char **type; + + for (type = tbtype; *type; ++type) { + char path[PATH_MAX]; + + snprintf(path, sizeof(path), "%s%s/%s", base_dir, *pathp, *type); + if (meta_expand(path, &g, NULL, version, ME_ALL)) + return -1; + + for (glb = g.pathv; glb && *glb; ++glb) { + modpath[nmodpath].type = xstrdup(*type); + modpath[nmodpath].path = *glb; + if (++nmodpath >= maxpath) { + maxpath += 100; + modpath = (struct PATH_TYPE *)xrealloc(modpath, + maxpath * sizeof(struct PATH_TYPE)); + } + } + } + } + } + + /* Environment overrides for testing only, undocumented */ + for (i = 0; i < gen_file_count; ++i) + gen_file_env(gen_file+i); + + } /* End of depth == 0 */ + + if (conf_file || + ((conf_file = getenv("MODULECONFIG")) != NULL && *conf_file && !safemode)) { + if (!(fin = fopen(conf_file, "r"))) { + error("Can't open %s", conf_file); + return -1; + } + conf_file_specified = 1; + } else { + if (!(fin = fopen((conf_file = ETC_MODULES_CONF), "r"))) { + /* Fall back to non-standard name */ + if ((fin = fopen((conf_file = old_name), "r"))) { + fprintf(stderr, + "Warning: modutils is reading from %s because\n" + " %s does not exist. The use of %s is\n" + " deprecated, please rename %s to %s\n" + " as soon as possible. Command\n" + " mv %s %s\n", + old_name, ETC_MODULES_CONF, + old_name, old_name, ETC_MODULES_CONF, + old_name, ETC_MODULES_CONF); + } + /* So what... use the default configuration */ + } + } + + if (fin) { + struct stat statbuf1, statbuf2; + if (fstat(fileno(fin), &statbuf1) == 0) + config_mtime = statbuf1.st_mtime; + config_file = xstrdup(conf_file); /* Save name actually used */ + if (!conf_file_specified && + stat(ETC_MODULES_CONF, &statbuf1) == 0 && + stat(old_name, &statbuf2) == 0) { + /* Both /etc files exist */ + if (statbuf1.st_dev == statbuf2.st_dev && + statbuf1.st_ino == statbuf2.st_ino) { + if (lstat(ETC_MODULES_CONF, &statbuf1) == 0 && + S_ISLNK(statbuf1.st_mode)) + fprintf(stderr, + "Warning: You do not need a link from %s to\n" + " %s. The use of %s is deprecated,\n" + " please remove %s and rename %s\n" + " to %s as soon as possible. Commands.\n" + " rm %s\n" + " mv %s %s\n", + ETC_MODULES_CONF, old_name, + old_name, ETC_MODULES_CONF, old_name, ETC_MODULES_CONF, + ETC_MODULES_CONF, + old_name, ETC_MODULES_CONF); + else { +#ifndef NO_WARN_ON_OLD_LINK + fprintf(stderr, + "Warning: You do not need a link from %s to\n" + " %s. The use of %s is deprecated,\n" + " please remove %s as soon as possible. Command\n" + " rm %s\n", + old_name, ETC_MODULES_CONF, + old_name, old_name, + old_name); +#endif + } + } + else + fprintf(stderr, + "Warning: modutils is reading from %s and\n" + " ignoring %s. The use of %s is deprecated,\n" + " please remove %s as soon as possible. Command\n" + " rm %s\n", + ETC_MODULES_CONF, old_name, + old_name, old_name, + old_name); + } + } + + /* + * Finally, decode the file + */ + while (fin && fgets_strip(buf, sizeof(buf) - 1, fin, &lineno) != NULL) { + char *arg2; + char *parm = buf; + char *arg; + int one_err = 0; + int adding; + + while (isspace(*parm)) + parm++; + + if (strncmp(parm, "add", 3) == 0) { + adding = 1; + parm += 3; + while (isspace(*parm)) + parm++; + } else + adding = 0; + + arg = parm; + + if (*parm == '\0') + continue; + + one_err = 1; + + while (*arg > ' ' && *arg != '=') + arg++; + + if (*arg == '=') + assgn = 1; + else + assgn = 0; + *arg++ = '\0'; + while (isspace(*arg)) + arg++; + + /* + * endif + */ + if (!assgn && strcmp(parm, "endif") == 0) { + if (level > 0) + --level; + else { + error("unmatched endif in line %d", lineno); + return -1; + } + continue; + } + + /* + * else + */ + if (!assgn && strcmp(parm, "else") == 0) { + if (level <= 0) { + error("else without if in line %d", lineno); + return -1; + } + state[level] = !state[level]; + continue; + } + + /* + * elseif + */ + if (!assgn && strcmp(parm, "elseif") == 0) { + if (level <= 0) { + error("elseif without if in line %d", lineno); + return -1; + } + if (state[level] != 0) { + /* + * We have already found a TRUE + * if statement in this "chain". + * That's what "2" means. + */ + state[level] = 2; + continue; + } + /* else: No TRUE if has been found, cheat */ + /* + * The "if" handling increments level, + * but this is the _same_ level as before. + * So, compensate for it. + */ + --level; + parm = "if"; + /* Fallthru to "if" */ + } + + /* + * if + */ + if (strcmp(parm, "if") == 0) { + char *cmp; + int not = 0; + int numeric = 0; + + if (level >= MAX_LEVEL) { + error("Too many nested if's in line %d\n", lineno); + return -1; + } + state[++level] = 0; /* default false */ + + if (*arg == '!') { + not = 1; + arg = next_word(arg); + } + + if (strncmp(arg, "-k", 2) == 0) { + state[level] = flag_autoclean; + continue; + } + + if (strncmp(arg, "-f", 2) == 0) { + char *file = next_word(arg); + meta_expand(file, &g, NULL, version, ME_ALL); + if (access(g.pathc ? g.pathv[0] : file, R_OK) == 0) + state[level] = !not; + else + state[level] = not; + continue; + } + + if (strncmp(arg, "-n", 2) == 0) { + numeric = 1; + arg = next_word(arg); + } + + + cmp = next_word(arg); + if (*cmp) { + GLOB_LIST g2; + long n1 = 0; + long n2 = 0; + char *w1 = ""; + char *w2 = ""; + + arg2 = next_word(cmp); + + meta_expand(arg, &g, NULL, version, ME_ALL); + if (g.pathc && g.pathv[0]) + w1 = g.pathv[0]; + + meta_expand(arg2, &g2, NULL, version, ME_ALL); + if (g2.pathc && g2.pathv[0]) + w2 = g2.pathv[0]; + + if (numeric) { + n1 = strtol(w1, NULL, 0); + n2 = strtol(w2, NULL, 0); + } + + if (strcmp(cmp, "==") == 0 || + strcmp(cmp, "=") == 0) { + if (numeric) + state[level] = (n1 == n2); + else + state[level] = strcmp(w1, w2) == 0; + } else if (strcmp(cmp, "!=") == 0) { + if (numeric) + state[level] = (n1 != n2); + else + state[level] = strcmp(w1, w2) != 0; + } else if (strcmp(cmp, ">=") == 0) { + if (numeric) + state[level] = (n1 >= n2); + else + state[level] = strcmp(w1, w2) >= 0; + } else if (strcmp(cmp, "<=") == 0) { + if (numeric) + state[level] = (n1 <= n2); + else + state[level] = strcmp(w1, w2) <= 0; + } else if (strcmp(cmp, ">") == 0) { + if (numeric) + state[level] = (n1 > n2); + else + state[level] = strcmp(w1, w2) > 0; + } else if (strcmp(cmp, "<") == 0) { + if (numeric) + state[level] = (n1 < n2); + else + state[level] = strcmp(w1, w2) < 0; + } + } else { /* Check defined value, if any */ + /* undef or defined as + * "" or "0" or "false" => false + * defined => true + */ + if (!meta_expand(arg, &g, NULL, version, ME_ALL) && + g.pathc > 0 && + strcmp(g.pathv[0], "0") != 0 && + strcmp(g.pathv[0], "false") != 0 && + strlen(g.pathv[0]) != 0) + state[level] = 1; /* true */ + } + if (not) + state[level] = !state[level]; + + continue; + } + + /* + * Should we bother? + */ + if (state[level] != 1) + continue; + + /* + * define + */ + if (!assgn && strcmp(parm, "define") == 0) { + char env[PATH_MAX]; + + arg2 = next_word(arg); + meta_expand(arg2, &g, NULL, version, ME_ALL); + snprintf(env, sizeof(env), "%s=%s", arg, (g.pathc ? g.pathv[0] : "")); + putenv(env); + one_err = 0; + } + + /* + * include + */ + if (!assgn && strcmp(parm, "include") == 0) { + meta_expand(arg, &g, NULL, version, ME_ALL); + + if (!do_read(all, version, base_dir, g.pathc ? g.pathv[0] : arg, depth+1)) + one_err = 0; + else + error("include %s failed\n", arg); + } + + /* + * above + */ + else if (all && !assgn && strcmp(parm, "above") == 0) { + decode_list(&n_abovelist, &abovelist, arg, adding, version, 0); + one_err = 0; + } + + /* + * below + */ + else if (all && !assgn && strcmp(parm, "below") == 0) { + decode_list(&n_belowlist, &belowlist, arg, adding, version, 0); + one_err = 0; + } + + /* + * prune + */ + else if (all && !assgn && strcmp(parm, "prune") == 0) { + decode_list(&n_prunelist, &prunelist, arg, adding, version, 0); + one_err = 0; + } + + /* + * probe + */ + else if (all && !assgn && strcmp(parm, "probe") == 0) { + decode_list(&n_probe_list, &probe_list, arg, adding, version, 0); + one_err = 0; + } + + /* + * probeall + */ + else if (all && !assgn && strcmp(parm, "probeall") == 0) { + decode_list(&n_probeall_list, &probeall_list, arg, adding, version, 0); + one_err = 0; + } + + /* + * options + */ + else if (all && !assgn && strcmp(parm, "options") == 0) { + decode_list(&n_opt_list, &opt_list, arg, adding, version, 1); + one_err = 0; + } + + /* + * alias + */ + else if (all && !assgn && strcmp(parm, "alias") == 0) { + /* + * Replace any previous (default) definitions + * for the same module + */ + decode_list(&n_aliases, &aliases, arg, 0, version, 0); + one_err = 0; + } + + /* + * Specification: /etc/modules.conf + * The format of the commands in /etc/modules.conf are: + * + * pre-install module command + * install module command + * post-install module command + * pre-remove module command + * remove module command + * post-remove module command + * + * The different words are separated by tabs or spaces. + */ + /* + * pre-install + */ + else if (all && !assgn && (strcmp(parm, "pre-install") == 0)) { + decode_exec(arg, EXEC_PRE_INSTALL); + one_err = 0; + } + + /* + * install + */ + else if (all && !assgn && (strcmp(parm, "install") == 0)) { + decode_exec(arg, EXEC_INSTALL); + one_err = 0; + } + + /* + * post-install + */ + else if (all && !assgn && (strcmp(parm, "post-install") == 0)) { + decode_exec(arg, EXEC_POST_INSTALL); + one_err = 0; + } + + /* + * pre-remove + */ + else if (all && !assgn && (strcmp(parm, "pre-remove") == 0)) { + decode_exec(arg, EXEC_PRE_REMOVE); + one_err = 0; + } + + /* + * remove + */ + else if (all && !assgn && (strcmp(parm, "remove") == 0)) { + decode_exec(arg, EXEC_REMOVE); + one_err = 0; + } + + /* + * post-remove + */ + else if (all && !assgn && (strcmp(parm, "post-remove") == 0)) { + decode_exec(arg, EXEC_POST_REMOVE); + one_err = 0; + } + + /* + * insmod_opt= + */ + else if (assgn && (strcmp(parm, "insmod_opt") == 0)) { + insmod_opt = xstrdup(arg); + one_err = 0; + } + + /* + * keep + */ + else if (!assgn && (strcmp(parm, "keep") == 0)) { + drop_default_paths = 0; + one_err = 0; + } + + /* + * path...= + */ + else if (assgn && strncmp(parm, "path", 4) == 0) { + /* + * Specification: config file / path parameter + * The path parameter specifies a directory to + * search for modules. + * This parameter may be repeated multiple times. + * + * Note that the actual path may be defined using + * wildcards and other shell meta-chars, such as "*?`". + * For example: + * path[misc]=/lib/modules/1.1.5?/misc + * + * Optionally the path keyword carries a tag. + * This tells us a little more about the purpose of + * this directory and allows some automated operations. + * A path is marked with a tag by adding the tag, + * enclosed in square brackets, to the path keyword: + * # + * path[boot]=/lib/modules/boot + * # + * This case identifies the path a of directory + * holding modules loadable a boot time. + */ + + if (drop_default_paths) { + int n; + + /* + * Specification: config file / path / default + * + * Whenever there is a path[] specification + * in the config file, all the default + * path are reset. + * + * If one instead wants to _add_ to the default + * set of paths, one has to have the option + * keep + * before the first path[]-specification line + * in the configuration file. + */ + drop_default_paths = 0; + for (n = 0; n < nmodpath; n++) { + free(modpath[n].path); + free(modpath[n].type); + } + nmodpath = 0; + } + + /* + * Get (the optional) tag + * If the tag is missing, the word "misc" + * is assumed. + */ + type = "misc"; + + if (parm[4] == '[') { + char *pt_type = parm + 5; + + while (*pt_type != '\0' && *pt_type != ']') + pt_type++; + + if (*pt_type == ']' && pt_type[1] == '\0') { + *pt_type = '\0'; + type = parm + 5; + } /* else CHECKME */ + } + + /* + * Handle the actual path description + */ + if (meta_expand(arg, &g, base_dir, version, ME_ALL)) + return -1; + for (glb = g.pathv; glb && *glb; ++glb) { + modpath[nmodpath].type = xstrdup(type); + modpath[nmodpath].path = *glb; + if (++nmodpath >= maxpath) { + maxpath += 100; + modpath = (struct PATH_TYPE *)xrealloc(modpath, + maxpath * sizeof(struct PATH_TYPE)); + } + } + one_err = 0; + } + + /* + * persistdir + */ + else if (assgn && strcmp(parm, "persistdir") == 0) { + meta_expand(arg, &g, NULL, version, ME_ALL); + persistdir = xstrdup(g.pathc ? g.pathv[0] : arg); + one_err = 0; + } + + /* Names for generated files in config file */ + for (i = 0; one_err && i < gen_file_count; ++i) + one_err = gen_file_conf(gen_file+i, assgn, parm, arg); + + /* + * any errors so far? + */ + if (all == 0) + one_err = 0; + else if (one_err) { + error("Invalid line %d in %s\n\t%s", + lineno, conf_file, buf); + ret = -1; + } + } + if (fin) + fclose(fin); + + if (level) { + error("missing endif at %s EOF", conf_file); + ret = -1; + } + + if (ret) + return ret; + /* else */ + + /* Check we have names for generated files */ + for (i = 0; !ret && i < gen_file_count; ++i) + ret = gen_file_check(gen_file+i, &g, base_dir, version); + + return ret; +} + +int config_read(int all, char *force_ver, char *base_dir, char *conf_file) +{ + int r; + if (modpath != NULL) + return 0; /* already initialized */ + + if (uname(&uts_info) < 0) { + error("Failed to find kernel name information"); + return -1; + } + + r = do_read(all, force_ver, base_dir, conf_file, 0); + + if (quick && !r && !need_update (force_ver, base_dir)) + exit (0); + + return r; +} + +/****************************************************************************/ +/* + * FIXME: Far too much global state. KAO. + */ +static int found; +static int favail; +static int one_only; +static int meta_expand_type; +char **list; +static const char *filter_by_file; +static char *filter_by_dir; + +/* + * Add a file name if it exist + */ +static int config_add(const char *file, const struct stat *sb) +{ + int i; + int npaths = 0; + char **paths = NULL; + + if (meta_expand_type) { + GLOB_LIST g; + char **p; + char full[PATH_MAX]; + + snprintf(full, sizeof(full), "%s/%s", file, filter_by_file); + + if (filter_by_dir && !strstr(full, filter_by_dir)) + return 0; + + if (meta_expand(full, &g, NULL, config_version, meta_expand_type)) + return 1; + for (p = g.pathv; p && *p; ++p) { + paths = (char **)xrealloc(paths, + (npaths + 1) * sizeof(char *)); + paths[npaths++] = *p; + } + } else { /* normal path match or match with "*" */ + if (!S_ISREG(sb->st_mode)) + return 0; + + if (strcmp(filter_by_file, "*")) { + char *p; + + if ((p = strrchr(file, '/')) == NULL) + p = (char *)file; + else + p += 1; + + if (strcmp(p, filter_by_file)) + return 0; + } + if (filter_by_dir && !strstr(file, filter_by_dir)) + return 0; + paths = (char **)xmalloc(sizeof(char **)); + *paths = xstrdup(file); + npaths = 1; + } + + for (i = 0; i < npaths; ++i) { + struct stat sbuf; + + if (S_ISDIR(sb->st_mode)) { + if (stat(paths[i], &sbuf) == 0) + sb = &sbuf; + } + if (S_ISREG(sb->st_mode) && sb->st_mode & S_IRUSR) { + int j; + char **this; + + if (!root_check_off) { + if (sb->st_uid != 0) { + error("%s is not owned by root", paths[i]); + continue; + } + } + + /* avoid duplicates */ + for (j = 0, this = list; j < found; ++j, ++this) { + if (strcmp(*this, paths[i]) == 0) { + free(paths[i]); + goto next; + } + } + + list[found] = paths[i]; + if (++found >= favail) + list = (char **)xrealloc(list, + (favail += 100) * sizeof(char *)); + + if (one_only) { + for (j = i + 1; j < npaths; ++j) + free(paths[j]); + free(paths); + return 1; /* finish xftw */ + } + } + next: + } + + if (npaths > 0) + free(paths); + + return 0; +} + +/* + * Find modules matching the name "match" in directory of type "type" + * (type == NULL matches all) + * + * Return a pointer to the list of modules found (or NULL if error). + * Update the counter (sent as parameter). + */ +GLOB_LIST *config_lstmod(const char *match, const char *type, int first_only) +{ + /* + * Note: + * There are _no_ wildcards remaining in the path descriptions! + */ + struct stat sb; + int i; + int ret = 0; + char *path = NULL; + char this[PATH_MAX]; + + if (!match) + match = "*"; + one_only = first_only; + found = 0; + filter_by_file = match; + filter_by_dir = NULL; + if (type) { + char tmpdir[PATH_MAX]; + snprintf(tmpdir, sizeof(tmpdir), "/%s/", type); + filter_by_dir = xstrdup(tmpdir); + } + /* In safe mode, the module name is always handled as is, without meta + * expansion. It might have come from an end user via kmod and must + * not be trusted. Even in unsafe mode, only apply globbing to the + * module name, not command expansion. We trust config file input so + * applying command expansion is safe, we do not trust command line input. + * This assumes that the only time the user can specify -C config file + * is when they run under their own authority. In particular all + * mechanisms that call modprobe as root on behalf of the user must + * run in safe mode, without letting the user supply a config filename. + */ + meta_expand_type = 0; + if (strpbrk(match, SHELL_META) && strcmp(match, "*") && !safemode) + meta_expand_type = ME_GLOB|ME_BUILTIN_COMMAND; + + list = (char **)xmalloc((favail = 100) * sizeof(char *)); + + for (i = 0; i < nmodpath; i++) { + path = modpath[i].path; + /* Special case: insmod: handle single, non-wildcard match */ + if (first_only && strpbrk(match, SHELL_META) == NULL) { + /* Fix for "2.1.121 syntax */ + snprintf(this, sizeof(this), "%s/%s/%s", path, + modpath[i].type, match); + if (stat(this, &sb) == 0 && + config_add(this, &sb)) + break; + /* End fix for "2.1.121 syntax */ + + snprintf(this, sizeof(this), "%s/%s", path, match); + if (stat(this, &sb) == 0 && + config_add(this, &sb)) + break; + } + + /* Start looking */ + if ((ret = xftw(path, config_add))) { + break; + } + } + if (ret >= 0) { + GLOB_LIST *g = (GLOB_LIST *)xmalloc(sizeof(GLOB_LIST)); + g->pathc = found; + g->pathv = list; + free(filter_by_dir); + return g; + } + free(list); + free(filter_by_dir); + return NULL; +} + +/* Given a bare module name, poke through the module path to find the file. */ +char *search_module_path(const char *base) +{ + GLOB_LIST *g; + + if (config_read(0, NULL, "", NULL) < 0) + return NULL; + /* else */ + g = config_lstmod(base, NULL, 1); + if (g == NULL || g->pathc == 0) { + char base_o[PATH_MAX]; + + snprintf(base_o, sizeof(base_o), "%s.o", base); + g = config_lstmod(base_o, NULL, 1); +#ifdef CONFIG_USE_ZLIB + if (g == NULL || g->pathc == 0) { + snprintf(base_o, sizeof(base_o), "%s.o.gz", base); + g = config_lstmod(base_o, NULL, 1); + } +#endif + } + if (g == NULL || g->pathc == 0) + return NULL; + /* else */ + return g->pathv[0]; +} diff --git a/mdk-stage1/insmod-modutils/util/gzfiles.c b/mdk-stage1/insmod-modutils/util/gzfiles.c new file mode 100644 index 000000000..8d02253bb --- /dev/null +++ b/mdk-stage1/insmod-modutils/util/gzfiles.c @@ -0,0 +1,74 @@ +/* + * This simple library intends to make it transparent to read gzipped and/or + * standard files. This is simple enough to fit modutils' needs, but may be + * easily adapted to anyone's needs. It's completely free, do what you want + * with it . - Willy Tarreau <willy@meta-x.org> - 2000/05/05 - + */ + +#ifdef CONFIG_USE_ZLIB + +#include <stdio.h> +#include <zlib.h> +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> + +/* redefinition of gz_stream which isn't exported by zlib */ +typedef struct gz_stream { + z_stream stream; + int z_err; /* error code for last stream operation */ + int z_eof; /* set if end of input file */ + FILE *file; /* .gz file */ + Byte *inbuf; /* input buffer */ + Byte *outbuf; /* output buffer */ + uLong crc; /* crc32 of uncompressed data */ + char *msg; /* error message */ + char *path; /* path name for debugging only */ + int transparent; /* 1 if input file is not a .gz file */ + char mode; /* 'w' or 'r' */ + long startpos; /* start of compressed data in file (header skipped) */ +} gz_stream; + +/* maximum number of simultaneous open files, also greater file descriptor number */ +#define MAXFD 64 + +/* this static list is assumed to be filled with NULLs at runtime */ +static gzFile gzf_fds[MAXFD]; + +/* returns the filedesc of the opened file. */ +int gzf_open(const char *name, int mode) { + int fd; + gzFile g; + + if ((g=gzopen(name, "rb")) != NULL) { + fd=fileno(((gz_stream*)g)->file); + gzf_fds[fd]=g; + } + else if ((fd=open(name, mode)) != -1) { + gzf_fds[fd]=NULL; /* NULL means not GZ mode */ + } + return fd; +} + +off_t gzf_lseek(int fd, off_t offset, int whence) { + if (fd<0 || fd>=MAXFD || gzf_fds[fd]==NULL) + return lseek(fd, offset, whence); + else + return gzseek(gzf_fds[fd], offset, whence); +} + +int gzf_read(int fd, void *buf, size_t count) { + if (fd<0 || fd>=MAXFD || gzf_fds[fd]==NULL) + return read(fd, buf, count); + else + return gzread(gzf_fds[fd], buf, count); +} + +void gzf_close(int fd) { + if (fd<0 || fd>=MAXFD || gzf_fds[fd]==NULL) + close(fd); + else + gzclose(gzf_fds[fd]); +} +#endif + diff --git a/mdk-stage1/insmod-modutils/util/logger.c b/mdk-stage1/insmod-modutils/util/logger.c new file mode 100644 index 000000000..3b790df5a --- /dev/null +++ b/mdk-stage1/insmod-modutils/util/logger.c @@ -0,0 +1,163 @@ +/* Error logging facilities. + Copyright 1996, 1997 Linux International. + + Contributed by Richard Henderson <rth@tamu.edu> + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#ident "$Id$" + +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <errno.h> +#include <syslog.h> + +#include "util.h" + +/*======================================================================*/ + +int errors; +const char *error_file; +int log; + +#define STOREMSG +#ifdef STOREMSG +struct cbuf { + struct cbuf *next; + int type; + char *msg; +} *head, *tail; + +static void savemsg(int type, char *msg) +{ + struct cbuf *me = (struct cbuf *)xmalloc(sizeof(struct cbuf)); + char *s = xstrdup(msg); + + me->next = NULL; + me->type = type; + me->msg = s; + + if (tail) + tail->next = me; + else + head = me; + tail = me; +} + +#endif /* STOREMSG */ + +static void dumpmsg(void) +{ + for (;head; head = head->next) + syslog(head->type, "%s", head->msg); +} + +void setsyslog(const char *program) +{ + openlog(program, LOG_CONS, LOG_DAEMON); +#ifdef STOREMSG + atexit(dumpmsg); +#endif + log = 1; +} + + + +#ifdef _STANDALONE_ +static int silent; + +const char *program_name; + +void error(const char *fmt,...) +{ + va_list args; + + if (silent) + ; + else if (log) { + char buf[1024]; + int n; + + if (error_file) + n = snprintf(buf, sizeof(buf), "%s: ", error_file); + else + n = 0; + va_start(args, fmt); + vsnprintf(buf + n, sizeof(buf) - n, fmt, args); + va_end(args); +#ifdef STOREMSG + savemsg(LOG_ERR, buf); +#else + syslog(LOG_ERR, "%s", buf); +#endif + } else { + if (error_file) + fprintf(stderr, "%s: ", error_file); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + putc('\n', stderr); + } + + errors++; +} + +void lprintf(const char *fmt,...) +{ + va_list args; + + if (silent); + else if (log) { + char buf[1024]; + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); +#ifdef STOREMSG + savemsg(LOG_INFO, buf); +#else + syslog(LOG_INFO, "%s", buf); +#endif + } else { + va_start(args, fmt); + vfprintf(stdout, fmt, args); + va_end(args); + putchar('\n'); + } +} + +#else /* _STANDALONE_ */ +#include "../../log.h" +void error(const char *s, ...) +{ + va_list p; + + va_start(p, s); + vlog_message(s, p); + va_end(p); +} + +void lprintf(const char *s, ...) +{ + va_list p; + + va_start(p, s); + vlog_message(s, p); + va_end(p); +} +#endif diff --git a/mdk-stage1/insmod-modutils/util/meta_expand.c b/mdk-stage1/insmod-modutils/util/meta_expand.c new file mode 100644 index 000000000..41fb4024c --- /dev/null +++ b/mdk-stage1/insmod-modutils/util/meta_expand.c @@ -0,0 +1,339 @@ +/* + * Handle expansion of meta charaters + * + * Copyright 1999 Björn Ekwall <bj0rn@blox.se> + * + * "kernelversion" idea from the Debian release via: + * Wichert Akkerman <wakkerma@cs.leidenuniv.nl> + * + * Use wordexp(): idea from Tim Waugh <tim@cyberelk.demon.co.uk> + * + * Alpha typecast: Michal Jaegermann <michal@ellpspace.math.ualberta.ca> + * + * This file is part of the Linux modutils. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_WORDEXP +#undef HAVE_WORDEXP +#define HAVE_WORDEXP 0 +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <string.h> +#include <ctype.h> +#if HAVE_WORDEXP +#include <wordexp.h> +#elif HAVE_GLOB +#include <glob.h> +#endif +#include "util.h" + +/* + * Split into words delimited by whitespace, + * handle remaining quotes though... + * If strip_quotes != 0 then strip one level of quotes from the line. + */ +static void split_line(GLOB_LIST *g, char *line, int strip_quotes) +{ + int len; + char *d; + char *e; + char *p; + char tmpline[PATH_MAX]; + + for (p = line; *p; p = e) { + /* Skip leading whitespace */ + while (*p && isspace(*p)) + ++p; + + /* find end of word */ + d = tmpline; + for (e = p; *e && !(isspace(*e)); ++e) { + char match; + + /* Quote handling */ + switch (*e) { + case '\\': + if (!strip_quotes) + *d++ = *e; + break; + + case '"': + case '\'': + match = *e; + if (!strip_quotes) + *d++ = *e; + for (++e; *e && *e != match; ++e) { + *d++ = *e; + if (*e == '\\' && *(e + 1) == match) + *d++ = *++e; + } + if (!strip_quotes) + *d++ = *e; + break; + + default: + *d++ = *e; + break; + } + } + + if ((len = (int)(d - tmpline)) > 0) { + char *str = xmalloc(len + 1); + strncpy(str, tmpline, len); + str[len] = '\0'; + g->pathv = (char **)xrealloc(g->pathv, + (g->pathc + 2) * sizeof(char *)); + g->pathv[g->pathc++] = str; + } + } + + if (g->pathc) + g->pathv[g->pathc] = NULL; +} + +static int glob_it(char *pt, GLOB_LIST *g) +{ +#if HAVE_WORDEXP + wordexp_t w; + + memset(&w, 0, sizeof(w)); + if (wordexp(pt, &w, WRDE_UNDEF)) { + /* + error("wordexp %s failed", pt); + */ + return -1; + } + /* else */ + g->pathc = w.we_wordc; + g->pathv = w.we_wordv; + + return 0; +#elif HAVE_GLOB /* but not wordexp */ + glob_t w; + + memset(&w, 0, sizeof(w)); + if (glob(pt, GLOB_NOSORT, NULL, &w)) { + /* + error("glob %s failed", pt); + */ + return -1; + } + /* else */ + if (w.gl_pathc && strpbrk(w.gl_pathv[0], SHELL_META)) { + globfree(&w); + return -1; + } + g->pathc = w.gl_pathc; + g->pathv = w.gl_pathv; + + return 0; +#else /* Neither wordexp nor glob */ + return -1; +#endif +} + +/* + * Expand the string (including meta-character) to a list of matches + * + * Return 0 if OK else -1 + */ +int meta_expand(char *pt, GLOB_LIST *g, char *base_dir, char *version, int type) +{ + FILE *fin; + int len = 0; + char *line = NULL; + char *p, *p1; + char tmpline[PATH_MAX + 1]; + char wrk[sizeof(tmpline)]; + char tmpcmd[2*sizeof(tmpline)+20]; /* room for /bin/echo "text" */ + + g->pathc = 0; + g->pathv = NULL; + + /* + * Take care of version dependent expansions + * Needed for forced version handling + */ + if ((p = strchr(pt, '`')) != NULL && (type & ME_BUILTIN_COMMAND)) { + do { + char *s; + + for (s = p + 1; isspace(*s); ++s) + ; + + if (strncmp(s, "uname -r", 8) == 0) { + while (*s && (*s != '`')) + ++s; + if (*s == '`') { + *p = '\0'; + snprintf(wrk, sizeof(wrk), "%s%s%s", + pt, + version, + s + 1); + *p = '`'; + } + strcpy(tmpline, wrk); /* safe, same size */ + pt = tmpline; + } else if (strncmp(s, "kernelversion", 13) == 0) { + while (*s && (*s != '`')) + ++s; + if (*s == '`') { + int n; + char *k; + + *p = '\0'; + for (n = 0, k = version; *k; ++k) { + if (*k == '.' && ++n == 2) + break; + } + snprintf(wrk, sizeof(wrk), "%s%.*s%s", + pt, + /* typecast for Alpha */ + (int)(k - version), + version, + s + 1); + *p = '`'; + strcpy(tmpline, wrk); /* safe, same size */ + pt = tmpline; + } + } else + break; + } while ((p = strchr(pt, '`')) != NULL); + } + + /* + * Any remaining meta-chars? + */ + if (strpbrk(pt, SHELL_META) == NULL) { + /* + * No meta-chars. + * Split into words, delimited by whitespace. + */ + snprintf(wrk, sizeof(wrk), "%s%s", (base_dir ? base_dir : ""), pt); + strcpy(tmpline, wrk); /* safe, same size */ + if ((p = strtok(tmpline, " \t\n")) != NULL) { + while (p) { + g->pathv = (char **)xrealloc(g->pathv, + (g->pathc + 2) * sizeof(char *)); + g->pathv[g->pathc++] = xstrdup(p); + p = strtok(NULL, " \t\n"); + } + } + if (g->pathc) + g->pathv[g->pathc] = NULL; + return 0; + } + /* else */ + /* + * Handle remaining meta-chars + */ + + /* + * Just plain quotes? + */ + if (strpbrk(pt, "&();|<>$`!{}[]~=+:?*") == NULL && + (p = strpbrk(pt, "\"'\\"))) { + split_line(g, pt, 1); + return 0; + } + + if (strpbrk(pt, "&();|<>$`\"'\\!{}~+:[]~?*") == NULL) { + /* Only "=" remaining, should be module options */ + split_line(g, pt, 0); + return 0; + } + + /* + * If there are meta-characters and + * if they are only shell glob meta-characters: do globbing + */ +#if HAVE_WORDEXP + if (strpbrk(pt, "&();|<>`\"'\\!{}~=+:") == NULL && + strpbrk(pt, "$[]~?*")) +#else + if (strpbrk(pt, "&();|<>$`\"'\\!{}~=+:") == NULL && + strpbrk(pt, "[]~?*")) +#endif + if ((type & ME_GLOB) && glob_it(pt, g) == 0) + return 0; + + if (strpbrk(pt, "&();|<>$`\"'\\!{}~+:[]~?*") == NULL) { + /* Only "=" remaining, should be module options */ + split_line(g, pt, 0); + return 0; + } + + /* + * Last resort: Use "echo". + * DANGER: Applying shell expansion to user supplied input is a + * major security risk. Modutils code should only do meta + * expansion via shell commands for trusted data. Basically + * this means only for data in the config file. Even that + * assumes that the user cannot run modprobe as root with + * their own config file. Programs (including the kernel) + * that invoke modprobe as root with user supplied input must + * pass exactly one user supplied parameter and must set + * safe mode. + */ + if (!(type & ME_SHELL_COMMAND)) + return 0; + snprintf(wrk, sizeof(wrk), "%s%s", (base_dir ? base_dir : ""), pt); + strcpy(tmpline, wrk); /* safe, same size */ + snprintf(tmpcmd, sizeof(tmpcmd), "/bin/echo \""); + for (p = tmpline, p1 = tmpcmd + strlen(tmpcmd); *p; ++p, ++p1) { + if (*p == '"' || *p == '\\') + *p1++ = '\\'; + *p1 = *p; + } + *p1++ = '"'; + *p1++ = '\0'; + if (p1 - tmpcmd > sizeof(tmpcmd)) { + error("tmpcmd overflow, should never happen"); + exit(1); + } + if ((fin = popen(tmpcmd, "r")) == NULL) { + error("Can't execute: %s", tmpcmd); + return -1; + } + /* else */ + + /* + * Collect the result + */ + while (fgets(tmpcmd, PATH_MAX, fin) != NULL) { + int l = strlen(tmpcmd); + + line = (char *)xrealloc(line, len + l + 1); + line[len] = '\0'; + strcat(line + len, tmpcmd); /* safe, realloc */ + len += l; + } + pclose(fin); + + if (line) { + /* shell used to strip one set of quotes. Paranoia code in + * 2.3.20 stops that strip so we do it ourselves. + */ + split_line(g, line, 1); + free(line); + } + + return 0; +} diff --git a/mdk-stage1/insmod-modutils/util/modstat.c b/mdk-stage1/insmod-modutils/util/modstat.c new file mode 100644 index 000000000..ad82306c0 --- /dev/null +++ b/mdk-stage1/insmod-modutils/util/modstat.c @@ -0,0 +1,419 @@ +/* + * Get kernel symbol table(s) and other relevant module info. + * + * Add module_name_list and l_module_name_list. + * Keith Owens <kaos@ocs.com.au> November 1999. + * Björn Ekwall <bj0rn@blox.se> in February 1999 (C) + * Initial work contributed by Richard Henderson <rth@tamu.edu> + * + * This file is part of the Linux modutils. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include "util.h" +#include "module.h" +#include "obj.h" +#include "modstat.h" + +struct module_stat *module_stat; +size_t n_module_stat; +char *module_name_list; +size_t l_module_name_list; +struct module_symbol *ksyms; +size_t nksyms; +int k_new_syscalls; + +static void *old_kernsym; + +/************************************************************************/ +static void drop(void) +{ + /* + * Clean the slate for multiple runs + */ + if (module_stat) { + struct module_stat *m; + int i; + + for (i = 0, m = module_stat; i < n_module_stat; ++i, ++m) { + if (m->syms) + free(m->syms); + if (m->refs) + free(m->refs); + } + free(module_stat); + module_stat = NULL; + n_module_stat = 0; + } + if (module_name_list) { + free(module_name_list); + module_name_list = NULL; + l_module_name_list = 0; + } + if (ksyms) { + free(ksyms); + ksyms = NULL; + nksyms = 0; + } + if (old_kernsym) { + free(old_kernsym); + old_kernsym = NULL; + } +} + +static int new_get_kernel_info(int type) +{ + struct module_stat *modules; + struct module_stat *m; + struct module_symbol *syms; + struct module_symbol *s; + size_t ret; + size_t bufsize; + size_t nmod; + size_t nsyms; + size_t i; + size_t j; + char *module_names; + char *mn; + + drop(); + + /* + * Collect the loaded modules + */ + module_names = xmalloc(bufsize = 256); + while (query_module(NULL, QM_MODULES, module_names, bufsize, &ret)) { + if (errno != ENOSPC) { + error("QM_MODULES: %m\n"); + return 0; + } + module_names = xrealloc(module_names, bufsize = ret); + } + module_name_list = module_names; + l_module_name_list = bufsize; + n_module_stat = nmod = ret; + module_stat = modules = xmalloc(nmod * sizeof(struct module_stat)); + memset(modules, 0, nmod * sizeof(struct module_stat)); + + /* Collect the info from the modules */ + for (i = 0, mn = module_names, m = modules; + i < nmod; + ++i, ++m, mn += strlen(mn) + 1) { + struct module_info info; + + m->name = mn; + if (query_module(mn, QM_INFO, &info, sizeof(info), &ret)) { + if (errno == ENOENT) { + /* The module was removed out from underneath us. */ + m->flags = NEW_MOD_DELETED; + continue; + } + /* else oops */ + error("module %s: QM_INFO: %m", mn); + return 0; + } + + m->addr = info.addr; + + if (type & K_INFO) { + m->size = info.size; + m->flags = info.flags; + m->usecount = info.usecount; + m->modstruct = info.addr; + } + + if (type & K_REFS) { + int mm; + char *mrefs; + char *mr; + + mrefs = xmalloc(bufsize = 64); + while (query_module(mn, QM_REFS, mrefs, bufsize, &ret)) { + if (errno != ENOSPC) { + error("QM_REFS: %m"); + return 1; + } + mrefs = xrealloc(mrefs, bufsize = ret); + } + for (j = 0, mr = mrefs; + j < ret; + ++j, mr += strlen(mr) + 1) { + for (mm = 0; mm < i; ++mm) { + if (strcmp(mr, module_stat[mm].name) == 0) { + m->nrefs += 1; + m->refs = xrealloc(m->refs, m->nrefs * sizeof(struct module_stat **)); + m->refs[m->nrefs - 1] = module_stat + mm; + break; + } + } + } + free(mrefs); + } + + if (type & K_SYMBOLS) { /* Want info about symbols */ + syms = xmalloc(bufsize = 1024); + while (query_module(mn, QM_SYMBOLS, syms, bufsize, &ret)) { + if (errno == ENOSPC) { + syms = xrealloc(syms, bufsize = ret); + continue; + } + if (errno == ENOENT) { + /* + * The module was removed out + * from underneath us. + */ + m->flags = NEW_MOD_DELETED; + free(syms); + goto next; + } else { + error("module %s: QM_SYMBOLS: %m", mn); + return 0; + } + } + nsyms = ret; + + m->nsyms = nsyms; + m->syms = syms; + + /* Convert string offsets to string pointers */ + for (j = 0, s = syms; j < nsyms; ++j, ++s) + s->name += (unsigned long) syms; + } + next: + } + + if (type & K_SYMBOLS) { /* Want info about symbols */ + /* Collect the kernel's symbols. */ + syms = xmalloc(bufsize = 16 * 1024); + while (query_module(NULL, QM_SYMBOLS, syms, bufsize, &ret)) { + if (errno != ENOSPC) { + error("kernel: QM_SYMBOLS: %m"); + return 0; + } + syms = xrealloc(syms, bufsize = ret); + } + nksyms = nsyms = ret; + ksyms = syms; + + /* Convert string offsets to string pointers */ + for (j = 0, s = syms; j < nsyms; ++j, ++s) + s->name += (unsigned long) syms; + } + + return 1; +} + +#ifdef COMPAT_2_0 +/************************************************************************/ + +#define mscan(offs,siz,ptr) \ + if (lseek(kmem_fd, (off_t)(offs), SEEK_SET) == -1 || \ + read(kmem_fd, (ptr), (siz)) != (siz)) { \ + if (kmem_fd != -1) \ + close(kmem_fd); \ + error("kmem: %m"); \ + return 0; \ + } + +#define OLD_MOD_RUNNING 1 +#define OLD_MOD_DELETED 2 +#define OLD_MOD_VISITED 0x20000000 + +/* Fetch all the symbols and divvy them up as appropriate for the modules. */ +static int old_get_kernel_info(int type) +{ + struct old_kernel_sym *kernsym; + struct old_kernel_sym *k; + struct module_stat *module; + struct module_stat *mod; + struct module_symbol *s = NULL; + int kmem_fd = -1; + int nkernsym; + int nmod; + int nm; + int nms; + int i; + + drop(); + module_name_list = xmalloc(1); + *module_name_list = '\0'; + + if ((nkernsym = get_kernel_syms(NULL)) < 0) { + error("get_kernel_syms: %m"); + return 0; + } + kernsym = k = xmalloc(nkernsym * sizeof(struct old_kernel_sym)); + old_kernsym = kernsym; + if (get_kernel_syms(kernsym) != nkernsym) { + error("inconsistency with get_kernel_syms -- is someone else " + "playing with modules?"); + free(kernsym); + return 0; + } + + /* Number of modules */ + for (k = kernsym, nmod = 0, i = 0; i < nkernsym; ++i, ++k) { + if (k->name[0] == '#') { + if (k->name[1]) { + ++nmod; + i = strlen(k->name+1) + 1; + module_name_list = + xrealloc(module_name_list, + l_module_name_list + i); + strcpy(module_name_list+l_module_name_list, /* safe, xrealloc */ + k->name+1); + l_module_name_list += i; /* NUL separated strings */ + } + else + break; + } + } + module_stat = mod = module = xmalloc(nmod * sizeof(struct module_stat)); + memset(module, 0, nmod * sizeof(struct module_stat)); + n_module_stat = nmod; + + /* + * Will we need kernel internal info? + */ + if ((type & K_INFO) || (type & K_REFS)) { + if ((kmem_fd = open("/dev/kmem", O_RDONLY)) < 0) { + perror("ksyms: open /dev/kmem"); + return 0; + } + } + + /* + * Collect the module information. + */ + for (k = kernsym, nm = 0, i = 0; i < nkernsym; ++i, ++k) { + if (k->name[0] == '#') { + struct old_kernel_sym *p; + struct old_module info; + + if (k->name[1] == '\0') + break; /* kernel resident symbols follow */ + /* else normal module */ + + module = mod++; + ++nm; + module->name = k->name + 1; + module->modstruct = k->value; + + if ((type & K_INFO) || (type & K_REFS)) { + long tmp; + /* + * k->value is the address of the + * struct old_module + * in the kernel (for use via /dev/kmem) + */ + mscan(k->value, sizeof(info), &info); + module->addr = info.addr; + module->size = info.size * getpagesize(); + + mscan(info.addr, sizeof(long), &tmp); + module->flags = info.state & + (OLD_MOD_RUNNING | OLD_MOD_DELETED); + module->flags |= NEW_MOD_USED_ONCE; /* Cheat */ + if (tmp & OLD_MOD_AUTOCLEAN) + module->flags |= NEW_MOD_AUTOCLEAN; + if (tmp & OLD_MOD_VISITED) + module->flags |= NEW_MOD_VISITED; + + module->usecount = tmp & ~(OLD_MOD_AUTOCLEAN | OLD_MOD_VISITED); + } + + if ((type & K_REFS) && info.ref) { + struct old_module_ref mr; + int j; + unsigned long ref = info.ref; + + do { + mscan(ref, sizeof(struct old_module_ref), &mr); + for (j = 0; j < nm -1; ++j) { + if (mr.module == module_stat[j].modstruct) { + module->nrefs += 1; + module->refs = xrealloc(module->refs, module->nrefs * sizeof(struct module_stat **)); + module->refs[module->nrefs - 1] = module_stat + j; + break; + } + } + } while ((ref = mr.next) != 0); + } + + if (!(type & K_SYMBOLS)) + continue; + /* + * Find out how many symbols this module has. + */ + for (nms = 0, p = k+1; p->name[0] != '#'; ++p) + ++nms; + s = xmalloc(nms * sizeof(struct module_symbol)); + module->syms = s; + module->nsyms = nms; + } else if (type & K_SYMBOLS) { /* Want info about symbols */ + s->name = (unsigned long) k->name; + s->value = k->value; + ++s; + } + } + if ((type & K_INFO) || (type & K_REFS)) { + if (kmem_fd != -1) + close(kmem_fd); + } + + /* + * Kernel resident symbols follows + */ + if (type & K_SYMBOLS) { /* Want info about symbols */ + if (k->name[0] == '#') + ++k; + nksyms = nkernsym - (k - kernsym); + if (nksyms) { + ksyms = s = xmalloc(nksyms * sizeof(struct module_symbol)); + for (i = 0; i < nksyms; ++i, ++k) { + if (k->name[0] != '#') { + s->name = (unsigned long) k->name; + s->value = k->value; + ++s; + } + } + nksyms = s - ksyms; + } else + ksyms = NULL; + } + + return 1; +} +#endif /* COMPAT_2_0 */ + +int get_kernel_info(int type) +{ + k_new_syscalls = !query_module(NULL, 0, NULL, 0, NULL); + +#ifdef COMPAT_2_0 + if (!k_new_syscalls) + return old_get_kernel_info(type); +#endif /* COMPAT_2_0 */ + + return new_get_kernel_info(type); +} diff --git a/mdk-stage1/insmod-modutils/util/snap_shot.c b/mdk-stage1/insmod-modutils/util/snap_shot.c new file mode 100644 index 000000000..ae0cc7c79 --- /dev/null +++ b/mdk-stage1/insmod-modutils/util/snap_shot.c @@ -0,0 +1,154 @@ +/* Take a snap shot of ksyms and modules for Oops debugging + Copyright 1999 Linux International. + + Contributed by Keith Owens <kaos@ocs.com.au> + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#ident "$Id$" + +#include <errno.h> +#include <stdio.h> +#include <stdarg.h> +#include <time.h> +#include <unistd.h> +#include <string.h> + +#include "module.h" +#include "obj.h" +#include "modstat.h" +#include "util.h" + +static char snap_dir[] = "/var/log/ksymoops"; + +/* If snap_dir exists, take a snap shot of ksyms and modules to snap_dir. + * Prefix the files with the equivalent of + * date +%Y%m%d%T%M%S | sed -e 's/://g' + */ +void snap_shot(const char *module_names, int n_module_names) +{ + char file[] = "ccyymmddhhmmss.modules", buffer[4096]; + static char *infile[] = { "/proc/ksyms", "/proc/modules" }; + static char *suffix[] = { "ksyms", "modules" }; + struct tm *local; + time_t t; + int i, l; + FILE *in, *out; + + if (module_names) { + /* Only snap shot if the list of modules has changed. + * Otherwise auto cleanup takes a snap shot every time + * and ends up with a large snap shot directory. + */ + char *new_module_names; + size_t n_new_module_names; + get_kernel_info(0); + new_module_names = module_name_list; + n_new_module_names = n_module_stat; + if (n_module_names && n_new_module_names == n_module_names) { + while (n_module_names) { + if (strcmp(module_names, new_module_names)) + break; /* difference detected */ + i = strlen(module_names) + 1; + module_names += i; + new_module_names += i; + --n_module_names; + } + } + if (!n_module_names) + return; /* no difference, no need for snap shot */ + } + + if (chdir(snap_dir)) + return; + t = time(NULL); + local = localtime(&t); + for (i = 0; i < sizeof(infile)/sizeof(infile[0]); ++i) { + snprintf(file, sizeof(file), "%04d%02d%02d%02d%02d%02d.%s", + local->tm_year+1900, + local->tm_mon + 1, + local->tm_mday, + local->tm_hour, + local->tm_min, + local->tm_sec, + suffix[i]); + out = fopen(file, "w"); + if (!out) { + error("cannot create %s/%s %m", snap_dir, file); + return; + } + in = fopen(infile[i], "r"); + if (!in) { + error("cannot open %s %m", infile[i]); + return; + } + while ((l = fread(buffer, 1, sizeof(buffer), in)) > 0) { + if (fwrite(buffer, l, 1, out) != 1) { + error("unable to write to %s %m", file); + fclose(in); + fclose(out); + return; + } + } + if (ferror(in)) + error("unable to read from %s %m", infile[i]); + fclose(in); + fclose(out); + } +} + +/* If snap_dir exists, log a message to snap_dir. The log file is called the + * equivalent of date +%Y%m%d | sed -e 's/://g'. Each line is prefixed with + * timestamp down to seconds and followed by a newline. + */ +void snap_shot_log(const char *fmt,...) +{ + char date[] = "ccyymmdd", file[] = "ccyymmdd.log", stamp[] = "ccyymmdd hhmmss"; + struct tm *local; + time_t t; + FILE *log; + va_list args; + int save_errno = errno; + + if (chdir(snap_dir)) + return; + t = time(NULL); + local = localtime(&t); + snprintf(date, sizeof(date), "%04d%02d%02d", + local->tm_year+1900, + local->tm_mon + 1, + local->tm_mday); + snprintf(file, sizeof(file), "%s.log", date); + log = fopen(file, "a"); + if (!log) { + error("cannot create %s/%s %m", snap_dir, file); + return; + } + snprintf(stamp, sizeof(stamp), "%s %02d%02d%02d", + date, + local->tm_hour, + local->tm_min, + local->tm_sec); + fprintf(log, "%s ", stamp); + va_start(args, fmt); + errno = save_errno; /* fmt may use %m */ + vfprintf(log, fmt, args); + va_end(args); + fprintf(log, "\n"); + fclose(log); +} diff --git a/mdk-stage1/insmod-modutils/util/sys_cm.c b/mdk-stage1/insmod-modutils/util/sys_cm.c new file mode 100644 index 000000000..851fb709e --- /dev/null +++ b/mdk-stage1/insmod-modutils/util/sys_cm.c @@ -0,0 +1,88 @@ +/* Functions for the Linux module syscall interface. + Copyright 1996, 1997 Linux International. + Contributed by Richard Henderson <rth@tamu.edu> + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ident "$Id$" + +#include <stdlib.h> +#include <errno.h> + +#include "module.h" + +/* Kernel headers before 2.1.mumble need this on the Alpha to get + _syscall* defined. */ +#define __LIBRARY__ + +#include <asm/unistd.h> + + +/*======================================================================*/ + +#if defined(__i386__) || defined(__m68k__) || defined(__arm__) + +#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) +{ + /* Why all this fuss? + + In linux 2.1, the address returned by create module point in + kernel space which is now mapped at the top of user space (at + 0xc0000000 on i386). This looks like a negative number for a + long. The normal syscall macro of linux 2.0 (and all libc compile + with linux 2.0 or below) consider that the return value is a + negative number and consider it is an error number (A kernel + convention, return value are positive or negative, indicating the + error number). + + By checking the value of errno, we know if we have been fooled by + the syscall2 macro and we fix it. */ + + long ret = _create_module(name, size); + if (ret == -1 && errno > 125) + { + ret = -errno; + errno = 0; + } + return ret; +} + +#elif defined(__alpha__) + +/* Alpha doesn't have the same problem, exactly, but a bug in older + kernels fails to clear the error flag. Clear it here explicitly. */ + +#define __NR__create_module __NR_create_module +static inline _syscall4(unsigned long, _create_module, const char *, name, + size_t, size, size_t, dummy, size_t, err); + +unsigned long create_module(const char *name, size_t size) +{ + return _create_module(name, size, 0, 0); +} + +#else + +/* Sparc, MIPS, (and Alpha, but that's another problem) don't mistake + return values for errors due to the nature of the system call. */ + +_syscall2(unsigned long, create_module, const char *, name, size_t, size) + +#endif diff --git a/mdk-stage1/insmod-modutils/util/sys_dm.c b/mdk-stage1/insmod-modutils/util/sys_dm.c new file mode 100644 index 000000000..a166a30f4 --- /dev/null +++ b/mdk-stage1/insmod-modutils/util/sys_dm.c @@ -0,0 +1,37 @@ +/* Functions for the Linux module syscall interface. + Copyright 1996, 1997 Linux International. + Contributed by Richard Henderson <rth@tamu.edu> + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ident "$Id$" + +#include <stdlib.h> +#include <errno.h> + +#include "module.h" + +/* Kernel headers before 2.1.mumble need this on the Alpha to get + _syscall* defined. */ +#define __LIBRARY__ + +#include <asm/unistd.h> + + +/*======================================================================*/ + +_syscall1(int, delete_module, const char *, name) diff --git a/mdk-stage1/insmod-modutils/util/sys_gks.c b/mdk-stage1/insmod-modutils/util/sys_gks.c new file mode 100644 index 000000000..f71772c71 --- /dev/null +++ b/mdk-stage1/insmod-modutils/util/sys_gks.c @@ -0,0 +1,37 @@ +/* Functions for the Linux module syscall interface. + Copyright 1996, 1997 Linux International. + Contributed by Richard Henderson <rth@tamu.edu> + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ident "$Id$" + +#include <stdlib.h> +#include <errno.h> + +#include "module.h" + +/* Kernel headers before 2.1.mumble need this on the Alpha to get + _syscall* defined. */ +#define __LIBRARY__ + +#include <asm/unistd.h> + + +/*======================================================================*/ + +_syscall1(int, get_kernel_syms, struct old_kernel_sym *, ksyms) diff --git a/mdk-stage1/insmod-modutils/util/sys_nim.c b/mdk-stage1/insmod-modutils/util/sys_nim.c new file mode 100644 index 000000000..bbe42135c --- /dev/null +++ b/mdk-stage1/insmod-modutils/util/sys_nim.c @@ -0,0 +1,53 @@ +/* Functions for the Linux module syscall interface. + Copyright 1996, 1997 Linux International. + Contributed by Richard Henderson <rth@tamu.edu> + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ident "$Id$" + +#include <stdlib.h> +#include <errno.h> + +#include "module.h" + +/* Kernel headers before 2.1.mumble need this on the Alpha to get + _syscall* defined. */ +#define __LIBRARY__ + +#include <asm/unistd.h> + + +/*======================================================================*/ + +#ifndef CONFIG_USE_SYSCALL + +extern int init_module(const char *name, const struct module *info); + +int +sys_init_module(const char *name, const struct module *info) +{ + return init_module(name, info); +} + +#else + +#define __NR_sys_init_module __NR_init_module +_syscall2(int, sys_init_module, const char *, name, + const struct module *, info) + +#endif diff --git a/mdk-stage1/insmod-modutils/util/sys_oim.c b/mdk-stage1/insmod-modutils/util/sys_oim.c new file mode 100644 index 000000000..73ac6be52 --- /dev/null +++ b/mdk-stage1/insmod-modutils/util/sys_oim.c @@ -0,0 +1,40 @@ +/* Functions for the Linux module syscall interface. + Copyright 1996, 1997 Linux International. + Contributed by Richard Henderson <rth@tamu.edu> + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ident "$Id$" + +#include <stdlib.h> +#include <errno.h> + +#include "module.h" + +/* Kernel headers before 2.1.mumble need this on the Alpha to get + _syscall* defined. */ +#define __LIBRARY__ + +#include <asm/unistd.h> + + +/*======================================================================*/ + +#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) diff --git a/mdk-stage1/insmod-modutils/util/sys_qm.c b/mdk-stage1/insmod-modutils/util/sys_qm.c new file mode 100644 index 000000000..119a219a2 --- /dev/null +++ b/mdk-stage1/insmod-modutils/util/sys_qm.c @@ -0,0 +1,56 @@ +/* Functions for the Linux module syscall interface. + Copyright 1996, 1997 Linux International. + Contributed by Richard Henderson <rth@tamu.edu> + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ident "$Id$" + +#include <stdlib.h> +#include <errno.h> + +#include "module.h" + +/* Kernel headers before 2.1.mumble need this on the Alpha to get + _syscall* defined. */ +#define __LIBRARY__ + +#include <asm/unistd.h> + + +/*======================================================================*/ + +/* I am fucking tired of the "this doesn't build on 2.0.x" questions. + But if you ask, we still officially require 2.1.x to build. */ +#if !defined(__NR_query_module) +# if defined(__i386__) +# define __NR_query_module 167 +# elif defined(__alpha__) +# define __NR_query_module 347 +# elif defined(__sparc__) +# define __NR_query_module 184 +# elif defined(__mc68000__) +# define __NR_query_module 167 +# elif defined(__arm__) +# define __NR_query_module (__NR_SYSCALL_BASE + 167) +# elif defined(__mips__) +# define __NR_query_module 4187 +# endif +#endif + +_syscall5(int, query_module, const char *, name, int, which, + void *, buf, size_t, bufsize, size_t *, ret); diff --git a/mdk-stage1/insmod-modutils/util/xftw.c b/mdk-stage1/insmod-modutils/util/xftw.c new file mode 100644 index 000000000..fe764a63c --- /dev/null +++ b/mdk-stage1/insmod-modutils/util/xftw.c @@ -0,0 +1,422 @@ +/* + * modutils specific implementation of ftw(). + * + * Copyright 2000: + * Keith Owens <kaos@ocs.com.au> August 2000 + * + * This file is part of the Linux modutils. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + modutils requires special processing during the file tree walk + of /lib/modules/<version> and any paths that the user specifies. + The standard ftw() does a blind walk of all paths and can end + up following the build symlink down the kernel source tree. + Although nftw() has the option to get more control such as not + automatically following symbolic links, even that is not enough + for modutils. The requirements are: + + Paths must be directories or symlinks to directories. + + Each directory is read and sorted into alphabetical order + before processing. + + A directory is type 1 iff it was specified on a path statement + (either explicit or default) and the directory contains a + subdirectory with one of the known names and the directory name + does not end with "/kernel". Otherwise it is type 2. + + In a type 1 directory, walk the kernel subdirectory if it exists, + then the old known names in their historical order then any + remaining directory entries in alphabetical order and finally any + non-directory entries in alphabetical order. + + Entries in a type 1 directory are filtered against the "prune" + list. A type 1 directory can contain additional files which + are not modules nor symlinks to modules. The prune list skips + known additional files, if a distribution wants to store + additional text files in the top level directory they should be + added to the prune list. + + A type 2 directory must contain only modules or symlinks to + modules. They are processed in alphabetical order, without + pruning. Symlinks to directories are an error in type 2 + directories. + + The user function is not called for type 1 directories, nor for + pruned entries. It is called for type 2 directories and their + contents. It is also called for any files left in a type 1 + directory after pruning and processing type 2 subdirectories. + The user function never sees symlinks, they are resolved before + calling the function. + + Why have different directory types? The original file tree + walk was not well defined. Some users specified each directory + individually, others just pointed at the top level directory. + Either version worked until the "build" symlink was added. Now + users who specify the top level directory end up running the + entire kernel source tree looking for modules, not nice. We + cannot just ignore symlinks because pcmcia uses symlinks to + modules for backwards compatibility. + + Type 1 is when a user specifies the top level directory which needs + special processing, type 2 is individual subdirectories. But the + only way to tell the difference is by looking at the contents. The + "/kernel" directory introduced in 2.3.12 either contains nothing + (old make modules_install) or contains all the kernel modules using + the same tree structure as the source. Because "/kernel" can + contain old names but is really a type 2 directory, it is detected + as a special case. + */ + +#include <dirent.h> +#include <errno.h> +#include <limits.h> +#include <malloc.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> + +#include "util.h" +#include "config.h" + +extern char *tbpath[]; + +extern OPT_LIST *prune_list; +extern int n_prune_list; + +extern char *tbtype[]; + +struct xftw_dirent { + struct stat statbuf; + char *name; + char *fullname; +}; + +#define XFTW_MAXDEPTH 64 /* Maximum directory depth handled */ + +typedef struct { + struct xftw_dirent *contents; + int size; + int used; +} xftw_tree_t; + +static xftw_tree_t tree[XFTW_MAXDEPTH]; + +/* Free all data for one tree level */ +static void xftw_free_tree(int depth) +{ + int i; + xftw_tree_t *t = tree+depth; + for (i = 0; i < t->size; ++i) { + free(t->contents[i].name); + free(t->contents[i].fullname); + } + free(t->contents); + t->contents = NULL; + t->size = 0; + t->used = 0; +} + +/* Increment dirents used at this depth, resizing if necessary */ +static void xftw_add_dirent(int depth) +{ + xftw_tree_t *t = tree+depth; + int i, size = t->size; + if (++t->used < size) + return; + size += 10; /* arbitrary increment */ + t->contents = xrealloc(t->contents, size*sizeof(*(t->contents))); + for (i = t->size; i < size; ++i) { + memset(&(t->contents[i].statbuf), 0, sizeof(t->contents[i].statbuf)); + t->contents[i].name = NULL; + t->contents[i].fullname = NULL; + } + t->size = size; +} + +/* Concatenate directory name and entry name into one string. + * Note: caller must free result or leak. + */ +static char *xftw_dir_name(const char *directory, const char *entry) +{ + int i = strlen(directory); + char *name; + if (entry) + i += strlen(entry); + i += 2; + name = xmalloc(i); + strcpy(name, directory); /* safe, xmalloc */ + if (*directory && entry) + strcat(name, "/"); /* safe, xmalloc */ + if (entry) + strcat(name, entry); /* safe, xmalloc */ + return(name); +} + +/* Call the user function for a directory entry */ +static int xftw_do_name(const char *directory, const char *entry, struct stat *sb, xftw_func_t funcptr) +{ + int ret = 0; + char *name = xftw_dir_name(directory, entry); + + if (S_ISLNK(sb->st_mode)) { + char real[PATH_MAX], *newname; + verbose("resolving %s symlink to ", name); + if (!(newname = realpath(name, real))) { + if (errno == ENOENT) { + verbose("%s: does not exist, dangling symlink ignored\n", real); + goto cleanup; + } + perror("... failed"); + goto cleanup; + } + verbose("%s ", newname); + if (lstat(newname, sb)) { + error("lstat on %s failed ", newname); + perror(""); + goto cleanup; + } + free(name); + name = xstrdup(newname); + } + + if (!S_ISREG(sb->st_mode) && + !S_ISDIR(sb->st_mode)) { + error("%s is not plain file nor directory\n", name); + goto cleanup; + } + + verbose("user function %s\n", name); + ret = (*funcptr)(name, sb); +cleanup: + free(name); + return(ret); +} + +/* Sort directory entries into alphabetical order */ +static int xftw_sortdir(const void *a, const void *b) +{ + return(strcmp(((struct xftw_dirent *)a)->name, ((struct xftw_dirent *)b)->name)); +} + +/* Read a directory and sort it, ignoring "." and ".." */ +static int xftw_readdir(const char *directory, int depth) +{ + DIR *d; + struct dirent *ent; + verbose("xftw_readdir %s\n", directory); + if (!(d = opendir(directory))) { + perror(directory); + return(1); + } + while ((ent = readdir(d))) { + char *name; + struct xftw_dirent *f; + if (strcmp(ent->d_name, ".") == 0 || + strcmp(ent->d_name, "..") == 0) + continue; + name = xftw_dir_name(directory, ent->d_name); + xftw_add_dirent(depth); + f = tree[depth].contents+tree[depth].used-1; + f->name = xstrdup(ent->d_name); + f->fullname = name; /* do not free name, it is in use */ + if (lstat(name, &(f->statbuf))) { + perror(name); + return(1); + } + } + closedir(d); + qsort(tree[depth].contents, tree[depth].used, sizeof(*(tree[0].contents)), &xftw_sortdir); + return(0); +} + +/* Process a type 2 directory */ +int xftw_type2(const char *directory, const char *entry, int depth, xftw_func_t funcptr) +{ + int ret, i; + xftw_tree_t *t = tree+depth; + struct stat statbuf; + char *dirname = xftw_dir_name(directory, entry); + + verbose("type 2 %s\n", dirname); + if (depth > XFTW_MAXDEPTH) { + error("xftw_type2 exceeded maxdepth\n"); + ret = 1; + goto cleanup; + } + if ((ret = xftw_readdir(dirname, depth))) + goto cleanup; + + t = tree+depth; + /* user function sees type 2 directories */ + if ((ret = lstat(dirname, &statbuf)) || + (ret = xftw_do_name("", dirname, &statbuf, funcptr))) + goto cleanup; + + /* user sees all contents of type 2 directory, no pruning */ + for (i = 0; i < t->used; ++i) { + struct xftw_dirent *c = t->contents+i; + if (S_ISLNK(c->statbuf.st_mode)) { + if (!stat(c->name, &(c->statbuf))) { + if (S_ISDIR(c->statbuf.st_mode)) { + error("symlink to directory is not allowed, %s ignored\n", c->name); + *(c->name) = '\0'; /* ignore it */ + } + } + } + if (!*(c->name)) + continue; + if (S_ISDIR(c->statbuf.st_mode)) { + /* recursion is the curse of the programming classes */ + ret = xftw_type2(dirname, c->name, depth+1, funcptr); + if (ret) + goto cleanup; + } + else if ((ret = xftw_do_name(dirname, c->name, &(c->statbuf), funcptr))) + goto cleanup; + *(c->name) = '\0'; /* processed */ + } + + ret = 0; +cleanup: + free(dirname); + return(ret); +} + +/* Only external visible function. Decide on the type of directory and + * process accordingly. + */ +int xftw(const char *directory, xftw_func_t funcptr) +{ + struct stat statbuf; + int ret, i, j, type; + xftw_tree_t *t; + struct xftw_dirent *c; + + verbose("xftw starting at %s ", directory); + if (lstat(directory, &statbuf)) { + verbose("lstat on %s failed\n", directory); + return(0); + } + if (S_ISLNK(statbuf.st_mode)) { + char real[PATH_MAX]; + verbose("resolving symlink to "); + if (!(directory = realpath(directory, real))) { + if (errno == ENOENT) { + verbose("%s: does not exist, dangling symlink ignored\n", real); + return(0); + } + perror("... failed"); + return(-1); + } + verbose("%s ", directory); + if (lstat(directory, &statbuf)) { + error("lstat on %s failed ", directory); + perror(""); + return(-1); + } + } + if (!S_ISDIR(statbuf.st_mode)) { + error("%s is not a directory\n", directory); + return(-1); + } + verbose("\n"); + + /* All returns after this point must be via cleanup */ + + if ((ret = xftw_readdir(directory, 0))) + goto cleanup; + + t = tree; /* depth 0 */ + type = 2; + for (i = 0 ; type == 2 && i < t->used; ++i) { + c = t->contents+i; + for (j = 0; tbtype[j]; ++j) { + if (strcmp(c->name, tbtype[j]) == 0 && + S_ISDIR(c->statbuf.st_mode)) { + const char *p = directory + strlen(directory) - 1; + if (*p == '/') + --p; + if (p - directory >= 6 && strncmp(p-6, "/kernel", 7) == 0) + continue; /* "/kernel" path is a special case, type 2 */ + type = 1; /* known subdirectory */ + break; + } + } + } + + if (type == 1) { + OPT_LIST *p; + /* prune entries in type 1 directories only */ + for (i = 0 ; i < t->used; ++i) { + for (p = prunelist; p->name; ++p) { + c = t->contents+i; + if (strcmp(p->name, c->name) == 0) { + verbose("pruned %s\n", c->name); + *(c->name) = '\0'; /* ignore */ + } + } + } + /* run known subdirectories first in historical order, "kernel" is now top of list */ + for (i = 0 ; i < t->used; ++i) { + c = t->contents+i; + for (j = 0; tbtype[j]; ++j) { + if (*(c->name) && + strcmp(c->name, tbtype[j]) == 0 && + S_ISDIR(c->statbuf.st_mode)) { + if ((ret = xftw_type2(directory, c->name, 1, funcptr))) + goto cleanup; + *(c->name) = '\0'; /* processed */ + } + } + } + /* any other directories left, in alphabetical order */ + for (i = 0 ; i < t->used; ++i) { + c = t->contents+i; + if (*(c->name) && + S_ISDIR(c->statbuf.st_mode)) { + if ((ret = xftw_type2(directory, c->name, 1, funcptr))) + goto cleanup; + *(c->name) = '\0'; /* processed */ + } + } + /* anything else is passed to the user function */ + for (i = 0 ; i < t->used; ++i) { + c = t->contents+i; + if (*(c->name)) { + verbose("%s found in type 1 directory %s\n", c->name, directory); + if ((ret = xftw_do_name(directory, c->name, &(c->statbuf), funcptr))) + goto cleanup; + *(c->name) = '\0'; /* processed */ + } + } + } + else { + /* type 2 */ + xftw_free_tree(0); + if ((ret = xftw_type2(directory, NULL, 0, funcptr))) + goto cleanup; + } + + /* amazing, it all worked */ + ret = 0; +cleanup: + for (i = 0; i < XFTW_MAXDEPTH; ++i) + xftw_free_tree(i); + return(ret); +} diff --git a/mdk-stage1/insmod-modutils/util/xmalloc.c b/mdk-stage1/insmod-modutils/util/xmalloc.c new file mode 100644 index 000000000..9113d47fe --- /dev/null +++ b/mdk-stage1/insmod-modutils/util/xmalloc.c @@ -0,0 +1,39 @@ +/* Misc utility functions. + Copyright 1996, 1997 Linux International. + Contributed by Richard Henderson <rth@tamu.edu> + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ident "$Id$" + +#include <stdlib.h> +#include "util.h" + + +/*======================================================================*/ + +void * +xmalloc(size_t size) +{ + void *ptr = malloc(size); + if (!ptr) + { + error("Out of memory"); + exit(1); + } + return ptr; +} diff --git a/mdk-stage1/insmod-modutils/util/xrealloc.c b/mdk-stage1/insmod-modutils/util/xrealloc.c new file mode 100644 index 000000000..d287486f7 --- /dev/null +++ b/mdk-stage1/insmod-modutils/util/xrealloc.c @@ -0,0 +1,39 @@ +/* Misc utility functions. + Copyright 1996, 1997 Linux International. + Contributed by Richard Henderson <rth@tamu.edu> + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ident "$Id$" + +#include <stdlib.h> +#include "util.h" + + +/*======================================================================*/ + +void * +xrealloc(void *old, size_t size) +{ + void *ptr = realloc(old, size); + if (!ptr) + { + error("Out of memory"); + exit(1); + } + return ptr; +} diff --git a/mdk-stage1/insmod-modutils/util/xstrcat.c b/mdk-stage1/insmod-modutils/util/xstrcat.c new file mode 100644 index 000000000..abb075c83 --- /dev/null +++ b/mdk-stage1/insmod-modutils/util/xstrcat.c @@ -0,0 +1,40 @@ +/* Misc utility functions. + Copyright 2000 Keith Owens <kaos@ocs.com.au> + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ident "$Id" + +#include <stdlib.h> +#include <string.h> +#include "util.h" + + +/*======================================================================*/ + +char * +xstrcat(char *dest, const char *src, size_t size) +{ + int ldest = strlen(dest); + int lsrc = strlen(src); + if ((size - ldest - 1) < lsrc) { + error("xstrcat: destination overflow"); + exit(1); + } + memcpy(dest+ldest, src, lsrc+1); + return(dest); +} diff --git a/mdk-stage1/insmod-modutils/util/xstrdup.c b/mdk-stage1/insmod-modutils/util/xstrdup.c new file mode 100644 index 000000000..11b289eb0 --- /dev/null +++ b/mdk-stage1/insmod-modutils/util/xstrdup.c @@ -0,0 +1,41 @@ +/* Misc utility functions. + Copyright 1996, 1997 Linux International. + Contributed by Richard Henderson <rth@tamu.edu> + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ident "$Id$" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "util.h" + + +/*======================================================================*/ + +char * +xstrdup(const char *s) +{ + char *n = strdup(s); + if (!n) + { + error("Out of memory"); + exit(1); + } + return n; +} diff --git a/mdk-stage1/insmod-modutils/util/xsystem.c b/mdk-stage1/insmod-modutils/util/xsystem.c new file mode 100644 index 000000000..edb995268 --- /dev/null +++ b/mdk-stage1/insmod-modutils/util/xsystem.c @@ -0,0 +1,51 @@ +/* Misc utility functions. + Copyright 2000 Keith Owens <kaos@ocs.com.au> + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ident "$Id" + +#include <sys/types.h> +#include <sys/wait.h> +#include <errno.h> +#include <unistd.h> + + +/*======================================================================*/ + +/* Clone of the system() function From Steven's Advanced Programming in a Unix + * Environment. Modified to use *argv[] and execvp to avoid shell expansion + * problems, modutils runs as root so system() is unsafe. + */ + +int +xsystem(const char *file, char *const argv[]) +{ + pid_t pid; + int status; + if ((pid = fork()) < 0) + return(-1); + if (pid == 0) { + execvp(file, argv); + _exit(127); + } + while (waitpid(pid, &status, 0) < 0) { + if (errno != EINTR) + return(-1); + } + return(status); +} diff --git a/mdk-stage1/insmod.h b/mdk-stage1/insmod.h new file mode 100644 index 000000000..d91f239cf --- /dev/null +++ b/mdk-stage1/insmod.h @@ -0,0 +1,20 @@ +/* + * Guillaume Cottenceau (gc@mandrakesoft.com) + * + * Copyright 2000 MandrakeSoft + * + * This software may be freely redistributed under the terms of the GNU + * public license. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _INSMOD_INTERFACE_H_ +#define _INSMOD_INTERFACE_H_ + +int insmod_call(char * full_filename, char * params); + +#endif diff --git a/mdk-stage1/modules.c b/mdk-stage1/modules.c index 1b85fa7c8..2788315f8 100644 --- a/mdk-stage1/modules.c +++ b/mdk-stage1/modules.c @@ -25,7 +25,7 @@ #include <unistd.h> #include <string.h> #include <stdio.h> -#include "insmod-busybox/insmod.h" +#include "insmod.h" #include "stage1.h" #include "log.h" #include "mar/mar-extract-only.h" diff --git a/mdk-stage1/stage1.c b/mdk-stage1/stage1.c index 64753bdcf..8956a2a7f 100644 --- a/mdk-stage1/stage1.c +++ b/mdk-stage1/stage1.c @@ -45,7 +45,7 @@ #include "tools.h" #include "automatic.h" #include "mount.h" -#include "insmod-busybox/insmod.h" +#include "insmod.h" #ifdef ENABLE_PCMCIA #include "pcmcia/pcmcia.h" |