| Index: third_party/crazy_linker/crazy_linker/src/crazy_linker_proc_maps.cpp
|
| diff --git a/third_party/crazy_linker/crazy_linker/src/crazy_linker_proc_maps.cpp b/third_party/crazy_linker/crazy_linker/src/crazy_linker_proc_maps.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..c4ea3e669705c802ece8c5201b2973c51fbd9d27
|
| --- /dev/null
|
| +++ b/third_party/crazy_linker/crazy_linker/src/crazy_linker_proc_maps.cpp
|
| @@ -0,0 +1,301 @@
|
| +// 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_debug.h"
|
| +#include "crazy_linker_line_reader.h"
|
| +#include "crazy_linker_util.h"
|
| +#include "crazy_linker_system.h"
|
| +
|
| +namespace crazy {
|
| +
|
| +namespace {
|
| +
|
| +// 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,
|
| + ProcMaps::Entry* 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;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +// Internal implementation of ProcMaps class.
|
| +class ProcMapsInternal {
|
| + public:
|
| + ProcMapsInternal()
|
| + : index_(0), entries_() {
|
| + }
|
| +
|
| + ~ProcMapsInternal() {
|
| + Reset();
|
| + }
|
| +
|
| + bool Open(const char* path) {
|
| + Reset();
|
| + LineReader reader(path);
|
| + index_ = 0;
|
| + while (reader.GetNextLine()) {
|
| + ProcMaps::Entry entry;
|
| + memset(&entry, 0, sizeof(entry));
|
| + if (!ParseProcMapsLine(reader.line(),
|
| + reader.line() + reader.length(),
|
| + &entry)) {
|
| + // Ignore broken lines.
|
| + continue;
|
| + }
|
| +
|
| + // Reallocate path.
|
| + const char* old_path = entry.path;
|
| + if (old_path) {
|
| + char* new_path = static_cast<char*>(::malloc(entry.path_len + 1));
|
| + ::memcpy(new_path, old_path, entry.path_len);
|
| + new_path[entry.path_len] = '\0';
|
| + entry.path = const_cast<const char*>(new_path);
|
| + }
|
| +
|
| + entries_.PushBack(entry);
|
| + }
|
| + return true;
|
| + }
|
| +
|
| + void Rewind() {
|
| + index_ = 0;
|
| + }
|
| +
|
| + bool GetNextEntry(ProcMaps::Entry* entry) {
|
| + if (index_ >= entries_.GetCount())
|
| + return false;
|
| +
|
| + *entry = entries_[index_++];
|
| + return true;
|
| + }
|
| +
|
| + private:
|
| + void Reset() {
|
| + for (size_t n = 0; n < entries_.GetCount(); ++n) {
|
| + ProcMaps::Entry& entry = entries_[n];
|
| + ::free(const_cast<char*>(entry.path));
|
| + }
|
| + entries_.Resize(0);
|
| + }
|
| +
|
| + size_t index_;
|
| + Vector<ProcMaps::Entry> entries_;
|
| +};
|
| +
|
| +ProcMaps::ProcMaps() {
|
| + internal_ = new ProcMapsInternal();
|
| + (void) internal_->Open("/proc/self/maps");
|
| +}
|
| +
|
| +ProcMaps::ProcMaps(pid_t pid) {
|
| + internal_ = new ProcMapsInternal();
|
| + char maps_file[32];
|
| + snprintf(maps_file, sizeof maps_file, "/proc/%u/maps", pid);
|
| + (void) internal_->Open(maps_file);
|
| +}
|
| +
|
| +ProcMaps::~ProcMaps() {
|
| + delete internal_;
|
| +}
|
| +
|
| +void ProcMaps::Rewind() {
|
| + internal_->Rewind();
|
| +}
|
| +
|
| +bool ProcMaps::GetNextEntry(Entry* entry) {
|
| + return internal_->GetNextEntry(entry);
|
| +}
|
| +
|
| +int ProcMaps::GetProtectionFlagsForAddress(void* address) {
|
| + size_t vma_addr = reinterpret_cast<size_t>(address);
|
| + internal_->Rewind();
|
| + ProcMaps::Entry entry;
|
| + while (internal_->GetNextEntry(&entry)) {
|
| + if (entry.vma_start <= vma_addr && vma_addr < entry.vma_end)
|
| + return entry.prot_flags;
|
| + }
|
| + return 0;
|
| +}
|
| +
|
| +bool FindElfBinaryForAddress(void* address,
|
| + uintptr_t* load_address,
|
| + char* path_buffer,
|
| + size_t path_buffer_len) {
|
| + ProcMaps self_maps;
|
| + ProcMaps::Entry entry;
|
| +
|
| + uintptr_t addr = reinterpret_cast<uintptr_t>(address);
|
| +
|
| + while (self_maps.GetNextEntry(&entry)) {
|
| + if (entry.vma_start <= addr && addr < entry.vma_end) {
|
| + *load_address = entry.vma_start;
|
| + if (!entry.path) {
|
| + LOG("Could not find ELF binary path!?\n");
|
| + return false;
|
| + }
|
| + if (entry.path_len >= path_buffer_len) {
|
| + LOG("ELF binary path too long: '%s'\n", entry.path);
|
| + return false;
|
| + }
|
| + memcpy(path_buffer, entry.path, entry.path_len);
|
| + path_buffer[entry.path_len] = '\0';
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +// 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) {
|
| + ProcMaps self_maps;
|
| + ProcMaps::Entry entry;
|
| +
|
| + uintptr_t addr = reinterpret_cast<uintptr_t>(address);
|
| +
|
| + while (self_maps.GetNextEntry(&entry)) {
|
| + if (entry.vma_start <= addr && addr < entry.vma_end) {
|
| + *prot_flags = entry.prot_flags;
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +bool FindLoadAddressForFile(const char* file_name,
|
| + uintptr_t* load_address,
|
| + uintptr_t* load_offset) {
|
| + size_t file_name_len = strlen(file_name);
|
| + bool is_base_name = (strchr(file_name, '/') == NULL);
|
| + ProcMaps self_maps;
|
| + ProcMaps::Entry 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
|
|
|