| /* 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 <sys/user.h> |
| #include <sys/ldr.h> |
| #include <sys/core.h> |
| |
| |
| /* 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 */ |