1// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5static const char kLinuxGateLibraryName[] = "linux-gate.so";
6static const char kMappedFileUnsafePrefix[] = "/dev/";
7static const char kDeletedSuffix[] = " (deleted)";
8
9// Traits classes so consumers can write templatized code to deal
10// with specific ELF bits.
11struct ElfClass32 {
12 typedef Elf32_Addr Addr;
13 typedef Elf32_Ehdr Ehdr;
14 typedef Elf32_Nhdr Nhdr;
15 typedef Elf32_Phdr Phdr;
16 typedef Elf32_Shdr Shdr;
17 typedef Elf32_Half Half;
18 typedef Elf32_Off Off;
19 typedef Elf32_Sym Sym;
20 typedef Elf32_Word Word;
21 typedef struct {
22 int32_t d_tag;
23 uint32_t d_val;
24 } ElfDyn;
25
26 static const int kClass = ELFCLASS32;
27 static const uint16_t kMachine = EM_386;
28 static const size_t kAddrSize = sizeof(Elf32_Addr);
29 static constexpr const char* kMachineName = "x86";
30};
31
32struct ElfClass64 {
33 typedef Elf64_Addr Addr;
34 typedef Elf64_Ehdr Ehdr;
35 typedef Elf64_Nhdr Nhdr;
36 typedef Elf64_Phdr Phdr;
37 typedef Elf64_Shdr Shdr;
38 typedef Elf64_Half Half;
39 typedef Elf64_Off Off;
40 typedef Elf64_Sym Sym;
41 typedef Elf64_Word Word;
42 typedef struct {
43 uint64_t d_tag;
44 uint64_t d_val;
45 } ElfDyn;
46
47 static const int kClass = ELFCLASS64;
48 static const uint16_t kMachine = EM_X86_64;
49 static const size_t kAddrSize = sizeof(Elf64_Addr);
50 static constexpr const char* kMachineName = "x86_64";
51};
52
53struct ElfSegment {
54 const void* start;
55 size_t size;
56};
57
58#define ELFMAG "\177ELF"
59#define SELFMAG 4
60bool IsValidElf(const void* elf_base)
61{
62 return strncmp(reinterpret_cast<const char*>(elf_base),
63 ELFMAG, SELFMAG) == 0;
64}
65
66int ElfClass(const void* elf_base)
67{
68 const ElfW(Ehdr)* elf_header =
69 reinterpret_cast<const ElfW(Ehdr)*>(elf_base);
70
71 return elf_header->e_ident[EI_CLASS];
72}
73
74template<typename ElfClass, typename T>
75const T* GetOffset(const typename ElfClass::Ehdr* elf_header,
76 typename ElfClass::Off offset) {
77 return reinterpret_cast<const T*>(reinterpret_cast<uintptr_t>(elf_header) +
78 offset);
79}
80
81template<typename ElfClass>
82void FindElfClassSegment(const char *elf_base,
83 typename ElfClass::Word segment_type,
84 vector<ElfSegment> *segments)
85{
86 typedef typename ElfClass::Ehdr Ehdr;
87 typedef typename ElfClass::Phdr Phdr;
88
89 assert(elf_base);
90 assert(segments);
91
92 assert(strncmp(elf_base, ELFMAG, SELFMAG) == 0);
93
94 const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base);
95 assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass);
96
97 const Phdr* phdrs =
98 GetOffset<ElfClass, Phdr>(elf_header, elf_header->e_phoff);
99
100 for (int i = 0; i < elf_header->e_phnum; ++i) {
101 if (phdrs[i].p_type == segment_type) {
102 ElfSegment seg = {0,0};
103 seg.start = elf_base + phdrs[i].p_offset;
104 seg.size = phdrs[i].p_filesz;
105 segments->push_back(seg);
106 }
107 }
108}
109
110template<typename ElfClass>
111const typename ElfClass::Shdr* FindElfSectionByName(
112 const char* name,
113 typename ElfClass::Word section_type,
114 const typename ElfClass::Shdr* sections,
115 const char* section_names,
116 const char* names_end,
117 int nsection)
118{
119 assert(name != NULL);
120 assert(sections != NULL);
121 assert(nsection > 0);
122
123 int name_len = strlen(name);
124 if (name_len == 0)
125 return NULL;
126
127 for (int i = 0; i < nsection; ++i) {
128 const char* section_name = section_names + sections[i].sh_name;
129 if (sections[i].sh_type == section_type &&
130 names_end - section_name >= name_len + 1 &&
131 strcmp(name, section_name) == 0) {
132 return sections + i;
133 }
134 }
135 return NULL;
136}
137
138template<typename ElfClass>
139void FindElfClassSection(const char *elf_base,
140 const char *section_name,
141 typename ElfClass::Word section_type,
142 const void **section_start,
143 size_t *section_size) {
144 typedef typename ElfClass::Ehdr Ehdr;
145 typedef typename ElfClass::Shdr Shdr;
146
147 assert(elf_base);
148 assert(section_start);
149 assert(section_size);
150
151 assert(strncmp(elf_base, ELFMAG, SELFMAG) == 0);
152
153 const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base);
154 assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass);
155
156 const Shdr* sections =
157 GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff);
158 const Shdr* section_names = sections + elf_header->e_shstrndx;
159 const char* names =
160 GetOffset<ElfClass, char>(elf_header, section_names->sh_offset);
161 const char *names_end = names + section_names->sh_size;
162
163 const Shdr* section =
164 FindElfSectionByName<ElfClass>(section_name, section_type,
165 sections, names, names_end,
166 elf_header->e_shnum);
167
168 if (section != NULL && section->sh_size > 0) {
169 *section_start = elf_base + section->sh_offset;
170 *section_size = section->sh_size;
171 }
172}
173
174template<typename ElfClass>
175const char* FindLibClassStartWith(const void *elf_base,
176 size_t size, const char* prefix)
177{
178 typedef typename ElfClass::Shdr Shdr;
179 typedef typename ElfClass::ElfDyn ElfDyn;
180
181 assert(elf_base);
182
183 Shdr* dynamic = nullptr;
184 size_t dynamic_size = 0;
185 FindElfClassSection<ElfClass>((const char*)elf_base, ".dynamic",
186 SHT_PROGBITS, (const void**)&dynamic, &dynamic_size);
187 if (nullptr == dynamic) {
188 return nullptr;
189 }
190
191 Shdr* dynstr = nullptr;
192 size_t dynstr_size = 0;
193 FindElfClassSection<ElfClass>((const char*)elf_base, ".dynstr",
194 SHT_PROGBITS, (const void**)&dynstr, &dynstr_size);
195 if (nullptr == dynstr) {
196 return nullptr;
197 }
198
199 if (dynamic->sh_entsize != sizeof(ElfDyn)) {
200 LOG(DEBUG) << "Invalid ELF file: incorrect .dynamic size "
201 << dynamic->sh_entsize;
202 return nullptr;
203 }
204 if (!dynamic->sh_size) {
205 return nullptr;
206 }
207 if (dynamic->sh_size % dynamic->sh_entsize) {
208 LOG(DEBUG) << "Invalid ELF file: incorrect .dynamic section size "
209 << dynamic->sh_size;
210 return nullptr;
211 }
212 if (dynstr->sh_size == 0) {
213 LOG(DEBUG) << "Invalid ELF file: empty string table";
214 return nullptr;
215 }
216
217 if (dynamic->sh_offset + dynamic->sh_size >= size) {
218 LOG(DEBUG) << "Invalid ELF file: can't read .dynamic";
219 return nullptr;
220 }
221
222 ElfDyn* dyn_list = (ElfDyn*)((char *)elf_base + dynamic->sh_offset);
223 const char* strtable = (const char*)((char *)elf_base + dynstr->sh_offset);
224
225 for (size_t i = 0; i < dynamic->sh_size / dynamic->sh_entsize; ++i) {
226 if (dyn_list[i].d_tag == DT_NEEDED &&
227 dyn_list[i].d_val < dynstr->sh_size) {
228 const char* name = strtable + dyn_list[i].d_val;
229 if (!strcmp(name, prefix)) {
230 return name;
231 }
232 }
233 }
234
235 return nullptr;
236}
237
238bool FindElfSegments(const void* elf_mapped_base,
239 uint32_t segment_type,
240 vector<ElfSegment>* segments)
241{
242 assert(elf_mapped_base);
243 assert(segments);
244
245 if (!IsValidElf(elf_mapped_base))
246 return false;
247
248 int cls = ElfClass(elf_mapped_base);
249 const char* elf_base =
250 static_cast<const char*>(elf_mapped_base);
251
252 if (cls == ELFCLASS32) {
253 FindElfClassSegment<ElfClass32>(elf_base, segment_type, segments);
254 return true;
255 } else if (cls == ELFCLASS64) {
256 FindElfClassSegment<ElfClass64>(elf_base, segment_type, segments);
257 return true;
258 }
259
260 return false;
261}
262
263// ELF note name and desc are 32-bits word padded.
264#define NOTE_PADDING(a) ((a + 3) & ~3)
265
266// These functions are also used inside the crashed process, so be safe
267// and use the syscall/libc wrappers instead of direct syscalls or libc.
268
269static bool ElfClassBuildIDNoteIdentifier(const void *section, size_t length,
270 uint8_t* identifier) {
271 static_assert(sizeof(ElfClass32::Nhdr) == sizeof(ElfClass64::Nhdr),
272 "Elf32_Nhdr and Elf64_Nhdr should be the same");
273 typedef typename ElfClass32::Nhdr Nhdr;
274
275 const void* section_end = reinterpret_cast<const char*>(section) + length;
276 const Nhdr* note_header = reinterpret_cast<const Nhdr*>(section);
277 while (reinterpret_cast<const void *>(note_header) < section_end) {
278 if (note_header->n_type == NT_GNU_BUILD_ID)
279 break;
280 note_header = reinterpret_cast<const Nhdr*>(
281 reinterpret_cast<const char*>(note_header) + sizeof(Nhdr) +
282 NOTE_PADDING(note_header->n_namesz) +
283 NOTE_PADDING(note_header->n_descsz));
284 }
285 if (reinterpret_cast<const void *>(note_header) >= section_end ||
286 note_header->n_descsz == 0) {
287 return false;
288 }
289
290 const uint8_t* build_id = reinterpret_cast<const uint8_t*>(note_header) +
291 sizeof(Nhdr) + NOTE_PADDING(note_header->n_namesz);
292 memcpy(identifier, build_id, note_header->n_descsz);
293
294 return true;
295}
296
297bool FindElfSection(const void *elf_mapped_base,
298 const char *section_name,
299 uint32_t section_type,
300 const void **section_start,
301 size_t *section_size)
302{
303 assert(elf_mapped_base);
304 assert(section_start);
305 assert(section_size);
306
307 *section_start = NULL;
308 *section_size = 0;
309
310 if (!IsValidElf(elf_mapped_base))
311 return false;
312
313 int cls = ElfClass(elf_mapped_base);
314 const char* elf_base =
315 static_cast<const char*>(elf_mapped_base);
316
317 if (cls == ELFCLASS32) {
318 FindElfClassSection<ElfClass32>(elf_base, section_name, section_type,
319 section_start, section_size);
320 return *section_start != NULL;
321 } else if (cls == ELFCLASS64) {
322 FindElfClassSection<ElfClass64>(elf_base, section_name, section_type,
323 section_start, section_size);
324 return *section_start != NULL;
325 }
326
327 return false;
328}
329
330///////////////////////////////////////////////////////////////////////////////
331//
332//
333// A utility class for mapping a file into memory for read-only access of
334// the file content. Its implementation avoids calling into libc functions
335// by directly making system calls for open, close, mmap, and munmap.
336class MemoryMappedFile {
337 public:
338 MemoryMappedFile();
339
340 // Constructor that calls Map() to map a file at |path| into memory.
341 // If Map() fails, the object behaves as if it is default constructed.
342 MemoryMappedFile(const char* path, size_t offset);
343
344 ~MemoryMappedFile();
345
346 // Maps a file at |path| into memory, which can then be accessed via
347 // content() as a MemoryRange object or via data(), and returns true on
348 // success. Mapping an empty file will succeed but with data() and size()
349 // returning nullptr and 0, respectively. An existing mapping is unmapped
350 // before a new mapping is created.
351 bool Map(const char* path, size_t offset);
352
353 // Unmaps the memory for the mapped file. It's a no-op if no file is
354 // mapped.
355 void Unmap();
356
357 // Returns a MemoryRange object that covers the memory for the mapped
358 // file. The MemoryRange object is empty if no file is mapped.
359 const MemoryRange& content() const { return content_; }
360
361 // Returns a pointer to the beginning of the memory for the mapped file.
362 // or nullptr if no file is mapped or the mapped file is empty.
363 const void* data() const { return content_.data(); }
364
365 // Returns the size in bytes of the mapped file, or zero if no file
366 // is mapped.
367 size_t size() const { return content_.length(); }
368
369 private:
370 // Mapped file content as a MemoryRange object.
371 MemoryRange content_;
372};
373
374MemoryMappedFile::MemoryMappedFile() {}
375
376MemoryMappedFile::MemoryMappedFile(const char* path, size_t offset) {
377 Map(path, offset);
378}
379
380MemoryMappedFile::~MemoryMappedFile() {
381 Unmap();
382}
383
384bool MemoryMappedFile::Map(const char* path, size_t offset) {
385 Unmap();
386
387 int fd = open(path, O_RDONLY, 0);
388 if (fd == -1) {
389 return false;
390 }
391
392#if defined(__x86_64__) || defined(__aarch64__) || defined(__sw_64) || \
393 (defined(__mips__) && _MIPS_SIM == _ABI64)
394
395 struct stat st;
396 if (fstat(fd, &st) == -1 || st.st_size < 0) {
397#else
398 struct kernel_stat64 st;
399 if (fstat64(fd, &st) == -1 || st.st_size < 0) {
400#endif
401 close(fd);
402 return false;
403 }
404
405 // Strangely file size can be negative, but we check above that it is not.
406 size_t file_len = static_cast<size_t>(st.st_size);
407 if (0 == file_len) {
408 void* data = mmap(nullptr, 16*1024,
409 PROT_READ|PROT_WRITE,
410 MAP_ANONYMOUS|MAP_PRIVATE, 0, 0);
411 if (data == MAP_FAILED) {
412 close(fd);
413 return false;
414 }
415
416 file_len = read(fd, data, 16*1024);
417 close(fd);
418 content_.Set(data, file_len);
419 return true;
420 }
421
422 // If the file does not extend beyond the offset, simply use an empty
423 // MemoryRange and return true. Don't bother to call mmap()
424 // even though mmap() can handle an empty file on some platforms.
425 if (offset >= file_len) {
426 close(fd);
427 return true;
428 }
429
430 void* data = mmap(nullptr, file_len, PROT_READ, MAP_PRIVATE, fd, offset);
431 close(fd);
432 if (data == MAP_FAILED) {
433 return false;
434 }
435
436 content_.Set(data, file_len - offset);
437 return true;
438}
439
440void MemoryMappedFile::Unmap() {
441 if (content_.data()) {
442 munmap(const_cast<uint8_t*>(content_.data()), content_.length());
443 content_.Set(nullptr, 0);
444 }
445}
446
447// A class for reading a file, line by line, without using fopen/fgets or other
448// functions which may allocate memory.
449class LineReader {
450 public:
451 LineReader(int fd)
452 : fd_(fd),
453 hit_eof_(false),
454 buf_used_(0) {
455 }
456
457 // The maximum length of a line.
458 static const size_t kMaxLineLen = 1024*4;
459
460 // Return the next line from the file.
461 // line: (output) a pointer to the start of the line. The line is NUL
462 // terminated.
463 // len: (output) the length of the line (not inc the NUL byte)
464 //
465 // Returns true iff successful (false on EOF).
466 //
467 // One must call |PopLine| after this function, otherwise you'll continue to
468 // get the same line over and over.
469 bool GetNextLine(const char **line, unsigned int *len) {
470 for (;;) {
471 if (buf_used_ == 0 && hit_eof_)
472 return false;
473
474 for (unsigned int i = 0; i < buf_used_; ++i) {
475 if (buf_[i] == '\n') {
476 buf_[i] = 0;
477 *len = i;
478 *line = buf_;
479 return true;
480 }
481 }
482
483 if (buf_used_ == sizeof(buf_)) {
484 // we scanned the whole buffer and didn't find an end-of-line marker.
485 // This line is too long to process.
486 return false;
487 }
488
489 // We didn't find any end-of-line terminators in the buffer. However, if
490 // this is the last line in the file it might not have one:
491 if (hit_eof_) {
492 assert(buf_used_);
493 // There's room for the NUL because of the buf_used_ == sizeof(buf_)
494 // check above.
495 buf_[buf_used_] = 0;
496 *len = buf_used_;
497 buf_used_ += 1; // since we appended the NUL.
498 *line = buf_;
499 return true;
500 }
501
502 // Otherwise, we should pull in more data from the file
503 const ssize_t n = read(fd_, buf_ + buf_used_,
504 sizeof(buf_) - buf_used_);
505 if (n < 0) {
506 return false;
507 } else if (n == 0) {
508 hit_eof_ = true;
509 } else {
510 buf_used_ += n;
511 }
512
513 // At this point, we have either set the hit_eof_ flag, or we have more
514 // data to process...
515 }
516 }
517
518 void PopLine(unsigned int len) {
519 // len doesn't include the NUL byte at the end.
520
521 assert(buf_used_ >= len + 1);
522 buf_used_ -= len + 1;
523 memmove(buf_, buf_ + len + 1, buf_used_);
524 }
525
526 private:
527 const int fd_;
528
529 bool hit_eof_;
530 unsigned int buf_used_;
531 char buf_[kMaxLineLen];
532};
533