kldpatch/Makefile000644 000423 000000 00000000266 11131471253 014550 0ustar00luigiwheel000000 000000 # Makefile for kldpatch WARNS?= 5 PROG= kldpatch MAN= kldpatch.8 SRCS= kldpatch.c elfdump.c LDADD= -lkvm CFLAGS+= -DSTANDALONE # disable main() in elfdump.c .include kldpatch/elfdump.c000644 000423 000000 00000076767 11131471254 014734 0ustar00luigiwheel000000 000000 /*- * Copyright (c) 2003 David O'Brien. All rights reserved. * Copyright (c) 2001 Jake Burkholder * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD: src/usr.bin/elfdump/elfdump.c,v 1.12.8.2 2006/01/28 18:40:55 marcel Exp $"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "elfdump.h" #define elf_get_addr elf_get_quad #define elf_get_off elf_get_quad #define elf_get_size elf_get_quad enum elf_member { D_TAG = 1, D_PTR, D_VAL, E_CLASS, E_DATA, E_OSABI, E_TYPE, E_MACHINE, E_VERSION, E_ENTRY, E_PHOFF, E_SHOFF, E_FLAGS, E_EHSIZE, E_PHENTSIZE, E_PHNUM, E_SHENTSIZE, E_SHNUM, E_SHSTRNDX, N_NAMESZ, N_DESCSZ, N_TYPE, P_TYPE, P_OFFSET, P_VADDR, P_PADDR, P_FILESZ, P_MEMSZ, P_FLAGS, P_ALIGN, SH_NAME, SH_TYPE, SH_FLAGS, SH_ADDR, SH_OFFSET, SH_SIZE, SH_LINK, SH_INFO, SH_ADDRALIGN, SH_ENTSIZE, ST_NAME, ST_VALUE, ST_SIZE, ST_INFO, ST_SHNDX, R_OFFSET, R_INFO, RA_OFFSET, RA_INFO, RA_ADDEND }; typedef enum elf_member elf_member_t; static int elf32_offsets[] = { 0, offsetof(Elf32_Dyn, d_tag), offsetof(Elf32_Dyn, d_un.d_ptr), offsetof(Elf32_Dyn, d_un.d_val), offsetof(Elf32_Ehdr, e_ident[EI_CLASS]), offsetof(Elf32_Ehdr, e_ident[EI_DATA]), offsetof(Elf32_Ehdr, e_ident[EI_OSABI]), offsetof(Elf32_Ehdr, e_type), offsetof(Elf32_Ehdr, e_machine), offsetof(Elf32_Ehdr, e_version), offsetof(Elf32_Ehdr, e_entry), offsetof(Elf32_Ehdr, e_phoff), offsetof(Elf32_Ehdr, e_shoff), offsetof(Elf32_Ehdr, e_flags), offsetof(Elf32_Ehdr, e_ehsize), offsetof(Elf32_Ehdr, e_phentsize), offsetof(Elf32_Ehdr, e_phnum), offsetof(Elf32_Ehdr, e_shentsize), offsetof(Elf32_Ehdr, e_shnum), offsetof(Elf32_Ehdr, e_shstrndx), offsetof(Elf_Note, n_namesz), offsetof(Elf_Note, n_descsz), offsetof(Elf_Note, n_type), offsetof(Elf32_Phdr, p_type), offsetof(Elf32_Phdr, p_offset), offsetof(Elf32_Phdr, p_vaddr), offsetof(Elf32_Phdr, p_paddr), offsetof(Elf32_Phdr, p_filesz), offsetof(Elf32_Phdr, p_memsz), offsetof(Elf32_Phdr, p_flags), offsetof(Elf32_Phdr, p_align), offsetof(Elf32_Shdr, sh_name), offsetof(Elf32_Shdr, sh_type), offsetof(Elf32_Shdr, sh_flags), offsetof(Elf32_Shdr, sh_addr), offsetof(Elf32_Shdr, sh_offset), offsetof(Elf32_Shdr, sh_size), offsetof(Elf32_Shdr, sh_link), offsetof(Elf32_Shdr, sh_info), offsetof(Elf32_Shdr, sh_addralign), offsetof(Elf32_Shdr, sh_entsize), offsetof(Elf32_Sym, st_name), offsetof(Elf32_Sym, st_value), offsetof(Elf32_Sym, st_size), offsetof(Elf32_Sym, st_info), offsetof(Elf32_Sym, st_shndx), offsetof(Elf32_Rel, r_offset), offsetof(Elf32_Rel, r_info), offsetof(Elf32_Rela, r_offset), offsetof(Elf32_Rela, r_info), offsetof(Elf32_Rela, r_addend) }; static int elf64_offsets[] = { 0, offsetof(Elf64_Dyn, d_tag), offsetof(Elf64_Dyn, d_un.d_ptr), offsetof(Elf64_Dyn, d_un.d_val), offsetof(Elf32_Ehdr, e_ident[EI_CLASS]), offsetof(Elf32_Ehdr, e_ident[EI_DATA]), offsetof(Elf32_Ehdr, e_ident[EI_OSABI]), offsetof(Elf64_Ehdr, e_type), offsetof(Elf64_Ehdr, e_machine), offsetof(Elf64_Ehdr, e_version), offsetof(Elf64_Ehdr, e_entry), offsetof(Elf64_Ehdr, e_phoff), offsetof(Elf64_Ehdr, e_shoff), offsetof(Elf64_Ehdr, e_flags), offsetof(Elf64_Ehdr, e_ehsize), offsetof(Elf64_Ehdr, e_phentsize), offsetof(Elf64_Ehdr, e_phnum), offsetof(Elf64_Ehdr, e_shentsize), offsetof(Elf64_Ehdr, e_shnum), offsetof(Elf64_Ehdr, e_shstrndx), offsetof(Elf_Note, n_namesz), offsetof(Elf_Note, n_descsz), offsetof(Elf_Note, n_type), offsetof(Elf64_Phdr, p_type), offsetof(Elf64_Phdr, p_offset), offsetof(Elf64_Phdr, p_vaddr), offsetof(Elf64_Phdr, p_paddr), offsetof(Elf64_Phdr, p_filesz), offsetof(Elf64_Phdr, p_memsz), offsetof(Elf64_Phdr, p_flags), offsetof(Elf64_Phdr, p_align), offsetof(Elf64_Shdr, sh_name), offsetof(Elf64_Shdr, sh_type), offsetof(Elf64_Shdr, sh_flags), offsetof(Elf64_Shdr, sh_addr), offsetof(Elf64_Shdr, sh_offset), offsetof(Elf64_Shdr, sh_size), offsetof(Elf64_Shdr, sh_link), offsetof(Elf64_Shdr, sh_info), offsetof(Elf64_Shdr, sh_addralign), offsetof(Elf64_Shdr, sh_entsize), offsetof(Elf64_Sym, st_name), offsetof(Elf64_Sym, st_value), offsetof(Elf64_Sym, st_size), offsetof(Elf64_Sym, st_info), offsetof(Elf64_Sym, st_shndx), offsetof(Elf64_Rel, r_offset), offsetof(Elf64_Rel, r_info), offsetof(Elf64_Rela, r_offset), offsetof(Elf64_Rela, r_info), offsetof(Elf64_Rela, r_addend) }; /* http://www.sco.com/developers/gabi/latest/ch5.dynamic.html#tag_encodings */ static const char * d_tags(u_int64_t tag) { switch (tag) { case 0: return "DT_NULL"; case 1: return "DT_NEEDED"; case 2: return "DT_PLTRELSZ"; case 3: return "DT_PLTGOT"; case 4: return "DT_HASH"; case 5: return "DT_STRTAB"; case 6: return "DT_SYMTAB"; case 7: return "DT_RELA"; case 8: return "DT_RELASZ"; case 9: return "DT_RELAENT"; case 10: return "DT_STRSZ"; case 11: return "DT_SYMENT"; case 12: return "DT_INIT"; case 13: return "DT_FINI"; case 14: return "DT_SONAME"; case 15: return "DT_RPATH"; case 16: return "DT_SYMBOLIC"; case 17: return "DT_REL"; case 18: return "DT_RELSZ"; case 19: return "DT_RELENT"; case 20: return "DT_PLTREL"; case 21: return "DT_DEBUG"; case 22: return "DT_TEXTREL"; case 23: return "DT_JMPREL"; case 24: return "DT_BIND_NOW"; case 25: return "DT_INIT_ARRAY"; case 26: return "DT_FINI_ARRAY"; case 27: return "DT_INIT_ARRAYSZ"; case 28: return "DT_FINI_ARRAYSZ"; case 29: return "DT_RUNPATH"; case 30: return "DT_FLAGS"; case 32: return "DT_PREINIT_ARRAY"; /* XXX: DT_ENCODING */ case 33: return "DT_PREINIT_ARRAYSZ"; /* 0x6000000D - 0x6ffff000 operating system-specific semantics */ case 0x6ffffdf5: return "DT_GNU_PRELINKED"; case 0x6ffffdf6: return "DT_GNU_CONFLICTSZ"; case 0x6ffffdf7: return "DT_GNU_LIBLISTSZ"; case 0x6ffffdf8: return "DT_SUNW_CHECKSUM"; case 0x6ffffdf9: return "DT_PLTPADSZ"; case 0x6ffffdfa: return "DT_MOVEENT"; case 0x6ffffdfb: return "DT_MOVESZ"; case 0x6ffffdfc: return "DT_FEATURE"; case 0x6ffffdfd: return "DT_POSFLAG_1"; case 0x6ffffdfe: return "DT_SYMINSZ"; case 0x6ffffdff: return "DT_SYMINENT (DT_VALRNGHI)"; case 0x6ffffe00: return "DT_ADDRRNGLO"; case 0x6ffffef8: return "DT_GNU_CONFLICT"; case 0x6ffffef9: return "DT_GNU_LIBLIST"; case 0x6ffffefa: return "DT_SUNW_CONFIG"; case 0x6ffffefb: return "DT_SUNW_DEPAUDIT"; case 0x6ffffefc: return "DT_SUNW_AUDIT"; case 0x6ffffefd: return "DT_SUNW_PLTPAD"; case 0x6ffffefe: return "DT_SUNW_MOVETAB"; case 0x6ffffeff: return "DT_SYMINFO (DT_ADDRRNGHI)"; case 0x6ffffff9: return "DT_RELACOUNT"; case 0x6ffffffa: return "DT_RELCOUNT"; case 0x6ffffffb: return "DT_FLAGS_1"; case 0x6ffffffc: return "DT_VERDEF"; case 0x6ffffffd: return "DT_VERDEFNUM"; case 0x6ffffffe: return "DT_VERNEED"; case 0x6fffffff: return "DT_VERNEEDNUM"; case 0x6ffffff0: return "DT_GNU_VERSYM"; /* 0x70000000 - 0x7fffffff processor-specific semantics */ case 0x70000000: return "DT_IA_64_PLT_RESERVE"; case 0x7ffffffd: return "DT_SUNW_AUXILIARY"; case 0x7ffffffe: return "DT_SUNW_USED"; case 0x7fffffff: return "DT_SUNW_FILTER"; default: return "ERROR: TAG NOT DEFINED"; } } static const char * e_machines(u_int mach) { static char machdesc[64]; switch (mach) { case EM_NONE: return "EM_NONE"; case EM_M32: return "EM_M32"; case EM_SPARC: return "EM_SPARC"; case EM_386: return "EM_386"; case EM_68K: return "EM_68K"; case EM_88K: return "EM_88K"; case EM_860: return "EM_860"; case EM_MIPS: return "EM_MIPS"; case EM_PPC: return "EM_PPC"; case EM_ARM: return "EM_ARM"; case EM_ALPHA: return "EM_ALPHA (legacy)"; case EM_SPARCV9:return "EM_SPARCV9"; case EM_IA_64: return "EM_IA_64"; case EM_X86_64: return "EM_X86_64"; } snprintf(machdesc, sizeof(machdesc), "(unknown machine) -- type 0x%x", mach); return (machdesc); } static const char *e_types[] = { "ET_NONE", "ET_REL", "ET_EXEC", "ET_DYN", "ET_CORE" }; static const char *ei_versions[] = { "EV_NONE", "EV_CURRENT" }; static const char *ei_classes[] = { "ELFCLASSNONE", "ELFCLASS32", "ELFCLASS64" }; static const char *ei_data[] = { "ELFDATANONE", "ELFDATA2LSB", "ELFDATA2MSB" }; static const char *ei_abis[] = { "ELFOSABI_SYSV", "ELFOSABI_HPUX", "ELFOSABI_NETBSD", "ELFOSABI_LINUX", "ELFOSABI_HURD", "ELFOSABI_86OPEN", "ELFOSABI_SOLARIS", "ELFOSABI_MONTEREY", "ELFOSABI_IRIX", "ELFOSABI_FREEBSD", "ELFOSABI_TRU64", "ELFOSABI_MODESTO", "ELFOSABI_OPENBSD" }; static const char *p_types[] = { "PT_NULL", "PT_LOAD", "PT_DYNAMIC", "PT_INTERP", "PT_NOTE", "PT_SHLIB", "PT_PHDR", "PT_TLS" }; static const char *p_flags[] = { "", "PF_X", "PF_W", "PF_X|PF_W", "PF_R", "PF_X|PF_R", "PF_W|PF_R", "PF_X|PF_W|PF_R" }; /* http://www.sco.com/developers/gabi/latest/ch4.sheader.html#sh_type */ static const char * sh_types(u_int64_t sht) { switch (sht) { case 0: return "SHT_NULL"; case 1: return "SHT_PROGBITS"; case 2: return "SHT_SYMTAB"; case 3: return "SHT_STRTAB"; case 4: return "SHT_RELA"; case 5: return "SHT_HASH"; case 6: return "SHT_DYNAMIC"; case 7: return "SHT_NOTE"; case 8: return "SHT_NOBITS"; case 9: return "SHT_REL"; case 10: return "SHT_SHLIB"; case 11: return "SHT_DYNSYM"; case 14: return "SHT_INIT_ARRAY"; case 15: return "SHT_FINI_ARRAY"; case 16: return "SHT_PREINIT_ARRAY"; case 17: return "SHT_GROUP"; case 18: return "SHT_SYMTAB_SHNDX"; /* 0x60000000 - 0x6fffffff operating system-specific semantics */ case 0x6ffffff0: return "XXX:VERSYM"; case 0x6ffffff7: return "SHT_GNU_LIBLIST"; case 0x6ffffffc: return "XXX:VERDEF"; case 0x6ffffffd: return "SHT_SUNW(GNU)_verdef"; case 0x6ffffffe: return "SHT_SUNW(GNU)_verneed"; case 0x6fffffff: return "SHT_SUNW(GNU)_versym"; /* 0x70000000 - 0x7fffffff processor-specific semantics */ case 0x70000000: return "SHT_IA_64_EXT"; case 0x70000001: return "SHT_IA_64_UNWIND"; case 0x7ffffffd: return "XXX:AUXILIARY"; case 0x7fffffff: return "XXX:FILTER"; /* 0x80000000 - 0xffffffff application programs */ default: return "ERROR: SHT NOT DEFINED"; } } static const char *sh_flags[] = { "", "SHF_WRITE", "SHF_ALLOC", "SHF_WRITE|SHF_ALLOC", "SHF_EXECINSTR", "SHF_WRITE|SHF_EXECINSTR", "SHF_ALLOC|SHF_EXECINSTR", "SHF_WRITE|SHF_ALLOC|SHF_EXECINSTR" }; static const char *st_types[] = { "STT_NOTYPE", "STT_OBJECT", "STT_FUNC", "STT_SECTION", "STT_FILE" }; static const char *st_bindings[] = { "STB_LOCAL", "STB_GLOBAL", "STB_WEAK" }; /* * The environment used by the parsing routines. */ struct _env { const char *dynstr; const char *shstrtab; const char *strtab; FILE *out; Elf32_Ehdr *e; struct elfdump_info *retval; }; typedef struct _env env_t; static u_int64_t elf_get_byte(Elf32_Ehdr *, const void *, elf_member_t); static u_int64_t elf_get_quarter(Elf32_Ehdr *, const void *, elf_member_t); //static u_int64_t elf_get_half(Elf32_Ehdr *, const void *, elf_member_t); static u_int64_t elf_get_word(Elf32_Ehdr *, const void *, elf_member_t); static u_int64_t elf_get_quad(Elf32_Ehdr *, const void *, elf_member_t); /* various print routines */ static void elf_print_ehdr(env_t *va); static void elf_print_phdr(env_t *va, const void *p); static void elf_print_shdr(env_t *va, const void *sh); static void elf_print_symtab(env_t *va, const void *sh, const char *str); static void elf_print_dynamic(env_t *va, const void *sh); static void elf_print_rel(env_t *va, const void *r); static void elf_print_rela(env_t *va, const void *ra); static void elf_print_interp(env_t *va, const void *p); static void elf_print_got(env_t *vae, const void *sh); static void elf_print_hash(env_t *va, const void *sh); static void elf_print_note(env_t *va, const void *sh); /* internal function doing the main processing */ static struct elfdump_info *elfdump_main(u_int flags, const char*file, const char *symbols, FILE *out); /* The externally visible function */ struct elfdump_info * elfdump(u_int flags, const char *file, const char *symbols) { return elfdump_main(flags, file, symbols, NULL); } /* return a pointer to the name for the section */ static const char * elf_name(env_t *va, const char *base) { return va->shstrtab + elf_get_word(va->e, base, SH_NAME); } #ifndef STANDALONE static void usage(void) { fprintf(stderr, "usage: elfdump -a | -cdeGhinprs [-w file] file\n"); exit(1); } int main(int ac, char **av) { u_int flags = 0; int ch; FILE *out = stdout; const char *symbols = NULL; while ((ch = getopt(ac, av, "acdeiGhnpqrsw:")) != -1) { switch (ch) { case 'a': flags = ED_ALL; break; case 'c': flags |= ED_SHDR; break; case 'd': flags |= ED_DYN; break; case 'e': flags |= ED_EHDR; break; case 'i': flags |= ED_INTERP; break; case 'G': flags |= ED_GOT; break; case 'h': flags |= ED_HASH; break; case 'n': flags |= ED_NOTE; break; case 'p': flags |= ED_PHDR; break; case 'q': out = NULL; /* quiet */ break; case 'r': flags |= ED_REL; break; case 's': flags |= ED_SYMTAB; break; case 'w': if ((out = fopen(optarg, "w")) == NULL) err(1, "%s", optarg); break; case '?': default: usage(); } } ac -= optind; av += optind; if (ac == 0 || flags == 0) usage(); if (ac > 1) symbols = av[1]; elfdump_main(flags, *av, symbols, out); return 0; } #endif struct elfdump_info * elfdump_main(u_int flags, const char *file, const char *symbols, FILE *out) { u_int64_t shentsize; u_int64_t shnum; u_int64_t shstrndx; u_int64_t offset; u_int64_t type; struct stat sb; Elf32_Ehdr *e; void *p; const char *sh; const void *v; int fd; int i; env_t env, *va = &env; bzero(va, sizeof(env)); va->out = out; /* prepare the storage for the match info */ if (symbols) { int ml, l = strlen(symbols) + 1; void *mem; char *syms; /* count number of entries to match */ for (i = 0, sh = symbols; (ml = strcspn(sh, ",|;")) ; i++) { sh += ml; if (*sh) sh++; } mem = calloc(1, l + sizeof(struct elfdump_info) + i * sizeof(struct elf_entry)); if (mem == NULL) err(1, "calloc failures"); va->retval = mem; va->retval->count = i; mem = (char *)mem + l + sizeof(struct elfdump_info) + i * sizeof(struct elf_entry); strcpy(mem, symbols); /* writable string */ for (i = 0, syms = mem; syms; i++) { va->retval->m[i].name =syms; strsep(&syms, ",;|"); } } if ((fd = open(file, O_RDONLY)) < 0 || fstat(fd, &sb) < 0) err(1, "%s", file); va->e = e = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); if (va->e == MAP_FAILED) err(1, NULL); if (!IS_ELF(*(Elf32_Ehdr *)e)) errx(1, "not an elf file"); shentsize = elf_get_quarter(e, e, E_SHENTSIZE); shnum = elf_get_quarter(e, e, E_SHNUM); shstrndx = elf_get_quarter(e, e, E_SHSTRNDX); p = (char *)e + elf_get_off(va->e, e, E_PHOFF); sh = (char *)e + elf_get_off(va->e, e, E_SHOFF); offset = elf_get_off(e, sh + shstrndx * shentsize, SH_OFFSET); va->shstrtab = (char *)e + offset; for (i = 0; (u_int64_t)i < shnum; i++) { const char *x = elf_name(va, sh + i * shentsize); offset = elf_get_off(e, sh + i * shentsize, SH_OFFSET); if (strcmp(x, ".strtab") == 0) va->strtab = (char *)e + offset; if (strcmp(x, ".dynstr") == 0) va->dynstr = (char *)e + offset; } if (va->out) { u_int64_t phnum = elf_get_quarter(e, e, E_PHNUM); u_int64_t phentsize = elf_get_quarter(e, e, E_PHENTSIZE); if (flags & ED_EHDR && va->out) elf_print_ehdr(va); if (flags & ED_PHDR && va->out) elf_print_phdr(va, p); if (flags & ED_SHDR && va->out) elf_print_shdr(va, sh); for (i = 0; (u_int64_t)i < phnum; i++) { v = (char *)p + i * phentsize; type = elf_get_word(e, v, P_TYPE); switch (type) { case PT_INTERP: if (flags & ED_INTERP) elf_print_interp(va, v); break; case PT_NULL: case PT_LOAD: case PT_DYNAMIC: case PT_NOTE: case PT_SHLIB: case PT_PHDR: break; } } } for (i = 0; (u_int64_t)i < shnum; i++) { v = sh + i * shentsize; type = elf_get_word(e, v, SH_TYPE); switch (type) { case SHT_SYMTAB: if (flags & ED_SYMTAB) elf_print_symtab(va, v, va->strtab); break; case SHT_DYNAMIC: if (flags & ED_DYN && va->out) elf_print_dynamic(va, v); break; case SHT_RELA: if (flags & ED_REL && va->out) elf_print_rela(va, v); break; case SHT_REL: if (flags & ED_REL && va->out) elf_print_rel(va, v); break; case SHT_NOTE: if (flags & ED_NOTE && va->out && strcmp(elf_name(va, v), ".note.ABI-tag") == 0) elf_print_note(va, v); break; case SHT_DYNSYM: if (flags & ED_SYMTAB) elf_print_symtab(va, v, va->dynstr); break; case SHT_PROGBITS: if (flags & ED_GOT && va->out && strcmp(elf_name(va, v), ".got") == 0) elf_print_got(va, v); break; case SHT_HASH: if (flags & ED_HASH && va->out) elf_print_hash(va, v); break; case SHT_NULL: case SHT_STRTAB: case SHT_NOBITS: case SHT_SHLIB: break; } } return va->retval; } static void elf_print_ehdr(env_t *va) { u_int64_t type; u_int64_t machine; u_int64_t version; u_int64_t entry; u_int64_t phoff; u_int64_t shoff; u_int64_t flags; u_int64_t ehsize; u_int64_t phentsize; u_int64_t phnum; u_int64_t shentsize; u_int64_t shnum; u_int64_t shstrndx; Elf32_Ehdr *e = va->e; FILE *out = va->out; type = elf_get_quarter(e, e, E_TYPE); machine = elf_get_quarter(e, e, E_MACHINE); version = elf_get_word(e, e, E_VERSION); entry = elf_get_addr(e, e, E_ENTRY); phoff = elf_get_off(e, e, E_PHOFF); shoff = elf_get_off(e, e, E_SHOFF); flags = elf_get_word(e, e, E_FLAGS); ehsize = elf_get_quarter(e, e, E_EHSIZE); phentsize = elf_get_quarter(e, e, E_PHENTSIZE); phnum = elf_get_quarter(e, e, E_PHNUM); shentsize = elf_get_quarter(e, e, E_SHENTSIZE); shnum = elf_get_quarter(e, e, E_SHNUM); shstrndx = elf_get_quarter(e, e, E_SHSTRNDX); fprintf(out, "\nelf header:\n\n"); fprintf(out, "\te_ident: %s %s %s\n", ei_classes[elf_get_byte(e, e, E_CLASS)], ei_data[elf_get_byte(e, e, E_DATA)], ei_abis[elf_get_byte(e, e, E_OSABI)]); fprintf(out, "\te_type: %s\n", e_types[type]); fprintf(out, "\te_machine: %s\n", e_machines(machine)); fprintf(out, "\te_version: %s\n", ei_versions[version]); fprintf(out, "\te_entry: %#jx\n", (intmax_t)entry); fprintf(out, "\te_phoff: %jd\n", (intmax_t)phoff); fprintf(out, "\te_shoff: %jd\n", (intmax_t)shoff); fprintf(out, "\te_flags: %jd\n", (intmax_t)flags); fprintf(out, "\te_ehsize: %jd\n", (intmax_t)ehsize); fprintf(out, "\te_phentsize: %jd\n", (intmax_t)phentsize); fprintf(out, "\te_phnum: %jd\n", (intmax_t)phnum); fprintf(out, "\te_shentsize: %jd\n", (intmax_t)shentsize); fprintf(out, "\te_shnum: %jd\n", (intmax_t)shnum); fprintf(out, "\te_shstrndx: %jd\n", (intmax_t)shstrndx); } static void elf_print_phdr(env_t *va, const void *p) { u_int64_t phentsize; u_int64_t phnum; u_int64_t type; u_int64_t offset; u_int64_t vaddr; u_int64_t paddr; u_int64_t filesz; u_int64_t memsz; u_int64_t flags; u_int64_t align; const void *v; int i; Elf32_Ehdr *e = va->e; FILE *out = va->out; phentsize = elf_get_quarter(e, e, E_PHENTSIZE); phnum = elf_get_quarter(e, e, E_PHNUM); fprintf(out, "\nprogram header:\n"); for (i = 0; (u_int64_t)i < phnum; i++) { v = (const char *)p + i * phentsize; type = elf_get_word(e, v, P_TYPE); offset = elf_get_off(e, v, P_OFFSET); vaddr = elf_get_addr(e, v, P_VADDR); paddr = elf_get_addr(e, v, P_PADDR); filesz = elf_get_size(e, v, P_FILESZ); memsz = elf_get_size(e, v, P_MEMSZ); flags = elf_get_word(e, v, P_FLAGS); align = elf_get_size(e, v, P_ALIGN); fprintf(out, "\nentry: %d\n", i); fprintf(out, "\tp_type: %s\n", p_types[type & 0x7]); fprintf(out, "\tp_offset: %jd\n", (intmax_t)offset); fprintf(out, "\tp_vaddr: %#jx\n", (intmax_t)vaddr); fprintf(out, "\tp_paddr: %#jx\n", (intmax_t)paddr); fprintf(out, "\tp_filesz: %jd\n", (intmax_t)filesz); fprintf(out, "\tp_memsz: %jd\n", (intmax_t)memsz); fprintf(out, "\tp_flags: %s\n", p_flags[flags]); fprintf(out, "\tp_align: %jd\n", (intmax_t)align); } } static void elf_print_shdr(env_t *va, const void *sh) { u_int64_t shentsize; u_int64_t shnum; u_int64_t type; u_int64_t flags; u_int64_t addr; u_int64_t offset; u_int64_t size; u_int64_t shlink; u_int64_t info; u_int64_t addralign; u_int64_t entsize; const void *v; int i; Elf32_Ehdr *e = va->e; FILE *out = va->out; shentsize = elf_get_quarter(e, e, E_SHENTSIZE); shnum = elf_get_quarter(e, e, E_SHNUM); fprintf(out, "\nsection header:\n"); for (i = 0; (u_int64_t)i < shnum; i++) { v = (const char *)sh + i * shentsize; type = elf_get_word(e, v, SH_TYPE); flags = elf_get_word(e, v, SH_FLAGS); addr = elf_get_addr(e, v, SH_ADDR); offset = elf_get_off(e, v, SH_OFFSET); size = elf_get_size(e, v, SH_SIZE); shlink = elf_get_word(e, v, SH_LINK); info = elf_get_word(e, v, SH_INFO); addralign = elf_get_size(e, v, SH_ADDRALIGN); entsize = elf_get_size(e, v, SH_ENTSIZE); fprintf(out, "\nentry: %d\n", i); fprintf(out, "\tsh_name: %s\n", elf_name(va, v)); fprintf(out, "\tsh_type: %s\n", sh_types(type)); fprintf(out, "\tsh_flags: %s\n", sh_flags[flags & 0x7]); fprintf(out, "\tsh_addr: %#jx\n", addr); fprintf(out, "\tsh_offset: %jd\n", (intmax_t)offset); fprintf(out, "\tsh_size: %jd\n", (intmax_t)size); fprintf(out, "\tsh_link: %jd\n", (intmax_t)shlink); fprintf(out, "\tsh_info: %jd\n", (intmax_t)info); fprintf(out, "\tsh_addralign: %jd\n", (intmax_t)addralign); fprintf(out, "\tsh_entsize: %jd\n", (intmax_t)entsize); } } static void elf_print_symtab(env_t *va, const void *sh, const char *str) { u_int64_t offset; u_int64_t entsize; u_int64_t size; u_int64_t name; u_int64_t value; u_int64_t info; u_int64_t shndx; void *st; int len; int i; Elf32_Ehdr *e = va->e; FILE *out = va->out; offset = elf_get_off(e, sh, SH_OFFSET); entsize = elf_get_size(e, sh, SH_ENTSIZE); size = elf_get_size(e, sh, SH_SIZE); len = size / entsize; if (out) fprintf(out, "\nsymbol table (%s):\n", elf_name(va, sh)); for (i = 0; i < len; i++) { st = (char *)e + offset + i * entsize; name = elf_get_word(e, st, ST_NAME); value = elf_get_addr(e, st, ST_VALUE); size = elf_get_size(e, st, ST_SIZE); info = elf_get_byte(e, st, ST_INFO); shndx = elf_get_quarter(e, st, ST_SHNDX); if (va->retval) { int j; for (j = 0; j < va->retval->count; j++) { if (!strcmp(str + name, va->retval->m[j].name)) break; /* found */ } if (j == va->retval->count) continue; /* not found */ va->retval->m[j].value = value; va->retval->m[j].size = size; fprintf(stderr, "entry %d %s 0x%x 0x%x\n", j, va->retval->m[j].name, (int)value, (int)size); } if (!out) continue; fprintf(out, "\nentry: %d\n", i); fprintf(out, "\tst_name: %s\n", str + name); fprintf(out, "\tst_value: %#jx\n", value); fprintf(out, "\tst_size: %jd\n", (intmax_t)size); fprintf(out, "\tst_info: %s %s\n", st_types[ELF32_ST_TYPE(info)], st_bindings[ELF32_ST_BIND(info)]); fprintf(out, "\tst_shndx: %jd\n", (intmax_t)shndx); } } static void elf_print_dynamic(env_t *va, const void *sh) { u_int64_t offset; u_int64_t entsize; u_int64_t size; int64_t tag; u_int64_t ptr; u_int64_t val; void *d; int i; Elf32_Ehdr *e = va->e; FILE *out = va->out; offset = elf_get_off(e, sh, SH_OFFSET); entsize = elf_get_size(e, sh, SH_ENTSIZE); size = elf_get_size(e, sh, SH_SIZE); fprintf(out, "\ndynamic:\n"); for (i = 0; (u_int64_t)i < size / entsize; i++) { d = (char *)e + offset + i * entsize; tag = elf_get_size(e, d, D_TAG); ptr = elf_get_size(e, d, D_PTR); val = elf_get_addr(e, d, D_VAL); fprintf(out, "\nentry: %d\n", i); fprintf(out, "\td_tag: %s\n", d_tags(tag)); switch (tag) { case DT_NEEDED: case DT_SONAME: case DT_RPATH: fprintf(out, "\td_val: %s\n", va->dynstr + val); break; case DT_PLTRELSZ: case DT_RELA: case DT_RELASZ: case DT_RELAENT: case DT_STRSZ: case DT_SYMENT: case DT_RELSZ: case DT_RELENT: case DT_PLTREL: fprintf(out, "\td_val: %jd\n", (intmax_t)val); break; case DT_PLTGOT: case DT_HASH: case DT_STRTAB: case DT_SYMTAB: case DT_INIT: case DT_FINI: case DT_REL: case DT_JMPREL: fprintf(out, "\td_ptr: %#jx\n", ptr); break; case DT_NULL: case DT_SYMBOLIC: case DT_DEBUG: case DT_TEXTREL: break; } } } static void elf_print_rela(env_t *va, const void *sh) { u_int64_t offset; u_int64_t entsize; u_int64_t size; u_int64_t info; int64_t addend; void *ra; void *v; int i; Elf32_Ehdr *e = va->e; FILE *out = va->out; offset = elf_get_off(e, sh, SH_OFFSET); entsize = elf_get_size(e, sh, SH_ENTSIZE); size = elf_get_size(e, sh, SH_SIZE); v = (char *)e + offset; fprintf(out, "\nrelocation with addend (%s):\n", elf_name(va, sh)); for (i = 0; (u_int64_t)i < size / entsize; i++) { ra = (char *)v + i * entsize; offset = elf_get_addr(e, ra, RA_OFFSET); info = elf_get_word(e, ra, RA_INFO); addend = elf_get_off(e, ra, RA_ADDEND); fprintf(out, "\nentry: %d\n", i); fprintf(out, "\tr_offset: %#jx\n", offset); fprintf(out, "\tr_info: %jd\n", (intmax_t)info); fprintf(out, "\tr_addend: %jd\n", (intmax_t)addend); } } static void elf_print_rel(env_t *va, const void *sh) { u_int64_t offset; u_int64_t entsize; u_int64_t size; u_int64_t info; const char *r; const char *v; int i; Elf32_Ehdr *e = va->e; FILE *out = va->out; entsize = elf_get_size(e, sh, SH_ENTSIZE); size = elf_get_size(e, sh, SH_SIZE); v = (const char *)e + elf_get_off(e, sh, SH_OFFSET); fprintf(out, "\nrelocation (%s):\n", elf_name(va, sh)); for (i = 0; (u_int64_t)i < size / entsize; i++) { r = v + i * entsize; offset = elf_get_addr(e, r, R_OFFSET); info = elf_get_word(e, r, R_INFO); fprintf(out, "\nentry: %d\n", i); fprintf(out, "\tr_offset: %#jx\n", offset); fprintf(out, "\tr_info: %jd\n", (intmax_t)info); } } static void elf_print_interp(env_t *va, const void *p) { fprintf(va->out, "\ninterp:\n\t%s\n", (const char *)va->e + elf_get_off(va->e, p, P_OFFSET)); } static void elf_print_got(env_t *va, const void *sh) { u_int64_t addralign; u_int64_t size; u_int64_t addr; const char *v; int i; addralign = elf_get_size(va->e, sh, SH_ADDRALIGN); size = elf_get_size(va->e, sh, SH_SIZE); v = (char *)va->e + elf_get_off(va->e, sh, SH_OFFSET); fprintf(va->out, "\nglobal offset table:\n"); for (i = 0; (u_int64_t)i < size / addralign; i++) { addr = elf_get_addr(va->e, v + i * addralign, 0); fprintf(va->out, "\nentry: %d\n\t%#jx\n", i, addr); } } static void elf_print_hash(env_t *va __unused, const void *sh __unused) { } static void elf_print_note(env_t *va, const void *sh) { const char *n, *lim; fprintf(va->out, "\nnote (%s):\n", elf_name(va, sh)); n = (const char *)va->e + elf_get_off(va->e, sh, SH_OFFSET); lim = n + elf_get_size(va->e, sh, SH_SIZE); while (n < lim) { u_int32_t namesz = elf_get_word(va->e, n, N_NAMESZ); u_int32_t descsz = elf_get_word(va->e, n, N_DESCSZ); // u_int_32_t type = elf_get_word(va->e, n, N_TYPE); const char *s = n + sizeof(Elf_Note); u_int32_t desc = elf_get_word(va->e, s + namesz, 0); fprintf(va->out, "\t%s %d\n", s, desc); n += sizeof(Elf_Note) + namesz + descsz; } } /* * return entries from the elf header 1..8 bytes long. * The value returned is always an u_int64_t */ static u_int64_t elf_get_byte(Elf32_Ehdr *e, const void *base, elf_member_t member) { u_int64_t val; val = 0; switch (e->e_ident[EI_CLASS]) { case ELFCLASS32: val = ((const char *)base)[elf32_offsets[member]]; break; case ELFCLASS64: val = ((const char *)base)[elf64_offsets[member]]; break; case ELFCLASSNONE: errx(1, "invalid class"); } return val; } static u_int64_t elf_get_quarter(Elf32_Ehdr *e, const void *base, elf_member_t member) { u_int64_t val; val = 0; switch (e->e_ident[EI_CLASS]) { case ELFCLASS32: base = (const char *)base + elf32_offsets[member]; switch (e->e_ident[EI_DATA]) { case ELFDATA2MSB: val = be16dec(base); break; case ELFDATA2LSB: val = le16dec(base); break; case ELFDATANONE: errx(1, "invalid data format"); } break; case ELFCLASS64: base = (const char *)base + elf64_offsets[member]; switch (e->e_ident[EI_DATA]) { case ELFDATA2MSB: val = be16dec(base); break; case ELFDATA2LSB: val = le16dec(base); break; case ELFDATANONE: errx(1, "invalid data format"); } break; case ELFCLASSNONE: errx(1, "invalid class"); } return val; } #if 0 static u_int64_t elf_get_half(Elf32_Ehdr *e, void *base, elf_member_t member) { u_int64_t val; val = 0; switch (e->e_ident[EI_CLASS]) { case ELFCLASS32: base = (char *)base + elf32_offsets[member]; switch (e->e_ident[EI_DATA]) { case ELFDATA2MSB: val = be16dec(base); break; case ELFDATA2LSB: val = le16dec(base); break; case ELFDATANONE: errx(1, "invalid data format"); } break; case ELFCLASS64: base = (char *)base + elf64_offsets[member]; switch (e->e_ident[EI_DATA]) { case ELFDATA2MSB: val = be32dec(base); break; case ELFDATA2LSB: val = le32dec(base); break; case ELFDATANONE: errx(1, "invalid data format"); } break; case ELFCLASSNONE: errx(1, "invalid class"); } return val; } #endif static u_int64_t elf_get_word(Elf32_Ehdr *e, const void *base, elf_member_t member) { u_int64_t val; val = 0; switch (e->e_ident[EI_CLASS]) { case ELFCLASS32: base = (const char *)base + elf32_offsets[member]; switch (e->e_ident[EI_DATA]) { case ELFDATA2MSB: val = be32dec(base); break; case ELFDATA2LSB: val = le32dec(base); break; case ELFDATANONE: errx(1, "invalid data format"); } break; case ELFCLASS64: base = (const char *)base + elf64_offsets[member]; switch (e->e_ident[EI_DATA]) { case ELFDATA2MSB: val = be32dec(base); break; case ELFDATA2LSB: val = le32dec(base); break; case ELFDATANONE: errx(1, "invalid data format"); } break; case ELFCLASSNONE: errx(1, "invalid class"); } return val; } static u_int64_t elf_get_quad(Elf32_Ehdr *e, const void *base, elf_member_t member) { u_int64_t val; val = 0; switch (e->e_ident[EI_CLASS]) { case ELFCLASS32: base = (const char *)base + elf32_offsets[member]; switch (e->e_ident[EI_DATA]) { case ELFDATA2MSB: val = be32dec(base); break; case ELFDATA2LSB: val = le32dec(base); break; case ELFDATANONE: errx(1, "invalid data format"); } break; case ELFCLASS64: base = (const char *)base + elf64_offsets[member]; switch (e->e_ident[EI_DATA]) { case ELFDATA2MSB: val = be64dec(base); break; case ELFDATA2LSB: val = le64dec(base); break; case ELFDATANONE: errx(1, "invalid data format"); } break; case ELFCLASSNONE: errx(1, "invalid class"); } return val; } kldpatch/elfdump.h000644 000423 000000 00000001361 11131471254 014713 0ustar00luigiwheel000000 000000 /* * The elfdump function and the return value */ struct elf_entry { const char *name; u_int64_t value; u_int64_t size; }; /* * The return value is a struct with appeneded the storage for strings * and the matched entries. It is allocated by the function and must be * freebd by the caller. */ struct elfdump_info { int count; struct elf_entry m[0]; /* 'count' entries */ }; /* flags values */ #define ED_DYN (1<<0) #define ED_EHDR (1<<1) #define ED_GOT (1<<2) #define ED_HASH (1<<3) #define ED_INTERP (1<<4) #define ED_NOTE (1<<5) #define ED_PHDR (1<<6) #define ED_REL (1<<7) #define ED_SHDR (1<<8) #define ED_SYMTAB (1<<9) #define ED_ALL ((1<<10)-1) struct elfdump_info *elfdump(u_int flags, const char* fn, const char *syms); kldpatch/kldpatch.8000644 000423 000000 00000016224 11132114671 014773 0ustar00luigiwheel000000 000000 .\" Copyright (c) 2009 Luigi Rizzo .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $FreeBSD: $ .\" .Dd January 1, 2009 .Dt KLDPATCH 8 .Os .Sh NAME .Nm kldpatch .Nd print/modify device/quirk tables in kernel modules .Sh SYNOPSIS .Nm .Op Fl v .Op Fl m Ar file .Op Fl t Ar file | format .Ar module_name table_name Op @offset Op args ... .Sh DESCRIPTION The .Nm utility can print or alter the content of device/quirk tables in kernel modules. These tables are generally used to identify devices, and possibly apply specific quirks to enable/disable certain features. .Pp .Nm is especially useful to let the kernel recognise a new device without rebooting and rebuilding/reinstalling kernel or modules, see Section .Sx EXAMPLES for some examples of use. .Pp .Sh OPTIONS .Pp The following options and arguments can be used: .Bl -tag -width indent .It Fl v Verbose mode, print some debugging info. .It Fl m Ar file Patch the module specified as an argument instead of the in-memory copy. Use this option when you want to make the change persistent across reboots. .It Fl t Ar file | format Specify the name and structure of descriptor tables that can be manipulated by .Nm . The argument can be a filename, or the description itself, as documented in Section .Sx MODULE DESCRIPTION . .It module_name table_name The module and table that we want to print/modify. Both values are compared against the content of the description file until a match is found. A value of ``-'' in either parameter indicates a wildcard. .It @offset Specify the entry in the quirk table that we want to list or modify. Values start from 0, and negative offsets can be used to count from the end of the table, with @-1 indicating the last element in the table, @-2 the previous one, and so on. .Pp If @offset is not specified, then .Nm operates in read mode and lists the full content of the table. .It args The values to be written to the table. If they are not specified, .Nm operates in read mode and prints the entry specified with the @offset argument. If they are present, they are checked against the description of the matching module/table and changes are applied to the memory or file version of the table, as required. .Pp .Sh MODULE DESCRIPTION .Nm relies on a list describing which modules and tables (associated to kernel symbols) it can work on, and what is the format of the records in each table. .Pp By default .Nm uses an internal list, which can be overridden from the command line using the .Fl t option followed by either a file name, or by immediate text. .Pp In write mode, .Nm checks that the arguments (module and table name, offset, parameters formats) are compatible with the structure indicated in the description, and also with the content of the module itself. .Pp The file (or text) containing the module description has a simple text format with one line per table. The `#' character is a comment delimiter, and can appear anywhere in the text. Each valid line must contain the following fields: .Bd -offset indent .Cm module_name .Cm symbol_name .Cm entry_format Op entry_format ... .Ed .Pp Each .Cm entry_format describes one field in the table, and it is made of a number and/or a character specifying the field size and format, followed by an optional ':' and a word describing the field itself. .Pp Supported formats are: .Bl -tag -width indent .It 1 8-bit unsigned; .It 2 | 2l | 2b 16-bit unsigned in host, little-endian or big-endian format; .It 4 | 4l | 4b 32-bit unsigned in host, little-endian or big-endian format; .It 8 | 8l | 8b 64-bit unsigned in host, little-endian or big-endian format; .It p a pointer; .It s a null-terminated string; .El .Pp The optional description is used as a header when listing the content of a table. .Pp .Ss EXAMPLE MODULE DESCRIPTION .Pp The following is an example of a file containing module descriptions. .Pp .Bd -literal -offset indent umass.ko umass_devdescrs 4:vendor 4:product 4:rev 2:proto 2:quirks uscanner.ko uscanner_devs 2:vendor 2:device 4:flags # comment if_nfe.ko nfe_devs 2:vendor 2:device s:name if_re.ko re_devs 2:vendor 2:device 4:type s:name .Ed .Pp .Sh EXAMPLES .Pp Spme examples of use: .Pp .Bl -tag -width mm .Pp .It kldpatch umass.ko - @0 0x4050 0x4a5 0x0101 0x4200 .Pp tell the kernel (actually, the umass module) to use flags "UMASS_PROTO_SCSI | UMASS_PROTO_BBB" and quirks "WRONG_CSWSIG | NO_SYNCHRONIZE_CACHE" for a certain GSM phone that implements the umass interface; .Pp .It kldpatch uscanner.ko - @0 0x04b8 0x084a 0 .Pp let uscanner.ko recognise a newly introduced MFP scanner device. .Pp .It kldpatch -m /boot/kernel/uscanner.ko uscanner.ko - @0 0x04b8 0x084a 0 .Pp as above, but patch the actual file instead of the in-memory copy of the module, so the effects will be visible when the module is loaded next. .Pp .It "kmodpatch if_re -" .Pp print all entries in module if_re. .Pp .It "kmodpatch umass - @-1" .Pp print the last entry in module umass. .Pp .Pp .It "kmodpatch uscanner.ko - @-1 0x04b8 0x0839 0" .Pp set the last entry in module uscanner.ko .El .Sh WARNINGS .Pp .Nm requires root privileges even to just read the content of the kernel tables. .Pp In write mode, the use of .Nm is as dangerous as accessing /dev/kmem in write mode, even though .Nm does some amount of checking to make sure that the arguments passed are reasonable. To this purpose, it is fundamental that the table descriptions used by .Nm match the actual structure of the kernel tables. There is no automatic way to extract this info. .Pp .Sh BUGS .Pp The algorithm used to navigate ELF files (.ko modules and the kernel) when using the .Fl m option makes some assumptions on the structure of the files which should be verified at runtime. .Pp .Sh SEE ALSO .Xr kldconfig 8 , .Xr kldload 8 , .Xr kldstat 8 , .Xr kldunload 8 .Sh HISTORY The .Nm utility first appeared in .Fx 8.0 inspired by a similar feature present on Linux. .Sh AUTHORS .An -nosplit The .Nm utility was designed and implemented by .An Luigi Rizzo Aq luigi@FreeBSD.org . kldpatch/kldpatch.c000644 000423 000000 00000046462 11132403144 015051 0ustar00luigiwheel000000 000000 /* * Copyright (C) 2009 Luigi Rizzo * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: kldpatch.c 1047 2009-01-11 12:08:03Z luigi $ * */ /* * DESCRIPTION Several USB, PCI and other device drivers require the kernel to have the exact product/vendor IDs (and sometimes matching quirks) in its internal tables, to identify the device and possibly enable/disable specific features. As an example, this is the case for many "umass" devices, where implementation of standard features varies, and for uscanner devices, where there is no specific device class info so the driver has to do an exact match using a lookup table. Apart from rebuilding the kernel or module, the device table can be altered with external tools, either on-disk or in memory. This program can be used to read or patch the in-memory kernel structures used to store the match/quirks tables. The program is also able to patch the file containing the module or kernel, so the change can be made persistent across reboots. When working on the live kernel, the program uses kldfind and kldsym to locate the module, address and size for the desired data structure, and then kvm_open/kvm_read/kvm_write to read and possibly update the table. When working on a file, the location of the symbol is determined using a custom version of the elfdump code. Patching a binary module is clearly dangerous, so we try to make the process a little less risky by putting some additional controls in this program. In particular, kmodpatch constains a table of the form module symbol record_format... defining which modules/symbols can be patched and the structure of each entry. As an example: uscanner.ko uscanner_devs 2:vendor 2:product 4:flags umass.ko umass_devdescrs 4 4 4 2 2 if_sis.ko sis_devs 2 2 s means that + module uscanner.ko has a device table called uscanner_devs where each record is made of 3 numeric fields of size 2 2 4 bytes, whose "names" (used to print the content of the table) are "vendor", "product" and "flags" respectively; + module umass.ko has a device table called umass_devdescr with records of size 4 4 4 2 2 bytes; + module if_sis.ko has a device table called sis_devs where each record has two numeric entries of 2 bytes each, plus a string pointer; Additional format specifiers can be used for little or big-endian numbers, generic pointers and so on. Furthermore, comments can be added to the fields. The program accepts commands of the form kldpatch if_sis.ko sis_devs write @3 0x1234 0x5678 "temporary entry" which means 'patch record 3 of table sis_devs with the values specified". The program will make sure that operands are of the required size, and that the table entry is within the table. */ /* * The table of allowed modules and symbols */ static const char _default_table[] ="\ umass.ko umass_devdescrs 4:vendor 4:product 4:rev 2:proto 2:quirks\n\ uscanner.ko uscanner_devs 2:vendor 2:device 4:flags #comment\n\ if_nfe.ko nfe_devs 2:vendor 2:device s:name \n\ if_re.ko re_devs 2:vendor 2:device 4:type s:name \n\ "; #include #include #include #include /* read() */ #include #include #include #include #include #include #include #include "elfdump.h" int verbose = 0; enum _ty_tags { TAG_LEN_MASK = 0x00ffffff, TAG_TYPE_MASK = 0x0f000000, TAG_HOST = 0, TAG_BE = 0x01000000, TAG_LE = 0x02000000, TAG_PTR = 0x03000000, TAG_STRING = 0x04000000, }; #define ARGCOUNT 16 /* how many arguments at most */ /* * A structure describing the matching entry */ struct match_info { const char *mod_name; const char *symbol; const char *filename; /* patch a file, not the memory */ void *dump_addr; /* dump address */ int data_ofs; /* read/write position */ /* the following two are looked up from the kernel */ struct kld_sym_lookup sym; kvm_t *k; /* kvm_open result */ const char *match_buf; /* copy of the description line */ int reclen; /* length of a single record */ int argcount; /* number of arguments */ const char *argdesc[ARGCOUNT]; /* description strings */ int args[ARGCOUNT]; /* ids for arguments */ /* The low 24 bit represent the length, * the high 8 bits represent various flags * .... .... * |''-- 00 = host order * | 01 = big endian * | 10 = little endian * | 11 = pointer * `---- 0 ordinary pointer * 1 string */ }; enum errcode { E_NO_ARGS, E_INVALID_MODULE, E_NO_SYMBOL, E_NO_ROOT, E_NO_MEMORY, E_NO_OFS, E_BAD_OFS, E_BAD_ARGS, E_UNSUPPORTED, E_WRITE_ERROR, E_FILE, }; /* print an error message */ static void help(enum errcode err, struct match_info *m) { switch (err) { case E_NO_ARGS: fprintf(stderr, "Need at least two arguments\n"); break; case E_INVALID_MODULE: fprintf(stderr, "Invalid module name %s\n", m->mod_name); break; case E_NO_SYMBOL: fprintf(stderr, "Symbol %s not found in module %s\n", m->symbol, m->mod_name); break; case E_NO_ROOT: fprintf(stderr, "Must be root to use kvm_read/kvm_write\n"); break; case E_NO_OFS: fprintf(stderr, "Missing write offset\n"); break; case E_BAD_OFS: fprintf(stderr, "Bad write offset\n"); break; case E_NO_MEMORY: perror("malloc failure"); break; case E_BAD_ARGS: fprintf(stderr, "Invalid argument length\n"); break; case E_UNSUPPORTED: fprintf(stderr, "Operand size unsupported\n"); break; case E_WRITE_ERROR: fprintf(stderr, "Error writing to kvm\n"); break; case E_FILE: fprintf(stderr, "Error opening/reading file\n"); break; } exit(1); } /* wrapper around calloc: try an allocation, exit on error */ static void * my_calloc(int size) { void *res = calloc(1, size); if (!res) help(E_NO_MEMORY, NULL); return res; } /* print the record */ static void table_dump(unsigned char *buf, struct match_info *m) { int i, l; int n; int n_args = m->sym.symsize / m->reclen; if (verbose) fprintf(stderr, "-- %d records of size %d, %d fields\n", n_args, m->reclen, m->argcount); /* print headers */ fprintf(stderr, "Index "); for (i = 0; i < m->argcount; i++) { l = m->args[i] & TAG_LEN_MASK; l = 2*l + 2; /* field length */ fprintf(stderr, "%*s ", l, m->argdesc[i] ? m->argdesc[i] : ""); } fprintf(stderr, "\n"); for (n = 0; n < n_args; n++) { const unsigned char *p = buf + n * m->reclen; if (m->dump_addr && m->dump_addr != p) { continue; } fprintf(stderr, "%4d: ", n); for (i = 0; i < m->argcount; i++) { uint16_t d16; uint32_t d32; uint64_t d64; int t = m->args[i] & TAG_TYPE_MASK; l = m->args[i] & TAG_LEN_MASK; if (t == TAG_STRING) { const char *s; char str[65]; bzero(str, sizeof(str)); bcopy(p, &s, l); kvm_read(m->k, (unsigned long)s, str, sizeof(str) - 1); fprintf(stderr, "%s ", str); continue; } switch (l) { default: fprintf(stderr, "arg size not recognised %d\n", l); break; case 1: fprintf(stderr, "0x%02x ", *(const unsigned char *)p); break; case 2: bcopy(p, &d16, l); if (t & TAG_BE) d16 = be16toh(d16); else if (t & TAG_LE) d16 = le16toh(d16); fprintf(stderr, "0x%04x ", d16); break; case 4: bcopy(p, &d32, l); if (t & TAG_BE) d32 = be32toh(d32); else if (t & TAG_LE) d32 = le32toh(d32); fprintf(stderr, "0x%08x ", d32); break; case 8: bcopy(p, &d64, l); if (t & TAG_BE) d64 = be64toh(d64); else if (t & TAG_LE) d64 = le64toh(d64); fprintf(stderr, "0x%016jx ", d64); break; } p += l; } fprintf(stderr, "\n"); } #if 0 /* hexdump code */ for (i = 0; i < m->sym.symsize; i++) { if ( (i & 15) == 0) printf("\n%04x: ", i); printf("%02x ", buf[i]); if ( (i & 15) == 7) printf(" "); } printf("\n"); #endif } /* * Match name and/or symbol with the content of the table. * All input and output fields are in *m. * Return the record filled with info from the matching entry. * Use NULL, "" or "-" to use a wildcard on name or symbol. * Retval is 0 on failure, 1 on match */ static int match_table(const char *table, struct match_info *m) { const char *c = NULL, *s; char *s1; int ll = 0; int i; int mod_len; /* length of module name */ int sym_len; /* length of symbol name */ if (!table || !m) return 0; /* store the length, set to 0 for wildcard */ mod_len = m->mod_name ? strlen(m->mod_name) : 0; if (mod_len == 1 && m->mod_name[0] == '-') mod_len = 0; sym_len = m->symbol ? strlen(m->symbol) : 0; if (sym_len == 1 && m->symbol[0] == '-') sym_len = 0; /* match module and symbol. On return, s and c point to the matching * module and symbol name, lengths are in mod_len and sym_len. */ for (s = table; *s; s += ll + 1) { int ln, ls; s += strspn(s, " \t"); /* skip blanks */ ll = strcspn(s, "\n"); /* remaining line length */ if (verbose) fprintf(stderr, "Analysing: [%.*s]\n", ll, s); if (*s == '\0') /* table complete */ break; ln = strcspn(s, " \t#\n"); /* module name length in table */ if (mod_len == 0) { /* no module name, assume a match */ } else if (mod_len == ln && !strncmp(s, m->mod_name, mod_len)) { /* exact match on module name */ } else if (ln > 3 && mod_len == ln - 3 && !strncmp(s, m->mod_name, mod_len) && !strncmp(s + ln - 3, ".ko", 3) ) { /* match without .ko suffix */ } else { continue; /* no match */ } c = s + ln; /* skip the module name */ c += strspn(c, " \t"); /* skip separators */ ls = strcspn(c, " \t#\n"); /* length of symbol name */ if (*c == '\0') continue; /* no symbol, fail at next cycle */ if (!sym_len || (ls == sym_len && !strncmp(c, m->symbol, sym_len))) { sym_len = ls; mod_len = ln; break; } } if (*s == '\0') return 0; /* * make a copy of the matching line to fetch module and symbol * names and comment fields. */ m->match_buf = s1 = my_calloc(ll+1); strncpy(s1, s, ll); if (verbose) { fprintf(stderr, "found: [%.*s]\n", ll, s); fprintf(stderr, "copy: [%s]\n", s1); fprintf(stderr, " name %.*s symbol %.*s\n", mod_len, s, sym_len, c); } /* store the matching values in the return struct */ m->mod_name = s1; s1[mod_len] = '\0'; /* truncate module name */ m->symbol = s1 + (c - s); s1[c + sym_len - s] = '\0'; /* truncate symbol name */ s1 += c + sym_len - s + 1; s1 += strspn(s1, " \t"); /* now scan the argument list extracting type, size and description */ m->reclen = 0; /* total record length so far */ ll = sizeof(m->args)/sizeof(m->args[0]); /* size of argument list */ for (i=0; !index("#\n", *s1) && i < ll; i++) { char cc, *endp; m->args[i] = strtoul(s1, &endp, 0); cc = *endp++; switch (cc) { default: endp--; /* not recognised */ break; case 'i': m->args[i] = sizeof(int); break; case 'b': m->args[i] |= TAG_BE; break; case 'l': m->args[i] |= TAG_LE; break; case 'p': case 's': if (m->args[i] == 0) m->args[i] = sizeof(void *); m->args[i] |= (cc == 's') ? TAG_STRING : TAG_PTR; break; } if (*endp == ':') /* comment up to the next space */ m->argdesc[i] = endp + 1; m->reclen += m->args[i] & TAG_LEN_MASK; if (verbose) fprintf(stderr, "found entry 0x%x\n", m->args[i]); s1 += strcspn(s1, " \t\n#"); /* skip descriptor */ if (*s1 == '\n') break; if (*s1) *s1++ = '\0'; /* truncate the string */ s1 += strspn(s1, " \t"); /* skip whitespace */ } *s1 = '\0'; /* truncate the string */ m->argcount = i; if (verbose) fprintf(stderr, "found: name [%s] sym [%s] reclen %d argcount %d\n", m->mod_name, m->symbol, m->reclen, m->argcount); return 1; } /* * read and possibly write into the module */ static int do_rw(struct match_info *m, int argc, char *argv[]) { unsigned char *buf, *srcbuf; int fd = -1, i, l, bufp; if (verbose) fprintf(stderr, "kldfind %s %s\n", m->mod_name, m->symbol); m->sym.version = sizeof(struct kld_sym_lookup); m->sym.symname = (char *)(int)m->symbol; /* XXX why not const ? */ m->sym.symvalue = 0; m->sym.symsize = 0; if (m->filename) { /* work on file, find offset and size */ /* * XXX working notes below. * for a .ko file must load at PT_LOAD and p_offset * entry 0 or entry 1 ? * * for a kernel... entry 2 in the prog header * * for a .o file look at offset on .rodata */ struct elfdump_info *res; char *syms; asprintf(&syms, "%s,kernload,kernbase", m->sym.symname); if (!syms) help(E_FILE, NULL); res = elfdump(ED_SYMTAB, m->filename, syms); free(syms); if (!res) help(E_FILE, NULL); if (res->m[0].size == 0) /* not found */ help(E_NO_SYMBOL, m); m->sym.symsize = res->m[0].size; m->sym.symvalue = res->m[0].value; if (res->m[1].value) m->sym.symvalue -= res->m[1].value; if (res->m[2].value) m->sym.symvalue -= res->m[2].value; } else { /* work on memory, find offset and size */ int kid = kldfind(m->mod_name); if (kid == -1) { fprintf(stderr, "module %s not found, try kernel\n", m->mod_name); kid = 0; } if (kldsym(kid, KLDSYM_LOOKUP, &m->sym)) help(E_NO_SYMBOL, m); } if (verbose) fprintf(stderr, "found %s at 0x%lx len %d\n", m->symbol, m->sym.symvalue, (int)m->sym.symsize); if (m->sym.symsize % m->reclen != 0) fprintf(stderr, "struct size %d not multiple of reclen %d\n", (int)m->sym.symsize, m->reclen); if (argc > 3) { /* compute the data offset, check bounds */ m->data_ofs *= m->reclen; if (m->data_ofs < 0) m->data_ofs = m->sym.symsize + m->data_ofs; if (verbose) fprintf(stderr, " data ofs %d\n", m->data_ofs); if (m->data_ofs < 0 || m->data_ofs > (int)m->sym.symsize - m->reclen) help(E_BAD_OFS, m); } buf = my_calloc(m->sym.symsize); if (m->filename) { /* work on the file */ fd = open(m->filename, (argc <= 4) ? O_RDONLY : O_RDWR); if (fd < 0) help(E_NO_ROOT, m); if (lseek(fd, m->sym.symvalue, SEEK_SET) < 0 || read(fd, buf, m->sym.symsize) != (int)m->sym.symsize) help(E_BAD_OFS, m); } else { /* work on memory */ m->k = kvm_open(NULL, NULL, NULL, (argc <= 4) ? O_RDONLY : O_RDWR, "kldpatch"); if (m->k == NULL || kvm_read(m->k, m->sym.symvalue, buf, m->sym.symsize) != (int)m->sym.symsize) help(E_NO_ROOT, m); } if (verbose) fprintf(stderr, "data read successfully\n"); if (argc <= 4) { /* read mode */ m->dump_addr = (argc != 4) ? NULL : buf + m->data_ofs; table_dump(buf, m); return 0; } /* * allocate the write buffer, dup the existing content and then * copy arguments passed from the command line */ srcbuf = my_calloc(m->reclen); bcopy(buf + m->data_ofs, srcbuf, m->reclen); /* prepare to copy in memory in endian-safe way */ for (i = 0, bufp = 0; i < m->argcount && i+4 < argc; i++, bufp += l) { uint64_t d64; uint32_t d32; uint16_t d16; l = m->args[i] & TAG_LEN_MASK; if ( m->args[i] & (TAG_PTR | TAG_STRING) ) { /* do not overwrite strings or pointers */ fprintf(stderr, "skip string/pointer %d:%s\n", i, argv[i+4]); continue; } switch (l) { default: fprintf(stderr, "unsupported format 0x%x, exit\n", m->args[i]); exit(1); case 8: d64 = strtoull(argv[i+4], NULL, 0); if (m->args[i] & TAG_LE) d32 = htole64(d64); else if (m->args[i] & TAG_BE) d64 = htobe32(d64); bcopy(&d64, srcbuf+bufp, sizeof(d64)); break; case 4: d32 = strtoul(argv[i+4], NULL, 0); if (m->args[i] & TAG_LE) d32 = htole32(d32); else if (m->args[i] & TAG_BE) d32 = htobe32(d32); bcopy(&d32, srcbuf+bufp, sizeof(d32)); break; case 2: d16 = strtoul(argv[i+4], NULL, 0); if (m->args[i] & TAG_LE) d16 = htole16(d16); else if (m->args[i] & TAG_BE) d16 = htobe16(d16); bcopy(&d16, srcbuf+bufp, sizeof(d16)); break; case 1: d16 = strtoul(argv[i+4], NULL, 0); srcbuf[bufp] = d16 & 0xff; break; } } if (bufp != m->reclen) help(E_BAD_ARGS, m); if (m->filename) { /* write to file */ if (lseek(fd, m->sym.symvalue + m->data_ofs, SEEK_SET) < 0 || write(fd, srcbuf, m->reclen) != (int)m->reclen) help(E_WRITE_ERROR, m); if (lseek(fd, m->sym.symvalue, SEEK_SET) < 0 || read(fd, buf, m->sym.symsize) != (int)m->sym.symsize) help(E_NO_ROOT, m); } else { /* write to memory */ if (kvm_write(m->k, m->sym.symvalue + m->data_ofs, srcbuf, m->reclen) != m->reclen) help(E_WRITE_ERROR, m); if (kvm_read(m->k, m->sym.symvalue, buf, m->sym.symsize) != (int)m->sym.symsize) help(E_NO_ROOT, m); } table_dump(buf, m); return 0; } /* * usage: program [options] module_name symbol [@recno [val ... ]] * where options are * -v: verbose * -t file external table * -m modulefile patch this file, not the kernel */ int main(int argc, char *argv[]) { char *module, *sym; const char *table = _default_table; struct match_info m; bzero(&m, sizeof(m)); if (argc > 1 && !strcmp(argv[1], "-v")) { verbose = 1; argc--; argv++; } if (argc > 2 && !strcmp(argv[1], "-m")) { /* patch a file */ m.filename = argv[2]; argc -= 2; argv += 2; } if (argc > 2 && !strcmp(argv[1], "-t")) { /* either a filename or the table line */ int fd = open(argv[2], O_RDONLY); if (fd < 0) { /* the table is on the command line */ table = argv[2]; } else { int l = lseek(fd, 0, SEEK_END); char *new_table; if (l > 100000) /* 100k is way too much for a table */ help(E_FILE, NULL); lseek(fd, 0, SEEK_SET); new_table = my_calloc(l); read(fd, new_table, l); table = new_table; close(fd); } argc -= 2; argv += 2; } if (argc < 3) help(E_NO_ARGS, NULL); /* need module and symbol */ /* argc==3 -> read all; argc==4 -> read one, argc>4 -> write */ module = argv[1]; sym = argv[2]; if (argc > 3) { if (argv[3][0] != '@') help(E_NO_OFS, &m); m.data_ofs = atoi(argv[3] + 1); } m.mod_name = module; m.symbol = sym; /* lookup the module/symbol in the description */ if (!match_table(table, &m)) help(E_INVALID_MODULE, &m); if (argc > 4 && argc - 4 != m.argcount) help(E_BAD_ARGS, &m); do_rw(&m, argc, argv); return 0; }