// Copyright (c) 2006, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Author: Satoru Takabayashi // Stack-footprint reduction work done by Raksit Ashok // // Implementation note: // // We don't use heaps but only use stacks. We want to reduce the // stack consumption so that the symbolizer can run on small stacks. // // Here are some numbers collected with GCC 4.1.0 on x86: // - sizeof(Elf32_Sym) = 16 // - sizeof(Elf32_Shdr) = 40 // - sizeof(Elf64_Sym) = 24 // - sizeof(Elf64_Shdr) = 64 // // This implementation is intended to be async-signal-safe but uses // 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. GLOG_OS_OPENBSD). #ifdef GLOG_BUILD_CONFIG_INCLUDE #include GLOG_BUILD_CONFIG_INCLUDE #endif // GLOG_BUILD_CONFIG_INCLUDE #include "symbolize.h" #include "utilities.h" #if defined(HAVE_SYMBOLIZE) #include <cstring> #include <cstdlib> #include <algorithm> #include <limits> #include "symbolize.h" #include "demangle.h" _START_GOOGLE_NAMESPACE_ // We don't use assert() since it's not guaranteed to be // async-signal-safe. Instead we define a minimal assertion // macro. So far, we don't need pretty printing for __FILE__, etc. // A wrapper for abort() to make it callable in ? :. static int AssertFail() { … } #define SAFE_ASSERT(expr) … static SymbolizeCallback g_symbolize_callback = …; void InstallSymbolizeCallback(SymbolizeCallback callback) { … } static SymbolizeOpenObjectFileCallback g_symbolize_open_object_file_callback = …; void InstallSymbolizeOpenObjectFileCallback( SymbolizeOpenObjectFileCallback 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 // get inlined. static ATTRIBUTE_NOINLINE void DemangleInplace(char* out, size_t out_size) { … } _END_GOOGLE_NAMESPACE_ #if defined(__ELF__) #if defined(HAVE_DLFCN_H) #include <dlfcn.h> #endif #if defined(GLOG_OS_OPENBSD) #include <sys/exec_elf.h> #else #include <elf.h> #endif #include <fcntl.h> #include <stdint.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include <cerrno> #include <climits> #include <cstddef> #include <cstdio> #include <cstdlib> #include <cstring> #include "symbolize.h" #include "config.h" #include "glog/raw_logging.h" // Re-runs fn until it doesn't cause EINTR. #define NO_INTR(fn) … _START_GOOGLE_NAMESPACE_ FileDescriptor::~FileDescriptor() { … } ssize_t ReadFromOffset(const int fd, void* buf, const size_t count, const size_t offset) { … } // Try reading exactly "count" bytes from "offset" bytes in a file // pointed by "fd" into the buffer starting at "buf" while handling // short reads and EINTR. On success, return true. Otherwise, return // false. static bool ReadFromOffsetExact(const int fd, void* buf, const size_t count, const size_t offset) { … } // Returns elf_header.e_type if the file pointed by fd is an ELF binary. static int FileGetElfType(const int fd) { … } // Read the section headers in the given ELF binary, and if a section // of the specified type is found, set the output to this section header // and return true. Otherwise, return false. // To keep stack consumption low, we would like this function to not get // inlined. static ATTRIBUTE_NOINLINE bool GetSectionHeaderByType(const int fd, ElfW(Half) sh_num, const size_t sh_offset, ElfW(Word) type, ElfW(Shdr) * out) { … } // There is no particular reason to limit section name to 63 characters, // but there has (as yet) been no need for anything longer either. const int kMaxSectionNameLen = …; // name_len should include terminating '\0'. bool GetSectionHeaderByName(int fd, const char *name, size_t name_len, ElfW(Shdr) *out) { … } // Read a symbol table and look for the symbol containing the // pc. Iterate over symbols in a symbol table and look for the symbol // containing "pc". On success, return true and write the symbol name // to out. Otherwise, return false. // To keep stack consumption low, we would like this function to not get // inlined. static ATTRIBUTE_NOINLINE bool FindSymbol(uint64_t pc, const int fd, char* out, size_t out_size, uint64_t symbol_offset, const ElfW(Shdr) * strtab, const ElfW(Shdr) * symtab) { … } // Get the symbol name of "pc" from the file pointed by "fd". Process // both regular and dynamic symbol tables if necessary. On success, // write the symbol name to "out" and return true. Otherwise, return // false. static bool GetSymbolFromObjectFile(const int fd, uint64_t pc, char* out, size_t out_size, uint64_t base_address) { … } namespace { // Helper class for reading lines from file. // // Note: we don't use ProcMapsIterator since the object is big (it has // a 5k array member) and uses async-unsafe functions such as sscanf() // and snprintf(). class LineReader { … }; } // namespace // Place the hex number read from "start" into "*hex". The pointer to // the first non-hex character or "end" is returned. static char *GetHex(const char *start, const char *end, uint64_t *hex) { … } static int OpenObjectFileContainingPcAndGetStartAddressNoHook( uint64_t pc, uint64_t& start_address, uint64_t& base_address, char* out_file_name, size_t out_file_name_size) { … } int OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc, uint64_t& start_address, uint64_t& base_address, char* out_file_name, size_t out_file_name_size) { … } // 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 an (unsigned) 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. static char* itoa_r(uintptr_t i, char* buf, size_t sz, unsigned base, size_t padding) { … } // Safely appends string |source| to string |dest|. Never writes past the // buffer size |dest_size| and guarantees that |dest| is null-terminated. static void SafeAppendString(const char* source, char* dest, size_t dest_size) { … } // 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. static void SafeAppendHexNumber(uint64_t value, char* dest, size_t 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". // Otherwise, returns false. If Callback function is installed via // InstallSymbolizeCallback(), the function is also called in this function, // and "out" is used as its output. // To keep stack consumption low, we would like this function to not // get inlined. static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, char* out, size_t out_size) { … } _END_GOOGLE_NAMESPACE_ #elif (defined(GLOG_OS_MACOSX) || defined(GLOG_OS_EMSCRIPTEN)) && \ defined(HAVE_DLADDR) #include <dlfcn.h> #include <cstring> _START_GOOGLE_NAMESPACE_ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, char* out, size_t out_size) { Dl_info info; if (dladdr(pc, &info)) { if (info.dli_sname) { if (strlen(info.dli_sname) < out_size) { strcpy(out, info.dli_sname); // Symbolization succeeded. Now we try to demangle the symbol. DemangleInplace(out, out_size); return true; } } } return false; } _END_GOOGLE_NAMESPACE_ #elif defined(GLOG_OS_WINDOWS) || defined(GLOG_OS_CYGWIN) #include <windows.h> #include <dbghelp.h> #ifdef _MSC_VER #pragma comment(lib, "dbghelp") #endif _START_GOOGLE_NAMESPACE_ class SymInitializer { public: HANDLE process; bool ready; SymInitializer() : process(NULL), ready(false) { // Initialize the symbol handler. // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680344(v=vs.85).aspx process = GetCurrentProcess(); // Defer symbol loading. // We do not request undecorated symbols with SYMOPT_UNDNAME // because the mangling library calls UnDecorateSymbolName. SymSetOptions(SYMOPT_DEFERRED_LOADS); if (SymInitialize(process, NULL, true)) { ready = true; } } ~SymInitializer() { SymCleanup(process); // We do not need to close `HANDLE process` because it's a "pseudo handle." } private: SymInitializer(const SymInitializer&); SymInitializer& operator=(const SymInitializer&); }; static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, char* out, size_t out_size) { const static SymInitializer symInitializer; if (!symInitializer.ready) { return false; } // Resolve symbol information from address. // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680578(v=vs.85).aspx char buf[sizeof(SYMBOL_INFO) + MAX_SYM_NAME]; SYMBOL_INFO *symbol = reinterpret_cast<SYMBOL_INFO *>(buf); symbol->SizeOfStruct = sizeof(SYMBOL_INFO); symbol->MaxNameLen = MAX_SYM_NAME; // We use the ANSI version to ensure the string type is always `char *`. // This could break if a symbol has Unicode in it. BOOL ret = SymFromAddr(symInitializer.process, reinterpret_cast<DWORD64>(pc), 0, symbol); if (ret == 1 && static_cast<ssize_t>(symbol->NameLen) < out_size) { // `NameLen` does not include the null terminating character. strncpy(out, symbol->Name, static_cast<size_t>(symbol->NameLen) + 1); out[static_cast<size_t>(symbol->NameLen)] = '\0'; // Symbolization succeeded. Now we try to demangle the symbol. DemangleInplace(out, out_size); return true; } return false; } _END_GOOGLE_NAMESPACE_ #else # error BUG: HAVE_SYMBOLIZE was wrongly set #endif _START_GOOGLE_NAMESPACE_ bool Symbolize(void* pc, char* out, size_t out_size) { … } _END_GOOGLE_NAMESPACE_ #else /* HAVE_SYMBOLIZE */ #include <cassert> #include "config.h" _START_GOOGLE_NAMESPACE_ // TODO: Support other environments. bool Symbolize(void* /*pc*/, char* /*out*/, size_t /*out_size*/) { assert(0); return false; } _END_GOOGLE_NAMESPACE_ #endif