2 * Copyright 2016 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include <folly/experimental/symbolizer/Elf.h>
21 #include <sys/types.h>
27 #include <glog/logging.h>
29 #include <folly/Conv.h>
30 #include <folly/Exception.h>
31 #include <folly/ScopeGuard.h>
34 namespace symbolizer {
36 ElfFile::ElfFile() noexcept
38 file_(static_cast<char*>(MAP_FAILED)),
43 ElfFile::ElfFile(const char* name, bool readOnly)
45 file_(static_cast<char*>(MAP_FAILED)),
51 void ElfFile::open(const char* name, bool readOnly) {
53 int r = openNoThrow(name, readOnly, &msg);
54 if (r == kSystemError) {
55 throwSystemError(msg);
57 CHECK_EQ(r, kSuccess) << msg;
61 int ElfFile::openNoThrow(const char* name, bool readOnly, const char** msg)
63 FOLLY_SAFE_CHECK(fd_ == -1, "File already open");
64 fd_ = ::open(name, readOnly ? O_RDONLY : O_RDWR);
66 if (msg) *msg = "open";
69 // Always close fd and unmap in case of failure along the way to avoid
70 // check failure above if we leave fd != -1 and the object is recycled
71 // like it is inside SignalSafeElfCache
72 ScopeGuard guard = makeGuard([&]{ reset(); });
74 int r = fstat(fd_, &st);
76 if (msg) *msg = "fstat";
85 file_ = static_cast<char*>(mmap(nullptr, length_, prot, MAP_SHARED, fd_, 0));
86 if (file_ == MAP_FAILED) {
87 if (msg) *msg = "mmap";
92 return kInvalidElfFile;
102 ElfFile::ElfFile(ElfFile&& other) noexcept
105 length_(other.length_),
106 baseAddress_(other.baseAddress_) {
108 other.file_ = static_cast<char*>(MAP_FAILED);
110 other.baseAddress_ = 0;
113 ElfFile& ElfFile::operator=(ElfFile&& other) {
114 assert(this != &other);
119 length_ = other.length_;
120 baseAddress_ = other.baseAddress_;
123 other.file_ = static_cast<char*>(MAP_FAILED);
125 other.baseAddress_ = 0;
130 void ElfFile::reset() {
131 if (file_ != MAP_FAILED) {
132 munmap(file_, length_);
133 file_ = static_cast<char*>(MAP_FAILED);
142 bool ElfFile::init(const char** msg) {
143 auto& elfHeader = this->elfHeader();
145 // Validate ELF magic numbers
146 if (!(elfHeader.e_ident[EI_MAG0] == ELFMAG0 &&
147 elfHeader.e_ident[EI_MAG1] == ELFMAG1 &&
148 elfHeader.e_ident[EI_MAG2] == ELFMAG2 &&
149 elfHeader.e_ident[EI_MAG3] == ELFMAG3)) {
150 if (msg) *msg = "invalid ELF magic";
154 // Validate ELF class (32/64 bits)
155 #define EXPECTED_CLASS P1(ELFCLASS, __ELF_NATIVE_CLASS)
156 #define P1(a, b) P2(a, b)
157 #define P2(a, b) a ## b
158 if (elfHeader.e_ident[EI_CLASS] != EXPECTED_CLASS) {
159 if (msg) *msg = "invalid ELF class";
164 #undef EXPECTED_CLASS
166 // Validate ELF data encoding (LSB/MSB)
167 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
168 # define EXPECTED_ENCODING ELFDATA2LSB
169 #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
170 # define EXPECTED_ENCODING ELFDATA2MSB
172 # error Unsupported byte order
174 if (elfHeader.e_ident[EI_DATA] != EXPECTED_ENCODING) {
175 if (msg) *msg = "invalid ELF encoding";
178 #undef EXPECTED_ENCODING
180 // Validate ELF version (1)
181 if (elfHeader.e_ident[EI_VERSION] != EV_CURRENT ||
182 elfHeader.e_version != EV_CURRENT) {
183 if (msg) *msg = "invalid ELF version";
187 // We only support executable and shared object files
188 if (elfHeader.e_type != ET_EXEC && elfHeader.e_type != ET_DYN) {
189 if (msg) *msg = "invalid ELF file type";
193 if (elfHeader.e_phnum == 0) {
194 if (msg) *msg = "no program header!";
198 if (elfHeader.e_phentsize != sizeof(ElfW(Phdr))) {
199 if (msg) *msg = "invalid program header entry size";
203 if (elfHeader.e_shentsize != sizeof(ElfW(Shdr))) {
204 if (msg) *msg = "invalid section header entry size";
207 const ElfW(Phdr)* programHeader = &at<ElfW(Phdr)>(elfHeader.e_phoff);
208 bool foundBase = false;
209 for (size_t i = 0; i < elfHeader.e_phnum; programHeader++, i++) {
210 // Program headers are sorted by load address, so the first PT_LOAD
211 // header gives us the base address.
212 if (programHeader->p_type == PT_LOAD) {
213 baseAddress_ = programHeader->p_vaddr;
220 if (msg) *msg = "could not find base address";
227 const ElfW(Shdr)* ElfFile::getSectionByIndex(size_t idx) const {
228 FOLLY_SAFE_CHECK(idx < elfHeader().e_shnum, "invalid section index");
229 return &at<ElfW(Shdr)>(elfHeader().e_shoff + idx * sizeof(ElfW(Shdr)));
232 folly::StringPiece ElfFile::getSectionBody(const ElfW(Shdr)& section) const {
233 return folly::StringPiece(file_ + section.sh_offset, section.sh_size);
236 void ElfFile::validateStringTable(const ElfW(Shdr)& stringTable) const {
237 FOLLY_SAFE_CHECK(stringTable.sh_type == SHT_STRTAB,
238 "invalid type for string table");
240 const char* start = file_ + stringTable.sh_offset;
241 // First and last bytes must be 0
242 FOLLY_SAFE_CHECK(stringTable.sh_size == 0 ||
243 (start[0] == '\0' && start[stringTable.sh_size - 1] == '\0'),
244 "invalid string table");
247 const char* ElfFile::getString(const ElfW(Shdr)& stringTable, size_t offset)
249 validateStringTable(stringTable);
250 FOLLY_SAFE_CHECK(offset < stringTable.sh_size,
251 "invalid offset in string table");
253 return file_ + stringTable.sh_offset + offset;
256 const char* ElfFile::getSectionName(const ElfW(Shdr)& section) const {
257 if (elfHeader().e_shstrndx == SHN_UNDEF) {
258 return nullptr; // no section name string table
261 const ElfW(Shdr)& sectionNames = *getSectionByIndex(elfHeader().e_shstrndx);
262 return getString(sectionNames, section.sh_name);
265 const ElfW(Shdr)* ElfFile::getSectionByName(const char* name) const {
266 if (elfHeader().e_shstrndx == SHN_UNDEF) {
267 return nullptr; // no section name string table
270 // Find offset in the section name string table of the requested name
271 const ElfW(Shdr)& sectionNames = *getSectionByIndex(elfHeader().e_shstrndx);
272 const char* foundName = iterateStrings(
274 [&] (const char* s) { return !strcmp(name, s); });
275 if (foundName == nullptr) {
279 size_t offset = foundName - (file_ + sectionNames.sh_offset);
281 // Find section with the appropriate sh_name offset
282 const ElfW(Shdr)* foundSection = iterateSections(
283 [&](const ElfW(Shdr)& sh) {
284 if (sh.sh_name == offset) {
292 ElfFile::Symbol ElfFile::getDefinitionByAddress(uintptr_t address) const {
293 Symbol foundSymbol {nullptr, nullptr};
295 auto findSection = [&](const ElfW(Shdr)& section) {
296 auto findSymbols = [&](const ElfW(Sym)& sym) {
297 if (sym.st_shndx == SHN_UNDEF) {
298 return false; // not a definition
300 if (address >= sym.st_value && address < sym.st_value + sym.st_size) {
301 foundSymbol.first = §ion;
302 foundSymbol.second = &sym;
309 return iterateSymbolsWithType(section, STT_OBJECT, findSymbols) ||
310 iterateSymbolsWithType(section, STT_FUNC, findSymbols);
313 // Try the .dynsym section first if it exists, it's smaller.
314 (iterateSectionsWithType(SHT_DYNSYM, findSection) ||
315 iterateSectionsWithType(SHT_SYMTAB, findSection));
320 ElfFile::Symbol ElfFile::getSymbolByName(const char* name) const {
321 Symbol foundSymbol{nullptr, nullptr};
323 auto findSection = [&](const ElfW(Shdr)& section) -> bool {
324 // This section has no string table associated w/ its symbols; hence we
325 // can't get names for them
326 if (section.sh_link == SHN_UNDEF) {
330 auto findSymbols = [&](const ElfW(Sym)& sym) -> bool {
331 if (sym.st_shndx == SHN_UNDEF) {
332 return false; // not a definition
334 if (sym.st_name == 0) {
335 return false; // no name for this symbol
337 const char* sym_name = getString(
338 *getSectionByIndex(section.sh_link), sym.st_name);
339 if (strcmp(sym_name, name) == 0) {
340 foundSymbol.first = §ion;
341 foundSymbol.second = &sym;
348 return iterateSymbolsWithType(section, STT_OBJECT, findSymbols) ||
349 iterateSymbolsWithType(section, STT_FUNC, findSymbols);
352 // Try the .dynsym section first if it exists, it's smaller.
353 iterateSectionsWithType(SHT_DYNSYM, findSection) ||
354 iterateSectionsWithType(SHT_SYMTAB, findSection);
359 const ElfW(Shdr)* ElfFile::getSectionContainingAddress(ElfW(Addr) addr) const {
360 return iterateSections([&](const ElfW(Shdr)& sh) -> bool {
361 return (addr >= sh.sh_addr) && (addr < (sh.sh_addr + sh.sh_size));
365 const char* ElfFile::getSymbolName(Symbol symbol) const {
366 if (!symbol.first || !symbol.second) {
370 if (symbol.second->st_name == 0) {
371 return nullptr; // symbol has no name
374 if (symbol.first->sh_link == SHN_UNDEF) {
375 return nullptr; // symbol table has no strings
378 return getString(*getSectionByIndex(symbol.first->sh_link),
379 symbol.second->st_name);
382 } // namespace symbolizer