Index: base/android/linker/crazy_linker/src/crazy_linker_proc_maps.cpp |
diff --git a/base/android/linker/crazy_linker/src/crazy_linker_proc_maps.cpp b/base/android/linker/crazy_linker/src/crazy_linker_proc_maps.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..831167568c41d31df3696b619d86f932678728e2 |
--- /dev/null |
+++ b/base/android/linker/crazy_linker/src/crazy_linker_proc_maps.cpp |
@@ -0,0 +1,284 @@ |
+// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "crazy_linker_proc_maps.h" |
+ |
+#include <inttypes.h> |
+#include <limits.h> |
+ |
+#include "elf_traits.h" |
+#include "crazy_linker_util.h" |
+#include "crazy_linker_system.h" |
+ |
+namespace crazy { |
+ |
+namespace { |
+ |
+struct ProcMapsEntry { |
+ size_t vma_start; |
+ size_t vma_end; |
+ size_t load_offset; |
+ int prot_flags; |
+ const char* path; // can be NULL, not zero-terminated. |
+ size_t path_len; |
+}; |
+ |
+// Decompose the components of a /proc/$PID/maps file into multiple |
+// components. |line| should be the address of a zero-terminated line |
+// of input. On success, returns true and sets |*entry|, false otherwise. |
+// |
+// IMPORTANT: On success, |entry->path| will point into the input line, |
+// the caller will have to copy the string into a different location if |
+// it needs to persist it. |
+bool ParseProcMapsLine(const char* line, |
+ const char* line_end, |
+ ProcMapsEntry* entry) { |
+ // Example input lines on a 64-bit system, one cannot assume that |
+ // everything is properly sized. |
+ // |
+ // 00400000-0040b000 r-xp 00000000 08:01 6570708 /bin/cat |
+ // 0060a000-0060b000 r--p 0000a000 08:01 6570708 /bin/cat |
+ // 0060b000-0060c000 rw-p 0000b000 08:01 6570708 /bin/cat |
+ // 01dd0000-01df1000 rw-p 00000000 00:00 0 [heap] |
+ // 7f4b8d4d7000-7f4b8e22a000 r--p 00000000 08:01 38666648 /usr/lib/locale/locale-archive |
+ // 7f4b8e22a000-7f4b8e3df000 r-xp 00000000 08:01 28836281 /lib/x86_64-linux-gnu/libc-2.15.so |
+ // 7f4b8e3df000-7f4b8e5de000 ---p 001b5000 08:01 28836281 /lib/x86_64-linux-gnu/libc-2.15.so |
+ // 7f4b8e5de000-7f4b8e5e2000 r--p 001b4000 08:01 28836281 /lib/x86_64-linux-gnu/libc-2.15.so |
+ // 7f4b8e5e2000-7f4b8e5e4000 rw-p 001b8000 08:01 28836281 /lib/x86_64-linux-gnu/libc-2.15.so |
+ const char* p = line; |
+ for (int token = 0; token < 7; ++token) { |
+ char separator = (token == 0) ? '-' : ' '; |
+ // skip leading token separators first. |
+ while (p < line_end && *p == separator) |
+ p++; |
+ |
+ // find start and end of current token, and compute start of |
+ // next search. |
+ const char* tok_start = p; |
+ const char* tok_end = static_cast<const char*>( |
+ memchr(p, separator, line_end - p)); |
+ if (!tok_end) { |
+ tok_end = line_end; |
+ p = line_end; |
+ } else { |
+ p = tok_end + 1; |
+ } |
+ |
+ if (tok_end == tok_start) { |
+ if (token == 6) { |
+ // empty token can happen for index 6, when there is no path |
+ // element on the line. This corresponds to anonymous memory |
+ // mapped segments. |
+ entry->path = NULL; |
+ entry->path_len = 0; |
+ break; |
+ } |
+ return false; |
+ } |
+ |
+ switch (token) { |
+ case 0: // vma_start |
+ entry->vma_start = static_cast<size_t>( |
+ strtoumax(tok_start, NULL, 16)); |
+ break; |
+ |
+ case 1: // vma_end |
+ entry->vma_end = static_cast<size_t>( |
+ strtoumax(tok_start, NULL, 16)); |
+ break; |
+ |
+ case 2: // protection bits |
+ { |
+ int flags = 0; |
+ for (const char* t = tok_start; t < tok_end; ++t) { |
+ if (*t == 'r') |
+ flags |= PROT_READ; |
+ if (*t == 'w') |
+ flags |= PROT_WRITE; |
+ if (*t == 'x') |
+ flags |= PROT_EXEC; |
+ } |
+ entry->prot_flags = flags; |
+ } |
+ break; |
+ |
+ case 3: // page offset |
+ entry->load_offset = static_cast<size_t>( |
+ strtoumax(tok_start, NULL, 16)) * PAGE_SIZE; |
+ break; |
+ |
+ case 6: // path |
+ // Get rid of trailing newlines, if any. |
+ while (tok_end > tok_start && tok_end[-1] == '\n') |
+ tok_end--; |
+ entry->path = tok_start; |
+ entry->path_len = tok_end - tok_start; |
+ break; |
+ |
+ default: // ignore all other tokens. |
+ ; |
+ } |
+ } |
+ return true; |
+} |
+ |
+// Helper class used to parse entries of /proc/self/maps. |
+// Usage: |
+// ProcSelfMaps self_maps; |
+// ProcMapsEntry entry; |
+// while (self_maps.GetNextEntry(&entry)) { |
+// .. proces entry. |
+// } |
+class ProcSelfMaps { |
+public: |
+ ProcSelfMaps(); |
+ ~ProcSelfMaps() {} |
+ bool GetNextEntry(ProcMapsEntry* entry); |
+ bool GetEntryForAddress(void* address, ProcMapsEntry* entry); |
+private: |
+ bool ReadLine(); |
+ |
+ FileDescriptor fd_; |
+ size_t line_len_; |
+ size_t buff_size_; |
+ bool buff_eof_; |
+ char buff_[512]; |
+}; |
+ |
+ProcSelfMaps::ProcSelfMaps() |
+ : fd_(), |
+ line_len_(0), |
+ buff_size_(0), |
+ buff_eof_(false) { |
+ if (!fd_.OpenReadOnly("/proc/self/maps")) |
+ DD_ERRNO("Cannot read /proc/self/maps"); |
+} |
+ |
+bool ProcSelfMaps::GetNextEntry(ProcMapsEntry* entry) { |
+ if (!fd_.IsOk() || !ReadLine()) |
+ return false; |
+ |
+ return ParseProcMapsLine(buff_, buff_ + line_len_, entry); |
+} |
+ |
+bool ProcSelfMaps::GetEntryForAddress(void* address, ProcMapsEntry* entry) { |
+ size_t vma_address = reinterpret_cast<size_t>(address); |
+ while (GetNextEntry(entry)) { |
+ if (entry->vma_start <= vma_address && vma_address < entry->vma_end) |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+bool ProcSelfMaps::ReadLine() { |
+ // Eat previous line in buffer. |
+ if (line_len_) { |
+ ::memmove(buff_, buff_ + line_len_, buff_size_ - line_len_); |
+ buff_size_ -= line_len_; |
+ line_len_ = 0; |
+ } |
+ |
+ for (;;) { |
+ // Is there a newline in the current buffer. |
+ // If so, just return the line-length. |
+ const char* p = |
+ reinterpret_cast<const char*>(memchr(buff_, '\n', buff_size_)); |
+ if (p) { |
+ line_len_ = p + 1 - buff_; |
+ return true; |
+ } |
+ |
+ // If the buffer can't be refilled, just return the buffer as-is. |
+ if (buff_eof_ || buff_size_ == sizeof(buff_)) { |
+ // Can't refill the buffer. |
+ line_len_ = buff_size_; |
+ return (line_len_ > 0); |
+ } |
+ |
+ size_t avail = sizeof(buff_) - buff_size_; |
+ int ret = fd_.Read(buff_ + buff_size_, avail); |
+ if (ret < 0) |
+ ret = 0; |
+ |
+ if (static_cast<size_t>(ret) < avail) |
+ buff_eof_ = true; |
+ |
+ buff_size_ += static_cast<size_t>(ret); |
+ } |
+} |
+ |
+} // namespace |
+ |
+bool FindElfBinaryForAddress(void* address, |
+ uintptr_t* load_address, |
+ char* path_buffer, |
+ size_t path_buffer_len) { |
+ ProcSelfMaps self_maps; |
+ ProcMapsEntry entry; |
+ if (!self_maps.GetEntryForAddress(address, &entry)) |
+ return false; |
+ |
+ *load_address = entry.vma_start; |
+ |
+ if (entry.path == NULL || entry.path_len == 0) { |
+ DD("Could not find ELF binary path!?\n"); |
+ return false; |
+ } |
+ if (entry.path_len >= path_buffer_len) { |
+ DD("ELF binary path too long: '%.*s'\n", entry.path_len, entry.path); |
+ return false; |
+ } |
+ memcpy(path_buffer, entry.path, entry.path_len); |
+ path_buffer[entry.path_len] = '\0'; |
+ |
+ return true; |
+} |
+ |
+// Returns the current protection bit flags for the page holding a given |
+// address. Returns true on success, or false if the address is not mapped. |
+bool FindProtectionFlagsForAddress(void* address, int* prot_flags) { |
+ ProcSelfMaps self_maps; |
+ ProcMapsEntry entry; |
+ if (!self_maps.GetEntryForAddress(address, &entry)) |
+ return false; |
+ *prot_flags = entry.prot_flags; |
+ return true; |
+} |
+ |
+bool FindLoadAddressForFile(const char* file_name, |
+ uintptr_t* load_address, |
+ uintptr_t* load_offset) { |
+ ProcSelfMaps self_maps; |
+ size_t file_name_len = strlen(file_name); |
+ bool is_base_name = (strchr(file_name, '/') == NULL); |
+ ProcMapsEntry entry; |
+ while (self_maps.GetNextEntry(&entry)) { |
+ // Skip vdso et al. |
+ if (entry.path_len == 0 || entry.path[0] == '[') |
+ continue; |
+ |
+ const char* entry_name = entry.path; |
+ size_t entry_len = entry.path_len; |
+ |
+ if (is_base_name) { |
+ const char* p = reinterpret_cast<const char*>( |
+ ::memrchr(entry.path, '/', entry.path_len)); |
+ if (p) { |
+ entry_name = p + 1; |
+ entry_len = entry.path_len - (p - entry.path) - 1; |
+ } |
+ } |
+ |
+ if (file_name_len == entry_len && |
+ !memcmp(file_name, entry_name, entry_len)) { |
+ *load_address = entry.vma_start; |
+ *load_offset = entry.load_offset; |
+ return true; |
+ } |
+ } |
+ |
+ return false; |
+} |
+ |
+} // namespace crazy |