forked from Imagelibrary/binutils-gdb
338 lines
11 KiB
C
338 lines
11 KiB
C
/* 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 */
|
||
};
|
||
|