OLD | NEW |
1 // Copyright (c) 2006, Google Inc. | 1 // Copyright (c) 2006, Google Inc. |
2 // All rights reserved. | 2 // All rights reserved. |
3 // | 3 // |
4 // Redistribution and use in source and binary forms, with or without | 4 // Redistribution and use in source and binary forms, with or without |
5 // modification, are permitted provided that the following conditions are | 5 // modification, are permitted provided that the following conditions are |
6 // met: | 6 // met: |
7 // | 7 // |
8 // * Redistributions of source code must retain the above copyright | 8 // * Redistributions of source code must retain the above copyright |
9 // notice, this list of conditions and the following disclaimer. | 9 // notice, this list of conditions and the following disclaimer. |
10 // * Redistributions in binary form must reproduce the above | 10 // * Redistributions in binary form must reproduce the above |
(...skipping 386 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
397 public: | 397 public: |
398 explicit LineReader(int fd, char *buf, int buf_len) : fd_(fd), | 398 explicit LineReader(int fd, char *buf, int buf_len) : fd_(fd), |
399 buf_(buf), buf_len_(buf_len), bol_(buf), eol_(buf), eod_(buf) { | 399 buf_(buf), buf_len_(buf_len), bol_(buf), eol_(buf), eod_(buf) { |
400 } | 400 } |
401 | 401 |
402 // Read '\n'-terminated line from file. On success, modify "bol" | 402 // Read '\n'-terminated line from file. On success, modify "bol" |
403 // and "eol", then return true. Otherwise, return false. | 403 // and "eol", then return true. Otherwise, return false. |
404 // | 404 // |
405 // Note: if the last line doesn't end with '\n', the line will be | 405 // Note: if the last line doesn't end with '\n', the line will be |
406 // dropped. It's an intentional behavior to make the code simple. | 406 // dropped. It's an intentional behavior to make the code simple. |
407 bool ReadLine(const char **bol, const char **eol) { | 407 bool ReadLine(char **bol, char **eol) { |
408 if (BufferIsEmpty()) { // First time. | 408 if (BufferIsEmpty()) { // First time. |
409 const ssize_t num_bytes = ReadPersistent(fd_, buf_, buf_len_); | 409 const ssize_t num_bytes = ReadPersistent(fd_, buf_, buf_len_); |
410 if (num_bytes <= 0) { // EOF or error. | 410 if (num_bytes <= 0) { // EOF or error. |
411 return false; | 411 return false; |
412 } | 412 } |
413 eod_ = buf_ + num_bytes; | 413 eod_ = buf_ + num_bytes; |
414 bol_ = buf_; | 414 bol_ = buf_; |
415 } else { | 415 } else { |
416 bol_ = eol_ + 1; // Advance to the next line in the buffer. | 416 bol_ = eol_ + 1; // Advance to the next line in the buffer. |
417 SAFE_ASSERT(bol_ <= eod_); // "bol_" can point to "eod_". | 417 SAFE_ASSERT(bol_ <= eod_); // "bol_" can point to "eod_". |
(...skipping 18 matching lines...) Expand all Loading... |
436 return false; | 436 return false; |
437 } | 437 } |
438 *eol_ = '\0'; // Replace '\n' with '\0'. | 438 *eol_ = '\0'; // Replace '\n' with '\0'. |
439 | 439 |
440 *bol = bol_; | 440 *bol = bol_; |
441 *eol = eol_; | 441 *eol = eol_; |
442 return true; | 442 return true; |
443 } | 443 } |
444 | 444 |
445 // Beginning of line. | 445 // Beginning of line. |
446 const char *bol() { | 446 char *bol() { |
447 return bol_; | 447 return bol_; |
448 } | 448 } |
449 | 449 |
450 // End of line. | 450 // End of line. |
451 const char *eol() { | 451 char *eol() { |
452 return eol_; | 452 return eol_; |
453 } | 453 } |
454 | 454 |
455 private: | 455 private: |
456 explicit LineReader(const LineReader&); | 456 explicit LineReader(const LineReader&); |
457 void operator=(const LineReader&); | 457 void operator=(const LineReader&); |
458 | 458 |
459 char *FindLineFeed() { | 459 char *FindLineFeed() { |
460 return reinterpret_cast<char *>(memchr(bol_, '\n', eod_ - bol_)); | 460 return reinterpret_cast<char *>(memchr(bol_, '\n', eod_ - bol_)); |
461 } | 461 } |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
502 // |out_file_name|, and attempts to open the object file. If the object | 502 // |out_file_name|, and attempts to open the object file. If the object |
503 // file is opened successfully, returns the file descriptor. Otherwise, | 503 // file is opened successfully, returns the file descriptor. Otherwise, |
504 // returns -1. |out_file_name_size| is the size of the file name buffer | 504 // returns -1. |out_file_name_size| is the size of the file name buffer |
505 // (including the null-terminator). | 505 // (including the null-terminator). |
506 static ATTRIBUTE_NOINLINE int | 506 static ATTRIBUTE_NOINLINE int |
507 OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc, | 507 OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc, |
508 uint64_t &start_address, | 508 uint64_t &start_address, |
509 uint64_t &base_address, | 509 uint64_t &base_address, |
510 char *out_file_name, | 510 char *out_file_name, |
511 int out_file_name_size) { | 511 int out_file_name_size) { |
512 int object_fd; | 512 struct FindArgs { |
| 513 const uint64_t pc; |
| 514 uint64_t* out_start_address; |
| 515 uint64_t* out_base_address; |
| 516 char* out_file_name; |
| 517 int out_file_name_size; |
| 518 // Fields below are default initialized |
| 519 int num_maps; |
| 520 int out_object_fd; |
| 521 }; |
513 | 522 |
514 // Open /proc/self/maps. | 523 auto find_callback = [](void* data, const MappedRegion& region) -> bool { |
515 int maps_fd; | 524 FindArgs* args = static_cast<FindArgs*>(data); |
516 NO_INTR(maps_fd = open("/proc/self/maps", O_RDONLY)); | 525 args->num_maps++; |
517 FileDescriptor wrapped_maps_fd(maps_fd); | |
518 if (wrapped_maps_fd.get() < 0) { | |
519 return -1; | |
520 } | |
521 | |
522 // Iterate over maps and look for the map containing the pc. Then | |
523 // look into the symbol tables inside. | |
524 char buf[1024]; // Big enough for line of sane /proc/self/maps | |
525 int num_maps = 0; | |
526 LineReader reader(wrapped_maps_fd.get(), buf, sizeof(buf)); | |
527 while (true) { | |
528 num_maps++; | |
529 const char *cursor; | |
530 const char *eol; | |
531 if (!reader.ReadLine(&cursor, &eol)) { // EOF or malformed line. | |
532 return -1; | |
533 } | |
534 | |
535 // Start parsing line in /proc/self/maps. Here is an example: | |
536 // | |
537 // 08048000-0804c000 r-xp 00000000 08:01 2142121 /bin/cat | |
538 // | |
539 // We want start address (08048000), end address (0804c000), flags | |
540 // (r-xp) and file name (/bin/cat). | |
541 | |
542 // Read start address. | |
543 cursor = GetHex(cursor, eol, &start_address); | |
544 if (cursor == eol || *cursor != '-') { | |
545 return -1; // Malformed line. | |
546 } | |
547 ++cursor; // Skip '-'. | |
548 | |
549 // Read end address. | |
550 uint64_t end_address; | |
551 cursor = GetHex(cursor, eol, &end_address); | |
552 if (cursor == eol || *cursor != ' ') { | |
553 return -1; // Malformed line. | |
554 } | |
555 ++cursor; // Skip ' '. | |
556 | 526 |
557 // Check start and end addresses. | 527 // Check start and end addresses. |
558 if (!(start_address <= pc && pc < end_address)) { | 528 if (!(region.start_address <= args->pc && args->pc < region.end_address)) { |
559 continue; // We skip this map. PC isn't in this map. | 529 return false; // We skip this map. PC isn't in this map. |
560 } | |
561 | |
562 // Read flags. Skip flags until we encounter a space or eol. | |
563 const char * const flags_start = cursor; | |
564 while (cursor < eol && *cursor != ' ') { | |
565 ++cursor; | |
566 } | |
567 // We expect at least four letters for flags (ex. "r-xp"). | |
568 if (cursor == eol || cursor < flags_start + 4) { | |
569 return -1; // Malformed line. | |
570 } | 530 } |
571 | 531 |
572 // Check flags. We are only interested in "r-x" maps. | 532 // Check flags. We are only interested in "r-x" maps. |
573 if (memcmp(flags_start, "r-x", 3) != 0) { // Not a "r-x" map. | 533 if (memcmp(region.flags, "r-x", 3) != 0) { // Not a "r-x" map. |
574 continue; // We skip this map. | 534 return false; // We skip this map. |
575 } | 535 } |
576 ++cursor; // Skip ' '. | |
577 | 536 |
578 // Read file offset. | 537 *args->out_start_address = region.start_address; |
579 uint64_t file_offset; | |
580 cursor = GetHex(cursor, eol, &file_offset); | |
581 if (cursor == eol || *cursor != ' ') { | |
582 return -1; // Malformed line. | |
583 } | |
584 ++cursor; // Skip ' '. | |
585 | 538 |
586 // Don't subtract 'start_address' from the first entry: | 539 // Don't subtract 'start_address' from the first entry: |
587 // * If a binary is compiled w/o -pie, then the first entry in | 540 // * If a binary is compiled w/o -pie, then the first entry in |
588 // process maps is likely the binary itself (all dynamic libs | 541 // process maps is likely the binary itself (all dynamic libs |
589 // are mapped higher in address space). For such a binary, | 542 // are mapped higher in address space). For such a binary, |
590 // instruction offset in binary coincides with the actual | 543 // instruction offset in binary coincides with the actual |
591 // instruction address in virtual memory (as code section | 544 // instruction address in virtual memory (as code section |
592 // is mapped to a fixed memory range). | 545 // is mapped to a fixed memory range). |
593 // * If a binary is compiled with -pie, all the modules are | 546 // * If a binary is compiled with -pie, all the modules are |
594 // mapped high at address space (in particular, higher than | 547 // mapped high at address space (in particular, higher than |
595 // shadow memory of the tool), so the module can't be the | 548 // shadow memory of the tool), so the module can't be the |
596 // first entry. | 549 // first entry. |
597 base_address = ((num_maps == 1) ? 0U : start_address) - file_offset; | 550 *args->out_base_address = |
| 551 ((args->num_maps == 1) ? 0U : region.start_address) - |
| 552 region.file_offset; |
| 553 |
| 554 // Open region's file. |
| 555 NO_INTR(args->out_object_fd = open(region.name, O_RDONLY)); |
| 556 if (args->out_object_fd < 0) { |
| 557 // Failed to open object file. Copy the object file name to |
| 558 // |out_file_name|. |
| 559 strncpy(args->out_file_name, region.name, args->out_file_name_size); |
| 560 // Making sure |out_file_name| is always null-terminated. |
| 561 args->out_file_name[args->out_file_name_size - 1] = '\0'; |
| 562 } |
| 563 |
| 564 return true; // We found the entry; stop iterating. |
| 565 }; |
| 566 |
| 567 FindArgs args = { |
| 568 pc, |
| 569 &start_address, |
| 570 &base_address, |
| 571 out_file_name, |
| 572 out_file_name_size |
| 573 }; |
| 574 bool found = FindMappedRegion(&args, find_callback); |
| 575 return found ? args.out_object_fd : -1; |
| 576 } |
| 577 |
| 578 bool FindMappedRegion(void* callback_data, |
| 579 bool (*callback)(void*, const MappedRegion&)) { |
| 580 // Open /proc/self/maps. |
| 581 int maps_fd; |
| 582 NO_INTR(maps_fd = open("/proc/self/maps", O_RDONLY)); |
| 583 FileDescriptor wrapped_maps_fd(maps_fd); |
| 584 if (wrapped_maps_fd.get() < 0) { |
| 585 return false; |
| 586 } |
| 587 |
| 588 char buf[1024]; // Big enough for line of sane /proc/self/maps |
| 589 int num_maps = 0; |
| 590 MappedRegion region; |
| 591 LineReader reader(wrapped_maps_fd.get(), buf, sizeof(buf)); |
| 592 while (true) { |
| 593 num_maps++; |
| 594 char *cursor; |
| 595 char *eol; |
| 596 if (!reader.ReadLine(&cursor, &eol)) { // EOF or malformed line. |
| 597 break; |
| 598 } |
| 599 |
| 600 // Zero-terminate the line. |
| 601 *eol = 0; |
| 602 |
| 603 // Start parsing line in /proc/self/maps. Here is an example: |
| 604 // |
| 605 // 08048000-0804c000 r-xp 00000000 08:01 2142121 /bin/cat |
| 606 // |
| 607 // We want start address (08048000), end address (0804c000), flags |
| 608 // (r-xp), file offset (2142121) and file name (/bin/cat). |
| 609 |
| 610 // Read start address. |
| 611 cursor = GetHex(cursor, eol, ®ion.start_address); |
| 612 if (cursor == eol || *cursor != '-') { |
| 613 break; // Malformed line. |
| 614 } |
| 615 ++cursor; // Skip '-'. |
| 616 |
| 617 // Read end address. |
| 618 uint64_t end_address; |
| 619 cursor = GetHex(cursor, eol, ®ion.end_address); |
| 620 if (cursor == eol || *cursor != ' ') { |
| 621 break; // Malformed line. |
| 622 } |
| 623 ++cursor; // Skip ' '. |
| 624 |
| 625 // Read flags. Skip flags until we encounter a space or eol. |
| 626 region.flags = cursor; |
| 627 while (cursor < eol && *cursor != ' ') { |
| 628 ++cursor; |
| 629 } |
| 630 // We expect at least four letters for flags (ex. "r-xp"). |
| 631 if (cursor == eol || cursor < region.flags + 4) { |
| 632 break; // Malformed line. |
| 633 } |
| 634 // Replace ' ' with zero and advance. |
| 635 *cursor++ = 0; |
| 636 |
| 637 // Read file offset. |
| 638 cursor = GetHex(cursor, eol, ®ion.file_offset); |
| 639 if (cursor == eol || *cursor != ' ') { |
| 640 break; // Malformed line. |
| 641 } |
| 642 ++cursor; // Skip ' '. |
598 | 643 |
599 // Skip to file name. "cursor" now points to dev. We need to | 644 // Skip to file name. "cursor" now points to dev. We need to |
600 // skip at least two spaces for dev and inode. | 645 // skip at least two spaces for dev and inode. Note that name can |
| 646 // be empty (cursor == eol). |
601 int num_spaces = 0; | 647 int num_spaces = 0; |
602 while (cursor < eol) { | 648 while (cursor < eol) { |
603 if (*cursor == ' ') { | 649 if (*cursor == ' ') { |
604 ++num_spaces; | 650 ++num_spaces; |
605 } else if (num_spaces >= 2) { | 651 } else if (num_spaces >= 2) { |
606 // The first non-space character after skipping two spaces | 652 // The first non-space character after skipping two spaces |
607 // is the beginning of the file name. | 653 // is the beginning of the file name. |
608 break; | 654 break; |
609 } | 655 } |
610 ++cursor; | 656 ++cursor; |
611 } | 657 } |
612 if (cursor == eol) { | 658 |
613 return -1; // Malformed line. | 659 // Finally, "cursor" now points to file name. |
| 660 region.name = cursor; |
| 661 |
| 662 // Report found region to the callback. |
| 663 if (callback(callback_data, region)) { |
| 664 return true; // Callback stopped the iteration. |
614 } | 665 } |
| 666 } |
615 | 667 |
616 // Finally, "cursor" now points to file name of our interest. | 668 return false; |
617 NO_INTR(object_fd = open(cursor, O_RDONLY)); | |
618 if (object_fd < 0) { | |
619 // Failed to open object file. Copy the object file name to | |
620 // |out_file_name|. | |
621 strncpy(out_file_name, cursor, out_file_name_size); | |
622 // Making sure |out_file_name| is always null-terminated. | |
623 out_file_name[out_file_name_size - 1] = '\0'; | |
624 return -1; | |
625 } | |
626 return object_fd; | |
627 } | |
628 } | 669 } |
629 | 670 |
630 // POSIX doesn't define any async-signal safe function for converting | 671 // POSIX doesn't define any async-signal safe function for converting |
631 // an integer to ASCII. We'll have to define our own version. | 672 // an integer to ASCII. We'll have to define our own version. |
632 // itoa_r() converts a (signed) integer to ASCII. It returns "buf", if the | 673 // itoa_r() converts a (signed) integer to ASCII. It returns "buf", if the |
633 // conversion was successful or NULL otherwise. It never writes more than "sz" | 674 // conversion was successful or NULL otherwise. It never writes more than "sz" |
634 // bytes. Output will be truncated as needed, and a NUL character is always | 675 // bytes. Output will be truncated as needed, and a NUL character is always |
635 // appended. | 676 // appended. |
636 // NOTE: code from sandbox/linux/seccomp-bpf/demo.cc. | 677 // NOTE: code from sandbox/linux/seccomp-bpf/demo.cc. |
637 char *itoa_r(intptr_t i, char *buf, size_t sz, int base, size_t padding) { | 678 char *itoa_r(intptr_t i, char *buf, size_t sz, int base, size_t padding) { |
(...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
840 | 881 |
841 // TODO: Support other environments. | 882 // TODO: Support other environments. |
842 bool Symbolize(void *pc, char *out, int out_size) { | 883 bool Symbolize(void *pc, char *out, int out_size) { |
843 assert(0); | 884 assert(0); |
844 return false; | 885 return false; |
845 } | 886 } |
846 | 887 |
847 _END_GOOGLE_NAMESPACE_ | 888 _END_GOOGLE_NAMESPACE_ |
848 | 889 |
849 #endif | 890 #endif |
OLD | NEW |