Hello Dave, I add more pseudo section symbols which do not own any named symbol like ffffffffa004ccf0 [.rodata.str1.1]: section start ffffffffa004d14a [.rodata.str1.1]: section end crash> rd ffffffffa004ccf0 -e ffffffffa004d14a This can access section data without symbol. This patch set way is learned from kernel/module.c layout_sections() and probably be possible to integrate existing calculate_load_order_v1/2() or add_symbol_file_kallsyms(). However, I can not confirm many kernel versions or architecrures, thus my choice is verifying and updating to installed sections. P.S. I want to make feature in http://grsecurity.net/ PaX linux in crash utility. The PaX patch changes module location by separating non contiguous RX/RW areas which makes virtual address hole in module, also translates virtual address. I tried but crash can not work out yet under PaX linux. I'm resolving them with brief/rough way and useful parts are merged into crash code, and then posting here. If you can accept such a non mainline kernel feature in crash utility, I would like to keep posting patch set until my whole work has done. Thanks, Toshi
Date: Fri, 9 Dec 2011 09:47:29 +0900 Subject: [PATCH 1/5] add more module section via patch_module_section_layout() Verify and update for validate sections which is not containing named symbol such like ".rodata.str#", ".eh_frame". If section patch succeed, mark MOD_NOPATCH and directly call gdb request without kallsym helper. Also this will drop calculate_load_order and add_symbol_file_kallsyms staff if section patch can work out at everything about kernel or architecture. Signed-off-by: Toshikazu Nakayama <nakayama.ts@xxxxxxxxxxxxxx> --- symbols.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 130 insertions(+), 1 deletions(-) diff --git a/symbols.c b/symbols.c index 6a9040b..e1a20b2 100755 --- a/symbols.c +++ b/symbols.c @@ -9467,7 +9467,126 @@ compare_prios(const void *v1, const void *v2) return (md1->priority < md2->priority ? -1 : 1); } +/* + * Try to resolve section layouts according by predefined layouts in kernel. + * This works well while bfd_map_over_sections() can callback each section + * with the same order as kernel's. + */ +static ulong +section_align(bfd *mbfd, asection *section, ulong size) +{ + int sh_addralign; + ulong align; + + sh_addralign = bfd_get_section_alignment(mbfd, section); + if (sh_addralign) { + align = 1 << sh_addralign; + size = roundup(size, align); + } + return size; +} + +static void +calc_module_section(bfd *mbfd, struct mod_section_data *section, ulong *offset) +{ + struct load_module *lm; + ulong start; + + lm = st->current; + if (!lm) + return; + + *offset = section_align(mbfd, section->section, *offset); + section->offset = *offset; + + start = lm->mod_base + section->offset; + if (STREQ(section->name, ".text") == 0) { + lm->mod_text_start = start; + lm->mod_etext_guess = start + section->size; + } else if (STREQ(section->name, ".rodata") == 0) + lm->mod_rodata_start = start; + else if (STREQ(section->name, ".bss") == 0) + lm->mod_bss_start = start; + else if (STREQ(section->name, ".data") == 0) + lm->mod_data_start = start; + + *offset += section->size; +} + +static int +patch_module_section_layout(bfd *mbfd, int do_init) +{ + struct load_module *lm; + ulong offset, verify; + int i, j; + int is_init; + ulong flags; + const ulong mask[][2] = { + /* + * NOTE: all executable code must be the first section + * in this array; otherwise modify the text_size + * finder in the two loops below + */ + { SEC_CODE | SEC_ALLOC, SEC_SMALL_DATA }, + { SEC_ALLOC | SEC_READONLY, SEC_CODE | SEC_SMALL_DATA }, + { SEC_ALLOC, SEC_READONLY | SEC_SMALL_DATA }, + { SEC_SMALL_DATA | SEC_ALLOC, 0 } + }; + + lm = st->current; + if (!lm) + return FALSE; +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + offset = 0; + for (i = 0; i < ARRAY_SIZE(mask); i++) { + for (j = 0; j < lm->mod_sections; j++) { + if (lm->mod_section_data[j].size == 0) + continue; + is_init = STRNEQ(lm->mod_section_data[j].name, ".init"); + if (is_init != do_init) + continue; + + flags = lm->mod_section_data[j].section->flags; + if ((flags & mask[i][0]) != mask[i][0] || + (flags & mask[i][1])) + continue; + /* + * This layouts here, compare previous offset + * if was resolved previously. + */ + if (lm->mod_section_data[j].flags & SEC_FOUND) + verify = lm->mod_section_data[j].offset; + calc_module_section(mbfd, &lm->mod_section_data[j], + &offset); + if (lm->mod_section_data[j].flags & SEC_FOUND) { + if (verify == lm->mod_section_data[j].offset) + continue; + error(INFO, + "Fail to resolve section layout for %s " + "calc=%lu : orig=%lu\n", + lm->mod_section_data[j].name, + lm->mod_section_data[j].offset, verify); + lm->mod_section_data[j].offset = verify; + lm->mod_flags &= ~MOD_NOPATCH; + return FALSE; + } + /* + * sections which don't own symbol entry are coming. + */ + lm->mod_section_data[j].flags |= SEC_FOUND; + if (CRASHDEBUG(2)) + fprintf(fp, + "Resolved more secion %s offset %lx\n", + lm->mod_section_data[j].name, + lm->mod_section_data[j].offset); + } + } + lm->mod_flags |= MOD_NOPATCH; + + return TRUE; +#undef ARRAY_SIZE +} /* * This routine scours a module object file namelist for global text and @@ -10060,7 +10179,17 @@ store_load_module_symbols(bfd *bfd, int dynamic, void *minisyms, else calculate_load_order_v2(lm, bfd, dynamic, minisyms, symcount, size); - + /* + * Try to verify and update section layouts. + */ + if (!patch_module_section_layout(bfd, FALSE) || + (lm->mod_init_size > 0 && + !patch_module_section_layout(bfd, TRUE))) { + error(INFO, "module section layout could not work out yet.\n"); + error(INFO, "ver: linux-%u.%u.%u mach: %s\n", + kt->kernel_version[0], kt->kernel_version[1], + kt->kernel_version[2], pc->machine_type); + } from = (bfd_byte *) minisyms; fromend = from + symcount * size; -- 1.7.7.rc0.72.g4b5ea
Date: Thu, 8 Dec 2011 17:57:46 +0900 Subject: [PATCH 2/5] handle excluded sections Excluded several sections by kernel versions or kconfig. Skip to update layouts for such sections. Signed-off-by: Toshikazu Nakayama <nakayama.ts@xxxxxxxxxxxxxx> --- symbols.c | 32 ++++++++++++++++++++++++++++++++ 1 files changed, 32 insertions(+), 0 deletions(-) diff --git a/symbols.c b/symbols.c index e1a20b2..8404009 100755 --- a/symbols.c +++ b/symbols.c @@ -9472,6 +9472,33 @@ compare_prios(const void *v1, const void *v2) * This works well while bfd_map_over_sections() can callback each section * with the same order as kernel's. */ +static int +section_excluded(char *secname) +{ + if (STREQ(secname, ".data.percpu") || STREQ(secname, ".data..percpu")) + return TRUE; + else if (THIS_KERNEL_VERSION >= LINUX(2,6,10) && + STREQ(secname, ".modinfo")) + /* + * .modinfo is excluded from 2.6. + */ + return TRUE; + else if (THIS_KERNEL_VERSION >= LINUX(2,6,29) && + STREQ(secname, "__versions")) + /* + * __versions is excluded from 2.6.29 + */ + return TRUE; + else if (!MEMBER_EXISTS("module", "modules_which_use_me") && + STRNEQ(secname, ".exit")) + /* + * .exit.[] is excluded if CONFIG_MODULE_UNLOAD = n + */ + return TRUE; + + return FALSE; +} + static ulong section_align(bfd *mbfd, asection *section, ulong size) { @@ -9546,6 +9573,11 @@ patch_module_section_layout(bfd *mbfd, int do_init) is_init = STRNEQ(lm->mod_section_data[j].name, ".init"); if (is_init != do_init) continue; + /* + * Check don't keep sections. + */ + if (section_excluded(lm->mod_section_data[j].name)) + continue; flags = lm->mod_section_data[j].section->flags; if ((flags & mask[i][0]) != mask[i][0] || -- 1.7.7.rc0.72.g4b5ea
Date: Fri, 9 Dec 2011 11:07:23 +0900 Subject: [PATCH 3/5] handle CONFIG_DEBUG_SET_MODULE_RONX When CONFIG_DEBUG_SET_MODULE_RONX is y, RO regions are aligned with page size for setting pte protection. Signed-off-by: Toshikazu Nakayama <nakayama.ts@xxxxxxxxxxxxxx> --- symbols.c | 15 +++++++++++++++ 1 files changed, 15 insertions(+), 0 deletions(-) diff --git a/symbols.c b/symbols.c index 8404009..be8ce8d 100755 --- a/symbols.c +++ b/symbols.c @@ -9500,6 +9500,19 @@ section_excluded(char *secname) } static ulong +section_debug_align(ulong size) +{ + /* + * CONFIG_DEBUG_SET_MODULE_RONX = y from 2.6.37 + */ + if (THIS_KERNEL_VERSION >= LINUX(2,6,37) && + (symbol_exists("set_section_ro_nx") || + get_kernel_config("DEBUG_SET_MODULE_RONX", NULL) != IKCONFIG_N)) + return roundup(size, PAGESIZE()); + return size; +} + +static ulong section_align(bfd *mbfd, asection *section, ulong size) { int sh_addralign; @@ -9612,6 +9625,8 @@ patch_module_section_layout(bfd *mbfd, int do_init) "Resolved more secion %s offset %lx\n", lm->mod_section_data[j].name, lm->mod_section_data[j].offset); + if (i != 2) + offset = section_debug_align(offset); } } lm->mod_flags |= MOD_NOPATCH; -- 1.7.7.rc0.72.g4b5ea
Date: Fri, 9 Dec 2011 11:59:55 +0900 Subject: [PATCH 4/5] handle symtab and strtab The 2.6 kernel kept symtab and strtab in the end of RO section and having been deleted to slim up module area in 2.6.31. This work use a way what is directly reading ELF headrs instead of bfd call because I could not find out suitable bfd API. Probably reason is enforced SHF_ALLOC by old 2.6 kernel. Signed-off-by: Toshikazu Nakayama <nakayama.ts@xxxxxxxxxxxxxx> --- symbols.c | 182 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 181 insertions(+), 1 deletions(-) diff --git a/symbols.c b/symbols.c index be8ce8d..5b637bd 100755 --- a/symbols.c +++ b/symbols.c @@ -9472,6 +9472,175 @@ compare_prios(const void *v1, const void *v2) * This works well while bfd_map_over_sections() can callback each section * with the same order as kernel's. */ +static void +install_new_bfd_section(bfd *bfd, char *secname, flagword flags, + bfd_size_type size, unsigned int alignment) +{ + asection *new_sec; + + new_sec = bfd_make_section_anyway(bfd, secname); + if (!new_sec) { + error(WARNING, "New section over bfd was failed\n"); + return; + } + bfd_set_section_flags(bfd, new_sec, flags); + bfd_set_section_size(bfd, new_sec, size); + alignment = ffs(alignment) - 1; + bfd_set_section_alignment(bfd, new_sec, alignment); + store_section_data(st->current, bfd, new_sec); +} + +static int +resolve_elf64_mod_stabs(bfd *mbfd, int fd, int swap) +{ + int i; + Elf64_Ehdr hdr; + Elf64_Shdr shdr; + char shstrtab[BUFSIZE], *secname; + off_t seeko; + + if (read(fd, &hdr, sizeof(hdr)) < sizeof(hdr)) + return FALSE; + + /* + * Seek to shstrtab section info at first. + */ + seeko = swap32(hdr.e_shoff, swap) + (swap32(hdr.e_shstrndx, swap) * + swap32(hdr.e_shentsize, swap)); + if (lseek(fd, seeko, SEEK_SET) == -1) + return FALSE; + if (read(fd, &shdr, sizeof(shdr)) < sizeof(shdr)) + return FALSE; + if (swap32(shdr.sh_type, swap) != SHT_STRTAB) { + error(WARNING, "EFL header miss reading\n"); + return FALSE; + } + /* + * Seek to shstrtab section area and get section names. + */ + seeko = swap32(shdr.sh_offset, swap); + if (lseek(fd, seeko, SEEK_SET) == -1) + return FALSE; + if (read(fd, shstrtab, swap32(shdr.sh_size, swap)) < + swap32(shdr.sh_size, swap)) + return FALSE; + + /* + * Seek to top of section headers and search symtab and strtab. + */ + seeko = swap32(hdr.e_shoff, swap); + if (lseek(fd, seeko, SEEK_SET) == -1) + return FALSE; + for (i = 0; i < swap32(hdr.e_shnum, swap); i++) { + if (read(fd, &shdr, sizeof(shdr)) < sizeof(shdr)) + return FALSE; + if (swap32(shdr.sh_type, swap) != SHT_STRTAB && + swap32(shdr.sh_type, swap) != SHT_SYMTAB) + continue; + + secname = &shstrtab[swap32(shdr.sh_name, swap)]; + if (STREQ(secname, ".symtab") || STREQ(secname, ".strtab")) + install_new_bfd_section(mbfd, secname, + SEC_ALLOC | SEC_READONLY, + swap32(shdr.sh_size, swap), + swap32(shdr.sh_addralign, + swap)); + } + return TRUE; +} + +static int +resolve_elf32_mod_stabs(bfd *mbfd, int fd, int swap) +{ + int i; + Elf32_Ehdr hdr; + Elf32_Shdr shdr; + char shstrtab[BUFSIZE], *secname; + off_t seeko; + + if (read(fd, &hdr, sizeof(hdr)) < sizeof(hdr)) + return FALSE; + + /* + * Seek to shstrtab section info at first. + */ + seeko = swap16(hdr.e_shoff, swap) + (swap16(hdr.e_shstrndx, swap) * + swap16(hdr.e_shentsize, swap)); + if (lseek(fd, seeko, SEEK_SET) == -1) + return FALSE; + if (read(fd, &shdr, sizeof(shdr)) < sizeof(shdr)) + return FALSE; + if (swap16(shdr.sh_type, swap) != SHT_STRTAB) { + error(WARNING, "EFL header miss reading\n"); + return FALSE; + } + /* + * Seek to shstrtab section area and get section names. + */ + seeko = swap16(shdr.sh_offset, swap); + if (lseek(fd, seeko, SEEK_SET) == -1) + return FALSE; + if (read(fd, shstrtab, swap16(shdr.sh_size, swap)) < + swap16(shdr.sh_size, swap)) + return FALSE; + + /* + * Seek to top of section headers and search symtab and strtab. + */ + seeko = swap16(hdr.e_shoff, swap); + if (lseek(fd, seeko, SEEK_SET) == -1) + return FALSE; + for (i = 0; i < swap16(hdr.e_shnum, swap); i++) { + if (read(fd, &shdr, sizeof(shdr)) < sizeof(shdr)) + return FALSE; + if (swap16(shdr.sh_type, swap) != SHT_STRTAB && + swap16(shdr.sh_type, swap) != SHT_SYMTAB) + continue; + + secname = &shstrtab[swap16(shdr.sh_name, swap)]; + if (STREQ(secname, ".symtab") || STREQ(secname, ".strtab")) + install_new_bfd_section(mbfd, secname, + SEC_ALLOC | SEC_READONLY, + swap16(shdr.sh_size, swap), + swap16(shdr.sh_addralign, + swap)); + } + return TRUE; +} + +static int +resolve_mod_stabs(bfd *mbfd, char *file) +{ + int fd, swap, ret; + char eheader[EI_NIDENT]; + + if ((fd = open(file, O_RDONLY)) < 0) { + error(INFO, "%s: %s\n", file, strerror(errno)); + return FALSE; + } + + ret = FALSE; + if (read(fd, eheader, EI_NIDENT) < EI_NIDENT) + goto close_ret; + if (!STRNEQ(eheader, ELFMAG) || eheader[EI_VERSION] != EV_CURRENT) + goto close_ret; + swap = (((eheader[EI_DATA] == ELFDATA2LSB) && + (__BYTE_ORDER == __BIG_ENDIAN)) || + ((eheader[EI_DATA] == ELFDATA2MSB) && + (__BYTE_ORDER == __LITTLE_ENDIAN))); + + if (lseek(fd, 0, SEEK_SET) == -1) + goto close_ret; + if (eheader[EI_CLASS] == ELFCLASS32) + ret = resolve_elf32_mod_stabs(mbfd, fd, swap); + else if (eheader[EI_CLASS] == ELFCLASS64) + ret = resolve_elf64_mod_stabs(mbfd, fd, swap); + +close_ret: + close(fd); + return ret; +} + static int section_excluded(char *secname) { @@ -10215,7 +10384,7 @@ store_load_module_symbols(bfd *bfd, int dynamic, void *minisyms, if (!(lm->mod_section_data = (struct mod_section_data *) malloc(sizeof(struct mod_section_data) * - (bfd->section_count+1)))) + (bfd->section_count+3)))) error(FATAL, "module section data array malloc: %s\n", strerror(errno)); @@ -10227,6 +10396,17 @@ store_load_module_symbols(bfd *bfd, int dynamic, void *minisyms, calculate_load_order_v2(lm, bfd, dynamic, minisyms, symcount, size); /* + * 2.6 kernel kept symtab and strtab until 2.6.31 by forcing SHF_ALLOC. + * Append these read only sections to lm before patching. + */ + if ((lm->mod_flags & MOD_KALLSYMS) && + THIS_KERNEL_VERSION < LINUX(2,6,31)) + /* lm->mod_namelist can be shrinked, do with namelist. */ + if (!resolve_mod_stabs(bfd, namelist)) + error(WARNING, + "Could not resolve mod: %s stabs section\n", + namelist); + /* * Try to verify and update section layouts. */ if (!patch_module_section_layout(bfd, FALSE) || -- 1.7.7.rc0.72.g4b5ea
Date: Fri, 9 Dec 2011 13:36:42 +0900 Subject: [PATCH 5/5] work out add_symbol_file() and use if patched add_symbol_file() request buffer work was probably corrupted. Fix and be treated as equivalent to add_symbol_file_kallsyms() if MOD_NOPATCH is marked at patch_module_section_layout(). Signed-off-by: Toshikazu Nakayama <nakayama.ts@xxxxxxxxxxxxxx> --- symbols.c | 60 +++++++++++++++++++++++++++++++++++++----------------------- 1 files changed, 37 insertions(+), 23 deletions(-) diff --git a/symbols.c b/symbols.c index 5b637bd..b56a531 100755 --- a/symbols.c +++ b/symbols.c @@ -9910,45 +9910,59 @@ add_symbols: static int add_symbol_file(struct load_module *lm) { - struct gnu_request request, *req; - char buf[BUFSIZE]; - int i, len; - char *secname; + struct gnu_request request, *req; + char buf[BUFSIZE]; + int i, len, buflen; + char *secname; req = &request; BZERO(req, sizeof(struct gnu_request)); - if ((lm->mod_flags & MOD_KALLSYMS) && + if ((lm->mod_flags & MOD_KALLSYMS) && !(lm->mod_flags & MOD_NOPATCH) && add_symbol_file_kallsyms(lm, req)) return TRUE; - for (i = len = 0; i < lm->mod_sections; i++) - { + buflen = 1024; + req->buf = GETBUF(buflen); + for (i = 0; i < lm->mod_sections; i++) { secname = lm->mod_section_data[i].name; - if ((lm->mod_section_data[i].flags & SEC_FOUND) && - (!STREQ(secname, ".text") && - !STREQ(secname, ".data.percpu") && - !STREQ(secname, ".data..percpu"))) { - sprintf(buf, " -s %s 0x%lx", secname, + if (STREQ(secname, ".text")) { + if (!(lm->mod_section_data[i].flags & SEC_FOUND)) { + error(WARNING, + ".text section could not be found.\n"); + return FALSE; + } + sprintf(req->buf, "add-symbol-file %s 0x%lx", + lm->mod_namelist, lm->mod_section_data[i].offset + lm->mod_base); - len += strlen(buf); + break; } } - for (i = 0; i < lm->mod_sections; i++) - { - secname = lm->mod_section_data[i].name; - if ((lm->mod_section_data[i].flags & SEC_FOUND) && - (STREQ(secname, ".data.percpu") || - STREQ(secname, ".data..percpu"))) { - sprintf(buf, " -s %s 0x%lx", secname, lm->mod_percpu); - len += strlen(buf); + for (i = 0; i < lm->mod_sections; i++) { + if (lm->mod_section_data[i].flags & SEC_FOUND) { + secname = lm->mod_section_data[i].name; + if (STREQ(secname, ".data.percpu") || + STREQ(secname, ".data..percpu")) + sprintf(buf, " -s %s 0x%lx", + secname, lm->mod_percpu); + else if (!STREQ(secname, ".text")) + sprintf(buf, " -s %s 0x%lx", secname, + lm->mod_section_data[i].offset + + lm->mod_base); + else + continue; + len = strlen(req->buf); + while ((len + strlen(buf)) > buflen) { + RESIZEBUF(req->buf, buflen, buflen * 2); + buflen *= 2; + } + strcat(req->buf, buf); } } - req->command = GNU_ADD_SYMBOL_FILE; + req->command = GNU_ADD_SYMBOL_FILE; req->addr = (ulong)lm; - req->buf = GETBUF(len+BUFSIZE); if (!CRASHDEBUG(1)) req->fp = pc->nullfp; -- 1.7.7.rc0.72.g4b5ea
-- Crash-utility mailing list Crash-utility@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/crash-utility