diff options
| author | Jordan K. Hubbard <jkh@FreeBSD.org> | 1994-12-30 23:33:10 +0000 |
|---|---|---|
| committer | Jordan K. Hubbard <jkh@FreeBSD.org> | 1994-12-30 23:33:10 +0000 |
| commit | 3c8ea7601ed05f427ea0108e2ec73d04597e1efd (patch) | |
| tree | 37db35d514c69a356634c1a2cbaecf365cbb68b2 /gnu/usr.bin/gdb/bfd | |
| parent | 01db5e69c1b8ed8f657fa883a9416c69b2e5425a (diff) | |
Notes
Diffstat (limited to 'gnu/usr.bin/gdb/bfd')
| -rw-r--r-- | gnu/usr.bin/gdb/bfd/coff-i386.c | 354 | ||||
| -rw-r--r-- | gnu/usr.bin/gdb/bfd/coffcode.h | 2544 | ||||
| -rw-r--r-- | gnu/usr.bin/gdb/bfd/coffswap.h | 741 | ||||
| -rw-r--r-- | gnu/usr.bin/gdb/bfd/ecofflink.c | 1616 | ||||
| -rw-r--r-- | gnu/usr.bin/gdb/bfd/elf32-i386.c | 931 | ||||
| -rw-r--r-- | gnu/usr.bin/gdb/bfd/elf32-target.h | 358 | ||||
| -rw-r--r-- | gnu/usr.bin/gdb/bfd/elf32.c | 23 | ||||
| -rw-r--r-- | gnu/usr.bin/gdb/bfd/elfcode.h | 6351 | ||||
| -rw-r--r-- | gnu/usr.bin/gdb/bfd/genlink.h | 106 | ||||
| -rw-r--r-- | gnu/usr.bin/gdb/bfd/hash.c | 470 | ||||
| -rw-r--r-- | gnu/usr.bin/gdb/bfd/i386aout.c | 68 | ||||
| -rw-r--r-- | gnu/usr.bin/gdb/bfd/linker.c | 2442 |
12 files changed, 16004 insertions, 0 deletions
diff --git a/gnu/usr.bin/gdb/bfd/coff-i386.c b/gnu/usr.bin/gdb/bfd/coff-i386.c new file mode 100644 index 000000000000..aaaedca74af8 --- /dev/null +++ b/gnu/usr.bin/gdb/bfd/coff-i386.c @@ -0,0 +1,354 @@ +/* BFD back-end for Intel 386 COFF files. + Copyright 1990, 1991, 1992, 1993, 1994 Free Software Foundation, Inc. + Written by 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. */ + +#include "bfd.h" +#include "sysdep.h" +#include "libbfd.h" +#include "obstack.h" +#include "coff/i386.h" +#include "coff/internal.h" +#include "libcoff.h" + +static bfd_reloc_status_type coff_i386_reloc PARAMS ((bfd *abfd, + arelent *reloc_entry, + asymbol *symbol, + PTR data, + asection *input_section, + bfd *output_bfd, + char **error_message)); + +/* The page size is a guess based on ELF. */ +#define COFF_PAGE_SIZE 0x1000 + +/* For some reason when using i386 COFF the value stored in the .text + section for a reference to a common symbol is the value itself plus + any desired offset. Ian Taylor, Cygnus Support. */ + +/* If we are producing relocateable output, we need to do some + adjustments to the object file that are not done by the + bfd_perform_relocation function. This function is called by every + reloc type to make any required adjustments. */ + +static bfd_reloc_status_type +coff_i386_reloc (abfd, reloc_entry, symbol, data, input_section, output_bfd, + error_message) + bfd *abfd; + arelent *reloc_entry; + asymbol *symbol; + PTR data; + asection *input_section; + bfd *output_bfd; + char **error_message; +{ + symvalue diff; + + if (output_bfd == (bfd *) NULL) + return bfd_reloc_continue; + + if (bfd_is_com_section (symbol->section)) + { + /* We are relocating a common symbol. The current value in the + object file is ORIG + OFFSET, where ORIG is the value of the + common symbol as seen by the object file when it was compiled + (this may be zero if the symbol was undefined) and OFFSET is + the offset into the common symbol (normally zero, but may be + non-zero when referring to a field in a common structure). + ORIG is the negative of reloc_entry->addend, which is set by + the CALC_ADDEND macro below. We want to replace the value in + the object file with NEW + OFFSET, where NEW is the value of + the common symbol which we are going to put in the final + object file. NEW is symbol->value. */ + diff = symbol->value + reloc_entry->addend; + } + else + { + /* For some reason bfd_perform_relocation always effectively + ignores the addend for a COFF target when producing + relocateable output. This seems to be always wrong for 386 + COFF, so we handle the addend here instead. */ + diff = reloc_entry->addend; + } + +#define DOIT(x) \ + x = ((x & ~howto->dst_mask) | (((x & howto->src_mask) + diff) & howto->dst_mask)) + + if (diff != 0) + { + const reloc_howto_type *howto = reloc_entry->howto; + unsigned char *addr = (unsigned char *) data + reloc_entry->address; + + switch (howto->size) + { + case 0: + { + char x = bfd_get_8 (abfd, addr); + DOIT (x); + bfd_put_8 (abfd, x, addr); + } + break; + + case 1: + { + short x = bfd_get_16 (abfd, addr); + DOIT (x); + bfd_put_16 (abfd, x, addr); + } + break; + + case 2: + { + long x = bfd_get_32 (abfd, addr); + DOIT (x); + bfd_put_32 (abfd, x, addr); + } + break; + + default: + abort (); + } + } + + /* Now let bfd_perform_relocation finish everything up. */ + return bfd_reloc_continue; +} + +static reloc_howto_type howto_table[] = +{ + {0}, + {1}, + {2}, + {3}, + {4}, + {5}, + HOWTO (R_DIR32, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + coff_i386_reloc, /* special_function */ + "dir32", /* name */ + true, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + false), /* pcrel_offset */ + {7}, + {010}, + {011}, + {012}, + {013}, + {014}, + {015}, + {016}, + HOWTO (R_RELBYTE, /* type */ + 0, /* rightshift */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 8, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + coff_i386_reloc, /* special_function */ + "8", /* name */ + true, /* partial_inplace */ + 0x000000ff, /* src_mask */ + 0x000000ff, /* dst_mask */ + false), /* pcrel_offset */ + HOWTO (R_RELWORD, /* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + coff_i386_reloc, /* special_function */ + "16", /* name */ + true, /* partial_inplace */ + 0x0000ffff, /* src_mask */ + 0x0000ffff, /* dst_mask */ + false), /* pcrel_offset */ + HOWTO (R_RELLONG, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + coff_i386_reloc, /* special_function */ + "32", /* name */ + true, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + false), /* pcrel_offset */ + HOWTO (R_PCRBYTE, /* type */ + 0, /* rightshift */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 8, /* bitsize */ + true, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + coff_i386_reloc, /* special_function */ + "DISP8", /* name */ + true, /* partial_inplace */ + 0x000000ff, /* src_mask */ + 0x000000ff, /* dst_mask */ + false), /* pcrel_offset */ + HOWTO (R_PCRWORD, /* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + true, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + coff_i386_reloc, /* special_function */ + "DISP16", /* name */ + true, /* partial_inplace */ + 0x0000ffff, /* src_mask */ + 0x0000ffff, /* dst_mask */ + false), /* pcrel_offset */ + HOWTO (R_PCRLONG, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + true, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + coff_i386_reloc, /* special_function */ + "DISP32", /* name */ + true, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + false) /* pcrel_offset */ +}; + +/* Turn a howto into a reloc nunmber */ + +#define SELECT_RELOC(x,howto) { x.r_type = howto->type; } +#define BADMAG(x) I386BADMAG(x) +#define I386 1 /* Customize coffcode.h */ + +#define RTYPE2HOWTO(cache_ptr, dst) \ + cache_ptr->howto = howto_table + (dst)->r_type; + +/* On SCO Unix 3.2.2 the native assembler generates two .data + sections. We handle that by renaming the second one to .data2. It + does no harm to do this for any 386 COFF target. */ +#define TWO_DATA_SECS + +/* For 386 COFF a STYP_NOLOAD | STYP_BSS section is part of a shared + library. On some other COFF targets STYP_BSS is normally + STYP_NOLOAD. */ +#define BSS_NOLOAD_IS_SHARED_LIBRARY + +/* Compute the addend of a reloc. If the reloc is to a common symbol, + the object file contains the value of the common symbol. By the + time this is called, the linker may be using a different symbol + from a different object file with a different value. Therefore, we + hack wildly to locate the original symbol from this file so that we + can make the correct adjustment. This macro sets coffsym to the + symbol from the original file, and uses it to set the addend value + correctly. If this is not a common symbol, the usual addend + calculation is done, except that an additional tweak is needed for + PC relative relocs. + FIXME: This macro refers to symbols and asect; these are from the + calling function, not the macro arguments. */ + +#define CALC_ADDEND(abfd, ptr, reloc, cache_ptr) \ + { \ + coff_symbol_type *coffsym = (coff_symbol_type *) NULL; \ + if (ptr && bfd_asymbol_bfd (ptr) != abfd) \ + coffsym = (obj_symbols (abfd) \ + + (cache_ptr->sym_ptr_ptr - symbols)); \ + else if (ptr) \ + coffsym = coff_symbol_from (abfd, ptr); \ + if (coffsym != (coff_symbol_type *) NULL \ + && coffsym->native->u.syment.n_scnum == 0) \ + cache_ptr->addend = - coffsym->native->u.syment.n_value; \ + else if (ptr && bfd_asymbol_bfd (ptr) == abfd \ + && ptr->section != (asection *) NULL) \ + cache_ptr->addend = - (ptr->section->vma + ptr->value); \ + else \ + cache_ptr->addend = 0; \ + if (ptr && howto_table[reloc.r_type].pc_relative) \ + cache_ptr->addend += asect->vma; \ + } + +#include "coffcode.h" + +static const bfd_target * +i3coff_object_p(a) + bfd *a; +{ + return coff_object_p(a); +} + +const bfd_target +#ifdef TARGET_SYM + TARGET_SYM = +#else + i386coff_vec = +#endif +{ +#ifdef TARGET_NAME + TARGET_NAME, +#else + "coff-i386", /* name */ +#endif + bfd_target_coff_flavour, + false, /* data byte order is little */ + false, /* header byte order is little */ + + (HAS_RELOC | EXEC_P | /* object flags */ + HAS_LINENO | HAS_DEBUG | + HAS_SYMS | HAS_LOCALS | WP_TEXT | D_PAGED), + + (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC), /* section flags */ + 0, /* leading underscore */ + '/', /* ar_pad_char */ + 15, /* ar_max_namelen */ + + 2, /* minimum alignment power */ + bfd_getl64, bfd_getl_signed_64, bfd_putl64, + bfd_getl32, bfd_getl_signed_32, bfd_putl32, + bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* data */ + bfd_getl64, bfd_getl_signed_64, bfd_putl64, + bfd_getl32, bfd_getl_signed_32, bfd_putl32, + bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* hdrs */ + +/* Note that we allow an object file to be treated as a core file as well. */ + {_bfd_dummy_target, i3coff_object_p, /* bfd_check_format */ + bfd_generic_archive_p, i3coff_object_p}, + {bfd_false, coff_mkobject, _bfd_generic_mkarchive, /* bfd_set_format */ + bfd_false}, + {bfd_false, coff_write_object_contents, /* bfd_write_contents */ + _bfd_write_archive_contents, bfd_false}, + + BFD_JUMP_TABLE_GENERIC (coff), + BFD_JUMP_TABLE_COPY (coff), + BFD_JUMP_TABLE_CORE (_bfd_nocore), + BFD_JUMP_TABLE_ARCHIVE (_bfd_archive_coff), + BFD_JUMP_TABLE_SYMBOLS (coff), + BFD_JUMP_TABLE_RELOCS (coff), + BFD_JUMP_TABLE_WRITE (coff), + BFD_JUMP_TABLE_LINK (coff), + BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic), + + COFF_SWAP_TABLE, +}; diff --git a/gnu/usr.bin/gdb/bfd/coffcode.h b/gnu/usr.bin/gdb/bfd/coffcode.h new file mode 100644 index 000000000000..206a38192f8e --- /dev/null +++ b/gnu/usr.bin/gdb/bfd/coffcode.h @@ -0,0 +1,2544 @@ +/* Support for the generic parts of most COFF variants, for BFD. + Copyright 1990, 1991, 1992, 1993, 1994 Free Software Foundation, Inc. + Written by 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. */ + +/* +Most of this hacked by Steve Chamberlain, + sac@cygnus.com +*/ +/* + +SECTION + coff backends + + BFD supports a number of different flavours of coff format. + The major differences between formats are the sizes and + alignments of fields in structures on disk, and the occasional + extra field. + + Coff in all its varieties is implemented with a few common + files and a number of implementation specific files. For + example, The 88k bcs coff format is implemented in the file + @file{coff-m88k.c}. This file @code{#include}s + @file{coff/m88k.h} which defines the external structure of the + coff format for the 88k, and @file{coff/internal.h} which + defines the internal structure. @file{coff-m88k.c} also + defines the relocations used by the 88k format + @xref{Relocations}. + + The Intel i960 processor version of coff is implemented in + @file{coff-i960.c}. This file has the same structure as + @file{coff-m88k.c}, except that it includes @file{coff/i960.h} + rather than @file{coff-m88k.h}. + +SUBSECTION + Porting to a new version of coff + + The recommended method is to select from the existing + implementations the version of coff which is most like the one + you want to use. For example, we'll say that i386 coff is + the one you select, and that your coff flavour is called foo. + Copy @file{i386coff.c} to @file{foocoff.c}, copy + @file{../include/coff/i386.h} to @file{../include/coff/foo.h}, + and add the lines to @file{targets.c} and @file{Makefile.in} + so that your new back end is used. Alter the shapes of the + structures in @file{../include/coff/foo.h} so that they match + what you need. You will probably also have to add + @code{#ifdef}s to the code in @file{coff/internal.h} and + @file{coffcode.h} if your version of coff is too wild. + + You can verify that your new BFD backend works quite simply by + building @file{objdump} from the @file{binutils} directory, + and making sure that its version of what's going on and your + host system's idea (assuming it has the pretty standard coff + dump utility, usually called @code{att-dump} or just + @code{dump}) are the same. Then clean up your code, and send + what you've done to Cygnus. Then your stuff will be in the + next release, and you won't have to keep integrating it. + +SUBSECTION + How the coff backend works + +SUBSUBSECTION + File layout + + The Coff backend is split into generic routines that are + applicable to any Coff target and routines that are specific + to a particular target. The target-specific routines are + further split into ones which are basically the same for all + Coff targets except that they use the external symbol format + or use different values for certain constants. + + The generic routines are in @file{coffgen.c}. These routines + work for any Coff target. They use some hooks into the target + specific code; the hooks are in a @code{bfd_coff_backend_data} + structure, one of which exists for each target. + + The essentially similar target-specific routines are in + @file{coffcode.h}. This header file includes executable C code. + The various Coff targets first include the appropriate Coff + header file, make any special defines that are needed, and + then include @file{coffcode.h}. + + Some of the Coff targets then also have additional routines in + the target source file itself. + + For example, @file{coff-i960.c} includes + @file{coff/internal.h} and @file{coff/i960.h}. It then + defines a few constants, such as @code{I960}, and includes + @file{coffcode.h}. Since the i960 has complex relocation + types, @file{coff-i960.c} also includes some code to + manipulate the i960 relocs. This code is not in + @file{coffcode.h} because it would not be used by any other + target. + +SUBSUBSECTION + Bit twiddling + + Each flavour of coff supported in BFD has its own header file + describing the external layout of the structures. There is also + an internal description of the coff layout, in + @file{coff/internal.h}. A major function of the + coff backend is swapping the bytes and twiddling the bits to + translate the external form of the structures into the normal + internal form. This is all performed in the + @code{bfd_swap}_@i{thing}_@i{direction} routines. Some + elements are different sizes between different versions of + coff; it is the duty of the coff version specific include file + to override the definitions of various packing routines in + @file{coffcode.h}. E.g., the size of line number entry in coff is + sometimes 16 bits, and sometimes 32 bits. @code{#define}ing + @code{PUT_LNSZ_LNNO} and @code{GET_LNSZ_LNNO} will select the + correct one. No doubt, some day someone will find a version of + coff which has a varying field size not catered to at the + moment. To port BFD, that person will have to add more @code{#defines}. + Three of the bit twiddling routines are exported to + @code{gdb}; @code{coff_swap_aux_in}, @code{coff_swap_sym_in} + and @code{coff_swap_linno_in}. @code{GDB} reads the symbol + table on its own, but uses BFD to fix things up. More of the + bit twiddlers are exported for @code{gas}; + @code{coff_swap_aux_out}, @code{coff_swap_sym_out}, + @code{coff_swap_lineno_out}, @code{coff_swap_reloc_out}, + @code{coff_swap_filehdr_out}, @code{coff_swap_aouthdr_out}, + @code{coff_swap_scnhdr_out}. @code{Gas} currently keeps track + of all the symbol table and reloc drudgery itself, thereby + saving the internal BFD overhead, but uses BFD to swap things + on the way out, making cross ports much safer. Doing so also + allows BFD (and thus the linker) to use the same header files + as @code{gas}, which makes one avenue to disaster disappear. + +SUBSUBSECTION + Symbol reading + + The simple canonical form for symbols used by BFD is not rich + enough to keep all the information available in a coff symbol + table. The back end gets around this problem by keeping the original + symbol table around, "behind the scenes". + + When a symbol table is requested (through a call to + @code{bfd_canonicalize_symtab}), a request gets through to + @code{coff_get_normalized_symtab}. This reads the symbol table from + the coff file and swaps all the structures inside into the + internal form. It also fixes up all the pointers in the table + (represented in the file by offsets from the first symbol in + the table) into physical pointers to elements in the new + internal table. This involves some work since the meanings of + fields change depending upon context: a field that is a + pointer to another structure in the symbol table at one moment + may be the size in bytes of a structure at the next. Another + pass is made over the table. All symbols which mark file names + (<<C_FILE>> symbols) are modified so that the internal + string points to the value in the auxent (the real filename) + rather than the normal text associated with the symbol + (@code{".file"}). + + At this time the symbol names are moved around. Coff stores + all symbols less than nine characters long physically + within the symbol table; longer strings are kept at the end of + the file in the string table. This pass moves all strings + into memory and replaces them with pointers to the strings. + + + The symbol table is massaged once again, this time to create + the canonical table used by the BFD application. Each symbol + is inspected in turn, and a decision made (using the + @code{sclass} field) about the various flags to set in the + @code{asymbol}. @xref{Symbols}. The generated canonical table + shares strings with the hidden internal symbol table. + + Any linenumbers are read from the coff file too, and attached + to the symbols which own the functions the linenumbers belong to. + +SUBSUBSECTION + Symbol writing + + Writing a symbol to a coff file which didn't come from a coff + file will lose any debugging information. The @code{asymbol} + structure remembers the BFD from which the symbol was taken, and on + output the back end makes sure that the same destination target as + source target is present. + + When the symbols have come from a coff file then all the + debugging information is preserved. + + Symbol tables are provided for writing to the back end in a + vector of pointers to pointers. This allows applications like + the linker to accumulate and output large symbol tables + without having to do too much byte copying. + + This function runs through the provided symbol table and + patches each symbol marked as a file place holder + (@code{C_FILE}) to point to the next file place holder in the + list. It also marks each @code{offset} field in the list with + the offset from the first symbol of the current symbol. + + Another function of this procedure is to turn the canonical + value form of BFD into the form used by coff. Internally, BFD + expects symbol values to be offsets from a section base; so a + symbol physically at 0x120, but in a section starting at + 0x100, would have the value 0x20. Coff expects symbols to + contain their final value, so symbols have their values + changed at this point to reflect their sum with their owning + section. This transformation uses the + <<output_section>> field of the @code{asymbol}'s + @code{asection} @xref{Sections}. + + o <<coff_mangle_symbols>> + + This routine runs though the provided symbol table and uses + the offsets generated by the previous pass and the pointers + generated when the symbol table was read in to create the + structured hierachy required by coff. It changes each pointer + to a symbol into the index into the symbol table of the asymbol. + + o <<coff_write_symbols>> + + This routine runs through the symbol table and patches up the + symbols from their internal form into the coff way, calls the + bit twiddlers, and writes out the table to the file. + +*/ + +/* +INTERNAL_DEFINITION + coff_symbol_type + +DESCRIPTION + The hidden information for an <<asymbol>> is described in a + <<combined_entry_type>>: + +CODE_FRAGMENT +. +.typedef struct coff_ptr_struct +.{ +. +. {* Remembers the offset from the first symbol in the file for +. this symbol. Generated by coff_renumber_symbols. *} +.unsigned int offset; +. +. {* Should the value of this symbol be renumbered. Used for +. XCOFF C_BSTAT symbols. Set by coff_slurp_symbol_table. *} +.unsigned int fix_value : 1; +. +. {* Should the tag field of this symbol be renumbered. +. Created by coff_pointerize_aux. *} +.unsigned int fix_tag : 1; +. +. {* Should the endidx field of this symbol be renumbered. +. Created by coff_pointerize_aux. *} +.unsigned int fix_end : 1; +. +. {* Should the x_csect.x_scnlen field be renumbered. +. Created by coff_slurp_symbol_table. *} +.unsigned int fix_scnlen : 1; +. +. {* The container for the symbol structure as read and translated +. from the file. *} +. +.union { +. union internal_auxent auxent; +. struct internal_syment syment; +. } u; +.} combined_entry_type; +. +. +.{* Each canonical asymbol really looks like this: *} +. +.typedef struct coff_symbol_struct +.{ +. {* The actual symbol which the rest of BFD works with *} +.asymbol symbol; +. +. {* A pointer to the hidden information for this symbol *} +.combined_entry_type *native; +. +. {* A pointer to the linenumber information for this symbol *} +.struct lineno_cache_entry *lineno; +. +. {* Have the line numbers been relocated yet ? *} +.boolean done_lineno; +.} coff_symbol_type; + + +*/ + +#include "coffswap.h" + +/* void warning(); */ + +/* + * Return a word with STYP_* (scnhdr.s_flags) flags set to represent the + * incoming SEC_* flags. The inverse of this function is styp_to_sec_flags(). + * NOTE: If you add to/change this routine, you should mirror the changes + * in styp_to_sec_flags(). + */ +static long +sec_to_styp_flags (sec_name, sec_flags) + CONST char *sec_name; + flagword sec_flags; +{ + long styp_flags = 0; + + if (!strcmp (sec_name, _TEXT)) + { + styp_flags = STYP_TEXT; + } + else if (!strcmp (sec_name, _DATA)) + { + styp_flags = STYP_DATA; +#ifdef TWO_DATA_SECS + } + else if (!strcmp (sec_name, ".data2")) + { + styp_flags = STYP_DATA; +#endif /* TWO_DATA_SECS */ + } + else if (!strcmp (sec_name, _BSS)) + { + styp_flags = STYP_BSS; +#ifdef _COMMENT + } + else if (!strcmp (sec_name, _COMMENT)) + { + styp_flags = STYP_INFO; +#endif /* _COMMENT */ +#ifdef _LIB + } + else if (!strcmp (sec_name, _LIB)) + { + styp_flags = STYP_LIB; +#endif /* _LIB */ +#ifdef _LIT + } + else if (!strcmp (sec_name, _LIT)) + { + styp_flags = STYP_LIT; +#endif /* _LIT */ + } + else if (!strcmp (sec_name, ".debug")) + { +#ifdef STYP_DEBUG + styp_flags = STYP_DEBUG; +#else + styp_flags = STYP_INFO; +#endif + } + else if (!strcmp (sec_name, ".stab") + || !strncmp (sec_name, ".stabstr", 8)) + { + styp_flags = STYP_INFO; + } + /* Try and figure out what it should be */ + else if (sec_flags & SEC_CODE) + { + styp_flags = STYP_TEXT; + } + else if (sec_flags & SEC_DATA) + { + styp_flags = STYP_DATA; + } + else if (sec_flags & SEC_READONLY) + { +#ifdef STYP_LIT /* 29k readonly text/data section */ + styp_flags = STYP_LIT; +#else + styp_flags = STYP_TEXT; +#endif /* STYP_LIT */ + } + else if (sec_flags & SEC_LOAD) + { + styp_flags = STYP_TEXT; + } + else if (sec_flags & SEC_ALLOC) + { + styp_flags = STYP_BSS; + } + +#ifdef STYP_NOLOAD + if ((sec_flags & (SEC_NEVER_LOAD | SEC_COFF_SHARED_LIBRARY)) != 0) + styp_flags |= STYP_NOLOAD; +#endif + + return (styp_flags); +} +/* + * Return a word with SEC_* flags set to represent the incoming + * STYP_* flags (from scnhdr.s_flags). The inverse of this + * function is sec_to_styp_flags(). + * NOTE: If you add to/change this routine, you should mirror the changes + * in sec_to_styp_flags(). + */ +static flagword +styp_to_sec_flags (abfd, hdr) + bfd * abfd; + PTR hdr; +{ + struct internal_scnhdr *internal_s = (struct internal_scnhdr *) hdr; + long styp_flags = internal_s->s_flags; + flagword sec_flags = 0; + +#ifdef STYP_NOLOAD + if (styp_flags & STYP_NOLOAD) + { + sec_flags |= SEC_NEVER_LOAD; + } +#endif /* STYP_NOLOAD */ + + /* For 386 COFF, at least, an unloadable text or data section is + actually a shared library section. */ + if (styp_flags & STYP_TEXT) + { + if (sec_flags & SEC_NEVER_LOAD) + sec_flags |= SEC_CODE | SEC_COFF_SHARED_LIBRARY; + else + sec_flags |= SEC_CODE | SEC_LOAD | SEC_ALLOC; + } + else if (styp_flags & STYP_DATA) + { + if (sec_flags & SEC_NEVER_LOAD) + sec_flags |= SEC_DATA | SEC_COFF_SHARED_LIBRARY; + else + sec_flags |= SEC_DATA | SEC_LOAD | SEC_ALLOC; + } + else if (styp_flags & STYP_BSS) + { +#ifdef BSS_NOLOAD_IS_SHARED_LIBRARY + if (sec_flags & SEC_NEVER_LOAD) + sec_flags |= SEC_ALLOC | SEC_COFF_SHARED_LIBRARY; + else +#endif + sec_flags |= SEC_ALLOC; + } + else if (styp_flags & STYP_INFO) + { + /* We mark these as SEC_DEBUGGING, but only if COFF_PAGE_SIZE is + defined. coff_compute_section_file_positions uses + COFF_PAGE_SIZE to ensure that the low order bits of the + section VMA and the file offset match. If we don't know + COFF_PAGE_SIZE, we can't ensure the correct correspondence, + and demand page loading of the file will fail. */ +#ifdef COFF_PAGE_SIZE + sec_flags |= SEC_DEBUGGING; +#endif + } + else + { + sec_flags |= SEC_ALLOC | SEC_LOAD; + } + +#ifdef STYP_LIT /* A29k readonly text/data section type */ + if ((styp_flags & STYP_LIT) == STYP_LIT) + { + sec_flags = (SEC_LOAD | SEC_ALLOC | SEC_READONLY); + } +#endif /* STYP_LIT */ +#ifdef STYP_OTHER_LOAD /* Other loaded sections */ + if (styp_flags & STYP_OTHER_LOAD) + { + sec_flags = (SEC_LOAD | SEC_ALLOC); + } +#endif /* STYP_SDATA */ + + return (sec_flags); +} + +#define get_index(symbol) ((long) (symbol)->udata) + +/* +INTERNAL_DEFINITION + bfd_coff_backend_data + +CODE_FRAGMENT + +Special entry points for gdb to swap in coff symbol table parts: +.typedef struct +.{ +. void (*_bfd_coff_swap_aux_in) PARAMS (( +. bfd *abfd, +. PTR ext, +. int type, +. int class, +. int indaux, +. int numaux, +. PTR in)); +. +. void (*_bfd_coff_swap_sym_in) PARAMS (( +. bfd *abfd , +. PTR ext, +. PTR in)); +. +. void (*_bfd_coff_swap_lineno_in) PARAMS (( +. bfd *abfd, +. PTR ext, +. PTR in)); +. + +Special entry points for gas to swap out coff parts: + +. unsigned int (*_bfd_coff_swap_aux_out) PARAMS (( +. bfd *abfd, +. PTR in, +. int type, +. int class, +. int indaux, +. int numaux, +. PTR ext)); +. +. unsigned int (*_bfd_coff_swap_sym_out) PARAMS (( +. bfd *abfd, +. PTR in, +. PTR ext)); +. +. unsigned int (*_bfd_coff_swap_lineno_out) PARAMS (( +. bfd *abfd, +. PTR in, +. PTR ext)); +. +. unsigned int (*_bfd_coff_swap_reloc_out) PARAMS (( +. bfd *abfd, +. PTR src, +. PTR dst)); +. +. unsigned int (*_bfd_coff_swap_filehdr_out) PARAMS (( +. bfd *abfd, +. PTR in, +. PTR out)); +. +. unsigned int (*_bfd_coff_swap_aouthdr_out) PARAMS (( +. bfd *abfd, +. PTR in, +. PTR out)); +. +. unsigned int (*_bfd_coff_swap_scnhdr_out) PARAMS (( +. bfd *abfd, +. PTR in, +. PTR out)); +. + +Special entry points for generic COFF routines to call target +dependent COFF routines: + +. unsigned int _bfd_filhsz; +. unsigned int _bfd_aoutsz; +. unsigned int _bfd_scnhsz; +. unsigned int _bfd_symesz; +. unsigned int _bfd_auxesz; +. unsigned int _bfd_linesz; +. boolean _bfd_coff_long_filenames; +. void (*_bfd_coff_swap_filehdr_in) PARAMS (( +. bfd *abfd, +. PTR ext, +. PTR in)); +. void (*_bfd_coff_swap_aouthdr_in) PARAMS (( +. bfd *abfd, +. PTR ext, +. PTR in)); +. void (*_bfd_coff_swap_scnhdr_in) PARAMS (( +. bfd *abfd, +. PTR ext, +. PTR in)); +. boolean (*_bfd_coff_bad_format_hook) PARAMS (( +. bfd *abfd, +. PTR internal_filehdr)); +. boolean (*_bfd_coff_set_arch_mach_hook) PARAMS (( +. bfd *abfd, +. PTR internal_filehdr)); +. PTR (*_bfd_coff_mkobject_hook) PARAMS (( +. bfd *abfd, +. PTR internal_filehdr, +. PTR internal_aouthdr)); +. flagword (*_bfd_styp_to_sec_flags_hook) PARAMS (( +. bfd *abfd, +. PTR internal_scnhdr)); +. asection *(*_bfd_make_section_hook) PARAMS (( +. bfd *abfd, +. char *name)); +. void (*_bfd_set_alignment_hook) PARAMS (( +. bfd *abfd, +. asection *sec, +. PTR internal_scnhdr)); +. boolean (*_bfd_coff_slurp_symbol_table) PARAMS (( +. bfd *abfd)); +. boolean (*_bfd_coff_symname_in_debug) PARAMS (( +. bfd *abfd, +. struct internal_syment *sym)); +. void (*_bfd_coff_reloc16_extra_cases) PARAMS (( +. bfd *abfd, +. struct bfd_link_info *link_info, +. struct bfd_link_order *link_order, +. arelent *reloc, +. bfd_byte *data, +. unsigned int *src_ptr, +. unsigned int *dst_ptr)); +. int (*_bfd_coff_reloc16_estimate) PARAMS (( +. bfd *abfd, +. asection *input_section, +. arelent *r, +. unsigned int shrink, +. struct bfd_link_info *link_info)); +. +.} bfd_coff_backend_data; +. +.#define coff_backend_info(abfd) ((bfd_coff_backend_data *) (abfd)->xvec->backend_data) +. +.#define bfd_coff_swap_aux_in(a,e,t,c,ind,num,i) \ +. ((coff_backend_info (a)->_bfd_coff_swap_aux_in) (a,e,t,c,ind,num,i)) +. +.#define bfd_coff_swap_sym_in(a,e,i) \ +. ((coff_backend_info (a)->_bfd_coff_swap_sym_in) (a,e,i)) +. +.#define bfd_coff_swap_lineno_in(a,e,i) \ +. ((coff_backend_info ( a)->_bfd_coff_swap_lineno_in) (a,e,i)) +. +.#define bfd_coff_swap_reloc_out(abfd, i, o) \ +. ((coff_backend_info (abfd)->_bfd_coff_swap_reloc_out) (abfd, i, o)) +. +.#define bfd_coff_swap_lineno_out(abfd, i, o) \ +. ((coff_backend_info (abfd)->_bfd_coff_swap_lineno_out) (abfd, i, o)) +. +.#define bfd_coff_swap_aux_out(a,i,t,c,ind,num,o) \ +. ((coff_backend_info (a)->_bfd_coff_swap_aux_out) (a,i,t,c,ind,num,o)) +. +.#define bfd_coff_swap_sym_out(abfd, i,o) \ +. ((coff_backend_info (abfd)->_bfd_coff_swap_sym_out) (abfd, i, o)) +. +.#define bfd_coff_swap_scnhdr_out(abfd, i,o) \ +. ((coff_backend_info (abfd)->_bfd_coff_swap_scnhdr_out) (abfd, i, o)) +. +.#define bfd_coff_swap_filehdr_out(abfd, i,o) \ +. ((coff_backend_info (abfd)->_bfd_coff_swap_filehdr_out) (abfd, i, o)) +. +.#define bfd_coff_swap_aouthdr_out(abfd, i,o) \ +. ((coff_backend_info (abfd)->_bfd_coff_swap_aouthdr_out) (abfd, i, o)) +. +.#define bfd_coff_filhsz(abfd) (coff_backend_info (abfd)->_bfd_filhsz) +.#define bfd_coff_aoutsz(abfd) (coff_backend_info (abfd)->_bfd_aoutsz) +.#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_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) \ +. ((coff_backend_info (abfd)->_bfd_coff_swap_filehdr_in) (abfd, i, o)) +. +.#define bfd_coff_swap_aouthdr_in(abfd, i,o) \ +. ((coff_backend_info (abfd)->_bfd_coff_swap_aouthdr_in) (abfd, i, o)) +. +.#define bfd_coff_swap_scnhdr_in(abfd, i,o) \ +. ((coff_backend_info (abfd)->_bfd_coff_swap_scnhdr_in) (abfd, i, o)) +. +.#define bfd_coff_bad_format_hook(abfd, filehdr) \ +. ((coff_backend_info (abfd)->_bfd_coff_bad_format_hook) (abfd, filehdr)) +. +.#define bfd_coff_set_arch_mach_hook(abfd, filehdr)\ +. ((coff_backend_info (abfd)->_bfd_coff_set_arch_mach_hook) (abfd, filehdr)) +.#define bfd_coff_mkobject_hook(abfd, filehdr, aouthdr)\ +. ((coff_backend_info (abfd)->_bfd_coff_mkobject_hook) (abfd, filehdr, aouthdr)) +. +.#define bfd_coff_styp_to_sec_flags_hook(abfd, scnhdr)\ +. ((coff_backend_info (abfd)->_bfd_styp_to_sec_flags_hook) (abfd, scnhdr)) +. +.#define bfd_coff_make_section_hook(abfd, name)\ +. ((coff_backend_info (abfd)->_bfd_make_section_hook) (abfd, name)) +. +.#define bfd_coff_set_alignment_hook(abfd, sec, scnhdr)\ +. ((coff_backend_info (abfd)->_bfd_set_alignment_hook) (abfd, sec, scnhdr)) +. +.#define bfd_coff_slurp_symbol_table(abfd)\ +. ((coff_backend_info (abfd)->_bfd_coff_slurp_symbol_table) (abfd)) +. +.#define bfd_coff_symname_in_debug(abfd, sym)\ +. ((coff_backend_info (abfd)->_bfd_coff_symname_in_debug) (abfd, sym)) +. +.#define bfd_coff_reloc16_extra_cases(abfd, link_info, link_order, reloc, data, src_ptr, dst_ptr)\ +. ((coff_backend_info (abfd)->_bfd_coff_reloc16_extra_cases)\ +. (abfd, link_info, link_order, reloc, data, src_ptr, dst_ptr)) +. +.#define bfd_coff_reloc16_estimate(abfd, section, reloc, shrink, link_info)\ +. ((coff_backend_info (abfd)->_bfd_coff_reloc16_estimate)\ +. (abfd, section, reloc, shrink, link_info)) +. +*/ + +/* See whether the magic number matches. */ + +static boolean +coff_bad_format_hook (abfd, filehdr) + bfd * abfd; + PTR filehdr; +{ + struct internal_filehdr *internal_f = (struct internal_filehdr *) filehdr; + + if (BADMAG (*internal_f)) + return false; + + /* if the optional header is NULL or not the correct size then + quit; the only difference I can see between m88k dgux headers (MC88DMAGIC) + and Intel 960 readwrite headers (I960WRMAGIC) is that the + optional header is of a different size. + + But the mips keeps extra stuff in it's opthdr, so dont check + when doing that + */ + +#if defined(M88) || defined(I960) + if (internal_f->f_opthdr != 0 && AOUTSZ != internal_f->f_opthdr) + return false; +#endif + + return true; +} + +static asection * +coff_make_section_hook (abfd, name) + bfd * abfd; + char *name; +{ +#ifdef TWO_DATA_SECS + /* FIXME: This predates the call to bfd_make_section_anyway + in make_a_section_from_file, and can probably go away. */ + /* On SCO a file created by the Microsoft assembler can have two + .data sections. We use .data2 for the second one. */ + if (strcmp (name, _DATA) == 0) + return bfd_make_section (abfd, ".data2"); +#endif + return (asection *) NULL; +} + +/* + initialize a section structure with information peculiar to this + particular implementation of coff +*/ + +static boolean +coff_new_section_hook (abfd, section) + bfd * abfd; + asection * section; +{ + section->alignment_power = abfd->xvec->align_power_min; + /* Allocate aux records for section symbols, to store size and + related info. + + @@ Shouldn't use constant multiplier here! */ + coffsymbol (section->symbol)->native = + (combined_entry_type *) bfd_zalloc (abfd, + sizeof (combined_entry_type) * 10); + +#ifdef COFF_SPARC + /* This is to allow double-word operations on addresses in data or bss. */ + if (strcmp (section->name, ".data") == 0 + || strcmp (section->name, ".bss") == 0) + section->alignment_power = 3; +#endif /* COFF_SPARC */ + + return true; +} + + +#ifdef I960 + +/* Set the alignment of a BFD section. */ + +static void +coff_set_alignment_hook (abfd, section, scnhdr) + bfd * abfd; + asection * section; + PTR scnhdr; +{ + struct internal_scnhdr *hdr = (struct internal_scnhdr *) scnhdr; + unsigned int i; + + for (i = 0; i < 32; i++) + if ((1 << i) >= hdr->s_align) + break; + section->alignment_power = i; +} + +#else /* ! I960 */ + +#define coff_set_alignment_hook \ + ((void (*) PARAMS ((bfd *, asection *, PTR))) bfd_void) + +#endif /* ! I960 */ + +static boolean +coff_mkobject (abfd) + bfd * abfd; +{ + coff_data_type *coff; + + abfd->tdata.coff_obj_data = (struct coff_tdata *) bfd_zalloc (abfd, sizeof (coff_data_type)); + if (abfd->tdata.coff_obj_data == 0) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + coff = coff_data (abfd); + coff->symbols = (coff_symbol_type *) NULL; + coff->conversion_table = (unsigned int *) NULL; + coff->raw_syments = (struct coff_ptr_struct *) NULL; + coff->raw_linenos = (struct lineno *) NULL; + coff->relocbase = 0; +/* make_abs_section(abfd);*/ + return true; +} + +/* Create the COFF backend specific information. */ + +static PTR +coff_mkobject_hook (abfd, filehdr, aouthdr) + bfd * abfd; + PTR filehdr; + PTR aouthdr; +{ + struct internal_filehdr *internal_f = (struct internal_filehdr *) filehdr; + coff_data_type *coff; + + if (coff_mkobject (abfd) == false) + return NULL; + + 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' + unfortunately vary among coff implementations... */ + coff->local_n_btmask = N_BTMASK; + coff->local_n_btshft = N_BTSHFT; + coff->local_n_tmask = N_TMASK; + coff->local_n_tshift = N_TSHIFT; + coff->local_symesz = SYMESZ; + coff->local_auxesz = AUXESZ; + coff->local_linesz = LINESZ; + + return (PTR) coff; +} + +/* Determine the machine architecture and type. FIXME: This is target + dependent because the magic numbers are defined in the target + dependent header files. But there is no particular need for this. + If the magic numbers were moved to a separate file, this function + would be target independent and would also be much more successful + at linking together COFF files for different architectures. */ + +static boolean +coff_set_arch_mach_hook (abfd, filehdr) + bfd *abfd; + PTR filehdr; +{ + long machine; + enum bfd_architecture arch; + struct internal_filehdr *internal_f = (struct internal_filehdr *) filehdr; + + machine = 0; + switch (internal_f->f_magic) + { +#ifdef I386MAGIC + case I386MAGIC: + case I386PTXMAGIC: + case I386AIXMAGIC: /* Danbury PS/2 AIX C Compiler */ + case LYNXCOFFMAGIC: /* shadows the m68k Lynx number below, sigh */ + arch = bfd_arch_i386; + machine = 0; + break; +#endif + +#ifdef A29K_MAGIC_BIG + case A29K_MAGIC_BIG: + case A29K_MAGIC_LITTLE: + arch = bfd_arch_a29k; + machine = 0; + break; +#endif + +#ifdef MC68MAGIC + case MC68MAGIC: + case M68MAGIC: +#ifdef MC68KBCSMAGIC + case MC68KBCSMAGIC: +#endif +#ifdef APOLLOM68KMAGIC + case APOLLOM68KMAGIC: +#endif +#ifdef LYNXCOFFMAGIC + case LYNXCOFFMAGIC: +#endif + arch = bfd_arch_m68k; + machine = 68020; + break; +#endif +#ifdef MC88MAGIC + case MC88MAGIC: + case MC88DMAGIC: + case MC88OMAGIC: + arch = bfd_arch_m88k; + machine = 88100; + break; +#endif +#ifdef Z8KMAGIC + case Z8KMAGIC: + arch = bfd_arch_z8k; + switch (internal_f->f_flags & F_MACHMASK) + { + case F_Z8001: + machine = bfd_mach_z8001; + break; + case F_Z8002: + machine = bfd_mach_z8002; + break; + default: + return false; + } + break; +#endif +#ifdef I960 +#ifdef I960ROMAGIC + case I960ROMAGIC: + case I960RWMAGIC: + arch = bfd_arch_i960; + switch (F_I960TYPE & internal_f->f_flags) + { + default: + case F_I960CORE: + machine = bfd_mach_i960_core; + break; + case F_I960KB: + machine = bfd_mach_i960_kb_sb; + break; + case F_I960MC: + machine = bfd_mach_i960_mc; + break; + case F_I960XA: + machine = bfd_mach_i960_xa; + break; + case F_I960CA: + machine = bfd_mach_i960_ca; + break; + case F_I960KA: + machine = bfd_mach_i960_ka_sa; + break; + } + break; +#endif +#endif + +#ifdef U802ROMAGIC + case U802ROMAGIC: + case U802WRMAGIC: + case U802TOCMAGIC: + arch = bfd_arch_rs6000; + machine = 6000; + break; +#endif + +#ifdef WE32KMAGIC + case WE32KMAGIC: + arch = bfd_arch_we32k; + machine = 0; + break; +#endif + +#ifdef H8300MAGIC + case H8300MAGIC: + arch = bfd_arch_h8300; + machine = bfd_mach_h8300; + /* !! FIXME this probably isn't the right place for this */ + abfd->flags |= BFD_IS_RELAXABLE; + break; +#endif + +#ifdef H8300HMAGIC + case H8300HMAGIC: + arch = bfd_arch_h8300; + machine = bfd_mach_h8300h; + /* !! FIXME this probably isn't the right place for this */ + abfd->flags |= BFD_IS_RELAXABLE; + break; +#endif + +#ifdef SH_ARCH_MAGIC + case SH_ARCH_MAGIC: + arch = bfd_arch_sh; + machine = 0; + break; +#endif + +#ifdef H8500MAGIC + case H8500MAGIC: + arch = bfd_arch_h8500; + machine = 0; + break; +#endif + +#ifdef SPARCMAGIC + case SPARCMAGIC: +#ifdef LYNXCOFFMAGIC + case LYNXCOFFMAGIC: +#endif + arch = bfd_arch_sparc; + machine = 0; + break; +#endif + + default: /* Unreadable input file type */ + arch = bfd_arch_obscure; + break; + } + + bfd_default_set_arch_mach (abfd, arch, machine); + return true; +} + +#ifdef SYMNAME_IN_DEBUG + +static boolean +symname_in_debug_hook (abfd, sym) + bfd * abfd; + struct internal_syment *sym; +{ + return SYMNAME_IN_DEBUG (sym) ? true : false; +} + +#else + +#define symname_in_debug_hook \ + (boolean (*) PARAMS ((bfd *, struct internal_syment *))) bfd_false + +#endif + +/* +SUBSUBSECTION + Writing relocations + + To write relocations, the back end steps though the + canonical relocation table and create an + @code{internal_reloc}. The symbol index to use is removed from + the @code{offset} field in the symbol table supplied. The + address comes directly from the sum of the section base + address and the relocation offset; the type is dug directly + from the howto field. Then the @code{internal_reloc} is + swapped into the shape of an @code{external_reloc} and written + out to disk. + +*/ + +static boolean +coff_write_relocs (abfd) + bfd * abfd; +{ + asection *s; + for (s = abfd->sections; s != (asection *) NULL; s = s->next) + { + unsigned int i; + struct external_reloc dst; + + arelent **p = s->orelocation; + if (bfd_seek (abfd, s->rel_filepos, SEEK_SET) != 0) + return false; + for (i = 0; i < s->reloc_count; i++) + { + struct internal_reloc n; + arelent *q = p[i]; + memset ((PTR) & n, 0, sizeof (n)); + + n.r_vaddr = q->address + s->vma; + +#ifdef R_IHCONST + /* The 29k const/consth reloc pair is a real kludge. The consth + part doesn't have a symbol; it has an offset. So rebuilt + that here. */ + if (q->howto->type == R_IHCONST) + n.r_symndx = q->addend; + else +#endif + if (q->sym_ptr_ptr) + { + if (q->sym_ptr_ptr == bfd_abs_section_ptr->symbol_ptr_ptr) + /* This is a relocation relative to the absolute symbol. */ + n.r_symndx = -1; + else + { + n.r_symndx = get_index ((*(q->sym_ptr_ptr))); + /* Take notice if the symbol reloc points to a symbol + we don't have in our symbol table. What should we + do for this?? */ + if (n.r_symndx > obj_conv_table_size (abfd)) + abort (); + } + } + +#ifdef SWAP_OUT_RELOC_OFFSET + n.r_offset = q->addend; +#endif + +#ifdef SELECT_RELOC + /* Work out reloc type from what is required */ + SELECT_RELOC (n, q->howto); +#else + n.r_type = q->howto->type; +#endif + coff_swap_reloc_out (abfd, &n, &dst); + if (bfd_write ((PTR) & dst, 1, RELSZ, abfd) != RELSZ) + return false; + } + } + + return true; +} + +/* Set flags and magic number of a coff file from architecture and machine + type. Result is true if we can represent the arch&type, false if not. */ + +static boolean +coff_set_flags (abfd, magicp, flagsp) + bfd * abfd; + unsigned *magicp; + unsigned short *flagsp; +{ + switch (bfd_get_arch (abfd)) + { +#ifdef Z8KMAGIC + case bfd_arch_z8k: + *magicp = Z8KMAGIC; + switch (bfd_get_mach (abfd)) + { + case bfd_mach_z8001: + *flagsp = F_Z8001; + break; + case bfd_mach_z8002: + *flagsp = F_Z8002; + break; + default: + return false; + } + return true; +#endif +#ifdef I960ROMAGIC + + case bfd_arch_i960: + + { + unsigned flags; + *magicp = I960ROMAGIC; + /* + ((bfd_get_file_flags(abfd) & WP_TEXT) ? I960ROMAGIC : + I960RWMAGIC); FIXME??? + */ + switch (bfd_get_mach (abfd)) + { + case bfd_mach_i960_core: + flags = F_I960CORE; + break; + case bfd_mach_i960_kb_sb: + flags = F_I960KB; + break; + case bfd_mach_i960_mc: + flags = F_I960MC; + break; + case bfd_mach_i960_xa: + flags = F_I960XA; + break; + case bfd_mach_i960_ca: + flags = F_I960CA; + break; + case bfd_mach_i960_ka_sa: + flags = F_I960KA; + break; + default: + return false; + } + *flagsp = flags; + return true; + } + break; +#endif +#ifdef I386MAGIC + case bfd_arch_i386: + *magicp = I386MAGIC; +#ifdef LYNXOS + /* Just overwrite the usual value if we're doing Lynx. */ + *magicp = LYNXCOFFMAGIC; +#endif + return true; + break; +#endif +#ifdef MC68MAGIC + case bfd_arch_m68k: +#ifdef APOLLOM68KMAGIC + *magicp = APOLLO_COFF_VERSION_NUMBER; +#else + *magicp = MC68MAGIC; +#endif +#ifdef LYNXOS + /* Just overwrite the usual value if we're doing Lynx. */ + *magicp = LYNXCOFFMAGIC; +#endif + return true; + break; +#endif + +#ifdef MC88MAGIC + case bfd_arch_m88k: + *magicp = MC88OMAGIC; + return true; + break; +#endif +#ifdef H8300MAGIC + case bfd_arch_h8300: + switch (bfd_get_mach (abfd)) + { + case bfd_mach_h8300: + *magicp = H8300MAGIC; + return true; + case bfd_mach_h8300h: + *magicp = H8300HMAGIC; + return true; + } + break; +#endif + +#ifdef SH_ARCH_MAGIC + case bfd_arch_sh: + *magicp = SH_ARCH_MAGIC; + return true; + break; +#endif + +#ifdef SPARCMAGIC + case bfd_arch_sparc: + *magicp = SPARCMAGIC; +#ifdef LYNXOS + /* Just overwrite the usual value if we're doing Lynx. */ + *magicp = LYNXCOFFMAGIC; +#endif + return true; + break; +#endif + +#ifdef H8500MAGIC + case bfd_arch_h8500: + *magicp = H8500MAGIC; + return true; + break; +#endif +#ifdef A29K_MAGIC_BIG + case bfd_arch_a29k: + if (abfd->xvec->byteorder_big_p) + *magicp = A29K_MAGIC_BIG; + else + *magicp = A29K_MAGIC_LITTLE; + return true; + break; +#endif + +#ifdef WE32KMAGIC + case bfd_arch_we32k: + *magicp = WE32KMAGIC; + return true; + break; +#endif + +#ifdef U802TOCMAGIC + case bfd_arch_rs6000: + case bfd_arch_powerpc: + *magicp = U802TOCMAGIC; + return true; + break; +#endif + + default: /* Unknown architecture */ + /* return false; -- fall through to "return false" below, to avoid + "statement never reached" errors on the one below. */ + break; + } + + return false; +} + + +static boolean +coff_set_arch_mach (abfd, arch, machine) + bfd * abfd; + enum bfd_architecture arch; + unsigned long machine; +{ + unsigned dummy1; + unsigned short dummy2; + + if (! bfd_default_set_arch_mach (abfd, arch, machine)) + return false; + + if (arch != bfd_arch_unknown && + coff_set_flags (abfd, &dummy1, &dummy2) != true) + return false; /* We can't represent this type */ + + return true; /* We're easy ... */ +} + + +/* Calculate the file position for each section. */ + +static void +coff_compute_section_file_positions (abfd) + bfd * abfd; +{ + asection *current; + asection *previous = (asection *) NULL; + file_ptr sofar = FILHSZ; +#ifndef I960 + file_ptr old_sofar; +#endif + if (bfd_get_start_address (abfd)) + { + /* A start address may have been added to the original file. In this + case it will need an optional header to record it. */ + abfd->flags |= EXEC_P; + } + + if (abfd->flags & EXEC_P) + sofar += AOUTSZ; + + sofar += abfd->section_count * SCNHSZ; + for (current = abfd->sections; + current != (asection *) NULL; + current = current->next) + { + + /* Only deal with sections which have contents */ + if (!(current->flags & SEC_HAS_CONTENTS)) + continue; + + /* Align the sections in the file to the same boundary on + which they are aligned in virtual memory. I960 doesn't + do this (FIXME) so we can stay in sync with Intel. 960 + doesn't yet page from files... */ +#ifndef I960 + { + /* make sure this section is aligned on the right boundary - by + padding the previous section up if necessary */ + + old_sofar = sofar; + sofar = BFD_ALIGN (sofar, 1 << current->alignment_power); + if (previous != (asection *) NULL) + { + previous->_raw_size += sofar - old_sofar; + } + } + +#endif + +#ifdef COFF_PAGE_SIZE + /* In demand paged files the low order bits of the file offset + must match the low order bits of the virtual address. */ + if ((abfd->flags & D_PAGED) != 0) + sofar += (current->vma - sofar) % COFF_PAGE_SIZE; +#endif + + current->filepos = sofar; + + sofar += current->_raw_size; +#ifndef I960 + /* make sure that this section is of the right size too */ + old_sofar = sofar; + sofar = BFD_ALIGN (sofar, 1 << current->alignment_power); + current->_raw_size += sofar - old_sofar; +#endif + +#ifdef _LIB + /* Force .lib sections to start at zero. The vma is then + incremented in coff_set_section_contents. This is right for + SVR3.2. */ + if (strcmp (current->name, _LIB) == 0) + bfd_set_section_vma (abfd, current, 0); +#endif + + previous = current; + } + obj_relocbase (abfd) = sofar; +} + +#ifndef RS6000COFF_C + +/* If .file, .text, .data, .bss symbols are missing, add them. */ +/* @@ Should we only be adding missing symbols, or overriding the aux + values for existing section symbols? */ +static boolean +coff_add_missing_symbols (abfd) + bfd *abfd; +{ + unsigned int nsyms = bfd_get_symcount (abfd); + asymbol **sympp = abfd->outsymbols; + asymbol **sympp2; + unsigned int i; + int need_text = 1, need_data = 1, need_bss = 1, need_file = 1; + + for (i = 0; i < nsyms; i++) + { + coff_symbol_type *csym = coff_symbol_from (abfd, sympp[i]); + CONST char *name; + if (csym) + { + /* only do this if there is a coff representation of the input + symbol */ + if (csym->native && csym->native->u.syment.n_sclass == C_FILE) + { + need_file = 0; + continue; + } + name = csym->symbol.name; + if (!name) + continue; + if (!strcmp (name, _TEXT)) + need_text = 0; +#ifdef APOLLO_M68 + else if (!strcmp (name, ".wtext")) + need_text = 0; +#endif + else if (!strcmp (name, _DATA)) + need_data = 0; + else if (!strcmp (name, _BSS)) + need_bss = 0; + } + } + /* Now i == bfd_get_symcount (abfd). */ + /* @@ For now, don't deal with .file symbol. */ + need_file = 0; + + if (!need_text && !need_data && !need_bss && !need_file) + return true; + nsyms += need_text + need_data + need_bss + need_file; + sympp2 = (asymbol **) bfd_alloc_by_size_t (abfd, nsyms * sizeof (asymbol *)); + if (!sympp2) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + memcpy (sympp2, sympp, i * sizeof (asymbol *)); + if (need_file) + { + /* @@ Generate fake .file symbol, in sympp2[i], and increment i. */ + abort (); + } + if (need_text) + sympp2[i++] = coff_section_symbol (abfd, _TEXT); + if (need_data) + sympp2[i++] = coff_section_symbol (abfd, _DATA); + if (need_bss) + sympp2[i++] = coff_section_symbol (abfd, _BSS); + BFD_ASSERT (i == nsyms); + bfd_set_symtab (abfd, sympp2, nsyms); + return true; +} + +#endif /* ! defined (RS6000COFF_C) */ + +/* SUPPRESS 558 */ +/* SUPPRESS 529 */ +static boolean +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; + asection *data_sec = NULL; + asection *bss_sec = NULL; + + struct internal_filehdr internal_f; + struct internal_aouthdr internal_a; + + + 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); + } + + 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; + } + + } + + lineno_base = reloc_base + reloc_size; + sym_base = lineno_base + lnno_size; + + /* Indicate in each section->line_filepos its actual file address */ + for (current = abfd->sections; current != NULL; current = + current->next) + { + if (current->target_index > 0) + { + + 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; + } + } + } + + + + /* Write section headers to the file. */ + internal_f.f_nscns = 0; + if (bfd_seek (abfd, + (file_ptr) ((abfd->flags & EXEC_P) ? + (FILHSZ + AOUTSZ) : FILHSZ), + SEEK_SET) + != 0) + 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); +#ifdef _LIB + /* 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; +#ifdef TWO_DATA_SECS + } + else if (!strcmp (current->name, ".data2")) + { + data_sec = current; +#endif /* TWO_DATA_SECS */ + } + else if (!strcmp (current->name, _BSS)) + { + bss_sec = current; + } + +#ifdef I960 + 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; + + } + + pad = 0; + } + } + } + + + /* OK, now set up the filehdr... */ + + /* Don't include the internal abs section in the section count */ + + /* + We will NOT put a fucking timestamp in the header here. Every time you + put it back, I will come in and take it out again. I'm sorry. This + field does not belong here. We fill it with a 0 so it compares the + same but is not a reasonable time. -- gnu@cygnus.com + */ + internal_f.f_timdat = 0; + + if (bfd_get_symcount (abfd) != 0) + internal_f.f_symptr = sym_base; + else + internal_f.f_symptr = 0; + + internal_f.f_flags = 0; + + if (abfd->flags & EXEC_P) + internal_f.f_opthdr = AOUTSZ; + else + internal_f.f_opthdr = 0; + + if (!hasrelocs) + internal_f.f_flags |= F_RELFLG; + if (!haslinno) + internal_f.f_flags |= F_LNNO; + if (0 == bfd_get_symcount (abfd)) + internal_f.f_flags |= F_LSYMS; + if (abfd->flags & EXEC_P) + internal_f.f_flags |= F_EXEC; + + if (!abfd->xvec->byteorder_big_p) + internal_f.f_flags |= F_AR32WR; + else + internal_f.f_flags |= F_AR32W; + + /* + FIXME, should do something about the other byte orders and + architectures. + */ + + memset (&internal_a, 0, sizeof internal_a); + + /* Set up architecture-dependent stuff */ + + { + unsigned int magic = 0; + unsigned short flags = 0; + coff_set_flags (abfd, &magic, &flags); + internal_f.f_magic = magic; + internal_f.f_flags |= flags; + /* ...and the "opt"hdr... */ + +#ifdef A29K +#ifdef ULTRA3 /* NYU's machine */ + /* FIXME: This is a bogus check. I really want to see if there + * is a .shbss or a .shdata section, if so then set the magic + * number to indicate a shared data executable. + */ + if (internal_f.f_nscns >= 7) + internal_a.magic = SHMAGIC; /* Shared magic */ + else +#endif /* ULTRA3 */ + internal_a.magic = NMAGIC;/* Assume separate i/d */ +#define __A_MAGIC_SET__ +#endif /* A29K */ +#ifdef I960 + internal_a.magic = (magic == I960ROMAGIC ? NMAGIC : OMAGIC); +#define __A_MAGIC_SET__ +#endif /* I960 */ +#if M88 +#define __A_MAGIC_SET__ + internal_a.magic = PAGEMAGICBCS; +#endif /* M88 */ + +#if APOLLO_M68 +#define __A_MAGIC_SET__ + internal_a.magic = APOLLO_COFF_VERSION_NUMBER; +#endif + +#if M68 || WE32K +#define __A_MAGIC_SET__ + /* Never was anything here for the 68k */ +#endif /* M68 || WE32K */ + +#if I386 +#define __A_MAGIC_SET__ + internal_a.magic = ZMAGIC; +#endif /* I386 */ + +#if RS6000COFF_C +#define __A_MAGIC_SET__ + internal_a.magic = (abfd->flags & D_PAGED) ? RS6K_AOUTHDR_ZMAGIC : + (abfd->flags & WP_TEXT) ? RS6K_AOUTHDR_NMAGIC : + RS6K_AOUTHDR_OMAGIC; +#endif + +#ifndef __A_MAGIC_SET__ +#include "Your aouthdr magic number is not being set!" +#else +#undef __A_MAGIC_SET__ +#endif + } + /* Now should write relocs, strings, syms */ + obj_sym_filepos (abfd) = sym_base; + + if (bfd_get_symcount (abfd) != 0) + { +#ifndef RS6000COFF_C + if (!coff_add_missing_symbols (abfd)) + return false; +#endif + if (!coff_renumber_symbols (abfd)) + return false; + coff_mangle_symbols (abfd); + if (! coff_write_symbols (abfd)) + return false; + if (! coff_write_linenumbers (abfd)) + return false; + if (! coff_write_relocs (abfd)) + return false; + } + if (text_sec) + { + internal_a.tsize = bfd_get_section_size_before_reloc (text_sec); + internal_a.text_start = internal_a.tsize ? text_sec->vma : 0; + } + if (data_sec) + { + internal_a.dsize = bfd_get_section_size_before_reloc (data_sec); + internal_a.data_start = internal_a.dsize ? data_sec->vma : 0; + } + if (bss_sec) + { + internal_a.bsize = bfd_get_section_size_before_reloc (bss_sec); + } + + internal_a.entry = bfd_get_start_address (abfd); + internal_f.f_nsyms = bfd_get_symcount (abfd); + + /* now write them */ + if (bfd_seek (abfd, (file_ptr) 0, SEEK_SET) != 0) + return false; + { + FILHDR buff; + coff_swap_filehdr_out (abfd, (PTR) & internal_f, (PTR) & buff); + if (bfd_write ((PTR) & buff, 1, FILHSZ, abfd) != FILHSZ) + return false; + } + if (abfd->flags & EXEC_P) + { + AOUTHDR buff; + coff_swap_aouthdr_out (abfd, (PTR) & internal_a, (PTR) & buff); + if (bfd_write ((PTR) & buff, 1, AOUTSZ, abfd) != AOUTSZ) + return false; + } + return true; +} + +static boolean +coff_set_section_contents (abfd, section, location, offset, count) + bfd * abfd; + sec_ptr section; + PTR location; + file_ptr offset; + bfd_size_type count; +{ + if (abfd->output_has_begun == false) /* set by bfd.c handler */ + coff_compute_section_file_positions (abfd); + +#ifdef _LIB + /* If this is a .lib section, bump the vma address so that it + winds up being the number of .lib sections output. This is + right for SVR3.2. Shared libraries should probably get more + generic support. Ian Taylor <ian@cygnus.com>. */ + if (strcmp (section->name, _LIB) == 0) + ++section->lma; +#endif + + /* Don't write out bss sections - one way to do this is to + see if the filepos has not been set. */ + if (section->filepos == 0) + return true; + + if (bfd_seek (abfd, (file_ptr) (section->filepos + offset), SEEK_SET) != 0) + return false; + + if (count != 0) + { + return (bfd_write (location, 1, count, abfd) == count) ? true : false; + } + return true; +} +#if 0 +static boolean +coff_close_and_cleanup (abfd) + bfd *abfd; +{ + if (!bfd_read_p (abfd)) + switch (abfd->format) + { + case bfd_archive: + if (!_bfd_write_archive_contents (abfd)) + return false; + break; + case bfd_object: + if (!coff_write_object_contents (abfd)) + return false; + break; + default: + bfd_set_error (bfd_error_invalid_operation); + return false; + } + + /* We depend on bfd_close to free all the memory on the obstack. */ + /* FIXME if bfd_release is not using obstacks! */ + return true; +} + +#endif + +static PTR +buy_and_read (abfd, where, seek_direction, size) + bfd *abfd; + file_ptr where; + int seek_direction; + size_t size; +{ + PTR area = (PTR) bfd_alloc (abfd, size); + if (!area) + { + bfd_set_error (bfd_error_no_memory); + return (NULL); + } + if (bfd_seek (abfd, where, seek_direction) != 0 + || bfd_read (area, 1, size, abfd) != size) + return (NULL); + return (area); +} /* buy_and_read() */ + +/* +SUBSUBSECTION + Reading linenumbers + + Creating the linenumber table is done by reading in the entire + coff linenumber table, and creating another table for internal use. + + A coff linenumber table is structured so that each function + is marked as having a line number of 0. Each line within the + function is an offset from the first line in the function. The + base of the line number information for the table is stored in + the symbol associated with the function. + + The information is copied from the external to the internal + table, and each symbol which marks a function is marked by + pointing its... + + How does this work ? + +*/ + +static boolean +coff_slurp_line_table (abfd, asect) + bfd *abfd; + asection *asect; +{ + LINENO *native_lineno; + alent *lineno_cache; + + BFD_ASSERT (asect->lineno == (alent *) NULL); + + native_lineno = (LINENO *) buy_and_read (abfd, + asect->line_filepos, + SEEK_SET, + (size_t) (LINESZ * + asect->lineno_count)); + lineno_cache = + (alent *) bfd_alloc (abfd, (size_t) ((asect->lineno_count + 1) * sizeof (alent))); + if (lineno_cache == NULL) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + else + { + unsigned int counter = 0; + alent *cache_ptr = lineno_cache; + LINENO *src = native_lineno; + + while (counter < asect->lineno_count) + { + struct internal_lineno dst; + coff_swap_lineno_in (abfd, src, &dst); + cache_ptr->line_number = dst.l_lnno; + + if (cache_ptr->line_number == 0) + { + coff_symbol_type *sym = + (coff_symbol_type *) (dst.l_addr.l_symndx + + obj_raw_syments (abfd))->u.syment._n._n_n._n_zeroes; + cache_ptr->u.sym = (asymbol *) sym; + sym->lineno = cache_ptr; + } + else + { + cache_ptr->u.offset = dst.l_addr.l_paddr + - bfd_section_vma (abfd, asect); + } /* If no linenumber expect a symbol index */ + + cache_ptr++; + src++; + counter++; + } + cache_ptr->line_number = 0; + + } + asect->lineno = lineno_cache; + /* FIXME, free native_lineno here, or use alloca or something. */ + return true; +} + +static boolean +coff_slurp_symbol_table (abfd) + bfd * abfd; +{ + combined_entry_type *native_symbols; + coff_symbol_type *cached_area; + unsigned int *table_ptr; + + unsigned int number_of_symbols = 0; + if (obj_symbols (abfd)) + return true; + if (bfd_seek (abfd, obj_sym_filepos (abfd), SEEK_SET) != 0) + return false; + + /* Read in the symbol table */ + if ((native_symbols = coff_get_normalized_symtab (abfd)) == NULL) + { + return (false); + } /* 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))); + + 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))); + + if (table_ptr == NULL) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + else + { + coff_symbol_type *dst = cached_area; + unsigned int last_native_index = bfd_get_symcount (abfd); + unsigned int this_index = 0; + while (this_index < last_native_index) + { + combined_entry_type *src = native_symbols + this_index; + table_ptr[this_index] = number_of_symbols; + dst->symbol.the_bfd = abfd; + + dst->symbol.name = (char *) (src->u.syment._n._n_n._n_offset); + /* We use the native name field to point to the cached field. */ + src->u.syment._n._n_n._n_zeroes = (long) dst; + dst->symbol.section = coff_section_from_bfd_index (abfd, + src->u.syment.n_scnum); + dst->symbol.flags = 0; + dst->done_lineno = false; + + switch (src->u.syment.n_sclass) + { +#ifdef I960 + case C_LEAFEXT: +#if 0 + dst->symbol.value = src->u.syment.n_value - dst->symbol.section->vma; + dst->symbol.flags = BSF_EXPORT | BSF_GLOBAL; + dst->symbol.flags |= BSF_NOT_AT_END; +#endif + /* Fall through to next case */ + +#endif + + case C_EXT: +#ifdef RS6000COFF_C + case C_HIDEXT: +#endif + if ((src->u.syment.n_scnum) == 0) + { + if ((src->u.syment.n_value) == 0) + { + dst->symbol.section = bfd_und_section_ptr; + dst->symbol.value = 0; + } + else + { + dst->symbol.section = bfd_com_section_ptr; + dst->symbol.value = (src->u.syment.n_value); + } + } + else + { + /* + Base the value as an index from the base of the + section + */ + + dst->symbol.flags = BSF_EXPORT | BSF_GLOBAL; + dst->symbol.value = src->u.syment.n_value - dst->symbol.section->vma; + + if (ISFCN ((src->u.syment.n_type))) + { + /* + A function ext does not go at the end of a file + */ + dst->symbol.flags |= BSF_NOT_AT_END; + } + } + +#ifdef RS6000COFF_C + /* If this symbol has a csect aux of type LD, the scnlen field + is actually the index of the containing csect symbol. We + need to pointerize it. */ + if (src->u.syment.n_numaux > 0) + { + combined_entry_type *aux; + + aux = src + src->u.syment.n_numaux - 1; + if (SMTYP_SMTYP (aux->u.auxent.x_csect.x_smtyp) == XTY_LD) + { + aux->u.auxent.x_csect.x_scnlen.p = + native_symbols + aux->u.auxent.x_csect.x_scnlen.l; + aux->fix_scnlen = 1; + } + } +#endif + + break; + + case C_STAT: /* static */ +#ifdef I960 + case C_LEAFSTAT: /* static leaf procedure */ +#endif + case C_LABEL: /* label */ + if (src->u.syment.n_scnum == -2) + dst->symbol.flags = BSF_DEBUGGING; + else + dst->symbol.flags = BSF_LOCAL; + /* + Base the value as an index from the base of the section, if + there is one + */ + if (dst->symbol.section) + dst->symbol.value = (src->u.syment.n_value) - + dst->symbol.section->vma; + else + dst->symbol.value = (src->u.syment.n_value); + break; + + case C_MOS: /* member of structure */ + case C_EOS: /* end of structure */ +#ifdef NOTDEF /* C_AUTOARG has the same value */ +#ifdef C_GLBLREG + case C_GLBLREG: /* A29k-specific storage class */ +#endif +#endif + case C_REGPARM: /* register parameter */ + case C_REG: /* register variable */ +#ifdef C_AUTOARG + case C_AUTOARG: /* 960-specific storage class */ +#endif + case C_TPDEF: /* type definition */ + case C_ARG: + case C_AUTO: /* automatic variable */ + case C_FIELD: /* bit field */ + case C_ENTAG: /* enumeration tag */ + case C_MOE: /* member of enumeration */ + case C_MOU: /* member of union */ + case C_UNTAG: /* union tag */ + dst->symbol.flags = BSF_DEBUGGING; + dst->symbol.value = (src->u.syment.n_value); + break; + + case C_FILE: /* file name */ + case C_STRTAG: /* structure tag */ +#ifdef RS6000COFF_C + case C_BINCL: /* beginning of include file */ + case C_EINCL: /* ending of include file */ + case C_GSYM: + case C_LSYM: + case C_PSYM: + case C_RSYM: + case C_RPSYM: + case C_STSYM: + case C_DECL: + case C_ENTRY: + case C_FUN: + case C_ESTAT: +#endif + dst->symbol.flags = BSF_DEBUGGING; + dst->symbol.value = (src->u.syment.n_value); + break; + +#ifdef RS6000COFF_C + case C_BSTAT: + dst->symbol.flags = BSF_DEBUGGING; + dst->symbol.value = src->u.syment.n_value; + + /* The value is actually a symbol index. Save a pointer to + the symbol instead of the index. FIXME: This should use a + union. */ + src->u.syment.n_value = + (long) (native_symbols + src->u.syment.n_value); + src->fix_value = 1; + break; +#endif + + case C_BLOCK: /* ".bb" or ".eb" */ + case C_FCN: /* ".bf" or ".ef" */ + case C_EFCN: /* physical end of function */ + dst->symbol.flags = BSF_LOCAL; + /* + Base the value as an index from the base of the section + */ + dst->symbol.value = (src->u.syment.n_value) - dst->symbol.section->vma; + break; + + case C_NULL: + case C_EXTDEF: /* external definition */ + case C_ULABEL: /* undefined label */ + case C_USTATIC: /* undefined static */ + case C_LINE: /* line # reformatted as symbol table entry */ + case C_ALIAS: /* duplicate tag */ + case C_HIDDEN: /* ext symbol in dmert public lib */ + default: + + fprintf (stderr, "Unrecognized storage class %d (assuming debugging)\n for %s symbol `%s'\n", + src->u.syment.n_sclass, dst->symbol.section->name, + dst->symbol.name); +/* abort();*/ + dst->symbol.flags = BSF_DEBUGGING; + dst->symbol.value = (src->u.syment.n_value); + break; + } + +/* BFD_ASSERT(dst->symbol.flags != 0);*/ + + dst->native = src; + + dst->symbol.udata = 0; + dst->lineno = (alent *) NULL; + this_index += (src->u.syment.n_numaux) + 1; + dst++; + number_of_symbols++; + } /* walk the native symtab */ + } /* bfdize the native symtab */ + + 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 */ + { + asection *p; + p = abfd->sections; + while (p) + { + coff_slurp_line_table (abfd, p); + p = p->next; + } + } + return true; +} /* coff_slurp_symbol_table() */ + +/* +SUBSUBSECTION + Reading relocations + + Coff relocations are easily transformed into the internal BFD form + (@code{arelent}). + + Reading a coff relocation table is done in the following stages: + + o Read the entire coff relocation table into memory. + + o Process each relocation in turn; first swap it from the + external to the internal form. + + o Turn the symbol referenced in the relocation's symbol index + into a pointer into the canonical symbol table. + This table is the same as the one returned by a call to + @code{bfd_canonicalize_symtab}. The back end will call that + routine and save the result if a canonicalization hasn't been done. + + o The reloc index is turned into a pointer to a howto + structure, in a back end specific way. For instance, the 386 + and 960 use the @code{r_type} to directly produce an index + into a howto table vector; the 88k subtracts a number from the + @code{r_type} field and creates an addend field. + + +*/ + +#ifndef CALC_ADDEND +#define CALC_ADDEND(abfd, ptr, reloc, cache_ptr) \ + { \ + coff_symbol_type *coffsym = (coff_symbol_type *) NULL; \ + if (ptr && bfd_asymbol_bfd (ptr) != abfd) \ + coffsym = (obj_symbols (abfd) \ + + (cache_ptr->sym_ptr_ptr - symbols)); \ + else if (ptr) \ + coffsym = coff_symbol_from (abfd, ptr); \ + if (coffsym != (coff_symbol_type *) NULL \ + && coffsym->native->u.syment.n_scnum == 0) \ + cache_ptr->addend = 0; \ + else if (ptr && bfd_asymbol_bfd (ptr) == abfd \ + && ptr->section != (asection *) NULL) \ + cache_ptr->addend = - (ptr->section->vma + ptr->value); \ + else \ + cache_ptr->addend = 0; \ + } +#endif + +static boolean +coff_slurp_reloc_table (abfd, asect, symbols) + bfd * abfd; + sec_ptr asect; + asymbol ** symbols; +{ + RELOC *native_relocs; + arelent *reloc_cache; + arelent *cache_ptr; + + unsigned int idx; + + if (asect->relocation) + return true; + if (asect->reloc_count == 0) + return true; + if (asect->flags & SEC_CONSTRUCTOR) + return true; + if (!coff_slurp_symbol_table (abfd)) + return false; + native_relocs = + (RELOC *) buy_and_read (abfd, + asect->rel_filepos, + SEEK_SET, + (size_t) (RELSZ * + asect->reloc_count)); + reloc_cache = (arelent *) + bfd_alloc (abfd, (size_t) (asect->reloc_count * sizeof (arelent))); + + if (reloc_cache == NULL) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + + + for (idx = 0; idx < asect->reloc_count; idx++) + { +#ifdef RELOC_PROCESSING + struct internal_reloc dst; + struct external_reloc *src; + + cache_ptr = reloc_cache + idx; + src = native_relocs + idx; + bfd_swap_reloc_in (abfd, src, &dst); + + RELOC_PROCESSING (cache_ptr, &dst, symbols, abfd, asect); +#else + struct internal_reloc dst; + asymbol *ptr; + struct external_reloc *src; + + cache_ptr = reloc_cache + idx; + src = native_relocs + idx; + + bfd_swap_reloc_in (abfd, src, &dst); + + + cache_ptr->address = dst.r_vaddr; + + if (dst.r_symndx != -1) + { + /* @@ Should never be greater than count of symbols! */ + if (dst.r_symndx >= obj_conv_table_size (abfd)) + abort (); + cache_ptr->sym_ptr_ptr = symbols + obj_convert (abfd)[dst.r_symndx]; + ptr = *(cache_ptr->sym_ptr_ptr); + } + else + { + cache_ptr->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr; + ptr = 0; + } + + /* The symbols definitions that we have read in have been + relocated as if their sections started at 0. But the offsets + refering to the symbols in the raw data have not been + modified, so we have to have a negative addend to compensate. + + Note that symbols which used to be common must be left alone */ + + /* Calculate any reloc addend by looking at the symbol */ + CALC_ADDEND (abfd, ptr, dst, cache_ptr); + + cache_ptr->address -= asect->vma; +/* !! cache_ptr->section = (asection *) NULL;*/ + + /* Fill in the cache_ptr->howto field from dst.r_type */ + RTYPE2HOWTO (cache_ptr, &dst); +#endif + + } + + asect->relocation = reloc_cache; + return true; +} + + +/* This is stupid. This function should be a boolean predicate. */ +static long +coff_canonicalize_reloc (abfd, section, relptr, symbols) + bfd * abfd; + sec_ptr section; + arelent ** relptr; + asymbol ** symbols; +{ + arelent *tblptr = section->relocation; + unsigned int count = 0; + + + if (section->flags & SEC_CONSTRUCTOR) + { + /* this section has relocs made up by us, they are not in the + file, so take them out of their chain and place them into + the data area provided */ + arelent_chain *chain = section->constructor_chain; + for (count = 0; count < section->reloc_count; count++) + { + *relptr++ = &chain->relent; + chain = chain->next; + } + + } + else + { + if (! coff_slurp_reloc_table (abfd, section, symbols)) + return -1; + + tblptr = section->relocation; + + for (; count++ < section->reloc_count;) + *relptr++ = tblptr++; + + + } + *relptr = 0; + return section->reloc_count; +} + +#ifdef GNU960 +file_ptr +coff_sym_filepos (abfd) + bfd *abfd; +{ + return obj_sym_filepos (abfd); +} +#endif + +#ifndef coff_reloc16_estimate +#define coff_reloc16_estimate dummy_reloc16_estimate + +static int +dummy_reloc16_estimate (abfd, input_section, reloc, shrink, link_info) + bfd *abfd; + asection *input_section; + arelent *reloc; + unsigned int shrink; + struct bfd_link_info *link_info; +{ + abort (); +} + +#endif + +#ifndef coff_reloc16_extra_cases +#define coff_reloc16_extra_cases dummy_reloc16_extra_cases +/* This works even if abort is not declared in any header file. */ +static void +dummy_reloc16_extra_cases (abfd, link_info, link_order, reloc, data, src_ptr, + dst_ptr) + bfd *abfd; + struct bfd_link_info *link_info; + struct bfd_link_order *link_order; + arelent *reloc; + bfd_byte *data; + unsigned int *src_ptr; + unsigned int *dst_ptr; +{ + fprintf (stderr, "%s\n", reloc->howto->name); + abort (); +} +#endif + +static CONST bfd_coff_backend_data bfd_coff_std_swap_table = +{ + coff_swap_aux_in, coff_swap_sym_in, coff_swap_lineno_in, + coff_swap_aux_out, coff_swap_sym_out, + 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, +#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 +}; + +#define coff_close_and_cleanup _bfd_generic_close_and_cleanup +#define coff_bfd_free_cached_info _bfd_generic_bfd_free_cached_info +#define coff_get_section_contents _bfd_generic_get_section_contents + +#define coff_bfd_copy_private_section_data \ + _bfd_generic_bfd_copy_private_section_data +#define coff_bfd_copy_private_bfd_data _bfd_generic_bfd_copy_private_bfd_data + +#ifndef coff_bfd_is_local_label +#define coff_bfd_is_local_label bfd_generic_is_local_label +#endif + +/* The reloc lookup routine must be supplied by each individual COFF + backend. */ +#ifndef coff_bfd_reloc_type_lookup +#define coff_bfd_reloc_type_lookup _bfd_norelocs_bfd_reloc_type_lookup +#endif + +#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/gnu/usr.bin/gdb/bfd/coffswap.h b/gnu/usr.bin/gdb/bfd/coffswap.h new file mode 100644 index 000000000000..74cc46ebe21f --- /dev/null +++ b/gnu/usr.bin/gdb/bfd/coffswap.h @@ -0,0 +1,741 @@ +/* Generic COFF swapping routines, for BFD. + Copyright 1990, 1991, 1992, 1993 Free Software Foundation, Inc. + Written by 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 routines used to swap COFF data. It is a header + file because the details of swapping depend on the details of the + structures used by each COFF implementation. This is included by + coffcode.h, as well as by the ECOFF backend. + + Any file which uses this must first include "coff/internal.h" and + "coff/CPU.h". The functions will then be correct for that CPU. */ + +#define PUTWORD bfd_h_put_32 +#define PUTHALF bfd_h_put_16 +#define PUTBYTE bfd_h_put_8 + +#ifndef GET_FCN_LNNOPTR +#define GET_FCN_LNNOPTR(abfd, ext) bfd_h_get_32(abfd, (bfd_byte *) ext->x_sym.x_fcnary.x_fcn.x_lnnoptr) +#endif + +#ifndef GET_FCN_ENDNDX +#define GET_FCN_ENDNDX(abfd, ext) bfd_h_get_32(abfd, (bfd_byte *) ext->x_sym.x_fcnary.x_fcn.x_endndx) +#endif + +#ifndef PUT_FCN_LNNOPTR +#define PUT_FCN_LNNOPTR(abfd, in, ext) PUTWORD(abfd, in, (bfd_byte *) ext->x_sym.x_fcnary.x_fcn.x_lnnoptr) +#endif +#ifndef PUT_FCN_ENDNDX +#define PUT_FCN_ENDNDX(abfd, in, ext) PUTWORD(abfd, in, (bfd_byte *) ext->x_sym.x_fcnary.x_fcn.x_endndx) +#endif +#ifndef GET_LNSZ_LNNO +#define GET_LNSZ_LNNO(abfd, ext) bfd_h_get_16(abfd, (bfd_byte *) ext->x_sym.x_misc.x_lnsz.x_lnno) +#endif +#ifndef GET_LNSZ_SIZE +#define GET_LNSZ_SIZE(abfd, ext) bfd_h_get_16(abfd, (bfd_byte *) ext->x_sym.x_misc.x_lnsz.x_size) +#endif +#ifndef PUT_LNSZ_LNNO +#define PUT_LNSZ_LNNO(abfd, in, ext) bfd_h_put_16(abfd, in, (bfd_byte *)ext->x_sym.x_misc.x_lnsz.x_lnno) +#endif +#ifndef PUT_LNSZ_SIZE +#define PUT_LNSZ_SIZE(abfd, in, ext) bfd_h_put_16(abfd, in, (bfd_byte*) ext->x_sym.x_misc.x_lnsz.x_size) +#endif +#ifndef GET_SCN_SCNLEN +#define GET_SCN_SCNLEN(abfd, ext) bfd_h_get_32(abfd, (bfd_byte *) ext->x_scn.x_scnlen) +#endif +#ifndef GET_SCN_NRELOC +#define GET_SCN_NRELOC(abfd, ext) bfd_h_get_16(abfd, (bfd_byte *)ext->x_scn.x_nreloc) +#endif +#ifndef GET_SCN_NLINNO +#define GET_SCN_NLINNO(abfd, ext) bfd_h_get_16(abfd, (bfd_byte *)ext->x_scn.x_nlinno) +#endif +#ifndef PUT_SCN_SCNLEN +#define PUT_SCN_SCNLEN(abfd,in, ext) bfd_h_put_32(abfd, in, (bfd_byte *) ext->x_scn.x_scnlen) +#endif +#ifndef PUT_SCN_NRELOC +#define PUT_SCN_NRELOC(abfd,in, ext) bfd_h_put_16(abfd, in, (bfd_byte *)ext->x_scn.x_nreloc) +#endif +#ifndef PUT_SCN_NLINNO +#define PUT_SCN_NLINNO(abfd,in, ext) bfd_h_put_16(abfd,in, (bfd_byte *) ext->x_scn.x_nlinno) +#endif +#ifndef GET_LINENO_LNNO +#define GET_LINENO_LNNO(abfd, ext) bfd_h_get_16(abfd, (bfd_byte *) (ext->l_lnno)); +#endif +#ifndef PUT_LINENO_LNNO +#define PUT_LINENO_LNNO(abfd,val, ext) bfd_h_put_16(abfd,val, (bfd_byte *) (ext->l_lnno)); +#endif + +/* The f_symptr field in the filehdr is sometimes 64 bits. */ +#ifndef GET_FILEHDR_SYMPTR +#define GET_FILEHDR_SYMPTR bfd_h_get_32 +#endif +#ifndef PUT_FILEHDR_SYMPTR +#define PUT_FILEHDR_SYMPTR bfd_h_put_32 +#endif + +/* Some fields in the aouthdr are sometimes 64 bits. */ +#ifndef GET_AOUTHDR_TSIZE +#define GET_AOUTHDR_TSIZE bfd_h_get_32 +#endif +#ifndef PUT_AOUTHDR_TSIZE +#define PUT_AOUTHDR_TSIZE bfd_h_put_32 +#endif +#ifndef GET_AOUTHDR_DSIZE +#define GET_AOUTHDR_DSIZE bfd_h_get_32 +#endif +#ifndef PUT_AOUTHDR_DSIZE +#define PUT_AOUTHDR_DSIZE bfd_h_put_32 +#endif +#ifndef GET_AOUTHDR_BSIZE +#define GET_AOUTHDR_BSIZE bfd_h_get_32 +#endif +#ifndef PUT_AOUTHDR_BSIZE +#define PUT_AOUTHDR_BSIZE bfd_h_put_32 +#endif +#ifndef GET_AOUTHDR_ENTRY +#define GET_AOUTHDR_ENTRY bfd_h_get_32 +#endif +#ifndef PUT_AOUTHDR_ENTRY +#define PUT_AOUTHDR_ENTRY bfd_h_put_32 +#endif +#ifndef GET_AOUTHDR_TEXT_START +#define GET_AOUTHDR_TEXT_START bfd_h_get_32 +#endif +#ifndef PUT_AOUTHDR_TEXT_START +#define PUT_AOUTHDR_TEXT_START bfd_h_put_32 +#endif +#ifndef GET_AOUTHDR_DATA_START +#define GET_AOUTHDR_DATA_START bfd_h_get_32 +#endif +#ifndef PUT_AOUTHDR_DATA_START +#define PUT_AOUTHDR_DATA_START bfd_h_put_32 +#endif + +/* Some fields in the scnhdr are sometimes 64 bits. */ +#ifndef GET_SCNHDR_PADDR +#define GET_SCNHDR_PADDR bfd_h_get_32 +#endif +#ifndef PUT_SCNHDR_PADDR +#define PUT_SCNHDR_PADDR bfd_h_put_32 +#endif +#ifndef GET_SCNHDR_VADDR +#define GET_SCNHDR_VADDR bfd_h_get_32 +#endif +#ifndef PUT_SCNHDR_VADDR +#define PUT_SCNHDR_VADDR bfd_h_put_32 +#endif +#ifndef GET_SCNHDR_SIZE +#define GET_SCNHDR_SIZE bfd_h_get_32 +#endif +#ifndef PUT_SCNHDR_SIZE +#define PUT_SCNHDR_SIZE bfd_h_put_32 +#endif +#ifndef GET_SCNHDR_SCNPTR +#define GET_SCNHDR_SCNPTR bfd_h_get_32 +#endif +#ifndef PUT_SCNHDR_SCNPTR +#define PUT_SCNHDR_SCNPTR bfd_h_put_32 +#endif +#ifndef GET_SCNHDR_RELPTR +#define GET_SCNHDR_RELPTR bfd_h_get_32 +#endif +#ifndef PUT_SCNHDR_RELPTR +#define PUT_SCNHDR_RELPTR bfd_h_put_32 +#endif +#ifndef GET_SCNHDR_LNNOPTR +#define GET_SCNHDR_LNNOPTR bfd_h_get_32 +#endif +#ifndef PUT_SCNHDR_LNNOPTR +#define PUT_SCNHDR_LNNOPTR bfd_h_put_32 +#endif + +#ifndef NO_COFF_RELOCS + +static void +bfd_swap_reloc_in (abfd, reloc_src, reloc_dst) + bfd *abfd; + RELOC *reloc_src; + struct internal_reloc *reloc_dst; +{ + reloc_dst->r_vaddr = bfd_h_get_32(abfd, (bfd_byte *)reloc_src->r_vaddr); + reloc_dst->r_symndx = bfd_h_get_signed_32(abfd, (bfd_byte *) reloc_src->r_symndx); + +#ifdef RS6000COFF_C + reloc_dst->r_type = bfd_h_get_8(abfd, reloc_src->r_type); + reloc_dst->r_size = bfd_h_get_8(abfd, reloc_src->r_size); +#else + reloc_dst->r_type = bfd_h_get_16(abfd, (bfd_byte *) reloc_src->r_type); +#endif + +#ifdef SWAP_IN_RELOC_OFFSET + reloc_dst->r_offset = SWAP_IN_RELOC_OFFSET(abfd, + (bfd_byte *) reloc_src->r_offset); +#endif +} + + +static unsigned int +coff_swap_reloc_out (abfd, src, dst) + bfd *abfd; + PTR src; + PTR dst; +{ + struct internal_reloc *reloc_src = (struct internal_reloc *)src; + struct external_reloc *reloc_dst = (struct external_reloc *)dst; + bfd_h_put_32(abfd, reloc_src->r_vaddr, (bfd_byte *) reloc_dst->r_vaddr); + bfd_h_put_32(abfd, reloc_src->r_symndx, (bfd_byte *) reloc_dst->r_symndx); + +#ifdef RS6000COFF_C + bfd_h_put_8 (abfd, reloc_src->r_type, (bfd_byte *) reloc_dst->r_type); + bfd_h_put_8 (abfd, reloc_src->r_size, (bfd_byte *) reloc_dst->r_size); +#else + bfd_h_put_16(abfd, reloc_src->r_type, (bfd_byte *) + reloc_dst->r_type); +#endif + +#ifdef SWAP_OUT_RELOC_OFFSET + SWAP_OUT_RELOC_OFFSET(abfd, + reloc_src->r_offset, + (bfd_byte *) reloc_dst->r_offset); +#endif +#ifdef SWAP_OUT_RELOC_EXTRA + SWAP_OUT_RELOC_EXTRA(abfd,reloc_src, reloc_dst); +#endif + + return sizeof(struct external_reloc); +} + +#endif /* NO_COFF_RELOCS */ + +static void +coff_swap_filehdr_in (abfd, src, dst) + bfd *abfd; + PTR src; + PTR dst; +{ + FILHDR *filehdr_src = (FILHDR *) src; + struct internal_filehdr *filehdr_dst = (struct internal_filehdr *) dst; + filehdr_dst->f_magic = bfd_h_get_16(abfd, (bfd_byte *) filehdr_src->f_magic); + filehdr_dst->f_nscns = bfd_h_get_16(abfd, (bfd_byte *)filehdr_src-> f_nscns); + filehdr_dst->f_timdat = bfd_h_get_32(abfd, (bfd_byte *)filehdr_src-> f_timdat); + filehdr_dst->f_symptr = + GET_FILEHDR_SYMPTR (abfd, (bfd_byte *) filehdr_src->f_symptr); + filehdr_dst->f_nsyms = bfd_h_get_32(abfd, (bfd_byte *)filehdr_src-> f_nsyms); + filehdr_dst->f_opthdr = bfd_h_get_16(abfd, (bfd_byte *)filehdr_src-> f_opthdr); + filehdr_dst->f_flags = bfd_h_get_16(abfd, (bfd_byte *)filehdr_src-> f_flags); +} + +static unsigned int +coff_swap_filehdr_out (abfd, in, out) + bfd *abfd; + PTR in; + PTR out; +{ + struct internal_filehdr *filehdr_in = (struct internal_filehdr *)in; + FILHDR *filehdr_out = (FILHDR *)out; + bfd_h_put_16(abfd, filehdr_in->f_magic, (bfd_byte *) filehdr_out->f_magic); + bfd_h_put_16(abfd, filehdr_in->f_nscns, (bfd_byte *) filehdr_out->f_nscns); + bfd_h_put_32(abfd, filehdr_in->f_timdat, (bfd_byte *) filehdr_out->f_timdat); + PUT_FILEHDR_SYMPTR (abfd, (bfd_vma) filehdr_in->f_symptr, + (bfd_byte *) filehdr_out->f_symptr); + bfd_h_put_32(abfd, filehdr_in->f_nsyms, (bfd_byte *) filehdr_out->f_nsyms); + bfd_h_put_16(abfd, filehdr_in->f_opthdr, (bfd_byte *) filehdr_out->f_opthdr); + bfd_h_put_16(abfd, filehdr_in->f_flags, (bfd_byte *) filehdr_out->f_flags); + return sizeof(FILHDR); +} + + +#ifndef NO_COFF_SYMBOLS + +static void +coff_swap_sym_in (abfd, ext1, in1) + bfd *abfd; + PTR ext1; + PTR in1; +{ + SYMENT *ext = (SYMENT *)ext1; + struct internal_syment *in = (struct internal_syment *)in1; + + if( ext->e.e_name[0] == 0) { + in->_n._n_n._n_zeroes = 0; + in->_n._n_n._n_offset = bfd_h_get_32(abfd, (bfd_byte *) ext->e.e.e_offset); + } + else { +#if SYMNMLEN != E_SYMNMLEN + -> Error, we need to cope with truncating or extending SYMNMLEN!; +#else + memcpy(in->_n._n_name, ext->e.e_name, SYMNMLEN); +#endif + } + in->n_value = bfd_h_get_32(abfd, (bfd_byte *) ext->e_value); + in->n_scnum = bfd_h_get_16(abfd, (bfd_byte *) ext->e_scnum); + if (sizeof(ext->e_type) == 2){ + in->n_type = bfd_h_get_16(abfd, (bfd_byte *) ext->e_type); + } + else { + in->n_type = bfd_h_get_32(abfd, (bfd_byte *) ext->e_type); + } + in->n_sclass = bfd_h_get_8(abfd, ext->e_sclass); + in->n_numaux = bfd_h_get_8(abfd, ext->e_numaux); +} + +static unsigned int +coff_swap_sym_out (abfd, inp, extp) + bfd *abfd; + PTR inp; + PTR extp; +{ + struct internal_syment *in = (struct internal_syment *)inp; + SYMENT *ext =(SYMENT *)extp; + if(in->_n._n_name[0] == 0) { + bfd_h_put_32(abfd, 0, (bfd_byte *) ext->e.e.e_zeroes); + bfd_h_put_32(abfd, in->_n._n_n._n_offset, (bfd_byte *) ext->e.e.e_offset); + } + else { +#if SYMNMLEN != E_SYMNMLEN + -> Error, we need to cope with truncating or extending SYMNMLEN!; +#else + memcpy(ext->e.e_name, in->_n._n_name, SYMNMLEN); +#endif + } + bfd_h_put_32(abfd, in->n_value , (bfd_byte *) ext->e_value); + bfd_h_put_16(abfd, in->n_scnum , (bfd_byte *) ext->e_scnum); + if (sizeof(ext->e_type) == 2) + { + bfd_h_put_16(abfd, in->n_type , (bfd_byte *) ext->e_type); + } + else + { + bfd_h_put_32(abfd, in->n_type , (bfd_byte *) ext->e_type); + } + bfd_h_put_8(abfd, in->n_sclass , ext->e_sclass); + bfd_h_put_8(abfd, in->n_numaux , ext->e_numaux); + return sizeof(SYMENT); +} + +static void +coff_swap_aux_in (abfd, ext1, type, class, indx, numaux, in1) + bfd *abfd; + PTR ext1; + int type; + int class; + int indx; + int numaux; + PTR in1; +{ + AUXENT *ext = (AUXENT *)ext1; + union internal_auxent *in = (union internal_auxent *)in1; + + switch (class) { + case C_FILE: + if (ext->x_file.x_fname[0] == 0) { + in->x_file.x_n.x_zeroes = 0; + in->x_file.x_n.x_offset = + bfd_h_get_32(abfd, (bfd_byte *) ext->x_file.x_n.x_offset); + } else { +#if FILNMLEN != E_FILNMLEN + -> Error, we need to cope with truncating or extending FILNMLEN!; +#else + memcpy (in->x_file.x_fname, ext->x_file.x_fname, FILNMLEN); +#endif + } + return; + + /* RS/6000 "csect" auxents */ +#ifdef RS6000COFF_C + case C_EXT: + case C_HIDEXT: + if (indx + 1 == numaux) + { + in->x_csect.x_scnlen.l = bfd_h_get_32 (abfd, ext->x_csect.x_scnlen); + in->x_csect.x_parmhash = bfd_h_get_32 (abfd, + ext->x_csect.x_parmhash); + in->x_csect.x_snhash = bfd_h_get_16 (abfd, ext->x_csect.x_snhash); + /* We don't have to hack bitfields in x_smtyp because it's + defined by shifts-and-ands, which are equivalent on all + byte orders. */ + in->x_csect.x_smtyp = bfd_h_get_8 (abfd, ext->x_csect.x_smtyp); + in->x_csect.x_smclas = bfd_h_get_8 (abfd, ext->x_csect.x_smclas); + in->x_csect.x_stab = bfd_h_get_32 (abfd, ext->x_csect.x_stab); + in->x_csect.x_snstab = bfd_h_get_16 (abfd, ext->x_csect.x_snstab); + return; + } + break; +#endif + + case C_STAT: +#ifdef C_LEAFSTAT + case C_LEAFSTAT: +#endif + case C_HIDDEN: + if (type == T_NULL) { + in->x_scn.x_scnlen = GET_SCN_SCNLEN(abfd, ext); + in->x_scn.x_nreloc = GET_SCN_NRELOC(abfd, ext); + in->x_scn.x_nlinno = GET_SCN_NLINNO(abfd, ext); + return; + } + break; + } + + in->x_sym.x_tagndx.l = bfd_h_get_32(abfd, (bfd_byte *) ext->x_sym.x_tagndx); +#ifndef NO_TVNDX + in->x_sym.x_tvndx = bfd_h_get_16(abfd, (bfd_byte *) ext->x_sym.x_tvndx); +#endif + + if (ISARY(type)) { +#if DIMNUM != E_DIMNUM + -> Error, we need to cope with truncating or extending DIMNUM!; +#else + in->x_sym.x_fcnary.x_ary.x_dimen[0] = bfd_h_get_16(abfd, (bfd_byte *) ext->x_sym.x_fcnary.x_ary.x_dimen[0]); + in->x_sym.x_fcnary.x_ary.x_dimen[1] = bfd_h_get_16(abfd, (bfd_byte *) ext->x_sym.x_fcnary.x_ary.x_dimen[1]); + in->x_sym.x_fcnary.x_ary.x_dimen[2] = bfd_h_get_16(abfd, (bfd_byte *) ext->x_sym.x_fcnary.x_ary.x_dimen[2]); + in->x_sym.x_fcnary.x_ary.x_dimen[3] = bfd_h_get_16(abfd, (bfd_byte *) ext->x_sym.x_fcnary.x_ary.x_dimen[3]); +#endif + } + if (class == C_BLOCK || ISFCN(type) || ISTAG(class)) { + in->x_sym.x_fcnary.x_fcn.x_lnnoptr = GET_FCN_LNNOPTR(abfd, ext); + in->x_sym.x_fcnary.x_fcn.x_endndx.l = GET_FCN_ENDNDX(abfd, ext); + } + + if (ISFCN(type)) { + in->x_sym.x_misc.x_fsize = bfd_h_get_32(abfd, (bfd_byte *) ext->x_sym.x_misc.x_fsize); + } + else { + in->x_sym.x_misc.x_lnsz.x_lnno = GET_LNSZ_LNNO(abfd, ext); + in->x_sym.x_misc.x_lnsz.x_size = GET_LNSZ_SIZE(abfd, ext); + } +} + +static unsigned int +coff_swap_aux_out (abfd, inp, type, class, indx, numaux, extp) + bfd *abfd; + PTR inp; + int type; + int class; + int indx; + int numaux; + PTR extp; +{ + union internal_auxent *in = (union internal_auxent *)inp; + AUXENT *ext = (AUXENT *)extp; + + memset((PTR)ext, 0, AUXESZ); + switch (class) { + case C_FILE: + if (in->x_file.x_fname[0] == 0) { + PUTWORD(abfd, 0, (bfd_byte *) ext->x_file.x_n.x_zeroes); + PUTWORD(abfd, + in->x_file.x_n.x_offset, + (bfd_byte *) ext->x_file.x_n.x_offset); + } + else { +#if FILNMLEN != E_FILNMLEN + -> Error, we need to cope with truncating or extending FILNMLEN!; +#else + memcpy (ext->x_file.x_fname, in->x_file.x_fname, FILNMLEN); +#endif + } + return sizeof (AUXENT); + +#ifdef RS6000COFF_C + /* RS/6000 "csect" auxents */ + case C_EXT: + case C_HIDEXT: + if (indx + 1 == numaux) + { + PUTWORD (abfd, in->x_csect.x_scnlen.l, ext->x_csect.x_scnlen); + PUTWORD (abfd, in->x_csect.x_parmhash, ext->x_csect.x_parmhash); + PUTHALF (abfd, in->x_csect.x_snhash, ext->x_csect.x_snhash); + /* We don't have to hack bitfields in x_smtyp because it's + defined by shifts-and-ands, which are equivalent on all + byte orders. */ + PUTBYTE (abfd, in->x_csect.x_smtyp, ext->x_csect.x_smtyp); + PUTBYTE (abfd, in->x_csect.x_smclas, ext->x_csect.x_smclas); + PUTWORD (abfd, in->x_csect.x_stab, ext->x_csect.x_stab); + PUTHALF (abfd, in->x_csect.x_snstab, ext->x_csect.x_snstab); + return sizeof (AUXENT); + } + break; +#endif + + case C_STAT: +#ifdef C_LEAFSTAT + case C_LEAFSTAT: +#endif + case C_HIDDEN: + if (type == T_NULL) { + PUT_SCN_SCNLEN(abfd, in->x_scn.x_scnlen, ext); + PUT_SCN_NRELOC(abfd, in->x_scn.x_nreloc, ext); + PUT_SCN_NLINNO(abfd, in->x_scn.x_nlinno, ext); + return sizeof (AUXENT); + } + break; + } + + PUTWORD(abfd, in->x_sym.x_tagndx.l, (bfd_byte *) ext->x_sym.x_tagndx); +#ifndef NO_TVNDX + bfd_h_put_16(abfd, in->x_sym.x_tvndx , (bfd_byte *) ext->x_sym.x_tvndx); +#endif + + if (class == C_BLOCK || ISFCN(type) || ISTAG(class)) { + PUT_FCN_LNNOPTR(abfd, in->x_sym.x_fcnary.x_fcn.x_lnnoptr, ext); + PUT_FCN_ENDNDX(abfd, in->x_sym.x_fcnary.x_fcn.x_endndx.l, ext); + } + + if (ISFCN(type)) { + PUTWORD(abfd, in->x_sym.x_misc.x_fsize, (bfd_byte *) ext->x_sym.x_misc.x_fsize); + } + else { + if (ISARY(type)) { +#if DIMNUM != E_DIMNUM + -> Error, we need to cope with truncating or extending DIMNUM!; +#else + bfd_h_put_16(abfd, in->x_sym.x_fcnary.x_ary.x_dimen[0], (bfd_byte *)ext->x_sym.x_fcnary.x_ary.x_dimen[0]); + bfd_h_put_16(abfd, in->x_sym.x_fcnary.x_ary.x_dimen[1], (bfd_byte *)ext->x_sym.x_fcnary.x_ary.x_dimen[1]); + bfd_h_put_16(abfd, in->x_sym.x_fcnary.x_ary.x_dimen[2], (bfd_byte *)ext->x_sym.x_fcnary.x_ary.x_dimen[2]); + bfd_h_put_16(abfd, in->x_sym.x_fcnary.x_ary.x_dimen[3], (bfd_byte *)ext->x_sym.x_fcnary.x_ary.x_dimen[3]); +#endif + } + PUT_LNSZ_LNNO(abfd, in->x_sym.x_misc.x_lnsz.x_lnno, ext); + PUT_LNSZ_SIZE(abfd, in->x_sym.x_misc.x_lnsz.x_size, ext); + } + return sizeof(AUXENT); +} + +#endif /* NO_COFF_SYMBOLS */ + +#ifndef NO_COFF_LINENOS + +static void +coff_swap_lineno_in (abfd, ext1, in1) + bfd *abfd; + PTR ext1; + PTR in1; +{ + LINENO *ext = (LINENO *)ext1; + struct internal_lineno *in = (struct internal_lineno *)in1; + + in->l_addr.l_symndx = bfd_h_get_32(abfd, (bfd_byte *) ext->l_addr.l_symndx); + in->l_lnno = GET_LINENO_LNNO(abfd, ext); +} + +static unsigned int +coff_swap_lineno_out (abfd, inp, outp) + bfd *abfd; + PTR inp; + PTR outp; +{ + struct internal_lineno *in = (struct internal_lineno *)inp; + struct external_lineno *ext = (struct external_lineno *)outp; + PUTWORD(abfd, in->l_addr.l_symndx, (bfd_byte *) + ext->l_addr.l_symndx); + + PUT_LINENO_LNNO (abfd, in->l_lnno, ext); + return sizeof(struct external_lineno); +} + +#endif /* NO_COFF_LINENOS */ + + +static void +coff_swap_aouthdr_in (abfd, aouthdr_ext1, aouthdr_int1) + bfd *abfd; + PTR aouthdr_ext1; + PTR aouthdr_int1; +{ + AOUTHDR *aouthdr_ext = (AOUTHDR *) aouthdr_ext1; + struct internal_aouthdr *aouthdr_int = (struct internal_aouthdr *)aouthdr_int1; + + aouthdr_int->magic = bfd_h_get_16(abfd, (bfd_byte *) aouthdr_ext->magic); + aouthdr_int->vstamp = bfd_h_get_16(abfd, (bfd_byte *) aouthdr_ext->vstamp); + aouthdr_int->tsize = + GET_AOUTHDR_TSIZE (abfd, (bfd_byte *) aouthdr_ext->tsize); + aouthdr_int->dsize = + GET_AOUTHDR_DSIZE (abfd, (bfd_byte *) aouthdr_ext->dsize); + aouthdr_int->bsize = + GET_AOUTHDR_BSIZE (abfd, (bfd_byte *) aouthdr_ext->bsize); + aouthdr_int->entry = + GET_AOUTHDR_ENTRY (abfd, (bfd_byte *) aouthdr_ext->entry); + aouthdr_int->text_start = + GET_AOUTHDR_TEXT_START (abfd, (bfd_byte *) aouthdr_ext->text_start); + aouthdr_int->data_start = + GET_AOUTHDR_DATA_START (abfd, (bfd_byte *) aouthdr_ext->data_start); + +#ifdef I960 + aouthdr_int->tagentries = bfd_h_get_32(abfd, (bfd_byte *) aouthdr_ext->tagentries); +#endif + +#ifdef APOLLO_M68 + bfd_h_put_32(abfd, aouthdr_int->o_inlib, (bfd_byte *) aouthdr_ext->o_inlib); + bfd_h_put_32(abfd, aouthdr_int->o_sri, (bfd_byte *) aouthdr_ext->o_sri); + bfd_h_put_32(abfd, aouthdr_int->vid[0], (bfd_byte *) aouthdr_ext->vid); + bfd_h_put_32(abfd, aouthdr_int->vid[1], (bfd_byte *) aouthdr_ext->vid + 4); +#endif + + +#ifdef RS6000COFF_C + aouthdr_int->o_toc = bfd_h_get_32(abfd, aouthdr_ext->o_toc); + aouthdr_int->o_snentry = bfd_h_get_16(abfd, aouthdr_ext->o_snentry); + aouthdr_int->o_sntext = bfd_h_get_16(abfd, aouthdr_ext->o_sntext); + aouthdr_int->o_sndata = bfd_h_get_16(abfd, aouthdr_ext->o_sndata); + aouthdr_int->o_sntoc = bfd_h_get_16(abfd, aouthdr_ext->o_sntoc); + aouthdr_int->o_snloader = bfd_h_get_16(abfd, aouthdr_ext->o_snloader); + aouthdr_int->o_snbss = bfd_h_get_16(abfd, aouthdr_ext->o_snbss); + aouthdr_int->o_algntext = bfd_h_get_16(abfd, aouthdr_ext->o_algntext); + aouthdr_int->o_algndata = bfd_h_get_16(abfd, aouthdr_ext->o_algndata); + aouthdr_int->o_modtype = bfd_h_get_16(abfd, aouthdr_ext->o_modtype); + aouthdr_int->o_maxstack = bfd_h_get_32(abfd, aouthdr_ext->o_maxstack); +#endif + +#ifdef MIPSECOFF + aouthdr_int->bss_start = bfd_h_get_32(abfd, aouthdr_ext->bss_start); + aouthdr_int->gp_value = bfd_h_get_32(abfd, aouthdr_ext->gp_value); + aouthdr_int->gprmask = bfd_h_get_32(abfd, aouthdr_ext->gprmask); + aouthdr_int->cprmask[0] = bfd_h_get_32(abfd, aouthdr_ext->cprmask[0]); + aouthdr_int->cprmask[1] = bfd_h_get_32(abfd, aouthdr_ext->cprmask[1]); + aouthdr_int->cprmask[2] = bfd_h_get_32(abfd, aouthdr_ext->cprmask[2]); + aouthdr_int->cprmask[3] = bfd_h_get_32(abfd, aouthdr_ext->cprmask[3]); +#endif + +#ifdef ALPHAECOFF + aouthdr_int->bss_start = bfd_h_get_64(abfd, aouthdr_ext->bss_start); + aouthdr_int->gp_value = bfd_h_get_64(abfd, aouthdr_ext->gp_value); + aouthdr_int->gprmask = bfd_h_get_32(abfd, aouthdr_ext->gprmask); + aouthdr_int->fprmask = bfd_h_get_32(abfd, aouthdr_ext->fprmask); +#endif +} + +static unsigned int +coff_swap_aouthdr_out (abfd, in, out) + bfd *abfd; + PTR in; + PTR out; +{ + struct internal_aouthdr *aouthdr_in = (struct internal_aouthdr *)in; + AOUTHDR *aouthdr_out = (AOUTHDR *)out; + + bfd_h_put_16(abfd, aouthdr_in->magic, (bfd_byte *) aouthdr_out->magic); + bfd_h_put_16(abfd, aouthdr_in->vstamp, (bfd_byte *) aouthdr_out->vstamp); + PUT_AOUTHDR_TSIZE (abfd, aouthdr_in->tsize, (bfd_byte *) aouthdr_out->tsize); + PUT_AOUTHDR_DSIZE (abfd, aouthdr_in->dsize, (bfd_byte *) aouthdr_out->dsize); + PUT_AOUTHDR_BSIZE (abfd, aouthdr_in->bsize, (bfd_byte *) aouthdr_out->bsize); + PUT_AOUTHDR_ENTRY (abfd, aouthdr_in->entry, (bfd_byte *) aouthdr_out->entry); + PUT_AOUTHDR_TEXT_START (abfd, aouthdr_in->text_start, + (bfd_byte *) aouthdr_out->text_start); + PUT_AOUTHDR_DATA_START (abfd, aouthdr_in->data_start, + (bfd_byte *) aouthdr_out->data_start); +#ifdef I960 + bfd_h_put_32(abfd, aouthdr_in->tagentries, (bfd_byte *) aouthdr_out->tagentries); +#endif + +#ifdef MIPSECOFF + bfd_h_put_32(abfd, aouthdr_in->bss_start, (bfd_byte *) aouthdr_out->bss_start); + bfd_h_put_32(abfd, aouthdr_in->gp_value, (bfd_byte *) aouthdr_out->gp_value); + bfd_h_put_32(abfd, aouthdr_in->gprmask, (bfd_byte *) aouthdr_out->gprmask); + bfd_h_put_32(abfd, aouthdr_in->cprmask[0], (bfd_byte *) aouthdr_out->cprmask[0]); + bfd_h_put_32(abfd, aouthdr_in->cprmask[1], (bfd_byte *) aouthdr_out->cprmask[1]); + bfd_h_put_32(abfd, aouthdr_in->cprmask[2], (bfd_byte *) aouthdr_out->cprmask[2]); + bfd_h_put_32(abfd, aouthdr_in->cprmask[3], (bfd_byte *) aouthdr_out->cprmask[3]); +#endif + +#ifdef ALPHAECOFF + /* FIXME: What does bldrev mean? */ + bfd_h_put_16(abfd, (bfd_vma) 2, (bfd_byte *) aouthdr_out->bldrev); + bfd_h_put_16(abfd, (bfd_vma) 0, (bfd_byte *) aouthdr_out->padding); + bfd_h_put_64(abfd, aouthdr_in->bss_start, (bfd_byte *) aouthdr_out->bss_start); + bfd_h_put_64(abfd, aouthdr_in->gp_value, (bfd_byte *) aouthdr_out->gp_value); + bfd_h_put_32(abfd, aouthdr_in->gprmask, (bfd_byte *) aouthdr_out->gprmask); + bfd_h_put_32(abfd, aouthdr_in->fprmask, (bfd_byte *) aouthdr_out->fprmask); +#endif + + return sizeof(AOUTHDR); +} + +static void +coff_swap_scnhdr_in (abfd, ext, in) + bfd *abfd; + PTR ext; + PTR in; +{ + SCNHDR *scnhdr_ext = (SCNHDR *) ext; + struct internal_scnhdr *scnhdr_int = (struct internal_scnhdr *) in; + + memcpy(scnhdr_int->s_name, scnhdr_ext->s_name, sizeof(scnhdr_int->s_name)); + scnhdr_int->s_vaddr = + GET_SCNHDR_VADDR (abfd, (bfd_byte *) scnhdr_ext->s_vaddr); + scnhdr_int->s_paddr = + GET_SCNHDR_PADDR (abfd, (bfd_byte *) scnhdr_ext->s_paddr); + scnhdr_int->s_size = + GET_SCNHDR_SIZE (abfd, (bfd_byte *) scnhdr_ext->s_size); + + scnhdr_int->s_scnptr = + GET_SCNHDR_SCNPTR (abfd, (bfd_byte *) scnhdr_ext->s_scnptr); + scnhdr_int->s_relptr = + GET_SCNHDR_RELPTR (abfd, (bfd_byte *) scnhdr_ext->s_relptr); + scnhdr_int->s_lnnoptr = + GET_SCNHDR_LNNOPTR (abfd, (bfd_byte *) scnhdr_ext->s_lnnoptr); + scnhdr_int->s_flags = bfd_h_get_32(abfd, (bfd_byte *) scnhdr_ext->s_flags); +#if defined(M88) + scnhdr_int->s_nreloc = bfd_h_get_32(abfd, (bfd_byte *) scnhdr_ext->s_nreloc); + scnhdr_int->s_nlnno = bfd_h_get_32(abfd, (bfd_byte *) scnhdr_ext->s_nlnno); +#else + scnhdr_int->s_nreloc = bfd_h_get_16(abfd, (bfd_byte *) scnhdr_ext->s_nreloc); + scnhdr_int->s_nlnno = bfd_h_get_16(abfd, (bfd_byte *) scnhdr_ext->s_nlnno); +#endif +#ifdef I960 + scnhdr_int->s_align = bfd_h_get_32(abfd, (bfd_byte *) scnhdr_ext->s_align); +#endif +} + +static unsigned int +coff_swap_scnhdr_out (abfd, in, out) + bfd *abfd; + PTR in; + PTR out; +{ + struct internal_scnhdr *scnhdr_int = (struct internal_scnhdr *)in; + SCNHDR *scnhdr_ext = (SCNHDR *)out; + + memcpy(scnhdr_ext->s_name, scnhdr_int->s_name, sizeof(scnhdr_int->s_name)); + PUT_SCNHDR_VADDR (abfd, scnhdr_int->s_vaddr, + (bfd_byte *) scnhdr_ext->s_vaddr); + PUT_SCNHDR_PADDR (abfd, scnhdr_int->s_paddr, + (bfd_byte *) scnhdr_ext->s_paddr); + PUT_SCNHDR_SIZE (abfd, scnhdr_int->s_size, + (bfd_byte *) scnhdr_ext->s_size); + PUT_SCNHDR_SCNPTR (abfd, scnhdr_int->s_scnptr, + (bfd_byte *) scnhdr_ext->s_scnptr); + PUT_SCNHDR_RELPTR (abfd, scnhdr_int->s_relptr, + (bfd_byte *) scnhdr_ext->s_relptr); + PUT_SCNHDR_LNNOPTR (abfd, scnhdr_int->s_lnnoptr, + (bfd_byte *) scnhdr_ext->s_lnnoptr); + PUTWORD(abfd, scnhdr_int->s_flags, (bfd_byte *) scnhdr_ext->s_flags); +#if defined(M88) + PUTWORD(abfd, scnhdr_int->s_nlnno, (bfd_byte *) scnhdr_ext->s_nlnno); + PUTWORD(abfd, scnhdr_int->s_nreloc, (bfd_byte *) scnhdr_ext->s_nreloc); +#else + PUTHALF(abfd, scnhdr_int->s_nlnno, (bfd_byte *) scnhdr_ext->s_nlnno); + PUTHALF(abfd, scnhdr_int->s_nreloc, (bfd_byte *) scnhdr_ext->s_nreloc); +#endif + +#if defined(I960) + PUTWORD(abfd, scnhdr_int->s_align, (bfd_byte *) scnhdr_ext->s_align); +#endif + return sizeof(SCNHDR); +} diff --git a/gnu/usr.bin/gdb/bfd/ecofflink.c b/gnu/usr.bin/gdb/bfd/ecofflink.c new file mode 100644 index 000000000000..784e87478f60 --- /dev/null +++ b/gnu/usr.bin/gdb/bfd/ecofflink.c @@ -0,0 +1,1616 @@ +/* Routines to link ECOFF debugging information. + Copyright 1993 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support, <ian@cygnus.com>. + +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. */ + +#include "bfd.h" +#include "sysdep.h" +#include "bfdlink.h" +#include "libbfd.h" +#include "obstack.h" +#include "coff/internal.h" +#include "coff/sym.h" +#include "coff/symconst.h" +#include "coff/ecoff.h" + +static boolean ecoff_add_bytes PARAMS ((char **buf, char **bufend, + size_t need)); +static struct bfd_hash_entry *string_hash_newfunc + PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, + const char *)); +static void ecoff_align_debug PARAMS ((bfd *abfd, + struct ecoff_debug_info *debug, + const struct ecoff_debug_swap *swap)); +static boolean ecoff_write_symhdr PARAMS ((bfd *, struct ecoff_debug_info *, + const struct ecoff_debug_swap *, + file_ptr where)); + +/* Obstack allocation and deallocation routines. */ +#define obstack_chunk_alloc malloc +#define obstack_chunk_free free + +/* The minimum amount of data to allocate. */ +#define ALLOC_SIZE (4064) + +/* Add bytes to a buffer. Return success. */ + +static boolean +ecoff_add_bytes (buf, bufend, need) + char **buf; + char **bufend; + size_t need; +{ + size_t have; + size_t want; + char *newbuf; + + have = *bufend - *buf; + if (have > need) + want = ALLOC_SIZE; + else + { + want = need - have; + if (want < ALLOC_SIZE) + want = ALLOC_SIZE; + } + if (*buf == NULL) + newbuf = (char *) malloc (have + want); + else + newbuf = (char *) realloc (*buf, have + want); + if (newbuf == NULL) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + *buf = newbuf; + *bufend = *buf + have + want; + return true; +} + +/* We keep a hash table which maps strings to numbers. We use it to + map FDR names to indices in the output file, and to map local + strings when combining stabs debugging information. */ + +struct string_hash_entry +{ + struct bfd_hash_entry root; + /* FDR index or string table offset. */ + long val; + /* Next entry in string table. */ + struct string_hash_entry *next; +}; + +struct string_hash_table +{ + struct bfd_hash_table table; +}; + +/* Routine to create an entry in a string hash table. */ + +static struct bfd_hash_entry * +string_hash_newfunc (entry, table, string) + struct bfd_hash_entry *entry; + struct bfd_hash_table *table; + const char *string; +{ + struct string_hash_entry *ret = (struct string_hash_entry *) entry; + + /* Allocate the structure if it has not already been allocated by a + subclass. */ + if (ret == (struct string_hash_entry *) NULL) + ret = ((struct string_hash_entry *) + bfd_hash_allocate (table, sizeof (struct string_hash_entry))); + if (ret == (struct string_hash_entry *) NULL) + { + bfd_set_error (bfd_error_no_memory); + return NULL; + } + + /* Call the allocation method of the superclass. */ + ret = ((struct string_hash_entry *) + bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string)); + + if (ret) + { + /* Initialize the local fields. */ + ret->val = -1; + ret->next = NULL; + } + + return (struct bfd_hash_entry *) ret; +} + +/* Look up an entry in an string hash table. */ + +#define string_hash_lookup(t, string, create, copy) \ + ((struct string_hash_entry *) \ + bfd_hash_lookup (&(t)->table, (string), (create), (copy))) + +/* We can't afford to read in all the debugging information when we do + a link. Instead, we build a list of these structures to show how + different parts of the input file map to the output file. */ + +struct shuffle +{ + /* The next entry in this linked list. */ + struct shuffle *next; + /* The length of the information. */ + unsigned long size; + /* Whether this information comes from a file or not. */ + boolean filep; + union + { + struct + { + /* The BFD the data comes from. */ + bfd *input_bfd; + /* The offset within input_bfd. */ + file_ptr offset; + } file; + /* The data to be written out. */ + PTR memory; + } u; +}; + +/* This structure holds information across calls to + bfd_ecoff_debug_accumulate. */ + +struct accumulate +{ + /* The FDR hash table. */ + struct string_hash_table fdr_hash; + /* The strings hash table. */ + struct string_hash_table str_hash; + /* Linked lists describing how to shuffle the input debug + information into the output file. We keep a pointer to both the + head and the tail. */ + struct shuffle *line; + struct shuffle *line_end; + struct shuffle *pdr; + struct shuffle *pdr_end; + struct shuffle *sym; + struct shuffle *sym_end; + struct shuffle *opt; + struct shuffle *opt_end; + struct shuffle *aux; + struct shuffle *aux_end; + struct shuffle *ss; + struct shuffle *ss_end; + struct string_hash_entry *ss_hash; + struct string_hash_entry *ss_hash_end; + struct shuffle *fdr; + struct shuffle *fdr_end; + struct shuffle *rfd; + struct shuffle *rfd_end; + /* The size of the largest file shuffle. */ + unsigned long largest_file_shuffle; + /* An obstack for debugging information. */ + struct obstack memory; +}; + +/* Add a file entry to a shuffle list. */ + +static boolean add_file_shuffle PARAMS ((struct accumulate *, + struct shuffle **, + struct shuffle **, bfd *, file_ptr, + unsigned long)); + +static boolean +add_file_shuffle (ainfo, head, tail, input_bfd, offset, size) + struct accumulate *ainfo; + struct shuffle **head; + struct shuffle **tail; + bfd *input_bfd; + file_ptr offset; + unsigned long size; +{ + struct shuffle *n; + + if (*tail != (struct shuffle *) NULL + && (*tail)->filep + && (*tail)->u.file.input_bfd == input_bfd + && (*tail)->u.file.offset + (*tail)->size == offset) + { + /* Just merge this entry onto the existing one. */ + (*tail)->size += size; + if ((*tail)->size > ainfo->largest_file_shuffle) + ainfo->largest_file_shuffle = (*tail)->size; + return true; + } + + n = (struct shuffle *) obstack_alloc (&ainfo->memory, + sizeof (struct shuffle)); + if (!n) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + n->next = NULL; + n->size = size; + n->filep = true; + n->u.file.input_bfd = input_bfd; + n->u.file.offset = offset; + if (*head == (struct shuffle *) NULL) + *head = n; + if (*tail != (struct shuffle *) NULL) + (*tail)->next = n; + *tail = n; + if (size > ainfo->largest_file_shuffle) + ainfo->largest_file_shuffle = size; + return true; +} + +/* Add a memory entry to a shuffle list. */ + +static boolean add_memory_shuffle PARAMS ((struct accumulate *, + struct shuffle **head, + struct shuffle **tail, + bfd_byte *data, unsigned long size)); + +static boolean +add_memory_shuffle (ainfo, head, tail, data, size) + struct accumulate *ainfo; + struct shuffle **head; + struct shuffle **tail; + bfd_byte *data; + unsigned long size; +{ + struct shuffle *n; + + n = (struct shuffle *) obstack_alloc (&ainfo->memory, + sizeof (struct shuffle)); + if (!n) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + n->next = NULL; + n->size = size; + n->filep = false; + n->u.memory = (PTR) data; + if (*head == (struct shuffle *) NULL) + *head = n; + if (*tail != (struct shuffle *) NULL) + (*tail)->next = n; + *tail = n; + return true; +} + +/* Initialize the FDR hash table. This returns a handle which is then + passed in to bfd_ecoff_debug_accumulate, et. al. */ + +/*ARGSUSED*/ +PTR +bfd_ecoff_debug_init (output_bfd, output_debug, output_swap, info) + bfd *output_bfd; + struct ecoff_debug_info *output_debug; + const struct ecoff_debug_swap *output_swap; + struct bfd_link_info *info; +{ + struct accumulate *ainfo; + + ainfo = (struct accumulate *) malloc (sizeof (struct accumulate)); + if (!ainfo) + { + bfd_set_error (bfd_error_no_memory); + return NULL; + } + if (! bfd_hash_table_init_n (&ainfo->fdr_hash.table, string_hash_newfunc, + 1021)) + return NULL; + + ainfo->line = NULL; + ainfo->line_end = NULL; + ainfo->pdr = NULL; + ainfo->pdr_end = NULL; + ainfo->sym = NULL; + ainfo->sym_end = NULL; + ainfo->opt = NULL; + ainfo->opt_end = NULL; + ainfo->aux = NULL; + ainfo->aux_end = NULL; + ainfo->ss = NULL; + ainfo->ss_end = NULL; + ainfo->ss_hash = NULL; + ainfo->ss_hash_end = NULL; + ainfo->fdr = NULL; + ainfo->fdr_end = NULL; + ainfo->rfd = NULL; + ainfo->rfd_end = NULL; + + ainfo->largest_file_shuffle = 0; + + if (! info->relocateable) + { + if (! bfd_hash_table_init (&ainfo->str_hash.table, string_hash_newfunc)) + return NULL; + + /* The first entry in the string table is the empty string. */ + output_debug->symbolic_header.issMax = 1; + } + + if (!obstack_begin (&ainfo->memory, 4050)) + { + bfd_set_error (bfd_error_no_memory); + return NULL; + } + + return (PTR) ainfo; +} + +/* Free the accumulated debugging information. */ + +/*ARGSUSED*/ +void +bfd_ecoff_debug_free (handle, output_bfd, output_debug, output_swap, info) + PTR handle; + bfd *output_bfd; + struct ecoff_debug_info *output_debug; + const struct ecoff_debug_swap *output_swap; + struct bfd_link_info *info; +{ + struct accumulate *ainfo = (struct accumulate *) handle; + + bfd_hash_table_free (&ainfo->fdr_hash.table); + + if (! info->relocateable) + bfd_hash_table_free (&ainfo->str_hash.table); + + obstack_free (&ainfo->memory, (PTR) NULL); + + free (ainfo); +} + +/* Accumulate the debugging information from INPUT_BFD into + OUTPUT_BFD. The INPUT_DEBUG argument points to some ECOFF + debugging information which we want to link into the information + pointed to by the OUTPUT_DEBUG argument. OUTPUT_SWAP and + INPUT_SWAP point to the swapping information needed. INFO is the + linker information structure. HANDLE is returned by + bfd_ecoff_debug_init. */ + +/*ARGSUSED*/ +boolean +bfd_ecoff_debug_accumulate (handle, output_bfd, output_debug, output_swap, + input_bfd, input_debug, input_swap, + info) + PTR handle; + bfd *output_bfd; + struct ecoff_debug_info *output_debug; + const struct ecoff_debug_swap *output_swap; + bfd *input_bfd; + struct ecoff_debug_info *input_debug; + const struct ecoff_debug_swap *input_swap; + struct bfd_link_info *info; +{ + struct accumulate *ainfo = (struct accumulate *) handle; + void (* const swap_sym_in) PARAMS ((bfd *, PTR, SYMR *)) + = input_swap->swap_sym_in; + void (* const swap_rfd_in) PARAMS ((bfd *, PTR, RFDT *)) + = input_swap->swap_rfd_in; + void (* const swap_sym_out) PARAMS ((bfd *, const SYMR *, PTR)) + = output_swap->swap_sym_out; + void (* const swap_fdr_out) PARAMS ((bfd *, const FDR *, PTR)) + = output_swap->swap_fdr_out; + void (* const swap_rfd_out) PARAMS ((bfd *, const RFDT *, PTR)) + = output_swap->swap_rfd_out; + bfd_size_type external_pdr_size = output_swap->external_pdr_size; + bfd_size_type external_sym_size = output_swap->external_sym_size; + bfd_size_type external_opt_size = output_swap->external_opt_size; + bfd_size_type external_fdr_size = output_swap->external_fdr_size; + bfd_size_type external_rfd_size = output_swap->external_rfd_size; + HDRR * const output_symhdr = &output_debug->symbolic_header; + HDRR * const input_symhdr = &input_debug->symbolic_header; + bfd_vma section_adjust[scMax]; + asection *sec; + bfd_byte *fdr_start; + bfd_byte *fdr_ptr; + bfd_byte *fdr_end; + bfd_size_type fdr_add; + unsigned int copied; + RFDT i; + unsigned long sz; + bfd_byte *rfd_out; + bfd_byte *rfd_in; + bfd_byte *rfd_end; + long newrfdbase = 0; + long oldrfdbase = 0; + bfd_byte *fdr_out; + + /* Use section_adjust to hold the value to add to a symbol in a + particular section. */ + memset ((PTR) section_adjust, 0, sizeof section_adjust); + +#define SET(name, indx) \ + sec = bfd_get_section_by_name (input_bfd, name); \ + if (sec != NULL) \ + section_adjust[indx] = (sec->output_section->vma \ + + sec->output_offset \ + - sec->vma); + + SET (".text", scText); + SET (".data", scData); + SET (".bss", scBss); + SET (".sdata", scSData); + SET (".sbss", scSBss); + /* scRdata section may be either .rdata or .rodata. */ + SET (".rdata", scRData); + SET (".rodata", scRData); + SET (".init", scInit); + SET (".fini", scFini); + +#undef SET + + /* Find all the debugging information based on the FDR's. We need + to handle them whether they are swapped or not. */ + if (input_debug->fdr != (FDR *) NULL) + { + fdr_start = (bfd_byte *) input_debug->fdr; + fdr_add = sizeof (FDR); + } + else + { + fdr_start = (bfd_byte *) input_debug->external_fdr; + fdr_add = input_swap->external_fdr_size; + } + fdr_end = fdr_start + input_symhdr->ifdMax * fdr_add; + + input_debug->ifdmap = (RFDT *) bfd_alloc (input_bfd, + (input_symhdr->ifdMax + * sizeof (RFDT))); + + sz = (input_symhdr->crfd + input_symhdr->ifdMax) * external_rfd_size; + rfd_out = (bfd_byte *) obstack_alloc (&ainfo->memory, sz); + if (!input_debug->ifdmap || !rfd_out) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + if (!add_memory_shuffle (ainfo, &ainfo->rfd, &ainfo->rfd_end, rfd_out, sz)) + return false; + + copied = 0; + + /* Look through the FDR's to see which ones we are going to include + in the final output. We do not want duplicate FDR information + for header files, because ECOFF debugging is often very large. + When we find an FDR with no line information which can be merged, + we look it up in a hash table to ensure that we only include it + once. We keep a table mapping FDR numbers to the final number + they get with the BFD, so that we can refer to it when we write + out the external symbols. */ + for (fdr_ptr = fdr_start, i = 0; + fdr_ptr < fdr_end; + fdr_ptr += fdr_add, i++, rfd_out += external_rfd_size) + { + FDR fdr; + + if (input_debug->fdr != (FDR *) NULL) + fdr = *(FDR *) fdr_ptr; + else + (*input_swap->swap_fdr_in) (input_bfd, (PTR) fdr_ptr, &fdr); + + /* See if this FDR can be merged with an existing one. */ + if (fdr.cbLine == 0 && fdr.rss != -1 && fdr.fMerge) + { + const char *name; + char *lookup; + struct string_hash_entry *fh; + + /* We look up a string formed from the file name and the + number of symbols. Sometimes an include file will + conditionally define a typedef or something based on the + order of include files. Using the number of symbols as a + hash reduces the chance that we will merge symbol + information that should not be merged. */ + name = input_debug->ss + fdr.issBase + fdr.rss; + + lookup = (char *) malloc (strlen (name) + 20); + if (lookup == NULL) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + sprintf (lookup, "%s %lx", name, fdr.csym); + + fh = string_hash_lookup (&ainfo->fdr_hash, lookup, true, true); + free (lookup); + if (fh == (struct string_hash_entry *) NULL) + return false; + + if (fh->val != -1) + { + input_debug->ifdmap[i] = fh->val; + (*swap_rfd_out) (output_bfd, input_debug->ifdmap + i, + (PTR) rfd_out); + + /* Don't copy this FDR. */ + continue; + } + + fh->val = output_symhdr->ifdMax + copied; + } + + input_debug->ifdmap[i] = output_symhdr->ifdMax + copied; + (*swap_rfd_out) (output_bfd, input_debug->ifdmap + i, (PTR) rfd_out); + ++copied; + } + + newrfdbase = output_symhdr->crfd; + output_symhdr->crfd += input_symhdr->ifdMax; + + /* Copy over any existing RFD's. RFD's are only created by the + linker, so this will only happen for input files which are the + result of a partial link. */ + rfd_in = (bfd_byte *) input_debug->external_rfd; + rfd_end = rfd_in + input_symhdr->crfd * input_swap->external_rfd_size; + for (; + rfd_in < rfd_end; + rfd_in += input_swap->external_rfd_size) + { + RFDT rfd; + + (*swap_rfd_in) (input_bfd, (PTR) rfd_in, &rfd); + BFD_ASSERT (rfd >= 0 && rfd < input_symhdr->ifdMax); + rfd = input_debug->ifdmap[rfd]; + (*swap_rfd_out) (output_bfd, &rfd, (PTR) rfd_out); + rfd_out += external_rfd_size; + } + + oldrfdbase = output_symhdr->crfd; + output_symhdr->crfd += input_symhdr->crfd; + + /* Look through the FDR's and copy over all associated debugging + information. */ + sz = copied * external_fdr_size; + fdr_out = (bfd_byte *) obstack_alloc (&ainfo->memory, sz); + if (!fdr_out) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + if (!add_memory_shuffle (ainfo, &ainfo->fdr, &ainfo->fdr_end, fdr_out, sz)) + return false; + for (fdr_ptr = fdr_start, i = 0; + fdr_ptr < fdr_end; + fdr_ptr += fdr_add, i++) + { + FDR fdr; + bfd_vma fdr_adr; + bfd_byte *sym_out; + bfd_byte *lraw_src; + bfd_byte *lraw_end; + boolean fgotfilename; + + if (input_debug->ifdmap[i] < output_symhdr->ifdMax) + { + /* We are not copying this FDR. */ + continue; + } + + if (input_debug->fdr != (FDR *) NULL) + fdr = *(FDR *) fdr_ptr; + else + (*input_swap->swap_fdr_in) (input_bfd, (PTR) fdr_ptr, &fdr); + + fdr_adr = fdr.adr; + + /* Adjust the FDR address for any changes that may have been + made by relaxing. */ + if (input_debug->adjust != (struct ecoff_value_adjust *) NULL) + { + struct ecoff_value_adjust *adjust; + + for (adjust = input_debug->adjust; + adjust != (struct ecoff_value_adjust *) NULL; + adjust = adjust->next) + if (fdr_adr >= adjust->start + && fdr_adr < adjust->end) + fdr.adr += adjust->adjust; + } + + /* FIXME: It is conceivable that this FDR points to the .init or + .fini section, in which case this will not do the right + thing. */ + fdr.adr += section_adjust[scText]; + + /* Swap in the local symbols, adjust their values, and swap them + out again. */ + fgotfilename = false; + sz = fdr.csym * external_sym_size; + sym_out = (bfd_byte *) obstack_alloc (&ainfo->memory, sz); + if (!sym_out) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + if (!add_memory_shuffle (ainfo, &ainfo->sym, &ainfo->sym_end, sym_out, + sz)) + return false; + lraw_src = ((bfd_byte *) input_debug->external_sym + + fdr.isymBase * input_swap->external_sym_size); + lraw_end = lraw_src + fdr.csym * input_swap->external_sym_size; + for (; lraw_src < lraw_end; lraw_src += input_swap->external_sym_size) + { + SYMR internal_sym; + + (*swap_sym_in) (input_bfd, (PTR) lraw_src, &internal_sym); + + BFD_ASSERT (internal_sym.sc != scCommon + && internal_sym.sc != scSCommon); + + /* Adjust the symbol value if appropriate. */ + switch (internal_sym.st) + { + case stNil: + if (ECOFF_IS_STAB (&internal_sym)) + break; + /* Fall through. */ + case stGlobal: + case stStatic: + case stLabel: + case stProc: + case stStaticProc: + if (input_debug->adjust != (struct ecoff_value_adjust *) NULL) + { + bfd_vma value; + struct ecoff_value_adjust *adjust; + + value = internal_sym.value; + for (adjust = input_debug->adjust; + adjust != (struct ecoff_value_adjust *) NULL; + adjust = adjust->next) + if (value >= adjust->start + && value < adjust->end) + internal_sym.value += adjust->adjust; + } + internal_sym.value += section_adjust[internal_sym.sc]; + break; + + default: + break; + } + + /* If we are doing a final link, we hash all the strings in + the local symbol table together. This reduces the amount + of space required by debugging information. We don't do + this when performing a relocateable link because it would + prevent us from easily merging different FDR's. */ + if (! info->relocateable) + { + boolean ffilename; + const char *name; + + if (! fgotfilename && internal_sym.iss == fdr.rss) + ffilename = true; + else + ffilename = false; + + /* Hash the name into the string table. */ + name = input_debug->ss + fdr.issBase + internal_sym.iss; + if (*name == '\0') + internal_sym.iss = 0; + else + { + struct string_hash_entry *sh; + + sh = string_hash_lookup (&ainfo->str_hash, name, true, true); + if (sh == (struct string_hash_entry *) NULL) + return false; + if (sh->val == -1) + { + sh->val = output_symhdr->issMax; + output_symhdr->issMax += strlen (name) + 1; + if (ainfo->ss_hash == (struct string_hash_entry *) NULL) + ainfo->ss_hash = sh; + if (ainfo->ss_hash_end + != (struct string_hash_entry *) NULL) + ainfo->ss_hash_end->next = sh; + ainfo->ss_hash_end = sh; + } + internal_sym.iss = sh->val; + } + + if (ffilename) + { + fdr.rss = internal_sym.iss; + fgotfilename = true; + } + } + + (*swap_sym_out) (output_bfd, &internal_sym, sym_out); + sym_out += external_sym_size; + } + + fdr.isymBase = output_symhdr->isymMax; + output_symhdr->isymMax += fdr.csym; + + /* Copy the information that does not need swapping. */ + + /* FIXME: If we are relaxing, we need to adjust the line + numbers. Frankly, forget it. Anybody using stabs debugging + information will not use this line number information, and + stabs are adjusted correctly. */ + if (fdr.cbLine > 0) + { + if (!add_file_shuffle (ainfo, &ainfo->line, &ainfo->line_end, + input_bfd, + input_symhdr->cbLineOffset + fdr.cbLineOffset, + fdr.cbLine)) + return false; + fdr.ilineBase = output_symhdr->ilineMax; + fdr.cbLineOffset = output_symhdr->cbLine; + output_symhdr->ilineMax += fdr.cline; + output_symhdr->cbLine += fdr.cbLine; + } + if (fdr.caux > 0) + { + if (!add_file_shuffle (ainfo, &ainfo->aux, &ainfo->aux_end, + input_bfd, + (input_symhdr->cbAuxOffset + + fdr.iauxBase * sizeof (union aux_ext)), + fdr.caux * sizeof (union aux_ext))) + return false; + fdr.iauxBase = output_symhdr->iauxMax; + output_symhdr->iauxMax += fdr.caux; + } + if (! info->relocateable) + { + + /* When are are hashing strings, we lie about the number of + strings attached to each FDR. We need to set cbSs + because some versions of dbx apparently use it to decide + how much of the string table to read in. */ + fdr.issBase = 0; + fdr.cbSs = output_symhdr->issMax; + } + else if (fdr.cbSs > 0) + { + if (!add_file_shuffle (ainfo, &ainfo->ss, &ainfo->ss_end, + input_bfd, + input_symhdr->cbSsOffset + fdr.issBase, + fdr.cbSs)) + return false; + fdr.issBase = output_symhdr->issMax; + output_symhdr->issMax += fdr.cbSs; + } + + if ((output_bfd->xvec->header_byteorder_big_p + == input_bfd->xvec->header_byteorder_big_p) + && input_debug->adjust == (struct ecoff_value_adjust *) NULL) + { + /* The two BFD's have the same endianness, and we don't have + to adjust the PDR addresses, so simply copying the + information will suffice. */ + BFD_ASSERT (external_pdr_size == input_swap->external_pdr_size); + if (fdr.cpd > 0) + { + if (!add_file_shuffle (ainfo, &ainfo->pdr, &ainfo->pdr_end, + input_bfd, + (input_symhdr->cbPdOffset + + fdr.ipdFirst * external_pdr_size), + fdr.cpd * external_pdr_size)) + return false; + } + BFD_ASSERT (external_opt_size == input_swap->external_opt_size); + if (fdr.copt > 0) + { + if (!add_file_shuffle (ainfo, &ainfo->opt, &ainfo->opt_end, + input_bfd, + (input_symhdr->cbOptOffset + + fdr.ioptBase * external_opt_size), + fdr.copt * external_opt_size)) + return false; + } + } + else + { + bfd_size_type outsz, insz; + bfd_byte *in; + bfd_byte *end; + bfd_byte *out; + + /* The two BFD's have different endianness, so we must swap + everything in and out. This code would always work, but + it would be unnecessarily slow in the normal case. */ + outsz = external_pdr_size; + insz = input_swap->external_pdr_size; + in = ((bfd_byte *) input_debug->external_pdr + + fdr.ipdFirst * insz); + end = in + fdr.cpd * insz; + sz = fdr.cpd * outsz; + out = (bfd_byte *) obstack_alloc (&ainfo->memory, sz); + if (!out) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + if (!add_memory_shuffle (ainfo, &ainfo->pdr, &ainfo->pdr_end, out, + sz)) + return false; + for (; in < end; in += insz, out += outsz) + { + PDR pdr; + + (*input_swap->swap_pdr_in) (input_bfd, (PTR) in, &pdr); + + /* If we have been relaxing, we may have to adjust the + address. */ + if (input_debug->adjust != (struct ecoff_value_adjust *) NULL) + { + bfd_vma adr; + struct ecoff_value_adjust *adjust; + + adr = fdr_adr + pdr.adr; + for (adjust = input_debug->adjust; + adjust != (struct ecoff_value_adjust *) NULL; + adjust = adjust->next) + if (adr >= adjust->start + && adr < adjust->end) + pdr.adr += adjust->adjust; + } + + (*output_swap->swap_pdr_out) (output_bfd, &pdr, (PTR) out); + } + + /* Swap over the optimization information. */ + outsz = external_opt_size; + insz = input_swap->external_opt_size; + in = ((bfd_byte *) input_debug->external_opt + + fdr.ioptBase * insz); + end = in + fdr.copt * insz; + sz = fdr.copt * outsz; + out = (bfd_byte *) obstack_alloc (&ainfo->memory, sz); + if (!out) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + if (!add_memory_shuffle (ainfo, &ainfo->opt, &ainfo->opt_end, out, + sz)) + return false; + for (; in < end; in += insz, out += outsz) + { + OPTR opt; + + (*input_swap->swap_opt_in) (input_bfd, (PTR) in, &opt); + (*output_swap->swap_opt_out) (output_bfd, &opt, (PTR) out); + } + } + + fdr.ipdFirst = output_symhdr->ipdMax; + output_symhdr->ipdMax += fdr.cpd; + fdr.ioptBase = output_symhdr->ioptMax; + output_symhdr->ioptMax += fdr.copt; + + if (fdr.crfd <= 0) + { + /* Point this FDR at the table of RFD's we created. */ + fdr.rfdBase = newrfdbase; + fdr.crfd = input_symhdr->ifdMax; + } + else + { + /* Point this FDR at the remapped RFD's. */ + fdr.rfdBase += oldrfdbase; + } + + (*swap_fdr_out) (output_bfd, &fdr, fdr_out); + fdr_out += external_fdr_size; + ++output_symhdr->ifdMax; + } + + return true; +} + +/* Add a string to the debugging information we are accumulating. + Return the offset from the fdr string base. */ + +static long ecoff_add_string PARAMS ((struct accumulate *, + struct bfd_link_info *, + struct ecoff_debug_info *, + FDR *fdr, const char *string)); + +static long +ecoff_add_string (ainfo, info, debug, fdr, string) + struct accumulate *ainfo; + struct bfd_link_info *info; + struct ecoff_debug_info *debug; + FDR *fdr; + const char *string; +{ + HDRR *symhdr; + size_t len; + bfd_size_type ret; + + symhdr = &debug->symbolic_header; + len = strlen (string); + if (info->relocateable) + { + if (!add_memory_shuffle (ainfo, &ainfo->ss, &ainfo->ss_end, (PTR) string, + len + 1)) + return -1; + ret = symhdr->issMax; + symhdr->issMax += len + 1; + fdr->cbSs += len + 1; + } + else + { + struct string_hash_entry *sh; + + sh = string_hash_lookup (&ainfo->str_hash, string, true, true); + if (sh == (struct string_hash_entry *) NULL) + return -1; + if (sh->val == -1) + { + sh->val = symhdr->issMax; + symhdr->issMax += len + 1; + if (ainfo->ss_hash == (struct string_hash_entry *) NULL) + ainfo->ss_hash = sh; + if (ainfo->ss_hash_end + != (struct string_hash_entry *) NULL) + ainfo->ss_hash_end->next = sh; + ainfo->ss_hash_end = sh; + } + ret = sh->val; + } + + return ret; +} + +/* Add debugging information from a non-ECOFF file. */ + +boolean +bfd_ecoff_debug_accumulate_other (handle, output_bfd, output_debug, + output_swap, input_bfd, info) + PTR handle; + bfd *output_bfd; + struct ecoff_debug_info *output_debug; + const struct ecoff_debug_swap *output_swap; + bfd *input_bfd; + struct bfd_link_info *info; +{ + struct accumulate *ainfo = (struct accumulate *) handle; + void (* const swap_sym_out) PARAMS ((bfd *, const SYMR *, PTR)) + = output_swap->swap_sym_out; + HDRR *output_symhdr = &output_debug->symbolic_header; + FDR fdr; + asection *sec; + asymbol **symbols; + asymbol **sym_ptr; + asymbol **sym_end; + long symsize; + long symcount; + PTR external_fdr; + + memset ((PTR) &fdr, 0, sizeof fdr); + + sec = bfd_get_section_by_name (input_bfd, ".text"); + if (sec != NULL) + fdr.adr = sec->output_section->vma + sec->output_offset; + else + { + /* FIXME: What about .init or .fini? */ + fdr.adr = 0; + } + + fdr.issBase = output_symhdr->issMax; + fdr.cbSs = 0; + fdr.rss = ecoff_add_string (ainfo, info, output_debug, &fdr, + bfd_get_filename (input_bfd)); + if (fdr.rss == -1) + return false; + fdr.isymBase = output_symhdr->isymMax; + + /* Get the local symbols from the input BFD. */ + symsize = bfd_get_symtab_upper_bound (input_bfd); + if (symsize < 0) + return false; + symbols = (asymbol **) bfd_alloc (output_bfd, symsize); + if (symbols == (asymbol **) NULL) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + symcount = bfd_canonicalize_symtab (input_bfd, symbols); + if (symcount < 0) + return false; + sym_end = symbols + symcount; + + /* Handle the local symbols. Any external symbols are handled + separately. */ + fdr.csym = 0; + for (sym_ptr = symbols; sym_ptr != sym_end; sym_ptr++) + { + SYMR internal_sym; + PTR external_sym; + + if (((*sym_ptr)->flags & BSF_EXPORT) != 0) + continue; + memset ((PTR) &internal_sym, 0, sizeof internal_sym); + internal_sym.iss = ecoff_add_string (ainfo, info, output_debug, &fdr, + (*sym_ptr)->name); + + if (internal_sym.iss == -1) + return false; + if (bfd_is_com_section ((*sym_ptr)->section) + || bfd_is_und_section ((*sym_ptr)->section)) + internal_sym.value = (*sym_ptr)->value; + else + internal_sym.value = ((*sym_ptr)->value + + (*sym_ptr)->section->output_offset + + (*sym_ptr)->section->output_section->vma); + internal_sym.st = stNil; + internal_sym.sc = scUndefined; + internal_sym.index = indexNil; + + external_sym = (PTR) obstack_alloc (&ainfo->memory, + output_swap->external_sym_size); + if (!external_sym) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + (*swap_sym_out) (output_bfd, &internal_sym, external_sym); + add_memory_shuffle (ainfo, &ainfo->sym, &ainfo->sym_end, + external_sym, output_swap->external_sym_size); + ++fdr.csym; + ++output_symhdr->isymMax; + } + + bfd_release (output_bfd, (PTR) symbols); + + /* Leave everything else in the FDR zeroed out. This will cause + the lang field to be langC. The fBigendian field will + indicate little endian format, but it doesn't matter because + it only applies to aux fields and there are none. */ + external_fdr = (PTR) obstack_alloc (&ainfo->memory, + output_swap->external_fdr_size); + if (!external_fdr) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + (*output_swap->swap_fdr_out) (output_bfd, &fdr, external_fdr); + add_memory_shuffle (ainfo, &ainfo->fdr, &ainfo->fdr_end, + external_fdr, output_swap->external_fdr_size); + + ++output_symhdr->ifdMax; + + return true; +} + +/* Set up ECOFF debugging information for the external symbols. + FIXME: This is done using a memory buffer, but it should be + probably be changed to use a shuffle structure. The assembler uses + this interface, so that must be changed to do something else. */ + +boolean +bfd_ecoff_debug_externals (abfd, debug, swap, relocateable, get_extr, + set_index) + bfd *abfd; + struct ecoff_debug_info *debug; + const struct ecoff_debug_swap *swap; + boolean relocateable; + boolean (*get_extr) PARAMS ((asymbol *, EXTR *)); + void (*set_index) PARAMS ((asymbol *, bfd_size_type)); +{ + HDRR * const symhdr = &debug->symbolic_header; + asymbol **sym_ptr_ptr; + size_t c; + + sym_ptr_ptr = bfd_get_outsymbols (abfd); + if (sym_ptr_ptr == NULL) + return true; + + for (c = bfd_get_symcount (abfd); c > 0; c--, sym_ptr_ptr++) + { + asymbol *sym_ptr; + EXTR esym; + + sym_ptr = *sym_ptr_ptr; + + /* Get the external symbol information. */ + if ((*get_extr) (sym_ptr, &esym) == false) + continue; + + /* If we're producing an executable, move common symbols into + bss. */ + if (relocateable == false) + { + if (esym.asym.sc == scCommon) + esym.asym.sc = scBss; + else if (esym.asym.sc == scSCommon) + esym.asym.sc = scSBss; + } + + if (bfd_is_com_section (sym_ptr->section) + || bfd_is_und_section (sym_ptr->section) + || sym_ptr->section->output_section == (asection *) NULL) + { + /* FIXME: gas does not keep the value of a small undefined + symbol in the symbol itself, because of relocation + problems. */ + if (esym.asym.sc != scSUndefined + || esym.asym.value == 0 + || sym_ptr->value != 0) + esym.asym.value = sym_ptr->value; + } + else + esym.asym.value = (sym_ptr->value + + sym_ptr->section->output_offset + + sym_ptr->section->output_section->vma); + + if (set_index) + (*set_index) (sym_ptr, (bfd_size_type) symhdr->iextMax); + + if (! bfd_ecoff_debug_one_external (abfd, debug, swap, + sym_ptr->name, &esym)) + return false; + } + + return true; +} + +/* Add a single external symbol to the debugging information. */ + +boolean +bfd_ecoff_debug_one_external (abfd, debug, swap, name, esym) + bfd *abfd; + struct ecoff_debug_info *debug; + const struct ecoff_debug_swap *swap; + const char *name; + EXTR *esym; +{ + const bfd_size_type external_ext_size = swap->external_ext_size; + void (* const swap_ext_out) PARAMS ((bfd *, const EXTR *, PTR)) + = swap->swap_ext_out; + HDRR * const symhdr = &debug->symbolic_header; + size_t namelen; + + namelen = strlen (name); + + if (debug->ssext_end - debug->ssext + < symhdr->issExtMax + namelen + 1) + { + if (ecoff_add_bytes ((char **) &debug->ssext, + (char **) &debug->ssext_end, + symhdr->issExtMax + namelen + 1) + == false) + return false; + } + if ((char *) debug->external_ext_end - (char *) debug->external_ext + < (symhdr->iextMax + 1) * external_ext_size) + { + if (ecoff_add_bytes ((char **) &debug->external_ext, + (char **) &debug->external_ext_end, + (symhdr->iextMax + 1) * external_ext_size) + == false) + return false; + } + + esym->asym.iss = symhdr->issExtMax; + + (*swap_ext_out) (abfd, esym, + ((char *) debug->external_ext + + symhdr->iextMax * swap->external_ext_size)); + + ++symhdr->iextMax; + + strcpy (debug->ssext + symhdr->issExtMax, name); + symhdr->issExtMax += namelen + 1; + + return true; +} + +/* Align the ECOFF debugging information. */ + +/*ARGSUSED*/ +static void +ecoff_align_debug (abfd, debug, swap) + bfd *abfd; + struct ecoff_debug_info *debug; + const struct ecoff_debug_swap *swap; +{ + HDRR * const symhdr = &debug->symbolic_header; + bfd_size_type debug_align, aux_align, rfd_align; + size_t add; + + /* Adjust the counts so that structures are aligned. */ + debug_align = swap->debug_align; + aux_align = debug_align / sizeof (union aux_ext); + rfd_align = debug_align / swap->external_rfd_size; + + add = debug_align - (symhdr->cbLine & (debug_align - 1)); + if (add != debug_align) + { + if (debug->line != (unsigned char *) NULL) + memset ((PTR) (debug->line + symhdr->cbLine), 0, add); + symhdr->cbLine += add; + } + + add = debug_align - (symhdr->issMax & (debug_align - 1)); + if (add != debug_align) + { + if (debug->ss != (char *) NULL) + memset ((PTR) (debug->ss + symhdr->issMax), 0, add); + symhdr->issMax += add; + } + + add = debug_align - (symhdr->issExtMax & (debug_align - 1)); + if (add != debug_align) + { + if (debug->ssext != (char *) NULL) + memset ((PTR) (debug->ssext + symhdr->issExtMax), 0, add); + symhdr->issExtMax += add; + } + + add = aux_align - (symhdr->iauxMax & (aux_align - 1)); + if (add != aux_align) + { + if (debug->external_aux != (union aux_ext *) NULL) + memset ((PTR) (debug->external_aux + symhdr->iauxMax), 0, + add * sizeof (union aux_ext)); + symhdr->iauxMax += add; + } + + add = rfd_align - (symhdr->crfd & (rfd_align - 1)); + if (add != rfd_align) + { + if (debug->external_rfd != (PTR) NULL) + memset ((PTR) ((char *) debug->external_rfd + + symhdr->crfd * swap->external_rfd_size), + 0, add * swap->external_rfd_size); + symhdr->crfd += add; + } +} + +/* Return the size required by the ECOFF debugging information. */ + +bfd_size_type +bfd_ecoff_debug_size (abfd, debug, swap) + bfd *abfd; + struct ecoff_debug_info *debug; + const struct ecoff_debug_swap *swap; +{ + bfd_size_type tot; + + ecoff_align_debug (abfd, debug, swap); + tot = swap->external_hdr_size; + +#define ADD(count, size) \ + tot += debug->symbolic_header.count * size + + ADD (cbLine, sizeof (unsigned char)); + ADD (idnMax, swap->external_dnr_size); + ADD (ipdMax, swap->external_pdr_size); + ADD (isymMax, swap->external_sym_size); + ADD (ioptMax, swap->external_opt_size); + ADD (iauxMax, sizeof (union aux_ext)); + ADD (issMax, sizeof (char)); + ADD (issExtMax, sizeof (char)); + ADD (ifdMax, swap->external_fdr_size); + ADD (crfd, swap->external_rfd_size); + ADD (iextMax, swap->external_ext_size); + +#undef ADD + + return tot; +} + +/* Write out the ECOFF symbolic header, given the file position it is + going to be placed at. This assumes that the counts are set + correctly. */ + +static boolean +ecoff_write_symhdr (abfd, debug, swap, where) + bfd *abfd; + struct ecoff_debug_info *debug; + const struct ecoff_debug_swap *swap; + file_ptr where; +{ + HDRR * const symhdr = &debug->symbolic_header; + char *buff = NULL; + + ecoff_align_debug (abfd, debug, swap); + + /* Go to the right location in the file. */ + if (bfd_seek (abfd, where, SEEK_SET) != 0) + return false; + + where += swap->external_hdr_size; + + symhdr->magic = swap->sym_magic; + + /* Fill in the file offsets. */ +#define SET(offset, count, size) \ + if (symhdr->count == 0) \ + symhdr->offset = 0; \ + else \ + { \ + symhdr->offset = where; \ + where += symhdr->count * size; \ + } + + SET (cbLineOffset, cbLine, sizeof (unsigned char)); + SET (cbDnOffset, idnMax, swap->external_dnr_size); + SET (cbPdOffset, ipdMax, swap->external_pdr_size); + SET (cbSymOffset, isymMax, swap->external_sym_size); + SET (cbOptOffset, ioptMax, swap->external_opt_size); + SET (cbAuxOffset, iauxMax, sizeof (union aux_ext)); + SET (cbSsOffset, issMax, sizeof (char)); + SET (cbSsExtOffset, issExtMax, sizeof (char)); + SET (cbFdOffset, ifdMax, swap->external_fdr_size); + SET (cbRfdOffset, crfd, swap->external_rfd_size); + SET (cbExtOffset, iextMax, swap->external_ext_size); +#undef SET + + buff = (PTR) malloc (swap->external_hdr_size); + if (buff == NULL && swap->external_hdr_size != 0) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } + + (*swap->swap_hdr_out) (abfd, symhdr, buff); + if (bfd_write (buff, 1, swap->external_hdr_size, abfd) + != swap->external_hdr_size) + goto error_return; + + if (buff != NULL) + free (buff); + return true; + error_return: + if (buff != NULL) + free (buff); + return false; +} + +/* Write out the ECOFF debugging information. This function assumes + that the information (the pointers and counts) in *DEBUG have been + set correctly. WHERE is the position in the file to write the + information to. This function fills in the file offsets in the + symbolic header. */ + +boolean +bfd_ecoff_write_debug (abfd, debug, swap, where) + bfd *abfd; + struct ecoff_debug_info *debug; + const struct ecoff_debug_swap *swap; + file_ptr where; +{ + HDRR * const symhdr = &debug->symbolic_header; + + if (! ecoff_write_symhdr (abfd, debug, swap, where)) + return false; + +#define WRITE(ptr, count, size, offset) \ + BFD_ASSERT (symhdr->offset == 0 || bfd_tell (abfd) == symhdr->offset); \ + if (bfd_write ((PTR) debug->ptr, size, symhdr->count, abfd) \ + != size * symhdr->count) \ + return false; + + WRITE (line, cbLine, sizeof (unsigned char), cbLineOffset); + WRITE (external_dnr, idnMax, swap->external_dnr_size, cbDnOffset); + WRITE (external_pdr, ipdMax, swap->external_pdr_size, cbPdOffset); + WRITE (external_sym, isymMax, swap->external_sym_size, cbSymOffset); + WRITE (external_opt, ioptMax, swap->external_opt_size, cbOptOffset); + WRITE (external_aux, iauxMax, sizeof (union aux_ext), cbAuxOffset); + WRITE (ss, issMax, sizeof (char), cbSsOffset); + WRITE (ssext, issExtMax, sizeof (char), cbSsExtOffset); + WRITE (external_fdr, ifdMax, swap->external_fdr_size, cbFdOffset); + WRITE (external_rfd, crfd, swap->external_rfd_size, cbRfdOffset); + WRITE (external_ext, iextMax, swap->external_ext_size, cbExtOffset); +#undef WRITE + + return true; +} + +/* Write out a shuffle list. */ + +static boolean ecoff_write_shuffle PARAMS ((bfd *, + const struct ecoff_debug_swap *, + struct shuffle *, PTR space)); + +static boolean +ecoff_write_shuffle (abfd, swap, shuffle, space) + bfd *abfd; + const struct ecoff_debug_swap *swap; + struct shuffle *shuffle; + PTR space; +{ + register struct shuffle *l; + unsigned long total; + + total = 0; + for (l = shuffle; l != (struct shuffle *) NULL; l = l->next) + { + if (! l->filep) + { + if (bfd_write (l->u.memory, 1, l->size, abfd) != l->size) + return false; + } + else + { + if (bfd_seek (l->u.file.input_bfd, l->u.file.offset, SEEK_SET) != 0 + || bfd_read (space, 1, l->size, l->u.file.input_bfd) != l->size + || bfd_write (space, 1, l->size, abfd) != l->size) + return false; + } + total += l->size; + } + + if ((total & (swap->debug_align - 1)) != 0) + { + int i; + bfd_byte *s; + + i = swap->debug_align - (total & (swap->debug_align - 1)); + s = (bfd_byte *) malloc (i); + if (s == NULL && i != 0) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + + memset ((PTR) s, 0, i); + if (bfd_write ((PTR) s, 1, i, abfd) != i) + { + free (s); + return false; + } + free (s); + } + + return true; +} + +/* Write out debugging information using accumulated linker + information. */ + +boolean +bfd_ecoff_write_accumulated_debug (handle, abfd, debug, swap, info, where) + PTR handle; + bfd *abfd; + struct ecoff_debug_info *debug; + const struct ecoff_debug_swap *swap; + struct bfd_link_info *info; + file_ptr where; +{ + struct accumulate *ainfo = (struct accumulate *) handle; + PTR space = NULL; + + if (! ecoff_write_symhdr (abfd, debug, swap, where)) + goto error_return; + + space = (PTR) malloc (ainfo->largest_file_shuffle); + if (space == NULL && ainfo->largest_file_shuffle != 0) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } + + if (! ecoff_write_shuffle (abfd, swap, ainfo->line, space) + || ! ecoff_write_shuffle (abfd, swap, ainfo->pdr, space) + || ! ecoff_write_shuffle (abfd, swap, ainfo->sym, space) + || ! ecoff_write_shuffle (abfd, swap, ainfo->opt, space) + || ! ecoff_write_shuffle (abfd, swap, ainfo->aux, space)) + goto error_return; + + /* The string table is written out from the hash table if this is a + final link. */ + if (info->relocateable) + { + BFD_ASSERT (ainfo->ss_hash == (struct string_hash_entry *) NULL); + if (! ecoff_write_shuffle (abfd, swap, ainfo->ss, space)) + goto error_return; + } + else + { + unsigned long total; + bfd_byte null; + struct string_hash_entry *sh; + + BFD_ASSERT (ainfo->ss == (struct shuffle *) NULL); + null = 0; + if (bfd_write ((PTR) &null, 1, 1, abfd) != 1) + goto error_return; + total = 1; + BFD_ASSERT (ainfo->ss_hash == NULL || ainfo->ss_hash->val == 1); + for (sh = ainfo->ss_hash; + sh != (struct string_hash_entry *) NULL; + sh = sh->next) + { + size_t len; + + len = strlen (sh->root.string); + if (bfd_write ((PTR) sh->root.string, 1, len + 1, abfd) != len + 1) + goto error_return; + total += len + 1; + } + + if ((total & (swap->debug_align - 1)) != 0) + { + int i; + bfd_byte *s; + + i = swap->debug_align - (total & (swap->debug_align - 1)); + s = (bfd_byte *) malloc (i); + if (s == NULL && i != 0) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } + memset ((PTR) s, 0, i); + if (bfd_write ((PTR) s, 1, i, abfd) != i) + { + free (s); + goto error_return; + } + free (s); + } + } + + /* The external strings and symbol are not converted over to using + shuffles. FIXME: They probably should be. */ + if (bfd_write (debug->ssext, 1, debug->symbolic_header.issExtMax, abfd) + != debug->symbolic_header.issExtMax) + goto error_return; + if ((debug->symbolic_header.issExtMax & (swap->debug_align - 1)) != 0) + { + int i; + bfd_byte *s; + + i = (swap->debug_align + - (debug->symbolic_header.issExtMax & (swap->debug_align - 1))); + s = (bfd_byte *) malloc (i); + if (s == NULL && i != 0) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } + memset ((PTR) s, 0, i); + if (bfd_write ((PTR) s, 1, i, abfd) != i) + { + free (s); + goto error_return; + } + free (s); + } + + if (! ecoff_write_shuffle (abfd, swap, ainfo->fdr, space) + || ! ecoff_write_shuffle (abfd, swap, ainfo->rfd, space)) + goto error_return; + + BFD_ASSERT (debug->symbolic_header.cbExtOffset == 0 + || debug->symbolic_header.cbExtOffset == bfd_tell (abfd)); + + if (bfd_write (debug->external_ext, swap->external_ext_size, + debug->symbolic_header.iextMax, abfd) + != debug->symbolic_header.iextMax * swap->external_ext_size) + goto error_return; + + if (space != NULL) + free (space); + return true; + + error_return: + if (space != NULL) + free (space); + return false; +} diff --git a/gnu/usr.bin/gdb/bfd/elf32-i386.c b/gnu/usr.bin/gdb/bfd/elf32-i386.c new file mode 100644 index 000000000000..d2b1ebacf9f7 --- /dev/null +++ b/gnu/usr.bin/gdb/bfd/elf32-i386.c @@ -0,0 +1,931 @@ +/* Intel 80386/80486-specific support for 32-bit ELF + Copyright 1993 Free Software Foundation, Inc. + +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. */ + +#include "bfd.h" +#include "sysdep.h" +#include "bfdlink.h" +#include "libbfd.h" +#include "libelf.h" + +static CONST struct reloc_howto_struct *elf_i386_reloc_type_lookup + PARAMS ((bfd *, bfd_reloc_code_real_type)); +static void elf_i386_info_to_howto + PARAMS ((bfd *, arelent *, Elf32_Internal_Rela *)); +static void elf_i386_info_to_howto_rel + PARAMS ((bfd *, arelent *, Elf32_Internal_Rel *)); +static boolean elf_i386_create_dynamic_sections + PARAMS ((bfd *, struct bfd_link_info *)); +static boolean elf_i386_adjust_dynamic_symbol + PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *)); +static boolean elf_i386_allocate_dynamic_section + PARAMS ((bfd *, const char *)); +static boolean elf_i386_size_dynamic_sections + PARAMS ((bfd *, struct bfd_link_info *)); +static boolean elf_i386_relocate_section + PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *, + Elf_Internal_Rela *, Elf_Internal_Sym *, asection **, char *)); +static boolean elf_i386_finish_dynamic_symbol + PARAMS ((bfd *, struct bfd_link_info *, struct elf_link_hash_entry *, + Elf_Internal_Sym *)); +static boolean elf_i386_finish_dynamic_sections + PARAMS ((bfd *, struct bfd_link_info *)); + +#define USE_REL 1 /* 386 uses REL relocations instead of RELA */ + +enum reloc_type + { + R_386_NONE = 0, + R_386_32, + R_386_PC32, + R_386_GOT32, + R_386_PLT32, + R_386_COPY, + R_386_GLOB_DAT, + R_386_JUMP_SLOT, + R_386_RELATIVE, + R_386_GOTOFF, + R_386_GOTPC, + R_386_max + }; + +#if 0 +static CONST char *CONST reloc_type_names[] = +{ + "R_386_NONE", + "R_386_32", + "R_386_PC32", + "R_386_GOT32", + "R_386_PLT32", + "R_386_COPY", + "R_386_GLOB_DAT", + "R_386_JUMP_SLOT", + "R_386_RELATIVE", + "R_386_GOTOFF", + "R_386_GOTPC", +}; +#endif + +static reloc_howto_type elf_howto_table[]= +{ + HOWTO(R_386_NONE, 0,0, 0,false,0,complain_overflow_bitfield, bfd_elf_generic_reloc,"R_386_NONE", true,0x00000000,0x00000000,false), + HOWTO(R_386_32, 0,2,32,false,0,complain_overflow_bitfield, bfd_elf_generic_reloc,"R_386_32", true,0xffffffff,0xffffffff,false), + HOWTO(R_386_PC32, 0,2,32,true, 0,complain_overflow_bitfield, bfd_elf_generic_reloc,"R_386_PC32", true,0xffffffff,0xffffffff,true), + HOWTO(R_386_GOT32, 0,2,32,false,0,complain_overflow_bitfield, bfd_elf_generic_reloc,"R_386_GOT32", true,0xffffffff,0xffffffff,false), + HOWTO(R_386_PLT32, 0,2,32,false,0,complain_overflow_bitfield, bfd_elf_generic_reloc,"R_386_PLT32", true,0xffffffff,0xffffffff,false), + HOWTO(R_386_COPY, 0,2,32,false,0,complain_overflow_bitfield, bfd_elf_generic_reloc,"R_386_COPY", true,0xffffffff,0xffffffff,false), + HOWTO(R_386_GLOB_DAT, 0,2,32,false,0,complain_overflow_bitfield, bfd_elf_generic_reloc,"R_386_GLOB_DAT", true,0xffffffff,0xffffffff,false), + HOWTO(R_386_JUMP_SLOT, 0,2,32,false,0,complain_overflow_bitfield, bfd_elf_generic_reloc,"R_386_JUMP_SLOT",true,0xffffffff,0xffffffff,false), + HOWTO(R_386_RELATIVE, 0,2,32,false,0,complain_overflow_bitfield, bfd_elf_generic_reloc,"R_386_RELATIVE", true,0xffffffff,0xffffffff,false), + HOWTO(R_386_GOTOFF, 0,2,32,false,0,complain_overflow_bitfield, bfd_elf_generic_reloc,"R_386_GOTOFF", true,0xffffffff,0xffffffff,false), + HOWTO(R_386_GOTPC, 0,2,32,false,0,complain_overflow_bitfield, bfd_elf_generic_reloc,"R_386_GOTPC", true,0xffffffff,0xffffffff,false), +}; + +#ifdef DEBUG_GEN_RELOC +#define TRACE(str) fprintf (stderr, "i386 bfd reloc lookup %d (%s)\n", code, str) +#else +#define TRACE(str) +#endif + +static CONST struct reloc_howto_struct * +elf_i386_reloc_type_lookup (abfd, code) + bfd *abfd; + bfd_reloc_code_real_type code; +{ + switch (code) + { + case BFD_RELOC_NONE: + TRACE ("BFD_RELOC_NONE"); + return &elf_howto_table[ (int)R_386_NONE ]; + + case BFD_RELOC_32: + TRACE ("BFD_RELOC_32"); + return &elf_howto_table[ (int)R_386_32 ]; + + case BFD_RELOC_32_PCREL: + TRACE ("BFD_RELOC_PC32"); + return &elf_howto_table[ (int)R_386_PC32 ]; + + case BFD_RELOC_386_GOT32: + TRACE ("BFD_RELOC_386_GOT32"); + return &elf_howto_table[ (int)R_386_GOT32 ]; + + case BFD_RELOC_386_PLT32: + TRACE ("BFD_RELOC_386_PLT32"); + return &elf_howto_table[ (int)R_386_PLT32 ]; + + case BFD_RELOC_386_COPY: + TRACE ("BFD_RELOC_386_COPY"); + return &elf_howto_table[ (int)R_386_COPY ]; + + case BFD_RELOC_386_GLOB_DAT: + TRACE ("BFD_RELOC_386_GLOB_DAT"); + return &elf_howto_table[ (int)R_386_GLOB_DAT ]; + + case BFD_RELOC_386_JUMP_SLOT: + TRACE ("BFD_RELOC_386_JUMP_SLOT"); + return &elf_howto_table[ (int)R_386_JUMP_SLOT ]; + + case BFD_RELOC_386_RELATIVE: + TRACE ("BFD_RELOC_386_RELATIVE"); + return &elf_howto_table[ (int)R_386_RELATIVE ]; + + case BFD_RELOC_386_GOTOFF: + TRACE ("BFD_RELOC_386_GOTOFF"); + return &elf_howto_table[ (int)R_386_GOTOFF ]; + + case BFD_RELOC_386_GOTPC: + TRACE ("BFD_RELOC_386_GOTPC"); + return &elf_howto_table[ (int)R_386_GOTPC ]; + + default: + break; + } + + TRACE ("Unknown"); + return 0; +} + +static void +elf_i386_info_to_howto (abfd, cache_ptr, dst) + bfd *abfd; + arelent *cache_ptr; + Elf32_Internal_Rela *dst; +{ + BFD_ASSERT (ELF32_R_TYPE(dst->r_info) < (unsigned int) R_386_max); + + cache_ptr->howto = &elf_howto_table[ELF32_R_TYPE(dst->r_info)]; +} + +static void +elf_i386_info_to_howto_rel (abfd, cache_ptr, dst) + bfd *abfd; + arelent *cache_ptr; + Elf32_Internal_Rel *dst; +{ + BFD_ASSERT (ELF32_R_TYPE(dst->r_info) < (unsigned int) R_386_max); + + cache_ptr->howto = &elf_howto_table[ELF32_R_TYPE(dst->r_info)]; +} + +/* Functions for the i386 ELF linker. */ + +/* The name of the dynamic interpreter. This is put in the .interp + section. */ + +#define ELF_DYNAMIC_INTERPRETER "/usr/lib/libc.so.1" + +/* The size in bytes of an entry in the procedure linkage table. */ + +#define PLT_ENTRY_SIZE 16 + +/* The first entry in an absolute procedure linkage table looks like + this. See the SVR4 ABI i386 supplement to see how this works. */ + +static bfd_byte elf_i386_plt0_entry[PLT_ENTRY_SIZE] = +{ + 0xff, 0x35, /* pushl contents of address */ + 0, 0, 0, 0, /* replaced with address of .got + 4. */ + 0xff, 0x25, /* jmp indirect */ + 0, 0, 0, 0, /* replaced with address of .got + 8. */ + 0, 0, 0, 0 /* pad out to 16 bytes. */ +}; + +/* Subsequent entries in an absolute procedure linkage table look like + this. */ + +static bfd_byte elf_i386_plt_entry[PLT_ENTRY_SIZE] = +{ + 0xff, 0x25, /* jmp indirect */ + 0, 0, 0, 0, /* replaced with address of this symbol in .got. */ + 0x68, /* pushl immediate */ + 0, 0, 0, 0, /* replaced with offset into relocation table. */ + 0xe9, /* jmp relative */ + 0, 0, 0, 0 /* replaced with offset to start of .plt. */ +}; + +/* Create dynamic sections when linking against a dynamic object. */ + +static boolean +elf_i386_create_dynamic_sections (abfd, info) + bfd *abfd; + struct bfd_link_info *info; +{ + flagword flags; + register asection *s; + struct elf_link_hash_entry *h; + + /* We need to create .plt, .rel.plt, .got, .dynbss, and .rel.bss + sections. */ + + flags = SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY; + + s = bfd_make_section (abfd, ".plt"); + if (s == NULL + || ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY | SEC_CODE) + || ! bfd_set_section_alignment (abfd, s, 2)) + return false; + + s = bfd_make_section (abfd, ".rel.plt"); + if (s == NULL + || ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY) + || ! bfd_set_section_alignment (abfd, s, 2)) + return false; + + s = bfd_make_section (abfd, ".got"); + if (s == NULL + || ! bfd_set_section_flags (abfd, s, flags) + || ! bfd_set_section_alignment (abfd, s, 2)) + return false; + + /* Define the symbol _GLOBAL_OFFSET_TABLE_ at the start of the .got + section. We don't do this in the linker script because we don't + want to define the symbol if we are not creating a global offset + table. */ + h = NULL; + if (! (_bfd_generic_link_add_one_symbol + (info, abfd, "_GLOBAL_OFFSET_TABLE_", BSF_GLOBAL, s, (bfd_vma) 0, + (const char *) NULL, false, get_elf_backend_data (abfd)->collect, + (struct bfd_link_hash_entry **) &h))) + return false; + h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR; + + /* The first three global offset table entries are reserved. */ + s->_raw_size += 3 * 4; + + /* The .dynbss section is a place to put symbols which are defined + by dynamic objects, are referenced by regular objects, and are + not functions. We must allocate space for them in the process + image and use a R_386_COPY reloc to tell the dynamic linker to + initialize them at run time. The linker script puts the .dynbss + section into the .bss section of the final image. */ + s = bfd_make_section (abfd, ".dynbss"); + if (s == NULL + || ! bfd_set_section_flags (abfd, s, SEC_ALLOC)) + return false; + + /* The .rel.bss section holds copy relocs. This section is not + normally needed. We need to create it here, though, so that the + linker will map it to an output section. If it turns out not to + be needed, we can discard it later. */ + s = bfd_make_section (abfd, ".rel.bss"); + if (s == NULL + || ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY) + || ! bfd_set_section_alignment (abfd, s, 2)) + return false; + + return true; +} + +/* Adjust a symbol defined by a dynamic object and referenced by a + regular object. The current definition is in some section of the + dynamic object, but we're not including those sections. We have to + change the definition to something the rest of the link can + understand. */ + +static boolean +elf_i386_adjust_dynamic_symbol (info, h) + struct bfd_link_info *info; + struct elf_link_hash_entry *h; +{ + bfd *dynobj; + asection *s; + unsigned int power_of_two; + size_t align; + + dynobj = elf_hash_table (info)->dynobj; + + /* Make sure we know what is going on here. */ + BFD_ASSERT (dynobj != NULL + && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0 + && (h->elf_link_hash_flags & ELF_LINK_HASH_REF_REGULAR) != 0 + && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0 + && h->root.type == bfd_link_hash_defined + && (bfd_get_flavour (h->root.u.def.section->owner) + == bfd_target_elf_flavour) + && (elf_elfheader (h->root.u.def.section->owner)->e_type + == ET_DYN) + && h->root.u.def.section->output_section == NULL); + + /* If this is a function, put it in the procedure linkage table. We + will fill in the contents of the procedure linkage table later, + when we know the address of the .got section. */ + if (h->type == STT_FUNC) + { + s = bfd_get_section_by_name (dynobj, ".plt"); + BFD_ASSERT (s != NULL); + + /* If this is the first .plt entry, make room for the special + first entry. */ + if (s->_raw_size == 0) + s->_raw_size += PLT_ENTRY_SIZE; + + /* Set the symbol to this location in the .plt. */ + h->root.u.def.section = s; + h->root.u.def.value = s->_raw_size; + + /* Make room for this entry. */ + s->_raw_size += PLT_ENTRY_SIZE; + + /* We also need to make an entry in the .got section. */ + + s = bfd_get_section_by_name (dynobj, ".got"); + BFD_ASSERT (s != NULL); + s->_raw_size += 4; + + /* We also need to make an entry in the .rel.plt section. */ + + s = bfd_get_section_by_name (dynobj, ".rel.plt"); + BFD_ASSERT (s != NULL); + s->_raw_size += sizeof (Elf32_External_Rel); + + return true; + } + + /* If this is a weak symbol, and there is a real definition, the + processor independent code will have arranged for us to see the + real definition first, and we can just use the same value. */ + if (h->weakdef != NULL) + { + BFD_ASSERT (h->weakdef->root.type == bfd_link_hash_defined); + h->root.u.def.section = h->weakdef->root.u.def.section; + h->root.u.def.value = h->weakdef->root.u.def.value; + h->align = (bfd_size_type) -1; + return true; + } + + /* This is a reference to a symbol defined by a dynamic object which + is not a function. We must allocate it in our .dynbss section, + which will become part of the .bss section of the executable. + There will be an entry for this symbol in the .dynsym section. + The dynamic object will contain position independent code, so all + references from the dynamic object to this symbol will go through + the global offset table. The dynamic linker will use the .dynsym + entry to determine the address it must put in the global offset + table, so both the dynamic object and the regular object will + refer to the same memory location for the variable. */ + + s = bfd_get_section_by_name (dynobj, ".dynbss"); + BFD_ASSERT (s != NULL); + + /* If the symbol is currently defined in the .bss section of the + dynamic object, then it is OK to simply initialize it to zero. + If the symbol is in some other section, we must generate a + R_386_COPY reloc to tell the dynamic linker to copy the initial + value out of the dynamic object and into the runtime process + image. We need to remember the offset into the .rel.bss section + we are going to use, and we coopt the align field for this + purpose (the align field is only used for common symbols, and + these symbols are always defined). It would be cleaner to use a + new field, but that would waste memory. */ + if ((h->root.u.def.section->flags & SEC_LOAD) == 0) + h->align = (bfd_size_type) -1; + else + { + asection *srel; + + srel = bfd_get_section_by_name (dynobj, ".rel.bss"); + BFD_ASSERT (srel != NULL); + h->align = srel->_raw_size; + srel->_raw_size += sizeof (Elf32_External_Rel); + } + + /* We need to figure out the alignment required for this symbol. I + have no idea how ELF linkers handle this. */ + switch (h->size) + { + case 0: + case 1: + power_of_two = 0; + align = 1; + break; + case 2: + power_of_two = 1; + align = 2; + break; + case 3: + case 4: + power_of_two = 2; + align = 4; + break; + case 5: + case 6: + case 7: + case 8: + power_of_two = 3; + align = 8; + break; + default: + power_of_two = 4; + align = 16; + break; + } + + /* Apply the required alignment. */ + s->_raw_size = BFD_ALIGN (s->_raw_size, align); + if (power_of_two > bfd_get_section_alignment (dynobj, s)) + { + if (! bfd_set_section_alignment (dynobj, s, power_of_two)) + return false; + } + + /* Define the symbol as being at this point in the section. */ + h->root.u.def.section = s; + h->root.u.def.value = s->_raw_size; + + /* Increment the section size to make room for the symbol. */ + s->_raw_size += h->size; + + return true; +} + +/* Allocate contents for a section. */ + +static INLINE boolean +elf_i386_allocate_dynamic_section (dynobj, name) + bfd *dynobj; + const char *name; +{ + register asection *s; + + s = bfd_get_section_by_name (dynobj, name); + BFD_ASSERT (s != NULL); + s->contents = (bfd_byte *) bfd_alloc (dynobj, s->_raw_size); + if (s->contents == NULL && s->_raw_size != 0) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + return true; +} + +/* Set the sizes of the dynamic sections. */ + +static boolean +elf_i386_size_dynamic_sections (output_bfd, info) + bfd *output_bfd; + struct bfd_link_info *info; +{ + bfd *dynobj; + asection *s; + + dynobj = elf_hash_table (info)->dynobj; + BFD_ASSERT (dynobj != NULL); + + /* Set the contents of the .interp section to the interpreter. */ + if (! info->shared) + { + s = bfd_get_section_by_name (dynobj, ".interp"); + BFD_ASSERT (s != NULL); + s->_raw_size = sizeof ELF_DYNAMIC_INTERPRETER; + s->contents = (unsigned char *) ELF_DYNAMIC_INTERPRETER; + } + + /* The adjust_dynamic_symbol entry point has determined the sizes of + the various dynamic sections. Allocate some memory for them to + hold contents. */ + if (! elf_i386_allocate_dynamic_section (dynobj, ".plt") + || ! elf_i386_allocate_dynamic_section (dynobj, ".rel.plt") + || ! elf_i386_allocate_dynamic_section (dynobj, ".got") + || ! elf_i386_allocate_dynamic_section (dynobj, ".rel.bss")) + return false; + + /* Add some entries to the .dynamic section. We fill in the values + later, in elf_i386_finish_dynamic_sections, but we must add the + entries now so that we get the correct size for the .dynamic + section. The DT_DEBUG entry is filled in by the dynamic linker + and used by the debugger. */ + if (! bfd_elf32_add_dynamic_entry (info, DT_DEBUG, 0) + || ! bfd_elf32_add_dynamic_entry (info, DT_PLTGOT, 0)) + return false; + + s = bfd_get_section_by_name (dynobj, ".plt"); + BFD_ASSERT (s != NULL); + if (s->_raw_size != 0) + { + if (! bfd_elf32_add_dynamic_entry (info, DT_PLTRELSZ, 0) + || ! bfd_elf32_add_dynamic_entry (info, DT_PLTREL, DT_REL) + || ! bfd_elf32_add_dynamic_entry (info, DT_JMPREL, 0)) + return false; + } + + /* If we didn't need the .rel.bss section, then discard it from the + output file. This is a hack. We don't bother to do it for the + other sections because they normally are needed. */ + s = bfd_get_section_by_name (dynobj, ".rel.bss"); + BFD_ASSERT (s != NULL); + if (s->_raw_size == 0) + { + asection **spp; + + for (spp = &s->output_section->owner->sections; + *spp != s->output_section; + spp = &(*spp)->next) + ; + *spp = s->output_section->next; + --s->output_section->owner->section_count; + } + else + { + if (! bfd_elf32_add_dynamic_entry (info, DT_REL, 0) + || ! bfd_elf32_add_dynamic_entry (info, DT_RELSZ, 0) + || ! bfd_elf32_add_dynamic_entry (info, DT_RELENT, + sizeof (Elf32_External_Rel))) + return false; + } + + return true; +} + +/* Relocate an i386 ELF section. */ + +static boolean +elf_i386_relocate_section (output_bfd, info, input_bfd, input_section, + contents, relocs, local_syms, local_sections, + output_names) + bfd *output_bfd; + struct bfd_link_info *info; + bfd *input_bfd; + asection *input_section; + bfd_byte *contents; + Elf_Internal_Rela *relocs; + Elf_Internal_Sym *local_syms; + asection **local_sections; + char *output_names; +{ + Elf_Internal_Shdr *symtab_hdr; + Elf_Internal_Rela *rel; + Elf_Internal_Rela *relend; + + symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; + + rel = relocs; + relend = relocs + input_section->reloc_count; + for (; rel < relend; rel++) + { + int r_type; + const reloc_howto_type *howto; + long r_symndx; + struct elf_link_hash_entry *h; + Elf_Internal_Sym *sym; + asection *sec; + bfd_vma relocation; + bfd_reloc_status_type r; + + r_type = ELF32_R_TYPE (rel->r_info); + if (r_type < 0 || r_type >= (int) R_386_max) + { + bfd_set_error (bfd_error_bad_value); + return false; + } + howto = elf_howto_table + r_type; + + r_symndx = ELF32_R_SYM (rel->r_info); + + if (info->relocateable) + { + /* This is a relocateable link. We don't have to change + anything, unless the reloc is against a section symbol, + in which case we have to adjust according to where the + section symbol winds up in the output section. */ + if (r_symndx < symtab_hdr->sh_info) + { + sym = local_syms + r_symndx; + if (ELF_ST_TYPE (sym->st_info) == STT_SECTION) + { + bfd_vma val; + + sec = local_sections[r_symndx]; + val = bfd_get_32 (input_bfd, contents + rel->r_offset); + val += sec->output_offset + sym->st_value; + bfd_put_32 (input_bfd, val, contents + rel->r_offset); + } + } + + continue; + } + + /* This is a final link. */ + h = NULL; + sym = NULL; + sec = NULL; + if (r_symndx < symtab_hdr->sh_info) + { + sym = local_syms + r_symndx; + sec = local_sections[r_symndx]; + relocation = (sec->output_section->vma + + sec->output_offset + + sym->st_value); + } + else + { + long indx; + + indx = r_symndx - symtab_hdr->sh_info; + h = elf_sym_hashes (input_bfd)[indx]; + if (h->root.type == bfd_link_hash_defined) + { + sec = h->root.u.def.section; + relocation = (h->root.u.def.value + + sec->output_section->vma + + sec->output_offset); + } + else if (h->root.type == bfd_link_hash_weak) + relocation = 0; + else + { + if (! ((*info->callbacks->undefined_symbol) + (info, h->root.root.string, input_bfd, + input_section, rel->r_offset))) + return false; + relocation = 0; + } + } + + r = _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, rel->r_offset, + relocation, (bfd_vma) 0); + + if (r != bfd_reloc_ok) + { + switch (r) + { + default: + case bfd_reloc_outofrange: + abort (); + case bfd_reloc_overflow: + { + const char *name; + + if (h != NULL) + name = h->root.root.string; + else + { + name = output_names + sym->st_name; + if (name == NULL) + return false; + if (*name == '\0') + name = bfd_section_name (input_bfd, sec); + } + if (! ((*info->callbacks->reloc_overflow) + (info, name, howto->name, (bfd_vma) 0, + input_bfd, input_section, rel->r_offset))) + return false; + } + break; + } + } + } + + return true; +} + +/* Finish up dynamic symbol handling. We set the contents of various + dynamic sections here. */ + +static boolean +elf_i386_finish_dynamic_symbol (output_bfd, info, h, sym) + bfd *output_bfd; + struct bfd_link_info *info; + struct elf_link_hash_entry *h; + Elf_Internal_Sym *sym; +{ + /* If this symbol is not defined by a dynamic object, or is not + referenced by a regular object, ignore it. */ + if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) != 0 + || (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) == 0 + || (h->elf_link_hash_flags & ELF_LINK_HASH_REF_REGULAR) == 0) + { + /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute. */ + if (strcmp (h->root.root.string, "_DYNAMIC") == 0 + || strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0) + sym->st_shndx = SHN_ABS; + return true; + } + + BFD_ASSERT (h->root.type == bfd_link_hash_defined); + BFD_ASSERT (h->dynindx != -1); + + if (h->type == STT_FUNC) + { + asection *splt; + asection *sgot; + asection *srel; + bfd_vma plt_index; + bfd_vma got_offset; + Elf_Internal_Rel rel; + + splt = h->root.u.def.section; + BFD_ASSERT (strcmp (bfd_get_section_name (splt->owner, splt), ".plt") + == 0); + sgot = bfd_get_section_by_name (splt->owner, ".got"); + BFD_ASSERT (sgot != NULL); + srel = bfd_get_section_by_name (splt->owner, ".rel.plt"); + BFD_ASSERT (srel != NULL); + + /* FIXME: This only handles an absolute procedure linkage table. + When producing a dynamic object, we need to generate a + position independent procedure linkage table. */ + + /* Get the index in the procedure linkage table which + corresponds to this symbol. This is the index of this symbol + in all the symbols for which we are making plt entries. The + first entry in the procedure linkage table is reserved. */ + plt_index = h->root.u.def.value / PLT_ENTRY_SIZE - 1; + + /* Get the offset into the .got table of the entry that + corresponds to this function. Each .got entry is 4 bytes. + The first three are reserved. */ + got_offset = (plt_index + 3) * 4; + + /* Fill in the entry in the procedure linkage table. */ + memcpy (splt->contents + h->root.u.def.value, elf_i386_plt_entry, + PLT_ENTRY_SIZE); + bfd_put_32 (output_bfd, + (sgot->output_section->vma + + sgot->output_offset + + got_offset), + splt->contents + h->root.u.def.value + 2); + bfd_put_32 (output_bfd, plt_index * sizeof (Elf32_External_Rel), + splt->contents + h->root.u.def.value + 7); + bfd_put_32 (output_bfd, - (h->root.u.def.value + PLT_ENTRY_SIZE), + splt->contents + h->root.u.def.value + 12); + + /* Fill in the entry in the global offset table. */ + bfd_put_32 (output_bfd, + (splt->output_section->vma + + splt->output_offset + + h->root.u.def.value + + 6), + sgot->contents + got_offset); + + /* Fill in the entry in the .rel.plt section. */ + rel.r_offset = (sgot->output_section->vma + + sgot->output_offset + + got_offset); + rel.r_info = ELF32_R_INFO (h->dynindx, R_386_JUMP_SLOT); + bfd_elf32_swap_reloc_out (output_bfd, &rel, + ((Elf32_External_Rel *) srel->contents + + plt_index)); + + /* Mark the symbol as undefined, rather than as defined in the + .plt section. Leave the value alone. */ + sym->st_shndx = SHN_UNDEF; + } + else + { + /* This is not a function. We have already allocated memory for + it in the .bss section (via .dynbss). All we have to do here + is create a COPY reloc if required. */ + if (h->align != (bfd_size_type) -1) + { + asection *s; + Elf_Internal_Rel rel; + + s = bfd_get_section_by_name (h->root.u.def.section->owner, + ".rel.bss"); + BFD_ASSERT (s != NULL); + + rel.r_offset = (h->root.u.def.value + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset); + rel.r_info = ELF32_R_INFO (h->dynindx, R_386_COPY); + bfd_elf32_swap_reloc_out (output_bfd, &rel, + ((Elf32_External_Rel *) + (s->contents + h->align))); + } + } + + return true; +} + +/* Finish up the dynamic sections. */ + +static boolean +elf_i386_finish_dynamic_sections (output_bfd, info) + bfd *output_bfd; + struct bfd_link_info *info; +{ + asection *splt; + asection *sgot; + asection *sdyn; + Elf32_External_Dyn *dyncon, *dynconend; + + splt = bfd_get_section_by_name (elf_hash_table (info)->dynobj, ".plt"); + sgot = bfd_get_section_by_name (elf_hash_table (info)->dynobj, ".got"); + sdyn = bfd_get_section_by_name (elf_hash_table (info)->dynobj, ".dynamic"); + BFD_ASSERT (splt != NULL && sgot != NULL && sdyn != NULL); + + dyncon = (Elf32_External_Dyn *) sdyn->contents; + dynconend = (Elf32_External_Dyn *) (sdyn->contents + sdyn->_raw_size); + for (; dyncon < dynconend; dyncon++) + { + Elf_Internal_Dyn dyn; + const char *name; + boolean size; + + bfd_elf32_swap_dyn_in (elf_hash_table (info)->dynobj, dyncon, &dyn); + + /* My reading of the SVR4 ABI indicates that the procedure + linkage table relocs (DT_JMPREL) should be included in the + overall relocs (DT_REL). This is what Solaris does. + However, UnixWare can not handle that case. Therefore, we + override the DT_REL and DT_RELSZ entries here to make them + not include the JMPREL relocs. */ + + switch (dyn.d_tag) + { + case DT_PLTGOT: name = ".got"; size = false; break; + case DT_PLTRELSZ: name = ".rel.plt"; size = true; break; + case DT_JMPREL: name = ".rel.plt"; size = false; break; + case DT_REL: name = ".rel.bss"; size = false; break; + case DT_RELSZ: name = ".rel.bss"; size = true; break; + default: name = NULL; size = false; break; + } + + if (name != NULL) + { + asection *s; + + s = bfd_get_section_by_name (output_bfd, name); + BFD_ASSERT (s != NULL); + if (! size) + dyn.d_un.d_ptr = s->vma; + else + { + if (s->_cooked_size != 0) + dyn.d_un.d_val = s->_cooked_size; + else + dyn.d_un.d_val = s->_raw_size; + } + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + } + } + + /* Fill in the first entry in the procedure linkage table. */ + if (splt->_raw_size > 0) + { + memcpy (splt->contents, elf_i386_plt0_entry, PLT_ENTRY_SIZE); + bfd_put_32 (output_bfd, + sgot->output_section->vma + sgot->output_offset + 4, + splt->contents + 2); + bfd_put_32 (output_bfd, + sgot->output_section->vma + sgot->output_offset + 8, + splt->contents + 8); + } + + /* Fill in the first three entries in the global offset table. */ + if (sgot->_raw_size > 0) + { + bfd_put_32 (output_bfd, + sdyn->output_section->vma + sdyn->output_offset, + sgot->contents); + bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + 4); + bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + 8); + } + + elf_section_data (sgot->output_section)->this_hdr.sh_entsize = 4; + + /* UnixWare sets the entsize of .plt to 4, although that doesn't + really seem like the right value. */ + elf_section_data (splt->output_section)->this_hdr.sh_entsize = 4; + + return true; +} + +#define TARGET_LITTLE_SYM bfd_elf32_i386_vec +#define TARGET_LITTLE_NAME "elf32-i386" +#define ELF_ARCH bfd_arch_i386 +#define ELF_MACHINE_CODE EM_386 +#define elf_info_to_howto elf_i386_info_to_howto +#define elf_info_to_howto_rel elf_i386_info_to_howto_rel +#define bfd_elf32_bfd_reloc_type_lookup elf_i386_reloc_type_lookup +#define ELF_MAXPAGESIZE 0x1000 +#define elf_backend_create_dynamic_sections \ + elf_i386_create_dynamic_sections +#define elf_backend_adjust_dynamic_symbol \ + elf_i386_adjust_dynamic_symbol +#define elf_backend_size_dynamic_sections \ + elf_i386_size_dynamic_sections +#define elf_backend_relocate_section elf_i386_relocate_section +#define elf_backend_finish_dynamic_symbol \ + elf_i386_finish_dynamic_symbol +#define elf_backend_finish_dynamic_sections \ + elf_i386_finish_dynamic_sections + +#include "elf32-target.h" diff --git a/gnu/usr.bin/gdb/bfd/elf32-target.h b/gnu/usr.bin/gdb/bfd/elf32-target.h new file mode 100644 index 000000000000..67dabecc71fe --- /dev/null +++ b/gnu/usr.bin/gdb/bfd/elf32-target.h @@ -0,0 +1,358 @@ +/* Target definitions for 32-bit ELF + Copyright 1993, 1994 Free Software Foundation, Inc. + +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 structure contains everything that BFD knows about a target. + It includes things like its byte order, name, what routines to call + to do various operations, etc. Every BFD points to a target structure + with its "xvec" member. + + There are two such structures here: one for big-endian machines and + one for little-endian machines. */ + +#define bfd_elf32_close_and_cleanup _bfd_generic_close_and_cleanup +#define bfd_elf32_bfd_free_cached_info _bfd_generic_bfd_free_cached_info +#ifndef bfd_elf32_get_section_contents +#define bfd_elf32_get_section_contents _bfd_generic_get_section_contents +#endif + +#define bfd_elf32_bfd_get_relocated_section_contents \ + bfd_generic_get_relocated_section_contents +#define bfd_elf32_bfd_relax_section bfd_generic_relax_section +#define bfd_elf32_bfd_make_debug_symbol \ + ((asymbol *(*) PARAMS ((bfd *, void *, unsigned long))) bfd_nullvoidptr) + +#ifndef bfd_elf32_bfd_copy_private_section_data +#define bfd_elf32_bfd_copy_private_section_data \ + ((boolean (*) PARAMS ((bfd *, asection *, bfd *, asection *))) bfd_true) +#endif +#ifndef bfd_elf32_bfd_copy_private_bfd_data +#define bfd_elf32_bfd_copy_private_bfd_data \ + ((boolean (*) PARAMS ((bfd *, bfd *))) bfd_true) +#endif +#ifndef bfd_elf32_bfd_is_local_label +#define bfd_elf32_bfd_is_local_label bfd_generic_is_local_label +#endif + +#ifndef bfd_elf32_get_dynamic_reloc_upper_bound +#define bfd_elf32_get_dynamic_reloc_upper_bound \ + _bfd_nodynamic_get_dynamic_reloc_upper_bound +#endif +#ifndef bfd_elf32_canonicalize_dynamic_reloc +#define bfd_elf32_canonicalize_dynamic_reloc \ + _bfd_nodynamic_canonicalize_dynamic_reloc +#endif + +#ifdef elf_backend_relocate_section +#ifndef bfd_elf32_bfd_link_hash_table_create +#define bfd_elf32_bfd_link_hash_table_create _bfd_elf_link_hash_table_create +#endif +#else /* ! defined (elf_backend_relocate_section) */ +/* If no backend relocate_section routine, use the generic linker. */ +#ifndef bfd_elf32_bfd_link_hash_table_create +#define bfd_elf32_bfd_link_hash_table_create \ + _bfd_generic_link_hash_table_create +#endif +#ifndef bfd_elf32_bfd_link_add_symbols +#define bfd_elf32_bfd_link_add_symbols _bfd_generic_link_add_symbols +#endif +#ifndef bfd_elf32_bfd_final_link +#define bfd_elf32_bfd_final_link _bfd_generic_final_link +#endif +#endif /* ! defined (elf_backend_relocate_section) */ + +#ifndef elf_info_to_howto_rel +#define elf_info_to_howto_rel 0 +#endif + +#ifndef ELF_MAXPAGESIZE +#define ELF_MAXPAGESIZE 1 +#endif + +#ifndef elf_backend_collect +#define elf_backend_collect false +#endif + +#ifndef elf_backend_sym_is_global +#define elf_backend_sym_is_global 0 +#endif +#ifndef elf_backend_object_p +#define elf_backend_object_p 0 +#endif +#ifndef elf_backend_symbol_processing +#define elf_backend_symbol_processing 0 +#endif +#ifndef elf_backend_symbol_table_processing +#define elf_backend_symbol_table_processing 0 +#endif +#ifndef elf_backend_section_processing +#define elf_backend_section_processing 0 +#endif +#ifndef elf_backend_section_from_shdr +#define elf_backend_section_from_shdr 0 +#endif +#ifndef elf_backend_fake_sections +#define elf_backend_fake_sections 0 +#endif +#ifndef elf_backend_section_from_bfd_section +#define elf_backend_section_from_bfd_section 0 +#endif +#ifndef elf_backend_add_symbol_hook +#define elf_backend_add_symbol_hook 0 +#endif +#ifndef elf_backend_link_output_symbol_hook +#define elf_backend_link_output_symbol_hook 0 +#endif +#ifndef elf_backend_create_dynamic_sections +#define elf_backend_create_dynamic_sections 0 +#endif +#ifndef elf_backend_adjust_dynamic_symbol +#define elf_backend_adjust_dynamic_symbol 0 +#endif +#ifndef elf_backend_size_dynamic_sections +#define elf_backend_size_dynamic_sections 0 +#endif +#ifndef elf_backend_relocate_section +#define elf_backend_relocate_section 0 +#endif +#ifndef elf_backend_finish_dynamic_symbol +#define elf_backend_finish_dynamic_symbol 0 +#endif +#ifndef elf_backend_finish_dynamic_sections +#define elf_backend_finish_dynamic_sections 0 +#endif +#ifndef elf_backend_begin_write_processing +#define elf_backend_begin_write_processing 0 +#endif +#ifndef elf_backend_final_write_processing +#define elf_backend_final_write_processing 0 +#endif +#ifndef elf_backend_ecoff_debug_swap +#define elf_backend_ecoff_debug_swap 0 +#endif + +static CONST struct elf_backend_data elf32_bed = +{ +#ifdef USE_REL + 0, /* use_rela_p */ +#else + 1, /* use_rela_p */ +#endif + 0, /* elf_64_p */ + ELF_ARCH, /* arch */ + ELF_MACHINE_CODE, /* elf_machine_code */ + ELF_MAXPAGESIZE, /* maxpagesize */ + elf_backend_collect, + elf_info_to_howto, + elf_info_to_howto_rel, + elf_backend_sym_is_global, + elf_backend_object_p, + elf_backend_symbol_processing, + elf_backend_symbol_table_processing, + elf_backend_section_processing, + elf_backend_section_from_shdr, + elf_backend_fake_sections, + elf_backend_section_from_bfd_section, + elf_backend_add_symbol_hook, + elf_backend_link_output_symbol_hook, + elf_backend_create_dynamic_sections, + elf_backend_adjust_dynamic_symbol, + elf_backend_size_dynamic_sections, + elf_backend_relocate_section, + elf_backend_finish_dynamic_symbol, + elf_backend_finish_dynamic_sections, + elf_backend_begin_write_processing, + elf_backend_final_write_processing, + elf_backend_ecoff_debug_swap +}; + +#ifdef TARGET_BIG_SYM +const bfd_target TARGET_BIG_SYM = +{ + /* name: identify kind of target */ + TARGET_BIG_NAME, + + /* flavour: general indication about file */ + bfd_target_elf_flavour, + + /* byteorder_big_p: data is big endian */ + true, + + /* header_byteorder_big_p: header is also big endian */ + true, + + /* object_flags: mask of all file flags */ + (HAS_RELOC | EXEC_P | HAS_LINENO | HAS_DEBUG | HAS_SYMS | HAS_LOCALS | + DYNAMIC | WP_TEXT | D_PAGED), + + /* section_flags: mask of all section flags */ + (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_READONLY | + SEC_CODE | SEC_DATA | SEC_DEBUGGING), + + /* leading_symbol_char: is the first char of a user symbol + predictable, and if so what is it */ + 0, + + /* ar_pad_char: pad character for filenames within an archive header + FIXME: this really has nothing to do with ELF, this is a characteristic + of the archiver and/or os and should be independently tunable */ + '/', + + /* ar_max_namelen: maximum number of characters in an archive header + FIXME: this really has nothing to do with ELF, this is a characteristic + of the archiver and should be independently tunable. This value is + a WAG (wild a** guess) */ + 14, + + /* align_power_min: minimum alignment restriction for any section + FIXME: this value may be target machine dependent */ + 3, + + /* Routines to byte-swap various sized integers from the data sections */ + bfd_getb64, bfd_getb_signed_64, bfd_putb64, + bfd_getb32, bfd_getb_signed_32, bfd_putb32, + bfd_getb16, bfd_getb_signed_16, bfd_putb16, + + /* Routines to byte-swap various sized integers from the file headers */ + bfd_getb64, bfd_getb_signed_64, bfd_putb64, + bfd_getb32, bfd_getb_signed_32, bfd_putb32, + bfd_getb16, bfd_getb_signed_16, bfd_putb16, + + /* bfd_check_format: check the format of a file being read */ + { _bfd_dummy_target, /* unknown format */ + bfd_elf32_object_p, /* assembler/linker output (object file) */ + bfd_generic_archive_p, /* an archive */ + bfd_elf32_core_file_p /* a core file */ + }, + + /* bfd_set_format: set the format of a file being written */ + { bfd_false, + bfd_elf_mkobject, + _bfd_generic_mkarchive, + bfd_false + }, + + /* bfd_write_contents: write cached information into a file being written */ + { bfd_false, + bfd_elf32_write_object_contents, + _bfd_write_archive_contents, + bfd_false + }, + + BFD_JUMP_TABLE_GENERIC (bfd_elf32), + BFD_JUMP_TABLE_COPY (bfd_elf32), + BFD_JUMP_TABLE_CORE (bfd_elf32), + BFD_JUMP_TABLE_ARCHIVE (_bfd_archive_coff), + BFD_JUMP_TABLE_SYMBOLS (bfd_elf32), + BFD_JUMP_TABLE_RELOCS (bfd_elf32), + BFD_JUMP_TABLE_WRITE (bfd_elf32), + BFD_JUMP_TABLE_LINK (bfd_elf32), + BFD_JUMP_TABLE_DYNAMIC (bfd_elf32), + + /* backend_data: */ + (PTR) &elf32_bed, +}; +#endif + +#ifdef TARGET_LITTLE_SYM +const bfd_target TARGET_LITTLE_SYM = +{ + /* name: identify kind of target */ + TARGET_LITTLE_NAME, + + /* flavour: general indication about file */ + bfd_target_elf_flavour, + + /* byteorder_big_p: data is big endian */ + false, /* Nope -- this one's little endian */ + + /* header_byteorder_big_p: header is also big endian */ + false, /* Nope -- this one's little endian */ + + /* object_flags: mask of all file flags */ + (HAS_RELOC | EXEC_P | HAS_LINENO | HAS_DEBUG | HAS_SYMS | HAS_LOCALS | + DYNAMIC | WP_TEXT | D_PAGED), + + /* section_flags: mask of all section flags */ + (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_READONLY | + SEC_CODE | SEC_DATA | SEC_DEBUGGING), + + /* leading_symbol_char: is the first char of a user symbol + predictable, and if so what is it */ + 0, + + /* ar_pad_char: pad character for filenames within an archive header + FIXME: this really has nothing to do with ELF, this is a characteristic + of the archiver and/or os and should be independently tunable */ + '/', + + /* ar_max_namelen: maximum number of characters in an archive header + FIXME: this really has nothing to do with ELF, this is a characteristic + of the archiver and should be independently tunable. This value is + a WAG (wild a** guess) */ + 14, + + /* align_power_min: minimum alignment restriction for any section + FIXME: this value may be target machine dependent */ + 3, + + /* Routines to byte-swap various sized integers from the data sections */ + bfd_getl64, bfd_getl_signed_64, bfd_putl64, + bfd_getl32, bfd_getl_signed_32, bfd_putl32, + bfd_getl16, bfd_getl_signed_16, bfd_putl16, + + /* Routines to byte-swap various sized integers from the file headers */ + bfd_getl64, bfd_getl_signed_64, bfd_putl64, + bfd_getl32, bfd_getl_signed_32, bfd_putl32, + bfd_getl16, bfd_getl_signed_16, bfd_putl16, + + /* bfd_check_format: check the format of a file being read */ + { _bfd_dummy_target, /* unknown format */ + bfd_elf32_object_p, /* assembler/linker output (object file) */ + bfd_generic_archive_p, /* an archive */ + bfd_elf32_core_file_p /* a core file */ + }, + + /* bfd_set_format: set the format of a file being written */ + { bfd_false, + bfd_elf_mkobject, + _bfd_generic_mkarchive, + bfd_false + }, + + /* bfd_write_contents: write cached information into a file being written */ + { bfd_false, + bfd_elf32_write_object_contents, + _bfd_write_archive_contents, + bfd_false + }, + + BFD_JUMP_TABLE_GENERIC (bfd_elf32), + BFD_JUMP_TABLE_COPY (bfd_elf32), + BFD_JUMP_TABLE_CORE (bfd_elf32), + BFD_JUMP_TABLE_ARCHIVE (_bfd_archive_coff), + BFD_JUMP_TABLE_SYMBOLS (bfd_elf32), + BFD_JUMP_TABLE_RELOCS (bfd_elf32), + BFD_JUMP_TABLE_WRITE (bfd_elf32), + BFD_JUMP_TABLE_LINK (bfd_elf32), + BFD_JUMP_TABLE_DYNAMIC (bfd_elf32), + + /* backend_data: */ + (PTR) &elf32_bed, +}; +#endif diff --git a/gnu/usr.bin/gdb/bfd/elf32.c b/gnu/usr.bin/gdb/bfd/elf32.c new file mode 100644 index 000000000000..862dd2df592c --- /dev/null +++ b/gnu/usr.bin/gdb/bfd/elf32.c @@ -0,0 +1,23 @@ +/* ELF 32-bit executable support for BFD. + Copyright 1993 Free Software Foundation, Inc. + +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. */ + +#define ARCH_SIZE 32 + + +#include "elfcode.h" diff --git a/gnu/usr.bin/gdb/bfd/elfcode.h b/gnu/usr.bin/gdb/bfd/elfcode.h new file mode 100644 index 000000000000..5d9ffd10d279 --- /dev/null +++ b/gnu/usr.bin/gdb/bfd/elfcode.h @@ -0,0 +1,6351 @@ +/* ELF executable support for BFD. + Copyright 1991, 1992, 1993, 1994 Free Software Foundation, Inc. + + Written by Fred Fish @ Cygnus Support, from information published + in "UNIX System V Release 4, Programmers Guide: ANSI C and + Programming Support Tools". Sufficient support for gdb. + + Rewritten by Mark Eichin @ Cygnus Support, from information + published in "System V Application Binary Interface", chapters 4 + and 5, as well as the various "Processor Supplement" documents + derived from it. Added support for assembler and other object file + utilities. Further work done by Ken Raeburn (Cygnus Support), Michael + Meissner (Open Software Foundation), and Peter Hoogenboom (University + of Utah) to finish and extend this. + +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. */ + +/* Problems and other issues to resolve. + + (1) BFD expects there to be some fixed number of "sections" in + the object file. I.E. there is a "section_count" variable in the + bfd structure which contains the number of sections. However, ELF + supports multiple "views" of a file. In particular, with current + implementations, executable files typically have two tables, a + program header table and a section header table, both of which + partition the executable. + + In ELF-speak, the "linking view" of the file uses the section header + table to access "sections" within the file, and the "execution view" + uses the program header table to access "segments" within the file. + "Segments" typically may contain all the data from one or more + "sections". + + Note that the section header table is optional in ELF executables, + but it is this information that is most useful to gdb. If the + section header table is missing, then gdb should probably try + to make do with the program header table. (FIXME) + + (2) The code in this file is compiled twice, once in 32-bit mode and + once in 64-bit mode. More of it should be made size-independent + and moved into elf.c. + + (3) ELF section symbols are handled rather sloppily now. This should + be cleaned up, and ELF section symbols reconciled with BFD section + symbols. + + (4) We need a published spec for 64-bit ELF. We've got some stuff here + that we're using for SPARC V9 64-bit chips, but don't assume that + it's cast in stone. + */ + +#include <string.h> /* For strrchr and friends */ +#include "bfd.h" +#include "sysdep.h" +#include "bfdlink.h" +#include "libbfd.h" +#include "libelf.h" + +/* Renaming structures, typedefs, macros and functions to be size-specific. */ +#define Elf_External_Ehdr NAME(Elf,External_Ehdr) +#define Elf_External_Sym NAME(Elf,External_Sym) +#define Elf_External_Shdr NAME(Elf,External_Shdr) +#define Elf_External_Phdr NAME(Elf,External_Phdr) +#define Elf_External_Rel NAME(Elf,External_Rel) +#define Elf_External_Rela NAME(Elf,External_Rela) +#define Elf_External_Dyn NAME(Elf,External_Dyn) + +#define elf_core_file_failing_command NAME(bfd_elf,core_file_failing_command) +#define elf_core_file_failing_signal NAME(bfd_elf,core_file_failing_signal) +#define elf_core_file_matches_executable_p \ + NAME(bfd_elf,core_file_matches_executable_p) +#define elf_object_p NAME(bfd_elf,object_p) +#define elf_core_file_p NAME(bfd_elf,core_file_p) +#define elf_get_symtab_upper_bound NAME(bfd_elf,get_symtab_upper_bound) +#define elf_get_dynamic_symtab_upper_bound \ + NAME(bfd_elf,get_dynamic_symtab_upper_bound) +#define elf_swap_reloc_in NAME(bfd_elf,swap_reloc_in) +#define elf_swap_reloca_in NAME(bfd_elf,swap_reloca_in) +#define elf_swap_reloc_out NAME(bfd_elf,swap_reloc_out) +#define elf_swap_reloca_out NAME(bfd_elf,swap_reloca_out) +#define elf_swap_symbol_in NAME(bfd_elf,swap_symbol_in) +#define elf_swap_symbol_out NAME(bfd_elf,swap_symbol_out) +#define elf_swap_dyn_in NAME(bfd_elf,swap_dyn_in) +#define elf_swap_dyn_out NAME(bfd_elf,swap_dyn_out) +#define elf_get_reloc_upper_bound NAME(bfd_elf,get_reloc_upper_bound) +#define elf_canonicalize_reloc NAME(bfd_elf,canonicalize_reloc) +#define elf_get_symtab NAME(bfd_elf,get_symtab) +#define elf_canonicalize_dynamic_symtab \ + NAME(bfd_elf,canonicalize_dynamic_symtab) +#define elf_make_empty_symbol NAME(bfd_elf,make_empty_symbol) +#define elf_get_symbol_info NAME(bfd_elf,get_symbol_info) +#define elf_print_symbol NAME(bfd_elf,print_symbol) +#define elf_get_lineno NAME(bfd_elf,get_lineno) +#define elf_set_arch_mach NAME(bfd_elf,set_arch_mach) +#define elf_find_nearest_line NAME(bfd_elf,find_nearest_line) +#define elf_sizeof_headers NAME(bfd_elf,sizeof_headers) +#define elf_set_section_contents NAME(bfd_elf,set_section_contents) +#define elf_no_info_to_howto NAME(bfd_elf,no_info_to_howto) +#define elf_no_info_to_howto_rel NAME(bfd_elf,no_info_to_howto_rel) +#define elf_new_section_hook NAME(bfd_elf,new_section_hook) +#define write_relocs NAME(bfd_elf,_write_relocs) +#define elf_find_section NAME(bfd_elf,find_section) +#define elf_bfd_link_add_symbols NAME(bfd_elf,bfd_link_add_symbols) +#define elf_add_dynamic_entry NAME(bfd_elf,add_dynamic_entry) +#define elf_bfd_final_link NAME(bfd_elf,bfd_final_link) + +#if ARCH_SIZE == 64 +#define ELF_R_INFO(X,Y) ELF64_R_INFO(X,Y) +#define ELF_R_SYM(X) ELF64_R_SYM(X) +#define ELF_R_TYPE(X) ELF64_R_TYPE(X) +#define ELFCLASS ELFCLASS64 +#define FILE_ALIGN 8 +#define LOG_FILE_ALIGN 3 +#endif +#if ARCH_SIZE == 32 +#define ELF_R_INFO(X,Y) ELF32_R_INFO(X,Y) +#define ELF_R_SYM(X) ELF32_R_SYM(X) +#define ELF_R_TYPE(X) ELF32_R_TYPE(X) +#define ELFCLASS ELFCLASS32 +#define FILE_ALIGN 4 +#define LOG_FILE_ALIGN 2 +#endif + +/* Forward declarations of static functions */ + +static unsigned long bfd_add_to_strtab + PARAMS ((bfd *, struct strtab *, const char *)); +static asection *section_from_elf_index PARAMS ((bfd *, unsigned int)); + +static int elf_section_from_bfd_section PARAMS ((bfd *, struct sec *)); + +static long elf_slurp_symbol_table PARAMS ((bfd *, asymbol **, boolean)); + +static boolean elf_slurp_reloc_table PARAMS ((bfd *, asection *, asymbol **)); + +static int elf_symbol_from_bfd_symbol PARAMS ((bfd *, + struct symbol_cache_entry **)); + +static boolean elf_compute_section_file_positions + PARAMS ((bfd *, struct bfd_link_info *)); +static boolean prep_headers PARAMS ((bfd *)); +static void elf_fake_sections PARAMS ((bfd *, asection *, PTR)); +static boolean assign_section_numbers PARAMS ((bfd *)); +static file_ptr align_file_position PARAMS ((file_ptr)); +static file_ptr assign_file_position_for_section + PARAMS ((Elf_Internal_Shdr *, file_ptr, boolean)); +static boolean assign_file_positions_except_relocs PARAMS ((bfd *, boolean)); +static void assign_file_positions_for_relocs PARAMS ((bfd *)); +static bfd_size_type get_program_header_size PARAMS ((bfd *)); +static file_ptr map_program_segments + PARAMS ((bfd *, file_ptr, Elf_Internal_Shdr *, bfd_size_type)); + +static boolean elf_map_symbols PARAMS ((bfd *)); +static boolean swap_out_syms PARAMS ((bfd *)); + +static boolean bfd_section_from_shdr PARAMS ((bfd *, unsigned int shindex)); + +#ifdef DEBUG +static void elf_debug_section PARAMS ((char *, int, Elf_Internal_Shdr *)); +static void elf_debug_file PARAMS ((Elf_Internal_Ehdr *)); +#endif + +#define elf_string_from_elf_strtab(abfd,strindex) \ + elf_string_from_elf_section(abfd,elf_elfheader(abfd)->e_shstrndx,strindex) + + +/* Structure swapping routines */ + +/* Should perhaps use put_offset, put_word, etc. For now, the two versions + can be handled by explicitly specifying 32 bits or "the long type". */ +#if ARCH_SIZE == 64 +#define put_word bfd_h_put_64 +#define get_word bfd_h_get_64 +#endif +#if ARCH_SIZE == 32 +#define put_word bfd_h_put_32 +#define get_word bfd_h_get_32 +#endif + +/* Translate an ELF symbol in external format into an ELF symbol in internal + format. */ + +void +elf_swap_symbol_in (abfd, src, dst) + bfd *abfd; + Elf_External_Sym *src; + Elf_Internal_Sym *dst; +{ + dst->st_name = bfd_h_get_32 (abfd, (bfd_byte *) src->st_name); + dst->st_value = get_word (abfd, (bfd_byte *) src->st_value); + dst->st_size = get_word (abfd, (bfd_byte *) src->st_size); + dst->st_info = bfd_h_get_8 (abfd, (bfd_byte *) src->st_info); + dst->st_other = bfd_h_get_8 (abfd, (bfd_byte *) src->st_other); + dst->st_shndx = bfd_h_get_16 (abfd, (bfd_byte *) src->st_shndx); +} + +/* Translate an ELF symbol in internal format into an ELF symbol in external + format. */ + +void +elf_swap_symbol_out (abfd, src, dst) + bfd *abfd; + Elf_Internal_Sym *src; + Elf_External_Sym *dst; +{ + bfd_h_put_32 (abfd, src->st_name, dst->st_name); + put_word (abfd, src->st_value, dst->st_value); + put_word (abfd, src->st_size, dst->st_size); + bfd_h_put_8 (abfd, src->st_info, dst->st_info); + bfd_h_put_8 (abfd, src->st_other, dst->st_other); + bfd_h_put_16 (abfd, src->st_shndx, dst->st_shndx); +} + + +/* Translate an ELF file header in external format into an ELF file header in + internal format. */ + +static void +elf_swap_ehdr_in (abfd, src, dst) + bfd *abfd; + Elf_External_Ehdr *src; + Elf_Internal_Ehdr *dst; +{ + memcpy (dst->e_ident, src->e_ident, EI_NIDENT); + dst->e_type = bfd_h_get_16 (abfd, (bfd_byte *) src->e_type); + dst->e_machine = bfd_h_get_16 (abfd, (bfd_byte *) src->e_machine); + dst->e_version = bfd_h_get_32 (abfd, (bfd_byte *) src->e_version); + dst->e_entry = get_word (abfd, (bfd_byte *) src->e_entry); + dst->e_phoff = get_word (abfd, (bfd_byte *) src->e_phoff); + dst->e_shoff = get_word (abfd, (bfd_byte *) src->e_shoff); + dst->e_flags = bfd_h_get_32 (abfd, (bfd_byte *) src->e_flags); + dst->e_ehsize = bfd_h_get_16 (abfd, (bfd_byte *) src->e_ehsize); + dst->e_phentsize = bfd_h_get_16 (abfd, (bfd_byte *) src->e_phentsize); + dst->e_phnum = bfd_h_get_16 (abfd, (bfd_byte *) src->e_phnum); + dst->e_shentsize = bfd_h_get_16 (abfd, (bfd_byte *) src->e_shentsize); + dst->e_shnum = bfd_h_get_16 (abfd, (bfd_byte *) src->e_shnum); + dst->e_shstrndx = bfd_h_get_16 (abfd, (bfd_byte *) src->e_shstrndx); +} + +/* Translate an ELF file header in internal format into an ELF file header in + external format. */ + +static void +elf_swap_ehdr_out (abfd, src, dst) + bfd *abfd; + Elf_Internal_Ehdr *src; + Elf_External_Ehdr *dst; +{ + memcpy (dst->e_ident, src->e_ident, EI_NIDENT); + /* note that all elements of dst are *arrays of unsigned char* already... */ + bfd_h_put_16 (abfd, src->e_type, dst->e_type); + bfd_h_put_16 (abfd, src->e_machine, dst->e_machine); + bfd_h_put_32 (abfd, src->e_version, dst->e_version); + put_word (abfd, src->e_entry, dst->e_entry); + put_word (abfd, src->e_phoff, dst->e_phoff); + put_word (abfd, src->e_shoff, dst->e_shoff); + bfd_h_put_32 (abfd, src->e_flags, dst->e_flags); + bfd_h_put_16 (abfd, src->e_ehsize, dst->e_ehsize); + bfd_h_put_16 (abfd, src->e_phentsize, dst->e_phentsize); + bfd_h_put_16 (abfd, src->e_phnum, dst->e_phnum); + bfd_h_put_16 (abfd, src->e_shentsize, dst->e_shentsize); + bfd_h_put_16 (abfd, src->e_shnum, dst->e_shnum); + bfd_h_put_16 (abfd, src->e_shstrndx, dst->e_shstrndx); +} + + +/* Translate an ELF section header table entry in external format into an + ELF section header table entry in internal format. */ + +static void +elf_swap_shdr_in (abfd, src, dst) + bfd *abfd; + Elf_External_Shdr *src; + Elf_Internal_Shdr *dst; +{ + dst->sh_name = bfd_h_get_32 (abfd, (bfd_byte *) src->sh_name); + dst->sh_type = bfd_h_get_32 (abfd, (bfd_byte *) src->sh_type); + dst->sh_flags = get_word (abfd, (bfd_byte *) src->sh_flags); + dst->sh_addr = get_word (abfd, (bfd_byte *) src->sh_addr); + dst->sh_offset = get_word (abfd, (bfd_byte *) src->sh_offset); + dst->sh_size = get_word (abfd, (bfd_byte *) src->sh_size); + dst->sh_link = bfd_h_get_32 (abfd, (bfd_byte *) src->sh_link); + dst->sh_info = bfd_h_get_32 (abfd, (bfd_byte *) src->sh_info); + dst->sh_addralign = get_word (abfd, (bfd_byte *) src->sh_addralign); + dst->sh_entsize = get_word (abfd, (bfd_byte *) src->sh_entsize); + /* we haven't done any processing on it yet, so... */ + dst->rawdata = (void *) 0; +} + +/* Translate an ELF section header table entry in internal format into an + ELF section header table entry in external format. */ + +static void +elf_swap_shdr_out (abfd, src, dst) + bfd *abfd; + Elf_Internal_Shdr *src; + Elf_External_Shdr *dst; +{ + /* note that all elements of dst are *arrays of unsigned char* already... */ + bfd_h_put_32 (abfd, src->sh_name, dst->sh_name); + bfd_h_put_32 (abfd, src->sh_type, dst->sh_type); + put_word (abfd, src->sh_flags, dst->sh_flags); + put_word (abfd, src->sh_addr, dst->sh_addr); + put_word (abfd, src->sh_offset, dst->sh_offset); + put_word (abfd, src->sh_size, dst->sh_size); + bfd_h_put_32 (abfd, src->sh_link, dst->sh_link); + bfd_h_put_32 (abfd, src->sh_info, dst->sh_info); + put_word (abfd, src->sh_addralign, dst->sh_addralign); + put_word (abfd, src->sh_entsize, dst->sh_entsize); +} + + +/* Translate an ELF program header table entry in external format into an + ELF program header table entry in internal format. */ + +static void +elf_swap_phdr_in (abfd, src, dst) + bfd *abfd; + Elf_External_Phdr *src; + Elf_Internal_Phdr *dst; +{ + dst->p_type = bfd_h_get_32 (abfd, (bfd_byte *) src->p_type); + dst->p_flags = bfd_h_get_32 (abfd, (bfd_byte *) src->p_flags); + dst->p_offset = get_word (abfd, (bfd_byte *) src->p_offset); + dst->p_vaddr = get_word (abfd, (bfd_byte *) src->p_vaddr); + dst->p_paddr = get_word (abfd, (bfd_byte *) src->p_paddr); + dst->p_filesz = get_word (abfd, (bfd_byte *) src->p_filesz); + dst->p_memsz = get_word (abfd, (bfd_byte *) src->p_memsz); + dst->p_align = get_word (abfd, (bfd_byte *) src->p_align); +} + +static void +elf_swap_phdr_out (abfd, src, dst) + bfd *abfd; + Elf_Internal_Phdr *src; + Elf_External_Phdr *dst; +{ + /* note that all elements of dst are *arrays of unsigned char* already... */ + bfd_h_put_32 (abfd, src->p_type, dst->p_type); + put_word (abfd, src->p_offset, dst->p_offset); + put_word (abfd, src->p_vaddr, dst->p_vaddr); + put_word (abfd, src->p_paddr, dst->p_paddr); + put_word (abfd, src->p_filesz, dst->p_filesz); + put_word (abfd, src->p_memsz, dst->p_memsz); + bfd_h_put_32 (abfd, src->p_flags, dst->p_flags); + put_word (abfd, src->p_align, dst->p_align); +} + +/* Translate an ELF reloc from external format to internal format. */ +INLINE void +elf_swap_reloc_in (abfd, src, dst) + bfd *abfd; + Elf_External_Rel *src; + Elf_Internal_Rel *dst; +{ + dst->r_offset = get_word (abfd, (bfd_byte *) src->r_offset); + dst->r_info = get_word (abfd, (bfd_byte *) src->r_info); +} + +INLINE void +elf_swap_reloca_in (abfd, src, dst) + bfd *abfd; + Elf_External_Rela *src; + Elf_Internal_Rela *dst; +{ + dst->r_offset = get_word (abfd, (bfd_byte *) src->r_offset); + dst->r_info = get_word (abfd, (bfd_byte *) src->r_info); + dst->r_addend = get_word (abfd, (bfd_byte *) src->r_addend); +} + +/* Translate an ELF reloc from internal format to external format. */ +INLINE void +elf_swap_reloc_out (abfd, src, dst) + bfd *abfd; + Elf_Internal_Rel *src; + Elf_External_Rel *dst; +{ + put_word (abfd, src->r_offset, dst->r_offset); + put_word (abfd, src->r_info, dst->r_info); +} + +INLINE void +elf_swap_reloca_out (abfd, src, dst) + bfd *abfd; + Elf_Internal_Rela *src; + Elf_External_Rela *dst; +{ + put_word (abfd, src->r_offset, dst->r_offset); + put_word (abfd, src->r_info, dst->r_info); + put_word (abfd, src->r_addend, dst->r_addend); +} + +INLINE void +elf_swap_dyn_in (abfd, src, dst) + bfd *abfd; + const Elf_External_Dyn *src; + Elf_Internal_Dyn *dst; +{ + dst->d_tag = get_word (abfd, src->d_tag); + dst->d_un.d_val = get_word (abfd, src->d_un.d_val); +} + +INLINE void +elf_swap_dyn_out (abfd, src, dst) + bfd *abfd; + const Elf_Internal_Dyn *src; + Elf_External_Dyn *dst; +{ + put_word (abfd, src->d_tag, dst->d_tag); + put_word (abfd, src->d_un.d_val, dst->d_un.d_val); +} + +/* String table creation/manipulation routines */ + +static struct strtab * +bfd_new_strtab (abfd) + bfd *abfd; +{ + struct strtab *ss; + + ss = (struct strtab *) malloc (sizeof (struct strtab)); + if (!ss) + { + bfd_set_error (bfd_error_no_memory); + return NULL; + } + ss->tab = malloc (1); + if (!ss->tab) + { + bfd_set_error (bfd_error_no_memory); + return NULL; + } + *ss->tab = 0; + ss->nentries = 0; + ss->length = 1; + + return ss; +} + +static unsigned long +bfd_add_to_strtab (abfd, ss, str) + bfd *abfd; + struct strtab *ss; + const char *str; +{ + /* should search first, but for now: */ + /* include the trailing NUL */ + int ln = strlen (str) + 1; + + /* FIXME: This is slow. Also, we could combine this with the a.out + string table building and use a hash table, although it might not + be worth it since ELF symbols don't include debugging information + and thus have much less overlap. */ + ss->tab = realloc (ss->tab, ss->length + ln); + if (ss->tab == NULL) + { + bfd_set_error (bfd_error_no_memory); + return (unsigned long) -1; + } + + strcpy (ss->tab + ss->length, str); + ss->nentries++; + ss->length += ln; + + return ss->length - ln; +} + +static int +bfd_add_2_to_strtab (abfd, ss, str, str2) + bfd *abfd; + struct strtab *ss; + char *str; + CONST char *str2; +{ + /* should search first, but for now: */ + /* include the trailing NUL */ + int ln = strlen (str) + strlen (str2) + 1; + + /* should this be using obstacks? */ + if (ss->length) + ss->tab = realloc (ss->tab, ss->length + ln); + else + ss->tab = malloc (ln); + + BFD_ASSERT (ss->tab != 0); /* FIXME */ + strcpy (ss->tab + ss->length, str); + strcpy (ss->tab + ss->length + strlen (str), str2); + ss->nentries++; + ss->length += ln; + + return ss->length - ln; +} + +/* ELF .o/exec file reading */ + +/* Create a new bfd section from an ELF section header. */ + +static boolean +bfd_section_from_shdr (abfd, shindex) + bfd *abfd; + unsigned int shindex; +{ + Elf_Internal_Shdr *hdr = elf_elfsections (abfd)[shindex]; + Elf_Internal_Ehdr *ehdr = elf_elfheader (abfd); + char *name; + + name = elf_string_from_elf_strtab (abfd, hdr->sh_name); + + switch (hdr->sh_type) + { + case SHT_NULL: + /* Inactive section. Throw it away. */ + return true; + + case SHT_PROGBITS: /* Normal section with contents. */ + case SHT_DYNAMIC: /* Dynamic linking information. */ + case SHT_NOBITS: /* .bss section. */ + case SHT_HASH: /* .hash section. */ + return _bfd_elf_make_section_from_shdr (abfd, hdr, name); + + case SHT_SYMTAB: /* A symbol table */ + if (elf_onesymtab (abfd) == shindex) + return true; + + BFD_ASSERT (hdr->sh_entsize == sizeof (Elf_External_Sym)); + BFD_ASSERT (elf_onesymtab (abfd) == 0); + elf_onesymtab (abfd) = shindex; + elf_tdata (abfd)->symtab_hdr = *hdr; + elf_elfsections (abfd)[shindex] = &elf_tdata (abfd)->symtab_hdr; + abfd->flags |= HAS_SYMS; + return true; + + case SHT_DYNSYM: /* A dynamic symbol table */ + if (elf_dynsymtab (abfd) == shindex) + return true; + + BFD_ASSERT (hdr->sh_entsize == sizeof (Elf_External_Sym)); + BFD_ASSERT (elf_dynsymtab (abfd) == 0); + elf_dynsymtab (abfd) = shindex; + elf_tdata (abfd)->dynsymtab_hdr = *hdr; + elf_elfsections (abfd)[shindex] = &elf_tdata (abfd)->dynsymtab_hdr; + abfd->flags |= HAS_SYMS; + + /* Besides being a symbol table, we also treat this as a regular + section, so that objcopy can handle it. */ + return _bfd_elf_make_section_from_shdr (abfd, hdr, name); + + case SHT_STRTAB: /* A string table */ + if (hdr->rawdata != NULL) + return true; + if (ehdr->e_shstrndx == shindex) + { + elf_tdata (abfd)->shstrtab_hdr = *hdr; + elf_elfsections (abfd)[shindex] = &elf_tdata (abfd)->shstrtab_hdr; + hdr->rawdata = (PTR) & elf_tdata (abfd)->shstrtab_hdr; + return true; + } + { + unsigned int i; + + for (i = 1; i < ehdr->e_shnum; i++) + { + Elf_Internal_Shdr *hdr2 = elf_elfsections (abfd)[i]; + if (hdr2->sh_link == shindex) + { + if (! bfd_section_from_shdr (abfd, i)) + return false; + if (elf_onesymtab (abfd) == i) + { + elf_tdata (abfd)->strtab_hdr = *hdr; + elf_elfsections (abfd)[shindex] = + &elf_tdata (abfd)->strtab_hdr; + return true; + } + if (elf_dynsymtab (abfd) == i) + { + elf_tdata (abfd)->dynstrtab_hdr = *hdr; + elf_elfsections (abfd)[shindex] = + &elf_tdata (abfd)->dynstrtab_hdr; + /* We also treat this as a regular section, so + that objcopy can handle it. */ + break; + } +#if 0 /* Not handling other string tables specially right now. */ + hdr2 = elf_elfsections (abfd)[i]; /* in case it moved */ + /* We have a strtab for some random other section. */ + newsect = (asection *) hdr2->rawdata; + if (!newsect) + break; + hdr->rawdata = (PTR) newsect; + hdr2 = &elf_section_data (newsect)->str_hdr; + *hdr2 = *hdr; + elf_elfsections (abfd)[shindex] = hdr2; +#endif + } + } + } + + return _bfd_elf_make_section_from_shdr (abfd, hdr, name); + + case SHT_REL: + case SHT_RELA: + /* *These* do a lot of work -- but build no sections! */ + { + asection *target_sect; + Elf_Internal_Shdr *hdr2; + int use_rela_p = get_elf_backend_data (abfd)->use_rela_p; + + /* Get the symbol table. */ + if (! bfd_section_from_shdr (abfd, hdr->sh_link)) + return false; + + /* If this reloc section does not use the main symbol table we + don't treat it as a reloc section. BFD can't adequately + represent such a section, so at least for now, we don't + try. We just present it as a normal section. */ + if (hdr->sh_link != elf_onesymtab (abfd)) + return _bfd_elf_make_section_from_shdr (abfd, hdr, name); + + /* Don't allow REL relocations on a machine that uses RELA and + vice versa. */ + /* @@ Actually, the generic ABI does suggest that both might be + used in one file. But the four ABI Processor Supplements I + have access to right now all specify that only one is used on + each of those architectures. It's conceivable that, e.g., a + bunch of absolute 32-bit relocs might be more compact in REL + form even on a RELA machine... */ + BFD_ASSERT (use_rela_p + ? (hdr->sh_type == SHT_RELA + && hdr->sh_entsize == sizeof (Elf_External_Rela)) + : (hdr->sh_type == SHT_REL + && hdr->sh_entsize == sizeof (Elf_External_Rel))); + + if (! bfd_section_from_shdr (abfd, hdr->sh_info)) + return false; + target_sect = section_from_elf_index (abfd, hdr->sh_info); + if (target_sect == NULL + || elf_section_data (target_sect) == NULL) + return false; + + hdr2 = &elf_section_data (target_sect)->rel_hdr; + *hdr2 = *hdr; + elf_elfsections (abfd)[shindex] = hdr2; + target_sect->reloc_count = hdr->sh_size / hdr->sh_entsize; + target_sect->flags |= SEC_RELOC; + target_sect->relocation = NULL; + target_sect->rel_filepos = hdr->sh_offset; + abfd->flags |= HAS_RELOC; + return true; + } + break; + + case SHT_NOTE: +#if 0 + fprintf (stderr, "Note Sections not yet supported.\n"); + BFD_FAIL (); +#endif + break; + + case SHT_SHLIB: +#if 0 + fprintf (stderr, "SHLIB Sections not supported (and non conforming.)\n"); +#endif + return true; + + default: + /* Check for any processor-specific section types. */ + { + struct elf_backend_data *bed = get_elf_backend_data (abfd); + + if (bed->elf_backend_section_from_shdr) + (*bed->elf_backend_section_from_shdr) (abfd, hdr, name); + } + break; + } + + return true; +} + +boolean +elf_new_section_hook (abfd, sec) + bfd *abfd + ; + asection *sec; +{ + struct bfd_elf_section_data *sdata; + + sdata = (struct bfd_elf_section_data *) bfd_alloc (abfd, sizeof (*sdata)); + if (!sdata) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + sec->used_by_bfd = (PTR) sdata; + memset (sdata, 0, sizeof (*sdata)); + return true; +} + +/* Create a new bfd section from an ELF program header. + + Since program segments have no names, we generate a synthetic name + of the form segment<NUM>, where NUM is generally the index in the + program header table. For segments that are split (see below) we + generate the names segment<NUM>a and segment<NUM>b. + + Note that some program segments may have a file size that is different than + (less than) the memory size. All this means is that at execution the + system must allocate the amount of memory specified by the memory size, + but only initialize it with the first "file size" bytes read from the + file. This would occur for example, with program segments consisting + of combined data+bss. + + To handle the above situation, this routine generates TWO bfd sections + for the single program segment. The first has the length specified by + the file size of the segment, and the second has the length specified + by the difference between the two sizes. In effect, the segment is split + into it's initialized and uninitialized parts. + + */ + +static boolean +bfd_section_from_phdr (abfd, hdr, index) + bfd *abfd; + Elf_Internal_Phdr *hdr; + int index; +{ + asection *newsect; + char *name; + char namebuf[64]; + int split; + + split = ((hdr->p_memsz > 0) && + (hdr->p_filesz > 0) && + (hdr->p_memsz > hdr->p_filesz)); + sprintf (namebuf, split ? "segment%da" : "segment%d", index); + name = bfd_alloc (abfd, strlen (namebuf) + 1); + if (!name) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + strcpy (name, namebuf); + newsect = bfd_make_section (abfd, name); + if (newsect == NULL) + return false; + newsect->vma = hdr->p_vaddr; + newsect->_raw_size = hdr->p_filesz; + newsect->filepos = hdr->p_offset; + newsect->flags |= SEC_HAS_CONTENTS; + if (hdr->p_type == PT_LOAD) + { + newsect->flags |= SEC_ALLOC; + newsect->flags |= SEC_LOAD; + if (hdr->p_flags & PF_X) + { + /* FIXME: all we known is that it has execute PERMISSION, + may be data. */ + newsect->flags |= SEC_CODE; + } + } + if (!(hdr->p_flags & PF_W)) + { + newsect->flags |= SEC_READONLY; + } + + if (split) + { + sprintf (namebuf, "segment%db", index); + name = bfd_alloc (abfd, strlen (namebuf) + 1); + if (!name) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + strcpy (name, namebuf); + newsect = bfd_make_section (abfd, name); + if (newsect == NULL) + return false; + newsect->vma = hdr->p_vaddr + hdr->p_filesz; + newsect->_raw_size = hdr->p_memsz - hdr->p_filesz; + if (hdr->p_type == PT_LOAD) + { + newsect->flags |= SEC_ALLOC; + if (hdr->p_flags & PF_X) + newsect->flags |= SEC_CODE; + } + if (!(hdr->p_flags & PF_W)) + newsect->flags |= SEC_READONLY; + } + + return true; +} + +/* Begin processing a given object. + + First we validate the file by reading in the ELF header and checking + the magic number. */ + +static INLINE boolean +elf_file_p (x_ehdrp) + Elf_External_Ehdr *x_ehdrp; +{ + return ((x_ehdrp->e_ident[EI_MAG0] == ELFMAG0) + && (x_ehdrp->e_ident[EI_MAG1] == ELFMAG1) + && (x_ehdrp->e_ident[EI_MAG2] == ELFMAG2) + && (x_ehdrp->e_ident[EI_MAG3] == ELFMAG3)); +} + +/* Check to see if the file associated with ABFD matches the target vector + that ABFD points to. + + Note that we may be called several times with the same ABFD, but different + target vectors, most of which will not match. We have to avoid leaving + any side effects in ABFD, or any data it points to (like tdata), if the + file does not match the target vector. */ + +const bfd_target * +elf_object_p (abfd) + bfd *abfd; +{ + Elf_External_Ehdr x_ehdr; /* Elf file header, external form */ + Elf_Internal_Ehdr *i_ehdrp; /* Elf file header, internal form */ + Elf_External_Shdr x_shdr; /* Section header table entry, external form */ + Elf_Internal_Shdr *i_shdrp = NULL; /* Section header table, internal form */ + unsigned int shindex; + char *shstrtab; /* Internal copy of section header stringtab */ + struct elf_backend_data *ebd; + struct elf_obj_tdata *preserved_tdata = elf_tdata (abfd); + struct elf_obj_tdata *new_tdata = NULL; + + /* Read in the ELF header in external format. */ + + if (bfd_read ((PTR) & x_ehdr, sizeof (x_ehdr), 1, abfd) != sizeof (x_ehdr)) + { + if (bfd_get_error () != bfd_error_system_call) + goto got_wrong_format_error; + else + goto got_no_match; + } + + /* Now check to see if we have a valid ELF file, and one that BFD can + make use of. The magic number must match, the address size ('class') + and byte-swapping must match our XVEC entry, and it must have a + section header table (FIXME: See comments re sections at top of this + file). */ + + if ((elf_file_p (&x_ehdr) == false) || + (x_ehdr.e_ident[EI_VERSION] != EV_CURRENT) || + (x_ehdr.e_ident[EI_CLASS] != ELFCLASS)) + goto got_wrong_format_error; + + /* Check that file's byte order matches xvec's */ + switch (x_ehdr.e_ident[EI_DATA]) + { + case ELFDATA2MSB: /* Big-endian */ + if (!abfd->xvec->header_byteorder_big_p) + goto got_wrong_format_error; + break; + case ELFDATA2LSB: /* Little-endian */ + if (abfd->xvec->header_byteorder_big_p) + goto got_wrong_format_error; + break; + case ELFDATANONE: /* No data encoding specified */ + default: /* Unknown data encoding specified */ + goto got_wrong_format_error; + } + + /* Allocate an instance of the elf_obj_tdata structure and hook it up to + the tdata pointer in the bfd. */ + + new_tdata = ((struct elf_obj_tdata *) + bfd_zalloc (abfd, sizeof (struct elf_obj_tdata))); + if (new_tdata == NULL) + goto got_no_memory_error; + elf_tdata (abfd) = new_tdata; + + /* Now that we know the byte order, swap in the rest of the header */ + i_ehdrp = elf_elfheader (abfd); + elf_swap_ehdr_in (abfd, &x_ehdr, i_ehdrp); +#if DEBUG & 1 + elf_debug_file (i_ehdrp); +#endif + + /* If there is no section header table, we're hosed. */ + if (i_ehdrp->e_shoff == 0) + goto got_wrong_format_error; + + /* As a simple sanity check, verify that the what BFD thinks is the + size of each section header table entry actually matches the size + recorded in the file. */ + if (i_ehdrp->e_shentsize != sizeof (x_shdr)) + goto got_wrong_format_error; + + ebd = get_elf_backend_data (abfd); + + /* Check that the ELF e_machine field matches what this particular + BFD format expects. */ + if (ebd->elf_machine_code != i_ehdrp->e_machine) + { + const bfd_target * const *target_ptr; + + if (ebd->elf_machine_code != EM_NONE) + goto got_wrong_format_error; + + /* This is the generic ELF target. Let it match any ELF target + for which we do not have a specific backend. */ + for (target_ptr = bfd_target_vector; *target_ptr != NULL; target_ptr++) + { + struct elf_backend_data *back; + + if ((*target_ptr)->flavour != bfd_target_elf_flavour) + continue; + back = (struct elf_backend_data *) (*target_ptr)->backend_data; + if (back->elf_machine_code == i_ehdrp->e_machine) + { + /* target_ptr is an ELF backend which matches this + object file, so reject the generic ELF target. */ + goto got_wrong_format_error; + } + } + } + + if (i_ehdrp->e_type == ET_EXEC) + abfd->flags |= EXEC_P; + else if (i_ehdrp->e_type == ET_DYN) + abfd->flags |= DYNAMIC; + + if (i_ehdrp->e_phnum > 0) + abfd->flags |= D_PAGED; + + if (! bfd_default_set_arch_mach (abfd, ebd->arch, 0)) + goto got_no_match; + + /* Remember the entry point specified in the ELF file header. */ + bfd_get_start_address (abfd) = i_ehdrp->e_entry; + + /* Allocate space for a copy of the section header table in + internal form, seek to the section header table in the file, + read it in, and convert it to internal form. */ + i_shdrp = ((Elf_Internal_Shdr *) + bfd_alloc (abfd, sizeof (*i_shdrp) * i_ehdrp->e_shnum)); + elf_elfsections (abfd) = ((Elf_Internal_Shdr **) + bfd_alloc (abfd, + sizeof (i_shdrp) * i_ehdrp->e_shnum)); + if (!i_shdrp || !elf_elfsections (abfd)) + goto got_no_memory_error; + if (bfd_seek (abfd, i_ehdrp->e_shoff, SEEK_SET) != 0) + goto got_no_match; + for (shindex = 0; shindex < i_ehdrp->e_shnum; shindex++) + { + if (bfd_read ((PTR) & x_shdr, sizeof x_shdr, 1, abfd) != sizeof (x_shdr)) + goto got_no_match; + elf_swap_shdr_in (abfd, &x_shdr, i_shdrp + shindex); + elf_elfsections (abfd)[shindex] = i_shdrp + shindex; + } + if (i_ehdrp->e_shstrndx) + { + if (! bfd_section_from_shdr (abfd, i_ehdrp->e_shstrndx)) + goto got_no_match; + } + + /* Read in the string table containing the names of the sections. We + will need the base pointer to this table later. */ + /* We read this inline now, so that we don't have to go through + bfd_section_from_shdr with it (since this particular strtab is + used to find all of the ELF section names.) */ + + shstrtab = elf_get_str_section (abfd, i_ehdrp->e_shstrndx); + if (!shstrtab) + goto got_no_match; + + /* Once all of the section headers have been read and converted, we + can start processing them. Note that the first section header is + a dummy placeholder entry, so we ignore it. */ + + for (shindex = 1; shindex < i_ehdrp->e_shnum; shindex++) + { + if (! bfd_section_from_shdr (abfd, shindex)) + goto got_no_match; + } + + /* Let the backend double check the format and override global + information. */ + if (ebd->elf_backend_object_p) + { + if ((*ebd->elf_backend_object_p) (abfd) == false) + goto got_wrong_format_error; + } + + return (abfd->xvec); + +got_wrong_format_error: + bfd_set_error (bfd_error_wrong_format); + goto got_no_match; +got_no_memory_error: + bfd_set_error (bfd_error_no_memory); + goto got_no_match; +got_no_match: + if (new_tdata != NULL + && new_tdata->elf_sect_ptr != NULL) + bfd_release (abfd, new_tdata->elf_sect_ptr); + if (i_shdrp != NULL) + bfd_release (abfd, i_shdrp); + if (new_tdata != NULL) + bfd_release (abfd, new_tdata); + elf_tdata (abfd) = preserved_tdata; + return (NULL); +} + + +/* ELF .o/exec file writing */ + +/* Takes a bfd and a symbol, returns a pointer to the elf specific area + of the symbol if there is one. */ +static INLINE elf_symbol_type * +elf_symbol_from (ignore_abfd, symbol) + bfd *ignore_abfd; + asymbol *symbol; +{ + if (symbol->the_bfd->xvec->flavour != bfd_target_elf_flavour) + return 0; + + if (symbol->the_bfd->tdata.elf_obj_data == (struct elf_obj_tdata *) NULL) + return 0; + + return (elf_symbol_type *) symbol; +} + +void +write_relocs (abfd, sec, xxx) + bfd *abfd; + asection *sec; + PTR xxx; +{ + Elf_Internal_Shdr *rela_hdr; + Elf_External_Rela *outbound_relocas; + Elf_External_Rel *outbound_relocs; + int idx; + int use_rela_p = get_elf_backend_data (abfd)->use_rela_p; + asymbol *last_sym = 0; + int last_sym_idx = 9999999; /* should always be written before use */ + + if ((sec->flags & SEC_RELOC) == 0) + return; + + /* The linker backend writes the relocs out itself, and sets the + reloc_count field to zero to inhibit writing them here. Also, + sometimes the SEC_RELOC flag gets set even when there aren't any + relocs. */ + if (sec->reloc_count == 0) + return; + + rela_hdr = &elf_section_data (sec)->rel_hdr; + + rela_hdr->sh_size = rela_hdr->sh_entsize * sec->reloc_count; + rela_hdr->contents = (void *) bfd_alloc (abfd, rela_hdr->sh_size); + if (!rela_hdr->contents) + { + bfd_set_error (bfd_error_no_memory); + abort (); /* FIXME */ + } + + /* orelocation has the data, reloc_count has the count... */ + if (use_rela_p) + { + outbound_relocas = (Elf_External_Rela *) rela_hdr->contents; + + for (idx = 0; idx < sec->reloc_count; idx++) + { + Elf_Internal_Rela dst_rela; + Elf_External_Rela *src_rela; + arelent *ptr; + asymbol *sym; + int n; + + ptr = sec->orelocation[idx]; + src_rela = outbound_relocas + idx; + if (!(abfd->flags & EXEC_P)) + dst_rela.r_offset = ptr->address - sec->vma; + else + dst_rela.r_offset = ptr->address; + + sym = *ptr->sym_ptr_ptr; + if (sym == last_sym) + n = last_sym_idx; + else + { + last_sym = sym; + last_sym_idx = n = elf_symbol_from_bfd_symbol (abfd, &sym); + } + dst_rela.r_info = ELF_R_INFO (n, ptr->howto->type); + + dst_rela.r_addend = ptr->addend; + elf_swap_reloca_out (abfd, &dst_rela, src_rela); + } + } + else + /* REL relocations */ + { + outbound_relocs = (Elf_External_Rel *) rela_hdr->contents; + + for (idx = 0; idx < sec->reloc_count; idx++) + { + Elf_Internal_Rel dst_rel; + Elf_External_Rel *src_rel; + arelent *ptr; + int n; + asymbol *sym; + + ptr = sec->orelocation[idx]; + sym = *ptr->sym_ptr_ptr; + src_rel = outbound_relocs + idx; + if (!(abfd->flags & EXEC_P)) + dst_rel.r_offset = ptr->address - sec->vma; + else + dst_rel.r_offset = ptr->address; + + if (sym == last_sym) + n = last_sym_idx; + else + { + last_sym = sym; + last_sym_idx = n = elf_symbol_from_bfd_symbol (abfd, &sym); + } + dst_rel.r_info = ELF_R_INFO (n, ptr->howto->type); + + elf_swap_reloc_out (abfd, &dst_rel, src_rel); + } + } +} + +/* Set up an ELF internal section header for a section. */ + +/*ARGSUSED*/ +static void +elf_fake_sections (abfd, asect, ignore) + bfd *abfd; + asection *asect; + PTR ignore; +{ + Elf_Internal_Shdr *this_hdr; + + this_hdr = &elf_section_data (asect)->this_hdr; + + this_hdr->sh_name = bfd_add_to_strtab (abfd, elf_shstrtab (abfd), + asect->name); + if (this_hdr->sh_name == (unsigned long) -1) + abort (); /* FIXME */ + + this_hdr->sh_flags = 0; + if ((asect->flags & SEC_ALLOC) != 0) + this_hdr->sh_addr = asect->vma; + else + this_hdr->sh_addr = 0; + this_hdr->sh_offset = 0; + this_hdr->sh_size = asect->_raw_size; + this_hdr->sh_link = 0; + this_hdr->sh_info = 0; + this_hdr->sh_addralign = 1 << asect->alignment_power; + this_hdr->sh_entsize = 0; + + this_hdr->rawdata = (PTR) asect; + this_hdr->contents = NULL; + this_hdr->size = 0; + + /* FIXME: This should not be based on section names. */ + if (strcmp (asect->name, ".dynstr") == 0) + this_hdr->sh_type = SHT_STRTAB; + else if (strcmp (asect->name, ".hash") == 0) + { + this_hdr->sh_type = SHT_HASH; + this_hdr->sh_entsize = ARCH_SIZE / 8; + } + else if (strcmp (asect->name, ".dynsym") == 0) + { + this_hdr->sh_type = SHT_DYNSYM; + this_hdr->sh_entsize = sizeof (Elf_External_Sym); + } + else if (strcmp (asect->name, ".dynamic") == 0) + { + this_hdr->sh_type = SHT_DYNAMIC; + this_hdr->sh_entsize = sizeof (Elf_External_Dyn); + } + else if (strncmp (asect->name, ".rel.", 5) == 0) + { + this_hdr->sh_type = SHT_REL; + this_hdr->sh_entsize = sizeof (Elf_External_Rel); + } + else if (strncmp (asect->name, ".rela.", 6) == 0) + { + this_hdr->sh_type = SHT_RELA; + this_hdr->sh_entsize = sizeof (Elf_External_Rela); + } + else if (strcmp (asect->name, ".note") == 0) + this_hdr->sh_type = SHT_NOTE; + else if (strncmp (asect->name, ".stab", 5) == 0 + && strcmp (asect->name + strlen (asect->name) - 3, "str") == 0) + this_hdr->sh_type = SHT_STRTAB; + else if ((asect->flags & SEC_ALLOC) != 0 + && (asect->flags & SEC_LOAD) != 0) + this_hdr->sh_type = SHT_PROGBITS; + else if ((asect->flags & SEC_ALLOC) != 0 + && ((asect->flags & SEC_LOAD) == 0)) + { + BFD_ASSERT (strcmp (asect->name, ".bss") == 0 + || strcmp (asect->name, ".sbss") == 0); + this_hdr->sh_type = SHT_NOBITS; + } + else + { + /* Who knows? */ + this_hdr->sh_type = SHT_PROGBITS; + } + + if ((asect->flags & SEC_ALLOC) != 0) + this_hdr->sh_flags |= SHF_ALLOC; + if ((asect->flags & SEC_READONLY) == 0) + this_hdr->sh_flags |= SHF_WRITE; + if ((asect->flags & SEC_CODE) != 0) + this_hdr->sh_flags |= SHF_EXECINSTR; + + /* Check for processor-specific section types. */ + { + struct elf_backend_data *bed = get_elf_backend_data (abfd); + + if (bed->elf_backend_fake_sections) + (*bed->elf_backend_fake_sections) (abfd, this_hdr, asect); + } + + /* If the section has relocs, set up a section header for the + SHT_REL[A] section. */ + if ((asect->flags & SEC_RELOC) != 0) + { + Elf_Internal_Shdr *rela_hdr; + int use_rela_p = get_elf_backend_data (abfd)->use_rela_p; + + rela_hdr = &elf_section_data (asect)->rel_hdr; + rela_hdr->sh_name = + bfd_add_2_to_strtab (abfd, elf_shstrtab (abfd), + use_rela_p ? ".rela" : ".rel", + asect->name); + rela_hdr->sh_type = use_rela_p ? SHT_RELA : SHT_REL; + rela_hdr->sh_entsize = (use_rela_p + ? sizeof (Elf_External_Rela) + : sizeof (Elf_External_Rel)); + rela_hdr->sh_addralign = FILE_ALIGN; + rela_hdr->sh_flags = 0; + rela_hdr->sh_addr = 0; + rela_hdr->sh_size = 0; + rela_hdr->sh_offset = 0; + rela_hdr->size = 0; + } +} + +/* Assign all ELF section numbers. The dummy first section is handled here + too. The link/info pointers for the standard section types are filled + in here too, while we're at it. */ + +static boolean +assign_section_numbers (abfd) + bfd *abfd; +{ + struct elf_obj_tdata *t = elf_tdata (abfd); + asection *sec; + unsigned int section_number; + Elf_Internal_Shdr **i_shdrp; + + section_number = 1; + + for (sec = abfd->sections; sec; sec = sec->next) + { + struct bfd_elf_section_data *d = elf_section_data (sec); + + d->this_idx = section_number++; + if ((sec->flags & SEC_RELOC) == 0) + d->rel_idx = 0; + else + d->rel_idx = section_number++; + } + + t->shstrtab_section = section_number++; + elf_elfheader (abfd)->e_shstrndx = t->shstrtab_section; + t->shstrtab_hdr.sh_size = elf_shstrtab (abfd)->length; + t->shstrtab_hdr.contents = (PTR) elf_shstrtab (abfd)->tab; + + if (abfd->symcount > 0) + { + t->symtab_section = section_number++; + t->strtab_section = section_number++; + } + + elf_elfheader (abfd)->e_shnum = section_number; + + /* Set up the list of section header pointers, in agreement with the + indices. */ + i_shdrp = ((Elf_Internal_Shdr **) + bfd_alloc (abfd, section_number * sizeof (Elf_Internal_Shdr *))); + if (i_shdrp == NULL) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + + i_shdrp[0] = ((Elf_Internal_Shdr *) + bfd_alloc (abfd, sizeof (Elf_Internal_Shdr))); + if (i_shdrp[0] == NULL) + { + bfd_release (abfd, i_shdrp); + bfd_set_error (bfd_error_no_memory); + return false; + } + memset (i_shdrp[0], 0, sizeof (Elf_Internal_Shdr)); + + elf_elfsections (abfd) = i_shdrp; + + i_shdrp[t->shstrtab_section] = &t->shstrtab_hdr; + if (abfd->symcount > 0) + { + i_shdrp[t->symtab_section] = &t->symtab_hdr; + i_shdrp[t->strtab_section] = &t->strtab_hdr; + t->symtab_hdr.sh_link = t->strtab_section; + } + for (sec = abfd->sections; sec; sec = sec->next) + { + struct bfd_elf_section_data *d = elf_section_data (sec); + asection *s; + const char *name; + + i_shdrp[d->this_idx] = &d->this_hdr; + if (d->rel_idx != 0) + i_shdrp[d->rel_idx] = &d->rel_hdr; + + /* Fill in the sh_link and sh_info fields while we're at it. */ + + /* sh_link of a reloc section is the section index of the symbol + table. sh_info is the section index of the section to which + the relocation entries apply. */ + if (d->rel_idx != 0) + { + d->rel_hdr.sh_link = t->symtab_section; + d->rel_hdr.sh_info = d->this_idx; + } + + switch (d->this_hdr.sh_type) + { + case SHT_REL: + case SHT_RELA: + /* A reloc section which we are treating as a normal BFD + section. sh_link is the section index of the symbol + table. sh_info is the section index of the section to + which the relocation entries apply. We assume that an + allocated reloc section uses the dynamic symbol table. + FIXME: How can we be sure? */ + s = bfd_get_section_by_name (abfd, ".dynsym"); + if (s != NULL) + d->this_hdr.sh_link = elf_section_data (s)->this_idx; + + /* We look up the section the relocs apply to by name. */ + name = sec->name; + if (d->this_hdr.sh_type == SHT_REL) + name += 4; + else + name += 5; + s = bfd_get_section_by_name (abfd, name); + if (s != NULL) + d->this_hdr.sh_info = elf_section_data (s)->this_idx; + break; + + case SHT_STRTAB: + /* We assume that a section named .stab*str is a stabs + string section. We look for a section with the same name + but without the trailing ``str'', and set its sh_link + field to point to this section. */ + if (strncmp (sec->name, ".stab", sizeof ".stab" - 1) == 0 + && strcmp (sec->name + strlen (sec->name) - 3, "str") == 0) + { + size_t len; + char *alc; + + len = strlen (sec->name); + alc = (char *) malloc (len - 2); + if (alc == NULL) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + strncpy (alc, sec->name, len - 3); + alc[len - 3] = '\0'; + s = bfd_get_section_by_name (abfd, alc); + free (alc); + if (s != NULL) + { + elf_section_data (s)->this_hdr.sh_link = d->this_idx; + + /* This is a .stab section. */ + elf_section_data (s)->this_hdr.sh_entsize = + 4 + 2 * (ARCH_SIZE / 8); + } + } + break; + + case SHT_DYNAMIC: + case SHT_DYNSYM: + /* sh_link is the section header index of the string table + used for the dynamic entries or symbol table. */ + s = bfd_get_section_by_name (abfd, ".dynstr"); + if (s != NULL) + d->this_hdr.sh_link = elf_section_data (s)->this_idx; + break; + + case SHT_HASH: + /* sh_link is the section header index of the symbol table + this hash table is for. */ + s = bfd_get_section_by_name (abfd, ".dynsym"); + if (s != NULL) + d->this_hdr.sh_link = elf_section_data (s)->this_idx; + break; + } + } + + return true; +} + +/* Map symbol from it's internal number to the external number, moving + all local symbols to be at the head of the list. */ + +static INLINE int +sym_is_global (abfd, sym) + bfd *abfd; + asymbol *sym; +{ + /* If the backend has a special mapping, use it. */ + if (get_elf_backend_data (abfd)->elf_backend_sym_is_global) + return ((*get_elf_backend_data (abfd)->elf_backend_sym_is_global) + (abfd, sym)); + + if (sym->flags & (BSF_GLOBAL | BSF_WEAK)) + { + if (sym->flags & BSF_LOCAL) + abort (); + return 1; + } + if (sym->section == 0) + { + /* Is this valid? */ + abort (); + + return 1; + } + if (bfd_is_und_section (sym->section)) + return 1; + if (bfd_is_com_section (sym->section)) + return 1; + if (sym->flags & (BSF_LOCAL | BSF_SECTION_SYM | BSF_FILE)) + return 0; + return 0; +} + +static boolean +elf_map_symbols (abfd) + bfd *abfd; +{ + int symcount = bfd_get_symcount (abfd); + asymbol **syms = bfd_get_outsymbols (abfd); + asymbol **sect_syms; + int num_locals = 0; + int num_globals = 0; + int num_locals2 = 0; + int num_globals2 = 0; + int max_index = 0; + int num_sections = 0; + Elf_Sym_Extra *sym_extra; + int idx; + asection *asect; + +#ifdef DEBUG + fprintf (stderr, "elf_map_symbols\n"); + fflush (stderr); +#endif + + /* Add local symbols for each section for which there are relocs. + FIXME: How can we tell which sections have relocs at this point? + Will reloc_count always be accurate? Actually, I think most ELF + targets create section symbols for all sections anyhow. */ + for (asect = abfd->sections; asect; asect = asect->next) + { + if (max_index < asect->index) + max_index = asect->index; + } + + max_index++; + elf_num_section_syms (abfd) = max_index; + sect_syms = (asymbol **) bfd_zalloc (abfd, max_index * sizeof (asymbol *)); + elf_section_syms (abfd) = sect_syms; + + if (sect_syms == 0) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + + for (asect = abfd->sections; asect; asect = asect->next) + { + asymbol *sym = bfd_make_empty_symbol (abfd); + if (!sym) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + sym->the_bfd = abfd; + sym->name = asect->name; + sym->value = asect->vma; + sym->flags = BSF_SECTION_SYM; + sym->section = asect; + sect_syms[asect->index] = sym; + num_sections++; +#ifdef DEBUG + fprintf (stderr, + "creating section symbol, name = %s, value = 0x%.8lx, index = %d, section = 0x%.8lx\n", + asect->name, (long) asect->vma, asect->index, (long) asect); +#endif + } + + if (num_sections) + { + if (syms) + syms = (asymbol **) bfd_realloc (abfd, syms, + ((symcount + num_sections + 1) + * sizeof (asymbol *))); + else + syms = (asymbol **) bfd_alloc (abfd, + (num_sections + 1) * sizeof (asymbol *)); + if (!syms) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + + for (asect = abfd->sections; asect; asect = asect->next) + { + if (sect_syms[asect->index]) + syms[symcount++] = sect_syms[asect->index]; + } + + syms[symcount] = (asymbol *) 0; + bfd_set_symtab (abfd, syms, symcount); + } + + elf_sym_extra (abfd) = sym_extra + = (Elf_Sym_Extra *) bfd_alloc (abfd, symcount * sizeof (Elf_Sym_Extra)); + if (!sym_extra) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + + /* Identify and classify all of the symbols. */ + for (idx = 0; idx < symcount; idx++) + { + if (!sym_is_global (abfd, syms[idx])) + num_locals++; + else + num_globals++; + } + + /* Now provide mapping information. Add +1 for skipping over the + dummy symbol. */ + for (idx = 0; idx < symcount; idx++) + { + syms[idx]->udata = (PTR) & sym_extra[idx]; + if (!sym_is_global (abfd, syms[idx])) + sym_extra[idx].elf_sym_num = 1 + num_locals2++; + else + sym_extra[idx].elf_sym_num = 1 + num_locals + num_globals2++; + } + + elf_num_locals (abfd) = num_locals; + elf_num_globals (abfd) = num_globals; + return true; +} + +/* Compute the file positions we are going to put the sections at, and + otherwise prepare to begin writing out the ELF file. If LINK_INFO + is not NULL, this is being called by the ELF backend linker. */ + +static boolean +elf_compute_section_file_positions (abfd, link_info) + bfd *abfd; + struct bfd_link_info *link_info; +{ + struct elf_backend_data *bed = get_elf_backend_data (abfd); + Elf_Internal_Shdr *shstrtab_hdr; + + if (abfd->output_has_begun) + return true; + + /* Do any elf backend specific processing first. */ + if (bed->elf_backend_begin_write_processing) + (*bed->elf_backend_begin_write_processing) (abfd, link_info); + + if (! prep_headers (abfd)) + return false; + + bfd_map_over_sections (abfd, elf_fake_sections, 0); + + if (!assign_section_numbers (abfd)) + return false; + + /* The backend linker builds symbol table information itself. */ + if (link_info == NULL) + { + if (! swap_out_syms (abfd)) + return false; + } + + shstrtab_hdr = &elf_tdata (abfd)->shstrtab_hdr; + /* sh_name was set in prep_headers. */ + shstrtab_hdr->sh_type = SHT_STRTAB; + shstrtab_hdr->sh_flags = 0; + shstrtab_hdr->sh_addr = 0; + shstrtab_hdr->sh_size = elf_shstrtab (abfd)->length; + shstrtab_hdr->sh_entsize = 0; + shstrtab_hdr->sh_link = 0; + shstrtab_hdr->sh_info = 0; + /* sh_offset is set in assign_file_positions_for_symtabs_and_strtabs. */ + shstrtab_hdr->sh_addralign = 1; + shstrtab_hdr->contents = (PTR) elf_shstrtab (abfd)->tab; + + if (!assign_file_positions_except_relocs (abfd, + link_info == NULL ? true : false)) + return false; + + abfd->output_has_begun = true; + + return true; +} + + +/* Align to the maximum file alignment that could be required for any + ELF data structure. */ + +static INLINE file_ptr +align_file_position (off) + file_ptr off; +{ + return (off + FILE_ALIGN - 1) & ~(FILE_ALIGN - 1); +} + +/* Assign a file position to a section, optionally aligning to the + required section alignment. */ + +static INLINE file_ptr +assign_file_position_for_section (i_shdrp, offset, align) + Elf_Internal_Shdr *i_shdrp; + file_ptr offset; + boolean align; +{ + if (align) + { + unsigned int al; + + al = i_shdrp->sh_addralign; + if (al > 1) + offset = BFD_ALIGN (offset, al); + } + i_shdrp->sh_offset = offset; + if (i_shdrp->rawdata != NULL) + ((asection *) i_shdrp->rawdata)->filepos = offset; + if (i_shdrp->sh_type != SHT_NOBITS) + offset += i_shdrp->sh_size; + return offset; +} + +/* Get the size of the program header. This is called by the linker + before any of the section VMA's are set, so it can't calculate the + correct value for a strange memory layout. */ + +static bfd_size_type +get_program_header_size (abfd) + bfd *abfd; +{ + size_t segs; + asection *s; + + /* Assume we will need exactly two PT_LOAD segments: one for text + and one for data. */ + segs = 2; + + s = bfd_get_section_by_name (abfd, ".interp"); + if (s != NULL && (s->flags & SEC_LOAD) != 0) + { + /* If we have a loadable interpreter section, we need a + PT_INTERP segment. In this case, assume we also need a + PT_PHDR segment, although that may not be true for all + targets. */ + segs += 2; + } + + if (bfd_get_section_by_name (abfd, ".dynamic") != NULL) + { + /* We need a PT_DYNAMIC segment. */ + ++segs; + } + + return segs * sizeof (Elf_External_Phdr); +} + +/* Create the program header. OFF is the file offset where the + program header should be written. FIRST is the first loadable ELF + section. PHDR_SIZE is the size of the program header as returned + by get_program_header_size. */ + +static file_ptr +map_program_segments (abfd, off, first, phdr_size) + bfd *abfd; + file_ptr off; + Elf_Internal_Shdr *first; + bfd_size_type phdr_size; +{ + Elf_Internal_Phdr phdrs[10]; + unsigned int phdr_count; + Elf_Internal_Phdr *phdr; + int phdr_size_adjust; + unsigned int i; + Elf_Internal_Shdr **hdrpp; + asection *sinterp, *sdyn; + unsigned int last_type; + Elf_Internal_Ehdr *i_ehdrp; + + BFD_ASSERT ((abfd->flags & EXEC_P) != 0); + BFD_ASSERT (phdr_size / sizeof (Elf_Internal_Phdr) + <= sizeof phdrs / sizeof (phdrs[0])); + + phdr_count = 0; + phdr = phdrs; + + phdr_size_adjust = 0; + + /* If we have a loadable .interp section, we must create a PT_INTERP + segment which must precede all PT_LOAD segments. We assume that + we must also create a PT_PHDR segment, although that may not be + true for all targets. */ + sinterp = bfd_get_section_by_name (abfd, ".interp"); + if (sinterp != NULL && (sinterp->flags & SEC_LOAD) != 0) + { + BFD_ASSERT (first != NULL); + + phdr->p_type = PT_PHDR; + + phdr->p_offset = off; + + /* Account for any adjustment made because of the alignment of + the first loadable section. */ + phdr_size_adjust = (first->sh_offset - phdr_size) - off; + BFD_ASSERT (phdr_size_adjust >= 0 && phdr_size_adjust < 128); + + /* The program header precedes all loadable sections. This lets + us compute its loadable address. This depends on the linker + script. */ + phdr->p_vaddr = first->sh_addr - (phdr_size + phdr_size_adjust); + + phdr->p_paddr = 0; + phdr->p_filesz = phdr_size; + phdr->p_memsz = phdr_size; + + /* FIXME: UnixWare and Solaris set PF_X, Irix 5 does not. */ + phdr->p_flags = PF_R | PF_X; + + phdr->p_align = FILE_ALIGN; + BFD_ASSERT ((phdr->p_vaddr - phdr->p_offset) % FILE_ALIGN == 0); + + /* Include the ELF header in the first loadable segment. */ + phdr_size_adjust += off; + + ++phdr_count; + ++phdr; + + phdr->p_type = PT_INTERP; + phdr->p_offset = sinterp->filepos; + phdr->p_vaddr = sinterp->vma; + phdr->p_paddr = 0; + phdr->p_filesz = sinterp->_raw_size; + phdr->p_memsz = sinterp->_raw_size; + phdr->p_flags = PF_R; + phdr->p_align = 1 << bfd_get_section_alignment (abfd, sinterp); + + ++phdr_count; + ++phdr; + } + + /* Look through the sections to see how they will be divided into + program segments. The sections must be arranged in order by + sh_addr for this to work correctly. */ + phdr->p_type = PT_NULL; + last_type = SHT_PROGBITS; + for (i = 1, hdrpp = elf_elfsections (abfd) + 1; + i < elf_elfheader (abfd)->e_shnum; + i++, hdrpp++) + { + Elf_Internal_Shdr *hdr; + + hdr = *hdrpp; + + /* Ignore any section which will not be part of the process + image. */ + if ((hdr->sh_flags & SHF_ALLOC) == 0) + continue; + + /* If this section fits in the segment we are constructing, add + it in. */ + if (phdr->p_type != PT_NULL + && (hdr->sh_offset - (phdr->p_offset + phdr->p_memsz) + == hdr->sh_addr - (phdr->p_vaddr + phdr->p_memsz)) + && (last_type != SHT_NOBITS || hdr->sh_type == SHT_NOBITS)) + { + bfd_size_type adjust; + + adjust = hdr->sh_addr - (phdr->p_vaddr + phdr->p_memsz); + phdr->p_memsz += hdr->sh_size + adjust; + if (hdr->sh_type != SHT_NOBITS) + phdr->p_filesz += hdr->sh_size + adjust; + if ((hdr->sh_flags & SHF_WRITE) != 0) + phdr->p_flags |= PF_W; + if ((hdr->sh_flags & SHF_EXECINSTR) != 0) + phdr->p_flags |= PF_X; + last_type = hdr->sh_type; + continue; + } + + /* If we have a segment, move to the next one. */ + if (phdr->p_type != PT_NULL) + { + ++phdr; + ++phdr_count; + } + + /* Start a new segment. */ + phdr->p_type = PT_LOAD; + phdr->p_offset = hdr->sh_offset; + phdr->p_vaddr = hdr->sh_addr; + phdr->p_paddr = 0; + if (hdr->sh_type == SHT_NOBITS) + phdr->p_filesz = 0; + else + phdr->p_filesz = hdr->sh_size; + phdr->p_memsz = hdr->sh_size; + phdr->p_flags = PF_R; + if ((hdr->sh_flags & SHF_WRITE) != 0) + phdr->p_flags |= PF_W; + if ((hdr->sh_flags & SHF_EXECINSTR) != 0) + phdr->p_flags |= PF_X; + phdr->p_align = get_elf_backend_data (abfd)->maxpagesize; + + if (hdr == first + && sinterp != NULL + && (sinterp->flags & SEC_LOAD) != 0) + { + phdr->p_offset -= phdr_size + phdr_size_adjust; + phdr->p_vaddr -= phdr_size + phdr_size_adjust; + phdr->p_filesz += phdr_size + phdr_size_adjust; + phdr->p_memsz += phdr_size + phdr_size_adjust; + } + + last_type = hdr->sh_type; + } + + if (phdr->p_type != PT_NULL) + { + ++phdr; + ++phdr_count; + } + + /* If we have a .dynamic section, create a PT_DYNAMIC segment. */ + sdyn = bfd_get_section_by_name (abfd, ".dynamic"); + if (sdyn != NULL && (sdyn->flags & SEC_LOAD) != 0) + { + phdr->p_type = PT_DYNAMIC; + phdr->p_offset = sdyn->filepos; + phdr->p_vaddr = sdyn->vma; + phdr->p_paddr = 0; + phdr->p_filesz = sdyn->_raw_size; + phdr->p_memsz = sdyn->_raw_size; + phdr->p_flags = PF_R; + if ((sdyn->flags & SEC_READONLY) == 0) + phdr->p_flags |= PF_W; + if ((sdyn->flags & SEC_CODE) != 0) + phdr->p_flags |= PF_X; + phdr->p_align = 1 << bfd_get_section_alignment (abfd, sdyn); + + ++phdr; + ++phdr_count; + } + + /* Make sure the return value from get_program_header_size matches + what we computed here. */ + if (phdr_count != phdr_size / sizeof (Elf_External_Phdr)) + abort (); + + /* Set up program header information. */ + i_ehdrp = elf_elfheader (abfd); + i_ehdrp->e_phentsize = sizeof (Elf_External_Phdr); + i_ehdrp->e_phoff = off; + i_ehdrp->e_phnum = phdr_count; + + /* Save the program headers away. I don't think anybody uses this + information right now. */ + elf_tdata (abfd)->phdr = ((Elf_Internal_Phdr *) + bfd_alloc (abfd, + (phdr_count + * sizeof (Elf_Internal_Phdr)))); + if (elf_tdata (abfd)->phdr == NULL && phdr_count != 0) + { + bfd_set_error (bfd_error_no_memory); + return (file_ptr) -1; + } + memcpy (elf_tdata (abfd)->phdr, phdrs, + phdr_count * sizeof (Elf_Internal_Phdr)); + + /* Write out the program headers. */ + if (bfd_seek (abfd, off, SEEK_SET) != 0) + return (file_ptr) -1; + + for (i = 0, phdr = phdrs; i < phdr_count; i++, phdr++) + { + Elf_External_Phdr extphdr; + + elf_swap_phdr_out (abfd, phdr, &extphdr); + if (bfd_write (&extphdr, sizeof (Elf_External_Phdr), 1, abfd) + != sizeof (Elf_External_Phdr)) + return (file_ptr) -1; + } + + return off + phdr_count * sizeof (Elf_External_Phdr); +} + +/* Work out the file positions of all the sections. This is called by + elf_compute_section_file_positions. All the section sizes and VMAs + must be known before this is called. + + We do not consider reloc sections at this point, unless they form + part of the loadable image. Reloc sections are assigned file + positions in assign_file_positions_for_relocs, which is called by + write_object_contents and final_link. + + If DOSYMS is false, we do not assign file positions for the symbol + table or the string table. */ + +static boolean +assign_file_positions_except_relocs (abfd, dosyms) + bfd *abfd; + boolean dosyms; +{ + struct elf_obj_tdata * const tdata = elf_tdata (abfd); + Elf_Internal_Ehdr * const i_ehdrp = elf_elfheader (abfd); + Elf_Internal_Shdr ** const i_shdrpp = elf_elfsections (abfd); + file_ptr off; + + /* Start after the ELF header. */ + off = i_ehdrp->e_ehsize; + + if ((abfd->flags & EXEC_P) == 0) + { + Elf_Internal_Shdr **hdrpp; + unsigned int i; + + /* We are not creating an executable, which means that we are + not creating a program header, and that the actual order of + the sections in the file is unimportant. */ + for (i = 1, hdrpp = i_shdrpp + 1; i < i_ehdrp->e_shnum; i++, hdrpp++) + { + Elf_Internal_Shdr *hdr; + + hdr = *hdrpp; + if (hdr->sh_type == SHT_REL || hdr->sh_type == SHT_RELA) + { + hdr->sh_offset = -1; + continue; + } + if (! dosyms + && (i == tdata->symtab_section + || i == tdata->strtab_section)) + { + hdr->sh_offset = -1; + continue; + } + + off = assign_file_position_for_section (hdr, off, true); + } + } + else + { + file_ptr phdr_off; + bfd_size_type phdr_size; + bfd_vma maxpagesize; + Elf_Internal_Shdr **hdrpp; + unsigned int i; + Elf_Internal_Shdr *first; + file_ptr phdr_map; + + /* We are creating an executable. We must create a program + header. We can't actually create the program header until we + have set the file positions for the sections, but we can + figure out how big it is going to be. */ + off = align_file_position (off); + phdr_size = get_program_header_size (abfd); + if (phdr_size == (file_ptr) -1) + return false; + phdr_off = off; + off += phdr_size; + + maxpagesize = get_elf_backend_data (abfd)->maxpagesize; + if (maxpagesize == 0) + maxpagesize = 1; + + /* FIXME: We might want to sort the sections on the sh_addr + field here. For now, we just assume that the linker will + create the sections in an appropriate order. */ + + /* Assign file positions in two passes. In the first pass, we + assign a file position to every section which forms part of + the executable image. */ + first = NULL; + for (i = 1, hdrpp = i_shdrpp + 1; i < i_ehdrp->e_shnum; i++, hdrpp++) + { + Elf_Internal_Shdr *hdr; + + hdr = *hdrpp; + if ((hdr->sh_flags & SHF_ALLOC) == 0) + continue; + + if (first == NULL) + first = hdr; + + if ((abfd->flags & D_PAGED) != 0) + { + /* The section VMA must equal the file position modulo + the page size. This is required by the program + header. */ + off += (hdr->sh_addr - off) % maxpagesize; + } + + off = assign_file_position_for_section (hdr, off, false); + } + + /* Assign file positions to all the sections which do not form + part of the loadable image, except for the relocs. */ + for (i = 1, hdrpp = i_shdrpp + 1; i < i_ehdrp->e_shnum; i++, hdrpp++) + { + Elf_Internal_Shdr *hdr; + + hdr = *hdrpp; + if ((hdr->sh_flags & SHF_ALLOC) != 0) + continue; + if (hdr->sh_type == SHT_REL || hdr->sh_type == SHT_RELA) + { + hdr->sh_offset = -1; + continue; + } + if (! dosyms + && (i == tdata->symtab_section + || i == tdata->strtab_section)) + { + hdr->sh_offset = -1; + continue; + } + + off = assign_file_position_for_section (hdr, off, true); + } + + phdr_map = map_program_segments (abfd, phdr_off, first, phdr_size); + if (phdr_map == (file_ptr) -1) + return false; + BFD_ASSERT (phdr_map == phdr_off + phdr_size); + } + + /* Place the section headers. */ + off = align_file_position (off); + i_ehdrp->e_shoff = off; + off += i_ehdrp->e_shnum * i_ehdrp->e_shentsize; + + elf_tdata (abfd)->next_file_pos = off; + + return true; +} + +static boolean +prep_headers (abfd) + bfd *abfd; +{ + Elf_Internal_Ehdr *i_ehdrp; /* Elf file header, internal form */ + Elf_Internal_Phdr *i_phdrp = 0; /* Program header table, internal form */ + Elf_Internal_Shdr **i_shdrp; /* Section header table, internal form */ + int count; + struct strtab *shstrtab; + + i_ehdrp = elf_elfheader (abfd); + i_shdrp = elf_elfsections (abfd); + + shstrtab = bfd_new_strtab (abfd); + if (!shstrtab) + return false; + + elf_shstrtab (abfd) = shstrtab; + + i_ehdrp->e_ident[EI_MAG0] = ELFMAG0; + i_ehdrp->e_ident[EI_MAG1] = ELFMAG1; + i_ehdrp->e_ident[EI_MAG2] = ELFMAG2; + i_ehdrp->e_ident[EI_MAG3] = ELFMAG3; + + i_ehdrp->e_ident[EI_CLASS] = ELFCLASS; + i_ehdrp->e_ident[EI_DATA] = + abfd->xvec->byteorder_big_p ? ELFDATA2MSB : ELFDATA2LSB; + i_ehdrp->e_ident[EI_VERSION] = EV_CURRENT; + + for (count = EI_PAD; count < EI_NIDENT; count++) + i_ehdrp->e_ident[count] = 0; + + if ((abfd->flags & DYNAMIC) != 0) + i_ehdrp->e_type = ET_DYN; + else if ((abfd->flags & EXEC_P) != 0) + i_ehdrp->e_type = ET_EXEC; + else + i_ehdrp->e_type = ET_REL; + + switch (bfd_get_arch (abfd)) + { + case bfd_arch_unknown: + i_ehdrp->e_machine = EM_NONE; + break; + case bfd_arch_sparc: +#if ARCH_SIZE == 64 + i_ehdrp->e_machine = EM_SPARC64; +#else + i_ehdrp->e_machine = EM_SPARC; +#endif + break; + case bfd_arch_i386: + i_ehdrp->e_machine = EM_386; + break; + case bfd_arch_m68k: + i_ehdrp->e_machine = EM_68K; + break; + case bfd_arch_m88k: + i_ehdrp->e_machine = EM_88K; + break; + case bfd_arch_i860: + i_ehdrp->e_machine = EM_860; + break; + case bfd_arch_mips: /* MIPS Rxxxx */ + i_ehdrp->e_machine = EM_MIPS; /* only MIPS R3000 */ + break; + case bfd_arch_hppa: + i_ehdrp->e_machine = EM_PARISC; + break; + case bfd_arch_powerpc: + i_ehdrp->e_machine = EM_CYGNUS_POWERPC; + break; + /* also note that EM_M32, AT&T WE32100 is unknown to bfd */ + default: + i_ehdrp->e_machine = EM_NONE; + } + i_ehdrp->e_version = EV_CURRENT; + i_ehdrp->e_ehsize = sizeof (Elf_External_Ehdr); + + /* no program header, for now. */ + i_ehdrp->e_phoff = 0; + i_ehdrp->e_phentsize = 0; + i_ehdrp->e_phnum = 0; + + /* each bfd section is section header entry */ + i_ehdrp->e_entry = bfd_get_start_address (abfd); + i_ehdrp->e_shentsize = sizeof (Elf_External_Shdr); + + /* if we're building an executable, we'll need a program header table */ + if (abfd->flags & EXEC_P) + { + /* it all happens later */ +#if 0 + i_ehdrp->e_phentsize = sizeof (Elf_External_Phdr); + + /* elf_build_phdrs() returns a (NULL-terminated) array of + Elf_Internal_Phdrs */ + i_phdrp = elf_build_phdrs (abfd, i_ehdrp, i_shdrp, &i_ehdrp->e_phnum); + i_ehdrp->e_phoff = outbase; + outbase += i_ehdrp->e_phentsize * i_ehdrp->e_phnum; +#endif + } + else + { + i_ehdrp->e_phentsize = 0; + i_phdrp = 0; + i_ehdrp->e_phoff = 0; + } + + elf_tdata (abfd)->symtab_hdr.sh_name = bfd_add_to_strtab (abfd, shstrtab, + ".symtab"); + elf_tdata (abfd)->strtab_hdr.sh_name = bfd_add_to_strtab (abfd, shstrtab, + ".strtab"); + elf_tdata (abfd)->shstrtab_hdr.sh_name = bfd_add_to_strtab (abfd, shstrtab, + ".shstrtab"); + if (elf_tdata (abfd)->symtab_hdr.sh_name == (unsigned int) -1 + || elf_tdata (abfd)->symtab_hdr.sh_name == (unsigned int) -1 + || elf_tdata (abfd)->shstrtab_hdr.sh_name == (unsigned int) -1) + return false; + + return true; +} + +static boolean +swap_out_syms (abfd) + bfd *abfd; +{ + if (!elf_map_symbols (abfd)) + return false; + + /* Dump out the symtabs. */ + { + int symcount = bfd_get_symcount (abfd); + asymbol **syms = bfd_get_outsymbols (abfd); + struct strtab *stt = bfd_new_strtab (abfd); + Elf_Internal_Shdr *symtab_hdr; + Elf_Internal_Shdr *symstrtab_hdr; + Elf_External_Sym *outbound_syms; + int idx; + + if (!stt) + return false; + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + symtab_hdr->sh_type = SHT_SYMTAB; + symtab_hdr->sh_entsize = sizeof (Elf_External_Sym); + symtab_hdr->sh_size = symtab_hdr->sh_entsize * (symcount + 1); + symtab_hdr->sh_info = elf_num_locals (abfd) + 1; + symtab_hdr->sh_addralign = FILE_ALIGN; + + symstrtab_hdr = &elf_tdata (abfd)->strtab_hdr; + symstrtab_hdr->sh_type = SHT_STRTAB; + + outbound_syms = (Elf_External_Sym *) + bfd_alloc (abfd, (1 + symcount) * sizeof (Elf_External_Sym)); + if (!outbound_syms) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + /* now generate the data (for "contents") */ + { + /* Fill in zeroth symbol and swap it out. */ + Elf_Internal_Sym sym; + sym.st_name = 0; + sym.st_value = 0; + sym.st_size = 0; + sym.st_info = 0; + sym.st_other = 0; + sym.st_shndx = SHN_UNDEF; + elf_swap_symbol_out (abfd, &sym, outbound_syms); + } + for (idx = 0; idx < symcount; idx++) + { + Elf_Internal_Sym sym; + bfd_vma value = syms[idx]->value; + elf_symbol_type *type_ptr; + + if (syms[idx]->flags & BSF_SECTION_SYM) + /* Section symbols have no names. */ + sym.st_name = 0; + else + { + sym.st_name = bfd_add_to_strtab (abfd, stt, syms[idx]->name); + if (sym.st_name == (unsigned long) -1) + return false; + } + + type_ptr = elf_symbol_from (abfd, syms[idx]); + + if (bfd_is_com_section (syms[idx]->section)) + { + /* ELF common symbols put the alignment into the `value' field, + and the size into the `size' field. This is backwards from + how BFD handles it, so reverse it here. */ + sym.st_size = value; + sym.st_value = type_ptr ? type_ptr->internal_elf_sym.st_value : 16; + sym.st_shndx = elf_section_from_bfd_section (abfd, + syms[idx]->section); + } + else + { + asection *sec = syms[idx]->section; + int shndx; + + if (sec->output_section) + { + value += sec->output_offset; + sec = sec->output_section; + } + value += sec->vma; + sym.st_value = value; + sym.st_size = type_ptr ? type_ptr->internal_elf_sym.st_size : 0; + sym.st_shndx = shndx = elf_section_from_bfd_section (abfd, sec); + if (shndx == -1) + { + asection *sec2; + /* Writing this would be a hell of a lot easier if we had + some decent documentation on bfd, and knew what to expect + of the library, and what to demand of applications. For + example, it appears that `objcopy' might not set the + section of a symbol to be a section that is actually in + the output file. */ + sec2 = bfd_get_section_by_name (abfd, sec->name); + BFD_ASSERT (sec2 != 0); + sym.st_shndx = shndx = elf_section_from_bfd_section (abfd, sec2); + BFD_ASSERT (shndx != -1); + } + } + + if (bfd_is_com_section (syms[idx]->section)) + sym.st_info = ELF_ST_INFO (STB_GLOBAL, STT_OBJECT); + else if (bfd_is_und_section (syms[idx]->section)) + sym.st_info = ELF_ST_INFO (STB_GLOBAL, STT_NOTYPE); + else if (syms[idx]->flags & BSF_SECTION_SYM) + sym.st_info = ELF_ST_INFO (STB_LOCAL, STT_SECTION); + else if (syms[idx]->flags & BSF_FILE) + sym.st_info = ELF_ST_INFO (STB_LOCAL, STT_FILE); + else + { + int bind = STB_LOCAL; + int type = STT_OBJECT; + unsigned int flags = syms[idx]->flags; + + if (flags & BSF_LOCAL) + bind = STB_LOCAL; + else if (flags & BSF_WEAK) + bind = STB_WEAK; + else if (flags & BSF_GLOBAL) + bind = STB_GLOBAL; + + if (flags & BSF_FUNCTION) + type = STT_FUNC; + + sym.st_info = ELF_ST_INFO (bind, type); + } + + sym.st_other = 0; + elf_swap_symbol_out (abfd, &sym, + (outbound_syms + + elf_sym_extra (abfd)[idx].elf_sym_num)); + } + + symtab_hdr->contents = (PTR) outbound_syms; + symstrtab_hdr->contents = (PTR) stt->tab; + symstrtab_hdr->sh_size = stt->length; + symstrtab_hdr->sh_type = SHT_STRTAB; + + symstrtab_hdr->sh_flags = 0; + symstrtab_hdr->sh_addr = 0; + symstrtab_hdr->sh_entsize = 0; + symstrtab_hdr->sh_link = 0; + symstrtab_hdr->sh_info = 0; + symstrtab_hdr->sh_addralign = 1; + symstrtab_hdr->size = 0; + } + + return true; +} + +static boolean +write_shdrs_and_ehdr (abfd) + bfd *abfd; +{ + Elf_External_Ehdr x_ehdr; /* Elf file header, external form */ + Elf_Internal_Ehdr *i_ehdrp; /* Elf file header, internal form */ + Elf_External_Shdr *x_shdrp; /* Section header table, external form */ + Elf_Internal_Shdr **i_shdrp; /* Section header table, internal form */ + unsigned int count; + struct strtab *shstrtab; + + i_ehdrp = elf_elfheader (abfd); + i_shdrp = elf_elfsections (abfd); + shstrtab = elf_shstrtab (abfd); + + /* swap the header before spitting it out... */ + +#if DEBUG & 1 + elf_debug_file (i_ehdrp); +#endif + elf_swap_ehdr_out (abfd, i_ehdrp, &x_ehdr); + if (bfd_seek (abfd, (file_ptr) 0, SEEK_SET) != 0 + || (bfd_write ((PTR) & x_ehdr, sizeof (x_ehdr), 1, abfd) + != sizeof (x_ehdr))) + return false; + + /* at this point we've concocted all the ELF sections... */ + x_shdrp = (Elf_External_Shdr *) + bfd_alloc (abfd, sizeof (*x_shdrp) * (i_ehdrp->e_shnum)); + if (!x_shdrp) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + + for (count = 0; count < i_ehdrp->e_shnum; count++) + { +#if DEBUG & 2 + elf_debug_section (shstrtab->tab + i_shdrp[count]->sh_name, count, + i_shdrp[count]); +#endif + elf_swap_shdr_out (abfd, i_shdrp[count], x_shdrp + count); + } + if (bfd_seek (abfd, (file_ptr) i_ehdrp->e_shoff, SEEK_SET) != 0 + || (bfd_write ((PTR) x_shdrp, sizeof (*x_shdrp), i_ehdrp->e_shnum, abfd) + != sizeof (*x_shdrp) * i_ehdrp->e_shnum)) + return false; + + /* need to dump the string table too... */ + + return true; +} + +/* Assign file positions for all the reloc sections which are not part + of the loadable file image. */ + +static void +assign_file_positions_for_relocs (abfd) + bfd *abfd; +{ + file_ptr off; + unsigned int i; + Elf_Internal_Shdr **shdrpp; + + off = elf_tdata (abfd)->next_file_pos; + + for (i = 1, shdrpp = elf_elfsections (abfd) + 1; + i < elf_elfheader (abfd)->e_shnum; + i++, shdrpp++) + { + Elf_Internal_Shdr *shdrp; + + shdrp = *shdrpp; + if ((shdrp->sh_type == SHT_REL || shdrp->sh_type == SHT_RELA) + && shdrp->sh_offset == -1) + off = assign_file_position_for_section (shdrp, off, true); + } + + elf_tdata (abfd)->next_file_pos = off; +} + +boolean +NAME(bfd_elf,write_object_contents) (abfd) + bfd *abfd; +{ + struct elf_backend_data *bed = get_elf_backend_data (abfd); + Elf_Internal_Ehdr *i_ehdrp; + Elf_Internal_Shdr **i_shdrp; + unsigned int count; + + if (! abfd->output_has_begun + && ! elf_compute_section_file_positions (abfd, + (struct bfd_link_info *) NULL)) + return false; + + i_shdrp = elf_elfsections (abfd); + i_ehdrp = elf_elfheader (abfd); + + bfd_map_over_sections (abfd, write_relocs, (PTR) 0); + assign_file_positions_for_relocs (abfd); + + /* After writing the headers, we need to write the sections too... */ + for (count = 1; count < i_ehdrp->e_shnum; count++) + { + if (bed->elf_backend_section_processing) + (*bed->elf_backend_section_processing) (abfd, i_shdrp[count]); + if (i_shdrp[count]->contents) + { + if (bfd_seek (abfd, i_shdrp[count]->sh_offset, SEEK_SET) != 0 + || (bfd_write (i_shdrp[count]->contents, i_shdrp[count]->sh_size, + 1, abfd) + != i_shdrp[count]->sh_size)) + return false; + } + } + + if (bed->elf_backend_final_write_processing) + (*bed->elf_backend_final_write_processing) (abfd, NULL); + + return write_shdrs_and_ehdr (abfd); +} + +/* Given an index of a section, retrieve a pointer to it. Note + that for our purposes, sections are indexed by {1, 2, ...} with + 0 being an illegal index. */ + +/* In the original, each ELF section went into exactly one BFD + section. This doesn't really make sense, so we need a real mapping. + The mapping has to hide in the Elf_Internal_Shdr since asection + doesn't have anything like a tdata field... */ + +static asection * +section_from_elf_index (abfd, index) + bfd *abfd; + unsigned int index; +{ + /* @@ Is bfd_com_section_ptr really correct in all the places it could + be returned from this routine? */ + + if (index == SHN_ABS) + return bfd_com_section_ptr; /* not abs? */ + if (index == SHN_COMMON) + return bfd_com_section_ptr; + + if (index >= elf_elfheader (abfd)->e_shnum) + return NULL; + + { + Elf_Internal_Shdr *hdr = elf_elfsections (abfd)[index]; + + switch (hdr->sh_type) + { + /* ELF sections that map to BFD sections */ + case SHT_PROGBITS: + case SHT_NOBITS: + case SHT_HASH: + case SHT_DYNAMIC: + if (hdr->rawdata == NULL) + { + if (! bfd_section_from_shdr (abfd, index)) + return NULL; + } + return (struct sec *) hdr->rawdata; + + default: + return bfd_abs_section_ptr; + } + } +} + +/* given a section, search the header to find them... */ +static int +elf_section_from_bfd_section (abfd, asect) + bfd *abfd; + struct sec *asect; +{ + Elf_Internal_Shdr **i_shdrp = elf_elfsections (abfd); + int index; + Elf_Internal_Shdr *hdr; + int maxindex = elf_elfheader (abfd)->e_shnum; + + if (asect->owner == NULL) + { + if (bfd_is_abs_section (asect)) + return SHN_ABS; + if (bfd_is_com_section (asect)) + return SHN_COMMON; + if (bfd_is_und_section (asect)) + return SHN_UNDEF; + return -1; + } + + BFD_ASSERT (asect->owner == abfd); + + for (index = 0; index < maxindex; index++) + { + hdr = i_shdrp[index]; + switch (hdr->sh_type) + { + /* ELF sections that map to BFD sections */ + case SHT_PROGBITS: + case SHT_NOBITS: + case SHT_NOTE: + case SHT_HASH: + case SHT_DYNAMIC: + case SHT_DYNSYM: + if (hdr->rawdata) + { + if (((struct sec *) (hdr->rawdata)) == asect) + return index; + } + break; + + case SHT_REL: + case SHT_RELA: + /* We sometimes map a reloc section to a BFD section. */ + if (hdr->sh_link != elf_onesymtab (abfd) + && (asection *) hdr->rawdata == asect) + return index; + break; + + case SHT_STRTAB: + /* We map most string tables to BFD sections. */ + if (index != elf_elfheader (abfd)->e_shstrndx + && index != elf_onesymtab (abfd) + && (asection *) hdr->rawdata == asect) + return index; + + /* FALL THROUGH */ + default: + { + struct elf_backend_data *bed = get_elf_backend_data (abfd); + + if (bed->elf_backend_section_from_bfd_section) + { + int retval; + + retval = index; + if ((*bed->elf_backend_section_from_bfd_section) + (abfd, hdr, asect, &retval)) + return retval; + } + } + break; + } + } + return -1; +} + +/* given a symbol, return the bfd index for that symbol. */ +static int +elf_symbol_from_bfd_symbol (abfd, asym_ptr_ptr) + bfd *abfd; + struct symbol_cache_entry **asym_ptr_ptr; +{ + struct symbol_cache_entry *asym_ptr = *asym_ptr_ptr; + int idx; + flagword flags = asym_ptr->flags; + + /* When gas creates relocations against local labels, it creates its + own symbol for the section, but does put the symbol into the + symbol chain, so udata is 0. When the linker is generating + relocatable output, this section symbol may be for one of the + input sections rather than the output section. */ + if (asym_ptr->udata == (PTR) 0 + && (flags & BSF_SECTION_SYM) + && asym_ptr->section) + { + int indx; + + if (asym_ptr->section->output_section != NULL) + indx = asym_ptr->section->output_section->index; + else + indx = asym_ptr->section->index; + if (elf_section_syms (abfd)[indx]) + asym_ptr->udata = elf_section_syms (abfd)[indx]->udata; + } + + if (asym_ptr->udata) + idx = ((Elf_Sym_Extra *) asym_ptr->udata)->elf_sym_num; + else + { + abort (); + } + +#if DEBUG & 4 + { + + fprintf (stderr, + "elf_symbol_from_bfd_symbol 0x%.8lx, name = %s, sym num = %d, flags = 0x%.8lx %s\n", + (long) asym_ptr, asym_ptr->name, idx, flags, elf_symbol_flags (flags)); + fflush (stderr); + } +#endif + + return idx; +} + +static long +elf_slurp_symbol_table (abfd, symptrs, dynamic) + bfd *abfd; + asymbol **symptrs; /* Buffer for generated bfd symbols */ + boolean dynamic; +{ + Elf_Internal_Shdr *hdr; + long symcount; /* Number of external ELF symbols */ + elf_symbol_type *sym; /* Pointer to current bfd symbol */ + elf_symbol_type *symbase; /* Buffer for generated bfd symbols */ + Elf_Internal_Sym i_sym; + Elf_External_Sym *x_symp = NULL; + + /* Read each raw ELF symbol, converting from external ELF form to + internal ELF form, and then using the information to create a + canonical bfd symbol table entry. + + Note that we allocate the initial bfd canonical symbol buffer + based on a one-to-one mapping of the ELF symbols to canonical + symbols. We actually use all the ELF symbols, so there will be no + space left over at the end. When we have all the symbols, we + build the caller's pointer vector. */ + + if (dynamic) + hdr = &elf_tdata (abfd)->dynsymtab_hdr; + else + hdr = &elf_tdata (abfd)->symtab_hdr; + if (bfd_seek (abfd, hdr->sh_offset, SEEK_SET) == -1) + return -1; + + symcount = hdr->sh_size / sizeof (Elf_External_Sym); + + if (symcount == 0) + sym = symbase = NULL; + else + { + long i; + + if (bfd_seek (abfd, hdr->sh_offset, SEEK_SET) == -1) + return -1; + + symbase = ((elf_symbol_type *) + bfd_zalloc (abfd, symcount * sizeof (elf_symbol_type))); + if (symbase == (elf_symbol_type *) NULL) + { + bfd_set_error (bfd_error_no_memory); + return -1; + } + sym = symbase; + + /* Temporarily allocate room for the raw ELF symbols. */ + x_symp = ((Elf_External_Sym *) + malloc (symcount * sizeof (Elf_External_Sym))); + if (x_symp == NULL && symcount != 0) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } + + if (bfd_read ((PTR) x_symp, sizeof (Elf_External_Sym), symcount, abfd) + != symcount * sizeof (Elf_External_Sym)) + goto error_return; + /* Skip first symbol, which is a null dummy. */ + for (i = 1; i < symcount; i++) + { + elf_swap_symbol_in (abfd, x_symp + i, &i_sym); + memcpy (&sym->internal_elf_sym, &i_sym, sizeof (Elf_Internal_Sym)); +#ifdef ELF_KEEP_EXTSYM + memcpy (&sym->native_elf_sym, x_symp + i, sizeof (Elf_External_Sym)); +#endif + sym->symbol.the_bfd = abfd; + + sym->symbol.name = elf_string_from_elf_section (abfd, hdr->sh_link, + i_sym.st_name); + + sym->symbol.value = i_sym.st_value; + + if (i_sym.st_shndx > 0 && i_sym.st_shndx < SHN_LORESERVE) + { + sym->symbol.section = section_from_elf_index (abfd, + i_sym.st_shndx); + if (sym->symbol.section == NULL) + { + /* This symbol is in a section for which we did not + create a BFD section. Just use bfd_abs_section, + although it is wrong. FIXME. */ + sym->symbol.section = bfd_abs_section_ptr; + } + } + else if (i_sym.st_shndx == SHN_ABS) + { + sym->symbol.section = bfd_abs_section_ptr; + } + else if (i_sym.st_shndx == SHN_COMMON) + { + sym->symbol.section = bfd_com_section_ptr; + /* Elf puts the alignment into the `value' field, and + the size into the `size' field. BFD wants to see the + size in the value field, and doesn't care (at the + moment) about the alignment. */ + sym->symbol.value = i_sym.st_size; + } + else if (i_sym.st_shndx == SHN_UNDEF) + { + sym->symbol.section = bfd_und_section_ptr; + } + else + sym->symbol.section = bfd_abs_section_ptr; + + sym->symbol.value -= sym->symbol.section->vma; + + switch (ELF_ST_BIND (i_sym.st_info)) + { + case STB_LOCAL: + sym->symbol.flags |= BSF_LOCAL; + break; + case STB_GLOBAL: + sym->symbol.flags |= BSF_GLOBAL; + break; + case STB_WEAK: + sym->symbol.flags |= BSF_WEAK; + break; + } + + switch (ELF_ST_TYPE (i_sym.st_info)) + { + case STT_SECTION: + sym->symbol.flags |= BSF_SECTION_SYM | BSF_DEBUGGING; + break; + case STT_FILE: + sym->symbol.flags |= BSF_FILE | BSF_DEBUGGING; + break; + case STT_FUNC: + sym->symbol.flags |= BSF_FUNCTION; + break; + } + + if (dynamic) + sym->symbol.flags |= BSF_DYNAMIC; + + /* Do some backend-specific processing on this symbol. */ + { + struct elf_backend_data *ebd = get_elf_backend_data (abfd); + if (ebd->elf_backend_symbol_processing) + (*ebd->elf_backend_symbol_processing) (abfd, &sym->symbol); + } + + sym++; + } + } + + /* Do some backend-specific processing on this symbol table. */ + { + struct elf_backend_data *ebd = get_elf_backend_data (abfd); + if (ebd->elf_backend_symbol_table_processing) + (*ebd->elf_backend_symbol_table_processing) (abfd, symbase, symcount); + } + + /* We rely on the zalloc to clear out the final symbol entry. */ + + symcount = sym - symbase; + + /* Fill in the user's symbol pointer vector if needed. */ + if (symptrs) + { + long l = symcount; + + sym = symbase; + while (l-- > 0) + { + *symptrs++ = &sym->symbol; + sym++; + } + *symptrs = 0; /* Final null pointer */ + } + + if (x_symp != NULL) + free (x_symp); + return symcount; +error_return: + if (x_symp != NULL) + free (x_symp); + return -1; +} + +/* Return the number of bytes required to hold the symtab vector. + + Note that we base it on the count plus 1, since we will null terminate + the vector allocated based on this size. However, the ELF symbol table + always has a dummy entry as symbol #0, so it ends up even. */ + +long +elf_get_symtab_upper_bound (abfd) + bfd *abfd; +{ + long symcount; + long symtab_size; + Elf_Internal_Shdr *hdr = &elf_tdata (abfd)->symtab_hdr; + + symcount = hdr->sh_size / sizeof (Elf_External_Sym); + symtab_size = (symcount - 1 + 1) * (sizeof (asymbol *)); + + return symtab_size; +} + +long +elf_get_dynamic_symtab_upper_bound (abfd) + bfd *abfd; +{ + long symcount; + long symtab_size; + Elf_Internal_Shdr *hdr = &elf_tdata (abfd)->dynsymtab_hdr; + + if (elf_dynsymtab (abfd) == 0) + { + bfd_set_error (bfd_error_invalid_operation); + return -1; + } + + symcount = hdr->sh_size / sizeof (Elf_External_Sym); + symtab_size = (symcount - 1 + 1) * (sizeof (asymbol *)); + + return symtab_size; +} + +long +elf_get_reloc_upper_bound (abfd, asect) + bfd *abfd; + sec_ptr asect; +{ + return (asect->reloc_count + 1) * sizeof (arelent *); +} + +/* Read in and swap the external relocs. */ + +static boolean +elf_slurp_reloc_table (abfd, asect, symbols) + bfd *abfd; + asection *asect; + asymbol **symbols; +{ + struct elf_backend_data * const ebd = get_elf_backend_data (abfd); + struct bfd_elf_section_data * const d = elf_section_data (asect); + PTR allocated = NULL; + bfd_byte *native_relocs; + arelent *relents; + arelent *relent; + unsigned int i; + int entsize; + + if (asect->relocation != NULL) + return true; + + BFD_ASSERT (asect->rel_filepos == d->rel_hdr.sh_offset + && (asect->reloc_count + == d->rel_hdr.sh_size / d->rel_hdr.sh_entsize)); + + native_relocs = (bfd_byte *) elf_section_data (asect)->relocs; + if (native_relocs == NULL) + { + allocated = (PTR) malloc (d->rel_hdr.sh_size); + if (allocated == NULL) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } + + if (bfd_seek (abfd, asect->rel_filepos, SEEK_SET) != 0 + || (bfd_read (allocated, 1, d->rel_hdr.sh_size, abfd) + != d->rel_hdr.sh_size)) + goto error_return; + + native_relocs = (bfd_byte *) allocated; + } + + relents = ((arelent *) + bfd_alloc (abfd, asect->reloc_count * sizeof (arelent))); + if (relents == NULL) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } + + entsize = d->rel_hdr.sh_entsize; + BFD_ASSERT (entsize == sizeof (Elf_External_Rel) + || entsize == sizeof (Elf_External_Rela)); + + for (i = 0, relent = relents; + i < asect->reloc_count; + i++, relent++, native_relocs += entsize) + { + Elf_Internal_Rela rela; + Elf_Internal_Rel rel; + + if (entsize == sizeof (Elf_External_Rela)) + elf_swap_reloca_in (abfd, (Elf_External_Rela *) native_relocs, &rela); + else + { + elf_swap_reloc_in (abfd, (Elf_External_Rel *) native_relocs, &rel); + rela.r_offset = rel.r_offset; + rela.r_info = rel.r_info; + rela.r_addend = 0; + } + + /* The address of an ELF reloc is section relative for an object + file, and absolute for an executable file or shared library. + The address of a BFD reloc is always section relative. */ + if ((abfd->flags & (EXEC_P | DYNAMIC)) == 0) + relent->address = rela.r_offset; + else + relent->address = rela.r_offset - asect->vma; + + if (ELF_R_SYM (rela.r_info) == 0) + relent->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr; + else + { + asymbol **ps, *s; + + ps = symbols + ELF_R_SYM (rela.r_info) - 1; + s = *ps; + + /* Canonicalize ELF section symbols. FIXME: Why? */ + if ((s->flags & BSF_SECTION_SYM) == 0) + relent->sym_ptr_ptr = ps; + else + relent->sym_ptr_ptr = s->section->symbol_ptr_ptr; + } + + relent->addend = rela.r_addend; + + if (entsize == sizeof (Elf_External_Rela)) + (*ebd->elf_info_to_howto) (abfd, relent, &rela); + else + (*ebd->elf_info_to_howto_rel) (abfd, relent, &rel); + } + + asect->relocation = relents; + + if (allocated != NULL) + free (allocated); + + return true; + + error_return: + if (allocated != NULL) + free (allocated); + return false; +} + +#ifdef DEBUG +static void +elf_debug_section (str, num, hdr) + char *str; + int num; + Elf_Internal_Shdr *hdr; +{ + fprintf (stderr, "\nSection#%d '%s' 0x%.8lx\n", num, str, (long) hdr); + fprintf (stderr, + "sh_name = %ld\tsh_type = %ld\tsh_flags = %ld\n", + (long) hdr->sh_name, + (long) hdr->sh_type, + (long) hdr->sh_flags); + fprintf (stderr, + "sh_addr = %ld\tsh_offset = %ld\tsh_size = %ld\n", + (long) hdr->sh_addr, + (long) hdr->sh_offset, + (long) hdr->sh_size); + fprintf (stderr, + "sh_link = %ld\tsh_info = %ld\tsh_addralign = %ld\n", + (long) hdr->sh_link, + (long) hdr->sh_info, + (long) hdr->sh_addralign); + fprintf (stderr, "sh_entsize = %ld\n", + (long) hdr->sh_entsize); + fprintf (stderr, "rawdata = 0x%.8lx\n", (long) hdr->rawdata); + fprintf (stderr, "contents = 0x%.8lx\n", (long) hdr->contents); + fprintf (stderr, "size = %ld\n", (long) hdr->size); + fflush (stderr); +} + +static void +elf_debug_file (ehdrp) + Elf_Internal_Ehdr *ehdrp; +{ + fprintf (stderr, "e_entry = 0x%.8lx\n", (long) ehdrp->e_entry); + fprintf (stderr, "e_phoff = %ld\n", (long) ehdrp->e_phoff); + fprintf (stderr, "e_phnum = %ld\n", (long) ehdrp->e_phnum); + fprintf (stderr, "e_phentsize = %ld\n", (long) ehdrp->e_phentsize); + fprintf (stderr, "e_shoff = %ld\n", (long) ehdrp->e_shoff); + fprintf (stderr, "e_shnum = %ld\n", (long) ehdrp->e_shnum); + fprintf (stderr, "e_shentsize = %ld\n", (long) ehdrp->e_shentsize); +} +#endif + +/* Canonicalize the relocs. */ + +long +elf_canonicalize_reloc (abfd, section, relptr, symbols) + bfd *abfd; + sec_ptr section; + arelent **relptr; + asymbol **symbols; +{ + arelent *tblptr; + unsigned int i; + + if (! elf_slurp_reloc_table (abfd, section, symbols)) + return -1; + + tblptr = section->relocation; + for (i = 0; i < section->reloc_count; i++) + *relptr++ = tblptr++; + + *relptr = NULL; + + return section->reloc_count; +} + +long +elf_get_symtab (abfd, alocation) + bfd *abfd; + asymbol **alocation; +{ + long symcount = elf_slurp_symbol_table (abfd, alocation, false); + + if (symcount >= 0) + bfd_get_symcount (abfd) = symcount; + return symcount; +} + +long +elf_canonicalize_dynamic_symtab (abfd, alocation) + bfd *abfd; + asymbol **alocation; +{ + return elf_slurp_symbol_table (abfd, alocation, true); +} + +asymbol * +elf_make_empty_symbol (abfd) + bfd *abfd; +{ + elf_symbol_type *newsym; + + newsym = (elf_symbol_type *) bfd_zalloc (abfd, sizeof (elf_symbol_type)); + if (!newsym) + { + bfd_set_error (bfd_error_no_memory); + return NULL; + } + else + { + newsym->symbol.the_bfd = abfd; + return &newsym->symbol; + } +} + +void +elf_get_symbol_info (ignore_abfd, symbol, ret) + bfd *ignore_abfd; + asymbol *symbol; + symbol_info *ret; +{ + bfd_symbol_info (symbol, ret); +} + +void +elf_print_symbol (ignore_abfd, filep, symbol, how) + bfd *ignore_abfd; + PTR filep; + asymbol *symbol; + bfd_print_symbol_type how; +{ + FILE *file = (FILE *) filep; + switch (how) + { + case bfd_print_symbol_name: + fprintf (file, "%s", symbol->name); + break; + case bfd_print_symbol_more: + fprintf (file, "elf "); + fprintf_vma (file, symbol->value); + fprintf (file, " %lx", (long) symbol->flags); + break; + case bfd_print_symbol_all: + { + CONST char *section_name; + section_name = symbol->section ? symbol->section->name : "(*none*)"; + bfd_print_symbol_vandf ((PTR) file, symbol); + fprintf (file, " %s\t%s", + section_name, + symbol->name); + } + break; + } + +} + +alent * +elf_get_lineno (ignore_abfd, symbol) + bfd *ignore_abfd; + asymbol *symbol; +{ + fprintf (stderr, "elf_get_lineno unimplemented\n"); + fflush (stderr); + BFD_FAIL (); + return NULL; +} + +boolean +elf_set_arch_mach (abfd, arch, machine) + bfd *abfd; + enum bfd_architecture arch; + unsigned long machine; +{ + /* If this isn't the right architecture for this backend, and this + isn't the generic backend, fail. */ + if (arch != get_elf_backend_data (abfd)->arch + && arch != bfd_arch_unknown + && get_elf_backend_data (abfd)->arch != bfd_arch_unknown) + return false; + + return bfd_default_set_arch_mach (abfd, arch, machine); +} + +boolean +elf_find_nearest_line (abfd, + section, + symbols, + offset, + filename_ptr, + functionname_ptr, + line_ptr) + bfd *abfd; + asection *section; + asymbol **symbols; + bfd_vma offset; + CONST char **filename_ptr; + CONST char **functionname_ptr; + unsigned int *line_ptr; +{ + return false; +} + +int +elf_sizeof_headers (abfd, reloc) + bfd *abfd; + boolean reloc; +{ + int ret; + + ret = sizeof (Elf_External_Ehdr); + if (! reloc) + ret += get_program_header_size (abfd); + return ret; +} + +boolean +elf_set_section_contents (abfd, section, location, offset, count) + bfd *abfd; + sec_ptr section; + PTR location; + file_ptr offset; + bfd_size_type count; +{ + Elf_Internal_Shdr *hdr; + + if (! abfd->output_has_begun + && ! elf_compute_section_file_positions (abfd, + (struct bfd_link_info *) NULL)) + return false; + + hdr = &elf_section_data (section)->this_hdr; + + if (bfd_seek (abfd, hdr->sh_offset + offset, SEEK_SET) == -1) + return false; + if (bfd_write (location, 1, count, abfd) != count) + return false; + + return true; +} + +void +elf_no_info_to_howto (abfd, cache_ptr, dst) + bfd *abfd; + arelent *cache_ptr; + Elf_Internal_Rela *dst; +{ + fprintf (stderr, "elf RELA relocation support for target machine unimplemented\n"); + fflush (stderr); + BFD_FAIL (); +} + +void +elf_no_info_to_howto_rel (abfd, cache_ptr, dst) + bfd *abfd; + arelent *cache_ptr; + Elf_Internal_Rel *dst; +{ + fprintf (stderr, "elf REL relocation support for target machine unimplemented\n"); + fflush (stderr); + BFD_FAIL (); +} + + +/* Core file support */ + +#ifdef HAVE_PROCFS /* Some core file support requires host /proc files */ +#include <sys/procfs.h> +#else +#define bfd_prstatus(abfd, descdata, descsz, filepos) true +#define bfd_fpregset(abfd, descdata, descsz, filepos) true +#define bfd_prpsinfo(abfd, descdata, descsz, filepos) true +#endif + +#ifdef HAVE_PROCFS + +static boolean +bfd_prstatus (abfd, descdata, descsz, filepos) + bfd *abfd; + char *descdata; + int descsz; + long filepos; +{ + asection *newsect; + prstatus_t *status = (prstatus_t *) 0; + + if (descsz == sizeof (prstatus_t)) + { + newsect = bfd_make_section (abfd, ".reg"); + if (newsect == NULL) + return false; + newsect->_raw_size = sizeof (status->pr_reg); + newsect->filepos = filepos + (long) &status->pr_reg; + newsect->flags = SEC_HAS_CONTENTS; + newsect->alignment_power = 2; + if ((core_prstatus (abfd) = bfd_alloc (abfd, descsz)) != NULL) + { + memcpy (core_prstatus (abfd), descdata, descsz); + } + } + return true; +} + +/* Stash a copy of the prpsinfo structure away for future use. */ + +static boolean +bfd_prpsinfo (abfd, descdata, descsz, filepos) + bfd *abfd; + char *descdata; + int descsz; + long filepos; +{ + if (descsz == sizeof (prpsinfo_t)) + { + if ((core_prpsinfo (abfd) = bfd_alloc (abfd, descsz)) == NULL) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + memcpy (core_prpsinfo (abfd), descdata, descsz); + } + return true; +} + +static boolean +bfd_fpregset (abfd, descdata, descsz, filepos) + bfd *abfd; + char *descdata; + int descsz; + long filepos; +{ + asection *newsect; + + newsect = bfd_make_section (abfd, ".reg2"); + if (newsect == NULL) + return false; + newsect->_raw_size = descsz; + newsect->filepos = filepos; + newsect->flags = SEC_HAS_CONTENTS; + newsect->alignment_power = 2; + return true; +} + +#endif /* HAVE_PROCFS */ + +/* Return a pointer to the args (including the command name) that were + seen by the program that generated the core dump. Note that for + some reason, a spurious space is tacked onto the end of the args + in some (at least one anyway) implementations, so strip it off if + it exists. */ + +char * +elf_core_file_failing_command (abfd) + bfd *abfd; +{ +#ifdef HAVE_PROCFS + if (core_prpsinfo (abfd)) + { + prpsinfo_t *p = core_prpsinfo (abfd); + char *scan = p->pr_psargs; + while (*scan++) + {; + } + scan -= 2; + if ((scan > p->pr_psargs) && (*scan == ' ')) + { + *scan = '\000'; + } + return p->pr_psargs; + } +#endif + return NULL; +} + +/* Return the number of the signal that caused the core dump. Presumably, + since we have a core file, we got a signal of some kind, so don't bother + checking the other process status fields, just return the signal number. + */ + +int +elf_core_file_failing_signal (abfd) + bfd *abfd; +{ +#ifdef HAVE_PROCFS + if (core_prstatus (abfd)) + { + return ((prstatus_t *) (core_prstatus (abfd)))->pr_cursig; + } +#endif + return -1; +} + +/* Check to see if the core file could reasonably be expected to have + come for the current executable file. Note that by default we return + true unless we find something that indicates that there might be a + problem. + */ + +boolean +elf_core_file_matches_executable_p (core_bfd, exec_bfd) + bfd *core_bfd; + bfd *exec_bfd; +{ +#ifdef HAVE_PROCFS + char *corename; + char *execname; +#endif + + /* First, xvecs must match since both are ELF files for the same target. */ + + if (core_bfd->xvec != exec_bfd->xvec) + { + bfd_set_error (bfd_error_system_call); + return false; + } + +#ifdef HAVE_PROCFS + + /* If no prpsinfo, just return true. Otherwise, grab the last component + of the exec'd pathname from the prpsinfo. */ + + if (core_prpsinfo (core_bfd)) + { + corename = (((struct prpsinfo *) core_prpsinfo (core_bfd))->pr_fname); + } + else + { + return true; + } + + /* Find the last component of the executable pathname. */ + + if ((execname = strrchr (exec_bfd->filename, '/')) != NULL) + { + execname++; + } + else + { + execname = (char *) exec_bfd->filename; + } + + /* See if they match */ + + return strcmp (execname, corename) ? false : true; + +#else + + return true; + +#endif /* HAVE_PROCFS */ +} + +/* ELF core files contain a segment of type PT_NOTE, that holds much of + the information that would normally be available from the /proc interface + for the process, at the time the process dumped core. Currently this + includes copies of the prstatus, prpsinfo, and fpregset structures. + + Since these structures are potentially machine dependent in size and + ordering, bfd provides two levels of support for them. The first level, + available on all machines since it does not require that the host + have /proc support or the relevant include files, is to create a bfd + section for each of the prstatus, prpsinfo, and fpregset structures, + without any interpretation of their contents. With just this support, + the bfd client will have to interpret the structures itself. Even with + /proc support, it might want these full structures for it's own reasons. + + In the second level of support, where HAVE_PROCFS is defined, bfd will + pick apart the structures to gather some additional information that + clients may want, such as the general register set, the name of the + exec'ed file and its arguments, the signal (if any) that caused the + core dump, etc. + + */ + +static boolean +elf_corefile_note (abfd, hdr) + bfd *abfd; + Elf_Internal_Phdr *hdr; +{ + Elf_External_Note *x_note_p; /* Elf note, external form */ + Elf_Internal_Note i_note; /* Elf note, internal form */ + char *buf = NULL; /* Entire note segment contents */ + char *namedata; /* Name portion of the note */ + char *descdata; /* Descriptor portion of the note */ + char *sectname; /* Name to use for new section */ + long filepos; /* File offset to descriptor data */ + asection *newsect; + + if (hdr->p_filesz > 0 + && (buf = (char *) malloc (hdr->p_filesz)) != NULL + && bfd_seek (abfd, hdr->p_offset, SEEK_SET) != -1 + && bfd_read ((PTR) buf, hdr->p_filesz, 1, abfd) == hdr->p_filesz) + { + x_note_p = (Elf_External_Note *) buf; + while ((char *) x_note_p < (buf + hdr->p_filesz)) + { + i_note.namesz = bfd_h_get_32 (abfd, (bfd_byte *) x_note_p->namesz); + i_note.descsz = bfd_h_get_32 (abfd, (bfd_byte *) x_note_p->descsz); + i_note.type = bfd_h_get_32 (abfd, (bfd_byte *) x_note_p->type); + namedata = x_note_p->name; + descdata = namedata + BFD_ALIGN (i_note.namesz, 4); + filepos = hdr->p_offset + (descdata - buf); + switch (i_note.type) + { + case NT_PRSTATUS: + /* process descdata as prstatus info */ + if (! bfd_prstatus (abfd, descdata, i_note.descsz, filepos)) + return false; + sectname = ".prstatus"; + break; + case NT_FPREGSET: + /* process descdata as fpregset info */ + if (! bfd_fpregset (abfd, descdata, i_note.descsz, filepos)) + return false; + sectname = ".fpregset"; + break; + case NT_PRPSINFO: + /* process descdata as prpsinfo */ + if (! bfd_prpsinfo (abfd, descdata, i_note.descsz, filepos)) + return false; + sectname = ".prpsinfo"; + break; + default: + /* Unknown descriptor, just ignore it. */ + sectname = NULL; + break; + } + if (sectname != NULL) + { + newsect = bfd_make_section (abfd, sectname); + if (newsect == NULL) + return false; + newsect->_raw_size = i_note.descsz; + newsect->filepos = filepos; + newsect->flags = SEC_ALLOC | SEC_HAS_CONTENTS; + newsect->alignment_power = 2; + } + x_note_p = (Elf_External_Note *) + (descdata + BFD_ALIGN (i_note.descsz, 4)); + } + } + if (buf != NULL) + { + free (buf); + } + else if (hdr->p_filesz > 0) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + return true; + +} + +/* Core files are simply standard ELF formatted files that partition + the file using the execution view of the file (program header table) + rather than the linking view. In fact, there is no section header + table in a core file. + + The process status information (including the contents of the general + register set) and the floating point register set are stored in a + segment of type PT_NOTE. We handcraft a couple of extra bfd sections + that allow standard bfd access to the general registers (.reg) and the + floating point registers (.reg2). + + */ + +const bfd_target * +elf_core_file_p (abfd) + bfd *abfd; +{ + Elf_External_Ehdr x_ehdr; /* Elf file header, external form */ + Elf_Internal_Ehdr *i_ehdrp; /* Elf file header, internal form */ + Elf_External_Phdr x_phdr; /* Program header table entry, external form */ + Elf_Internal_Phdr *i_phdrp; /* Program header table, internal form */ + unsigned int phindex; + struct elf_backend_data *ebd; + + /* Read in the ELF header in external format. */ + + if (bfd_read ((PTR) & x_ehdr, sizeof (x_ehdr), 1, abfd) != sizeof (x_ehdr)) + { + if (bfd_get_error () != bfd_error_system_call) + bfd_set_error (bfd_error_wrong_format); + return NULL; + } + + /* Now check to see if we have a valid ELF file, and one that BFD can + make use of. The magic number must match, the address size ('class') + and byte-swapping must match our XVEC entry, and it must have a + program header table (FIXME: See comments re segments at top of this + file). */ + + if (elf_file_p (&x_ehdr) == false) + { + wrong: + bfd_set_error (bfd_error_wrong_format); + return NULL; + } + + /* FIXME, Check EI_VERSION here ! */ + + { +#if ARCH_SIZE == 32 + int desired_address_size = ELFCLASS32; +#endif +#if ARCH_SIZE == 64 + int desired_address_size = ELFCLASS64; +#endif + + if (x_ehdr.e_ident[EI_CLASS] != desired_address_size) + goto wrong; + } + + /* Switch xvec to match the specified byte order. */ + switch (x_ehdr.e_ident[EI_DATA]) + { + case ELFDATA2MSB: /* Big-endian */ + if (abfd->xvec->byteorder_big_p == false) + goto wrong; + break; + case ELFDATA2LSB: /* Little-endian */ + if (abfd->xvec->byteorder_big_p == true) + goto wrong; + break; + case ELFDATANONE: /* No data encoding specified */ + default: /* Unknown data encoding specified */ + goto wrong; + } + + /* Allocate an instance of the elf_obj_tdata structure and hook it up to + the tdata pointer in the bfd. */ + + elf_tdata (abfd) = + (struct elf_obj_tdata *) bfd_zalloc (abfd, sizeof (struct elf_obj_tdata)); + if (elf_tdata (abfd) == NULL) + { + bfd_set_error (bfd_error_no_memory); + return NULL; + } + + /* FIXME, `wrong' returns from this point onward, leak memory. */ + + /* Now that we know the byte order, swap in the rest of the header */ + i_ehdrp = elf_elfheader (abfd); + elf_swap_ehdr_in (abfd, &x_ehdr, i_ehdrp); +#if DEBUG & 1 + elf_debug_file (i_ehdrp); +#endif + + ebd = get_elf_backend_data (abfd); + + /* Check that the ELF e_machine field matches what this particular + BFD format expects. */ + if (ebd->elf_machine_code != i_ehdrp->e_machine) + { + const bfd_target * const *target_ptr; + + if (ebd->elf_machine_code != EM_NONE) + goto wrong; + + /* This is the generic ELF target. Let it match any ELF target + for which we do not have a specific backend. */ + for (target_ptr = bfd_target_vector; *target_ptr != NULL; target_ptr++) + { + struct elf_backend_data *back; + + if ((*target_ptr)->flavour != bfd_target_elf_flavour) + continue; + back = (struct elf_backend_data *) (*target_ptr)->backend_data; + if (back->elf_machine_code == i_ehdrp->e_machine) + { + /* target_ptr is an ELF backend which matches this + object file, so reject the generic ELF target. */ + goto wrong; + } + } + } + + /* If there is no program header, or the type is not a core file, then + we are hosed. */ + if (i_ehdrp->e_phoff == 0 || i_ehdrp->e_type != ET_CORE) + goto wrong; + + /* Allocate space for a copy of the program header table in + internal form, seek to the program header table in the file, + read it in, and convert it to internal form. As a simple sanity + check, verify that the what BFD thinks is the size of each program + header table entry actually matches the size recorded in the file. */ + + if (i_ehdrp->e_phentsize != sizeof (x_phdr)) + goto wrong; + i_phdrp = (Elf_Internal_Phdr *) + bfd_alloc (abfd, sizeof (*i_phdrp) * i_ehdrp->e_phnum); + if (!i_phdrp) + { + bfd_set_error (bfd_error_no_memory); + return NULL; + } + if (bfd_seek (abfd, i_ehdrp->e_phoff, SEEK_SET) == -1) + return NULL; + for (phindex = 0; phindex < i_ehdrp->e_phnum; phindex++) + { + if (bfd_read ((PTR) & x_phdr, sizeof (x_phdr), 1, abfd) + != sizeof (x_phdr)) + return NULL; + elf_swap_phdr_in (abfd, &x_phdr, i_phdrp + phindex); + } + + /* Once all of the program headers have been read and converted, we + can start processing them. */ + + for (phindex = 0; phindex < i_ehdrp->e_phnum; phindex++) + { + bfd_section_from_phdr (abfd, i_phdrp + phindex, phindex); + if ((i_phdrp + phindex)->p_type == PT_NOTE) + { + if (! elf_corefile_note (abfd, i_phdrp + phindex)) + return NULL; + } + } + + /* Remember the entry point specified in the ELF file header. */ + + bfd_get_start_address (abfd) = i_ehdrp->e_entry; + + return abfd->xvec; +} + +/* ELF linker code. */ + +static boolean elf_link_add_object_symbols + PARAMS ((bfd *, struct bfd_link_info *)); +static boolean elf_link_add_archive_symbols + PARAMS ((bfd *, struct bfd_link_info *)); +static INLINE boolean elf_link_record_dynamic_symbol + PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *)); +static boolean elf_link_create_dynamic_sections + PARAMS ((bfd *, struct bfd_link_info *)); +static boolean elf_adjust_dynamic_symbol + PARAMS ((struct elf_link_hash_entry *, PTR)); + +/* Given an ELF BFD, add symbols to the global hash table as + appropriate. */ + +boolean +elf_bfd_link_add_symbols (abfd, info) + bfd *abfd; + struct bfd_link_info *info; +{ + switch (bfd_get_format (abfd)) + { + case bfd_object: + return elf_link_add_object_symbols (abfd, info); + case bfd_archive: + return elf_link_add_archive_symbols (abfd, info); + default: + bfd_set_error (bfd_error_wrong_format); + return false; + } +} + +/* Add symbols from an ELF archive file to the linker hash table. We + don't use _bfd_generic_link_add_archive_symbols because of a + problem which arises on UnixWare. The UnixWare libc.so is an + archive which includes an entry libc.so.1 which defines a bunch of + symbols. The libc.so archive also includes a number of other + object files, which also define symbols, some of which are the same + as those defined in libc.so.1. Correct linking requires that we + consider each object file in turn, and include it if it defines any + symbols we need. _bfd_generic_link_add_archive_symbols does not do + this; it looks through the list of undefined symbols, and includes + any object file which defines them. When this algorithm is used on + UnixWare, it winds up pulling in libc.so.1 early and defining a + bunch of symbols. This means that some of the other objects in the + archive are not included in the link, which is incorrect since they + precede libc.so.1 in the archive. + + Fortunately, ELF archive handling is simpler than that done by + _bfd_generic_link_add_archive_symbols, which has to allow for a.out + oddities. In ELF, if we find a symbol in the archive map, and the + symbol is currently undefined, we know that we must pull in that + object file. + + Unfortunately, we do have to make multiple passes over the symbol + table until nothing further is resolved. */ + +static boolean +elf_link_add_archive_symbols (abfd, info) + bfd *abfd; + struct bfd_link_info *info; +{ + symindex c; + boolean *defined = NULL; + boolean *included = NULL; + carsym *symdefs; + boolean loop; + + if (! bfd_has_map (abfd)) + { + bfd_set_error (bfd_error_no_symbols); + return false; + } + + /* Keep track of all symbols we know to be already defined, and all + files we know to be already included. This is to speed up the + second and subsequent passes. */ + c = bfd_ardata (abfd)->symdef_count; + if (c == 0) + return true; + defined = (boolean *) malloc (c * sizeof (boolean)); + included = (boolean *) malloc (c * sizeof (boolean)); + if (defined == (boolean *) NULL || included == (boolean *) NULL) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } + memset (defined, 0, c * sizeof (boolean)); + memset (included, 0, c * sizeof (boolean)); + + symdefs = bfd_ardata (abfd)->symdefs; + + do + { + file_ptr last; + symindex i; + carsym *symdef; + carsym *symdefend; + + loop = false; + last = -1; + + symdef = symdefs; + symdefend = symdef + c; + for (i = 0; symdef < symdefend; symdef++, i++) + { + struct elf_link_hash_entry *h; + bfd *element; + struct bfd_link_hash_entry *undefs_tail; + symindex mark; + + if (defined[i] || included[i]) + continue; + if (symdef->file_offset == last) + { + included[i] = true; + continue; + } + + h = elf_link_hash_lookup (elf_hash_table (info), symdef->name, + false, false, false); + if (h == (struct elf_link_hash_entry *) NULL) + continue; + if (h->root.type != bfd_link_hash_undefined) + { + defined[i] = true; + continue; + } + + /* We need to include this archive member. */ + + element = _bfd_get_elt_at_filepos (abfd, symdef->file_offset); + if (element == (bfd *) NULL) + goto error_return; + + if (! bfd_check_format (element, bfd_object)) + goto error_return; + + /* Doublecheck that we have not included this object + already--it should be impossible, but there may be + something wrong with the archive. */ + if (element->archive_pass != 0) + { + bfd_set_error (bfd_error_bad_value); + goto error_return; + } + element->archive_pass = 1; + + undefs_tail = info->hash->undefs_tail; + + if (! (*info->callbacks->add_archive_element) (info, element, + symdef->name)) + goto error_return; + if (! elf_link_add_object_symbols (element, info)) + goto error_return; + + /* If there are any new undefined symbols, we need to make + another pass through the archive in order to see whether + they can be defined. FIXME: This isn't perfect, because + common symbols wind up on undefs_tail and because an + undefined symbol which is defined later on in this pass + does not require another pass. This isn't a bug, but it + does make the code less efficient than it could be. */ + if (undefs_tail != info->hash->undefs_tail) + loop = true; + + /* Look backward to mark all symbols from this object file + which we have already seen in this pass. */ + mark = i; + do + { + included[mark] = true; + if (mark == 0) + break; + --mark; + } + while (symdefs[mark].file_offset == symdef->file_offset); + + /* We mark subsequent symbols from this object file as we go + on through the loop. */ + last = symdef->file_offset; + } + } + while (loop); + + free (defined); + free (included); + + return true; + + error_return: + if (defined != (boolean *) NULL) + free (defined); + if (included != (boolean *) NULL) + free (included); + return false; +} + +/* Record a new dynamic symbol. We record the dynamic symbols as we + read the input files, since we need to have a list of all of them + before we can determine the final sizes of the output sections. */ + +static INLINE boolean +elf_link_record_dynamic_symbol (info, h) + struct bfd_link_info *info; + struct elf_link_hash_entry *h; +{ + if (h->dynindx == -1) + { + h->dynindx = elf_hash_table (info)->dynsymcount; + ++elf_hash_table (info)->dynsymcount; + h->dynstr_index = bfd_add_to_strtab (elf_hash_table (info)->dynobj, + elf_hash_table (info)->dynstr, + h->root.root.string); + if (h->dynstr_index == (unsigned long) -1) + return false; + } + + return true; +} + +/* Add symbols from an ELF object file to the linker hash table. */ + +static boolean +elf_link_add_object_symbols (abfd, info) + bfd *abfd; + struct bfd_link_info *info; +{ + boolean (*add_symbol_hook) PARAMS ((bfd *, struct bfd_link_info *, + const Elf_Internal_Sym *, + const char **, flagword *, + asection **, bfd_vma *)); + boolean collect; + Elf_Internal_Shdr *hdr; + size_t symcount; + size_t extsymcount; + size_t extsymoff; + Elf_External_Sym *buf = NULL; + struct elf_link_hash_entry **sym_hash; + boolean dynamic; + Elf_External_Dyn *dynbuf = NULL; + struct elf_link_hash_entry *weaks; + Elf_External_Sym *esym; + Elf_External_Sym *esymend; + + add_symbol_hook = get_elf_backend_data (abfd)->elf_add_symbol_hook; + collect = get_elf_backend_data (abfd)->collect; + + hdr = &elf_tdata (abfd)->symtab_hdr; + symcount = hdr->sh_size / sizeof (Elf_External_Sym); + + /* The sh_info field of the symtab header tells us where the + external symbols start. We don't care about the local symbols at + this point. */ + if (elf_bad_symtab (abfd)) + { + extsymcount = symcount; + extsymoff = 0; + } + else + { + extsymcount = symcount - hdr->sh_info; + extsymoff = hdr->sh_info; + } + + buf = (Elf_External_Sym *) malloc (extsymcount * sizeof (Elf_External_Sym)); + if (buf == NULL && extsymcount != 0) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } + + /* We store a pointer to the hash table entry for each external + symbol. */ + sym_hash = ((struct elf_link_hash_entry **) + bfd_alloc (abfd, + extsymcount * sizeof (struct elf_link_hash_entry *))); + if (sym_hash == NULL) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } + elf_sym_hashes (abfd) = sym_hash; + + if (elf_elfheader (abfd)->e_type != ET_DYN) + { + dynamic = false; + + /* If we are creating a shared library, create all the dynamic + sections immediately. We need to attach them to something, + so we attach them to this BFD, provided it is the right + format. FIXME: If there are no input BFD's of the same + format as the output, we can't make a shared library. */ + if (info->shared + && elf_hash_table (info)->dynobj == NULL + && abfd->xvec == info->hash->creator) + { + if (! elf_link_create_dynamic_sections (abfd, info)) + goto error_return; + elf_hash_table (info)->dynobj = abfd; + } + } + else + { + asection *s; + const char *name; + unsigned long strindex; + + dynamic = true; + + /* You can't use -r against a dynamic object. There's no hope + of using a dynamic object which does not exactly match the + format of the output file. */ + if (info->relocateable + || info->hash->creator != abfd->xvec) + { + bfd_set_error (bfd_error_invalid_operation); + goto error_return; + } + + /* Find the name to use in a DT_NEEDED entry that refers to this + object. If the object has a DT_SONAME entry, we use it. + Otherwise, if the generic linker stuck something in + elf_dt_needed_name, we use that. Otherwise, we just use the + file name. */ + name = bfd_get_filename (abfd); + if (elf_dt_needed_name (abfd) != NULL) + name = elf_dt_needed_name (abfd); + s = bfd_get_section_by_name (abfd, ".dynamic"); + if (s != NULL) + { + Elf_External_Dyn *extdyn; + Elf_External_Dyn *extdynend; + + dynbuf = (Elf_External_Dyn *) malloc (s->_raw_size); + if (dynbuf == NULL) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } + + if (! bfd_get_section_contents (abfd, s, (PTR) dynbuf, + (file_ptr) 0, s->_raw_size)) + goto error_return; + + extdyn = dynbuf; + extdynend = extdyn + s->_raw_size / sizeof (Elf_External_Dyn); + for (; extdyn < extdynend; extdyn++) + { + Elf_Internal_Dyn dyn; + + elf_swap_dyn_in (abfd, extdyn, &dyn); + if (dyn.d_tag == DT_SONAME) + { + int elfsec; + unsigned long link; + + elfsec = elf_section_from_bfd_section (abfd, s); + if (elfsec == -1) + goto error_return; + link = elf_elfsections (abfd)[elfsec]->sh_link; + name = elf_string_from_elf_section (abfd, link, + dyn.d_un.d_val); + if (name == NULL) + goto error_return; + + break; + } + } + + free (dynbuf); + dynbuf = NULL; + } + + /* We do not want to include any of the sections in a dynamic + object in the output file. We hack by simply clobbering the + list of sections in the BFD. This could be handled more + cleanly by, say, a new section flag; the existing + SEC_NEVER_LOAD flag is not the one we want, because that one + still implies that the section takes up space in the output + file. */ + abfd->sections = NULL; + + /* If this is the first dynamic object found in the link, create + the special sections required for dynamic linking. We need + to put them somewhere, and attaching them to the first + dynamic object is as good place as any. */ + if (elf_hash_table (info)->dynobj == NULL) + { + if (! elf_link_create_dynamic_sections (abfd, info)) + goto error_return; + elf_hash_table (info)->dynobj = abfd; + } + + /* Add a DT_NEEDED entry for this dynamic object. */ + strindex = bfd_add_to_strtab (abfd, + elf_hash_table (info)->dynstr, + name); + + if (strindex == (unsigned long) -1) + goto error_return; + if (! elf_add_dynamic_entry (info, DT_NEEDED, strindex)) + goto error_return; + } + + if (bfd_seek (abfd, + hdr->sh_offset + extsymoff * sizeof (Elf_External_Sym), + SEEK_SET) != 0 + || (bfd_read ((PTR) buf, sizeof (Elf_External_Sym), extsymcount, abfd) + != extsymcount * sizeof (Elf_External_Sym))) + goto error_return; + + weaks = NULL; + + esymend = buf + extsymcount; + for (esym = buf; esym < esymend; esym++, sym_hash++) + { + Elf_Internal_Sym sym; + int bind; + bfd_vma value; + asection *sec; + flagword flags; + const char *name; + struct elf_link_hash_entry *h = NULL; + boolean definition; + + elf_swap_symbol_in (abfd, esym, &sym); + + flags = BSF_NO_FLAGS; + sec = NULL; + value = sym.st_value; + *sym_hash = NULL; + + bind = ELF_ST_BIND (sym.st_info); + if (bind == STB_LOCAL) + { + /* This should be impossible, since ELF requires that all + global symbols follow all local symbols, and that sh_info + point to the first global symbol. Unfortunatealy, Irix 5 + screws this up. */ + continue; + } + else if (bind == STB_GLOBAL) + flags = BSF_GLOBAL; + else if (bind == STB_WEAK) + flags = BSF_WEAK; + else + { + /* Leave it up to the processor backend. */ + } + + if (sym.st_shndx == SHN_UNDEF) + sec = bfd_und_section_ptr; + else if (sym.st_shndx > 0 && sym.st_shndx < SHN_LORESERVE) + { + sec = section_from_elf_index (abfd, sym.st_shndx); + if (sec == NULL) + goto error_return; + value -= sec->vma; + } + else if (sym.st_shndx == SHN_ABS) + sec = bfd_abs_section_ptr; + else if (sym.st_shndx == SHN_COMMON) + { + sec = bfd_com_section_ptr; + /* What ELF calls the size we call the value. What ELF + calls the value we call the alignment. */ + value = sym.st_size; + } + else + { + /* Leave it up to the processor backend. */ + } + + name = elf_string_from_elf_section (abfd, hdr->sh_link, sym.st_name); + if (name == (const char *) NULL) + goto error_return; + + if (add_symbol_hook) + { + if (! (*add_symbol_hook) (abfd, info, &sym, &name, &flags, &sec, + &value)) + goto error_return; + + /* The hook function sets the name to NULL if this symbol + should be skipped for some reason. */ + if (name == (const char *) NULL) + continue; + } + + /* Sanity check that all possibilities were handled. */ + if (flags == BSF_NO_FLAGS || sec == (asection *) NULL) + { + bfd_set_error (bfd_error_bad_value); + goto error_return; + } + + if (bfd_is_und_section (sec) + || bfd_is_com_section (sec)) + definition = false; + else + definition = true; + + if (info->hash->creator->flavour == bfd_target_elf_flavour) + { + /* We need to look up the symbol now in order to get some of + the dynamic object handling right. We pass the hash + table entry in to _bfd_generic_link_add_one_symbol so + that it does not have to look it up again. */ + h = elf_link_hash_lookup (elf_hash_table (info), name, + true, false, false); + if (h == NULL) + goto error_return; + *sym_hash = h; + + /* If we are looking at a dynamic object, and this is a + definition, we need to see if it has already been defined + by some other object. If it has, we want to use the + existing definition, and we do not want to report a + multiple symbol definition error; we do this by + clobbering sec to be bfd_und_section_ptr. */ + if (dynamic && definition) + { + if (h->root.type == bfd_link_hash_defined) + sec = bfd_und_section_ptr; + } + + /* Similarly, if we are not looking at a dynamic object, and + we have a definition, we want to override any definition + we may have from a dynamic object. Symbols from regular + files always take precedence over symbols from dynamic + objects, even if they are defined after the dynamic + object in the link. */ + if (! dynamic + && definition + && h->root.type == bfd_link_hash_defined + && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0 + && (bfd_get_flavour (h->root.u.def.section->owner) + == bfd_target_elf_flavour) + && (elf_elfheader (h->root.u.def.section->owner)->e_type + == ET_DYN)) + { + /* Change the hash table entry to undefined, and let + _bfd_generic_link_add_one_symbol do the right thing + with the new definition. */ + h->root.type = bfd_link_hash_undefined; + h->root.u.undef.abfd = h->root.u.def.section->owner; + h->elf_link_hash_flags &=~ ELF_LINK_HASH_DEFINED_WEAK; + } + + /* If this is a weak definition which we are going to use, + and the symbol is currently undefined, record that the + definition is weak. */ + if (definition + && (flags & BSF_WEAK) != 0 + && ! bfd_is_und_section (sec) + && (h->root.type == bfd_link_hash_new + || h->root.type == bfd_link_hash_undefined + || h->root.type == bfd_link_hash_weak)) + h->elf_link_hash_flags |= ELF_LINK_HASH_DEFINED_WEAK; + } + + if (! (_bfd_generic_link_add_one_symbol + (info, abfd, name, flags, sec, value, (const char *) NULL, + false, collect, (struct bfd_link_hash_entry **) sym_hash))) + goto error_return; + + if (dynamic + && definition + && (flags & BSF_WEAK) != 0 + && ELF_ST_TYPE (sym.st_info) != STT_FUNC + && (*sym_hash)->weakdef == NULL) + { + /* Keep a list of all weak defined non function symbols from + a dynamic object, using the weakdef field. Later in this + function we will set the weakdef field to the correct + value. We only put non-function symbols from dynamic + objects on this list, because that happens to be the only + time we need to know the normal symbol corresponding to a + weak symbol, and the information is time consuming to + figure out. If the weakdef field is not already NULL, + then this symbol was already defined by some previous + dynamic object, and we will be using that previous + definition anyhow. */ + + (*sym_hash)->weakdef = weaks; + weaks = *sym_hash; + } + + if (info->hash->creator->flavour == bfd_target_elf_flavour) + { + int old_flags; + boolean dynsym; + int new_flag; + + /* Remember the symbol size, type and alignment. */ + if (sym.st_size != 0) + { + /* FIXME: We should probably somehow give a warning if + the symbol size changes. */ + h->size = sym.st_size; + } + if (sym.st_shndx == SHN_COMMON + && sym.st_value > h->align) + h->align = sym.st_value; + if (ELF_ST_TYPE (sym.st_info) != STT_NOTYPE) + { + /* FIXME: We should probably somehow give a warning if + the symbol type changes. */ + h->type = ELF_ST_TYPE (sym.st_info); + } + + /* Set a flag in the hash table entry indicating the type of + reference or definition we just found. Keep a count of + the number of dynamic symbols we find. A dynamic symbol + is one which is referenced or defined by both a regular + object and a shared object, or one which is referenced or + defined by more than one shared object. */ + old_flags = h->elf_link_hash_flags; + dynsym = false; + if (! dynamic) + { + if (! definition) + new_flag = ELF_LINK_HASH_REF_REGULAR; + else + new_flag = ELF_LINK_HASH_DEF_REGULAR; + if ((old_flags & (ELF_LINK_HASH_DEF_DYNAMIC + | ELF_LINK_HASH_REF_DYNAMIC)) != 0) + dynsym = true; + } + else + { + if (! definition) + new_flag = ELF_LINK_HASH_REF_DYNAMIC; + else + new_flag = ELF_LINK_HASH_DEF_DYNAMIC; + if ((old_flags & new_flag) != 0) + { + if (! definition) + new_flag = ELF_LINK_HASH_REF_DYNAMIC_MULTIPLE; + else + new_flag = ELF_LINK_HASH_DEF_DYNAMIC_MULTIPLE; + dynsym = true; + } + else + { + if ((old_flags & (ELF_LINK_HASH_DEF_REGULAR + | ELF_LINK_HASH_REF_REGULAR)) != 0) + dynsym = true; + } + } + + h->elf_link_hash_flags |= new_flag; + if (dynsym && h->dynindx == -1) + { + if (! elf_link_record_dynamic_symbol (info, h)) + goto error_return; + } + } + } + + /* Now set the weakdefs field correctly for all the weak defined + symbols we found. The only way to do this is to search all the + symbols. Since we only need the information for non functions in + dynamic objects, that's the only time we actually put anything on + the list WEAKS. We need this information so that if a regular + object refers to a symbol defined weakly in a dynamic object, the + real symbol in the dynamic object is also put in the dynamic + symbols; we also must arrange for both symbols to point to the + same memory location. We could handle the general case of symbol + aliasing, but a general symbol alias can only be generated in + assembler code, handling it correctly would be very time + consuming, and other ELF linkers don't handle general aliasing + either. */ + while (weaks != NULL) + { + struct elf_link_hash_entry *hlook; + asection *slook; + bfd_vma vlook; + struct elf_link_hash_entry **hpp; + struct elf_link_hash_entry **hppend; + + hlook = weaks; + weaks = hlook->weakdef; + hlook->weakdef = NULL; + + BFD_ASSERT (hlook->root.type == bfd_link_hash_defined); + slook = hlook->root.u.def.section; + vlook = hlook->root.u.def.value; + + hpp = elf_sym_hashes (abfd); + hppend = hpp + extsymcount; + for (; hpp < hppend; hpp++) + { + struct elf_link_hash_entry *h; + + h = *hpp; + if (h != hlook + && h->root.type == bfd_link_hash_defined + && h->root.u.def.section == slook + && h->root.u.def.value == vlook) + { + hlook->weakdef = h; + + /* If the weak definition is in the list of dynamic + symbols, make sure the real definition is put there + as well. */ + if (hlook->dynindx != -1 + && h->dynindx == -1) + { + if (! elf_link_record_dynamic_symbol (info, h)) + goto error_return; + } + + break; + } + } + } + + if (buf != NULL) + free (buf); + + return true; + + error_return: + if (buf != NULL) + free (buf); + if (dynbuf != NULL) + free (dynbuf); + return false; +} + +/* Create some sections which will be filled in with dynamic linking + information. The ABFD argument is an input file which is a dynamic + object. The dynamic sections take up virtual memory space when the + final executable is run, so we need to create them before addresses + are assigned to the output sections. We work out the actual + contents and size of these sections later. */ + +static boolean +elf_link_create_dynamic_sections (abfd, info) + bfd *abfd; + struct bfd_link_info *info; +{ + flagword flags; + register asection *s; + struct elf_link_hash_entry *h; + struct elf_backend_data *bed; + + /* Note that we set the SEC_IN_MEMORY flag for all of these + sections. */ + flags = SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY; + + /* A dynamically linked executable has a .interp section, but a + shared library does not. */ + if (! info->shared) + { + s = bfd_make_section (abfd, ".interp"); + if (s == NULL + || ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY)) + return false; + } + + s = bfd_make_section (abfd, ".dynamic"); + if (s == NULL + || ! bfd_set_section_flags (abfd, s, flags) + || ! bfd_set_section_alignment (abfd, s, LOG_FILE_ALIGN)) + return false; + + /* The special symbol _DYNAMIC is always set to the start of the + .dynamic section. This call occurs before we have processed the + symbols for any dynamic object, so we don't have to worry about + overriding a dynamic definition. We could set _DYNAMIC in a + linker script, but we only want to define it if we are, in fact, + creating a .dynamic section. We don't want to define it if there + is no .dynamic section, since on some ELF platforms the start up + code examines it to decide how to initialize the process. */ + h = NULL; + if (! (_bfd_generic_link_add_one_symbol + (info, abfd, "_DYNAMIC", BSF_GLOBAL, s, (bfd_vma) 0, + (const char *) NULL, false, get_elf_backend_data (abfd)->collect, + (struct bfd_link_hash_entry **) &h))) + return false; + h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR; + + s = bfd_make_section (abfd, ".dynsym"); + if (s == NULL + || ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY) + || ! bfd_set_section_alignment (abfd, s, LOG_FILE_ALIGN)) + return false; + + /* The first .dynsym symbol is a dummy. */ + elf_hash_table (info)->dynsymcount = 1; + + s = bfd_make_section (abfd, ".dynstr"); + if (s == NULL + || ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY)) + return false; + + /* Create a strtab to hold the dynamic symbol names. */ + elf_hash_table (info)->dynstr = bfd_new_strtab (abfd); + if (elf_hash_table (info)->dynstr == NULL) + return false; + + s = bfd_make_section (abfd, ".hash"); + if (s == NULL + || ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY) + || ! bfd_set_section_alignment (abfd, s, LOG_FILE_ALIGN)) + return false; + + /* Let the backend create the rest of the sections. This lets the + backend set the right flags. The backend will normally create + the .got and .plt sections. */ + bed = get_elf_backend_data (abfd); + return (*bed->elf_backend_create_dynamic_sections) (abfd, info); +} + +/* Add an entry to the .dynamic table. */ + +boolean +elf_add_dynamic_entry (info, tag, val) + struct bfd_link_info *info; + bfd_vma tag; + bfd_vma val; +{ + Elf_Internal_Dyn dyn; + bfd *dynobj; + asection *s; + size_t newsize; + bfd_byte *newcontents; + + dynobj = elf_hash_table (info)->dynobj; + + s = bfd_get_section_by_name (dynobj, ".dynamic"); + BFD_ASSERT (s != NULL); + + newsize = s->_raw_size + sizeof (Elf_External_Dyn); + if (s->contents == NULL) + newcontents = (bfd_byte *) malloc (newsize); + else + newcontents = (bfd_byte *) realloc (s->contents, newsize); + if (newcontents == NULL) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + + dyn.d_tag = tag; + dyn.d_un.d_val = val; + elf_swap_dyn_out (dynobj, &dyn, + (Elf_External_Dyn *) (newcontents + s->_raw_size)); + + s->_raw_size = newsize; + s->contents = newcontents; + + return true; +} + +/* Record an assignment to a symbol made by a linker script. We need + this in case some dynamic object refers to this symbol. */ + +/*ARGSUSED*/ +boolean +NAME(bfd_elf,record_link_assignment) (output_bfd, info, name) + bfd *output_bfd; + struct bfd_link_info *info; + const char *name; +{ + struct elf_link_hash_entry *h; + + /* This is called after we have examined all the input objects. If + the symbol does not exist, it merely means that no object refers + to it, and we can just ignore it at this point. */ + h = elf_link_hash_lookup (elf_hash_table (info), name, + false, false, false); + if (h == NULL) + return true; + + h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR; + + if ((h->elf_link_hash_flags & (ELF_LINK_HASH_DEF_DYNAMIC + | ELF_LINK_HASH_REF_DYNAMIC)) != 0 + && h->dynindx == -1) + { + if (! elf_link_record_dynamic_symbol (info, h)) + return false; + + /* If this is a weak defined symbol, and we know a corresponding + real symbol from the same dynamic object, make sure the real + symbol is also made into a dynamic symbol. */ + if (h->weakdef != NULL + && h->weakdef->dynindx == -1) + { + if (! elf_link_record_dynamic_symbol (info, h->weakdef)) + return false; + } + } + + return true; +} + +/* Array used to determine the number of hash table buckets to use + based on the number of symbols there are. If there are fewer than + 3 symbols we use 1 bucket, fewer than 17 symbols we use 3 buckets, + fewer than 37 we use 17 buckets, and so forth. We never use more + than 521 buckets. */ + +static const size_t elf_buckets[] = +{ + 1, 3, 17, 37, 67, 97, 131, 197, 263, 521, 0 +}; + +/* Set up the sizes and contents of the ELF dynamic sections. This is + called by the ELF linker emulation before_allocation routine. We + must set the sizes of the sections before the linker sets the + addresses of the various sections. */ + +boolean +NAME(bfd_elf,size_dynamic_sections) (output_bfd, info, sinterpptr) + bfd *output_bfd; + struct bfd_link_info *info; + asection **sinterpptr; +{ + bfd *dynobj; + size_t dynsymcount; + asection *s; + Elf_Internal_Sym isym; + size_t i; + size_t bucketcount; + struct elf_backend_data *bed; + + *sinterpptr = NULL; + + dynobj = elf_hash_table (info)->dynobj; + dynsymcount = elf_hash_table (info)->dynsymcount; + + /* If there were no dynamic objects in the link, there is nothing to + do here. */ + if (dynobj == NULL) + return true; + + *sinterpptr = bfd_get_section_by_name (dynobj, ".interp"); + BFD_ASSERT (*sinterpptr != NULL || info->shared); + + /* Set the size of the .dynsym and .hash sections. We counted the + number of dynamic symbols in elf_link_add_object_symbols. We + will build the contents of .dynsym and .hash when we build the + final symbol table, because until then we do not know the correct + value to give the symbols. We built the .dynstr section as we + went along in elf_link_add_object_symbols. */ + s = bfd_get_section_by_name (dynobj, ".dynsym"); + BFD_ASSERT (s != NULL); + s->_raw_size = dynsymcount * sizeof (Elf_External_Sym); + s->contents = (bfd_byte *) bfd_alloc (output_bfd, s->_raw_size); + if (s->contents == NULL && s->_raw_size != 0) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + + /* The first entry in .dynsym is a dummy symbol. */ + isym.st_value = 0; + isym.st_size = 0; + isym.st_name = 0; + isym.st_info = 0; + isym.st_other = 0; + isym.st_shndx = 0; + elf_swap_symbol_out (output_bfd, &isym, + (Elf_External_Sym *) s->contents); + + for (i = 0; elf_buckets[i] != 0; i++) + { + bucketcount = elf_buckets[i]; + if (dynsymcount < elf_buckets[i + 1]) + break; + } + + s = bfd_get_section_by_name (dynobj, ".hash"); + BFD_ASSERT (s != NULL); + s->_raw_size = (2 + bucketcount + dynsymcount) * (ARCH_SIZE / 8); + s->contents = (bfd_byte *) bfd_alloc (output_bfd, s->_raw_size); + if (s->contents == NULL) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + memset (s->contents, 0, s->_raw_size); + + put_word (output_bfd, bucketcount, s->contents); + put_word (output_bfd, dynsymcount, s->contents + (ARCH_SIZE / 8)); + + elf_hash_table (info)->bucketcount = bucketcount; + + s = bfd_get_section_by_name (dynobj, ".dynstr"); + BFD_ASSERT (s != NULL); + s->_raw_size = elf_hash_table (info)->dynstr->length; + s->contents = (unsigned char *) elf_hash_table (info)->dynstr->tab; + + /* Find all symbols which were defined in a dynamic object and make + the backend pick a reasonable value for them. */ + elf_link_hash_traverse (elf_hash_table (info), + elf_adjust_dynamic_symbol, + (PTR) info); + + /* Add some entries to the .dynamic section. We fill in some of the + values later, in elf_bfd_final_link, but we must add the entries + now so that we know the final size of the .dynamic section. */ + if (bfd_get_section_by_name (output_bfd, ".init") != NULL) + { + if (! elf_add_dynamic_entry (info, DT_INIT, 0)) + return false; + } + if (bfd_get_section_by_name (output_bfd, ".fini") != NULL) + { + if (! elf_add_dynamic_entry (info, DT_FINI, 0)) + return false; + } + if (! elf_add_dynamic_entry (info, DT_HASH, 0) + || ! elf_add_dynamic_entry (info, DT_STRTAB, 0) + || ! elf_add_dynamic_entry (info, DT_SYMTAB, 0) + || ! elf_add_dynamic_entry (info, DT_STRSZ, + elf_hash_table (info)->dynstr->length) + || ! elf_add_dynamic_entry (info, DT_SYMENT, + sizeof (Elf_External_Sym))) + return false; + + /* The backend must work out the sizes of all the other dynamic + sections. */ + bed = get_elf_backend_data (output_bfd); + if (! (*bed->elf_backend_size_dynamic_sections) (output_bfd, info)) + return false; + + return elf_add_dynamic_entry (info, DT_NULL, 0); +} + +/* Make the backend pick a good value for a dynamic symbol. This is + called via elf_link_hash_traverse, and also calls itself + recursively. */ + +static boolean +elf_adjust_dynamic_symbol (h, data) + struct elf_link_hash_entry *h; + PTR data; +{ + struct bfd_link_info *info = (struct bfd_link_info *) data; + bfd *dynobj; + struct elf_backend_data *bed; + + /* If this symbol is not defined by a dynamic object, or is not + referenced by a regular object, ignore it. FIXME: Do we need to + worry about symbols which are defined by one dynamic object and + referenced by another one? */ + if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) != 0 + || (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) == 0 + || (h->elf_link_hash_flags & ELF_LINK_HASH_REF_REGULAR) == 0) + return true; + + /* If we've already adjusted this symbol, don't do it again. This + can happen via a recursive call. */ + if ((h->elf_link_hash_flags & ELF_LINK_HASH_DYNAMIC_ADJUSTED) != 0) + return true; + + /* Don't look at this symbol again. Note that we must set this + after checking the above conditions, because we may look at a + symbol once, decide not to do anything, and then get called + recursively later after REF_REGULAR is set below. */ + h->elf_link_hash_flags |= ELF_LINK_HASH_DYNAMIC_ADJUSTED; + + /* If this is a weak definition, and we know a real definition, and + the real symbol is not itself defined by a regular object file, + then get a good value for the real definition. We handle the + real symbol first, for the convenience of the backend routine. + + Note that there is a confusing case here. If the real definition + is defined by a regular object file, we don't get the real symbol + from the dynamic object, but we do get the weak symbol. If the + processor backend uses a COPY reloc, then if some routine in the + dynamic object changes the real symbol, we will not see that + change in the corresponding weak symbol. This is the way other + ELF linkers work as well, and seems to be a result of the shared + library model. + + I will clarify this issue. Most SVR4 shared libraries define the + variable _timezone and define timezone as a weak synonym. The + tzset call changes _timezone. If you write + extern int timezone; + int _timezone = 5; + int main () { tzset (); printf ("%d %d\n", timezone, _timezone); } + you might expect that, since timezone is a synonym for _timezone, + the same number will print both times. However, if the processor + backend uses a COPY reloc, then actually timezone will be copied + into your process image, and, since you define _timezone + yourself, _timezone will not. Thus timezone and _timezone will + wind up at different memory locations. The tzset call will set + _timezone, leaving timezone unchanged. */ + + if (h->weakdef != NULL) + { + struct elf_link_hash_entry *weakdef; + + BFD_ASSERT (h->root.type == bfd_link_hash_defined); + weakdef = h->weakdef; + BFD_ASSERT (weakdef->root.type == bfd_link_hash_defined); + BFD_ASSERT (weakdef->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC); + if ((weakdef->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) != 0 + || (weakdef->elf_link_hash_flags & ELF_LINK_HASH_REF_REGULAR) == 0) + { + /* This symbol is defined or referenced by a regular object + file, so we will not do anything special. Clear weakdef + for the convenience of the processor backend. */ + h->weakdef = NULL; + } + else + { + /* There is an implicit reference by a regular object file + via the weak symbol. */ + weakdef->elf_link_hash_flags |= ELF_LINK_HASH_REF_REGULAR; + if (! elf_adjust_dynamic_symbol (weakdef, (PTR) info)) + return false; + } + } + + dynobj = elf_hash_table (info)->dynobj; + bed = get_elf_backend_data (dynobj); + if (! (*bed->elf_backend_adjust_dynamic_symbol) (info, h)) + { + /* FIXME: No way to return error. */ + abort (); + } + + return true; +} + +/* Final phase of ELF linker. */ + +/* A structure we use to avoid passing large numbers of arguments. */ + +struct elf_final_link_info +{ + /* General link information. */ + struct bfd_link_info *info; + /* Output BFD. */ + bfd *output_bfd; + /* Symbol string table. */ + struct strtab *symstrtab; + /* .dynsym section. */ + asection *dynsym_sec; + /* .hash section. */ + asection *hash_sec; + /* Buffer large enough to hold contents of any section. */ + bfd_byte *contents; + /* Buffer large enough to hold external relocs of any section. */ + PTR external_relocs; + /* Buffer large enough to hold internal relocs of any section. */ + Elf_Internal_Rela *internal_relocs; + /* Buffer large enough to hold external local symbols of any input + BFD. */ + Elf_External_Sym *external_syms; + /* Buffer large enough to hold internal local symbols of any input + BFD. */ + Elf_Internal_Sym *internal_syms; + /* Array large enough to hold a symbol index for each local symbol + of any input BFD. */ + long *indices; + /* Array large enough to hold a section pointer for each local + symbol of any input BFD. */ + asection **sections; + /* Buffer to hold swapped out symbols. */ + Elf_External_Sym *symbuf; + /* Number of swapped out symbols in buffer. */ + size_t symbuf_count; + /* Number of symbols which fit in symbuf. */ + size_t symbuf_size; +}; + +static boolean elf_link_output_sym + PARAMS ((struct elf_final_link_info *, const char *, + Elf_Internal_Sym *, asection *)); +static boolean elf_link_flush_output_syms + PARAMS ((struct elf_final_link_info *)); +static boolean elf_link_output_extsym + PARAMS ((struct elf_link_hash_entry *, PTR)); +static boolean elf_link_input_bfd + PARAMS ((struct elf_final_link_info *, bfd *)); +static boolean elf_reloc_link_order + PARAMS ((bfd *, struct bfd_link_info *, asection *, + struct bfd_link_order *)); + +/* Do the final step of an ELF link. */ + +boolean +elf_bfd_final_link (abfd, info) + bfd *abfd; + struct bfd_link_info *info; +{ + bfd *dynobj; + struct elf_final_link_info finfo; + register asection *o; + register struct bfd_link_order *p; + register bfd *sub; + size_t max_contents_size; + size_t max_external_reloc_size; + size_t max_internal_reloc_count; + size_t max_sym_count; + file_ptr off; + Elf_Internal_Sym elfsym; + unsigned int i; + Elf_Internal_Shdr *symtab_hdr; + Elf_Internal_Shdr *symstrtab_hdr; + struct elf_backend_data *bed = get_elf_backend_data (abfd); + + if (info->shared) + { + fprintf (stderr, + "Generating ELF shared libraries is not yet supported\n"); + bfd_set_error (bfd_error_invalid_operation); + return false; + } + + dynobj = elf_hash_table (info)->dynobj; + + finfo.info = info; + finfo.output_bfd = abfd; + finfo.symstrtab = bfd_new_strtab (abfd); + if (finfo.symstrtab == NULL) + return false; + if (dynobj == NULL) + { + finfo.dynsym_sec = NULL; + finfo.hash_sec = NULL; + } + else + { + finfo.dynsym_sec = bfd_get_section_by_name (dynobj, ".dynsym"); + finfo.hash_sec = bfd_get_section_by_name (dynobj, ".hash"); + if (finfo.dynsym_sec == NULL + || finfo.hash_sec == NULL) + abort (); + } + finfo.contents = NULL; + finfo.external_relocs = NULL; + finfo.internal_relocs = NULL; + finfo.external_syms = NULL; + finfo.internal_syms = NULL; + finfo.indices = NULL; + finfo.sections = NULL; + finfo.symbuf = NULL; + finfo.symbuf_count = 0; + + /* Count up the number of relocations we will output for each output + section, so that we know the sizes of the reloc sections. We + also figure out some maximum sizes. */ + max_contents_size = 0; + max_external_reloc_size = 0; + max_internal_reloc_count = 0; + max_sym_count = 0; + for (o = abfd->sections; o != (asection *) NULL; o = o->next) + { + o->reloc_count = 0; + + for (p = o->link_order_head; p != NULL; p = p->next) + { + if (p->type == bfd_section_reloc_link_order + || p->type == bfd_symbol_reloc_link_order) + ++o->reloc_count; + else if (p->type == bfd_indirect_link_order) + { + asection *sec; + + sec = p->u.indirect.section; + + if (info->relocateable) + o->reloc_count += sec->reloc_count; + + if (sec->_raw_size > max_contents_size) + max_contents_size = sec->_raw_size; + if (sec->_cooked_size > max_contents_size) + max_contents_size = sec->_cooked_size; + + /* We are interested in just local symbols, not all + symbols. */ + if (bfd_get_flavour (sec->owner) == bfd_target_elf_flavour) + { + size_t sym_count; + + if (elf_bad_symtab (sec->owner)) + sym_count = (elf_tdata (sec->owner)->symtab_hdr.sh_size + / sizeof (Elf_External_Sym)); + else + sym_count = elf_tdata (sec->owner)->symtab_hdr.sh_info; + + if (sym_count > max_sym_count) + max_sym_count = sym_count; + + if ((sec->flags & SEC_RELOC) != 0) + { + size_t ext_size; + + ext_size = elf_section_data (sec)->rel_hdr.sh_size; + if (ext_size > max_external_reloc_size) + max_external_reloc_size = ext_size; + if (sec->reloc_count > max_internal_reloc_count) + max_internal_reloc_count = sec->reloc_count; + } + } + } + } + + if (o->reloc_count > 0) + o->flags |= SEC_RELOC; + else + { + /* Explicitly clear the SEC_RELOC flag. The linker tends to + set it (this is probably a bug) and if it is set + assign_section_numbers will create a reloc section. */ + o->flags &=~ SEC_RELOC; + } + } + + /* Figure out the file positions for everything but the symbol table + and the relocs. We set symcount to force assign_section_numbers + to create a symbol table. */ + abfd->symcount = info->strip == strip_all ? 0 : 1; + BFD_ASSERT (! abfd->output_has_begun); + if (! elf_compute_section_file_positions (abfd, info)) + goto error_return; + + /* That created the reloc sections. Set their sizes, and assign + them file positions, and allocate some buffers. */ + for (o = abfd->sections; o != NULL; o = o->next) + { + if ((o->flags & SEC_RELOC) != 0) + { + Elf_Internal_Shdr *rel_hdr; + register struct elf_link_hash_entry **p, **pend; + + rel_hdr = &elf_section_data (o)->rel_hdr; + + rel_hdr->sh_size = rel_hdr->sh_entsize * o->reloc_count; + + /* The contents field must last into write_object_contents, + so we allocate it with bfd_alloc rather than malloc. */ + rel_hdr->contents = (PTR) bfd_alloc (abfd, rel_hdr->sh_size); + if (rel_hdr->contents == NULL && rel_hdr->sh_size != 0) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } + + p = ((struct elf_link_hash_entry **) + malloc (o->reloc_count + * sizeof (struct elf_link_hash_entry *))); + if (p == NULL && o->reloc_count != 0) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } + elf_section_data (o)->rel_hashes = p; + pend = p + o->reloc_count; + for (; p < pend; p++) + *p = NULL; + + /* Use the reloc_count field as an index when outputting the + relocs. */ + o->reloc_count = 0; + } + } + + assign_file_positions_for_relocs (abfd); + + /* We have now assigned file positions for all the sections except + .symtab and .strtab. We start the .symtab section at the current + file position, and write directly to it. We build the .strtab + section in memory. When we add .dynsym support, we will build + that in memory as well (.dynsym is smaller than .symtab). */ + abfd->symcount = 0; + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + /* sh_name is set in prep_headers. */ + symtab_hdr->sh_type = SHT_SYMTAB; + symtab_hdr->sh_flags = 0; + symtab_hdr->sh_addr = 0; + symtab_hdr->sh_size = 0; + symtab_hdr->sh_entsize = sizeof (Elf_External_Sym); + /* sh_link is set in assign_section_numbers. */ + /* sh_info is set below. */ + /* sh_offset is set just below. */ + symtab_hdr->sh_addralign = 4; /* FIXME: system dependent? */ + + off = elf_tdata (abfd)->next_file_pos; + off = assign_file_position_for_section (symtab_hdr, off, true); + + /* Note that at this point elf_tdata (abfd)->next_file_pos is + incorrect. We do not yet know the size of the .symtab section. + We correct next_file_pos below, after we do know the size. */ + + /* Allocate a buffer to hold swapped out symbols. This is to avoid + continuously seeking to the right position in the file. */ + if (! info->keep_memory || max_sym_count < 20) + finfo.symbuf_size = 20; + else + finfo.symbuf_size = max_sym_count; + finfo.symbuf = ((Elf_External_Sym *) + malloc (finfo.symbuf_size * sizeof (Elf_External_Sym))); + if (finfo.symbuf == NULL) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } + + /* Start writing out the symbol table. The first symbol is always a + dummy symbol. */ + elfsym.st_value = 0; + elfsym.st_size = 0; + elfsym.st_info = 0; + elfsym.st_other = 0; + elfsym.st_shndx = SHN_UNDEF; + if (! elf_link_output_sym (&finfo, (const char *) NULL, + &elfsym, bfd_und_section_ptr)) + goto error_return; + +#if 0 + /* Some standard ELF linkers do this, but we don't because it causes + bootstrap comparison failures. */ + /* Output a file symbol for the output file as the second symbol. + We output this even if we are discarding local symbols, although + I'm not sure if this is correct. */ + elfsym.st_value = 0; + elfsym.st_size = 0; + elfsym.st_info = ELF_ST_INFO (STB_LOCAL, STT_FILE); + elfsym.st_other = 0; + elfsym.st_shndx = SHN_ABS; + if (! elf_link_output_sym (&finfo, bfd_get_filename (abfd), + &elfsym, bfd_abs_section_ptr)) + goto error_return; +#endif + + /* Output a symbol for each section. We output these even if we are + discarding local symbols, since they are used for relocs. These + symbols have no names. We store the index of each one in the + index field of the section, so that we can find it again when + outputting relocs. */ + elfsym.st_value = 0; + elfsym.st_size = 0; + elfsym.st_info = ELF_ST_INFO (STB_LOCAL, STT_SECTION); + elfsym.st_other = 0; + for (i = 1; i < elf_elfheader (abfd)->e_shnum; i++) + { + o = section_from_elf_index (abfd, i); + if (! bfd_is_abs_section (o)) + o->target_index = abfd->symcount; + elfsym.st_shndx = i; + if (! elf_link_output_sym (&finfo, (const char *) NULL, + &elfsym, o)) + goto error_return; + } + + /* Allocate some memory to hold information read in from the input + files. */ + finfo.contents = (bfd_byte *) malloc (max_contents_size); + finfo.external_relocs = (PTR) malloc (max_external_reloc_size); + finfo.internal_relocs = ((Elf_Internal_Rela *) + malloc (max_internal_reloc_count + * sizeof (Elf_Internal_Rela))); + finfo.external_syms = ((Elf_External_Sym *) + malloc (max_sym_count * sizeof (Elf_External_Sym))); + finfo.internal_syms = ((Elf_Internal_Sym *) + malloc (max_sym_count * sizeof (Elf_Internal_Sym))); + finfo.indices = (long *) malloc (max_sym_count * sizeof (long)); + finfo.sections = (asection **) malloc (max_sym_count * sizeof (asection *)); + if ((finfo.contents == NULL && max_contents_size != 0) + || (finfo.external_relocs == NULL && max_external_reloc_size != 0) + || (finfo.internal_relocs == NULL && max_internal_reloc_count != 0) + || (finfo.external_syms == NULL && max_sym_count != 0) + || (finfo.internal_syms == NULL && max_sym_count != 0) + || (finfo.indices == NULL && max_sym_count != 0) + || (finfo.sections == NULL && max_sym_count != 0)) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } + + /* Since ELF permits relocations to be against local symbols, we + must have the local symbols available when we do the relocations. + Since we would rather only read the local symbols once, and we + would rather not keep them in memory, we handle all the + relocations for a single input file at the same time. + + Unfortunately, there is no way to know the total number of local + symbols until we have seen all of them, and the local symbol + indices precede the global symbol indices. This means that when + we are generating relocateable output, and we see a reloc against + a global symbol, we can not know the symbol index until we have + finished examining all the local symbols to see which ones we are + going to output. To deal with this, we keep the relocations in + memory, and don't output them until the end of the link. This is + an unfortunate waste of memory, but I don't see a good way around + it. Fortunately, it only happens when performing a relocateable + link, which is not the common case. FIXME: If keep_memory is set + we could write the relocs out and then read them again; I don't + know how bad the memory loss will be. */ + + for (sub = info->input_bfds; sub != NULL; sub = sub->next) + sub->output_has_begun = false; + 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_elf_flavour)) + { + sub = p->u.indirect.section->owner; + if (! sub->output_has_begun) + { + if (! elf_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 (! elf_reloc_link_order (abfd, info, o, p)) + goto error_return; + } + else + { + if (! _bfd_default_link_order (abfd, info, o, p)) + goto error_return; + } + } + } + + /* That wrote out all the local symbols. Finish up the symbol table + with the global symbols. */ + + /* The sh_info field records the index of the first non local + symbol. */ + symtab_hdr->sh_info = abfd->symcount; + if (dynobj != NULL) + elf_section_data (finfo.dynsym_sec->output_section)->this_hdr.sh_info = 1; + + /* We get the global symbols from the hash table. */ + elf_link_hash_traverse (elf_hash_table (info), elf_link_output_extsym, + (PTR) &finfo); + + /* Flush all symbols to the file. */ + if (! elf_link_flush_output_syms (&finfo)) + return false; + + /* Now we know the size of the symtab section. */ + off += symtab_hdr->sh_size; + + /* Finish up the symbol string table (.strtab) section. */ + symstrtab_hdr = &elf_tdata (abfd)->strtab_hdr; + /* sh_name was set in prep_headers. */ + symstrtab_hdr->sh_type = SHT_STRTAB; + symstrtab_hdr->sh_flags = 0; + symstrtab_hdr->sh_addr = 0; + symstrtab_hdr->sh_size = finfo.symstrtab->length; + symstrtab_hdr->sh_entsize = 0; + symstrtab_hdr->sh_link = 0; + symstrtab_hdr->sh_info = 0; + /* sh_offset is set just below. */ + symstrtab_hdr->sh_addralign = 1; + symstrtab_hdr->contents = (PTR) finfo.symstrtab->tab; + + off = assign_file_position_for_section (symstrtab_hdr, off, true); + elf_tdata (abfd)->next_file_pos = off; + + /* Adjust the relocs to have the correct symbol indices. */ + for (o = abfd->sections; o != NULL; o = o->next) + { + struct elf_link_hash_entry **rel_hash; + Elf_Internal_Shdr *rel_hdr; + + if ((o->flags & SEC_RELOC) == 0) + continue; + + rel_hash = elf_section_data (o)->rel_hashes; + rel_hdr = &elf_section_data (o)->rel_hdr; + for (i = 0; i < o->reloc_count; i++, rel_hash++) + { + if (*rel_hash == NULL) + continue; + + BFD_ASSERT ((*rel_hash)->indx >= 0); + + if (rel_hdr->sh_entsize == sizeof (Elf_External_Rel)) + { + Elf_External_Rel *erel; + Elf_Internal_Rel irel; + + erel = (Elf_External_Rel *) rel_hdr->contents + i; + elf_swap_reloc_in (abfd, erel, &irel); + irel.r_info = ELF_R_INFO ((*rel_hash)->indx, + ELF_R_TYPE (irel.r_info)); + elf_swap_reloc_out (abfd, &irel, erel); + } + else + { + Elf_External_Rela *erela; + Elf_Internal_Rela irela; + + BFD_ASSERT (rel_hdr->sh_entsize + == sizeof (Elf_External_Rela)); + + erela = (Elf_External_Rela *) rel_hdr->contents + i; + elf_swap_reloca_in (abfd, erela, &irela); + irela.r_info = ELF_R_INFO ((*rel_hash)->indx, + ELF_R_TYPE (irela.r_info)); + elf_swap_reloca_out (abfd, &irela, erela); + } + } + + /* Set the reloc_count field to 0 to prevent write_relocs from + trying to swap the relocs out itself. */ + o->reloc_count = 0; + } + + /* If we are linking against a dynamic object, finish up the dynamic + linking information. */ + if (dynobj != NULL) + { + Elf_External_Dyn *dyncon, *dynconend; + + /* Fix up .dynamic entries. */ + o = bfd_get_section_by_name (dynobj, ".dynamic"); + BFD_ASSERT (o != NULL); + + dyncon = (Elf_External_Dyn *) o->contents; + dynconend = (Elf_External_Dyn *) (o->contents + o->_raw_size); + for (; dyncon < dynconend; dyncon++) + { + Elf_Internal_Dyn dyn; + const char *name; + unsigned int type; + + elf_swap_dyn_in (dynobj, dyncon, &dyn); + + switch (dyn.d_tag) + { + default: + break; + + case DT_INIT: + name = ".init"; + goto get_vma; + case DT_FINI: + name = ".fini"; + goto get_vma; + case DT_HASH: + name = ".hash"; + goto get_vma; + case DT_STRTAB: + name = ".dynstr"; + goto get_vma; + case DT_SYMTAB: + name = ".dynsym"; + get_vma: + o = bfd_get_section_by_name (abfd, name); + BFD_ASSERT (o != NULL); + dyn.d_un.d_ptr = o->vma; + elf_swap_dyn_out (dynobj, &dyn, dyncon); + break; + + case DT_REL: + case DT_RELA: + case DT_RELSZ: + case DT_RELASZ: + if (dyn.d_tag == DT_REL || dyn.d_tag == DT_RELSZ) + type = SHT_REL; + else + type = SHT_RELA; + dyn.d_un.d_val = 0; + for (i = 1; i < elf_elfheader (abfd)->e_shnum; i++) + { + Elf_Internal_Shdr *hdr; + + hdr = elf_elfsections (abfd)[i]; + if (hdr->sh_type == type + && (hdr->sh_flags & SHF_ALLOC) != 0) + { + if (dyn.d_tag == DT_RELSZ || dyn.d_tag == DT_RELASZ) + dyn.d_un.d_val += hdr->sh_size; + else + { + if (dyn.d_un.d_val == 0 + || hdr->sh_addr < dyn.d_un.d_val) + dyn.d_un.d_val = hdr->sh_addr; + } + } + } + elf_swap_dyn_out (dynobj, &dyn, dyncon); + break; + } + } + + if (! (*bed->elf_backend_finish_dynamic_sections) (abfd, info)) + goto error_return; + + for (o = dynobj->sections; o != NULL; o = o->next) + { + if ((o->flags & SEC_HAS_CONTENTS) == 0) + continue; + if ((o->flags & SEC_IN_MEMORY) == 0) + { + BFD_ASSERT (info->shared); + continue; + } + if (! bfd_set_section_contents (abfd, o->output_section, + o->contents, o->output_offset, + o->_raw_size)) + goto error_return; + } + } + + /* Now backend stuff. */ + if (bed->elf_backend_final_write_processing) + (*bed->elf_backend_final_write_processing) (abfd, NULL); + + 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 (finfo.external_syms != NULL) + free (finfo.external_syms); + if (finfo.internal_syms != NULL) + free (finfo.internal_syms); + if (finfo.indices != NULL) + free (finfo.indices); + if (finfo.sections != NULL) + free (finfo.sections); + if (finfo.symbuf != NULL) + free (finfo.symbuf); + for (o = abfd->sections; o != NULL; o = o->next) + { + if ((o->flags & SEC_RELOC) != 0 + && elf_section_data (o)->rel_hashes != NULL) + free (elf_section_data (o)->rel_hashes); + } + + return true; + + error_return: + 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 (finfo.external_syms != NULL) + free (finfo.external_syms); + if (finfo.internal_syms != NULL) + free (finfo.internal_syms); + if (finfo.indices != NULL) + free (finfo.indices); + if (finfo.sections != NULL) + free (finfo.sections); + if (finfo.symbuf != NULL) + free (finfo.symbuf); + for (o = abfd->sections; o != NULL; o = o->next) + { + if ((o->flags & SEC_RELOC) != 0 + && elf_section_data (o)->rel_hashes != NULL) + free (elf_section_data (o)->rel_hashes); + } + + return false; +} + +/* Add a symbol to the output symbol table. */ + +static boolean +elf_link_output_sym (finfo, name, elfsym, input_sec) + struct elf_final_link_info *finfo; + const char *name; + Elf_Internal_Sym *elfsym; + asection *input_sec; +{ + boolean (*output_symbol_hook) PARAMS ((bfd *, + struct bfd_link_info *info, + const char *, + Elf_Internal_Sym *, + asection *)); + + output_symbol_hook = get_elf_backend_data (finfo->output_bfd)-> + elf_backend_link_output_symbol_hook; + if (output_symbol_hook != NULL) + { + if (! ((*output_symbol_hook) + (finfo->output_bfd, finfo->info, name, elfsym, input_sec))) + return false; + } + + if (name == (const char *) NULL || *name == '\0') + elfsym->st_name = 0; + else + { + elfsym->st_name = bfd_add_to_strtab (finfo->output_bfd, + finfo->symstrtab, name); + if (elfsym->st_name == (unsigned long) -1) + return false; + } + + if (finfo->symbuf_count >= finfo->symbuf_size) + { + if (! elf_link_flush_output_syms (finfo)) + return false; + } + + elf_swap_symbol_out (finfo->output_bfd, elfsym, + finfo->symbuf + finfo->symbuf_count); + ++finfo->symbuf_count; + + ++finfo->output_bfd->symcount; + + return true; +} + +/* Flush the output symbols to the file. */ + +static boolean +elf_link_flush_output_syms (finfo) + struct elf_final_link_info *finfo; +{ + Elf_Internal_Shdr *symtab; + + symtab = &elf_tdata (finfo->output_bfd)->symtab_hdr; + + if (bfd_seek (finfo->output_bfd, symtab->sh_offset + symtab->sh_size, + SEEK_SET) != 0 + || (bfd_write ((PTR) finfo->symbuf, finfo->symbuf_count, + sizeof (Elf_External_Sym), finfo->output_bfd) + != finfo->symbuf_count * sizeof (Elf_External_Sym))) + return false; + + symtab->sh_size += finfo->symbuf_count * sizeof (Elf_External_Sym); + + finfo->symbuf_count = 0; + + return true; +} + +/* Add an external symbol to the symbol table. This is called from + the hash table traversal routine. */ + +static boolean +elf_link_output_extsym (h, data) + struct elf_link_hash_entry *h; + PTR data; +{ + struct elf_final_link_info *finfo = (struct elf_final_link_info *) data; + boolean strip; + Elf_Internal_Sym sym; + asection *input_sec; + + /* We don't want to output symbols that have never been mentioned by + a regular file, or that we have been told to strip. However, if + h->indx is set to -2, the symbol is used by a reloc and we must + output it. */ + if (h->indx == -2) + strip = false; + else if (((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0 + || (h->elf_link_hash_flags & ELF_LINK_HASH_REF_DYNAMIC) != 0) + && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0 + && (h->elf_link_hash_flags & ELF_LINK_HASH_REF_REGULAR) == 0) + strip = true; + else if (finfo->info->strip == strip_all + || (finfo->info->strip == strip_some + && bfd_hash_lookup (finfo->info->keep_hash, + h->root.root.string, + false, false) == NULL)) + strip = true; + else + strip = false; + + /* If we're stripping it, and it's not a dynamic symbol, there's + nothing else to do. */ + if (strip && h->dynindx == -1) + return true; + + sym.st_value = 0; + sym.st_size = h->size; + sym.st_other = 0; + if (h->root.type == bfd_link_hash_weak + || (h->elf_link_hash_flags & ELF_LINK_HASH_DEFINED_WEAK) != 0) + sym.st_info = ELF_ST_INFO (STB_WEAK, h->type); + else + sym.st_info = ELF_ST_INFO (STB_GLOBAL, h->type); + + switch (h->root.type) + { + default: + case bfd_link_hash_new: + abort (); + return false; + + case bfd_link_hash_undefined: + input_sec = bfd_und_section_ptr; + sym.st_shndx = SHN_UNDEF; + break; + + case bfd_link_hash_weak: + input_sec = bfd_und_section_ptr; + sym.st_shndx = SHN_UNDEF; + break; + + case bfd_link_hash_defined: + { + + input_sec = h->root.u.def.section; + if (input_sec->output_section != NULL) + { + sym.st_shndx = elf_section_from_bfd_section (finfo->output_bfd, + input_sec->output_section); + if (sym.st_shndx == (unsigned short) -1) + { + /* FIXME: No way to handle errors. */ + abort (); + } + + /* ELF symbols in relocateable files are section relative, + but in nonrelocateable files they are virtual + addresses. */ + sym.st_value = h->root.u.def.value + input_sec->output_offset; + if (! finfo->info->relocateable) + sym.st_value += input_sec->output_section->vma; + } + else + { + BFD_ASSERT (bfd_get_flavour (input_sec->owner) + == bfd_target_elf_flavour + && elf_elfheader (input_sec->owner)->e_type == ET_DYN); + sym.st_shndx = SHN_UNDEF; + input_sec = bfd_und_section_ptr; + } + } + break; + + case bfd_link_hash_common: + input_sec = bfd_com_section_ptr; + sym.st_shndx = SHN_COMMON; + if (h->align == 0) + sym.st_value = 1; + else + sym.st_value = h->align; + break; + + case bfd_link_hash_indirect: + case bfd_link_hash_warning: + /* I have no idea how these should be handled. */ + return true; + } + + /* If this symbol should be put in the .dynsym section, then put it + there now. We have already know the symbol index. We also fill + in the entry in the .hash section. */ + if (h->dynindx != -1) + { + struct elf_backend_data *bed; + size_t bucketcount; + size_t bucket; + bfd_byte *bucketpos; + bfd_vma chain; + + sym.st_name = h->dynstr_index; + + /* Give the processor backend a chance to tweak the symbol + value, and also to finish up anything that needs to be done + for this symbol. */ + bed = get_elf_backend_data (finfo->output_bfd); + if (! ((*bed->elf_backend_finish_dynamic_symbol) + (finfo->output_bfd, finfo->info, h, &sym))) + { + /* FIXME: No way to return error. */ + abort (); + } + + elf_swap_symbol_out (finfo->output_bfd, &sym, + ((Elf_External_Sym *) finfo->dynsym_sec->contents + + h->dynindx)); + + bucketcount = elf_hash_table (finfo->info)->bucketcount; + bucket = bfd_elf_hash (h->root.root.string) % bucketcount; + bucketpos = ((bfd_byte *) finfo->hash_sec->contents + + (bucket + 2) * (ARCH_SIZE / 8)); + chain = get_word (finfo->output_bfd, bucketpos); + put_word (finfo->output_bfd, h->dynindx, bucketpos); + put_word (finfo->output_bfd, chain, + ((bfd_byte *) finfo->hash_sec->contents + + (bucketcount + 2 + h->dynindx) * (ARCH_SIZE / 8))); + } + + /* If we're stripping it, then it was just a dynamic symbol, and + there's nothing else to do. */ + if (strip) + return true; + + h->indx = finfo->output_bfd->symcount; + + if (! elf_link_output_sym (finfo, h->root.root.string, &sym, input_sec)) + { + /* FIXME: No way to return error. */ + abort (); + } + + return true; +} + +/* Link an input file into the linker output file. This function + handles all the sections and relocations of the input file at once. + This is so that we only have to read the local symbols once, and + don't have to keep them in memory. */ + +static boolean +elf_link_input_bfd (finfo, input_bfd) + struct elf_final_link_info *finfo; + bfd *input_bfd; +{ + boolean (*relocate_section) PARAMS ((bfd *, struct bfd_link_info *, + bfd *, asection *, bfd_byte *, + Elf_Internal_Rela *, + Elf_Internal_Sym *, + asection **, char *)); + bfd *output_bfd; + Elf_Internal_Shdr *symtab_hdr; + size_t locsymcount; + size_t extsymoff; + Elf_External_Sym *esym; + Elf_External_Sym *esymend; + Elf_Internal_Sym *isym; + long *pindex; + asection **ppsection; + asection *o; + + output_bfd = finfo->output_bfd; + relocate_section = + get_elf_backend_data (output_bfd)->elf_backend_relocate_section; + + /* If this is a dynamic object, we don't want to do anything here: + we don't want the local symbols, and we don't want the section + contents. */ + if (elf_elfheader (input_bfd)->e_type == ET_DYN) + return true; + + symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; + if (elf_bad_symtab (input_bfd)) + { + locsymcount = symtab_hdr->sh_size / sizeof (Elf_External_Sym); + extsymoff = 0; + } + else + { + locsymcount = symtab_hdr->sh_info; + extsymoff = symtab_hdr->sh_info; + } + + /* Read the local symbols. */ + if (locsymcount > 0 + && (bfd_seek (input_bfd, symtab_hdr->sh_offset, SEEK_SET) != 0 + || (bfd_read (finfo->external_syms, sizeof (Elf_External_Sym), + locsymcount, input_bfd) + != locsymcount * sizeof (Elf_External_Sym)))) + return false; + + /* Swap in the local symbols and write out the ones which we know + are going into the output file. */ + esym = finfo->external_syms; + esymend = esym + locsymcount; + isym = finfo->internal_syms; + pindex = finfo->indices; + ppsection = finfo->sections; + for (; esym < esymend; esym++, isym++, pindex++, ppsection++) + { + asection *isec; + const char *name; + bfd_vma oldval; + + elf_swap_symbol_in (input_bfd, esym, isym); + *pindex = -1; + + if (elf_bad_symtab (input_bfd)) + { + if (ELF_ST_BIND (isym->st_info) != STB_LOCAL) + { + *ppsection = NULL; + continue; + } + } + + if (isym->st_shndx == SHN_UNDEF) + isec = bfd_und_section_ptr; + else if (isym->st_shndx > 0 && isym->st_shndx < SHN_LORESERVE) + { + isec = section_from_elf_index (input_bfd, isym->st_shndx); + if (isec == NULL) + return false; + } + else if (isym->st_shndx == SHN_ABS) + isec = bfd_abs_section_ptr; + else if (isym->st_shndx == SHN_COMMON) + isec = bfd_com_section_ptr; + else + { + /* Who knows? */ + isec = NULL; + } + + *ppsection = isec; + + /* Don't output the first, undefined, symbol. */ + if (esym == finfo->external_syms) + continue; + + /* If we are stripping all symbols, we don't want to output this + one. */ + if (finfo->info->strip == strip_all) + continue; + + /* We never output section symbols. Instead, we use the section + symbol of the corresponding section in the output file. */ + if (ELF_ST_TYPE (isym->st_info) == STT_SECTION) + continue; + + /* If we are discarding all local symbols, we don't want to + output this one. If we are generating a relocateable output + file, then some of the local symbols may be required by + relocs; we output them below as we discover that they are + needed. */ + if (finfo->info->discard == discard_all) + continue; + + /* Get the name of the symbol. */ + name = elf_string_from_elf_section (input_bfd, symtab_hdr->sh_link, + isym->st_name); + if (name == NULL) + return false; + + /* See if we are discarding symbols with this name. */ + if ((finfo->info->strip == strip_some + && (bfd_hash_lookup (finfo->info->keep_hash, name, false, false) + == NULL)) + || (finfo->info->discard == discard_l + && strncmp (name, finfo->info->lprefix, + finfo->info->lprefix_len) == 0)) + continue; + + /* If we get here, we are going to output this symbol. */ + + /* Adjust the section index for the output file. */ + isym->st_shndx = elf_section_from_bfd_section (output_bfd, + isec->output_section); + if (isym->st_shndx == (unsigned short) -1) + return false; + + *pindex = output_bfd->symcount; + + /* ELF symbols in relocateable files are section relative, but + in executable files they are virtual addresses. Note that + this code assumes that all ELF sections have an associated + BFD section with a reasonable value for output_offset; below + we assume that they also have a reasonable value for + output_section. Any special sections must be set up to meet + these requirements. */ + oldval = isym->st_value; + isym->st_value += isec->output_offset; + if (! finfo->info->relocateable) + isym->st_value += isec->output_section->vma; + + if (! elf_link_output_sym (finfo, name, isym, isec)) + return false; + + /* Restore the old value for reloc handling. */ + isym->st_value = oldval; + } + + /* Relocate the contents of each section. */ + for (o = input_bfd->sections; o != NULL; o = o->next) + { + Elf_Internal_Shdr *input_rel_hdr; + + if ((o->flags & SEC_HAS_CONTENTS) == 0) + continue; + + if ((o->flags & SEC_IN_MEMORY) != 0 + && input_bfd == elf_hash_table (finfo->info)->dynobj) + { + /* Section was created by elf_link_create_dynamic_sections. + FIXME: This test is fragile. */ + continue; + } + + /* Read the contents of the section. */ + if (! bfd_get_section_contents (input_bfd, o, finfo->contents, + (file_ptr) 0, o->_raw_size)) + return false; + + if ((o->flags & SEC_RELOC) != 0) + { + PTR external_relocs; + + /* Get the external relocs. They may have been cached. */ + external_relocs = elf_section_data (o)->relocs; + if (external_relocs == NULL) + { + input_rel_hdr = &elf_section_data (o)->rel_hdr; + if ((bfd_seek (input_bfd, input_rel_hdr->sh_offset, SEEK_SET) + != 0) + || (bfd_read (finfo->external_relocs, 1, + input_rel_hdr->sh_size, input_bfd) + != input_rel_hdr->sh_size)) + return false; + external_relocs = finfo->external_relocs; + } + + /* Swap in the relocs. For convenience, we always produce + an Elf_Internal_Rela array; if the relocs are Rel, we set + the addend to 0. */ + if (input_rel_hdr->sh_entsize == sizeof (Elf_External_Rel)) + { + Elf_External_Rel *erel; + Elf_External_Rel *erelend; + Elf_Internal_Rela *irela; + + erel = (Elf_External_Rel *) external_relocs; + erelend = erel + o->reloc_count; + irela = finfo->internal_relocs; + for (; erel < erelend; erel++, irela++) + { + Elf_Internal_Rel irel; + + elf_swap_reloc_in (input_bfd, erel, &irel); + irela->r_offset = irel.r_offset; + irela->r_info = irel.r_info; + irela->r_addend = 0; + } + } + else + { + Elf_External_Rela *erela; + Elf_External_Rela *erelaend; + Elf_Internal_Rela *irela; + + BFD_ASSERT (input_rel_hdr->sh_entsize + == sizeof (Elf_External_Rela)); + + erela = (Elf_External_Rela *) external_relocs; + erelaend = erela + o->reloc_count; + irela = finfo->internal_relocs; + for (; erela < erelaend; erela++, irela++) + elf_swap_reloca_in (input_bfd, erela, irela); + } + + /* Relocate the section by invoking a back end routine. + + The back end routine is responsible for adjusting the + section contents as necessary, and (if using Rela relocs + and generating a relocateable output file) adjusting the + reloc addend as necessary. + + The back end routine does not have to worry about setting + the reloc address or the reloc symbol index. + + The back end routine is given a pointer to the swapped in + internal symbols, and can access the hash table entries + for the external symbols via elf_sym_hashes (input_bfd). + + When generating relocateable output, the back end routine + must handle STB_LOCAL/STT_SECTION symbols specially. The + output symbol is going to be a section symbol + corresponding to the output section, which will require + the addend to be adjusted. */ + + if (! (*relocate_section) (output_bfd, finfo->info, + input_bfd, o, + finfo->contents, + finfo->internal_relocs, + finfo->internal_syms, + finfo->sections, + finfo->symstrtab->tab)) + return false; + + if (finfo->info->relocateable) + { + Elf_Internal_Rela *irela; + Elf_Internal_Rela *irelaend; + struct elf_link_hash_entry **rel_hash; + Elf_Internal_Shdr *output_rel_hdr; + + /* Adjust the reloc addresses and symbol indices. */ + + irela = finfo->internal_relocs; + irelaend = irela + o->reloc_count; + rel_hash = (elf_section_data (o->output_section)->rel_hashes + + o->output_section->reloc_count); + for (; irela < irelaend; irela++, rel_hash++) + { + long r_symndx; + Elf_Internal_Sym *isym; + asection *sec; + + irela->r_offset += o->output_offset; + + r_symndx = ELF_R_SYM (irela->r_info); + + if (r_symndx == 0) + continue; + + if (r_symndx >= locsymcount + || (elf_bad_symtab (input_bfd) + && finfo->sections[r_symndx] == NULL)) + { + long indx; + + /* This is a reloc against a global symbol. We + have not yet output all the local symbols, so + we do not know the symbol index of any global + symbol. We set the rel_hash entry for this + reloc to point to the global hash table entry + for this symbol. The symbol index is then + set at the end of elf_bfd_final_link. */ + indx = r_symndx - extsymoff; + *rel_hash = elf_sym_hashes (input_bfd)[indx]; + + /* Setting the index to -2 tells + elf_link_output_extsym that this symbol is + used by a reloc. */ + BFD_ASSERT ((*rel_hash)->indx < 0); + (*rel_hash)->indx = -2; + + continue; + } + + /* This is a reloc against a local symbol. */ + + *rel_hash = NULL; + isym = finfo->internal_syms + r_symndx; + sec = finfo->sections[r_symndx]; + if (ELF_ST_TYPE (isym->st_info) == STT_SECTION) + { + /* I suppose the backend ought to fill in the + section of any STT_SECTION symbol against a + processor specific section. */ + if (sec != NULL && bfd_is_abs_section (sec)) + r_symndx = 0; + else if (sec == NULL || sec->owner == NULL) + { + bfd_set_error (bfd_error_bad_value); + return false; + } + else + { + r_symndx = sec->output_section->target_index; + if (r_symndx == 0) + abort (); + } + } + else + { + if (finfo->indices[r_symndx] == -1) + { + unsigned long link; + const char *name; + asection *osec; + + if (finfo->info->strip == strip_all) + { + /* You can't do ld -r -s. */ + bfd_set_error (bfd_error_invalid_operation); + return false; + } + + /* This symbol was skipped earlier, but + since it is needed by a reloc, we + must output it now. */ + link = symtab_hdr->sh_link; + name = elf_string_from_elf_section (input_bfd, + link, + isym->st_name); + if (name == NULL) + return false; + + osec = sec->output_section; + isym->st_shndx = + elf_section_from_bfd_section (output_bfd, + osec); + if (isym->st_shndx == (unsigned short) -1) + return false; + + isym->st_value += sec->output_offset; + if (! finfo->info->relocateable) + isym->st_value += osec->vma; + + finfo->indices[r_symndx] = output_bfd->symcount; + + if (! elf_link_output_sym (finfo, name, isym, sec)) + return false; + } + + r_symndx = finfo->indices[r_symndx]; + } + + irela->r_info = ELF_R_INFO (r_symndx, + ELF_R_TYPE (irela->r_info)); + } + + /* Swap out the relocs. */ + output_rel_hdr = &elf_section_data (o->output_section)->rel_hdr; + BFD_ASSERT (output_rel_hdr->sh_entsize + == input_rel_hdr->sh_entsize); + irela = finfo->internal_relocs; + irelaend = irela + o->reloc_count; + if (input_rel_hdr->sh_entsize == sizeof (Elf_External_Rel)) + { + Elf_External_Rel *erel; + + erel = ((Elf_External_Rel *) output_rel_hdr->contents + + o->output_section->reloc_count); + for (; irela < irelaend; irela++, erel++) + { + Elf_Internal_Rel irel; + + irel.r_offset = irela->r_offset; + irel.r_info = irela->r_info; + BFD_ASSERT (irela->r_addend == 0); + elf_swap_reloc_out (output_bfd, &irel, erel); + } + } + else + { + Elf_External_Rela *erela; + + BFD_ASSERT (input_rel_hdr->sh_entsize + == sizeof (Elf_External_Rela)); + erela = ((Elf_External_Rela *) output_rel_hdr->contents + + o->output_section->reloc_count); + for (; irela < irelaend; irela++, erela++) + elf_swap_reloca_out (output_bfd, irela, erela); + } + + 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; + } + + return true; +} + +/* Generate a reloc when linking an ELF file. This is a reloc + requested by the linker, and does come from any input file. This + is used to build constructor and destructor tables when linking + with -Ur. */ + +static boolean +elf_reloc_link_order (output_bfd, info, output_section, link_order) + bfd *output_bfd; + struct bfd_link_info *info; + asection *output_section; + struct bfd_link_order *link_order; +{ + const reloc_howto_type *howto; + long indx; + bfd_vma offset; + struct elf_link_hash_entry **rel_hash_ptr; + Elf_Internal_Shdr *rel_hdr; + + 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 this is an inplace reloc, we must write the addend into the + object file. */ + if (howto->partial_inplace + && link_order->u.reloc.p->addend != 0) + { + bfd_size_type size; + bfd_reloc_status_type rstat; + bfd_byte *buf; + boolean ok; + + size = bfd_get_reloc_size (howto); + buf = (bfd_byte *) bfd_zmalloc (size); + if (buf == (bfd_byte *) 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 (! ((*info->callbacks->reloc_overflow) + (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; + } + + /* Figure out the symbol index. */ + rel_hash_ptr = (elf_section_data (output_section)->rel_hashes + + output_section->reloc_count); + if (link_order->type == bfd_section_reloc_link_order) + { + indx = link_order->u.reloc.p->u.section->target_index; + if (indx == 0) + abort (); + *rel_hash_ptr = NULL; + } + else + { + struct elf_link_hash_entry *h; + + h = elf_link_hash_lookup (elf_hash_table (info), + link_order->u.reloc.p->u.name, + false, false, true); + if (h != NULL) + { + /* Setting the index to -2 tells elf_link_output_extsym that + this symbol is used by a reloc. */ + h->indx = -2; + *rel_hash_ptr = h; + indx = 0; + } + else + { + if (! ((*info->callbacks->unattached_reloc) + (info, link_order->u.reloc.p->u.name, (bfd *) NULL, + (asection *) NULL, (bfd_vma) 0))) + return false; + indx = 0; + } + } + + /* The address of a reloc is relative to the section in a + relocateable file, and is a virtual address in an executable + file. */ + offset = link_order->offset; + if (! info->relocateable) + offset += output_section->vma; + + rel_hdr = &elf_section_data (output_section)->rel_hdr; + + if (rel_hdr->sh_type == SHT_REL) + { + Elf_Internal_Rel irel; + Elf_External_Rel *erel; + + irel.r_offset = offset; + irel.r_info = ELF_R_INFO (indx, howto->type); + erel = ((Elf_External_Rel *) rel_hdr->contents + + output_section->reloc_count); + elf_swap_reloc_out (output_bfd, &irel, erel); + } + else + { + Elf_Internal_Rela irela; + Elf_External_Rela *erela; + + irela.r_offset = offset; + irela.r_info = ELF_R_INFO (indx, howto->type); + irela.r_addend = link_order->u.reloc.p->addend; + erela = ((Elf_External_Rela *) rel_hdr->contents + + output_section->reloc_count); + elf_swap_reloca_out (output_bfd, &irela, erela); + } + + ++output_section->reloc_count; + + return true; +} diff --git a/gnu/usr.bin/gdb/bfd/genlink.h b/gnu/usr.bin/gdb/bfd/genlink.h new file mode 100644 index 000000000000..06f6bfc629c2 --- /dev/null +++ b/gnu/usr.bin/gdb/bfd/genlink.h @@ -0,0 +1,106 @@ +/* genlink.h -- interface to the BFD generic linker + Copyright 1993, 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. */ + +#ifndef GENLINK_H +#define GENLINK_H + +/* This header file is internal to BFD. It describes the internal + structures and functions used by the BFD generic linker, in case + any of the more specific linkers want to use or call them. Note + that some functions, such as _bfd_generic_link_hash_table_create, + are declared in libbfd.h, because they are expected to be widely + used. The functions and structures in this file will probably only + be used by a few files besides linker.c itself. In fact, this file + is not particularly complete; I have only put in the interfaces I + actually needed. */ + +/* The generic linker uses a hash table which is a derived class of + the standard linker hash table, just as the other backend specific + linkers do. Do not confuse the generic linker hash table with the + standard BFD linker hash table it is built upon. */ + +/* Generic linker hash table entries. */ + +struct generic_link_hash_entry +{ + struct bfd_link_hash_entry root; + /* Whether this symbol has been written out. */ + boolean written; + /* Symbol from input BFD. */ + asymbol *sym; +}; + +/* Generic linker hash table. */ + +struct generic_link_hash_table +{ + struct bfd_link_hash_table root; +}; + +/* Look up an entry in an generic link hash table. */ + +#define _bfd_generic_link_hash_lookup(table, string, create, copy, follow) \ + ((struct generic_link_hash_entry *) \ + bfd_link_hash_lookup (&(table)->root, (string), (create), (copy), (follow))) + +/* Traverse an generic link hash table. */ + +#define _bfd_generic_link_hash_traverse(table, func, info) \ + (bfd_link_hash_traverse \ + (&(table)->root, \ + (boolean (*) PARAMS ((struct bfd_link_hash_entry *, PTR))) (func), \ + (info))) + +/* Get the generic link hash table from the info structure. This is + just a cast. */ + +#define _bfd_generic_hash_table(p) \ + ((struct generic_link_hash_table *) ((p)->hash)) + +/* The generic linker reads in the asymbol structures for an input BFD + and keeps them in the outsymbol and symcount fields. */ + +#define _bfd_generic_link_get_symbols(abfd) ((abfd)->outsymbols) +#define _bfd_generic_link_get_symcount(abfd) ((abfd)->symcount) + +/* Add the symbols of input_bfd to the symbols being built for + output_bfd. */ +extern boolean _bfd_generic_link_output_symbols + PARAMS ((bfd *output_bfd, bfd *input_bfd, struct bfd_link_info *, + size_t *psymalloc)); + +/* This structure is used to pass information to + _bfd_generic_link_write_global_symbol, which may be called via + _bfd_generic_link_hash_traverse. */ + +struct generic_write_global_symbol_info +{ + struct bfd_link_info *info; + bfd *output_bfd; + size_t *psymalloc; +}; + +/* Write out a single global symbol. This is expected to be called + via _bfd_generic_link_hash_traverse. The second argument must + actually be a struct generic_write_global_symbol_info *. */ +extern boolean _bfd_generic_link_write_global_symbol + PARAMS ((struct generic_link_hash_entry *, PTR)); + +#endif diff --git a/gnu/usr.bin/gdb/bfd/hash.c b/gnu/usr.bin/gdb/bfd/hash.c new file mode 100644 index 000000000000..cb11c2380e41 --- /dev/null +++ b/gnu/usr.bin/gdb/bfd/hash.c @@ -0,0 +1,470 @@ +/* hash.c -- hash table routines for BFD + Copyright (C) 1993, 94 Free Software Foundation, Inc. + Written by Steve Chamberlain <sac@cygnus.com> + +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. */ + +#include "bfd.h" +#include "sysdep.h" +#include "libbfd.h" +#include "obstack.h" + +/* +SECTION + Hash Tables + +@cindex Hash tables + BFD provides a simple set of hash table functions. Routines + are provided to initialize a hash table, to free a hash table, + to look up a string in a hash table and optionally create an + entry for it, and to traverse a hash table. There is + currently no routine to delete an string from a hash table. + + The basic hash table does not permit any data to be stored + with a string. However, a hash table is designed to present a + base class from which other types of hash tables may be + derived. These derived types may store additional information + with the string. Hash tables were implemented in this way, + rather than simply providing a data pointer in a hash table + entry, because they were designed for use by the linker back + ends. The linker may create thousands of hash table entries, + and the overhead of allocating private data and storing and + following pointers becomes noticeable. + + The basic hash table code is in <<hash.c>>. + +@menu +@* Creating and Freeing a Hash Table:: +@* Looking Up or Entering a String:: +@* Traversing a Hash Table:: +@* Deriving a New Hash Table Type:: +@end menu + +INODE +Creating and Freeing a Hash Table, Looking Up or Entering a String, Hash Tables, Hash Tables +SUBSECTION + Creating and freeing a hash table + +@findex bfd_hash_table_init +@findex bfd_hash_table_init_n + To create a hash table, create an instance of a <<struct + bfd_hash_table>> (defined in <<bfd.h>>) and call + <<bfd_hash_table_init>> (if you know approximately how many + entries you will need, the function <<bfd_hash_table_init_n>>, + which takes a @var{size} argument, may be used). + <<bfd_hash_table_init>> returns <<false>> if some sort of + error occurs. + +@findex bfd_hash_newfunc + The function <<bfd_hash_table_init>> take as an argument a + function to use to create new entries. For a basic hash + table, use the function <<bfd_hash_newfunc>>. @xref{Deriving + a New Hash Table Type} for why you would want to use a + different value for this argument. + +@findex bfd_hash_allocate + <<bfd_hash_table_init>> will create an obstack which will be + used to allocate new entries. You may allocate memory on this + obstack using <<bfd_hash_allocate>>. + +@findex bfd_hash_table_free + Use <<bfd_hash_table_free>> to free up all the memory that has + been allocated for a hash table. This will not free up the + <<struct bfd_hash_table>> itself, which you must provide. + +INODE +Looking Up or Entering a String, Traversing a Hash Table, Creating and Freeing a Hash Table, Hash Tables +SUBSECTION + Looking up or entering a string + +@findex bfd_hash_lookup + The function <<bfd_hash_lookup>> is used both to look up a + string in the hash table and to create a new entry. + + If the @var{create} argument is <<false>>, <<bfd_hash_lookup>> + will look up a string. If the string is found, it will + returns a pointer to a <<struct bfd_hash_entry>>. If the + string is not found in the table <<bfd_hash_lookup>> will + return <<NULL>>. You should not modify any of the fields in + the returns <<struct bfd_hash_entry>>. + + If the @var{create} argument is <<true>>, the string will be + entered into the hash table if it is not already there. + Either way a pointer to a <<struct bfd_hash_entry>> will be + returned, either to the existing structure or to a newly + created one. In this case, a <<NULL>> return means that an + error occurred. + + If the @var{create} argument is <<true>>, and a new entry is + created, the @var{copy} argument is used to decide whether to + copy the string onto the hash table obstack or not. If + @var{copy} is passed as <<false>>, you must be careful not to + deallocate or modify the string as long as the hash table + exists. + +INODE +Traversing a Hash Table, Deriving a New Hash Table Type, Looking Up or Entering a String, Hash Tables +SUBSECTION + Traversing a hash table + +@findex bfd_hash_traverse + The function <<bfd_hash_traverse>> may be used to traverse a + hash table, calling a function on each element. The traversal + is done in a random order. + + <<bfd_hash_traverse>> takes as arguments a function and a + generic <<void *>> pointer. The function is called with a + hash table entry (a <<struct bfd_hash_entry *>>) and the + generic pointer passed to <<bfd_hash_traverse>>. The function + must return a <<boolean>> value, which indicates whether to + continue traversing the hash table. If the function returns + <<false>>, <<bfd_hash_traverse>> will stop the traversal and + return immediately. + +INODE +Deriving a New Hash Table Type, , Traversing a Hash Table, Hash Tables +SUBSECTION + Deriving a new hash table type + + Many uses of hash tables want to store additional information + which each entry in the hash table. Some also find it + convenient to store additional information with the hash table + itself. This may be done using a derived hash table. + + Since C is not an object oriented language, creating a derived + hash table requires sticking together some boilerplate + routines with a few differences specific to the type of hash + table you want to create. + + An example of a derived hash table is the linker hash table. + The structures for this are defined in <<bfdlink.h>>. The + functions are in <<linker.c>>. + + You may also derive a hash table from an already derived hash + table. For example, the a.out linker backend code uses a hash + table derived from the linker hash table. + +@menu +@* Define the Derived Structures:: +@* Write the Derived Creation Routine:: +@* Write Other Derived Routines:: +@end menu + +INODE +Define the Derived Structures, Write the Derived Creation Routine, Deriving a New Hash Table Type, Deriving a New Hash Table Type +SUBSUBSECTION + Define the derived structures + + You must define a structure for an entry in the hash table, + and a structure for the hash table itself. + + The first field in the structure for an entry in the hash + table must be of the type used for an entry in the hash table + you are deriving from. If you are deriving from a basic hash + table this is <<struct bfd_hash_entry>>, which is defined in + <<bfd.h>>. The first field in the structure for the hash + table itself must be of the type of the hash table you are + deriving from itself. If you are deriving from a basic hash + table, this is <<struct bfd_hash_table>>. + + For example, the linker hash table defines <<struct + bfd_link_hash_entry>> (in <<bfdlink.h>>). The first field, + <<root>>, is of type <<struct bfd_hash_entry>>. Similarly, + the first field in <<struct bfd_link_hash_table>>, <<table>>, + is of type <<struct bfd_hash_table>>. + +INODE +Write the Derived Creation Routine, Write Other Derived Routines, Define the Derived Structures, Deriving a New Hash Table Type +SUBSUBSECTION + Write the derived creation routine + + You must write a routine which will create and initialize an + entry in the hash table. This routine is passed as the + function argument to <<bfd_hash_table_init>>. + + In order to permit other hash tables to be derived from the + hash table you are creating, this routine must be written in a + standard way. + + The first argument to the creation routine is a pointer to a + hash table entry. This may be <<NULL>>, in which case the + routine should allocate the right amount of space. Otherwise + the space has already been allocated by a hash table type + derived from this one. + + After allocating space, the creation routine must call the + creation routine of the hash table type it is derived from, + passing in a pointer to the space it just allocated. This + will initialize any fields used by the base hash table. + + Finally the creation routine must initialize any local fields + for the new hash table type. + + Here is a boilerplate example of a creation routine. + @var{function_name} is the name of the routine. + @var{entry_type} is the type of an entry in the hash table you + are creating. @var{base_newfunc} is the name of the creation + routine of the hash table type your hash table is derived + from. + +EXAMPLE + +.struct bfd_hash_entry * +.@var{function_name} (entry, table, string) +. struct bfd_hash_entry *entry; +. struct bfd_hash_table *table; +. const char *string; +.{ +. struct @var{entry_type} *ret = (@var{entry_type} *) entry; +. +. {* Allocate the structure if it has not already been allocated by a +. derived class. *} +. if (ret == (@var{entry_type} *) NULL) +. { +. ret = ((@var{entry_type} *) +. bfd_hash_allocate (table, sizeof (@var{entry_type}))); +. if (ret == (@var{entry_type} *) NULL) +. return NULL; +. } +. +. {* Call the allocation method of the base class. *} +. ret = ((@var{entry_type} *) +. @var{base_newfunc} ((struct bfd_hash_entry *) ret, table, string)); +. +. {* Initialize the local fields here. *} +. +. return (struct bfd_hash_entry *) ret; +.} + +DESCRIPTION + The creation routine for the linker hash table, which is in + <<linker.c>>, looks just like this example. + @var{function_name} is <<_bfd_link_hash_newfunc>>. + @var{entry_type} is <<struct bfd_link_hash_entry>>. + @var{base_newfunc} is <<bfd_hash_newfunc>>, the creation + routine for a basic hash table. + + <<_bfd_link_hash_newfunc>> also initializes the local fields + in a linker hash table entry: <<type>>, <<written>> and + <<next>>. + +INODE +Write Other Derived Routines, , Write the Derived Creation Routine, Deriving a New Hash Table Type +SUBSUBSECTION + Write other derived routines + + You will want to write other routines for your new hash table, + as well. + + You will want an initialization routine which calls the + initialization routine of the hash table you are deriving from + and initializes any other local fields. For the linker hash + table, this is <<_bfd_link_hash_table_init>> in <<linker.c>>. + + You will want a lookup routine which calls the lookup routine + of the hash table you are deriving from and casts the result. + The linker hash table uses <<bfd_link_hash_lookup>> in + <<linker.c>> (this actually takes an additional argument which + it uses to decide how to return the looked up value). + + You may want a traversal routine. This should just call the + traversal routine of the hash table you are deriving from with + appropriate casts. The linker hash table uses + <<bfd_link_hash_traverse>> in <<linker.c>>. + + These routines may simply be defined as macros. For example, + the a.out backend linker hash table, which is derived from the + linker hash table, uses macros for the lookup and traversal + routines. These are <<aout_link_hash_lookup>> and + <<aout_link_hash_traverse>> in aoutx.h. +*/ + +/* Obstack allocation and deallocation routines. */ +#define obstack_chunk_alloc malloc +#define obstack_chunk_free free + +/* The default number of entries to use when creating a hash table. */ +#define DEFAULT_SIZE (4051) + +/* Create a new hash table, given a number of entries. */ + +boolean +bfd_hash_table_init_n (table, newfunc, size) + struct bfd_hash_table *table; + struct bfd_hash_entry *(*newfunc) PARAMS ((struct bfd_hash_entry *, + struct bfd_hash_table *, + const char *)); + unsigned int size; +{ + unsigned int alloc; + + alloc = size * sizeof (struct bfd_hash_entry *); + if (!obstack_begin (&table->memory, alloc)) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + table->table = ((struct bfd_hash_entry **) + obstack_alloc (&table->memory, alloc)); + if (!table->table) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + memset ((PTR) table->table, 0, alloc); + table->size = size; + table->newfunc = newfunc; + return true; +} + +/* Create a new hash table with the default number of entries. */ + +boolean +bfd_hash_table_init (table, newfunc) + struct bfd_hash_table *table; + struct bfd_hash_entry *(*newfunc) PARAMS ((struct bfd_hash_entry *, + struct bfd_hash_table *, + const char *)); +{ + return bfd_hash_table_init_n (table, newfunc, DEFAULT_SIZE); +} + +/* Free a hash table. */ + +void +bfd_hash_table_free (table) + struct bfd_hash_table *table; +{ + obstack_free (&table->memory, (PTR) NULL); +} + +/* Look up a string in a hash table. */ + +struct bfd_hash_entry * +bfd_hash_lookup (table, string, create, copy) + struct bfd_hash_table *table; + const char *string; + boolean create; + boolean copy; +{ + register const unsigned char *s; + register unsigned long hash; + register unsigned int c; + struct bfd_hash_entry *hashp; + unsigned int len; + unsigned int index; + + hash = 0; + len = 0; + s = (const unsigned char *) string; + while ((c = *s++) != '\0') + { + hash += c + (c << 17); + hash ^= hash >> 2; + ++len; + } + hash += len + (len << 17); + hash ^= hash >> 2; + + index = hash % table->size; + for (hashp = table->table[index]; + hashp != (struct bfd_hash_entry *) NULL; + hashp = hashp->next) + { + if (hashp->hash == hash + && strcmp (hashp->string, string) == 0) + return hashp; + } + + if (! create) + return (struct bfd_hash_entry *) NULL; + + hashp = (*table->newfunc) ((struct bfd_hash_entry *) NULL, table, string); + if (hashp == (struct bfd_hash_entry *) NULL) + return (struct bfd_hash_entry *) NULL; + if (copy) + { + char *new; + + new = (char *) obstack_alloc (&table->memory, len + 1); + if (!new) + { + bfd_set_error (bfd_error_no_memory); + return (struct bfd_hash_entry *) NULL; + } + strcpy (new, string); + string = new; + } + hashp->string = string; + hashp->hash = hash; + hashp->next = table->table[index]; + table->table[index] = hashp; + + return hashp; +} + +/* Base method for creating a new hash table entry. */ + +/*ARGSUSED*/ +struct bfd_hash_entry * +bfd_hash_newfunc (entry, table, string) + struct bfd_hash_entry *entry; + struct bfd_hash_table *table; + const char *string; +{ + if (entry == (struct bfd_hash_entry *) NULL) + entry = ((struct bfd_hash_entry *) + bfd_hash_allocate (table, sizeof (struct bfd_hash_entry))); + return entry; +} + +/* Allocate space in a hash table. */ + +PTR +bfd_hash_allocate (table, size) + struct bfd_hash_table *table; + unsigned int size; +{ + PTR ret; + + ret = obstack_alloc (&table->memory, size); + if (ret == NULL && size != 0) + bfd_set_error (bfd_error_no_memory); + return ret; +} + +/* Traverse a hash table. */ + +void +bfd_hash_traverse (table, func, info) + struct bfd_hash_table *table; + boolean (*func) PARAMS ((struct bfd_hash_entry *, PTR)); + PTR info; +{ + unsigned int i; + + for (i = 0; i < table->size; i++) + { + struct bfd_hash_entry *p; + + for (p = table->table[i]; p != NULL; p = p->next) + { + if (! (*func) (p, info)) + return; + } + } +} diff --git a/gnu/usr.bin/gdb/bfd/i386aout.c b/gnu/usr.bin/gdb/bfd/i386aout.c new file mode 100644 index 000000000000..0771fb0e2450 --- /dev/null +++ b/gnu/usr.bin/gdb/bfd/i386aout.c @@ -0,0 +1,68 @@ +/* BFD back-end for i386 a.out binaries. + Copyright 1990, 1991, 1992 Free Software Foundation, Inc. + +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. */ + + +/* The only 386 aout system we have here is GO32 from DJ. + These numbers make BFD work with that. If your aout 386 system + doesn't work with these, we'll have to split them into different + files. Send me (sac@cygnus.com) the runes to make it work on your + system, and I'll stick it in for the next release. */ + +#define N_HEADER_IN_TEXT(x) 0 +#define BYTES_IN_WORD 4 + +#define N_TXTOFF(x) 0x20 +#define N_TXTADDR(x) (N_MAGIC(x)==ZMAGIC ? 0x1020 : 0) + +#define N_TXTSIZE(x) ((x).a_text) +#if 0 +#define N_DATADDR(x) (N_MAGIC(x)==OMAGIC? (N_TXTADDR(x)+(x).a_text) : (SEGMENT_SIZE + ((0x1020+(x).a_text-1) & ~(SEGMENT_SIZE-1)))) +#define NOSUBEXECB + +#endif +#define PAGE_SIZE 4096 +#define SEGMENT_SIZE 0x400000 +#define DEFAULT_ARCH bfd_arch_i386 + +#define MY(OP) CAT(i386aout_,OP) +#define TARGETNAME "a.out-i386" +#define NO_WRITE_HEADER_KLUDGE 1 + +#include "bfd.h" +#include "sysdep.h" +#include "libbfd.h" +#include "libaout.h" +static boolean MY(set_sizes)(); +#define MY_backend_data &MY(backend_data) +static CONST struct aout_backend_data MY(backend_data) = { + 0, /* zmagic contiguous */ + 1, /* text incl header */ + 0, /* exec_hdr_flags */ + 0, /* text vma? */ + MY(set_sizes), + 1, /* exec header not counted */ + 0, /* add_dynamic_symbols */ + 0, /* add_one_symbol */ + 0, /* link_dynamic_object */ + 0, /* write_dynamic_symbol */ + 0, /* check_dynamic_reloc */ + 0 /* finish_dynamic_link */ +}; + +#include "aout-target.h" diff --git a/gnu/usr.bin/gdb/bfd/linker.c b/gnu/usr.bin/gdb/bfd/linker.c new file mode 100644 index 000000000000..d8275b5a0b57 --- /dev/null +++ b/gnu/usr.bin/gdb/bfd/linker.c @@ -0,0 +1,2442 @@ +/* linker.c -- BFD linker routines + Copyright (C) 1993, 94 Free Software Foundation, Inc. + Written by Steve Chamberlain and 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. */ + +#include "bfd.h" +#include "sysdep.h" +#include "libbfd.h" +#include "bfdlink.h" +#include "genlink.h" + +/* +SECTION + Linker Functions + +@cindex Linker + The linker uses three special entry points in the BFD target + vector. It is not necessary to write special routines for + these entry points when creating a new BFD back end, since + generic versions are provided. However, writing them can + speed up linking and make it use significantly less runtime + memory. + + The first routine creates a hash table used by the other + routines. The second routine adds the symbols from an object + file to the hash table. The third routine takes all the + object files and links them together to create the output + file. These routines are designed so that the linker proper + does not need to know anything about the symbols in the object + files that it is linking. The linker merely arranges the + sections as directed by the linker script and lets BFD handle + the details of symbols and relocs. + + The second routine and third routines are passed a pointer to + a <<struct bfd_link_info>> structure (defined in + <<bfdlink.h>>) which holds information relevant to the link, + including the linker hash table (which was created by the + first routine) and a set of callback functions to the linker + proper. + + The generic linker routines are in <<linker.c>>, and use the + header file <<genlink.h>>. As of this writing, the only back + ends which have implemented versions of these routines are + a.out (in <<aoutx.h>>) and ECOFF (in <<ecoff.c>>). The a.out + routines are used as examples throughout this section. + +@menu +@* Creating a Linker Hash Table:: +@* Adding Symbols to the Hash Table:: +@* Performing the Final Link:: +@end menu + +INODE +Creating a Linker Hash Table, Adding Symbols to the Hash Table, Linker Functions, Linker Functions +SUBSECTION + Creating a linker hash table + +@cindex _bfd_link_hash_table_create in target vector +@cindex target vector (_bfd_link_hash_table_create) + The linker routines must create a hash table, which must be + derived from <<struct bfd_link_hash_table>> described in + <<bfdlink.c>>. @xref{Hash Tables} for information on how to + create a derived hash table. This entry point is called using + the target vector of the linker output file. + + The <<_bfd_link_hash_table_create>> entry point must allocate + and initialize an instance of the desired hash table. If the + back end does not require any additional information to be + stored with the entries in the hash table, the entry point may + simply create a <<struct bfd_link_hash_table>>. Most likely, + however, some additional information will be needed. + + For example, with each entry in the hash table the a.out + linker keeps the index the symbol has in the final output file + (this index number is used so that when doing a relocateable + link the symbol index used in the output file can be quickly + filled in when copying over a reloc). The a.out linker code + defines the required structures and functions for a hash table + derived from <<struct bfd_link_hash_table>>. The a.out linker + hash table is created by the function + <<NAME(aout,link_hash_table_create)>>; it simply allocates + space for the hash table, initializes it, and returns a + pointer to it. + + When writing the linker routines for a new back end, you will + generally not know exactly which fields will be required until + you have finished. You should simply create a new hash table + which defines no additional fields, and then simply add fields + as they become necessary. + +INODE +Adding Symbols to the Hash Table, Performing the Final Link, Creating a Linker Hash Table, Linker Functions +SUBSECTION + Adding symbols to the hash table + +@cindex _bfd_link_add_symbols in target vector +@cindex target vector (_bfd_link_add_symbols) + The linker proper will call the <<_bfd_link_add_symbols>> + entry point for each object file or archive which is to be + linked (typically these are the files named on the command + line, but some may also come from the linker script). The + entry point is responsible for examining the file. For an + object file, BFD must add any relevant symbol information to + the hash table. For an archive, BFD must determine which + elements of the archive should be used and adding them to the + link. + + The a.out version of this entry point is + <<NAME(aout,link_add_symbols)>>. + +@menu +@* Differing file formats:: +@* Adding symbols from an object file:: +@* Adding symbols from an archive:: +@end menu + +INODE +Differing file formats, Adding symbols from an object file, Adding Symbols to the Hash Table, Adding Symbols to the Hash Table +SUBSUBSECTION + Differing file formats + + Normally all the files involved in a link will be of the same + format, but it is also possible to link together different + format object files, and the back end must support that. The + <<_bfd_link_add_symbols>> entry point is called via the target + vector of the file to be added. This has an important + consequence: the function may not assume that the hash table + is the type created by the corresponding + <<_bfd_link_hash_table_create>> vector. All the + <<_bfd_link_add_symbols>> function can assume about the hash + table is that it is derived from <<struct + bfd_link_hash_table>>. + + Sometimes the <<_bfd_link_add_symbols>> function must store + some information in the hash table entry to be used by the + <<_bfd_final_link>> function. In such a case the <<creator>> + field of the hash table must be checked to make sure that the + hash table was created by an object file of the same format. + + The <<_bfd_final_link>> routine must be prepared to handle a + hash entry without any extra information added by the + <<_bfd_link_add_symbols>> function. A hash entry without + extra information will also occur when the linker script + directs the linker to create a symbol. Note that, regardless + of how a hash table entry is added, all the fields will be + initialized to some sort of null value by the hash table entry + initialization function. + + See <<ecoff_link_add_externals>> for an example of how to + check the <<creator>> field before saving information (in this + case, the ECOFF external symbol debugging information) in a + hash table entry. + +INODE +Adding symbols from an object file, Adding symbols from an archive, Differing file formats, Adding Symbols to the Hash Table +SUBSUBSECTION + Adding symbols from an object file + + When the <<_bfd_link_add_symbols>> routine is passed an object + file, it must add all externally visible symbols in that + object file to the hash table. The actual work of adding the + symbol to the hash table is normally handled by the function + <<_bfd_generic_link_add_one_symbol>>. The + <<_bfd_link_add_symbols>> routine is responsible for reading + all the symbols from the object file and passing the correct + information to <<_bfd_generic_link_add_one_symbol>>. + + The <<_bfd_link_add_symbols>> routine should not use + <<bfd_canonicalize_symtab>> to read the symbols. The point of + providing this routine is to avoid the overhead of converting + the symbols into generic <<asymbol>> structures. + +@findex _bfd_generic_link_add_one_symbol + <<_bfd_generic_link_add_one_symbol>> handles the details of + combining common symbols, warning about multiple definitions, + and so forth. It takes arguments which describe the symbol to + add, notably symbol flags, a section, and an offset. The + symbol flags include such things as <<BSF_WEAK>> or + <<BSF_INDIRECT>>. The section is a section in the object + file, or something like <<bfd_und_section_ptr>> for an undefined + symbol or <<bfd_com_section_ptr>> for a common symbol. + + If the <<_bfd_final_link>> routine is also going to need to + read the symbol information, the <<_bfd_link_add_symbols>> + routine should save it somewhere attached to the object file + BFD. However, the information should only be saved if the + <<keep_memory>> field of the <<info>> argument is true, so + that the <<-no-keep-memory>> linker switch is effective. + + The a.out function which adds symbols from an object file is + <<aout_link_add_object_symbols>>, and most of the interesting + work is in <<aout_link_add_symbols>>. The latter saves + pointers to the hash tables entries created by + <<_bfd_generic_link_add_one_symbol>> indexed by symbol number, + so that the <<_bfd_final_link>> routine does not have to call + the hash table lookup routine to locate the entry. + +INODE +Adding symbols from an archive, , Adding symbols from an object file, Adding Symbols to the Hash Table +SUBSUBSECTION + Adding symbols from an archive + + When the <<_bfd_link_add_symbols>> routine is passed an + archive, it must look through the symbols defined by the + archive and decide which elements of the archive should be + included in the link. For each such element it must call the + <<add_archive_element>> linker callback, and it must add the + symbols from the object file to the linker hash table. + +@findex _bfd_generic_link_add_archive_symbols + In most cases the work of looking through the symbols in the + archive should be done by the + <<_bfd_generic_link_add_archive_symbols>> function. This + function builds a hash table from the archive symbol table and + looks through the list of undefined symbols to see which + elements should be included. + <<_bfd_generic_link_add_archive_symbols>> is passed a function + to call to make the final decision about adding an archive + element to the link and to do the actual work of adding the + symbols to the linker hash table. + + The function passed to + <<_bfd_generic_link_add_archive_symbols>> must read the + symbols of the archive element and decide whether the archive + element should be included in the link. If the element is to + be included, the <<add_archive_element>> linker callback + routine must be called with the element as an argument, and + the elements symbols must be added to the linker hash table + just as though the element had itself been passed to the + <<_bfd_link_add_symbols>> function. + + When the a.out <<_bfd_link_add_symbols>> function receives an + archive, it calls <<_bfd_generic_link_add_archive_symbols>> + passing <<aout_link_check_archive_element>> as the function + argument. <<aout_link_check_archive_element>> calls + <<aout_link_check_ar_symbols>>. If the latter decides to add + the element (an element is only added if it provides a real, + non-common, definition for a previously undefined or common + symbol) it calls the <<add_archive_element>> callback and then + <<aout_link_check_archive_element>> calls + <<aout_link_add_symbols>> to actually add the symbols to the + linker hash table. + + The ECOFF back end is unusual in that it does not normally + call <<_bfd_generic_link_add_archive_symbols>>, because ECOFF + archives already contain a hash table of symbols. The ECOFF + back end searches the archive itself to avoid the overhead of + creating a new hash table. + +INODE +Performing the Final Link, , Adding Symbols to the Hash Table, Linker Functions +SUBSECTION + Performing the final link + +@cindex _bfd_link_final_link in target vector +@cindex target vector (_bfd_final_link) + When all the input files have been processed, the linker calls + the <<_bfd_final_link>> entry point of the output BFD. This + routine is responsible for producing the final output file, + which has several aspects. It must relocate the contents of + the input sections and copy the data into the output sections. + It must build an output symbol table including any local + symbols from the input files and the global symbols from the + hash table. When producing relocateable output, it must + modify the input relocs and write them into the output file. + There may also be object format dependent work to be done. + + The linker will also call the <<write_object_contents>> entry + point when the BFD is closed. The two entry points must work + together in order to produce the correct output file. + + The details of how this works are inevitably dependent upon + the specific object file format. The a.out + <<_bfd_final_link>> routine is <<NAME(aout,final_link)>>. + +@menu +@* Information provided by the linker:: +@* Relocating the section contents:: +@* Writing the symbol table:: +@end menu + +INODE +Information provided by the linker, Relocating the section contents, Performing the Final Link, Performing the Final Link +SUBSUBSECTION + Information provided by the linker + + Before the linker calls the <<_bfd_final_link>> entry point, + it sets up some data structures for the function to use. + + The <<input_bfds>> field of the <<bfd_link_info>> structure + will point to a list of all the input files included in the + link. These files are linked through the <<link_next>> field + of the <<bfd>> structure. + + Each section in the output file will have a list of + <<link_order>> structures attached to the <<link_order_head>> + field (the <<link_order>> structure is defined in + <<bfdlink.h>>). These structures describe how to create the + contents of the output section in terms of the contents of + various input sections, fill constants, and, eventually, other + types of information. They also describe relocs that must be + created by the BFD backend, but do not correspond to any input + file; this is used to support -Ur, which builds constructors + while generating a relocateable object file. + +INODE +Relocating the section contents, Writing the symbol table, Information provided by the linker, Performing the Final Link +SUBSUBSECTION + Relocating the section contents + + The <<_bfd_final_link>> function should look through the + <<link_order>> structures attached to each section of the + output file. Each <<link_order>> structure should either be + handled specially, or it should be passed to the function + <<_bfd_default_link_order>> which will do the right thing + (<<_bfd_default_link_order>> is defined in <<linker.c>>). + + For efficiency, a <<link_order>> of type + <<bfd_indirect_link_order>> whose associated section belongs + to a BFD of the same format as the output BFD must be handled + specially. This type of <<link_order>> describes part of an + output section in terms of a section belonging to one of the + input files. The <<_bfd_final_link>> function should read the + contents of the section and any associated relocs, apply the + relocs to the section contents, and write out the modified + section contents. If performing a relocateable link, the + relocs themselves must also be modified and written out. + +@findex _bfd_relocate_contents +@findex _bfd_final_link_relocate + The functions <<_bfd_relocate_contents>> and + <<_bfd_final_link_relocate>> provide some general support for + performing the actual relocations, notably overflow checking. + Their arguments include information about the symbol the + relocation is against and a <<reloc_howto_type>> argument + which describes the relocation to perform. These functions + are defined in <<reloc.c>>. + + The a.out function which handles reading, relocating, and + writing section contents is <<aout_link_input_section>>. The + actual relocation is done in <<aout_link_input_section_std>> + and <<aout_link_input_section_ext>>. + +INODE +Writing the symbol table, , Relocating the section contents, Performing the Final Link +SUBSUBSECTION + Writing the symbol table + + The <<_bfd_final_link>> function must gather all the symbols + in the input files and write them out. It must also write out + all the symbols in the global hash table. This must be + controlled by the <<strip>> and <<discard>> fields of the + <<bfd_link_info>> structure. + + The local symbols of the input files will not have been + entered into the linker hash table. The <<_bfd_final_link>> + routine must consider each input file and include the symbols + in the output file. It may be convenient to do this when + looking through the <<link_order>> structures, or it may be + done by stepping through the <<input_bfds>> list. + + The <<_bfd_final_link>> routine must also traverse the global + hash table to gather all the externally visible symbols. It + is possible that most of the externally visible symbols may be + written out when considering the symbols of each input file, + but it is still necessary to traverse the hash table since the + linker script may have defined some symbols that are not in + any of the input files. The <<written>> field in the + <<bfd_link_hash_entry>> structure may be used to determine + which entries in the hash table have not already been written + out. + + The <<strip>> field of the <<bfd_link_info>> structure + controls which symbols are written out. The possible values + are listed in <<bfdlink.h>>. If the value is <<strip_some>>, + then the <<keep_hash>> field of the <<bfd_link_info>> + structure is a hash table of symbols to keep; each symbol + should be looked up in this hash table, and only symbols which + are present should be included in the output file. + + If the <<strip>> field of the <<bfd_link_info>> structure + permits local symbols to be written out, the <<discard>> field + is used to further controls which local symbols are included + in the output file. If the value is <<discard_l>>, then all + local symbols which begin with a certain prefix are discarded; + this prefix is described by the <<lprefix>> and + <<lprefix_len>> fields of the <<bfd_link_info>> structure. + + The a.out backend handles symbols by calling + <<aout_link_write_symbols>> on each input BFD and then + traversing the global hash table with the function + <<aout_link_write_other_symbol>>. It builds a string table + while writing out the symbols, which is written to the output + file at the end of <<NAME(aout,final_link)>>. +*/ + +static struct bfd_hash_entry *generic_link_hash_newfunc + PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, + const char *)); +static boolean generic_link_read_symbols + PARAMS ((bfd *)); +static boolean generic_link_add_symbols + PARAMS ((bfd *, struct bfd_link_info *, boolean collect)); +static boolean generic_link_add_object_symbols + PARAMS ((bfd *, struct bfd_link_info *, boolean collect)); +static boolean generic_link_check_archive_element_no_collect + PARAMS ((bfd *, struct bfd_link_info *, boolean *pneeded)); +static boolean generic_link_check_archive_element_collect + PARAMS ((bfd *, struct bfd_link_info *, boolean *pneeded)); +static boolean generic_link_check_archive_element + PARAMS ((bfd *, struct bfd_link_info *, boolean *pneeded, boolean collect)); +static boolean generic_link_add_symbol_list + PARAMS ((bfd *, struct bfd_link_info *, bfd_size_type count, asymbol **, + boolean collect)); +static boolean generic_add_output_symbol + PARAMS ((bfd *, size_t *psymalloc, asymbol *)); +static boolean default_fill_link_order + PARAMS ((bfd *, struct bfd_link_info *, asection *, + struct bfd_link_order *)); +static boolean default_indirect_link_order + PARAMS ((bfd *, struct bfd_link_info *, asection *, + struct bfd_link_order *)); + +/* The link hash table structure is defined in bfdlink.h. It provides + a base hash table which the backend specific hash tables are built + upon. */ + +/* Routine to create an entry in the link hash table. */ + +struct bfd_hash_entry * +_bfd_link_hash_newfunc (entry, table, string) + struct bfd_hash_entry *entry; + struct bfd_hash_table *table; + const char *string; +{ + struct bfd_link_hash_entry *ret = (struct bfd_link_hash_entry *) entry; + + /* Allocate the structure if it has not already been allocated by a + subclass. */ + if (ret == (struct bfd_link_hash_entry *) NULL) + ret = ((struct bfd_link_hash_entry *) + bfd_hash_allocate (table, sizeof (struct bfd_link_hash_entry))); + if (ret == (struct bfd_link_hash_entry *) NULL) + { + bfd_set_error (bfd_error_no_memory); + return NULL; + } + + /* Call the allocation method of the superclass. */ + ret = ((struct bfd_link_hash_entry *) + bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string)); + + if (ret) + { + /* Initialize the local fields. */ + ret->type = bfd_link_hash_new; + ret->next = NULL; + } + + return (struct bfd_hash_entry *) ret; +} + +/* Initialize a link hash table. The BFD argument is the one + responsible for creating this table. */ + +boolean +_bfd_link_hash_table_init (table, abfd, newfunc) + struct bfd_link_hash_table *table; + bfd *abfd; + struct bfd_hash_entry *(*newfunc) PARAMS ((struct bfd_hash_entry *, + struct bfd_hash_table *, + const char *)); +{ + table->creator = abfd->xvec; + table->undefs = NULL; + table->undefs_tail = NULL; + return bfd_hash_table_init (&table->table, newfunc); +} + +/* Look up a symbol in a link hash table. If follow is true, we + follow bfd_link_hash_indirect and bfd_link_hash_warning links to + the real symbol. */ + +struct bfd_link_hash_entry * +bfd_link_hash_lookup (table, string, create, copy, follow) + struct bfd_link_hash_table *table; + const char *string; + boolean create; + boolean copy; + boolean follow; +{ + struct bfd_link_hash_entry *ret; + + ret = ((struct bfd_link_hash_entry *) + bfd_hash_lookup (&table->table, string, create, copy)); + + if (follow && ret != (struct bfd_link_hash_entry *) NULL) + { + while (ret->type == bfd_link_hash_indirect + || ret->type == bfd_link_hash_warning) + ret = ret->u.i.link; + } + + return ret; +} + +/* Traverse a generic link hash table. The only reason this is not a + macro is to do better type checking. This code presumes that an + argument passed as a struct bfd_hash_entry * may be caught as a + struct bfd_link_hash_entry * with no explicit cast required on the + call. */ + +void +bfd_link_hash_traverse (table, func, info) + struct bfd_link_hash_table *table; + boolean (*func) PARAMS ((struct bfd_link_hash_entry *, PTR)); + PTR info; +{ + bfd_hash_traverse (&table->table, + ((boolean (*) PARAMS ((struct bfd_hash_entry *, PTR))) + func), + info); +} + +/* Add a symbol to the linker hash table undefs list. */ + +INLINE void +bfd_link_add_undef (table, h) + struct bfd_link_hash_table *table; + struct bfd_link_hash_entry *h; +{ + BFD_ASSERT (h->next == NULL); + if (table->undefs_tail != (struct bfd_link_hash_entry *) NULL) + table->undefs_tail->next = h; + if (table->undefs == (struct bfd_link_hash_entry *) NULL) + table->undefs = h; + table->undefs_tail = h; +} + +/* Routine to create an entry in an generic link hash table. */ + +static struct bfd_hash_entry * +generic_link_hash_newfunc (entry, table, string) + struct bfd_hash_entry *entry; + struct bfd_hash_table *table; + const char *string; +{ + struct generic_link_hash_entry *ret = + (struct generic_link_hash_entry *) entry; + + /* Allocate the structure if it has not already been allocated by a + subclass. */ + if (ret == (struct generic_link_hash_entry *) NULL) + ret = ((struct generic_link_hash_entry *) + bfd_hash_allocate (table, sizeof (struct generic_link_hash_entry))); + if (ret == (struct generic_link_hash_entry *) NULL) + { + bfd_set_error (bfd_error_no_memory); + return NULL; + } + + /* Call the allocation method of the superclass. */ + ret = ((struct generic_link_hash_entry *) + _bfd_link_hash_newfunc ((struct bfd_hash_entry *) ret, + table, string)); + + if (ret) + { + /* Set local fields. */ + ret->written = false; + ret->sym = NULL; + } + + return (struct bfd_hash_entry *) ret; +} + +/* Create an generic link hash table. */ + +struct bfd_link_hash_table * +_bfd_generic_link_hash_table_create (abfd) + bfd *abfd; +{ + struct generic_link_hash_table *ret; + + ret = ((struct generic_link_hash_table *) + malloc (sizeof (struct generic_link_hash_table))); + if (!ret) + { + bfd_set_error (bfd_error_no_memory); + return (struct bfd_link_hash_table *) NULL; + } + if (! _bfd_link_hash_table_init (&ret->root, abfd, + generic_link_hash_newfunc)) + { + free (ret); + return (struct bfd_link_hash_table *) NULL; + } + return &ret->root; +} + +/* Grab the symbols for an object file when doing a generic link. We + store the symbols in the outsymbols field. We need to keep them + around for the entire link to ensure that we only read them once. + If we read them multiple times, we might wind up with relocs and + the hash table pointing to different instances of the symbol + structure. */ + +static boolean +generic_link_read_symbols (abfd) + bfd *abfd; +{ + if (abfd->outsymbols == (asymbol **) NULL) + { + long symsize; + long symcount; + + symsize = bfd_get_symtab_upper_bound (abfd); + if (symsize < 0) + return false; + abfd->outsymbols = (asymbol **) bfd_alloc (abfd, symsize); + if (abfd->outsymbols == NULL && symsize != 0) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + symcount = bfd_canonicalize_symtab (abfd, abfd->outsymbols); + if (symcount < 0) + return false; + abfd->symcount = symcount; + } + + return true; +} + +/* Generic function to add symbols to from an object file to the + global hash table. This version does not automatically collect + constructors by name. */ + +boolean +_bfd_generic_link_add_symbols (abfd, info) + bfd *abfd; + struct bfd_link_info *info; +{ + return generic_link_add_symbols (abfd, info, false); +} + +/* Generic function to add symbols from an object file to the global + hash table. This version automatically collects constructors by + name, as the collect2 program does. It should be used for any + target which does not provide some other mechanism for setting up + constructors and destructors; these are approximately those targets + for which gcc uses collect2 and do not support stabs. */ + +boolean +_bfd_generic_link_add_symbols_collect (abfd, info) + bfd *abfd; + struct bfd_link_info *info; +{ + return generic_link_add_symbols (abfd, info, true); +} + +/* Add symbols from an object file to the global hash table. */ + +static boolean +generic_link_add_symbols (abfd, info, collect) + bfd *abfd; + struct bfd_link_info *info; + boolean collect; +{ + boolean ret; + + switch (bfd_get_format (abfd)) + { + case bfd_object: + ret = generic_link_add_object_symbols (abfd, info, collect); + break; + case bfd_archive: + ret = (_bfd_generic_link_add_archive_symbols + (abfd, info, + (collect + ? generic_link_check_archive_element_collect + : generic_link_check_archive_element_no_collect))); + break; + default: + bfd_set_error (bfd_error_wrong_format); + ret = false; + } + + return ret; +} + +/* Add symbols from an object file to the global hash table. */ + +static boolean +generic_link_add_object_symbols (abfd, info, collect) + bfd *abfd; + struct bfd_link_info *info; + boolean collect; +{ + if (! generic_link_read_symbols (abfd)) + return false; + return generic_link_add_symbol_list (abfd, info, + _bfd_generic_link_get_symcount (abfd), + _bfd_generic_link_get_symbols (abfd), + collect); +} + +/* We build a hash table of all symbols defined in an archive. */ + +/* An archive symbol may be defined by multiple archive elements. + This linked list is used to hold the elements. */ + +struct archive_list +{ + struct archive_list *next; + int indx; +}; + +/* An entry in an archive hash table. */ + +struct archive_hash_entry +{ + struct bfd_hash_entry root; + /* Where the symbol is defined. */ + struct archive_list *defs; +}; + +/* An archive hash table itself. */ + +struct archive_hash_table +{ + struct bfd_hash_table table; +}; + +static struct bfd_hash_entry *archive_hash_newfunc + PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *)); +static boolean archive_hash_table_init + PARAMS ((struct archive_hash_table *, + struct bfd_hash_entry *(*) (struct bfd_hash_entry *, + struct bfd_hash_table *, + const char *))); + +/* Create a new entry for an archive hash table. */ + +static struct bfd_hash_entry * +archive_hash_newfunc (entry, table, string) + struct bfd_hash_entry *entry; + struct bfd_hash_table *table; + const char *string; +{ + struct archive_hash_entry *ret = (struct archive_hash_entry *) entry; + + /* Allocate the structure if it has not already been allocated by a + subclass. */ + if (ret == (struct archive_hash_entry *) NULL) + ret = ((struct archive_hash_entry *) + bfd_hash_allocate (table, sizeof (struct archive_hash_entry))); + if (ret == (struct archive_hash_entry *) NULL) + { + bfd_set_error (bfd_error_no_memory); + return NULL; + } + + /* Call the allocation method of the superclass. */ + ret = ((struct archive_hash_entry *) + bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string)); + + if (ret) + { + /* Initialize the local fields. */ + ret->defs = (struct archive_list *) NULL; + } + + return (struct bfd_hash_entry *) ret; +} + +/* Initialize an archive hash table. */ + +static boolean +archive_hash_table_init (table, newfunc) + struct archive_hash_table *table; + struct bfd_hash_entry *(*newfunc) PARAMS ((struct bfd_hash_entry *, + struct bfd_hash_table *, + const char *)); +{ + return bfd_hash_table_init (&table->table, newfunc); +} + +/* Look up an entry in an archive hash table. */ + +#define archive_hash_lookup(t, string, create, copy) \ + ((struct archive_hash_entry *) \ + bfd_hash_lookup (&(t)->table, (string), (create), (copy))) + +/* Allocate space in an archive hash table. */ + +#define archive_hash_allocate(t, size) bfd_hash_allocate (&(t)->table, (size)) + +/* Free an archive hash table. */ + +#define archive_hash_table_free(t) bfd_hash_table_free (&(t)->table) + +/* Generic function to add symbols from an archive file to the global + hash file. This function presumes that the archive symbol table + has already been read in (this is normally done by the + bfd_check_format entry point). It looks through the undefined and + common symbols and searches the archive symbol table for them. If + it finds an entry, it includes the associated object file in the + link. + + The old linker looked through the archive symbol table for + undefined symbols. We do it the other way around, looking through + undefined symbols for symbols defined in the archive. The + advantage of the newer scheme is that we only have to look through + the list of undefined symbols once, whereas the old method had to + re-search the symbol table each time a new object file was added. + + The CHECKFN argument is used to see if an object file should be + included. CHECKFN should set *PNEEDED to true if the object file + should be included, and must also call the bfd_link_info + add_archive_element callback function and handle adding the symbols + to the global hash table. CHECKFN should only return false if some + sort of error occurs. + + For some formats, such as a.out, it is possible to look through an + object file but not actually include it in the link. The + archive_pass field in a BFD is used to avoid checking the symbols + of an object files too many times. When an object is included in + the link, archive_pass is set to -1. If an object is scanned but + not included, archive_pass is set to the pass number. The pass + number is incremented each time a new object file is included. The + pass number is used because when a new object file is included it + may create new undefined symbols which cause a previously examined + object file to be included. */ + +boolean +_bfd_generic_link_add_archive_symbols (abfd, info, checkfn) + bfd *abfd; + struct bfd_link_info *info; + boolean (*checkfn) PARAMS ((bfd *, struct bfd_link_info *, + boolean *pneeded)); +{ + carsym *arsyms; + carsym *arsym_end; + register carsym *arsym; + int pass; + struct archive_hash_table arsym_hash; + int indx; + struct bfd_link_hash_entry **pundef; + + if (! bfd_has_map (abfd)) + { + bfd_set_error (bfd_error_no_symbols); + return false; + } + + arsyms = bfd_ardata (abfd)->symdefs; + arsym_end = arsyms + bfd_ardata (abfd)->symdef_count; + + /* In order to quickly determine whether an symbol is defined in + this archive, we build a hash table of the symbols. */ + if (! archive_hash_table_init (&arsym_hash, archive_hash_newfunc)) + return false; + for (arsym = arsyms, indx = 0; arsym < arsym_end; arsym++, indx++) + { + struct archive_hash_entry *arh; + struct archive_list *l, **pp; + + arh = archive_hash_lookup (&arsym_hash, arsym->name, true, false); + if (arh == (struct archive_hash_entry *) NULL) + goto error_return; + l = ((struct archive_list *) + archive_hash_allocate (&arsym_hash, sizeof (struct archive_list))); + if (l == NULL) + goto error_return; + l->indx = indx; + for (pp = &arh->defs; + *pp != (struct archive_list *) NULL; + pp = &(*pp)->next) + ; + *pp = l; + l->next = NULL; + } + + /* The archive_pass field in the archive itself is used to + initialize PASS, sine we may search the same archive multiple + times. */ + pass = abfd->archive_pass; + if (pass == 0) + pass = 1; + + /* New undefined symbols are added to the end of the list, so we + only need to look through it once. */ + pundef = &info->hash->undefs; + while (*pundef != (struct bfd_link_hash_entry *) NULL) + { + struct bfd_link_hash_entry *h; + struct archive_hash_entry *arh; + struct archive_list *l; + + h = *pundef; + + /* When a symbol is defined, it is not necessarily removed from + the list. */ + if (h->type != bfd_link_hash_undefined + && h->type != bfd_link_hash_common) + { + /* Remove this entry from the list, for general cleanliness + and because we are going to look through the list again + if we search any more libraries. We can't remove the + entry if it is the tail, because that would lose any + entries we add to the list later on (it would also cause + us to lose track of whether the symbol has been + referenced). */ + if (*pundef != info->hash->undefs_tail) + *pundef = (*pundef)->next; + else + pundef = &(*pundef)->next; + continue; + } + + /* Look for this symbol in the archive symbol map. */ + arh = archive_hash_lookup (&arsym_hash, h->root.string, false, false); + if (arh == (struct archive_hash_entry *) NULL) + { + pundef = &(*pundef)->next; + continue; + } + + /* Look at all the objects which define this symbol. */ + for (l = arh->defs; l != (struct archive_list *) NULL; l = l->next) + { + bfd *element; + boolean needed; + + /* If the symbol has gotten defined along the way, quit. */ + if (h->type != bfd_link_hash_undefined + && h->type != bfd_link_hash_common) + break; + + element = bfd_get_elt_at_index (abfd, l->indx); + if (element == (bfd *) NULL) + goto error_return; + + /* If we've already included this element, or if we've + already checked it on this pass, continue. */ + if (element->archive_pass == -1 + || element->archive_pass == pass) + continue; + + /* If we can't figure this element out, just ignore it. */ + if (! bfd_check_format (element, bfd_object)) + { + element->archive_pass = -1; + continue; + } + + /* CHECKFN will see if this element should be included, and + go ahead and include it if appropriate. */ + if (! (*checkfn) (element, info, &needed)) + goto error_return; + + if (! needed) + element->archive_pass = pass; + else + { + element->archive_pass = -1; + + /* Increment the pass count to show that we may need to + recheck object files which were already checked. */ + ++pass; + } + } + + pundef = &(*pundef)->next; + } + + archive_hash_table_free (&arsym_hash); + + /* Save PASS in case we are called again. */ + abfd->archive_pass = pass; + + return true; + + error_return: + archive_hash_table_free (&arsym_hash); + return false; +} + +/* See if we should include an archive element. This version is used + when we do not want to automatically collect constructors based on + the symbol name, presumably because we have some other mechanism + for finding them. */ + +static boolean +generic_link_check_archive_element_no_collect (abfd, info, pneeded) + bfd *abfd; + struct bfd_link_info *info; + boolean *pneeded; +{ + return generic_link_check_archive_element (abfd, info, pneeded, false); +} + +/* See if we should include an archive element. This version is used + when we want to automatically collect constructors based on the + symbol name, as collect2 does. */ + +static boolean +generic_link_check_archive_element_collect (abfd, info, pneeded) + bfd *abfd; + struct bfd_link_info *info; + boolean *pneeded; +{ + return generic_link_check_archive_element (abfd, info, pneeded, true); +} + +/* See if we should include an archive element. Optionally collect + constructors. */ + +static boolean +generic_link_check_archive_element (abfd, info, pneeded, collect) + bfd *abfd; + struct bfd_link_info *info; + boolean *pneeded; + boolean collect; +{ + asymbol **pp, **ppend; + + *pneeded = false; + + if (! generic_link_read_symbols (abfd)) + return false; + + pp = _bfd_generic_link_get_symbols (abfd); + ppend = pp + _bfd_generic_link_get_symcount (abfd); + for (; pp < ppend; pp++) + { + asymbol *p; + struct bfd_link_hash_entry *h; + + p = *pp; + + /* We are only interested in globally visible symbols. */ + if (! bfd_is_com_section (p->section) + && (p->flags & (BSF_GLOBAL | BSF_INDIRECT | BSF_WEAK)) == 0) + continue; + + /* We are only interested if we know something about this + symbol, and it is undefined or common. An undefined weak + symbol (type bfd_link_hash_weak) is not considered to be a + reference when pulling files out of an archive. See the SVR4 + ABI, p. 4-27. */ + h = bfd_link_hash_lookup (info->hash, bfd_asymbol_name (p), false, + false, true); + if (h == (struct bfd_link_hash_entry *) NULL + || (h->type != bfd_link_hash_undefined + && h->type != bfd_link_hash_common)) + continue; + + /* P is a symbol we are looking for. */ + + if (! bfd_is_com_section (p->section)) + { + bfd_size_type symcount; + asymbol **symbols; + + /* This object file defines this symbol, so pull it in. */ + if (! (*info->callbacks->add_archive_element) (info, abfd, + bfd_asymbol_name (p))) + return false; + symcount = _bfd_generic_link_get_symcount (abfd); + symbols = _bfd_generic_link_get_symbols (abfd); + if (! generic_link_add_symbol_list (abfd, info, symcount, + symbols, collect)) + return false; + *pneeded = true; + return true; + } + + /* P is a common symbol. */ + + 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 for the -u option in the linker. */ + if (! (*info->callbacks->add_archive_element) + (info, abfd, bfd_asymbol_name (p))) + return false; + *pneeded = true; + return true; + } + + /* Turn the symbol into a common symbol but do not link in + the object file. This is how a.out works. Object + formats that require different semantics must implement + this function differently. This symbol is already on the + undefs list. We add the section to a common section + attached to symbfd to ensure that it is in a BFD which + will be linked in. */ + h->type = bfd_link_hash_common; + h->u.c.size = bfd_asymbol_value (p); + if (p->section == bfd_com_section_ptr) + h->u.c.section = bfd_make_section_old_way (symbfd, "COMMON"); + else + h->u.c.section = bfd_make_section_old_way (symbfd, + p->section->name); + h->u.c.section->flags = SEC_ALLOC; + } + else + { + /* Adjust the size of the common symbol if necessary. This + is how a.out works. Object formats that require + different semantics must implement this function + differently. */ + if (bfd_asymbol_value (p) > h->u.c.size) + h->u.c.size = bfd_asymbol_value (p); + } + } + + /* This archive element is not needed. */ + return true; +} + +/* Add the symbols from an object file to the global hash table. ABFD + is the object file. INFO is the linker information. SYMBOL_COUNT + is the number of symbols. SYMBOLS is the list of symbols. COLLECT + is true if constructors should be automatically collected by name + as is done by collect2. */ + +static boolean +generic_link_add_symbol_list (abfd, info, symbol_count, symbols, collect) + bfd *abfd; + struct bfd_link_info *info; + bfd_size_type symbol_count; + asymbol **symbols; + boolean collect; +{ + asymbol **pp, **ppend; + + pp = symbols; + ppend = symbols + symbol_count; + for (; pp < ppend; pp++) + { + asymbol *p; + + p = *pp; + + if ((p->flags & (BSF_INDIRECT + | BSF_WARNING + | BSF_GLOBAL + | BSF_CONSTRUCTOR + | BSF_WEAK)) != 0 + || bfd_is_und_section (bfd_get_section (p)) + || bfd_is_com_section (bfd_get_section (p)) + || bfd_is_ind_section (bfd_get_section (p))) + { + const char *name; + const char *string; + struct generic_link_hash_entry *h; + + name = bfd_asymbol_name (p); + if ((p->flags & BSF_INDIRECT) != 0 + || bfd_is_ind_section (p->section)) + string = bfd_asymbol_name ((asymbol *) p->value); + else if ((p->flags & BSF_WARNING) != 0) + { + /* The name of P is actually the warning string, and the + value is actually a pointer to the symbol to warn + about. */ + string = name; + name = bfd_asymbol_name ((asymbol *) p->value); + } + else + string = NULL; + + h = NULL; + if (! (_bfd_generic_link_add_one_symbol + (info, abfd, name, p->flags, bfd_get_section (p), + p->value, string, false, collect, + (struct bfd_link_hash_entry **) &h))) + return false; + + /* Save the BFD symbol so that we don't lose any backend + specific information that may be attached to it. We only + want this one if it gives more information than the + existing one; we don't want to replace a defined symbol + with an undefined one. This routine may be called with a + hash table other than the generic hash table, so we only + do this if we are certain that the hash table is a + generic one. */ + if (info->hash->creator == abfd->xvec) + { + if (h->sym == (asymbol *) NULL + || (! bfd_is_und_section (bfd_get_section (p)) + && (! bfd_is_com_section (bfd_get_section (p)) + || bfd_is_und_section (bfd_get_section (h->sym))))) + { + h->sym = p; + /* BSF_OLD_COMMON is a hack to support COFF reloc + reading, and it should go away when the COFF + linker is switched to the new version. */ + if (bfd_is_com_section (bfd_get_section (p))) + p->flags |= BSF_OLD_COMMON; + } + + /* Store a back pointer from the symbol to the hash + table entry for the benefit of relaxation code until + it gets rewritten to not use asymbol structures. */ + p->udata = (PTR) h; + } + } + } + + return true; +} + +/* We use a state table to deal with adding symbols from an object + file. The first index into the state table describes the symbol + from the object file. The second index into the state table is the + type of the symbol in the hash table. */ + +/* The symbol from the object file is turned into one of these row + values. */ + +enum link_row +{ + UNDEF_ROW, /* Undefined. */ + UNDEFW_ROW, /* Weak undefined. */ + DEF_ROW, /* Defined. */ + DEFW_ROW, /* Weak defined. */ + COMMON_ROW, /* Common. */ + INDR_ROW, /* Indirect. */ + WARN_ROW, /* Warning. */ + SET_ROW /* Member of set. */ +}; + +/* apparently needed for Hitachi 3050R(HI-UX/WE2)? */ +#undef FAIL + +/* The actions to take in the state table. */ + +enum link_action +{ + FAIL, /* Abort. */ + UND, /* Mark symbol undefined. */ + WEAK, /* Mark symbol weak undefined. */ + DEF, /* Mark symbol defined. */ + COM, /* Mark symbol common. */ + REF, /* Mark defined symbol referenced. */ + CREF, /* Possibly warn about common reference to defined symbol. */ + CDEF, /* Define existing common symbol. */ + NOACT, /* No action. */ + BIG, /* Mark symbol common using largest size. */ + MDEF, /* Multiple definition error. */ + MIND, /* Multiple indirect symbols. */ + IND, /* Make indirect symbol. */ + SET, /* Add value to set. */ + MWARN, /* Make warning symbol. */ + WARN, /* Issue warning. */ + CWARN, /* Warn if referenced, else MWARN. */ + CYCLE, /* Repeat with symbol pointed to. */ + REFC, /* Mark indirect symbol referenced and then CYCLE. */ + WARNC /* Issue warning and then CYCLE. */ +}; + +/* The state table itself. The first index is a link_row and the + second index is a bfd_link_hash_type. */ + +static const enum link_action link_action[8][7] = +{ + /* current\prev new undef weak def com indr warn */ + /* UNDEF_ROW */ {UND, NOACT, NOACT, REF, NOACT, REFC, WARNC }, + /* UNDEFW_ROW */ {WEAK, WEAK, NOACT, REF, NOACT, REFC, WARNC }, + /* DEF_ROW */ {DEF, DEF, DEF, MDEF, CDEF, MDEF, CYCLE }, + /* DEFW_ROW */ {DEF, DEF, DEF, NOACT, NOACT, NOACT, CYCLE }, + /* COMMON_ROW */ {COM, COM, COM, CREF, BIG, MDEF, WARNC }, + /* INDR_ROW */ {IND, IND, IND, MDEF, MDEF, MIND, CYCLE }, + /* WARN_ROW */ {MWARN, WARN, WARN, CWARN, WARN, CWARN, CYCLE }, + /* SET_ROW */ {SET, SET, SET, SET, SET, CYCLE, CYCLE } +}; + +/* Most of the entries in the LINK_ACTION table are straightforward, + but a few are somewhat subtle. + + A reference to an indirect symbol (UNDEF_ROW/indr or + UNDEFW_ROW/indr) is counted as a reference both to the indirect + symbol and to the symbol the indirect symbol points to. + + A reference to a warning symbol (UNDEF_ROW/warn or UNDEFW_ROW/warn) + causes the warning to be issued. + + A common definition of an indirect symbol (COMMON_ROW/indr) is + treated as a multiple definition error. Likewise for an indirect + definition of a common symbol (INDR_ROW/com). + + An indirect definition of a warning (INDR_ROW/warn) does not cause + the warning to be issued. + + If a warning is created for an indirect symbol (WARN_ROW/indr) no + warning is created for the symbol the indirect symbol points to. + + Adding an entry to a set does not count as a reference to a set, + and no warning is issued (SET_ROW/warn). */ + +/* Add a symbol to the global hash table. + ABFD is the BFD the symbol comes from. + NAME is the name of the symbol. + FLAGS is the BSF_* bits associated with the symbol. + SECTION is the section in which the symbol is defined; this may be + bfd_und_section_ptr or bfd_com_section_ptr. + VALUE is the value of the symbol, relative to the section. + STRING is used for either an indirect symbol, in which case it is + the name of the symbol to indirect to, or a warning symbol, in + which case it is the warning string. + COPY is true if NAME or STRING must be copied into locally + allocated memory if they need to be saved. + COLLECT is true if we should automatically collect gcc constructor + or destructor names as collect2 does. + HASHP, if not NULL, is a place to store the created hash table + entry; if *HASHP is not NULL, the caller has already looked up + the hash table entry, and stored it in *HASHP. */ + +boolean +_bfd_generic_link_add_one_symbol (info, abfd, name, flags, section, value, + string, copy, collect, hashp) + struct bfd_link_info *info; + bfd *abfd; + const char *name; + flagword flags; + asection *section; + bfd_vma value; + const char *string; + boolean copy; + boolean collect; + struct bfd_link_hash_entry **hashp; +{ + enum link_row row; + struct bfd_link_hash_entry *h; + boolean cycle; + + if (bfd_is_ind_section (section) + || (flags & BSF_INDIRECT) != 0) + row = INDR_ROW; + else if ((flags & BSF_WARNING) != 0) + row = WARN_ROW; + else if ((flags & BSF_CONSTRUCTOR) != 0) + row = SET_ROW; + else if (bfd_is_und_section (section)) + { + if ((flags & BSF_WEAK) != 0) + row = UNDEFW_ROW; + else + row = UNDEF_ROW; + } + else if ((flags & BSF_WEAK) != 0) + row = DEFW_ROW; + else if (bfd_is_com_section (section)) + row = COMMON_ROW; + else + row = DEF_ROW; + + if (hashp != NULL && *hashp != NULL) + { + h = *hashp; + BFD_ASSERT (strcmp (h->root.string, name) == 0); + } + else + { + h = bfd_link_hash_lookup (info->hash, name, true, copy, false); + if (h == NULL) + { + if (hashp != NULL) + *hashp = NULL; + return false; + } + } + + if (info->notice_hash != (struct bfd_hash_table *) NULL + && (bfd_hash_lookup (info->notice_hash, name, false, false) + != (struct bfd_hash_entry *) NULL)) + { + if (! (*info->callbacks->notice) (info, name, abfd, section, value)) + return false; + } + + if (hashp != (struct bfd_link_hash_entry **) NULL) + *hashp = h; + + do + { + enum link_action action; + + cycle = false; + action = link_action[(int) row][(int) h->type]; + switch (action) + { + case FAIL: + abort (); + + case NOACT: + /* Do nothing. */ + break; + + case UND: + /* Make a new undefined symbol. */ + h->type = bfd_link_hash_undefined; + h->u.undef.abfd = abfd; + bfd_link_add_undef (info->hash, h); + break; + + case WEAK: + /* Make a new weak undefined symbol. */ + h->type = bfd_link_hash_weak; + h->u.undef.abfd = abfd; + break; + + case CDEF: + /* We have found a definition for a symbol which was + previously common. */ + BFD_ASSERT (h->type == bfd_link_hash_common); + if (! ((*info->callbacks->multiple_common) + (info, name, + h->u.c.section->owner, bfd_link_hash_common, h->u.c.size, + abfd, bfd_link_hash_defined, (bfd_vma) 0))) + return false; + /* Fall through. */ + case DEF: + /* Define a symbol. */ + h->type = bfd_link_hash_defined; + h->u.def.section = section; + h->u.def.value = value; + + /* If we have been asked to, we act like collect2 and + identify all functions that might be global constructors + and destructors and pass them up in a callback. We only + do this for certain object file types, since many object + file types can handle this automatically. */ + if (collect && name[0] == '_') + { + const char *s; + + /* A constructor or destructor name starts like this: + _+GLOBAL_[_.$][ID][_.$] + where the first [_.$] and the second are the same + character (we accept any character there, in case a + new object file format comes along with even worse + naming restrictions). */ + +#define CONS_PREFIX "GLOBAL_" +#define CONS_PREFIX_LEN (sizeof CONS_PREFIX - 1) + + s = name + 1; + while (*s == '_') + ++s; + if (s[0] == 'G' + && strncmp (s, CONS_PREFIX, CONS_PREFIX_LEN - 1) == 0) + { + char c; + + c = s[CONS_PREFIX_LEN + 1]; + if ((c == 'I' || c == 'D') + && s[CONS_PREFIX_LEN] == s[CONS_PREFIX_LEN + 2]) + { + if (! ((*info->callbacks->constructor) + (info, + c == 'I' ? true : false, + name, abfd, section, value))) + return false; + } + } + } + + break; + + case COM: + /* We have found a common definition for a symbol. */ + if (h->type == bfd_link_hash_new) + bfd_link_add_undef (info->hash, h); + h->type = bfd_link_hash_common; + h->u.c.size = value; + if (section == bfd_com_section_ptr) + { + h->u.c.section = bfd_make_section_old_way (abfd, "COMMON"); + h->u.c.section->flags = SEC_ALLOC; + } + else if (section->owner != abfd) + { + h->u.c.section = bfd_make_section_old_way (abfd, section->name); + h->u.c.section->flags = SEC_ALLOC; + } + else + h->u.c.section = section; + break; + + case REF: + /* A reference to a defined symbol. */ + if (h->next == NULL && info->hash->undefs_tail != h) + h->next = h; + break; + + case BIG: + /* We have found a common definition for a symbol which + already had a common definition. Use the maximum of the + two sizes. */ + BFD_ASSERT (h->type == bfd_link_hash_common); + if (! ((*info->callbacks->multiple_common) + (info, name, + h->u.c.section->owner, bfd_link_hash_common, h->u.c.size, + abfd, bfd_link_hash_common, value))) + return false; + if (value > h->u.c.size) + h->u.c.size = value; + break; + + case CREF: + /* We have found a common definition for a symbol which was + already defined. */ + BFD_ASSERT (h->type == bfd_link_hash_defined); + if (! ((*info->callbacks->multiple_common) + (info, name, + h->u.def.section->owner, bfd_link_hash_defined, (bfd_vma) 0, + abfd, bfd_link_hash_common, value))) + return false; + break; + + case MIND: + /* Multiple indirect symbols. This is OK if they both point + to the same symbol. */ + if (strcmp (h->u.i.link->root.string, string) == 0) + break; + /* Fall through. */ + case MDEF: + /* Handle a multiple definition. */ + { + asection *msec; + bfd_vma mval; + + switch (h->type) + { + case bfd_link_hash_defined: + msec = h->u.def.section; + mval = h->u.def.value; + break; + case bfd_link_hash_common: + msec = bfd_com_section_ptr; + mval = h->u.c.size; + break; + case bfd_link_hash_indirect: + msec = bfd_ind_section_ptr; + mval = 0; + break; + default: + abort (); + } + + if (! ((*info->callbacks->multiple_definition) + (info, name, msec->owner, msec, mval, abfd, section, + value))) + return false; + } + break; + + case IND: + /* Create an indirect symbol. */ + { + struct bfd_link_hash_entry *inh; + + /* STRING is the name of the symbol we want to indirect + to. */ + inh = bfd_link_hash_lookup (info->hash, string, true, copy, + false); + if (inh == (struct bfd_link_hash_entry *) NULL) + return false; + if (inh->type == bfd_link_hash_new) + { + inh->type = bfd_link_hash_undefined; + inh->u.undef.abfd = abfd; + bfd_link_add_undef (info->hash, inh); + } + + /* If the indirect symbol has been referenced, we need to + push the reference down to the symbol we are + referencing. */ + if (h->type != bfd_link_hash_new) + { + row = UNDEF_ROW; + cycle = true; + } + + h->type = bfd_link_hash_indirect; + h->u.i.link = inh; + } + break; + + case SET: + /* Add an entry to a set. */ + if (! (*info->callbacks->add_to_set) (info, h, BFD_RELOC_CTOR, + abfd, section, value)) + return false; + break; + + case WARNC: + /* Issue a warning and cycle. */ + if (h->u.i.warning != NULL) + { + if (! (*info->callbacks->warning) (info, h->u.i.warning)) + return false; + /* Only issue a warning once. */ + h->u.i.warning = NULL; + } + /* Fall through. */ + case CYCLE: + /* Try again with the referenced symbol. */ + h = h->u.i.link; + cycle = true; + break; + + case REFC: + /* A reference to an indirect symbol. */ + if (h->next == NULL && info->hash->undefs_tail != h) + h->next = h; + h = h->u.i.link; + cycle = true; + break; + + case WARN: + /* Issue a warning. */ + if (! (*info->callbacks->warning) (info, string)) + return false; + break; + + case CWARN: + /* Warn if this symbol has been referenced already, + otherwise either add a warning or cycle. A symbol has + been referenced if the next field is not NULL, or it is + the tail of the undefined symbol list. The REF case + above helps to ensure this. */ + if (h->next != NULL || info->hash->undefs_tail == h) + { + if (! (*info->callbacks->warning) (info, string)) + return false; + break; + } + /* Fall through. */ + case MWARN: + /* Make a warning symbol. */ + { + struct bfd_link_hash_entry *sub; + + /* STRING is the warning to give. */ + sub = ((struct bfd_link_hash_entry *) + bfd_hash_allocate (&info->hash->table, + sizeof (struct bfd_link_hash_entry))); + if (!sub) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + *sub = *h; + h->type = bfd_link_hash_warning; + h->u.i.link = sub; + if (! copy) + h->u.i.warning = string; + else + { + char *w; + + w = bfd_hash_allocate (&info->hash->table, + strlen (string) + 1); + strcpy (w, string); + h->u.i.warning = w; + } + } + break; + } + } + while (cycle); + + return true; +} + +/* Generic final link routine. */ + +boolean +_bfd_generic_final_link (abfd, info) + bfd *abfd; + struct bfd_link_info *info; +{ + bfd *sub; + asection *o; + struct bfd_link_order *p; + size_t outsymalloc; + struct generic_write_global_symbol_info wginfo; + + abfd->outsymbols = (asymbol **) NULL; + abfd->symcount = 0; + outsymalloc = 0; + + /* Build the output symbol table. */ + for (sub = info->input_bfds; sub != (bfd *) NULL; sub = sub->link_next) + if (! _bfd_generic_link_output_symbols (abfd, sub, info, &outsymalloc)) + return false; + + /* Accumulate the global symbols. */ + wginfo.info = info; + wginfo.output_bfd = abfd; + wginfo.psymalloc = &outsymalloc; + _bfd_generic_link_hash_traverse (_bfd_generic_hash_table (info), + _bfd_generic_link_write_global_symbol, + (PTR) &wginfo); + + if (info->relocateable) + { + /* Allocate space for the output relocs for each section. */ + for (o = abfd->sections; + o != (asection *) NULL; + o = o->next) + { + o->reloc_count = 0; + for (p = o->link_order_head; + p != (struct bfd_link_order *) NULL; + p = p->next) + { + if (p->type == bfd_section_reloc_link_order + || p->type == bfd_symbol_reloc_link_order) + ++o->reloc_count; + else if (p->type == bfd_indirect_link_order) + { + asection *input_section; + bfd *input_bfd; + long relsize; + arelent **relocs; + asymbol **symbols; + long reloc_count; + + input_section = p->u.indirect.section; + input_bfd = input_section->owner; + relsize = bfd_get_reloc_upper_bound (input_bfd, + input_section); + if (relsize < 0) + return false; + relocs = (arelent **) malloc ((size_t) relsize); + if (!relocs && relsize != 0) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + symbols = _bfd_generic_link_get_symbols (input_bfd); + reloc_count = bfd_canonicalize_reloc (input_bfd, + input_section, + relocs, + symbols); + if (reloc_count < 0) + return false; + BFD_ASSERT (reloc_count == input_section->reloc_count); + o->reloc_count += reloc_count; + free (relocs); + } + } + if (o->reloc_count > 0) + { + o->orelocation = ((arelent **) + bfd_alloc (abfd, + (o->reloc_count + * sizeof (arelent *)))); + if (!o->orelocation) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + o->flags |= SEC_RELOC; + /* Reset the count so that it can be used as an index + when putting in the output relocs. */ + o->reloc_count = 0; + } + } + } + + /* Handle all the link order information for the sections. */ + for (o = abfd->sections; + o != (asection *) NULL; + o = o->next) + { + for (p = o->link_order_head; + p != (struct bfd_link_order *) NULL; + p = p->next) + { + switch (p->type) + { + case bfd_section_reloc_link_order: + case bfd_symbol_reloc_link_order: + if (! _bfd_generic_reloc_link_order (abfd, info, o, p)) + return false; + break; + default: + if (! _bfd_default_link_order (abfd, info, o, p)) + return false; + break; + } + } + } + + return true; +} + +/* Add an output symbol to the output BFD. */ + +static boolean +generic_add_output_symbol (output_bfd, psymalloc, sym) + bfd *output_bfd; + size_t *psymalloc; + asymbol *sym; +{ + if (output_bfd->symcount >= *psymalloc) + { + asymbol **newsyms; + + if (*psymalloc == 0) + *psymalloc = 124; + else + *psymalloc *= 2; + if (output_bfd->outsymbols == (asymbol **) NULL) + newsyms = (asymbol **) malloc (*psymalloc * sizeof (asymbol *)); + else + newsyms = (asymbol **) realloc (output_bfd->outsymbols, + *psymalloc * sizeof (asymbol *)); + if (newsyms == (asymbol **) NULL) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + output_bfd->outsymbols = newsyms; + } + + output_bfd->outsymbols[output_bfd->symcount] = sym; + ++output_bfd->symcount; + + return true; +} + +/* Handle the symbols for an input BFD. */ + +boolean +_bfd_generic_link_output_symbols (output_bfd, input_bfd, info, psymalloc) + bfd *output_bfd; + bfd *input_bfd; + struct bfd_link_info *info; + size_t *psymalloc; +{ + asymbol **sym_ptr; + asymbol **sym_end; + + if (! generic_link_read_symbols (input_bfd)) + return false; + + /* Create a filename symbol if we are supposed to. */ + if (info->create_object_symbols_section != (asection *) NULL) + { + asection *sec; + + for (sec = input_bfd->sections; + sec != (asection *) NULL; + sec = sec->next) + { + if (sec->output_section == info->create_object_symbols_section) + { + asymbol *newsym; + + newsym = bfd_make_empty_symbol (input_bfd); + if (!newsym) + return false; + newsym->name = input_bfd->filename; + newsym->value = 0; + newsym->flags = BSF_LOCAL | BSF_FILE; + newsym->section = sec; + + if (! generic_add_output_symbol (output_bfd, psymalloc, + newsym)) + return false; + + break; + } + } + } + + /* Adjust the values of the globally visible symbols, and write out + local symbols. */ + sym_ptr = _bfd_generic_link_get_symbols (input_bfd); + sym_end = sym_ptr + _bfd_generic_link_get_symcount (input_bfd); + for (; sym_ptr < sym_end; sym_ptr++) + { + asymbol *sym; + struct generic_link_hash_entry *h; + boolean output; + + h = (struct generic_link_hash_entry *) NULL; + sym = *sym_ptr; + if ((sym->flags & (BSF_INDIRECT + | BSF_WARNING + | BSF_GLOBAL + | BSF_CONSTRUCTOR + | BSF_WEAK)) != 0 + || bfd_is_und_section (bfd_get_section (sym)) + || bfd_is_com_section (bfd_get_section (sym)) + || bfd_is_ind_section (bfd_get_section (sym))) + { + h = _bfd_generic_link_hash_lookup (_bfd_generic_hash_table (info), + bfd_asymbol_name (sym), + false, false, true); + if (h != (struct generic_link_hash_entry *) NULL) + { + /* Force all references to this symbol to point to + the same area in memory. It is possible that + this routine will be called with a hash table + other than a generic hash table, so we double + check that. */ + if (info->hash->creator == input_bfd->xvec) + { + if (h->sym != (asymbol *) NULL) + *sym_ptr = sym = h->sym; + } + + switch (h->root.type) + { + default: + case bfd_link_hash_new: + abort (); + case bfd_link_hash_undefined: + case bfd_link_hash_weak: + break; + case bfd_link_hash_defined: + sym->value = h->root.u.def.value; + sym->section = h->root.u.def.section; + sym->flags |= BSF_GLOBAL; + break; + case bfd_link_hash_common: + sym->value = h->root.u.c.size; + sym->flags |= BSF_GLOBAL; + if (! bfd_is_com_section (sym->section)) + { + BFD_ASSERT (bfd_is_und_section (sym->section)); + sym->section = bfd_com_section_ptr; + } + /* We do not set the section of the symbol to + h->root.u.c.section. That value was saved so + that we would know where to allocate the symbol + if it was defined. In this case the type is + still bfd_link_hash_common, so we did not define + it, so we do not want to use that section. */ + break; + } + } + } + + /* This switch is straight from the old code in + write_file_locals in ldsym.c. */ + if (info->strip == strip_some + && (bfd_hash_lookup (info->keep_hash, bfd_asymbol_name (sym), + false, false) + == (struct bfd_hash_entry *) NULL)) + output = false; + else if ((sym->flags & (BSF_GLOBAL | BSF_WEAK)) != 0) + { + /* If this symbol is marked as occurring now, rather + than at the end, output it now. This is used for + COFF C_EXT FCN symbols. FIXME: There must be a + better way. */ + if (bfd_asymbol_bfd (sym) == input_bfd + && (sym->flags & BSF_NOT_AT_END) != 0) + output = true; + else + output = false; + } + else if (bfd_is_ind_section (sym->section)) + output = false; + else if ((sym->flags & BSF_DEBUGGING) != 0) + { + if (info->strip == strip_none) + output = true; + else + output = false; + } + else if (bfd_is_und_section (sym->section) + || bfd_is_com_section (sym->section)) + output = false; + else if ((sym->flags & BSF_LOCAL) != 0) + { + if ((sym->flags & BSF_WARNING) != 0) + output = false; + else + { + switch (info->discard) + { + default: + case discard_all: + output = false; + break; + case discard_l: + if (bfd_asymbol_name (sym)[0] == info->lprefix[0] + && (info->lprefix_len == 1 + || strncmp (bfd_asymbol_name (sym), info->lprefix, + info->lprefix_len) == 0)) + output = false; + else + output = true; + break; + case discard_none: + output = true; + break; + } + } + } + else if ((sym->flags & BSF_CONSTRUCTOR)) + { + if (info->strip != strip_all) + output = true; + else + output = false; + } + else + abort (); + + if (output) + { + if (! generic_add_output_symbol (output_bfd, psymalloc, sym)) + return false; + if (h != (struct generic_link_hash_entry *) NULL) + h->written = true; + } + } + + return true; +} + +/* Write out a global symbol, if it hasn't already been written out. + This is called for each symbol in the hash table. */ + +boolean +_bfd_generic_link_write_global_symbol (h, data) + struct generic_link_hash_entry *h; + PTR data; +{ + struct generic_write_global_symbol_info *wginfo = + (struct generic_write_global_symbol_info *) data; + asymbol *sym; + + if (h->written) + return true; + + h->written = true; + + if (wginfo->info->strip == strip_all + || (wginfo->info->strip == strip_some + && bfd_hash_lookup (wginfo->info->keep_hash, h->root.root.string, + false, false) == NULL)) + return true; + + if (h->sym != (asymbol *) NULL) + { + sym = h->sym; + BFD_ASSERT (strcmp (bfd_asymbol_name (sym), h->root.root.string) == 0); + } + else + { + sym = bfd_make_empty_symbol (wginfo->output_bfd); + if (!sym) + return false; + sym->name = h->root.root.string; + sym->flags = 0; + } + + switch (h->root.type) + { + default: + case bfd_link_hash_new: + abort (); + case bfd_link_hash_undefined: + sym->section = bfd_und_section_ptr; + sym->value = 0; + break; + case bfd_link_hash_weak: + sym->section = bfd_und_section_ptr; + sym->value = 0; + sym->flags |= BSF_WEAK; + break; + case bfd_link_hash_defined: + sym->section = h->root.u.def.section; + sym->value = h->root.u.def.value; + break; + case bfd_link_hash_common: + sym->value = h->root.u.c.size; + if (sym->section == NULL) + sym->section = bfd_com_section_ptr; + else if (! bfd_is_com_section (sym->section)) + { + BFD_ASSERT (bfd_is_und_section (sym->section)); + sym->section = bfd_com_section_ptr; + } + /* Do not set the section; see _bfd_generic_link_output_symbols. */ + break; + case bfd_link_hash_indirect: + case bfd_link_hash_warning: + /* FIXME: What should we do here? */ + break; + } + + sym->flags |= BSF_GLOBAL; + + if (! generic_add_output_symbol (wginfo->output_bfd, wginfo->psymalloc, + sym)) + { + /* FIXME: No way to return failure. */ + abort (); + } + + return true; +} + +/* Create a relocation. */ + +boolean +_bfd_generic_reloc_link_order (abfd, info, sec, link_order) + bfd *abfd; + struct bfd_link_info *info; + asection *sec; + struct bfd_link_order *link_order; +{ + arelent *r; + + if (! info->relocateable) + abort (); + if (sec->orelocation == (arelent **) NULL) + abort (); + + r = (arelent *) bfd_alloc (abfd, sizeof (arelent)); + if (r == (arelent *) NULL) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + + r->address = link_order->offset; + r->howto = bfd_reloc_type_lookup (abfd, link_order->u.reloc.p->reloc); + if (r->howto == (const reloc_howto_type *) NULL) + { + bfd_set_error (bfd_error_bad_value); + return false; + } + + /* Get the symbol to use for the relocation. */ + if (link_order->type == bfd_section_reloc_link_order) + r->sym_ptr_ptr = link_order->u.reloc.p->u.section->symbol_ptr_ptr; + else + { + struct generic_link_hash_entry *h; + + h = _bfd_generic_link_hash_lookup (_bfd_generic_hash_table (info), + link_order->u.reloc.p->u.name, + false, false, true); + if (h == (struct generic_link_hash_entry *) NULL + || ! h->written) + { + if (! ((*info->callbacks->unattached_reloc) + (info, link_order->u.reloc.p->u.name, + (bfd *) NULL, (asection *) NULL, (bfd_vma) 0))) + return false; + bfd_set_error (bfd_error_bad_value); + return false; + } + r->sym_ptr_ptr = &h->sym; + } + + /* If this is an inplace reloc, write the addend to the object file. + Otherwise, store it in the reloc addend. */ + if (! r->howto->partial_inplace) + r->addend = link_order->u.reloc.p->addend; + else + { + bfd_size_type size; + bfd_reloc_status_type rstat; + bfd_byte *buf; + boolean ok; + + size = bfd_get_reloc_size (r->howto); + buf = (bfd_byte *) bfd_zmalloc (size); + if (buf == (bfd_byte *) NULL) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + rstat = _bfd_relocate_contents (r->howto, abfd, + 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 (! ((*info->callbacks->reloc_overflow) + (info, + (link_order->type == bfd_section_reloc_link_order + ? bfd_section_name (abfd, link_order->u.reloc.p->u.section) + : link_order->u.reloc.p->u.name), + r->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 (abfd, sec, (PTR) buf, + (file_ptr) link_order->offset, size); + free (buf); + if (! ok) + return false; + + r->addend = 0; + } + + sec->orelocation[sec->reloc_count] = r; + ++sec->reloc_count; + + return true; +} + +/* Allocate a new link_order for a section. */ + +struct bfd_link_order * +bfd_new_link_order (abfd, section) + bfd *abfd; + asection *section; +{ + struct bfd_link_order *new; + + new = ((struct bfd_link_order *) + bfd_alloc_by_size_t (abfd, sizeof (struct bfd_link_order))); + if (!new) + { + bfd_set_error (bfd_error_no_memory); + return NULL; + } + + new->type = bfd_undefined_link_order; + new->offset = 0; + new->size = 0; + new->next = (struct bfd_link_order *) NULL; + + if (section->link_order_tail != (struct bfd_link_order *) NULL) + section->link_order_tail->next = new; + else + section->link_order_head = new; + section->link_order_tail = new; + + return new; +} + +/* Default link order processing routine. Note that we can not handle + the reloc_link_order types here, since they depend upon the details + of how the particular backends generates relocs. */ + +boolean +_bfd_default_link_order (abfd, info, sec, link_order) + bfd *abfd; + struct bfd_link_info *info; + asection *sec; + struct bfd_link_order *link_order; +{ + switch (link_order->type) + { + case bfd_undefined_link_order: + case bfd_section_reloc_link_order: + case bfd_symbol_reloc_link_order: + default: + abort (); + case bfd_indirect_link_order: + return default_indirect_link_order (abfd, info, sec, link_order); + case bfd_fill_link_order: + return default_fill_link_order (abfd, info, sec, link_order); + case bfd_data_link_order: + return bfd_set_section_contents (abfd, sec, + (PTR) link_order->u.data.contents, + (file_ptr) link_order->offset, + link_order->size); + } +} + +/* Default routine to handle a bfd_fill_link_order. */ + +/*ARGSUSED*/ +static boolean +default_fill_link_order (abfd, info, sec, link_order) + bfd *abfd; + struct bfd_link_info *info; + asection *sec; + struct bfd_link_order *link_order; +{ + size_t size; + char *space; + size_t i; + int fill; + boolean result; + + BFD_ASSERT ((sec->flags & SEC_HAS_CONTENTS) != 0); + + size = (size_t) link_order->size; + space = (char *) malloc (size); + if (space == NULL && size != 0) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + + fill = link_order->u.fill.value; + for (i = 0; i < size; i += 2) + space[i] = fill >> 8; + for (i = 1; i < size; i += 2) + space[i] = fill; + result = bfd_set_section_contents (abfd, sec, space, + (file_ptr) link_order->offset, + link_order->size); + free (space); + return result; +} + +/* Default routine to handle a bfd_indirect_link_order. */ + +static boolean +default_indirect_link_order (output_bfd, info, output_section, link_order) + bfd *output_bfd; + struct bfd_link_info *info; + asection *output_section; + struct bfd_link_order *link_order; +{ + asection *input_section; + bfd *input_bfd; + bfd_byte *contents = NULL; + bfd_byte *new_contents; + + BFD_ASSERT ((output_section->flags & SEC_HAS_CONTENTS) != 0); + + if (link_order->size == 0) + return true; + + input_section = link_order->u.indirect.section; + input_bfd = input_section->owner; + + BFD_ASSERT (input_section->output_section == output_section); + BFD_ASSERT (input_section->output_offset == link_order->offset); + BFD_ASSERT (input_section->_cooked_size == link_order->size); + + if (info->relocateable + && input_section->reloc_count > 0 + && output_section->orelocation == (arelent **) NULL) + { + /* Space has not been allocated for the output relocations. + This can happen when we are called by a specific backend + because somebody is attempting to link together different + types of object files. Handling this case correctly is + difficult, and sometimes impossible. */ + abort (); + } + + /* Get the canonical symbols. The generic linker will always have + retrieved them by this point, but we may be being called by a + specific linker when linking different types of object files + together. */ + if (! generic_link_read_symbols (input_bfd)) + return false; + + /* Get and relocate the section contents. */ + contents = (bfd_byte *) malloc (bfd_section_size (input_bfd, input_section)); + if (contents == NULL && bfd_section_size (input_bfd, input_section) != 0) + { + bfd_set_error (bfd_error_no_memory); + goto error_return; + } + new_contents = (bfd_get_relocated_section_contents + (output_bfd, info, link_order, contents, info->relocateable, + _bfd_generic_link_get_symbols (input_bfd))); + if (!new_contents) + goto error_return; + + /* Output the section contents. */ + if (! bfd_set_section_contents (output_bfd, output_section, + (PTR) new_contents, + link_order->offset, link_order->size)) + goto error_return; + + if (contents != NULL) + free (contents); + return true; + + error_return: + if (contents != NULL) + free (contents); + return false; +} + +/* A little routine to count the number of relocs in a link_order + list. */ + +unsigned int +_bfd_count_link_order_relocs (link_order) + struct bfd_link_order *link_order; +{ + register unsigned int c; + register struct bfd_link_order *l; + + c = 0; + for (l = link_order; l != (struct bfd_link_order *) NULL; l = l->next) + { + if (l->type == bfd_section_reloc_link_order + || l->type == bfd_symbol_reloc_link_order) + ++c; + } + + return c; +} |
