1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17 #include "elf_reader.h"
18
19 #include <elf.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 #include <unistd.h>
25
26 #include <berberis/base/bit_util.h>
27 #include <berberis/base/checks.h>
28 #include <berberis/base/macros.h>
29 #include <berberis/base/mapped_file_fragment.h>
30 #include <berberis/base/stringprintf.h>
31
32 #include "string_offset_table.h"
33 #include "string_table.h"
34
35 namespace nogrod {
36
37 using berberis::bit_cast;
38 using berberis::StringPrintf;
39
40 namespace {
41
ElfStType(uint32_t info)42 [[nodiscard]] constexpr uint8_t ElfStType(uint32_t info) {
43 return info & 0xf;
44 }
45
46 class Elf32 {
47 public:
48 using Off = Elf32_Off;
49 using Word = Elf32_Word;
50
51 using Ehdr = Elf32_Ehdr;
52 using Shdr = Elf32_Shdr;
53 using Sym = Elf32_Sym;
54
55 Elf32() = delete;
56 Elf32(const Elf32&) = delete;
57 const Elf32& operator=(const Elf32&) = delete;
58 };
59
60 class Elf64 {
61 public:
62 using Off = Elf64_Off;
63 using Word = Elf64_Word;
64
65 using Ehdr = Elf64_Ehdr;
66 using Shdr = Elf64_Shdr;
67 using Sym = Elf64_Sym;
68
69 Elf64() = delete;
70 Elf64(const Elf64&) = delete;
71 const Elf64& operator=(const Elf64&) = delete;
72 };
73
74 template <typename ElfT>
75 class ElfFileImpl : public ElfFile {
76 public:
77 ~ElfFileImpl() override;
78
79 static std::unique_ptr<ElfFileImpl<ElfT>> Create(const char* path,
80 int fd,
81 std::string* error_msg);
82
83 [[nodiscard]] bool ReadExportedSymbols(std::vector<std::string>* symbols,
84 std::string* error_msg) override;
85 [[nodiscard]] std::unique_ptr<DwarfInfo> ReadDwarfInfo(std::string* error_msg) override;
86
87 private:
88 explicit ElfFileImpl(const char* path, int fd);
89 [[nodiscard]] bool Init(std::string* error_msg);
90 [[nodiscard]] bool ValidateShdrTable(std::string* error_msg);
91
92 const typename ElfT::Shdr* FindSectionHeaderByType(typename ElfT::Word sh_type);
93 const typename ElfT::Shdr* FindSectionHeaderByName(const char* name);
94
95 template <typename T>
96 [[nodiscard]] const T* OffsetToAddr(typename ElfT::Off offset) const;
97
98 template <typename T>
99 [[nodiscard]] const T* ShdrOffsetToAddr(const typename ElfT::Shdr* shdr) const;
100
101 std::string path_;
102 int fd_;
103
104 MappedFileFragment mapped_file_;
105
106 const typename ElfT::Ehdr* header_;
107
108 const typename ElfT::Shdr* shdr_table_;
109 size_t shdr_num_;
110
111 StringTable strtab_;
112 };
113
114 template <typename ElfT>
ElfFileImpl(const char * path,int fd)115 ElfFileImpl<ElfT>::ElfFileImpl(const char* path, int fd)
116 : path_(path), fd_(fd), header_(nullptr), shdr_table_(nullptr), shdr_num_(0) {}
117
118 template <typename ElfT>
~ElfFileImpl()119 ElfFileImpl<ElfT>::~ElfFileImpl() {
120 close(fd_);
121 }
122
123 template <typename ElfT>
ValidateShdrTable(std::string * error_msg)124 bool ElfFileImpl<ElfT>::ValidateShdrTable(std::string* error_msg) {
125 size_t file_size = mapped_file_.size();
126 for (size_t i = 0; i < shdr_num_; ++i) {
127 const typename ElfT::Shdr* shdr = shdr_table_ + i;
128
129 if (shdr->sh_link >= shdr_num_) {
130 *error_msg = StringPrintf(
131 "section %zd: sh_link (%d) is out of bounds (shnum=%zd)", i, shdr->sh_link, shdr_num_);
132 return false;
133 }
134
135 // Skip boundary checks for SHT_NOBIT section headers.
136 if (shdr->sh_type == SHT_NOBITS) {
137 continue;
138 }
139
140 if (shdr->sh_offset >= file_size) {
141 *error_msg = StringPrintf("section %zd: offset (%zd) is out of bounds (file_size=%zd)",
142 i,
143 static_cast<size_t>(shdr->sh_offset),
144 file_size);
145 return false;
146 }
147
148 size_t section_end = shdr->sh_offset + shdr->sh_size;
149 if (section_end > file_size) {
150 *error_msg = StringPrintf("section %zd: offset+size (%zd) is out of bounds (file_size=%zd)",
151 i,
152 section_end,
153 file_size);
154 return false;
155 }
156 }
157
158 return true;
159 }
160
161 template <typename ElfT>
162 template <typename T>
OffsetToAddr(typename ElfT::Off offset) const163 const T* ElfFileImpl<ElfT>::OffsetToAddr(typename ElfT::Off offset) const {
164 auto start = bit_cast<uintptr_t>(mapped_file_.data());
165 return bit_cast<const T*>(start + offset);
166 }
167
168 template <typename ElfT>
169 template <typename T>
ShdrOffsetToAddr(const typename ElfT::Shdr * shdr) const170 const T* ElfFileImpl<ElfT>::ShdrOffsetToAddr(const typename ElfT::Shdr* shdr) const {
171 CHECK(shdr->sh_type != SHT_NOBITS);
172 return OffsetToAddr<T>(shdr->sh_offset);
173 }
174
175 template <typename ElfT>
Init(std::string * error_msg)176 bool ElfFileImpl<ElfT>::Init(std::string* error_msg) {
177 struct stat st {};
178 if (fstat(fd_, &st) == -1) {
179 *error_msg = StringPrintf("unable to stat \"%s\": %s", path_.c_str(), strerror(errno));
180 return false;
181 }
182
183 size_t size = st.st_size;
184
185 if (!mapped_file_.Map(fd_, 0, 0, size)) {
186 *error_msg = StringPrintf("unable to map the file \"%s\"", path_.c_str());
187 return false;
188 }
189
190 if (size < sizeof(typename ElfT::Ehdr)) {
191 *error_msg = StringPrintf(
192 "file \"%s\" is too small(%zd), there is not enough space for an ELF header(%zd)",
193 path_.c_str(),
194 size,
195 sizeof(typename ElfT::Ehdr));
196 return false;
197 }
198
199 header_ = OffsetToAddr<const typename ElfT::Ehdr>(0);
200
201 uintptr_t shdr_offset = header_->e_shoff;
202 size_t shdr_num = header_->e_shnum;
203
204 if (header_->e_shentsize != sizeof(typename ElfT::Shdr)) {
205 *error_msg = StringPrintf("invalid e_shentsize: %d, expected: %zd",
206 header_->e_shentsize,
207 sizeof(typename ElfT::Shdr));
208 return false;
209 }
210
211 if (shdr_offset >= size) {
212 *error_msg = StringPrintf("file \"%s\" is too small, e_shoff(%zd) is out of bounds (%zd)",
213 path_.c_str(),
214 shdr_offset,
215 size);
216 return false;
217 }
218
219 if (shdr_offset + (shdr_num * sizeof(typename ElfT::Shdr)) > size) {
220 *error_msg =
221 StringPrintf("file \"%s\" is too small, e_shoff + shdr_size (%zd) is out of bounds (%zd)",
222 path_.c_str(),
223 shdr_offset + (shdr_num * sizeof(typename ElfT::Shdr)),
224 size);
225 return false;
226 }
227
228 shdr_table_ = OffsetToAddr<const typename ElfT::Shdr>(shdr_offset);
229 shdr_num_ = shdr_num;
230
231 if (!ValidateShdrTable(error_msg)) {
232 return false;
233 }
234
235 if (header_->e_shstrndx == SHN_UNDEF) {
236 *error_msg = StringPrintf(
237 "\"%s\": e_shstrndx is not defined, this is not good because "
238 "section names are needed to extract dwarf_info",
239 path_.c_str());
240 return false;
241 }
242
243 if (header_->e_shstrndx >= shdr_num) {
244 *error_msg = StringPrintf("\"%s\" invalid e_shstrndx (%d) - out of bounds (e_shnum=%zd)",
245 path_.c_str(),
246 header_->e_shstrndx,
247 shdr_num);
248 return false;
249 }
250
251 const typename ElfT::Shdr* strtab_shdr = &shdr_table_[header_->e_shstrndx];
252
253 strtab_ = StringTable(ShdrOffsetToAddr<const char>(strtab_shdr), strtab_shdr->sh_size);
254
255 return true;
256 }
257
258 template <typename ElfT>
Create(const char * path,int fd,std::string * error_msg)259 std::unique_ptr<ElfFileImpl<ElfT>> ElfFileImpl<ElfT>::Create(const char* path,
260 int fd,
261 std::string* error_msg) {
262 std::unique_ptr<ElfFileImpl<ElfT>> result(new ElfFileImpl<ElfT>(path, fd));
263 if (!result->Init(error_msg)) {
264 return nullptr;
265 }
266
267 return result;
268 }
269
270 template <typename ElfT>
FindSectionHeaderByType(typename ElfT::Word sh_type)271 const typename ElfT::Shdr* ElfFileImpl<ElfT>::FindSectionHeaderByType(typename ElfT::Word sh_type) {
272 for (size_t i = 0; i < shdr_num_; ++i) {
273 if (shdr_table_[i].sh_type == sh_type) {
274 return shdr_table_ + i;
275 }
276 }
277
278 return nullptr;
279 }
280
281 template <typename ElfT>
FindSectionHeaderByName(const char * name)282 const typename ElfT::Shdr* ElfFileImpl<ElfT>::FindSectionHeaderByName(const char* name) {
283 for (size_t i = 0; i < shdr_num_; ++i) {
284 if (strcmp(name, strtab_.GetString(shdr_table_[i].sh_name)) == 0) {
285 return shdr_table_ + i;
286 }
287 }
288
289 return nullptr;
290 }
291
292 template <typename ElfT>
ReadExportedSymbols(std::vector<std::string> * symbols,std::string * error_msg)293 bool ElfFileImpl<ElfT>::ReadExportedSymbols(std::vector<std::string>* symbols,
294 std::string* error_msg) {
295 const typename ElfT::Shdr* dynsym_shdr = FindSectionHeaderByType(SHT_DYNSYM);
296
297 if (dynsym_shdr == nullptr) {
298 *error_msg = "dynamic symbol section was not found";
299 return false;
300 }
301
302 if (dynsym_shdr->sh_size % sizeof(typename ElfT::Sym) != 0) {
303 *error_msg = StringPrintf("invalid SHT_DYNSYM section size(%zd): should be divisible by %zd",
304 static_cast<size_t>(dynsym_shdr->sh_size),
305 sizeof(typename ElfT::Sym));
306 return false;
307 }
308
309 size_t dynsym_num = dynsym_shdr->sh_size / sizeof(typename ElfT::Sym);
310 const auto* dynsyms = ShdrOffsetToAddr<const typename ElfT::Sym>(dynsym_shdr);
311
312 const typename ElfT::Shdr* strtab_shdr = shdr_table_ + dynsym_shdr->sh_link;
313
314 const StringTable strtab(ShdrOffsetToAddr<const char>(strtab_shdr), strtab_shdr->sh_size);
315
316 for (size_t i = 0; i < dynsym_num; ++i) {
317 const typename ElfT::Sym* sym = dynsyms + i;
318 // skip undefined symbols
319 if (sym->st_shndx == SHN_UNDEF) {
320 continue;
321 }
322
323 // We are interested only in functions and variables.
324 // This is a bit strange but the fact of the matter is that ld.gold generates OBJECT
325 // of size 0 for version labels - we need to skip them as well.
326 uint8_t st_type = ElfStType(sym->st_info);
327 if (st_type == STT_FUNC || (st_type == STT_OBJECT && sym->st_size != 0)) {
328 symbols->push_back(strtab.GetString(sym->st_name));
329 }
330 }
331
332 return true;
333 }
334
335 template <typename ElfT>
ReadDwarfInfo(std::string * error_msg)336 std::unique_ptr<DwarfInfo> ElfFileImpl<ElfT>::ReadDwarfInfo(std::string* error_msg) {
337 const typename ElfT::Shdr* dwarf_abbrev_shdr = FindSectionHeaderByName(".debug_abbrev");
338 if (dwarf_abbrev_shdr == nullptr) {
339 *error_msg = "couldn't find .debug_abbrev section";
340 return nullptr;
341 }
342
343 const typename ElfT::Shdr* dwarf_info_shdr = FindSectionHeaderByName(".debug_info");
344 if (dwarf_info_shdr == nullptr) {
345 *error_msg = "couldn't find .debug_info section";
346 return nullptr;
347 }
348
349 const typename ElfT::Shdr* dwarf_str_shdr = FindSectionHeaderByName(".debug_str");
350 if (dwarf_str_shdr == nullptr) {
351 *error_msg = "couldn't find .debug_str section";
352 return nullptr;
353 }
354
355 StringTable string_table{ShdrOffsetToAddr<const char>(dwarf_str_shdr), dwarf_str_shdr->sh_size};
356
357 // This section is optional (at least as of now)
358 const typename ElfT::Shdr* debug_str_offsets_shdr = FindSectionHeaderByName(".debug_str_offsets");
359 std::optional<StringOffsetTable> string_offsets_table;
360 if (debug_str_offsets_shdr != nullptr) {
361 string_offsets_table.emplace(ShdrOffsetToAddr<const uint8_t>(debug_str_offsets_shdr),
362 debug_str_offsets_shdr->sh_size);
363 }
364
365 std::unique_ptr<DwarfInfo> dwarf_info(
366 new DwarfInfo(ShdrOffsetToAddr<const uint8_t>(dwarf_abbrev_shdr),
367 dwarf_abbrev_shdr->sh_size,
368 ShdrOffsetToAddr<const uint8_t>(dwarf_info_shdr),
369 dwarf_info_shdr->sh_size,
370 string_table,
371 string_offsets_table));
372
373 if (!dwarf_info->Parse(error_msg)) {
374 return nullptr;
375 }
376
377 return dwarf_info;
378 }
379
380 } // namespace
381
Load(const char * path,std::string * error_msg)382 std::unique_ptr<ElfFile> ElfFile::Load(const char* path, std::string* error_msg) {
383 int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC));
384 if (fd == -1) {
385 *error_msg = strerror(errno);
386 return nullptr;
387 }
388
389 // Read header in order verify the file and detect bitness
390
391 uint8_t e_ident[EI_NIDENT];
392 ssize_t res = TEMP_FAILURE_RETRY(pread64(fd, e_ident, sizeof(e_ident), 0));
393 if (res < 0) {
394 *error_msg = strerror(errno);
395 return nullptr;
396 }
397
398 if (res != sizeof(e_ident)) {
399 *error_msg = "file is too small for an ELF file";
400 return nullptr;
401 }
402
403 if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) {
404 *error_msg = "bad ELF magic";
405 return nullptr;
406 }
407
408 std::unique_ptr<ElfFile> result;
409
410 if (e_ident[EI_CLASS] == ELFCLASS32) {
411 result = ElfFileImpl<Elf32>::Create(path, fd, error_msg);
412 } else if (e_ident[EI_CLASS] == ELFCLASS64) {
413 result = ElfFileImpl<Elf64>::Create(path, fd, error_msg);
414 } else {
415 *error_msg = StringPrintf("bad EI_CLASS: %d", e_ident[EI_CLASS]);
416 }
417
418 return result;
419 }
420
421 } // namespace nogrod
422