• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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