summaryrefslogtreecommitdiff
path: root/exec
diff options
context:
space:
mode:
authorRoland McGrath <roland@gnu.org>2002-03-08 23:35:51 +0000
committerRoland McGrath <roland@gnu.org>2002-03-08 23:35:51 +0000
commit0993c13eeffb0b8ea6f612c5d3884df9c8c870c2 (patch)
treeb78b2f3fc69aeb20800adec4dd8b3188a39409b2 /exec
parent71387a05d31a64c39f28e2a9381a3cfeef0061ff (diff)
Untested code.
Diffstat (limited to 'exec')
-rw-r--r--exec/elfcore.c414
1 files changed, 326 insertions, 88 deletions
diff --git a/exec/elfcore.c b/exec/elfcore.c
index 523973d8..aef25d3b 100644
--- a/exec/elfcore.c
+++ b/exec/elfcore.c
@@ -1,100 +1,338 @@
+#include <hurd.h>
+#include <elf.h>
+#include <link.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/utsname.h>
+
+
+#define ELF_MACHINE EM_386 /* XXX */
+
+#define ELF_CLASS PASTE (ELFCLASS, __ELF_NATIVE_CLASS)
+#define PASTE(a, b) PASTE_1 (a, b)
+#define PASTE_1(a, b) a##b
+
+#include <endian.h>
+#if BYTE_ORDER == BIG_ENDIAN
+#define ELF_DATA ELFDATA2MSB
+#elif BYTE_ORDER == LITTLE_ENDIAN
+#define ELF_DATA ELFDATA2LSB
+#endif
+
+
+error_t
+dump_core (task_t task, file_t file, off_t corelimit,
+ int signo, long int sigcode, int sigerror)
{
- processor_set_name_t pset;
- host_t host;
- processor_set_basic_info_data_t pinfo;
+ error_t err;
+ ElfW(Phdr) *phdrs, *ph;
+ ElfW(Ehdr) hdr = /* ELF header for the core file. */
+ {
+ e_ident:
+ {
+ [EI_MAG0] = ELFMAG0,
+ [EI_MAG1] = ELFMAG1,
+ [EI_MAG2] = ELFMAG2,
+ [EI_MAG3] = ELFMAG3,
+ [EI_CLASS] = ELF_CLASS,
+ [EI_DATA] = ELF_DATA,
+ [EI_VERSION] = EV_CURRENT,
+ [EI_OSABI] = ELFOSABI_SYSV,
+ [EI_ABIVERSION] = 0
+ },
+ e_type: ET_CORE,
+ e_version: EV_CURRENT,
+ e_machine: ELF_MACHINE,
+ e_ehsize: sizeof hdr,
+ e_phentsize: sizeof phdrs[0],
+ e_phoff: sizeof hdr, /* Fill in e_phnum later. */
+ };
+ off_t offset;
+ size_t wrote;
thread_t *threads;
- size_t nthreads;
-
- vm_address_t addr;
- vm_size_t size;
- vm_prot_t prot, maxprot;
- vm_inherit_t inherit;
- boolean_t shared;
- memory_object_name_t objname;
- vm_offset_t offset;
-
- /* Figure out what flavor of machine the task is on. */
- if (err = task_get_assignment (task, &pset))
- goto lose;
- err = processor_set_info (pset, PROCESSOR_SET_BASIC_INFO, &host,
- &pinfo, PROCESSOR_SET_BASIC_INFO_COUNT);
- mach_port_deallocate (mach_task_self (), pset);
- if (err)
- goto lose;
- err = bfd_mach_host_arch_mach (host, &arch, &machine, &e_machine);
- mach_port_deallocate (mach_task_self (), host);
+ size_t nthreads, i;
+ off_t notestart;
+
+ /* Helper macros for writing notes. */
+#define DEFINE_NOTE(typename) struct { struct note_header hdr; typename data; }
+#define WRITE_NOTE(type, var) ({ \
+ (var).hdr = NOTE_HEADER ((type), sizeof (var)); \
+ write_note (&(var).hdr); \
+})
+ struct note_header
+ {
+ ElfW(Nhdr) note;
+ char name[4];
+ };
+#define NOTE_HEADER(type, size) \
+ ((struct note_header) { { 4, (size), (type) }, "CORE" })
+ inline error_t write_note (struct note_header *hdr)
+ {
+ error_t err = 0;
+ char *data = (char *) hdr;
+ size_t size = sizeof *hdr + hdr->note.n_descsz;
+ if (corelimit >= 0 && offset + size > corelimit)
+ size = corelimit - offset;
+ while (size > 0)
+ {
+ err = io_write (file, data, size, offset, &wrote);
+ if (err)
+ return err;
+ offset = (offset + wrote + 3) &~ 3; /* Pad it to word alignment. */
+ if (wrote > size)
+ break;
+ data += wrote;
+ size -= wrote;
+ }
+ return err;
+ }
+
+ struct vm_region_list
+ {
+ struct vm_region_list *next;
+ vm_prot_t protection;
+ vm_address_t start;
+ vm_size_t length;
+ };
+ struct vm_region_list *regions = NULL, **tailp = &regions, *r;
+ unsigned int nregions = 0;
+
+ if (corelimit >= 0 && corelimit < sizeof hdr)
+ return EFBIG;
+
+ {
+ /* Examine the task and record the locations of contiguous memory
+ segments that we will dump into the core file. */
+
+ vm_address_t region_address, last_region_address, last_region_end;
+ vm_prot_t last_protection;
+ inline void record_last_region (void)
+ {
+ if (last_region_end > last_region_address
+ && last_protection != VM_PROT_NONE)
+ {
+ struct vm_region_list *region = alloca (sizeof *region);
+ *tailp = region;
+ tailp = &region->next;
+ region->next = NULL;
+ region->start = last_region_address;
+ region->length = last_region_end - last_region_address;
+ region->protection = last_protection;
+ ++nregions;
+ }
+ }
+
+ region_address = last_region_address = last_region_end = VM_MIN_ADDRESS;
+ last_protection = VM_PROT_NONE;
+ while (region_address < VM_MAX_ADDRESS)
+ {
+ vm_prot_t protection;
+ vm_prot_t max_protection;
+ vm_inherit_t inheritance;
+ boolean_t shared;
+ mach_port_t object_name;
+ vm_offset_t offset;
+ vm_size_t region_length = VM_MAX_ADDRESS - region_address;
+
+ err = vm_region (task,
+ &region_address,
+ &region_length,
+ &protection,
+ &max_protection,
+ &inheritance,
+ &shared,
+ &object_name,
+ &offset);
+ if (err == KERN_NO_SPACE)
+ break;
+ if (err != KERN_SUCCESS)
+ return err;
+
+ if (protection == last_protection && region_address == last_region_end)
+ /* This region is contiguous with and indistinguishable from
+ the previous one, so we just extend that one. */
+ last_region_end = region_address += region_length;
+ else
+ {
+ /* This region is distinct from the last one we saw,
+ so record that previous one. */
+ record_last_region ();
+ last_region_address = region_address;
+ last_region_end = region_address += region_length;
+ last_protection = protection;
+ }
+ }
+ /* Record the final region. */
+ record_last_region ();
+ }
+
+ /* Now we start laying out the file. */
+ offset = round_page (sizeof hdr + ((nregions + 1) * sizeof *phdrs));
+
+ /* Final check for tiny core limit. From now on, we will simply truncate
+ the file at CORELIMIT but not change the contents of what we write. */
+ if (corelimit >= 0 && corelimit < offset)
+ return EFBIG;
+
+ /* Now we can complete the file header and write it. */
+ hdr.e_phnum = nregions + 1;
+ err = io_write (file, (char *) &hdr, sizeof hdr, 0, &wrote);
if (err)
- goto lose;
+ return err;
+ if (wrote < sizeof hdr)
+ return EGRATUITOUS; /* XXX */
+
+ /* Now we can write the various notes. */
+ notestart = offset;
- if (err = task_threads (task, &threads, &nthreads))
- goto lose;
+ /* First a dull note containing the results of `uname', a la Solaris. */
+ {
+ DEFINE_NOTE (struct utsname) note;
+ if (uname (&note.data) == 0) /* XXX Use proc_uname on task's proc port? */
+ err = WRITE_NOTE (NT_UTSNAME, note);
+ }
+ if (err || (corelimit >= 0 && corelimit <= offset))
+ return err;
- /* Create a BFD section to describe each contiguous chunk
- of the task's address space with the same stats. */
- sec = NULL;
- addr = 0;
- while (!vm_region (task, &addr, &size, &prot, &maxprot,
- &inherit, &shared, &objname, &offset))
+#if 0
+ /* The pstatus_t note should contain the death info and some process-global
+ info we should get from the proc server, but no thread-specific info
+ like register state. We need to define this type. */
+ {
+ DEFINE_NOTE (pstatus_t) note;
+ note.data.pr_info.si_signo = signo;
+ note.data.pr_info.si_code = sigcode;
+ note.data.pr_info.si_errno = sigerror;
+ err = WRITE_NOTE (NT_PSTATUS, note);
+ }
+ if (err || (corelimit >= 0 && corelimit <= offset))
+ return err;
+#endif
+
+ /* Now examine all the threads in the task.
+ For each thread we produce one or more notes. */
+ err = task_threads (task, &threads, &nthreads);
+ if (err)
+ return err;
+ for (i = 0; i < nthreads; ++i)
{
- mach_port_deallocate (mach_task_self (), objname);
+ {
+ /* lwpinfo_t a la Solaris gives thread's CPU time and such. */
+ DEFINE_NOTE (struct thread_basic_info) note;
+ mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
+ err = thread_info (threads[i], THREAD_BASIC_INFO,
+ (thread_info_t)&note.data, &count);
+ if (err == 0)
+ err = WRITE_NOTE (NT_LWPSINFO, note);
+ else /* Just skip it if we can't get the info. */
+ err = 0;
+ }
+ if (err || (corelimit >= 0 && corelimit <= offset))
+ break;
- if (prot != VM_PROT_NONE)
- {
- flagword flags = SEC_NO_FLAGS;
-
- if (!(prot & VM_PROT_WRITE))
- flags |= SEC_READONLY;
- if (!(prot & VM_PROT_EXECUTE))
- flags |= SEC_DATA;
-
- if (sec != NULL &&
- (vm_address_t) (bfd_section_vma (bfd, sec) +
- bfd_section_size (bfd, sec)) == addr &&
- flags == (bfd_get_section_flags (bfd, sec) &
- (SEC_READONLY|SEC_DATA)))
- /* Coalesce with the previous section. */
- bfd_set_section_size (bfd, sec,
- bfd_section_size (bfd, sec) + size);
- else
- {
- /* Make a new section (which might grow by
- the next region being coalesced onto it). */
- char *name = bfd_intuit_section_name (addr, size, &flags);
- if (name == NULL)
- {
- /* No guess from BFD. */
- if (asprintf (&name, "[%p,%p) %c%c%c",
- (void *) addr, (void *) (addr + size),
- (prot & VM_PROT_READ) ? 'r' : '-',
- (prot & VM_PROT_WRITE) ? 'w' : '-',
- (prot & VM_PROT_EXECUTE) ? 'x' : '-') == -1)
- goto lose;
- }
- sec = bfd_make_section (name);
- bfd_set_section_flags (bfd, sec, flags);
- bfd_set_section_vma (bfd, sec, addr);
- bfd_set_section_size (bfd, sec, size);
- }
- }
+#ifdef WRITE_THREAD_NOTES
+ /* XXX Here would go the note flavors for machine thread states. */
+ err = WRITE_THREAD_NOTES (i, threads[i]);
+#endif
+ if (err || (corelimit >= 0 && corelimit <= offset))
+ break;
+ mach_port_deallocate (mach_task_self (), threads[i]);
}
+ while (i < nthreads)
+ mach_port_deallocate (mach_task_self (), threads[i++]);
+ munmap (threads, nthreads * sizeof *threads);
+ if (err || (corelimit >= 0 && corelimit <= offset))
+ return err;
+
+ /* Make an array of program headers and fill them in.
+ The first one describes the note segment. */
+ ph = phdrs = alloca ((nregions + 1) * sizeof *phdrs);
- /* Write all the sections' data. */
- for (sec = bfd->sections; sec != NULL; sec = sec->next)
+ memset (ph, 0, sizeof *ph);
+ ph->p_type = PT_NOTE;
+ ph->p_offset = notestart;
+ ph->p_filesz = offset - notestart;
+ ++ph;
+
+ /* Now make ELF program headers for each of the record memory regions.
+ Consistent with the Linux kernel, we create PT_LOAD headers with
+ p_filesz = 0 for the read-only segments that we are not dumping
+ into the file. */
+ for (r = regions; r != NULL; r = r->next)
{
- void *data;
- err = vm_read (task, bfd_section_vma (bfd, sec),
- bfd_section_size (bfd, sec), &data);
- if (err)
- /* XXX What to do?
- 1. lose
- 2. remove this section
- 3. mark this section as having ungettable contents (how?)
- */
- goto lose;
- err = bfd_set_section_contents (bfd, sec, data, 0,
- bfd_section_size (bfd, sec));
- munmap ((caddr_t) data, bfd_section_size (bfd, sec));
- if (err)
- goto bfdlose;
+ memset (ph, 0, sizeof *ph);
+ ph->p_type = PT_LOAD;
+ ph->p_align = vm_page_size;
+ ph->p_flags = (((r->protection & VM_PROT_READ) ? PF_R : 0)
+ | ((r->protection & VM_PROT_WRITE) ? PF_W : 0)
+ | ((r->protection & VM_PROT_EXECUTE) ? PF_X : 0));
+ ph->p_vaddr = r->start;
+ ph->p_memsz = r->length;
+ ph->p_filesz = (r->protection & VM_PROT_WRITE) ? ph->p_memsz : 0;
+ ph->p_offset = offset;
+ offset += ph->p_filesz;
+ ++ph;
}
+
+ /* Now write the memory segment data. */
+ for (ph = phdrs; ph < &phdrs[nregions]; ++ph)
+ if (ph->p_filesz > 0)
+ {
+ vm_address_t va = ph->p_vaddr;
+ vm_size_t sz = ph->p_memsz;
+ off_t ofs = ph->p_offset;
+ int wrote_any = 0;
+ do
+ {
+ pointer_t copied;
+ int copy_count;
+ err = vm_read (task, va, sz, &copied, &copy_count);
+ if (err == 0)
+ {
+ char *data = (void *) copied;
+ size_t left = copy_count, wrote;
+
+ va += copy_count;
+ sz -= copy_count;
+
+ do
+ {
+ if (corelimit >= 0 && ofs + left > corelimit)
+ left = corelimit - ofs;
+ err = io_write (file, data, left, ofs, &wrote);
+ if (err)
+ break;
+ ofs += wrote;
+ data += wrote;
+ left -= wrote;
+ if (ofs >= corelimit)
+ break;
+ } while (left > 0);
+
+ munmap ((void *) copied, copy_count);
+
+ if (left < copy_count)
+ wrote_any = 1;
+ }
+ else
+ {
+ /* Leave a hole in the file for pages we can't read. */
+ va += vm_page_size;
+ sz -= vm_page_size;
+ ofs += vm_page_size;
+ }
+ } while (sz > 0 && (corelimit < 0 || ofs < corelimit));
+
+ if (! wrote_any)
+ /* If we failed to write any contents at all,
+ don't claim the big hole as the contents. */
+ ph->p_filesz = 0;
+ }
+
+ /* Finally, we go back and write the program headers. */
+ err = io_write (file, (char *) phdrs, (nregions + 1) * sizeof phdrs[0],
+ sizeof hdr, &wrote);
+
+ return err;
+}