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>
27 #include <glog/logging.h>
29 #include "folly/Conv.h"
30 #include "folly/Exception.h"
33 namespace symbolizer {
37 file_(static_cast<char*>(MAP_FAILED)),
42 ElfFile::ElfFile(const char* name, bool readOnly)
43 : fd_(open(name, (readOnly) ? O_RDONLY : O_RDWR)),
44 file_(static_cast<char*>(MAP_FAILED)),
48 folly::throwSystemError("open ", name);
52 int r = fstat(fd_, &st);
54 folly::throwSystemError("fstat");
62 file_ = static_cast<char*>(mmap(nullptr, length_, prot, MAP_SHARED, fd_, 0));
63 if (file_ == MAP_FAILED) {
64 folly::throwSystemError("mmap");
73 ElfFile::ElfFile(ElfFile&& other)
76 length_(other.length_),
77 baseAddress_(other.baseAddress_) {
79 other.file_ = static_cast<char*>(MAP_FAILED);
81 other.baseAddress_ = 0;
84 ElfFile& ElfFile::operator=(ElfFile&& other) {
85 assert(this != &other);
90 length_ = other.length_;
91 baseAddress_ = other.baseAddress_;
94 other.file_ = static_cast<char*>(MAP_FAILED);
96 other.baseAddress_ = 0;
101 void ElfFile::destroy() {
102 if (file_ != MAP_FAILED) {
103 munmap(file_, length_);
111 void ElfFile::init() {
112 auto& elfHeader = this->elfHeader();
114 // Validate ELF magic numbers
115 enforce(elfHeader.e_ident[EI_MAG0] == ELFMAG0 &&
116 elfHeader.e_ident[EI_MAG1] == ELFMAG1 &&
117 elfHeader.e_ident[EI_MAG2] == ELFMAG2 &&
118 elfHeader.e_ident[EI_MAG3] == ELFMAG3,
119 "invalid ELF magic");
121 // Validate ELF class (32/64 bits)
122 #define EXPECTED_CLASS P1(ELFCLASS, __ELF_NATIVE_CLASS)
123 #define P1(a, b) P2(a, b)
124 #define P2(a, b) a ## b
125 enforce(elfHeader.e_ident[EI_CLASS] == EXPECTED_CLASS,
126 "invalid ELF class");
129 #undef EXPECTED_CLASS
131 // Validate ELF data encoding (LSB/MSB)
132 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
133 # define EXPECTED_ENCODING ELFDATA2LSB
134 #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
135 # define EXPECTED_ENCODING ELFDATA2MSB
137 # error Unsupported byte order
139 enforce(elfHeader.e_ident[EI_DATA] == EXPECTED_ENCODING,
140 "invalid ELF encoding");
141 #undef EXPECTED_ENCODING
143 // Validate ELF version (1)
144 enforce(elfHeader.e_ident[EI_VERSION] == EV_CURRENT &&
145 elfHeader.e_version == EV_CURRENT,
146 "invalid ELF version");
148 // We only support executable and shared object files
149 enforce(elfHeader.e_type == ET_EXEC || elfHeader.e_type == ET_DYN,
150 "invalid ELF file type");
152 enforce(elfHeader.e_phnum != 0, "no program header!");
153 enforce(elfHeader.e_phentsize == sizeof(ElfW(Phdr)),
154 "invalid program header entry size");
155 enforce(elfHeader.e_shentsize == sizeof(ElfW(Shdr)),
156 "invalid section header entry size");
158 const ElfW(Phdr)* programHeader = &at<ElfW(Phdr)>(elfHeader.e_phoff);
159 bool foundBase = false;
160 for (size_t i = 0; i < elfHeader.e_phnum; programHeader++, i++) {
161 // Program headers are sorted by load address, so the first PT_LOAD
162 // header gives us the base address.
163 if (programHeader->p_type == PT_LOAD) {
164 baseAddress_ = programHeader->p_vaddr;
170 enforce(foundBase, "could not find base address");
173 const ElfW(Shdr)* ElfFile::getSectionByIndex(size_t idx) const {
174 enforce(idx < elfHeader().e_shnum, "invalid section index");
175 return &at<ElfW(Shdr)>(elfHeader().e_shoff + idx * sizeof(ElfW(Shdr)));
178 folly::StringPiece ElfFile::getSectionBody(const ElfW(Shdr)& section) const {
179 return folly::StringPiece(file_ + section.sh_offset, section.sh_size);
182 void ElfFile::validateStringTable(const ElfW(Shdr)& stringTable) const {
183 enforce(stringTable.sh_type == SHT_STRTAB, "invalid type for string table");
185 const char* start = file_ + stringTable.sh_offset;
186 // First and last bytes must be 0
187 enforce(stringTable.sh_size == 0 ||
188 (start[0] == '\0' && start[stringTable.sh_size - 1] == '\0'),
189 "invalid string table");
192 const char* ElfFile::getString(const ElfW(Shdr)& stringTable, size_t offset)
194 validateStringTable(stringTable);
195 enforce(offset < stringTable.sh_size, "invalid offset in string table");
197 return file_ + stringTable.sh_offset + offset;
200 const char* ElfFile::getSectionName(const ElfW(Shdr)& section) const {
201 if (elfHeader().e_shstrndx == SHN_UNDEF) {
202 return nullptr; // no section name string table
205 const ElfW(Shdr)& sectionNames = *getSectionByIndex(elfHeader().e_shstrndx);
206 return getString(sectionNames, section.sh_name);
209 const ElfW(Shdr)* ElfFile::getSectionByName(const char* name) const {
210 if (elfHeader().e_shstrndx == SHN_UNDEF) {
211 return nullptr; // no section name string table
214 // Find offset in the section name string table of the requested name
215 const ElfW(Shdr)& sectionNames = *getSectionByIndex(elfHeader().e_shstrndx);
216 const char* foundName = iterateStrings(
218 [&] (const char* s) { return !strcmp(name, s); });
219 if (foundName == nullptr) {
223 size_t offset = foundName - (file_ + sectionNames.sh_offset);
225 // Find section with the appropriate sh_name offset
226 const ElfW(Shdr)* foundSection = iterateSections(
227 [&](const ElfW(Shdr)& sh) {
228 if (sh.sh_name == offset) {
236 ElfFile::Symbol ElfFile::getDefinitionByAddress(uintptr_t address) const {
237 Symbol foundSymbol {nullptr, nullptr};
239 auto findSection = [&](const ElfW(Shdr)& section) {
240 auto findSymbols = [&](const ElfW(Sym)& sym) {
241 if (sym.st_shndx == SHN_UNDEF) {
242 return false; // not a definition
244 if (address >= sym.st_value && address < sym.st_value + sym.st_size) {
245 foundSymbol.first = §ion;
246 foundSymbol.second = &sym;
253 return iterateSymbolsWithType(section, STT_OBJECT, findSymbols) ||
254 iterateSymbolsWithType(section, STT_FUNC, findSymbols);
257 // Try the .dynsym section first if it exists, it's smaller.
258 (iterateSectionsWithType(SHT_DYNSYM, findSection) ||
259 iterateSectionsWithType(SHT_SYMTAB, findSection));
264 ElfFile::Symbol ElfFile::getSymbolByName(const char* name) const {
265 Symbol foundSymbol{nullptr, nullptr};
267 auto findSection = [&](const ElfW(Shdr)& section) -> bool {
268 // This section has no string table associated w/ its symbols; hence we
269 // can't get names for them
270 if (section.sh_link == SHN_UNDEF) {
274 auto findSymbols = [&](const ElfW(Sym)& sym) -> bool {
275 if (sym.st_shndx == SHN_UNDEF) {
276 return false; // not a definition
278 if (sym.st_name == 0) {
279 return false; // no name for this symbol
281 const char* sym_name = getString(
282 *getSectionByIndex(section.sh_link), sym.st_name);
283 if (strcmp(sym_name, name) == 0) {
284 foundSymbol.first = §ion;
285 foundSymbol.second = &sym;
292 return iterateSymbolsWithType(section, STT_OBJECT, findSymbols) ||
293 iterateSymbolsWithType(section, STT_FUNC, findSymbols);
296 // Try the .dynsym section first if it exists, it's smaller.
297 iterateSectionsWithType(SHT_DYNSYM, findSection) ||
298 iterateSectionsWithType(SHT_SYMTAB, findSection);
303 const ElfW(Shdr)* ElfFile::getSectionContainingAddress(ElfW(Addr) addr) const {
304 return iterateSections([&](const ElfW(Shdr)& sh) -> bool {
305 return (addr >= sh.sh_addr) && (addr < (sh.sh_addr + sh.sh_size));
309 const char* ElfFile::getSymbolName(Symbol symbol) const {
310 if (!symbol.first || !symbol.second) {
314 if (symbol.second->st_name == 0) {
315 return nullptr; // symbol has no name
318 if (symbol.first->sh_link == SHN_UNDEF) {
319 return nullptr; // symbol table has no strings
322 return getString(*getSectionByIndex(symbol.first->sh_link),
323 symbol.second->st_name);
326 } // namespace symbolizer