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 {
35 ElfFile::ElfFile() noexcept
37 file_(static_cast<char*>(MAP_FAILED)),
42 ElfFile::ElfFile(const char* name, bool readOnly)
44 file_(static_cast<char*>(MAP_FAILED)),
50 void ElfFile::open(const char* name, bool readOnly) {
52 int r = openNoThrow(name, readOnly, &msg);
53 folly::checkUnixError(r, msg);
56 int ElfFile::openNoThrow(const char* name, bool readOnly, const char** msg)
58 FOLLY_SAFE_CHECK(fd_ == -1, "File already open");
59 fd_ = ::open(name, readOnly ? O_RDONLY : O_RDWR);
61 if (msg) *msg = "open";
66 int r = fstat(fd_, &st);
68 if (msg) *msg = "fstat";
77 file_ = static_cast<char*>(mmap(nullptr, length_, prot, MAP_SHARED, fd_, 0));
78 if (file_ == MAP_FAILED) {
79 if (msg) *msg = "mmap";
90 ElfFile::ElfFile(ElfFile&& other)
93 length_(other.length_),
94 baseAddress_(other.baseAddress_) {
96 other.file_ = static_cast<char*>(MAP_FAILED);
98 other.baseAddress_ = 0;
101 ElfFile& ElfFile::operator=(ElfFile&& other) {
102 assert(this != &other);
107 length_ = other.length_;
108 baseAddress_ = other.baseAddress_;
111 other.file_ = static_cast<char*>(MAP_FAILED);
113 other.baseAddress_ = 0;
118 void ElfFile::destroy() {
119 if (file_ != MAP_FAILED) {
120 munmap(file_, length_);
128 void ElfFile::init() {
129 auto& elfHeader = this->elfHeader();
131 // Validate ELF magic numbers
132 FOLLY_SAFE_CHECK(elfHeader.e_ident[EI_MAG0] == ELFMAG0 &&
133 elfHeader.e_ident[EI_MAG1] == ELFMAG1 &&
134 elfHeader.e_ident[EI_MAG2] == ELFMAG2 &&
135 elfHeader.e_ident[EI_MAG3] == ELFMAG3,
136 "invalid ELF magic");
138 // Validate ELF class (32/64 bits)
139 #define EXPECTED_CLASS P1(ELFCLASS, __ELF_NATIVE_CLASS)
140 #define P1(a, b) P2(a, b)
141 #define P2(a, b) a ## b
142 FOLLY_SAFE_CHECK(elfHeader.e_ident[EI_CLASS] == EXPECTED_CLASS,
143 "invalid ELF class");
146 #undef EXPECTED_CLASS
148 // Validate ELF data encoding (LSB/MSB)
149 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
150 # define EXPECTED_ENCODING ELFDATA2LSB
151 #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
152 # define EXPECTED_ENCODING ELFDATA2MSB
154 # error Unsupported byte order
156 FOLLY_SAFE_CHECK(elfHeader.e_ident[EI_DATA] == EXPECTED_ENCODING,
157 "invalid ELF encoding");
158 #undef EXPECTED_ENCODING
160 // Validate ELF version (1)
161 FOLLY_SAFE_CHECK(elfHeader.e_ident[EI_VERSION] == EV_CURRENT &&
162 elfHeader.e_version == EV_CURRENT,
163 "invalid ELF version");
165 // We only support executable and shared object files
166 FOLLY_SAFE_CHECK(elfHeader.e_type == ET_EXEC || elfHeader.e_type == ET_DYN,
167 "invalid ELF file type");
169 FOLLY_SAFE_CHECK(elfHeader.e_phnum != 0, "no program header!");
170 FOLLY_SAFE_CHECK(elfHeader.e_phentsize == sizeof(ElfW(Phdr)),
171 "invalid program header entry size");
172 FOLLY_SAFE_CHECK(elfHeader.e_shentsize == sizeof(ElfW(Shdr)),
173 "invalid section header entry size");
175 const ElfW(Phdr)* programHeader = &at<ElfW(Phdr)>(elfHeader.e_phoff);
176 bool foundBase = false;
177 for (size_t i = 0; i < elfHeader.e_phnum; programHeader++, i++) {
178 // Program headers are sorted by load address, so the first PT_LOAD
179 // header gives us the base address.
180 if (programHeader->p_type == PT_LOAD) {
181 baseAddress_ = programHeader->p_vaddr;
187 FOLLY_SAFE_CHECK(foundBase, "could not find base address");
190 const ElfW(Shdr)* ElfFile::getSectionByIndex(size_t idx) const {
191 FOLLY_SAFE_CHECK(idx < elfHeader().e_shnum, "invalid section index");
192 return &at<ElfW(Shdr)>(elfHeader().e_shoff + idx * sizeof(ElfW(Shdr)));
195 folly::StringPiece ElfFile::getSectionBody(const ElfW(Shdr)& section) const {
196 return folly::StringPiece(file_ + section.sh_offset, section.sh_size);
199 void ElfFile::validateStringTable(const ElfW(Shdr)& stringTable) const {
200 FOLLY_SAFE_CHECK(stringTable.sh_type == SHT_STRTAB,
201 "invalid type for string table");
203 const char* start = file_ + stringTable.sh_offset;
204 // First and last bytes must be 0
205 FOLLY_SAFE_CHECK(stringTable.sh_size == 0 ||
206 (start[0] == '\0' && start[stringTable.sh_size - 1] == '\0'),
207 "invalid string table");
210 const char* ElfFile::getString(const ElfW(Shdr)& stringTable, size_t offset)
212 validateStringTable(stringTable);
213 FOLLY_SAFE_CHECK(offset < stringTable.sh_size,
214 "invalid offset in string table");
216 return file_ + stringTable.sh_offset + offset;
219 const char* ElfFile::getSectionName(const ElfW(Shdr)& section) const {
220 if (elfHeader().e_shstrndx == SHN_UNDEF) {
221 return nullptr; // no section name string table
224 const ElfW(Shdr)& sectionNames = *getSectionByIndex(elfHeader().e_shstrndx);
225 return getString(sectionNames, section.sh_name);
228 const ElfW(Shdr)* ElfFile::getSectionByName(const char* name) const {
229 if (elfHeader().e_shstrndx == SHN_UNDEF) {
230 return nullptr; // no section name string table
233 // Find offset in the section name string table of the requested name
234 const ElfW(Shdr)& sectionNames = *getSectionByIndex(elfHeader().e_shstrndx);
235 const char* foundName = iterateStrings(
237 [&] (const char* s) { return !strcmp(name, s); });
238 if (foundName == nullptr) {
242 size_t offset = foundName - (file_ + sectionNames.sh_offset);
244 // Find section with the appropriate sh_name offset
245 const ElfW(Shdr)* foundSection = iterateSections(
246 [&](const ElfW(Shdr)& sh) {
247 if (sh.sh_name == offset) {
255 ElfFile::Symbol ElfFile::getDefinitionByAddress(uintptr_t address) const {
256 Symbol foundSymbol {nullptr, nullptr};
258 auto findSection = [&](const ElfW(Shdr)& section) {
259 auto findSymbols = [&](const ElfW(Sym)& sym) {
260 if (sym.st_shndx == SHN_UNDEF) {
261 return false; // not a definition
263 if (address >= sym.st_value && address < sym.st_value + sym.st_size) {
264 foundSymbol.first = §ion;
265 foundSymbol.second = &sym;
272 return iterateSymbolsWithType(section, STT_OBJECT, findSymbols) ||
273 iterateSymbolsWithType(section, STT_FUNC, findSymbols);
276 // Try the .dynsym section first if it exists, it's smaller.
277 (iterateSectionsWithType(SHT_DYNSYM, findSection) ||
278 iterateSectionsWithType(SHT_SYMTAB, findSection));
283 ElfFile::Symbol ElfFile::getSymbolByName(const char* name) const {
284 Symbol foundSymbol{nullptr, nullptr};
286 auto findSection = [&](const ElfW(Shdr)& section) -> bool {
287 // This section has no string table associated w/ its symbols; hence we
288 // can't get names for them
289 if (section.sh_link == SHN_UNDEF) {
293 auto findSymbols = [&](const ElfW(Sym)& sym) -> bool {
294 if (sym.st_shndx == SHN_UNDEF) {
295 return false; // not a definition
297 if (sym.st_name == 0) {
298 return false; // no name for this symbol
300 const char* sym_name = getString(
301 *getSectionByIndex(section.sh_link), sym.st_name);
302 if (strcmp(sym_name, name) == 0) {
303 foundSymbol.first = §ion;
304 foundSymbol.second = &sym;
311 return iterateSymbolsWithType(section, STT_OBJECT, findSymbols) ||
312 iterateSymbolsWithType(section, STT_FUNC, findSymbols);
315 // Try the .dynsym section first if it exists, it's smaller.
316 iterateSectionsWithType(SHT_DYNSYM, findSection) ||
317 iterateSectionsWithType(SHT_SYMTAB, findSection);
322 const ElfW(Shdr)* ElfFile::getSectionContainingAddress(ElfW(Addr) addr) const {
323 return iterateSections([&](const ElfW(Shdr)& sh) -> bool {
324 return (addr >= sh.sh_addr) && (addr < (sh.sh_addr + sh.sh_size));
328 const char* ElfFile::getSymbolName(Symbol symbol) const {
329 if (!symbol.first || !symbol.second) {
333 if (symbol.second->st_name == 0) {
334 return nullptr; // symbol has no name
337 if (symbol.first->sh_link == SHN_UNDEF) {
338 return nullptr; // symbol table has no strings
341 return getString(*getSectionByIndex(symbol.first->sh_link),
342 symbol.second->st_name);
345 } // namespace symbolizer