From 252b5132c753830d5fd56823373aed85f2a0db63 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 3 May 1999 07:29:11 +0000 Subject: 19990502 sourceware import --- bfd/rs6000-core.c | 507 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 507 insertions(+) create mode 100644 bfd/rs6000-core.c (limited to 'bfd/rs6000-core.c') diff --git a/bfd/rs6000-core.c b/bfd/rs6000-core.c new file mode 100644 index 0000000..b20555f --- /dev/null +++ b/bfd/rs6000-core.c @@ -0,0 +1,507 @@ +/* IBM RS/6000 "XCOFF" back-end for BFD. + Copyright 1990, 91, 92, 93, 94, 95, 96, 97, 1998 + Free Software Foundation, Inc. + FIXME: Can someone provide a transliteration of this name into ASCII? + Using the following chars caused a compiler warning on HIUX (so I replaced + them with octal escapes), and isn't useful without an understanding of what + character set it is. + Written by Metin G. Ozisik, Mimi Ph\373\364ng-Th\345o V\365, + and John Gilmore. + Archive support from Damon A. Permezel. + Contributed by IBM Corporation and Cygnus Support. + +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 2 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* This port currently only handles reading object files, except when + compiled on an RS/6000 host. -- no archive support, no core files. + In all cases, it does not support writing. + + FIXMEmgo comments are left from Metin Ozisik's original port. + + This is in a separate file from coff-rs6000.c, because it includes + system include files that conflict with coff/rs6000.h. + */ + +/* Internalcoff.h and coffcode.h modify themselves based on this flag. */ +#define RS6000COFF_C 1 + +/* The AIX 4.1 kernel is obviously compiled with -D_LONG_LONG, so + we have to define _LONG_LONG for older versions of gcc to get the + proper alignments in the user structure. */ +#if defined(_AIX41) && !defined(_LONG_LONG) +#define _LONG_LONG +#endif + +#include "bfd.h" +#include "sysdep.h" +#include "libbfd.h" + +#ifdef AIX_CORE + +/* AOUTHDR is defined by the above. We need another defn of it, from the + system include files. Punt the old one and get us a new name for the + typedef in the system include files. */ +#ifdef AOUTHDR +#undef AOUTHDR +#endif +#define AOUTHDR second_AOUTHDR + +#undef SCNHDR + + +/* ------------------------------------------------------------------------ */ +/* Support for core file stuff.. */ +/* ------------------------------------------------------------------------ */ + +#include +#include +#include + + +/* Number of special purpose registers supported by gdb. This value + should match `tm.h' in gdb directory. Clean this mess up and use + the macros in sys/reg.h. FIXMEmgo. */ + +#define NUM_OF_SPEC_REGS 7 + +#define core_hdr(bfd) (((Rs6kCorData*)(bfd->tdata.any))->hdr) + +/* AIX 4.1 Changed the names and locations of a few items in the core file, + this seems to be the quickest easiet way to deal with it. + + Note however that encoding magic addresses (STACK_END_ADDR) is going + to be _very_ fragile. But I don't see any easy way to get that info + right now. */ +#ifdef CORE_VERSION_1 +#define CORE_DATA_SIZE_FIELD c_u.U_dsize +#define CORE_COMM_FIELD c_u.U_comm +#define SAVE_FIELD c_mst +#define STACK_END_ADDR 0x2ff23000 +#else +#define CORE_DATA_SIZE_FIELD c_u.u_dsize +#define CORE_COMM_FIELD c_u.u_comm +#define SAVE_FIELD c_u.u_save +#define STACK_END_ADDR 0x2ff80000 +#endif + +/* These are stored in the bfd's tdata */ +typedef struct { + struct core_dump hdr; /* core file header */ +} Rs6kCorData; + +static asection *make_bfd_asection PARAMS ((bfd *, CONST char *, flagword, + bfd_size_type, bfd_vma, file_ptr)); + +static asection * +make_bfd_asection (abfd, name, flags, _raw_size, vma, filepos) + bfd *abfd; + CONST char *name; + flagword flags; + bfd_size_type _raw_size; + bfd_vma vma; + file_ptr filepos; +{ + asection *asect; + + asect = bfd_make_section_anyway (abfd, name); + if (!asect) + return NULL; + + asect->flags = flags; + asect->_raw_size = _raw_size; + asect->vma = vma; + asect->filepos = filepos; + asect->alignment_power = 8; + + return asect; +} + +/* Decide if a given bfd represents a `core' file or not. There really is no + magic number or anything like, in rs6000coff. */ + +const bfd_target * +rs6000coff_core_p (abfd) + bfd *abfd; +{ + struct core_dump coredata; + struct stat statbuf; + bfd_size_type nread; + char *tmpptr; + + if (bfd_seek (abfd, 0, SEEK_SET) != 0) + return NULL; + + nread = bfd_read (&coredata, 1, sizeof (struct core_dump), abfd); + if (nread != sizeof (struct core_dump)) + { + if (bfd_get_error () != bfd_error_system_call) + bfd_set_error (bfd_error_wrong_format); + return NULL; + } + + if (bfd_stat (abfd, &statbuf) < 0) + { + bfd_set_error (bfd_error_system_call); + return NULL; + } + + /* If the core file ulimit is too small, the system will first + omit the data segment, then omit the stack, then decline to + dump core altogether (as far as I know UBLOCK_VALID and LE_VALID + are always set) (this is based on experimentation on AIX 3.2). + Now, the thing is that GDB users will be surprised + if segments just silently don't appear (well, maybe they would + think to check "info files", I don't know). + + For the data segment, we have no choice but to keep going if it's + not there, since the default behavior is not to dump it (regardless + of the ulimit, it's based on SA_FULLDUMP). But for the stack segment, + if it's not there, we refuse to have anything to do with this core + file. The usefulness of a core dump without a stack segment is pretty + limited anyway. */ + + if (!(coredata.c_flag & UBLOCK_VALID) + || !(coredata.c_flag & LE_VALID)) + { + bfd_set_error (bfd_error_wrong_format); + return NULL; + } + + if (!(coredata.c_flag & USTACK_VALID)) + { + bfd_set_error (bfd_error_file_truncated); + return NULL; + } + + /* Don't check the core file size for a full core, AIX 4.1 includes + additional shared library sections in a full core. */ + if (!(coredata.c_flag & (FULL_CORE | CORE_TRUNC)) + && ((bfd_vma)coredata.c_stack + coredata.c_size) != statbuf.st_size) + { + /* If the size is wrong, it means we're misinterpreting something. */ + bfd_set_error (bfd_error_wrong_format); + return NULL; + } + + /* Sanity check on the c_tab field. */ + if ((u_long) coredata.c_tab < sizeof coredata || + (u_long) coredata.c_tab >= statbuf.st_size || + (long) coredata.c_tab >= (long)coredata.c_stack) + { + bfd_set_error (bfd_error_wrong_format); + return NULL; + } + + /* Issue warning if the core file was truncated during writing. */ + if (coredata.c_flag & CORE_TRUNC) + (*_bfd_error_handler) (_("%s: warning core file truncated"), + bfd_get_filename (abfd)); + + /* Allocate core file header. */ + tmpptr = (char*) bfd_zalloc (abfd, sizeof (Rs6kCorData)); + if (!tmpptr) + return NULL; + + set_tdata (abfd, tmpptr); + + /* Copy core file header. */ + core_hdr (abfd) = coredata; + + /* .stack section. */ + if (!make_bfd_asection (abfd, ".stack", + SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS, + (bfd_size_type) coredata.c_size, + (bfd_vma) (STACK_END_ADDR - coredata.c_size), + (file_ptr) coredata.c_stack)) + return NULL; + + /* .reg section for GPRs and special registers. */ + if (!make_bfd_asection (abfd, ".reg", + SEC_HAS_CONTENTS, + (bfd_size_type) ((32 + NUM_OF_SPEC_REGS) * 4), + (bfd_vma) 0, + (file_ptr) ((char *) &coredata.SAVE_FIELD + - (char *) &coredata))) + return NULL; + + /* .reg2 section for FPRs (floating point registers). */ + if (!make_bfd_asection (abfd, ".reg2", + SEC_HAS_CONTENTS, + (bfd_size_type) 8 * 32, /* 32 FPRs. */ + (bfd_vma) 0, + (file_ptr) ((char *) &coredata.SAVE_FIELD.fpr[0] + - (char *) &coredata))) + return NULL; + + /* .ldinfo section. + To actually find out how long this section is in this particular + core dump would require going down the whole list of struct ld_info's. + See if we can just fake it. */ + if (!make_bfd_asection (abfd, ".ldinfo", + SEC_HAS_CONTENTS, + (bfd_size_type) 0x7fffffff, + (bfd_vma) 0, + (file_ptr) coredata.c_tab)) + return NULL; + +#ifndef CORE_VERSION_1 + /* .data section if present. + AIX 3 dumps the complete data section and sets FULL_CORE if the + ulimit is large enough, otherwise the data section is omitted. + AIX 4 sets FULL_CORE even if the core file is truncated, we have + to examine coredata.c_datasize below to find out the actual size of + the .data section. */ + if (coredata.c_flag & FULL_CORE) + { + if (!make_bfd_asection (abfd, ".data", + SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS, + (bfd_size_type) coredata.CORE_DATA_SIZE_FIELD, + (bfd_vma) + CDATA_ADDR (coredata.CORE_DATA_SIZE_FIELD), + (file_ptr) coredata.c_stack + coredata.c_size)) + return NULL; + } +#endif + +#ifdef CORE_VERSION_1 + /* AIX 4 adds data sections from loaded objects to the core file, + which can be found by examining ldinfo, and anonymously mmapped + regions. */ + { + struct ld_info ldinfo; + bfd_size_type ldinfo_size; + file_ptr ldinfo_offset = (file_ptr) coredata.c_tab; + + /* .data section from executable. */ + if (coredata.c_datasize) + { + if (!make_bfd_asection (abfd, ".data", + SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS, + (bfd_size_type) coredata.c_datasize, + (bfd_vma) + CDATA_ADDR (coredata.CORE_DATA_SIZE_FIELD), + (file_ptr) coredata.c_data)) + return NULL; + } + + /* .data sections from loaded objects. */ + ldinfo_size = (char *) &ldinfo.ldinfo_filename[0] + - (char *) &ldinfo.ldinfo_next; + while (1) + { + if (bfd_seek (abfd, ldinfo_offset, SEEK_SET) != 0) + return NULL; + if (bfd_read (&ldinfo, ldinfo_size, 1, abfd) != ldinfo_size) + return NULL; + if (ldinfo.ldinfo_core) + { + if (!make_bfd_asection (abfd, ".data", + SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS, + (bfd_size_type) ldinfo.ldinfo_datasize, + (bfd_vma) ldinfo.ldinfo_dataorg, + (file_ptr) ldinfo.ldinfo_core)) + return NULL; + } + if (ldinfo.ldinfo_next == 0) + break; + ldinfo_offset += ldinfo.ldinfo_next; + } + + /* .vmdata sections from anonymously mmapped regions. */ + if (coredata.c_vmregions) + { + int i; + + if (bfd_seek (abfd, (file_ptr) coredata.c_vmm, SEEK_SET) != 0) + return NULL; + + for (i = 0; i < coredata.c_vmregions; i++) + { + struct vm_info vminfo; + + if (bfd_read (&vminfo, sizeof (vminfo), 1, abfd) != sizeof (vminfo)) + return NULL; + if (vminfo.vminfo_offset) + { + if (!make_bfd_asection (abfd, ".vmdata", + SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS, + (bfd_size_type) vminfo.vminfo_size, + (bfd_vma) vminfo.vminfo_addr, + (file_ptr) vminfo.vminfo_offset)) + return NULL; + } + } + } + } +#endif + + return abfd->xvec; /* this is garbage for now. */ +} + + + +/* return `true' if given core is from the given executable.. */ +boolean +rs6000coff_core_file_matches_executable_p (core_bfd, exec_bfd) + bfd *core_bfd; + bfd *exec_bfd; +{ + struct core_dump coredata; + struct ld_info ldinfo; + bfd_size_type size; + char *path, *s; + size_t alloc; + const char *str1, *str2; + boolean ret; + + if (bfd_seek (core_bfd, 0, SEEK_SET) != 0 + || bfd_read (&coredata, sizeof coredata, 1, core_bfd) != sizeof coredata) + return false; + + if (bfd_seek (core_bfd, (long) coredata.c_tab, SEEK_SET) != 0) + return false; + + size = (char *) &ldinfo.ldinfo_filename[0] - (char *) &ldinfo.ldinfo_next; + if (bfd_read (&ldinfo, size, 1, core_bfd) != size) + return false; + + alloc = 100; + path = bfd_malloc (alloc); + if (path == NULL) + return false; + s = path; + + while (1) + { + if (bfd_read (s, 1, 1, core_bfd) != 1) + { + free (path); + return false; + } + if (*s == '\0') + break; + ++s; + if (s == path + alloc) + { + char *n; + + alloc *= 2; + n = bfd_realloc (path, alloc); + if (n == NULL) + { + free (path); + return false; + } + s = n + (path - s); + path = n; + } + } + + str1 = strrchr (path, '/'); + str2 = strrchr (exec_bfd->filename, '/'); + + /* step over character '/' */ + str1 = str1 != NULL ? str1 + 1 : path; + str2 = str2 != NULL ? str2 + 1 : exec_bfd->filename; + + if (strcmp (str1, str2) == 0) + ret = true; + else + ret = false; + + free (path); + + return ret; +} + +char * +rs6000coff_core_file_failing_command (abfd) + bfd *abfd; +{ + char *com = core_hdr (abfd).CORE_COMM_FIELD; + if (*com) + return com; + else + return 0; +} + +int +rs6000coff_core_file_failing_signal (abfd) + bfd *abfd; +{ + return core_hdr (abfd).c_signo; +} + + +boolean +rs6000coff_get_section_contents (abfd, section, location, offset, count) + bfd *abfd; + sec_ptr section; + PTR location; + file_ptr offset; + bfd_size_type count; +{ + if (count == 0) + return true; + + /* Reading a core file's sections will be slightly different. For the + rest of them we can use bfd_generic_get_section_contents () I suppose. */ + /* Make sure this routine works for any bfd and any section. FIXMEmgo. */ + + if (abfd->format == bfd_core && strcmp (section->name, ".reg") == 0) { + + struct mstsave mstatus; + int regoffset = (char*)&mstatus.gpr[0] - (char*)&mstatus; + + /* Assert that the only way this code will be executed is reading the + whole section. */ + if (offset || count != (sizeof(mstatus.gpr) + (4 * NUM_OF_SPEC_REGS))) + (*_bfd_error_handler) + (_("ERROR! in rs6000coff_get_section_contents()\n")); + + /* for `.reg' section, `filepos' is a pointer to the `mstsave' structure + in the core file. */ + + /* read GPR's into the location. */ + if ( bfd_seek(abfd, section->filepos + regoffset, SEEK_SET) == -1 + || bfd_read(location, sizeof (mstatus.gpr), 1, abfd) != sizeof (mstatus.gpr)) + return (false); /* on error */ + + /* increment location to the beginning of special registers in the section, + reset register offset value to the beginning of first special register + in mstsave structure, and read special registers. */ + + location = (PTR) ((char*)location + sizeof (mstatus.gpr)); + regoffset = (char*)&mstatus.iar - (char*)&mstatus; + + if ( bfd_seek(abfd, section->filepos + regoffset, SEEK_SET) == -1 + || bfd_read(location, 4 * NUM_OF_SPEC_REGS, 1, abfd) != + 4 * NUM_OF_SPEC_REGS) + return (false); /* on error */ + + /* increment location address, and read the special registers.. */ + /* FIXMEmgo */ + return (true); + } + + /* else, use default bfd section content transfer. */ + else + return _bfd_generic_get_section_contents + (abfd, section, location, offset, count); +} + +#endif /* AIX_CORE */ -- cgit v1.1