2 * Copyright 2012 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"
33 namespace symbolizer {
37 file_(static_cast<char*>(MAP_FAILED)),
42 ElfFile::ElfFile(const char* name)
43 : fd_(open(name, O_RDONLY)),
44 file_(static_cast<char*>(MAP_FAILED)),
48 systemError("open ", name);
52 int r = fstat(fd_, &st);
58 file_ = static_cast<char*>(
59 mmap(nullptr, length_, PROT_READ, MAP_SHARED, fd_, 0));
60 if (file_ == MAP_FAILED) {
70 ElfFile::ElfFile(ElfFile&& other)
73 length_(other.length_),
74 baseAddress_(other.baseAddress_) {
76 other.file_ = static_cast<char*>(MAP_FAILED);
78 other.baseAddress_ = 0;
81 ElfFile& ElfFile::operator=(ElfFile&& other) {
82 assert(this != &other);
87 length_ = other.length_;
88 baseAddress_ = other.baseAddress_;
91 other.file_ = static_cast<char*>(MAP_FAILED);
93 other.baseAddress_ = 0;
98 void ElfFile::destroy() {
99 if (file_ != MAP_FAILED) {
100 munmap(file_, length_);
108 void ElfFile::init() {
109 auto& elfHeader = this->elfHeader();
111 // Validate ELF magic numbers
112 enforce(elfHeader.e_ident[EI_MAG0] == ELFMAG0 &&
113 elfHeader.e_ident[EI_MAG1] == ELFMAG1 &&
114 elfHeader.e_ident[EI_MAG2] == ELFMAG2 &&
115 elfHeader.e_ident[EI_MAG3] == ELFMAG3,
116 "invalid ELF magic");
118 // Validate ELF class (32/64 bits)
119 #define EXPECTED_CLASS P1(ELFCLASS, __ELF_NATIVE_CLASS)
120 #define P1(a, b) P2(a, b)
121 #define P2(a, b) a ## b
122 enforce(elfHeader.e_ident[EI_CLASS] == EXPECTED_CLASS,
123 "invalid ELF class");
126 #undef EXPECTED_CLASS
128 // Validate ELF data encoding (LSB/MSB)
129 #if __BYTE_ORDER == __LITTLE_ENDIAN
130 # define EXPECTED_ENCODING ELFDATA2LSB
131 #elif __BYTE_ORDER == __BIG_ENDIAN
132 # define EXPECTED_ENCODING ELFDATA2MSB
134 # error Unsupported byte order
136 enforce(elfHeader.e_ident[EI_DATA] == EXPECTED_ENCODING,
137 "invalid ELF encoding");
138 #undef EXPECTED_ENCODING
140 // Validate ELF version (1)
141 enforce(elfHeader.e_ident[EI_VERSION] == EV_CURRENT &&
142 elfHeader.e_version == EV_CURRENT,
143 "invalid ELF version");
145 // We only support executable and shared object files
146 enforce(elfHeader.e_type == ET_EXEC || elfHeader.e_type == ET_DYN,
147 "invalid ELF file type");
149 enforce(elfHeader.e_phnum != 0, "no program header!");
150 enforce(elfHeader.e_phentsize == sizeof(ElfW(Phdr)),
151 "invalid program header entry size");
152 enforce(elfHeader.e_shentsize == sizeof(ElfW(Shdr)),
153 "invalid section header entry size");
155 const ElfW(Phdr)* programHeader = &at<ElfW(Phdr)>(elfHeader.e_phoff);
156 bool foundBase = false;
157 for (size_t i = 0; i < elfHeader.e_phnum; programHeader++, i++) {
158 // Program headers are sorted by load address, so the first PT_LOAD
159 // header gives us the base address.
160 if (programHeader->p_type == PT_LOAD) {
161 baseAddress_ = programHeader->p_vaddr;
167 enforce(foundBase, "could not find base address");
170 const ElfW(Shdr)* ElfFile::getSectionByIndex(size_t idx) const {
171 enforce(idx < elfHeader().e_shnum, "invalid section index");
172 return &at<ElfW(Shdr)>(elfHeader().e_shoff + idx * sizeof(ElfW(Shdr)));
175 folly::StringPiece ElfFile::getSectionBody(const ElfW(Shdr)& section) const {
176 return folly::StringPiece(file_ + section.sh_offset, section.sh_size);
179 void ElfFile::validateStringTable(const ElfW(Shdr)& stringTable) const {
180 enforce(stringTable.sh_type == SHT_STRTAB, "invalid type for string table");
182 const char* start = file_ + stringTable.sh_offset;
183 // First and last bytes must be 0
184 enforce(stringTable.sh_size == 0 ||
185 (start[0] == '\0' && start[stringTable.sh_size - 1] == '\0'),
186 "invalid string table");
189 const char* ElfFile::getString(const ElfW(Shdr)& stringTable, size_t offset)
191 validateStringTable(stringTable);
192 enforce(offset < stringTable.sh_size, "invalid offset in string table");
194 return file_ + stringTable.sh_offset + offset;
197 const char* ElfFile::getSectionName(const ElfW(Shdr)& section) const {
198 if (elfHeader().e_shstrndx == SHN_UNDEF) {
199 return nullptr; // no section name string table
202 const ElfW(Shdr)& sectionNames = *getSectionByIndex(elfHeader().e_shstrndx);
203 return getString(sectionNames, section.sh_name);
206 const ElfW(Shdr)* ElfFile::getSectionByName(const char* name) const {
207 if (elfHeader().e_shstrndx == SHN_UNDEF) {
208 return nullptr; // no section name string table
211 // Find offset in the section name string table of the requested name
212 const ElfW(Shdr)& sectionNames = *getSectionByIndex(elfHeader().e_shstrndx);
213 const char* foundName = iterateStrings(
215 [&] (const char* s) { return !strcmp(name, s); });
216 if (foundName == nullptr) {
220 size_t offset = foundName - (file_ + sectionNames.sh_offset);
222 // Find section with the appropriate sh_name offset
223 const ElfW(Shdr)* foundSection = iterateSections(
224 [&](const ElfW(Shdr)& sh) {
225 if (sh.sh_name == offset) {
233 ElfFile::Symbol ElfFile::getDefinitionByAddress(uintptr_t address) const {
234 Symbol foundSymbol {nullptr, nullptr};
236 auto find = [&] (const ElfW(Shdr)& section) {
237 enforce(section.sh_entsize == sizeof(ElfW(Sym)),
238 "invalid entry size in symbol table");
240 const ElfW(Sym)* sym = &at<ElfW(Sym)>(section.sh_offset);
241 const ElfW(Sym)* end = &at<ElfW(Sym)>(section.sh_offset + section.sh_size);
242 for (; sym != end; ++sym) {
243 // st_info has the same representation on 32- and 64-bit platforms
244 auto type = ELF32_ST_TYPE(sym->st_info);
246 // TODO(tudorb): Handle STT_TLS, but then we'd have to understand
247 // thread-local relocations. If all we're looking up is functions
248 // (instruction pointers), it doesn't matter, though.
249 if (type != STT_OBJECT && type != STT_FUNC) {
252 if (sym->st_shndx == SHN_UNDEF) {
253 continue; // not a definition
255 if (address >= sym->st_value && address < sym->st_value + sym->st_size) {
256 foundSymbol.first = §ion;
257 foundSymbol.second = sym;
265 // Try the .dynsym section first if it exists, it's smaller.
266 (iterateSectionsWithType(SHT_DYNSYM, find) ||
267 iterateSectionsWithType(SHT_SYMTAB, find));
272 const char* ElfFile::getSymbolName(Symbol symbol) const {
273 if (!symbol.first || !symbol.second) {
277 if (symbol.second->st_name == 0) {
278 return nullptr; // symbol has no name
281 if (symbol.first->sh_link == SHN_UNDEF) {
282 return nullptr; // symbol table has no strings
285 return getString(*getSectionByIndex(symbol.first->sh_link),
286 symbol.second->st_name);
289 } // namespace symbolizer