diff options
Diffstat (limited to 'breakpad/linux/linux_dumper.cc')
-rw-r--r-- | breakpad/linux/linux_dumper.cc | 154 |
1 files changed, 119 insertions, 35 deletions
diff --git a/breakpad/linux/linux_dumper.cc b/breakpad/linux/linux_dumper.cc index a40f7cd..3dc64db 100644 --- a/breakpad/linux/linux_dumper.cc +++ b/breakpad/linux/linux_dumper.cc @@ -110,73 +110,158 @@ bool LinuxDumper::ThreadsResume() { return good; } +void +LinuxDumper::BuildProcPath(char* path, pid_t pid, const char* node) const { + assert(path); + if (!path) { + return; + } + + path[0] = '\0'; + + const unsigned pid_len = my_int_len(pid); + + assert(node); + if (!node) { + return; + } + + size_t node_len = my_strlen(node); + assert(node_len < NAME_MAX); + if (node_len >= NAME_MAX) { + return; + } + + assert(node_len > 0); + if (node_len == 0) { + return; + } + + assert(pid > 0); + if (pid <= 0) { + return; + } + + const size_t total_length = 6 + pid_len + 1 + node_len; + + assert(total_length < NAME_MAX); + if (total_length >= NAME_MAX) { + return; + } + + memcpy(path, "/proc/", 6); + my_itos(path + 6, pid, pid_len); + memcpy(path + 6 + pid_len, "/", 1); + memcpy(path + 6 + pid_len + 1, node, node_len); + memcpy(path + total_length, "\0", 1); +} + +void* +LinuxDumper::FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const { + char auxv_path[80]; + BuildProcPath(auxv_path, pid, "auxv"); + + // If BuildProcPath errors out due to invalid input, we'll handle it when + // we try to sys_open the file. + + // Find the AT_SYSINFO_EHDR entry for linux-gate.so + // See http://www.trilithium.com/johan/2005/08/linux-gate/ for more + // information. + int fd = sys_open(auxv_path, O_RDONLY, 0); + if (fd < 0) { + return NULL; + } + + elf_aux_entry one_aux_entry; + while (sys_read(fd, + &one_aux_entry, + sizeof(elf_aux_entry)) == sizeof(elf_aux_entry) && + one_aux_entry.a_type != AT_NULL) { + if (one_aux_entry.a_type == AT_SYSINFO_EHDR) { + close(fd); + return reinterpret_cast<void*>(one_aux_entry.a_un.a_val); + } + } + close(fd); + return NULL; +} + bool -LinuxDumper::EnumerateMappings(wasteful_vector<MappingInfo*> *result) const { +LinuxDumper::EnumerateMappings(wasteful_vector<MappingInfo*>* result) const { char maps_path[80]; - memcpy(maps_path, "/proc/", 6); - const unsigned pid_len = my_int_len(pid_); - my_itos(maps_path + 6, pid_, pid_len); - memcpy(maps_path + 6 + pid_len, "/maps", 6); + BuildProcPath(maps_path, pid_, "maps"); + + // linux_gate_loc is the beginning of the kernel's mapping of + // linux-gate.so in the process. It doesn't actually show up in the + // maps list as a filename, so we use the aux vector to find it's + // load location and special case it's entry when creating the list + // of mappings. + const void* linux_gate_loc; + linux_gate_loc = FindBeginningOfLinuxGateSharedLibrary(pid_); const int fd = sys_open(maps_path, O_RDONLY, 0); if (fd < 0) return false; - LineReader *const line_reader = new(allocator_) LineReader(fd); + LineReader* const line_reader = new(allocator_) LineReader(fd); - const char *line; + const char* line; unsigned line_len; while (line_reader->GetNextLine(&line, &line_len)) { uintptr_t start_addr, end_addr, offset; const char* i1 = my_read_hex_ptr(&start_addr, line); if (*i1 == '-') { - const char *i2 = my_read_hex_ptr(&end_addr, i1 + 1); + const char* i2 = my_read_hex_ptr(&end_addr, i1 + 1); if (*i2 == ' ') { - const char *i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */); + const char* i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */); if (*i3 == ' ') { - MappingInfo *const module = new(allocator_) MappingInfo; + MappingInfo* const module = new(allocator_) MappingInfo; memset(module, 0, sizeof(MappingInfo)); module->start_addr = start_addr; module->size = end_addr - start_addr; module->offset = offset; - const char *name = NULL; - // Only copy name if the name is a valid path name. + const char* name = NULL; + // Only copy name if the name is a valid path name, or if + // we've found the VDSO image if ((name = my_strchr(line, '/')) != NULL) { const unsigned l = my_strlen(name); if (l < sizeof(module->name)) memcpy(module->name, name, l); + } else if (linux_gate_loc && + reinterpret_cast<void*>(module->start_addr) == + linux_gate_loc) { + memcpy(module->name, + kLinuxGateLibraryName, + my_strlen(kLinuxGateLibraryName)); + module->offset = 0; } - result->push_back(module); } } } - line_reader->PopLine(line_len); } sys_close(fd); + return result->size() > 0; } // Parse /proc/$pid/task to list all the threads of the process identified by // pid. -bool LinuxDumper::EnumerateThreads(wasteful_vector<pid_t> *result) const { +bool LinuxDumper::EnumerateThreads(wasteful_vector<pid_t>* result) const { char task_path[80]; - memcpy(task_path, "/proc/", 6); - const unsigned pid_len = my_int_len(pid_); - my_itos(task_path + 6, pid_, pid_len); - memcpy(task_path + 6 + pid_len, "/task", 6); + BuildProcPath(task_path, pid_, "task"); const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0); if (fd < 0) return false; - DirectoryReader *dir_reader = new(allocator_) DirectoryReader(fd); + DirectoryReader* dir_reader = new(allocator_) DirectoryReader(fd); // The directory may contain duplicate entries which we filter by assuming - // that they are consecutive + // that they are consecutive. int last_tid = -1; - const char *dent_name; + const char* dent_name; while (dir_reader->GetNextEntry(&dent_name)) { if (my_strcmp(dent_name, ".") && my_strcmp(dent_name, "..")) { @@ -198,19 +283,17 @@ bool LinuxDumper::EnumerateThreads(wasteful_vector<pid_t> *result) const { // Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailible, // these members are set to -1. Returns true iff all three members are // availible. -bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo *info) { +bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) { assert(info != NULL); char status_path[80]; - memcpy(status_path, "/proc/", 6); - const unsigned tid_len = my_int_len(tid); - my_itos(status_path + 6, tid, tid_len); - memcpy(status_path + 6 + tid_len, "/status", 8); + BuildProcPath(status_path, tid, "status"); + const int fd = open(status_path, O_RDONLY); if (fd < 0) return false; - LineReader *const line_reader = new(allocator_) LineReader(fd); - const char *line; + LineReader* const line_reader = new(allocator_) LineReader(fd); + const char* line; unsigned line_len; info->ppid = info->tgid = -1; @@ -240,15 +323,16 @@ bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo *info) { for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) { if (sys_ptrace( PTRACE_PEEKUSER, tid, - (void *) (offsetof(struct user, - u_debugreg[0]) + i * sizeof(debugreg_t)), + reinterpret_cast<void*> (offsetof(struct user, + u_debugreg[0]) + i * + sizeof(debugreg_t)), &info->dregs[i]) == -1) { return false; } } #endif - const uint8_t *stack_pointer; + const uint8_t* stack_pointer; #if defined(__i386) memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp)); #elif defined(__x86_64) @@ -277,12 +361,12 @@ bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len, #endif // Move the stack pointer to the bottom of the page that it's in. uint8_t* const stack_pointer = - (uint8_t *) (int_stack_pointer & ~(page_size - 1)); + reinterpret_cast<uint8_t*>(int_stack_pointer & ~(page_size - 1)); // The number of bytes of stack which we try to capture. static unsigned kStackToCapture = 32 * 1024; - const MappingInfo *mapping = FindMapping(stack_pointer); + const MappingInfo* mapping = FindMapping(stack_pointer); if (!mapping) return false; if (stack_grows_down) { @@ -320,7 +404,7 @@ void LinuxDumper::CopyFromProcess(void* dest, pid_t child, const void* src, } // Find the mapping which the given memory address falls in. -const MappingInfo *LinuxDumper::FindMapping(const void *address) const { +const MappingInfo* LinuxDumper::FindMapping(const void* address) const { const uintptr_t addr = (uintptr_t) address; for (size_t i = 0; i < mappings_.size(); ++i) { |