diff options
Diffstat (limited to 'bfd/aoutx.h')
-rw-r--r-- | bfd/aoutx.h | 2118 |
1 files changed, 1926 insertions, 192 deletions
diff --git a/bfd/aoutx.h b/bfd/aoutx.h index 1b096b2..57eeaa6 100644 --- a/bfd/aoutx.h +++ b/bfd/aoutx.h @@ -93,17 +93,18 @@ DESCRIPTION | HOST_TEXT_START_ADDR | HOST_STACK_END_ADDR - in the file <<../include/sys/h-XXX.h>> (for your host). These - values, plus the structures and macros defined in <<a.out.h>> on + in the file @file{../include/sys/h-@var{XXX}.h} (for your host). These + values, plus the structures and macros defined in @file{a.out.h} on your host system, will produce a BFD target that will access ordinary a.out files on your host. To configure a new machine - to use <<host-aout.c>., specify: + to use @file{host-aout.c}, specify: | TDEFAULTS = -DDEFAULT_VECTOR=host_aout_big_vec | TDEPFILES= host-aout.o trad-core.o - in the <<config/mt-XXX>> file, and modify @file{configure.in} to use the - <<mt-XXX>> file (by setting "<<bfd_target=XXX>>") when your + in the @file{config/@var{XXX}.mt} file, and modify @file{configure.in} + to use the + @file{@var{XXX}.mt} file (by setting "<<bfd_target=XXX>>") when your configuration is selected. */ @@ -125,6 +126,7 @@ DESCRIPTION #include "bfd.h" #include <sysdep.h> #include <ansidecl.h> +#include "bfdlink.h" struct external_exec; #include "libaout.h" @@ -133,11 +135,9 @@ struct external_exec; #include "aout/stab_gnu.h" #include "aout/ar.h" -extern void (*bfd_error_trap)(); - /* SUBSECTION - relocations + Relocations DESCRIPTION The file @file{aoutx.h} provides for both the @emph{standard} @@ -156,7 +156,7 @@ DESCRIPTION reloc_howto_type howto_table_ext[] = { - /* type rs size bsz pcrel bitpos ovrf sf name part_inpl readmask setmask pcdone */ + /* type rs size bsz pcrel bitpos ovrf sf name part_inpl readmask setmask pcdone */ HOWTO(RELOC_8, 0, 0, 8, false, 0, complain_overflow_bitfield,0,"8", false, 0,0x000000ff, false), HOWTO(RELOC_16, 0, 1, 16, false, 0, complain_overflow_bitfield,0,"16", false, 0,0x0000ffff, false), HOWTO(RELOC_32, 0, 2, 32, false, 0, complain_overflow_bitfield,0,"32", false, 0,0xffffffff, false), @@ -186,7 +186,7 @@ reloc_howto_type howto_table_ext[] = /* Convert standard reloc records to "arelent" format (incl byte swap). */ reloc_howto_type howto_table_std[] = { - /* type rs size bsz pcrel bitpos ovrf sf name part_inpl readmask setmask pcdone */ + /* type rs size bsz pcrel bitpos ovrf sf name part_inpl readmask setmask pcdone */ HOWTO( 0, 0, 0, 8, false, 0, complain_overflow_bitfield,0,"8", true, 0x000000ff,0x000000ff, false), HOWTO( 1, 0, 1, 16, false, 0, complain_overflow_bitfield,0,"16", true, 0x0000ffff,0x0000ffff, false), HOWTO( 2, 0, 2, 32, false, 0, complain_overflow_bitfield,0,"32", true, 0xffffffff,0xffffffff, false), @@ -242,11 +242,9 @@ DEFUN(NAME(aout,reloc_type_lookup),(abfd,code), } } -extern bfd_error_vector_type bfd_error_vector; - /* SUBSECTION - Internal Entry Points + Internal entry points DESCRIPTION @file{aoutx.h} exports several routines for accessing the @@ -681,29 +679,199 @@ DEFUN(NAME(aout,set_arch_mach),(abfd, arch, machine), return (*aout_backend_info(abfd)->set_sizes) (abfd); } +static void +adjust_o_magic (abfd, execp) + bfd *abfd; + struct internal_exec *execp; +{ + file_ptr pos = adata (abfd).exec_bytes_size; + bfd_vma vma = 0; + int pad = 0; + + /* Text. */ + obj_textsec(abfd)->filepos = pos; + pos += obj_textsec(abfd)->_raw_size; + vma += obj_textsec(abfd)->_raw_size; + + /* Data. */ + if (!obj_datasec(abfd)->user_set_vma) + { +#if 0 /* ?? Does alignment in the file image really matter? */ + pad = align_power (vma, obj_datasec(abfd)->alignment_power) - vma; +#endif + obj_textsec(abfd)->_raw_size += pad; + pos += pad; + vma += pad; + obj_datasec(abfd)->vma = vma; + } + obj_datasec(abfd)->filepos = pos; + pos += obj_datasec(abfd)->_raw_size; + vma += obj_datasec(abfd)->_raw_size; + + /* BSS. */ + if (!obj_bsssec(abfd)->user_set_vma) + { +#if 0 + pad = align_power (vma, obj_bsssec(abfd)->alignment_power) - vma; +#endif + obj_datasec(abfd)->_raw_size += pad; + pos += pad; + vma += pad; + obj_bsssec(abfd)->vma = vma; + } + obj_bsssec(abfd)->filepos = pos; + + /* Fix up the exec header. */ + execp->a_text = obj_textsec(abfd)->_raw_size; + execp->a_data = obj_datasec(abfd)->_raw_size; + execp->a_bss = obj_bsssec(abfd)->_raw_size; + N_SET_MAGIC (*execp, OMAGIC); +} + +static void +adjust_z_magic (abfd, execp) + bfd *abfd; + struct internal_exec *execp; +{ + bfd_size_type data_pad, text_pad; + file_ptr text_end; + CONST struct aout_backend_data *abdp; + int ztih; /* Nonzero if text includes exec header. */ + bfd_vma data_vma; + + abdp = aout_backend_info (abfd); + + /* Text. */ + ztih = abdp && abdp->text_includes_header; + obj_textsec(abfd)->filepos = (ztih + ? adata(abfd).exec_bytes_size + : adata(abfd).page_size); + if (! obj_textsec(abfd)->user_set_vma) + /* ?? Do we really need to check for relocs here? */ + obj_textsec(abfd)->vma = ((abfd->flags & HAS_RELOC) + ? 0 + : (ztih + ? (abdp->default_text_vma + + adata(abfd).exec_bytes_size) + : abdp->default_text_vma)); + /* Could take strange alignment of text section into account here? */ + + /* Find start of data. */ + text_end = obj_textsec(abfd)->filepos + obj_textsec(abfd)->_raw_size; + text_pad = BFD_ALIGN (text_end, adata(abfd).page_size) - text_end; + obj_textsec(abfd)->_raw_size += text_pad; + text_end += text_pad; + + /* Data. */ + if (!obj_datasec(abfd)->user_set_vma) + { + bfd_vma vma; + vma = obj_textsec(abfd)->vma + obj_textsec(abfd)->_raw_size; + obj_datasec(abfd)->vma = BFD_ALIGN (vma, adata(abfd).segment_size); + } + data_vma = obj_datasec(abfd)->vma; + if (abdp && abdp->zmagic_mapped_contiguous) + { + text_pad = (obj_datasec(abfd)->vma + - obj_textsec(abfd)->vma + - obj_textsec(abfd)->_raw_size); + obj_textsec(abfd)->_raw_size += text_pad; + } + obj_datasec(abfd)->filepos = (obj_textsec(abfd)->filepos + + obj_textsec(abfd)->_raw_size); + + /* Fix up exec header while we're at it. */ + execp->a_text = obj_textsec(abfd)->_raw_size; + if (ztih && (!abdp || (abdp && !abdp->exec_header_not_counted))) + execp->a_text += adata(abfd).exec_bytes_size; + N_SET_MAGIC (*execp, ZMAGIC); + /* Spec says data section should be rounded up to page boundary. */ + /* If extra space in page is left after data section, fudge data + in the header so that the bss section looks smaller by that + amount. We'll start the bss section there, and lie to the OS. */ + obj_datasec(abfd)->_raw_size + = align_power (obj_datasec(abfd)->_raw_size, + obj_bsssec(abfd)->alignment_power); + execp->a_data = BFD_ALIGN (obj_datasec(abfd)->_raw_size, + adata(abfd).page_size); + data_pad = execp->a_data - obj_datasec(abfd)->_raw_size; + + /* BSS. */ + if (!obj_bsssec(abfd)->user_set_vma) + obj_bsssec(abfd)->vma = (obj_datasec(abfd)->vma + + obj_datasec(abfd)->_raw_size); + execp->a_bss = (data_pad > obj_bsssec(abfd)->_raw_size) ? 0 : + obj_bsssec(abfd)->_raw_size - data_pad; +} + +static void +adjust_n_magic (abfd, execp) + bfd *abfd; + struct internal_exec *execp; +{ + file_ptr pos = adata(abfd).exec_bytes_size; + bfd_vma vma = 0; + int pad; + + /* Text. */ + obj_textsec(abfd)->filepos = pos; + if (!obj_textsec(abfd)->user_set_vma) + obj_textsec(abfd)->vma = vma; + else + vma = obj_textsec(abfd)->vma; + pos += obj_textsec(abfd)->_raw_size; + vma += obj_textsec(abfd)->_raw_size; + + /* Data. */ + obj_datasec(abfd)->filepos = pos; + if (!obj_datasec(abfd)->user_set_vma) + obj_datasec(abfd)->vma = BFD_ALIGN (vma, adata(abfd).segment_size); + vma = obj_datasec(abfd)->vma; + + /* Since BSS follows data immediately, see if it needs alignment. */ + vma += obj_datasec(abfd)->_raw_size; + pad = align_power (vma, obj_bsssec(abfd)->alignment_power) - vma; + obj_datasec(abfd)->_raw_size += pad; + pos += obj_datasec(abfd)->_raw_size; + + /* BSS. */ + if (!obj_bsssec(abfd)->user_set_vma) + obj_bsssec(abfd)->vma = vma; + else + vma = obj_bsssec(abfd)->vma; + + /* Fix up exec header. */ + execp->a_text = obj_textsec(abfd)->_raw_size; + execp->a_data = obj_datasec(abfd)->_raw_size; + execp->a_bss = obj_bsssec(abfd)->_raw_size; + N_SET_MAGIC (*execp, NMAGIC); +} + boolean -DEFUN (NAME (aout,adjust_sizes_and_vmas), (abfd, text_size, text_end), +DEFUN (NAME(aout,adjust_sizes_and_vmas), (abfd, text_size, text_end), bfd *abfd AND bfd_size_type *text_size AND file_ptr *text_end) { struct internal_exec *execp = exec_hdr (abfd); + if ((obj_textsec (abfd) == NULL) || (obj_datasec (abfd) == NULL)) { bfd_error = invalid_operation; return false; } if (adata(abfd).magic != undecided_magic) return true; + obj_textsec(abfd)->_raw_size = align_power(obj_textsec(abfd)->_raw_size, obj_textsec(abfd)->alignment_power); *text_size = obj_textsec (abfd)->_raw_size; /* Rule (heuristic) for when to pad to a new page. Note that there - * are (at least) two ways demand-paged (ZMAGIC) files have been - * handled. Most Berkeley-based systems start the text segment at - * (PAGE_SIZE). However, newer versions of SUNOS start the text - * segment right after the exec header; the latter is counted in the - * text segment size, and is paged in by the kernel with the rest of - * the text. */ + are (at least) two ways demand-paged (ZMAGIC) files have been + handled. Most Berkeley-based systems start the text segment at + (PAGE_SIZE). However, newer versions of SUNOS start the text + segment right after the exec header; the latter is counted in the + text segment size, and is paged in by the kernel with the rest of + the text. */ /* This perhaps isn't the right way to do this, but made it simpler for me to understand enough to implement it. Better would probably be to go @@ -712,17 +880,15 @@ DEFUN (NAME (aout,adjust_sizes_and_vmas), (abfd, text_size, text_end), other magic, that it was a little hard for me to understand. I think I understand it better now, but I haven't time to do the cleanup this minute. */ - if (adata(abfd).magic == undecided_magic) - { - if (abfd->flags & D_PAGED) - /* Whether or not WP_TEXT is set -- let D_PAGED override. */ - /* @@ What about QMAGIC? */ - adata(abfd).magic = z_magic; - else if (abfd->flags & WP_TEXT) - adata(abfd).magic = n_magic; - else - adata(abfd).magic = o_magic; - } + + if (abfd->flags & D_PAGED) + /* Whether or not WP_TEXT is set -- let D_PAGED override. */ + /* @@ What about QMAGIC? */ + adata(abfd).magic = z_magic; + else if (abfd->flags & WP_TEXT) + adata(abfd).magic = n_magic; + else + adata(abfd).magic = o_magic; #ifdef BFD_AOUT_DEBUG /* requires gcc2 */ #if __GNUC__ >= 2 @@ -736,167 +902,39 @@ DEFUN (NAME (aout,adjust_sizes_and_vmas), (abfd, text_size, text_end), } str; }), - obj_textsec(abfd)->vma, obj_textsec(abfd)->_raw_size, obj_textsec(abfd)->alignment_power, - obj_datasec(abfd)->vma, obj_datasec(abfd)->_raw_size, obj_datasec(abfd)->alignment_power, - obj_bsssec(abfd)->vma, obj_bsssec(abfd)->_raw_size, obj_bsssec(abfd)->alignment_power); + obj_textsec(abfd)->vma, obj_textsec(abfd)->_raw_size, + obj_textsec(abfd)->alignment_power, + obj_datasec(abfd)->vma, obj_datasec(abfd)->_raw_size, + obj_datasec(abfd)->alignment_power, + obj_bsssec(abfd)->vma, obj_bsssec(abfd)->_raw_size, + obj_bsssec(abfd)->alignment_power); #endif #endif switch (adata(abfd).magic) { case o_magic: - { - file_ptr pos = adata (abfd).exec_bytes_size; - bfd_vma vma = 0; - int pad = 0; - - obj_textsec(abfd)->filepos = pos; - pos += obj_textsec(abfd)->_raw_size; - vma += obj_textsec(abfd)->_raw_size; - if (!obj_datasec(abfd)->user_set_vma) - { -#if 0 /* ?? Does alignment in the file image really matter? */ - pad = align_power (vma, obj_datasec(abfd)->alignment_power) - vma; -#endif - obj_textsec(abfd)->_raw_size += pad; - pos += pad; - vma += pad; - obj_datasec(abfd)->vma = vma; - } - obj_datasec(abfd)->filepos = pos; - pos += obj_datasec(abfd)->_raw_size; - vma += obj_datasec(abfd)->_raw_size; - if (!obj_bsssec(abfd)->user_set_vma) - { -#if 0 - pad = align_power (vma, obj_bsssec(abfd)->alignment_power) - vma; -#endif - obj_datasec(abfd)->_raw_size += pad; - pos += pad; - vma += pad; - obj_bsssec(abfd)->vma = vma; - } - obj_bsssec(abfd)->filepos = pos; - execp->a_text = obj_textsec(abfd)->_raw_size; - execp->a_data = obj_datasec(abfd)->_raw_size; - execp->a_bss = obj_bsssec(abfd)->_raw_size; - N_SET_MAGIC (*execp, OMAGIC); - } + adjust_o_magic (abfd, execp); break; case z_magic: - { - bfd_size_type data_pad, text_pad; - file_ptr text_end; - CONST struct aout_backend_data *abdp; - int ztih; - bfd_vma data_vma; - - abdp = aout_backend_info (abfd); - ztih = abdp && abdp->text_includes_header; - obj_textsec(abfd)->filepos = (ztih - ? adata(abfd).exec_bytes_size - : adata(abfd).page_size); - if (! obj_textsec(abfd)->user_set_vma) - /* ?? Do we really need to check for relocs here? */ - obj_textsec(abfd)->vma = ((abfd->flags & HAS_RELOC) - ? 0 - : (ztih - ? (abdp->default_text_vma - + adata(abfd).exec_bytes_size) - : abdp->default_text_vma)); - /* Could take strange alignment of text section into account here? */ - - /* Find start of data. */ - text_end = obj_textsec(abfd)->filepos + obj_textsec(abfd)->_raw_size; - text_pad = BFD_ALIGN (text_end, adata(abfd).page_size) - text_end; - obj_textsec(abfd)->_raw_size += text_pad; - text_end += text_pad; - - if (!obj_datasec(abfd)->user_set_vma) - { - bfd_vma vma; - vma = obj_textsec(abfd)->vma + obj_textsec(abfd)->_raw_size; - obj_datasec(abfd)->vma = BFD_ALIGN (vma, adata(abfd).segment_size); - } - data_vma = obj_datasec(abfd)->vma; - if (abdp && abdp->zmagic_mapped_contiguous) - { - text_pad = (obj_datasec(abfd)->vma - - obj_textsec(abfd)->vma - - obj_textsec(abfd)->_raw_size); - obj_textsec(abfd)->_raw_size += text_pad; - } - obj_datasec(abfd)->filepos = (obj_textsec(abfd)->filepos - + obj_textsec(abfd)->_raw_size); - - /* Fix up exec header while we're at it. */ - execp->a_text = obj_textsec(abfd)->_raw_size; - if (ztih && (!abdp || (abdp && !abdp->exec_header_not_counted))) - execp->a_text += adata(abfd).exec_bytes_size; - N_SET_MAGIC (*execp, ZMAGIC); - /* Spec says data section should be rounded up to page boundary. */ - /* If extra space in page is left after data section, fudge data - in the header so that the bss section looks smaller by that - amount. We'll start the bss section there, and lie to the OS. */ - obj_datasec(abfd)->_raw_size - = align_power (obj_datasec(abfd)->_raw_size, - obj_bsssec(abfd)->alignment_power); - execp->a_data = BFD_ALIGN (obj_datasec(abfd)->_raw_size, - adata(abfd).page_size); - data_pad = execp->a_data - obj_datasec(abfd)->_raw_size; - - if (!obj_bsssec(abfd)->user_set_vma) - obj_bsssec(abfd)->vma = (obj_datasec(abfd)->vma - + obj_datasec(abfd)->_raw_size); - if (data_pad > obj_bsssec(abfd)->_raw_size) - execp->a_bss = 0; - else - execp->a_bss = obj_bsssec(abfd)->_raw_size - data_pad; - } + adjust_z_magic (abfd, execp); break; case n_magic: - { - file_ptr pos = adata(abfd).exec_bytes_size; - bfd_vma vma = 0; - int pad; - - obj_textsec(abfd)->filepos = pos; - if (!obj_textsec(abfd)->user_set_vma) - obj_textsec(abfd)->vma = vma; - else - vma = obj_textsec(abfd)->vma; - pos += obj_textsec(abfd)->_raw_size; - vma += obj_textsec(abfd)->_raw_size; - obj_datasec(abfd)->filepos = pos; - if (!obj_datasec(abfd)->user_set_vma) - obj_datasec(abfd)->vma = BFD_ALIGN (vma, adata(abfd).segment_size); - vma = obj_datasec(abfd)->vma; - - /* Since BSS follows data immediately, see if it needs alignment. */ - vma += obj_datasec(abfd)->_raw_size; - pad = align_power (vma, obj_bsssec(abfd)->alignment_power) - vma; - obj_datasec(abfd)->_raw_size += pad; - pos += obj_datasec(abfd)->_raw_size; - - if (!obj_bsssec(abfd)->user_set_vma) - obj_bsssec(abfd)->vma = vma; - else - vma = obj_bsssec(abfd)->vma; - } - execp->a_text = obj_textsec(abfd)->_raw_size; - execp->a_data = obj_datasec(abfd)->_raw_size; - execp->a_bss = obj_bsssec(abfd)->_raw_size; - N_SET_MAGIC (*execp, NMAGIC); + adjust_n_magic (abfd, execp); break; default: abort (); } + #ifdef BFD_AOUT_DEBUG fprintf (stderr, " text=<%x,%x,%x> data=<%x,%x,%x> bss=<%x,%x>\n", - obj_textsec(abfd)->vma, obj_textsec(abfd)->_raw_size, obj_textsec(abfd)->filepos, - obj_datasec(abfd)->vma, obj_datasec(abfd)->_raw_size, obj_datasec(abfd)->filepos, + obj_textsec(abfd)->vma, obj_textsec(abfd)->_raw_size, + obj_textsec(abfd)->filepos, + obj_datasec(abfd)->vma, obj_datasec(abfd)->_raw_size, + obj_datasec(abfd)->filepos, obj_bsssec(abfd)->vma, obj_bsssec(abfd)->_raw_size); #endif + return true; } @@ -1163,7 +1201,6 @@ DEFUN (translate_from_native_sym_flags, (sym_pointer, cache_ptr, abfd), break; default: case N_ABS: - cache_ptr->symbol.section = &bfd_abs_section; break; } @@ -1231,7 +1268,7 @@ DEFUN (translate_from_native_sym_flags, (sym_pointer, cache_ptr, abfd), -static void +static boolean DEFUN(translate_to_native_sym_flags,(sym_pointer, cache_ptr, abfd), struct external_nlist *sym_pointer AND asymbol *cache_ptr AND @@ -1270,19 +1307,8 @@ DEFUN(translate_to_native_sym_flags,(sym_pointer, cache_ptr, abfd), sym_pointer->e_type[0] = (N_UNDF | N_EXT); } else { - if (cache_ptr->section->output_section) - { - - bfd_error_vector.nonrepresentable_section(abfd, - bfd_get_output_section(cache_ptr)->name); - } - else - { - bfd_error_vector.nonrepresentable_section(abfd, - cache_ptr->section->name); - - } - + bfd_error = bfd_error_nonrepresentable_section; + return false; } /* Turn the symbol from section relative to absolute again */ @@ -1312,6 +1338,8 @@ DEFUN(translate_to_native_sym_flags,(sym_pointer, cache_ptr, abfd), } PUT_WORD(abfd, value, sym_pointer->e_value); + + return true; } /* Native-level interface to symbols. */ @@ -1804,7 +1832,7 @@ emit_strtab (abfd, tab) } */ } -void +boolean DEFUN(NAME(aout,write_syms),(abfd), bfd *abfd) { @@ -1838,9 +1866,12 @@ DEFUN(NAME(aout,write_syms),(abfd), bfd_h_put_8(abfd, 0, nsp.e_type); } - translate_to_native_sym_flags (&nsp, g, abfd); + if (! translate_to_native_sym_flags (&nsp, g, abfd)) + return false; - bfd_write((PTR)&nsp,1,EXTERNAL_NLIST_SIZE, abfd); + if (bfd_write((PTR)&nsp,1,EXTERNAL_NLIST_SIZE, abfd) + != EXTERNAL_NLIST_SIZE) + return false; /* NB: `KEEPIT' currently overlays `flags', so set this only here, at the end. */ @@ -1848,6 +1879,8 @@ DEFUN(NAME(aout,write_syms),(abfd), } emit_strtab (abfd, &strtab); + + return true; } @@ -2562,3 +2595,1704 @@ DEFUN(NAME(aout,sizeof_headers),(abfd, execable), { return adata(abfd).exec_bytes_size; } + +/* a.out link code. */ + +/* a.out linker hash table entries. */ + +struct aout_link_hash_entry +{ + struct bfd_link_hash_entry root; + /* Symbol index in output file. */ + int indx; +}; + +/* a.out linker hash table. */ + +struct aout_link_hash_table +{ + struct bfd_link_hash_table root; +}; + +static struct bfd_hash_entry *aout_link_hash_newfunc + PARAMS ((struct bfd_hash_entry *entry, + struct bfd_hash_table *table, + const char *string)); +static boolean aout_link_add_object_symbols + PARAMS ((bfd *, struct bfd_link_info *)); +static boolean aout_link_check_archive_element + PARAMS ((bfd *, struct bfd_link_info *, boolean *)); +static boolean aout_link_get_symbols PARAMS ((bfd *)); +static boolean aout_link_free_symbols PARAMS ((bfd *)); +static boolean aout_link_check_ar_symbols + PARAMS ((bfd *, struct bfd_link_info *, boolean *pneeded)); +static boolean aout_link_add_symbols + PARAMS ((bfd *, struct bfd_link_info *)); + +/* Routine to create an entry in an a.out link hash table. */ + +static struct bfd_hash_entry * +aout_link_hash_newfunc (entry, table, string) + struct bfd_hash_entry *entry; + struct bfd_hash_table *table; + const char *string; +{ + struct aout_link_hash_entry *ret = (struct aout_link_hash_entry *) entry; + + /* Allocate the structure if it has not already been allocated by a + subclass. */ + if (ret == (struct aout_link_hash_entry *) NULL) + ret = ((struct aout_link_hash_entry *) + bfd_hash_allocate (table, sizeof (struct aout_link_hash_entry))); + + /* Call the allocation method of the superclass. */ + ret = ((struct aout_link_hash_entry *) + _bfd_link_hash_newfunc ((struct bfd_hash_entry *) ret, + table, string)); + + /* Set local fields. */ + ret->indx = -1; + + return (struct bfd_hash_entry *) ret; +} + +/* Create an a.out link hash table. */ + +struct bfd_link_hash_table * +NAME(aout,link_hash_table_create) (abfd) + bfd *abfd; +{ + struct aout_link_hash_table *ret; + + ret = ((struct aout_link_hash_table *) + bfd_xmalloc (sizeof (struct aout_link_hash_table))); + if (! _bfd_link_hash_table_init (&ret->root, abfd, + aout_link_hash_newfunc)) + { + free (ret); + return (struct bfd_link_hash_table *) NULL; + } + return &ret->root; +} + +/* Look up an entry in an a.out link hash table. */ + +#define aout_link_hash_lookup(table, string, create, copy, follow) \ + ((struct aout_link_hash_entry *) \ + bfd_link_hash_lookup (&(table)->root, (string), (create), (copy), (follow))) + +/* Traverse an a.out link hash table. */ + +#define aout_link_hash_traverse(table, func, info) \ + (bfd_link_hash_traverse \ + (&(table)->root, \ + (boolean (*) PARAMS ((struct bfd_link_hash_entry *, PTR))) (func), \ + (info))) + +/* Get the a.out link hash table from the info structure. This is + just a cast. */ + +#define aout_hash_table(p) ((struct aout_link_hash_table *) ((p)->hash)) + +/* Given an a.out BFD, add symbols to the global hash table as + appropriate. */ + +boolean +NAME(aout,link_add_symbols) (abfd, info) + bfd *abfd; + struct bfd_link_info *info; +{ + switch (bfd_get_format (abfd)) + { + case bfd_object: + return aout_link_add_object_symbols (abfd, info); + case bfd_archive: + return _bfd_generic_link_add_archive_symbols + (abfd, info, aout_link_check_archive_element); + default: + bfd_error = wrong_format; + return false; + } +} + +/* Add symbols from an a.out object file. */ + +static boolean +aout_link_add_object_symbols (abfd, info) + bfd *abfd; + struct bfd_link_info *info; +{ + if (! aout_link_get_symbols (abfd)) + return false; + if (! aout_link_add_symbols (abfd, info)) + return false; + if (! info->keep_memory) + { + if (! aout_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 from + _bfd_generic_link_add_archive_symbols. */ + +static boolean +aout_link_check_archive_element (abfd, info, pneeded) + bfd *abfd; + struct bfd_link_info *info; + boolean *pneeded; +{ + if (! aout_link_get_symbols (abfd)) + return false; + + if (! aout_link_check_ar_symbols (abfd, info, pneeded)) + return false; + + if (*pneeded) + { + if (! aout_link_add_symbols (abfd, info)) + return false; + } + + /* We keep around the symbols even if we aren't going to use this + object file, because we may want to reread it. This doesn't + waste too much memory, because it isn't all that common to read + an archive element but not need it. */ + if (! info->keep_memory) + { + if (! aout_link_free_symbols (abfd)) + return false; + } + + return true; +} + +/* Read the internal symbols from an a.out file. */ + +static boolean +aout_link_get_symbols (abfd) + bfd *abfd; +{ + bfd_size_type count; + struct external_nlist *syms; + unsigned char string_chars[BYTES_IN_WORD]; + bfd_size_type stringsize; + char *strings; + + if (obj_aout_external_syms (abfd) != (struct external_nlist *) NULL) + { + /* We already have them. */ + return true; + } + + count = exec_hdr (abfd)->a_syms / EXTERNAL_NLIST_SIZE; + + /* We allocate using bfd_xmalloc to make the values easy to free + later on. If we put them on the obstack it might not be possible + to free them. */ + syms = ((struct external_nlist *) + bfd_xmalloc ((size_t) count * EXTERNAL_NLIST_SIZE)); + + if (bfd_seek (abfd, obj_sym_filepos (abfd), SEEK_SET) != 0 + || (bfd_read ((PTR) syms, 1, exec_hdr (abfd)->a_syms, abfd) + != exec_hdr (abfd)->a_syms)) + return false; + + /* Get the size of the strings. */ + if (bfd_seek (abfd, obj_str_filepos (abfd), SEEK_SET) != 0 + || (bfd_read ((PTR) string_chars, BYTES_IN_WORD, 1, abfd) + != BYTES_IN_WORD)) + return false; + stringsize = GET_WORD (abfd, string_chars); + strings = (char *) bfd_xmalloc ((size_t) stringsize); + + /* Skip space for the string count in the buffer for convenience + when using indexes. */ + if (bfd_read (strings + BYTES_IN_WORD, 1, stringsize - BYTES_IN_WORD, abfd) + != stringsize - BYTES_IN_WORD) + return false; + + /* Save the data. */ + obj_aout_external_syms (abfd) = syms; + obj_aout_external_sym_count (abfd) = count; + obj_aout_external_strings (abfd) = strings; + + return true; +} + +/* Free up the internal symbols read from an a.out file. */ + +static boolean +aout_link_free_symbols (abfd) + bfd *abfd; +{ + if (obj_aout_external_syms (abfd) != (struct external_nlist *) NULL) + { + free ((PTR) obj_aout_external_syms (abfd)); + obj_aout_external_syms (abfd) = (struct external_nlist *) NULL; + } + if (obj_aout_external_strings (abfd) != (char *) NULL) + { + free ((PTR) obj_aout_external_strings (abfd)); + obj_aout_external_strings (abfd) = (char *) NULL; + } + return true; +} + +/* Look through the internal symbols to see if this object file should + be included in the link. We should include this object file if it + defines any symbols which are currently undefined. If this object + file defines a common symbol, then we may adjust the size of the + known symbol but we do not include the object file in the link + (unless there is some other reason to include it). */ + +static boolean +aout_link_check_ar_symbols (abfd, info, pneeded) + bfd *abfd; + struct bfd_link_info *info; + boolean *pneeded; +{ + register struct external_nlist *p; + struct external_nlist *pend; + char *strings; + + *pneeded = false; + + /* Look through all the symbols. */ + p = obj_aout_external_syms (abfd); + pend = p + obj_aout_external_sym_count (abfd); + strings = obj_aout_external_strings (abfd); + for (; p < pend; p++) + { + int type = bfd_h_get_8 (abfd, p->e_type); + const char *name; + struct bfd_link_hash_entry *h; + + /* Ignore symbols that are not externally visible. */ + if ((type & N_EXT) == 0) + continue; + + name = strings + GET_WORD (abfd, p->e_strx); + h = bfd_link_hash_lookup (info->hash, name, false, false, true); + + /* We are only interested in symbols that are currently + undefined or common. */ + if (h == (struct bfd_link_hash_entry *) NULL + || (h->type != bfd_link_hash_undefined + && h->type != bfd_link_hash_common)) + continue; + + if ((type & (N_TEXT | N_DATA | N_BSS)) != 0) + { + /* This object file defines this symbol. We must link it + in. This is true regardless of whether the current + definition of the symbol is undefined or common. If the + current definition is common, we have a case in which we + have already seen an object file including + int a; + and this object file from the archive includes + int a = 5; + In such a case we must include this object file. */ + if (! (*info->callbacks->add_archive_element) (info, abfd, name)) + return false; + *pneeded = true; + return true; + } + + if (type == (N_EXT | N_UNDF)) + { + bfd_vma value; + + value = GET_WORD (abfd, p->e_value); + if (value != 0) + { + /* This symbol is common in the object from the archive + file. */ + if (h->type == bfd_link_hash_undefined) + { + bfd *symbfd; + + symbfd = h->u.undef.abfd; + if (symbfd == (bfd *) NULL) + { + /* This symbol was created as undefined from + outside BFD. We assume that we should link + in the object file. This is done for the -u + option in the linker. */ + if (! (*info->callbacks->add_archive_element) (info, + abfd, + name)) + return false; + *pneeded = true; + return true; + } + /* Turn the current link symbol into a common + symbol. It is already on the undefs list. */ + h->type = bfd_link_hash_common; + h->u.c.size = value; + h->u.c.section = bfd_make_section_old_way (symbfd, + "COMMON"); + } + else + { + /* Adjust the size of the common symbol if + necessary. */ + if (value > h->u.c.size) + h->u.c.size = value; + } + } + } + } + + /* We do not need this object file. */ + return true; +} + +/* Add all symbols from an object file to the hash table. */ + +static boolean +aout_link_add_symbols (abfd, info) + bfd *abfd; + struct bfd_link_info *info; +{ + bfd_size_type sym_count; + char *strings; + boolean copy; + struct aout_link_hash_entry **sym_hash; + register struct external_nlist *p; + struct external_nlist *pend; + + sym_count = obj_aout_external_sym_count (abfd); + strings = obj_aout_external_strings (abfd); + if (info->keep_memory) + copy = false; + else + copy = true; + + + /* We keep a list of the linker hash table entries that correspond + to particular symbols. We could just look them up in the hash + table, but keeping the list is more efficient. Perhaps this + should be conditional on info->keep_memory. */ + sym_hash = ((struct aout_link_hash_entry **) + bfd_alloc (abfd, + ((size_t) sym_count + * sizeof (struct aout_link_hash_entry *)))); + obj_aout_sym_hashes (abfd) = sym_hash; + + p = obj_aout_external_syms (abfd); + pend = p + sym_count; + for (; p < pend; p++, sym_hash++) + { + int type; + const char *name; + bfd_vma value; + asection *section; + flagword flags; + const char *string; + + *sym_hash = NULL; + + type = bfd_h_get_8 (abfd, p->e_type); + + /* Ignore debugging symbols. */ + if ((type & N_STAB) != 0) + continue; + + /* Ignore symbols that are not external. */ + if ((type & N_EXT) == 0 + && type != N_WARNING + && type != N_SETA + && type != N_SETT + && type != N_SETD + && type != N_SETB) + { + /* If this is an N_INDR symbol we must skip the next entry, + which is the symbol to indirect to (actually, an N_INDR + symbol without N_EXT set is pretty useless). */ + if (type == N_INDR) + ++p; + continue; + } + + /* Ignore N_FN symbols (these appear to have N_EXT set). */ + if (type == N_FN) + continue; + + name = strings + GET_WORD (abfd, p->e_strx); + value = GET_WORD (abfd, p->e_value); + flags = BSF_GLOBAL; + string = NULL; + switch (type) + { + default: + abort (); + case N_UNDF | N_EXT: + if (value != 0) + section = &bfd_com_section; + else + section = &bfd_und_section; + break; + case N_ABS | N_EXT: + section = &bfd_abs_section; + break; + case N_TEXT | N_EXT: + section = obj_textsec (abfd); + value -= bfd_get_section_vma (abfd, section); + break; + case N_DATA | N_EXT: + section = obj_datasec (abfd); + value -= bfd_get_section_vma (abfd, section); + break; + case N_BSS | N_EXT: + section = obj_bsssec (abfd); + value -= bfd_get_section_vma (abfd, section); + break; + case N_INDR | N_EXT: + /* An indirect symbol. The next symbol is the symbol + which this one really is. */ + BFD_ASSERT (p + 1 < pend); + ++p; + string = strings + GET_WORD (abfd, p->e_strx); + section = &bfd_ind_section; + flags |= BSF_INDIRECT; + break; + case N_COMM | N_EXT: + section = &bfd_com_section; + break; + case N_SETA: + section = &bfd_abs_section; + flags |= BSF_CONSTRUCTOR; + break; + case N_SETT: + section = obj_textsec (abfd); + flags |= BSF_CONSTRUCTOR; + value -= bfd_get_section_vma (abfd, section); + break; + case N_SETD: + section = obj_datasec (abfd); + flags |= BSF_CONSTRUCTOR; + value -= bfd_get_section_vma (abfd, section); + break; + case N_SETB: + section = obj_bsssec (abfd); + flags |= BSF_CONSTRUCTOR; + value -= bfd_get_section_vma (abfd, section); + break; + case N_WARNING: + /* A warning symbol. The next symbol is the one to warn + about. */ + BFD_ASSERT (p + 1 < pend); + ++p; + string = name; + name = strings + GET_WORD (abfd, p->e_strx); + section = &bfd_und_section; + flags |= BSF_WARNING; + break; + } + + if (! (_bfd_generic_link_add_one_symbol + (info, abfd, name, flags, section, value, string, copy, + (struct bfd_link_hash_entry **) sym_hash))) + return false; + } + + return true; +} + +/* During the final link step we need to pass around a bunch of + information, so we do it in an instance of this structure. */ + +struct aout_final_link_info +{ + /* General link information. */ + struct bfd_link_info *info; + /* Output bfd. */ + bfd *output_bfd; + /* Reloc file positions. */ + file_ptr treloff, dreloff; + /* File position of symbols. */ + file_ptr symoff; + /* String table. */ + struct stringtab_data strtab; +}; + +static boolean aout_link_input_bfd + PARAMS ((struct aout_final_link_info *, bfd *input_bfd)); +static boolean aout_link_write_symbols + PARAMS ((struct aout_final_link_info *, bfd *input_bfd, int *symbol_map)); +static boolean aout_link_write_other_symbol + PARAMS ((struct aout_link_hash_entry *, PTR)); +static boolean aout_link_input_section + PARAMS ((struct aout_final_link_info *, bfd *input_bfd, + asection *input_section, file_ptr *reloff_ptr, + bfd_size_type rel_size, int *symbol_map)); +static boolean aout_link_input_section_std + PARAMS ((struct aout_final_link_info *, bfd *input_bfd, + asection *input_section, struct reloc_std_external *, + bfd_size_type rel_size, bfd_byte *contents, int *symbol_map)); +static boolean aout_link_input_section_ext + PARAMS ((struct aout_final_link_info *, bfd *input_bfd, + asection *input_section, struct reloc_ext_external *, + bfd_size_type rel_size, bfd_byte *contents, int *symbol_map)); +static INLINE asection *aout_reloc_index_to_section + PARAMS ((bfd *, int)); + +/* Do the final link step. This is called on the output BFD. The + INFO structure should point to a list of BFDs linked through the + link_next field which can be used to find each BFD which takes part + in the output. Also, each section in ABFD should point to a list + of bfd_link_order structures which list all the input sections for + the output section. */ + +boolean +NAME(aout,final_link) (abfd, info, callback) + bfd *abfd; + struct bfd_link_info *info; + void (*callback) PARAMS ((bfd *, file_ptr *, file_ptr *, file_ptr *)); +{ + struct aout_final_link_info aout_info; + register bfd *sub; + bfd_size_type text_size; + file_ptr text_end; + register struct bfd_link_order *p; + asection *o; + + aout_info.info = info; + aout_info.output_bfd = abfd; + + if (! info->relocateable) + { + exec_hdr (abfd)->a_trsize = 0; + exec_hdr (abfd)->a_drsize = 0; + } + else + { + bfd_size_type trsize, drsize; + + /* Count up the relocation sizes. */ + trsize = 0; + drsize = 0; + for (sub = info->input_bfds; sub != (bfd *) NULL; sub = sub->link_next) + { + if (bfd_get_flavour (abfd) == bfd_target_aout_flavour) + { + trsize += exec_hdr (sub)->a_trsize; + drsize += exec_hdr (sub)->a_drsize; + } + else + { + /* FIXME: We need to identify the .text and .data sections + and call get_reloc_upper_bound and canonicalize_reloc to + work out the number of relocs needed, and then multiply + by the reloc size. */ + abort (); + } + } + exec_hdr (abfd)->a_trsize = trsize; + exec_hdr (abfd)->a_drsize = drsize; + } + + /* Adjust the section sizes and vmas according to the magic number. + This sets a_text, a_data and a_bss in the exec_hdr and sets the + filepos for each section. */ + if (! NAME(aout,adjust_sizes_and_vmas) (abfd, &text_size, &text_end)) + return false; + + /* The relocation and symbol file positions differ among a.out + targets. We are passed a callback routine from the backend + specific code to handle this. + FIXME: At this point we do not know how much space the symbol + table will require. This will not work for any (nonstandard) + a.out target that needs to know the symbol table size before it + can compute the relocation file positions. This may or may not + be the case for the hp300hpux target, for example. */ + (*callback) (abfd, &aout_info.treloff, &aout_info.dreloff, + &aout_info.symoff); + obj_textsec (abfd)->rel_filepos = aout_info.treloff; + obj_datasec (abfd)->rel_filepos = aout_info.dreloff; + obj_sym_filepos (abfd) = aout_info.symoff; + + /* We keep a count of the symbols as we output them. */ + obj_aout_external_sym_count (abfd) = 0; + + /* We accumulate the string table as we write out the symbols. */ + stringtab_init (&aout_info.strtab); + + /* The most time efficient way to do the link would be to read all + the input object files into memory and then sort out the + information into the output file. Unfortunately, that will + probably use too much memory. Another method would be to step + through everything that composes the text section and write it + out, and then everything that composes the data section and write + it out, and then write out the relocs, and then write out the + symbols. Unfortunately, that requires reading stuff from each + input file several times, and we will not be able to keep all the + input files open simultaneously, and reopening them will be slow. + + What we do is basically process one input file at a time. We do + everything we need to do with an input file once--copy over the + section contents, handle the relocation information, and write + out the symbols--and then we throw away the information we read + from it. This approach requires a lot of lseeks of the output + file, which is unfortunate but still faster than reopening a lot + of files. + + We use the output_has_begun field of the input BFDs to see + whether we have already handled it. */ + for (sub = info->input_bfds; sub != (bfd *) NULL; sub = sub->link_next) + sub->output_has_begun = false; + + for (o = abfd->sections; o != (asection *) NULL; o = o->next) + { + bfd *input_bfd; + + for (p = o->link_order_head; + p != (struct bfd_link_order *) NULL; + p = p->next) + { + /* If we might be using the C based alloca function, we need + to dump the memory allocated by aout_link_input_bfd. */ +#ifndef __GNUC__ +#ifndef alloca + (void) alloca (0); +#endif +#endif + switch (p->type) + { + case bfd_indirect_link_order: + input_bfd = p->u.indirect.section->owner; + if (bfd_get_flavour (input_bfd) == bfd_target_aout_flavour) + { + if (! input_bfd->output_has_begun) + { + if (! aout_link_input_bfd (&aout_info, input_bfd)) + return false; + input_bfd->output_has_begun = true; + } + } + else + { + /* FIXME. */ + abort (); + } + break; + default: + if (! _bfd_default_link_order (abfd, info, o, p)) + return false; + } + } + } + + /* Write out any symbols that we have not already written out. */ + aout_link_hash_traverse (aout_hash_table (info), + aout_link_write_other_symbol, + (PTR) &aout_info); + + /* Update the header information. */ + abfd->symcount = obj_aout_external_sym_count (abfd); + exec_hdr (abfd)->a_syms = abfd->symcount * EXTERNAL_NLIST_SIZE; + obj_str_filepos (abfd) = obj_sym_filepos (abfd) + exec_hdr (abfd)->a_syms; + obj_textsec (abfd)->reloc_count = + exec_hdr (abfd)->a_trsize / obj_reloc_entry_size (abfd); + obj_datasec (abfd)->reloc_count = + exec_hdr (abfd)->a_drsize / obj_reloc_entry_size (abfd); + + /* Write out the string table. */ + if (bfd_seek (abfd, obj_str_filepos (abfd), SEEK_SET) != 0) + return false; + emit_strtab (abfd, &aout_info.strtab); + + return true; +} + +/* Link an a.out input BFD into the output file. */ + +static boolean +aout_link_input_bfd (finfo, input_bfd) + struct aout_final_link_info *finfo; + bfd *input_bfd; +{ + bfd_size_type sym_count; + int *symbol_map; + + BFD_ASSERT (bfd_get_format (input_bfd) == bfd_object); + + /* Get the symbols. We probably have them already, unless + finfo->info->keep_memory is false. */ + if (! aout_link_get_symbols (input_bfd)) + return false; + + sym_count = obj_aout_external_sym_count (input_bfd); + symbol_map = (int *) alloca ((size_t) sym_count * sizeof (int)); + + /* Write out the symbols and get a map of the new indices. */ + if (! aout_link_write_symbols (finfo, input_bfd, symbol_map)) + return false; + + /* Relocate and write out the sections. */ + if (! aout_link_input_section (finfo, input_bfd, + obj_textsec (input_bfd), + &finfo->treloff, + exec_hdr (input_bfd)->a_trsize, + symbol_map) + || ! aout_link_input_section (finfo, input_bfd, + obj_datasec (input_bfd), + &finfo->dreloff, + exec_hdr (input_bfd)->a_drsize, + symbol_map)) + return false; + + /* If we are not keeping memory, we don't need the symbols any + longer. We still need them if we are keeping memory, because the + strings in the hash table point into them. */ + if (! finfo->info->keep_memory) + { + if (! aout_link_free_symbols (input_bfd)) + return false; + } + + return true; +} + +/* Adjust and write out the symbols for an a.out file. Set the new + symbol indices into a symbol_map. */ + +static boolean +aout_link_write_symbols (finfo, input_bfd, symbol_map) + struct aout_final_link_info *finfo; + bfd *input_bfd; + int *symbol_map; +{ + bfd *output_bfd; + bfd_size_type sym_count; + char *strings; + enum bfd_link_strip strip; + enum bfd_link_discard discard; + struct external_nlist *output_syms; + struct external_nlist *outsym; + register struct external_nlist *sym; + struct external_nlist *sym_end; + struct aout_link_hash_entry **sym_hash; + boolean pass; + + output_bfd = finfo->output_bfd; + sym_count = obj_aout_external_sym_count (input_bfd); + strings = obj_aout_external_strings (input_bfd); + strip = finfo->info->strip; + discard = finfo->info->discard; + output_syms = ((struct external_nlist *) + alloca ((size_t) (sym_count + 1) * EXTERNAL_NLIST_SIZE)); + outsym = output_syms; + + /* First write out a symbol for this object file, unless we are + discarding such symbols. */ + if (strip != strip_all + && (strip != strip_some + || bfd_hash_lookup (finfo->info->keep_hash, input_bfd->filename, + false, false) != NULL) + && discard != discard_all) + { + bfd_h_put_8 (output_bfd, N_TEXT, outsym->e_type); + bfd_h_put_8 (output_bfd, 0, outsym->e_other); + bfd_h_put_16 (output_bfd, (bfd_vma) 0, outsym->e_desc); + PUT_WORD (output_bfd, + add_to_stringtab (output_bfd, input_bfd->filename, + &finfo->strtab), + outsym->e_strx); + PUT_WORD (output_bfd, + bfd_get_section_vma (input_bfd, obj_textsec (input_bfd)), + outsym->e_value); + ++obj_aout_external_sym_count (output_bfd); + ++outsym; + } + + pass = false; + sym = obj_aout_external_syms (input_bfd); + sym_end = sym + sym_count; + sym_hash = obj_aout_sym_hashes (input_bfd); + for (; sym < sym_end; sym++, sym_hash++, symbol_map++) + { + const char *name; + int type; + boolean skip; + asection *symsec; + bfd_vma val = 0; + + *symbol_map = -1; + + type = bfd_h_get_8 (input_bfd, sym->e_type); + name = strings + GET_WORD (input_bfd, sym->e_strx); + + if (pass) + { + /* Pass this symbol through. */ + val = GET_WORD (input_bfd, sym->e_value); + pass = false; + } + else + { + struct aout_link_hash_entry *h; + + /* We have saved the hash table entry for this symbol, if + there is one. Note that we could just look it up again + in the hash table, provided we first check that it is an + external symbol. */ + h = *sym_hash; + + /* If the symbol has already been written out, skip it. */ + if (h != (struct aout_link_hash_entry *) NULL + && h->root.written) + { + *symbol_map = h->indx; + continue; + } + + /* See if we are stripping this symbol. */ + skip = false; + switch (strip) + { + case strip_none: + break; + case strip_debugger: + if ((type & N_STAB) != 0) + skip = true; + break; + case strip_some: + if (bfd_hash_lookup (finfo->info->keep_hash, name, false, false) + == NULL) + skip = true; + break; + case strip_all: + skip = true; + break; + } + if (skip) + { + if (h != (struct aout_link_hash_entry *) NULL) + h->root.written = true; + continue; + } + + /* Get the value of the symbol. */ + if ((type & N_TYPE) == N_TEXT) + symsec = obj_textsec (input_bfd); + else if ((type & N_TYPE) == N_DATA) + symsec = obj_datasec (input_bfd); + else if ((type & N_TYPE) == N_BSS) + symsec = obj_bsssec (input_bfd); + else if ((type & N_TYPE) == N_ABS) + symsec = &bfd_abs_section; + else if ((type & N_TYPE) == N_INDR + || type == N_WARNING) + { + /* Pass the next symbol through unchanged. */ + pass = true; + val = GET_WORD (input_bfd, sym->e_value); + symsec = NULL; + } + else if ((type & N_STAB) != 0) + { + val = GET_WORD (input_bfd, sym->e_value); + symsec = NULL; + } + else + { + if (h == (struct aout_link_hash_entry *) NULL) + val = 0; + else if (h->root.type == bfd_link_hash_defined) + { + asection *output_section; + + /* This case means a common symbol which was turned + into a defined symbol. */ + output_section = h->root.u.def.section->output_section; + BFD_ASSERT (output_section == &bfd_abs_section + || output_section->owner == output_bfd); + val = (h->root.u.def.value + + bfd_get_section_vma (output_bfd, output_section) + + h->root.u.def.section->output_offset); + + /* Get the correct type based on the section. If + this is a constructed set, force it to be + globally visible. */ + if (type == N_SETT + || type == N_SETD + || type == N_SETB + || type == N_SETA) + type |= N_EXT; + + type &=~ N_TYPE; + + if (output_section == obj_textsec (output_bfd)) + type |= N_TEXT; + else if (output_section == obj_datasec (output_bfd)) + type |= N_DATA; + else if (output_section == obj_bsssec (output_bfd)) + type |= N_BSS; + else + type |= N_ABS; + } + else if (h->root.type == bfd_link_hash_common) + val = h->root.u.c.size; + else + val = 0; + + symsec = NULL; + } + if (symsec != (asection *) NULL) + val = (symsec->output_section->vma + + symsec->output_offset + + (GET_WORD (input_bfd, sym->e_value) + - symsec->vma)); + + /* If this is a global symbol set the written flag, and if + it is a local symbol see if we should discard it. */ + if (h != (struct aout_link_hash_entry *) NULL) + { + h->root.written = true; + h->indx = obj_aout_external_sym_count (output_bfd); + } + else + { + switch (discard) + { + case discard_none: + break; + case discard_l: + if (*name == *finfo->info->lprefix + && (finfo->info->lprefix_len == 1 + || strncmp (name, finfo->info->lprefix, + finfo->info->lprefix_len) == 0)) + skip = true; + break; + case discard_all: + skip = true; + break; + } + if (skip) + { + pass = false; + continue; + } + } + } + + /* Copy this symbol into the list of symbols we are going to + write out. */ + bfd_h_put_8 (output_bfd, type, outsym->e_type); + bfd_h_put_8 (output_bfd, bfd_h_get_8 (input_bfd, sym->e_other), + outsym->e_other); + bfd_h_put_16 (output_bfd, bfd_h_get_16 (input_bfd, sym->e_desc), + outsym->e_desc); + PUT_WORD (output_bfd, + add_to_stringtab (output_bfd, name, &finfo->strtab), + outsym->e_strx); + PUT_WORD (output_bfd, val, outsym->e_value); + *symbol_map = obj_aout_external_sym_count (output_bfd); + ++obj_aout_external_sym_count (output_bfd); + ++outsym; + } + + /* Write out the output symbols we have just constructed. */ + if (outsym > output_syms) + { + bfd_size_type outsym_count; + + if (bfd_seek (output_bfd, finfo->symoff, SEEK_SET) != 0) + return false; + outsym_count = outsym - output_syms; + if (bfd_write ((PTR) output_syms, (bfd_size_type) EXTERNAL_NLIST_SIZE, + (bfd_size_type) outsym_count, output_bfd) + != outsym_count * EXTERNAL_NLIST_SIZE) + return false; + finfo->symoff += outsym_count * EXTERNAL_NLIST_SIZE; + } + + return true; +} + +/* Write out a symbol that was not associated with an a.out input + object. */ + +static boolean +aout_link_write_other_symbol (h, data) + struct aout_link_hash_entry *h; + PTR data; +{ + struct aout_final_link_info *finfo = (struct aout_final_link_info *) data; + bfd *output_bfd; + int type; + bfd_vma val; + struct external_nlist outsym; + + if (h->root.written) + return true; + + output_bfd = finfo->output_bfd; + + switch (h->root.type) + { + default: + case bfd_link_hash_new: + abort (); + /* Avoid variable not initialized warnings. */ + return true; + case bfd_link_hash_undefined: + type = N_UNDF | N_EXT; + val = 0; + break; + case bfd_link_hash_defined: + { + asection *sec; + + sec = h->root.u.def.section; + BFD_ASSERT (sec == &bfd_abs_section + || sec->owner == output_bfd); + if (sec == obj_textsec (output_bfd)) + type = N_TEXT | N_EXT; + else if (sec == obj_datasec (output_bfd)) + type = N_DATA | N_EXT; + else if (sec == obj_bsssec (output_bfd)) + type = N_BSS | N_EXT; + else + type = N_ABS | N_EXT; + val = (h->root.u.def.value + + sec->output_section->vma + + sec->output_offset); + } + break; + case bfd_link_hash_common: + type = N_UNDF | N_EXT; + val = h->root.u.c.size; + break; + case bfd_link_hash_indirect: + case bfd_link_hash_warning: + /* FIXME: Ignore these for now. The circumstances under which + they should be written out are not clear to me. */ + return true; + } + + bfd_h_put_8 (output_bfd, type, outsym.e_type); + bfd_h_put_8 (output_bfd, 0, outsym.e_other); + bfd_h_put_16 (output_bfd, 0, outsym.e_desc); + PUT_WORD (output_bfd, + add_to_stringtab (output_bfd, h->root.root.string, &finfo->strtab), + outsym.e_strx); + PUT_WORD (output_bfd, val, outsym.e_value); + + if (bfd_seek (output_bfd, finfo->symoff, SEEK_SET) != 0 + || bfd_write ((PTR) &outsym, (bfd_size_type) EXTERNAL_NLIST_SIZE, + (bfd_size_type) 1, output_bfd) != EXTERNAL_NLIST_SIZE) + { + /* FIXME: No way to handle errors. */ + abort (); + } + + finfo->symoff += EXTERNAL_NLIST_SIZE; + h->indx = obj_aout_external_sym_count (output_bfd); + ++obj_aout_external_sym_count (output_bfd); + + return true; +} + +/* Link an a.out section into the output file. */ + +static boolean +aout_link_input_section (finfo, input_bfd, input_section, reloff_ptr, + rel_size, symbol_map) + struct aout_final_link_info *finfo; + bfd *input_bfd; + asection *input_section; + file_ptr *reloff_ptr; + bfd_size_type rel_size; + int *symbol_map; +{ + bfd_size_type input_size; + bfd_byte *contents; + PTR relocs; + + /* Get the section contents. */ + input_size = bfd_section_size (input_bfd, input_section); + contents = (bfd_byte *) alloca (input_size); + if (! bfd_get_section_contents (input_bfd, input_section, contents, + (file_ptr) 0, input_size)) + return false; + + /* Read in the relocs. */ + relocs = (PTR) alloca (rel_size); + if (bfd_seek (input_bfd, input_section->rel_filepos, SEEK_SET) != 0 + || bfd_read (relocs, 1, rel_size, input_bfd) != rel_size) + return false; + + /* Relocate the section contents. */ + if (obj_reloc_entry_size (input_bfd) == RELOC_STD_SIZE) + { + if (! aout_link_input_section_std (finfo, input_bfd, input_section, + (struct reloc_std_external *) relocs, + rel_size, contents, symbol_map)) + return false; + } + else + { + if (! aout_link_input_section_ext (finfo, input_bfd, input_section, + (struct reloc_ext_external *) relocs, + rel_size, contents, symbol_map)) + return false; + } + + /* Write out the section contents. */ + if (! bfd_set_section_contents (finfo->output_bfd, + input_section->output_section, + contents, input_section->output_offset, + input_size)) + return false; + + /* If we are producing relocateable output, the relocs were + modified, and we now write them out. */ + if (finfo->info->relocateable) + { + if (bfd_seek (finfo->output_bfd, *reloff_ptr, SEEK_SET) != 0) + return false; + if (bfd_write (relocs, (bfd_size_type) 1, rel_size, finfo->output_bfd) + != rel_size) + return false; + *reloff_ptr += rel_size; + + /* Assert that the relocs have not run into the symbols, and + that if these are the text relocs they have not run into the + data relocs. */ + BFD_ASSERT (*reloff_ptr <= obj_sym_filepos (finfo->output_bfd) + && (reloff_ptr != &finfo->treloff + || (*reloff_ptr + <= obj_datasec (finfo->output_bfd)->rel_filepos))); + } + + return true; +} + +/* Get the section corresponding to a reloc index. */ + +static INLINE asection * +aout_reloc_index_to_section (abfd, indx) + bfd *abfd; + int indx; +{ + switch (indx & N_TYPE) + { + case N_TEXT: + return obj_textsec (abfd); + case N_DATA: + return obj_datasec (abfd); + case N_BSS: + return obj_bsssec (abfd); + case N_ABS: + return &bfd_abs_section; + default: + abort (); + } +} + +/* Relocate an a.out section using standard a.out relocs. */ + +static boolean +aout_link_input_section_std (finfo, input_bfd, input_section, relocs, + rel_size, contents, symbol_map) + struct aout_final_link_info *finfo; + bfd *input_bfd; + asection *input_section; + struct reloc_std_external *relocs; + bfd_size_type rel_size; + bfd_byte *contents; + int *symbol_map; +{ + bfd *output_bfd; + boolean relocateable; + struct external_nlist *syms; + char *strings; + struct aout_link_hash_entry **sym_hashes; + bfd_size_type reloc_count; + register struct reloc_std_external *rel; + struct reloc_std_external *rel_end; + + output_bfd = finfo->output_bfd; + + BFD_ASSERT (obj_reloc_entry_size (input_bfd) == RELOC_STD_SIZE); + BFD_ASSERT (input_bfd->xvec->header_byteorder_big_p + == output_bfd->xvec->header_byteorder_big_p); + + relocateable = finfo->info->relocateable; + syms = obj_aout_external_syms (input_bfd); + strings = obj_aout_external_strings (input_bfd); + sym_hashes = obj_aout_sym_hashes (input_bfd); + + reloc_count = rel_size / RELOC_STD_SIZE; + rel = relocs; + rel_end = rel + reloc_count; + for (; rel < rel_end; rel++) + { + bfd_vma r_addr; + int r_index; + int r_extern; + int r_pcrel; + int r_baserel; + int r_jmptable; + int r_relative; + int r_length; + int howto_idx; + bfd_vma relocation; + bfd_reloc_status_type r; + + r_addr = GET_SWORD (input_bfd, rel->r_address); + + if (input_bfd->xvec->header_byteorder_big_p) + { + r_index = ((rel->r_index[0] << 16) + | (rel->r_index[1] << 8) + | rel->r_index[2]); + r_extern = (0 != (rel->r_type[0] & RELOC_STD_BITS_EXTERN_BIG)); + r_pcrel = (0 != (rel->r_type[0] & RELOC_STD_BITS_PCREL_BIG)); + r_baserel = (0 != (rel->r_type[0] & RELOC_STD_BITS_BASEREL_BIG)); + r_jmptable= (0 != (rel->r_type[0] & RELOC_STD_BITS_JMPTABLE_BIG)); + r_relative= (0 != (rel->r_type[0] & RELOC_STD_BITS_RELATIVE_BIG)); + r_length = ((rel->r_type[0] & RELOC_STD_BITS_LENGTH_BIG) + >> RELOC_STD_BITS_LENGTH_SH_BIG); + } + else + { + r_index = ((rel->r_index[2] << 16) + | (rel->r_index[1] << 8) + | rel->r_index[0]); + r_extern = (0 != (rel->r_type[0] & RELOC_STD_BITS_EXTERN_LITTLE)); + r_pcrel = (0 != (rel->r_type[0] & RELOC_STD_BITS_PCREL_LITTLE)); + r_baserel = (0 != (rel->r_type[0] & RELOC_STD_BITS_BASEREL_LITTLE)); + r_jmptable= (0 != (rel->r_type[0] & RELOC_STD_BITS_JMPTABLE_LITTLE)); + r_relative= (0 != (rel->r_type[0] & RELOC_STD_BITS_RELATIVE_LITTLE)); + r_length = ((rel->r_type[0] & RELOC_STD_BITS_LENGTH_LITTLE) + >> RELOC_STD_BITS_LENGTH_SH_LITTLE); + } + + howto_idx = r_length + 4 * r_pcrel + 8 * r_baserel; + BFD_ASSERT (howto_idx < TABLE_SIZE (howto_table_std)); + BFD_ASSERT (r_jmptable == 0); + BFD_ASSERT (r_relative == 0); + + if (relocateable) + { + /* We are generating a relocateable output file, and must + modify the reloc accordingly. */ + if (r_extern) + { + struct aout_link_hash_entry *h; + + /* If we know the symbol this relocation is against, + convert it into a relocation against a section. This + is what the native linker does. */ + h = sym_hashes[r_index]; + if (h != (struct aout_link_hash_entry *) NULL + && h->root.type == bfd_link_hash_defined) + { + asection *output_section; + + /* Change the r_extern value. */ + if (output_bfd->xvec->header_byteorder_big_p) + rel->r_type[0] &=~ RELOC_STD_BITS_EXTERN_BIG; + else + rel->r_type[0] &=~ RELOC_STD_BITS_EXTERN_LITTLE; + + /* Compute a new r_index. */ + output_section = h->root.u.def.section->output_section; + if (output_section == obj_textsec (output_bfd)) + r_index = N_TEXT; + else if (output_section == obj_datasec (output_bfd)) + r_index = N_DATA; + else if (output_section == obj_bsssec (output_bfd)) + r_index = N_BSS; + else + r_index = N_ABS; + + /* Add the symbol value and the section VMA to the + addend stored in the contents. */ + relocation = (h->root.u.def.value + + output_section->vma + + h->root.u.def.section->output_offset); + } + else + { + /* We must change r_index according to the symbol + map. */ + r_index = symbol_map[r_index]; + + if (r_index == -1) + { + const char *name; + + name = strings + GET_WORD (input_bfd, + syms[r_index].e_strx); + if (! ((*finfo->info->callbacks->unattached_reloc) + (finfo->info, name, input_bfd, input_section, + r_addr))) + return false; + r_index = 0; + } + + relocation = 0; + } + + /* Write out the new r_index value. */ + if (output_bfd->xvec->header_byteorder_big_p) + { + rel->r_index[0] = r_index >> 16; + rel->r_index[1] = r_index >> 8; + rel->r_index[2] = r_index; + } + else + { + rel->r_index[2] = r_index >> 16; + rel->r_index[1] = r_index >> 8; + rel->r_index[0] = r_index; + } + } + else + { + asection *section; + + /* This is a relocation against a section. We must + adjust by the amount that the section moved. */ + section = aout_reloc_index_to_section (input_bfd, r_index); + relocation = (section->output_section->vma + + section->output_offset + - section->vma); + } + + /* Change the address of the relocation. */ + PUT_WORD (output_bfd, + r_addr + input_section->output_offset, + rel->r_address); + + /* Adjust a PC relative relocation by removing the reference + to the original address in the section and then including + the reference to the new address. */ + if (r_pcrel) + { + relocation += input_section->vma; + relocation -= (input_section->output_section->vma + + input_section->output_offset); + } + + if (relocation == 0) + r = bfd_reloc_ok; + else + r = _bfd_relocate_contents (howto_table_std + howto_idx, + input_bfd, relocation, + contents + r_addr); + } + else + { + /* We are generating an executable, and must do a full + relocation. */ + if (r_extern) + { + struct aout_link_hash_entry *h; + + h = sym_hashes[r_index]; + if (h != (struct aout_link_hash_entry *) NULL + && h->root.type == bfd_link_hash_defined) + { + relocation = (h->root.u.def.value + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset); + } + else + { + const char *name; + + name = strings + GET_WORD (input_bfd, syms[r_index].e_strx); + if (! ((*finfo->info->callbacks->undefined_symbol) + (finfo->info, name, input_bfd, input_section, + r_addr))) + return false; + relocation = 0; + } + } + else + { + asection *section; + + section = aout_reloc_index_to_section (input_bfd, r_index); + relocation = (section->output_section->vma + + section->output_offset + - section->vma); + } + + + r = _bfd_final_link_relocate (howto_table_std + howto_idx, + input_bfd, input_section, + contents, r_addr, relocation, + (bfd_vma) 0); + } + + if (r != bfd_reloc_ok) + { + switch (r) + { + default: + case bfd_reloc_outofrange: + abort (); + case bfd_reloc_overflow: + if (! ((*finfo->info->callbacks->reloc_overflow) + (finfo->info, input_bfd, input_section, r_addr))) + return false; + break; + } + } + } + + return true; +} + +/* Relocate an a.out section using extended a.out relocs. */ + +static boolean +aout_link_input_section_ext (finfo, input_bfd, input_section, relocs, + rel_size, contents, symbol_map) + struct aout_final_link_info *finfo; + bfd *input_bfd; + asection *input_section; + struct reloc_ext_external *relocs; + bfd_size_type rel_size; + bfd_byte *contents; + int *symbol_map; +{ + bfd *output_bfd; + boolean relocateable; + struct external_nlist *syms; + char *strings; + struct aout_link_hash_entry **sym_hashes; + bfd_size_type reloc_count; + register struct reloc_ext_external *rel; + struct reloc_ext_external *rel_end; + + output_bfd = finfo->output_bfd; + + BFD_ASSERT (obj_reloc_entry_size (input_bfd) == RELOC_EXT_SIZE); + BFD_ASSERT (input_bfd->xvec->header_byteorder_big_p + == output_bfd->xvec->header_byteorder_big_p); + + relocateable = finfo->info->relocateable; + syms = obj_aout_external_syms (input_bfd); + strings = obj_aout_external_strings (input_bfd); + sym_hashes = obj_aout_sym_hashes (input_bfd); + + reloc_count = rel_size / RELOC_EXT_SIZE; + rel = relocs; + rel_end = rel + reloc_count; + for (; rel < rel_end; rel++) + { + bfd_vma r_addr; + int r_index; + int r_extern; + int r_type; + bfd_vma r_addend; + bfd_vma relocation; + + r_addr = GET_SWORD (input_bfd, rel->r_address); + + if (input_bfd->xvec->header_byteorder_big_p) + { + r_index = ((rel->r_index[0] << 16) + | (rel->r_index[1] << 8) + | rel->r_index[2]); + r_extern = (0 != (rel->r_type[0] & RELOC_EXT_BITS_EXTERN_BIG)); + r_type = ((rel->r_type[0] & RELOC_EXT_BITS_TYPE_BIG) + >> RELOC_EXT_BITS_TYPE_SH_BIG); + } + else + { + r_index = ((rel->r_index[2] << 16) + | (rel->r_index[1] << 8) + | rel->r_index[0]); + r_extern = (0 != (rel->r_type[0] & RELOC_EXT_BITS_EXTERN_LITTLE)); + r_type = ((rel->r_type[0] & RELOC_EXT_BITS_TYPE_LITTLE) + >> RELOC_EXT_BITS_TYPE_SH_LITTLE); + } + + r_addend = GET_SWORD (input_bfd, rel->r_addend); + + if (relocateable) + { + /* We are generating a relocateable output file, and must + modify the reloc accordingly. */ + if (r_extern) + { + struct aout_link_hash_entry *h; + + /* If we know the symbol this relocation is against, + convert it into a relocation against a section. This + is what the native linker does. */ + h = sym_hashes[r_index]; + if (h != (struct aout_link_hash_entry *) NULL + && h->root.type == bfd_link_hash_defined) + { + asection *output_section; + + /* Change the r_extern value. */ + if (output_bfd->xvec->header_byteorder_big_p) + rel->r_type[0] &=~ RELOC_EXT_BITS_EXTERN_BIG; + else + rel->r_type[0] &=~ RELOC_EXT_BITS_EXTERN_LITTLE; + + /* Compute a new r_index. */ + output_section = h->root.u.def.section->output_section; + if (output_section == obj_textsec (output_bfd)) + r_index = N_TEXT; + else if (output_section == obj_datasec (output_bfd)) + r_index = N_DATA; + else if (output_section == obj_bsssec (output_bfd)) + r_index = N_BSS; + else + r_index = N_ABS; + + /* Add the symbol value and the section VMA to the + addend. */ + relocation = (h->root.u.def.value + + output_section->vma + + h->root.u.def.section->output_offset); + } + else + { + /* We must change r_index according to the symbol + map. */ + r_index = symbol_map[r_index]; + + if (r_index == -1) + { + const char *name; + + name = (strings + + GET_WORD (input_bfd, syms[r_index].e_strx)); + if (! ((*finfo->info->callbacks->unattached_reloc) + (finfo->info, name, input_bfd, input_section, + r_addr))) + return false; + r_index = 0; + } + + relocation = 0; + } + + /* Write out the new r_index value. */ + if (output_bfd->xvec->header_byteorder_big_p) + { + rel->r_index[0] = r_index >> 16; + rel->r_index[1] = r_index >> 8; + rel->r_index[2] = r_index; + } + else + { + rel->r_index[2] = r_index >> 16; + rel->r_index[1] = r_index >> 8; + rel->r_index[0] = r_index; + } + } + else + { + asection *section; + + /* This is a relocation against a section. We must + adjust by the amount that the section moved. */ + section = aout_reloc_index_to_section (input_bfd, r_index); + relocation = (section->output_section->vma + + section->output_offset + - section->vma); + } + + /* Adjust a PC relative relocation by removing the reference + to the original address in the section and then including + the reference to the new address. */ + if (howto_table_ext[r_type].pc_relative + && ! howto_table_ext[r_type].pcrel_offset) + { + relocation += input_section->vma; + relocation -= (input_section->output_section->vma + + input_section->output_offset); + } + + /* Change the addend if necessary. */ + if (relocation != 0) + PUT_WORD (output_bfd, r_addend + relocation, rel->r_addend); + + /* Change the address of the relocation. */ + PUT_WORD (output_bfd, + r_addr + input_section->output_offset, + rel->r_address); + } + else + { + bfd_reloc_status_type r; + + /* We are generating an executable, and must do a full + relocation. */ + if (r_extern) + { + struct aout_link_hash_entry *h; + + h = sym_hashes[r_index]; + if (h != (struct aout_link_hash_entry *) NULL + && h->root.type == bfd_link_hash_defined) + { + relocation = (h->root.u.def.value + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset); + } + else + { + const char *name; + + name = strings + GET_WORD (input_bfd, syms[r_index].e_strx); + if (! ((*finfo->info->callbacks->undefined_symbol) + (finfo->info, name, input_bfd, input_section, + r_addr))) + return false; + relocation = 0; + } + } + else + { + asection *section; + + section = aout_reloc_index_to_section (input_bfd, r_index); + relocation = (section->output_section->vma + + section->output_offset + - section->vma); + } + + BFD_ASSERT (r_type >= 0 + && r_type < TABLE_SIZE (howto_table_ext)); + + r = _bfd_final_link_relocate (howto_table_ext + r_type, + input_bfd, input_section, + contents, r_addr, relocation, + r_addend); + if (r != bfd_reloc_ok) + { + switch (r) + { + default: + case bfd_reloc_outofrange: + abort (); + case bfd_reloc_overflow: + if (! ((*finfo->info->callbacks->reloc_overflow) + (finfo->info, input_bfd, input_section, r_addr))) + return false; + break; + } + } + } + } + + return true; +} |