X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2Fexperimental%2Fsymbolizer%2FElf.cpp;h=cc643866d432a78528ad48ee757cc1029100dd2a;hb=f975d3c54800b7065985e5e548ce1d91fef4926c;hp=b4b76e13ed3a6ac97fca6677b4432ce789595b49;hpb=ba00a0d45569463799f67ed37e66c77f0cd6f015;p=folly.git diff --git a/folly/experimental/symbolizer/Elf.cpp b/folly/experimental/symbolizer/Elf.cpp index b4b76e13..cc643866 100644 --- a/folly/experimental/symbolizer/Elf.cpp +++ b/folly/experimental/symbolizer/Elf.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2014 Facebook, Inc. + * Copyright 2017-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,14 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - - #include -#include -#include -#include #include +#include +#include +#include #include @@ -28,6 +26,7 @@ #include #include +#include namespace folly { namespace symbolizer { @@ -57,15 +56,19 @@ 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) { if (msg) *msg = "open"; return kSystemError; } - + // Always close fd and unmap in case of failure along the way to avoid + // check failure above if we leave fd != -1 and the object is recycled + // like it is inside SignalSafeElfCache + ScopeGuard guard = makeGuard([&]{ reset(); }); struct stat st; int r = fstat(fd_, &st); if (r == -1) { @@ -87,12 +90,49 @@ int ElfFile::openNoThrow(const char* name, bool readOnly, const char** msg) errno = EINVAL; return kInvalidElfFile; } - + guard.dismiss(); 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() { - destroy(); + reset(); } ElfFile::ElfFile(ElfFile&& other) noexcept @@ -108,7 +148,7 @@ ElfFile::ElfFile(ElfFile&& other) noexcept ElfFile& ElfFile::operator=(ElfFile&& other) { assert(this != &other); - destroy(); + reset(); fd_ = other.fd_; file_ = other.file_; @@ -123,13 +163,15 @@ ElfFile& ElfFile::operator=(ElfFile&& other) { return *this; } -void ElfFile::destroy() { +void ElfFile::reset() { if (file_ != MAP_FAILED) { munmap(file_, length_); + file_ = static_cast(MAP_FAILED); } if (fd_ != -1) { close(fd_); + fd_ = -1; } } @@ -158,18 +200,12 @@ bool ElfFile::init(const char** msg) { #undef EXPECTED_CLASS // Validate ELF data encoding (LSB/MSB) -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -# define EXPECTED_ENCODING ELFDATA2LSB -#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -# define EXPECTED_ENCODING ELFDATA2MSB -#else -# error Unsupported byte order -#endif - if (elfHeader.e_ident[EI_DATA] != EXPECTED_ENCODING) { + static constexpr auto kExpectedEncoding = + kIsLittleEndian ? ELFDATA2LSB : ELFDATA2MSB; + if (elfHeader.e_ident[EI_DATA] != kExpectedEncoding) { if (msg) *msg = "invalid ELF encoding"; return false; } -#undef EXPECTED_ENCODING // Validate ELF version (1) if (elfHeader.e_ident[EI_VERSION] != EV_CURRENT || @@ -375,4 +411,3 @@ const char* ElfFile::getSymbolName(Symbol symbol) const { } // namespace symbolizer } // namespace folly -