Index: base/third_party/symbolize/symbolize.cc |
diff --git a/base/third_party/symbolize/symbolize.cc b/base/third_party/symbolize/symbolize.cc |
index 85976bbc7b998d370a909ce4b0d2183c3659b3ee..b25f7479d0df09f0c9dd87637df1a929124fed41 100644 |
--- a/base/third_party/symbolize/symbolize.cc |
+++ b/base/third_party/symbolize/symbolize.cc |
@@ -45,8 +45,13 @@ |
// some functions which are not guaranteed to be so, such as memchr() |
// and memmove(). We assume they are async-signal-safe. |
// |
+// Additional header can be specified by the GLOG_BUILD_CONFIG_INCLUDE |
+// macro to add platform specific defines (e.g. OS_OPENBSD). |
+ |
+#ifdef GLOG_BUILD_CONFIG_INCLUDE |
+#include GLOG_BUILD_CONFIG_INCLUDE |
+#endif // GLOG_BUILD_CONFIG_INCLUDE |
-#include "build/build_config.h" |
#include "utilities.h" |
#if defined(HAVE_SYMBOLIZE) |
@@ -75,6 +80,13 @@ void InstallSymbolizeCallback(SymbolizeCallback callback) { |
g_symbolize_callback = callback; |
} |
+static SymbolizeOpenObjectFileCallback g_symbolize_open_object_file_callback = |
+ NULL; |
+void InstallSymbolizeOpenObjectFileCallback( |
+ SymbolizeOpenObjectFileCallback callback) { |
+ g_symbolize_open_object_file_callback = callback; |
+} |
+ |
// This function wraps the Demangle function to provide an interface |
// where the input symbol is demangled in-place. |
// To keep stack consumption low, we would like this function to not |
@@ -83,8 +95,8 @@ static ATTRIBUTE_NOINLINE void DemangleInplace(char *out, int out_size) { |
char demangled[256]; // Big enough for sane demangled symbols. |
if (Demangle(out, demangled, sizeof(demangled))) { |
// Demangling succeeded. Copy to out if the space allows. |
- int len = strlen(demangled); |
- if (len + 1 <= out_size) { // +1 for '\0'. |
+ size_t len = strlen(demangled); |
+ if (len + 1 <= (size_t)out_size) { // +1 for '\0'. |
SAFE_ASSERT(len < sizeof(demangled)); |
memmove(out, demangled, len + 1); |
} |
@@ -483,13 +495,20 @@ static char *GetHex(const char *start, const char *end, uint64_t *hex) { |
return const_cast<char *>(p); |
} |
-// Search for the object file (from /proc/self/maps) that contains |
-// the specified pc. If found, open this file and return the file handle, |
-// and also set start_address to the start address of where this object |
-// file is mapped to in memory. Otherwise, return -1. |
+// Searches for the object file (from /proc/self/maps) that contains |
+// the specified pc. If found, sets |start_address| to the start address |
+// of where this object file is mapped in memory, sets the module base |
+// address into |base_address|, copies the object file name into |
+// |out_file_name|, and attempts to open the object file. If the object |
+// file is opened successfully, returns the file descriptor. Otherwise, |
+// returns -1. |out_file_name_size| is the size of the file name buffer |
+// (including the null-terminator). |
static ATTRIBUTE_NOINLINE int |
OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc, |
- uint64_t &start_address) { |
+ uint64_t &start_address, |
+ uint64_t &base_address, |
+ char *out_file_name, |
+ int out_file_name_size) { |
int object_fd; |
// Open /proc/self/maps. |
@@ -503,8 +522,10 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc, |
// Iterate over maps and look for the map containing the pc. Then |
// look into the symbol tables inside. |
char buf[1024]; // Big enough for line of sane /proc/self/maps |
+ int num_maps = 0; |
LineReader reader(wrapped_maps_fd.get(), buf, sizeof(buf)); |
while (true) { |
+ num_maps++; |
const char *cursor; |
const char *eol; |
if (!reader.ReadLine(&cursor, &eol)) { // EOF or malformed line. |
@@ -554,14 +575,35 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc, |
} |
++cursor; // Skip ' '. |
- // Skip to file name. "cursor" now points to file offset. We need to |
- // skip at least three spaces for file offset, dev, and inode. |
+ // Read file offset. |
+ uint64_t file_offset; |
+ cursor = GetHex(cursor, eol, &file_offset); |
+ if (cursor == eol || *cursor != ' ') { |
+ return -1; // Malformed line. |
+ } |
+ ++cursor; // Skip ' '. |
+ |
+ // Don't subtract 'start_address' from 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. |
+ base_address = ((num_maps == 1) ? 0U : start_address) - file_offset; |
+ |
+ // Skip to file name. "cursor" now points to dev. We need to |
+ // skip at least two spaces for dev and inode. |
int num_spaces = 0; |
while (cursor < eol) { |
if (*cursor == ' ') { |
++num_spaces; |
- } else if (num_spaces >= 3) { |
- // The first non-space character after skipping three spaces |
+ } else if (num_spaces >= 2) { |
+ // The first non-space character after skipping two spaces |
// is the beginning of the file name. |
break; |
} |
@@ -574,12 +616,105 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc, |
// Finally, "cursor" now points to file name of our interest. |
NO_INTR(object_fd = open(cursor, O_RDONLY)); |
if (object_fd < 0) { |
+ // Failed to open object file. Copy the object file name to |
+ // |out_file_name|. |
+ strncpy(out_file_name, cursor, out_file_name_size); |
+ // Making sure |out_file_name| is always null-terminated. |
+ out_file_name[out_file_name_size - 1] = '\0'; |
return -1; |
} |
return object_fd; |
} |
} |
+// POSIX doesn't define any async-signal safe function for converting |
+// an integer to ASCII. We'll have to define our own version. |
+// itoa_r() converts a (signed) integer to ASCII. It returns "buf", if the |
+// conversion was successful or NULL otherwise. It never writes more than "sz" |
+// bytes. Output will be truncated as needed, and a NUL character is always |
+// appended. |
+// NOTE: code from sandbox/linux/seccomp-bpf/demo.cc. |
+char *itoa_r(intptr_t i, char *buf, size_t sz, int base, size_t padding) { |
+ // Make sure we can write at least one NUL byte. |
+ size_t n = 1; |
+ if (n > sz) |
+ return NULL; |
+ |
+ if (base < 2 || base > 16) { |
+ buf[0] = '\000'; |
+ return NULL; |
+ } |
+ |
+ char *start = buf; |
+ |
+ uintptr_t j = i; |
+ |
+ // Handle negative numbers (only for base 10). |
+ if (i < 0 && base == 10) { |
+ j = -i; |
+ |
+ // Make sure we can write the '-' character. |
+ if (++n > sz) { |
+ buf[0] = '\000'; |
+ return NULL; |
+ } |
+ *start++ = '-'; |
+ } |
+ |
+ // Loop until we have converted the entire number. Output at least one |
+ // character (i.e. '0'). |
+ char *ptr = start; |
+ do { |
+ // Make sure there is still enough space left in our output buffer. |
+ if (++n > sz) { |
+ buf[0] = '\000'; |
+ return NULL; |
+ } |
+ |
+ // Output the next digit. |
+ *ptr++ = "0123456789abcdef"[j % base]; |
+ j /= base; |
+ |
+ if (padding > 0) |
+ padding--; |
+ } while (j > 0 || padding > 0); |
+ |
+ // Terminate the output with a NUL character. |
+ *ptr = '\000'; |
+ |
+ // Conversion to ASCII actually resulted in the digits being in reverse |
+ // order. We can't easily generate them in forward order, as we can't tell |
+ // the number of characters needed until we are done converting. |
+ // So, now, we reverse the string (except for the possible "-" sign). |
+ while (--ptr > start) { |
+ char ch = *ptr; |
+ *ptr = *start; |
+ *start++ = ch; |
+ } |
+ return buf; |
+} |
+ |
+// Safely appends string |source| to string |dest|. Never writes past the |
+// buffer size |dest_size| and guarantees that |dest| is null-terminated. |
+void SafeAppendString(const char* source, char* dest, int dest_size) { |
+ int dest_string_length = strlen(dest); |
+ SAFE_ASSERT(dest_string_length < dest_size); |
+ dest += dest_string_length; |
+ dest_size -= dest_string_length; |
+ strncpy(dest, source, dest_size); |
+ // Making sure |dest| is always null-terminated. |
+ dest[dest_size - 1] = '\0'; |
+} |
+ |
+// Converts a 64-bit value into a hex string, and safely appends it to |dest|. |
+// Never writes past the buffer size |dest_size| and guarantees that |dest| is |
+// null-terminated. |
+void SafeAppendHexNumber(uint64_t value, char* dest, int dest_size) { |
+ // 64-bit numbers in hex can have up to 16 digits. |
+ char buf[17] = {'\0'}; |
+ SafeAppendString(itoa_r(value, buf, sizeof(buf), 16, 0), dest, dest_size); |
+} |
+ |
// The implementation of our symbolization routine. If it |
// successfully finds the symbol containing "pc" and obtains the |
// symbol name, returns true and write the symbol name to "out". |
@@ -592,10 +727,40 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out, |
int out_size) { |
uint64_t pc0 = reinterpret_cast<uintptr_t>(pc); |
uint64_t start_address = 0; |
+ uint64_t base_address = 0; |
+ int object_fd = -1; |
- int object_fd = OpenObjectFileContainingPcAndGetStartAddress(pc0, |
- start_address); |
- if (object_fd == -1) { |
+ if (out_size < 1) { |
+ return false; |
+ } |
+ out[0] = '\0'; |
+ SafeAppendString("(", out, out_size); |
+ |
+ if (g_symbolize_open_object_file_callback) { |
+ object_fd = g_symbolize_open_object_file_callback(pc0, start_address, |
+ base_address, out + 1, |
+ out_size - 1); |
+ } else { |
+ object_fd = OpenObjectFileContainingPcAndGetStartAddress(pc0, start_address, |
+ base_address, |
+ out + 1, |
+ out_size - 1); |
+ } |
+ |
+ // Check whether a file name was returned. |
+ if (object_fd < 0) { |
+ if (out[1]) { |
+ // The object file containing PC was determined successfully however the |
+ // object file was not opened successfully. This is still considered |
+ // success because the object file name and offset are known and tools |
+ // like asan_symbolize.py can be used for the symbolization. |
+ out[out_size - 1] = '\0'; // Making sure |out| is always null-terminated. |
+ SafeAppendString("+0x", out, out_size); |
+ SafeAppendHexNumber(pc0 - base_address, out, out_size); |
+ SafeAppendString(")", out, out_size); |
+ return true; |
+ } |
+ // Failed to determine the object file containing PC. Bail out. |
return false; |
} |
FileDescriptor wrapped_object_fd(object_fd); |
@@ -639,7 +804,7 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out, |
int out_size) { |
Dl_info info; |
if (dladdr(pc, &info)) { |
- if (strlen(info.dli_sname) < out_size) { |
+ if ((int)strlen(info.dli_sname) < out_size) { |
strcpy(out, info.dli_sname); |
// Symbolization succeeded. Now we try to demangle the symbol. |
DemangleInplace(out, out_size); |