From: Mark Williams Date: Wed, 14 Sep 2016 22:48:52 +0000 (-0700) Subject: Make ElfCache follow .gnu_debuglink X-Git-Tag: v2016.09.19.00~13 X-Git-Url: http://plrg.eecs.uci.edu/git/?a=commitdiff_plain;h=3272dfdbe243e0ac02acca1dd82bc391d1da079a;p=folly.git Make ElfCache follow .gnu_debuglink Summary: If you split out debug info into a separate file, folly::Symbolizer didn't find it, and failed to include file and line number info in backtraces. This adds a new open mode which follows the .gnu_debuginfo link, and uses it from ElfCache and SignalSafeElfCache. Reviewed By: meyering, luciang Differential Revision: D3852311 fbshipit-source-id: fec9e57378ae59fa1b82d41a163bb9cfcf9ca23c --- diff --git a/folly/experimental/symbolizer/Elf.cpp b/folly/experimental/symbolizer/Elf.cpp index a61829a5..b0e3f9a7 100644 --- a/folly/experimental/symbolizer/Elf.cpp +++ b/folly/experimental/symbolizer/Elf.cpp @@ -58,8 +58,9 @@ void ElfFile::open(const char* name, bool readOnly) { } } -int ElfFile::openNoThrow(const char* name, bool readOnly, const char** msg) - noexcept { +int ElfFile::openNoThrow(const char* name, + bool readOnly, + const char** msg) noexcept { FOLLY_SAFE_CHECK(fd_ == -1, "File already open"); fd_ = ::open(name, readOnly ? O_RDONLY : O_RDWR); if (fd_ == -1) { @@ -95,6 +96,43 @@ int ElfFile::openNoThrow(const char* name, bool readOnly, const char** msg) return kSuccess; } +int ElfFile::openAndFollow(const char* name, + bool readOnly, + const char** msg) noexcept { + auto result = openNoThrow(name, readOnly, msg); + if (!readOnly || result != kSuccess) return result; + + /* NOTE .gnu_debuglink specifies only the name of the debugging info file + * (with no directory components). GDB checks 3 different directories, but + * ElfFile only supports the first version: + * - dirname(name) + * - dirname(name) + /.debug/ + * - X/dirname(name)/ - where X is set in gdb's `debug-file-directory`. + */ + auto dirend = strrchr(name, '/'); + // include ending '/' if any. + auto dirlen = dirend != nullptr ? dirend + 1 - name : 0; + + auto debuginfo = getSectionByName(".gnu_debuglink"); + if (!debuginfo) return result; + + // The section starts with the filename, with any leading directory + // components removed, followed by a zero byte. + auto debugFileName = getSectionBody(*debuginfo); + auto debugFileLen = strlen(debugFileName.begin()); + if (dirlen + debugFileLen >= PATH_MAX) { + return result; + } + + char linkname[PATH_MAX]; + memcpy(linkname, name, dirlen); + memcpy(linkname + dirlen, debugFileName.begin(), debugFileLen + 1); + reset(); + result = openNoThrow(linkname, readOnly, msg); + if (result == kSuccess) return result; + return openNoThrow(name, readOnly, msg); +} + ElfFile::~ElfFile() { reset(); } diff --git a/folly/experimental/symbolizer/Elf.h b/folly/experimental/symbolizer/Elf.h index 176a9e89..b3292881 100644 --- a/folly/experimental/symbolizer/Elf.h +++ b/folly/experimental/symbolizer/Elf.h @@ -58,9 +58,14 @@ class ElfFile { kSystemError = -1, kInvalidElfFile = -2, }; + // Open the ELF file. Does not throw on error. int openNoThrow(const char* name, bool readOnly=true, const char** msg=nullptr) noexcept; + // Like openNoThrow, but follow .gnu_debuglink if present + int openAndFollow(const char* name, bool readOnly=true, + const char** msg=nullptr) noexcept; + // Open the ELF file. Throws on error. void open(const char* name, bool readOnly=true); diff --git a/folly/experimental/symbolizer/ElfCache.cpp b/folly/experimental/symbolizer/ElfCache.cpp index 8e9eff2f..472c0b93 100644 --- a/folly/experimental/symbolizer/ElfCache.cpp +++ b/folly/experimental/symbolizer/ElfCache.cpp @@ -48,7 +48,7 @@ std::shared_ptr SignalSafeElfCache::getFile(StringPiece p) { auto& f = slots_[n]; const char* msg = ""; - int r = f->openNoThrow(path.data(), true, &msg); + int r = f->openAndFollow(path.data(), true, &msg); if (r != ElfFile::kSuccess) { return nullptr; } @@ -77,7 +77,7 @@ std::shared_ptr ElfCache::getFile(StringPiece p) { // No negative caching const char* msg = ""; - int r = entry->file.openNoThrow(path.c_str(), true, &msg); + int r = entry->file.openAndFollow(path.c_str(), true, &msg); if (r != ElfFile::kSuccess) { return nullptr; } diff --git a/folly/experimental/symbolizer/test/gnu_debuglink_test.sh b/folly/experimental/symbolizer/test/gnu_debuglink_test.sh new file mode 100755 index 00000000..115ddc2a --- /dev/null +++ b/folly/experimental/symbolizer/test/gnu_debuglink_test.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +crash="$1" +rm -f "$crash."{debuginfo,strip} +objcopy --only-keep-debug "$crash" "$crash.debuginfo" +objcopy --strip-debug --add-gnu-debuglink="$crash.debuginfo" "$crash" "$crash.strip" + +echo '{"op":"start","test":"gnu_debuglink_test"}'; +start=$(date +%s) +if "$crash.strip" 2>&1 | grep 'Crash.cpp:[0-9]*$' > /dev/null; then + result='"status":"passed"'; +else + result='"status":"failed"'; +fi +end=$(date +%s) +echo '{"op":"test_done","test":"gnu_debuglink_test",'"$result"'}' +echo '{"op":"all_done","results":[{"name":"gnu_debuglink_test",'"$result"',"start_time":'"$start"',"end_time":'"$end"',"details":"nothing"}]}'