+
+/* Try to convert a non-ELF reloc into an ELF one. */
+
+boolean
+_bfd_elf_validate_reloc (abfd, areloc)
+ bfd *abfd;
+ arelent *areloc;
+{
+ /* Check whether we really have an ELF howto. */
+
+ if ((*areloc->sym_ptr_ptr)->the_bfd->xvec != abfd->xvec)
+ {
+ bfd_reloc_code_real_type code;
+ reloc_howto_type *howto;
+
+ /* Alien reloc: Try to determine its type to replace it with an
+ equivalent ELF reloc. */
+
+ if (areloc->howto->pc_relative)
+ {
+ switch (areloc->howto->bitsize)
+ {
+ case 8:
+ code = BFD_RELOC_8_PCREL;
+ break;
+ case 12:
+ code = BFD_RELOC_12_PCREL;
+ break;
+ case 16:
+ code = BFD_RELOC_16_PCREL;
+ break;
+ case 24:
+ code = BFD_RELOC_24_PCREL;
+ break;
+ case 32:
+ code = BFD_RELOC_32_PCREL;
+ break;
+ case 64:
+ code = BFD_RELOC_64_PCREL;
+ break;
+ default:
+ goto fail;
+ }
+
+ howto = bfd_reloc_type_lookup (abfd, code);
+
+ if (areloc->howto->pcrel_offset != howto->pcrel_offset)
+ {
+ if (howto->pcrel_offset)
+ areloc->addend += areloc->address;
+ else
+ areloc->addend -= areloc->address; /* addend is unsigned!! */
+ }
+ }
+ else
+ {
+ switch (areloc->howto->bitsize)
+ {
+ case 8:
+ code = BFD_RELOC_8;
+ break;
+ case 14:
+ code = BFD_RELOC_14;
+ break;
+ case 16:
+ code = BFD_RELOC_16;
+ break;
+ case 26:
+ code = BFD_RELOC_26;
+ break;
+ case 32:
+ code = BFD_RELOC_32;
+ break;
+ case 64:
+ code = BFD_RELOC_64;
+ break;
+ default:
+ goto fail;
+ }
+
+ howto = bfd_reloc_type_lookup (abfd, code);
+ }
+
+ if (howto)
+ areloc->howto = howto;
+ else
+ goto fail;
+ }
+
+ return true;
+
+ fail:
+ (*_bfd_error_handler)
+ (_("%s: unsupported relocation type %s"),
+ bfd_get_filename (abfd), areloc->howto->name);
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+}
+
+boolean
+_bfd_elf_close_and_cleanup (abfd)
+ bfd *abfd;
+{
+ if (bfd_get_format (abfd) == bfd_object)
+ {
+ if (elf_shstrtab (abfd) != NULL)
+ _bfd_stringtab_free (elf_shstrtab (abfd));
+ }
+
+ return _bfd_generic_close_and_cleanup (abfd);
+}
+
+/* For Rel targets, we encode meaningful data for BFD_RELOC_VTABLE_ENTRY
+ in the relocation's offset. Thus we cannot allow any sort of sanity
+ range-checking to interfere. There is nothing else to do in processing
+ this reloc. */
+
+bfd_reloc_status_type
+_bfd_elf_rel_vtable_reloc_fn (abfd, re, symbol, data, is, obfd, errmsg)
+ bfd *abfd ATTRIBUTE_UNUSED;
+ arelent *re ATTRIBUTE_UNUSED;
+ struct symbol_cache_entry *symbol ATTRIBUTE_UNUSED;
+ PTR data ATTRIBUTE_UNUSED;
+ asection *is ATTRIBUTE_UNUSED;
+ bfd *obfd ATTRIBUTE_UNUSED;
+ char **errmsg ATTRIBUTE_UNUSED;
+{
+ return bfd_reloc_ok;
+}
+
+\f
+/* Elf core file support. Much of this only works on native
+ toolchains, since we rely on knowing the
+ machine-dependent procfs structure in order to pick
+ out details about the corefile. */
+
+#ifdef HAVE_SYS_PROCFS_H
+# include <sys/procfs.h>
+#endif
+
+
+/* Define offsetof for those systems which lack it. */
+
+#ifndef offsetof
+# define offsetof(TYPE, MEMBER) ((unsigned long) &((TYPE *)0)->MEMBER)
+#endif
+
+
+/* FIXME: this is kinda wrong, but it's what gdb wants. */
+
+static int
+elfcore_make_pid (abfd)
+ bfd* abfd;
+{
+ return ((elf_tdata (abfd)->core_lwpid << 16)
+ + (elf_tdata (abfd)->core_pid));
+}
+
+
+/* If there isn't a section called NAME, make one, using
+ data from SECT. Note, this function will generate a
+ reference to NAME, so you shouldn't deallocate or
+ overwrite it. */
+
+static boolean
+elfcore_maybe_make_sect (abfd, name, sect)
+ bfd* abfd;
+ char* name;
+ asection* sect;
+{
+ asection* sect2;
+
+ if (bfd_get_section_by_name (abfd, name) != NULL)
+ return true;
+
+ sect2 = bfd_make_section (abfd, name);
+ if (sect2 == NULL)
+ return false;
+
+ sect2->_raw_size = sect->_raw_size;
+ sect2->filepos = sect->filepos;
+ sect2->flags = sect->flags;
+ sect2->alignment_power = sect->alignment_power;
+ return true;
+}
+
+
+/* prstatus_t exists on:
+ solaris 2.[567]
+ linux 2.[01] + glibc
+ unixware 4.2
+*/
+
+#if defined (HAVE_PRSTATUS_T)
+static boolean
+elfcore_grok_prstatus (abfd, note)
+ bfd* abfd;
+ Elf_Internal_Note* note;
+{
+ prstatus_t prstat;
+ char buf[100];
+ char* name;
+ asection* sect;
+
+ if (note->descsz != sizeof (prstat))
+ return true;
+
+ memcpy (&prstat, note->descdata, sizeof (prstat));
+
+ elf_tdata (abfd)->core_signal = prstat.pr_cursig;
+ elf_tdata (abfd)->core_pid = prstat.pr_pid;
+
+ /* pr_who exists on:
+ solaris 2.[567]
+ unixware 4.2
+ pr_who doesn't exist on:
+ linux 2.[01]
+ */
+#if defined (HAVE_PRSTATUS_T_PR_WHO)
+ elf_tdata (abfd)->core_lwpid = prstat.pr_who;
+#endif
+
+ /* Make a ".reg/999" section. */
+
+ sprintf (buf, ".reg/%d", elfcore_make_pid (abfd));
+ name = bfd_alloc (abfd, strlen (buf) + 1);
+ if (name == NULL)
+ return false;
+ strcpy (name, buf);
+
+ sect = bfd_make_section (abfd, name);
+ if (sect == NULL)
+ return false;
+ sect->_raw_size = sizeof (prstat.pr_reg);
+ sect->filepos = note->descpos + offsetof (prstatus_t, pr_reg);
+ sect->flags = SEC_HAS_CONTENTS;
+ sect->alignment_power = 2;
+
+ if (! elfcore_maybe_make_sect (abfd, ".reg", sect))
+ return false;
+
+ return true;
+}
+#endif /* defined (HAVE_PRSTATUS_T) */
+
+
+/* There isn't a consistent prfpregset_t across platforms,
+ but it doesn't matter, because we don't have to pick this
+ data structure apart. */
+
+static boolean
+elfcore_grok_prfpreg (abfd, note)
+ bfd* abfd;
+ Elf_Internal_Note* note;
+{
+ char buf[100];
+ char* name;
+ asection* sect;
+
+ /* Make a ".reg2/999" section. */
+
+ sprintf (buf, ".reg2/%d", elfcore_make_pid (abfd));
+ name = bfd_alloc (abfd, strlen (buf) + 1);
+ if (name == NULL)
+ return false;
+ strcpy (name, buf);
+
+ sect = bfd_make_section (abfd, name);
+ if (sect == NULL)
+ return false;
+ sect->_raw_size = note->descsz;
+ sect->filepos = note->descpos;
+ sect->flags = SEC_HAS_CONTENTS;
+ sect->alignment_power = 2;
+
+ if (! elfcore_maybe_make_sect (abfd, ".reg2", sect))
+ return false;
+
+ return true;
+}
+
+#if defined (HAVE_PRPSINFO_T)
+# define elfcore_psinfo_t prpsinfo_t
+#endif
+
+#if defined (HAVE_PSINFO_T)
+# define elfcore_psinfo_t psinfo_t
+#endif
+
+
+#if defined (HAVE_PRPSINFO_T) || defined (HAVE_PSINFO_T)
+
+/* return a malloc'ed copy of a string at START which is at
+ most MAX bytes long, possibly without a terminating '\0'.
+ the copy will always have a terminating '\0'. */
+
+static char*
+elfcore_strndup (abfd, start, max)
+ bfd* abfd;
+ char* start;
+ int max;
+{
+ char* dup;
+ char* end = memchr (start, '\0', max);
+ int len;
+
+ if (end == NULL)
+ len = max;
+ else
+ len = end - start;
+
+ dup = bfd_alloc (abfd, len + 1);
+ if (dup == NULL)
+ return NULL;
+
+ memcpy (dup, start, len);
+ dup[len] = '\0';
+
+ return dup;
+}
+
+static boolean
+elfcore_grok_psinfo (abfd, note)
+ bfd* abfd;
+ Elf_Internal_Note* note;
+{
+ elfcore_psinfo_t psinfo;
+
+ if (note->descsz != sizeof (elfcore_psinfo_t))
+ return true;
+
+ memcpy (&psinfo, note->descdata, note->descsz);
+
+ elf_tdata (abfd)->core_program
+ = elfcore_strndup (abfd, psinfo.pr_fname, sizeof (psinfo.pr_fname));
+
+ elf_tdata (abfd)->core_command
+ = elfcore_strndup (abfd, psinfo.pr_psargs, sizeof (psinfo.pr_psargs));
+
+ /* Note that for some reason, a spurious space is tacked
+ onto the end of the args in some (at least one anyway)
+ implementations, so strip it off if it exists. */
+
+ {
+ char* command = elf_tdata (abfd)->core_command;
+ int n = strlen (command);
+
+ if (0 < n && command[n - 1] == ' ')
+ command[n - 1] = '\0';
+ }
+
+ return true;
+}
+#endif /* defined (HAVE_PRPSINFO_T) || defined (HAVE_PSINFO_T) */
+
+
+#if defined (HAVE_PSTATUS_T)
+static boolean
+elfcore_grok_pstatus (abfd, note)
+ bfd* abfd;
+ Elf_Internal_Note* note;
+{
+ pstatus_t pstat;
+
+ if (note->descsz != sizeof (pstat))
+ return true;
+
+ memcpy (&pstat, note->descdata, sizeof (pstat));
+
+ elf_tdata (abfd)->core_pid = pstat.pr_pid;
+
+ /* Could grab some more details from the "representative"
+ lwpstatus_t in pstat.pr_lwp, but we'll catch it all in an
+ NT_LWPSTATUS note, presumably. */
+
+ return true;
+}
+#endif /* defined (HAVE_PSTATUS_T) */
+
+
+#if defined (HAVE_LWPSTATUS_T)
+static boolean
+elfcore_grok_lwpstatus (abfd, note)
+ bfd* abfd;
+ Elf_Internal_Note* note;
+{
+ lwpstatus_t lwpstat;
+ char buf[100];
+ char* name;
+ asection* sect;
+
+ if (note->descsz != sizeof (lwpstat))
+ return true;
+
+ memcpy (&lwpstat, note->descdata, sizeof (lwpstat));
+
+ elf_tdata (abfd)->core_lwpid = lwpstat.pr_lwpid;
+ elf_tdata (abfd)->core_signal = lwpstat.pr_cursig;
+
+ /* Make a ".reg/999" section. */
+
+ sprintf (buf, ".reg/%d", elfcore_make_pid (abfd));
+ name = bfd_alloc (abfd, strlen (buf) + 1);
+ if (name == NULL)
+ return false;
+ strcpy (name, buf);
+
+ sect = bfd_make_section (abfd, name);
+ if (sect == NULL)
+ return false;
+
+#if defined (HAVE_LWPSTATUS_T_PR_CONTEXT)
+ sect->_raw_size = sizeof (lwpstat.pr_context.uc_mcontext.gregs);
+ sect->filepos = note->descpos
+ + offsetof (lwpstatus_t, pr_context.uc_mcontext.gregs);
+#endif
+
+#if defined (HAVE_LWPSTATUS_T_PR_REG)
+ sect->_raw_size = sizeof (lwpstat.pr_reg);
+ sect->filepos = note->descpos + offsetof (lwpstatus_t, pr_reg);
+#endif
+
+ sect->flags = SEC_HAS_CONTENTS;
+ sect->alignment_power = 2;
+
+ if (!elfcore_maybe_make_sect (abfd, ".reg", sect))
+ return false;
+
+ /* Make a ".reg2/999" section */
+
+ sprintf (buf, ".reg2/%d", elfcore_make_pid (abfd));
+ name = bfd_alloc (abfd, strlen (buf) + 1);
+ if (name == NULL)
+ return false;
+ strcpy (name, buf);
+
+ sect = bfd_make_section (abfd, name);
+ if (sect == NULL)
+ return false;
+
+#if defined (HAVE_LWPSTATUS_T_PR_CONTEXT)
+ sect->_raw_size = sizeof (lwpstat.pr_context.uc_mcontext.fpregs);
+ sect->filepos = note->descpos
+ + offsetof (lwpstatus_t, pr_context.uc_mcontext.fpregs);
+#endif
+
+#if defined (HAVE_LWPSTATUS_T_PR_FPREG)
+ sect->_raw_size = sizeof (lwpstat.pr_fpreg);
+ sect->filepos = note->descpos + offsetof (lwpstatus_t, pr_fpreg);
+#endif
+
+ sect->flags = SEC_HAS_CONTENTS;
+ sect->alignment_power = 2;
+
+ if (!elfcore_maybe_make_sect (abfd, ".reg2", sect))
+ return false;
+
+ return true;
+}
+#endif /* defined (HAVE_LWPSTATUS_T) */
+
+
+
+static boolean
+elfcore_grok_note (abfd, note)
+ bfd* abfd;
+ Elf_Internal_Note* note;
+{
+ switch (note->type)
+ {
+ default:
+ return true;
+
+#if defined (HAVE_PRSTATUS_T)
+ case NT_PRSTATUS:
+ return elfcore_grok_prstatus (abfd, note);
+#endif
+
+#if defined (HAVE_PSTATUS_T)
+ case NT_PSTATUS:
+ return elfcore_grok_pstatus (abfd, note);
+#endif
+
+#if defined (HAVE_LWPSTATUS_T)
+ case NT_LWPSTATUS:
+ return elfcore_grok_lwpstatus (abfd, note);
+#endif
+
+ case NT_FPREGSET: /* FIXME: rename to NT_PRFPREG */
+ return elfcore_grok_prfpreg (abfd, note);
+
+#if defined (HAVE_PRPSINFO_T) || defined (HAVE_PSINFO_T)
+ case NT_PRPSINFO:
+ case NT_PSINFO:
+ return elfcore_grok_psinfo (abfd, note);
+#endif
+ }
+}
+
+
+static boolean
+elfcore_read_notes (abfd, offset, size)
+ bfd* abfd;
+ bfd_vma offset;
+ bfd_vma size;
+{
+ char* buf;
+ char* p;
+
+ if (size <= 0)
+ return true;
+
+ if (bfd_seek (abfd, offset, SEEK_SET) == -1)
+ return false;
+
+ buf = bfd_malloc ((size_t) size);
+ if (buf == NULL)
+ return false;
+
+ if (bfd_read (buf, size, 1, abfd) != size)
+ {
+ error:
+ free (buf);
+ return false;
+ }
+
+ p = buf;
+ while (p < buf + size)
+ {
+ /* FIXME: bad alignment assumption. */
+ Elf_External_Note* xnp = (Elf_External_Note*) p;
+ Elf_Internal_Note in;
+
+ in.type = bfd_h_get_32 (abfd, (bfd_byte *) xnp->type);
+
+ in.namesz = bfd_h_get_32 (abfd, (bfd_byte *) xnp->namesz);
+ in.namedata = xnp->name;
+
+ in.descsz = bfd_h_get_32 (abfd, (bfd_byte *) xnp->descsz);
+ in.descdata = in.namedata + BFD_ALIGN (in.namesz, 4);
+ in.descpos = offset + (in.descdata - buf);
+
+ if (! elfcore_grok_note (abfd, &in))
+ goto error;
+
+ p = in.descdata + BFD_ALIGN (in.descsz, 4);
+ }
+
+ free (buf);
+ return true;
+}
+
+
+
+boolean
+_bfd_elfcore_section_from_phdr (abfd, phdr, sec_num)
+ bfd* abfd;
+ Elf_Internal_Phdr* phdr;
+ int sec_num;
+{
+ if (! bfd_section_from_phdr (abfd, phdr, sec_num))
+ return false;
+
+ if (phdr->p_type == PT_NOTE
+ && ! elfcore_read_notes (abfd, phdr->p_offset, phdr->p_filesz))
+ return false;
+
+ return true;
+}
+