/* BFD backend for SunOS binaries. Copyright (C) 1990-1991 Free Software Foundation, Inc. Written by 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #define ARCH 32 #define TARGETNAME "a.out-sunos-big" #define MY(OP) CAT(sunos_big_,OP) #include "bfd.h" /* Static routines defined in this file. */ struct external_nlist; static boolean sunos_read_dynamic_info PARAMS ((bfd *)); static bfd_size_type MY(read_dynamic_symbols) PARAMS ((bfd *, struct external_nlist **, char **, bfd_size_type *)); static bfd_size_type MY(read_dynamic_relocs) PARAMS ((bfd *, PTR *)); #define MY_read_dynamic_symbols MY(read_dynamic_symbols) #define MY_read_dynamic_relocs MY(read_dynamic_relocs) /* Include the usual a.out support. */ #include "aoutf1.h" /* SunOS shared library support. We store a pointer to this structure in obj_aout_dynamic_info (abfd). */ struct sunos_dynamic_info { /* Whether we found any dynamic information. */ boolean valid; /* Dynamic information. */ struct internal_sun4_dynamic_link dyninfo; /* Number of dynamic symbols. */ bfd_size_type dynsym_count; /* Read in nlists for dynamic symbols. */ struct external_nlist *dynsym; /* Read in dynamic string table. */ char *dynstr; /* Number of dynamic relocs. */ bfd_size_type dynrel_count; /* Read in dynamic relocs. This may be reloc_std_external or reloc_ext_external. */ PTR dynrel; }; /* Read in the basic dynamic information. This locates the __DYNAMIC structure and uses it to find the dynamic_link structure. It creates and saves a sunos_dynamic_info structure. If it can't find __DYNAMIC, it sets the valid field of the sunos_dynamic_info structure to false to avoid doing this work again. */ static boolean sunos_read_dynamic_info (abfd) bfd *abfd; { struct sunos_dynamic_info *info; struct external_nlist dynsym; char buf[sizeof "__DYNAMIC"]; asection *dynsec; file_ptr dynoff; struct external_sun4_dynamic dyninfo; unsigned long dynver; struct external_sun4_dynamic_link linkinfo; if (obj_aout_dynamic_info (abfd) != (PTR) NULL) return true; info = ((struct sunos_dynamic_info *) bfd_zalloc (abfd, sizeof (struct sunos_dynamic_info))); if (!info) { bfd_set_error (bfd_error_no_memory); return false; } info->valid = false; info->dynsym = NULL; info->dynstr = NULL; info->dynrel = NULL; obj_aout_dynamic_info (abfd) = (PTR) info; /* This code used to look for the __DYNAMIC symbol to locate the dynamic linking information. However this inhibits recovering the dynamic symbols from a stripped object file, so blindly assume that the dynamic linking information is located at the start of the data section. We could verify this assumption later by looking through the dynamic symbols for the __DYNAMIC symbol. */ if ((abfd->flags & DYNAMIC) == 0) return true; if (! bfd_get_section_contents (abfd, obj_datasec (abfd), (PTR) &dyninfo, (file_ptr) 0, sizeof dyninfo)) return true; dynver = GET_WORD (abfd, dyninfo.ld_version); if (dynver != 2 && dynver != 3) return true; dynoff = GET_WORD (abfd, dyninfo.ld); /* dynoff is a virtual address. It is probably always in the .data section, but this code should work even if it moves. */ if (dynoff < bfd_get_section_vma (abfd, obj_datasec (abfd))) dynsec = obj_textsec (abfd); else dynsec = obj_datasec (abfd); dynoff -= bfd_get_section_vma (abfd, dynsec); if (dynoff < 0 || dynoff > bfd_section_size (abfd, dynsec)) return true; /* This executable appears to be dynamically linked in a way that we can understand. */ if (! bfd_get_section_contents (abfd, dynsec, (PTR) &linkinfo, dynoff, (bfd_size_type) sizeof linkinfo)) return true; /* Swap in the dynamic link information. */ info->dyninfo.ld_loaded = GET_WORD (abfd, linkinfo.ld_loaded); info->dyninfo.ld_need = GET_WORD (abfd, linkinfo.ld_need); info->dyninfo.ld_rules = GET_WORD (abfd, linkinfo.ld_rules); info->dyninfo.ld_got = GET_WORD (abfd, linkinfo.ld_got); info->dyninfo.ld_plt = GET_WORD (abfd, linkinfo.ld_plt); info->dyninfo.ld_rel = GET_WORD (abfd, linkinfo.ld_rel); info->dyninfo.ld_hash = GET_WORD (abfd, linkinfo.ld_hash); info->dyninfo.ld_stab = GET_WORD (abfd, linkinfo.ld_stab); info->dyninfo.ld_stab_hash = GET_WORD (abfd, linkinfo.ld_stab_hash); info->dyninfo.ld_buckets = GET_WORD (abfd, linkinfo.ld_buckets); info->dyninfo.ld_symbols = GET_WORD (abfd, linkinfo.ld_symbols); info->dyninfo.ld_symb_size = GET_WORD (abfd, linkinfo.ld_symb_size); info->dyninfo.ld_text = GET_WORD (abfd, linkinfo.ld_text); info->dyninfo.ld_plt_sz = GET_WORD (abfd, linkinfo.ld_plt_sz); /* The only way to get the size of the symbol information appears to be to determine the distance between it and the string table. */ info->dynsym_count = ((info->dyninfo.ld_symbols - info->dyninfo.ld_stab) / EXTERNAL_NLIST_SIZE); BFD_ASSERT (info->dynsym_count * EXTERNAL_NLIST_SIZE == info->dyninfo.ld_symbols - info->dyninfo.ld_stab); /* Similarly, the relocs end at the hash table. */ info->dynrel_count = ((info->dyninfo.ld_hash - info->dyninfo.ld_rel) / obj_reloc_entry_size (abfd)); BFD_ASSERT (info->dynrel_count * obj_reloc_entry_size (abfd) == info->dyninfo.ld_hash - info->dyninfo.ld_rel); info->valid = true; return true; } /* Read in the dynamic symbols. */ static bfd_size_type MY(read_dynamic_symbols) (abfd, syms, strs, strsize) bfd *abfd; struct external_nlist **syms; char **strs; bfd_size_type *strsize; { struct sunos_dynamic_info *info; if (obj_aout_dynamic_info (abfd) == (PTR) NULL) { if (! sunos_read_dynamic_info (abfd)) return (bfd_size_type) -1; } info = (struct sunos_dynamic_info *) obj_aout_dynamic_info (abfd); if (! info->valid || info->dynsym_count == 0) return 0; if (info->dynsym == (struct external_nlist *) NULL) { info->dynsym = ((struct external_nlist *) bfd_alloc (abfd, (info->dynsym_count * EXTERNAL_NLIST_SIZE))); info->dynstr = (char *) bfd_alloc (abfd, info->dyninfo.ld_symb_size); if (!info->dynsym || !info->dynstr) { bfd_set_error (bfd_error_no_memory); return 0; } if (bfd_seek (abfd, info->dyninfo.ld_stab, SEEK_SET) != 0 || (bfd_read ((PTR) info->dynsym, info->dynsym_count, EXTERNAL_NLIST_SIZE, abfd) != info->dynsym_count * EXTERNAL_NLIST_SIZE) || bfd_seek (abfd, info->dyninfo.ld_symbols, SEEK_SET) != 0 || (bfd_read ((PTR) info->dynstr, 1, info->dyninfo.ld_symb_size, abfd) != info->dyninfo.ld_symb_size)) return (bfd_size_type) -1; } *syms = info->dynsym; *strs = info->dynstr; *strsize = info->dyninfo.ld_symb_size; #ifdef CHECK_DYNAMIC_HASH /* Check my understanding of the dynamic hash table by making sure that each symbol can be located in the hash table. */ { bfd_size_type table_size; bfd_byte *table; bfd_size_type i; if (info->dyninfo.ld_buckets > info->dynsym_count) abort (); table_size = info->dyninfo.ld_stab - info->dyninfo.ld_hash; table = (bfd_byte *) malloc (table_size); if (table == NULL) abort (); if (bfd_seek (abfd, info->dyninfo.ld_hash, SEEK_SET) != 0 || bfd_read ((PTR) table, 1, table_size, abfd) != table_size) abort (); for (i = 0; i < info->dynsym_count; i++) { unsigned char *name; unsigned long hash; name = ((unsigned char *) info->dynstr + GET_WORD (abfd, info->dynsym[i].e_strx)); hash = 0; while (*name != '\0') hash = (hash << 1) + *name++; hash &= 0x7fffffff; hash %= info->dyninfo.ld_buckets; while (GET_WORD (abfd, table + 8 * hash) != i) { hash = GET_WORD (abfd, table + 8 * hash + 4); if (hash == 0 || hash >= table_size / 8) abort (); } } free (table); } #endif /* CHECK_DYNAMIC_HASH */ return info->dynsym_count; } /* Read in the dynamic relocs for a section. */ static bfd_size_type MY(read_dynamic_relocs) (abfd, relocs) bfd *abfd; PTR *relocs; { struct sunos_dynamic_info *info; if (obj_aout_dynamic_info (abfd) == (PTR) NULL) { if (! sunos_read_dynamic_info (abfd)) return (bfd_size_type) -1; } info = (struct sunos_dynamic_info *) obj_aout_dynamic_info (abfd); if (! info->valid || info->dynrel_count == 0) return 0; if (info->dynrel == NULL) { info->dynrel = (PTR) bfd_alloc (abfd, (info->dynrel_count * obj_reloc_entry_size (abfd))); if (!info->dynrel) { bfd_set_error (bfd_error_no_memory); return (bfd_size_type) -1; } if (bfd_seek (abfd, info->dyninfo.ld_rel, SEEK_SET) != 0 || (bfd_read ((PTR) info->dynrel, info->dynrel_count, obj_reloc_entry_size (abfd), abfd) != info->dynrel_count * obj_reloc_entry_size (abfd))) return (bfd_size_type) -1; } *relocs = info->dynrel; return info->dynrel_count; }