diff options
Diffstat (limited to 'contrib/binutils/bfd/cofflink.c')
-rw-r--r-- | contrib/binutils/bfd/cofflink.c | 241 |
1 files changed, 210 insertions, 31 deletions
diff --git a/contrib/binutils/bfd/cofflink.c b/contrib/binutils/bfd/cofflink.c index f824158..7b2b830 100644 --- a/contrib/binutils/bfd/cofflink.c +++ b/contrib/binutils/bfd/cofflink.c @@ -38,6 +38,7 @@ static char *dores_com PARAMS ((char *, bfd *, int)); static char *get_name PARAMS ((char *, char **)); static int process_embedded_commands PARAMS ((bfd *, struct bfd_link_info *, bfd *)); +static void mark_relocs PARAMS ((struct coff_final_link_info *, bfd *)); /* Create an entry in a COFF linker hash table. */ @@ -243,6 +244,9 @@ coff_link_check_ar_symbols (abfd, info, pneeded) bfd_coff_swap_sym_in (abfd, (PTR) esym, (PTR) &sym); if ((sym.n_sclass == C_EXT +#ifdef C_SYSTEM + || sym.n_sclass == C_SYSTEM +#endif || (sym_is_global && (*sym_is_global) (abfd, &sym))) && (sym.n_scnum != 0 || sym.n_value != 0)) { @@ -333,6 +337,9 @@ coff_link_add_symbols (abfd, info) bfd_coff_swap_sym_in (abfd, (PTR) esym, (PTR) &sym); if (sym.n_sclass == C_EXT +#ifdef C_SYSTEM + || sym.n_sclass == C_SYSTEM +#endif || (sym_is_global && (*sym_is_global) (abfd, &sym))) { const char *name; @@ -373,7 +380,8 @@ coff_link_add_symbols (abfd, info) { flags = BSF_EXPORT | BSF_GLOBAL; section = coff_section_from_bfd_index (abfd, sym.n_scnum); - value -= section->vma; + if (! obj_pe (abfd)) + value -= section->vma; } if (! (bfd_coff_link_add_one_symbol @@ -530,6 +538,7 @@ _bfd_coff_final_link (abfd, info) finfo.contents = NULL; finfo.external_relocs = NULL; finfo.internal_relocs = NULL; + finfo.global_to_static = false; debug_merge_allocated = false; coff_data (abfd)->link_info = info; @@ -759,7 +768,7 @@ _bfd_coff_final_link (abfd, info) == bfd_target_coff_flavour)) { sub = p->u.indirect.section->owner; - if (! sub->output_has_begun) + if (! bfd_coff_link_output_has_begun (sub)) { if (! _bfd_coff_link_input_bfd (&finfo, sub)) goto error_return; @@ -780,6 +789,9 @@ _bfd_coff_final_link (abfd, info) } } + if (! bfd_coff_final_link_postscript (abfd, & finfo)) + goto error_return; + /* Free up the buffers used by _bfd_coff_link_input_bfd. */ coff_debug_merge_hash_table_free (&finfo.debug_merge); @@ -838,6 +850,18 @@ _bfd_coff_final_link (abfd, info) return false; } + /* If doing task linking (ld --task-link) then make a pass through the + global symbols, writing out any that are defined, and making them + static. */ + if (info->task_link) + { + finfo.failed = false; + coff_link_hash_traverse (coff_hash_table (info), _bfd_coff_write_task_globals, + (PTR) &finfo); + if (finfo.failed) + goto error_return; + } + /* Write out the global symbols. */ finfo.failed = false; coff_link_hash_traverse (coff_hash_table (info), _bfd_coff_write_global_sym, @@ -1117,6 +1141,60 @@ process_embedded_commands (output_bfd, info, abfd) return 1; } +/* Place a marker against all symbols which are used by relocations. + This marker can be picked up by the 'do we skip this symbol ?' + loop in _bfd_coff_link_input_bfd() and used to prevent skipping + that symbol. + */ + +static void +mark_relocs (finfo, input_bfd) + struct coff_final_link_info * finfo; + bfd * input_bfd; +{ + asection * a; + + if ((bfd_get_file_flags (input_bfd) & HAS_SYMS) == 0) + return; + + for (a = input_bfd->sections; a != (asection *) NULL; a = a->next) + { + struct internal_reloc * internal_relocs; + struct internal_reloc * irel; + struct internal_reloc * irelend; + + + if ((a->flags & SEC_RELOC) == 0 || a->reloc_count < 1) + continue; + + /* Read in the relocs. */ + internal_relocs = _bfd_coff_read_internal_relocs + (input_bfd, a, false, + finfo->external_relocs, + finfo->info->relocateable, + (finfo->info->relocateable + ? (finfo->section_info[ a->output_section->target_index ].relocs + a->output_section->reloc_count) + : finfo->internal_relocs) + ); + + if (internal_relocs == NULL) + continue; + + irel = internal_relocs; + irelend = irel + a->reloc_count; + + /* Place a mark in the sym_indices array (whose entries have + been initialised to 0) for all of the symbols that are used + in the relocation table. This will then be picked up in the + skip/don't pass */ + + for (; irel < irelend; irel++) + { + finfo->sym_indices[ irel->r_symndx ] = -1; + } + } +} + /* Link an input file into the linker output file. This function handles all the sections and relocations of the input file at once. */ @@ -1190,11 +1268,25 @@ _bfd_coff_link_input_bfd (finfo, input_bfd) return false; } + /* If we are going to perform relocations and also strip/discard some symbols + then we must make sure that we do not strip/discard those symbols that are + going to be involved in the relocations */ + if (( finfo->info->strip != strip_none + || finfo->info->discard != discard_none) + && finfo->info->relocateable) + { + /* mark the symbol array as 'not-used' */ + memset (indexp, 0, obj_raw_syment_count (input_bfd) * sizeof * indexp); + + mark_relocs (finfo, input_bfd); + } + while (esym < esym_end) { struct internal_syment isym; boolean skip; boolean global; + boolean dont_skip_symbol; int add; bfd_coff_swap_sym_in (input_bfd, (PTR) esym, (PTR) isymp); @@ -1215,6 +1307,15 @@ _bfd_coff_link_input_bfd (finfo, input_bfd) *secpp = bfd_com_section_ptr; } + /* Extract the flag indicating if this symbol is used by a + relocation. */ + if ((finfo->info->strip != strip_none + || finfo->info->discard != discard_none) + && finfo->info->relocateable) + dont_skip_symbol = *indexp; + else + dont_skip_symbol = false; + *indexp = -1; skip = false; @@ -1222,12 +1323,15 @@ _bfd_coff_link_input_bfd (finfo, input_bfd) add = 1 + isym.n_numaux; /* If we are stripping all symbols, we want to skip this one. */ - if (finfo->info->strip == strip_all) + if (finfo->info->strip == strip_all && ! dont_skip_symbol) skip = true; if (! skip) { if (isym.n_sclass == C_EXT +#ifdef C_SYSTEM + || isym.n_sclass == C_SYSTEM +#endif || (sym_is_global && (*sym_is_global) (input_bfd, &isym))) { /* This is a global symbol. Global symbols come at the @@ -1242,16 +1346,29 @@ _bfd_coff_link_input_bfd (finfo, input_bfd) { /* This is a local symbol. Skip it if we are discarding local symbols. */ - if (finfo->info->discard == discard_all) + if (finfo->info->discard == discard_all && ! dont_skip_symbol) skip = true; } } /* If we stripping debugging symbols, and this is a debugging - symbol, then skip it. */ + symbol, then skip it. FIXME: gas sets the section to N_ABS + for some types of debugging symbols; I don't know if this is + a bug or not. In any case, we handle it here. */ if (! skip && finfo->info->strip == strip_debugger - && isym.n_scnum == N_DEBUG) + && ! dont_skip_symbol + && (isym.n_scnum == N_DEBUG + || (isym.n_scnum == N_ABS + && (isym.n_sclass == C_AUTO + || isym.n_sclass == C_REG + || isym.n_sclass == C_MOS + || isym.n_sclass == C_MOE + || isym.n_sclass == C_MOU + || isym.n_sclass == C_ARG + || isym.n_sclass == C_REGPARM + || isym.n_sclass == C_FIELD + || isym.n_sclass == C_EOS)))) skip = true; /* If some symbols are stripped based on the name, work out the @@ -1267,12 +1384,13 @@ _bfd_coff_link_input_bfd (finfo, input_bfd) if (name == NULL) return false; - if ((finfo->info->strip == strip_some - && (bfd_hash_lookup (finfo->info->keep_hash, name, false, + if (! dont_skip_symbol + && ((finfo->info->strip == strip_some + && (bfd_hash_lookup (finfo->info->keep_hash, name, false, false) == NULL)) - || (! global - && finfo->info->discard == discard_l - && bfd_is_local_label_name (input_bfd, name))) + || (! global + && finfo->info->discard == discard_l + && bfd_is_local_label_name (input_bfd, name)))) skip = true; } @@ -1472,9 +1590,11 @@ _bfd_coff_link_input_bfd (finfo, input_bfd) 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); + isym.n_value += (*secpp)->output_offset; + if (! obj_pe (input_bfd)) + isym.n_value -= (*secpp)->vma; + if (! obj_pe (finfo->output_bfd)) + isym.n_value += (*secpp)->output_section->vma; } /* The value of a C_FILE symbol is the symbol index of the @@ -1525,6 +1645,12 @@ _bfd_coff_link_input_bfd (finfo, input_bfd) finfo->last_file = isym; } + /* If doing task linking, convert normal global function symbols to + static functions. */ + + if (finfo->info->task_link && isym.n_sclass == C_EXT) + isym.n_sclass = C_STAT; + /* Output the symbol. */ bfd_coff_swap_sym_out (output_bfd, (PTR) &isym, (PTR) outsym); @@ -2066,9 +2192,10 @@ _bfd_coff_link_input_bfd (finfo, input_bfd) 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. */ + stripping. This should have been handled + by the 'dont_skip_symbol' code in the while + loop at the top of this function. */ + is = finfo->internal_syms + irel->r_symndx; name = (_bfd_coff_internal_syment_name @@ -2166,8 +2293,9 @@ _bfd_coff_write_global_sym (h, data) else isym.n_scnum = sec->target_index; isym.n_value = (h->root.u.def.value - + sec->vma + h->root.u.def.section->output_offset); + if (! obj_pe (finfo->output_bfd)) + isym.n_value += sec->vma; } break; @@ -2209,6 +2337,18 @@ _bfd_coff_write_global_sym (h, data) if (isym.n_sclass == C_NULL) isym.n_sclass = C_EXT; + /* If doing task linking and this is the pass where we convert defined globals to + statics, then do that conversion now. If the symbol is not being converted, + just ignore it and it will be output during a later pass. */ + if (finfo->global_to_static) + { + if (isym.n_sclass != C_EXT) + { + return true; + } + isym.n_sclass = C_STAT; + } + isym.n_numaux = h->numaux; bfd_coff_swap_sym_out (output_bfd, (PTR) &isym, (PTR) finfo->outsyms); @@ -2247,6 +2387,37 @@ _bfd_coff_write_global_sym (h, data) return true; } +/* Write out task global symbols, converting them to statics. Called + via coff_link_hash_traverse. Calls bfd_coff_write_global_sym to do + the dirty work, if the symbol we are processing needs conversion. */ + +boolean +_bfd_coff_write_task_globals (h, data) + struct coff_link_hash_entry *h; + PTR data; +{ + struct coff_final_link_info *finfo = (struct coff_final_link_info *) data; + boolean rtnval = true; + boolean save_global_to_static; + + if (h->indx < 0) + { + switch (h->root.type) + { + case bfd_link_hash_defined: + case bfd_link_hash_defweak: + save_global_to_static = finfo->global_to_static; + finfo->global_to_static = true; + rtnval = _bfd_coff_write_global_sym (h, data); + finfo->global_to_static = save_global_to_static; + break; + default: + break; + } + } + return (rtnval); +} + /* Handle a link order which is supposed to generate a reloc. */ boolean @@ -2465,8 +2636,9 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd, sec = sections[symndx]; val = (sec->output_section->vma + sec->output_offset - + sym->n_value - - sec->vma); + + sym->n_value); + if (! obj_pe (input_bfd)) + val -= sec->vma; } } else @@ -2494,19 +2666,26 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd, if (info->base_file) { /* Emit a reloc if the backend thinks it needs it. */ - if (sym && pe_data(output_bfd)->in_reloc_p(output_bfd, howto)) + if (sym && pe_data (output_bfd)->in_reloc_p (output_bfd, howto)) { - /* relocation to a symbol in a section which - isn't absolute - we output the address here - to a file */ - bfd_vma addr = rel->r_vaddr - - input_section->vma - + input_section->output_offset - + input_section->output_section->vma; - if (coff_data(output_bfd)->pe) + /* Relocation to a symbol in a section which isn't + absolute. We output the address here to a file. + This file is then read by dlltool when generating the + reloc section. Note that the base file is not + portable between systems. We write out a long here, + and dlltool reads in a long. */ + long addr = (rel->r_vaddr + - input_section->vma + + input_section->output_offset + + input_section->output_section->vma); + if (coff_data (output_bfd)->pe) addr -= pe_data(output_bfd)->pe_opthdr.ImageBase; - /* FIXME: Shouldn't 4 be sizeof (addr)? */ - fwrite (&addr, 1,4, (FILE *) info->base_file); + if (fwrite (&addr, 1, sizeof (long), (FILE *) info->base_file) + != sizeof (long)) + { + bfd_set_error (bfd_error_system_call); + return false; + } } } |