shared-libraries-linux.cc (31640B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "mozilla/SharedLibraries.h" 8 9 #define PATH_MAX_TOSTRING(x) #x 10 #define PATH_MAX_STRING(x) PATH_MAX_TOSTRING(x) 11 #include <stdio.h> 12 #include <string.h> 13 #include <limits.h> 14 #include <unistd.h> 15 #include <fstream> 16 #include "platform.h" 17 #include "mozilla/Sprintf.h" 18 19 #include <algorithm> 20 #include <arpa/inet.h> 21 #include <elf.h> 22 #include <fcntl.h> 23 #if defined(GP_OS_linux) || defined(GP_OS_android) 24 # include <features.h> 25 #endif 26 #include <sys/mman.h> 27 #include <sys/stat.h> 28 #include <sys/types.h> 29 #include <vector> 30 #include <optional> 31 32 #if defined(GP_OS_linux) || defined(GP_OS_android) || defined(GP_OS_freebsd) 33 # include <link.h> // dl_phdr_info, ElfW() 34 #else 35 # error "Unexpected configuration" 36 #endif 37 38 #if defined(GP_OS_android) 39 extern "C" MOZ_EXPORT __attribute__((weak)) int dl_iterate_phdr( 40 int (*callback)(struct dl_phdr_info* info, size_t size, void* data), 41 void* data); 42 #endif 43 44 #if defined(GP_OS_freebsd) && !defined(ElfW) 45 # define ElfW(type) Elf_##type 46 #endif 47 48 // ---------------------------------------------------------------------------- 49 // Starting imports from toolkit/crashreporter/google-breakpad/, as needed by 50 // this file when moved to mozglue. 51 52 // Imported from 53 // toolkit/crashreporter/google-breakpad/src/common/memory_range.h. 54 // A lightweight wrapper with a pointer and a length to encapsulate a contiguous 55 // range of memory. It provides helper methods for checked access of a subrange 56 // of the memory. Its implemementation does not allocate memory or call into 57 // libc functions, and is thus safer to use in a crashed environment. 58 class MemoryRange { 59 public: 60 MemoryRange() : data_(NULL), length_(0) {} 61 62 MemoryRange(const void* data, size_t length) { Set(data, length); } 63 64 // Returns true if this memory range contains no data. 65 bool IsEmpty() const { 66 // Set() guarantees that |length_| is zero if |data_| is NULL. 67 return length_ == 0; 68 } 69 70 // Resets to an empty range. 71 void Reset() { 72 data_ = NULL; 73 length_ = 0; 74 } 75 76 // Sets this memory range to point to |data| and its length to |length|. 77 void Set(const void* data, size_t length) { 78 data_ = reinterpret_cast<const uint8_t*>(data); 79 // Always set |length_| to zero if |data_| is NULL. 80 length_ = data ? length : 0; 81 } 82 83 // Returns true if this range covers a subrange of |sub_length| bytes 84 // at |sub_offset| bytes of this memory range, or false otherwise. 85 bool Covers(size_t sub_offset, size_t sub_length) const { 86 // The following checks verify that: 87 // 1. sub_offset is within [ 0 .. length_ - 1 ] 88 // 2. sub_offset + sub_length is within 89 // [ sub_offset .. length_ ] 90 return sub_offset < length_ && sub_offset + sub_length >= sub_offset && 91 sub_offset + sub_length <= length_; 92 } 93 94 // Returns a raw data pointer to a subrange of |sub_length| bytes at 95 // |sub_offset| bytes of this memory range, or NULL if the subrange 96 // is out of bounds. 97 const void* GetData(size_t sub_offset, size_t sub_length) const { 98 return Covers(sub_offset, sub_length) ? (data_ + sub_offset) : NULL; 99 } 100 101 // Same as the two-argument version of GetData() but uses sizeof(DataType) 102 // as the subrange length and returns an |DataType| pointer for convenience. 103 template <typename DataType> 104 const DataType* GetData(size_t sub_offset) const { 105 return reinterpret_cast<const DataType*>( 106 GetData(sub_offset, sizeof(DataType))); 107 } 108 109 // Returns a raw pointer to the |element_index|-th element of an array 110 // of elements of length |element_size| starting at |sub_offset| bytes 111 // of this memory range, or NULL if the element is out of bounds. 112 const void* GetArrayElement(size_t element_offset, size_t element_size, 113 unsigned element_index) const { 114 size_t sub_offset = element_offset + element_index * element_size; 115 return GetData(sub_offset, element_size); 116 } 117 118 // Same as the three-argument version of GetArrayElement() but deduces 119 // the element size using sizeof(ElementType) and returns an |ElementType| 120 // pointer for convenience. 121 template <typename ElementType> 122 const ElementType* GetArrayElement(size_t element_offset, 123 unsigned element_index) const { 124 return reinterpret_cast<const ElementType*>( 125 GetArrayElement(element_offset, sizeof(ElementType), element_index)); 126 } 127 128 // Returns a subrange of |sub_length| bytes at |sub_offset| bytes of 129 // this memory range, or an empty range if the subrange is out of bounds. 130 MemoryRange Subrange(size_t sub_offset, size_t sub_length) const { 131 return Covers(sub_offset, sub_length) 132 ? MemoryRange(data_ + sub_offset, sub_length) 133 : MemoryRange(); 134 } 135 136 // Returns a pointer to the beginning of this memory range. 137 const uint8_t* data() const { return data_; } 138 139 // Returns the length, in bytes, of this memory range. 140 size_t length() const { return length_; } 141 142 private: 143 // Pointer to the beginning of this memory range. 144 const uint8_t* data_; 145 146 // Length, in bytes, of this memory range. 147 size_t length_; 148 }; 149 150 // Imported from 151 // toolkit/crashreporter/google-breakpad/src/common/linux/memory_mapped_file.h 152 // and inlined .cc. 153 // A utility class for mapping a file into memory for read-only access of the 154 // file content. Its implementation avoids calling into libc functions by 155 // directly making system calls for open, close, mmap, and munmap. 156 class MemoryMappedFile { 157 public: 158 MemoryMappedFile() {} 159 160 // Constructor that calls Map() to map a file at |path| into memory. 161 // If Map() fails, the object behaves as if it is default constructed. 162 MemoryMappedFile(const char* path, size_t offset) { Map(path, offset); } 163 164 MemoryMappedFile(const MemoryMappedFile&) = delete; 165 MemoryMappedFile& operator=(const MemoryMappedFile&) = delete; 166 167 ~MemoryMappedFile() { Unmap(); } 168 169 // Maps a file at |path| into memory, which can then be accessed via 170 // content() as a MemoryRange object or via data(), and returns true on 171 // success. Mapping an empty file will succeed but with data() and size() 172 // returning NULL and 0, respectively. An existing mapping is unmapped 173 // before a new mapping is created. 174 bool Map(const char* path, size_t offset) { 175 Unmap(); 176 177 int fd = open(path, O_RDONLY, 0); 178 if (fd == -1) { 179 return false; 180 } 181 182 #if defined(__x86_64__) || defined(__aarch64__) || \ 183 (defined(__mips__) && _MIPS_SIM == _ABI64) || \ 184 !(defined(GP_OS_linux) || defined(GP_OS_android)) 185 186 struct stat st; 187 if (fstat(fd, &st) == -1 || st.st_size < 0) { 188 #else 189 struct stat64 st; 190 if (fstat64(fd, &st) == -1 || st.st_size < 0) { 191 #endif 192 close(fd); 193 return false; 194 } 195 196 // Strangely file size can be negative, but we check above that it is not. 197 size_t file_len = static_cast<size_t>(st.st_size); 198 // If the file does not extend beyond the offset, simply use an empty 199 // MemoryRange and return true. Don't bother to call mmap() 200 // even though mmap() can handle an empty file on some platforms. 201 if (offset >= file_len) { 202 close(fd); 203 return true; 204 } 205 206 void* data = mmap(NULL, file_len, PROT_READ, MAP_PRIVATE, fd, offset); 207 close(fd); 208 if (data == MAP_FAILED) { 209 return false; 210 } 211 212 content_.Set(data, file_len - offset); 213 return true; 214 } 215 216 // Unmaps the memory for the mapped file. It's a no-op if no file is 217 // mapped. 218 void Unmap() { 219 if (content_.data()) { 220 munmap(const_cast<uint8_t*>(content_.data()), content_.length()); 221 content_.Set(NULL, 0); 222 } 223 } 224 225 // Returns a MemoryRange object that covers the memory for the mapped 226 // file. The MemoryRange object is empty if no file is mapped. 227 const MemoryRange& content() const { return content_; } 228 229 // Returns a pointer to the beginning of the memory for the mapped file. 230 // or NULL if no file is mapped or the mapped file is empty. 231 const void* data() const { return content_.data(); } 232 233 // Returns the size in bytes of the mapped file, or zero if no file 234 // is mapped. 235 size_t size() const { return content_.length(); } 236 237 private: 238 // Mapped file content as a MemoryRange object. 239 MemoryRange content_; 240 }; 241 242 // Imported from 243 // toolkit/crashreporter/google-breakpad/src/common/linux/file_id.h and inlined 244 // .cc. 245 // GNU binutils' ld defaults to 'sha1', which is 160 bits == 20 bytes, 246 // so this is enough to fit that, which most binaries will use. 247 // This is just a sensible default for vectors so most callers can get away with 248 // stack allocation. 249 static const size_t kDefaultBuildIdSize = 20; 250 251 // Used in a few places for backwards-compatibility. 252 typedef struct { 253 uint32_t data1; 254 uint16_t data2; 255 uint16_t data3; 256 uint8_t data4[8]; 257 } MDGUID; /* GUID */ 258 259 const size_t kMDGUIDSize = sizeof(MDGUID); 260 261 class FileID { 262 public: 263 explicit FileID(const char* path) : path_(path) {} 264 ~FileID() {} 265 266 // Load the identifier for the elf file path specified in the constructor into 267 // |identifier|. 268 // 269 // The current implementation will look for a .note.gnu.build-id 270 // section and use that as the file id, otherwise it falls back to 271 // XORing the first 4096 bytes of the .text section to generate an identifier. 272 bool ElfFileIdentifier(std::vector<uint8_t>& identifier) { 273 MemoryMappedFile mapped_file(path_.c_str(), 0); 274 if (!mapped_file.data()) // Should probably check if size >= ElfW(Ehdr)? 275 return false; 276 277 return ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier); 278 } 279 280 // Traits classes so consumers can write templatized code to deal 281 // with specific ELF bits. 282 struct ElfClass32 { 283 typedef Elf32_Addr Addr; 284 typedef Elf32_Ehdr Ehdr; 285 typedef Elf32_Nhdr Nhdr; 286 typedef Elf32_Phdr Phdr; 287 typedef Elf32_Shdr Shdr; 288 typedef Elf32_Half Half; 289 typedef Elf32_Off Off; 290 typedef Elf32_Sym Sym; 291 typedef Elf32_Word Word; 292 293 static const int kClass = ELFCLASS32; 294 static const uint16_t kMachine = EM_386; 295 static const size_t kAddrSize = sizeof(Elf32_Addr); 296 static constexpr const char* kMachineName = "x86"; 297 }; 298 299 struct ElfClass64 { 300 typedef Elf64_Addr Addr; 301 typedef Elf64_Ehdr Ehdr; 302 typedef Elf64_Nhdr Nhdr; 303 typedef Elf64_Phdr Phdr; 304 typedef Elf64_Shdr Shdr; 305 typedef Elf64_Half Half; 306 typedef Elf64_Off Off; 307 typedef Elf64_Sym Sym; 308 typedef Elf64_Word Word; 309 310 static const int kClass = ELFCLASS64; 311 static const uint16_t kMachine = EM_X86_64; 312 static const size_t kAddrSize = sizeof(Elf64_Addr); 313 static constexpr const char* kMachineName = "x86_64"; 314 }; 315 316 // Internal helper method, exposed for convenience for callers 317 // that already have more info. 318 template <typename ElfClass> 319 static const typename ElfClass::Shdr* FindElfSectionByName( 320 const char* name, typename ElfClass::Word section_type, 321 const typename ElfClass::Shdr* sections, const char* section_names, 322 const char* names_end, int nsection) { 323 if (!name || !sections || nsection == 0) { 324 return NULL; 325 } 326 327 int name_len = strlen(name); 328 if (name_len == 0) return NULL; 329 330 for (int i = 0; i < nsection; ++i) { 331 const char* section_name = section_names + sections[i].sh_name; 332 if (sections[i].sh_type == section_type && 333 names_end - section_name >= name_len + 1 && 334 strcmp(name, section_name) == 0) { 335 return sections + i; 336 } 337 } 338 return NULL; 339 } 340 341 struct ElfSegment { 342 const void* start; 343 size_t size; 344 }; 345 346 // Convert an offset from an Elf header into a pointer to the mapped 347 // address in the current process. Takes an extra template parameter 348 // to specify the return type to avoid having to dynamic_cast the 349 // result. 350 template <typename ElfClass, typename T> 351 static const T* GetOffset(const typename ElfClass::Ehdr* elf_header, 352 typename ElfClass::Off offset) { 353 return reinterpret_cast<const T*>(reinterpret_cast<uintptr_t>(elf_header) + 354 offset); 355 } 356 357 // ELF note name and desc are 32-bits word padded. 358 #define NOTE_PADDING(a) ((a + 3) & ~3) 359 360 static bool ElfClassBuildIDNoteIdentifier(const void* section, size_t length, 361 std::vector<uint8_t>& identifier) { 362 static_assert(sizeof(ElfClass32::Nhdr) == sizeof(ElfClass64::Nhdr), 363 "Elf32_Nhdr and Elf64_Nhdr should be the same"); 364 typedef typename ElfClass32::Nhdr Nhdr; 365 366 const void* section_end = reinterpret_cast<const char*>(section) + length; 367 const Nhdr* note_header = reinterpret_cast<const Nhdr*>(section); 368 while (reinterpret_cast<const void*>(note_header) < section_end) { 369 if (note_header->n_type == NT_GNU_BUILD_ID) break; 370 note_header = reinterpret_cast<const Nhdr*>( 371 reinterpret_cast<const char*>(note_header) + sizeof(Nhdr) + 372 NOTE_PADDING(note_header->n_namesz) + 373 NOTE_PADDING(note_header->n_descsz)); 374 } 375 if (reinterpret_cast<const void*>(note_header) >= section_end || 376 note_header->n_descsz == 0) { 377 return false; 378 } 379 380 const uint8_t* build_id = reinterpret_cast<const uint8_t*>(note_header) + 381 sizeof(Nhdr) + 382 NOTE_PADDING(note_header->n_namesz); 383 identifier.insert(identifier.end(), build_id, 384 build_id + note_header->n_descsz); 385 386 return true; 387 } 388 389 template <typename ElfClass> 390 static bool FindElfClassSection(const char* elf_base, 391 const char* section_name, 392 typename ElfClass::Word section_type, 393 const void** section_start, 394 size_t* section_size) { 395 typedef typename ElfClass::Ehdr Ehdr; 396 typedef typename ElfClass::Shdr Shdr; 397 398 if (!elf_base || !section_start || !section_size) { 399 return false; 400 } 401 402 if (strncmp(elf_base, ELFMAG, SELFMAG) != 0) { 403 return false; 404 } 405 406 const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base); 407 if (elf_header->e_ident[EI_CLASS] != ElfClass::kClass) { 408 return false; 409 } 410 411 if (elf_header->e_shoff == 0) { 412 *section_start = nullptr; 413 *section_size = 0; 414 return false; 415 } 416 417 const Shdr* sections = 418 GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff); 419 const Shdr* section_names = sections + elf_header->e_shstrndx; 420 const char* names = 421 GetOffset<ElfClass, char>(elf_header, section_names->sh_offset); 422 const char* names_end = names + section_names->sh_size; 423 424 const Shdr* section = 425 FindElfSectionByName<ElfClass>(section_name, section_type, sections, 426 names, names_end, elf_header->e_shnum); 427 428 if (section != NULL && section->sh_size > 0) { 429 *section_start = elf_base + section->sh_offset; 430 *section_size = section->sh_size; 431 } 432 433 return true; 434 } 435 436 template <typename ElfClass> 437 static bool FindElfClassSegment(const char* elf_base, 438 typename ElfClass::Word segment_type, 439 std::vector<ElfSegment>* segments) { 440 typedef typename ElfClass::Ehdr Ehdr; 441 typedef typename ElfClass::Phdr Phdr; 442 443 if (!elf_base || !segments) { 444 return false; 445 } 446 447 if (strncmp(elf_base, ELFMAG, SELFMAG) != 0) { 448 return false; 449 } 450 451 const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base); 452 if (elf_header->e_ident[EI_CLASS] != ElfClass::kClass) { 453 return false; 454 } 455 456 const Phdr* phdrs = 457 GetOffset<ElfClass, Phdr>(elf_header, elf_header->e_phoff); 458 459 for (int i = 0; i < elf_header->e_phnum; ++i) { 460 if (phdrs[i].p_type == segment_type) { 461 ElfSegment seg = {}; 462 seg.start = elf_base + phdrs[i].p_offset; 463 seg.size = phdrs[i].p_filesz; 464 segments->push_back(seg); 465 } 466 } 467 468 return true; 469 } 470 471 static bool IsValidElf(const void* elf_base) { 472 return strncmp(reinterpret_cast<const char*>(elf_base), ELFMAG, SELFMAG) == 473 0; 474 } 475 476 static int ElfClass(const void* elf_base) { 477 const ElfW(Ehdr)* elf_header = 478 reinterpret_cast<const ElfW(Ehdr)*>(elf_base); 479 480 return elf_header->e_ident[EI_CLASS]; 481 } 482 483 static bool FindElfSection(const void* elf_mapped_base, 484 const char* section_name, uint32_t section_type, 485 const void** section_start, size_t* section_size) { 486 if (!elf_mapped_base || !section_start || !section_size) { 487 return false; 488 } 489 490 *section_start = NULL; 491 *section_size = 0; 492 493 if (!IsValidElf(elf_mapped_base)) return false; 494 495 int cls = ElfClass(elf_mapped_base); 496 const char* elf_base = static_cast<const char*>(elf_mapped_base); 497 498 if (cls == ELFCLASS32) { 499 return FindElfClassSection<ElfClass32>(elf_base, section_name, 500 section_type, section_start, 501 section_size) && 502 *section_start != NULL; 503 } else if (cls == ELFCLASS64) { 504 return FindElfClassSection<ElfClass64>(elf_base, section_name, 505 section_type, section_start, 506 section_size) && 507 *section_start != NULL; 508 } 509 510 return false; 511 } 512 513 static bool FindElfSegments(const void* elf_mapped_base, 514 uint32_t segment_type, 515 std::vector<ElfSegment>* segments) { 516 if (!elf_mapped_base || !segments) { 517 return false; 518 } 519 520 if (!IsValidElf(elf_mapped_base)) return false; 521 522 int cls = ElfClass(elf_mapped_base); 523 const char* elf_base = static_cast<const char*>(elf_mapped_base); 524 525 if (cls == ELFCLASS32) { 526 return FindElfClassSegment<ElfClass32>(elf_base, segment_type, segments); 527 } 528 if (cls == ELFCLASS64) { 529 return FindElfClassSegment<ElfClass64>(elf_base, segment_type, segments); 530 } 531 532 return false; 533 } 534 535 // Attempt to locate a .note.gnu.build-id section in an ELF binary 536 // and copy it into |identifier|. 537 static bool FindElfBuildIDNote(const void* elf_mapped_base, 538 std::vector<uint8_t>& identifier) { 539 // lld normally creates 2 PT_NOTEs, gold normally creates 1. 540 std::vector<ElfSegment> segs; 541 if (FindElfSegments(elf_mapped_base, PT_NOTE, &segs)) { 542 for (ElfSegment& seg : segs) { 543 if (ElfClassBuildIDNoteIdentifier(seg.start, seg.size, identifier)) { 544 return true; 545 } 546 } 547 } 548 549 void* note_section; 550 size_t note_size; 551 if (FindElfSection(elf_mapped_base, ".note.gnu.build-id", SHT_NOTE, 552 (const void**)¬e_section, ¬e_size)) { 553 return ElfClassBuildIDNoteIdentifier(note_section, note_size, identifier); 554 } 555 556 return false; 557 } 558 559 // Attempt to locate the .text section of an ELF binary and generate 560 // a simple hash by XORing the first page worth of bytes into |identifier|. 561 static bool HashElfTextSection(const void* elf_mapped_base, 562 std::vector<uint8_t>& identifier) { 563 void* text_section; 564 size_t text_size; 565 if (!FindElfSection(elf_mapped_base, ".text", SHT_PROGBITS, 566 (const void**)&text_section, &text_size) || 567 text_size == 0) { 568 return false; 569 } 570 571 // Only provide |kMDGUIDSize| bytes to keep identifiers produced by this 572 // function backwards-compatible. 573 identifier.resize(kMDGUIDSize); 574 memset(&identifier[0], 0, kMDGUIDSize); 575 const uint8_t* ptr = reinterpret_cast<const uint8_t*>(text_section); 576 const uint8_t* ptr_end = 577 ptr + std::min(text_size, static_cast<size_t>(4096)); 578 while (ptr < ptr_end) { 579 for (unsigned i = 0; i < kMDGUIDSize; i++) identifier[i] ^= ptr[i]; 580 ptr += kMDGUIDSize; 581 } 582 return true; 583 } 584 585 // Load the identifier for the elf file mapped into memory at |base| into 586 // |identifier|. Return false if the identifier could not be created for this 587 // file. 588 static bool ElfFileIdentifierFromMappedFile( 589 const void* base, std::vector<uint8_t>& identifier) { 590 // Look for a build id note first. 591 if (FindElfBuildIDNote(base, identifier)) return true; 592 593 // Fall back on hashing the first page of the text section. 594 return HashElfTextSection(base, identifier); 595 } 596 597 // These three functions are not ever called in an unsafe context, so it's OK 598 // to allocate memory and use libc. 599 static std::string bytes_to_hex_string(const uint8_t* bytes, size_t count, 600 bool lowercase = false) { 601 std::string result; 602 for (unsigned int idx = 0; idx < count; ++idx) { 603 char buf[3]; 604 SprintfLiteral(buf, lowercase ? "%02x" : "%02X", bytes[idx]); 605 result.append(buf); 606 } 607 return result; 608 } 609 610 // Convert the |identifier| data to a string. The string will 611 // be formatted as a UUID in all uppercase without dashes. 612 // (e.g., 22F065BBFC9C49F780FE26A7CEBD7BCE). 613 static std::string ConvertIdentifierToUUIDString( 614 const std::vector<uint8_t>& identifier) { 615 uint8_t identifier_swapped[kMDGUIDSize] = {0}; 616 617 // Endian-ness swap to match dump processor expectation. 618 memcpy(identifier_swapped, &identifier[0], 619 std::min(kMDGUIDSize, identifier.size())); 620 uint32_t* data1 = reinterpret_cast<uint32_t*>(identifier_swapped); 621 *data1 = htonl(*data1); 622 uint16_t* data2 = reinterpret_cast<uint16_t*>(identifier_swapped + 4); 623 *data2 = htons(*data2); 624 uint16_t* data3 = reinterpret_cast<uint16_t*>(identifier_swapped + 6); 625 *data3 = htons(*data3); 626 627 return bytes_to_hex_string(identifier_swapped, kMDGUIDSize); 628 } 629 630 // Convert the entire |identifier| data to a lowercase hex string. 631 static std::string ConvertIdentifierToString( 632 const std::vector<uint8_t>& identifier) { 633 return bytes_to_hex_string(&identifier[0], identifier.size(), 634 /* lowercase */ true); 635 } 636 637 private: 638 // Storage for the path specified 639 std::string path_; 640 }; 641 642 // End of imports from toolkit/crashreporter/google-breakpad/. 643 // ---------------------------------------------------------------------------- 644 645 struct LoadedLibraryInfo { 646 LoadedLibraryInfo(const char* aName, unsigned long aBaseAddress, 647 unsigned long aFirstMappingStart, 648 unsigned long aLastMappingEnd, 649 std::optional<std::vector<uint8_t>>&& aElfFileIdentifier) 650 : mName(aName), 651 mBaseAddress(aBaseAddress), 652 mFirstMappingStart(aFirstMappingStart), 653 mLastMappingEnd(aLastMappingEnd), 654 mElfFileIdentifier(std::move(aElfFileIdentifier)) {} 655 656 std::string mName; 657 unsigned long mBaseAddress; 658 unsigned long mFirstMappingStart; 659 unsigned long mLastMappingEnd; 660 std::optional<std::vector<uint8_t>> mElfFileIdentifier; 661 }; 662 663 static std::string IDtoUUIDString(const std::vector<uint8_t>& aIdentifier) { 664 std::string uuid = FileID::ConvertIdentifierToUUIDString(aIdentifier); 665 // This is '0', not '\0', since it represents the breakpad id age. 666 uuid += '0'; 667 return uuid; 668 } 669 670 // Return raw Build ID in hex. 671 static std::string IDtoString(const std::vector<uint8_t>& aIdentifier) { 672 std::string uuid = FileID::ConvertIdentifierToString(aIdentifier); 673 return uuid; 674 } 675 676 // Get the ELF file identifier from file, which will be used for getting the 677 // breakpad Id and code Id for the binary file pointed by bin_name. 678 static std::optional<std::vector<uint8_t>> getElfFileIdentifierFromFile( 679 const char* bin_name) { 680 std::vector<uint8_t> identifier; 681 identifier.reserve(kDefaultBuildIdSize); 682 683 FileID file_id(bin_name); 684 if (file_id.ElfFileIdentifier(identifier)) { 685 return identifier; 686 } 687 688 return {}; 689 } 690 691 // Get the breakpad Id for the ELF file identifier. 692 static std::string getBreakpadId( 693 const std::optional<std::vector<uint8_t>>& aIdentifier) { 694 if (aIdentifier) { 695 return IDtoUUIDString(aIdentifier.value()); 696 } 697 698 return {}; 699 } 700 701 // Get the code Id for the ELF file identifier. 702 static std::string getCodeId( 703 const std::optional<std::vector<uint8_t>>& aIdentifier) { 704 if (aIdentifier) { 705 return IDtoString(aIdentifier.value()); 706 } 707 708 return {}; 709 } 710 711 static SharedLibrary SharedLibraryAtPath( 712 const char* path, unsigned long libStart, unsigned long libEnd, 713 unsigned long offset = 0, 714 const std::optional<std::vector<uint8_t>>& elfFileIdentifier = 715 std::nullopt) { 716 std::string pathStr = path; 717 718 size_t pos = pathStr.rfind('/'); 719 std::string nameStr = 720 (pos != std::string::npos) ? pathStr.substr(pos + 1) : pathStr; 721 722 const auto identifier = elfFileIdentifier 723 ? elfFileIdentifier 724 : getElfFileIdentifierFromFile(path); 725 726 return SharedLibrary(libStart, libEnd, offset, getBreakpadId(identifier), 727 getCodeId(identifier), nameStr, pathStr, nameStr, 728 pathStr, std::string{}, ""); 729 } 730 731 static int dl_iterate_callback(struct dl_phdr_info* dl_info, size_t size, 732 void* data) { 733 auto libInfoList = reinterpret_cast<std::vector<LoadedLibraryInfo>*>(data); 734 735 if (dl_info->dlpi_phnum <= 0) return 0; 736 737 unsigned long baseAddress = dl_info->dlpi_addr; 738 739 // Skip entries with null base address. 740 // Correct implementations of dl_iterate_phdr should never pass a null base 741 // address here, but we have a custom implementation of dl_iterate_phdr in 742 // in our custom linker which is used on Android 22 and older, and this 743 // implementation can sometimes pass null, e.g., from SystemElf::GetBase(). We 744 // can remove this workaround once we remove the custom linker when we drop 745 // support for those Android versions. 746 if (baseAddress == 0) { 747 return 0; 748 } 749 750 unsigned long firstMappingStart = -1; 751 unsigned long lastMappingEnd = 0; 752 std::vector<uint8_t> elfFileIdentifier; 753 754 for (size_t i = 0; i < dl_info->dlpi_phnum; i++) { 755 // Find the mapping start and end. 756 if (dl_info->dlpi_phdr[i].p_type == PT_LOAD) { 757 unsigned long start = dl_info->dlpi_addr + dl_info->dlpi_phdr[i].p_vaddr; 758 unsigned long end = start + dl_info->dlpi_phdr[i].p_memsz; 759 if (start < firstMappingStart) { 760 firstMappingStart = start; 761 } 762 if (end > lastMappingEnd) { 763 lastMappingEnd = end; 764 } 765 } 766 767 // Try to find the ELF file identifier from memory by looking at the 768 // PT_NOTE segments. 769 if (dl_info->dlpi_phdr[i].p_type == PT_NOTE && elfFileIdentifier.empty()) { 770 const void* section_start = reinterpret_cast<const void*>( 771 dl_info->dlpi_addr + dl_info->dlpi_phdr[i].p_vaddr); 772 size_t section_length = dl_info->dlpi_phdr[i].p_memsz; 773 FileID::ElfClassBuildIDNoteIdentifier(section_start, section_length, 774 elfFileIdentifier); 775 } 776 } 777 778 auto optionalElfFileId = 779 elfFileIdentifier.size() > 0 780 ? std::make_optional(std::move(elfFileIdentifier)) 781 : std::nullopt; 782 // Check in case it's a nullptr, as we will construct a std::string with it. 783 // It's UB to pass nullptr to the std::string constructor. 784 const char* libName = dl_info->dlpi_name ? dl_info->dlpi_name : ""; 785 libInfoList->push_back(LoadedLibraryInfo(libName, baseAddress, 786 firstMappingStart, lastMappingEnd, 787 std::move(optionalElfFileId))); 788 789 return 0; 790 } 791 792 SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() { 793 SharedLibraryInfo info; 794 795 #if defined(GP_OS_linux) 796 // We need to find the name of the executable (exeName, exeNameLen) and the 797 // address of its executable section (exeExeAddr) in the running image. 798 char exeName[PATH_MAX]; 799 memset(exeName, 0, sizeof(exeName)); 800 801 ssize_t exeNameLen = readlink("/proc/self/exe", exeName, sizeof(exeName) - 1); 802 if (exeNameLen == -1) { 803 // readlink failed for whatever reason. Note this, but keep going. 804 exeName[0] = '\0'; 805 exeNameLen = 0; 806 // LOG("SharedLibraryInfo::GetInfoForSelf(): readlink failed"); 807 } else { 808 // Assert no buffer overflow. 809 MOZ_RELEASE_ASSERT(exeNameLen >= 0 && 810 exeNameLen < static_cast<ssize_t>(sizeof(exeName))); 811 } 812 813 unsigned long exeExeAddr = 0; 814 #endif 815 816 #if defined(GP_OS_android) 817 // If dl_iterate_phdr doesn't exist, we give up immediately. 818 if (!dl_iterate_phdr) { 819 // On ARM Android, dl_iterate_phdr is provided by the custom linker. 820 // So if libxul was loaded by the system linker (e.g. as part of 821 // xpcshell when running tests), it won't be available and we should 822 // not call it. 823 return info; 824 } 825 #endif 826 827 #if defined(GP_OS_linux) || defined(GP_OS_android) 828 // Read info from /proc/self/maps. We ignore most of it. 829 pid_t pid = mozilla::baseprofiler::profiler_current_process_id().ToNumber(); 830 char path[PATH_MAX]; 831 SprintfLiteral(path, "/proc/%d/maps", pid); 832 std::ifstream maps(path); 833 std::string line; 834 while (std::getline(maps, line)) { 835 int ret; 836 unsigned long start; 837 unsigned long end; 838 char perm[6 + 1] = ""; 839 unsigned long offset; 840 char modulePath[PATH_MAX + 1] = ""; 841 ret = sscanf(line.c_str(), 842 "%lx-%lx %6s %lx %*s %*x %" PATH_MAX_STRING(PATH_MAX) "s\n", 843 &start, &end, perm, &offset, modulePath); 844 if (!strchr(perm, 'x')) { 845 // Ignore non executable entries 846 continue; 847 } 848 if (ret != 5 && ret != 4) { 849 // LOG("SharedLibraryInfo::GetInfoForSelf(): " 850 // "reading /proc/self/maps failed"); 851 continue; 852 } 853 854 # if defined(GP_OS_linux) 855 // Try to establish the main executable's load address. 856 if (exeNameLen > 0 && strcmp(modulePath, exeName) == 0) { 857 exeExeAddr = start; 858 } 859 # elif defined(GP_OS_android) 860 // Use /proc/pid/maps to get the dalvik-jit section since it has no 861 // associated phdrs. 862 if (0 == strcmp(modulePath, "/dev/ashmem/dalvik-jit-code-cache")) { 863 info.AddSharedLibrary( 864 SharedLibraryAtPath(modulePath, start, end, offset)); 865 if (info.GetSize() > 10000) { 866 // LOG("SharedLibraryInfo::GetInfoForSelf(): " 867 // "implausibly large number of mappings acquired"); 868 break; 869 } 870 } 871 # endif 872 } 873 #endif 874 875 std::vector<LoadedLibraryInfo> libInfoList; 876 877 // We collect the bulk of the library info using dl_iterate_phdr. 878 dl_iterate_phdr(dl_iterate_callback, &libInfoList); 879 880 #if defined(GP_OS_linux) 881 bool exeNameAssigned = false; 882 #endif 883 for (const auto& libInfo : libInfoList) { 884 const char* libraryName = libInfo.mName.c_str(); 885 #if defined(GP_OS_linux) 886 // If we see a nameless object mapped at what we earlier established to be 887 // the main executable's load address, use the executable's name instead. 888 if (!exeNameAssigned && libInfo.mFirstMappingStart <= exeExeAddr && 889 exeExeAddr <= libInfo.mLastMappingEnd && libInfo.mName.empty()) { 890 libraryName = exeName; 891 exeNameAssigned = true; 892 } 893 #endif 894 895 info.AddSharedLibrary(SharedLibraryAtPath( 896 libraryName, libInfo.mFirstMappingStart, libInfo.mLastMappingEnd, 897 libInfo.mFirstMappingStart - libInfo.mBaseAddress, 898 libInfo.mElfFileIdentifier)); 899 } 900 901 return info; 902 } 903 904 void SharedLibraryInfo::Initialize() { /* do nothing */ }