diff options
-rw-r--r-- | bfd/.Sanitize | 1 | ||||
-rw-r--r-- | bfd/ChangeLog | 68 | ||||
-rw-r--r-- | bfd/coff-a29k.c | 221 | ||||
-rw-r--r-- | bfd/coff-alpha.c | 60 | ||||
-rw-r--r-- | bfd/coff-mips.c | 23 | ||||
-rw-r--r-- | bfd/coffcode.h | 351 | ||||
-rw-r--r-- | bfd/coffgen.c | 102 | ||||
-rw-r--r-- | bfd/cofflink.c | 2006 | ||||
-rw-r--r-- | bfd/configure.in | 2 | ||||
-rw-r--r-- | bfd/libcoff-in.h | 84 | ||||
-rw-r--r-- | bfd/libcoff.h | 115 |
11 files changed, 2788 insertions, 245 deletions
diff --git a/bfd/.Sanitize b/bfd/.Sanitize index e8e636b..f2baca1 100644 --- a/bfd/.Sanitize +++ b/bfd/.Sanitize @@ -78,6 +78,7 @@ coff-we32k.c coff-z8k.c coffcode.h coffgen.c +cofflink.c coffswap.h config config.bfd diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 4fa9cb0..fc4a4f7 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,71 @@ +Tue Sep 6 14:51:11 1994 Ian Lance Taylor (ian@sanguine.cygnus.com) + + Add new style linker support to COFF backend. a29k only for now. + * cofflink.c: New file. + * libcoff-in.h: Include bfdlink.h. + (obj_coff_external_syms, obj_coff_strings): Define accessor macro. + (obj_coff_sym_hashes): Define accessor macro. + (struct coff_tdata): Add fields external_syms, strings, and + sym_hashes. + (struct coff_link_hash_entry): Define. + (struct coff_link_hash_table): Define. + (coff_link_hash_lookup, coff_link_hash_traverse): Define. + (coff_hash_table): Define. + (_bfd_coff_link_hash_table_create): Declare. + (_bfd_coff_link_add_symbols, _bfd_coff_final_link): Declare. + * coffcode.h (bfd_coff_backend_data): Add fields _bfd_relsz, + _bfd_coff_swap_reloc_in, _bfd_coff_sym_is_global, + _bfd_coff_compute_section_file_positions, + _bfd_coff_relocate_section. + (bfd_coff_relsz, bfd_coff_swap_reloc_in): Define. + (bfd_coff_sym_is_global): Define. + (bfd_coff_compute_section_file_positions): Define. + (bfd_coff_relocate_section): Define. + (coff_mkobject_hook): Initialize obj_raw_syment_count and + obj_conv_table_size. + (coff_compute_section_file_positions): Set target_index of all + sections. Set output_has_begun field. + (coff_write_object_contents): Don't set target_index; now done by + coff_compute_section_file_positions. Remove obsolete handling of + scn_base and data_base. Don't bother to check that target_index + is positive, since it always is. Remove use of pad, which is + always zero. Check obj_raw_syment_count, not bfd_get_symcount, + for the number of symbols, but only write them out if + bfd_get_symcount is non-zero. + (coff_slurp_symbol_table): Use obj_raw_syment_count, not + bfd_get_symcount for the number of symbols. Don't set + obj_conv_table_size. + (coff_sym_is_global): New static function or macro. + (coff_slurp_reloc_table): Call coff_swap_reloc_in, not + bfd_swap_reloc_in. + (coff_bfd_link_hash_table_create): If coff_relocate_section is + defined, define as _bfd_coff_link_hash_table_create. + (coff_bfd_link_add_symbols): Similar change. + (coff_bfd_final_link): Similar change. + (coff_relocate_section): Define as NULL if not defined. + (bfd_coff_std_swap_table): Initialize new fields. + * coffgen.c (coff_real_object_p): Don't set obj_raw_syment_count + and obj_conv_table_size here. + (coff_count_linenumbers): Reindent. If bfd_get_symcount is zero, + add up the line numbers from the sections. + (coff_write_symbols): Don't set bfd_get_symcount. + (coff_pointerize_aux): Don't pointerize a nonpositive x_endndx + field. + (coff_get_normalized_symtab): Use obj_raw_syment_count, not + bfd_get_symcount. + (coff_print_symbol): If auxp->fix_end, print x_endndx value. + * coffswap.h (coff_swap_reloc_in): Rename from bfd_swap_reloc_in. + Reindent. Change argument type to PTR. + * coff-a29k.c (coff_a29k_relocate_section): New static function. + (coff_relocate_section): Define. + * configure.in (a29kcoff_big_vec): Compile cofflink.o. + * coff-alpha.c (alpha_ecoff_backend_data): Initialize new fields. + * coff-mips.c (mips_ecoff_backend_data): Likewise. + * libcoff.h: Rebuilt. + * Makefile.in: Rebuilt dependencies. + (BFD32_BACKENDS): Add cofflink.o. + (CFILES): Add cofflink.c. + Tue Sep 6 14:00:45 1994 Ken Raeburn (raeburn@cujo.cygnus.com) * coffgen.c (coff_print_symbol, case bfd_symbol_print_all): Check diff --git a/bfd/coff-a29k.c b/bfd/coff-a29k.c index 473c372..35e6ea3 100644 --- a/bfd/coff-a29k.c +++ b/bfd/coff-a29k.c @@ -31,6 +31,9 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ static long get_symbol_value PARAMS ((asymbol *)); static bfd_reloc_status_type a29k_reloc PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **)); +static boolean coff_a29k_relocate_section + PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *, + struct internal_reloc *, struct internal_syment *, asection **)); #define INSERT_HWORD(WORD,HWORD) \ (((WORD) & 0xff00ff00) | (((HWORD) & 0xff00) << 8) | ((HWORD)& 0xff)) @@ -303,6 +306,224 @@ reloc_processing (relent,reloc, symbols, abfd, section) } } +/* The reloc processing routine for the optimized COFF linker. */ + +static boolean +coff_a29k_relocate_section (output_bfd, info, input_bfd, input_section, + contents, relocs, syms, sections) + bfd *output_bfd; + struct bfd_link_info *info; + bfd *input_bfd; + asection *input_section; + bfd_byte *contents; + struct internal_reloc *relocs; + struct internal_syment *syms; + asection **sections; +{ + struct internal_reloc *rel; + struct internal_reloc *relend; + boolean hihalf; + bfd_vma hihalf_val; + + /* If we are performing a relocateable link, we don't need to do a + thing. The caller will take care of adjusting the reloc + addresses and symbol indices. */ + if (info->relocateable) + return true; + + hihalf = false; + hihalf_val = 0; + + rel = relocs; + relend = rel + input_section->reloc_count; + for (; rel < relend; rel++) + { + long symndx; + bfd_byte *loc; + struct coff_link_hash_entry *h; + struct internal_syment *sym; + asection *sec; + bfd_vma val; + boolean overflow; + unsigned long insn; + long signed_value; + unsigned long unsigned_value; + bfd_reloc_status_type rstat; + + symndx = rel->r_symndx; + loc = contents + rel->r_vaddr - input_section->vma; + + h = obj_coff_sym_hashes (input_bfd)[symndx]; + + sym = NULL; + sec = NULL; + val = 0; + + /* An R_IHCONST reloc does not have a symbol. Instead, the + symbol index is an addend. R_IHCONST is always used in + conjunction with R_IHHALF. */ + if (rel->r_type != R_IHCONST) + { + if (h == NULL) + { + sym = syms + symndx; + sec = sections[symndx]; + val = (sec->output_section->vma + + sec->output_offset + + sym->n_value + - sec->vma); + } + else + { + if (h->root.type == bfd_link_hash_defined) + { + sec = h->root.u.def.section; + val = (h->root.u.def.value + + sec->output_section->vma + + sec->output_offset); + } + else + { + if (! ((*info->callbacks->undefined_symbol) + (info, h->root.root.string, input_bfd, input_section, + rel->r_vaddr - input_section->vma))) + return false; + } + } + + if (hihalf) + { + if (! ((*info->callbacks->reloc_dangerous) + (info, "missing IHCONST reloc", input_bfd, + input_section, rel->r_vaddr - input_section->vma))) + return false; + hihalf = false; + } + } + + overflow = false; + + switch (rel->r_type) + { + default: + bfd_set_error (bfd_error_bad_value); + return false; + + case R_IREL: + insn = bfd_get_32 (input_bfd, loc); + + /* Extract the addend. */ + signed_value = EXTRACT_HWORD (insn); + signed_value = SIGN_EXTEND_HWORD (signed_value); + signed_value <<= 2; + + /* Determine the destination of the jump. */ + signed_value += val + rel->r_vaddr - input_section->vma; + + if ((signed_value & ~0x3ffff) == 0) + { + /* We can use an absolute jump. */ + insn |= (1 << 24); + } + else + { + /* Make the destination PC relative. */ + signed_value -= (input_section->output_section->vma + + input_section->output_offset + + (rel->r_vaddr - input_section->vma)); + if (signed_value > 0x1ffff || signed_value < - 0x20000) + { + overflow = true; + signed_value = 0; + } + } + + /* Put the adjusted value back into the instruction. */ + signed_value >>= 2; + insn = INSERT_HWORD (insn, signed_value); + + bfd_put_32 (input_bfd, (bfd_vma) insn, loc); + + break; + + case R_ILOHALF: + insn = bfd_get_32 (input_bfd, loc); + unsigned_value = EXTRACT_HWORD (insn); + unsigned_value += val; + insn = INSERT_HWORD (insn, unsigned_value); + bfd_put_32 (input_bfd, insn, loc); + break; + + case R_IHIHALF: + /* Save the value for the R_IHCONST reloc. */ + hihalf = true; + hihalf_val = val; + break; + + case R_IHCONST: + if (! hihalf) + { + if (! ((*info->callbacks->reloc_dangerous) + (info, "missing IHIHALF reloc", input_bfd, + input_section, rel->r_vaddr - input_section->vma))) + return false; + hihalf_val = 0; + } + + insn = bfd_get_32 (input_bfd, loc); + unsigned_value = rel->r_symndx + hihalf_val; + unsigned_value >>= 16; + insn = INSERT_HWORD (insn, unsigned_value); + bfd_put_32 (input_bfd, (bfd_vma) insn, loc); + + hihalf = false; + + break; + + case R_BYTE: + case R_HWORD: + case R_WORD: + rstat = _bfd_relocate_contents (howto_table + rel->r_type, + input_bfd, val, loc); + if (rstat == bfd_reloc_overflow) + overflow = true; + else if (rstat != bfd_reloc_ok) + abort (); + break; + } + + if (overflow) + { + const char *name; + char buf[SYMNMLEN + 1]; + + if (h != NULL) + name = h->root.root.string; + else if (sym == NULL) + name = "*unknown*"; + else if (sym->_n._n_n._n_zeroes == 0 + && sym->_n._n_n._n_offset != 0) + name = obj_coff_strings (input_bfd) + sym->_n._n_n._n_offset; + else + { + strncpy (buf, sym->_n._n_name, SYMNMLEN); + buf[SYMNMLEN] = '\0'; + name = buf; + } + + if (! ((*info->callbacks->reloc_overflow) + (info, name, howto_table[rel->r_type].name, (bfd_vma) 0, + input_bfd, input_section, + rel->r_vaddr - input_section->vma))) + return false; + } + } + + return true; +} + +#define coff_relocate_section coff_a29k_relocate_section + #include "coffcode.h" const bfd_target a29kcoff_big_vec = diff --git a/bfd/coff-alpha.c b/bfd/coff-alpha.c index 1bbfbdb..9b67dfb 100644 --- a/bfd/coff-alpha.c +++ b/bfd/coff-alpha.c @@ -35,6 +35,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ static const bfd_target *alpha_ecoff_object_p PARAMS ((bfd *)); static boolean alpha_ecoff_bad_format_hook PARAMS ((bfd *abfd, PTR filehdr)); +static PTR alpha_ecoff_mkobject_hook PARAMS ((bfd *, PTR filehdr, PTR aouthdr)); static void alpha_ecoff_swap_reloc_in PARAMS ((bfd *, PTR, struct internal_reloc *)); static void alpha_ecoff_swap_reloc_out PARAMS ((bfd *, @@ -472,6 +473,40 @@ alpha_ecoff_bad_format_hook (abfd, filehdr) return true; } + +/* This is a hook called by coff_real_object_p to create any backend + specific information. */ + +static PTR +alpha_ecoff_mkobject_hook (abfd, filehdr, aouthdr) + bfd *abfd; + PTR filehdr; + PTR aouthdr; +{ + PTR ecoff; + + ecoff = _bfd_ecoff_mkobject_hook (abfd, filehdr, aouthdr); + + if (ecoff != NULL) + { + struct internal_filehdr *internal_f = (struct internal_filehdr *) filehdr; + + /* Set additional BFD flags according to the object type from the + machine specific file header flags. */ + switch (internal_f->f_flags & F_ALPHA_OBJECT_TYPE_MASK) + { + case F_ALPHA_SHARABLE: + abfd->flags |= DYNAMIC; + break; + case F_ALPHA_CALL_SHARED: + /* Always executable if using shared libraries as the run time + loader might resolve undefined references. */ + abfd->flags |= (DYNAMIC | EXEC_P); + break; + } + } + return ecoff; +} /* Reloc handling. */ @@ -641,7 +676,7 @@ alpha_adjust_reloc_in (abfd, intern, rptr) some reason the address of this reloc type is not adjusted by the section vma. We record the gp value for this object file here, for convenience when doing the GPDISP relocation. */ - rptr->sym_ptr_ptr = bfd_abs_section.symbol_ptr_ptr; + rptr->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr; rptr->address = intern->r_vaddr; rptr->addend = ecoff_data (abfd)->gp; break; @@ -954,7 +989,7 @@ alpha_ecoff_get_relocated_section_contents (abfd, link_info, link_order, /* Figure out the relocation of this symbol. */ symbol = *rel->sym_ptr_ptr; - if (symbol->section == &bfd_und_section) + if (bfd_is_und_section (symbol->section)) r = bfd_reloc_undefined; if (bfd_is_com_section (symbol->section)) @@ -1014,7 +1049,7 @@ alpha_ecoff_get_relocated_section_contents (abfd, link_info, link_order, /* Figure out the relocation of this symbol. */ symbol = *rel->sym_ptr_ptr; - if (symbol->section == &bfd_und_section) + if (bfd_is_und_section (symbol->section)) r = bfd_reloc_undefined; if (bfd_is_com_section (symbol->section)) @@ -1047,7 +1082,7 @@ alpha_ecoff_get_relocated_section_contents (abfd, link_info, link_order, /* Figure out the relocation of this symbol. */ symbol = *rel->sym_ptr_ptr; - if (symbol->section == &bfd_und_section) + if (bfd_is_und_section (symbol->section)) r = bfd_reloc_undefined; if (bfd_is_com_section (symbol->section)) @@ -1382,7 +1417,7 @@ alpha_relocate_section (output_bfd, info, input_bfd, input_section, bfd_get_section_by_name (input_bfd, ".fini"); symndx_to_section[RELOC_SECTION_LITA] = bfd_get_section_by_name (input_bfd, ".lita"); - symndx_to_section[RELOC_SECTION_ABS] = &bfd_abs_section; + symndx_to_section[RELOC_SECTION_ABS] = bfd_abs_section_ptr; ecoff_data (input_bfd)->symndx_to_section = symndx_to_section; } @@ -1911,13 +1946,14 @@ static const struct ecoff_backend_data alpha_ecoff_backend_data = (unsigned (*) PARAMS ((bfd *,PTR,PTR))) bfd_void, /* reloc_out */ alpha_ecoff_swap_filehdr_out, alpha_ecoff_swap_aouthdr_out, alpha_ecoff_swap_scnhdr_out, - FILHSZ, AOUTSZ, SCNHSZ, 0, 0, 0, true, + FILHSZ, AOUTSZ, SCNHSZ, 0, 0, 0, 0, true, alpha_ecoff_swap_filehdr_in, alpha_ecoff_swap_aouthdr_in, - alpha_ecoff_swap_scnhdr_in, alpha_ecoff_bad_format_hook, - _bfd_ecoff_set_arch_mach_hook, _bfd_ecoff_mkobject_hook, - _bfd_ecoff_styp_to_sec_flags, _bfd_ecoff_make_section_hook, - _bfd_ecoff_set_alignment_hook, _bfd_ecoff_slurp_symbol_table, - NULL, NULL + alpha_ecoff_swap_scnhdr_in, NULL, + alpha_ecoff_bad_format_hook, _bfd_ecoff_set_arch_mach_hook, + alpha_ecoff_mkobject_hook, _bfd_ecoff_styp_to_sec_flags, + _bfd_ecoff_make_section_hook, _bfd_ecoff_set_alignment_hook, + _bfd_ecoff_slurp_symbol_table, + NULL, NULL, NULL, NULL, NULL, NULL }, /* Supported architecture. */ bfd_arch_alpha, @@ -2004,7 +2040,7 @@ const bfd_target ecoffalpha_little_vec = (HAS_RELOC | EXEC_P | /* object flags */ HAS_LINENO | HAS_DEBUG | - HAS_SYMS | HAS_LOCALS | WP_TEXT | D_PAGED), + HAS_SYMS | HAS_LOCALS | DYNAMIC | WP_TEXT | D_PAGED), (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC), /* sect flags */ diff --git a/bfd/coff-mips.c b/bfd/coff-mips.c index 97b7294..4b7fb32 100644 --- a/bfd/coff-mips.c +++ b/bfd/coff-mips.c @@ -542,7 +542,7 @@ mips_adjust_reloc_in (abfd, intern, rptr) /* If the type is MIPS_R_IGNORE, make sure this is a reference to the absolute section so that the reloc is ignored. */ if (intern->r_type == MIPS_R_IGNORE) - rptr->sym_ptr_ptr = bfd_abs_section.symbol_ptr_ptr; + rptr->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr; /* If this is a MIPS_R_SWITCH reloc, or an internal MIPS_R_RELHI or MIPS_R_RELLO reloc, we want the addend field of the BFD relocto @@ -657,7 +657,7 @@ mips_refhi_reloc (abfd, } ret = bfd_reloc_ok; - if (symbol->section == &bfd_und_section + if (bfd_is_und_section (symbol->section) && output_bfd == (bfd *) NULL) ret = bfd_reloc_undefined; @@ -784,7 +784,7 @@ mips_gprel_reloc (abfd, output_bfd = symbol->section->output_section->owner; } - if (symbol->section == &bfd_und_section + if (bfd_is_und_section (symbol->section) && relocateable == false) return bfd_reloc_undefined; @@ -925,7 +925,7 @@ mips_relhi_reloc (abfd, } ret = bfd_reloc_ok; - if (symbol->section == &bfd_und_section + if (bfd_is_und_section (symbol->section) && output_bfd == (bfd *) NULL) ret = bfd_reloc_undefined; @@ -990,7 +990,7 @@ mips_rello_reloc (abfd, symbol is not defined we don't want to do this, because we don't want the value in the object file to incorporate the address of the reloc. */ - if (bfd_get_section (symbol) != &bfd_und_section + if (! bfd_is_und_section (bfd_get_section (symbol)) && ! bfd_is_com_section (bfd_get_section (symbol))) val -= (input_section->output_section->vma + input_section->output_offset @@ -2275,13 +2275,14 @@ static const struct ecoff_backend_data mips_ecoff_backend_data = (unsigned (*) PARAMS ((bfd *,PTR,PTR))) bfd_void, /* reloc_out */ mips_ecoff_swap_filehdr_out, mips_ecoff_swap_aouthdr_out, mips_ecoff_swap_scnhdr_out, - FILHSZ, AOUTSZ, SCNHSZ, 0, 0, 0, true, + FILHSZ, AOUTSZ, SCNHSZ, 0, 0, 0, 0, true, mips_ecoff_swap_filehdr_in, mips_ecoff_swap_aouthdr_in, - mips_ecoff_swap_scnhdr_in, mips_ecoff_bad_format_hook, - _bfd_ecoff_set_arch_mach_hook, _bfd_ecoff_mkobject_hook, - _bfd_ecoff_styp_to_sec_flags, _bfd_ecoff_make_section_hook, - _bfd_ecoff_set_alignment_hook, _bfd_ecoff_slurp_symbol_table, - NULL, NULL + mips_ecoff_swap_scnhdr_in, NULL, + mips_ecoff_bad_format_hook, _bfd_ecoff_set_arch_mach_hook, + _bfd_ecoff_mkobject_hook, _bfd_ecoff_styp_to_sec_flags, + _bfd_ecoff_make_section_hook, _bfd_ecoff_set_alignment_hook, + _bfd_ecoff_slurp_symbol_table, + NULL, NULL, NULL, NULL, NULL, NULL }, /* Supported architecture. */ bfd_arch_mips, diff --git a/bfd/coffcode.h b/bfd/coffcode.h index 0eed749..f1729dd 100644 --- a/bfd/coffcode.h +++ b/bfd/coffcode.h @@ -608,6 +608,7 @@ dependent COFF routines: . unsigned int _bfd_scnhsz; . unsigned int _bfd_symesz; . unsigned int _bfd_auxesz; +. unsigned int _bfd_relsz; . unsigned int _bfd_linesz; . boolean _bfd_coff_long_filenames; . void (*_bfd_coff_swap_filehdr_in) PARAMS (( @@ -622,6 +623,10 @@ dependent COFF routines: . bfd *abfd, . PTR ext, . PTR in)); +. void (*_bfd_coff_swap_reloc_in) PARAMS (( +. bfd *abfd, +. PTR ext, +. PTR in)); . boolean (*_bfd_coff_bad_format_hook) PARAMS (( . bfd *abfd, . PTR internal_filehdr)); @@ -662,6 +667,20 @@ dependent COFF routines: . arelent *r, . unsigned int shrink, . struct bfd_link_info *link_info)); +. boolean (*_bfd_coff_sym_is_global) PARAMS (( +. bfd *abfd, +. struct internal_syment *)); +. void (*_bfd_coff_compute_section_file_positions) PARAMS (( +. bfd *abfd)); +. boolean (*_bfd_coff_relocate_section) PARAMS (( +. bfd *output_bfd, +. struct bfd_link_info *info, +. bfd *input_bfd, +. asection *input_section, +. bfd_byte *contents, +. struct internal_reloc *relocs, +. struct internal_syment *syms, +. asection **sections)); . .} bfd_coff_backend_data; . @@ -702,6 +721,7 @@ dependent COFF routines: .#define bfd_coff_scnhsz(abfd) (coff_backend_info (abfd)->_bfd_scnhsz) .#define bfd_coff_symesz(abfd) (coff_backend_info (abfd)->_bfd_symesz) .#define bfd_coff_auxesz(abfd) (coff_backend_info (abfd)->_bfd_auxesz) +.#define bfd_coff_relsz(abfd) (coff_backend_info (abfd)->_bfd_relsz) .#define bfd_coff_linesz(abfd) (coff_backend_info (abfd)->_bfd_linesz) .#define bfd_coff_long_filenames(abfd) (coff_backend_info (abfd)->_bfd_coff_long_filenames) .#define bfd_coff_swap_filehdr_in(abfd, i,o) \ @@ -713,6 +733,9 @@ dependent COFF routines: .#define bfd_coff_swap_scnhdr_in(abfd, i,o) \ . ((coff_backend_info (abfd)->_bfd_coff_swap_scnhdr_in) (abfd, i, o)) . +.#define bfd_coff_swap_reloc_in(abfd, i, o) \ +. ((coff_backend_info (abfd)->_bfd_coff_swap_reloc_in) (abfd, i, o)) +. .#define bfd_coff_bad_format_hook(abfd, filehdr) \ . ((coff_backend_info (abfd)->_bfd_coff_bad_format_hook) (abfd, filehdr)) . @@ -744,6 +767,18 @@ dependent COFF routines: . ((coff_backend_info (abfd)->_bfd_coff_reloc16_estimate)\ . (abfd, section, reloc, shrink, link_info)) . +.#define bfd_coff_sym_is_global(abfd, sym)\ +. ((coff_backend_info (abfd)->_bfd_coff_sym_is_global)\ +. (abfd, sym)) +. +.#define bfd_coff_compute_section_file_positions(abfd)\ +. ((coff_backend_info (abfd)->_bfd_coff_compute_section_file_positions)\ +. (abfd)) +. +.#define bfd_coff_relocate_section(obfd,info,ibfd,o,con,rel,isyms,secs)\ +. ((coff_backend_info (ibfd)->_bfd_coff_relocate_section)\ +. (obfd, info, ibfd, o, con, rel, isyms, secs)) +. */ /* See whether the magic number matches. */ @@ -863,7 +898,6 @@ coff_mkobject (abfd) coff->symbols = (coff_symbol_type *) NULL; coff->conversion_table = (unsigned int *) NULL; coff->raw_syments = (struct coff_ptr_struct *) NULL; - coff->raw_linenos = (struct lineno *) NULL; coff->relocbase = 0; /* make_abs_section(abfd);*/ return true; @@ -886,7 +920,6 @@ coff_mkobject_hook (abfd, filehdr, aouthdr) coff = coff_data (abfd); coff->sym_filepos = internal_f->f_symptr; - coff->flags = internal_f->f_flags; /* These members communicate important constants about the symbol table to GDB's symbol-reading code. These `constants' @@ -899,6 +932,10 @@ coff_mkobject_hook (abfd, filehdr, aouthdr) coff->local_auxesz = AUXESZ; coff->local_linesz = LINESZ; + obj_raw_syment_count (abfd) = + obj_conv_table_size (abfd) = + internal_f->f_nsyms; + return (PTR) coff; } @@ -1386,6 +1423,8 @@ coff_compute_section_file_positions (abfd) #ifndef I960 file_ptr old_sofar; #endif + unsigned int count; + if (bfd_get_start_address (abfd)) { /* A start address may have been added to the original file. In this @@ -1397,10 +1436,11 @@ coff_compute_section_file_positions (abfd) sofar += AOUTSZ; sofar += abfd->section_count * SCNHSZ; - for (current = abfd->sections; + for (current = abfd->sections, count = 1; current != (asection *) NULL; - current = current->next) + current = current->next, ++count) { + current->target_index = count; /* Only deal with sections which have contents */ if (!(current->flags & SEC_HAS_CONTENTS)) @@ -1454,6 +1494,7 @@ coff_compute_section_file_positions (abfd) previous = current; } obj_relocbase (abfd) = sofar; + abfd->output_has_begun = true; } #ifndef RS6000COFF_C @@ -1538,15 +1579,11 @@ coff_write_object_contents (abfd) bfd * abfd; { asection *current; - unsigned int count; - boolean hasrelocs = false; boolean haslinno = false; file_ptr reloc_base; file_ptr lineno_base; file_ptr sym_base; - file_ptr scn_base; - file_ptr data_base; unsigned long reloc_size = 0; unsigned long lnno_size = 0; asection *text_sec = NULL; @@ -1558,55 +1595,22 @@ coff_write_object_contents (abfd) bfd_set_error (bfd_error_system_call); - /* Number the output sections, starting from one on the first section - with a name which doesn't start with a *. - @@ The code doesn't make this check. Is it supposed to be done, - or isn't it?? */ - count = 1; - for (current = abfd->sections; current != (asection *) NULL; - current = current->next) - { - current->target_index = count; - count++; - } if (abfd->output_has_begun == false) - { - coff_compute_section_file_positions (abfd); - } + coff_compute_section_file_positions (abfd); - if (abfd->sections != (asection *) NULL) - { - scn_base = abfd->sections->filepos; - } - else - { - scn_base = 0; - } - if (bfd_seek (abfd, scn_base, SEEK_SET) != 0) - return false; reloc_base = obj_relocbase (abfd); /* Make a pass through the symbol table to count line number entries and put them into the correct asections */ lnno_size = coff_count_linenumbers (abfd) * LINESZ; - data_base = scn_base; /* Work out the size of the reloc and linno areas */ for (current = abfd->sections; current != NULL; current = current->next) - { - /* We give section headers to +ve indexes */ - if (current->target_index > 0) - { - - reloc_size += current->reloc_count * RELSZ; - data_base += SCNHSZ; - } - - } + reloc_size += current->reloc_count * RELSZ; lineno_base = reloc_base + reloc_size; sym_base = lineno_base + lnno_size; @@ -1615,33 +1619,27 @@ coff_write_object_contents (abfd) for (current = abfd->sections; current != NULL; current = current->next) { - if (current->target_index > 0) + if (current->lineno_count) { - - if (current->lineno_count) - { - current->line_filepos = lineno_base; - current->moving_line_filepos = lineno_base; - lineno_base += current->lineno_count * LINESZ; - } - else - { - current->line_filepos = 0; - } - if (current->reloc_count) - { - current->rel_filepos = reloc_base; - reloc_base += current->reloc_count * RELSZ; - } - else - { - current->rel_filepos = 0; - } + current->line_filepos = lineno_base; + current->moving_line_filepos = lineno_base; + lineno_base += current->lineno_count * LINESZ; + } + else + { + current->line_filepos = 0; + } + if (current->reloc_count) + { + current->rel_filepos = reloc_base; + reloc_base += current->reloc_count * RELSZ; + } + else + { + current->rel_filepos = 0; } } - - /* Write section headers to the file. */ internal_f.f_nscns = 0; if (bfd_seek (abfd, @@ -1652,90 +1650,81 @@ coff_write_object_contents (abfd) return false; { -#if 0 - unsigned int pad = abfd->flags & D_PAGED ? data_base : 0; -#endif - unsigned int pad = 0; - for (current = abfd->sections; current != NULL; current = current->next) { struct internal_scnhdr section; - if (current->target_index > 0) - { - internal_f.f_nscns++; - strncpy (&(section.s_name[0]), current->name, 8); + + internal_f.f_nscns++; + strncpy (&(section.s_name[0]), current->name, 8); #ifdef _LIB - /* Always set s_vaddr of .lib to 0. This is right for SVR3.2 + /* Always set s_vaddr of .lib to 0. This is right for SVR3.2 Ian Taylor <ian@cygnus.com>. */ - if (strcmp (current->name, _LIB) == 0) - section.s_vaddr = 0; - else -#endif - section.s_vaddr = current->lma + pad; - section.s_paddr = current->lma + pad; - section.s_size = current->_raw_size - pad; - /* - If this section has no size or is unloadable then the scnptr - will be 0 too - */ - if (current->_raw_size - pad == 0 || - (current->flags & (SEC_LOAD | SEC_HAS_CONTENTS)) == 0) - { - section.s_scnptr = 0; - } - else - { - section.s_scnptr = current->filepos; - } - section.s_relptr = current->rel_filepos; - section.s_lnnoptr = current->line_filepos; - section.s_nreloc = current->reloc_count; - section.s_nlnno = current->lineno_count; - if (current->reloc_count != 0) - hasrelocs = true; - if (current->lineno_count != 0) - haslinno = true; - - section.s_flags = sec_to_styp_flags (current->name, current->flags); - - if (!strcmp (current->name, _TEXT)) - { - text_sec = current; - } - else if (!strcmp (current->name, _DATA)) - { - data_sec = current; + if (strcmp (current->name, _LIB) == 0) + section.s_vaddr = 0; + else +#endif + section.s_vaddr = current->lma; + section.s_paddr = current->lma; + section.s_size = current->_raw_size; + /* + If this section has no size or is unloadable then the scnptr + will be 0 too + */ + if (current->_raw_size == 0 || + (current->flags & (SEC_LOAD | SEC_HAS_CONTENTS)) == 0) + { + section.s_scnptr = 0; + } + else + { + section.s_scnptr = current->filepos; + } + section.s_relptr = current->rel_filepos; + section.s_lnnoptr = current->line_filepos; + section.s_nreloc = current->reloc_count; + section.s_nlnno = current->lineno_count; + if (current->reloc_count != 0) + hasrelocs = true; + if (current->lineno_count != 0) + haslinno = true; + + section.s_flags = sec_to_styp_flags (current->name, current->flags); + + if (!strcmp (current->name, _TEXT)) + { + text_sec = current; + } + else if (!strcmp (current->name, _DATA)) + { + data_sec = current; #ifdef TWO_DATA_SECS - } - else if (!strcmp (current->name, ".data2")) - { - data_sec = current; + } + else if (!strcmp (current->name, ".data2")) + { + data_sec = current; #endif /* TWO_DATA_SECS */ - } - else if (!strcmp (current->name, _BSS)) - { - bss_sec = current; - } + } + else if (!strcmp (current->name, _BSS)) + { + bss_sec = current; + } #ifdef I960 - section.s_align = (current->alignment_power - ? 1 << current->alignment_power - : 0); + section.s_align = (current->alignment_power + ? 1 << current->alignment_power + : 0); #endif - { - SCNHDR buff; - - coff_swap_scnhdr_out (abfd, §ion, &buff); - if (bfd_write ((PTR) (&buff), 1, SCNHSZ, abfd) != SCNHSZ) - return false; + { + SCNHDR buff; - } + coff_swap_scnhdr_out (abfd, §ion, &buff); + if (bfd_write ((PTR) (&buff), 1, SCNHSZ, abfd) != SCNHSZ) + return false; - pad = 0; - } + } } } @@ -1752,7 +1741,7 @@ coff_write_object_contents (abfd) */ internal_f.f_timdat = 0; - if (bfd_get_symcount (abfd) != 0) + if (obj_raw_syment_count (abfd) != 0) internal_f.f_symptr = sym_base; else internal_f.f_symptr = 0; @@ -1768,7 +1757,7 @@ coff_write_object_contents (abfd) internal_f.f_flags |= F_RELFLG; if (!haslinno) internal_f.f_flags |= F_LNNO; - if (0 == bfd_get_symcount (abfd)) + if (obj_raw_syment_count (abfd) == 0) internal_f.f_flags |= F_LSYMS; if (abfd->flags & EXEC_P) internal_f.f_flags |= F_EXEC; @@ -1893,7 +1882,7 @@ coff_write_object_contents (abfd) } internal_a.entry = bfd_get_start_address (abfd); - internal_f.f_nsyms = bfd_get_symcount (abfd); + internal_f.f_nsyms = obj_raw_syment_count (abfd); /* now write them */ if (bfd_seek (abfd, (file_ptr) 0, SEEK_SET) != 0) @@ -2097,18 +2086,20 @@ coff_slurp_symbol_table (abfd) } /* on error */ /* Allocate enough room for all the symbols in cached form */ - cached_area = - (coff_symbol_type *) - bfd_alloc (abfd, (size_t) (bfd_get_symcount (abfd) * sizeof (coff_symbol_type))); + cached_area = ((coff_symbol_type *) + bfd_alloc (abfd, + (obj_raw_syment_count (abfd) + * sizeof (coff_symbol_type)))); if (cached_area == NULL) { bfd_set_error (bfd_error_no_memory); return false; } /* on error */ - table_ptr = - (unsigned int *) - bfd_alloc (abfd, (size_t) (bfd_get_symcount (abfd) * sizeof (unsigned int))); + table_ptr = ((unsigned int *) + bfd_alloc (abfd, + (obj_raw_syment_count (abfd) + * sizeof (unsigned int)))); if (table_ptr == NULL) { @@ -2118,7 +2109,7 @@ coff_slurp_symbol_table (abfd) else { coff_symbol_type *dst = cached_area; - unsigned int last_native_index = bfd_get_symcount (abfd); + unsigned int last_native_index = obj_raw_syment_count (abfd); unsigned int this_index = 0; while (this_index < last_native_index) { @@ -2324,7 +2315,6 @@ coff_slurp_symbol_table (abfd) obj_symbols (abfd) = cached_area; obj_raw_syments (abfd) = native_symbols; - obj_conv_table_size (abfd) = bfd_get_symcount (abfd); bfd_get_symcount (abfd) = number_of_symbols; obj_convert (abfd) = table_ptr; /* Slurp the line tables for each section too */ @@ -2340,6 +2330,43 @@ coff_slurp_symbol_table (abfd) return true; } /* coff_slurp_symbol_table() */ +/* Check whether a symbol is globally visible. This is used by the + COFF backend linker code in cofflink.c, since a couple of targets + have globally visible symbols which are not class C_EXT. This + function need not handle the case of n_class == C_EXT. */ + +#undef OTHER_GLOBAL_CLASS + +#ifdef I960 +#define OTHER_GLOBAL_CLASS C_LEAFEXT +#endif + +#ifdef RS6000COFF_C +#define OTHER_GLOBAL_CLASS C_HIDEXT +#endif + +#ifdef OTHER_GLOBAL_CLASS + +static boolean +coff_sym_is_global (abfd, syment) + bfd *abfd; + struct internal_syment *syment; +{ + if (syment->n_sclass == OTHER_GLOBAL_CLASS) + return true; + return false; +} + +#undef OTHER_GLOBAL_CLASS + +#else /* ! defined (OTHER_GLOBAL_CLASS) */ + +/* sym_is_global should not be defined if it has nothing to do. */ + +#define coff_sym_is_global 0 + +#endif /* ! defined (OTHER_GLOBAL_CLASS) */ + /* SUBSUBSECTION Reading relocations @@ -2433,7 +2460,7 @@ coff_slurp_reloc_table (abfd, asect, symbols) cache_ptr = reloc_cache + idx; src = native_relocs + idx; - bfd_swap_reloc_in (abfd, src, &dst); + coff_swap_reloc_in (abfd, src, &dst); RELOC_PROCESSING (cache_ptr, &dst, symbols, abfd, asect); #else @@ -2444,7 +2471,7 @@ coff_slurp_reloc_table (abfd, asect, symbols) cache_ptr = reloc_cache + idx; src = native_relocs + idx; - bfd_swap_reloc_in (abfd, src, &dst); + coff_swap_reloc_in (abfd, src, &dst); cache_ptr->address = dst.r_vaddr; @@ -2572,6 +2599,19 @@ dummy_reloc16_extra_cases (abfd, link_info, link_order, reloc, data, src_ptr, } #endif +/* If coff_relocate_section is defined, we can use the optimized COFF + backend linker. Otherwise we must continue to use the old linker. */ +#ifdef coff_relocate_section +#define coff_bfd_link_hash_table_create _bfd_coff_link_hash_table_create +#define coff_bfd_link_add_symbols _bfd_coff_link_add_symbols +#define coff_bfd_final_link _bfd_coff_final_link +#else /* ! defined (coff_relocate_section) */ +#define coff_relocate_section NULL +#define coff_bfd_link_hash_table_create _bfd_generic_link_hash_table_create +#define coff_bfd_link_add_symbols _bfd_generic_link_add_symbols +#define coff_bfd_final_link _bfd_generic_final_link +#endif /* ! defined (coff_relocate_section) */ + static CONST bfd_coff_backend_data bfd_coff_std_swap_table = { coff_swap_aux_in, coff_swap_sym_in, coff_swap_lineno_in, @@ -2579,17 +2619,19 @@ static CONST bfd_coff_backend_data bfd_coff_std_swap_table = coff_swap_lineno_out, coff_swap_reloc_out, coff_swap_filehdr_out, coff_swap_aouthdr_out, coff_swap_scnhdr_out, - FILHSZ, AOUTSZ, SCNHSZ, SYMESZ, AUXESZ, LINESZ, + FILHSZ, AOUTSZ, SCNHSZ, SYMESZ, AUXESZ, RELSZ, LINESZ, #ifdef COFF_LONG_FILENAMES true, #else false, #endif coff_swap_filehdr_in, coff_swap_aouthdr_in, coff_swap_scnhdr_in, - coff_bad_format_hook, coff_set_arch_mach_hook, coff_mkobject_hook, - styp_to_sec_flags, coff_make_section_hook, coff_set_alignment_hook, - coff_slurp_symbol_table, symname_in_debug_hook, - coff_reloc16_extra_cases, coff_reloc16_estimate + coff_swap_reloc_in, coff_bad_format_hook, coff_set_arch_mach_hook, + coff_mkobject_hook, styp_to_sec_flags, coff_make_section_hook, + coff_set_alignment_hook, coff_slurp_symbol_table, symname_in_debug_hook, + coff_reloc16_extra_cases, coff_reloc16_estimate, + coff_sym_is_global, coff_compute_section_file_positions, + coff_relocate_section }; #define coff_close_and_cleanup _bfd_generic_close_and_cleanup @@ -2613,6 +2655,3 @@ static CONST bfd_coff_backend_data bfd_coff_std_swap_table = #define coff_bfd_get_relocated_section_contents \ bfd_generic_get_relocated_section_contents #define coff_bfd_relax_section bfd_generic_relax_section -#define coff_bfd_link_hash_table_create _bfd_generic_link_hash_table_create -#define coff_bfd_link_add_symbols _bfd_generic_link_add_symbols -#define coff_bfd_final_link _bfd_generic_final_link diff --git a/bfd/coffgen.c b/bfd/coffgen.c index fe72cc3..8e91d1c 100644 --- a/bfd/coffgen.c +++ b/bfd/coffgen.c @@ -180,10 +180,7 @@ coff_real_object_p (abfd, nscns, internal_f, internal_a) if ((internal_f->f_flags & F_EXEC) != 0) abfd->flags |= D_PAGED; - obj_raw_syment_count (abfd) = - obj_conv_table_size (abfd) = - bfd_get_symcount(abfd) = - internal_f->f_nsyms; + bfd_get_symcount(abfd) = internal_f->f_nsyms; if (internal_f->f_nsyms) abfd->flags |= HAS_SYMS; @@ -343,42 +340,53 @@ coff_get_symtab (abfd, alocation) int coff_count_linenumbers (abfd) - bfd *abfd; + bfd *abfd; { - unsigned int limit = bfd_get_symcount(abfd); - unsigned int i; + unsigned int limit = bfd_get_symcount(abfd); + unsigned int i; int total = 0; - asymbol **p; - { - asection *s = abfd->sections->output_section; - while (s) { - BFD_ASSERT(s->lineno_count == 0); - s = s->next; - } - } + asymbol **p; + asection *s; + if (limit == 0) + { + /* This may be from the backend linker, in which case the + lineno_count in the sections is correct. */ + for (s = abfd->sections; s != NULL; s = s->next) + total += s->lineno_count; + return total; + } - for (p = abfd->outsymbols, i = 0; i < limit; i++, p++) { - asymbol *q_maybe = *p; - if (bfd_asymbol_flavour(q_maybe) == bfd_target_coff_flavour) { - coff_symbol_type *q = coffsymbol(q_maybe); - if (q->lineno) { - /* - This symbol has a linenumber, increment the owning - section's linenumber count - */ - alent *l = q->lineno; - q->symbol.section->output_section->lineno_count++; - total ++; - l++; - while (l->line_number) { - total ++; - q->symbol.section->output_section->lineno_count++; - l++; + for (s = abfd->sections; s != NULL; s = s->next) + BFD_ASSERT (s->lineno_count == 0); + + for (p = abfd->outsymbols, i = 0; i < limit; i++, p++) + { + asymbol *q_maybe = *p; + + if (bfd_asymbol_flavour (q_maybe) == bfd_target_coff_flavour) + { + coff_symbol_type *q = coffsymbol (q_maybe); + + if (q->lineno != NULL) + { + /* This symbol has line numbers. Increment the owning + section's linenumber count. */ + alent *l = q->lineno; + + ++q->symbol.section->output_section->lineno_count; + ++total; + ++l; + while (l->line_number != 0) + { + ++total; + ++q->symbol.section->output_section->lineno_count; + ++l; + } + } } - } } - } + return total; } @@ -920,8 +928,6 @@ coff_write_symbols (abfd) } } - bfd_get_symcount (abfd) = written; - /* Now write out strings */ if (string_size != 0) @@ -1137,9 +1143,12 @@ coff_pointerize_aux (abfd, table_base, type, class, auxent) /* Otherwise patch up */ #define N_TMASK coff_data (abfd)->local_n_tmask #define N_BTSHFT coff_data (abfd)->local_n_btshft - if (ISFCN(type) || ISTAG(class) || class == C_BLOCK) { - auxent->u.auxent.x_sym.x_fcnary.x_fcn.x_endndx.p = table_base + - auxent->u.auxent.x_sym.x_fcnary.x_fcn.x_endndx.l; + if ((ISFCN(type) || ISTAG(class) || class == C_BLOCK) + && auxent->u.auxent.x_sym.x_fcnary.x_fcn.x_endndx.l > 0) + { + auxent->u.auxent.x_sym.x_fcnary.x_fcn.x_endndx.p = + (table_base + + auxent->u.auxent.x_sym.x_fcnary.x_fcn.x_endndx.l); auxent->fix_end = 1; } /* A negative tagndx is meaningless, but the SCO 3.2v4 cc can @@ -1276,7 +1285,9 @@ coff_get_normalized_symtab (abfd) if (obj_raw_syments(abfd) != (combined_entry_type *)NULL) { return obj_raw_syments(abfd); } - if ((size = bfd_get_symcount(abfd) * sizeof(combined_entry_type)) == 0) { + size = obj_raw_syment_count (abfd) * sizeof (combined_entry_type); + if (size == 0) + { bfd_set_error (bfd_error_no_symbols); return (NULL); } @@ -1287,10 +1298,10 @@ coff_get_normalized_symtab (abfd) bfd_set_error (bfd_error_no_memory); return NULL; } - internal_end = internal + bfd_get_symcount(abfd); + internal_end = internal + obj_raw_syment_count (abfd); symesz = bfd_coff_symesz (abfd); - raw_size = bfd_get_symcount(abfd) * symesz; + raw_size = obj_raw_syment_count (abfd) * symesz; raw = bfd_alloc(abfd,raw_size); if (!raw) { @@ -1302,7 +1313,7 @@ coff_get_normalized_symtab (abfd) || bfd_read(raw, raw_size, 1, abfd) != raw_size) return (NULL); /* mark the end of the symbols */ - raw_end = (char *) raw + bfd_get_symcount(abfd) * symesz; + raw_end = (char *) raw + obj_raw_syment_count (abfd) * symesz; /* FIXME SOMEDAY. A string table size of zero is very weird, but probably possible. If one shows up, it will probably kill us. @@ -1575,6 +1586,11 @@ coff_print_symbol (abfd, filep, symbol, how) auxp->u.auxent.x_sym.x_misc.x_lnsz.x_lnno, auxp->u.auxent.x_sym.x_misc.x_lnsz.x_size, tagndx); + if (auxp->fix_end) + fprintf (file, " endndx %ld", + ((long) + (auxp->u.auxent.x_sym.x_fcnary.x_fcn.x_endndx.p + - root))); break; } } diff --git a/bfd/cofflink.c b/bfd/cofflink.c new file mode 100644 index 0000000..31f2597 --- /dev/null +++ b/bfd/cofflink.c @@ -0,0 +1,2006 @@ +/* COFF specific linker code. + Copyright 1994 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support. + +This file is part of BFD, the Binary File Descriptor library. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* This file contains the COFF backend linker code. */ + +#include "bfd.h" +#include "sysdep.h" +#include "bfdlink.h" +#include "libbfd.h" +#include "coff/internal.h" +#include "libcoff.h" + +#define STRING_SIZE_SIZE (4) + +/* Information we keep for each section in the output file when doing + a relocateable link. */ + +struct coff_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 coff_link_hash_entry **rel_hashes; +}; + +/* Information that we pass around while doing the final link step. */ + +struct coff_final_link_info +{ + /* General link information. */ + struct bfd_link_info *info; + /* Output BFD. */ + bfd *output_bfd; + /* Used to indicate failure in traversal routine. */ + boolean failed; + /* Hash table for long symbol name. */ + struct bfd_strtab_hash *strtab; + /* When doing a relocateable link, an array of information kept for + each output section, indexed by the target_index field. */ + struct coff_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; + /* Buffer large enough to hold swapped symbols of any input file. */ + struct internal_syment *internal_syms; + /* Buffer large enough to hold sections of symbols of any input file. */ + asection **sec_ptrs; + /* 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; + /* Buffer large enough to hold swapped relocs of any input section. */ + struct internal_reloc *internal_relocs; +}; + +static struct bfd_hash_entry *coff_link_hash_newfunc + PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *)); +static boolean coff_link_add_object_symbols + PARAMS ((bfd *, struct bfd_link_info *)); +static boolean coff_link_check_archive_element + PARAMS ((bfd *, struct bfd_link_info *, boolean *)); +static boolean coff_link_get_symbols PARAMS ((bfd *)); +static const char *coff_read_string_table PARAMS ((bfd *)); +static boolean coff_link_free_symbols PARAMS ((bfd *)); +static boolean coff_link_check_ar_symbols + PARAMS ((bfd *, struct bfd_link_info *, boolean *)); +static boolean coff_link_add_symbols PARAMS ((bfd *, struct bfd_link_info *)); +static boolean coff_link_input_bfd + PARAMS ((struct coff_final_link_info *, bfd *)); +static boolean coff_write_global_sym + PARAMS ((struct coff_link_hash_entry *, PTR)); +static boolean coff_reloc_link_order + PARAMS ((bfd *, struct coff_final_link_info *, asection *, + struct bfd_link_order *)); + +/* Create an entry in a COFF linker hash table. */ + +static struct bfd_hash_entry * +coff_link_hash_newfunc (entry, table, string) + struct bfd_hash_entry *entry; + struct bfd_hash_table *table; + const char *string; +{ + struct coff_link_hash_entry *ret = (struct coff_link_hash_entry *) entry; + + /* Allocate the structure if it has not already been allocated by a + subclass. */ + if (ret == (struct coff_link_hash_entry *) NULL) + ret = ((struct coff_link_hash_entry *) + bfd_hash_allocate (table, sizeof (struct coff_link_hash_entry))); + if (ret == (struct coff_link_hash_entry *) NULL) + { + bfd_set_error (bfd_error_no_memory); + return (struct bfd_hash_entry *) ret; + } + + /* Call the allocation method of the superclass. */ + ret = ((struct coff_link_hash_entry *) + _bfd_link_hash_newfunc ((struct bfd_hash_entry *) ret, + table, string)); + if (ret != (struct coff_link_hash_entry *) NULL) + { + /* Set local fields. */ + ret->indx = -1; + ret->type = T_NULL; + ret->class = C_NULL; + ret->numaux = 0; + ret->auxbfd = NULL; + ret->aux = NULL; + } + + return (struct bfd_hash_entry *) ret; +} + +/* Create a COFF linker hash table. */ + +struct bfd_link_hash_table * +_bfd_coff_link_hash_table_create (abfd) + bfd *abfd; +{ + struct coff_link_hash_table *ret; + + ret = ((struct coff_link_hash_table *) + malloc (sizeof (struct coff_link_hash_table))); + if (ret == NULL) + { + bfd_set_error (bfd_error_no_memory); + return NULL; + } + if (! _bfd_link_hash_table_init (&ret->root, abfd, + coff_link_hash_newfunc)) + { + free (ret); + return (struct bfd_link_hash_table *) NULL; + } + return &ret->root; +} + +/* Given a COFF BFD, add symbols to the global hash table as + appropriate. */ + +boolean +_bfd_coff_link_add_symbols (abfd, info) + bfd *abfd; + struct bfd_link_info *info; +{ + switch (bfd_get_format (abfd)) + { + case bfd_object: + return coff_link_add_object_symbols (abfd, info); + case bfd_archive: + return (_bfd_generic_link_add_archive_symbols + (abfd, info, coff_link_check_archive_element)); + default: + bfd_set_error (bfd_error_wrong_format); + return false; + } +} + +/* Add symbols from a COFF object file. */ + +static boolean +coff_link_add_object_symbols (abfd, info) + bfd *abfd; + struct bfd_link_info *info; +{ + if (! coff_link_get_symbols (abfd)) + return false; + if (! coff_link_add_symbols (abfd, info)) + return false; + if (! info->keep_memory) + { + if (! coff_link_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 +coff_link_check_archive_element (abfd, info, pneeded) + bfd *abfd; + struct bfd_link_info *info; + boolean *pneeded; +{ + if (! coff_link_get_symbols (abfd)) + return false; + + if (! coff_link_check_ar_symbols (abfd, info, pneeded)) + return false; + + if (*pneeded) + { + if (! coff_link_add_symbols (abfd, info)) + return false; + } + + if (! info->keep_memory || ! *pneeded) + { + if (! coff_link_free_symbols (abfd)) + return false; + } + + return true; +} + +/* Read in the external symbols. */ + +static boolean +coff_link_get_symbols (abfd) + bfd *abfd; +{ + bfd_size_type symesz; + size_t size; + PTR syms; + + if (obj_coff_external_syms (abfd) != NULL) + return true; + + symesz = bfd_coff_symesz (abfd); + + size = obj_raw_syment_count (abfd) * symesz; + + syms = malloc (size); + if (syms == NULL && size != 0) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + + if (bfd_seek (abfd, obj_sym_filepos (abfd), SEEK_SET) != 0 + || bfd_read (syms, size, 1, abfd) != size) + { + if (syms != NULL) + free (syms); + return false; + } + + obj_coff_external_syms (abfd) = syms; + + return true; +} + +/* Read in the external strings. The strings are not loaded until + they are needed. This is because we have no simple way of + detecting a missing string table in an archive. */ + +static const char * +coff_read_string_table (abfd) + bfd *abfd; +{ + char extstrsize[STRING_SIZE_SIZE]; + size_t strsize; + char *strings; + + if (obj_coff_strings (abfd) != NULL) + return obj_coff_strings (abfd); + + if (bfd_seek (abfd, + (obj_sym_filepos (abfd) + + obj_raw_syment_count (abfd) * bfd_coff_symesz (abfd)), + SEEK_SET) != 0) + return NULL; + + if (bfd_read (extstrsize, sizeof extstrsize, 1, abfd) != sizeof extstrsize) + { + if (bfd_get_error () != bfd_error_file_truncated) + return NULL; + + /* There is no string table. */ + strsize = STRING_SIZE_SIZE; + } + else + { +#if STRING_SIZE_SIZE == 4 + strsize = bfd_h_get_32 (abfd, extstrsize); +#else + #error Change bfd_h_get_32 +#endif + } + + strings = malloc (strsize); + if (strings == NULL) + { + bfd_set_error (bfd_error_no_memory); + return NULL; + } + + if (bfd_read (strings + STRING_SIZE_SIZE, + strsize - STRING_SIZE_SIZE, 1, abfd) + != strsize - STRING_SIZE_SIZE) + { + free (strings); + return NULL; + } + + obj_coff_strings (abfd) = strings; + + return strings; +} + +/* Free up the external symbols and strings read from a COFF file. */ + +static boolean +coff_link_free_symbols (abfd) + bfd *abfd; +{ + if (obj_coff_external_syms (abfd) != NULL) + { + free (obj_coff_external_syms (abfd)); + obj_coff_external_syms (abfd) = NULL; + } + if (obj_coff_strings (abfd) != NULL) + { + free (obj_coff_strings (abfd)); + obj_coff_strings (abfd) = NULL; + } + return true; +} + +/* Look through the symbols to see if this object file should be + included in the link. */ + +static boolean +coff_link_check_ar_symbols (abfd, info, pneeded) + bfd *abfd; + struct bfd_link_info *info; + boolean *pneeded; +{ + boolean (*sym_is_global) PARAMS ((bfd *, struct internal_syment *)); + const char *strings; + bfd_size_type symesz; + bfd_byte *esym; + bfd_byte *esym_end; + + *pneeded = false; + + sym_is_global = coff_backend_info (abfd)->_bfd_coff_sym_is_global; + strings = NULL; + + 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_is_global && (*sym_is_global) (abfd, &sym))) + && (sym.n_scnum != 0 || sym.n_value != 0)) + { + 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. */ + + /* FIXME: It's not clear this will work correctly if sizeof + (_n_zeroes) != 4. */ + if (sym._n._n_n._n_zeroes != 0 + || sym._n._n_n._n_offset == 0) + { + memcpy (buf, sym._n._n_name, SYMNMLEN); + buf[SYMNMLEN] = '\0'; + name = buf; + } + else + { + BFD_ASSERT (sym._n._n_n._n_offset >= STRING_SIZE_SIZE); + if (strings == NULL) + { + strings = coff_read_string_table (abfd); + if (strings == NULL) + return false; + } + name = strings + sym._n._n_n._n_offset; + } + + 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, + COFF linkers do not bring in an object file which defines + it. */ + if (h != (struct bfd_link_hash_entry *) NULL + && h->type == bfd_link_hash_undefined) + { + 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; +} + +/* Add all the symbols from an object file to the hash table. */ + +static boolean +coff_link_add_symbols (abfd, info) + bfd *abfd; + struct bfd_link_info *info; +{ + boolean (*sym_is_global) PARAMS ((bfd *, struct internal_syment *)); + const char *strings; + boolean default_copy; + bfd_size_type symcount; + struct coff_link_hash_entry **sym_hash; + bfd_size_type symesz; + bfd_byte *esym; + bfd_byte *esym_end; + + sym_is_global = coff_backend_info (abfd)->_bfd_coff_sym_is_global; + strings = NULL; + + 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 particular symbols. */ + sym_hash = ((struct coff_link_hash_entry **) + bfd_alloc (abfd, + ((size_t) symcount + * sizeof (struct coff_link_hash_entry *)))); + if (sym_hash == NULL && symcount != 0) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + obj_coff_sym_hashes (abfd) = sym_hash; + memset (sym_hash, 0, + (size_t) symcount * sizeof (struct coff_link_hash_entry *)); + + 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; + boolean copy; + + bfd_coff_swap_sym_in (abfd, (PTR) esym, (PTR) &sym); + + if (sym.n_sclass == C_EXT + || (sym_is_global && (*sym_is_global) (abfd, &sym))) + { + const char *name; + char buf[SYMNMLEN + 1]; + flagword flags; + asection *section; + bfd_vma value; + + /* This symbol is externally visible. */ + + /* FIXME: It's not clear this will work correctly if sizeof + (_n_zeroes) != 4. */ + copy = default_copy; + if (sym._n._n_n._n_zeroes == 0 + && sym._n._n_n._n_offset != 0) + { + BFD_ASSERT (sym._n._n_n._n_offset >= STRING_SIZE_SIZE); + if (strings == NULL) + { + strings = coff_read_string_table (abfd); + if (strings == NULL) + return false; + } + name = strings + sym._n._n_n._n_offset; + } + else + { + memcpy (buf, sym._n._n_name, SYMNMLEN); + buf[SYMNMLEN] = '\0'; + name = buf; + copy = true; + } + + value = sym.n_value; + + if (sym.n_scnum == 0) + { + if (value == 0) + { + flags = 0; + section = bfd_und_section_ptr; + } + else + { + flags = BSF_GLOBAL; + section = bfd_com_section_ptr; + } + } + else + { + flags = BSF_EXPORT | BSF_GLOBAL; + section = coff_section_from_bfd_index (abfd, sym.n_scnum); + value -= section->vma; + } + + if (! (_bfd_generic_link_add_one_symbol + (info, abfd, name, flags, section, value, + (const char *) NULL, copy, false, + (struct bfd_link_hash_entry **) sym_hash))) + return false; + + if (info->hash->creator->flavour == bfd_get_flavour (abfd)) + { + if (((*sym_hash)->class == C_NULL + && (*sym_hash)->type == T_NULL) + || sym.n_scnum != 0 + || (sym.n_value != 0 + && (*sym_hash)->root.type != bfd_link_hash_defined)) + { + (*sym_hash)->class = sym.n_sclass; + (*sym_hash)->type = sym.n_type; + (*sym_hash)->numaux = sym.n_numaux; + (*sym_hash)->auxbfd = abfd; + if (sym.n_numaux != 0) + { + union internal_auxent *alloc; + unsigned int i; + bfd_byte *eaux; + union internal_auxent *iaux; + + alloc = bfd_hash_allocate (&info->hash->table, + (sym.n_numaux + * sizeof (*alloc))); + if (alloc == NULL) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + for (i = 0, eaux = esym + symesz, iaux = alloc; + i < sym.n_numaux; + i++, eaux += symesz, iaux++) + bfd_coff_swap_aux_in (abfd, (PTR) eaux, sym.n_type, + sym.n_sclass, i, sym.n_numaux, + (PTR) iaux); + (*sym_hash)->aux = alloc; + } + } + } + } + + esym += (sym.n_numaux + 1) * symesz; + sym_hash += sym.n_numaux + 1; + } + + return true; +} + +/* Do the final link step. */ + +boolean +_bfd_coff_final_link (abfd, info) + bfd *abfd; + struct bfd_link_info *info; +{ + bfd_size_type symesz; + struct coff_final_link_info finfo; + asection *o; + struct bfd_link_order *p; + size_t max_contents_size; + size_t max_sym_count; + size_t max_lineno_count; + size_t max_reloc_count; + size_t max_output_reloc_count; + file_ptr rel_filepos; + unsigned int relsz; + file_ptr line_filepos; + unsigned int linesz; + bfd *sub; + bfd_byte *external_relocs = NULL; + char strbuf[STRING_SIZE_SIZE]; + + symesz = bfd_coff_symesz (abfd); + + finfo.info = info; + finfo.output_bfd = abfd; + finfo.strtab = NULL; + finfo.section_info = NULL; + finfo.last_file_index = -1; + finfo.internal_syms = NULL; + finfo.sec_ptrs = NULL; + finfo.sym_indices = NULL; + finfo.outsyms = NULL; + finfo.linenos = NULL; + finfo.contents = NULL; + finfo.external_relocs = NULL; + finfo.internal_relocs = NULL; + + finfo.strtab = _bfd_stringtab_init (); + if (finfo.strtab == NULL) + goto error_return; + + /* Compute the file positions for all the sections. */ + if (! abfd->output_has_begun) + bfd_coff_compute_section_file_positions (abfd); + + /* Count the line numbers and relocation entries required for the + output file. Set the file positions for the relocs. */ + rel_filepos = obj_relocbase (abfd); + relsz = bfd_coff_relsz (abfd); + max_contents_size = 0; + max_lineno_count = 0; + max_reloc_count = 0; + for (o = abfd->sections; o != NULL; o = o->next) + { + o->reloc_count = 0; + o->lineno_count = 0; + for (p = o->link_order_head; p != NULL; p = p->next) + { + if (p->type == bfd_indirect_link_order) + { + asection *sec; + + sec = p->u.indirect.section; + + if (info->strip == strip_none + || info->strip == strip_some) + o->lineno_count += sec->lineno_count; + + if (info->relocateable) + o->reloc_count += sec->reloc_count; + + if (sec->_raw_size > max_contents_size) + max_contents_size = sec->_raw_size; + if (sec->lineno_count > max_lineno_count) + max_lineno_count = sec->lineno_count; + if (sec->reloc_count > max_reloc_count) + max_reloc_count = sec->reloc_count; + } + else if (info->relocateable + && (p->type == bfd_section_reloc_link_order + || p->type == bfd_symbol_reloc_link_order)) + ++o->reloc_count; + } + if (o->reloc_count == 0) + o->rel_filepos = 0; + else + { + o->flags |= SEC_RELOC; + o->rel_filepos = rel_filepos; + rel_filepos += o->reloc_count * relsz; + } + } + + /* If doing a relocateable link, allocate space for the pointers we + need to keep. */ + if (info->relocateable) + { + unsigned int i; + + finfo.section_info = ((struct coff_link_section_info *) + malloc (abfd->section_count + * sizeof (struct coff_link_section_info))); + if (finfo.section_info == NULL) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } + for (i = 0; i < abfd->section_count; i++) + { + finfo.section_info[i].relocs = NULL; + finfo.section_info[i].rel_hashes = NULL; + } + } + + /* We now know the size of the relocs, so we can determine the file + positions of the line numbers. */ + line_filepos = rel_filepos; + linesz = bfd_coff_linesz (abfd); + max_output_reloc_count = 0; + for (o = abfd->sections; o != NULL; o = o->next) + { + if (o->lineno_count == 0) + o->line_filepos = 0; + else + { + o->line_filepos = line_filepos; + line_filepos += o->lineno_count * linesz; + } + + if (o->reloc_count != 0) + { + /* We don't know the indices of global symbols until we have + written out all the local symbols. For each section in + the output file, we keep an array of pointers to hash + table entries. Each entry in the array corresponds to a + reloc. When we find a reloc against a global symbol, we + set the corresponding entry in this array so that we can + fix up the symbol index after we have written out all the + local symbols. + + Because of this problem, we also keep the relocs in + memory until the end of the link. This wastes memory, + but only when doing a relocateable link, which is not the + common case. */ + BFD_ASSERT (info->relocateable); + finfo.section_info[o->target_index].relocs = + ((struct internal_reloc *) + malloc (o->reloc_count * sizeof (struct internal_reloc))); + finfo.section_info[o->target_index].rel_hashes = + ((struct coff_link_hash_entry **) + malloc (o->reloc_count + * sizeof (struct coff_link_hash_entry *))); + if (finfo.section_info[o->target_index].relocs == NULL + || finfo.section_info[o->target_index].rel_hashes == NULL) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } + + if (o->reloc_count > max_output_reloc_count) + max_output_reloc_count = o->reloc_count; + } + + /* Reset the reloc and lineno counts, so that we can use them to + count the number of entries we have output so far. */ + o->reloc_count = 0; + o->lineno_count = 0; + } + + obj_sym_filepos (abfd) = line_filepos; + + /* Figure out the largest number of symbols in an input BFD. Take + the opportunity to clear the output_has_begun fields of all the + input BFD's. */ + max_sym_count = 0; + for (sub = info->input_bfds; sub != NULL; sub = sub->link_next) + { + size_t sz; + + sub->output_has_begun = false; + sz = obj_raw_syment_count (sub); + if (sz > max_sym_count) + max_sym_count = sz; + } + + /* Allocate some buffers used while linking. */ + finfo.internal_syms = ((struct internal_syment *) + malloc (max_sym_count + * sizeof (struct internal_syment))); + finfo.sec_ptrs = (asection **) malloc (max_sym_count * sizeof (asection *)); + finfo.sym_indices = (long *) malloc (max_sym_count * sizeof (long)); + finfo.outsyms = (bfd_byte *) malloc ((max_sym_count + 1) * symesz); + finfo.linenos = (bfd_byte *) malloc (max_lineno_count + * bfd_coff_linesz (abfd)); + finfo.contents = (bfd_byte *) malloc (max_contents_size); + finfo.external_relocs = (bfd_byte *) malloc (max_reloc_count * relsz); + if (! info->relocateable) + finfo.internal_relocs = ((struct internal_reloc *) + malloc (max_reloc_count + * sizeof (struct internal_reloc))); + if ((finfo.internal_syms == NULL && max_sym_count > 0) + || (finfo.sec_ptrs == NULL && max_sym_count > 0) + || (finfo.sym_indices == NULL && max_sym_count > 0) + || finfo.outsyms == NULL + || (finfo.linenos == NULL && max_lineno_count > 0) + || (finfo.contents == NULL && max_contents_size > 0) + || (finfo.external_relocs == NULL && max_reloc_count > 0) + || (! info->relocateable + && finfo.internal_relocs == NULL + && max_reloc_count > 0)) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } + + /* We now know the position of everything in the file, except that + we don't know the size of the symbol table and therefore we don't + know where the string table starts. We just build the string + table in memory as we go along. We process all the relocations + for a single input file at once. */ + obj_raw_syment_count (abfd) = 0; + for (o = abfd->sections; o != NULL; o = o->next) + { + for (p = o->link_order_head; p != NULL; p = p->next) + { + if (p->type == bfd_indirect_link_order + && (bfd_get_flavour (p->u.indirect.section->owner) + == bfd_target_coff_flavour)) + { + sub = p->u.indirect.section->owner; + if (! sub->output_has_begun) + { + if (! coff_link_input_bfd (&finfo, sub)) + goto error_return; + sub->output_has_begun = true; + } + } + else if (p->type == bfd_section_reloc_link_order + || p->type == bfd_symbol_reloc_link_order) + { + if (! coff_reloc_link_order (abfd, &finfo, o, p)) + goto error_return; + } + else + { + if (! _bfd_default_link_order (abfd, info, o, p)) + goto error_return; + } + } + } + + /* Free up the buffers used by coff_link_input_bfd. */ + if (finfo.internal_syms != NULL) + { + free (finfo.internal_syms); + finfo.internal_syms = NULL; + } + if (finfo.sec_ptrs != NULL) + { + free (finfo.sec_ptrs); + finfo.sec_ptrs = NULL; + } + if (finfo.sym_indices != NULL) + { + free (finfo.sym_indices); + finfo.sym_indices = NULL; + } + if (finfo.linenos != NULL) + { + free (finfo.linenos); + finfo.linenos = NULL; + } + if (finfo.contents != NULL) + { + free (finfo.contents); + finfo.contents = NULL; + } + if (finfo.external_relocs != NULL) + { + free (finfo.external_relocs); + finfo.external_relocs = NULL; + } + if (finfo.internal_relocs != NULL) + { + free (finfo.internal_relocs); + finfo.internal_relocs = NULL; + } + + /* The value of the last C_FILE symbol is supposed to be the symbol + index of the first external symbol. Write it out again if + necessary. */ + if (finfo.last_file_index != -1 + && finfo.last_file.n_value != obj_raw_syment_count (abfd)) + { + finfo.last_file.n_value = obj_raw_syment_count (abfd); + bfd_coff_swap_sym_out (abfd, (PTR) &finfo.last_file, + (PTR) finfo.outsyms); + if (bfd_seek (abfd, + (obj_sym_filepos (abfd) + + finfo.last_file_index * symesz), + SEEK_SET) != 0 + || bfd_write (finfo.outsyms, symesz, 1, abfd) != symesz) + return false; + } + + /* Write out the global symbols. */ + finfo.failed = false; + coff_link_hash_traverse (coff_hash_table (info), coff_write_global_sym, + (PTR) &finfo); + if (finfo.failed) + goto error_return; + + /* The outsyms buffer is used by coff_write_global_sym. */ + if (finfo.outsyms != NULL) + { + free (finfo.outsyms); + finfo.outsyms = NULL; + } + + if (info->relocateable) + { + /* Now that we have written out all the global symbols, we know + the symbol indices to use for relocs against them, and we can + finally write out the relocs. */ + external_relocs = (bfd_byte *) malloc (max_output_reloc_count * relsz); + if (external_relocs == NULL) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } + + for (o = abfd->sections; o != NULL; o = o->next) + { + struct internal_reloc *irel; + struct internal_reloc *irelend; + struct coff_link_hash_entry **rel_hash; + bfd_byte *erel; + + if (o->reloc_count == 0) + continue; + + irel = finfo.section_info[o->target_index].relocs; + irelend = irel + o->reloc_count; + rel_hash = finfo.section_info[o->target_index].rel_hashes; + erel = external_relocs; + for (; irel < irelend; irel++, rel_hash++, erel += relsz) + { + if (*rel_hash != NULL) + { + BFD_ASSERT ((*rel_hash)->indx >= 0); + irel->r_symndx = (*rel_hash)->indx; + } + bfd_coff_swap_reloc_out (abfd, (PTR) irel, (PTR) erel); + } + + if (bfd_seek (abfd, o->rel_filepos, SEEK_SET) != 0 + || bfd_write ((PTR) external_relocs, relsz, o->reloc_count, + abfd) != relsz * o->reloc_count) + goto error_return; + } + + free (external_relocs); + external_relocs = NULL; + } + + /* Free up the section information. */ + if (finfo.section_info != NULL) + { + unsigned int i; + + for (i = 0; i < abfd->section_count; i++) + { + if (finfo.section_info[i].relocs != NULL) + free (finfo.section_info[i].relocs); + if (finfo.section_info[i].rel_hashes != NULL) + free (finfo.section_info[i].rel_hashes); + } + free (finfo.section_info); + finfo.section_info = NULL; + } + + /* Write out the string table. */ + if (bfd_seek (abfd, + (obj_sym_filepos (abfd) + + obj_raw_syment_count (abfd) * symesz), + SEEK_SET) != 0) + return false; + +#if STRING_SIZE_SIZE == 4 + bfd_h_put_32 (abfd, + _bfd_stringtab_size (finfo.strtab) + STRING_SIZE_SIZE, + strbuf); +#else + #error Change bfd_h_put_32 +#endif + + if (bfd_write (strbuf, 1, STRING_SIZE_SIZE, abfd) != STRING_SIZE_SIZE) + return false; + + if (! _bfd_stringtab_emit (abfd, finfo.strtab)) + return false; + + _bfd_stringtab_free (finfo.strtab); + + /* Setting bfd_get_symcount to 0 will cause write_object_contents to + not try to write out the symbols. */ + bfd_get_symcount (abfd) = 0; + + return true; + + error_return: + if (finfo.strtab != NULL) + _bfd_stringtab_free (finfo.strtab); + if (finfo.section_info != NULL) + { + unsigned int i; + + for (i = 0; i < abfd->section_count; i++) + { + if (finfo.section_info[i].relocs != NULL) + free (finfo.section_info[i].relocs); + if (finfo.section_info[i].rel_hashes != NULL) + free (finfo.section_info[i].rel_hashes); + } + free (finfo.section_info); + } + if (finfo.internal_syms != NULL) + free (finfo.internal_syms); + if (finfo.sec_ptrs != NULL) + free (finfo.sec_ptrs); + if (finfo.sym_indices != NULL) + free (finfo.sym_indices); + if (finfo.outsyms != NULL) + free (finfo.outsyms); + if (finfo.linenos != NULL) + free (finfo.linenos); + if (finfo.contents != NULL) + free (finfo.contents); + if (finfo.external_relocs != NULL) + free (finfo.external_relocs); + if (finfo.internal_relocs != NULL) + free (finfo.internal_relocs); + if (external_relocs != NULL) + free (external_relocs); + return false; +} + +/* Link an input file into the linker output file. This function + handles all the sections and relocations of the input file at once. */ + +static boolean +coff_link_input_bfd (finfo, input_bfd) + struct coff_final_link_info *finfo; + bfd *input_bfd; +{ + boolean (*sym_is_global) PARAMS ((bfd *, struct internal_syment *)); + bfd *output_bfd; + const char *strings; + bfd_size_type syment_base; + unsigned int n_tmask; + unsigned int n_btshft; + boolean copy, hash; + bfd_size_type isymesz; + bfd_size_type osymesz; + bfd_size_type linesz; + bfd_byte *esym; + bfd_byte *esym_end; + struct internal_syment *isymp; + asection **secpp; + long *indexp; + long output_index; + bfd_byte *outsym; + struct coff_link_hash_entry **sym_hash; + bfd_size_type relsz; + asection *o; + + /* Move all the symbols to the output file. */ + + output_bfd = finfo->output_bfd; + sym_is_global = coff_backend_info (input_bfd)->_bfd_coff_sym_is_global; + strings = NULL; + syment_base = obj_raw_syment_count (output_bfd); + isymesz = bfd_coff_symesz (input_bfd); + osymesz = bfd_coff_symesz (output_bfd); + linesz = bfd_coff_linesz (input_bfd); + BFD_ASSERT (linesz == bfd_coff_linesz (output_bfd)); + + n_tmask = coff_data (input_bfd)->local_n_tmask; + n_btshft = coff_data (input_bfd)->local_n_btshft; + + /* Define macros so that ISFCN, et. al., macros work correctly. */ +#define N_TMASK n_tmask +#define N_BTSHFT n_btshft + + copy = false; + if (! finfo->info->keep_memory) + copy = true; + hash = true; + if ((output_bfd->flags & BFD_TRADITIONAL_FORMAT) != 0) + hash = false; + + if (! coff_link_get_symbols (input_bfd)) + return false; + + esym = (bfd_byte *) obj_coff_external_syms (input_bfd); + esym_end = esym + obj_raw_syment_count (input_bfd) * isymesz; + isymp = finfo->internal_syms; + secpp = finfo->sec_ptrs; + indexp = finfo->sym_indices; + output_index = syment_base; + outsym = finfo->outsyms; + while (esym < esym_end) + { + struct internal_syment isym; + boolean skip; + boolean global; + int add; + + bfd_coff_swap_sym_in (input_bfd, (PTR) esym, (PTR) isymp); + + /* Make a copy of *isymp so that the relocate_section function + always sees the original values. This is more reliable than + always recomputing the symbol value even if we are stripping + the symbol. */ + isym = *isymp; + + *secpp = coff_section_from_bfd_index (input_bfd, isym.n_scnum); + *indexp = -1; + + skip = false; + global = false; + add = 1 + isym.n_numaux; + + /* If we are stripping all symbols, we want to skip this one. */ + if (finfo->info->strip == strip_all) + skip = true; + + if (! skip) + { + if (isym.n_sclass == C_EXT + || (sym_is_global && (*sym_is_global) (input_bfd, &isym))) + { + /* This is a global symbol. Global symbols come at the + end of the symbol table, so skip them for now. + Function symbols, however, are an exception, and are + not moved to the end. */ + global = true; + if (! ISFCN (isym.n_type)) + skip = true; + } + else + { + /* This is a local symbol. Skip it if we are discarding + local symbols. */ + if (finfo->info->discard == discard_all) + skip = true; + } + } + + /* If we stripping debugging symbols, and this is a debugging + symbol, then skip it. */ + if (! skip + && finfo->info->strip == strip_debugger + && isym.n_scnum == N_DEBUG) + skip = true; + + /* If some symbols are stripped based on the name, work out the + name and decide whether to skip this symbol. */ + if (! skip + && (finfo->info->strip == strip_some + || finfo->info->discard == discard_l)) + { + const char *name; + char buf[SYMNMLEN + 1]; + + if (isym._n._n_n._n_zeroes == 0 + && isym._n._n_n._n_offset != 0) + { + if (strings == NULL) + { + strings = coff_read_string_table (input_bfd); + if (strings == NULL) + return false; + } + name = strings + isym._n._n_n._n_offset; + } + else + { + memcpy (buf, isym._n._n_name, SYMNMLEN); + buf[SYMNMLEN] = '\0'; + name = buf; + } + + if ((finfo->info->strip == strip_some + && (bfd_hash_lookup (finfo->info->keep_hash, name, false, + false) == NULL)) + || (! global + && finfo->info->discard == discard_l + && strncmp (name, finfo->info->lprefix, + finfo->info->lprefix_len) == 0)) + skip = true; + } + + /* We now know whether we are to skip this symbol or not. */ + if (! skip) + { + /* Adjust the symbol in order to output it. */ + + if (isym._n._n_n._n_zeroes == 0 + && isym._n._n_n._n_offset != 0) + { + const char *name; + bfd_size_type indx; + + /* This symbol has a long name. Enter it in the string + table we are building. Note that we do not check + bfd_coff_symname_in_debug. That is only true for + XCOFF, and XCOFF requires different linking code + anyhow. */ + BFD_ASSERT (isym._n._n_n._n_offset >= STRING_SIZE_SIZE); + if (strings == NULL) + { + strings = coff_read_string_table (input_bfd); + if (strings == NULL) + return false; + } + name = strings + isym._n._n_n._n_offset; + indx = _bfd_stringtab_add (finfo->strtab, name, hash, copy); + if (indx == (bfd_size_type) -1) + return false; + isym._n._n_n._n_offset = STRING_SIZE_SIZE + indx; + } + + if (isym.n_scnum > 0) + { + isym.n_scnum = (*secpp)->output_section->target_index; + isym.n_value += ((*secpp)->output_section->vma + + (*secpp)->output_offset + - (*secpp)->vma); + } + + /* The value of a C_FILE symbol is the symbol index of the + next C_FILE symbol. The value of the last C_FILE symbol + is the symbol index to the first external symbol + (actually, coff_renumber_symbols does not get this + right--it just sets the value of the last C_FILE symbol + to zero--and nobody has ever complained about it). We + try to get this right, below, just before we write the + symbols out, but in the general case we may have to write + the symbol out twice. */ + if (isym.n_sclass == C_FILE) + { + if (finfo->last_file_index != -1 + && finfo->last_file.n_value != output_index) + { + /* We must correct the value of the last C_FILE entry. */ + finfo->last_file.n_value = output_index; + if (finfo->last_file_index >= syment_base) + { + /* The last C_FILE symbol is in this input file. */ + bfd_coff_swap_sym_out (output_bfd, + (PTR) &finfo->last_file, + (PTR) (finfo->outsyms + + ((finfo->last_file_index + - syment_base) + * osymesz))); + } + else + { + /* We have already written out the last C_FILE + symbol. We need to write it out again. We + borrow *outsym temporarily. */ + bfd_coff_swap_sym_out (output_bfd, + (PTR) &finfo->last_file, + (PTR) outsym); + if (bfd_seek (output_bfd, + (obj_sym_filepos (output_bfd) + + finfo->last_file_index * osymesz), + SEEK_SET) != 0 + || (bfd_write (outsym, osymesz, 1, output_bfd) + != osymesz)) + return false; + } + } + + finfo->last_file_index = output_index; + finfo->last_file = isym; + } + + /* Output the symbol. */ + + bfd_coff_swap_sym_out (output_bfd, (PTR) &isym, (PTR) outsym); + + *indexp = output_index; + + if (global) + { + long indx; + struct coff_link_hash_entry *h; + + indx = ((esym - (bfd_byte *) obj_coff_external_syms (input_bfd)) + / isymesz); + h = obj_coff_sym_hashes (input_bfd)[indx]; + BFD_ASSERT (h != NULL); + h->indx = output_index; + } + + output_index += add; + outsym += add * osymesz; + } + + esym += add * isymesz; + isymp += add; + ++secpp; + ++indexp; + for (--add; add > 0; --add) + { + *secpp++ = NULL; + *indexp++ = -1; + } + } + + /* Fix up the aux entries. This must be done in a separate pass, + because we don't know the correct symbol indices until we have + already decided which symbols we are going to keep. */ + + esym = (bfd_byte *) obj_coff_external_syms (input_bfd); + esym_end = esym + obj_raw_syment_count (input_bfd) * isymesz; + isymp = finfo->internal_syms; + indexp = finfo->sym_indices; + sym_hash = obj_coff_sym_hashes (input_bfd); + outsym = finfo->outsyms; + while (esym < esym_end) + { + int add; + + add = 1 + isymp->n_numaux; + + if (*indexp < 0 + && (*sym_hash == NULL + || (*sym_hash)->auxbfd != input_bfd)) + esym += add * isymesz; + else + { + struct coff_link_hash_entry *h; + int i; + + h = NULL; + if (*indexp < 0) + { + h = *sym_hash; + BFD_ASSERT (h->numaux == isymp->n_numaux); + } + + esym += isymesz; + + if (h == NULL) + outsym += osymesz; + + /* Handle the aux entries. This handling is based on + coff_pointerize_aux. I don't know if it always correct. */ + for (i = 0; i < isymp->n_numaux && esym < esym_end; i++) + { + union internal_auxent aux; + union internal_auxent *auxp; + + if (h != NULL) + auxp = h->aux + i; + else + { + bfd_coff_swap_aux_in (input_bfd, (PTR) esym, isymp->n_type, + isymp->n_sclass, i, isymp->n_numaux, + (PTR) &aux); + auxp = &aux; + } + + if (isymp->n_sclass == C_FILE) + { + /* If this is a long filename, we must put it in the + string table. */ + if (auxp->x_file.x_n.x_zeroes == 0) + { + const char *filename; + bfd_size_type indx; + + BFD_ASSERT (auxp->x_file.x_n.x_offset + >= STRING_SIZE_SIZE); + if (strings == NULL) + { + strings = coff_read_string_table (input_bfd); + if (strings == NULL) + return false; + } + filename = strings + auxp->x_file.x_n.x_offset; + indx = _bfd_stringtab_add (finfo->strtab, filename, + hash, copy); + if (indx == (bfd_size_type) -1) + return false; + auxp->x_file.x_n.x_offset = STRING_SIZE_SIZE + indx; + } + } + else if (isymp->n_sclass != C_STAT || isymp->n_type != T_NULL) + { + long indx; + + if (ISFCN (isymp->n_type) + || ISTAG (isymp->n_sclass) + || isymp->n_sclass == C_BLOCK) + { + indx = auxp->x_sym.x_fcnary.x_fcn.x_endndx.l; + if (indx > 0 + && indx < obj_raw_syment_count (input_bfd)) + { + /* We look forward through the symbol for + the index of the next symbol we are going + to include. I don't know if this is + entirely right. */ + while (finfo->sym_indices[indx] < 0 + && indx < obj_raw_syment_count (input_bfd)) + ++indx; + if (indx >= obj_raw_syment_count (input_bfd)) + indx = output_index; + else + indx = finfo->sym_indices[indx]; + auxp->x_sym.x_fcnary.x_fcn.x_endndx.l = indx; + } + } + + indx = auxp->x_sym.x_tagndx.l; + if (indx > 0 && indx < obj_raw_syment_count (input_bfd)) + { + indx = finfo->sym_indices[indx]; + if (indx < 0) + auxp->x_sym.x_tagndx.l = 0; + else + auxp->x_sym.x_tagndx.l = indx; + } + } + + if (h == NULL) + { + bfd_coff_swap_aux_out (output_bfd, (PTR) auxp, isymp->n_type, + isymp->n_sclass, i, isymp->n_numaux, + (PTR) outsym); + outsym += osymesz; + } + + esym += isymesz; + } + } + + indexp += add; + isymp += add; + sym_hash += add; + } + + /* Relocate the line numbers, unless we are stripping them. */ + if (finfo->info->strip == strip_none + || finfo->info->strip == strip_some) + { + for (o = input_bfd->sections; o != NULL; o = o->next) + { + bfd_vma offset; + bfd_byte *eline; + bfd_byte *elineend; + + if (o->lineno_count == 0) + continue; + + if (bfd_seek (input_bfd, o->line_filepos, SEEK_SET) != 0 + || bfd_read (finfo->linenos, linesz, o->lineno_count, + input_bfd) != linesz * o->lineno_count) + return false; + + offset = o->output_section->vma + o->output_offset - o->vma; + eline = finfo->linenos; + elineend = eline + linesz * o->lineno_count; + for (; eline < elineend; eline += linesz) + { + struct internal_lineno iline; + + bfd_coff_swap_lineno_in (input_bfd, (PTR) eline, (PTR) &iline); + + if (iline.l_lnno != 0) + iline.l_addr.l_paddr += offset; + else if (iline.l_addr.l_symndx >= 0 + && (iline.l_addr.l_symndx + < obj_raw_syment_count (input_bfd))) + { + long indx; + + indx = finfo->sym_indices[iline.l_addr.l_symndx]; + + if (indx < 0) + { + /* These line numbers are attached to a symbol + which we are stripping. We should really + just discard the line numbers, but that would + be a pain because we have already counted + them. */ + indx = 0; + } + else + { + struct internal_syment is; + union internal_auxent ia; + + /* Fix up the lnnoptr field in the aux entry of + the symbol. It turns out that we can't do + this when we modify the symbol aux entries, + because gas sometimes screws up the lnnoptr + field and makes it an offset from the start + of the line numbers rather than an absolute + file index. */ + bfd_coff_swap_sym_in (output_bfd, + (PTR) (finfo->outsyms + + ((indx - syment_base) + * osymesz)), + (PTR) &is); + if ((ISFCN (is.n_type) + || is.n_sclass == C_BLOCK) + && is.n_numaux >= 1) + { + PTR auxptr; + + auxptr = (PTR) (finfo->outsyms + + ((indx - syment_base + 1) + * osymesz)); + bfd_coff_swap_aux_in (output_bfd, auxptr, + is.n_type, is.n_sclass, + 0, is.n_numaux, (PTR) &ia); + ia.x_sym.x_fcnary.x_fcn.x_lnnoptr = + (o->output_section->line_filepos + + o->output_section->lineno_count * linesz + + eline - finfo->linenos); + bfd_coff_swap_aux_out (output_bfd, (PTR) &ia, + is.n_type, is.n_sclass, 0, + is.n_numaux, auxptr); + } + } + + iline.l_addr.l_symndx = indx; + } + + bfd_coff_swap_lineno_out (output_bfd, (PTR) &iline, (PTR) eline); + } + + if (bfd_seek (output_bfd, + (o->output_section->line_filepos + + o->output_section->lineno_count * linesz), + SEEK_SET) != 0 + || bfd_write (finfo->linenos, linesz, o->lineno_count, + output_bfd) != linesz * o->lineno_count) + return false; + + o->output_section->lineno_count += o->lineno_count; + } + } + + /* If we swapped out a C_FILE symbol, guess that the next C_FILE + symbol will be the first symbol in the next input file. In the + normal case, this will save us from writing out the C_FILE symbol + again. */ + if (finfo->last_file_index >= syment_base) + { + finfo->last_file.n_value = output_index; + bfd_coff_swap_sym_out (output_bfd, (PTR) &finfo->last_file, + (PTR) (finfo->outsyms + + ((finfo->last_file_index - syment_base) + * osymesz))); + } + + /* Write the modified symbols to the output file. */ + if (outsym > finfo->outsyms) + { + if (bfd_seek (output_bfd, + obj_sym_filepos (output_bfd) + syment_base * osymesz, + SEEK_SET) != 0 + || bfd_write (finfo->outsyms, outsym - finfo->outsyms, 1, + output_bfd) != outsym - finfo->outsyms) + return false; + + BFD_ASSERT ((obj_raw_syment_count (output_bfd) + + (outsym - finfo->outsyms) / osymesz) + == output_index); + + obj_raw_syment_count (output_bfd) = output_index; + } + + /* Relocate the contents of each section. */ + relsz = bfd_coff_relsz (input_bfd); + for (o = input_bfd->sections; o != NULL; o = o->next) + { + if ((o->flags & SEC_HAS_CONTENTS) == 0) + continue; + + if (! bfd_get_section_contents (input_bfd, o, finfo->contents, + (file_ptr) 0, o->_raw_size)) + return false; + + if ((o->flags & SEC_RELOC) != 0) + { + int target_index; + struct internal_reloc *internal_relocs; + bfd_byte *erel; + bfd_byte *erel_end; + struct internal_reloc *irel; + + /* Read in the relocs. */ + if (bfd_seek (input_bfd, o->rel_filepos, SEEK_SET) != 0 + || (bfd_read (finfo->external_relocs, relsz, o->reloc_count, + input_bfd) != relsz * o->reloc_count)) + return false; + + /* If we are doing a relocateable link, we keep the swapped + in relocs in memory, and don't write them out until the + end of the link. */ + target_index = o->output_section->target_index; + if (! finfo->info->relocateable) + internal_relocs = finfo->internal_relocs; + else + internal_relocs = (finfo->section_info[target_index].relocs + + o->output_section->reloc_count); + + /* Swap in the relocs. */ + erel = finfo->external_relocs; + erel_end = erel + relsz * o->reloc_count; + irel = internal_relocs; + for (; erel < erel_end; erel += relsz, irel++) + bfd_coff_swap_reloc_in (input_bfd, (PTR) erel, (PTR) irel); + + /* Call processor specific code to relocate the section + contents. */ + if (! bfd_coff_relocate_section (output_bfd, finfo->info, + input_bfd, o, + finfo->contents, + internal_relocs, + finfo->internal_syms, + finfo->sec_ptrs)) + return false; + + if (finfo->info->relocateable) + { + bfd_vma offset; + struct internal_reloc *irelend; + struct coff_link_hash_entry **rel_hash; + + offset = o->output_section->vma + o->output_offset - o->vma; + + irel = internal_relocs; + irelend = irel + o->reloc_count; + rel_hash = finfo->section_info[target_index].rel_hashes; + for (; irel < irelend; irel++, rel_hash++) + { + struct coff_link_hash_entry *h; + + *rel_hash = NULL; + + /* Adjust the reloc address and symbol index. */ + + irel->r_vaddr += offset; + + h = obj_coff_sym_hashes (input_bfd)[irel->r_symndx]; + if (h != NULL) + { + /* This is a global symbol. */ + if (h->indx >= 0) + irel->r_symndx = h->indx; + else + { + /* This symbol is being written at the end + of the file, and we do not yet know the + symbol index. We save the pointer to the + hash table entry in the rel_hash list. + We set the indx field to -2 to indicate + that this symbol must not be stripped. */ + *rel_hash = h; + h->indx = -2; + } + } + else + { + long indx; + + indx = finfo->sym_indices[irel->r_symndx]; + if (indx != -1) + irel->r_symndx = indx; + else + { + struct internal_syment *is; + const char *name; + char buf[SYMNMLEN + 1]; + + /* This reloc is against a symbol we are + stripping. It would be possible to + handle this case, but I don't think it's + worth it. */ + is = finfo->internal_syms + irel->r_symndx; + + if (is->_n._n_n._n_zeroes == 0 + && is->_n._n_n._n_offset != 0) + { + if (strings == NULL) + { + strings = coff_read_string_table (input_bfd); + if (strings == NULL) + return false; + } + name = strings + is->_n._n_n._n_offset; + } + else + { + memcpy (buf, is->_n._n_name, SYMNMLEN); + buf[SYMNMLEN] = '\0'; + name = buf; + } + + if (! ((*finfo->info->callbacks->unattached_reloc) + (finfo->info, name, input_bfd, o, + irel->r_vaddr))) + return false; + } + } + } + + o->output_section->reloc_count += o->reloc_count; + } + } + + /* Write out the modified section contents. */ + if (! bfd_set_section_contents (output_bfd, o->output_section, + finfo->contents, o->output_offset, + (o->_cooked_size != 0 + ? o->_cooked_size + : o->_raw_size))) + return false; + } + + if (! finfo->info->keep_memory) + { + if (! coff_link_free_symbols (input_bfd)) + return false; + } + + return true; +} + +/* Write out a global symbol. Called via coff_link_hash_traverse. */ + +static boolean +coff_write_global_sym (h, data) + struct coff_link_hash_entry *h; + PTR data; +{ + struct coff_final_link_info *finfo = (struct coff_final_link_info *) data; + bfd *output_bfd; + struct internal_syment isym; + bfd_size_type symesz; + unsigned int i; + + output_bfd = finfo->output_bfd; + + if (h->indx >= 0) + return true; + + if (h->indx != -2 + && (finfo->info->strip == strip_all + || (finfo->info->strip == strip_some + && (bfd_hash_lookup (finfo->info->keep_hash, + h->root.root.string, false, false) + == NULL)))) + return true; + + switch (h->root.type) + { + default: + case bfd_link_hash_new: + abort (); + return false; + + case bfd_link_hash_undefined: + case bfd_link_hash_weak: + isym.n_scnum = N_UNDEF; + isym.n_value = 0; + break; + + case bfd_link_hash_defined: + { + asection *sec; + + sec = h->root.u.def.section->output_section; + if (bfd_is_abs_section (sec)) + isym.n_scnum = N_ABS; + else + isym.n_scnum = sec->target_index; + isym.n_value = (h->root.u.def.value + + sec->vma + + h->root.u.def.section->output_offset); + } + break; + + case bfd_link_hash_common: + isym.n_scnum = N_UNDEF; + isym.n_value = h->root.u.c.size; + break; + + case bfd_link_hash_indirect: + case bfd_link_hash_warning: + /* Just ignore these. They can't be handled anyhow. */ + return true; + } + + if (strlen (h->root.root.string) <= SYMNMLEN) + strncpy (isym._n._n_name, h->root.root.string, SYMNMLEN); + else + { + boolean hash; + bfd_size_type indx; + + hash = true; + if ((output_bfd->flags & BFD_TRADITIONAL_FORMAT) != 0) + hash = false; + indx = _bfd_stringtab_add (finfo->strtab, h->root.root.string, hash, + false); + if (indx == (bfd_size_type) -1) + { + finfo->failed = true; + return false; + } + isym._n._n_n._n_zeroes = 0; + isym._n._n_n._n_offset = STRING_SIZE_SIZE + indx; + } + + isym.n_sclass = h->class; + isym.n_type = h->type; + + if (isym.n_sclass == C_NULL) + isym.n_sclass = C_EXT; + + isym.n_numaux = h->numaux; + + bfd_coff_swap_sym_out (output_bfd, (PTR) &isym, (PTR) finfo->outsyms); + + symesz = bfd_coff_symesz (output_bfd); + + if (bfd_seek (output_bfd, + (obj_sym_filepos (output_bfd) + + obj_raw_syment_count (output_bfd) * symesz), + SEEK_SET) != 0 + || bfd_write (finfo->outsyms, symesz, 1, output_bfd) != symesz) + { + finfo->failed = true; + return false; + } + + h->indx = obj_raw_syment_count (output_bfd); + + ++obj_raw_syment_count (output_bfd); + + /* Write out any associated aux entries. There normally will be + none. If there are any, I have no idea how to modify them. */ + for (i = 0; i < isym.n_numaux; i++) + { + bfd_coff_swap_aux_out (output_bfd, (PTR) (h->aux + i), isym.n_type, + isym.n_sclass, i, isym.n_numaux, + (PTR) finfo->outsyms); + if (bfd_write (finfo->outsyms, symesz, 1, output_bfd) != symesz) + { + finfo->failed = true; + return false; + } + ++obj_raw_syment_count (output_bfd); + } + + return true; +} + +/* Handle a link order which is supposed to generate a reloc. */ + +static boolean +coff_reloc_link_order (output_bfd, finfo, output_section, link_order) + bfd *output_bfd; + struct coff_final_link_info *finfo; + asection *output_section; + struct bfd_link_order *link_order; +{ + const reloc_howto_type *howto; + struct internal_reloc *irel; + struct coff_link_hash_entry **rel_hash_ptr; + + howto = bfd_reloc_type_lookup (output_bfd, link_order->u.reloc.p->reloc); + if (howto == NULL) + { + bfd_set_error (bfd_error_bad_value); + return false; + } + + if (link_order->u.reloc.p->addend != 0) + { + bfd_size_type size; + bfd_byte *buf; + bfd_reloc_status_type rstat; + boolean ok; + + size = bfd_get_reloc_size (howto); + buf = (bfd_byte *) bfd_zmalloc (size); + if (buf == NULL) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + + rstat = _bfd_relocate_contents (howto, output_bfd, + link_order->u.reloc.p->addend, buf); + switch (rstat) + { + case bfd_reloc_ok: + break; + default: + case bfd_reloc_outofrange: + abort (); + case bfd_reloc_overflow: + if (! ((*finfo->info->callbacks->reloc_overflow) + (finfo->info, + (link_order->type == bfd_section_reloc_link_order + ? bfd_section_name (output_bfd, + link_order->u.reloc.p->u.section) + : link_order->u.reloc.p->u.name), + howto->name, link_order->u.reloc.p->addend, + (bfd *) NULL, (asection *) NULL, (bfd_vma) 0))) + { + free (buf); + return false; + } + break; + } + ok = bfd_set_section_contents (output_bfd, output_section, (PTR) buf, + (file_ptr) link_order->offset, size); + free (buf); + if (! ok) + return false; + } + + /* Store the reloc information in the right place. It will get + swapped and written out at the end of the final_link routine. */ + + irel = (finfo->section_info[output_section->target_index].relocs + + output_section->reloc_count); + rel_hash_ptr = (finfo->section_info[output_section->target_index].rel_hashes + + output_section->reloc_count); + + memset (irel, 0, sizeof (struct internal_reloc)); + *rel_hash_ptr = NULL; + + irel->r_vaddr = output_section->vma + link_order->offset; + + if (link_order->type == bfd_section_reloc_link_order) + { + /* We need to somehow locate a symbol in the right section. The + symbol must either have a value of zero, or we must adjust + the addend by the value of the symbol. FIXME: Write this + when we need it. The old linker couldn't handle this anyhow. */ + abort (); + *rel_hash_ptr = NULL; + irel->r_symndx = 0; + } + else + { + struct coff_link_hash_entry *h; + + h = coff_link_hash_lookup (coff_hash_table (finfo->info), + link_order->u.reloc.p->u.name, + false, false, true); + if (h != NULL) + { + if (h->indx >= 0) + irel->r_symndx = h->indx; + else + { + /* Set the index to -2 to force this symbol to get + written out. */ + h->indx = -2; + *rel_hash_ptr = h; + irel->r_symndx = 0; + } + } + else + { + if (! ((*finfo->info->callbacks->unattached_reloc) + (finfo->info, link_order->u.reloc.p->u.name, (bfd *) NULL, + (asection *) NULL, (bfd_vma) 0))) + return false; + irel->r_symndx = 0; + } + } + + /* FIXME: Is this always right? */ + irel->r_type = howto->type; + + /* r_size is only used on the RS/6000, which needs its own linker + routines anyhow. r_extern is only used for ECOFF. */ + + /* FIXME: What is the right value for r_offset? Is zero OK? */ + + ++output_section->reloc_count; + + return true; +} diff --git a/bfd/configure.in b/bfd/configure.in index 4418f6a..e8c50de 100644 --- a/bfd/configure.in +++ b/bfd/configure.in @@ -135,7 +135,7 @@ do case "$vec" in # This list is alphabetized to make it easy to compare # with the two vector lists in targets.c. - a29kcoff_big_vec) tb="$tb coff-a29k.o" ;; + a29kcoff_big_vec) tb="$tb coff-a29k.o cofflink.o" ;; a_out_adobe_vec) tb="$tb aout-adobe.o aout32.o stab-syms.o" ;; aout0_big_vec) tb="$tb aout0.o aout32.o stab-syms.o" ;; aout_mips_big_vec) tb="$tb mipsbsd.o aout32.o stab-syms.o" ;; diff --git a/bfd/libcoff-in.h b/bfd/libcoff-in.h index 277080e..4b14921 100644 --- a/bfd/libcoff-in.h +++ b/bfd/libcoff-in.h @@ -18,6 +18,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "bfdlink.h" /* Object file tdata; access macros */ @@ -31,12 +32,10 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #define obj_raw_syment_count(bfd) (coff_data(bfd)->raw_syment_count) #define obj_convert(bfd) (coff_data(bfd)->conversion_table) #define obj_conv_table_size(bfd) (coff_data(bfd)->conv_table_size) -#if CFILE_STUFF -#define obj_symbol_slew(bfd) (coff_data(bfd)->symbol_index_slew) -#else -#define obj_symbol_slew(bfd) 0 -#endif +#define obj_coff_external_syms(bfd) (coff_data (bfd)->external_syms) +#define obj_coff_strings(bfd) (coff_data (bfd)->strings) +#define obj_coff_sym_hashes(bfd) (coff_data (bfd)->sym_hashes) /* `Tdata' information kept for COFF files. */ @@ -47,13 +46,8 @@ typedef struct coff_tdata int conv_table_size; file_ptr sym_filepos; - long symbol_index_slew; /* used during read to mark whether a - C_FILE symbol as been added. */ - struct coff_ptr_struct *raw_syments; - struct lineno *raw_linenos; unsigned int raw_syment_count; - unsigned short flags; /* These are only valid once writing has begun */ long int relocbase; @@ -68,14 +62,71 @@ typedef struct coff_tdata unsigned local_symesz; unsigned local_auxesz; unsigned local_linesz; + + /* Used by the COFF backend linker. */ + PTR external_syms; + char *strings; + struct coff_link_hash_entry **sym_hashes; } coff_data_type; /* We take the address of the first element of a asymbol to ensure that the * macro is only ever applied to an asymbol. */ #define coffsymbol(asymbol) ((coff_symbol_type *)(&((asymbol)->the_bfd))) +/* COFF linker hash table entries. */ + +struct coff_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; + + /* Symbol type. */ + unsigned short type; + + /* Symbol class. */ + unsigned char class; + + /* Number of auxiliary entries. */ + char numaux; + + /* BFD to take auxiliary entries from. */ + bfd *auxbfd; + + /* Pointer to array of auxiliary entries, if any. */ + union internal_auxent *aux; +}; + +/* COFF linker hash table. */ + +struct coff_link_hash_table +{ + struct bfd_link_hash_table root; +}; + +/* Look up an entry in a COFF linker hash table. */ + +#define coff_link_hash_lookup(table, string, create, copy, follow) \ + ((struct coff_link_hash_entry *) \ + bfd_link_hash_lookup (&(table)->root, (string), (create), \ + (copy), (follow))) + +/* Traverse a COFF linker hash table. */ + +#define coff_link_hash_traverse(table, func, info) \ + (bfd_link_hash_traverse \ + (&(table)->root, \ + (boolean (*) PARAMS ((struct bfd_link_hash_entry *, PTR))) (func), \ + (info))) + +/* Get the COFF linker hash table from a link_info structure. */ + +#define coff_hash_table(p) ((struct coff_link_hash_table *) ((p)->hash)) + /* Functions in coffgen.c. */ -extern bfd_target *coff_object_p PARAMS ((bfd *)); +extern const bfd_target *coff_object_p PARAMS ((bfd *)); extern struct sec *coff_section_from_bfd_index PARAMS ((bfd *, int)); extern long coff_get_symtab_upper_bound PARAMS ((bfd *)); extern long coff_get_symtab PARAMS ((bfd *, asymbol **)); @@ -83,7 +134,7 @@ extern int coff_count_linenumbers PARAMS ((bfd *)); extern struct coff_symbol_struct *coff_symbol_from PARAMS ((bfd *, asymbol *)); extern boolean coff_renumber_symbols PARAMS ((bfd *)); extern void coff_mangle_symbols PARAMS ((bfd *)); -extern void coff_write_symbols PARAMS ((bfd *)); +extern boolean coff_write_symbols PARAMS ((bfd *)); extern boolean coff_write_linenumbers PARAMS ((bfd *)); extern alent *coff_get_lineno PARAMS ((bfd *, asymbol *)); extern asymbol *coff_section_symbol PARAMS ((bfd *, char *)); @@ -116,5 +167,14 @@ extern void bfd_perform_slip PARAMS ((bfd *abfd, unsigned int slip, asection *input_section, bfd_vma val)); +/* Functions in cofflink.c. */ + +extern struct bfd_link_hash_table *_bfd_coff_link_hash_table_create + PARAMS ((bfd *)); +extern boolean _bfd_coff_link_add_symbols + PARAMS ((bfd *, struct bfd_link_info *)); +extern boolean _bfd_coff_final_link + PARAMS ((bfd *, struct bfd_link_info *)); + /* And more taken from the source .. */ diff --git a/bfd/libcoff.h b/bfd/libcoff.h index 0ffb49f..99a2347 100644 --- a/bfd/libcoff.h +++ b/bfd/libcoff.h @@ -18,6 +18,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "bfdlink.h" /* Object file tdata; access macros */ @@ -31,12 +32,10 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #define obj_raw_syment_count(bfd) (coff_data(bfd)->raw_syment_count) #define obj_convert(bfd) (coff_data(bfd)->conversion_table) #define obj_conv_table_size(bfd) (coff_data(bfd)->conv_table_size) -#if CFILE_STUFF -#define obj_symbol_slew(bfd) (coff_data(bfd)->symbol_index_slew) -#else -#define obj_symbol_slew(bfd) 0 -#endif +#define obj_coff_external_syms(bfd) (coff_data (bfd)->external_syms) +#define obj_coff_strings(bfd) (coff_data (bfd)->strings) +#define obj_coff_sym_hashes(bfd) (coff_data (bfd)->sym_hashes) /* `Tdata' information kept for COFF files. */ @@ -47,13 +46,8 @@ typedef struct coff_tdata int conv_table_size; file_ptr sym_filepos; - long symbol_index_slew; /* used during read to mark whether a - C_FILE symbol as been added. */ - struct coff_ptr_struct *raw_syments; - struct lineno *raw_linenos; unsigned int raw_syment_count; - unsigned short flags; /* These are only valid once writing has begun */ long int relocbase; @@ -68,12 +62,69 @@ typedef struct coff_tdata unsigned local_symesz; unsigned local_auxesz; unsigned local_linesz; + + /* Used by the COFF backend linker. */ + PTR external_syms; + char *strings; + struct coff_link_hash_entry **sym_hashes; } coff_data_type; /* We take the address of the first element of a asymbol to ensure that the * macro is only ever applied to an asymbol. */ #define coffsymbol(asymbol) ((coff_symbol_type *)(&((asymbol)->the_bfd))) +/* COFF linker hash table entries. */ + +struct coff_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; + + /* Symbol type. */ + unsigned short type; + + /* Symbol class. */ + unsigned char class; + + /* Number of auxiliary entries. */ + char numaux; + + /* BFD to take auxiliary entries from. */ + bfd *auxbfd; + + /* Pointer to array of auxiliary entries, if any. */ + union internal_auxent *aux; +}; + +/* COFF linker hash table. */ + +struct coff_link_hash_table +{ + struct bfd_link_hash_table root; +}; + +/* Look up an entry in a COFF linker hash table. */ + +#define coff_link_hash_lookup(table, string, create, copy, follow) \ + ((struct coff_link_hash_entry *) \ + bfd_link_hash_lookup (&(table)->root, (string), (create), \ + (copy), (follow))) + +/* Traverse a COFF linker hash table. */ + +#define coff_link_hash_traverse(table, func, info) \ + (bfd_link_hash_traverse \ + (&(table)->root, \ + (boolean (*) PARAMS ((struct bfd_link_hash_entry *, PTR))) (func), \ + (info))) + +/* Get the COFF linker hash table from a link_info structure. */ + +#define coff_hash_table(p) ((struct coff_link_hash_table *) ((p)->hash)) + /* Functions in coffgen.c. */ extern const bfd_target *coff_object_p PARAMS ((bfd *)); extern struct sec *coff_section_from_bfd_index PARAMS ((bfd *, int)); @@ -116,6 +167,15 @@ extern void bfd_perform_slip PARAMS ((bfd *abfd, unsigned int slip, asection *input_section, bfd_vma val)); +/* Functions in cofflink.c. */ + +extern struct bfd_link_hash_table *_bfd_coff_link_hash_table_create + PARAMS ((bfd *)); +extern boolean _bfd_coff_link_add_symbols + PARAMS ((bfd *, struct bfd_link_info *)); +extern boolean _bfd_coff_final_link + PARAMS ((bfd *, struct bfd_link_info *)); + /* And more taken from the source .. */ typedef struct coff_ptr_struct @@ -232,6 +292,7 @@ typedef struct unsigned int _bfd_scnhsz; unsigned int _bfd_symesz; unsigned int _bfd_auxesz; + unsigned int _bfd_relsz; unsigned int _bfd_linesz; boolean _bfd_coff_long_filenames; void (*_bfd_coff_swap_filehdr_in) PARAMS (( @@ -246,6 +307,10 @@ typedef struct bfd *abfd, PTR ext, PTR in)); + void (*_bfd_coff_swap_reloc_in) PARAMS (( + bfd *abfd, + PTR ext, + PTR in)); boolean (*_bfd_coff_bad_format_hook) PARAMS (( bfd *abfd, PTR internal_filehdr)); @@ -286,6 +351,20 @@ typedef struct arelent *r, unsigned int shrink, struct bfd_link_info *link_info)); + boolean (*_bfd_coff_sym_is_global) PARAMS (( + bfd *abfd, + struct internal_syment *)); + void (*_bfd_coff_compute_section_file_positions) PARAMS (( + bfd *abfd)); + boolean (*_bfd_coff_relocate_section) PARAMS (( + bfd *output_bfd, + struct bfd_link_info *info, + bfd *input_bfd, + asection *input_section, + bfd_byte *contents, + struct internal_reloc *relocs, + struct internal_syment *syms, + asection **sections)); } bfd_coff_backend_data; @@ -326,6 +405,7 @@ typedef struct #define bfd_coff_scnhsz(abfd) (coff_backend_info (abfd)->_bfd_scnhsz) #define bfd_coff_symesz(abfd) (coff_backend_info (abfd)->_bfd_symesz) #define bfd_coff_auxesz(abfd) (coff_backend_info (abfd)->_bfd_auxesz) +#define bfd_coff_relsz(abfd) (coff_backend_info (abfd)->_bfd_relsz) #define bfd_coff_linesz(abfd) (coff_backend_info (abfd)->_bfd_linesz) #define bfd_coff_long_filenames(abfd) (coff_backend_info (abfd)->_bfd_coff_long_filenames) #define bfd_coff_swap_filehdr_in(abfd, i,o) \ @@ -337,6 +417,9 @@ typedef struct #define bfd_coff_swap_scnhdr_in(abfd, i,o) \ ((coff_backend_info (abfd)->_bfd_coff_swap_scnhdr_in) (abfd, i, o)) +#define bfd_coff_swap_reloc_in(abfd, i, o) \ + ((coff_backend_info (abfd)->_bfd_coff_swap_reloc_in) (abfd, i, o)) + #define bfd_coff_bad_format_hook(abfd, filehdr) \ ((coff_backend_info (abfd)->_bfd_coff_bad_format_hook) (abfd, filehdr)) @@ -368,3 +451,15 @@ typedef struct ((coff_backend_info (abfd)->_bfd_coff_reloc16_estimate)\ (abfd, section, reloc, shrink, link_info)) +#define bfd_coff_sym_is_global(abfd, sym)\ + ((coff_backend_info (abfd)->_bfd_coff_sym_is_global)\ + (abfd, sym)) + +#define bfd_coff_compute_section_file_positions(abfd)\ + ((coff_backend_info (abfd)->_bfd_coff_compute_section_file_positions)\ + (abfd)) + +#define bfd_coff_relocate_section(obfd,info,ibfd,o,con,rel,isyms,secs)\ + ((coff_backend_info (ibfd)->_bfd_coff_relocate_section)\ + (obfd, info, ibfd, o, con, rel, isyms, secs)) + |