| /* BFD back-end for IBM RS/6000 "XCOFF" files. |
| Copyright (C) 1990-2024 Free Software Foundation, Inc. |
| Written by Metin G. Ozisik, Mimi Phuong-Thao Vo, and John Gilmore. |
| Archive support from Damon A. Permezel. |
| Contributed by IBM Corporation and Cygnus Support. |
| |
| This file is part of BFD, the Binary File Descriptor library. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 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., 51 Franklin Street - Fifth Floor, Boston, |
| MA 02110-1301, USA. */ |
| |
| #include "sysdep.h" |
| #include "libiberty.h" |
| #include "bfd.h" |
| #include "bfdlink.h" |
| #include "libbfd.h" |
| #include "coff/internal.h" |
| #include "coff/xcoff.h" |
| #include "coff/rs6000.h" |
| #include "libcoff.h" |
| #include "libxcoff.h" |
| |
| extern bool _bfd_xcoff_mkobject (bfd *); |
| extern bool _bfd_xcoff_copy_private_bfd_data (bfd *, bfd *); |
| extern bool _bfd_xcoff_is_local_label_name (bfd *, const char *); |
| extern reloc_howto_type *_bfd_xcoff_reloc_type_lookup |
| (bfd *, bfd_reloc_code_real_type); |
| extern bool _bfd_xcoff_slurp_armap (bfd *); |
| extern bfd_cleanup _bfd_xcoff_archive_p (bfd *); |
| extern void * _bfd_xcoff_read_ar_hdr (bfd *); |
| extern bfd *_bfd_xcoff_openr_next_archived_file (bfd *, bfd *); |
| extern int _bfd_xcoff_stat_arch_elt (bfd *, struct stat *); |
| extern bool _bfd_xcoff_write_armap |
| (bfd *, unsigned int, struct orl *, unsigned int, int); |
| extern bool _bfd_xcoff_write_archive_contents (bfd *); |
| extern int _bfd_xcoff_sizeof_headers (bfd *, struct bfd_link_info *); |
| extern void _bfd_xcoff_swap_sym_in (bfd *, void *, void *); |
| extern unsigned int _bfd_xcoff_swap_sym_out (bfd *, void *, void *); |
| extern void _bfd_xcoff_swap_aux_in (bfd *, void *, int, int, int, int, void *); |
| extern unsigned int _bfd_xcoff_swap_aux_out |
| (bfd *, void *, int, int, int, int, void *); |
| static void xcoff_swap_reloc_in (bfd *, void *, void *); |
| static unsigned int xcoff_swap_reloc_out (bfd *, void *, void *); |
| |
| /* Forward declare xcoff_rtype2howto for coffcode.h macro. */ |
| void xcoff_rtype2howto (arelent *, struct internal_reloc *); |
| |
| /* coffcode.h needs these to be defined. */ |
| #define RS6000COFF_C 1 |
| |
| #define SELECT_RELOC(internal, howto) \ |
| { \ |
| internal.r_type = howto->type; \ |
| internal.r_size = \ |
| ((howto->complain_on_overflow == complain_overflow_signed \ |
| ? 0x80 \ |
| : 0) \ |
| | (howto->bitsize - 1)); \ |
| } |
| |
| #define COFF_DEFAULT_SECTION_ALIGNMENT_POWER (3) |
| #define COFF_LONG_FILENAMES |
| #define NO_COFF_SYMBOLS |
| #define RTYPE2HOWTO(cache_ptr, dst) xcoff_rtype2howto (cache_ptr, dst) |
| #define coff_mkobject _bfd_xcoff_mkobject |
| #define coff_bfd_is_local_label_name _bfd_xcoff_is_local_label_name |
| #ifdef AIX_CORE |
| extern bfd_cleanup rs6000coff_core_p (bfd *abfd); |
| extern bool rs6000coff_core_file_matches_executable_p |
| (bfd *cbfd, bfd *ebfd); |
| extern char *rs6000coff_core_file_failing_command (bfd *abfd); |
| extern int rs6000coff_core_file_failing_signal (bfd *abfd); |
| #define CORE_FILE_P rs6000coff_core_p |
| #define coff_core_file_failing_command \ |
| rs6000coff_core_file_failing_command |
| #define coff_core_file_failing_signal \ |
| rs6000coff_core_file_failing_signal |
| #define coff_core_file_matches_executable_p \ |
| rs6000coff_core_file_matches_executable_p |
| #define coff_core_file_pid \ |
| _bfd_nocore_core_file_pid |
| #else |
| #define CORE_FILE_P _bfd_dummy_target |
| #define coff_core_file_failing_command \ |
| _bfd_nocore_core_file_failing_command |
| #define coff_core_file_failing_signal \ |
| _bfd_nocore_core_file_failing_signal |
| #define coff_core_file_matches_executable_p \ |
| _bfd_nocore_core_file_matches_executable_p |
| #define coff_core_file_pid \ |
| _bfd_nocore_core_file_pid |
| #endif |
| #define coff_SWAP_sym_in _bfd_xcoff_swap_sym_in |
| #define coff_SWAP_sym_out _bfd_xcoff_swap_sym_out |
| #define coff_SWAP_aux_in _bfd_xcoff_swap_aux_in |
| #define coff_SWAP_aux_out _bfd_xcoff_swap_aux_out |
| #define coff_swap_reloc_in xcoff_swap_reloc_in |
| #define coff_swap_reloc_out xcoff_swap_reloc_out |
| #define NO_COFF_RELOCS |
| |
| #ifndef bfd_pe_print_pdata |
| #define bfd_pe_print_pdata NULL |
| #endif |
| |
| #include "coffcode.h" |
| |
| /* The main body of code is in coffcode.h. */ |
| |
| static const char *normalize_filename (bfd *); |
| static bool xcoff_write_armap_old |
| (bfd *, unsigned int, struct orl *, unsigned int, int); |
| static bool xcoff_write_armap_big |
| (bfd *, unsigned int, struct orl *, unsigned int, int); |
| static bool xcoff_write_archive_contents_old (bfd *); |
| static bool xcoff_write_archive_contents_big (bfd *); |
| static void xcoff_swap_ldhdr_in (bfd *, const void *, struct internal_ldhdr *); |
| static void xcoff_swap_ldhdr_out (bfd *, const struct internal_ldhdr *, void *); |
| static void xcoff_swap_ldsym_in (bfd *, const void *, struct internal_ldsym *); |
| static void xcoff_swap_ldsym_out (bfd *, const struct internal_ldsym *, void *); |
| static void xcoff_swap_ldrel_in (bfd *, const void *, struct internal_ldrel *); |
| static void xcoff_swap_ldrel_out (bfd *, const struct internal_ldrel *, void *); |
| static bool xcoff_ppc_relocate_section |
| (bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *, |
| struct internal_reloc *, struct internal_syment *, asection **); |
| static bool _bfd_xcoff_put_ldsymbol_name |
| (bfd *, struct xcoff_loader_info *, struct internal_ldsym *, const char *); |
| static asection *xcoff_create_csect_from_smclas |
| (bfd *, union internal_auxent *, const char *); |
| static bool xcoff_is_lineno_count_overflow (bfd *, bfd_vma); |
| static bool xcoff_is_reloc_count_overflow (bfd *, bfd_vma); |
| static bfd_vma xcoff_loader_symbol_offset (bfd *, struct internal_ldhdr *); |
| static bfd_vma xcoff_loader_reloc_offset (bfd *, struct internal_ldhdr *); |
| static bool xcoff_generate_rtinit |
| (bfd *, const char *, const char *, bool); |
| static bool do_pad (bfd *, unsigned int); |
| static bool do_copy (bfd *, bfd *); |
| |
| /* Relocation functions */ |
| static xcoff_reloc_function xcoff_reloc_type_br; |
| |
| static xcoff_complain_function xcoff_complain_overflow_dont_func; |
| static xcoff_complain_function xcoff_complain_overflow_bitfield_func; |
| static xcoff_complain_function xcoff_complain_overflow_signed_func; |
| static xcoff_complain_function xcoff_complain_overflow_unsigned_func; |
| |
| xcoff_reloc_function *const |
| xcoff_calculate_relocation[XCOFF_MAX_CALCULATE_RELOCATION] = |
| { |
| xcoff_reloc_type_pos, /* R_POS (0x00) */ |
| xcoff_reloc_type_neg, /* R_NEG (0x01) */ |
| xcoff_reloc_type_rel, /* R_REL (0x02) */ |
| xcoff_reloc_type_toc, /* R_TOC (0x03) */ |
| xcoff_reloc_type_toc, /* R_TRL (0x04) */ |
| xcoff_reloc_type_toc, /* R_GL (0x05) */ |
| xcoff_reloc_type_toc, /* R_TCL (0x06) */ |
| xcoff_reloc_type_fail, /* (0x07) */ |
| xcoff_reloc_type_ba, /* R_BA (0x08) */ |
| xcoff_reloc_type_fail, /* (0x09) */ |
| xcoff_reloc_type_br, /* R_BR (0x0a) */ |
| xcoff_reloc_type_fail, /* (0x0b) */ |
| xcoff_reloc_type_pos, /* R_RL (0x0c) */ |
| xcoff_reloc_type_pos, /* R_RLA (0x0d) */ |
| xcoff_reloc_type_fail, /* (0x0e) */ |
| xcoff_reloc_type_noop, /* R_REF (0x0f) */ |
| xcoff_reloc_type_fail, /* (0x10) */ |
| xcoff_reloc_type_fail, /* (0x11) */ |
| xcoff_reloc_type_fail, /* (0x12) */ |
| xcoff_reloc_type_toc, /* R_TRLA (0x13) */ |
| xcoff_reloc_type_fail, /* R_RRTBI (0x14) */ |
| xcoff_reloc_type_fail, /* R_RRTBA (0x15) */ |
| xcoff_reloc_type_ba, /* R_CAI (0x16) */ |
| xcoff_reloc_type_crel, /* R_CREL (0x17) */ |
| xcoff_reloc_type_ba, /* R_RBA (0x18) */ |
| xcoff_reloc_type_ba, /* R_RBAC (0x19) */ |
| xcoff_reloc_type_br, /* R_RBR (0x1a) */ |
| xcoff_reloc_type_ba, /* R_RBRC (0x1b) */ |
| xcoff_reloc_type_fail, /* (0x1c) */ |
| xcoff_reloc_type_fail, /* (0x1d) */ |
| xcoff_reloc_type_fail, /* (0x1e) */ |
| xcoff_reloc_type_fail, /* (0x1f) */ |
| xcoff_reloc_type_tls, /* R_TLS (0x20) */ |
| xcoff_reloc_type_tls, /* R_TLS_IE (0x21) */ |
| xcoff_reloc_type_tls, /* R_TLS_LD (0x22) */ |
| xcoff_reloc_type_tls, /* R_TLS_LE (0x23) */ |
| xcoff_reloc_type_tls, /* R_TLSM (0x24) */ |
| xcoff_reloc_type_tls, /* R_TLSML (0x25) */ |
| xcoff_reloc_type_fail, /* (0x26) */ |
| xcoff_reloc_type_fail, /* (0x27) */ |
| xcoff_reloc_type_fail, /* (0x28) */ |
| xcoff_reloc_type_fail, /* (0x29) */ |
| xcoff_reloc_type_fail, /* (0x2a) */ |
| xcoff_reloc_type_fail, /* (0x2b) */ |
| xcoff_reloc_type_fail, /* (0x2c) */ |
| xcoff_reloc_type_fail, /* (0x2d) */ |
| xcoff_reloc_type_fail, /* (0x2e) */ |
| xcoff_reloc_type_fail, /* (0x2f) */ |
| xcoff_reloc_type_toc, /* R_TOCU (0x30) */ |
| xcoff_reloc_type_toc, /* R_TOCL (0x31) */ |
| }; |
| |
| xcoff_complain_function *const |
| xcoff_complain_overflow[XCOFF_MAX_COMPLAIN_OVERFLOW] = |
| { |
| xcoff_complain_overflow_dont_func, |
| xcoff_complain_overflow_bitfield_func, |
| xcoff_complain_overflow_signed_func, |
| xcoff_complain_overflow_unsigned_func, |
| }; |
| |
| /* Information about one member of an archive. */ |
| struct member_layout |
| { |
| /* The archive member that this structure describes. */ |
| bfd *member; |
| |
| /* The number of bytes of padding that must be inserted before the |
| start of the member in order to ensure that the section contents |
| are correctly aligned. */ |
| unsigned int leading_padding; |
| |
| /* The offset of MEMBER from the start of the archive (i.e. the end |
| of the leading padding). */ |
| file_ptr offset; |
| |
| /* The normalized name of MEMBER. */ |
| const char *name; |
| |
| /* The length of NAME, without padding. */ |
| bfd_size_type namlen; |
| |
| /* The length of NAME, with padding. */ |
| bfd_size_type padded_namlen; |
| |
| /* The size of MEMBER's header, including the name and magic sequence. */ |
| bfd_size_type header_size; |
| |
| /* The size of the MEMBER's contents. */ |
| bfd_size_type contents_size; |
| |
| /* The number of bytes of padding that must be inserted after MEMBER |
| in order to preserve even alignment. */ |
| bfd_size_type trailing_padding; |
| }; |
| |
| /* A structure used for iterating over the members of an archive. */ |
| struct archive_iterator |
| { |
| /* The archive itself. */ |
| bfd *archive; |
| |
| /* Information about the current archive member. */ |
| struct member_layout current; |
| |
| /* Information about the next archive member. MEMBER is null if there |
| are no more archive members, in which case OFFSET is the offset of |
| the first unused byte. */ |
| struct member_layout next; |
| }; |
| |
| /* Initialize INFO so that it describes member MEMBER of archive ARCHIVE. |
| OFFSET is the even-padded offset of MEMBER, not including any leading |
| padding needed for section alignment. */ |
| |
| static void |
| member_layout_init (struct member_layout *info, bfd *archive, |
| bfd *member, file_ptr offset) |
| { |
| info->member = member; |
| info->leading_padding = 0; |
| if (member) |
| { |
| info->name = normalize_filename (member); |
| info->namlen = strlen (info->name); |
| info->padded_namlen = info->namlen + (info->namlen & 1); |
| if (xcoff_big_format_p (archive)) |
| info->header_size = SIZEOF_AR_HDR_BIG; |
| else |
| info->header_size = SIZEOF_AR_HDR; |
| info->header_size += info->padded_namlen + SXCOFFARFMAG; |
| info->contents_size = arelt_size (member); |
| info->trailing_padding = info->contents_size & 1; |
| |
| if (bfd_check_format (member, bfd_object) |
| && bfd_get_flavour (member) == bfd_target_xcoff_flavour |
| && (member->flags & DYNAMIC) != 0) |
| info->leading_padding |
| = (-(offset + info->header_size) |
| & ((1 << bfd_xcoff_text_align_power (member)) - 1)); |
| } |
| info->offset = offset + info->leading_padding; |
| } |
| |
| /* Set up ITERATOR to iterate through archive ARCHIVE. */ |
| |
| static void |
| archive_iterator_begin (struct archive_iterator *iterator, |
| bfd *archive) |
| { |
| iterator->archive = archive; |
| member_layout_init (&iterator->next, archive, archive->archive_head, |
| xcoff_big_format_p (archive) |
| ? SIZEOF_AR_FILE_HDR_BIG |
| : SIZEOF_AR_FILE_HDR); |
| } |
| |
| /* Make ITERATOR visit the first unvisited archive member. Return true |
| on success; return false if all members have been visited. */ |
| |
| static bool |
| archive_iterator_next (struct archive_iterator *iterator) |
| { |
| if (!iterator->next.member) |
| return false; |
| |
| iterator->current = iterator->next; |
| member_layout_init (&iterator->next, iterator->archive, |
| iterator->current.member->archive_next, |
| iterator->current.offset |
| + iterator->current.header_size |
| + iterator->current.contents_size |
| + iterator->current.trailing_padding); |
| return true; |
| } |
| |
| /* We use our own tdata type. Its first field is the COFF tdata type, |
| so the COFF routines are compatible. */ |
| |
| bool |
| _bfd_xcoff_mkobject (bfd *abfd) |
| { |
| coff_data_type *coff; |
| size_t amt = sizeof (struct xcoff_tdata); |
| |
| abfd->tdata.xcoff_obj_data = (struct xcoff_tdata *) bfd_zalloc (abfd, amt); |
| if (abfd->tdata.xcoff_obj_data == NULL) |
| return false; |
| coff = coff_data (abfd); |
| coff->symbols = (coff_symbol_type *) NULL; |
| coff->conversion_table = (unsigned int *) NULL; |
| coff->raw_syments = (struct coff_ptr_struct *) NULL; |
| coff->relocbase = 0; |
| |
| xcoff_data (abfd)->modtype = ('1' << 8) | 'L'; |
| |
| /* We set cputype to -1 to indicate that it has not been |
| initialized. */ |
| xcoff_data (abfd)->cputype = -1; |
| |
| xcoff_data (abfd)->csects = NULL; |
| xcoff_data (abfd)->debug_indices = NULL; |
| |
| /* text section alignment is different than the default */ |
| bfd_xcoff_text_align_power (abfd) = 2; |
| |
| return true; |
| } |
| |
| /* Copy XCOFF data from one BFD to another. */ |
| |
| bool |
| _bfd_xcoff_copy_private_bfd_data (bfd *ibfd, bfd *obfd) |
| { |
| struct xcoff_tdata *ix, *ox; |
| asection *sec; |
| |
| if (ibfd->xvec != obfd->xvec) |
| return true; |
| ix = xcoff_data (ibfd); |
| ox = xcoff_data (obfd); |
| ox->full_aouthdr = ix->full_aouthdr; |
| ox->toc = ix->toc; |
| if (ix->sntoc == 0) |
| ox->sntoc = 0; |
| else |
| { |
| sec = coff_section_from_bfd_index (ibfd, ix->sntoc); |
| if (sec == NULL || sec->output_section == NULL) |
| ox->sntoc = 0; |
| else |
| ox->sntoc = sec->output_section->target_index; |
| } |
| if (ix->snentry == 0) |
| ox->snentry = 0; |
| else |
| { |
| sec = coff_section_from_bfd_index (ibfd, ix->snentry); |
| if (sec == NULL || sec->output_section == NULL) |
| ox->snentry = 0; |
| else |
| ox->snentry = sec->output_section->target_index; |
| } |
| bfd_xcoff_text_align_power (obfd) = bfd_xcoff_text_align_power (ibfd); |
| bfd_xcoff_data_align_power (obfd) = bfd_xcoff_data_align_power (ibfd); |
| ox->modtype = ix->modtype; |
| ox->cputype = ix->cputype; |
| ox->maxdata = ix->maxdata; |
| ox->maxstack = ix->maxstack; |
| return true; |
| } |
| |
| /* I don't think XCOFF really has a notion of local labels based on |
| name. This will mean that ld -X doesn't actually strip anything. |
| The AIX native linker does not have a -X option, and it ignores the |
| -x option. */ |
| |
| bool |
| _bfd_xcoff_is_local_label_name (bfd *abfd ATTRIBUTE_UNUSED, |
| const char *name ATTRIBUTE_UNUSED) |
| { |
| return false; |
| } |
| |
| void |
| _bfd_xcoff_swap_sym_in (bfd *abfd, void * ext1, void * in1) |
| { |
| SYMENT *ext = (SYMENT *)ext1; |
| struct internal_syment * in = (struct internal_syment *)in1; |
| |
| if (ext->e.e_name[0] != 0) |
| { |
| memcpy (in->_n._n_name, ext->e.e_name, SYMNMLEN); |
| } |
| else |
| { |
| in->_n._n_n._n_zeroes = 0; |
| in->_n._n_n._n_offset = H_GET_32 (abfd, ext->e.e.e_offset); |
| } |
| |
| in->n_value = H_GET_32 (abfd, ext->e_value); |
| in->n_scnum = (short) H_GET_16 (abfd, ext->e_scnum); |
| in->n_type = H_GET_16 (abfd, ext->e_type); |
| in->n_sclass = H_GET_8 (abfd, ext->e_sclass); |
| in->n_numaux = H_GET_8 (abfd, ext->e_numaux); |
| } |
| |
| unsigned int |
| _bfd_xcoff_swap_sym_out (bfd *abfd, void * inp, void * extp) |
| { |
| struct internal_syment *in = (struct internal_syment *)inp; |
| SYMENT *ext =(SYMENT *)extp; |
| |
| if (in->_n._n_name[0] != 0) |
| { |
| memcpy (ext->e.e_name, in->_n._n_name, SYMNMLEN); |
| } |
| else |
| { |
| H_PUT_32 (abfd, 0, ext->e.e.e_zeroes); |
| H_PUT_32 (abfd, in->_n._n_n._n_offset, ext->e.e.e_offset); |
| } |
| |
| H_PUT_32 (abfd, in->n_value, ext->e_value); |
| H_PUT_16 (abfd, in->n_scnum, ext->e_scnum); |
| H_PUT_16 (abfd, in->n_type, ext->e_type); |
| H_PUT_8 (abfd, in->n_sclass, ext->e_sclass); |
| H_PUT_8 (abfd, in->n_numaux, ext->e_numaux); |
| return bfd_coff_symesz (abfd); |
| } |
| |
| void |
| _bfd_xcoff_swap_aux_in (bfd *abfd, void * ext1, int type ATTRIBUTE_UNUSED, |
| int in_class, int indx, int numaux, void * in1) |
| { |
| AUXENT * ext = (AUXENT *)ext1; |
| union internal_auxent *in = (union internal_auxent *)in1; |
| |
| switch (in_class) |
| { |
| default: |
| _bfd_error_handler |
| /* xgettext: c-format */ |
| (_("%pB: unsupported swap_aux_in for storage class %#x"), |
| abfd, (unsigned int) in_class); |
| bfd_set_error (bfd_error_bad_value); |
| break; |
| |
| case C_FILE: |
| if (ext->x_file.x_n.x_fname[0] == 0) |
| { |
| in->x_file.x_n.x_n.x_zeroes = 0; |
| in->x_file.x_n.x_n.x_offset = |
| H_GET_32 (abfd, ext->x_file.x_n.x_n.x_offset); |
| } |
| else |
| memcpy (in->x_file.x_n.x_fname, ext->x_file.x_n.x_fname, FILNMLEN); |
| in->x_file.x_ftype = H_GET_8 (abfd, ext->x_file.x_ftype); |
| break; |
| |
| /* RS/6000 "csect" auxents. |
| There is always a CSECT auxiliary entry. But functions can |
| have FCN ones too. In this case, CSECT is always the last |
| one. */ |
| case C_EXT: |
| case C_AIX_WEAKEXT: |
| case C_HIDEXT: |
| if (indx + 1 == numaux) |
| { |
| in->x_csect.x_scnlen.u64 = H_GET_32 (abfd, ext->x_csect.x_scnlen); |
| in->x_csect.x_parmhash = H_GET_32 (abfd, ext->x_csect.x_parmhash); |
| in->x_csect.x_snhash = H_GET_16 (abfd, ext->x_csect.x_snhash); |
| /* We don't have to hack bitfields in x_smtyp because it's |
| defined by shifts-and-ands, which are equivalent on all |
| byte orders. */ |
| in->x_csect.x_smtyp = H_GET_8 (abfd, ext->x_csect.x_smtyp); |
| in->x_csect.x_smclas = H_GET_8 (abfd, ext->x_csect.x_smclas); |
| in->x_csect.x_stab = H_GET_32 (abfd, ext->x_csect.x_stab); |
| in->x_csect.x_snstab = H_GET_16 (abfd, ext->x_csect.x_snstab); |
| } |
| else |
| { |
| /* x_exptr isn't supported. */ |
| in->x_sym.x_misc.x_fsize |
| = H_GET_32 (abfd, ext->x_fcn.x_fsize); |
| in->x_sym.x_fcnary.x_fcn.x_lnnoptr |
| = H_GET_32 (abfd, ext->x_fcn.x_lnnoptr); |
| in->x_sym.x_fcnary.x_fcn.x_endndx.u32 |
| = H_GET_32 (abfd, ext->x_fcn.x_endndx); |
| } |
| break; |
| |
| case C_STAT: |
| in->x_scn.x_scnlen = H_GET_32 (abfd, ext->x_scn.x_scnlen); |
| in->x_scn.x_nreloc = H_GET_16 (abfd, ext->x_scn.x_nreloc); |
| in->x_scn.x_nlinno = H_GET_16 (abfd, ext->x_scn.x_nlinno); |
| /* PE defines some extra fields; we zero them out for |
| safety. */ |
| in->x_scn.x_checksum = 0; |
| in->x_scn.x_associated = 0; |
| in->x_scn.x_comdat = 0; |
| break; |
| |
| case C_BLOCK: |
| case C_FCN: |
| in->x_sym.x_misc.x_lnsz.x_lnno |
| = H_GET_32 (abfd, ext->x_sym.x_lnno); |
| break; |
| |
| case C_DWARF: |
| in->x_sect.x_scnlen = H_GET_32 (abfd, ext->x_sect.x_scnlen); |
| in->x_sect.x_nreloc = H_GET_32 (abfd, ext->x_sect.x_nreloc); |
| break; |
| |
| } |
| } |
| |
| unsigned int |
| _bfd_xcoff_swap_aux_out (bfd *abfd, void * inp, int type ATTRIBUTE_UNUSED, |
| int in_class, int indx, int numaux, void * extp) |
| { |
| union internal_auxent *in = (union internal_auxent *)inp; |
| AUXENT *ext = (AUXENT *)extp; |
| |
| memset (ext, 0, bfd_coff_auxesz (abfd)); |
| switch (in_class) |
| { |
| default: |
| _bfd_error_handler |
| /* xgettext: c-format */ |
| (_("%pB: unsupported swap_aux_out for storage class %#x"), |
| abfd, (unsigned int) in_class); |
| bfd_set_error (bfd_error_bad_value); |
| break; |
| |
| case C_FILE: |
| if (in->x_file.x_n.x_fname[0] == 0) |
| { |
| H_PUT_32 (abfd, 0, ext->x_file.x_n.x_n.x_zeroes); |
| H_PUT_32 (abfd, in->x_file.x_n.x_n.x_offset, |
| ext->x_file.x_n.x_n.x_offset); |
| } |
| else |
| memcpy (ext->x_file.x_n.x_fname, in->x_file.x_n.x_fname, FILNMLEN); |
| H_PUT_8 (abfd, in->x_file.x_ftype, ext->x_file.x_ftype); |
| break; |
| |
| /* RS/6000 "csect" auxents */ |
| case C_EXT: |
| case C_AIX_WEAKEXT: |
| case C_HIDEXT: |
| if (indx + 1 == numaux) |
| { |
| H_PUT_32 (abfd, in->x_csect.x_scnlen.u64, ext->x_csect.x_scnlen); |
| H_PUT_32 (abfd, in->x_csect.x_parmhash, ext->x_csect.x_parmhash); |
| H_PUT_16 (abfd, in->x_csect.x_snhash, ext->x_csect.x_snhash); |
| /* We don't have to hack bitfields in x_smtyp because it's |
| defined by shifts-and-ands, which are equivalent on all |
| byte orders. */ |
| H_PUT_8 (abfd, in->x_csect.x_smtyp, ext->x_csect.x_smtyp); |
| H_PUT_8 (abfd, in->x_csect.x_smclas, ext->x_csect.x_smclas); |
| H_PUT_32 (abfd, in->x_csect.x_stab, ext->x_csect.x_stab); |
| H_PUT_16 (abfd, in->x_csect.x_snstab, ext->x_csect.x_snstab); |
| } |
| else |
| { |
| H_PUT_32 (abfd, in->x_sym.x_misc.x_fsize, ext->x_fcn.x_fsize); |
| H_PUT_32 (abfd, in->x_sym.x_fcnary.x_fcn.x_lnnoptr, |
| ext->x_fcn.x_lnnoptr); |
| H_PUT_32 (abfd, in->x_sym.x_fcnary.x_fcn.x_endndx.u32, |
| ext->x_fcn.x_endndx); |
| } |
| break; |
| |
| case C_STAT: |
| H_PUT_32 (abfd, in->x_scn.x_scnlen, ext->x_scn.x_scnlen); |
| H_PUT_16 (abfd, in->x_scn.x_nreloc, ext->x_scn.x_nreloc); |
| H_PUT_16 (abfd, in->x_scn.x_nlinno, ext->x_scn.x_nlinno); |
| break; |
| |
| case C_BLOCK: |
| case C_FCN: |
| H_PUT_32 (abfd, in->x_sym.x_misc.x_lnsz.x_lnno, ext->x_sym.x_lnno); |
| break; |
| |
| case C_DWARF: |
| H_PUT_32 (abfd, in->x_sect.x_scnlen, ext->x_sect.x_scnlen); |
| H_PUT_32 (abfd, in->x_sect.x_nreloc, ext->x_sect.x_nreloc); |
| break; |
| } |
| |
| return bfd_coff_auxesz (abfd); |
| } |
| |
| /* The XCOFF reloc table. |
| XCOFF relocations aren't defined only by the type field r_type. |
| The bitsize and whether they are signed or not, are defined by |
| r_size field. Thus, it's complicated to create a constant |
| table reference every possible relocation. |
| This table contains the "default" relocation and few modified |
| relocations what were already there. It's enough when |
| xcoff_rtype2howto is called. |
| For relocations from an input bfd to an output bfd, the default |
| relocation is retrieved and when manually adapted. |
| |
| For now, it seems to be enought. */ |
| |
| reloc_howto_type xcoff_howto_table[] = |
| { |
| /* 0x00: Standard 32 bit relocation. */ |
| HOWTO (R_POS, /* type */ |
| 0, /* rightshift */ |
| 4, /* size */ |
| 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 */ |
| |
| /* 0x01: 32 bit relocation, but store negative value. */ |
| HOWTO (R_NEG, /* type */ |
| 0, /* rightshift */ |
| -4, /* size */ |
| 32, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_NEG", /* name */ |
| true, /* partial_inplace */ |
| 0xffffffff, /* src_mask */ |
| 0xffffffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* 0x02: 32 bit PC relative relocation. */ |
| HOWTO (R_REL, /* type */ |
| 0, /* rightshift */ |
| 4, /* size */ |
| 32, /* bitsize */ |
| true, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_signed, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_REL", /* name */ |
| true, /* partial_inplace */ |
| 0xffffffff, /* src_mask */ |
| 0xffffffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* 0x03: 16 bit TOC relative relocation. */ |
| HOWTO (R_TOC, /* type */ |
| 0, /* rightshift */ |
| 2, /* size */ |
| 16, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_TOC", /* name */ |
| true, /* partial_inplace */ |
| 0, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* 0x04: Same as R_TOC */ |
| HOWTO (R_TRL, /* type */ |
| 0, /* rightshift */ |
| 2, /* size */ |
| 16, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_TRL", /* name */ |
| true, /* partial_inplace */ |
| 0, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* 0x05: External TOC relative symbol. */ |
| HOWTO (R_GL, /* type */ |
| 0, /* rightshift */ |
| 2, /* size */ |
| 16, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_GL", /* name */ |
| true, /* partial_inplace */ |
| 0, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* 0x06: Local TOC relative symbol. */ |
| HOWTO (R_TCL, /* type */ |
| 0, /* rightshift */ |
| 2, /* size */ |
| 16, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_TCL", /* name */ |
| true, /* partial_inplace */ |
| 0, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| EMPTY_HOWTO (7), |
| |
| /* 0x08: Same as R_RBA. */ |
| HOWTO (R_BA, /* type */ |
| 0, /* rightshift */ |
| 4, /* size */ |
| 26, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_BA_26", /* name */ |
| true, /* partial_inplace */ |
| 0x03fffffc, /* src_mask */ |
| 0x03fffffc, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| EMPTY_HOWTO (9), |
| |
| /* 0x0a: Same as R_RBR. */ |
| HOWTO (R_BR, /* type */ |
| 0, /* rightshift */ |
| 4, /* size */ |
| 26, /* bitsize */ |
| true, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_signed, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_BR", /* name */ |
| true, /* partial_inplace */ |
| 0x03fffffc, /* src_mask */ |
| 0x03fffffc, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| EMPTY_HOWTO (0xb), |
| |
| /* 0x0c: Same as R_POS. */ |
| HOWTO (R_RL, /* type */ |
| 0, /* rightshift */ |
| 4, /* size */ |
| 32, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_RL", /* name */ |
| true, /* partial_inplace */ |
| 0xffffffff, /* src_mask */ |
| 0xffffffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* 0x0d: Same as R_POS. */ |
| HOWTO (R_RLA, /* type */ |
| 0, /* rightshift */ |
| 4, /* size */ |
| 32, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_RLA", /* name */ |
| true, /* partial_inplace */ |
| 0xffffffff, /* src_mask */ |
| 0xffffffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| EMPTY_HOWTO (0xe), |
| |
| /* 0x0f: Non-relocating reference. Bitsize is 1 so that r_rsize is 0. */ |
| HOWTO (R_REF, /* type */ |
| 0, /* rightshift */ |
| 1, /* size */ |
| 1, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_REF", /* name */ |
| false, /* partial_inplace */ |
| 0, /* src_mask */ |
| 0, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| EMPTY_HOWTO (0x10), |
| EMPTY_HOWTO (0x11), |
| EMPTY_HOWTO (0x12), |
| |
| /* 0x13: Same as R_TOC. */ |
| HOWTO (R_TRLA, /* type */ |
| 0, /* rightshift */ |
| 2, /* size */ |
| 16, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_TRLA", /* name */ |
| true, /* partial_inplace */ |
| 0, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* 0x14: Modifiable relative branch. */ |
| HOWTO (R_RRTBI, /* type */ |
| 1, /* rightshift */ |
| 4, /* size */ |
| 32, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_RRTBI", /* name */ |
| true, /* partial_inplace */ |
| 0xffffffff, /* src_mask */ |
| 0xffffffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* 0x15: Modifiable absolute branch. */ |
| HOWTO (R_RRTBA, /* type */ |
| 1, /* rightshift */ |
| 4, /* size */ |
| 32, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_RRTBA", /* name */ |
| true, /* partial_inplace */ |
| 0xffffffff, /* src_mask */ |
| 0xffffffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* 0x16: Modifiable call absolute indirect. */ |
| HOWTO (R_CAI, /* type */ |
| 0, /* rightshift */ |
| 2, /* size */ |
| 16, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_CAI", /* name */ |
| true, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* 0x17: Modifiable call relative. */ |
| HOWTO (R_CREL, /* type */ |
| 0, /* rightshift */ |
| 2, /* size */ |
| 16, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_CREL", /* name */ |
| true, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* 0x18: Modifiable branch absolute. */ |
| HOWTO (R_RBA, /* type */ |
| 0, /* rightshift */ |
| 4, /* size */ |
| 26, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_RBA", /* name */ |
| true, /* partial_inplace */ |
| 0x03fffffc, /* src_mask */ |
| 0x03fffffc, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* 0x19: Modifiable branch absolute. */ |
| HOWTO (R_RBAC, /* type */ |
| 0, /* rightshift */ |
| 4, /* size */ |
| 32, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_RBAC", /* name */ |
| true, /* partial_inplace */ |
| 0xffffffff, /* src_mask */ |
| 0xffffffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* 0x1a: Modifiable branch relative. */ |
| HOWTO (R_RBR, /* type */ |
| 0, /* rightshift */ |
| 4, /* size */ |
| 26, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_signed, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_RBR_26", /* name */ |
| true, /* partial_inplace */ |
| 0x03fffffc, /* src_mask */ |
| 0x03fffffc, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* 0x1b: Modifiable branch absolute. */ |
| HOWTO (R_RBRC, /* type */ |
| 0, /* rightshift */ |
| 2, /* size */ |
| 16, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_RBRC", /* name */ |
| true, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* 0x1c: 16 bit Non modifiable absolute branch. */ |
| HOWTO (R_BA, /* type */ |
| 0, /* rightshift */ |
| 2, /* size */ |
| 16, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_BA_16", /* name */ |
| true, /* partial_inplace */ |
| 0xfffc, /* src_mask */ |
| 0xfffc, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* 0x1d: Modifiable branch relative. */ |
| HOWTO (R_RBR, /* type */ |
| 0, /* rightshift */ |
| 2, /* size */ |
| 16, /* bitsize */ |
| true, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_signed, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_RBR_16", /* name */ |
| true, /* partial_inplace */ |
| 0xfffc, /* src_mask */ |
| 0xfffc, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* 0x1e: Modifiable branch relative. */ |
| HOWTO (R_RBA, /* type */ |
| 0, /* rightshift */ |
| 2, /* size */ |
| 16, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_signed, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_RBA_16", /* name */ |
| true, /* partial_inplace */ |
| 0xffff, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| EMPTY_HOWTO (0x1f), |
| |
| /* 0x20: General-dynamic TLS relocation. */ |
| HOWTO (R_TLS, /* type */ |
| 0, /* rightshift */ |
| 4, /* size */ |
| 32, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_TLS", /* name */ |
| true, /* partial_inplace */ |
| 0xffffffff, /* src_mask */ |
| 0xffffffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* 0x21: Initial-exec TLS relocation. */ |
| HOWTO (R_TLS_IE, /* type */ |
| 0, /* rightshift */ |
| 4, /* size */ |
| 32, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_TLS_IE", /* name */ |
| true, /* partial_inplace */ |
| 0xffffffff, /* src_mask */ |
| 0xffffffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* 0x22: Local-dynamic TLS relocation. */ |
| HOWTO (R_TLS_LD, /* type */ |
| 0, /* rightshift */ |
| 4, /* size */ |
| 32, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_TLS_LD", /* name */ |
| true, /* partial_inplace */ |
| 0xffffffff, /* src_mask */ |
| 0xffffffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* 0x23: Local-exec TLS relocation. */ |
| HOWTO (R_TLS_LE, /* type */ |
| 0, /* rightshift */ |
| 4, /* size */ |
| 32, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_TLS_LE", /* name */ |
| true, /* partial_inplace */ |
| 0xffffffff, /* src_mask */ |
| 0xffffffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* 0x24: TLS relocation. */ |
| HOWTO (R_TLSM, /* type */ |
| 0, /* rightshift */ |
| 4, /* size */ |
| 32, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_TLSM", /* name */ |
| true, /* partial_inplace */ |
| 0xffffffff, /* src_mask */ |
| 0xffffffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| |
| /* 0x25: TLS module relocation. */ |
| HOWTO (R_TLSML, /* type */ |
| 0, /* rightshift */ |
| 4, /* size */ |
| 32, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_TLSML", /* name */ |
| true, /* partial_inplace */ |
| 0xffffffff, /* src_mask */ |
| 0xffffffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| EMPTY_HOWTO(0x26), |
| EMPTY_HOWTO(0x27), |
| EMPTY_HOWTO(0x28), |
| EMPTY_HOWTO(0x29), |
| EMPTY_HOWTO(0x2a), |
| EMPTY_HOWTO(0x2b), |
| EMPTY_HOWTO(0x2c), |
| EMPTY_HOWTO(0x2d), |
| EMPTY_HOWTO(0x2e), |
| EMPTY_HOWTO(0x2f), |
| |
| /* 0x30: High-order 16 bit TOC relative relocation. */ |
| HOWTO (R_TOCU, /* type */ |
| 16, /* rightshift */ |
| 2, /* size */ |
| 16, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_TOCU", /* name */ |
| true, /* partial_inplace */ |
| 0, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* 0x31: Low-order 16 bit TOC relative relocation. */ |
| HOWTO (R_TOCL, /* type */ |
| 0, /* rightshift */ |
| 2, /* size */ |
| 16, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont, /* complain_on_overflow */ |
| 0, /* special_function */ |
| "R_TOCL", /* name */ |
| true, /* partial_inplace */ |
| 0, /* src_mask */ |
| 0xffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| }; |
| |
| void |
| xcoff_rtype2howto (arelent *relent, struct internal_reloc *internal) |
| { |
| if (internal->r_type > R_TOCL) |
| abort (); |
| |
| /* Default howto layout works most of the time */ |
| relent->howto = &xcoff_howto_table[internal->r_type]; |
| |
| /* Special case some 16 bit reloc */ |
| if (15 == (internal->r_size & 0x1f)) |
| { |
| if (R_BA == internal->r_type) |
| relent->howto = &xcoff_howto_table[0x1c]; |
| else if (R_RBR == internal->r_type) |
| relent->howto = &xcoff_howto_table[0x1d]; |
| else if (R_RBA == internal->r_type) |
| relent->howto = &xcoff_howto_table[0x1e]; |
| } |
| |
| /* The r_size field of an XCOFF reloc encodes the bitsize of the |
| relocation, as well as indicating whether it is signed or not. |
| Doublecheck that the relocation information gathered from the |
| type matches this information. The bitsize is not significant |
| for R_REF relocs. */ |
| if (relent->howto->dst_mask != 0 |
| && (relent->howto->bitsize |
| != ((unsigned int) internal->r_size & 0x1f) + 1)) |
| abort (); |
| } |
| |
| reloc_howto_type * |
| _bfd_xcoff_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, |
| bfd_reloc_code_real_type code) |
| { |
| switch (code) |
| { |
| case BFD_RELOC_PPC_B26: |
| return &xcoff_howto_table[0xa]; |
| case BFD_RELOC_PPC_BA16: |
| return &xcoff_howto_table[0x1c]; |
| case BFD_RELOC_PPC_BA26: |
| return &xcoff_howto_table[8]; |
| case BFD_RELOC_PPC_TOC16: |
| return &xcoff_howto_table[3]; |
| case BFD_RELOC_PPC_TOC16_HI: |
| return &xcoff_howto_table[0x30]; |
| case BFD_RELOC_PPC_TOC16_LO: |
| return &xcoff_howto_table[0x31]; |
| case BFD_RELOC_PPC_B16: |
| return &xcoff_howto_table[0x1d]; |
| case BFD_RELOC_32: |
| case BFD_RELOC_CTOR: |
| return &xcoff_howto_table[0]; |
| case BFD_RELOC_NONE: |
| return &xcoff_howto_table[0xf]; |
| case BFD_RELOC_PPC_NEG: |
| return &xcoff_howto_table[0x1]; |
| case BFD_RELOC_PPC_TLSGD: |
| return &xcoff_howto_table[0x20]; |
| case BFD_RELOC_PPC_TLSIE: |
| return &xcoff_howto_table[0x21]; |
| case BFD_RELOC_PPC_TLSLD: |
| return &xcoff_howto_table[0x22]; |
| case BFD_RELOC_PPC_TLSLE: |
| return &xcoff_howto_table[0x23]; |
| case BFD_RELOC_PPC_TLSM: |
| return &xcoff_howto_table[0x24]; |
| case BFD_RELOC_PPC_TLSML: |
| return &xcoff_howto_table[0x25]; |
| default: |
| return NULL; |
| } |
| } |
| |
| static reloc_howto_type * |
| _bfd_xcoff_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, |
| const char *r_name) |
| { |
| unsigned int i; |
| |
| for (i = 0; |
| i < sizeof (xcoff_howto_table) / sizeof (xcoff_howto_table[0]); |
| i++) |
| if (xcoff_howto_table[i].name != NULL |
| && strcasecmp (xcoff_howto_table[i].name, r_name) == 0) |
| return &xcoff_howto_table[i]; |
| |
| return NULL; |
| } |
| |
| /* XCOFF archive support. The original version of this code was by |
| Damon A. Permezel. It was enhanced to permit cross support, and |
| writing archive files, by Ian Lance Taylor, Cygnus Support. |
| |
| XCOFF uses its own archive format. Everything is hooked together |
| with file offset links, so it is possible to rapidly update an |
| archive in place. Of course, we don't do that. An XCOFF archive |
| has a real file header, not just an ARMAG string. The structure of |
| the file header and of each archive header appear below. |
| |
| An XCOFF archive also has a member table, which is a list of |
| elements in the archive (you can get that by looking through the |
| linked list, but you have to read a lot more of the file). The |
| member table has a normal archive header with an empty name. It is |
| normally (and perhaps must be) the second to last entry in the |
| archive. The member table data is almost printable ASCII. It |
| starts with a 12 character decimal string which is the number of |
| entries in the table. For each entry it has a 12 character decimal |
| string which is the offset in the archive of that member. These |
| entries are followed by a series of null terminated strings which |
| are the member names for each entry. |
| |
| Finally, an XCOFF archive has a global symbol table, which is what |
| we call the armap. The global symbol table has a normal archive |
| header with an empty name. It is normally (and perhaps must be) |
| the last entry in the archive. The contents start with a four byte |
| binary number which is the number of entries. This is followed by |
| a that many four byte binary numbers; each is the file offset of an |
| entry in the archive. These numbers are followed by a series of |
| null terminated strings, which are symbol names. |
| |
| AIX 4.3 introduced a new archive format which can handle larger |
| files and also 32- and 64-bit objects in the same archive. The |
| things said above remain true except that there is now more than |
| one global symbol table. The one is used to index 32-bit objects, |
| the other for 64-bit objects. |
| |
| The new archives (recognizable by the new ARMAG string) has larger |
| field lengths so that we cannot really share any code. Also we have |
| to take care that we are not generating the new form of archives |
| on AIX 4.2 or earlier systems. */ |
| |
| /* PR 21786: The PE/COFF standard does not require NUL termination for any of |
| the ASCII fields in the archive headers. So in order to be able to extract |
| numerical values we provide our own versions of strtol and strtoll which |
| take a maximum length as an additional parameter. Also - just to save space, |
| we omit the endptr return parameter, since we know that it is never used. */ |
| |
| static unsigned long |
| _bfd_strntol (const char * nptr, int base, unsigned int maxlen) |
| { |
| char buf[24]; /* Should be enough. */ |
| |
| BFD_ASSERT (maxlen < (sizeof (buf) - 1)); |
| |
| memcpy (buf, nptr, maxlen); |
| buf[maxlen] = 0; |
| return strtol (buf, NULL, base); |
| } |
| |
| static unsigned long long |
| _bfd_strntoll (const char * nptr, int base, unsigned int maxlen) |
| { |
| char buf[32]; /* Should be enough. */ |
| |
| BFD_ASSERT (maxlen < (sizeof (buf) - 1)); |
| |
| memcpy (buf, nptr, maxlen); |
| buf[maxlen] = 0; |
| return strtoll (buf, NULL, base); |
| } |
| |
| /* Macro to read an ASCII value stored in an archive header field. */ |
| #define GET_VALUE_IN_FIELD(VAR, FIELD, BASE) \ |
| do \ |
| { \ |
| (VAR) = (sizeof (VAR) > sizeof (long) \ |
| ? _bfd_strntoll (FIELD, BASE, sizeof FIELD) \ |
| : _bfd_strntol (FIELD, BASE, sizeof FIELD)); \ |
| } \ |
| while (0) |
| |
| #define EQ_VALUE_IN_FIELD(VAR, FIELD, BASE) \ |
| (sizeof (VAR) > sizeof (long) \ |
| ? (VAR) == _bfd_strntoll (FIELD, BASE, sizeof FIELD) \ |
| : (VAR) == _bfd_strntol (FIELD, BASE, sizeof FIELD)) |
| |
| /* Read in the armap of an XCOFF archive. */ |
| |
| bool |
| _bfd_xcoff_slurp_armap (bfd *abfd) |
| { |
| ufile_ptr off; |
| size_t namlen; |
| bfd_size_type sz; |
| bfd_byte *contents, *cend; |
| bfd_vma c, i; |
| carsym *arsym; |
| bfd_byte *p; |
| |
| if (x_artdata (abfd) == NULL) |
| { |
| abfd->has_armap = false; |
| return true; |
| } |
| |
| if (! xcoff_big_format_p (abfd)) |
| { |
| /* This is for the old format. */ |
| struct xcoff_ar_hdr hdr; |
| |
| GET_VALUE_IN_FIELD (off, x_artdata (abfd)->u.hdr.symoff, 10); |
| if (off == 0) |
| { |
| abfd->has_armap = false; |
| return true; |
| } |
| |
| if (bfd_seek (abfd, off, SEEK_SET) != 0) |
| return false; |
| |
| /* The symbol table starts with a normal archive header. */ |
| if (bfd_read (&hdr, SIZEOF_AR_HDR, abfd) != SIZEOF_AR_HDR) |
| return false; |
| |
| /* Skip the name (normally empty). */ |
| GET_VALUE_IN_FIELD (namlen, hdr.namlen, 10); |
| off = ((namlen + 1) & ~ (size_t) 1) + SXCOFFARFMAG; |
| if (bfd_seek (abfd, off, SEEK_CUR) != 0) |
| return false; |
| |
| GET_VALUE_IN_FIELD (sz, hdr.size, 10); |
| if (sz + 1 < 5) |
| { |
| bfd_set_error (bfd_error_bad_value); |
| return false; |
| } |
| |
| /* Read in the entire symbol table. */ |
| contents = (bfd_byte *) _bfd_alloc_and_read (abfd, sz + 1, sz); |
| if (contents == NULL) |
| return false; |
| |
| /* Ensure strings are NULL terminated so we don't wander off the |
| end of the buffer. */ |
| contents[sz] = 0; |
| |
| /* The symbol table starts with a four byte count. */ |
| c = H_GET_32 (abfd, contents); |
| |
| if (c >= sz / 4) |
| { |
| bfd_set_error (bfd_error_bad_value); |
| return false; |
| } |
| |
| bfd_ardata (abfd)->symdefs = |
| ((carsym *) bfd_alloc (abfd, c * sizeof (carsym))); |
| if (bfd_ardata (abfd)->symdefs == NULL) |
| return false; |
| |
| /* After the count comes a list of four byte file offsets. */ |
| for (i = 0, arsym = bfd_ardata (abfd)->symdefs, p = contents + 4; |
| i < c; |
| ++i, ++arsym, p += 4) |
| arsym->file_offset = H_GET_32 (abfd, p); |
| } |
| else |
| { |
| /* This is for the new format. */ |
| struct xcoff_ar_hdr_big hdr; |
| |
| GET_VALUE_IN_FIELD (off, x_artdata (abfd)->u.bhdr.symoff, 10); |
| if (off == 0) |
| { |
| abfd->has_armap = false; |
| return true; |
| } |
| |
| if (bfd_seek (abfd, off, SEEK_SET) != 0) |
| return false; |
| |
| /* The symbol table starts with a normal archive header. */ |
| if (bfd_read (&hdr, SIZEOF_AR_HDR_BIG, abfd) != SIZEOF_AR_HDR_BIG) |
| return false; |
| |
| /* Skip the name (normally empty). */ |
| GET_VALUE_IN_FIELD (namlen, hdr.namlen, 10); |
| off = ((namlen + 1) & ~ (size_t) 1) + SXCOFFARFMAG; |
| if (bfd_seek (abfd, off, SEEK_CUR) != 0) |
| return false; |
| |
| GET_VALUE_IN_FIELD (sz, hdr.size, 10); |
| if (sz + 1 < 9) |
| { |
| bfd_set_error (bfd_error_bad_value); |
| return false; |
| } |
| |
| /* Read in the entire symbol table. */ |
| contents = (bfd_byte *) _bfd_alloc_and_read (abfd, sz + 1, sz); |
| if (contents == NULL) |
| return false; |
| |
| /* Ensure strings are NULL terminated so we don't wander off the |
| end of the buffer. */ |
| contents[sz] = 0; |
| |
| /* The symbol table starts with an eight byte count. */ |
| c = H_GET_64 (abfd, contents); |
| |
| if (c >= sz / 8) |
| { |
| bfd_set_error (bfd_error_bad_value); |
| return false; |
| } |
| |
| bfd_ardata (abfd)->symdefs = |
| ((carsym *) bfd_alloc (abfd, c * sizeof (carsym))); |
| if (bfd_ardata (abfd)->symdefs == NULL) |
| return false; |
| |
| /* After the count comes a list of eight byte file offsets. */ |
| for (i = 0, arsym = bfd_ardata (abfd)->symdefs, p = contents + 8; |
| i < c; |
| ++i, ++arsym, p += 8) |
| arsym->file_offset = H_GET_64 (abfd, p); |
| } |
| |
| /* After the file offsets come null terminated symbol names. */ |
| cend = contents + sz; |
| for (i = 0, arsym = bfd_ardata (abfd)->symdefs; |
| i < c; |
| ++i, ++arsym, p += strlen ((char *) p) + 1) |
| { |
| if (p >= cend) |
| { |
| bfd_set_error (bfd_error_bad_value); |
| return false; |
| } |
| arsym->name = (char *) p; |
| } |
| |
| bfd_ardata (abfd)->symdef_count = c; |
| abfd->has_armap = true; |
| |
| return true; |
| } |
| |
| /* See if this is an XCOFF archive. */ |
| |
| bfd_cleanup |
| _bfd_xcoff_archive_p (bfd *abfd) |
| { |
| struct artdata *tdata_hold; |
| char magic[SXCOFFARMAG]; |
| size_t amt = SXCOFFARMAG; |
| |
| if (bfd_read (magic, amt, abfd) != amt) |
| { |
| if (bfd_get_error () != bfd_error_system_call) |
| bfd_set_error (bfd_error_wrong_format); |
| return NULL; |
| } |
| |
| if (strncmp (magic, XCOFFARMAG, SXCOFFARMAG) != 0 |
| && strncmp (magic, XCOFFARMAGBIG, SXCOFFARMAG) != 0) |
| { |
| bfd_set_error (bfd_error_wrong_format); |
| return NULL; |
| } |
| |
| tdata_hold = bfd_ardata (abfd); |
| |
| amt = sizeof (struct artdata); |
| bfd_ardata (abfd) = (struct artdata *) bfd_zalloc (abfd, amt); |
| if (bfd_ardata (abfd) == (struct artdata *) NULL) |
| goto error_ret_restore; |
| |
| /* Now handle the two formats. */ |
| if (magic[1] != 'b') |
| { |
| /* This is the old format. */ |
| struct xcoff_ar_file_hdr hdr; |
| |
| /* Copy over the magic string. */ |
| memcpy (hdr.magic, magic, SXCOFFARMAG); |
| |
| /* Now read the rest of the file header. */ |
| amt = SIZEOF_AR_FILE_HDR - SXCOFFARMAG; |
| if (bfd_read (&hdr.memoff, amt, abfd) != amt) |
| { |
| if (bfd_get_error () != bfd_error_system_call) |
| bfd_set_error (bfd_error_wrong_format); |
| goto error_ret; |
| } |
| |
| GET_VALUE_IN_FIELD (bfd_ardata (abfd)->first_file_filepos, |
| hdr.firstmemoff, 10); |
| |
| amt = sizeof (struct xcoff_artdata); |
| bfd_ardata (abfd)->tdata = bfd_zalloc (abfd, amt); |
| if (bfd_ardata (abfd)->tdata == NULL) |
| goto error_ret; |
| |
| memcpy (&x_artdata (abfd)->u.hdr, &hdr, SIZEOF_AR_FILE_HDR); |
| } |
| else |
| { |
| /* This is the new format. */ |
| struct xcoff_ar_file_hdr_big hdr; |
| |
| /* Copy over the magic string. */ |
| memcpy (hdr.magic, magic, SXCOFFARMAG); |
| |
| /* Now read the rest of the file header. */ |
| amt = SIZEOF_AR_FILE_HDR_BIG - SXCOFFARMAG; |
| if (bfd_read (&hdr.memoff, amt, abfd) != amt) |
| { |
| if (bfd_get_error () != bfd_error_system_call) |
| bfd_set_error (bfd_error_wrong_format); |
| goto error_ret; |
| } |
| |
| bfd_ardata (abfd)->first_file_filepos = bfd_scan_vma (hdr.firstmemoff, |
| (const char **) 0, |
| 10); |
| |
| amt = sizeof (struct xcoff_artdata); |
| bfd_ardata (abfd)->tdata = bfd_zalloc (abfd, amt); |
| if (bfd_ardata (abfd)->tdata == NULL) |
| goto error_ret; |
| |
| memcpy (&x_artdata (abfd)->u.bhdr, &hdr, SIZEOF_AR_FILE_HDR_BIG); |
| } |
| |
| if (! _bfd_xcoff_slurp_armap (abfd)) |
| { |
| error_ret: |
| bfd_release (abfd, bfd_ardata (abfd)); |
| error_ret_restore: |
| bfd_ardata (abfd) = tdata_hold; |
| return NULL; |
| } |
| |
| return _bfd_no_cleanup; |
| } |
| |
| /* Track file ranges occupied by elements. Add [START,END) to the |
| list of ranges and return TRUE if there is no overlap between the |
| new and any other element or the archive file header. This is |
| aimed at preventing infinite looping on malformed archives, for |
| "ar" and similar which typically use code like: |
| . for (last = bfd_openr_next_archived_file (archive, NULL); |
| . last; |
| . last = next) |
| . { |
| . do_something_with (last); |
| . next = bfd_openr_next_archived_file (archive, last); |
| . bfd_close (last); |
| . } |
| The check implemented here is only possible due to the fact that |
| for XCOFF archives bfd_openr_next_archived_file is the only code |
| path leading to _bfd_read_ar_hdr. _bfd_read_ar_hdr is not called |
| when reading the armap, nor do XCOFF archives use the extended name |
| scheme implemented in archive.c. |
| |
| Note that the check relies on the previous element being closed, |
| and there is one case where add_range might fail but I think it is |
| sufficently unusual that it doesn't warrant fixing: |
| If the loop body above called bfd_openr_next_archived_file twice |
| with the same arguments and the element returned is bfd_close'd |
| between those calls then we'll return false here for the second |
| call. (For why this is so see _bfd_look_for_bfd_in_cache in |
| _bfd_get_elt_at_filepos, and know that bfd_close removes elements |
| from the cache.) */ |
| |
| static bool |
| add_range (bfd *abfd, ufile_ptr start, ufile_ptr end) |
| { |
| if (end <= start) |
| { |
| err: |
| bfd_set_error (bfd_error_malformed_archive); |
| return false; |
| } |
| |
| /* This list is kept sorted by address. Find the highest address |
| range on the list that ends before the new range starts. Exit |
| the loop with that range in LO, and the mext higher range in HI. */ |
| struct ar_ranges *hi = &x_artdata (abfd)->ranges; |
| struct ar_ranges *lo = NULL; |
| while (hi && hi->end <= start) |
| { |
| lo = hi; |
| hi = hi->next; |
| } |
| |
| if (lo == NULL) |
| /* Start overlaps the file header or elements adjacent to it. */ |
| goto err; |
| |
| if (hi && hi->start < end) |
| /* Overlap with another element. */ |
| goto err; |
| |
| /* A zero size element with a one char name is this big. */ |
| unsigned min_elt = x_artdata (abfd)->ar_hdr_size + 2 + SXCOFFARFMAG; |
| if (start - lo->end < min_elt) |
| { |
| /* Merge into an existing range. */ |
| lo->end = end; |
| if (hi && hi->start - end < min_elt) |
| { |
| /* In fact, we can merge two ranges. */ |
| lo->end = hi->end; |
| lo->next = hi->next; |
| /* The list uses bfd_alloc so don't free HI. */ |
| } |
| return true; |
| } |
| |
| if (hi && hi->start - end < min_elt) |
| { |
| /* Merge into an existing range. */ |
| hi->start = start; |
| return true; |
| } |
| |
| struct ar_ranges *newr = bfd_alloc (abfd, sizeof (*newr)); |
| if (newr == NULL) |
| return false; |
| newr->start = start; |
| newr->end = end; |
| newr->next = hi; |
| lo->next = newr; |
| return true; |
| } |
| |
| /* Read the archive header in an XCOFF archive. */ |
| |
| void * |
| _bfd_xcoff_read_ar_hdr (bfd *abfd) |
| { |
| bfd_size_type namlen; |
| struct areltdata *ret; |
| bfd_size_type amt; |
| ufile_ptr start = abfd->where; |
| |
| if (! xcoff_big_format_p (abfd)) |
| { |
| struct xcoff_ar_hdr hdr; |
| struct xcoff_ar_hdr *hdrp; |
| |
| if (bfd_read (&hdr, SIZEOF_AR_HDR, abfd) != SIZEOF_AR_HDR) |
| return NULL; |
| |
| GET_VALUE_IN_FIELD (namlen, hdr.namlen, 10); |
| if (namlen > bfd_get_file_size (abfd)) |
| return NULL; |
| amt = sizeof (struct areltdata) + SIZEOF_AR_HDR + namlen + 1; |
| ret = (struct areltdata *) bfd_malloc (amt); |
| if (ret == NULL) |
| return ret; |
| |
| hdrp = (struct xcoff_ar_hdr *) (ret + 1); |
| memcpy (hdrp, &hdr, SIZEOF_AR_HDR); |
| if (bfd_read ((char *) hdrp + SIZEOF_AR_HDR, namlen, abfd) != namlen) |
| { |
| free (ret); |
| return NULL; |
| } |
| ((char *) hdrp)[SIZEOF_AR_HDR + namlen] = '\0'; |
| |
| ret->arch_header = (char *) hdrp; |
| GET_VALUE_IN_FIELD (ret->parsed_size, hdr.size, 10); |
| ret->filename = (char *) hdrp + SIZEOF_AR_HDR; |
| } |
| else |
| { |
| struct xcoff_ar_hdr_big hdr; |
| struct xcoff_ar_hdr_big *hdrp; |
| |
| if (bfd_read (&hdr, SIZEOF_AR_HDR_BIG, abfd) != SIZEOF_AR_HDR_BIG) |
| return NULL; |
| |
| GET_VALUE_IN_FIELD (namlen, hdr.namlen, 10); |
| if (namlen > bfd_get_file_size (abfd)) |
| return NULL; |
| amt = sizeof (struct areltdata) + SIZEOF_AR_HDR_BIG + namlen + 1; |
| ret = (struct areltdata *) bfd_malloc (amt); |
| if (ret == NULL) |
| return ret; |
| |
| hdrp = (struct xcoff_ar_hdr_big *) (ret + 1); |
| memcpy (hdrp, &hdr, SIZEOF_AR_HDR_BIG); |
| if (bfd_read ((char *) hdrp + SIZEOF_AR_HDR_BIG, namlen, abfd) != namlen) |
| { |
| free (ret); |
| return NULL; |
| } |
| ((char *) hdrp)[SIZEOF_AR_HDR_BIG + namlen] = '\0'; |
| |
| ret->arch_header = (char *) hdrp; |
| GET_VALUE_IN_FIELD (ret->parsed_size, hdr.size, 10); |
| ret->filename = (char *) hdrp + SIZEOF_AR_HDR_BIG; |
| } |
| |
| /* Size occupied by the header above that covered in the fixed |
| SIZEOF_AR_HDR or SIZEOF_AR_HDR_BIG. */ |
| ret->extra_size = namlen + (namlen & 1) + SXCOFFARFMAG; |
| |
| /* Skip over the XCOFFARFMAG at the end of the file name. */ |
| if (bfd_seek (abfd, (namlen & 1) + SXCOFFARFMAG, SEEK_CUR) != 0 |
| || !add_range (abfd, start, abfd->where + ret->parsed_size)) |
| { |
| free (ret); |
| return NULL; |
| } |
| |
| return ret; |
| } |
| |
| /* Open the next element in an XCOFF archive. */ |
| |
| bfd * |
| _bfd_xcoff_openr_next_archived_file (bfd *archive, bfd *last_file) |
| { |
| ufile_ptr filestart; |
| |
| if (x_artdata (archive) == NULL) |
| { |
| bfd_set_error (bfd_error_invalid_operation); |
| return NULL; |
| } |
| |
| if (! xcoff_big_format_p (archive)) |
| { |
| if (last_file == NULL) |
| { |
| /* If we are scanning over elements twice in an open archive, |
| which can happen in gdb after a fork, ensure we start the |
| second scan with clean ranges. */ |
| x_artdata (archive)->ranges.start = 0; |
| x_artdata (archive)->ranges.end = SIZEOF_AR_FILE_HDR; |
| x_artdata (archive)->ranges.next = NULL; |
| x_artdata (archive)->ar_hdr_size = SIZEOF_AR_HDR; |
| filestart = bfd_ardata (archive)->first_file_filepos; |
| } |
| else |
| GET_VALUE_IN_FIELD (filestart, arch_xhdr (last_file)->nextoff, 10); |
| |
| if (filestart == 0 |
| || EQ_VALUE_IN_FIELD (filestart, |
| x_artdata (archive)->u.hdr.memoff, 10) |
| || EQ_VALUE_IN_FIELD (filestart, |
| x_artdata (archive)->u.hdr.symoff, 10)) |
| { |
| bfd_set_error (bfd_error_no_more_archived_files); |
| return NULL; |
| } |
| } |
| else |
| { |
| if (last_file == NULL) |
| { |
| x_artdata (archive)->ranges.start = 0; |
| x_artdata (archive)->ranges.end = SIZEOF_AR_FILE_HDR_BIG; |
| x_artdata (archive)->ranges.next = NULL; |
| x_artdata (archive)->ar_hdr_size = SIZEOF_AR_HDR_BIG; |
| filestart = bfd_ardata (archive)->first_file_filepos; |
| } |
| else |
| GET_VALUE_IN_FIELD (filestart, arch_xhdr_big (last_file)->nextoff, 10); |
| |
| if (filestart == 0 |
| || EQ_VALUE_IN_FIELD (filestart, |
| x_artdata (archive)->u.bhdr.memoff, 10) |
| || EQ_VALUE_IN_FIELD (filestart, |
| x_artdata (archive)->u.bhdr.symoff, 10)) |
| { |
| bfd_set_error (bfd_error_no_more_archived_files); |
| return NULL; |
| } |
| } |
| |
| /* Check that we aren't pointing back at the last element. This is |
| necessary depite the add_range checking in _bfd_xcoff_read_ar_hdr |
| because archive.c leaves the last element open and thus in the |
| archive element cache until the next element is opened. */ |
| if (last_file != NULL) |
| { |
| ufile_ptr laststart = last_file->proxy_origin; |
| laststart -= x_artdata (archive)->ar_hdr_size; |
| laststart -= arch_eltdata (last_file)->extra_size; |
| if (filestart == laststart) |
| { |
| bfd_set_error (bfd_error_malformed_archive); |
| return NULL; |
| } |
| } |
| |
| return _bfd_get_elt_at_filepos (archive, filestart, NULL); |
| } |
| |
| /* Stat an element in an XCOFF archive. */ |
| |
| int |
| _bfd_xcoff_stat_arch_elt (bfd *abfd, struct stat *s) |
| { |
| if (abfd->arelt_data == NULL) |
| { |
| bfd_set_error (bfd_error_invalid_operation); |
| return -1; |
| } |
| |
| if (! xcoff_big_format_p (abfd->my_archive)) |
| { |
| struct xcoff_ar_hdr *hdrp = arch_xhdr (abfd); |
| |
| GET_VALUE_IN_FIELD (s->st_mtime, hdrp->date, 10); |
| GET_VALUE_IN_FIELD (s->st_uid, hdrp->uid, 10); |
| GET_VALUE_IN_FIELD (s->st_gid, hdrp->gid, 10); |
| GET_VALUE_IN_FIELD (s->st_mode, hdrp->mode, 8); |
| s->st_size = arch_eltdata (abfd)->parsed_size; |
| } |
| else |
| { |
| struct xcoff_ar_hdr_big *hdrp = arch_xhdr_big (abfd); |
| |
| GET_VALUE_IN_FIELD (s->st_mtime, hdrp->date, 10); |
| GET_VALUE_IN_FIELD (s->st_uid, hdrp->uid, 10); |
| GET_VALUE_IN_FIELD (s->st_gid, hdrp->gid, 10); |
| GET_VALUE_IN_FIELD (s->st_mode, hdrp->mode, 8); |
| s->st_size = arch_eltdata (abfd)->parsed_size; |
| } |
| |
| return 0; |
| } |
| |
| /* Normalize a file name for inclusion in an archive. */ |
| |
| static const char * |
| normalize_filename (bfd *abfd) |
| { |
| const char *file; |
| const char *filename; |
| |
| file = bfd_get_filename (abfd); |
| filename = strrchr (file, '/'); |
| if (filename != NULL) |
| filename++; |
| else |
| filename = file; |
| return filename; |
| } |
| |
| /* Write out an XCOFF armap. */ |
| |
| static bool |
| xcoff_write_armap_old (bfd *abfd, unsigned int elength ATTRIBUTE_UNUSED, |
| struct orl *map, unsigned int orl_count, int stridx) |
| { |
| struct archive_iterator iterator; |
| struct xcoff_ar_hdr hdr; |
| char *p; |
| unsigned char buf[4]; |
| unsigned int i; |
| |
| memset (&hdr, 0, sizeof hdr); |
| sprintf (hdr.size, "%ld", (long) (4 + orl_count * 4 + stridx)); |
| sprintf (hdr.nextoff, "%d", 0); |
| memcpy (hdr.prevoff, x_artdata (abfd)->u.hdr.memoff, |
| XCOFFARMAG_ELEMENT_SIZE); |
| sprintf (hdr.date, "%d", 0); |
| sprintf (hdr.uid, "%d", 0); |
| sprintf (hdr.gid, "%d", 0); |
| sprintf (hdr.mode, "%d", 0); |
| sprintf (hdr.namlen, "%d", 0); |
| |
| /* We need spaces, not null bytes, in the header. */ |
| for (p = (char *) &hdr; p < (char *) &hdr + SIZEOF_AR_HDR; p++) |
| if (*p == '\0') |
| *p = ' '; |
| |
| if (bfd_write (&hdr, SIZEOF_AR_HDR, abfd) != SIZEOF_AR_HDR |
| || bfd_write (XCOFFARFMAG, SXCOFFARFMAG, abfd) != SXCOFFARFMAG) |
| return false; |
| |
| H_PUT_32 (abfd, orl_count, buf); |
| if (bfd_write (buf, 4, abfd) != 4) |
| return false; |
| |
| i = 0; |
| archive_iterator_begin (&iterator, abfd); |
| while (i < orl_count && archive_iterator_next (&iterator)) |
| while (map[i].u.abfd == iterator.current.member) |
| { |
| H_PUT_32 (abfd, iterator.current.offset, buf); |
| if (bfd_write (buf, 4, abfd) != 4) |
| return false; |
| ++i; |
| } |
| |
| for (i = 0; i < orl_count; i++) |
| { |
| const char *name; |
| size_t namlen; |
| |
| name = *map[i].name; |
| namlen = strlen (name); |
| if (bfd_write (name, namlen + 1, abfd) != namlen + 1) |
| return false; |
| } |
| |
| if ((stridx & 1) != 0) |
| { |
| char b; |
| |
| b = '\0'; |
| if (bfd_write (&b, 1, abfd) != 1) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static char buff20[XCOFFARMAGBIG_ELEMENT_SIZE + 1]; |
| #define FMT20 "%-20" PRId64 |
| #define FMT12 "%-12d" |
| #define FMT12_OCTAL "%-12o" |
| #define FMT4 "%-4d" |
| #define PRINT20(d, v) \ |
| sprintf (buff20, FMT20, (uint64_t) (v)), \ |
| memcpy ((void *) (d), buff20, 20) |
| |
| #define PRINT12(d, v) \ |
| sprintf (buff20, FMT12, (int)(v)), \ |
| memcpy ((void *) (d), buff20, 12) |
| |
| #define PRINT12_OCTAL(d, v) \ |
| sprintf (buff20, FMT12_OCTAL, (unsigned int)(v)), \ |
| memcpy ((void *) (d), buff20, 12) |
| |
| #define PRINT4(d, v) \ |
| sprintf (buff20, FMT4, (int)(v)), \ |
| memcpy ((void *) (d), buff20, 4) |
| |
| #define READ20(d, v) \ |
| buff20[20] = 0, \ |
| memcpy (buff20, (d), 20), \ |
| (v) = bfd_scan_vma (buff20, (const char **) NULL, 10) |
| |
| static bool |
| do_pad (bfd *abfd, unsigned int number) |
| { |
| bfd_byte b = 0; |
| |
| /* Limit pad to <= 4096. */ |
| if (number > 4096) |
| return false; |
| |
| while (number--) |
| if (bfd_write (&b, 1, abfd) != 1) |
| return false; |
| |
| return true; |
| } |
| |
| static bool |
| do_copy (bfd *out_bfd, bfd *in_bfd) |
| { |
| bfd_size_type remaining; |
| bfd_byte buffer[8 * 1024]; |
| |
| if (bfd_seek (in_bfd, 0, SEEK_SET) != 0) |
| return false; |
| |
| remaining = arelt_size (in_bfd); |
| |
| while (remaining >= sizeof (buffer)) |
| { |
| if (bfd_read (buffer, sizeof (buffer), in_bfd) != sizeof (buffer) |
| || bfd_write (buffer, sizeof (buffer), out_bfd) != sizeof (buffer)) |
| return false; |
| |
| remaining -= sizeof (buffer); |
| } |
| |
| if (remaining) |
| { |
| if (bfd_read (buffer, remaining, in_bfd) != remaining |
| || bfd_write (buffer, remaining, out_bfd) != remaining) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static bool |
| xcoff_write_armap_big (bfd *abfd, unsigned int elength ATTRIBUTE_UNUSED, |
| struct orl *map, unsigned int orl_count, int stridx) |
| { |
| struct archive_iterator iterator; |
| struct xcoff_ar_file_hdr_big *fhdr; |
| bfd_vma i, sym_32, sym_64, str_32, str_64; |
| const bfd_arch_info_type *arch_info; |
| bfd *current_bfd; |
| size_t string_length; |
| file_ptr nextoff, prevoff; |
| |
| /* First, we look through the symbols and work out which are |
| from 32-bit objects and which from 64-bit ones. */ |
| sym_32 = sym_64 = str_32 = str_64 = 0; |
| |
| i = 0; |
| for (current_bfd = abfd->archive_head; |
| current_bfd != NULL && i < orl_count; |
| current_bfd = current_bfd->archive_next) |
| { |
| arch_info = bfd_get_arch_info (current_bfd); |
| while (map[i].u.abfd == current_bfd) |
| { |
| string_length = strlen (*map[i].name) + 1; |
| if (arch_info->bits_per_address == 64) |
| { |
| sym_64++; |
| str_64 += string_length; |
| } |
| else |
| { |
| sym_32++; |
| str_32 += string_length; |
| } |
| i++; |
| } |
| } |
| |
| /* A quick sanity check... */ |
| BFD_ASSERT (sym_64 + sym_32 == orl_count); |
| /* Explicit cast to int for compiler. */ |
| BFD_ASSERT ((int)(str_64 + str_32) == stridx); |
| |
| fhdr = &x_artdata (abfd)->u.bhdr; |
| |
| /* xcoff_write_archive_contents_big passes nextoff in symoff. */ |
| READ20 (fhdr->memoff, prevoff); |
| READ20 (fhdr->symoff, nextoff); |
| |
| BFD_ASSERT (nextoff == bfd_tell (abfd)); |
| |
| /* Write out the symbol table. |
| Layout : |
| |
| standard big archive header |
| 0x0000 ar_size [0x14] |
| 0x0014 ar_nxtmem [0x14] |
| 0x0028 ar_prvmem [0x14] |
| 0x003C ar_date [0x0C] |
| 0x0048 ar_uid [0x0C] |
| 0x0054 ar_gid [0x0C] |
| 0x0060 ar_mod [0x0C] |
| 0x006C ar_namelen[0x04] |
| 0x0070 ar_fmag [SXCOFFARFMAG] |
| |
| Symbol table |
| 0x0072 num_syms [0x08], binary |
| 0x0078 offsets [0x08 * num_syms], binary |
| 0x0086 + 0x08 * num_syms names [??] |
| ?? pad to even bytes. |
| */ |
| |
| if (sym_32) |
| { |
| struct xcoff_ar_hdr_big *hdr; |
| char *symbol_table; |
| char *st; |
| |
| bfd_vma symbol_table_size = |
| SIZEOF_AR_HDR_BIG |
| + SXCOFFARFMAG |
| + 8 |
| + 8 * sym_32 |
| + str_32 + (str_32 & 1); |
| |
| symbol_table = bfd_zmalloc (symbol_table_size); |
| if (symbol_table == NULL) |
| return false; |
| |
| hdr = (struct xcoff_ar_hdr_big *) symbol_table; |
| |
| PRINT20 (hdr->size, 8 + 8 * sym_32 + str_32 + (str_32 & 1)); |
| |
| if (sym_64) |
| PRINT20 (hdr->nextoff, nextoff + symbol_table_size); |
| else |
| PRINT20 (hdr->nextoff, 0); |
| |
| PRINT20 (hdr->prevoff, prevoff); |
| PRINT12 (hdr->date, 0); |
| PRINT12 (hdr->uid, 0); |
| PRINT12 (hdr->gid, 0); |
| PRINT12 (hdr->mode, 0); |
| PRINT4 (hdr->namlen, 0) ; |
| |
| st = symbol_table + SIZEOF_AR_HDR_BIG; |
| memcpy (st, XCOFFARFMAG, SXCOFFARFMAG); |
| st += SXCOFFARFMAG; |
| |
| bfd_h_put_64 (abfd, sym_32, st); |
| st += 8; |
| |
| /* loop over the 32 bit offsets */ |
| i = 0; |
| archive_iterator_begin (&iterator, abfd); |
| while (i < orl_count && archive_iterator_next (&iterator)) |
| { |
| arch_info = bfd_get_arch_info (iterator.current.member); |
| while (map[i].u.abfd == iterator.current.member) |
| { |
| if (arch_info->bits_per_address == 32) |
| { |
| bfd_h_put_64 (abfd, iterator.current.offset, st); |
| st += 8; |
| } |
| i++; |
| } |
| } |
| |
| /* loop over the 32 bit symbol names */ |
| i = 0; |
| for (current_bfd = abfd->archive_head; |
| current_bfd != NULL && i < orl_count; |
| current_bfd = current_bfd->archive_next) |
| { |
| arch_info = bfd_get_arch_info (current_bfd); |
| while (map[i].u.abfd == current_bfd) |
| { |
| if (arch_info->bits_per_address == 32) |
| { |
| string_length = sprintf (st, "%s", *map[i].name); |
| st += string_length + 1; |
| } |
| i++; |
| } |
| } |
| |
| if (bfd_write (symbol_table, symbol_table_size, abfd) |
| != symbol_table_size) |
| { |
| free (symbol_table); |
| return false; |
| } |
| free (symbol_table); |
| |
| prevoff = nextoff; |
| nextoff = nextoff + symbol_table_size; |
| } |
| else |
| PRINT20 (fhdr->symoff, 0); |
| |
| if (sym_64) |
| { |
| struct xcoff_ar_hdr_big *hdr; |
| char *symbol_table; |
| char *st; |
| |
| bfd_vma symbol_table_size = |
| SIZEOF_AR_HDR_BIG |
| + SXCOFFARFMAG |
| + 8 |
| + 8 * sym_64 |
| + str_64 + (str_64 & 1); |
| |
| symbol_table = bfd_zmalloc (symbol_table_size); |
| if (symbol_table == NULL) |
| return false; |
| |
| hdr = (struct xcoff_ar_hdr_big *) symbol_table; |
| |
| PRINT20 (hdr->size, 8 + 8 * sym_64 + str_64 + (str_64 & 1)); |
| PRINT20 (hdr->nextoff, 0); |
| PRINT20 (hdr->prevoff, prevoff); |
| PRINT12 (hdr->date, 0); |
| PRINT12 (hdr->uid, 0); |
| PRINT12 (hdr->gid, 0); |
| PRINT12 (hdr->mode, 0); |
| PRINT4 (hdr->namlen, 0); |
| |
| st = symbol_table + SIZEOF_AR_HDR_BIG; |
| memcpy (st, XCOFFARFMAG, SXCOFFARFMAG); |
| st += SXCOFFARFMAG; |
| |
| bfd_h_put_64 (abfd, sym_64, st); |
| st += 8; |
| |
| /* loop over the 64 bit offsets */ |
| i = 0; |
| archive_iterator_begin (&iterator, abfd); |
| while (i < orl_count && archive_iterator_next (&iterator)) |
| { |
| arch_info = bfd_get_arch_info (iterator.current.member); |
| while (map[i].u.abfd == iterator.current.member) |
| { |
| if (arch_info->bits_per_address == 64) |
| { |
| bfd_h_put_64 (abfd, iterator.current.offset, st); |
| st += 8; |
| } |
| i++; |
| } |
| } |
| |
| /* loop over the 64 bit symbol names */ |
| i = 0; |
| for (current_bfd = abfd->archive_head; |
| current_bfd != NULL && i < orl_count; |
| current_bfd = current_bfd->archive_next) |
| { |
| arch_info = bfd_get_arch_info (current_bfd); |
| while (map[i].u.abfd == current_bfd) |
| { |
| if (arch_info->bits_per_address == 64) |
| { |
| string_length = sprintf (st, "%s", *map[i].name); |
| st += string_length + 1; |
| } |
| i++; |
| } |
| } |
| |
| if (bfd_write (symbol_table, symbol_table_size, abfd) |
| != symbol_table_size) |
| { |
| free (symbol_table); |
| return false; |
| } |
| free (symbol_table); |
| |
| PRINT20 (fhdr->symoff64, nextoff); |
| } |
| else |
| PRINT20 (fhdr->symoff64, 0); |
| |
| return true; |
| } |
| |
| bool |
| _bfd_xcoff_write_armap (bfd *abfd, unsigned int elength ATTRIBUTE_UNUSED, |
| struct orl *map, unsigned int orl_count, int stridx) |
| { |
| if (! xcoff_big_format_p (abfd)) |
| return xcoff_write_armap_old (abfd, elength, map, orl_count, stridx); |
| else |
| return xcoff_write_armap_big (abfd, elength, map, orl_count, stridx); |
| } |
| |
| /* Write out an XCOFF archive. We always write an entire archive, |
| rather than fussing with the freelist and so forth. */ |
| |
| static bool |
| xcoff_write_archive_contents_old (bfd *abfd) |
| { |
| struct archive_iterator iterator; |
| struct xcoff_artdata xtdata; |
| struct xcoff_ar_file_hdr *fhdr = &xtdata.u.hdr; |
| bfd_size_type count; |
| bfd_size_type total_namlen; |
| file_ptr *offsets; |
| bool makemap; |
| bool hasobjects; |
| file_ptr prevoff, nextoff; |
| bfd *sub; |
| size_t i; |
| struct xcoff_ar_hdr ahdr; |
| bfd_size_type size; |
| char *p; |
| char decbuf[XCOFFARMAG_ELEMENT_SIZE + 1]; |
| |
| memset (&xtdata, 0, sizeof (xtdata)); |
| memcpy (fhdr->magic, XCOFFARMAG, SXCOFFARMAG); |
| sprintf (fhdr->firstmemoff, "%zu", SIZEOF_AR_FILE_HDR); |
| sprintf (fhdr->freeoff, "%d", 0); |
| |
| count = 0; |
| total_namlen = 0; |
| for (sub = abfd->archive_head; sub != NULL; sub = sub->archive_next) |
| { |
| ++count; |
| total_namlen += strlen (normalize_filename (sub)) + 1; |
| if (sub->arelt_data == NULL) |
| { |
| sub->arelt_data = bfd_zmalloc (sizeof (struct areltdata)); |
| if (sub->arelt_data == NULL) |
| return false; |
| } |
| if (arch_xhdr (sub) == NULL) |
| { |
| struct xcoff_ar_hdr *ahdrp; |
| struct stat s; |
| |
| if ((sub->flags & BFD_IN_MEMORY) != 0) |
| { |
| /* Assume we just "made" the member, and fake it. */ |
| struct bfd_in_memory *bim |
| = (struct bfd_in_memory *) sub->iostream; |
| time (&s.st_mtime); |
| s.st_uid = getuid (); |
| s.st_gid = getgid (); |
| s.st_mode = 0644; |
| s.st_size = bim->size; |
| } |
| else if (stat (bfd_get_filename (sub), &s) != 0) |
| { |
| bfd_set_input_error (sub, bfd_error_system_call); |
| return false; |
| } |
| |
| if ((abfd->flags & BFD_DETERMINISTIC_OUTPUT) != 0) |
| { |
| s.st_mtime = 0; |
| s.st_uid = 0; |
| s.st_gid = 0; |
| s.st_mode = 0644; |
| } |
| |
| ahdrp = bfd_zalloc (sub, sizeof (*ahdrp)); |
| if (ahdrp == NULL) |
| return false; |
| |
| sprintf (ahdrp->size, "%ld", (long) s.st_size); |
| sprintf (ahdrp->date, "%ld", (long) s.st_mtime); |
| sprintf (ahdrp->uid, "%ld", (long) s.st_uid); |
| sprintf (ahdrp->gid, "%ld", (long) s.st_gid); |
| sprintf (ahdrp->mode, "%o", (unsigned int) s.st_mode); |
| |
| arch_eltdata (sub)->arch_header = (char *) ahdrp; |
| arch_eltdata (sub)->parsed_size = s.st_size; |
| } |
| } |
| offsets = (file_ptr *) bfd_alloc (abfd, count * sizeof (file_ptr)); |
| if (offsets == NULL) |
| return false; |
| |
| if (bfd_seek (abfd, SIZEOF_AR_FILE_HDR, SEEK_SET) != 0) |
| return false; |
| |
| makemap = bfd_has_map (abfd); |
| hasobjects = false; |
| prevoff = 0; |
| for (archive_iterator_begin (&iterator, abfd), i = 0; |
| archive_iterator_next (&iterator); |
| i++) |
| { |
| bfd_size_type namlen; |
| struct xcoff_ar_hdr *ahdrp; |
| |
| if (makemap && ! hasobjects) |
| { |
| if (bfd_check_format (iterator.current.member, bfd_object)) |
| hasobjects = true; |
| } |
| |
| ahdrp = arch_xhdr (iterator.current.member); |
| sprintf (ahdrp->prevoff, "%ld", (long) prevoff); |
| sprintf (ahdrp->namlen, "%ld", (long) iterator.current.namlen); |
| sprintf (ahdrp->nextoff, "%ld", (long) iterator.next.offset); |
| |
| /* We need spaces, not null bytes, in the header. */ |
| for (p = (char *) ahdrp; p < (char *) ahdrp + SIZEOF_AR_HDR; p++) |
| if (*p == '\0') |
| *p = ' '; |
| |
| if (!do_pad (abfd, iterator.current.leading_padding)) |
| return false; |
| |
| BFD_ASSERT (iterator.current.offset == bfd_tell (abfd)); |
| namlen = iterator.current.padded_namlen; |
| if (bfd_write (ahdrp, SIZEOF_AR_HDR, abfd) != SIZEOF_AR_HDR |
| || bfd_write (iterator.current.name, namlen, abfd) != namlen |
| || bfd_write (XCOFFARFMAG, SXCOFFARFMAG, abfd) != SXCOFFARFMAG |
| || bfd_seek (iterator.current.member, 0, SEEK_SET) != 0 |
| || !do_copy (abfd, iterator.current.member) |
| || !do_pad (abfd, iterator.current.trailing_padding)) |
| return false; |
| |
| offsets[i] = iterator.current.offset; |
| prevoff = iterator.current.offset; |
| } |
| |
| sprintf (fhdr->lastmemoff, "%ld", (long) prevoff); |
| |
| /* Write out the member table. */ |
| |
| nextoff = iterator.next.offset; |
| BFD_ASSERT (nextoff == bfd_tell (abfd)); |
| sprintf (fhdr->memoff, "%ld", (long) nextoff); |
| |
| memset (&ahdr, 0, sizeof ahdr); |
| sprintf (ahdr.size, "%ld", (long) (XCOFFARMAG_ELEMENT_SIZE |
| + count * XCOFFARMAG_ELEMENT_SIZE |
| + total_namlen)); |
| sprintf (ahdr.prevoff, "%ld", (long) prevoff); |
| sprintf (ahdr.date, "%d", 0); |
| sprintf (ahdr.uid, "%d", 0); |
| sprintf (ahdr.gid, "%d", 0); |
| sprintf (ahdr.mode, "%d", 0); |
| sprintf (ahdr.namlen, "%d", 0); |
| |
| size = (SIZEOF_AR_HDR |
| + XCOFFARMAG_ELEMENT_SIZE |
| + count * XCOFFARMAG_ELEMENT_SIZE |
| + total_namlen |
| + SXCOFFARFMAG); |
| |
| prevoff = nextoff; |
| nextoff += size + (size & 1); |
| |
| if (makemap && hasobjects) |
| sprintf (ahdr.nextoff, "%ld", (long) nextoff); |
| else |
| sprintf (ahdr.nextoff, "%d", 0); |
| |
| /* We need spaces, not null bytes, in the header. */ |
| for (p = (char *) &ahdr; p < (char *) &ahdr + SIZEOF_AR_HDR; p++) |
| if (*p == '\0') |
| *p = ' '; |
| |
| if ((bfd_write (&ahdr, SIZEOF_AR_HDR, abfd) != SIZEOF_AR_HDR) |
| || bfd_write (XCOFFARFMAG, SXCOFFARFMAG, abfd) != SXCOFFARFMAG) |
| return false; |
| |
| sprintf (decbuf, "%-12ld", (long) count); |
| if (bfd_write (decbuf, XCOFFARMAG_ELEMENT_SIZE, abfd) |
| != XCOFFARMAG_ELEMENT_SIZE) |
| return false; |
| for (i = 0; i < (size_t) count; i++) |
| { |
| sprintf (decbuf, "%-12ld", (long) offsets[i]); |
| if (bfd_write (decbuf, XCOFFARMAG_ELEMENT_SIZE, abfd) |
| != XCOFFARMAG_ELEMENT_SIZE) |
| return false; |
| } |
| for (sub = abfd->archive_head; sub != NULL; sub = sub->archive_next) |
| { |
| const char *name; |
| bfd_size_type namlen; |
| |
| name = normalize_filename (sub); |
| namlen = strlen (name); |
| if (bfd_write (name, namlen + 1, abfd) != namlen + 1) |
| return false; |
| } |
| |
| if (! do_pad (abfd, size & 1)) |
| return false; |
| |
| /* Write out the armap, if appropriate. */ |
| if (! makemap || ! hasobjects) |
| sprintf (fhdr->symoff, "%d", 0); |
| else |
| { |
| BFD_ASSERT (nextoff == bfd_tell (abfd)); |
| sprintf (fhdr->symoff, "%ld", (long) nextoff); |
| bfd_ardata (abfd)->tdata = &xtdata; |
| bool ret = _bfd_compute_and_write_armap (abfd, 0); |
| bfd_ardata (abfd)->tdata = NULL; |
| if (!ret) |
| return false; |
| } |
| |
| /* Write out the archive file header. */ |
| |
| /* We need spaces, not null bytes, in the header. */ |
| for (p = (char *) fhdr; p < (char *) fhdr + SIZEOF_AR_FILE_HDR; p++) |
| if (*p == '\0') |
| *p = ' '; |
| |
| if (bfd_seek (abfd, 0, SEEK_SET) != 0 |
| || (bfd_write (fhdr, SIZEOF_AR_FILE_HDR, abfd) != SIZEOF_AR_FILE_HDR)) |
| return false; |
| |
| return true; |
| } |
| |
| static bool |
| xcoff_write_archive_contents_big (bfd *abfd) |
| { |
| struct xcoff_artdata xtdata; |
| struct xcoff_ar_file_hdr_big *fhdr = &xtdata.u.bhdr; |
| bfd_size_type count; |
| bfd_size_type total_namlen; |
| file_ptr *offsets; |
| bool makemap; |
| bool hasobjects; |
| file_ptr prevoff, nextoff; |
| bfd *current_bfd; |
| size_t i; |
| struct xcoff_ar_hdr_big *hdr; |
| bfd_size_type size; |
| char *member_table, *mt; |
| bfd_vma member_table_size; |
| struct archive_iterator iterator; |
| |
| memset (&xtdata, 0, sizeof (xtdata)); |
| memcpy (fhdr->magic, XCOFFARMAGBIG, SXCOFFARMAG); |
| |
| if (bfd_seek (abfd, SIZEOF_AR_FILE_HDR_BIG, SEEK_SET) != 0) |
| return false; |
| |
| /* Calculate count and total_namlen. */ |
| makemap = bfd_has_map (abfd); |
| hasobjects = false; |
| for (current_bfd = abfd->archive_head, count = 0, total_namlen = 0; |
| current_bfd != NULL; |
| current_bfd = current_bfd->archive_next, count++) |
| { |
| total_namlen += strlen (normalize_filename (current_bfd)) + 1; |
| |
| if (makemap |
| && ! hasobjects |
| && bfd_check_format (current_bfd, bfd_object)) |
| hasobjects = true; |
| |
| if (current_bfd->arelt_data == NULL) |
| { |
| size = sizeof (struct areltdata); |
| current_bfd->arelt_data = bfd_zmalloc (size); |
| if (current_bfd->arelt_data == NULL) |
| return false; |
| } |
| |
| if (arch_xhdr_big (current_bfd) == NULL) |
| { |
| struct xcoff_ar_hdr_big *ahdrp; |
| struct stat s; |
| |
| if ((current_bfd->flags & BFD_IN_MEMORY) != 0) |
| { |
| /* Assume we just "made" the member, and fake it. */ |
| struct bfd_in_memory *bim |
| = (struct bfd_in_memory *) current_bfd->iostream; |
| time (&s.st_mtime); |
| s.st_uid = getuid (); |
| s.st_gid = getgid (); |
| s.st_mode = 0644; |
| s.st_size = bim->size; |
| } |
| else if (stat (bfd_get_filename (current_bfd), &s) != 0) |
| { |
| bfd_set_input_error (current_bfd, bfd_error_system_call); |
| return false; |
| } |
| |
| if ((abfd->flags & BFD_DETERMINISTIC_OUTPUT) != 0) |
| { |
| s.st_mtime = 0; |
| s.st_uid = 0; |
| s.st_gid = 0; |
| s.st_mode = 0644; |
| } |
| |
| ahdrp = bfd_zalloc (current_bfd, sizeof (*ahdrp)); |
| if (ahdrp == NULL) |
| return false; |
| |
| PRINT20 (ahdrp->size, s.st_size); |
| PRINT12 (ahdrp->date, s.st_mtime); |
| PRINT12 (ahdrp->uid, s.st_uid); |
| PRINT12 (ahdrp->gid, s.st_gid); |
| PRINT12_OCTAL (ahdrp->mode, s.st_mode); |
| |
| arch_eltdata (current_bfd)->arch_header = (char *) ahdrp; |
| arch_eltdata (current_bfd)->parsed_size = s.st_size; |
| } |
| } |
| |
| offsets = NULL; |
| if (count) |
| { |
| offsets = (file_ptr *) bfd_malloc (count * sizeof (file_ptr)); |
| if (offsets == NULL) |
| return false; |
| } |
| |
| prevoff = 0; |
| for (archive_iterator_begin (&iterator, abfd), i = 0; |
| archive_iterator_next (&iterator); |
| i++) |
| { |
| bfd_size_type namlen; |
| struct xcoff_ar_hdr_big *ahdrp; |
| |
| ahdrp = arch_xhdr_big (iterator.current.member); |
| PRINT20 (ahdrp->prevoff, prevoff); |
| PRINT4 (ahdrp->namlen, iterator.current.namlen); |
| PRINT20 (ahdrp->nextoff, iterator.next.offset); |
| |
| if (!do_pad (abfd, iterator.current.leading_padding)) |
| { |
| free (offsets); |
| return false; |
| } |
| |
| BFD_ASSERT (iterator.current.offset == bfd_tell (abfd)); |
| namlen = iterator.current.padded_namlen; |
| if (bfd_write (ahdrp, SIZEOF_AR_HDR_BIG, abfd) != SIZEOF_AR_HDR_BIG |
| || bfd_write (iterator.current.name, namlen, abfd) != namlen |
| || bfd_write (XCOFFARFMAG, SXCOFFARFMAG, abfd) != SXCOFFARFMAG |
| || bfd_seek (iterator.current.member, 0, SEEK_SET) != 0 |
| || !do_copy (abfd, iterator.current.member) |
| || !do_pad (abfd, iterator.current.trailing_padding)) |
| { |
| free (offsets); |
| return false; |
| } |
| |
| offsets[i] = iterator.current.offset; |
| prevoff = iterator.current.offset; |
| } |
| |
| if (count) |
| { |
| PRINT20 (fhdr->firstmemoff, offsets[0]); |
| PRINT20 (fhdr->lastmemoff, prevoff); |
| } |
| |
| /* Write out the member table. |
| Layout : |
| |
| standard big archive header |
| 0x0000 ar_size [0x14] |
| 0x0014 ar_nxtmem [0x14] |
| 0x0028 ar_prvmem [0x14] |
| 0x003C ar_date [0x0C] |
| 0x0048 ar_uid [0x0C] |
| 0x0054 ar_gid [0x0C] |
| 0x0060 ar_mod [0x0C] |
| 0x006C ar_namelen[0x04] |
| 0x0070 ar_fmag [0x02] |
| |
| Member table |
| 0x0072 count [0x14] |
| 0x0086 offsets [0x14 * counts] |
| 0x0086 + 0x14 * counts names [??] |
| ?? pad to even bytes. |
| */ |
| |
| nextoff = iterator.next.offset; |
| BFD_ASSERT (nextoff == bfd_tell (abfd)); |
| |
| member_table_size = (SIZEOF_AR_HDR_BIG |
| + SXCOFFARFMAG |
| + XCOFFARMAGBIG_ELEMENT_SIZE |
| + count * XCOFFARMAGBIG_ELEMENT_SIZE |
| + total_namlen); |
| |
| member_table_size += member_table_size & 1; |
| member_table = bfd_zmalloc (member_table_size); |
| if (member_table == NULL) |
| { |
| free (offsets); |
| return false; |
| } |
| |
| hdr = (struct xcoff_ar_hdr_big *) member_table; |
| |
| PRINT20 (hdr->size, (XCOFFARMAGBIG_ELEMENT_SIZE |
| + count * XCOFFARMAGBIG_ELEMENT_SIZE |
| + total_namlen + (total_namlen & 1))); |
| if (makemap && hasobjects) |
| PRINT20 (hdr->nextoff, nextoff + member_table_size); |
| else |
| PRINT20 (hdr->nextoff, 0); |
| PRINT20 (hdr->prevoff, prevoff); |
| PRINT12 (hdr->date, 0); |
| PRINT12 (hdr->uid, 0); |
| PRINT12 (hdr->gid, 0); |
| PRINT12 (hdr->mode, 0); |
| PRINT4 (hdr->namlen, 0); |
| |
| mt = member_table + SIZEOF_AR_HDR_BIG; |
| memcpy (mt, XCOFFARFMAG, SXCOFFARFMAG); |
| mt += SXCOFFARFMAG; |
| |
| PRINT20 (mt, count); |
| mt += XCOFFARMAGBIG_ELEMENT_SIZE; |
| for (i = 0; i < (size_t) count; i++) |
| { |
| PRINT20 (mt, offsets[i]); |
| mt += XCOFFARMAGBIG_ELEMENT_SIZE; |
| } |
| |
| if (count) |
| { |
| free (offsets); |
| offsets = NULL; |
| } |
| |
| for (current_bfd = abfd->archive_head; |
| current_bfd != NULL; |
| current_bfd = current_bfd->archive_next) |
| { |
| const char *name; |
| size_t namlen; |
| |
| name = normalize_filename (current_bfd); |
| namlen = sprintf (mt, "%s", name); |
| mt += namlen + 1; |
| } |
| |
| if (bfd_write (member_table, member_table_size, abfd) != member_table_size) |
| return false; |
| |
| free (member_table); |
| |
| PRINT20 (fhdr->memoff, nextoff); |
| |
| prevoff = nextoff; |
| nextoff += member_table_size; |
| |
| /* Write out the armap, if appropriate. */ |
| |
| if (! makemap || ! hasobjects) |
| PRINT20 (fhdr->symoff, 0); |
| else |
| { |
| BFD_ASSERT (nextoff == bfd_tell (abfd)); |
| |
| /* Save nextoff in fhdr->symoff so the armap routine can use it. */ |
| PRINT20 (fhdr->symoff, nextoff); |
| |
| bfd_ardata (abfd)->tdata = &xtdata; |
| bool ret = _bfd_compute_and_write_armap (abfd, 0); |
| bfd_ardata (abfd)->tdata = NULL; |
| if (!ret) |
| return false; |
| } |
| |
| /* Write out the archive file header. */ |
| |
| if (bfd_seek (abfd, 0, SEEK_SET) != 0 |
| || (bfd_write (fhdr, SIZEOF_AR_FILE_HDR_BIG, abfd) |
| != SIZEOF_AR_FILE_HDR_BIG)) |
| return false; |
| |
| return true; |
| } |
| |
| bool |
| _bfd_xcoff_write_archive_contents (bfd *abfd) |
| { |
| if (! xcoff_big_format_p (abfd)) |
| return xcoff_write_archive_contents_old (abfd); |
| else |
| return xcoff_write_archive_contents_big (abfd); |
| } |
| |
| /* We can't use the usual coff_sizeof_headers routine, because AIX |
| always uses an a.out header. */ |
| |
| int |
| _bfd_xcoff_sizeof_headers (bfd *abfd, |
| struct bfd_link_info *info ATTRIBUTE_UNUSED) |
| { |
| int size; |
| |
| size = FILHSZ; |
| if (xcoff_data (abfd)->full_aouthdr) |
| size += AOUTSZ; |
| else |
| size += SMALL_AOUTSZ; |
| size += abfd->section_count * SCNHSZ; |
| |
| if (info->strip != strip_all) |
| { |
| /* There can be additional sections just for dealing with overflow in |
| reloc and lineno counts. But the numbers of relocs and lineno aren't |
| known when bfd_sizeof_headers is called, so we compute them by |
| summing the numbers from input sections. */ |
| struct nbr_reloc_lineno |
| { |
| unsigned int reloc_count; |
| unsigned int lineno_count; |
| }; |
| struct nbr_reloc_lineno *n_rl; |
| bfd *sub; |
| unsigned int max_index; |
| asection *s; |
| |
| /* Although the number of sections is known, the maximum value of |
| section->index isn't (because some sections may have been removed). |
| Don't try to renumber sections, just compute the upper bound. */ |
| max_index = 0; |
| for (s = abfd->sections; s != NULL; s = s->next) |
| if (s->index > max_index) |
| max_index = s->index; |
| |
| /* Allocate the per section counters. It could be possible to use a |
| preallocated array as the number of sections is limited on XCOFF, |
| but this creates a maintainance issue. */ |
| n_rl = bfd_zmalloc ((max_index + 1) * sizeof (*n_rl)); |
| if (n_rl == NULL) |
| return -1; |
| |
| /* Sum. */ |
| for (sub = info->input_bfds; sub != NULL; sub = sub->link.next) |
| for (s = sub->sections; s != NULL; s = s->next) |
| if (s->output_section->owner == abfd |
| && !bfd_section_removed_from_list (abfd, s->output_section)) |
| { |
| struct nbr_reloc_lineno *e = &n_rl[s->output_section->index]; |
| e->reloc_count += s->reloc_count; |
| e->lineno_count += s->lineno_count; |
| } |
| |
| /* Add the size of a section for each section with an overflow. */ |
| for (s = abfd->sections; s != NULL; s = s->next) |
| { |
| struct nbr_reloc_lineno *e = &n_rl[s->index]; |
| |
| if (e->reloc_count >= 0xffff |
| || (e->lineno_count >= 0xffff && info->strip != strip_debugger)) |
| size += SCNHSZ; |
| } |
| |
| free (n_rl); |
| } |
| |
| return size; |
| } |
| |
| /* 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 (bfd *abfd, const void * s, struct internal_ldhdr *dst) |
| { |
| const struct external_ldhdr *src = (const struct external_ldhdr *) s; |
| |
| 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 (bfd *abfd, const struct internal_ldhdr *src, void * d) |
| { |
| struct external_ldhdr *dst = (struct external_ldhdr *) d; |
| |
| bfd_put_32 (abfd, (bfd_vma) 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 (bfd *abfd, const void * s, struct internal_ldsym *dst) |
| { |
| const struct external_ldsym *src = (const struct external_ldsym *) s; |
| |
| 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 (bfd *abfd, const struct internal_ldsym *src, void * d) |
| { |
| struct external_ldsym *dst = (struct external_ldsym *) d; |
| |
| if (src->_l._l_l._l_zeroes != 0) |
| memcpy (dst->_l._l_name, src->_l._l_name, SYMNMLEN); |
| else |
| { |
| bfd_put_32 (abfd, (bfd_vma) 0, dst->_l._l_l._l_zeroes); |
| bfd_put_32 (abfd, (bfd_vma) 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, (bfd_vma) 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); |
| } |
| |
| static void |
| xcoff_swap_reloc_in (bfd *abfd, void * s, void * d) |
| { |
| struct external_reloc *src = (struct external_reloc *) s; |
| struct internal_reloc *dst = (struct internal_reloc *) d; |
| |
| memset (dst, 0, sizeof (struct internal_reloc)); |
| |
| dst->r_vaddr = bfd_get_32 (abfd, src->r_vaddr); |
| dst->r_symndx = bfd_get_32 (abfd, src->r_symndx); |
| dst->r_size = bfd_get_8 (abfd, src->r_size); |
| dst->r_type = bfd_get_8 (abfd, src->r_type); |
| } |
| |
|