this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fixes for darling-coredump to produce usable coredumps

It seems that LLDB expects any region listed as a load command to be
present in the core file, even regions with a reported file size of 0.
So instead, let's do the same thing LLDB does when saving core dumps and
just zero out those regions.

Previously, we were getting lots of "missing NT_FILE" warnings, but these
were actually wrong. It seems that if a region in the ELF core dump has
a file size of 0 and there's no corresponding NT_FILE entry, this means
it doesn't actually reside in a file (as usually indicated by a program
header file size of 0); instead, it means that the region was simply
zero-filled and the kernel did not include it in the core dump.

An additional fix has also been added that allows the main executable to
be detected even when the prefix is unmounted. This is just applying the
same logic we were already using when reading from files in the
read-and-dump stage.

+109 -69
+1 -1
src/hosttools/CMakeLists.txt
··· 1 1 project(hosttools) 2 2 3 - set(DARLING_COREDUMP_SANITIZE OFF) 3 + option(DARLING_COREDUMP_SANITIZE "Enable/disable ASAN and UBSAN in darling-coredump" OFF) 4 4 5 5 add_executable(darling-coredump 6 6 src/coredump/main.c
+108 -68
src/hosttools/src/coredump/main.c
··· 20 20 #error Not implemented 21 21 #endif 22 22 23 + #include <darling-config.h> 24 + 23 25 // interestingly enough, there are no existing tools that can perform this conversion (ELF coredump to Mach-O coredump). 24 26 // neither objcopy nor llvm-objcopy support Mach-O conversion like that, nor does objconv (it considers coredumps to be executables and refuses to operate on them). 25 27 // porting our existing code from the LKM is simple enough, so that's what we do here. ··· 114 116 static const Elf64_Nhdr* find_next_note(const Elf64_Nhdr* note) { 115 117 uint64_t length = sizeof(*note) + round_up_pow2(note->n_namesz, 4) + round_up_pow2(note->n_descsz, 4); 116 118 return (Elf64_Nhdr*)((char*)note + length); 119 + }; 120 + 121 + // first tries to open the file directly, then tries to open the file in the lower layer of the overlay 122 + // (because if we're outside the container, the overlay won't be mounted, but the core dump paths would refer to it) 123 + static int open_file(struct coredump_params* cprm, const char* filename, size_t filename_length) { 124 + int fd = -1; 125 + 126 + // try to open it directly first 127 + fd = open(filename, O_RDONLY); 128 + 129 + // if that fails, try to see if it refers to the mounted prefix; if it does, try the lower layer 130 + if (fd < 0 && filename_length >= cprm->prefix_length && strncmp(filename, cprm->prefix, cprm->prefix_length) == 0) { 131 + char* temp_filename = malloc(sizeof(LIBEXEC_PATH "/") + (filename_length - cprm->prefix_length)); 132 + sprintf(temp_filename, "%s/%s", LIBEXEC_PATH, &filename[cprm->prefix_length]); 133 + fd = open(temp_filename, O_RDONLY); 134 + free(temp_filename); 135 + } 136 + 137 + return fd; 117 138 }; 118 139 119 140 void macho_coredump(struct coredump_params* cprm); ··· 288 309 289 310 // try to determine if this is the main executable file 290 311 if (strncmp(filename, "/dev/", sizeof("/dev/") - 1) != 0) { 291 - int tmpfd = open(filename, O_RDONLY); 312 + int tmpfd = open_file(&cprm, filename, strlen(filename)); 292 313 if (tmpfd >= 0) { 293 314 struct mach_header_64 mh; 294 315 if (read(tmpfd, &mh, sizeof(mh)) == sizeof(mh) && mh.filetype == MH_EXECUTE) { ··· 393 414 if (!vm_area->filename) { 394 415 if (vm_area->protection == 0) { 395 416 // if the area has no protection flags, it most likely wasn't included anywhere. 396 - // no need to warn. 397 - } else if (vm_area->memory_address == 0) { 398 - // ignore it if starts at 0; that's most likely the __PAGEZERO segment. 399 - // no need to warn. 400 - } else { 401 - // otherwise, warn. 402 - printf("warning: missing NT_FILE entry for memory region 0x%zx-%zx (%lu bytes)\n", vm_area->memory_address, vm_area->memory_address + vm_area->memory_size, vm_area->memory_size); 417 + // no need to include it in the core dump. this also handles the case of __PAGEZERO. 418 + vm_area->valid = false; 403 419 } 420 + 421 + // otherwise, it's valid, just not present anywhere. 422 + // this can occur for pages that are allocated but never accessed, so there would be no reason to include them in the core dump. 423 + // in this case, the program header literally indicates that the region's file size is 0, since it's unnecessary to include anywhere, 424 + // not necessarily that it resides in a file. 425 + 404 426 vm_area->file_size = 0; 405 - vm_area->valid = false; 406 427 } 407 428 } else { 408 429 // contents contained within this corefile ··· 410 431 vm_area->filename_length = 0; 411 432 vm_area->file_size = program_header->p_filesz; 412 433 } 413 - 414 - if (vm_area->memory_address == 0) { 415 - // overrides for the __PAGEZERO segment 416 - vm_area->valid = false; 417 - vm_area->file_size = 0; 418 - vm_area->protection = 0; 419 - } 420 434 } 421 435 } 422 436 ··· 564 578 unsigned int segs = cprm->vm_area_count; 565 579 unsigned int threads = cprm->thread_info_count; 566 580 581 + for (size_t i = 0; i < cprm->vm_area_count; ++i) { 582 + struct vm_area* vma = &cprm->vm_areas[i]; 583 + 584 + if (!vma->valid) { 585 + // we don't dump inaccessible/invalid regions, so remove them from the count 586 + --segs; 587 + } 588 + } 589 + 567 590 struct mach_header mh; 568 591 569 592 mh.magic = MH_MAGIC; ··· 585 608 586 609 uint32_t file_offset = mh.sizeofcmds + sizeof(mh); 587 610 611 + // TODO: maybe align the initial offset to 0x1000 612 + 588 613 for (size_t i = 0; i < cprm->vm_area_count; ++i) { 589 614 const struct vm_area* vma = &cprm->vm_areas[i]; 590 615 struct segment_command sc; 616 + 617 + if (!vma->valid) { 618 + // ignore inaccessible/invalid regions (like the __PAGEZERO segment, which may be really large) 619 + continue; 620 + } 591 621 592 622 sc.cmd = LC_SEGMENT; 593 623 sc.cmdsize = sizeof(sc); ··· 598 628 sc.vmsize = vma->memory_size; 599 629 sc.fileoff = file_offset; 600 630 601 - if (sc.vmaddr > 0) // avoid dumping the __PAGEZERO segment which may be really large 602 - sc.filesize = vma->file_size; 603 - else 604 - sc.filesize = 0; 631 + sc.filesize = (vma->file_size == 0) ? vma->memory_size : vma->file_size; 605 632 sc.initprot = 0; 606 633 607 634 if (vma->protection & PROT_READ) ··· 663 690 unsigned int threads = cprm->thread_info_count; 664 691 struct mach_header_64 mh; 665 692 693 + for (size_t i = 0; i < cprm->vm_area_count; ++i) { 694 + struct vm_area* vma = &cprm->vm_areas[i]; 695 + 696 + if (!vma->valid) { 697 + // we don't dump inaccessible/invalid regions, so remove them from the count 698 + --segs; 699 + } 700 + } 701 + 666 702 mh.magic = MH_MAGIC_64; 667 703 #ifdef __x86_64__ 668 704 mh.cputype = CPU_TYPE_X86_64; ··· 679 715 mh.reserved = 0; 680 716 681 717 if (cprm->main_executable_path) { 682 - int tmpfd = open(cprm->main_executable_path, O_RDONLY); 718 + int tmpfd = open_file(cprm, cprm->main_executable_path, cprm->main_executable_path_length); 683 719 if (tmpfd >= 0) { 684 720 struct mach_header_64 that_mh; 685 721 if (read(tmpfd, &that_mh, sizeof(that_mh)) == sizeof(that_mh)) { ··· 701 737 struct vm_area* vma = &cprm->vm_areas[i]; 702 738 struct segment_command_64 sc; 703 739 740 + if (!vma->valid) { 741 + // ignore inaccessible/invalid regions (like the __PAGEZERO segment, which may be really large) 742 + continue; 743 + } 744 + 704 745 sc.cmd = LC_SEGMENT_64; 705 746 sc.cmdsize = sizeof(sc); 706 747 sc.segname[0] = 0; ··· 712 753 713 754 vma->expected_offset = sc.fileoff; 714 755 715 - if (vma->valid) // avoid dumping the __PAGEZERO segment which may be really large 716 - sc.filesize = vma->file_size; 717 - else 718 - sc.filesize = 0; 756 + sc.filesize = (vma->file_size == 0) ? vma->memory_size : vma->file_size; 719 757 sc.initprot = 0; 720 758 721 759 if (vma->protection & PROT_READ) ··· 795 833 const struct vm_area* vma = &cprm->vm_areas[i]; 796 834 unsigned long addr; 797 835 836 + if (!vma->valid) { 837 + continue; 838 + } 839 + 798 840 uint64_t curr = dump_offset_get(cprm); 799 841 if (curr != vma->expected_offset) { 800 842 fprintf(stderr, "Expected offset %lx, got offset %lx\n", vma->expected_offset, curr); 801 843 exit(EXIT_FAILURE); 802 844 } 803 845 804 - if (!vma->valid) { 805 - if (!dump_skip(cprm, vma->file_size)) 846 + if (vma->file_size == 0) { 847 + // this region needs to be zeroed out 848 + // 849 + // it's likely a region that was allocated with mmap but never accessed, 850 + // so it remained zero-filled and there was no need to include it in the ELF core file. 851 + if (!dump_skip(cprm, vma->memory_size)) 806 852 exit(EXIT_FAILURE); 807 - } else { 808 - if (vma->filename) { 809 - if (strncmp(vma->filename, "/dev/", sizeof("/dev/") - 1) == 0) { 810 - printf("Warning: skipping device \"%s\"\n", vma->filename); 811 - if (!dump_skip(cprm, vma->file_size)) { 812 - exit(EXIT_FAILURE); 813 - } 814 - continue; 815 - } 853 + continue; 854 + } 816 855 817 - int fd = -1; 818 - fd = open(vma->filename, O_RDONLY); 819 - if (fd < 0 && vma->filename_length >= cprm->prefix_length && strncmp(vma->filename, cprm->prefix, cprm->prefix_length) == 0) { 820 - char* filename = malloc(sizeof("/usr/local/libexec/darling/") + (vma->filename_length - cprm->prefix_length)); 821 - sprintf(filename, "%s/%s", "/usr/local/libexec/darling", &vma->filename[cprm->prefix_length]); 822 - fd = open(filename, O_RDONLY); 823 - free(filename); 824 - } 825 - if (fd < 0) { 826 - fprintf(stderr, "Warning: failed to open %s: %d (%s)\n", vma->filename, errno, strerror(errno)); 827 - //exit(EXIT_FAILURE); 828 - // just zero it out 829 - if (!dump_skip(cprm, vma->file_size)) { 830 - exit(EXIT_FAILURE); 831 - } 832 - continue; 856 + if (vma->filename) { 857 + if (strncmp(vma->filename, "/dev/", sizeof("/dev/") - 1) == 0) { 858 + printf("Warning: skipping device \"%s\"\n", vma->filename); 859 + if (!dump_skip(cprm, vma->file_size)) { 860 + exit(EXIT_FAILURE); 833 861 } 862 + continue; 863 + } 834 864 835 - uintptr_t aligned_offset = round_down_pow2(vma->file_offset, sysconf(_SC_PAGESIZE)); 836 - size_t aligned_size = round_up_pow2(vma->file_size, sysconf(_SC_PAGESIZE)); 837 - void* mapping = mmap(NULL, aligned_size, PROT_READ, MAP_PRIVATE, fd, aligned_offset); 838 - if (mapping == MAP_FAILED) { 839 - perror("mmap"); 865 + int fd = open_file(cprm, vma->filename, vma->filename_length); 866 + if (fd < 0) { 867 + fprintf(stderr, "Warning: failed to open %s: %d (%s)\n", vma->filename, errno, strerror(errno)); 868 + //exit(EXIT_FAILURE); 869 + // just zero it out 870 + if (!dump_skip(cprm, vma->file_size)) { 840 871 exit(EXIT_FAILURE); 841 872 } 842 - const void* start = (const char*)mapping + (vma->file_offset - aligned_offset); 873 + continue; 874 + } 843 875 844 - if (!dump_emit(cprm, start, vma->file_size)) 845 - exit(EXIT_FAILURE); 876 + uintptr_t aligned_offset = round_down_pow2(vma->file_offset, sysconf(_SC_PAGESIZE)); 877 + size_t aligned_size = round_up_pow2(vma->file_size + (vma->file_offset - aligned_offset), sysconf(_SC_PAGESIZE)); 878 + void* mapping = mmap(NULL, aligned_size, PROT_READ, MAP_PRIVATE, fd, aligned_offset); 879 + if (mapping == MAP_FAILED) { 880 + perror("mmap"); 881 + exit(EXIT_FAILURE); 882 + } 883 + const void* start = (const char*)mapping + (vma->file_offset - aligned_offset); 846 884 847 - if (munmap(mapping, aligned_size) < 0) { 848 - perror("munmap"); 849 - exit(EXIT_FAILURE); 850 - } 885 + if (!dump_emit(cprm, start, vma->file_size)) 886 + exit(EXIT_FAILURE); 851 887 852 - close(fd); 853 - } else { 854 - if (!dump_emit(cprm, (const char*)cprm->input_corefile_mapping + vma->file_offset, vma->file_size)) 855 - exit(EXIT_FAILURE); 888 + if (munmap(mapping, aligned_size) < 0) { 889 + perror("munmap"); 890 + exit(EXIT_FAILURE); 856 891 } 892 + 893 + close(fd); 894 + } else { 895 + if (!dump_emit(cprm, (const char*)cprm->input_corefile_mapping + vma->file_offset, vma->file_size)) 896 + exit(EXIT_FAILURE); 857 897 } 858 898 } 859 899 }