makedumpfile: backport security filter feature fixes. From: Mahesh Salgaonkar This patch backports the makedumpfile security filter fixes from upstream makedumpfile-1.4.0 to kexec-tools on RHEL6.3. This patch cleanly applies on kexec-tools--2.0.0-214.el6 This patch is generated by backporting following upstream patches: - [PATCH] Use the same format as erase commands for eraseinfo data. 894680e - [PATCH] Fix array traversal for array of structure and char type. 6a78de7 - [PATCH] Cleanup: Revert unnecessary change about writing sub header. 4edcbc5 - [PATCH] Identify eraseinfo without NT_ERASE_INFO. 389e1c8 - [PATCH] Bugfix: Avoid a SIGSEGV at the memset() of 40c5772 write_cache_zero(). - [PATCH] Bugfix: Avoid writting offset_eraseinfo in kdump_sub_header de50b06 if not containing eraseinfo. - [PATCH] Bugfix: set kdump_sub_header data with --reassemble option. bb646d8 - [PATCH] Add free() and use CONFIG_SKIP_SECTION. 706ab0b - [PATCH] Move debuginfo search to set_dwarf_debuginfo() routine. af54031 - [PATCH] Cleanup: Get a pointer size by sizeof(void -). d07bc64 - [PATCH] Cleanup: Add __load_module_symbol() for shrinking function 91f3696 size. - [PATCH] Cleanup: Output error message if mod_st.num_modules is 0. e80c858 - [PATCH] Cleanup: Make the call of dwfl_report_offline() clear. 2536dd9 - [PATCH] Cleanup: Fix return values of get_symbol_addr, etc. c20e415 - [PATCH] Bugfix: Write both vmcoreinfo and pt_note when reassembling. 3a93bbb - [PATCH] Bugfix: Skip writing PT_LOAD segment not having real data. e3ac177 Signed-off-by: Mahesh Salgaonkar --- makedumpfile-1.3.5/IMPLEMENTATION | 4 makedumpfile-1.3.5/makedumpfile.c | 940 ++++++++++++++++++++++--------------- makedumpfile-1.3.5/makedumpfile.h | 18 - makedumpfile-1.3.5/s390x.c | 4 4 files changed, 562 insertions(+), 404 deletions(-) diff --git a/makedumpfile-1.3.5/IMPLEMENTATION b/makedumpfile-1.3.5/IMPLEMENTATION index cafc596..94390e4 100644 --- a/makedumpfile-1.3.5/IMPLEMENTATION +++ b/makedumpfile-1.3.5/IMPLEMENTATION @@ -26,9 +26,9 @@ | : | | page data (pfn Z) | +------------------------------------------+ offset_eraseinfo - | erase mystruct2.mystruct1.var 4 | + | erase mystruct2.mystruct1.var size 4 | | erase mystruct2.mystruct1.ptr nullify | - | erase mystruct2.mystruct.array 100 | + | erase mystruct2.mystruct.array size 100 | +------------------------------------------+ diff --git a/makedumpfile-1.3.5/makedumpfile.c b/makedumpfile-1.3.5/makedumpfile.c index fac95e8..e0c6ed8 100644 --- a/makedumpfile-1.3.5/makedumpfile.c +++ b/makedumpfile-1.3.5/makedumpfile.c @@ -34,7 +34,6 @@ unsigned long num_erase_info = 1; /* Node 0 is unused. */ char filename_stdout[] = FILENAME_STDOUT; int message_level; int flag_ignore_r_char; /* 0: '\r' is effective. 1: not effective. */ -long pointer_size; char config_buf[BUFSIZE_FGETS]; /* @@ -1407,12 +1406,61 @@ clean_dwfl_info(void) } /* - * Intitialize the dwarf info. - * Linux kernel module debuginfo are of ET_REL (relocatable) type. The old - * implementation of get_debug_info() function that reads the debuginfo was - * not relocation-aware and hence could not read the dwarf info properly - * from module debuginfo. + * Search module debuginfo. + * This function searches for module debuginfo in default debuginfo path for + * a given module in dwarf_info.module_name. * + * On success, dwarf_info.name_debuginfo is set to absolute path of + * module debuginfo. + */ +static int +search_module_debuginfo(void) +{ + Dwfl *dwfl = NULL; + static char *debuginfo_path = DEFAULT_DEBUGINFO_PATH; + static const Dwfl_Callbacks callbacks = { + .section_address = dwfl_offline_section_address, + .find_debuginfo = dwfl_standard_find_debuginfo, + .debuginfo_path = &debuginfo_path, + }; + + /* + * Check if We already have debuginfo file name with us. If yes, + * then we don't need to proceed with search method. + */ + if (dwarf_info.name_debuginfo) + return TRUE; + + if ((dwfl = dwfl_begin(&callbacks)) == NULL) { + ERRMSG("Can't create a handle for a new dwfl session.\n"); + return FALSE; + } + + /* Search for module debuginfo file. */ + if (dwfl_linux_kernel_report_offline(dwfl, + info->system_utsname.release, + &dwfl_report_module_p)) { + ERRMSG("Can't get Module debuginfo for module '%s'\n", + dwarf_info.module_name); + dwfl_end(dwfl); + return FALSE; + } + dwfl_report_end(dwfl, NULL, NULL); + dwfl_getmodules(dwfl, &process_module, NULL, 0); + + dwfl_end(dwfl); + clean_dwfl_info(); + + /* Return success if module debuginfo is found. */ + if (dwarf_info.name_debuginfo) + return TRUE; + + return FALSE; +} + +/* + * Initialize the dwarf info. + * Linux kernel module debuginfo are of ET_REL (relocatable) type. * This function uses dwfl API's to apply relocation before reading the * dwarf information from module debuginfo. * On success, this function sets the dwarf_info.elfd and dwarf_info.dwarfd @@ -1423,54 +1471,45 @@ init_dwarf_info(void) { Dwfl *dwfl = NULL; int dwfl_fd = -1; - static char *debuginfo_path = DEFAULT_DEBUGINFO_PATH; static const Dwfl_Callbacks callbacks = { .section_address = dwfl_offline_section_address, - .find_debuginfo = dwfl_standard_find_debuginfo, - .debuginfo_path = &debuginfo_path, }; dwarf_info.elfd = NULL; dwarf_info.dwarfd = NULL; + /* + * We already know the absolute path of debuginfo file. Fail if we + * still don't have one. Ideally we should never be in this situation. + */ + if (!dwarf_info.name_debuginfo) { + ERRMSG("Can't find absolute path to debuginfo file.\n"); + return FALSE; + } + if ((dwfl = dwfl_begin(&callbacks)) == NULL) { ERRMSG("Can't create a handle for a new dwfl session.\n"); return FALSE; } - if (dwarf_info.name_debuginfo) { - /* We have absolute path for debuginfo file, use it directly - * instead of searching it through - * dwfl_linux_kernel_report_offline() call. - * - * Open the debuginfo file if it is not already open. - */ - if (dwarf_info.fd_debuginfo < 0) - dwarf_info.fd_debuginfo = - open(dwarf_info.name_debuginfo, O_RDONLY); - - dwfl_fd = dup(dwarf_info.fd_debuginfo); - if (dwfl_fd < 0) { - ERRMSG("Failed to get a duplicate handle for" - " debuginfo.\n"); - goto err_out; - } - } - if (dwarf_info.fd_debuginfo > 0) { - if (dwfl_report_offline(dwfl, dwarf_info.module_name, - dwarf_info.name_debuginfo, dwfl_fd) == NULL) { - ERRMSG("Failed reading %s: %s\n", - dwarf_info.name_debuginfo, dwfl_errmsg (-1)); - /* dwfl_fd is consumed on success, not on failure */ - close(dwfl_fd); - goto err_out; - } + /* Open the debuginfo file if it is not already open. */ + if (dwarf_info.fd_debuginfo < 0) + dwarf_info.fd_debuginfo = + open(dwarf_info.name_debuginfo, O_RDONLY); + + dwfl_fd = dup(dwarf_info.fd_debuginfo); + if (dwfl_fd < 0) { + ERRMSG("Failed to get a duplicate handle for" + " debuginfo.\n"); + goto err_out; } - else if (dwfl_linux_kernel_report_offline(dwfl, - info->system_utsname.release, - &dwfl_report_module_p)) { - ERRMSG("Can't get Module debuginfo for module '%s'\n", - dwarf_info.module_name); + /* Apply relocations. */ + if (dwfl_report_offline(dwfl, dwarf_info.module_name, + dwarf_info.name_debuginfo, dwfl_fd) == NULL) { + ERRMSG("Failed reading %s: %s\n", + dwarf_info.name_debuginfo, dwfl_errmsg (-1)); + /* dwfl_fd is consumed on success, not on failure */ + close(dwfl_fd); goto err_out; } dwfl_report_end(dwfl, NULL, NULL); @@ -1565,7 +1604,7 @@ get_symbol_addr(char *symname) return symbol; if (!init_dwarf_info()) - return 0; + return NOT_FOUND_SYMBOL; elfd = dwarf_info.elfd; @@ -1624,7 +1663,7 @@ get_next_symbol_addr(char *symname) char *sym_name = NULL; if (!init_dwarf_info()) - return 0; + return NOT_FOUND_SYMBOL; elfd = dwarf_info.elfd; @@ -1755,6 +1794,14 @@ get_data_array_length(Dwarf *dwarfd, Dwarf_Die *die) return FALSE; } tag = dwarf_tag(&die_type); + if (tag == DW_TAG_const_type) { + /* This array is of const type. Get the die type again */ + if (!get_die_type(dwarfd, &die_type, &die_type)) { + ERRMSG("Can't get CU die of DW_AT_type.\n"); + return FALSE; + } + tag = dwarf_tag(&die_type); + } if (tag != DW_TAG_array_type) { /* * This kernel doesn't have the member of array. @@ -2283,10 +2330,6 @@ get_debug_info(void) */ while (dwarf_nextcu(dwarfd, off, &next_off, &header_size, &abbrev_offset, &address_size, &offset_size) == 0) { - if (dwarf_info.cmd == DWARF_INFO_GET_PTR_SIZE) { - dwarf_info.struct_size = address_size; - break; - } off += header_size; if (dwarf_offdie(dwarfd, off, &cu_die) == NULL) { ERRMSG("Can't get CU die.\n"); @@ -2330,14 +2373,7 @@ get_structure_size(char *structname, int flag_typedef) long get_pointer_size() { - dwarf_info.cmd = DWARF_INFO_GET_PTR_SIZE; - /* reuse struct_size member to report pointer size */ - dwarf_info.struct_size = NOT_FOUND_STRUCTURE; - - if (!get_debug_info()) - return FAILED_DWARFINFO; - - return dwarf_info.struct_size; + return sizeof(void *); } /* @@ -2510,8 +2546,6 @@ get_symbol_info(void) if (SYMBOL(node_memblk) != NOT_FOUND_SYMBOL) SYMBOL_ARRAY_LENGTH_INIT(node_memblk, "node_memblk"); - pointer_size = get_pointer_size(); - return TRUE; } @@ -2772,20 +2806,19 @@ set_dwarf_debuginfo(char *mod_name, char *name_debuginfo, int fd_debuginfo) if (dwarf_info.name_debuginfo) free(dwarf_info.name_debuginfo); } + if (dwarf_info.module_name) + free(dwarf_info.module_name); + dwarf_info.fd_debuginfo = fd_debuginfo; dwarf_info.name_debuginfo = name_debuginfo; - dwarf_info.module_name = mod_name; + dwarf_info.module_name = strdup(mod_name); if (!strcmp(dwarf_info.module_name, "vmlinux") || !strcmp(dwarf_info.module_name, "xen-syms")) return TRUE; /* check to see whether module debuginfo is available */ - if (!init_dwarf_info()) - return FALSE; - else - clean_dwfl_info(); - return TRUE; + return search_module_debuginfo(); } int @@ -3325,7 +3358,15 @@ get_pt_note_info(off_t off_note, unsigned long sz_note) return FALSE; } info->p2m_mfn = p2m_mfn; - } else if (n_type == NT_ERASE_INFO) { + + /* + * Check whether a source dumpfile contains eraseinfo. + * /proc/vmcore does not contain eraseinfo, because eraseinfo + * is added only by makedumpfile and makedumpfile does not + * create /proc/vmcore. + */ + } else if (!strncmp(ERASEINFO_NOTE_NAME, buf, + ERASEINFO_NOTE_NAME_BYTES)) { info->offset_eraseinfo = offset_desc; info->size_eraseinfo = size_desc; } @@ -4625,6 +4666,9 @@ write_cache_bufsz(struct cache_data *cd) int write_cache_zero(struct cache_data *cd, size_t size) { + if (!write_cache_bufsz(cd)) + return FALSE; + memset(cd->buf + cd->buf_size, 0, size); cd->buf_size += size; @@ -5833,7 +5877,7 @@ write_elf_header(struct cache_data *cd_header) if (fl_info->nullify) sprintf(size_str, "nullify\n"); else - sprintf(size_str, "%ld\n", fl_info->size); + sprintf(size_str, "size %ld\n", fl_info->size); size_eraseinfo += strlen("erase ") + strlen(ei->symbol_expr) + 1 + @@ -6019,15 +6063,9 @@ write_kdump_header(void) kh.size_vmcoreinfo = info->size_vmcoreinfo; } } - /* - * While writing dump data to STDOUT, delay the writing of sub header - * untill we gather erase info offset and size. - */ - if (!info->flag_flatten) { - if (!write_buffer(info->fd_dumpfile, dh->block_size, &kh, - size, info->name_dumpfile)) - goto out; - } + if (!write_buffer(info->fd_dumpfile, dh->block_size, &kh, + size, info->name_dumpfile)) + goto out; info->sub_header = kh; info->offset_bitmap1 @@ -6328,7 +6366,15 @@ write_elf_pages(struct cache_data *cd_header, struct cache_data *cd_page) */ load.p_memsz = memsz; load.p_filesz = filesz; - load.p_offset = off_seg_load; + if (load.p_filesz) + load.p_offset = off_seg_load; + else + /* + * If PT_LOAD segment does not have real data + * due to the all excluded pages, the file + * offset is not effective and it should be 0. + */ + load.p_offset = 0; /* * Write a PT_LOAD header. @@ -6339,9 +6385,10 @@ write_elf_pages(struct cache_data *cd_header, struct cache_data *cd_page) /* * Write a PT_LOAD segment. */ - if (!write_elf_load_segment(cd_page, paddr, off_memory, - load.p_filesz)) - return FALSE; + if (load.p_filesz) + if (!write_elf_load_segment(cd_page, paddr, + off_memory, load.p_filesz)) + return FALSE; load.p_paddr += load.p_memsz; #ifdef __x86__ @@ -6378,8 +6425,10 @@ write_elf_pages(struct cache_data *cd_header, struct cache_data *cd_page) /* * Write a PT_LOAD segment. */ - if (!write_elf_load_segment(cd_page, paddr, off_memory, load.p_filesz)) - return FALSE; + if (load.p_filesz) + if (!write_elf_load_segment(cd_page, paddr, + off_memory, load.p_filesz)) + return FALSE; off_seg_load += load.p_filesz; } @@ -6690,7 +6739,7 @@ write_eraseinfo(struct cache_data *cd_page, unsigned long *size_out) continue; for (j = 0; j < erase_info[i].num_sizes; j++) { if (erase_info[i].sizes[j] > 0) - sprintf(size_str, "%ld\n", + sprintf(size_str, "size %ld\n", erase_info[i].sizes[j]); else if (erase_info[i].sizes[j] == -1) sprintf(size_str, "nullify\n"); @@ -6761,14 +6810,14 @@ write_elf_eraseinfo(struct cache_data *cd_header) note_header_size = sizeof(Elf64_Nhdr); nh->n_namesz = ERASEINFO_NOTE_NAME_BYTES; nh->n_descsz = info->size_eraseinfo; - nh->n_type = NT_ERASE_INFO; + nh->n_type = 0; } else { Elf32_Nhdr *nh = (Elf32_Nhdr *)note; note_header_size = sizeof(Elf32_Nhdr); nh->n_namesz = ERASEINFO_NOTE_NAME_BYTES; nh->n_descsz = info->size_eraseinfo; - nh->n_type = NT_ERASE_INFO; + nh->n_type = 0; } if (!write_cache(cd_header, note, note_header_size)) return FALSE; @@ -6826,9 +6875,10 @@ write_kdump_eraseinfo(struct cache_data *cd_page) DEBUG_MSG("offset_eraseinfo: %lx, size_eraseinfo: %ld\n", info->offset_eraseinfo, info->size_eraseinfo); - /* Update the erase info offset and size in kdump sub header */ - if (!update_sub_header()) - return FALSE; + if (info->size_eraseinfo) + /* Update the erase info offset and size in kdump sub header */ + if (!update_sub_header()) + return FALSE; return TRUE; } @@ -7851,27 +7901,165 @@ clean_module_symbols(void) } static int -load_module_symbols(void) +__load_module_symbol(struct module_info *modules, unsigned long addr_module) { - unsigned long head, cur, cur_module; + int ret = FALSE; + unsigned int nsym; unsigned long symtab, strtab; unsigned long mod_base, mod_init; unsigned int mod_size, mod_init_size; - unsigned char *module_struct_mem, *module_core_mem; + unsigned char *module_struct_mem = NULL; + unsigned char *module_core_mem = NULL; unsigned char *module_init_mem = NULL; unsigned char *symtab_mem; char *module_name, *strtab_mem, *nameptr; - struct module_info *modules = NULL; - struct symbol_info *sym_info; unsigned int num_symtab; - unsigned int i = 0, nsym; - head = SYMBOL(modules); - if (!get_num_modules(head, &mod_st.num_modules)) { - ERRMSG("Can't get module count\n"); + /* Allocate buffer to read struct module data from vmcore. */ + if ((module_struct_mem = calloc(1, SIZE(module))) == NULL) { + ERRMSG("Failed to allocate buffer for module\n"); return FALSE; } - if (!mod_st.num_modules) { + if (!readmem(VADDR, addr_module, module_struct_mem, + SIZE(module))) { + ERRMSG("Can't get module info.\n"); + goto out; + } + + module_name = (char *)(module_struct_mem + OFFSET(module.name)); + if (strlen(module_name) < MOD_NAME_LEN) + strcpy(modules->name, module_name); + else + strncpy(modules->name, module_name, MOD_NAME_LEN-1); + + mod_init = ULONG(module_struct_mem + + OFFSET(module.module_init)); + mod_init_size = UINT(module_struct_mem + + OFFSET(module.init_size)); + mod_base = ULONG(module_struct_mem + + OFFSET(module.module_core)); + mod_size = UINT(module_struct_mem + + OFFSET(module.core_size)); + + DEBUG_MSG("Module: %s, Base: 0x%lx, Size: %u\n", + module_name, mod_base, mod_size); + if (mod_init_size > 0) { + module_init_mem = calloc(1, mod_init_size); + if (module_init_mem == NULL) { + ERRMSG("Can't allocate memory for module " + "init\n"); + goto out; + } + if (!readmem(VADDR, mod_init, module_init_mem, + mod_init_size)) { + ERRMSG("Can't access module init in memory.\n"); + goto out; + } + } + + if ((module_core_mem = calloc(1, mod_size)) == NULL) { + ERRMSG("Can't allocate memory for module\n"); + goto out; + } + if (!readmem(VADDR, mod_base, module_core_mem, mod_size)) { + ERRMSG("Can't access module in memory.\n"); + goto out; + } + + num_symtab = UINT(module_struct_mem + + OFFSET(module.num_symtab)); + if (!num_symtab) { + ERRMSG("%s: Symbol info not available\n", module_name); + goto out; + } + modules->num_syms = num_symtab; + DEBUG_MSG("num_sym: %d\n", num_symtab); + + symtab = ULONG(module_struct_mem + OFFSET(module.symtab)); + strtab = ULONG(module_struct_mem + OFFSET(module.strtab)); + + /* check if symtab and strtab are inside the module space. */ + if (!IN_RANGE(symtab, mod_base, mod_size) && + !IN_RANGE(symtab, mod_init, mod_init_size)) { + ERRMSG("%s: module symtab is outseide of module " + "address space\n", module_name); + goto out; + } + if (IN_RANGE(symtab, mod_base, mod_size)) + symtab_mem = module_core_mem + (symtab - mod_base); + else + symtab_mem = module_init_mem + (symtab - mod_init); + + if (!IN_RANGE(strtab, mod_base, mod_size) && + !IN_RANGE(strtab, mod_init, mod_init_size)) { + ERRMSG("%s: module strtab is outseide of module " + "address space\n", module_name); + goto out; + } + if (IN_RANGE(strtab, mod_base, mod_size)) + strtab_mem = (char *)(module_core_mem + + (strtab - mod_base)); + else + strtab_mem = (char *)(module_init_mem + + (strtab - mod_init)); + + modules->sym_info = calloc(num_symtab, sizeof(struct symbol_info)); + if (modules->sym_info == NULL) { + ERRMSG("Can't allocate memory to store sym info\n"); + goto out; + } + + /* symbols starts from 1 */ + for (nsym = 1; nsym < num_symtab; nsym++) { + Elf32_Sym *sym32; + Elf64_Sym *sym64; + /* If case of ELF vmcore then the word size can be + * determined using info->flag_elf64_memory flag. + * But in case of kdump-compressed dump, kdump header + * does not carry word size info. May be in future + * this info will be available in kdump header. + * Until then, in order to make this logic work on both + * situation we depend on pointer_size that is + * extracted from vmlinux dwarf information. + */ + if ((get_pointer_size() * 8) == 64) { + sym64 = (Elf64_Sym *) (symtab_mem + + (nsym * sizeof(Elf64_Sym))); + modules->sym_info[nsym].value = + (unsigned long long) sym64->st_value; + nameptr = strtab_mem + sym64->st_name; + } else { + sym32 = (Elf32_Sym *) (symtab_mem + + (nsym * sizeof(Elf32_Sym))); + modules->sym_info[nsym].value = + (unsigned long long) sym32->st_value; + nameptr = strtab_mem + sym32->st_name; + } + if (strlen(nameptr)) + modules->sym_info[nsym].name = strdup(nameptr); + DEBUG_MSG("\t[%d] %llx %s\n", nsym, + modules->sym_info[nsym].value, nameptr); + } + ret = TRUE; +out: + free(module_struct_mem); + free(module_core_mem); + free(module_init_mem); + + return ret; +} + +static int +load_module_symbols(void) +{ + unsigned long head, cur, cur_module; + struct module_info *modules = NULL; + unsigned int i = 0; + + head = SYMBOL(modules); + if (!get_num_modules(head, &mod_st.num_modules) || + !mod_st.num_modules) { + ERRMSG("Can't get module count\n"); return FALSE; } mod_st.modules = calloc(mod_st.num_modules, @@ -7882,12 +8070,6 @@ load_module_symbols(void) } modules = mod_st.modules; - /* Allocate buffer to read struct module data from vmcore. */ - if ((module_struct_mem = calloc(1, SIZE(module))) == NULL) { - ERRMSG("Failed to allocate buffer for module\n"); - return FALSE; - } - if (!readmem(VADDR, head + OFFSET(list_head.next), &cur, sizeof cur)) { ERRMSG("Can't get next list_head.\n"); return FALSE; @@ -7896,129 +8078,9 @@ load_module_symbols(void) /* Travese the list and read module symbols */ while (cur != head) { cur_module = cur - OFFSET(module.list); - if (!readmem(VADDR, cur_module, module_struct_mem, - SIZE(module))) { - ERRMSG("Can't get module info.\n"); - return FALSE; - } - - module_name = (char *)(module_struct_mem + OFFSET(module.name)); - if (strlen(module_name) < MOD_NAME_LEN) - strcpy(modules[i].name, module_name); - else - strncpy(modules[i].name, module_name, MOD_NAME_LEN-1); - - mod_init = ULONG(module_struct_mem + - OFFSET(module.module_init)); - mod_init_size = UINT(module_struct_mem + - OFFSET(module.init_size)); - mod_base = ULONG(module_struct_mem + - OFFSET(module.module_core)); - mod_size = UINT(module_struct_mem + - OFFSET(module.core_size)); - - DEBUG_MSG("Module: %s, Base: 0x%lx, Size: %u\n", - module_name, mod_base, mod_size); - if (mod_init_size > 0) { - module_init_mem = calloc(1, mod_init_size); - if (module_init_mem == NULL) { - ERRMSG("Can't allocate memory for module " - "init\n"); - return FALSE; - } - if (!readmem(VADDR, mod_init, module_init_mem, - mod_init_size)) { - ERRMSG("Can't access module init in memory.\n"); - return FALSE; - } - } - - if ((module_core_mem = calloc(1, mod_size)) == NULL) { - ERRMSG("Can't allocate memory for module\n"); - return FALSE; - } - if (!readmem(VADDR, mod_base, module_core_mem, mod_size)) { - ERRMSG("Can't access module in memory.\n"); - return FALSE; - } - - num_symtab = UINT(module_struct_mem + - OFFSET(module.num_symtab)); - if (!num_symtab) { - ERRMSG("%s: Symbol info not available\n", module_name); - return FALSE; - } - modules[i].num_syms = num_symtab; - DEBUG_MSG("num_sym: %d\n", num_symtab); - - symtab = ULONG(module_struct_mem + OFFSET(module.symtab)); - strtab = ULONG(module_struct_mem + OFFSET(module.strtab)); - - /* check if symtab and strtab are inside the module space. */ - if (!IN_RANGE(symtab, mod_base, mod_size) && - !IN_RANGE(symtab, mod_init, mod_init_size)) { - ERRMSG("%s: module symtab is outseide of module " - "address space\n", module_name); - return FALSE; - } - if (IN_RANGE(symtab, mod_base, mod_size)) - symtab_mem = module_core_mem + (symtab - mod_base); - else - symtab_mem = module_init_mem + (symtab - mod_init); - - if (!IN_RANGE(strtab, mod_base, mod_size) && - !IN_RANGE(strtab, mod_init, mod_init_size)) { - ERRMSG("%s: module strtab is outseide of module " - "address space\n", module_name); - return FALSE; - } - if (IN_RANGE(strtab, mod_base, mod_size)) - strtab_mem = (char *)(module_core_mem - + (strtab - mod_base)); - else - strtab_mem = (char *)(module_init_mem - + (strtab - mod_init)); - modules[i].sym_info = calloc(num_symtab, - sizeof(struct symbol_info)); - if (modules[i].sym_info == NULL) { - ERRMSG("Can't allocate memory to store sym info\n"); + if (!__load_module_symbol(&modules[i], cur_module)) return FALSE; - } - sym_info = modules[i].sym_info; - - /* symbols starts from 1 */ - for (nsym = 1; nsym < num_symtab; nsym++) { - Elf32_Sym *sym32; - Elf64_Sym *sym64; - /* If case of ELF vmcore then the word size can be - * determined using info->flag_elf64_memory flag. - * But in case of kdump-compressed dump, kdump header - * does not carry word size info. May be in future - * this info will be available in kdump header. - * Until then, in order to make this logic work on both - * situation we depend on pointer_size that is - * extracted from vmlinux dwarf information. - */ - if ((pointer_size * 8) == 64) { - sym64 = (Elf64_Sym *) (symtab_mem - + (nsym * sizeof(Elf64_Sym))); - sym_info[nsym].value = - (unsigned long long) sym64->st_value; - nameptr = strtab_mem + sym64->st_name; - } - else { - sym32 = (Elf32_Sym *) (symtab_mem - + (nsym * sizeof(Elf32_Sym))); - sym_info[nsym].value = - (unsigned long long) sym32->st_value; - nameptr = strtab_mem + sym32->st_name; - } - if (strlen(nameptr)) - sym_info[nsym].name = strdup(nameptr); - DEBUG_MSG("\t[%d] %llx %s\n", nsym, - sym_info[nsym].value, nameptr); - } if (!readmem(VADDR, cur + OFFSET(list_head.next), &cur, sizeof cur)) { @@ -8026,11 +8088,7 @@ load_module_symbols(void) return FALSE; } i++; - free(module_core_mem); - if (mod_init_size > 0) - free(module_init_mem); } while (cur != head); - free(module_struct_mem); return TRUE; } @@ -8082,7 +8140,7 @@ print_config_entry(struct config_entry *ce) DEBUG_MSG("flag: %x, ", ce->flag); DEBUG_MSG("Type flag: %lx, ", ce->type_flag); DEBUG_MSG("sym_addr: %llx, ", ce->sym_addr); - DEBUG_MSG("addr: %llx, ", ce->addr); + DEBUG_MSG("addr: %lx, ", ce->addr); DEBUG_MSG("offset: %lx, ", ce->offset); DEBUG_MSG("size: %zd\n", ce->size); @@ -8188,6 +8246,7 @@ err_out: free_config_entry(ce); if (ptr) free_config_entry(ptr); + free(str); return NULL; } @@ -8582,7 +8641,10 @@ get_config(int skip) static int line_count = 0; char *cur_module = NULL; int eof = 0; - unsigned char flag = CONFIG_NEW_CMD | skip; + unsigned char flag = CONFIG_NEW_CMD; + + if (skip) + flag |= CONFIG_SKIP_SECTION; if ((config = calloc(1, sizeof(struct config))) == NULL) return NULL; @@ -8617,36 +8679,39 @@ err_out: return NULL; } -static unsigned long long +static unsigned long read_pointer_value(unsigned long long addr) { - uint32_t val_32; - uint64_t val_64; + unsigned long val; - switch (pointer_size) { - case 4: - if (!readmem(VADDR, addr, &val_32, sizeof(val_32))) { - ERRMSG("Can't read pointer value\n"); - return 0; - } - return (unsigned long long)val_32; - break; - case 8: - if (!readmem(VADDR, addr, &val_64, sizeof(val_64))) { - ERRMSG("Can't read pointer value\n"); - return 0; - } - return (unsigned long long)val_64; - break; + if (!readmem(VADDR, addr, &val, sizeof(val))) { + ERRMSG("Can't read pointer value\n"); + return 0; } - return 0; + return val; +} + +static long +get_strlen(unsigned long long vaddr) +{ + char buf[BUFSIZE + 1]; + long len = 0; + + /* + * Determine the string length for 'char' pointer. + * BUFSIZE(1024) is the upper limit for string length. + */ + if (readmem(VADDR, vaddr, buf, BUFSIZE)) { + buf[BUFSIZE] = '\0'; + len = strlen(buf); + } + return len; } int resolve_config_entry(struct config_entry *ce, unsigned long long base_addr, char *base_struct_name) { - char buf[BUFSIZE + 1]; if (ce->flag & SYMBOL_ENTRY) { /* find the symbol info */ @@ -8737,7 +8802,7 @@ resolve_config_entry(struct config_entry *ce, unsigned long long base_addr, * If this is a struct or list_head data type then * create a leaf node entry with 'next' member. */ - if ((ce->type_flag & TYPE_BASE) + if (((ce->type_flag & (TYPE_BASE | TYPE_ARRAY)) == TYPE_BASE) && (strcmp(ce->type_name, "void"))) return FALSE; @@ -8776,17 +8841,14 @@ resolve_config_entry(struct config_entry *ce, unsigned long long base_addr, ce->size = 0; } - if ((ce->type_flag & TYPE_BASE) && (ce->type_flag & TYPE_PTR)) { + if ((ce->type_flag & TYPE_BASE) && (ce->type_flag & TYPE_PTR) + && !(ce->type_flag & TYPE_ARRAY)) { /* * Determine the string length for 'char' pointer. * BUFSIZE(1024) is the upper limit for string length. */ - if (!strcmp(ce->type_name, "char")) { - if (readmem(VADDR, ce->addr, buf, BUFSIZE)) { - buf[BUFSIZE] = '\0'; - ce->size = strlen(buf); - } - } + if (!strcmp(ce->type_name, "char")) + ce->size = get_strlen(ce->addr); } if (!ce->next && (ce->flag & SIZE_ENTRY)) { void *val; @@ -8839,80 +8901,11 @@ get_config_symbol_addr(struct config_entry *ce, unsigned long long base_addr, char *base_struct_name) { - unsigned long long addr = 0; - if (!(ce->flag & ENTRY_RESOLVED)) { if (!resolve_config_entry(ce, base_addr, base_struct_name)) return 0; } - if ((ce->flag & LIST_ENTRY)) { - /* handle List entry differently */ - if (!ce->next) { - /* leaf node. */ - if (ce->type_flag & TYPE_ARRAY) { - if (ce->index == ce->array_length) - return 0; - if (!(ce->type_flag & TYPE_PTR)) - return (ce->addr + - (ce->index * ce->size)); - /* Array of pointers. - * - * Array may contain NULL pointers at some - * indexes. Hence return the next non-null - * address value. - */ - while (ce->index < ce->array_length) { - addr = read_pointer_value(ce->addr + - (ce->index * pointer_size)); - ce->index++; - if (addr) - break; - } - return addr; - } - else { - if (ce->addr == ce->cmp_addr) - return 0; - - /* Set the leaf node as unresolved, so that - * it will be resolved every time when - * get_config_symbol_addr is called untill - * it hits the exit condiftion. - */ - ce->flag &= ~ENTRY_RESOLVED; - } - } - else if ((ce->next->next == NULL) && - !(ce->next->type_flag & TYPE_ARRAY)) { - /* the next node is leaf node. for non-array element - * Set the sym_addr and addr of this node with that of - * leaf node. - */ - addr = ce->addr; - ce->addr = ce->next->addr; - - if (!(ce->type_flag & TYPE_LIST_HEAD)) { - if (addr == ce->next->cmp_addr) - return 0; - - if (!ce->next->cmp_addr) { - /* safeguard against circular - * link-list - */ - ce->next->cmp_addr = addr; - } - - /* Force resolution of traversal node */ - if (ce->addr && !resolve_config_entry(ce->next, - ce->addr, ce->type_name)) - return 0; - - return addr; - } - } - } - if (ce->next && ce->addr) { /* Populate nullify flag down the list */ ce->next->nullify = ce->nullify; @@ -8946,7 +8939,7 @@ get_config_symbol_size(struct config_entry *ce, else { if (ce->type_flag & TYPE_ARRAY) { if (ce->type_flag & TYPE_PTR) - return ce->array_length * pointer_size; + return ce->array_length * get_pointer_size(); else return ce->array_length * ce->size; } @@ -8954,6 +8947,116 @@ get_config_symbol_size(struct config_entry *ce, } } +int +get_next_list_entry(struct config_entry *ce, unsigned long long base_addr, + char *base_struct_name, struct config_entry *out_ce) +{ + unsigned long addr = 0; + + /* This function only deals with LIST_ENTRY config entry. */ + if (!(ce->flag & LIST_ENTRY)) + return FALSE; + + if (!(ce->flag & ENTRY_RESOLVED)) { + if (!resolve_config_entry(ce, base_addr, base_struct_name)) + return FALSE; + } + + if (!ce->next) { + /* leaf node. */ + if (ce->type_flag & TYPE_ARRAY) { + if (ce->index == ce->array_length) + return FALSE; + + if (ce->type_flag & TYPE_PTR) { + /* Array of pointers. + * + * Array may contain NULL pointers at some + * indexes. Hence jump to the next non-null + * address value. + */ + while (ce->index < ce->array_length) { + addr = read_pointer_value(ce->addr + + (ce->index * get_pointer_size())); + if (addr) + break; + ce->index++; + } + if (ce->index == ce->array_length) + return FALSE; + out_ce->sym_addr = ce->addr + (ce->index * + get_pointer_size()); + out_ce->addr = addr; + if (!strcmp(ce->type_name, "char")) + out_ce->size = get_strlen(addr); + else + out_ce->size = ce->size; + } + else { + out_ce->sym_addr = ce->addr + + (ce->index * ce->size); + out_ce->addr = out_ce->sym_addr; + out_ce->size = ce->size; + } + ce->index++; + } + else { + if (ce->addr == ce->cmp_addr) + return FALSE; + + out_ce->addr = ce->addr; + /* Set the leaf node as unresolved, so that + * it will be resolved every time when + * get_next_list_entry is called untill + * it hits the exit condiftion. + */ + ce->flag &= ~ENTRY_RESOLVED; + } + return TRUE; + } + else if ((ce->next->next == NULL) && + !(ce->next->type_flag & TYPE_ARRAY)) { + /* the next node is leaf node. for non-array element + * Set the sym_addr and addr of this node with that of + * leaf node. + */ + if (!(ce->type_flag & TYPE_LIST_HEAD)) { + if (!ce->addr || ce->addr == ce->next->cmp_addr) + return FALSE; + + if (!ce->next->cmp_addr) { + /* safeguard against circular + * link-list + */ + ce->next->cmp_addr = ce->addr; + } + + out_ce->addr = ce->addr; + out_ce->sym_addr = ce->sym_addr; + out_ce->size = ce->size; + + ce->sym_addr = ce->next->sym_addr; + ce->addr = ce->next->addr; + + /* Force resolution of traversal node */ + if (ce->addr && !resolve_config_entry(ce->next, + ce->addr, ce->type_name)) + return FALSE; + + return TRUE; + } + else { + ce->sym_addr = ce->next->sym_addr; + ce->addr = ce->next->addr; + } + } + + if (ce->next && ce->addr) + return get_next_list_entry(ce->next, ce->addr, + ce->type_name, out_ce); + return FALSE; +} + static int resolve_list_entry(struct config_entry *ce, unsigned long long base_addr, char *base_struct_name, char **out_type_name, @@ -9127,7 +9230,7 @@ update_filter_info(struct config_entry *filter_symbol, return FALSE; if (filter_symbol->nullify) - size = pointer_size; + size = get_pointer_size(); else if (size_symbol) { size = get_config_symbol_size(size_symbol, 0, NULL); if (message_level & ML_PRINT_DEBUG_MSG) @@ -9179,31 +9282,14 @@ initialize_iteration_entry(struct config_entry *ie, ie->line); ie->next->flag &= ~SYMBOL_ENTRY; } - } - else { - if (ie->type_name) { - /* looks like user has used 'within' keyword for - * non-list_head VAR. Print the warning and continue. - */ - ERRMSG("Warning: line %d: 'within' keyword not " - "required for ArrayVar/StructVar.\n", ie->line); - free(ie->type_name); - /* remove the next list_head member from iteration - * entry that would have added as part of 'within' - * keyword processing. - */ - if (ie->next) { - free_config_entry(ie->next); - ie->next = NULL; - } - } - ie->type_name = strdup(type_name); - } - - if (!ie->size) { - ie->size = get_structure_size(ie->type_name, - DWARF_INFO_GET_STRUCT_SIZE); + /* + * For list_head find out the size of the StructName and + * populate ie->size now. For array and link list we get the + * size info from config entry returned by + * get_next_list_entry(). + */ + ie->size = get_structure_size(ie->type_name, 0); if (ie->size == FAILED_DWARFINFO) { ERRMSG("Config error at %d: " "Can't get size for type: %s.\n", @@ -9216,8 +9302,7 @@ initialize_iteration_entry(struct config_entry *ie, ie->line, ie->type_name); return FALSE; } - } - if (type_flag & TYPE_LIST_HEAD) { + if (!resolve_config_entry(ie->next, 0, ie->type_name)) return FALSE; @@ -9227,6 +9312,34 @@ initialize_iteration_entry(struct config_entry *ie, ie->next->line, ie->next->name); return FALSE; } + ie->type_flag = TYPE_STRUCT; + } + else { + if (ie->type_name) { + /* looks like user has used 'within' keyword for + * non-list_head VAR. Print the warning and continue. + */ + ERRMSG("Warning: line %d: 'within' keyword not " + "required for ArrayVar/StructVar.\n", ie->line); + free(ie->type_name); + + /* remove the next list_head member from iteration + * entry that would have added as part of 'within' + * keyword processing. + */ + if (ie->next) { + free_config_entry(ie->next); + ie->next = NULL; + } + } + /* + * Set type flag for iteration entry. The iteration entry holds + * individual element from array/list, hence strip off the + * array type flag bit. + */ + ie->type_name = strdup(type_name); + ie->type_flag = type_flag; + ie->type_flag &= ~TYPE_ARRAY; } return TRUE; } @@ -9234,25 +9347,29 @@ initialize_iteration_entry(struct config_entry *ie, int list_entry_empty(struct config_entry *le, struct config_entry *ie) { - unsigned long long addr; + struct config_entry ce; /* Error out if arguments are not correct */ if (!(le->flag & LIST_ENTRY) || !(ie->flag & ITERATION_ENTRY)) { ERRMSG("Invalid arguments\n"); return TRUE; } - addr = get_config_symbol_addr(le, 0, NULL); - if (!addr) + + memset(&ce, 0, sizeof(struct config_entry)); + /* get next available entry from LIST entry. */ + if (!get_next_list_entry(le, 0, NULL, &ce)) return TRUE; if (ie->next) { /* we are dealing with list_head */ - ie->next->addr = addr; - ie->addr = addr - ie->next->offset; - //resolve_iteration_entry(ie, addr); + ie->next->addr = ce.addr; + ie->addr = ce.addr - ie->next->offset; + } + else { + ie->addr = ce.addr; + ie->sym_addr = ce.sym_addr; + ie->size = ce.size; } - else - ie->addr = addr; return FALSE; } @@ -9692,17 +9809,53 @@ get_splitting_info(void) if (!check_splitting_info()) return FALSE; + if (!get_kdump_compressed_header_info(SPLITTING_DUMPFILE(0))) + return FALSE; + return TRUE; } int +copy_same_data(int src_fd, int dst_fd, off_t offset, unsigned long size) +{ + int ret = FALSE; + char *buf = NULL; + + if ((buf = malloc(size)) == NULL) { + ERRMSG("Can't allcate memory.\n"); + return FALSE; + } + if (lseek(src_fd, offset, SEEK_SET) < 0) { + ERRMSG("Can't seek a source file. %s\n", strerror(errno)); + goto out; + } + if (read(src_fd, buf, size) != size) { + ERRMSG("Can't read a source file. %s\n", strerror(errno)); + goto out; + } + if (lseek(dst_fd, offset, SEEK_SET) < 0) { + ERRMSG("Can't seek a destination file. %s\n", strerror(errno)); + goto out; + } + if (write(dst_fd, buf, size) != size) { + ERRMSG("Can't write a destination file. %s\n", strerror(errno)); + goto out; + } + ret = TRUE; +out: + free(buf); + return ret; +} + +int reassemble_kdump_header(void) { - int fd, ret = FALSE; - off_t offset_bitmap; + int fd = -1, ret = FALSE; + off_t offset; + unsigned long size; struct disk_dump_header dh; struct kdump_sub_header kh; - char *buf_bitmap; + char *buf_bitmap = NULL; /* * Write common header. @@ -9741,25 +9894,42 @@ reassemble_kdump_header(void) info->name_dumpfile, strerror(errno)); return FALSE; } + memcpy(&info->sub_header, &kh, sizeof(kh)); + + if ((fd = open(SPLITTING_DUMPFILE(0), O_RDONLY)) < 0) { + ERRMSG("Can't open a file(%s). %s\n", + SPLITTING_DUMPFILE(0), strerror(errno)); + return FALSE; + } + if (info->offset_note && info->size_note) { + offset = info->offset_note; + size = info->size_note; + if (!copy_same_data(fd, info->fd_dumpfile, offset, size)) { + ERRMSG("Can't copy pt_note data to %s.\n", + info->name_dumpfile); + goto out; + } + } + if (info->offset_vmcoreinfo && info->size_vmcoreinfo) { + offset = info->offset_vmcoreinfo; + size = info->size_vmcoreinfo; + if (!copy_same_data(fd, info->fd_dumpfile, offset, size)) { + ERRMSG("Can't copy vmcoreinfo data to %s.\n", + info->name_dumpfile); + goto out; + } + } /* * Write dump bitmap to both a dumpfile and a bitmap file. */ - offset_bitmap - = (DISKDUMP_HEADER_BLOCKS + dh.sub_hdr_size) * dh.block_size; + offset = (DISKDUMP_HEADER_BLOCKS + dh.sub_hdr_size) * dh.block_size; info->len_bitmap = dh.bitmap_blocks * dh.block_size; if ((buf_bitmap = malloc(info->len_bitmap)) == NULL) { ERRMSG("Can't allcate memory for bitmap.\n"); - return FALSE; - } - - if ((fd = open(SPLITTING_DUMPFILE(0), O_RDONLY)) < 0) { - ERRMSG("Can't open a file(%s). %s\n", - SPLITTING_DUMPFILE(0), strerror(errno)); - free(buf_bitmap); - return FALSE; + goto out; } - if (lseek(fd, offset_bitmap, SEEK_SET) < 0) { + if (lseek(fd, offset, SEEK_SET) < 0) { ERRMSG("Can't seek a file(%s). %s\n", SPLITTING_DUMPFILE(0), strerror(errno)); goto out; @@ -9770,7 +9940,7 @@ reassemble_kdump_header(void) goto out; } - if (lseek(info->fd_dumpfile, offset_bitmap, SEEK_SET) < 0) { + if (lseek(info->fd_dumpfile, offset, SEEK_SET) < 0) { ERRMSG("Can't seek a file(%s). %s\n", info->name_dumpfile, strerror(errno)); goto out; @@ -9796,7 +9966,9 @@ reassemble_kdump_header(void) ret = TRUE; out: - close(fd); + if (fd > 0) + close(fd); + free(buf_bitmap); return ret; } @@ -9932,6 +10104,7 @@ reassemble_kdump_pages(void) goto out; info->offset_eraseinfo = cd_data.offset; + info->size_eraseinfo = 0; /* Copy eraseinfo from split dumpfiles to o/p dumpfile */ for (i = 0; i < info->num_dumpfile; i++) { if (!SPLITTING_SIZE_EI(i)) @@ -9968,12 +10141,13 @@ reassemble_kdump_pages(void) close(fd); fd = 0; } - if (!write_cache_bufsz(&cd_data)) - goto out; - - if (!update_sub_header()) - goto out; + if (info->size_eraseinfo) { + if (!write_cache_bufsz(&cd_data)) + goto out; + if (!update_sub_header()) + goto out; + } print_progress(PROGRESS_COPY, num_dumpable, num_dumpable); ret = TRUE; out: diff --git a/makedumpfile-1.3.5/makedumpfile.h b/makedumpfile-1.3.5/makedumpfile.h index 312a0c3..6cc36c4 100644 --- a/makedumpfile-1.3.5/makedumpfile.h +++ b/makedumpfile-1.3.5/makedumpfile.h @@ -504,21 +504,8 @@ do { \ #define XEN_ELFNOTE_CRASH_INFO (0x1000001) #define SIZE_XEN_CRASH_INFO_V2 (sizeof(unsigned long) * 10) -#define MAX_SIZE_STR_LEN (21) +#define MAX_SIZE_STR_LEN (26) -/* - * ELF note section for erase information - * - * According to elf.h the unused values are 0x15(21) through 0xff. The value - * range 0x1XX, 0x2XX and 0x3XX is been used for PPC, i386 and s390 - * respectively. - * - * Using 0xff to be on safer side so that any new Elf Note addition in elf.h - * after 0x15 value would not clash. - */ -#ifndef NT_ERASE_INFO -#define NT_ERASE_INFO (0xff) /* Contains erased information. */ -#endif #define ERASEINFO_NOTE_NAME "ERASEINFO" #define ERASEINFO_NOTE_NAME_BYTES (sizeof(ERASEINFO_NOTE_NAME)) @@ -1244,7 +1231,6 @@ enum { DWARF_INFO_CHECK_SYMBOL_ARRAY_TYPE, DWARF_INFO_GET_SYMBOL_TYPE, DWARF_INFO_GET_MEMBER_TYPE, - DWARF_INFO_GET_PTR_SIZE, }; struct dwarf_info { @@ -1310,7 +1296,7 @@ struct config_entry { unsigned short flag; unsigned short nullify; unsigned long long sym_addr; /* Symbol address */ - unsigned long long addr; /* Symbol address or + unsigned long addr; /* Symbol address or value pointed by sym_addr */ unsigned long long cmp_addr; /* for LIST_ENTRY */ unsigned long offset; diff --git a/makedumpfile-1.3.5/s390x.c b/makedumpfile-1.3.5/s390x.c index 3ee1e7a..5e136de 100644 --- a/makedumpfile-1.3.5/s390x.c +++ b/makedumpfile-1.3.5/s390x.c @@ -274,9 +274,7 @@ vaddr_to_paddr_s390x(unsigned long vaddr) paddr = vtop_s390x(vaddr); } else { - ERRMSG("Can't convert a virtual address(%lx) to " \ - "physical address.\n", vaddr); - return NOT_PADDR; + paddr = vaddr - KVBASE; } return paddr;