// quick sort: requires n>1 static void qsort_map_array(map_elem_t *a, uint32_t n) { uint32_t i, j; map_elem_t x, y; // x = random pivot i = random_uint(n); x = a[i]; // swap a[i] and a[0] a[i] = a[0]; a[0] = x; i = 0; j = n; do { j--; } while (a[j].index > x.index); do { i++; } while (i <= j && a[i].index < x.index); while (i < j) { // swap a[i] and a[j] y = a[i]; a[i] = a[j]; a[j] = y; do { j--; } while (a[j].index > x.index); do { i++; } while (a[i].index < x.index); } // swap a[0] = x and a[j] a[0] = a[j]; a[j] = x; // recursive sort sort_map_array(a, j); j ++; sort_map_array(a + j, n - j); }
/* * Normalize map: * - sort all elements in increasing index order */ void normalize_map(map_t *map) { sort_map_array(map->data, map->nelems); }
// the one and only one exposed stuff from this file struct ps_prochandle* Pgrab_core(const char* exec_file, const char* core_file) { ELF_EHDR core_ehdr; ELF_EHDR exec_ehdr; ELF_EHDR lib_ehdr; struct ps_prochandle* ph = (struct ps_prochandle*) calloc(1, sizeof(struct ps_prochandle)); if (ph == NULL) { print_debug("can't allocate ps_prochandle\n"); return NULL; } if ((ph->core = (struct core_data*) calloc(1, sizeof(struct core_data))) == NULL) { free(ph); print_debug("can't allocate ps_prochandle\n"); return NULL; } // initialize ph ph->ops = &core_ops; ph->core->core_fd = -1; ph->core->exec_fd = -1; ph->core->interp_fd = -1; // open the core file if ((ph->core->core_fd = open(core_file, O_RDONLY)) < 0) { print_debug("can't open core file\n"); goto err; } // read core file ELF header if (read_elf_header(ph->core->core_fd, &core_ehdr) != true || core_ehdr.e_type != ET_CORE) { print_debug("core file is not a valid ELF ET_CORE file\n"); goto err; } if ((ph->core->exec_fd = open(exec_file, O_RDONLY)) < 0) { print_debug("can't open executable file\n"); goto err; } if (read_elf_header(ph->core->exec_fd, &exec_ehdr) != true || exec_ehdr.e_type != ET_EXEC) { print_debug("executable file is not a valid ELF ET_EXEC file\n"); goto err; } // process core file segments if (read_core_segments(ph, &core_ehdr) != true) goto err; // process exec file segments if (read_exec_segments(ph, &exec_ehdr) != true) goto err; // exec file is also treated like a shared object for symbol search if (add_lib_info_fd(ph, exec_file, ph->core->exec_fd, (uintptr_t)0 + find_base_address(ph->core->exec_fd, &exec_ehdr)) == NULL) goto err; // allocate and sort maps into map_array, we need to do this // here because read_shared_lib_info needs to read from debuggee // address space if (sort_map_array(ph) != true) goto err; if (read_shared_lib_info(ph) != true) goto err; // sort again because we have added more mappings from shared objects if (sort_map_array(ph) != true) goto err; if (init_classsharing_workaround(ph) != true) goto err; return ph; err: Prelease(ph); return NULL; }
// read shared library info from runtime linker's data structures. // This work is done by librtlb_db in Solaris static bool read_shared_lib_info(struct ps_prochandle* ph) { uintptr_t addr = ph->core->dynamic_addr; uintptr_t debug_base; uintptr_t first_link_map_addr; uintptr_t ld_base_addr; uintptr_t link_map_addr; uintptr_t lib_base_diff; uintptr_t lib_base; uintptr_t lib_name_addr; char lib_name[BUF_SIZE]; ELF_DYN dyn; ELF_EHDR elf_ehdr; int lib_fd; // _DYNAMIC has information of the form // [tag] [data] [tag] [data] ..... // Both tag and data are pointer sized. // We look for dynamic info with DT_DEBUG. This has shared object info. // refer to struct r_debug in link.h dyn.d_tag = DT_NULL; while (dyn.d_tag != DT_DEBUG) { if (ps_pdread(ph, (psaddr_t) addr, &dyn, sizeof(ELF_DYN)) != PS_OK) { print_debug("can't read debug info from _DYNAMIC\n"); return false; } addr += sizeof(ELF_DYN); } // we have got Dyn entry with DT_DEBUG debug_base = dyn.d_un.d_ptr; // at debug_base we have struct r_debug. This has first link map in r_map field if (ps_pdread(ph, (psaddr_t) debug_base + FIRST_LINK_MAP_OFFSET, &first_link_map_addr, sizeof(uintptr_t)) != PS_OK) { print_debug("can't read first link map address\n"); return false; } // read ld_base address from struct r_debug if (ps_pdread(ph, (psaddr_t) debug_base + LD_BASE_OFFSET, &ld_base_addr, sizeof(uintptr_t)) != PS_OK) { print_debug("can't read ld base address\n"); return false; } ph->core->ld_base_addr = ld_base_addr; print_debug("interpreter base address is 0x%lx\n", ld_base_addr); // now read segments from interp (i.e ld.so or ld-linux.so) if (read_interp_segments(ph) != true) return false; // after adding interpreter (ld.so) mappings sort again if (sort_map_array(ph) != true) return false; print_debug("first link map is at 0x%lx\n", first_link_map_addr); link_map_addr = first_link_map_addr; while (link_map_addr != 0) { // read library base address of the .so. Note that even though <sys/link.h> calls // link_map->l_addr as "base address", this is * not * really base virtual // address of the shared object. This is actually the difference b/w the virtual // address mentioned in shared object and the actual virtual base where runtime // linker loaded it. We use "base diff" in read_lib_segments call below. if (ps_pdread(ph, (psaddr_t) link_map_addr + LINK_MAP_ADDR_OFFSET, &lib_base_diff, sizeof(uintptr_t)) != PS_OK) { print_debug("can't read shared object base address diff\n"); return false; } // read address of the name if (ps_pdread(ph, (psaddr_t) link_map_addr + LINK_MAP_NAME_OFFSET, &lib_name_addr, sizeof(uintptr_t)) != PS_OK) { print_debug("can't read address of shared object name\n"); return false; } // read name of the shared object lib_name[0] = '\0'; if (lib_name_addr != 0 && read_string(ph, (uintptr_t) lib_name_addr, lib_name, sizeof(lib_name)) != true) { print_debug("can't read shared object name\n"); // don't let failure to read the name stop opening the file. If something is really wrong // it will fail later. } if (lib_name[0] != '\0') { // ignore empty lib names lib_fd = pathmap_open(lib_name); if (lib_fd < 0) { print_debug("can't open shared object %s\n", lib_name); // continue with other libraries... } else { if (read_elf_header(lib_fd, &elf_ehdr)) { lib_base = lib_base_diff + find_base_address(lib_fd, &elf_ehdr); print_debug("reading library %s @ 0x%lx [ 0x%lx ]\n", lib_name, lib_base, lib_base_diff); // while adding library mappings we need to use "base difference". if (! read_lib_segments(ph, lib_fd, &elf_ehdr, lib_base_diff)) { print_debug("can't read shared object's segments\n"); close(lib_fd); return false; } add_lib_info_fd(ph, lib_name, lib_fd, lib_base); // Map info is added for the library (lib_name) so // we need to re-sort it before calling the p_pdread. if (sort_map_array(ph) != true) return false; } else { print_debug("can't read ELF header for shared object %s\n", lib_name); close(lib_fd); // continue with other libraries... } } } // read next link_map address if (ps_pdread(ph, (psaddr_t) link_map_addr + LINK_MAP_NEXT_OFFSET, &link_map_addr, sizeof(uintptr_t)) != PS_OK) { print_debug("can't read next link in link_map\n"); return false; } } return true; }