/* $Id$ */ #include #include #include #include #include #include #include #include #include "build.h" #include "objid.h" #include "common.h" #include "maps.h" #include "hook.h" #include "hook-arch-c.h" /* Make the checksumming faster but core file specific by mincore(2)? For some important swapped out libraries the checksum would get void. */ static ElfW(LibobjidHdr) *header; static size_t pages_size; static unsigned obj_allocated; static ElfW(LibobjidObj) *obj; static void header_update (void) { obj_allocated = (pages_size - offsetof (ElfW(LibobjidHdr), obj)) / sizeof (header->obj[0]); assert (obj_allocated >= header->obj_count); } static int header_init (void) { pages_size = PAGE_SIZE; header = mmap (NULL, pages_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (header == MAP_FAILED) { if (debug >= 1) fprintf (stderr, "libobjid: Failed to map %lu bytes: %m\n", (unsigned long) pages_size); return -1; } header->obj_count = 0; header_update (); return 0; } static int header_resize (size_t pages_size_new) { ElfW(LibobjidHdr) *header_new; pages_size_new = (pages_size_new + PAGE_SIZE - 1) & PAGE_MASK; header_new = mremap (header, pages_size, pages_size_new, MREMAP_MAYMOVE); if (header_new == MAP_FAILED) { if (debug >= 1) fprintf (stderr, "libobjid: Failed to remap %lu to %lu bytes: %m\n", (unsigned long) pages_size, (unsigned long) pages_size_new); return -1; } header = header_new; pages_size = pages_size_new; header_update (); return 0; } static void header_finish (void) { size_t strings_size = 0; unsigned obji; char *string_ptr; for (obji = 0; obji < header->obj_count; obji++) strings_size += strlen ((const char *) header->obj[obji].filename) + 1; header->size = sizeof (*header) + header->obj_count * sizeof (*header->obj) + strings_size; /* Possibly even shrink the allocated pages. */ header_resize (header->size); /* Do not reuse any address above, header_resize() could move it. */ string_ptr = (char *) header + sizeof (*header) + header->obj_count * sizeof (*header->obj); for (obji = 0; obji < header->obj_count; obji++) { const char *filename_orig = (const char *) header->obj[obji].filename; strcpy (string_ptr, filename_orig); header->obj[obji].filename = (ElfW (Addr)) string_ptr; string_ptr += strlen (filename_orig) + 1; } assert (string_ptr == (char *) header + header->size); header->magic = ElfW(Magic); header->self = (ElfW(Addr)) header; header->self_not = ~(ElfW(Addr)) header; } /* Returns non-zero on error. */ static int checksum_open (const char *filename) { assert (header != NULL); if (header->obj_count == obj_allocated) { assert (pages_size >= 1); if (header_resize (pages_size * 2)) return -1; assert (header->obj_count < obj_allocated); } obj = &header->obj[header->obj_count]; /* It should not happen but avoid the array termination. */ if (filename == NULL) filename = ""; obj->filename = (ElfW(Addr)) filename; obj->checksum = 0; if (debug >= 1) fprintf (stderr, "%s\n", filename); return 0; } static void checksum_data (ElfW(Word) *sum, void *start, void *end) { unsigned char *ptr; for (ptr = start; (void *) ptr < end; ptr++) *sum = *sum * 31 + *ptr; if (debug >= 1) fprintf (stderr, "\t%p - %p (0x%lx)\n", start, end, (unsigned long) *sum); } static void checksum_close (void) { header->obj_count++; assert (header->obj_count <= obj_allocated); } /* Range is provided only for debug purposes. */ static void checksum_cancel (void *start, void *end) { if (debug >= 1) fprintf (stderr, "\tCANCEL: %p - %p\n", start, end); } static int phdr_callback (struct dl_phdr_info *info, size_t size, void *data) { int j; int started = 0; for (j = 0; j < info->dlpi_phnum; j++) { void *start, *end; if (info->dlpi_phdr[j].p_type != PT_LOAD) continue; if (!(info->dlpi_phdr[j].p_flags & PF_R)) continue; /* Present? We probably could not checksum such one. */ if (info->dlpi_phdr[j].p_flags & PF_W) continue; if (!started) { /* No longer storage space? No other object may succeed. */ if (checksum_open (info->dlpi_name) != 0) return 1; started = 1; } start = (void *) (info->dlpi_addr + info->dlpi_phdr[j].p_vaddr); end = start + info->dlpi_phdr[j].p_memsz; /* Use maps_verify() after checksum_open() for more debugging info. */ if (maps_verify (start, end) != 0) { /* This object's checksum is invalid but other objects may be checksummable. */ if (started) checksum_cancel (start, end); return 0; } if ((void *) sigaction_orig_libc_cont < start || (void *) ((char *) sigaction_orig_libc_cont - trampoline_size) >= end) checksum_data (&obj->checksum, start, end); else { checksum_data (&obj->checksum, start, (char *) sigaction_orig_libc_cont - trampoline_size); checksum_data (&obj->checksum, sigaction_trampoline, (char *) sigaction_trampoline + trampoline_size); checksum_data (&obj->checksum, sigaction_orig_libc_cont, end); } } if (started) checksum_close (); return 0; } INTERNAL void core_dump (void) { const char *msg = "libobjid: core\n"; write (STDERR_FILENO, msg, strlen (msg)); if (header_init ()) return; maps_read (); dl_iterate_phdr (phdr_callback, NULL); header_finish (); }