| /* BFD back-end for Windows minidump (.dmp) files. |
| Copyright (C) 2020 Free Software Foundation, Inc. |
| |
| This file is part of BFD, the Binary File Descriptor library. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
| MA 02110-1301, USA. */ |
| |
| #define __STDC_FORMAT_MACROS |
| #include "sysdep.h" |
| #include "bfd.h" |
| #include "libbfd.h" |
| #include "minidump-format/minidump_format.h" |
| |
| /* These are stored in the bfd's tdata */ |
| |
| /* .lwpid and .user_tid are only valid if PROC_INFO_HAS_THREAD_ID, else they |
| are set to 0. Also, until HP-UX implements MxN threads, .user_tid and |
| .lwpid are synonymous. */ |
| struct minidump_core_struct |
| { |
| int sig; |
| int lwpid; /* Kernel thread ID. */ |
| unsigned long user_tid; /* User thread ID. */ |
| }; |
| |
| #define core_hdr(bfd) ((bfd)->tdata.minidump_core_data) |
| #define core_signal(bfd) (core_hdr(bfd)->sig) |
| #define core_kernel_thread_id(bfd) (core_hdr(bfd)->lwpid) |
| #define core_user_thread_id(bfd) (core_hdr(bfd)->user_tid) |
| #define minidump_core_core_file_matches_executable_p generic_core_file_matches_executable_p |
| #define minidump_core_core_file_pid _bfd_nocore_core_file_pid |
| |
| static const bfd_target *minidump_core_core_file_p (bfd *); |
| static char *minidump_core_core_file_failing_command (bfd *); |
| static int minidump_core_core_file_failing_signal (bfd *); |
| static void swap_abort (void); |
| |
| static asection * |
| make_bfd_memory_asection (bfd *abfd, const char *name, flagword flags, |
| char *data, size_t size, |
| unsigned int alignment_power) |
| { |
| asection *asect; |
| char *newname; |
| flags |= SEC_IN_MEMORY; |
| |
| newname = bfd_alloc (abfd, (bfd_size_type) strlen (name) + 1); |
| if (!newname) |
| return NULL; |
| |
| strcpy (newname, name); |
| |
| asect = bfd_make_section_anyway_with_flags (abfd, newname, flags); |
| if (!asect) |
| return NULL; |
| |
| asect->size = size; |
| //asect->vma = -1; |
| //asect->filepos = -1; |
| asect->contents = data; /* TODO: does this need to be freed? */ |
| asect->alignment_power = alignment_power; |
| fprintf (stderr, "Making section at %lx len %lx\n", asect->filepos, asect->size); |
| |
| return asect; |
| } |
| static asection * |
| make_bfd_asection (bfd *abfd, const char *name, flagword flags, |
| MDLocationDescriptor loc, bfd_vma vma, |
| unsigned int alignment_power) |
| { |
| asection *asect; |
| char *newname; |
| |
| newname = bfd_alloc (abfd, (bfd_size_type) strlen (name) + 1); |
| if (!newname) |
| return NULL; |
| |
| strcpy (newname, name); |
| |
| asect = bfd_make_section_anyway_with_flags (abfd, newname, flags); |
| if (!asect) |
| return NULL; |
| |
| asect->size = bfd_getl32 (&loc.data_size); |
| asect->vma = vma; |
| asect->filepos = bfd_getl32 (&loc.rva); |
| asect->alignment_power = alignment_power; |
| fprintf (stderr, "Making section at %lx len %lx\n", asect->filepos, asect->size); |
| |
| return asect; |
| } |
| |
| static void |
| handle_thread_ctx (bfd *abfd, int thread_id, MDLocationDescriptor location, int first) |
| { |
| MDRawContextAMD64 ctx; /* TODO: Make this work for other CPUs. */ |
| char *registers; |
| |
| if (bfd_seek (abfd, location.rva, SEEK_SET) != 0) |
| return; |
| if (bfd_bread (&ctx, (bfd_size_type) sizeof ctx, abfd) != sizeof ctx) |
| return; |
| |
| /* TODO: Make this work for win/mac too */ |
| |
| registers = malloc (216); // TODO |
| /* These need to match the offsets in amd64_linux_gregset_reg_offset */ |
| *(unsigned long *) registers = ctx.r15; |
| *(unsigned long *) (registers + 8) = ctx.r14; |
| *(unsigned long *) (registers + 2 * 8) = ctx.r13; |
| *(unsigned long *) (registers + 3 * 8) = ctx.r12; |
| *(unsigned long *) (registers + 4 * 8) = ctx.rbp; |
| *(unsigned long *) (registers + 5 * 8) = ctx.rbx; |
| *(unsigned long *) (registers + 6 * 8) = ctx.r11; |
| *(unsigned long *) (registers + 7 * 8) = ctx.r10; |
| *(unsigned long *) (registers + 8 * 8) = ctx.r9; |
| *(unsigned long *) (registers + 9 * 8) = ctx.r8; |
| *(unsigned long *) (registers + 10 * 8) = ctx.rax; |
| *(unsigned long *) (registers + 11 * 8) = ctx.rcx; |
| *(unsigned long *) (registers + 12 * 8) = ctx.rdx; |
| *(unsigned long *) (registers + 13 * 8) = ctx.rsi; |
| *(unsigned long *) (registers + 14 * 8) = ctx.rdi; |
| /* skipping orig_rax */ |
| *(unsigned long *) (registers + 16 * 8) = ctx.rip; |
| *(unsigned long *) (registers + 17 * 8) = ctx.cs; |
| *(unsigned long *) (registers + 18 * 8) = ctx.eflags; |
| *(unsigned long *) (registers + 19 * 8) = ctx.rsp; |
| *(unsigned long *) (registers + 20 * 8) = ctx.ss; |
| *(unsigned long *) (registers + 23 * 8) = ctx.ds; |
| *(unsigned long *) (registers + 24 * 8) = ctx.es; |
| *(unsigned long *) (registers + 25 * 8) = ctx.fs; |
| *(unsigned long *) (registers + 26 * 8) = ctx.gs; |
| |
| |
| /* TODO: Make this work for other threads. Use .reg/123 as section name */ |
| if (first) |
| make_bfd_memory_asection (abfd, ".reg", SEC_ALLOC + SEC_LOAD + SEC_HAS_CONTENTS, registers, 216, 4); |
| } |
| |
| static void |
| handle_thread_list (bfd *abfd, MDLocationDescriptor location) |
| { |
| MDRawThreadList list; |
| unsigned num_threads; |
| unsigned cur_thread; |
| |
| if (bfd_seek (abfd, location.rva, SEEK_SET) != 0) |
| return; |
| if (bfd_bread (&list, (bfd_size_type) MDRawThreadList_minsize, abfd) != MDRawThreadList_minsize) |
| return; |
| num_threads = bfd_getl32 (&list.number_of_threads); |
| for (cur_thread = 0; cur_thread < num_threads; ++cur_thread) |
| { |
| MDRawThread thread; |
| if (bfd_bread (&thread, (bfd_size_type) sizeof thread, abfd) != sizeof thread) |
| return; |
| |
| handle_thread_ctx (abfd, bfd_getl32 (&thread.thread_id), thread.thread_context, cur_thread == 0); |
| } |
| } |
| static void |
| handle_memory_list (bfd *abfd, MDLocationDescriptor location) |
| { |
| MDRawMemoryList list; |
| unsigned num_ranges; |
| unsigned cur_range; |
| |
| if (bfd_seek (abfd, location.rva, SEEK_SET) != 0) |
| return; |
| if (bfd_bread (&list, (bfd_size_type) 4, abfd) != 4) /* FIXME - magic number */ |
| return; |
| num_ranges = bfd_getl32 (&list.number_of_memory_ranges); |
| for (cur_range = 0; cur_range < num_ranges; ++cur_range) |
| { |
| MDMemoryDescriptor desc; |
| bfd_vma base_addr; |
| if (bfd_bread (&desc, (bfd_size_type) sizeof desc, abfd) != sizeof desc) |
| return; |
| |
| base_addr = bfd_getl64 (&desc.start_of_memory_range); |
| if (!make_bfd_asection (abfd, ".data", |
| SEC_ALLOC + SEC_LOAD + SEC_HAS_CONTENTS, |
| desc.memory, base_addr, 4)) |
| return; /* TODO: report error */ |
| } |
| } |
| |
| static const bfd_target * |
| minidump_core_core_file_p (bfd *abfd) |
| { |
| MDRawHeader header; |
| MDRawDirectory dir; |
| unsigned stream_count; |
| unsigned stream_directory_pos; |
| if (bfd_bread (&header, (bfd_size_type) sizeof header, abfd) != sizeof header) |
| return NULL; |
| if (bfd_getl32 (&header.signature) != 0x504d444d) |
| return NULL; |
| stream_count = bfd_getl32 (&header.stream_count); |
| if (stream_count == 0) |
| return NULL; |
| fprintf(stderr, "Valid DMP file!\n"); |
| |
| stream_directory_pos = bfd_getl32 (&header.stream_directory_rva); |
| |
| /* TODO: support other CPUs */ |
| bfd_default_set_arch_mach (abfd, bfd_arch_i386, bfd_mach_x86_64); |
| |
| for (unsigned i = 0; i < stream_count; ++i) |
| { |
| unsigned stream_type; |
| if (bfd_seek (abfd, stream_directory_pos + i * sizeof dir, SEEK_SET) != 0) |
| return NULL; |
| if (bfd_bread (&dir, (bfd_size_type) sizeof dir, abfd) != sizeof dir) |
| return NULL; |
| stream_type = bfd_getl32 (&dir.stream_type); |
| fprintf (stderr, "Found dir %x\n", stream_type); |
| switch (stream_type) |
| { |
| case MD_THREAD_LIST_STREAM: |
| handle_thread_list (abfd, dir.location); |
| break; |
| case MD_MODULE_LIST_STREAM: |
| break; |
| case MD_MEMORY_LIST_STREAM: |
| handle_memory_list (abfd, dir.location); |
| break; |
| case MD_EXCEPTION_STREAM: |
| break; |
| case MD_SYSTEM_INFO_STREAM: |
| break; |
| } |
| } |
| |
| core_hdr (abfd) = (struct minidump_core_struct *) |
| bfd_zalloc (abfd, (bfd_size_type) sizeof (struct minidump_core_struct)); |
| if (!core_hdr (abfd)) |
| return NULL; |
| |
| return abfd->xvec; |
| } |
| |
| static char * |
| minidump_core_core_file_failing_command (bfd *abfd) |
| { |
| return "placeholder"; |
| } |
| |
| static int |
| minidump_core_core_file_failing_signal (bfd *abfd) |
| { |
| return core_signal (abfd); |
| } |
| |
| |
| /* If somebody calls any byte-swapping routines, shoot them. */ |
| static void |
| swap_abort (void) |
| { |
| abort(); /* This way doesn't require any declaration for ANSI to fuck up */ |
| } |
| |
| #define NO_GET ((bfd_vma (*) (const void *)) swap_abort) |
| #define NO_PUT ((void (*) (bfd_vma, void *)) swap_abort) |
| #define NO_GETS ((bfd_signed_vma (*) (const void *)) swap_abort) |
| #define NO_GET64 ((bfd_uint64_t (*) (const void *)) swap_abort) |
| #define NO_PUT64 ((void (*) (bfd_uint64_t, void *)) swap_abort) |
| #define NO_GETS64 ((bfd_int64_t (*) (const void *)) swap_abort) |
| |
| const bfd_target core_minidump_vec = |
| { |
| "minidump-core", |
| bfd_target_unknown_flavour, |
| BFD_ENDIAN_LITTLE, /* target byte order */ |
| BFD_ENDIAN_LITTLE, /* target headers byte order */ |
| (HAS_RELOC | EXEC_P | /* object flags */ |
| HAS_LINENO | HAS_DEBUG | |
| HAS_SYMS | HAS_LOCALS | WP_TEXT | D_PAGED), |
| (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC), /* section flags */ |
| 0, /* symbol prefix */ |
| ' ', /* ar_pad_char */ |
| 16, /* ar_max_namelen */ |
| 0, /* match priority. */ |
| NO_GET64, NO_GETS64, NO_PUT64, /* 64 bit data */ |
| NO_GET, NO_GETS, NO_PUT, /* 32 bit data */ |
| NO_GET, NO_GETS, NO_PUT, /* 16 bit data */ |
| NO_GET64, NO_GETS64, NO_PUT64, /* 64 bit hdrs */ |
| NO_GET, NO_GETS, NO_PUT, /* 32 bit hdrs */ |
| NO_GET, NO_GETS, NO_PUT, /* 16 bit hdrs */ |
| |
| { /* bfd_check_format */ |
| _bfd_dummy_target, /* unknown format */ |
| _bfd_dummy_target, /* object file */ |
| _bfd_dummy_target, /* archive */ |
| minidump_core_core_file_p /* a core file */ |
| }, |
| { /* bfd_set_format */ |
| _bfd_bool_bfd_false_error, |
| _bfd_bool_bfd_false_error, |
| _bfd_bool_bfd_false_error, |
| _bfd_bool_bfd_false_error |
| }, |
| { /* bfd_write_contents */ |
| _bfd_bool_bfd_false_error, |
| _bfd_bool_bfd_false_error, |
| _bfd_bool_bfd_false_error, |
| _bfd_bool_bfd_false_error |
| }, |
| |
| BFD_JUMP_TABLE_GENERIC (_bfd_generic), |
| BFD_JUMP_TABLE_COPY (_bfd_generic), |
| BFD_JUMP_TABLE_CORE (minidump_core), |
| BFD_JUMP_TABLE_ARCHIVE (_bfd_noarchive), |
| BFD_JUMP_TABLE_SYMBOLS (_bfd_nosymbols), |
| BFD_JUMP_TABLE_RELOCS (_bfd_norelocs), |
| BFD_JUMP_TABLE_WRITE (_bfd_generic), |
| BFD_JUMP_TABLE_LINK (_bfd_nolink), |
| BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic), |
| |
| NULL, |
| |
| NULL /* backend_data */ |
| }; |
| |