forked from OSchip/llvm-project
[Sanitizer] Move OS-specific code for MemoryMappingLayout into separate source files.
llvm-svn: 198003
This commit is contained in:
parent
3c849f6a06
commit
2f392d237c
|
@ -14,6 +14,9 @@ set(SANITIZER_SOURCES
|
|||
sanitizer_platform_limits_posix.cc
|
||||
sanitizer_posix.cc
|
||||
sanitizer_printf.cc
|
||||
sanitizer_procmaps_linux.cc
|
||||
sanitizer_procmaps_mac.cc
|
||||
sanitizer_procmaps_posix.cc
|
||||
sanitizer_stackdepot.cc
|
||||
sanitizer_stacktrace.cc
|
||||
sanitizer_suppressions.cc
|
||||
|
|
|
@ -318,213 +318,6 @@ void PrepareForSandboxing() {
|
|||
#endif
|
||||
}
|
||||
|
||||
// ----------------- sanitizer_procmaps.h
|
||||
// Linker initialized.
|
||||
ProcSelfMapsBuff MemoryMappingLayout::cached_proc_self_maps_;
|
||||
StaticSpinMutex MemoryMappingLayout::cache_lock_; // Linker initialized.
|
||||
|
||||
MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {
|
||||
proc_self_maps_.len =
|
||||
ReadFileToBuffer("/proc/self/maps", &proc_self_maps_.data,
|
||||
&proc_self_maps_.mmaped_size, 1 << 26);
|
||||
if (cache_enabled) {
|
||||
if (proc_self_maps_.mmaped_size == 0) {
|
||||
LoadFromCache();
|
||||
CHECK_GT(proc_self_maps_.len, 0);
|
||||
}
|
||||
} else {
|
||||
CHECK_GT(proc_self_maps_.mmaped_size, 0);
|
||||
}
|
||||
Reset();
|
||||
// FIXME: in the future we may want to cache the mappings on demand only.
|
||||
if (cache_enabled)
|
||||
CacheMemoryMappings();
|
||||
}
|
||||
|
||||
MemoryMappingLayout::~MemoryMappingLayout() {
|
||||
// Only unmap the buffer if it is different from the cached one. Otherwise
|
||||
// it will be unmapped when the cache is refreshed.
|
||||
if (proc_self_maps_.data != cached_proc_self_maps_.data) {
|
||||
UnmapOrDie(proc_self_maps_.data, proc_self_maps_.mmaped_size);
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryMappingLayout::Reset() {
|
||||
current_ = proc_self_maps_.data;
|
||||
}
|
||||
|
||||
// static
|
||||
void MemoryMappingLayout::CacheMemoryMappings() {
|
||||
SpinMutexLock l(&cache_lock_);
|
||||
// Don't invalidate the cache if the mappings are unavailable.
|
||||
ProcSelfMapsBuff old_proc_self_maps;
|
||||
old_proc_self_maps = cached_proc_self_maps_;
|
||||
cached_proc_self_maps_.len =
|
||||
ReadFileToBuffer("/proc/self/maps", &cached_proc_self_maps_.data,
|
||||
&cached_proc_self_maps_.mmaped_size, 1 << 26);
|
||||
if (cached_proc_self_maps_.mmaped_size == 0) {
|
||||
cached_proc_self_maps_ = old_proc_self_maps;
|
||||
} else {
|
||||
if (old_proc_self_maps.mmaped_size) {
|
||||
UnmapOrDie(old_proc_self_maps.data,
|
||||
old_proc_self_maps.mmaped_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryMappingLayout::LoadFromCache() {
|
||||
SpinMutexLock l(&cache_lock_);
|
||||
if (cached_proc_self_maps_.data) {
|
||||
proc_self_maps_ = cached_proc_self_maps_;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse a hex value in str and update str.
|
||||
static uptr ParseHex(char **str) {
|
||||
uptr x = 0;
|
||||
char *s;
|
||||
for (s = *str; ; s++) {
|
||||
char c = *s;
|
||||
uptr v = 0;
|
||||
if (c >= '0' && c <= '9')
|
||||
v = c - '0';
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
v = c - 'a' + 10;
|
||||
else if (c >= 'A' && c <= 'F')
|
||||
v = c - 'A' + 10;
|
||||
else
|
||||
break;
|
||||
x = x * 16 + v;
|
||||
}
|
||||
*str = s;
|
||||
return x;
|
||||
}
|
||||
|
||||
static bool IsOneOf(char c, char c1, char c2) {
|
||||
return c == c1 || c == c2;
|
||||
}
|
||||
|
||||
static bool IsDecimal(char c) {
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
static bool IsHex(char c) {
|
||||
return (c >= '0' && c <= '9')
|
||||
|| (c >= 'a' && c <= 'f');
|
||||
}
|
||||
|
||||
static uptr ReadHex(const char *p) {
|
||||
uptr v = 0;
|
||||
for (; IsHex(p[0]); p++) {
|
||||
if (p[0] >= '0' && p[0] <= '9')
|
||||
v = v * 16 + p[0] - '0';
|
||||
else
|
||||
v = v * 16 + p[0] - 'a' + 10;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
static uptr ReadDecimal(const char *p) {
|
||||
uptr v = 0;
|
||||
for (; IsDecimal(p[0]); p++)
|
||||
v = v * 10 + p[0] - '0';
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
|
||||
char filename[], uptr filename_size,
|
||||
uptr *protection) {
|
||||
char *last = proc_self_maps_.data + proc_self_maps_.len;
|
||||
if (current_ >= last) return false;
|
||||
uptr dummy;
|
||||
if (!start) start = &dummy;
|
||||
if (!end) end = &dummy;
|
||||
if (!offset) offset = &dummy;
|
||||
char *next_line = (char*)internal_memchr(current_, '\n', last - current_);
|
||||
if (next_line == 0)
|
||||
next_line = last;
|
||||
// Example: 08048000-08056000 r-xp 00000000 03:0c 64593 /foo/bar
|
||||
*start = ParseHex(¤t_);
|
||||
CHECK_EQ(*current_++, '-');
|
||||
*end = ParseHex(¤t_);
|
||||
CHECK_EQ(*current_++, ' ');
|
||||
uptr local_protection = 0;
|
||||
CHECK(IsOneOf(*current_, '-', 'r'));
|
||||
if (*current_++ == 'r')
|
||||
local_protection |= kProtectionRead;
|
||||
CHECK(IsOneOf(*current_, '-', 'w'));
|
||||
if (*current_++ == 'w')
|
||||
local_protection |= kProtectionWrite;
|
||||
CHECK(IsOneOf(*current_, '-', 'x'));
|
||||
if (*current_++ == 'x')
|
||||
local_protection |= kProtectionExecute;
|
||||
CHECK(IsOneOf(*current_, 's', 'p'));
|
||||
if (*current_++ == 's')
|
||||
local_protection |= kProtectionShared;
|
||||
if (protection) {
|
||||
*protection = local_protection;
|
||||
}
|
||||
CHECK_EQ(*current_++, ' ');
|
||||
*offset = ParseHex(¤t_);
|
||||
CHECK_EQ(*current_++, ' ');
|
||||
ParseHex(¤t_);
|
||||
CHECK_EQ(*current_++, ':');
|
||||
ParseHex(¤t_);
|
||||
CHECK_EQ(*current_++, ' ');
|
||||
while (IsDecimal(*current_))
|
||||
current_++;
|
||||
// Qemu may lack the trailing space.
|
||||
// http://code.google.com/p/address-sanitizer/issues/detail?id=160
|
||||
// CHECK_EQ(*current_++, ' ');
|
||||
// Skip spaces.
|
||||
while (current_ < next_line && *current_ == ' ')
|
||||
current_++;
|
||||
// Fill in the filename.
|
||||
uptr i = 0;
|
||||
while (current_ < next_line) {
|
||||
if (filename && i < filename_size - 1)
|
||||
filename[i++] = *current_;
|
||||
current_++;
|
||||
}
|
||||
if (filename && i < filename_size)
|
||||
filename[i] = 0;
|
||||
current_ = next_line + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Gets the object name and the offset by walking MemoryMappingLayout.
|
||||
bool MemoryMappingLayout::GetObjectNameAndOffset(uptr addr, uptr *offset,
|
||||
char filename[],
|
||||
uptr filename_size,
|
||||
uptr *protection) {
|
||||
return IterateForObjectNameAndOffset(addr, offset, filename, filename_size,
|
||||
protection);
|
||||
}
|
||||
|
||||
void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) {
|
||||
char *smaps = 0;
|
||||
uptr smaps_cap = 0;
|
||||
uptr smaps_len = ReadFileToBuffer("/proc/self/smaps",
|
||||
&smaps, &smaps_cap, 64<<20);
|
||||
uptr start = 0;
|
||||
bool file = false;
|
||||
const char *pos = smaps;
|
||||
while (pos < smaps + smaps_len) {
|
||||
if (IsHex(pos[0])) {
|
||||
start = ReadHex(pos);
|
||||
for (; *pos != '/' && *pos > '\n'; pos++) {}
|
||||
file = *pos == '/';
|
||||
} else if (internal_strncmp(pos, "Rss:", 4) == 0) {
|
||||
for (; *pos < '0' || *pos > '9'; pos++) {}
|
||||
uptr rss = ReadDecimal(pos) * 1024;
|
||||
cb(start, rss, file, stats, stats_size);
|
||||
}
|
||||
while (*pos++ != '\n') {}
|
||||
}
|
||||
UnmapOrDie(smaps, smaps_cap);
|
||||
}
|
||||
|
||||
enum MutexState {
|
||||
MtxUnlocked = 0,
|
||||
MtxLocked = 1,
|
||||
|
|
|
@ -30,8 +30,6 @@
|
|||
|
||||
#include <crt_externs.h> // for _NSGetEnviron
|
||||
#include <fcntl.h>
|
||||
#include <mach-o/dyld.h>
|
||||
#include <mach-o/loader.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <sys/mman.h>
|
||||
|
@ -179,148 +177,6 @@ uptr GetPageSize() {
|
|||
return sysconf(_SC_PAGESIZE);
|
||||
}
|
||||
|
||||
// ----------------- sanitizer_procmaps.h
|
||||
|
||||
MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {
|
||||
Reset();
|
||||
}
|
||||
|
||||
MemoryMappingLayout::~MemoryMappingLayout() {
|
||||
}
|
||||
|
||||
// More information about Mach-O headers can be found in mach-o/loader.h
|
||||
// Each Mach-O image has a header (mach_header or mach_header_64) starting with
|
||||
// a magic number, and a list of linker load commands directly following the
|
||||
// header.
|
||||
// A load command is at least two 32-bit words: the command type and the
|
||||
// command size in bytes. We're interested only in segment load commands
|
||||
// (LC_SEGMENT and LC_SEGMENT_64), which tell that a part of the file is mapped
|
||||
// into the task's address space.
|
||||
// The |vmaddr|, |vmsize| and |fileoff| fields of segment_command or
|
||||
// segment_command_64 correspond to the memory address, memory size and the
|
||||
// file offset of the current memory segment.
|
||||
// Because these fields are taken from the images as is, one needs to add
|
||||
// _dyld_get_image_vmaddr_slide() to get the actual addresses at runtime.
|
||||
|
||||
void MemoryMappingLayout::Reset() {
|
||||
// Count down from the top.
|
||||
// TODO(glider): as per man 3 dyld, iterating over the headers with
|
||||
// _dyld_image_count is thread-unsafe. We need to register callbacks for
|
||||
// adding and removing images which will invalidate the MemoryMappingLayout
|
||||
// state.
|
||||
current_image_ = _dyld_image_count();
|
||||
current_load_cmd_count_ = -1;
|
||||
current_load_cmd_addr_ = 0;
|
||||
current_magic_ = 0;
|
||||
current_filetype_ = 0;
|
||||
}
|
||||
|
||||
// static
|
||||
void MemoryMappingLayout::CacheMemoryMappings() {
|
||||
// No-op on Mac for now.
|
||||
}
|
||||
|
||||
void MemoryMappingLayout::LoadFromCache() {
|
||||
// No-op on Mac for now.
|
||||
}
|
||||
|
||||
// Next and NextSegmentLoad were inspired by base/sysinfo.cc in
|
||||
// Google Perftools, http://code.google.com/p/google-perftools.
|
||||
|
||||
// NextSegmentLoad scans the current image for the next segment load command
|
||||
// and returns the start and end addresses and file offset of the corresponding
|
||||
// segment.
|
||||
// Note that the segment addresses are not necessarily sorted.
|
||||
template<u32 kLCSegment, typename SegmentCommand>
|
||||
bool MemoryMappingLayout::NextSegmentLoad(
|
||||
uptr *start, uptr *end, uptr *offset,
|
||||
char filename[], uptr filename_size, uptr *protection) {
|
||||
if (protection)
|
||||
UNIMPLEMENTED();
|
||||
const char* lc = current_load_cmd_addr_;
|
||||
current_load_cmd_addr_ += ((const load_command *)lc)->cmdsize;
|
||||
if (((const load_command *)lc)->cmd == kLCSegment) {
|
||||
const sptr dlloff = _dyld_get_image_vmaddr_slide(current_image_);
|
||||
const SegmentCommand* sc = (const SegmentCommand *)lc;
|
||||
if (start) *start = sc->vmaddr + dlloff;
|
||||
if (end) *end = sc->vmaddr + sc->vmsize + dlloff;
|
||||
if (offset) {
|
||||
if (current_filetype_ == /*MH_EXECUTE*/ 0x2) {
|
||||
*offset = sc->vmaddr;
|
||||
} else {
|
||||
*offset = sc->fileoff;
|
||||
}
|
||||
}
|
||||
if (filename) {
|
||||
internal_strncpy(filename, _dyld_get_image_name(current_image_),
|
||||
filename_size);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
|
||||
char filename[], uptr filename_size,
|
||||
uptr *protection) {
|
||||
for (; current_image_ >= 0; current_image_--) {
|
||||
const mach_header* hdr = _dyld_get_image_header(current_image_);
|
||||
if (!hdr) continue;
|
||||
if (current_load_cmd_count_ < 0) {
|
||||
// Set up for this image;
|
||||
current_load_cmd_count_ = hdr->ncmds;
|
||||
current_magic_ = hdr->magic;
|
||||
current_filetype_ = hdr->filetype;
|
||||
switch (current_magic_) {
|
||||
#ifdef MH_MAGIC_64
|
||||
case MH_MAGIC_64: {
|
||||
current_load_cmd_addr_ = (char*)hdr + sizeof(mach_header_64);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case MH_MAGIC: {
|
||||
current_load_cmd_addr_ = (char*)hdr + sizeof(mach_header);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (; current_load_cmd_count_ >= 0; current_load_cmd_count_--) {
|
||||
switch (current_magic_) {
|
||||
// current_magic_ may be only one of MH_MAGIC, MH_MAGIC_64.
|
||||
#ifdef MH_MAGIC_64
|
||||
case MH_MAGIC_64: {
|
||||
if (NextSegmentLoad<LC_SEGMENT_64, struct segment_command_64>(
|
||||
start, end, offset, filename, filename_size, protection))
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case MH_MAGIC: {
|
||||
if (NextSegmentLoad<LC_SEGMENT, struct segment_command>(
|
||||
start, end, offset, filename, filename_size, protection))
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If we get here, no more load_cmd's in this image talk about
|
||||
// segments. Go on to the next image.
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MemoryMappingLayout::GetObjectNameAndOffset(uptr addr, uptr *offset,
|
||||
char filename[],
|
||||
uptr filename_size,
|
||||
uptr *protection) {
|
||||
return IterateForObjectNameAndOffset(addr, offset, filename, filename_size,
|
||||
protection);
|
||||
}
|
||||
|
||||
BlockingMutex::BlockingMutex(LinkerInitialized) {
|
||||
// We assume that OS_SPINLOCK_INIT is zero
|
||||
}
|
||||
|
|
|
@ -71,33 +71,10 @@ class MemoryMappingLayout {
|
|||
// lookup.
|
||||
bool IterateForObjectNameAndOffset(uptr addr, uptr *offset,
|
||||
char filename[], uptr filename_size,
|
||||
uptr *protection) {
|
||||
Reset();
|
||||
uptr start, end, file_offset;
|
||||
for (int i = 0; Next(&start, &end, &file_offset, filename, filename_size,
|
||||
protection);
|
||||
i++) {
|
||||
if (addr >= start && addr < end) {
|
||||
// Don't subtract 'start' for the first entry:
|
||||
// * If a binary is compiled w/o -pie, then the first entry in
|
||||
// process maps is likely the binary itself (all dynamic libs
|
||||
// are mapped higher in address space). For such a binary,
|
||||
// instruction offset in binary coincides with the actual
|
||||
// instruction address in virtual memory (as code section
|
||||
// is mapped to a fixed memory range).
|
||||
// * If a binary is compiled with -pie, all the modules are
|
||||
// mapped high at address space (in particular, higher than
|
||||
// shadow memory of the tool), so the module can't be the
|
||||
// first entry.
|
||||
*offset = (addr - (i ? start : 0)) + file_offset;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (filename_size)
|
||||
filename[0] = '\0';
|
||||
return false;
|
||||
}
|
||||
uptr *protection);
|
||||
|
||||
// FIXME: Hide implementation details for different platforms in
|
||||
// platform-specific files.
|
||||
# if SANITIZER_LINUX
|
||||
ProcSelfMapsBuff proc_self_maps_;
|
||||
char *current_;
|
||||
|
|
|
@ -0,0 +1,227 @@
|
|||
//===-- sanitizer_procmaps_linux.cc ---------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Information about the process mappings (Linux-specific parts).
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
#if SANITIZER_LINUX
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_procmaps.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// Linker initialized.
|
||||
ProcSelfMapsBuff MemoryMappingLayout::cached_proc_self_maps_;
|
||||
StaticSpinMutex MemoryMappingLayout::cache_lock_; // Linker initialized.
|
||||
|
||||
MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {
|
||||
proc_self_maps_.len =
|
||||
ReadFileToBuffer("/proc/self/maps", &proc_self_maps_.data,
|
||||
&proc_self_maps_.mmaped_size, 1 << 26);
|
||||
if (cache_enabled) {
|
||||
if (proc_self_maps_.mmaped_size == 0) {
|
||||
LoadFromCache();
|
||||
CHECK_GT(proc_self_maps_.len, 0);
|
||||
}
|
||||
} else {
|
||||
CHECK_GT(proc_self_maps_.mmaped_size, 0);
|
||||
}
|
||||
Reset();
|
||||
// FIXME: in the future we may want to cache the mappings on demand only.
|
||||
if (cache_enabled)
|
||||
CacheMemoryMappings();
|
||||
}
|
||||
|
||||
MemoryMappingLayout::~MemoryMappingLayout() {
|
||||
// Only unmap the buffer if it is different from the cached one. Otherwise
|
||||
// it will be unmapped when the cache is refreshed.
|
||||
if (proc_self_maps_.data != cached_proc_self_maps_.data) {
|
||||
UnmapOrDie(proc_self_maps_.data, proc_self_maps_.mmaped_size);
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryMappingLayout::Reset() {
|
||||
current_ = proc_self_maps_.data;
|
||||
}
|
||||
|
||||
// static
|
||||
void MemoryMappingLayout::CacheMemoryMappings() {
|
||||
SpinMutexLock l(&cache_lock_);
|
||||
// Don't invalidate the cache if the mappings are unavailable.
|
||||
ProcSelfMapsBuff old_proc_self_maps;
|
||||
old_proc_self_maps = cached_proc_self_maps_;
|
||||
cached_proc_self_maps_.len =
|
||||
ReadFileToBuffer("/proc/self/maps", &cached_proc_self_maps_.data,
|
||||
&cached_proc_self_maps_.mmaped_size, 1 << 26);
|
||||
if (cached_proc_self_maps_.mmaped_size == 0) {
|
||||
cached_proc_self_maps_ = old_proc_self_maps;
|
||||
} else {
|
||||
if (old_proc_self_maps.mmaped_size) {
|
||||
UnmapOrDie(old_proc_self_maps.data,
|
||||
old_proc_self_maps.mmaped_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryMappingLayout::LoadFromCache() {
|
||||
SpinMutexLock l(&cache_lock_);
|
||||
if (cached_proc_self_maps_.data) {
|
||||
proc_self_maps_ = cached_proc_self_maps_;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse a hex value in str and update str.
|
||||
static uptr ParseHex(char **str) {
|
||||
uptr x = 0;
|
||||
char *s;
|
||||
for (s = *str; ; s++) {
|
||||
char c = *s;
|
||||
uptr v = 0;
|
||||
if (c >= '0' && c <= '9')
|
||||
v = c - '0';
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
v = c - 'a' + 10;
|
||||
else if (c >= 'A' && c <= 'F')
|
||||
v = c - 'A' + 10;
|
||||
else
|
||||
break;
|
||||
x = x * 16 + v;
|
||||
}
|
||||
*str = s;
|
||||
return x;
|
||||
}
|
||||
|
||||
static bool IsOneOf(char c, char c1, char c2) {
|
||||
return c == c1 || c == c2;
|
||||
}
|
||||
|
||||
static bool IsDecimal(char c) {
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
static bool IsHex(char c) {
|
||||
return (c >= '0' && c <= '9')
|
||||
|| (c >= 'a' && c <= 'f');
|
||||
}
|
||||
|
||||
static uptr ReadHex(const char *p) {
|
||||
uptr v = 0;
|
||||
for (; IsHex(p[0]); p++) {
|
||||
if (p[0] >= '0' && p[0] <= '9')
|
||||
v = v * 16 + p[0] - '0';
|
||||
else
|
||||
v = v * 16 + p[0] - 'a' + 10;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
static uptr ReadDecimal(const char *p) {
|
||||
uptr v = 0;
|
||||
for (; IsDecimal(p[0]); p++)
|
||||
v = v * 10 + p[0] - '0';
|
||||
return v;
|
||||
}
|
||||
|
||||
bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
|
||||
char filename[], uptr filename_size,
|
||||
uptr *protection) {
|
||||
char *last = proc_self_maps_.data + proc_self_maps_.len;
|
||||
if (current_ >= last) return false;
|
||||
uptr dummy;
|
||||
if (!start) start = &dummy;
|
||||
if (!end) end = &dummy;
|
||||
if (!offset) offset = &dummy;
|
||||
char *next_line = (char*)internal_memchr(current_, '\n', last - current_);
|
||||
if (next_line == 0)
|
||||
next_line = last;
|
||||
// Example: 08048000-08056000 r-xp 00000000 03:0c 64593 /foo/bar
|
||||
*start = ParseHex(¤t_);
|
||||
CHECK_EQ(*current_++, '-');
|
||||
*end = ParseHex(¤t_);
|
||||
CHECK_EQ(*current_++, ' ');
|
||||
uptr local_protection = 0;
|
||||
CHECK(IsOneOf(*current_, '-', 'r'));
|
||||
if (*current_++ == 'r')
|
||||
local_protection |= kProtectionRead;
|
||||
CHECK(IsOneOf(*current_, '-', 'w'));
|
||||
if (*current_++ == 'w')
|
||||
local_protection |= kProtectionWrite;
|
||||
CHECK(IsOneOf(*current_, '-', 'x'));
|
||||
if (*current_++ == 'x')
|
||||
local_protection |= kProtectionExecute;
|
||||
CHECK(IsOneOf(*current_, 's', 'p'));
|
||||
if (*current_++ == 's')
|
||||
local_protection |= kProtectionShared;
|
||||
if (protection) {
|
||||
*protection = local_protection;
|
||||
}
|
||||
CHECK_EQ(*current_++, ' ');
|
||||
*offset = ParseHex(¤t_);
|
||||
CHECK_EQ(*current_++, ' ');
|
||||
ParseHex(¤t_);
|
||||
CHECK_EQ(*current_++, ':');
|
||||
ParseHex(¤t_);
|
||||
CHECK_EQ(*current_++, ' ');
|
||||
while (IsDecimal(*current_))
|
||||
current_++;
|
||||
// Qemu may lack the trailing space.
|
||||
// http://code.google.com/p/address-sanitizer/issues/detail?id=160
|
||||
// CHECK_EQ(*current_++, ' ');
|
||||
// Skip spaces.
|
||||
while (current_ < next_line && *current_ == ' ')
|
||||
current_++;
|
||||
// Fill in the filename.
|
||||
uptr i = 0;
|
||||
while (current_ < next_line) {
|
||||
if (filename && i < filename_size - 1)
|
||||
filename[i++] = *current_;
|
||||
current_++;
|
||||
}
|
||||
if (filename && i < filename_size)
|
||||
filename[i] = 0;
|
||||
current_ = next_line + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Gets the object name and the offset by walking MemoryMappingLayout.
|
||||
bool MemoryMappingLayout::GetObjectNameAndOffset(uptr addr, uptr *offset,
|
||||
char filename[],
|
||||
uptr filename_size,
|
||||
uptr *protection) {
|
||||
return IterateForObjectNameAndOffset(addr, offset, filename, filename_size,
|
||||
protection);
|
||||
}
|
||||
|
||||
void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) {
|
||||
char *smaps = 0;
|
||||
uptr smaps_cap = 0;
|
||||
uptr smaps_len = ReadFileToBuffer("/proc/self/smaps",
|
||||
&smaps, &smaps_cap, 64<<20);
|
||||
uptr start = 0;
|
||||
bool file = false;
|
||||
const char *pos = smaps;
|
||||
while (pos < smaps + smaps_len) {
|
||||
if (IsHex(pos[0])) {
|
||||
start = ReadHex(pos);
|
||||
for (; *pos != '/' && *pos > '\n'; pos++) {}
|
||||
file = *pos == '/';
|
||||
} else if (internal_strncmp(pos, "Rss:", 4) == 0) {
|
||||
for (; *pos < '0' || *pos > '9'; pos++) {}
|
||||
uptr rss = ReadDecimal(pos) * 1024;
|
||||
cb(start, rss, file, stats, stats_size);
|
||||
}
|
||||
while (*pos++ != '\n') {}
|
||||
}
|
||||
UnmapOrDie(smaps, smaps_cap);
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_LINUX
|
|
@ -0,0 +1,165 @@
|
|||
//===-- sanitizer_procmaps_mac.cc -----------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Information about the process mappings (Mac-specific parts).
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
#if SANITIZER_MAC
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_procmaps.h"
|
||||
|
||||
#include <mach-o/dyld.h>
|
||||
#include <mach-o/loader.h>
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {
|
||||
Reset();
|
||||
}
|
||||
|
||||
MemoryMappingLayout::~MemoryMappingLayout() {
|
||||
}
|
||||
|
||||
// More information about Mach-O headers can be found in mach-o/loader.h
|
||||
// Each Mach-O image has a header (mach_header or mach_header_64) starting with
|
||||
// a magic number, and a list of linker load commands directly following the
|
||||
// header.
|
||||
// A load command is at least two 32-bit words: the command type and the
|
||||
// command size in bytes. We're interested only in segment load commands
|
||||
// (LC_SEGMENT and LC_SEGMENT_64), which tell that a part of the file is mapped
|
||||
// into the task's address space.
|
||||
// The |vmaddr|, |vmsize| and |fileoff| fields of segment_command or
|
||||
// segment_command_64 correspond to the memory address, memory size and the
|
||||
// file offset of the current memory segment.
|
||||
// Because these fields are taken from the images as is, one needs to add
|
||||
// _dyld_get_image_vmaddr_slide() to get the actual addresses at runtime.
|
||||
|
||||
void MemoryMappingLayout::Reset() {
|
||||
// Count down from the top.
|
||||
// TODO(glider): as per man 3 dyld, iterating over the headers with
|
||||
// _dyld_image_count is thread-unsafe. We need to register callbacks for
|
||||
// adding and removing images which will invalidate the MemoryMappingLayout
|
||||
// state.
|
||||
current_image_ = _dyld_image_count();
|
||||
current_load_cmd_count_ = -1;
|
||||
current_load_cmd_addr_ = 0;
|
||||
current_magic_ = 0;
|
||||
current_filetype_ = 0;
|
||||
}
|
||||
|
||||
// static
|
||||
void MemoryMappingLayout::CacheMemoryMappings() {
|
||||
// No-op on Mac for now.
|
||||
}
|
||||
|
||||
void MemoryMappingLayout::LoadFromCache() {
|
||||
// No-op on Mac for now.
|
||||
}
|
||||
|
||||
// Next and NextSegmentLoad were inspired by base/sysinfo.cc in
|
||||
// Google Perftools, http://code.google.com/p/google-perftools.
|
||||
|
||||
// NextSegmentLoad scans the current image for the next segment load command
|
||||
// and returns the start and end addresses and file offset of the corresponding
|
||||
// segment.
|
||||
// Note that the segment addresses are not necessarily sorted.
|
||||
template<u32 kLCSegment, typename SegmentCommand>
|
||||
bool MemoryMappingLayout::NextSegmentLoad(
|
||||
uptr *start, uptr *end, uptr *offset,
|
||||
char filename[], uptr filename_size, uptr *protection) {
|
||||
if (protection)
|
||||
UNIMPLEMENTED();
|
||||
const char* lc = current_load_cmd_addr_;
|
||||
current_load_cmd_addr_ += ((const load_command *)lc)->cmdsize;
|
||||
if (((const load_command *)lc)->cmd == kLCSegment) {
|
||||
const sptr dlloff = _dyld_get_image_vmaddr_slide(current_image_);
|
||||
const SegmentCommand* sc = (const SegmentCommand *)lc;
|
||||
if (start) *start = sc->vmaddr + dlloff;
|
||||
if (end) *end = sc->vmaddr + sc->vmsize + dlloff;
|
||||
if (offset) {
|
||||
if (current_filetype_ == /*MH_EXECUTE*/ 0x2) {
|
||||
*offset = sc->vmaddr;
|
||||
} else {
|
||||
*offset = sc->fileoff;
|
||||
}
|
||||
}
|
||||
if (filename) {
|
||||
internal_strncpy(filename, _dyld_get_image_name(current_image_),
|
||||
filename_size);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
|
||||
char filename[], uptr filename_size,
|
||||
uptr *protection) {
|
||||
for (; current_image_ >= 0; current_image_--) {
|
||||
const mach_header* hdr = _dyld_get_image_header(current_image_);
|
||||
if (!hdr) continue;
|
||||
if (current_load_cmd_count_ < 0) {
|
||||
// Set up for this image;
|
||||
current_load_cmd_count_ = hdr->ncmds;
|
||||
current_magic_ = hdr->magic;
|
||||
current_filetype_ = hdr->filetype;
|
||||
switch (current_magic_) {
|
||||
#ifdef MH_MAGIC_64
|
||||
case MH_MAGIC_64: {
|
||||
current_load_cmd_addr_ = (char*)hdr + sizeof(mach_header_64);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case MH_MAGIC: {
|
||||
current_load_cmd_addr_ = (char*)hdr + sizeof(mach_header);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (; current_load_cmd_count_ >= 0; current_load_cmd_count_--) {
|
||||
switch (current_magic_) {
|
||||
// current_magic_ may be only one of MH_MAGIC, MH_MAGIC_64.
|
||||
#ifdef MH_MAGIC_64
|
||||
case MH_MAGIC_64: {
|
||||
if (NextSegmentLoad<LC_SEGMENT_64, struct segment_command_64>(
|
||||
start, end, offset, filename, filename_size, protection))
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case MH_MAGIC: {
|
||||
if (NextSegmentLoad<LC_SEGMENT, struct segment_command>(
|
||||
start, end, offset, filename, filename_size, protection))
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If we get here, no more load_cmd's in this image talk about
|
||||
// segments. Go on to the next image.
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MemoryMappingLayout::GetObjectNameAndOffset(uptr addr, uptr *offset,
|
||||
char filename[],
|
||||
uptr filename_size,
|
||||
uptr *protection) {
|
||||
return IterateForObjectNameAndOffset(addr, offset, filename, filename_size,
|
||||
protection);
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_MAC
|
|
@ -0,0 +1,51 @@
|
|||
//===-- sanitizer_procmaps_posix.cc ---------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Information about the process mappings.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
#if SANITIZER_POSIX
|
||||
#include "sanitizer_procmaps.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
bool MemoryMappingLayout::IterateForObjectNameAndOffset(uptr addr, uptr *offset,
|
||||
char filename[],
|
||||
uptr filename_size,
|
||||
uptr *protection) {
|
||||
Reset();
|
||||
uptr start, end, file_offset;
|
||||
for (int i = 0; Next(&start, &end, &file_offset, filename, filename_size,
|
||||
protection);
|
||||
i++) {
|
||||
if (addr >= start && addr < end) {
|
||||
// Don't subtract 'start' for the first entry:
|
||||
// * If a binary is compiled w/o -pie, then the first entry in
|
||||
// process maps is likely the binary itself (all dynamic libs
|
||||
// are mapped higher in address space). For such a binary,
|
||||
// instruction offset in binary coincides with the actual
|
||||
// instruction address in virtual memory (as code section
|
||||
// is mapped to a fixed memory range).
|
||||
// * If a binary is compiled with -pie, all the modules are
|
||||
// mapped high at address space (in particular, higher than
|
||||
// shadow memory of the tool), so the module can't be the
|
||||
// first entry.
|
||||
*offset = (addr - (i ? start : 0)) + file_offset;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (filename_size)
|
||||
filename[0] = '\0';
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_POSIX
|
Loading…
Reference in New Issue