2 * Copyright 2013 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>
28 #include <glog/logging.h>
30 #include "folly/Conv.h"
31 #include "folly/Exception.h"
34 namespace symbolizer {
38 file_(static_cast<char*>(MAP_FAILED)),
43 ElfFile::ElfFile(const char* name)
44 : fd_(open(name, O_RDONLY)),
45 file_(static_cast<char*>(MAP_FAILED)),
49 folly::throwSystemError("open ", name);
53 int r = fstat(fd_, &st);
55 folly::throwSystemError("fstat");
59 file_ = static_cast<char*>(
60 mmap(nullptr, length_, PROT_READ, MAP_SHARED, fd_, 0));
61 if (file_ == MAP_FAILED) {
62 folly::throwSystemError("mmap");
71 ElfFile::ElfFile(ElfFile&& other)
74 length_(other.length_),
75 baseAddress_(other.baseAddress_) {
77 other.file_ = static_cast<char*>(MAP_FAILED);
79 other.baseAddress_ = 0;
82 ElfFile& ElfFile::operator=(ElfFile&& other) {
83 assert(this != &other);
88 length_ = other.length_;
89 baseAddress_ = other.baseAddress_;
92 other.file_ = static_cast<char*>(MAP_FAILED);
94 other.baseAddress_ = 0;
99 void ElfFile::destroy() {
100 if (file_ != MAP_FAILED) {
101 munmap(file_, length_);
109 void ElfFile::init() {
110 auto& elfHeader = this->elfHeader();
112 // Validate ELF magic numbers
113 enforce(elfHeader.e_ident[EI_MAG0] == ELFMAG0 &&
114 elfHeader.e_ident[EI_MAG1] == ELFMAG1 &&
115 elfHeader.e_ident[EI_MAG2] == ELFMAG2 &&
116 elfHeader.e_ident[EI_MAG3] == ELFMAG3,
117 "invalid ELF magic");
119 // Validate ELF class (32/64 bits)
120 #define EXPECTED_CLASS P1(ELFCLASS, __ELF_NATIVE_CLASS)
121 #define P1(a, b) P2(a, b)
122 #define P2(a, b) a ## b
123 enforce(elfHeader.e_ident[EI_CLASS] == EXPECTED_CLASS,
124 "invalid ELF class");
127 #undef EXPECTED_CLASS
129 // Validate ELF data encoding (LSB/MSB)
130 #if __BYTE_ORDER == __LITTLE_ENDIAN
131 # define EXPECTED_ENCODING ELFDATA2LSB
132 #elif __BYTE_ORDER == __BIG_ENDIAN
133 # define EXPECTED_ENCODING ELFDATA2MSB
135 # error Unsupported byte order
137 enforce(elfHeader.e_ident[EI_DATA] == EXPECTED_ENCODING,
138 "invalid ELF encoding");
139 #undef EXPECTED_ENCODING
141 // Validate ELF version (1)
142 enforce(elfHeader.e_ident[EI_VERSION] == EV_CURRENT &&
143 elfHeader.e_version == EV_CURRENT,
144 "invalid ELF version");
146 // We only support executable and shared object files
147 enforce(elfHeader.e_type == ET_EXEC || elfHeader.e_type == ET_DYN,
148 "invalid ELF file type");
150 enforce(elfHeader.e_phnum != 0, "no program header!");
151 enforce(elfHeader.e_phentsize == sizeof(ElfW(Phdr)),
152 "invalid program header entry size");
153 enforce(elfHeader.e_shentsize == sizeof(ElfW(Shdr)),
154 "invalid section header entry size");
156 const ElfW(Phdr)* programHeader = &at<ElfW(Phdr)>(elfHeader.e_phoff);
157 bool foundBase = false;
158 for (size_t i = 0; i < elfHeader.e_phnum; programHeader++, i++) {
159 // Program headers are sorted by load address, so the first PT_LOAD
160 // header gives us the base address.
161 if (programHeader->p_type == PT_LOAD) {
162 baseAddress_ = programHeader->p_vaddr;
168 enforce(foundBase, "could not find base address");
171 const ElfW(Shdr)* ElfFile::getSectionByIndex(size_t idx) const {
172 enforce(idx < elfHeader().e_shnum, "invalid section index");
173 return &at<ElfW(Shdr)>(elfHeader().e_shoff + idx * sizeof(ElfW(Shdr)));
176 folly::StringPiece ElfFile::getSectionBody(const ElfW(Shdr)& section) const {
177 return folly::StringPiece(file_ + section.sh_offset, section.sh_size);
180 void ElfFile::validateStringTable(const ElfW(Shdr)& stringTable) const {
181 enforce(stringTable.sh_type == SHT_STRTAB, "invalid type for string table");
183 const char* start = file_ + stringTable.sh_offset;
184 // First and last bytes must be 0
185 enforce(stringTable.sh_size == 0 ||
186 (start[0] == '\0' && start[stringTable.sh_size - 1] == '\0'),
187 "invalid string table");
190 const char* ElfFile::getString(const ElfW(Shdr)& stringTable, size_t offset)
192 validateStringTable(stringTable);
193 enforce(offset < stringTable.sh_size, "invalid offset in string table");
195 return file_ + stringTable.sh_offset + offset;
198 const char* ElfFile::getSectionName(const ElfW(Shdr)& section) const {
199 if (elfHeader().e_shstrndx == SHN_UNDEF) {
200 return nullptr; // no section name string table
203 const ElfW(Shdr)& sectionNames = *getSectionByIndex(elfHeader().e_shstrndx);
204 return getString(sectionNames, section.sh_name);
207 const ElfW(Shdr)* ElfFile::getSectionByName(const char* name) const {
208 if (elfHeader().e_shstrndx == SHN_UNDEF) {
209 return nullptr; // no section name string table
212 // Find offset in the section name string table of the requested name
213 const ElfW(Shdr)& sectionNames = *getSectionByIndex(elfHeader().e_shstrndx);
214 const char* foundName = iterateStrings(
216 [&] (const char* s) { return !strcmp(name, s); });
217 if (foundName == nullptr) {
221 size_t offset = foundName - (file_ + sectionNames.sh_offset);
223 // Find section with the appropriate sh_name offset
224 const ElfW(Shdr)* foundSection = iterateSections(
225 [&](const ElfW(Shdr)& sh) {
226 if (sh.sh_name == offset) {
234 ElfFile::Symbol ElfFile::getDefinitionByAddress(uintptr_t address) const {
235 Symbol foundSymbol {nullptr, nullptr};
237 auto findSection = [&](const ElfW(Shdr)& section) {
238 auto findSymbols = [&](const ElfW(Sym)& sym) {
239 if (sym.st_shndx == SHN_UNDEF) {
240 return false; // not a definition
242 if (address >= sym.st_value && address < sym.st_value + sym.st_size) {
243 foundSymbol.first = §ion;
244 foundSymbol.second = &sym;
251 return iterateSymbolsWithType(section, STT_OBJECT, findSymbols) ||
252 iterateSymbolsWithType(section, STT_FUNC, findSymbols);
255 // Try the .dynsym section first if it exists, it's smaller.
256 (iterateSectionsWithType(SHT_DYNSYM, findSection) ||
257 iterateSectionsWithType(SHT_SYMTAB, findSection));
262 ElfFile::Symbol ElfFile::getSymbolByName(const char* name) const {
263 Symbol foundSymbol{nullptr, nullptr};
265 auto findSection = [&](const ElfW(Shdr)& section) -> bool {
266 // This section has no string table associated w/ its symbols; hence we
267 // can't get names for them
268 if (section.sh_link == SHN_UNDEF) {
272 auto findSymbols = [&](const ElfW(Sym)& sym) -> bool {
273 if (sym.st_shndx == SHN_UNDEF) {
274 return false; // not a definition
276 if (sym.st_name == 0) {
277 return false; // no name for this symbol
279 const char* sym_name = getString(
280 *getSectionByIndex(section.sh_link), sym.st_name);
281 if (strcmp(sym_name, name) == 0) {
282 foundSymbol.first = §ion;
283 foundSymbol.second = &sym;
290 return iterateSymbolsWithType(section, STT_OBJECT, findSymbols) ||
291 iterateSymbolsWithType(section, STT_FUNC, findSymbols);
294 // Try the .dynsym section first if it exists, it's smaller.
295 iterateSectionsWithType(SHT_DYNSYM, findSection) ||
296 iterateSectionsWithType(SHT_SYMTAB, findSection);
301 const ElfW(Shdr)* ElfFile::getSectionContainingAddress(ElfW(Addr) addr) const {
302 return iterateSections([&](const ElfW(Shdr)& sh) -> bool {
303 return (addr >= sh.sh_addr) && (addr < (sh.sh_addr + sh.sh_size));
307 const char* ElfFile::getSymbolName(Symbol symbol) const {
308 if (!symbol.first || !symbol.second) {
312 if (symbol.second->st_name == 0) {
313 return nullptr; // symbol has no name
316 if (symbol.first->sh_link == SHN_UNDEF) {
317 return nullptr; // symbol table has no strings
320 return getString(*getSectionByIndex(symbol.first->sh_link),
321 symbol.second->st_name);
324 } // namespace symbolizer