| /* POWER/PowerPC XCOFF linker support. |
| Copyright 1995, 1996, 1997, 1998, 1999, 2000 |
| Free Software Foundation, Inc. |
| Written by Ian Lance Taylor <ian@cygnus.com>, 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. */ |
| |
| #include "bfd.h" |
| #include "sysdep.h" |
| #include "bfdlink.h" |
| #include "libbfd.h" |
| #include "coff/internal.h" |
| #include "libcoff.h" |
| |
| /* This file holds the XCOFF linker code. */ |
| |
| #define STRING_SIZE_SIZE (4) |
| |
| /* Get the XCOFF hash table entries for a BFD. */ |
| #define obj_xcoff_sym_hashes(bfd) \ |
| ((struct xcoff_link_hash_entry **) obj_coff_sym_hashes (bfd)) |
| |
| /* XCOFF relocation types. These probably belong in a header file |
| somewhere. The relocations are described in the function |
| _bfd_ppc_xcoff_relocate_section in this file. */ |
| |
| #define R_POS (0x00) |
| #define R_NEG (0x01) |
| #define R_REL (0x02) |
| #define R_TOC (0x03) |
| #define R_RTB (0x04) |
| #define R_GL (0x05) |
| #define R_TCL (0x06) |
| #define R_BA (0x08) |
| #define R_BR (0x0a) |
| #define R_RL (0x0c) |
| #define R_RLA (0x0d) |
| #define R_REF (0x0f) |
| #define R_TRL (0x12) |
| #define R_TRLA (0x13) |
| #define R_RRTBI (0x14) |
| #define R_RRTBA (0x15) |
| #define R_CAI (0x16) |
| #define R_CREL (0x17) |
| #define R_RBA (0x18) |
| #define R_RBAC (0x19) |
| #define R_RBR (0x1a) |
| #define R_RBRC (0x1b) |
| |
| /* The first word of global linkage code. This must be modified by |
| filling in the correct TOC offset. */ |
| |
| #define XCOFF_GLINK_FIRST (0x81820000) /* lwz r12,0(r2) */ |
| |
| /* The remaining words of global linkage code. */ |
| |
| static unsigned long xcoff_glink_code[] = { |
| 0x90410014, /* stw r2,20(r1) */ |
| 0x800c0000, /* lwz r0,0(r12) */ |
| 0x804c0004, /* lwz r2,4(r12) */ |
| 0x7c0903a6, /* mtctr r0 */ |
| 0x4e800420, /* bctr */ |
| 0x0, /* start of traceback table */ |
| 0x000c8000, /* traceback table */ |
| 0x0 /* traceback table */ |
| }; |
| |
| #define XCOFF_GLINK_SIZE \ |
| (((sizeof xcoff_glink_code / sizeof xcoff_glink_code[0]) * 4) + 4) |
| |
| /* We reuse the SEC_ROM flag as a mark flag for garbage collection. |
| This flag will only be used on input sections. */ |
| |
| #define SEC_MARK (SEC_ROM) |
| |
| /* The ldhdr structure. This appears at the start of the .loader |
| section. */ |
| |
| struct internal_ldhdr { |
| /* The version number: currently always 1. */ |
| unsigned long l_version; |
| /* The number of symbol table entries. */ |
| bfd_size_type l_nsyms; |
| /* The number of relocation table entries. */ |
| bfd_size_type l_nreloc; |
| /* The length of the import file string table. */ |
| bfd_size_type l_istlen; |
| /* The number of import files. */ |
| bfd_size_type l_nimpid; |
| /* The offset from the start of the .loader section to the first |
| entry in the import file table. */ |
| bfd_size_type l_impoff; |
| /* The length of the string table. */ |
| bfd_size_type l_stlen; |
| /* The offset from the start of the .loader section to the first |
| entry in the string table. */ |
| bfd_size_type l_stoff; |
| }; |
| |
| struct external_ldhdr { |
| bfd_byte l_version[4]; |
| bfd_byte l_nsyms[4]; |
| bfd_byte l_nreloc[4]; |
| bfd_byte l_istlen[4]; |
| bfd_byte l_nimpid[4]; |
| bfd_byte l_impoff[4]; |
| bfd_byte l_stlen[4]; |
| bfd_byte l_stoff[4]; |
| }; |
| |
| #define LDHDRSZ (8 * 4) |
| |
| /* The ldsym structure. This is used to represent a symbol in the |
| .loader section. */ |
| |
| struct internal_ldsym { |
| union { |
| /* The symbol name if <= SYMNMLEN characters. */ |
| char _l_name[SYMNMLEN]; |
| struct { |
| /* Zero if the symbol name is more than SYMNMLEN characters. */ |
| long _l_zeroes; |
| /* The offset in the string table if the symbol name is more |
| than SYMNMLEN characters. */ |
| long _l_offset; |
| } _l_l; |
| } _l; |
| /* The symbol value. */ |
| bfd_vma l_value; |
| /* The symbol section number. */ |
| short l_scnum; |
| /* The symbol type and flags. */ |
| char l_smtype; |
| /* The symbol storage class. */ |
| char l_smclas; |
| /* The import file ID. */ |
| bfd_size_type l_ifile; |
| /* Offset to the parameter type check string. */ |
| bfd_size_type l_parm; |
| }; |
| |
| struct external_ldsym { |
| union { |
| bfd_byte _l_name[SYMNMLEN]; |
| struct { |
| bfd_byte _l_zeroes[4]; |
| bfd_byte _l_offset[4]; |
| } _l_l; |
| } _l; |
| bfd_byte l_value[4]; |
| bfd_byte l_scnum[2]; |
| bfd_byte l_smtype[1]; |
| bfd_byte l_smclas[1]; |
| bfd_byte l_ifile[4]; |
| bfd_byte l_parm[4]; |
| }; |
| |
| #define LDSYMSZ (8 + 3 * 4 + 2 + 2) |
| |
| /* These flags are for the l_smtype field (the lower three bits are an |
| XTY_* value). */ |
| |
| /* Imported symbol. */ |
| #define L_IMPORT (0x40) |
| /* Entry point. */ |
| #define L_ENTRY (0x20) |
| /* Exported symbol. */ |
| #define L_EXPORT (0x10) |
| |
| /* The ldrel structure. This is used to represent a reloc in the |
| .loader section. */ |
| |
| struct internal_ldrel { |
| /* The reloc address. */ |
| bfd_vma l_vaddr; |
| /* The symbol table index in the .loader section symbol table. */ |
| bfd_size_type l_symndx; |
| /* The relocation type and size. */ |
| short l_rtype; |
| /* The section number this relocation applies to. */ |
| short l_rsecnm; |
| }; |
| |
| struct external_ldrel { |
| bfd_byte l_vaddr[4]; |
| bfd_byte l_symndx[4]; |
| bfd_byte l_rtype[2]; |
| bfd_byte l_rsecnm[2]; |
| }; |
| |
| #define LDRELSZ (2 * 4 + 2 * 2) |
| |
| /* The list of import files. */ |
| |
| struct xcoff_import_file { |
| /* The next entry in the list. */ |
| struct xcoff_import_file *next; |
| /* The path. */ |
| const char *path; |
| /* The file name. */ |
| const char *file; |
| /* The member name. */ |
| const char *member; |
| }; |
| |
| /* An entry in the XCOFF linker hash table. */ |
| |
| struct xcoff_link_hash_entry { |
| struct bfd_link_hash_entry root; |
| |
| /* Symbol index in output file. Set to -1 initially. Set to -2 if |
| there is a reloc against this symbol. */ |
| long indx; |
| |
| /* If we have created a TOC entry for this symbol, this is the .tc |
| section which holds it. */ |
| asection *toc_section; |
| |
| union { |
| /* If we have created a TOC entry (the XCOFF_SET_TOC flag is set), |
| this is the offset in toc_section. */ |
| bfd_vma toc_offset; |
| /* If the TOC entry comes from an input file, this is set to the |
| symbol index of the C_HIDEXT XMC_TC or XMC_TD symbol. */ |
| long toc_indx; |
| } u; |
| |
| /* If this symbol is a function entry point which is called, this |
| field holds a pointer to the function descriptor. If this symbol |
| is a function descriptor, this field holds a pointer to the |
| function entry point. */ |
| struct xcoff_link_hash_entry *descriptor; |
| |
| /* The .loader symbol table entry, if there is one. */ |
| struct internal_ldsym *ldsym; |
| |
| /* If XCOFF_BUILT_LDSYM is set, this is the .loader symbol table |
| index. If XCOFF_BUILD_LDSYM is clear, and XCOFF_IMPORT is set, |
| this is the l_ifile value. */ |
| long ldindx; |
| |
| /* Some linker flags. */ |
| unsigned short flags; |
| /* Symbol is referenced by a regular object. */ |
| #define XCOFF_REF_REGULAR (01) |
| /* Symbol is defined by a regular object. */ |
| #define XCOFF_DEF_REGULAR (02) |
| /* Symbol is defined by a dynamic object. */ |
| #define XCOFF_DEF_DYNAMIC (04) |
| /* Symbol is used in a reloc being copied into the .loader section. */ |
| #define XCOFF_LDREL (010) |
| /* Symbol is the entry point. */ |
| #define XCOFF_ENTRY (020) |
| /* Symbol is called; this is, it appears in a R_BR reloc. */ |
| #define XCOFF_CALLED (040) |
| /* Symbol needs the TOC entry filled in. */ |
| #define XCOFF_SET_TOC (0100) |
| /* Symbol is explicitly imported. */ |
| #define XCOFF_IMPORT (0200) |
| /* Symbol is explicitly exported. */ |
| #define XCOFF_EXPORT (0400) |
| /* Symbol has been processed by xcoff_build_ldsyms. */ |
| #define XCOFF_BUILT_LDSYM (01000) |
| /* Symbol is mentioned by a section which was not garbage collected. */ |
| #define XCOFF_MARK (02000) |
| /* Symbol size is recorded in size_list list from hash table. */ |
| #define XCOFF_HAS_SIZE (04000) |
| /* Symbol is a function descriptor. */ |
| #define XCOFF_DESCRIPTOR (010000) |
| /* Multiple definitions have been for the symbol. */ |
| #define XCOFF_MULTIPLY_DEFINED (020000) |
| |
| /* The storage mapping class. */ |
| unsigned char smclas; |
| }; |
| |
| /* The XCOFF linker hash table. */ |
| |
| struct xcoff_link_hash_table { |
| struct bfd_link_hash_table root; |
| |
| /* The .debug string hash table. We need to compute this while |
| reading the input files, so that we know how large the .debug |
| section will be before we assign section positions. */ |
| struct bfd_strtab_hash *debug_strtab; |
| |
| /* The .debug section we will use for the final output. */ |
| asection *debug_section; |
| |
| /* The .loader section we will use for the final output. */ |
| asection *loader_section; |
| |
| /* A count of non TOC relative relocs which will need to be |
| allocated in the .loader section. */ |
| size_t ldrel_count; |
| |
| /* The .loader section header. */ |
| struct internal_ldhdr ldhdr; |
| |
| /* The .gl section we use to hold global linkage code. */ |
| asection *linkage_section; |
| |
| /* The .tc section we use to hold toc entries we build for global |
| linkage code. */ |
| asection *toc_section; |
| |
| /* The .ds section we use to hold function descriptors which we |
| create for exported symbols. */ |
| asection *descriptor_section; |
| |
| /* The list of import files. */ |
| struct xcoff_import_file *imports; |
| |
| /* Required alignment of sections within the output file. */ |
| unsigned long file_align; |
| |
| /* Whether the .text section must be read-only. */ |
| boolean textro; |
| |
| /* Whether garbage collection was done. */ |
| boolean gc; |
| |
| /* A linked list of symbols for which we have size information. */ |
| struct xcoff_link_size_list { |
| struct xcoff_link_size_list *next; |
| struct xcoff_link_hash_entry *h; |
| bfd_size_type size; |
| } *size_list; |
| |
| /* Magic sections: _text, _etext, _data, _edata, _end, end. */ |
| asection *special_sections[6]; |
| }; |
| |
| /* Information we keep for each section in the output file during the |
| final link phase. */ |
| |
| struct xcoff_link_section_info { |
| /* The relocs to be output. */ |
| struct internal_reloc *relocs; |
| /* For each reloc against a global symbol whose index was not known |
| when the reloc was handled, the global hash table entry. */ |
| struct xcoff_link_hash_entry **rel_hashes; |
| /* If there is a TOC relative reloc against a global symbol, and the |
| index of the TOC symbol is not known when the reloc was handled, |
| an entry is added to this linked list. This is not an array, |
| like rel_hashes, because this case is quite uncommon. */ |
| struct xcoff_toc_rel_hash { |
| struct xcoff_toc_rel_hash *next; |
| struct xcoff_link_hash_entry *h; |
| struct internal_reloc *rel; |
| } *toc_rel_hashes; |
| }; |
| |
| /* Information that we pass around while doing the final link step. */ |
| |
| struct xcoff_final_link_info { |
| /* General link information. */ |
| struct bfd_link_info *info; |
| /* Output BFD. */ |
| bfd *output_bfd; |
| /* Hash table for long symbol names. */ |
| struct bfd_strtab_hash *strtab; |
| /* Array of information kept for each output section, indexed by the |
| target_index field. */ |
| struct xcoff_link_section_info *section_info; |
| /* Symbol index of last C_FILE symbol (-1 if none). */ |
| long last_file_index; |
| /* Contents of last C_FILE symbol. */ |
| struct internal_syment last_file; |
| /* Symbol index of TOC symbol. */ |
| long toc_symindx; |
| /* Start of .loader symbols. */ |
| struct external_ldsym *ldsym; |
| /* Next .loader reloc to swap out. */ |
| struct external_ldrel *ldrel; |
| /* File position of start of line numbers. */ |
| file_ptr line_filepos; |
| /* Buffer large enough to hold swapped symbols of any input file. */ |
| struct internal_syment *internal_syms; |
| /* Buffer large enough to hold output indices of symbols of any |
| input file. */ |
| long *sym_indices; |
| /* Buffer large enough to hold output symbols for any input file. */ |
| bfd_byte *outsyms; |
| /* Buffer large enough to hold external line numbers for any input |
| section. */ |
| bfd_byte *linenos; |
| /* Buffer large enough to hold any input section. */ |
| bfd_byte *contents; |
| /* Buffer large enough to hold external relocs of any input section. */ |
| bfd_byte *external_relocs; |
| }; |
| |
| static void xcoff_swap_ldhdr_in |
| PARAMS ((bfd *, const struct external_ldhdr *, struct internal_ldhdr *)); |
| static void xcoff_swap_ldhdr_out |
| PARAMS ((bfd *, const struct internal_ldhdr *, struct external_ldhdr *)); |
| static void xcoff_swap_ldsym_in |
| PARAMS ((bfd *, const struct external_ldsym *, struct internal_ldsym *)); |
| static void xcoff_swap_ldsym_out |
| PARAMS ((bfd *, const struct internal_ldsym *, struct external_ldsym *)); |
| static void xcoff_swap_ldrel_in |
| PARAMS ((bfd *, const struct external_ldrel *, struct internal_ldrel *)); |
| static void xcoff_swap_ldrel_out |
| PARAMS ((bfd *, const struct internal_ldrel *, struct external_ldrel *)); |
| static struct bfd_hash_entry *xcoff_link_hash_newfunc |
| PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *)); |
| static boolean xcoff_get_section_contents PARAMS ((bfd *, asection *)); |
| static struct internal_reloc *xcoff_read_internal_relocs |
| PARAMS ((bfd *, asection *, boolean, bfd_byte *, boolean, |
| struct internal_reloc *)); |
| static boolean xcoff_link_add_object_symbols |
| PARAMS ((bfd *, struct bfd_link_info *)); |
| static boolean xcoff_link_check_archive_element |
| PARAMS ((bfd *, struct bfd_link_info *, boolean *)); |
| static boolean xcoff_link_check_ar_symbols |
| PARAMS ((bfd *, struct bfd_link_info *, boolean *)); |
| static boolean xcoff_link_check_dynamic_ar_symbols |
| PARAMS ((bfd *, struct bfd_link_info *, boolean *)); |
| static bfd_size_type xcoff_find_reloc |
| PARAMS ((struct internal_reloc *, bfd_size_type, bfd_vma)); |
| static boolean xcoff_link_add_symbols PARAMS ((bfd *, struct bfd_link_info *)); |
| static boolean xcoff_link_add_dynamic_symbols |
| PARAMS ((bfd *, struct bfd_link_info *)); |
| static boolean xcoff_mark_symbol |
| PARAMS ((struct bfd_link_info *, struct xcoff_link_hash_entry *)); |
| static boolean xcoff_mark PARAMS ((struct bfd_link_info *, asection *)); |
| static void xcoff_sweep PARAMS ((struct bfd_link_info *)); |
| static boolean xcoff_build_ldsyms |
| PARAMS ((struct xcoff_link_hash_entry *, PTR)); |
| static boolean xcoff_link_input_bfd |
| PARAMS ((struct xcoff_final_link_info *, bfd *)); |
| static boolean xcoff_write_global_symbol |
| PARAMS ((struct xcoff_link_hash_entry *, PTR)); |
| static boolean xcoff_reloc_link_order |
| PARAMS ((bfd *, struct xcoff_final_link_info *, asection *, |
| struct bfd_link_order *)); |
| static int xcoff_sort_relocs PARAMS ((const PTR, const PTR)); |
| |
| /* Routines to swap information in the XCOFF .loader section. If we |
| ever need to write an XCOFF loader, this stuff will need to be |
| moved to another file shared by the linker (which XCOFF calls the |
| ``binder'') and the loader. */ |
| |
| /* Swap in the ldhdr structure. */ |
| |
| static void |
| xcoff_swap_ldhdr_in (abfd, src, dst) |
| bfd *abfd; |
| const struct external_ldhdr *src; |
| struct internal_ldhdr *dst; |
| { |
| dst->l_version = bfd_get_32 (abfd, src->l_version); |
| dst->l_nsyms = bfd_get_32 (abfd, src->l_nsyms); |
| dst->l_nreloc = bfd_get_32 (abfd, src->l_nreloc); |
| dst->l_istlen = bfd_get_32 (abfd, src->l_istlen); |
| dst->l_nimpid = bfd_get_32 (abfd, src->l_nimpid); |
| dst->l_impoff = bfd_get_32 (abfd, src->l_impoff); |
| dst->l_stlen = bfd_get_32 (abfd, src->l_stlen); |
| dst->l_stoff = bfd_get_32 (abfd, src->l_stoff); |
| } |
| |
| /* Swap out the ldhdr structure. */ |
| |
| static void |
| xcoff_swap_ldhdr_out (abfd, src, dst) |
| bfd *abfd; |
| const struct internal_ldhdr *src; |
| struct external_ldhdr *dst; |
| { |
| bfd_put_32 (abfd, src->l_version, dst->l_version); |
| bfd_put_32 (abfd, src->l_nsyms, dst->l_nsyms); |
| bfd_put_32 (abfd, src->l_nreloc, dst->l_nreloc); |
| bfd_put_32 (abfd, src->l_istlen, dst->l_istlen); |
| bfd_put_32 (abfd, src->l_nimpid, dst->l_nimpid); |
| bfd_put_32 (abfd, src->l_impoff, dst->l_impoff); |
| bfd_put_32 (abfd, src->l_stlen, dst->l_stlen); |
| bfd_put_32 (abfd, src->l_stoff, dst->l_stoff); |
| } |
| |
| /* Swap in the ldsym structure. */ |
| |
| static void |
| xcoff_swap_ldsym_in (abfd, src, dst) |
| bfd *abfd; |
| const struct external_ldsym *src; |
| struct internal_ldsym *dst; |
| { |
| if (bfd_get_32 (abfd, src->_l._l_l._l_zeroes) != 0) |
| memcpy (dst->_l._l_name, src->_l._l_name, SYMNMLEN); |
| else |
| { |
| dst->_l._l_l._l_zeroes = 0; |
| dst->_l._l_l._l_offset = bfd_get_32 (abfd, src->_l._l_l._l_offset); |
| } |
| dst->l_value = bfd_get_32 (abfd, src->l_value); |
| dst->l_scnum = bfd_get_16 (abfd, src->l_scnum); |
| dst->l_smtype = bfd_get_8 (abfd, src->l_smtype); |
| dst->l_smclas = bfd_get_8 (abfd, src->l_smclas); |
| dst->l_ifile = bfd_get_32 (abfd, src->l_ifile); |
| dst->l_parm = bfd_get_32 (abfd, src->l_parm); |
| } |
| |
| /* Swap out the ldsym structure. */ |
| |
| static void |
| xcoff_swap_ldsym_out (abfd, src, dst) |
| bfd *abfd; |
| const struct internal_ldsym *src; |
| struct external_ldsym *dst; |
| { |
| if (src->_l._l_l._l_zeroes != 0) |
| memcpy (dst->_l._l_name, src->_l._l_name, SYMNMLEN); |
| else |
| { |
| bfd_put_32 (abfd, 0, dst->_l._l_l._l_zeroes); |
| bfd_put_32 (abfd, src->_l._l_l._l_offset, dst->_l._l_l._l_offset); |
| } |
| bfd_put_32 (abfd, src->l_value, dst->l_value); |
| bfd_put_16 (abfd, src->l_scnum, dst->l_scnum); |
| bfd_put_8 (abfd, src->l_smtype, dst->l_smtype); |
| bfd_put_8 (abfd, src->l_smclas, dst->l_smclas); |
| bfd_put_32 (abfd, src->l_ifile, dst->l_ifile); |
| bfd_put_32 (abfd, src->l_parm, dst->l_parm); |
| } |
| |
| /* Swap in the ldrel structure. */ |
| |
| static void |
| xcoff_swap_ldrel_in (abfd, src, dst) |
| bfd *abfd; |
| const struct external_ldrel *src; |
| struct internal_ldrel *dst; |
| { |
| dst->l_vaddr = bfd_get_32 (abfd, src->l_vaddr); |
| dst->l_symndx = bfd_get_32 (abfd, src->l_symndx); |
| dst->l_rtype = bfd_get_16 (abfd, src->l_rtype); |
| dst->l_rsecnm = bfd_get_16 (abfd, src->l_rsecnm); |
| } |
| |
| /* Swap out the ldrel structure. */ |
| |
| static void |
| xcoff_swap_ldrel_out (abfd, src, dst) |
| bfd *abfd; |
| const struct internal_ldrel *src; |
| struct external_ldrel *dst; |
| { |
| bfd_put_32 (abfd, src->l_vaddr, dst->l_vaddr); |
| bfd_put_32 (abfd, src->l_symndx, dst->l_symndx); |
| bfd_put_16 (abfd, src->l_rtype, dst->l_rtype); |
| bfd_put_16 (abfd, src->l_rsecnm, dst->l_rsecnm); |
| } |
| |
| /* Routines to read XCOFF dynamic information. This don't really |
| belong here, but we already have the ldsym manipulation routines |
| here. */ |
| |
| /* Read the contents of a section. */ |
| |
| static boolean |
| xcoff_get_section_contents (abfd, sec) |
| bfd *abfd; |
| asection *sec; |
| { |
| if (coff_section_data (abfd, sec) == NULL) |
| { |
| sec->used_by_bfd = bfd_zalloc (abfd, |
| sizeof (struct coff_section_tdata)); |
| if (sec->used_by_bfd == NULL) |
| return false; |
| } |
| |
| if (coff_section_data (abfd, sec)->contents == NULL) |
| { |
| coff_section_data (abfd, sec)->contents = |
| (bfd_byte *) bfd_malloc (sec->_raw_size); |
| if (coff_section_data (abfd, sec)->contents == NULL) |
| return false; |
| |
| if (! bfd_get_section_contents (abfd, sec, |
| coff_section_data (abfd, sec)->contents, |
| (file_ptr) 0, sec->_raw_size)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* Get the size required to hold the dynamic symbols. */ |
| |
| long |
| _bfd_xcoff_get_dynamic_symtab_upper_bound (abfd) |
| bfd *abfd; |
| { |
| asection *lsec; |
| bfd_byte *contents; |
| struct internal_ldhdr ldhdr; |
| |
| if ((abfd->flags & DYNAMIC) == 0) |
| { |
| bfd_set_error (bfd_error_invalid_operation); |
| return -1; |
| } |
| |
| lsec = bfd_get_section_by_name (abfd, ".loader"); |
| if (lsec == NULL) |
| { |
| bfd_set_error (bfd_error_no_symbols); |
| return -1; |
| } |
| |
| if (! xcoff_get_section_contents (abfd, lsec)) |
| return -1; |
| contents = coff_section_data (abfd, lsec)->contents; |
| |
| xcoff_swap_ldhdr_in (abfd, (struct external_ldhdr *) contents, &ldhdr); |
| |
| return (ldhdr.l_nsyms + 1) * sizeof (asymbol *); |
| } |
| |
| /* Get the dynamic symbols. */ |
| |
| long |
| _bfd_xcoff_canonicalize_dynamic_symtab (abfd, psyms) |
| bfd *abfd; |
| asymbol **psyms; |
| { |
| asection *lsec; |
| bfd_byte *contents; |
| struct internal_ldhdr ldhdr; |
| const char *strings; |
| struct external_ldsym *elsym, *elsymend; |
| coff_symbol_type *symbuf; |
| |
| if ((abfd->flags & DYNAMIC) == 0) |
| { |
| bfd_set_error (bfd_error_invalid_operation); |
| return -1; |
| } |
| |
| lsec = bfd_get_section_by_name (abfd, ".loader"); |
| if (lsec == NULL) |
| { |
| bfd_set_error (bfd_error_no_symbols); |
| return -1; |
| } |
| |
| if (! xcoff_get_section_contents (abfd, lsec)) |
| return -1; |
| contents = coff_section_data (abfd, lsec)->contents; |
| |
| coff_section_data (abfd, lsec)->keep_contents = true; |
| |
| xcoff_swap_ldhdr_in (abfd, (struct external_ldhdr *) contents, &ldhdr); |
| |
| strings = (char *) contents + ldhdr.l_stoff; |
| |
| symbuf = ((coff_symbol_type *) |
| bfd_zalloc (abfd, ldhdr.l_nsyms * sizeof (coff_symbol_type))); |
| if (symbuf == NULL) |
| return -1; |
| |
| elsym = (struct external_ldsym *) (contents + LDHDRSZ); |
| elsymend = elsym + ldhdr.l_nsyms; |
| for (; elsym < elsymend; elsym++, symbuf++, psyms++) |
| { |
| struct internal_ldsym ldsym; |
| |
| xcoff_swap_ldsym_in (abfd, elsym, &ldsym); |
| |
| symbuf->symbol.the_bfd = abfd; |
| |
| if (ldsym._l._l_l._l_zeroes == 0) |
| symbuf->symbol.name = strings + ldsym._l._l_l._l_offset; |
| else |
| { |
| int i; |
| |
| for (i = 0; i < SYMNMLEN; i++) |
| if (ldsym._l._l_name[i] == '\0') |
| break; |
| if (i < SYMNMLEN) |
| symbuf->symbol.name = (char *) elsym->_l._l_name; |
| else |
| { |
| char *c; |
| |
| c = bfd_alloc (abfd, SYMNMLEN + 1); |
| if (c == NULL) |
| return -1; |
| memcpy (c, ldsym._l._l_name, SYMNMLEN); |
| c[SYMNMLEN] = '\0'; |
| symbuf->symbol.name = c; |
| } |
| } |
| |
| if (ldsym.l_smclas == XMC_XO) |
| symbuf->symbol.section = bfd_abs_section_ptr; |
| else |
| symbuf->symbol.section = coff_section_from_bfd_index (abfd, |
| ldsym.l_scnum); |
| symbuf->symbol.value = ldsym.l_value - symbuf->symbol.section->vma; |
| |
| symbuf->symbol.flags = BSF_NO_FLAGS; |
| if ((ldsym.l_smtype & L_EXPORT) != 0) |
| symbuf->symbol.flags |= BSF_GLOBAL; |
| |
| /* FIXME: We have no way to record the other information stored |
| with the loader symbol. */ |
| |
| *psyms = (asymbol *) symbuf; |
| } |
| |
| *psyms = NULL; |
| |
| return ldhdr.l_nsyms; |
| } |
| |
| /* Get the size required to hold the dynamic relocs. */ |
| |
| long |
| _bfd_xcoff_get_dynamic_reloc_upper_bound (abfd) |
| bfd *abfd; |
| { |
| asection *lsec; |
| bfd_byte *contents; |
| struct internal_ldhdr ldhdr; |
| |
| if ((abfd->flags & DYNAMIC) == 0) |
| { |
| bfd_set_error (bfd_error_invalid_operation); |
| return -1; |
| } |
| |
| lsec = bfd_get_section_by_name (abfd, ".loader"); |
| if (lsec == NULL) |
| { |
| bfd_set_error (bfd_error_no_symbols); |
| return -1; |
| } |
| |
| if (! xcoff_get_section_contents (abfd, lsec)) |
| return -1; |
| contents = coff_section_data (abfd, lsec)->contents; |
| |
| xcoff_swap_ldhdr_in (abfd, (struct external_ldhdr *) contents, &ldhdr); |
| |
| return (ldhdr.l_nreloc + 1) * sizeof (arelent *); |
| } |
| |
| /* The typical dynamic reloc. */ |
| |
| static reloc_howto_type xcoff_dynamic_reloc = |
| HOWTO (0, /* type */ |
| 0, /* rightshift */ |
| 2, /* size (0 = byte, 1 = short, 2 = long) */ |
| 32, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_POS", /* name */ |
| true, /* partial_inplace */ |
| 0xffffffff, /* src_mask */ |
| 0xffffffff, /* dst_mask */ |
| false); /* pcrel_offset */ |
| |
| /* Get the dynamic relocs. */ |
| |
| long |
| _bfd_xcoff_canonicalize_dynamic_reloc (abfd, prelocs, syms) |
| bfd *abfd; |
| arelent **prelocs; |
| asymbol **syms; |
| { |
| asection *lsec; |
| bfd_byte *contents; |
| struct internal_ldhdr ldhdr; |
| arelent *relbuf; |
| struct external_ldrel *elrel, *elrelend; |
| |
| if ((abfd->flags & DYNAMIC) == 0) |
| { |
| bfd_set_error (bfd_error_invalid_operation); |
| return -1; |
| } |
| |
| lsec = bfd_get_section_by_name (abfd, ".loader"); |
| if (lsec == NULL) |
| { |
| bfd_set_error (bfd_error_no_symbols); |
| return -1; |
| } |
| |
| if (! xcoff_get_section_contents (abfd, lsec)) |
| return -1; |
| contents = coff_section_data (abfd, lsec)->contents; |
| |
| xcoff_swap_ldhdr_in (abfd, (struct external_ldhdr *) contents, &ldhdr); |
| |
| relbuf = (arelent *) bfd_alloc (abfd, ldhdr.l_nreloc * sizeof (arelent)); |
| if (relbuf == NULL) |
| return -1; |
| |
| elrel = ((struct external_ldrel *) |
| (contents + LDHDRSZ + ldhdr.l_nsyms * LDSYMSZ)); |
| elrelend = elrel + ldhdr.l_nreloc; |
| for (; elrel < elrelend; elrel++, relbuf++, prelocs++) |
| { |
| struct internal_ldrel ldrel; |
| |
| xcoff_swap_ldrel_in (abfd, elrel, &ldrel); |
| |
| if (ldrel.l_symndx >= 3) |
| relbuf->sym_ptr_ptr = syms + (ldrel.l_symndx - 3); |
| else |
| { |
| const char *name; |
| asection *sec; |
| |
| switch (ldrel.l_symndx) |
| { |
| case 0: |
| name = ".text"; |
| break; |
| case 1: |
| name = ".data"; |
| break; |
| case 2: |
| name = ".bss"; |
| break; |
| default: |
| abort (); |
| break; |
| } |
| |
| sec = bfd_get_section_by_name (abfd, name); |
| if (sec == NULL) |
| { |
| bfd_set_error (bfd_error_bad_value); |
| return -1; |
| } |
| |
| relbuf->sym_ptr_ptr = sec->symbol_ptr_ptr; |
| } |
| |
| relbuf->address = ldrel.l_vaddr; |
| relbuf->addend = 0; |
| |
| /* Most dynamic relocs have the same type. FIXME: This is only |
| correct if ldrel.l_rtype == 0. In other cases, we should use |
| a different howto. */ |
| relbuf->howto = &xcoff_dynamic_reloc; |
| |
| /* FIXME: We have no way to record the l_rsecnm field. */ |
| |
| *prelocs = relbuf; |
| } |
| |
| *prelocs = NULL; |
| |
| return ldhdr.l_nreloc; |
| } |
| |
| /* Routine to create an entry in an XCOFF link hash table. */ |
| |
| static struct bfd_hash_entry * |
| xcoff_link_hash_newfunc (entry, table, string) |
| struct bfd_hash_entry *entry; |
| struct bfd_hash_table *table; |
| const char *string; |
| { |
| struct xcoff_link_hash_entry *ret = (struct xcoff_link_hash_entry *) entry; |
| |
| /* Allocate the structure if it has not already been allocated by a |
| subclass. */ |
| if (ret == (struct xcoff_link_hash_entry *) NULL) |
| ret = ((struct xcoff_link_hash_entry *) |
| bfd_hash_allocate (table, sizeof (struct xcoff_link_hash_entry))); |
| if (ret == (struct xcoff_link_hash_entry *) NULL) |
| return (struct bfd_hash_entry *) ret; |
| |
| /* Call the allocation method of the superclass. */ |
| ret = ((struct xcoff_link_hash_entry *) |
| _bfd_link_hash_newfunc ((struct bfd_hash_entry *) ret, |
| table, string)); |
| if (ret != NULL) |
| { |
| /* Set local fields. */ |
| ret->indx = -1; |
| ret->toc_section = NULL; |
| ret->u.toc_indx = -1; |
| ret->descriptor = NULL; |
| ret->ldsym = NULL; |
| ret->ldindx = -1; |
| ret->flags = 0; |
| ret->smclas = XMC_UA; |
| } |
| |
| return (struct bfd_hash_entry *) ret; |
| } |
| |
| /* Create a XCOFF link hash table. */ |
| |
| struct bfd_link_hash_table * |
| _bfd_xcoff_bfd_link_hash_table_create (abfd) |
| bfd *abfd; |
| { |
| struct xcoff_link_hash_table *ret; |
| |
| ret = ((struct xcoff_link_hash_table *) |
| bfd_alloc (abfd, sizeof (struct xcoff_link_hash_table))); |
| if (ret == (struct xcoff_link_hash_table *) NULL) |
| return (struct bfd_link_hash_table *) NULL; |
| if (! _bfd_link_hash_table_init (&ret->root, abfd, xcoff_link_hash_newfunc)) |
| { |
| bfd_release (abfd, ret); |
| return (struct bfd_link_hash_table *) NULL; |
| } |
| |
| ret->debug_strtab = _bfd_xcoff_stringtab_init (); |
| ret->debug_section = NULL; |
| ret->loader_section = NULL; |
| ret->ldrel_count = 0; |
| memset (&ret->ldhdr, 0, sizeof (struct internal_ldhdr)); |
| ret->linkage_section = NULL; |
| ret->toc_section = NULL; |
| ret->descriptor_section = NULL; |
| ret->imports = NULL; |
| ret->file_align = 0; |
| ret->textro = false; |
| ret->gc = false; |
| memset (ret->special_sections, 0, sizeof ret->special_sections); |
| |
| /* The linker will always generate a full a.out header. We need to |
| record that fact now, before the sizeof_headers routine could be |
| called. */ |
| xcoff_data (abfd)->full_aouthdr = true; |
| |
| return &ret->root; |
| } |
| |
| /* Look up an entry in an XCOFF link hash table. */ |
| |
| #define xcoff_link_hash_lookup(table, string, create, copy, follow) \ |
| ((struct xcoff_link_hash_entry *) \ |
| bfd_link_hash_lookup (&(table)->root, (string), (create), (copy),\ |
| (follow))) |
| |
| /* Traverse an XCOFF link hash table. */ |
| |
| #define xcoff_link_hash_traverse(table, func, info) \ |
| (bfd_link_hash_traverse \ |
| (&(table)->root, \ |
| (boolean (*) PARAMS ((struct bfd_link_hash_entry *, PTR))) (func), \ |
| (info))) |
| |
| /* Get the XCOFF link hash table from the info structure. This is |
| just a cast. */ |
| |
| #define xcoff_hash_table(p) ((struct xcoff_link_hash_table *) ((p)->hash)) |
| |
| /* Read internal relocs for an XCOFF csect. This is a wrapper around |
| _bfd_coff_read_internal_relocs which tries to take advantage of any |
| relocs which may have been cached for the enclosing section. */ |
| |
| static struct internal_reloc * |
| xcoff_read_internal_relocs (abfd, sec, cache, external_relocs, |
| require_internal, internal_relocs) |
| bfd *abfd; |
| asection *sec; |
| boolean cache; |
| bfd_byte *external_relocs; |
| boolean require_internal; |
| struct internal_reloc *internal_relocs; |
| { |
| if (coff_section_data (abfd, sec) != NULL |
| && coff_section_data (abfd, sec)->relocs == NULL |
| && xcoff_section_data (abfd, sec) != NULL) |
| { |
| asection *enclosing; |
| |
| enclosing = xcoff_section_data (abfd, sec)->enclosing; |
| |
| if (enclosing != NULL |
| && (coff_section_data (abfd, enclosing) == NULL |
| || coff_section_data (abfd, enclosing)->relocs == NULL) |
| && cache |
| && enclosing->reloc_count > 0) |
| { |
| if (_bfd_coff_read_internal_relocs (abfd, enclosing, true, |
| external_relocs, false, |
| (struct internal_reloc *) NULL) |
| == NULL) |
| return NULL; |
| } |
| |
| if (enclosing != NULL |
| && coff_section_data (abfd, enclosing) != NULL |
| && coff_section_data (abfd, enclosing)->relocs != NULL) |
| { |
| size_t off; |
| |
| off = ((sec->rel_filepos - enclosing->rel_filepos) |
| / bfd_coff_relsz (abfd)); |
| if (! require_internal) |
| return coff_section_data (abfd, enclosing)->relocs + off; |
| memcpy (internal_relocs, |
| coff_section_data (abfd, enclosing)->relocs + off, |
| sec->reloc_count * sizeof (struct internal_reloc)); |
| return internal_relocs; |
| } |
| } |
| |
| return _bfd_coff_read_internal_relocs (abfd, sec, cache, external_relocs, |
| require_internal, internal_relocs); |
| } |
| |
| /* Given an XCOFF BFD, add symbols to the global hash table as |
| appropriate. */ |
| |
| boolean |
| _bfd_xcoff_bfd_link_add_symbols (abfd, info) |
| bfd *abfd; |
| struct bfd_link_info *info; |
| { |
| switch (bfd_get_format (abfd)) |
| { |
| case bfd_object: |
| return xcoff_link_add_object_symbols (abfd, info); |
| |
| case bfd_archive: |
| /* If the archive has a map, do the usual search. We then need |
| to check the archive for stripped dynamic objects, because |
| they will not appear in the archive map even though they |
| should, perhaps, be included. If the archive has no map, we |
| just consider each object file in turn, since that apparently |
| is what the AIX native linker does. */ |
| if (bfd_has_map (abfd)) |
| { |
| if (! (_bfd_generic_link_add_archive_symbols |
| (abfd, info, xcoff_link_check_archive_element))) |
| return false; |
| } |
| |
| { |
| bfd *member; |
| |
| member = bfd_openr_next_archived_file (abfd, (bfd *) NULL); |
| while (member != NULL) |
| { |
| if (bfd_check_format (member, bfd_object) |
| && (! bfd_has_map (abfd) |
| || ((member->flags & DYNAMIC) != 0 |
| && (member->flags & HAS_SYMS) == 0))) |
| { |
| boolean needed; |
| |
| if (! xcoff_link_check_archive_element (member, info, &needed)) |
| return false; |
| if (needed) |
| member->archive_pass = -1; |
| } |
| member = bfd_openr_next_archived_file (abfd, member); |
| } |
| } |
| |
| return true; |
| |
| default: |
| bfd_set_error (bfd_error_wrong_format); |
| return false; |
| } |
| } |
| |
| /* Add symbols from an XCOFF object file. */ |
| |
| static boolean |
| xcoff_link_add_object_symbols (abfd, info) |
| bfd *abfd; |
| struct bfd_link_info *info; |
| { |
| if (! _bfd_coff_get_external_symbols (abfd)) |
| return false; |
| if (! xcoff_link_add_symbols (abfd, info)) |
| return false; |
| if (! info->keep_memory) |
| { |
| if (! _bfd_coff_free_symbols (abfd)) |
| return false; |
| } |
| return true; |
| } |
| |
| /* Check a single archive element to see if we need to include it in |
| the link. *PNEEDED is set according to whether this element is |
| needed in the link or not. This is called via |
| _bfd_generic_link_add_archive_symbols. */ |
| |
| static boolean |
| xcoff_link_check_archive_element (abfd, info, pneeded) |
| bfd *abfd; |
| struct bfd_link_info *info; |
| boolean *pneeded; |
| { |
| if (! _bfd_coff_get_external_symbols (abfd)) |
| return false; |
| |
| if (! xcoff_link_check_ar_symbols (abfd, info, pneeded)) |
| return false; |
| |
| if (*pneeded) |
| { |
| if (! xcoff_link_add_symbols (abfd, info)) |
| return false; |
| } |
| |
| if (! info->keep_memory || ! *pneeded) |
| { |
| if (! _bfd_coff_free_symbols (abfd)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* Look through the symbols to see if this object file should be |
| included in the link. */ |
| |
| static boolean |
| xcoff_link_check_ar_symbols (abfd, info, pneeded) |
| bfd *abfd; |
| struct bfd_link_info *info; |
| boolean *pneeded; |
| { |
| bfd_size_type symesz; |
| bfd_byte *esym; |
| bfd_byte *esym_end; |
| |
| *pneeded = false; |
| |
| if ((abfd->flags & DYNAMIC) != 0 |
| && ! info->static_link |
| && info->hash->creator == abfd->xvec) |
| return xcoff_link_check_dynamic_ar_symbols (abfd, info, pneeded); |
| |
| symesz = bfd_coff_symesz (abfd); |
| esym = (bfd_byte *) obj_coff_external_syms (abfd); |
| esym_end = esym + obj_raw_syment_count (abfd) * symesz; |
| while (esym < esym_end) |
| { |
| struct internal_syment sym; |
| |
| bfd_coff_swap_sym_in (abfd, (PTR) esym, (PTR) &sym); |
| |
| if (sym.n_sclass == C_EXT && sym.n_scnum != N_UNDEF) |
| { |
| const char *name; |
| char buf[SYMNMLEN + 1]; |
| struct bfd_link_hash_entry *h; |
| |
| /* This symbol is externally visible, and is defined by this |
| object file. */ |
| |
| name = _bfd_coff_internal_syment_name (abfd, &sym, buf); |
| if (name == NULL) |
| return false; |
| h = bfd_link_hash_lookup (info->hash, name, false, false, true); |
| |
| /* We are only interested in symbols that are currently |
| undefined. If a symbol is currently known to be common, |
| XCOFF linkers do not bring in an object file which |
| defines it. We also don't bring in symbols to satisfy |
| undefined references in shared objects. */ |
| if (h != (struct bfd_link_hash_entry *) NULL |
| && h->type == bfd_link_hash_undefined |
| && (info->hash->creator != abfd->xvec |
| || (((struct xcoff_link_hash_entry *) h)->flags |
| & XCOFF_DEF_DYNAMIC) == 0)) |
| { |
| if (! (*info->callbacks->add_archive_element) (info, abfd, name)) |
| return false; |
| *pneeded = true; |
| return true; |
| } |
| } |
| |
| esym += (sym.n_numaux + 1) * symesz; |
| } |
| |
| /* We do not need this object file. */ |
| return true; |
| } |
| |
| /* Look through the loader symbols to see if this dynamic object |
| should be included in the link. The native linker uses the loader |
| symbols, not the normal symbol table, so we do too. */ |
| |
| static boolean |
| xcoff_link_check_dynamic_ar_symbols (abfd, info, pneeded) |
| bfd *abfd; |
| struct bfd_link_info *info; |
| boolean *pneeded; |
| { |
| asection *lsec; |
| bfd_byte *buf; |
| struct internal_ldhdr ldhdr; |
| const char *strings; |
| struct external_ldsym *elsym, *elsymend; |
| |
| *pneeded = false; |
| |
| lsec = bfd_get_section_by_name (abfd, ".loader"); |
| if (lsec == NULL) |
| { |
| /* There are no symbols, so don't try to include it. */ |
| return true; |
| } |
| |
| if (! xcoff_get_section_contents (abfd, lsec)) |
| return false; |
| buf = coff_section_data (abfd, lsec)->contents; |
| |
| xcoff_swap_ldhdr_in (abfd, (struct external_ldhdr *) buf, &ldhdr); |
| |
| strings = (char *) buf + ldhdr.l_stoff; |
| |
| elsym = (struct external_ldsym *) (buf + LDHDRSZ); |
| elsymend = elsym + ldhdr.l_nsyms; |
| for (; elsym < elsymend; elsym++) |
| { |
| struct internal_ldsym ldsym; |
| char nambuf[SYMNMLEN + 1]; |
| const char *name; |
| struct bfd_link_hash_entry *h; |
| |
| xcoff_swap_ldsym_in (abfd, elsym, &ldsym); |
| |
| /* We are only interested in exported symbols. */ |
| if ((ldsym.l_smtype & L_EXPORT) == 0) |
| continue; |
| |
| if (ldsym._l._l_l._l_zeroes == 0) |
| name = strings + ldsym._l._l_l._l_offset; |
| else |
| { |
| memcpy (nambuf, ldsym._l._l_name, SYMNMLEN); |
| nambuf[SYMNMLEN] = '\0'; |
| name = nambuf; |
| } |
| |
| h = bfd_link_hash_lookup (info->hash, name, false, false, true); |
| |
| /* We are only interested in symbols that are currently |
| undefined. At this point we know that we are using an XCOFF |
| hash table. */ |
| if (h != NULL |
| && h->type == bfd_link_hash_undefined |
| && (((struct xcoff_link_hash_entry *) h)->flags |
| & XCOFF_DEF_DYNAMIC) == 0) |
| { |
| if (! (*info->callbacks->add_archive_element) (info, abfd, name)) |
| return false; |
| *pneeded = true; |
| return true; |
| } |
| } |
| |
| /* We do not need this shared object. */ |
| |
| if (buf != NULL && ! coff_section_data (abfd, lsec)->keep_contents) |
| { |
| free (coff_section_data (abfd, lsec)->contents); |
| coff_section_data (abfd, lsec)->contents = NULL; |
| } |
| |
| return true; |
| } |
| |
| /* Returns the index of reloc in RELOCS with the least address greater |
| than or equal to ADDRESS. The relocs are sorted by address. */ |
| |
| static bfd_size_type |
| xcoff_find_reloc (relocs, count, address) |
| struct internal_reloc *relocs; |
| bfd_size_type count; |
| bfd_vma address; |
| { |
| bfd_size_type min, max, this; |
| |
| if (count < 2) |
| { |
| if (count == 1 && relocs[0].r_vaddr < address) |
| return 1; |
| else |
| return 0; |
| } |
| |
| min = 0; |
| max = count; |
| |
| /* Do a binary search over (min,max]. */ |
| while (min + 1 < max) |
| { |
| bfd_vma raddr; |
| |
| this = (max + min) / 2; |
| raddr = relocs[this].r_vaddr; |
| if (raddr > address) |
| max = this; |
| else if (raddr < address) |
| min = this; |
| else |
| { |
| min = this; |
| break; |
| } |
| } |
| |
| if (relocs[min].r_vaddr < address) |
| return min + 1; |
| |
| while (min > 0 |
| && relocs[min - 1].r_vaddr == address) |
| --min; |
| |
| return min; |
| } |
| |
| /* Add all the symbols from an object file to the hash table. |
| |
| XCOFF is a weird format. A normal XCOFF .o files will have three |
| COFF sections--.text, .data, and .bss--but each COFF section will |
| contain many csects. These csects are described in the symbol |
| table. From the linker's point of view, each csect must be |
| considered a section in its own right. For example, a TOC entry is |
| handled as a small XMC_TC csect. The linker must be able to merge |
| different TOC entries together, which means that it must be able to |
| extract the XMC_TC csects from the .data section of the input .o |
| file. |
| |
| From the point of view of our linker, this is, of course, a hideous |
| nightmare. We cope by actually creating sections for each csect, |
| and discarding the original sections. We then have to handle the |
| relocation entries carefully, since the only way to tell which |
| csect they belong to is to examine the address. */ |
| |
| static boolean |
| xcoff_link_add_symbols (abfd, info) |
| bfd *abfd; |
| struct bfd_link_info *info; |
| { |
| unsigned int n_tmask; |
| unsigned int n_btshft; |
| boolean default_copy; |
| bfd_size_type symcount; |
| struct xcoff_link_hash_entry **sym_hash; |
| asection **csect_cache; |
| bfd_size_type linesz; |
| asection *o; |
| asection *last_real; |
| boolean keep_syms; |
| asection *csect; |
| unsigned int csect_index; |
| asection *first_csect; |
| bfd_size_type symesz; |
| bfd_byte *esym; |
| bfd_byte *esym_end; |
| struct reloc_info_struct |
| { |
| struct internal_reloc *relocs; |
| asection **csects; |
| bfd_byte *linenos; |
| } *reloc_info = NULL; |
| |
| keep_syms = obj_coff_keep_syms (abfd); |
| |
| if ((abfd->flags & DYNAMIC) != 0 |
| && ! info->static_link) |
| { |
| if (! xcoff_link_add_dynamic_symbols (abfd, info)) |
| return false; |
| } |
| |
| if (info->hash->creator == abfd->xvec) |
| { |
| /* We need to build a .loader section, so we do it here. This |
| won't work if we're producing an XCOFF output file with no |
| XCOFF input files. FIXME. */ |
| if (xcoff_hash_table (info)->loader_section == NULL) |
| { |
| asection *lsec; |
| |
| lsec = bfd_make_section_anyway (abfd, ".loader"); |
| if (lsec == NULL) |
| goto error_return; |
| xcoff_hash_table (info)->loader_section = lsec; |
| lsec->flags |= SEC_HAS_CONTENTS | SEC_IN_MEMORY; |
| } |
| /* Likewise for the linkage section. */ |
| if (xcoff_hash_table (info)->linkage_section == NULL) |
| { |
| asection *lsec; |
| |
| lsec = bfd_make_section_anyway (abfd, ".gl"); |
| if (lsec == NULL) |
| goto error_return; |
| xcoff_hash_table (info)->linkage_section = lsec; |
| lsec->flags |= (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS |
| | SEC_IN_MEMORY); |
| lsec->alignment_power = 2; |
| } |
| /* Likewise for the TOC section. */ |
| if (xcoff_hash_table (info)->toc_section == NULL) |
| { |
| asection *tsec; |
| |
| tsec = bfd_make_section_anyway (abfd, ".tc"); |
| if (tsec == NULL) |
| goto error_return; |
| xcoff_hash_table (info)->toc_section = tsec; |
| tsec->flags |= (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS |
| | SEC_IN_MEMORY); |
| tsec->alignment_power = 2; |
| } |
| /* Likewise for the descriptor section. */ |
| if (xcoff_hash_table (info)->descriptor_section == NULL) |
| { |
| asection *dsec; |
| |
| dsec = bfd_make_section_anyway (abfd, ".ds"); |
| if (dsec == NULL) |
| goto error_return; |
| xcoff_hash_table (info)->descriptor_section = dsec; |
| dsec->flags |= (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS |
| | SEC_IN_MEMORY); |
| dsec->alignment_power = 2; |
| } |
| /* Likewise for the .debug section. */ |
| if (xcoff_hash_table (info)->debug_section == NULL |
| && info->strip != strip_all) |
| { |
| asection *dsec; |
| |
| dsec = bfd_make_section_anyway (abfd, ".debug"); |
| if (dsec == NULL) |
| goto error_return; |
| xcoff_hash_table (info)->debug_section = dsec; |
| dsec->flags |= SEC_HAS_CONTENTS | SEC_IN_MEMORY; |
| } |
| } |
| |
| if ((abfd->flags & DYNAMIC) != 0 |
| && ! info->static_link) |
| return true; |
| |
| n_tmask = coff_data (abfd)->local_n_tmask; |
| n_btshft = coff_data (abfd)->local_n_btshft; |
| |
| /* Define macros so that ISFCN, et. al., macros work correctly. */ |
| #define N_TMASK n_tmask |
| #define N_BTSHFT n_btshft |
| |
| if (info->keep_memory) |
| default_copy = false; |
| else |
| default_copy = true; |
| |
| symcount = obj_raw_syment_count (abfd); |
| |
| /* We keep a list of the linker hash table entries that correspond |
| to each external symbol. */ |
| sym_hash = ((struct xcoff_link_hash_entry **) |
| bfd_alloc (abfd, |
| (symcount |
| * sizeof (struct xcoff_link_hash_entry *)))); |
| if (sym_hash == NULL && symcount != 0) |
| goto error_return; |
| coff_data (abfd)->sym_hashes = (struct coff_link_hash_entry **) sym_hash; |
| memset (sym_hash, 0, |
| (size_t) symcount * sizeof (struct xcoff_link_hash_entry *)); |
| |
| /* Because of the weird stuff we are doing with XCOFF csects, we can |
| not easily determine which section a symbol is in, so we store |
| the information in the tdata for the input file. */ |
| csect_cache = ((asection **) |
| bfd_alloc (abfd, symcount * sizeof (asection *))); |
| if (csect_cache == NULL && symcount != 0) |
| goto error_return; |
| xcoff_data (abfd)->csects = csect_cache; |
| memset (csect_cache, 0, (size_t) symcount * sizeof (asection *)); |
| |
| /* While splitting sections into csects, we need to assign the |
| relocs correctly. The relocs and the csects must both be in |
| order by VMA within a given section, so we handle this by |
| scanning along the relocs as we process the csects. We index |
| into reloc_info using the section target_index. */ |
| reloc_info = ((struct reloc_info_struct *) |
| bfd_malloc ((abfd->section_count + 1) |
| * sizeof (struct reloc_info_struct))); |
| if (reloc_info == NULL) |
| goto error_return; |
| memset ((PTR) reloc_info, 0, |
| (abfd->section_count + 1) * sizeof (struct reloc_info_struct)); |
| |
| /* Read in the relocs and line numbers for each section. */ |
| linesz = bfd_coff_linesz (abfd); |
| last_real = NULL; |
| for (o = abfd->sections; o != NULL; o = o->next) |
| { |
| last_real = o; |
| if ((o->flags & SEC_RELOC) != 0) |
| { |
| reloc_info[o->target_index].relocs = |
| xcoff_read_internal_relocs (abfd, o, true, (bfd_byte *) NULL, |
| false, (struct internal_reloc *) NULL); |
| reloc_info[o->target_index].csects = |
| (asection **) bfd_malloc (o->reloc_count * sizeof (asection *)); |
| if (reloc_info[o->target_index].csects == NULL) |
| goto error_return; |
| memset (reloc_info[o->target_index].csects, 0, |
| o->reloc_count * sizeof (asection *)); |
| } |
| |
| if ((info->strip == strip_none || info->strip == strip_some) |
| && o->lineno_count > 0) |
| { |
| bfd_byte *linenos; |
| |
| linenos = (bfd_byte *) bfd_malloc (o->lineno_count * linesz); |
| if (linenos == NULL) |
| goto error_return; |
| reloc_info[o->target_index].linenos = linenos; |
| if (bfd_seek (abfd, o->line_filepos, SEEK_SET) != 0 |
| || (bfd_read (linenos, linesz, o->lineno_count, abfd) |
| != linesz * o->lineno_count)) |
| goto error_return; |
| } |
| } |
| |
| /* Don't let the linker relocation routines discard the symbols. */ |
| obj_coff_keep_syms (abfd) = true; |
| |
| csect = NULL; |
| csect_index = 0; |
| first_csect = NULL; |
| |
| symesz = bfd_coff_symesz (abfd); |
| BFD_ASSERT (symesz == bfd_coff_auxesz (abfd)); |
| esym = (bfd_byte *) obj_coff_external_syms (abfd); |
| esym_end = esym + symcount * symesz; |
| while (esym < esym_end) |
| { |
| struct internal_syment sym; |
| union internal_auxent aux; |
| const char *name; |
| char buf[SYMNMLEN + 1]; |
| int smtyp; |
| flagword flags; |
| asection *section; |
| bfd_vma value; |
| struct xcoff_link_hash_entry *set_toc; |
| |
| bfd_coff_swap_sym_in (abfd, (PTR) esym, (PTR) &sym); |
| |
| /* In this pass we are only interested in symbols with csect |
| information. */ |
| if (sym.n_sclass != C_EXT && sym.n_sclass != C_HIDEXT) |
| { |
| if (sym.n_sclass == C_FILE && csect != NULL) |
| { |
| xcoff_section_data (abfd, csect)->last_symndx = |
| ((esym |
| - (bfd_byte *) obj_coff_external_syms (abfd)) |
| / symesz); |
| csect = NULL; |
| } |
| |
| if (csect != NULL) |
| *csect_cache = csect; |
| else if (first_csect == NULL || sym.n_sclass == C_FILE) |
| *csect_cache = coff_section_from_bfd_index (abfd, sym.n_scnum); |
| else |
| *csect_cache = NULL; |
| esym += (sym.n_numaux + 1) * symesz; |
| sym_hash += sym.n_numaux + 1; |
| csect_cache += sym.n_numaux + 1; |
| continue; |
| } |
| |
| name = _bfd_coff_internal_syment_name (abfd, &sym, buf); |
| if (name == NULL) |
| goto error_return; |
| |
| /* If this symbol has line number information attached to it, |
| and we're not stripping it, count the number of entries and |
| add them to the count for this csect. In the final link pass |
| we are going to attach line number information by symbol, |
| rather than by section, in order to more easily handle |
| garbage collection. */ |
| if ((info->strip == strip_none || info->strip == strip_some) |
| && sym.n_numaux > 1 |
| && csect != NULL |
| && ISFCN (sym.n_type)) |
| { |
| union internal_auxent auxlin; |
| |
| bfd_coff_swap_aux_in (abfd, (PTR) (esym + symesz), |
| sym.n_type, sym.n_sclass, |
| 0, sym.n_numaux, (PTR) &auxlin); |
| if (auxlin.x_sym.x_fcnary.x_fcn.x_lnnoptr != 0) |
| { |
| asection *enclosing; |
| bfd_size_type linoff; |
| |
| enclosing = xcoff_section_data (abfd, csect)->enclosing; |
| if (enclosing == NULL) |
| { |
| (*_bfd_error_handler) |
| (_("%s: `%s' has line numbers but no enclosing section"), |
| bfd_get_filename (abfd), name); |
| bfd_set_error (bfd_error_bad_value); |
| goto error_return; |
| } |
| linoff = (auxlin.x_sym.x_fcnary.x_fcn.x_lnnoptr |
| - enclosing->line_filepos); |
| if (linoff < enclosing->lineno_count * linesz) |
| { |
| struct internal_lineno lin; |
| bfd_byte *linpstart; |
| |
| linpstart = (reloc_info[enclosing->target_index].linenos |
| + linoff); |
| bfd_coff_swap_lineno_in (abfd, (PTR) linpstart, (PTR) &lin); |
| if (lin.l_lnno == 0 |
| && ((bfd_size_type) lin.l_addr.l_symndx |
| == ((esym |
| - (bfd_byte *) obj_coff_external_syms (abfd)) |
| / symesz))) |
| { |
| bfd_byte *linpend, *linp; |
| |
| linpend = (reloc_info[enclosing->target_index].linenos |
| + enclosing->lineno_count * linesz); |
| for (linp = linpstart + linesz; |
| linp < linpend; |
| linp += linesz) |
| { |
| bfd_coff_swap_lineno_in (abfd, (PTR) linp, |
| (PTR) &lin); |
| if (lin.l_lnno == 0) |
| break; |
| } |
| csect->lineno_count += (linp - linpstart) / linesz; |
| /* The setting of line_filepos will only be |
| useful if all the line number entries for a |
| csect are contiguous; this only matters for |
| error reporting. */ |
| if (csect->line_filepos == 0) |
| csect->line_filepos = |
| auxlin.x_sym.x_fcnary.x_fcn.x_lnnoptr; |
| } |
| } |
| } |
| } |
| |
| /* Pick up the csect auxiliary information. */ |
| |
| if (sym.n_numaux == 0) |
| { |
| (*_bfd_error_handler) |
| (_("%s: class %d symbol `%s' has no aux entries"), |
| bfd_get_filename (abfd), sym.n_sclass, name); |
| bfd_set_error (bfd_error_bad_value); |
| goto error_return; |
| } |
| |
| bfd_coff_swap_aux_in (abfd, |
| (PTR) (esym + symesz * sym.n_numaux), |
| sym.n_type, sym.n_sclass, |
| sym.n_numaux - 1, sym.n_numaux, |
| (PTR) &aux); |
| |
| smtyp = SMTYP_SMTYP (aux.x_csect.x_smtyp); |
| |
| flags = BSF_GLOBAL; |
| section = NULL; |
| value = 0; |
| set_toc = NULL; |
| |
| switch (smtyp) |
| { |
| default: |
| (*_bfd_error_handler) |
| (_("%s: symbol `%s' has unrecognized csect type %d"), |
| bfd_get_filename (abfd), name, smtyp); |
| bfd_set_error (bfd_error_bad_value); |
| goto error_return; |
| |
| case XTY_ER: |
| /* This is an external reference. */ |
| if (sym.n_sclass == C_HIDEXT |
| || sym.n_scnum != N_UNDEF |
| || aux.x_csect.x_scnlen.l != 0) |
| { |
| (*_bfd_error_handler) |
| (_("%s: bad XTY_ER symbol `%s': class %d scnum %d scnlen %d"), |
| bfd_get_filename (abfd), name, sym.n_sclass, sym.n_scnum, |
| aux.x_csect.x_scnlen.l); |
| bfd_set_error (bfd_error_bad_value); |
| goto error_return; |
| } |
| |
| /* An XMC_XO external reference is actually a reference to |
| an absolute location. */ |
| if (aux.x_csect.x_smclas != XMC_XO) |
| section = bfd_und_section_ptr; |
| else |
| { |
| section = bfd_abs_section_ptr; |
| value = sym.n_value; |
| } |
| break; |
| |
| case XTY_SD: |
| /* This is a csect definition. */ |
| |
| if (csect != NULL) |
| { |
| xcoff_section_data (abfd, csect)->last_symndx = |
| ((esym |
| - (bfd_byte *) obj_coff_external_syms (abfd)) |
| / symesz); |
| } |
| |
| csect = NULL; |
| csect_index = -1; |
| |
| /* When we see a TOC anchor, we record the TOC value. */ |
| if (aux.x_csect.x_smclas == XMC_TC0) |
| { |
| if (sym.n_sclass != C_HIDEXT |
| || aux.x_csect.x_scnlen.l != 0) |
| { |
| (*_bfd_error_handler) |
| (_("%s: XMC_TC0 symbol `%s' is class %d scnlen %d"), |
| bfd_get_filename (abfd), name, sym.n_sclass, |
| aux.x_csect.x_scnlen.l); |
| bfd_set_error (bfd_error_bad_value); |
| goto error_return; |
| } |
| xcoff_data (abfd)->toc = sym.n_value; |
| } |
| |
| /* We must merge TOC entries for the same symbol. We can |
| merge two TOC entries if they are both C_HIDEXT, they |
| both have the same name, they are both 4 bytes long, and |
| they both have a relocation table entry for an external |
| symbol with the same name. Unfortunately, this means |
| that we must look through the relocations. Ick. */ |
| if (aux.x_csect.x_smclas == XMC_TC |
| && sym.n_sclass == C_HIDEXT |
| && aux.x_csect.x_scnlen.l == 4 |
| && info->hash->creator == abfd->xvec) |
| { |
| asection *enclosing; |
| struct internal_reloc *relocs; |
| bfd_size_type relindx; |
| struct internal_reloc *rel; |
| |
| enclosing = coff_section_from_bfd_index (abfd, sym.n_scnum); |
| if (enclosing == NULL) |
| goto error_return; |
| |
| relocs = reloc_info[enclosing->target_index].relocs; |
| relindx = xcoff_find_reloc (relocs, enclosing->reloc_count, |
| sym.n_value); |
| rel = relocs + relindx; |
| if (relindx < enclosing->reloc_count |
| && rel->r_vaddr == (bfd_vma) sym.n_value |
| && rel->r_size == 31 |
| && rel->r_type == R_POS) |
| { |
| bfd_byte *erelsym; |
| struct internal_syment relsym; |
| |
| erelsym = ((bfd_byte *) obj_coff_external_syms (abfd) |
| + rel->r_symndx * symesz); |
| bfd_coff_swap_sym_in (abfd, (PTR) erelsym, (PTR) &relsym); |
| if (relsym.n_sclass == C_EXT) |
| { |
| const char *relname; |
| char relbuf[SYMNMLEN + 1]; |
| boolean copy; |
| struct xcoff_link_hash_entry *h; |
| |
| /* At this point we know that the TOC entry is |
| for an externally visible symbol. */ |
| relname = _bfd_coff_internal_syment_name (abfd, &relsym, |
| relbuf); |
| if (relname == NULL) |
| goto error_return; |
| |
| /* We only merge TOC entries if the TC name is |
| the same as the symbol name. This handles |
| the normal case, but not common cases like |
| SYM.P4 which gcc generates to store SYM + 4 |
| in the TOC. FIXME. */ |
| if (strcmp (name, relname) == 0) |
| { |
| copy = (! info->keep_memory |
| || relsym._n._n_n._n_zeroes != 0 |
| || relsym._n._n_n._n_offset == 0); |
| h = xcoff_link_hash_lookup (xcoff_hash_table (info), |
| relname, true, copy, |
| false); |
| if (h == NULL) |
| goto error_return; |
| |
| /* At this point h->root.type could be |
| bfd_link_hash_new. That should be OK, |
| since we know for sure that we will come |
| across this symbol as we step through the |
| file. */ |
| |
| /* We store h in *sym_hash for the |
| convenience of the relocate_section |
| function. */ |
| *sym_hash = h; |
| |
| if (h->toc_section != NULL) |
| { |
| asection **rel_csects; |
| |
| /* We already have a TOC entry for this |
| symbol, so we can just ignore this |
| one. */ |
| rel_csects = |
| reloc_info[enclosing->target_index].csects; |
| rel_csects[relindx] = bfd_und_section_ptr; |
| break; |
| } |
| |
| /* We are about to create a TOC entry for |
| this symbol. */ |
| set_toc = h; |
| } |
| } |
| } |
| } |
| |
| /* We need to create a new section. We get the name from |
| the csect storage mapping class, so that the linker can |
| accumulate similar csects together. */ |
| { |
| static const char *csect_name_by_class[] = { |
| ".pr", ".ro", ".db", ".tc", ".ua", ".rw", ".gl", ".xo", |
| ".sv", ".bs", ".ds", ".uc", ".ti", ".tb", NULL, ".tc0", |
| ".td" |
| }; |
| const char *csect_name; |
| asection *enclosing; |
| |
| if ((aux.x_csect.x_smclas >= |
| sizeof csect_name_by_class / sizeof csect_name_by_class[0]) |
| || csect_name_by_class[aux.x_csect.x_smclas] == NULL) |
| { |
| (*_bfd_error_handler) |
| (_("%s: symbol `%s' has unrecognized smclas %d"), |
| bfd_get_filename (abfd), name, aux.x_csect.x_smclas); |
| bfd_set_error (bfd_error_bad_value); |
| goto error_return; |
| } |
| |
| csect_name = csect_name_by_class[aux.x_csect.x_smclas]; |
| csect = bfd_make_section_anyway (abfd, csect_name); |
| if (csect == NULL) |
| goto error_return; |
| enclosing = coff_section_from_bfd_index (abfd, sym.n_scnum); |
| if (enclosing == NULL) |
| goto error_return; |
| if (! bfd_is_abs_section (enclosing) |
| && ((bfd_vma) sym.n_value < enclosing->vma |
| || ((bfd_vma) sym.n_value + aux.x_csect.x_scnlen.l |
| > enclosing->vma + enclosing->_raw_size))) |
| { |
| (*_bfd_error_handler) |
| (_("%s: csect `%s' not in enclosing section"), |
| bfd_get_filename (abfd), name); |
| bfd_set_error (bfd_error_bad_value); |
| goto error_return; |
| } |
| csect->vma = sym.n_value; |
| csect->filepos = (enclosing->filepos |
| + sym.n_value |
| - enclosing->vma); |
| csect->_raw_size = aux.x_csect.x_scnlen.l; |
| csect->flags |= SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS; |
| csect->alignment_power = SMTYP_ALIGN (aux.x_csect.x_smtyp); |
| |
| /* Record the enclosing section in the tdata for this new |
| section. */ |
| csect->used_by_bfd = |
| (PTR) bfd_zalloc (abfd, sizeof (struct coff_section_tdata)); |
| if (csect->used_by_bfd == NULL) |
| goto error_return; |
| coff_section_data (abfd, csect)->tdata = |
| bfd_zalloc (abfd, sizeof (struct xcoff_section_tdata)); |
| if (coff_section_data (abfd, csect)->tdata == NULL) |
| goto error_return; |
| xcoff_section_data (abfd, csect)->enclosing = enclosing; |
| xcoff_section_data (abfd, csect)->lineno_count = |
| enclosing->lineno_count; |
| |
| if (enclosing->owner == abfd) |
| { |
| struct internal_reloc *relocs; |
| bfd_size_type relindx; |
| struct internal_reloc *rel; |
| asection **rel_csect; |
| |
| relocs = reloc_info[enclosing->target_index].relocs; |
| relindx = xcoff_find_reloc (relocs, enclosing->reloc_count, |
| csect->vma); |
| rel = relocs + relindx; |
| rel_csect = (reloc_info[enclosing->target_index].csects |
| + relindx); |
| csect->rel_filepos = (enclosing->rel_filepos |
| + relindx * bfd_coff_relsz (abfd)); |
| while (relindx < enclosing->reloc_count |
| && *rel_csect == NULL |
| && rel->r_vaddr < csect->vma + csect->_raw_size) |
| { |
| *rel_csect = csect; |
| csect->flags |= SEC_RELOC; |
| ++csect->reloc_count; |
| ++relindx; |
| ++rel; |
| ++rel_csect; |
| } |
| } |
| |
| /* There are a number of other fields and section flags |
| which we do not bother to set. */ |
| |
| csect_index = ((esym |
| - (bfd_byte *) obj_coff_external_syms (abfd)) |
| / symesz); |
| |
| xcoff_section_data (abfd, csect)->first_symndx = csect_index; |
| |
| if (first_csect == NULL) |
| first_csect = csect; |
| |
| /* If this symbol is C_EXT, we treat it as starting at the |
| beginning of the newly created section. */ |
| if (sym.n_sclass == C_EXT) |
| { |
| section = csect; |
| value = 0; |
| } |
| |
| /* If this is a TOC section for a symbol, record it. */ |
| if (set_toc != NULL) |
| set_toc->toc_section = csect; |
| } |
| break; |
| |
| case XTY_LD: |
| /* This is a label definition. The x_scnlen field is the |
| symbol index of the csect. I believe that this must |
| always follow the appropriate XTY_SD symbol, so I will |
| insist on it. */ |
| { |
| boolean bad; |
| |
| bad = false; |
| if (aux.x_csect.x_scnlen.l < 0 |
| || (aux.x_csect.x_scnlen.l |
| >= esym - (bfd_byte *) obj_coff_external_syms (abfd))) |
| bad = true; |
| if (! bad) |
| { |
| section = xcoff_data (abfd)->csects[aux.x_csect.x_scnlen.l]; |
| if (section == NULL |
| || (section->flags & SEC_HAS_CONTENTS) == 0) |
| bad = true; |
| } |
| if (bad) |
| { |
| (*_bfd_error_handler) |
| (_("%s: misplaced XTY_LD `%s'"), |
| bfd_get_filename (abfd), name); |
| bfd_set_error (bfd_error_bad_value); |
| goto error_return; |
| } |
| |
| value = sym.n_value - csect->vma; |
| } |
| break; |
| |
| case XTY_CM: |
| /* This is an unitialized csect. We could base the name on |
| the storage mapping class, but we don't bother except for |
| an XMC_TD symbol. If this csect is externally visible, |
| it is a common symbol. We put XMC_TD symbols in sections |
| named .tocbss, and rely on the linker script to put that |
| in the TOC area. */ |
| |
| if (csect != NULL) |
| { |
| xcoff_section_data (abfd, csect)->last_symndx = |
| ((esym |
| - (bfd_byte *) obj_coff_external_syms (abfd)) |
| / symesz); |
| } |
| |
| if (aux.x_csect.x_smclas == XMC_TD) |
| csect = bfd_make_section_anyway (abfd, ".tocbss"); |
| else |
| csect = bfd_make_section_anyway (abfd, ".bss"); |
| if (csect == NULL) |
| goto error_return; |
| csect->vma = sym.n_value; |
| csect->_raw_size = aux.x_csect.x_scnlen.l; |
| csect->flags |= SEC_ALLOC; |
| csect->alignment_power = SMTYP_ALIGN (aux.x_csect.x_smtyp); |
| /* There are a number of other fields and section flags |
| which we do not bother to set. */ |
| |
| csect_index = ((esym |
| - (bfd_byte *) obj_coff_external_syms (abfd)) |
| / symesz); |
| |
| csect->used_by_bfd = |
| (PTR) bfd_zalloc (abfd, sizeof (struct coff_section_tdata)); |
| if (csect->used_by_bfd == NULL) |
| goto error_return; |
| coff_section_data (abfd, csect)->tdata = |
| bfd_zalloc (abfd, sizeof (struct xcoff_section_tdata)); |
| if (coff_section_data (abfd, csect)->tdata == NULL) |
| goto error_return; |
| xcoff_section_data (abfd, csect)->first_symndx = csect_index; |
| |
| if (first_csect == NULL) |
| first_csect = csect; |
| |
| if (sym.n_sclass == C_EXT) |
| { |
| csect->flags |= SEC_IS_COMMON; |
| csect->_raw_size = 0; |
| section = csect; |
| value = aux.x_csect.x_scnlen.l; |
| } |
| |
| break; |
| } |
| |
| /* Check for magic symbol names. */ |
| if ((smtyp == XTY_SD || smtyp == XTY_CM) |
| && aux.x_csect.x_smclas != XMC_TC |
| && aux.x_csect.x_smclas != XMC_TD) |
| { |
| int i; |
| |
| i = -1; |
| if (name[0] == '_') |
| { |
| if (strcmp (name, "_text") == 0) |
| i = 0; |
| else if (strcmp (name, "_etext") == 0) |
| i = 1; |
| else if (strcmp (name, "_data") == 0) |
| i = 2; |
| else if (strcmp (name, "_edata") == 0) |
| i = 3; |
| else if (strcmp (name, "_end") == 0) |
| i = 4; |
| } |
| else if (name[0] == 'e' && strcmp (name, "end") == 0) |
| i = 5; |
| |
| if (i != -1) |
| xcoff_hash_table (info)->special_sections[i] = csect; |
| } |
| |
| /* Now we have enough information to add the symbol to the |
| linker hash table. */ |
| |
| if (sym.n_sclass == C_EXT) |
| { |
| boolean copy; |
| |
| BFD_ASSERT (section != NULL); |
| |
| /* We must copy the name into memory if we got it from the |
| syment itself, rather than the string table. */ |
| copy = default_copy; |
| if (sym._n._n_n._n_zeroes != 0 |
| || sym._n._n_n._n_offset == 0) |
| copy = true; |
| |
| /* The AIX linker appears to only detect multiple symbol |
| definitions when there is a reference to the symbol. If |
| a symbol is defined multiple times, and the only |
| references are from the same object file, the AIX linker |
| appears to permit it. It does not merge the different |
| definitions, but handles them independently. On the |
| other hand, if there is a reference, the linker reports |
| an error. |
| |
| This matters because the AIX <net/net_globals.h> header |
| file actually defines an initialized array, so we have to |
| actually permit that to work. |
| |
| Just to make matters even more confusing, the AIX linker |
| appears to permit multiple symbol definitions whenever |
| the second definition is in an archive rather than an |
| object file. This may be a consequence of the manner in |
| which it handles archives: I think it may load the entire |
| archive in as separate csects, and then let garbage |
| collection discard symbols. |
| |
| We also have to handle the case of statically linking a |
| shared object, which will cause symbol redefinitions, |
| although this is an easier case to detect. */ |
| |
| if (info->hash->creator == abfd->xvec) |
| { |
| if (! bfd_is_und_section (section)) |
| *sym_hash = xcoff_link_hash_lookup (xcoff_hash_table (info), |
| name, true, copy, false); |
| else |
| *sym_hash = ((struct xcoff_link_hash_entry *) |
| bfd_wrapped_link_hash_lookup (abfd, info, name, |
| true, copy, false)); |
| if (*sym_hash == NULL) |
| goto error_return; |
| if (((*sym_hash)->root.type == bfd_link_hash_defined |
| || (*sym_hash)->root.type == bfd_link_hash_defweak) |
| && ! bfd_is_und_section (section) |
| && ! bfd_is_com_section (section)) |
| { |
| /* This is a second definition of a defined symbol. */ |
| if ((abfd->flags & DYNAMIC) != 0 |
| && ((*sym_hash)->smclas != XMC_GL |
| || aux.x_csect.x_smclas == XMC_GL |
| || ((*sym_hash)->root.u.def.section->owner->flags |
| & DYNAMIC) == 0)) |
| { |
| /* The new symbol is from a shared library, and |
| either the existing symbol is not global |
| linkage code or this symbol is global linkage |
| code. If the existing symbol is global |
| linkage code and the new symbol is not, then |
| we want to use the new symbol. */ |
| section = bfd_und_section_ptr; |
| value = 0; |
| } |
| else if (((*sym_hash)->root.u.def.section->owner->flags |
| & DYNAMIC) != 0) |
| { |
| /* The existing symbol is from a shared library. |
| Replace it. */ |
| (*sym_hash)->root.type = bfd_link_hash_undefined; |
| (*sym_hash)->root.u.undef.abfd = |
| (*sym_hash)->root.u.def.section->owner; |
| } |
| else if (abfd->my_archive != NULL) |
| { |
| /* This is a redefinition in an object contained |
| in an archive. Just ignore it. See the |
| comment above. */ |
| section = bfd_und_section_ptr; |
| value = 0; |
| } |
| else if ((*sym_hash)->root.next != NULL |
| || info->hash->undefs_tail == &(*sym_hash)->root) |
| { |
| /* This symbol has been referenced. In this |
| case, we just continue and permit the |
| multiple definition error. See the comment |
| above about the behaviour of the AIX linker. */ |
| } |
| else if ((*sym_hash)->smclas == aux.x_csect.x_smclas) |
| { |
| /* The symbols are both csects of the same |
| class. There is at least a chance that this |
| is a semi-legitimate redefinition. */ |
| section = bfd_und_section_ptr; |
| value = 0; |
| (*sym_hash)->flags |= XCOFF_MULTIPLY_DEFINED; |
| } |
| } |
| else if (((*sym_hash)->flags & XCOFF_MULTIPLY_DEFINED) != 0 |
| && ((*sym_hash)->root.type == bfd_link_hash_defined |
| || (*sym_hash)->root.type == bfd_link_hash_defweak) |
| && (bfd_is_und_section (section) |
| || bfd_is_com_section (section))) |
| { |
| /* This is a reference to a multiply defined symbol. |
| Report the error now. See the comment above |
| about the behaviour of the AIX linker. We could |
| also do this with warning symbols, but I'm not |
| sure the XCOFF linker is wholly prepared to |
| handle them, and that would only be a warning, |
| not an error. */ |
| if (! ((*info->callbacks->multiple_definition) |
| (info, (*sym_hash)->root.root.string, |
| (bfd *) NULL, (asection *) NULL, 0, |
| (*sym_hash)->root.u.def.section->owner, |
| (*sym_hash)->root.u.def.section, |
| (*sym_hash)->root.u.def.value))) |
| goto error_return; |
| /* Try not to give this error too many times. */ |
| (*sym_hash)->flags &= ~XCOFF_MULTIPLY_DEFINED; |
| } |
| } |
| |
| /* _bfd_generic_link_add_one_symbol may call the linker to |
| generate an error message, and the linker may try to read |
| the symbol table to give a good error. Right now, the |
| line numbers are in an inconsistent state, since they are |
| counted both in the real sections and in the new csects. |
| We need to leave the count in the real sections so that |
| the linker can report the line number of the error |
| correctly, so temporarily clobber the link to the csects |
| so that the linker will not try to read the line numbers |
| a second time from the csects. */ |
| BFD_ASSERT (last_real->next == first_csect); |
| last_real->next = NULL; |
| if (! (_bfd_generic_link_add_one_symbol |
| (info, abfd, name, flags, section, value, |
| (const char *) NULL, copy, true, |
| (struct bfd_link_hash_entry **) sym_hash))) |
| goto error_return; |
| last_real->next = first_csect; |
| |
| if (smtyp == XTY_CM) |
| { |
| if ((*sym_hash)->root.type != bfd_link_hash_common |
| || (*sym_hash)->root.u.c.p->section != csect) |
| { |
| /* We don't need the common csect we just created. */ |
| csect->_raw_size = 0; |
| } |
| else |
| { |
| (*sym_hash)->root.u.c.p->alignment_power |
| = csect->alignment_power; |
| } |
| } |
| |
| if (info->hash->creator == abfd->xvec) |
| { |
| int flag; |
| |
| if (smtyp == XTY_ER || smtyp == XTY_CM) |
| flag = XCOFF_REF_REGULAR; |
| else |
| flag = XCOFF_DEF_REGULAR; |
| (*sym_hash)->flags |= flag; |
| |
| if ((*sym_hash)->smclas == XMC_UA |
| || flag == XCOFF_DEF_REGULAR) |
| (*sym_hash)->smclas = aux.x_csect.x_smclas; |
| } |
| } |
| |
| *csect_cache = csect; |
| |
| esym += (sym.n_numaux + 1) * symesz; |
| sym_hash += sym.n_numaux + 1; |
| csect_cache += sym.n_numaux + 1; |
| } |
| |
| BFD_ASSERT (last_real == NULL || last_real->next == first_csect); |
| |
| /* Make sure that we have seen all the relocs. */ |
| for (o = abfd->sections; o != first_csect; o = o->next) |
| { |
| /* Reset the section size and the line number count, since the |
| data is now attached to the csects. Don't reset the size of |
| the .debug section, since we need to read it below in |
| bfd_xcoff_size_dynamic_sections. */ |
| if (strcmp (bfd_get_section_name (abfd, o), ".debug") != 0) |
| o->_raw_size = 0; |
| o->lineno_count = 0; |
| |
| if ((o->flags & SEC_RELOC) != 0) |
| { |
| bfd_size_type i; |
| struct internal_reloc *rel; |
| asection **rel_csect; |
| |
| rel = reloc_info[o->target_index].relocs; |
| rel_csect = reloc_info[o->target_index].csects; |
| for (i = 0; i < o->reloc_count; i++, rel++, rel_csect++) |
| { |
| if (*rel_csect == NULL) |
| { |
| (*_bfd_error_handler) |
| (_("%s: reloc %s:%d not in csect"), |
| bfd_get_filename (abfd), o->name, i); |
| bfd_set_error (bfd_error_bad_value); |
| goto error_return; |
| } |
| |
| /* We identify all symbols which are called, so that we |
| can create glue code for calls to functions imported |
| from dynamic objects. */ |
| if (info->hash->creator == abfd->xvec |
| && *rel_csect != bfd_und_section_ptr |
| && (rel->r_type == R_BR |
| || rel->r_type == R_RBR) |
| && obj_xcoff_sym_hashes (abfd)[rel->r_symndx] != NULL) |
| { |
| struct xcoff_link_hash_entry *h; |
| |
| h = obj_xcoff_sym_hashes (abfd)[rel->r_symndx]; |
| h->flags |= XCOFF_CALLED; |
| /* If the symbol name starts with a period, it is |
| the code of a function. If the symbol is |
| currently undefined, then add an undefined symbol |
| for the function descriptor. This should do no |
| harm, because any regular object that defines the |
| function should also define the function |
| descriptor. It helps, because it means that we |
| will identify the function descriptor with a |
| dynamic object if a dynamic object defines it. */ |
| if (h->root.root.string[0] == '.' |
| && h->descriptor == NULL) |
| { |
| struct xcoff_link_hash_entry *hds; |
| |
| hds = xcoff_link_hash_lookup (xcoff_hash_table (info), |
| h->root.root.string + 1, |
| true, false, true); |
| if (hds == NULL) |
| goto error_return; |
| if (hds->root.type == bfd_link_hash_new) |
| { |
| if (! (_bfd_generic_link_add_one_symbol |
| (info, abfd, hds->root.root.string, |
| (flagword) 0, bfd_und_section_ptr, |
| (bfd_vma) 0, (const char *) NULL, false, |
| true, |
| (struct bfd_link_hash_entry **) &hds))) |
| goto error_return; |
| } |
| hds->flags |= XCOFF_DESCRIPTOR; |
| BFD_ASSERT ((hds->flags & XCOFF_CALLED) == 0 |
| && (h->flags & XCOFF_DESCRIPTOR) == 0); |
| hds->descriptor = h; |
| h->descriptor = hds; |
| } |
| } |
| } |
| |
| free (reloc_info[o->target_index].csects); |
| reloc_info[o->target_index].csects = NULL; |
| |
| /* Reset SEC_RELOC and the reloc_count, since the reloc |
| information is now attached to the csects. */ |
| o->flags &= ~SEC_RELOC; |
| o->reloc_count = 0; |
| |
| /* If we are not keeping memory, free the reloc information. */ |
| if (! info->keep_memory |
| && coff_section_data (abfd, o) != NULL |
| && coff_section_data (abfd, o)->relocs != NULL |
| && ! coff_section_data (abfd, o)->keep_relocs) |
| { |
| free (coff_section_data (abfd, o)->relocs); |
| coff_section_data (abfd, o)->relocs = NULL; |
| } |
| } |
| |
| /* Free up the line numbers. FIXME: We could cache these |
| somewhere for the final link, to avoid reading them again. */ |
| if (reloc_info[o->target_index].linenos != NULL) |
| { |
| free (reloc_info[o->target_index].linenos); |
| reloc_info[o->target_index].linenos = NULL; |
| } |
| } |
| |
| free (reloc_info); |
| |
| obj_coff_keep_syms (abfd) = keep_syms; |
| |
| return true; |
| |
| error_return: |
| if (reloc_info != NULL) |
| { |
| for (o = abfd->sections; o != NULL; o = o->next) |
| { |
| if (reloc_info[o->target_index].csects != NULL) |
| free (reloc_info[o->target_index].csects); |
| if (reloc_info[o->target_index].linenos != NULL) |
| free (reloc_info[o->target_index].linenos); |
| } |
| free (reloc_info); |
| } |
| obj_coff_keep_syms (abfd) = keep_syms; |
| return false; |
| } |
| |
| #undef N_TMASK |
| #undef N_BTSHFT |
| |
| /* This function is used to add symbols from a dynamic object to the |
| global symbol table. */ |
| |
| static boolean |
| xcoff_link_add_dynamic_symbols (abfd, info) |
| bfd *abfd; |
| struct bfd_link_info *info; |
| { |
| asection *lsec; |
| bfd_byte *buf; |
| struct internal_ldhdr ldhdr; |
| const char *strings; |
| struct external_ldsym *elsym, *elsymend; |
| struct xcoff_import_file *n; |
| const char *bname; |
| const char *mname; |
| const char *s; |
| unsigned int c; |
| struct xcoff_import_file **pp; |
| |
| /* We can only handle a dynamic object if we are generating an XCOFF |
| output file. */ |
| if (info->hash->creator != abfd->xvec) |
| { |
| (*_bfd_error_handler) |
| (_("%s: XCOFF shared object when not producing XCOFF output"), |
| bfd_get_filename (abfd)); |
| bfd_set_error (bfd_error_invalid_operation); |
| return false; |
| } |
| |
| /* The symbols we use from a dynamic object are not the symbols in |
| the normal symbol table, but, rather, the symbols in the export |
| table. If there is a global symbol in a dynamic object which is |
| not in the export table, the loader will not be able to find it, |
| so we don't want to find it either. Also, on AIX 4.1.3, shr.o in |
| libc.a has symbols in the export table which are not in the |
| symbol table. */ |
| |
| /* Read in the .loader section. FIXME: We should really use the |
| o_snloader field in the a.out header, rather than grabbing the |
| section by name. */ |
| lsec = bfd_get_section_by_name (abfd, ".loader"); |
| if (lsec == NULL) |
| { |
| (*_bfd_error_handler) |
| (_("%s: dynamic object with no .loader section"), |
| bfd_get_filename (abfd)); |
| bfd_set_error (bfd_error_no_symbols); |
| return false; |
| } |
| |
| if (! xcoff_get_section_contents (abfd, lsec)) |
| return false; |
| buf = coff_section_data (abfd, lsec)->contents; |
| |
| /* Remove the sections from this object, so that they do not get |
| included in the link. */ |
| abfd->sections = NULL; |
| |
| xcoff_swap_ldhdr_in (abfd, (struct external_ldhdr *) buf, &ldhdr); |
| |
| strings = (char *) buf + ldhdr.l_stoff; |
| |
| elsym = (struct external_ldsym *) (buf + LDHDRSZ); |
| elsymend = elsym + ldhdr.l_nsyms; |
| BFD_ASSERT (sizeof (struct external_ldsym) == LDSYMSZ); |
| for (; elsym < elsymend; elsym++) |
| { |
| struct internal_ldsym ldsym; |
| char nambuf[SYMNMLEN + 1]; |
| const char *name; |
| struct xcoff_link_hash_entry *h; |
| |
| xcoff_swap_ldsym_in (abfd, elsym, &ldsym); |
| |
| /* We are only interested in exported symbols. */ |
| if ((ldsym.l_smtype & L_EXPORT) == 0) |
| continue; |
| |
| if (ldsym._l._l_l._l_zeroes == 0) |
| name = strings + ldsym._l._l_l._l_offset; |
| else |
| { |
| memcpy (nambuf, ldsym._l._l_name, SYMNMLEN); |
| nambuf[SYMNMLEN] = '\0'; |
| name = nambuf; |
| } |
| |
| /* Normally we could not call xcoff_link_hash_lookup in an add |
| symbols routine, since we might not be using an XCOFF hash |
| table. However, we verified above that we are using an XCOFF |
| hash table. */ |
| |
| h = xcoff_link_hash_lookup (xcoff_hash_table (info), name, true, |
| true, true); |
| if (h == NULL) |
| return false; |
| |
| h->flags |= XCOFF_DEF_DYNAMIC; |
| |
| /* If the symbol is undefined, and the BFD it was found in is |
| not a dynamic object, change the BFD to this dynamic object, |
| so that we can get the correct import file ID. */ |
| if ((h->root.type == bfd_link_hash_undefined |
| || h->root.type == bfd_link_hash_undefweak) |
| && (h->root.u.undef.abfd == NULL |
| || (h->root.u.undef.abfd->flags & DYNAMIC) == 0)) |
| h->root.u.undef.abfd = abfd; |
| |
| if (h->root.type == bfd_link_hash_new) |
| { |
| h->root.type = bfd_link_hash_undefined; |
| h->root.u.undef.abfd = abfd; |
| /* We do not want to add this to the undefined symbol list. */ |
| } |
| |
| if (h->smclas == XMC_UA |
| || h->root.type == bfd_link_hash_undefined |
| || h->root.type == bfd_link_hash_undefweak) |
| h->smclas = ldsym.l_smclas; |
| |
| /* Unless this is an XMC_XO symbol, we don't bother to actually |
| define it, since we don't have a section to put it in anyhow. |
| Instead, the relocation routines handle the DEF_DYNAMIC flag |
| correctly. */ |
| |
| if (h->smclas == XMC_XO |
| && (h->root.type == bfd_link_hash_undefined |
| || h->root.type == bfd_link_hash_undefweak)) |
| { |
| /* This symbol has an absolute value. */ |
| h->root.type = bfd_link_hash_defined; |
| h->root.u.def.section = bfd_abs_section_ptr; |
| h->root.u.def.value = ldsym.l_value; |
| } |
| |
| /* If this symbol defines a function descriptor, then it |
| implicitly defines the function code as well. */ |
| if (h->smclas == XMC_DS |
| || (h->smclas == XMC_XO && name[0] != '.')) |
| h->flags |= XCOFF_DESCRIPTOR; |
| if ((h->flags & XCOFF_DESCRIPTOR) != 0) |
| { |
| struct xcoff_link_hash_entry *hds; |
| |
| hds = h->descriptor; |
| if (hds == NULL) |
| { |
| char *dsnm; |
| |
| dsnm = bfd_malloc (strlen (name) + 2); |
| if (dsnm == NULL) |
| return false; |
| dsnm[0] = '.'; |
| strcpy (dsnm + 1, name); |
| hds = xcoff_link_hash_lookup (xcoff_hash_table (info), dsnm, |
| true, true, true); |
| free (dsnm); |
| if (hds == NULL) |
| return false; |
| |
| if (hds->root.type == bfd_link_hash_new) |
| { |
| hds->root.type = bfd_link_hash_undefined; |
| hds->root.u.undef.abfd = abfd; |
| /* We do not want to add this to the undefined |
| symbol list. */ |
| } |
| |
| hds->descriptor = h; |
| h->descriptor = hds; |
| } |
| |
| hds->flags |= XCOFF_DEF_DYNAMIC; |
| if (hds->smclas == XMC_UA) |
| hds->smclas = XMC_PR; |
| |
| /* An absolute symbol appears to actually define code, not a |
| function descriptor. This is how some math functions are |
| implemented on AIX 4.1. */ |
| if (h->smclas == XMC_XO |
| && (hds->root.type == bfd_link_hash_undefined |
| || hds->root.type == bfd_link_hash_undefweak)) |
| { |
| hds->smclas = XMC_XO; |
| hds->root.type = bfd_link_hash_defined; |
| hds->root.u.def.section = bfd_abs_section_ptr; |
| hds->root.u.def.value = ldsym.l_value; |
| } |
| } |
| } |
| |
| if (buf != NULL && ! coff_section_data (abfd, lsec)->keep_contents) |
| { |
| free (coff_section_data (abfd, lsec)->contents); |
| coff_section_data (abfd, lsec)->contents = NULL; |
| } |
| |
| /* Record this file in the import files. */ |
| |
| n = ((struct xcoff_import_file *) |
| bfd_alloc (abfd, sizeof (struct xcoff_import_file))); |
| if (n == NULL) |
| return false; |
| n->next = NULL; |
| |
| /* For some reason, the path entry in the import file list for a |
| shared object appears to always be empty. The file name is the |
| base name. */ |
| n->path = ""; |
| if (abfd->my_archive == NULL) |
| { |
| bname = bfd_get_filename (abfd); |
| mname = ""; |
| } |
| else |
| { |
| bname = bfd_get_filename (abfd->my_archive); |
| mname = bfd_get_filename (abfd); |
| } |
| s = strrchr (bname, '/'); |
| if (s != NULL) |
| bname = s + 1; |
| n->file = bname; |
| n->member = mname; |
| |
| /* We start c at 1 because the first import file number is reserved |
| for LIBPATH. */ |
| for (pp = &xcoff_hash_table (info)->imports, c = 1; |
| *pp != NULL; |
| pp = &(*pp)->next, ++c) |
| ; |
| *pp = n; |
| |
| xcoff_data (abfd)->import_file_id = c; |
| |
| return true; |
| } |
| |
| /* Routines that are called after all the input files have been |
| handled, but before the sections are laid out in memory. */ |
| |
| /* Mark a symbol as not being garbage, including the section in which |
| it is defined. */ |
| |
| static INLINE boolean |
| xcoff_mark_symbol (info, h) |
| struct bfd_link_info *info; |
| struct xcoff_link_hash_entry *h; |
| { |
| if ((h->flags & XCOFF_MARK) != 0) |
| return true; |
| |
| h->flags |= XCOFF_MARK; |
| if (h->root.type == bfd_link_hash_defined |
| || h->root.type == bfd_link_hash_defweak) |
| { |
| asection *hsec; |
| |
| hsec = h->root.u.def.section; |
| if (! bfd_is_abs_section (hsec) |
| && (hsec->flags & SEC_MARK) == 0) |
| { |
| if (! xcoff_mark (info, hsec)) |
| return false; |
| } |
| } |
| |
| if (h->toc_section != NULL |
| && (h->toc_section->flags & SEC_MARK) == 0) |
| { |
| if (! xcoff_mark (info, h->toc_section)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* The mark phase of garbage collection. For a given section, mark |
| it, and all the sections which define symbols to which it refers. |
| Because this function needs to look at the relocs, we also count |
| the number of relocs which need to be copied into the .loader |
| section. */ |
| |
| static boolean |
| xcoff_mark (info, sec) |
| struct bfd_link_info *info; |
| asection *sec; |
| { |
| if (bfd_is_abs_section (sec) |
| || (sec->flags & SEC_MARK) != 0) |
| return true; |
| |
| sec->flags |= SEC_MARK; |
| |
| if (sec->owner->xvec == info->hash->creator |
| && coff_section_data (sec->owner, sec) != NULL |
| && xcoff_section_data (sec->owner, sec) != NULL) |
| { |
| register struct xcoff_link_hash_entry **hp, **hpend; |
| struct internal_reloc *rel, *relend; |
| |
| /* Mark all the symbols in this section. */ |
| |
| hp = (obj_xcoff_sym_hashes (sec->owner) |
| + xcoff_section_data (sec->owner, sec)->first_symndx); |
| hpend = (obj_xcoff_sym_hashes (sec->owner) |
| + xcoff_section_data (sec->owner, sec)->last_symndx); |
| for (; hp < hpend; hp++) |
| { |
| register struct xcoff_link_hash_entry *h; |
| |
| h = *hp; |
| if (h != NULL |
| && (h->flags & XCOFF_MARK) == 0) |
| { |
| if (! xcoff_mark_symbol (info, h)) |
| return false; |
| } |
| } |
| |
| /* Look through the section relocs. */ |
| |
| if ((sec->flags & SEC_RELOC) != 0 |
| && sec->reloc_count > 0) |
| { |
| rel = xcoff_read_internal_relocs (sec->owner, sec, true, |
| (bfd_byte *) NULL, false, |
| (struct internal_reloc *) NULL); |
| if (rel == NULL) |
| return false; |
| relend = rel + sec->reloc_count; |
| for (; rel < relend; rel++) |
| { |
| asection *rsec; |
| struct xcoff_link_hash_entry *h; |
| |
| if ((unsigned int) rel->r_symndx |
| > obj_raw_syment_count (sec->owner)) |
|