OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /openrisc/trunk/gnu-old/binutils-2.18.50/ld/emultempl
    from Rev 164 to Rev 816
    Reverse comparison

Rev 164 → Rev 816

/spu_ovl.o Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream
spu_ovl.o Property changes : Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Index: netbsd.em =================================================================== --- netbsd.em (nonexistent) +++ netbsd.em (revision 816) @@ -0,0 +1,34 @@ +# This shell script emits a C file. -*- C -*- +# Copyright 2007 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +LDEMUL_BEFORE_PARSE=gldnetbsd_before_parse + +fragment <the_bfd, + avr_stub_section, + avr_no_stubs, + avr_debug_stubs, + avr_debug_relax, + avr_pc_wrap_around, + avr_replace_call_ret_sequences); +} + + +/* Makes a conservative estimate of the trampoline section size that could + be corrected later on. */ + +static void +avr_elf_${EMULATION_NAME}_before_allocation (void) +{ + int ret; + + gld${EMULATION_NAME}_before_allocation (); + + /* We only need stubs for the avr6 family. */ + if (strcmp ("${EMULATION_NAME}","avr6")) + avr_no_stubs = TRUE; + + avr_elf_set_global_bfd_parameters (); + + /* If generating a relocatable output file, then + we don't have to generate the trampolines. */ + if (link_info.relocatable) + avr_no_stubs = TRUE; + + if (avr_no_stubs) + return; + + ret = elf32_avr_setup_section_lists (link_info.output_bfd, &link_info); + + if (ret < 0) + einfo ("%X%P: can not setup the input section list: %E\n"); + + if (ret <= 0) + return; + + /* Call into the BFD backend to do the real "stub"-work. */ + if (! elf32_avr_size_stubs (link_info.output_bfd, &link_info, TRUE)) + einfo ("%X%P: can not size stub section: %E\n"); +} + +/* This is called before the input files are opened. We create a new + fake input file to hold the stub section and generate the section itself. */ + +static void +avr_elf_create_output_section_statements (void) +{ + flagword flags; + + stub_file = lang_add_input_file ("linker stubs", + lang_input_file_is_fake_enum, + NULL); + + stub_file->the_bfd = bfd_create ("linker stubs", link_info.output_bfd); + if (stub_file->the_bfd == NULL + || !bfd_set_arch_mach (stub_file->the_bfd, + bfd_get_arch (link_info.output_bfd), + bfd_get_mach (link_info.output_bfd))) + { + einfo ("%X%P: can not create stub BFD %E\n"); + return; + } + + /* Now we add the stub section. */ + + avr_stub_section = bfd_make_section_anyway (stub_file->the_bfd, + ".trampolines"); + if (avr_stub_section == NULL) + goto err_ret; + + flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE + | SEC_HAS_CONTENTS | SEC_RELOC | SEC_IN_MEMORY | SEC_KEEP); + if (!bfd_set_section_flags (stub_file->the_bfd, avr_stub_section, flags)) + goto err_ret; + + avr_stub_section->alignment_power = 1; + + ldlang_add_file (stub_file); + + return; + + err_ret: + einfo ("%X%P: can not make stub section: %E\n"); + return; +} + +/* Re-calculates the size of the stubs so that we won't waste space. */ + +static void +avr_elf_finish (void) +{ + if (!avr_no_stubs) + { + /* Now build the linker stubs. */ + if (stub_file->the_bfd->sections != NULL) + { + /* Call again the trampoline analyzer to initialize the trampoline + stubs with the correct symbol addresses. Since there could have + been relaxation, the symbol addresses that were found during + first call may no longer be correct. */ + if (!elf32_avr_size_stubs (link_info.output_bfd, &link_info, FALSE)) + { + einfo ("%X%P: can not size stub section: %E\n"); + return; + } + + if (!elf32_avr_build_stubs (&link_info)) + einfo ("%X%P: can not build stubs: %E\n"); + } + } + + gld${EMULATION_NAME}_finish (); +} + + +EOF + + +PARSE_AND_LIST_PROLOGUE=' + +#define OPTION_NO_CALL_RET_REPLACEMENT 301 +#define OPTION_PMEM_WRAP_AROUND 302 +#define OPTION_NO_STUBS 303 +#define OPTION_DEBUG_STUBS 304 +#define OPTION_DEBUG_RELAX 305 +' + +PARSE_AND_LIST_LONGOPTS=' + { "no-call-ret-replacement", no_argument, + NULL, OPTION_NO_CALL_RET_REPLACEMENT}, + { "pmem-wrap-around", required_argument, + NULL, OPTION_PMEM_WRAP_AROUND}, + { "no-stubs", no_argument, + NULL, OPTION_NO_STUBS}, + { "debug-stubs", no_argument, + NULL, OPTION_DEBUG_STUBS}, + { "debug-relax", no_argument, + NULL, OPTION_DEBUG_RELAX}, +' + +PARSE_AND_LIST_OPTIONS=' + fprintf (file, _(" --pmem-wrap-around= " + "Make the linker relaxation machine assume that a\n" + " " + " program counter wrap-around occures at address\n" + " " + " . Supported values: 8k, 16k, 32k and 64k.\n")); + fprintf (file, _(" --no-call-ret-replacement " + "The relaxation machine normally will\n" + " " + " substitute two immediately following call/ret\n" + " " + " instructions by a single jump instruction.\n" + " " + " This option disables this optimization.\n")); + fprintf (file, _(" --no-stubs " + "If the linker detects to attempt to access\n" + " " + " an instruction beyond 128k by a reloc that\n" + " " + " is limited to 128k max, it inserts a jump\n" + " " + " stub. You can de-active this with this switch.\n")); + fprintf (file, _(" --debug-stubs " + "Used for debugging avr-ld.\n")); + fprintf (file, _(" --debug-relax " + "Used for debugging avr-ld.\n")); +' + +PARSE_AND_LIST_ARGS_CASES=' + + case OPTION_PMEM_WRAP_AROUND: + { + /* This variable is defined in the bfd library. */ + if ((!strcmp (optarg,"32k")) || (!strcmp (optarg,"32K"))) + avr_pc_wrap_around = 32768; + else if ((!strcmp (optarg,"8k")) || (!strcmp (optarg,"8K"))) + avr_pc_wrap_around = 8192; + else if ((!strcmp (optarg,"16k")) || (!strcmp (optarg,"16K"))) + avr_pc_wrap_around = 16384; + else if ((!strcmp (optarg,"64k")) || (!strcmp (optarg,"64K"))) + avr_pc_wrap_around = 0x10000; + else + return FALSE; + } + break; + + case OPTION_DEBUG_STUBS: + avr_debug_stubs = TRUE; + break; + + case OPTION_DEBUG_RELAX: + avr_debug_relax = TRUE; + break; + + case OPTION_NO_STUBS: + avr_no_stubs = TRUE; + break; + + case OPTION_NO_CALL_RET_REPLACEMENT: + { + /* This variable is defined in the bfd library. */ + avr_replace_call_ret_sequences = FALSE; + } + break; +' + +# +# Put these extra avr-elf routines in ld_${EMULATION_NAME}_emulation +# +LDEMUL_BEFORE_ALLOCATION=avr_elf_${EMULATION_NAME}_before_allocation +LDEMUL_FINISH=avr_elf_finish +LDEMUL_CREATE_OUTPUT_SECTION_STATEMENTS=avr_elf_create_output_section_statements Index: elf-generic.em =================================================================== --- elf-generic.em (nonexistent) +++ elf-generic.em (revision 816) @@ -0,0 +1,86 @@ +# This shell script emits a C file. -*- C -*- +# Copyright 2006, 2007, 2008 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +# This file is sourced from elf32.em and from ELF targets that use +# generic.em. +# +fragment <xvec->flavour == bfd_target_elf_flavour + && !link_info.relocatable) + { + bfd_size_type phdr_size; + + phdr_size = elf_tdata (link_info.output_bfd)->program_header_size; + /* If we don't have user supplied phdrs, throw away any + previous linker generated program headers. */ + if (lang_phdr_list == NULL) + elf_tdata (link_info.output_bfd)->segment_map = NULL; + if (!_bfd_elf_map_sections_to_segments (link_info.output_bfd, + &link_info)) + einfo ("%F%P: map sections to segments failed: %E\n"); + + if (phdr_size + != elf_tdata (link_info.output_bfd)->program_header_size) + { + if (tries > 6) + /* The first few times we allow any change to + phdr_size . */ + need_layout = TRUE; + else if (phdr_size + < elf_tdata (link_info.output_bfd)->program_header_size) + /* After that we only allow the size to grow. */ + need_layout = TRUE; + else + elf_tdata (link_info.output_bfd)->program_header_size + = phdr_size; + } + } + } + while (need_layout && --tries); + + if (tries == 0) + einfo (_("%P%F: looping in map_segments")); +} +EOF Index: aix.em =================================================================== --- aix.em (nonexistent) +++ aix.em (revision 816) @@ -0,0 +1,1398 @@ +# This shell script emits a C file. -*- C -*- +# It does some substitutions. +if [ -z "$MACHINE" ]; then + OUTPUT_ARCH=${ARCH} +else + OUTPUT_ARCH=${ARCH}:${MACHINE} +fi +fragment < + AIX support by Ian Lance Taylor + AIX 64 bit support by Tom Rix + + This file is part of the GNU Binutils. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#define TARGET_IS_${EMULATION_NAME} + +#include "sysdep.h" +#include "bfd.h" +#include "libiberty.h" +#include "safe-ctype.h" +#include "getopt.h" +#include "obstack.h" +#include "bfdlink.h" + +#include "ld.h" +#include "ldmain.h" +#include "ldmisc.h" +#include "ldexp.h" +#include "ldlang.h" +#include "ldfile.h" +#include "ldemul.h" +#include "ldctor.h" +#include + +#include "coff/internal.h" +#include "coff/xcoff.h" +#include "libcoff.h" +#include "libxcoff.h" + +static void gld${EMULATION_NAME}_read_file (const char *, bfd_boolean); +static void gld${EMULATION_NAME}_free (void *); +static void gld${EMULATION_NAME}_find_relocs (lang_statement_union_type *); +static void gld${EMULATION_NAME}_find_exp_assignment (etree_type *); + + +/* The file alignment required for each section. */ +static unsigned long file_align; + +/* The maximum size the stack is permitted to grow. This is stored in + the a.out header. */ +static unsigned long maxstack; + +/* The maximum data size. This is stored in the a.out header. */ +static unsigned long maxdata; + +/* Whether to perform garbage collection. */ +static int gc = 1; + +/* The module type to use. */ +static unsigned short modtype = ('1' << 8) | 'L'; + +/* Whether the .text section must be read-only (i.e., no relocs + permitted). */ +static int textro; + +/* Whether to implement Unix like linker semantics. */ +static int unix_ld; + +/* Structure used to hold import file list. */ + +struct filelist +{ + struct filelist *next; + const char *name; +}; + +/* List of import files. */ +static struct filelist *import_files; + +/* List of export symbols read from the export files. */ + +struct export_symbol_list +{ + struct export_symbol_list *next; + const char *name; +}; + +static struct export_symbol_list *export_symbols; + +/* Maintains the 32 or 64 bit mode state of import file */ +static unsigned int symbol_mode = 0x04; + +/* Which symbol modes are valid */ +static unsigned int symbol_mode_mask = 0x0d; + +/* Whether this is a 64 bit link */ +static int is_64bit = 0; + +/* Which syscalls from import file are valid */ +static unsigned int syscall_mask = 0x77; + +/* fake file for -binitfini support */ +static lang_input_statement_type *initfini_file; + +/* Whether to do run time linking + -brtl enables, -bnortl and -bnortllib disable. */ +static int rtld; + +/* Explicit command line library path, -blibpath */ +static char *command_line_blibpath = NULL; + +/* This routine is called before anything else is done. */ + +static void +gld${EMULATION_NAME}_before_parse (void) +{ + ldfile_set_output_arch ("${OUTPUT_ARCH}", bfd_arch_`echo ${ARCH} | sed -e 's/:.*//'`); + + config.has_shared = TRUE; + + /* The link_info.[init|fini]_functions are initialized in ld/lexsup.c. + Override them here so we can use the link_info.init_function as a + state flag that lets the backend know that -binitfini has been done. */ + + link_info.init_function = NULL; + link_info.fini_function = NULL; +} + +/* Handle AIX specific options. */ + +enum + { + OPTION_IGNORE = 300, + OPTION_AUTOIMP, + OPTION_ERNOTOK, + OPTION_EROK, + OPTION_EXPORT, + OPTION_IMPORT, + OPTION_INITFINI, + OPTION_LOADMAP, + OPTION_MAXDATA, + OPTION_MAXSTACK, + OPTION_MODTYPE, + OPTION_NOAUTOIMP, + OPTION_NOSTRCMPCT, + OPTION_PD, + OPTION_PT, + OPTION_STRCMPCT, + OPTION_UNIX, + OPTION_32, + OPTION_64, + OPTION_LIBPATH, + OPTION_NOLIBPATH, + }; + +static void +gld${EMULATION_NAME}_add_options + (int ns, char **shortopts, int nl, struct option **longopts, + int nrl ATTRIBUTE_UNUSED, struct option **really_longopts ATTRIBUTE_UNUSED) +{ + static const char xtra_short[] = "D:H:KT:z"; + static const struct option xtra_long[] = { + /* -binitfini has special handling in the linker backend. The native linker + uses the arguemnts to generate a table of init and fini functions for + the executable. The important use for this option is to support aix 4.2+ + c++ constructors and destructors. This is tied into gcc via collect2.c. + + The function table is accessed by the runtime linker/loader by checking if + the first symbol in the loader symbol table is __rtinit. The gnu linker + generates this symbol and makes it the first loader symbol. */ + + {"basis", no_argument, NULL, OPTION_IGNORE}, + {"bautoimp", no_argument, NULL, OPTION_AUTOIMP}, + {"bcomprld", no_argument, NULL, OPTION_IGNORE}, + {"bcrld", no_argument, NULL, OPTION_IGNORE}, + {"bcror31", no_argument, NULL, OPTION_IGNORE}, + {"bD", required_argument, NULL, OPTION_MAXDATA}, + {"bE", required_argument, NULL, OPTION_EXPORT}, + {"bernotok", no_argument, NULL, OPTION_ERNOTOK}, + {"berok", no_argument, NULL, OPTION_EROK}, + {"berrmsg", no_argument, NULL, OPTION_IGNORE}, + {"bexport", required_argument, NULL, OPTION_EXPORT}, + {"bf", no_argument, NULL, OPTION_ERNOTOK}, + {"bgc", no_argument, &gc, 1}, + {"bh", required_argument, NULL, OPTION_IGNORE}, + {"bhalt", required_argument, NULL, OPTION_IGNORE}, + {"bI", required_argument, NULL, OPTION_IMPORT}, + {"bimport", required_argument, NULL, OPTION_IMPORT}, + {"binitfini", required_argument, NULL, OPTION_INITFINI}, + {"bl", required_argument, NULL, OPTION_LOADMAP}, + {"bloadmap", required_argument, NULL, OPTION_LOADMAP}, + {"bmaxdata", required_argument, NULL, OPTION_MAXDATA}, + {"bmaxstack", required_argument, NULL, OPTION_MAXSTACK}, + {"bM", required_argument, NULL, OPTION_MODTYPE}, + {"bmodtype", required_argument, NULL, OPTION_MODTYPE}, + {"bnoautoimp", no_argument, NULL, OPTION_NOAUTOIMP}, + {"bnodelcsect", no_argument, NULL, OPTION_IGNORE}, + {"bnoentry", no_argument, NULL, OPTION_IGNORE}, + {"bnogc", no_argument, &gc, 0}, + {"bnso", no_argument, NULL, OPTION_NOAUTOIMP}, + {"bnostrcmpct", no_argument, NULL, OPTION_NOSTRCMPCT}, + {"bnotextro", no_argument, &textro, 0}, + {"bnro", no_argument, &textro, 0}, + {"bpD", required_argument, NULL, OPTION_PD}, + {"bpT", required_argument, NULL, OPTION_PT}, + {"bro", no_argument, &textro, 1}, + {"brtl", no_argument, &rtld, 1}, + {"bnortl", no_argument, &rtld, 0}, + {"bnortllib", no_argument, &rtld, 0}, + {"bS", required_argument, NULL, OPTION_MAXSTACK}, + {"bso", no_argument, NULL, OPTION_AUTOIMP}, + {"bstrcmpct", no_argument, NULL, OPTION_STRCMPCT}, + {"btextro", no_argument, &textro, 1}, + {"b32", no_argument, NULL, OPTION_32}, + {"b64", no_argument, NULL, OPTION_64}, + {"static", no_argument, NULL, OPTION_NOAUTOIMP}, + {"unix", no_argument, NULL, OPTION_UNIX}, + {"blibpath", required_argument, NULL, OPTION_LIBPATH}, + {"bnolibpath", required_argument, NULL, OPTION_NOLIBPATH}, + {NULL, no_argument, NULL, 0} + }; + + /* Options supported by the AIX linker which we do not support: -f, + -S, -v, -Z, -bbindcmds, -bbinder, -bbindopts, -bcalls, -bcaps, + -bcror15, -bdebugopt, -bdbg, -bdelcsect, -bex?, -bfilelist, -bfl, + -bgcbypass, -bglink, -binsert, -bi, -bloadmap, -bl, -bmap, -bnl, + -bnobind, -bnocomprld, -bnocrld, -bnoerrmsg, -bnoglink, + -bnoloadmap, -bnl, -bnoobjreorder, -bnoquiet, -bnoreorder, + -bnotypchk, -bnox, -bquiet, -bR, -brename, -breorder, -btypchk, + -bx, -bX, -bxref. */ + + *shortopts = (char *) xrealloc (*shortopts, ns + sizeof (xtra_short)); + memcpy (*shortopts + ns, &xtra_short, sizeof (xtra_short)); + *longopts = xrealloc (*longopts, + nl * sizeof (struct option) + sizeof (xtra_long)); + memcpy (*longopts + nl, &xtra_long, sizeof (xtra_long)); +} + +static bfd_boolean +gld${EMULATION_NAME}_parse_args (int argc, char **argv) +{ + int indx; + + /* If the current option starts with -b, change the first : to an =. + The AIX linker uses : to separate the option from the argument; + changing it to = lets us treat it as a getopt option. */ + indx = optind; + if (indx == 0) + indx = 1; + + if (indx < argc && CONST_STRNEQ (argv[indx], "-b")) + { + char *s; + + for (s = argv[indx]; *s != '\0'; s++) + { + if (*s == ':') + { + *s = '='; + break; + } + } + } + return FALSE; +} + +static bfd_boolean +gld${EMULATION_NAME}_handle_option (int optc) +{ + bfd_signed_vma val; + const char *end; + + switch (optc) + { + default: + return FALSE; + + case 0: + /* Long option which just sets a flag. */ + break; + + case 'D': + val = bfd_scan_vma (optarg, &end, 0); + if (*end != '\0') + einfo ("%P: warning: ignoring invalid -D number %s\n", optarg); + else if (val != -1) + lang_section_start (".data", exp_intop (val), NULL); + break; + + case 'H': + val = bfd_scan_vma (optarg, &end, 0); + if (*end != '\0' || (val & (val - 1)) != 0) + einfo ("%P: warning: ignoring invalid -H number %s\n", optarg); + else + file_align = val; + break; + + case 'K': + case 'z': + /* FIXME: This should use the page size for the target system. */ + file_align = 4096; + break; + + case 'T': + /* On AIX this is the same as GNU ld -Ttext. When we see -T + number, we assume the AIX option is intended. Otherwise, we + assume the usual GNU ld -T option is intended. We can't just + ignore the AIX option, because gcc passes it to the linker. */ + val = bfd_scan_vma (optarg, &end, 0); + if (*end != '\0') + return FALSE; + lang_section_start (".text", exp_intop (val), NULL); + break; + + case OPTION_IGNORE: + break; + + case OPTION_INITFINI: + { + /* + * The aix linker init fini has the format : + * + * -binitfini:[ Initial][:Termination][:Priority] + * + * it allows the Termination and Priority to be optional. + * + * Since we support only one init/fini pair, we ignore the Priority. + * + * Define the special symbol __rtinit. + * + * strtok does not correctly handle the case of -binitfini::fini: so + * do it by hand + */ + char *t, *i, *f; + + i = t = optarg; + while (*t && ':' != *t) + t++; + if (*t) + *t++ = 0; + + if (0 != strlen (i)) + link_info.init_function = i; + + f = t; + while (*t && ':' != *t) + t++; + *t = 0; + + if (0 != strlen (f)) + link_info.fini_function = f; + } + break; + + case OPTION_AUTOIMP: + link_info.static_link = FALSE; + break; + + case OPTION_ERNOTOK: + force_make_executable = FALSE; + break; + + case OPTION_EROK: + force_make_executable = TRUE; + break; + + case OPTION_EXPORT: + gld${EMULATION_NAME}_read_file (optarg, FALSE); + break; + + case OPTION_IMPORT: + { + struct filelist *n; + struct filelist **flpp; + + n = (struct filelist *) xmalloc (sizeof (struct filelist)); + n->next = NULL; + n->name = optarg; + flpp = &import_files; + while (*flpp != NULL) + flpp = &(*flpp)->next; + *flpp = n; + } + break; + + case OPTION_LOADMAP: + config.map_filename = optarg; + break; + + case OPTION_MAXDATA: + val = bfd_scan_vma (optarg, &end, 0); + if (*end != '\0') + einfo ("%P: warning: ignoring invalid -bmaxdata number %s\n", optarg); + else + maxdata = val; + break; + + case OPTION_MAXSTACK: + val = bfd_scan_vma (optarg, &end, 0); + if (*end != '\0') + einfo ("%P: warning: ignoring invalid -bmaxstack number %s\n", + optarg); + else + maxstack = val; + break; + + case OPTION_MODTYPE: + if (*optarg == 'S') + { + link_info.shared = TRUE; + ++optarg; + } + if (*optarg == '\0' || optarg[1] == '\0') + einfo ("%P: warning: ignoring invalid module type %s\n", optarg); + else + modtype = (*optarg << 8) | optarg[1]; + break; + + case OPTION_NOAUTOIMP: + link_info.static_link = TRUE; + break; + + case OPTION_NOSTRCMPCT: + link_info.traditional_format = TRUE; + break; + + case OPTION_PD: + /* This sets the page that the .data section is supposed to + start on. The offset within the page should still be the + offset within the file, so we need to build an appropriate + expression. */ + val = bfd_scan_vma (optarg, &end, 0); + if (*end != '\0') + einfo ("%P: warning: ignoring invalid -pD number %s\n", optarg); + else + { + etree_type *t; + + t = exp_binop ('+', + exp_intop (val), + exp_binop ('&', + exp_nameop (NAME, "."), + exp_intop (0xfff))); + t = exp_binop ('&', + exp_binop ('+', t, exp_intop (31)), + exp_intop (~(bfd_vma) 31)); + lang_section_start (".data", t, NULL); + } + break; + + case OPTION_PT: + /* This set the page that the .text section is supposed to start + on. The offset within the page should still be the offset + within the file. */ + val = bfd_scan_vma (optarg, &end, 0); + if (*end != '\0') + einfo ("%P: warning: ignoring invalid -pT number %s\n", optarg); + else + { + etree_type *t; + + t = exp_binop ('+', + exp_intop (val), + exp_nameop (SIZEOF_HEADERS, NULL)); + t = exp_binop ('&', + exp_binop ('+', t, exp_intop (31)), + exp_intop (~(bfd_vma) 31)); + lang_section_start (".text", t, NULL); + } + break; + + case OPTION_STRCMPCT: + link_info.traditional_format = FALSE; + break; + + case OPTION_UNIX: + unix_ld = TRUE; + break; + + case OPTION_32: + is_64bit = 0; + syscall_mask = 0x77; + symbol_mode_mask = 0x0d; + break; + + case OPTION_64: + is_64bit = 1; + syscall_mask = 0xcc; + symbol_mode_mask = 0x0e; + break; + + case OPTION_LIBPATH: + command_line_blibpath = optarg; + break; + + case OPTION_NOLIBPATH: + command_line_blibpath = NULL; + break; + + } + + return TRUE; +} + +/* This is called when an input file can not be recognized as a BFD + object or an archive. If the file starts with #!, we must treat it + as an import file. This is for AIX compatibility. */ + +static bfd_boolean +gld${EMULATION_NAME}_unrecognized_file (lang_input_statement_type *entry) +{ + FILE *e; + bfd_boolean ret; + + e = fopen (entry->filename, FOPEN_RT); + if (e == NULL) + return FALSE; + + ret = FALSE; + + if (getc (e) == '#' && getc (e) == '!') + { + struct filelist *n; + struct filelist **flpp; + + n = (struct filelist *) xmalloc (sizeof (struct filelist)); + n->next = NULL; + n->name = entry->filename; + flpp = &import_files; + while (*flpp != NULL) + flpp = &(*flpp)->next; + *flpp = n; + + ret = TRUE; + entry->loaded = TRUE; + } + + fclose (e); + + return ret; +} + +/* This is called after the input files have been opened. */ + +static void +gld${EMULATION_NAME}_after_open (void) +{ + bfd_boolean r; + struct set_info *p; + + /* Call ldctor_build_sets, after pretending that this is a + relocatable link. We do this because AIX requires relocation + entries for all references to symbols, even in a final + executable. Of course, we only want to do this if we are + producing an XCOFF output file. */ + r = link_info.relocatable; + if (strstr (bfd_get_target (link_info.output_bfd), "xcoff") != NULL) + link_info.relocatable = TRUE; + ldctor_build_sets (); + link_info.relocatable = r; + + /* For each set, record the size, so that the XCOFF backend can + output the correct csect length. */ + for (p = sets; p != (struct set_info *) NULL; p = p->next) + { + bfd_size_type size; + + /* If the symbol is defined, we may have been invoked from + collect, and the sets may already have been built, so we do + not do anything. */ + if (p->h->type == bfd_link_hash_defined + || p->h->type == bfd_link_hash_defweak) + continue; + + if (p->reloc != BFD_RELOC_CTOR) + { + /* Handle this if we need to. */ + abort (); + } + + size = (p->count + 2) * 4; + if (!bfd_xcoff_link_record_set (link_info.output_bfd, &link_info, + p->h, size)) + einfo ("%F%P: bfd_xcoff_link_record_set failed: %E\n"); + } +} + +/* This is called after the sections have been attached to output + sections, but before any sizes or addresses have been set. */ + +static void +gld${EMULATION_NAME}_before_allocation (void) +{ + struct filelist *fl; + struct export_symbol_list *el; + char *libpath; + asection *special_sections[XCOFF_NUMBER_OF_SPECIAL_SECTIONS]; + int i; + + /* Handle the import and export files, if any. */ + for (fl = import_files; fl != NULL; fl = fl->next) + gld${EMULATION_NAME}_read_file (fl->name, TRUE); + for (el = export_symbols; el != NULL; el = el->next) + { + struct bfd_link_hash_entry *h; + + h = bfd_link_hash_lookup (link_info.hash, el->name, FALSE, FALSE, FALSE); + if (h == NULL) + einfo ("%P%F: bfd_link_hash_lookup of export symbol failed: %E\n"); + if (!bfd_xcoff_export_symbol (link_info.output_bfd, &link_info, h)) + einfo ("%P%F: bfd_xcoff_export_symbol failed: %E\n"); + } + + /* Track down all relocations called for by the linker script (these + are typically constructor/destructor entries created by + CONSTRUCTORS) and let the backend know it will need to create + .loader relocs for them. */ + lang_for_each_statement (gld${EMULATION_NAME}_find_relocs); + + /* Precedence of LIBPATH + -blibpath: native support always first + -rpath: gnu extension + -L build from command line -L's */ + if (command_line_blibpath != NULL) + libpath = command_line_blibpath; + else if (command_line.rpath != NULL) + libpath = command_line.rpath; + else if (search_head == NULL) + libpath = (char *) ""; + else + { + size_t len; + search_dirs_type *search; + + /* PR ld/4023: Strip sysroot prefix from any paths + being inserted into the output binary's DT_RPATH. */ + if (ld_sysroot != NULL + && * ld_sysroot != 0) + { + const char * name = search_head->name; + size_t ld_sysroot_len = strlen (ld_sysroot); + + if (strncmp (name, ld_sysroot, ld_sysroot_len) == 0) + name += ld_sysroot_len; + + len = strlen (name); + libpath = xmalloc (len + 1); + strcpy (libpath, name); + + for (search = search_head->next; search != NULL; search = search->next) + { + size_t nlen; + + name = search->name; + if (strncmp (name, ld_sysroot, ld_sysroot_len) == 0) + name += ld_sysroot_len; + + nlen = strlen (name); + libpath = xrealloc (libpath, len + nlen + 2); + libpath[len] = ':'; + strcpy (libpath + len + 1, name); + len += nlen + 1; + } + } + else + { + len = strlen (search_head->name); + libpath = xmalloc (len + 1); + strcpy (libpath, search_head->name); + + for (search = search_head->next; search != NULL; search = search->next) + { + size_t nlen; + + nlen = strlen (search->name); + libpath = xrealloc (libpath, len + nlen + 2); + libpath[len] = ':'; + strcpy (libpath + len + 1, search->name); + len += nlen + 1; + } + } + } + + /* Let the XCOFF backend set up the .loader section. */ + if (!bfd_xcoff_size_dynamic_sections + (link_info.output_bfd, &link_info, libpath, entry_symbol.name, file_align, + maxstack, maxdata, gc && !unix_ld ? TRUE : FALSE, + modtype, textro ? TRUE : FALSE, unix_ld, special_sections, + rtld ? TRUE : FALSE)) + einfo ("%P%F: failed to set dynamic section sizes: %E\n"); + + /* Look through the special sections, and put them in the right + place in the link ordering. This is especially magic. */ + for (i = 0; i < XCOFF_NUMBER_OF_SPECIAL_SECTIONS; i++) + { + asection *sec; + lang_output_section_statement_type *os; + lang_statement_union_type **pls; + lang_input_section_type *is; + const char *oname; + bfd_boolean start; + + sec = special_sections[i]; + if (sec == NULL) + continue; + + /* Remove this section from the list of the output section. + This assumes we know what the script looks like. */ + is = NULL; + os = lang_output_section_find (sec->output_section->name); + if (os == NULL) + einfo ("%P%F: can't find output section %s\n", + sec->output_section->name); + + for (pls = &os->children.head; *pls != NULL; pls = &(*pls)->header.next) + { + if ((*pls)->header.type == lang_input_section_enum + && (*pls)->input_section.section == sec) + { + is = (lang_input_section_type *) * pls; + *pls = (*pls)->header.next; + break; + } + + if ((*pls)->header.type == lang_wild_statement_enum) + { + lang_statement_union_type **pwls; + + for (pwls = &(*pls)->wild_statement.children.head; + *pwls != NULL; pwls = &(*pwls)->header.next) + { + + if ((*pwls)->header.type == lang_input_section_enum + && (*pwls)->input_section.section == sec) + { + is = (lang_input_section_type *) * pwls; + *pwls = (*pwls)->header.next; + break; + } + } + + if (is != NULL) + break; + } + } + + if (is == NULL) + { + einfo ("%P%F: can't find %s in output section\n", + bfd_get_section_name (sec->owner, sec)); + } + + /* Now figure out where the section should go. */ + switch (i) + { + + default: /* to avoid warnings */ + case XCOFF_SPECIAL_SECTION_TEXT: + /* _text */ + oname = ".text"; + start = TRUE; + break; + + case XCOFF_SPECIAL_SECTION_ETEXT: + /* _etext */ + oname = ".text"; + start = FALSE; + break; + + case XCOFF_SPECIAL_SECTION_DATA: + /* _data */ + oname = ".data"; + start = TRUE; + break; + + case XCOFF_SPECIAL_SECTION_EDATA: + /* _edata */ + oname = ".data"; + start = FALSE; + break; + + case XCOFF_SPECIAL_SECTION_END: + case XCOFF_SPECIAL_SECTION_END2: + /* _end and end */ + oname = ".bss"; + start = FALSE; + break; + } + + os = lang_output_section_find (oname); + + if (start) + { + is->header.next = os->children.head; + os->children.head = (lang_statement_union_type *) is; + } + else + { + is->header.next = NULL; + lang_statement_append (&os->children, + (lang_statement_union_type *) is, + &is->header.next); + } + } + + before_allocation_default (); +} + +static char * +gld${EMULATION_NAME}_choose_target (int argc, char **argv) +{ + int i, j, jmax; + static char *from_outside; + static char *from_inside; + static char *argv_to_target[][2] = { + {NULL, "${OUTPUT_FORMAT}"}, + {"-b32", "${OUTPUT_FORMAT_32BIT}"}, + {"-b64", "${OUTPUT_FORMAT_64BIT}"}, + }; + + jmax = 3; + + from_outside = getenv (TARGET_ENVIRON); + if (from_outside != (char *) NULL) + return from_outside; + + /* Set to default. */ + from_inside = argv_to_target[0][1]; + for (i = 1; i < argc; i++) + { + for (j = 1; j < jmax; j++) + { + if (0 == strcmp (argv[i], argv_to_target[j][0])) + from_inside = argv_to_target[j][1]; + } + } + + return from_inside; +} + +/* Returns + 1 : state changed + 0 : no change */ +static int +change_symbol_mode (char *input) +{ + char *symbol_mode_string[] = { + "# 32", /* 0x01 */ + "# 64", /* 0x02 */ + "# no32", /* 0x04 */ + "# no64", /* 0x08 */ + NULL, + }; + + unsigned int bit; + char *string; + + for (bit = 0;; bit++) + { + string = symbol_mode_string[bit]; + if (string == NULL) + return 0; + + if (0 == strcmp (input, string)) + { + symbol_mode = (1 << bit); + return 1; + } + } + /* should not be here */ + return 0; +} + +/* Returns + 1 : yes + 0 : ignore + -1 : error, try something else */ +static int +is_syscall (char *input, unsigned int *flag) +{ + unsigned int bit; + char *string; + + struct sc { + char *syscall_string; + unsigned int flag; + } s [] = { + { "svc" /* 0x01 */, XCOFF_SYSCALL32 }, + { "svc32" /* 0x02 */, XCOFF_SYSCALL32 }, + { "svc3264" /* 0x04 */, XCOFF_SYSCALL32 | XCOFF_SYSCALL64 }, + { "svc64" /* 0x08 */, XCOFF_SYSCALL64 }, + { "syscall" /* 0x10 */, XCOFF_SYSCALL32 }, + { "syscall32" /* 0x20 */, XCOFF_SYSCALL32 }, + { "syscall3264" /* 0x40 */, XCOFF_SYSCALL32 | XCOFF_SYSCALL64 }, + { "syscall64" /* 0x80 */, XCOFF_SYSCALL64 }, + { NULL, 0 }, + }; + + *flag = 0; + + for (bit = 0;; bit++) + { + string = s[bit].syscall_string; + if (string == NULL) + return -1; + + if (0 == strcmp (input, string)) + { + if (1 << bit & syscall_mask) + { + *flag = s[bit].flag; + return 1; + } + else + { + return 0; + } + } + } + /* should not be here */ + return -1; +} + +/* Read an import or export file. For an import file, this is called + by the before_allocation emulation routine. For an export file, + this is called by the handle_option emulation routine. */ + +static void +gld${EMULATION_NAME}_read_file (const char *filename, bfd_boolean import) +{ + struct obstack *o; + FILE *f; + int lineno; + int c; + bfd_boolean keep; + const char *imppath; + const char *impfile; + const char *impmember; + + o = (struct obstack *) xmalloc (sizeof (struct obstack)); + obstack_specify_allocation (o, 0, 0, xmalloc, gld${EMULATION_NAME}_free); + + f = fopen (filename, FOPEN_RT); + if (f == NULL) + { + bfd_set_error (bfd_error_system_call); + einfo ("%F%s: %E\n", filename); + } + + keep = FALSE; + + imppath = NULL; + impfile = NULL; + impmember = NULL; + + lineno = 0; + + /* Default to 32 and 64 bit mode + symbols at top of /lib/syscalls.exp do not have a mode modifier and they + are not repeated, assume 64 bit routines also want to use them. + See the routine change_symbol_mode for more information. */ + + symbol_mode = 0x04; + + while ((c = getc (f)) != EOF) + { + char *s; + char *symname; + unsigned int syscall_flag = 0; + bfd_vma address; + struct bfd_link_hash_entry *h; + + if (c != '\n') + { + obstack_1grow (o, c); + continue; + } + + obstack_1grow (o, '\0'); + ++lineno; + + s = (char *) obstack_base (o); + while (ISSPACE (*s)) + ++s; + if (*s == '\0' + || *s == '*' + || change_symbol_mode (s) + || (*s == '#' && s[1] == ' ') + || (!import && *s == '#' && s[1] == '!')) + { + obstack_free (o, obstack_base (o)); + continue; + } + + if (*s == '#' && s[1] == '!') + { + s += 2; + while (ISSPACE (*s)) + ++s; + if (*s == '\0') + { + imppath = NULL; + impfile = NULL; + impmember = NULL; + obstack_free (o, obstack_base (o)); + } + else if (*s == '(') + einfo ("%F%s%d: #! ([member]) is not supported in import files\n", + filename, lineno); + else + { + char cs; + char *file; + + (void) obstack_finish (o); + keep = TRUE; + imppath = s; + file = NULL; + while (!ISSPACE (*s) && *s != '(' && *s != '\0') + { + if (*s == '/') + file = s + 1; + ++s; + } + if (file != NULL) + { + file[-1] = '\0'; + impfile = file; + if (imppath == file - 1) + imppath = "/"; + } + else + { + impfile = imppath; + imppath = ""; + } + cs = *s; + *s = '\0'; + while (ISSPACE (cs)) + { + ++s; + cs = *s; + } + if (cs != '(') + { + impmember = ""; + if (cs != '\0') + einfo ("%s:%d: warning: syntax error in import file\n", + filename, lineno); + } + else + { + ++s; + impmember = s; + while (*s != ')' && *s != '\0') + ++s; + if (*s == ')') + *s = '\0'; + else + einfo ("%s:%d: warning: syntax error in import file\n", + filename, lineno); + } + } + + continue; + } + + if (symbol_mode & symbol_mode_mask) + { + /* This is a symbol to be imported or exported. */ + symname = s; + syscall_flag = 0; + address = (bfd_vma) -1; + + while (!ISSPACE (*s) && *s != '\0') + ++s; + if (*s != '\0') + { + char *se; + + *s++ = '\0'; + + while (ISSPACE (*s)) + ++s; + + se = s; + while (!ISSPACE (*se) && *se != '\0') + ++se; + if (*se != '\0') + { + *se++ = '\0'; + while (ISSPACE (*se)) + ++se; + if (*se != '\0') + einfo ("%s%d: warning: syntax error in import/export file\n", + filename, lineno); + } + + if (s != se) + { + int status; + const char *end; + + status = is_syscall (s, &syscall_flag); + + if (0 > status) + { + /* not a system call, check for address */ + address = bfd_scan_vma (s, &end, 0); + if (*end != '\0') + { + einfo ("%s:%d: warning: syntax error in import/export file\n", + filename, lineno); + + } + } + } + } + + if (!import) + { + struct export_symbol_list *n; + + ldlang_add_undef (symname); + n = ((struct export_symbol_list *) + xmalloc (sizeof (struct export_symbol_list))); + n->next = export_symbols; + n->name = xstrdup (symname); + export_symbols = n; + } + else + { + h = bfd_link_hash_lookup (link_info.hash, symname, FALSE, FALSE, + TRUE); + if (h == NULL || h->type == bfd_link_hash_new) + { + /* We can just ignore attempts to import an unreferenced + symbol. */ + } + else + { + if (!bfd_xcoff_import_symbol (link_info.output_bfd, + &link_info, h, + address, imppath, impfile, + impmember, syscall_flag)) + einfo ("%X%s:%d: failed to import symbol %s: %E\n", + filename, lineno, symname); + } + } + } + obstack_free (o, obstack_base (o)); + } + + if (obstack_object_size (o) > 0) + { + einfo ("%s:%d: warning: ignoring unterminated last line\n", + filename, lineno); + obstack_free (o, obstack_base (o)); + } + + if (!keep) + { + obstack_free (o, NULL); + free (o); + } +} + +/* This routine saves us from worrying about declaring free. */ + +static void +gld${EMULATION_NAME}_free (void *p) +{ + free (p); +} + +/* This is called by the before_allocation routine via + lang_for_each_statement. It looks for relocations and assignments + to symbols. */ + +static void +gld${EMULATION_NAME}_find_relocs (lang_statement_union_type *s) +{ + if (s->header.type == lang_reloc_statement_enum) + { + lang_reloc_statement_type *rs; + + rs = &s->reloc_statement; + if (rs->name == NULL) + einfo ("%F%P: only relocations against symbols are permitted\n"); + if (!bfd_xcoff_link_count_reloc (link_info.output_bfd, &link_info, + rs->name)) + einfo ("%F%P: bfd_xcoff_link_count_reloc failed: %E\n"); + } + + if (s->header.type == lang_assignment_statement_enum) + gld${EMULATION_NAME}_find_exp_assignment (s->assignment_statement.exp); +} + +/* Look through an expression for an assignment statement. */ + +static void +gld${EMULATION_NAME}_find_exp_assignment (etree_type *exp) +{ + struct bfd_link_hash_entry *h; + + switch (exp->type.node_class) + { + case etree_provide: + h = bfd_link_hash_lookup (link_info.hash, exp->assign.dst, + FALSE, FALSE, FALSE); + if (h == NULL) + break; + /* Fall through. */ + case etree_assign: + if (strcmp (exp->assign.dst, ".") != 0) + { + if (!bfd_xcoff_record_link_assignment (link_info.output_bfd, + &link_info, + exp->assign.dst)) + einfo ("%P%F: failed to record assignment to %s: %E\n", + exp->assign.dst); + } + gld${EMULATION_NAME}_find_exp_assignment (exp->assign.src); + break; + + case etree_binary: + gld${EMULATION_NAME}_find_exp_assignment (exp->binary.lhs); + gld${EMULATION_NAME}_find_exp_assignment (exp->binary.rhs); + break; + + case etree_trinary: + gld${EMULATION_NAME}_find_exp_assignment (exp->trinary.cond); + gld${EMULATION_NAME}_find_exp_assignment (exp->trinary.lhs); + gld${EMULATION_NAME}_find_exp_assignment (exp->trinary.rhs); + break; + + case etree_unary: + gld${EMULATION_NAME}_find_exp_assignment (exp->unary.child); + break; + + default: + break; + } +} + +static char * +gld${EMULATION_NAME}_get_script (int *isfile) +EOF + +if test -n "$COMPILE_IN" +then +# Scripts compiled in. + +# sed commands to quote an ld script as a C string. +sc="-f ${srcdir}/emultempl/ostring.sed" + +fragment <> e${EMULATION_NAME}.c +echo ' ; else if (link_info.relocatable) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xr >> e${EMULATION_NAME}.c +echo ' ; else if (!config.text_read_only) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xbn >> e${EMULATION_NAME}.c +echo ' ; else if (!config.magic_demand_paged) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xn >> e${EMULATION_NAME}.c +echo ' ; else return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.x >> e${EMULATION_NAME}.c +echo '; }' >> e${EMULATION_NAME}.c + +else +# Scripts read from the filesystem. + +fragment <the_bfd = bfd_create ("initfini", link_info.output_bfd); + if (initfini_file->the_bfd == NULL + || ! bfd_set_arch_mach (initfini_file->the_bfd, + bfd_get_arch (link_info.output_bfd), + bfd_get_mach (link_info.output_bfd))) + { + einfo ("%X%P: can not create BFD %E\n"); + return; + } + + /* Call backend to fill in the rest */ + if (! bfd_xcoff_link_generate_rtinit (initfini_file->the_bfd, + link_info.init_function, + link_info.fini_function, + rtld)) + { + einfo ("%X%P: can not create BFD %E\n"); + return; + } + + /* __rtld defined in /lib/librtl.a */ + if (rtld) + lang_add_input_file ("rtl", lang_input_file_is_l_enum, NULL); + } +} + +static void +gld${EMULATION_NAME}_set_output_arch (void) +{ + bfd_set_arch_mach (link_info.output_bfd, + bfd_xcoff_architecture (link_info.output_bfd), + bfd_xcoff_machine (link_info.output_bfd)); + + ldfile_output_architecture = bfd_get_arch (link_info.output_bfd); + ldfile_output_machine = bfd_get_mach (link_info.output_bfd); + ldfile_output_machine_name = bfd_printable_name (link_info.output_bfd); +} + +struct ld_emulation_xfer_struct ld_${EMULATION_NAME}_emulation = { + gld${EMULATION_NAME}_before_parse, + syslib_default, + hll_default, + after_parse_default, + gld${EMULATION_NAME}_after_open, + after_allocation_default, + gld${EMULATION_NAME}_set_output_arch, + gld${EMULATION_NAME}_choose_target, + gld${EMULATION_NAME}_before_allocation, + gld${EMULATION_NAME}_get_script, + "${EMULATION_NAME}", + "${OUTPUT_FORMAT}", + finish_default, + gld${EMULATION_NAME}_create_output_section_statements, + 0, /* open_dynamic_archive */ + 0, /* place_orphan */ + 0, /* set_symbols */ + gld${EMULATION_NAME}_parse_args, + gld${EMULATION_NAME}_add_options, + gld${EMULATION_NAME}_handle_option, + gld${EMULATION_NAME}_unrecognized_file, + NULL, /* list_options */ + NULL, /* recognized_file */ + NULL, /* find potential_libraries */ + NULL /* new_vers_pattern */ +}; +EOF Index: z80.em =================================================================== --- z80.em (nonexistent) +++ z80.em (revision 816) @@ -0,0 +1,102 @@ +# This shell script emits C code -*- C -*- +# to keep track of the machine type of Z80 object files +# It does some substitutions. +# Copyright 2007, 2008 Free Software Foundation, Inc. +# This file is part of the GNU Binutils. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. + +LDEMUL_BEFORE_PARSE=gldz80_before_parse +LDEMUL_RECOGNIZED_FILE=gldz80_recognized_file +LDEMUL_AFTER_OPEN=gldz80_after_open + +fragment <the_bfd); + switch (mach_type) + { + case bfd_mach_z80strict: + result_mach_type |= M_Z80STRICT; + break; + case bfd_mach_z80: + result_mach_type |= M_Z80; + break; + case bfd_mach_z80full: + result_mach_type |= M_Z80FULL; + break; + case bfd_mach_r800: + result_mach_type |= M_R800; + break; + default: + result_mach_type |= M_Z80ANY; + } + return FALSE; +} + +/* Set the machine type of the output file based on result_mach_type. */ +static void +gldz80_after_open (void) +{ + unsigned long mach_type; + + switch (result_mach_type) + { + case M_Z80STRICT: + mach_type = bfd_mach_z80strict; + break; + case M_Z80: + mach_type = bfd_mach_z80; + break; + case M_Z80FULL: + mach_type = bfd_mach_z80full; + break; + case M_R800: + mach_type = bfd_mach_r800; + break; + default: + mach_type = 0; + } + bfd_set_arch_mach (link_info.output_bfd, bfd_arch_z80, mach_type); +} +/* --- \end{z80.em} */ +EOF Index: m68kcoff.em =================================================================== --- m68kcoff.em (nonexistent) +++ m68kcoff.em (revision 816) @@ -0,0 +1,244 @@ +# This shell script emits a C file. -*- C -*- +# It does some substitutions. +fragment <, based on generic.em + by Steve Chamberlain , embedded relocs code based on + mipsecoff.em by Ian Lance Taylor . + + This file is part of the GNU Binutils. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#define TARGET_IS_${EMULATION_NAME} + +#include "sysdep.h" +#include "bfd.h" +#include "bfdlink.h" + +#include "ld.h" +#include "ldmain.h" +#include "ldexp.h" +#include "ldlang.h" +#include "ldfile.h" +#include "ldemul.h" +#include "ldmisc.h" + +static void check_sections (bfd *, asection *, void *); + +static void +gld${EMULATION_NAME}_before_parse (void) +{ +#ifndef TARGET_ /* I.e., if not generic. */ + ldfile_set_output_arch ("`echo ${ARCH}`", bfd_arch_unknown); +#endif /* not TARGET_ */ +} + +/* This function is run after all the input files have been opened. + We create a .emreloc section for each input file with a non zero + .data section. The BFD backend will fill in these sections with + magic numbers which can be used to relocate the data section at run + time. */ + +static void +gld${EMULATION_NAME}_after_open (void) +{ + bfd *abfd; + + if (! command_line.embedded_relocs + || link_info.relocatable) + return; + + for (abfd = link_info.input_bfds; abfd != NULL; abfd = abfd->link_next) + { + asection *datasec; + + /* As first-order business, make sure that each input BFD is COFF. It + better be, as we are directly calling a COFF backend function. */ + if (bfd_get_flavour (abfd) != bfd_target_coff_flavour) + einfo ("%F%B: all input objects must be COFF for --embedded-relocs\n"); + + datasec = bfd_get_section_by_name (abfd, ".data"); + + /* Note that we assume that the reloc_count field has already + been set up. We could call bfd_get_reloc_upper_bound, but + that returns the size of a memory buffer rather than a reloc + count. We do not want to call bfd_canonicalize_reloc, + because although it would always work it would force us to + read in the relocs into BFD canonical form, which would waste + a significant amount of time and memory. */ + if (datasec != NULL && datasec->reloc_count > 0) + { + asection *relsec; + + relsec = bfd_make_section (abfd, ".emreloc"); + if (relsec == NULL + || ! bfd_set_section_flags (abfd, relsec, + (SEC_ALLOC + | SEC_LOAD + | SEC_HAS_CONTENTS + | SEC_IN_MEMORY)) + || ! bfd_set_section_alignment (abfd, relsec, 2) + || ! bfd_set_section_size (abfd, relsec, + datasec->reloc_count * 12)) + einfo ("%F%B: can not create .emreloc section: %E\n"); + } + + /* Double check that all other data sections are empty, as is + required for embedded PIC code. */ + bfd_map_over_sections (abfd, check_sections, datasec); + } +} + +/* Check that of the data sections, only the .data section has + relocs. This is called via bfd_map_over_sections. */ + +static void +check_sections (bfd *abfd, asection *sec, void *datasec) +{ + if ((bfd_get_section_flags (abfd, sec) & SEC_DATA) + && sec != datasec + && sec->reloc_count != 0) + einfo ("%B%X: section %s has relocs; can not use --embedded-relocs\n", + abfd, bfd_get_section_name (abfd, sec)); +} + +/* This function is called after the section sizes and offsets have + been set. If we are generating embedded relocs, it calls a special + BFD backend routine to do the work. */ + +static void +gld${EMULATION_NAME}_after_allocation (void) +{ + bfd *abfd; + + if (! command_line.embedded_relocs + || link_info.relocatable) + return; + + for (abfd = link_info.input_bfds; abfd != NULL; abfd = abfd->link_next) + { + asection *datasec, *relsec; + char *errmsg; + + datasec = bfd_get_section_by_name (abfd, ".data"); + + if (datasec == NULL || datasec->reloc_count == 0) + continue; + + relsec = bfd_get_section_by_name (abfd, ".emreloc"); + ASSERT (relsec != NULL); + + if (! bfd_m68k_coff_create_embedded_relocs (abfd, &link_info, + datasec, relsec, + &errmsg)) + { + if (errmsg == NULL) + einfo ("%B%X: can not create runtime reloc information: %E\n", + abfd); + else + einfo ("%X%B: can not create runtime reloc information: %s\n", + abfd, errmsg); + } + } +} + +static char * +gld${EMULATION_NAME}_get_script (int *isfile) +EOF + +if test -n "$COMPILE_IN" +then +# Scripts compiled in. + +# sed commands to quote an ld script as a C string. +sc="-f stringify.sed" + +fragment <> e${EMULATION_NAME}.c +echo ' ; else if (link_info.relocatable) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xr >> e${EMULATION_NAME}.c +echo ' ; else if (!config.text_read_only) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xbn >> e${EMULATION_NAME}.c +echo ' ; else if (!config.magic_demand_paged) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xn >> e${EMULATION_NAME}.c +echo ' ; else return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.x >> e${EMULATION_NAME}.c +echo '; }' >> e${EMULATION_NAME}.c + +else +# Scripts read from the filesystem. + +fragment <dynamic_sections_created) + einfo ("%X%P: Dynamic sections created in non-dynamic link\n"); +} + +EOF + +PARSE_AND_LIST_PROLOGUE=$PARSE_AND_LIST_PROLOGUE' +enum { + OPTION_FORCE_DYNAMIC = 501 +}; +' + +PARSE_AND_LIST_LONGOPTS=$PARSE_AND_LIST_LONGOPTS' + {"force-dynamic", no_argument, NULL, OPTION_FORCE_DYNAMIC}, +' + +PARSE_AND_LIST_OPTIONS=$PARSE_AND_LIST_OPTIONS' + fprintf (file, _("\ + --force-dynamic Always create dynamic sections\n")); +' + +PARSE_AND_LIST_ARGS_CASES=$PARSE_AND_LIST_ARGS_CASES' + case OPTION_FORCE_DYNAMIC: + force_dynamic = 1; + break; +' + +# Hook in our routines above. There are three possibilities: +# +# (1) VXWORKS_BASE_EM_FILE did not set the hook's LDEMUL_FOO variable. +# We want to define LDEMUL_FOO to vxworks_foo in that case, +# +# (2) VXWORKS_BASE_EM_FILE set the hook's LDEMUL_FOO variable to +# gld${EMULATION_NAME}_foo. This means that the file has +# replaced elf32.em's default definition, so we simply #define +# the current value of LDEMUL_FOO to vxworks_foo. +# +# (3) VXWORKS_BASE_EM_FILE set the hook's LDEMUL_FOO variable to +# something other than gld${EMULATION_NAME}_foo. We handle +# this case in the same way as (1). +for override in before_parse after_open; do + var="LDEMUL_`echo ${override} | tr a-z A-Z`" + eval value=\$${var} + if test "${value}" = "gld${EMULATION_NAME}_${override}"; then + fragment < +#include "ldlex.h" +#include "ldmisc.h" +#include "ldctor.h" +#include "coff/internal.h" +#include "../bfd/libcoff.h" + +#define TARGET_IS_${EMULATION_NAME} + +static struct internal_extra_pe_aouthdr pe; +static int dll; + +extern const char *output_filename; + +static void +gld_${EMULATION_NAME}_before_parse (void) +{ + ldfile_set_output_arch ("${OUTPUT_ARCH}", bfd_arch_`echo ${ARCH} | sed -e 's/:.*//'`); + output_filename = "a.exe"; +} + +/* PE format extra command line options. */ + +/* Used for setting flags in the PE header. */ +#define OPTION_BASE_FILE (300 + 1) +#define OPTION_DLL (OPTION_BASE_FILE + 1) +#define OPTION_FILE_ALIGNMENT (OPTION_DLL + 1) +#define OPTION_IMAGE_BASE (OPTION_FILE_ALIGNMENT + 1) +#define OPTION_MAJOR_IMAGE_VERSION (OPTION_IMAGE_BASE + 1) +#define OPTION_MAJOR_OS_VERSION (OPTION_MAJOR_IMAGE_VERSION + 1) +#define OPTION_MAJOR_SUBSYSTEM_VERSION (OPTION_MAJOR_OS_VERSION + 1) +#define OPTION_MINOR_IMAGE_VERSION (OPTION_MAJOR_SUBSYSTEM_VERSION + 1) +#define OPTION_MINOR_OS_VERSION (OPTION_MINOR_IMAGE_VERSION + 1) +#define OPTION_MINOR_SUBSYSTEM_VERSION (OPTION_MINOR_OS_VERSION + 1) +#define OPTION_SECTION_ALIGNMENT (OPTION_MINOR_SUBSYSTEM_VERSION + 1) +#define OPTION_STACK (OPTION_SECTION_ALIGNMENT + 1) +#define OPTION_SUBSYSTEM (OPTION_STACK + 1) +#define OPTION_HEAP (OPTION_SUBSYSTEM + 1) + +static void +gld${EMULATION_NAME}_add_options + (int ns ATTRIBUTE_UNUSED, char **shortopts ATTRIBUTE_UNUSED, int nl, + struct option **longopts, int nrl ATTRIBUTE_UNUSED, + struct option **really_longopts ATTRIBUTE_UNUSED) +{ + static const struct option xtra_long[] = { + /* PE options */ + {"base-file", required_argument, NULL, OPTION_BASE_FILE}, + {"dll", no_argument, NULL, OPTION_DLL}, + {"file-alignment", required_argument, NULL, OPTION_FILE_ALIGNMENT}, + {"heap", required_argument, NULL, OPTION_HEAP}, + {"image-base", required_argument, NULL, OPTION_IMAGE_BASE}, + {"major-image-version", required_argument, NULL, OPTION_MAJOR_IMAGE_VERSION}, + {"major-os-version", required_argument, NULL, OPTION_MAJOR_OS_VERSION}, + {"major-subsystem-version", required_argument, NULL, OPTION_MAJOR_SUBSYSTEM_VERSION}, + {"minor-image-version", required_argument, NULL, OPTION_MINOR_IMAGE_VERSION}, + {"minor-os-version", required_argument, NULL, OPTION_MINOR_OS_VERSION}, + {"minor-subsystem-version", required_argument, NULL, OPTION_MINOR_SUBSYSTEM_VERSION}, + {"section-alignment", required_argument, NULL, OPTION_SECTION_ALIGNMENT}, + {"stack", required_argument, NULL, OPTION_STACK}, + {"subsystem", required_argument, NULL, OPTION_SUBSYSTEM}, + {NULL, no_argument, NULL, 0} + }; + + *longopts = (struct option *) + xrealloc (*longopts, nl * sizeof (struct option) + sizeof (xtra_long)); + memcpy (*longopts + nl, &xtra_long, sizeof (xtra_long)); +} + + +/* PE/WIN32; added routines to get the subsystem type, heap and/or stack + parameters which may be input from the command line */ + +typedef struct { + void *ptr; + int size; + int value; + char *symbol; + int inited; +} definfo; + +#define D(field,symbol,def) {&pe.field,sizeof(pe.field), def, symbol,0} + +static definfo init[] = +{ + /* imagebase must be first */ +#define IMAGEBASEOFF 0 + D(ImageBase,"__image_base__", BEOS_EXE_IMAGE_BASE), +#define DLLOFF 1 + {&dll, sizeof(dll), 0, "__dll__", 0}, + D(SectionAlignment,"__section_alignment__", PE_DEF_SECTION_ALIGNMENT), + D(FileAlignment,"__file_alignment__", PE_DEF_FILE_ALIGNMENT), + D(MajorOperatingSystemVersion,"__major_os_version__", 4), + D(MinorOperatingSystemVersion,"__minor_os_version__", 0), + D(MajorImageVersion,"__major_image_version__", 1), + D(MinorImageVersion,"__minor_image_version__", 0), + D(MajorSubsystemVersion,"__major_subsystem_version__", 4), + D(MinorSubsystemVersion,"__minor_subsystem_version__", 0), + D(Subsystem,"__subsystem__", 3), + D(SizeOfStackReserve,"__size_of_stack_reserve__", 0x2000000), + D(SizeOfStackCommit,"__size_of_stack_commit__", 0x1000), + D(SizeOfHeapReserve,"__size_of_heap_reserve__", 0x100000), + D(SizeOfHeapCommit,"__size_of_heap_commit__", 0x1000), + D(LoaderFlags,"__loader_flags__", 0x0), + { NULL, 0, 0, NULL, 0 } +}; + + +static void +set_pe_name (char *name, long val) +{ + int i; + /* Find the name and set it. */ + for (i = 0; init[i].ptr; i++) + { + if (strcmp (name, init[i].symbol) == 0) + { + init[i].value = val; + init[i].inited = 1; + return; + } + } + abort(); +} + + +static void +set_pe_subsystem (void) +{ + const char *sver; + int len; + int i; + static const struct + { + const char *name; + const int value; + const char *entry; + } + v[] = + { + { "native", 1, "_NtProcessStartup" }, + { "windows", 2, "_WinMainCRTStartup" }, + { "wwindows", 2, "_wWinMainCRTStartup" }, + { "console", 3, "_mainCRTStartup" }, + { "wconsole", 3, "_wmainCRTStartup" }, + { "posix", 7, "___PosixProcessStartup"}, + { 0, 0, 0 } + }; + + sver = strchr (optarg, ':'); + if (sver == NULL) + len = strlen (optarg); + else + { + char *end; + + len = sver - optarg; + set_pe_name ("__major_subsystem_version__", + strtoul (sver + 1, &end, 0)); + if (*end == '.') + set_pe_name ("__minor_subsystem_version__", + strtoul (end + 1, &end, 0)); + if (*end != '\0') + einfo ("%P: warning: bad version number in -subsystem option\n"); + } + + for (i = 0; v[i].name; i++) + { + if (strncmp (optarg, v[i].name, len) == 0 + && v[i].name[len] == '\0') + { + set_pe_name ("__subsystem__", v[i].value); + + /* If the subsystem is windows, we use a different entry + point. */ + lang_default_entry (v[i].entry); + + return; + } + } + einfo ("%P%F: invalid subsystem type %s\n", optarg); +} + + +static void +set_pe_value (char *name) +{ + char *end; + set_pe_name (name, strtoul (optarg, &end, 0)); + if (end == optarg) + { + einfo ("%P%F: invalid hex number for PE parameter '%s'\n", optarg); + } + + optarg = end; +} + +static void +set_pe_stack_heap (char *resname, char *comname) +{ + set_pe_value (resname); + if (*optarg == ',') + { + optarg++; + set_pe_value (comname); + } + else if (*optarg) + { + einfo ("%P%F: strange hex info for PE parameter '%s'\n", optarg); + } +} + + +static bfd_boolean +gld${EMULATION_NAME}_handle_option (int optc) +{ + switch (optc) + { + default: + return FALSE; + + case OPTION_BASE_FILE: + link_info.base_file = fopen (optarg, FOPEN_WB); + if (link_info.base_file == NULL) + { + fprintf (stderr, "%s: Can't open base file %s\n", + program_name, optarg); + xexit (1); + } + break; + + /* PE options */ + case OPTION_HEAP: + set_pe_stack_heap ("__size_of_heap_reserve__", "__size_of_heap_commit__"); + break; + case OPTION_STACK: + set_pe_stack_heap ("__size_of_stack_reserve__", "__size_of_stack_commit__"); + break; + case OPTION_SUBSYSTEM: + set_pe_subsystem (); + break; + case OPTION_MAJOR_OS_VERSION: + set_pe_value ("__major_os_version__"); + break; + case OPTION_MINOR_OS_VERSION: + set_pe_value ("__minor_os_version__"); + break; + case OPTION_MAJOR_SUBSYSTEM_VERSION: + set_pe_value ("__major_subsystem_version__"); + break; + case OPTION_MINOR_SUBSYSTEM_VERSION: + set_pe_value ("__minor_subsystem_version__"); + break; + case OPTION_MAJOR_IMAGE_VERSION: + set_pe_value ("__major_image_version__"); + break; + case OPTION_MINOR_IMAGE_VERSION: + set_pe_value ("__minor_image_version__"); + break; + case OPTION_FILE_ALIGNMENT: + set_pe_value ("__file_alignment__"); + break; + case OPTION_SECTION_ALIGNMENT: + set_pe_value ("__section_alignment__"); + break; + case OPTION_DLL: + set_pe_name ("__dll__", 1); + break; + case OPTION_IMAGE_BASE: + set_pe_value ("__image_base__"); + break; + } + return TRUE; +} + +/* Assign values to the special symbols before the linker script is + read. */ + +static void +gld_${EMULATION_NAME}_set_symbols (void) +{ + /* Run through and invent symbols for all the + names and insert the defaults. */ + int j; + lang_statement_list_type *save; + + if (!init[IMAGEBASEOFF].inited) + { + if (link_info.relocatable) + init[IMAGEBASEOFF].value = 0; + else if (init[DLLOFF].value) + init[IMAGEBASEOFF].value = BEOS_DLL_IMAGE_BASE; + else + init[IMAGEBASEOFF].value = BEOS_EXE_IMAGE_BASE; + } + + /* Don't do any symbol assignments if this is a relocatable link. */ + if (link_info.relocatable) + return; + + /* Glue the assignments into the abs section */ + save = stat_ptr; + + stat_ptr = &(abs_output_section->children); + + for (j = 0; init[j].ptr; j++) + { + long val = init[j].value; + lang_add_assignment (exp_assop ('=', init[j].symbol, exp_intop (val))); + if (init[j].size == sizeof(short)) + *(short *)init[j].ptr = val; + else if (init[j].size == sizeof(int)) + *(int *)init[j].ptr = val; + else if (init[j].size == sizeof(long)) + *(long *)init[j].ptr = val; + /* This might be a long long or other special type. */ + else if (init[j].size == sizeof(bfd_vma)) + *(bfd_vma *)init[j].ptr = val; + else abort(); + } + /* Restore the pointer. */ + stat_ptr = save; + + if (pe.FileAlignment > + pe.SectionAlignment) + { + einfo ("%P: warning, file alignment > section alignment.\n"); + } +} + +static void +gld_${EMULATION_NAME}_after_open (void) +{ + /* Pass the wacky PE command line options into the output bfd. + FIXME: This should be done via a function, rather than by + including an internal BFD header. */ + if (!coff_data(link_info.output_bfd)->pe) + { + einfo ("%F%P: PE operations on non PE file.\n"); + } + + pe_data(link_info.output_bfd)->pe_opthdr = pe; + pe_data(link_info.output_bfd)->dll = init[DLLOFF].value; + +} + +/* Callback functions for qsort in sort_sections. */ + +static int +sort_by_file_name (const void *a, const void *b) +{ + const lang_statement_union_type *const *ra = a; + const lang_statement_union_type *const *rb = b; + int i, a_sec, b_sec; + + i = strcmp ((*ra)->input_section.section->owner->my_archive->filename, + (*rb)->input_section.section->owner->my_archive->filename); + if (i != 0) + return i; + + i = strcmp ((*ra)->input_section.section->owner->filename, + (*rb)->input_section.section->owner->filename); + if (i != 0) + return i; + /* the tail idata4/5 are the only ones without relocs to an + idata$6 section unless we are importing by ordinal, + so sort them to last to terminate the IAT + and HNT properly. if no reloc this one is import by ordinal + so we have to sort by section contents */ + + if ( ((*ra)->input_section.section->reloc_count + (*rb)->input_section.section->reloc_count) ) + { + i = (((*ra)->input_section.section->reloc_count > + (*rb)->input_section.section->reloc_count) ? -1 : 0); + if ( i != 0) + return i; + + return (((*ra)->input_section.section->reloc_count > + (*rb)->input_section.section->reloc_count) ? 0 : 1); + } + else + { + if ( (strcmp( (*ra)->input_section.section->name, ".idata$6") == 0) ) + return 0; /* don't sort .idata$6 or .idata$7 FIXME dlltool eliminate .idata$7 */ + + if (! bfd_get_section_contents ((*ra)->input_section.section->owner, + (*ra)->input_section.section, &a_sec, (file_ptr) 0, (bfd_size_type)sizeof(a_sec))) + einfo ("%F%B: Can't read contents of section .idata: %E\n", + (*ra)->input_section.section->owner); + + if (! bfd_get_section_contents ((*rb)->input_section.section->owner, + (*rb)->input_section.section, &b_sec, (file_ptr) 0, (bfd_size_type)sizeof(b_sec) )) + einfo ("%F%B: Can't read contents of section .idata: %E\n", + (*rb)->input_section.section->owner); + + i = ((a_sec < b_sec) ? -1 : 0); + if ( i != 0) + return i; + return ((a_sec < b_sec) ? 0 : 1); + } +return 0; +} + +static int +sort_by_section_name (const void *a, const void *b) +{ + const lang_statement_union_type *const *ra = a; + const lang_statement_union_type *const *rb = b; + int i; + i = strcmp ((*ra)->input_section.section->name, + (*rb)->input_section.section->name); + /* This is a hack to make .stab and .stabstr last, so we don't have + to fix strip/objcopy for .reloc sections. + FIXME stripping images with a .rsrc section still needs to be fixed. */ + if (i != 0) + { + if ((CONST_STRNEQ ((*ra)->input_section.section->name, ".stab")) + && (! CONST_STRNEQ ((*rb)->input_section.section->name, ".stab"))) + return 1; + } + return i; +} + +/* Subroutine of sort_sections to a contiguous subset of a list of sections. + NEXT_AFTER is the element after the last one to sort. + The result is a pointer to the last element's "next" pointer. */ + +static lang_statement_union_type ** +sort_sections_1 (lang_statement_union_type **startptr, + lang_statement_union_type *next_after, + int count, + int (*sort_func) (const void *, const void *)) +{ + lang_statement_union_type **vec; + lang_statement_union_type *p; + int i; + lang_statement_union_type **ret; + + if (count == 0) + return startptr; + + vec = ((lang_statement_union_type **) + xmalloc (count * sizeof (lang_statement_union_type *))); + + for (p = *startptr, i = 0; i < count; i++, p = p->header.next) + vec[i] = p; + + qsort (vec, count, sizeof (vec[0]), sort_func); + + /* Fill in the next pointers again. */ + *startptr = vec[0]; + for (i = 0; i < count - 1; i++) + vec[i]->header.next = vec[i + 1]; + vec[i]->header.next = next_after; + ret = &vec[i]->header.next; + free (vec); + return ret; +} + +/* Sort the .idata\$foo input sections of archives into filename order. + The reason is so dlltool can arrange to have the pe dll import information + generated correctly - the head of the list goes into dh.o, the tail into + dt.o, and the guts into ds[nnnn].o. Note that this is only needed for the + .idata section. + FIXME: This may no longer be necessary with grouped sections. Instead of + sorting on dh.o, ds[nnnn].o, dt.o, one could, for example, have dh.o use + .idata\$4h, have ds[nnnn].o use .idata\$4s[nnnn], and have dt.o use .idata\$4t. + This would have to be elaborated upon to handle multiple dll's + [assuming such an eloboration is possible of course]. + + We also sort sections in '\$' wild statements. These are created by the + place_orphans routine to implement grouped sections. */ + +static void +sort_sections (lang_statement_union_type *s) +{ + for (; s ; s = s->header.next) + switch (s->header.type) + { + case lang_output_section_statement_enum: + sort_sections (s->output_section_statement.children.head); + break; + case lang_wild_statement_enum: + { + lang_statement_union_type **p = &s->wild_statement.children.head; + struct wildcard_list *sec; + + for (sec = s->wild_statement.section_list; sec; sec = sec->next) + { + /* Is this the .idata section? */ + if (sec->spec.name != NULL + && CONST_STRNEQ (sec->spec.name, ".idata")) + { + /* Sort the children. We want to sort any objects in + the same archive. In order to handle the case of + including a single archive multiple times, we sort + all the children by archive name and then by object + name. After sorting them, we re-thread the pointer + chain. */ + + while (*p) + { + lang_statement_union_type *start = *p; + if (start->header.type != lang_input_section_enum + || !start->input_section.section->owner->my_archive) + p = &(start->header.next); + else + { + lang_statement_union_type *end; + int count; + + for (end = start, count = 0; + end && (end->header.type + == lang_input_section_enum); + end = end->header.next) + count++; + + p = sort_sections_1 (p, end, count, + sort_by_file_name); + } + } + break; + } + + /* If this is a collection of grouped sections, sort them. + The linker script must explicitly mention "*(.foo\$)" or + "*(.foo\$*)". Don't sort them if \$ is not the last + character (not sure if this is really useful, but it + allows explicitly mentioning some \$ sections and letting + the linker handle the rest). */ + if (sec->spec.name != NULL) + { + char *q = strchr (sec->spec.name, '\$'); + + if (q != NULL + && (q[1] == '\0' + || (q[1] == '*' && q[2] == '\0'))) + { + lang_statement_union_type *end; + int count; + + for (end = *p, count = 0; end; end = end->header.next) + { + if (end->header.type != lang_input_section_enum) + abort (); + count++; + } + (void) sort_sections_1 (p, end, count, + sort_by_section_name); + } + break; + } + } + } + break; + default: + break; + } +} + +static void +gld_${EMULATION_NAME}_before_allocation (void) +{ + extern lang_statement_list_type *stat_ptr; + +#ifdef TARGET_IS_ppcpe + /* Here we rummage through the found bfds to collect toc information */ + { + LANG_FOR_EACH_INPUT_STATEMENT (is) + { + if (!ppc_process_before_allocation(is->the_bfd, &link_info)) + { + einfo("Errors encountered processing file %s\n", is->filename); + } + } + } + + /* We have seen it all. Allocate it, and carry on */ + ppc_allocate_toc_section (&link_info); +#else +#ifdef TARGET_IS_armpe + /* FIXME: we should be able to set the size of the interworking stub + section. + + Here we rummage through the found bfds to collect glue + information. FIXME: should this be based on a command line + option? krk@cygnus.com */ + { + LANG_FOR_EACH_INPUT_STATEMENT (is) + { + if (!arm_process_before_allocation (is->the_bfd, & link_info)) + { + einfo ("Errors encountered processing file %s", is->filename); + } + } + } + + /* We have seen it all. Allocate it, and carry on */ + arm_allocate_interworking_sections (& link_info); +#endif /* TARGET_IS_armpe */ +#endif /* TARGET_IS_ppcpe */ + + sort_sections (stat_ptr->head); + + before_allocation_default (); +} + +/* Place an orphan section. We use this to put sections with a '\$' in them + into the right place. Any section with a '\$' in them (e.g. .text\$foo) + gets mapped to the output section with everything from the '\$' on stripped + (e.g. .text). + See the Microsoft Portable Executable and Common Object File Format + Specification 4.1, section 4.2, Grouped Sections. + + FIXME: This is now handled by the linker script using wildcards, + but I'm leaving this here in case we want to enable it for sections + which are not mentioned in the linker script. */ + +static bfd_boolean +gld${EMULATION_NAME}_place_orphan (asection *s) +{ + const char *secname; + char *output_secname, *ps; + lang_output_section_statement_type *os; + lang_statement_union_type *l; + + if ((s->flags & SEC_ALLOC) == 0) + return FALSE; + + /* Don't process grouped sections unless doing a final link. + If they're marked as COMDAT sections, we don't want .text\$foo to + end up in .text and then have .text disappear because it's marked + link-once-discard. */ + if (link_info.relocatable) + return FALSE; + + secname = bfd_get_section_name (s->owner, s); + + /* Everything from the '\$' on gets deleted so don't allow '\$' as the + first character. */ + if (*secname == '\$') + einfo ("%P%F: section %s has '\$' as first character\n", secname); + if (strchr (secname + 1, '\$') == NULL) + return FALSE; + + /* Look up the output section. The Microsoft specs say sections names in + image files never contain a '\$'. Fortunately, lang_..._lookup creates + the section if it doesn't exist. */ + output_secname = xstrdup (secname); + ps = strchr (output_secname + 1, '\$'); + *ps = 0; + os = lang_output_section_statement_lookup (output_secname); + + /* Find the '\$' wild statement for this section. We currently require the + linker script to explicitly mention "*(.foo\$)". + FIXME: ppcpe.sc has .CRT\$foo in the .rdata section. According to the + Microsoft docs this isn't correct so it's not (currently) handled. */ + + ps[0] = '\$'; + ps[1] = 0; + for (l = os->children.head; l; l = l->header.next) + if (l->header.type == lang_wild_statement_enum) + { + struct wildcard_list *sec; + + for (sec = l->wild_statement.section_list; sec; sec = sec->next) + if (sec->spec.name && strcmp (sec->spec.name, output_secname) == 0) + break; + if (sec) + break; + } + ps[0] = 0; + if (l == NULL) + einfo ("%P%F: *(%s\$) missing from linker script\n", output_secname); + + /* Link the input section in and we're done for now. + The sections still have to be sorted, but that has to wait until + all such sections have been processed by us. The sorting is done by + sort_sections. */ + lang_add_section (&l->wild_statement.children, s, os); + + return TRUE; +} + +static char * +gld_${EMULATION_NAME}_get_script (int *isfile) +EOF +# Scripts compiled in. +# sed commands to quote an ld script as a C string. +sc="-f stringify.sed" + +fragment <> e${EMULATION_NAME}.c +echo ' ; else if (link_info.relocatable) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xr >> e${EMULATION_NAME}.c +echo ' ; else if (!config.text_read_only) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xbn >> e${EMULATION_NAME}.c +echo ' ; else if (!config.magic_demand_paged) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xn >> e${EMULATION_NAME}.c +echo ' ; else return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.x >> e${EMULATION_NAME}.c +echo '; }' >> e${EMULATION_NAME}.c + +fragment < +#include "../bfd/elf-bfd.h" +#include "../bfd/libbfd.h" +#include "elf/xtensa.h" +#include "bfd.h" + +/* Provide default values for new configuration settings. */ +#ifndef XSHAL_ABI +#define XSHAL_ABI 0 +#endif + +static void xtensa_wild_group_interleave (lang_statement_union_type *); +static void xtensa_colocate_output_literals (lang_statement_union_type *); +static void xtensa_strip_inconsistent_linkonce_sections + (lang_statement_list_type *); + + +/* Flag for the emulation-specific "--no-relax" option. */ +static bfd_boolean disable_relaxation = FALSE; + +/* This number is irrelevant until we turn on use_literal_pages */ +static bfd_vma xtensa_page_power = 12; /* 4K pages. */ + +/* To force a page break between literals and text, change + xtensa_use_literal_pages to "TRUE". */ +static bfd_boolean xtensa_use_literal_pages = FALSE; + +#define EXTRA_VALIDATION 0 + + +static char * +elf_xtensa_choose_target (int argc ATTRIBUTE_UNUSED, + char **argv ATTRIBUTE_UNUSED) +{ + if (XCHAL_HAVE_BE) + return "${BIG_OUTPUT_FORMAT}"; + else + return "${LITTLE_OUTPUT_FORMAT}"; +} + + +static void +elf_xtensa_before_parse (void) +{ + /* Just call the default hook.... Tensilica's version of this function + does some other work that isn't relevant here. */ + gld${EMULATION_NAME}_before_parse (); +} + + +static void +remove_section (bfd *abfd, asection *os) +{ + asection **spp; + for (spp = &abfd->sections; *spp; spp = &(*spp)->next) + if (*spp == os) + { + *spp = os->next; + os->owner->section_count--; + break; + } +} + + +static bfd_boolean +replace_insn_sec_with_prop_sec (bfd *abfd, + const char *insn_sec_name, + const char *prop_sec_name, + char **error_message) +{ + asection *insn_sec; + asection *prop_sec; + bfd_byte *prop_contents = NULL; + bfd_byte *insn_contents = NULL; + unsigned entry_count; + unsigned entry; + Elf_Internal_Shdr *symtab_hdr; + Elf_Internal_Rela *internal_relocs = NULL; + unsigned reloc_count; + + *error_message = ""; + insn_sec = bfd_get_section_by_name (abfd, insn_sec_name); + if (insn_sec == NULL) + return TRUE; + entry_count = insn_sec->size / 8; + + prop_sec = bfd_get_section_by_name (abfd, prop_sec_name); + if (prop_sec != NULL && insn_sec != NULL) + { + *error_message = _("file already has property tables"); + return FALSE; + } + + if (insn_sec->size != 0) + { + insn_contents = (bfd_byte *) bfd_malloc (insn_sec->size); + if (insn_contents == NULL) + { + *error_message = _("out of memory"); + goto cleanup; + } + if (! bfd_get_section_contents (abfd, insn_sec, insn_contents, + (file_ptr) 0, insn_sec->size)) + { + *error_message = _("failed to read section contents"); + goto cleanup; + } + } + + /* Create a property table section for it. */ + prop_sec_name = strdup (prop_sec_name); + prop_sec = bfd_make_section_with_flags + (abfd, prop_sec_name, bfd_get_section_flags (abfd, insn_sec)); + if (prop_sec == NULL + || ! bfd_set_section_alignment (abfd, prop_sec, 2)) + { + *error_message = _("could not create new section"); + goto cleanup; + } + + prop_sec->size = entry_count * 12; + prop_contents = (bfd_byte *) bfd_zalloc (abfd, prop_sec->size); + elf_section_data (prop_sec)->this_hdr.contents = prop_contents; + + /* The entry size and size must be set to allow the linker to compute + the number of relocations since it does not use reloc_count. */ + elf_section_data (prop_sec)->rel_hdr.sh_entsize = + sizeof (Elf32_External_Rela); + elf_section_data (prop_sec)->rel_hdr.sh_size = + elf_section_data (insn_sec)->rel_hdr.sh_size; + + if (prop_contents == NULL && prop_sec->size != 0) + { + *error_message = _("could not allocate section contents"); + goto cleanup; + } + + /* Read the relocations. */ + reloc_count = insn_sec->reloc_count; + if (reloc_count != 0) + { + /* If there is already an internal_reloc, then save it so that the + read_relocs function freshly allocates a copy. */ + Elf_Internal_Rela *saved_relocs = elf_section_data (insn_sec)->relocs; + + elf_section_data (insn_sec)->relocs = NULL; + internal_relocs = + _bfd_elf_link_read_relocs (abfd, insn_sec, NULL, NULL, FALSE); + elf_section_data (insn_sec)->relocs = saved_relocs; + + if (internal_relocs == NULL) + { + *error_message = _("out of memory"); + goto cleanup; + } + } + + /* Create a relocation section for the property section. */ + if (internal_relocs != NULL) + { + elf_section_data (prop_sec)->relocs = internal_relocs; + prop_sec->reloc_count = reloc_count; + } + + /* Now copy each insn table entry to the prop table entry with + appropriate flags. */ + for (entry = 0; entry < entry_count; ++entry) + { + unsigned value; + unsigned flags = (XTENSA_PROP_INSN | XTENSA_PROP_NO_TRANSFORM + | XTENSA_PROP_INSN_NO_REORDER); + value = bfd_get_32 (abfd, insn_contents + entry * 8 + 0); + bfd_put_32 (abfd, value, prop_contents + entry * 12 + 0); + value = bfd_get_32 (abfd, insn_contents + entry * 8 + 4); + bfd_put_32 (abfd, value, prop_contents + entry * 12 + 4); + bfd_put_32 (abfd, flags, prop_contents + entry * 12 + 8); + } + + /* Now copy all of the relocations. Change offsets for the + instruction table section to offsets in the property table + section. */ + if (internal_relocs) + { + unsigned i; + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + + for (i = 0; i < reloc_count; i++) + { + Elf_Internal_Rela *rela; + unsigned r_offset; + + rela = &internal_relocs[i]; + + /* If this relocation is to the .xt.insn section, + change the section number and the offset. */ + r_offset = rela->r_offset; + r_offset += 4 * (r_offset / 8); + rela->r_offset = r_offset; + } + } + + remove_section (abfd, insn_sec); + + if (insn_contents) + free (insn_contents); + + return TRUE; + + cleanup: + if (prop_sec && prop_sec->owner) + remove_section (abfd, prop_sec); + if (insn_contents) + free (insn_contents); + if (internal_relocs) + free (internal_relocs); + + return FALSE; +} + + +#define PROP_SEC_BASE_NAME ".xt.prop" +#define INSN_SEC_BASE_NAME ".xt.insn" +#define LINKONCE_SEC_OLD_TEXT_BASE_NAME ".gnu.linkonce.x." + + +static void +replace_instruction_table_sections (bfd *abfd, asection *sec) +{ + char *message = ""; + const char *insn_sec_name = NULL; + char *prop_sec_name = NULL; + char *owned_prop_sec_name = NULL; + const char *sec_name; + + sec_name = bfd_get_section_name (abfd, sec); + if (strcmp (sec_name, INSN_SEC_BASE_NAME) == 0) + { + insn_sec_name = INSN_SEC_BASE_NAME; + prop_sec_name = PROP_SEC_BASE_NAME; + } + else if (CONST_STRNEQ (sec_name, LINKONCE_SEC_OLD_TEXT_BASE_NAME)) + { + insn_sec_name = sec_name; + owned_prop_sec_name = (char *) xmalloc (strlen (sec_name) + 20); + prop_sec_name = owned_prop_sec_name; + strcpy (prop_sec_name, ".gnu.linkonce.prop.t."); + strcat (prop_sec_name, + sec_name + strlen (LINKONCE_SEC_OLD_TEXT_BASE_NAME)); + } + if (insn_sec_name != NULL) + { + if (! replace_insn_sec_with_prop_sec (abfd, insn_sec_name, prop_sec_name, + &message)) + { + einfo (_("%P: warning: failed to convert %s table in %B (%s); subsequent disassembly may be incomplete\n"), + insn_sec_name, abfd, message); + } + } + if (owned_prop_sec_name) + free (owned_prop_sec_name); +} + + +/* This is called after all input sections have been opened to convert + instruction tables (.xt.insn, gnu.linkonce.x.*) tables into property + tables (.xt.prop) before any section placement. */ + +static void +elf_xtensa_after_open (void) +{ + /* First call the ELF version. */ + gld${EMULATION_NAME}_after_open (); + + /* Now search the input files looking for instruction table sections. */ + LANG_FOR_EACH_INPUT_STATEMENT (f) + { + asection *sec = f->the_bfd->sections; + asection *next_sec; + + /* Do not use bfd_map_over_sections here since we are removing + sections as we iterate. */ + while (sec != NULL) + { + next_sec = sec->next; + replace_instruction_table_sections (f->the_bfd, sec); + sec = next_sec; + } + } +} + + +static bfd_boolean +xt_config_info_unpack_and_check (char *data, + bfd_boolean *pmismatch, + char **pmsg) +{ + char *d, *key; + unsigned num; + + *pmismatch = FALSE; + + d = data; + while (*d) + { + key = d; + d = strchr (d, '='); + if (! d) + goto error; + + /* Overwrite the equal sign. */ + *d++ = 0; + + /* Check if this is a quoted string or a number. */ + if (*d == '"') + { + /* No string values are currently checked by LD; + just skip over the quotes. */ + d++; + d = strchr (d, '"'); + if (! d) + goto error; + /* Overwrite the trailing quote. */ + *d++ = 0; + } + else + { + if (*d == 0) + goto error; + num = strtoul (d, &d, 0); + + if (! strcmp (key, "ABI")) + { + if (num != XSHAL_ABI) + { + *pmismatch = TRUE; + *pmsg = "ABI does not match"; + } + } + else if (! strcmp (key, "USE_ABSOLUTE_LITERALS")) + { + if (num != XSHAL_USE_ABSOLUTE_LITERALS) + { + *pmismatch = TRUE; + *pmsg = "incompatible use of the Extended L32R option"; + } + } + } + + if (*d++ != '\n') + goto error; + } + + return TRUE; + + error: + return FALSE; +} + + +#define XTINFO_NAME "Xtensa_Info" +#define XTINFO_NAMESZ 12 +#define XTINFO_TYPE 1 + +static void +check_xtensa_info (bfd *abfd, asection *info_sec) +{ + char *data, *errmsg = ""; + bfd_boolean mismatch; + + data = xmalloc (info_sec->size); + if (! bfd_get_section_contents (abfd, info_sec, data, 0, info_sec->size)) + einfo (_("%F%P:%B: cannot read contents of section %A\n"), abfd, info_sec); + + if (info_sec->size > 24 + && info_sec->size >= 24 + bfd_get_32 (abfd, data + 4) + && bfd_get_32 (abfd, data + 0) == XTINFO_NAMESZ + && bfd_get_32 (abfd, data + 8) == XTINFO_TYPE + && strcmp (data + 12, XTINFO_NAME) == 0 + && xt_config_info_unpack_and_check (data + 12 + XTINFO_NAMESZ, + &mismatch, &errmsg)) + { + if (mismatch) + einfo (_("%P:%B: warning: incompatible Xtensa configuration (%s)\n"), + abfd, errmsg); + } + else + einfo (_("%P:%B: warning: cannot parse .xtensa.info section\n"), abfd); + + free (data); +} + + +/* This is called after the sections have been attached to output + sections, but before any sizes or addresses have been set. */ + +static void +elf_xtensa_before_allocation (void) +{ + asection *info_sec, *first_info_sec; + bfd *first_bfd; + bfd_boolean is_big_endian = XCHAL_HAVE_BE; + + /* Check that the output endianness matches the Xtensa + configuration. The BFD library always includes both big and + little endian target vectors for Xtensa, but it only supports the + detailed instruction encode/decode operations (such as are + required to process relocations) for the selected Xtensa + configuration. */ + + if (is_big_endian + && link_info.output_bfd->xvec->byteorder == BFD_ENDIAN_LITTLE) + { + einfo (_("%F%P: little endian output does not match " + "Xtensa configuration\n")); + } + if (!is_big_endian + && link_info.output_bfd->xvec->byteorder == BFD_ENDIAN_BIG) + { + einfo (_("%F%P: big endian output does not match " + "Xtensa configuration\n")); + } + + /* Keep track of the first input .xtensa.info section, and as a fallback, + the first input bfd where a .xtensa.info section could be created. + After the input .xtensa.info has been checked, the contents of the + first one will be replaced with the output .xtensa.info table. */ + first_info_sec = 0; + first_bfd = 0; + + LANG_FOR_EACH_INPUT_STATEMENT (f) + { + /* Check that the endianness for each input file matches the output. + The merge_private_bfd_data hook has already reported any mismatches + as errors, but those errors are not fatal. At this point, we + cannot go any further if there are any mismatches. */ + if ((is_big_endian && f->the_bfd->xvec->byteorder == BFD_ENDIAN_LITTLE) + || (!is_big_endian && f->the_bfd->xvec->byteorder == BFD_ENDIAN_BIG)) + einfo (_("%F%P: cross-endian linking for %B not supported\n"), + f->the_bfd); + + if (! first_bfd) + first_bfd = f->the_bfd; + + info_sec = bfd_get_section_by_name (f->the_bfd, ".xtensa.info"); + if (! info_sec) + continue; + + if (! first_info_sec) + first_info_sec = info_sec; + + /* Unpack the .xtensa.info section and check it against the current + Xtensa configuration. */ + check_xtensa_info (f->the_bfd, info_sec); + + /* Do not include this copy of .xtensa.info in the output. */ + info_sec->size = 0; + info_sec->flags |= SEC_EXCLUDE; + } + + /* Reuse the first .xtensa.info input section to hold the output + .xtensa.info; or, if none were found, create a new section in the + first input bfd (assuming there is one). */ + info_sec = first_info_sec; + if (! info_sec && first_bfd) + { + info_sec = bfd_make_section_with_flags (first_bfd, ".xtensa.info", + SEC_HAS_CONTENTS | SEC_READONLY); + if (! info_sec) + einfo (_("%F%P: failed to create .xtensa.info section\n")); + } + if (info_sec) + { + int xtensa_info_size; + char *data; + + info_sec->flags &= ~SEC_EXCLUDE; + info_sec->flags |= SEC_IN_MEMORY; + + data = xmalloc (100); + sprintf (data, "USE_ABSOLUTE_LITERALS=%d\nABI=%d\n", + XSHAL_USE_ABSOLUTE_LITERALS, XSHAL_ABI); + xtensa_info_size = strlen (data) + 1; + + /* Add enough null terminators to pad to a word boundary. */ + do + data[xtensa_info_size++] = 0; + while ((xtensa_info_size & 3) != 0); + + info_sec->size = 12 + XTINFO_NAMESZ + xtensa_info_size; + info_sec->contents = xmalloc (info_sec->size); + bfd_put_32 (info_sec->owner, XTINFO_NAMESZ, info_sec->contents + 0); + bfd_put_32 (info_sec->owner, xtensa_info_size, info_sec->contents + 4); + bfd_put_32 (info_sec->owner, XTINFO_TYPE, info_sec->contents + 8); + memcpy (info_sec->contents + 12, XTINFO_NAME, XTINFO_NAMESZ); + memcpy (info_sec->contents + 12 + XTINFO_NAMESZ, data, xtensa_info_size); + free (data); + } + + /* Enable relaxation by default if the "--no-relax" option was not + specified. This is done here instead of in the before_parse hook + because there is a check in main() to prohibit use of --relax and + -r together and that combination should be allowed for Xtensa. */ + + if (!disable_relaxation) + command_line.relax = TRUE; + + xtensa_strip_inconsistent_linkonce_sections (stat_ptr); + + gld${EMULATION_NAME}_before_allocation (); + + xtensa_wild_group_interleave (stat_ptr->head); + if (command_line.relax) + xtensa_colocate_output_literals (stat_ptr->head); + + /* TBD: We need to force the page alignments to here and only do + them as needed for the entire output section. Finally, if this + is a relocatable link then we need to add alignment notes so + that the literals can be separated later. */ +} + + +typedef struct wildcard_list section_name_list; + +typedef struct reloc_deps_e_t reloc_deps_e; +typedef struct reloc_deps_section_t reloc_deps_section; +typedef struct reloc_deps_graph_t reloc_deps_graph; + + +struct reloc_deps_e_t +{ + asection *src; /* Contains l32rs. */ + asection *tgt; /* Contains literals. */ + reloc_deps_e *next; +}; + +/* Place these in the userdata field. */ +struct reloc_deps_section_t +{ + reloc_deps_e *preds; + reloc_deps_e *succs; + bfd_boolean is_only_literal; +}; + + +struct reloc_deps_graph_t +{ + size_t count; + size_t size; + asection **sections; +}; + +static void xtensa_layout_wild + (const reloc_deps_graph *, lang_wild_statement_type *); + +typedef void (*deps_callback_t) (asection *, /* src_sec */ + bfd_vma, /* src_offset */ + asection *, /* target_sec */ + bfd_vma, /* target_offset */ + void *); /* closure */ + +extern bfd_boolean xtensa_callback_required_dependence + (bfd *, asection *, struct bfd_link_info *, deps_callback_t, void *); +static void xtensa_ldlang_clear_addresses (lang_statement_union_type *); +static bfd_boolean ld_local_file_relocations_fit + (lang_statement_union_type *, const reloc_deps_graph *); +static bfd_vma ld_assign_relative_paged_dot + (bfd_vma, lang_statement_union_type *, const reloc_deps_graph *, + bfd_boolean); +static bfd_vma ld_xtensa_insert_page_offsets + (bfd_vma, lang_statement_union_type *, reloc_deps_graph *, bfd_boolean); +#if EXTRA_VALIDATION +static size_t ld_count_children (lang_statement_union_type *); +#endif + +extern lang_statement_list_type constructor_list; + +/* Begin verbatim code from ldlang.c: + the following are copied from ldlang.c because they are defined + there statically. */ + +static void +lang_for_each_statement_worker (void (*func) (lang_statement_union_type *), + lang_statement_union_type *s) +{ + for (; s != (lang_statement_union_type *) NULL; s = s->header.next) + { + func (s); + + switch (s->header.type) + { + case lang_constructors_statement_enum: + lang_for_each_statement_worker (func, constructor_list.head); + break; + case lang_output_section_statement_enum: + lang_for_each_statement_worker + (func, + s->output_section_statement.children.head); + break; + case lang_wild_statement_enum: + lang_for_each_statement_worker + (func, + s->wild_statement.children.head); + break; + case lang_group_statement_enum: + lang_for_each_statement_worker (func, + s->group_statement.children.head); + break; + case lang_data_statement_enum: + case lang_reloc_statement_enum: + case lang_object_symbols_statement_enum: + case lang_output_statement_enum: + case lang_target_statement_enum: + case lang_input_section_enum: + case lang_input_statement_enum: + case lang_assignment_statement_enum: + case lang_padding_statement_enum: + case lang_address_statement_enum: + case lang_fill_statement_enum: + break; + default: + FAIL (); + break; + } + } +} + +/* End of verbatim code from ldlang.c. */ + + +static reloc_deps_section * +xtensa_get_section_deps (const reloc_deps_graph *deps ATTRIBUTE_UNUSED, + asection *sec) +{ + /* We have a separate function for this so that + we could in the future keep a completely independent + structure that maps a section to its dependence edges. + For now, we place these in the sec->userdata field. */ + reloc_deps_section *sec_deps = sec->userdata; + return sec_deps; +} + +static void +xtensa_set_section_deps (const reloc_deps_graph *deps ATTRIBUTE_UNUSED, + asection *sec, + reloc_deps_section *deps_section) +{ + sec->userdata = deps_section; +} + + +/* This is used to keep a list of all of the sections participating in + the graph so we can clean them up quickly. */ + +static void +xtensa_append_section_deps (reloc_deps_graph *deps, asection *sec) +{ + if (deps->size <= deps->count) + { + asection **new_sections; + size_t i; + size_t new_size; + + new_size = deps->size * 2; + if (new_size == 0) + new_size = 20; + + new_sections = xmalloc (sizeof (asection *) * new_size); + memset (new_sections, 0, sizeof (asection *) * new_size); + for (i = 0; i < deps->count; i++) + { + new_sections[i] = deps->sections[i]; + } + if (deps->sections != NULL) + free (deps->sections); + deps->sections = new_sections; + deps->size = new_size; + } + deps->sections[deps->count] = sec; + deps->count++; +} + + +static void +free_reloc_deps_graph (reloc_deps_graph *deps) +{ + size_t i; + for (i = 0; i < deps->count; i++) + { + asection *sec = deps->sections[i]; + reloc_deps_section *sec_deps; + sec_deps = xtensa_get_section_deps (deps, sec); + if (sec_deps) + { + reloc_deps_e *next; + while (sec_deps->succs != NULL) + { + next = sec_deps->succs->next; + free (sec_deps->succs); + sec_deps->succs = next; + } + + while (sec_deps->preds != NULL) + { + next = sec_deps->preds->next; + free (sec_deps->preds); + sec_deps->preds = next; + } + free (sec_deps); + } + xtensa_set_section_deps (deps, sec, NULL); + } + if (deps->sections) + free (deps->sections); + + free (deps); +} + + +static bfd_boolean +section_is_source (const reloc_deps_graph *deps ATTRIBUTE_UNUSED, + lang_statement_union_type *s) +{ + asection *sec; + const reloc_deps_section *sec_deps; + + if (s->header.type != lang_input_section_enum) + return FALSE; + sec = s->input_section.section; + + sec_deps = xtensa_get_section_deps (deps, sec); + return sec_deps && sec_deps->succs != NULL; +} + + +static bfd_boolean +section_is_target (const reloc_deps_graph *deps ATTRIBUTE_UNUSED, + lang_statement_union_type *s) +{ + asection *sec; + const reloc_deps_section *sec_deps; + + if (s->header.type != lang_input_section_enum) + return FALSE; + sec = s->input_section.section; + + sec_deps = xtensa_get_section_deps (deps, sec); + return sec_deps && sec_deps->preds != NULL; +} + + +static bfd_boolean +section_is_source_or_target (const reloc_deps_graph *deps ATTRIBUTE_UNUSED, + lang_statement_union_type *s) +{ + return (section_is_source (deps, s) + || section_is_target (deps, s)); +} + + +typedef struct xtensa_ld_iter_stack_t xtensa_ld_iter_stack; +typedef struct xtensa_ld_iter_t xtensa_ld_iter; + +struct xtensa_ld_iter_t +{ + lang_statement_union_type *parent; /* Parent of the list. */ + lang_statement_list_type *l; /* List that holds it. */ + lang_statement_union_type **loc; /* Place in the list. */ +}; + +struct xtensa_ld_iter_stack_t +{ + xtensa_ld_iter iterloc; /* List that hold it. */ + + xtensa_ld_iter_stack *next; /* Next in the stack. */ + xtensa_ld_iter_stack *prev; /* Back pointer for stack. */ +}; + + +static void +ld_xtensa_move_section_after (xtensa_ld_iter *to, xtensa_ld_iter *current) +{ + lang_statement_union_type *to_next; + lang_statement_union_type *current_next; + lang_statement_union_type **e; + +#if EXTRA_VALIDATION + size_t old_to_count, new_to_count; + size_t old_current_count, new_current_count; +#endif + + if (to == current) + return; + +#if EXTRA_VALIDATION + old_to_count = ld_count_children (to->parent); + old_current_count = ld_count_children (current->parent); +#endif + + to_next = *(to->loc); + current_next = (*current->loc)->header.next; + + *(to->loc) = *(current->loc); + + *(current->loc) = current_next; + (*(to->loc))->header.next = to_next; + + /* reset "to" list tail */ + for (e = &to->l->head; *e != NULL; e = &(*e)->header.next) + ; + to->l->tail = e; + + /* reset "current" list tail */ + for (e = ¤t->l->head; *e != NULL; e = &(*e)->header.next) + ; + current->l->tail = e; + +#if EXTRA_VALIDATION + new_to_count = ld_count_children (to->parent); + new_current_count = ld_count_children (current->parent); + + ASSERT ((old_to_count + old_current_count) + == (new_to_count + new_current_count)); +#endif +} + + +/* Can only be called with lang_statements that have lists. Returns + FALSE if the list is empty. */ + +static bfd_boolean +iter_stack_empty (xtensa_ld_iter_stack **stack_p) +{ + return *stack_p == NULL; +} + + +static bfd_boolean +iter_stack_push (xtensa_ld_iter_stack **stack_p, + lang_statement_union_type *parent) +{ + xtensa_ld_iter_stack *stack; + lang_statement_list_type *l = NULL; + + switch (parent->header.type) + { + case lang_output_section_statement_enum: + l = &parent->output_section_statement.children; + break; + case lang_wild_statement_enum: + l = &parent->wild_statement.children; + break; + case lang_group_statement_enum: + l = &parent->group_statement.children; + break; + default: + ASSERT (0); + return FALSE; + } + + /* Empty. do not push. */ + if (l->tail == &l->head) + return FALSE; + + stack = xmalloc (sizeof (xtensa_ld_iter_stack)); + memset (stack, 0, sizeof (xtensa_ld_iter_stack)); + stack->iterloc.parent = parent; + stack->iterloc.l = l; + stack->iterloc.loc = &l->head; + + stack->next = *stack_p; + stack->prev = NULL; + if (*stack_p != NULL) + (*stack_p)->prev = stack; + *stack_p = stack; + return TRUE; +} + + +static void +iter_stack_pop (xtensa_ld_iter_stack **stack_p) +{ + xtensa_ld_iter_stack *stack; + + stack = *stack_p; + + if (stack == NULL) + { + ASSERT (stack != NULL); + return; + } + + if (stack->next != NULL) + stack->next->prev = NULL; + + *stack_p = stack->next; + free (stack); +} + + +/* This MUST be called if, during iteration, the user changes the + underlying structure. It will check for a NULL current and advance + accordingly. */ + +static void +iter_stack_update (xtensa_ld_iter_stack **stack_p) +{ + if (!iter_stack_empty (stack_p) + && (*(*stack_p)->iterloc.loc) == NULL) + { + iter_stack_pop (stack_p); + + while (!iter_stack_empty (stack_p) + && ((*(*stack_p)->iterloc.loc)->header.next == NULL)) + { + iter_stack_pop (stack_p); + } + if (!iter_stack_empty (stack_p)) + (*stack_p)->iterloc.loc = &(*(*stack_p)->iterloc.loc)->header.next; + } +} + + +static void +iter_stack_next (xtensa_ld_iter_stack **stack_p) +{ + xtensa_ld_iter_stack *stack; + lang_statement_union_type *current; + stack = *stack_p; + + current = *stack->iterloc.loc; + /* If we are on the first element. */ + if (current != NULL) + { + switch (current->header.type) + { + case lang_output_section_statement_enum: + case lang_wild_statement_enum: + case lang_group_statement_enum: + /* If the list if not empty, we are done. */ + if (iter_stack_push (stack_p, *stack->iterloc.loc)) + return; + /* Otherwise increment the pointer as normal. */ + break; + default: + break; + } + } + + while (!iter_stack_empty (stack_p) + && ((*(*stack_p)->iterloc.loc)->header.next == NULL)) + { + iter_stack_pop (stack_p); + } + if (!iter_stack_empty (stack_p)) + (*stack_p)->iterloc.loc = &(*(*stack_p)->iterloc.loc)->header.next; +} + + +static lang_statement_union_type * +iter_stack_current (xtensa_ld_iter_stack **stack_p) +{ + return *((*stack_p)->iterloc.loc); +} + + +/* The iter stack is a preorder. */ + +static void +iter_stack_create (xtensa_ld_iter_stack **stack_p, + lang_statement_union_type *parent) +{ + iter_stack_push (stack_p, parent); +} + + +static void +iter_stack_copy_current (xtensa_ld_iter_stack **stack_p, xtensa_ld_iter *front) +{ + *front = (*stack_p)->iterloc; +} + + +static void +xtensa_colocate_literals (reloc_deps_graph *deps, + lang_statement_union_type *statement) +{ + /* Keep a stack of pointers to control iteration through the contours. */ + xtensa_ld_iter_stack *stack = NULL; + xtensa_ld_iter_stack **stack_p = &stack; + + xtensa_ld_iter front; /* Location where new insertion should occur. */ + xtensa_ld_iter *front_p = NULL; + + xtensa_ld_iter current; /* Location we are checking. */ + xtensa_ld_iter *current_p = NULL; + bfd_boolean in_literals = FALSE; + + if (deps->count == 0) + return; + + iter_stack_create (stack_p, statement); + + while (!iter_stack_empty (stack_p)) + { + bfd_boolean skip_increment = FALSE; + lang_statement_union_type *l = iter_stack_current (stack_p); + + switch (l->header.type) + { + case lang_assignment_statement_enum: + /* Any assignment statement should block reordering across it. */ + front_p = NULL; + in_literals = FALSE; + break; + + case lang_input_section_enum: + if (front_p == NULL) + { + in_literals = (section_is_target (deps, l) + && !section_is_source (deps, l)); + if (in_literals) + { + front_p = &front; + iter_stack_copy_current (stack_p, front_p); + } + } + else + { + bfd_boolean is_target; + current_p = ¤t; + iter_stack_copy_current (stack_p, current_p); + is_target = (section_is_target (deps, l) + && !section_is_source (deps, l)); + + if (in_literals) + { + iter_stack_copy_current (stack_p, front_p); + if (!is_target) + in_literals = FALSE; + } + else + { + if (is_target) + { + /* Try to insert in place. */ + ld_xtensa_move_section_after (front_p, current_p); + ld_assign_relative_paged_dot (0x100000, + statement, + deps, + xtensa_use_literal_pages); + + /* We use this code because it's already written. */ + if (!ld_local_file_relocations_fit (statement, deps)) + { + /* Move it back. */ + ld_xtensa_move_section_after (current_p, front_p); + /* Reset the literal placement. */ + iter_stack_copy_current (stack_p, front_p); + } + else + { + /* Move front pointer up by one. */ + front_p->loc = &(*front_p->loc)->header.next; + + /* Do not increment the current pointer. */ + skip_increment = TRUE; + } + } + } + } + break; + default: + break; + } + + if (!skip_increment) + iter_stack_next (stack_p); + else + /* Be careful to update the stack_p if it now is a null. */ + iter_stack_update (stack_p); + } + + lang_for_each_statement_worker (xtensa_ldlang_clear_addresses, statement); +} + + +static void +xtensa_move_dependencies_to_front (reloc_deps_graph *deps, + lang_wild_statement_type *w) +{ + /* Keep a front pointer and a current pointer. */ + lang_statement_union_type **front; + lang_statement_union_type **current; + + /* Walk to the end of the targets. */ + for (front = &w->children.head; + (*front != NULL) && section_is_source_or_target (deps, *front); + front = &(*front)->header.next) + ; + + if (*front == NULL) + return; + + current = &(*front)->header.next; + while (*current != NULL) + { + if (section_is_source_or_target (deps, *current)) + { + /* Insert in place. */ + xtensa_ld_iter front_iter; + xtensa_ld_iter current_iter; + + front_iter.parent = (lang_statement_union_type *) w; + front_iter.l = &w->children; + front_iter.loc = front; + + current_iter.parent = (lang_statement_union_type *) w; + current_iter.l = &w->children; + current_iter.loc = current; + + ld_xtensa_move_section_after (&front_iter, ¤t_iter); + front = &(*front)->header.next; + } + else + { + current = &(*current)->header.next; + } + } +} + + +static bfd_boolean +deps_has_sec_edge (const reloc_deps_graph *deps, asection *src, asection *tgt) +{ + const reloc_deps_section *sec_deps; + const reloc_deps_e *sec_deps_e; + + sec_deps = xtensa_get_section_deps (deps, src); + if (sec_deps == NULL) + return FALSE; + + for (sec_deps_e = sec_deps->succs; + sec_deps_e != NULL; + sec_deps_e = sec_deps_e->next) + { + ASSERT (sec_deps_e->src == src); + if (sec_deps_e->tgt == tgt) + return TRUE; + } + return FALSE; +} + + +static bfd_boolean +deps_has_edge (const reloc_deps_graph *deps, + lang_statement_union_type *src, + lang_statement_union_type *tgt) +{ + if (!section_is_source (deps, src)) + return FALSE; + if (!section_is_target (deps, tgt)) + return FALSE; + + if (src->header.type != lang_input_section_enum) + return FALSE; + if (tgt->header.type != lang_input_section_enum) + return FALSE; + + return deps_has_sec_edge (deps, src->input_section.section, + tgt->input_section.section); +} + + +static void +add_deps_edge (reloc_deps_graph *deps, asection *src_sec, asection *tgt_sec) +{ + reloc_deps_section *src_sec_deps; + reloc_deps_section *tgt_sec_deps; + + reloc_deps_e *src_edge; + reloc_deps_e *tgt_edge; + + if (deps_has_sec_edge (deps, src_sec, tgt_sec)) + return; + + src_sec_deps = xtensa_get_section_deps (deps, src_sec); + if (src_sec_deps == NULL) + { + /* Add a section. */ + src_sec_deps = xmalloc (sizeof (reloc_deps_section)); + memset (src_sec_deps, 0, sizeof (reloc_deps_section)); + src_sec_deps->is_only_literal = 0; + src_sec_deps->preds = NULL; + src_sec_deps->succs = NULL; + xtensa_set_section_deps (deps, src_sec, src_sec_deps); + xtensa_append_section_deps (deps, src_sec); + } + + tgt_sec_deps = xtensa_get_section_deps (deps, tgt_sec); + if (tgt_sec_deps == NULL) + { + /* Add a section. */ + tgt_sec_deps = xmalloc (sizeof (reloc_deps_section)); + memset (tgt_sec_deps, 0, sizeof (reloc_deps_section)); + tgt_sec_deps->is_only_literal = 0; + tgt_sec_deps->preds = NULL; + tgt_sec_deps->succs = NULL; + xtensa_set_section_deps (deps, tgt_sec, tgt_sec_deps); + xtensa_append_section_deps (deps, tgt_sec); + } + + /* Add the edges. */ + src_edge = xmalloc (sizeof (reloc_deps_e)); + memset (src_edge, 0, sizeof (reloc_deps_e)); + src_edge->src = src_sec; + src_edge->tgt = tgt_sec; + src_edge->next = src_sec_deps->succs; + src_sec_deps->succs = src_edge; + + tgt_edge = xmalloc (sizeof (reloc_deps_e)); + memset (tgt_edge, 0, sizeof (reloc_deps_e)); + tgt_edge->src = src_sec; + tgt_edge->tgt = tgt_sec; + tgt_edge->next = tgt_sec_deps->preds; + tgt_sec_deps->preds = tgt_edge; +} + + +static void +build_deps_graph_callback (asection *src_sec, + bfd_vma src_offset ATTRIBUTE_UNUSED, + asection *target_sec, + bfd_vma target_offset ATTRIBUTE_UNUSED, + void *closure) +{ + reloc_deps_graph *deps = closure; + + /* If the target is defined. */ + if (target_sec != NULL) + add_deps_edge (deps, src_sec, target_sec); +} + + +static reloc_deps_graph * +ld_build_required_section_dependence (lang_statement_union_type *s) +{ + reloc_deps_graph *deps; + xtensa_ld_iter_stack *stack = NULL; + + deps = xmalloc (sizeof (reloc_deps_graph)); + deps->sections = NULL; + deps->count = 0; + deps->size = 0; + + for (iter_stack_create (&stack, s); + !iter_stack_empty (&stack); + iter_stack_next (&stack)) + { + lang_statement_union_type *l = iter_stack_current (&stack); + + if (l->header.type == lang_input_section_enum) + { + lang_input_section_type *input; + input = &l->input_section; + xtensa_callback_required_dependence (input->section->owner, + input->section, + &link_info, + /* Use the same closure. */ + build_deps_graph_callback, + deps); + } + } + return deps; +} + + +#if EXTRA_VALIDATION +static size_t +ld_count_children (lang_statement_union_type *s) +{ + size_t count = 0; + xtensa_ld_iter_stack *stack = NULL; + for (iter_stack_create (&stack, s); + !iter_stack_empty (&stack); + iter_stack_next (&stack)) + { + lang_statement_union_type *l = iter_stack_current (&stack); + ASSERT (l != NULL); + count++; + } + return count; +} +#endif /* EXTRA_VALIDATION */ + + +/* Check if a particular section is included in the link. This will only + be true for one instance of a particular linkonce section. */ + +static bfd_boolean input_section_found = FALSE; +static asection *input_section_target = NULL; + +static void +input_section_linked_worker (lang_statement_union_type *statement) +{ + if ((statement->header.type == lang_input_section_enum + && (statement->input_section.section == input_section_target))) + input_section_found = TRUE; +} + +static bfd_boolean +input_section_linked (asection *sec) +{ + input_section_found = FALSE; + input_section_target = sec; + lang_for_each_statement_worker (input_section_linked_worker, stat_ptr->head); + return input_section_found; +} + + +/* Strip out any linkonce property tables or XCC exception tables where the + associated linkonce text is from a different object file. Normally, + a matching set of linkonce sections is taken from the same object file, + but sometimes the files are compiled differently so that some of the + linkonce sections are not present in all files. Stripping the + inconsistent sections like this is not completely robust -- a much + better solution is to use comdat groups. */ + +static int linkonce_len = sizeof (".gnu.linkonce.") - 1; + +static bfd_boolean +is_inconsistent_linkonce_section (asection *sec) +{ + bfd *abfd = sec->owner; + const char *sec_name = bfd_get_section_name (abfd, sec); + const char *name; + + if ((bfd_get_section_flags (abfd, sec) & SEC_LINK_ONCE) == 0 + || strncmp (sec_name, ".gnu.linkonce.", linkonce_len) != 0) + return FALSE; + + /* Check if this is an Xtensa property section or an exception table + for Tensilica's XCC compiler. */ + name = sec_name + linkonce_len; + if (CONST_STRNEQ (name, "prop.")) + name = strchr (name + 5, '.') + 1; + else if (name[1] == '.' + && (name[0] == 'p' || name[0] == 'e' || name[0] == 'h')) + name += 2; + else + name = 0; + + if (name) + { + char *dep_sec_name = xmalloc (strlen (sec_name) + 1); + asection *dep_sec; + + /* Get the associated linkonce text section and check if it is + included in the link. If not, this section is inconsistent + and should be stripped. */ + strcpy (dep_sec_name, ".gnu.linkonce.t."); + strcat (dep_sec_name, name); + dep_sec = bfd_get_section_by_name (abfd, dep_sec_name); + if (dep_sec == NULL || ! input_section_linked (dep_sec)) + { + free (dep_sec_name); + return TRUE; + } + free (dep_sec_name); + } + + return FALSE; +} + + +static void +xtensa_strip_inconsistent_linkonce_sections (lang_statement_list_type *slist) +{ + lang_statement_union_type **s_p = &slist->head; + while (*s_p) + { + lang_statement_union_type *s = *s_p; + lang_statement_union_type *s_next = (*s_p)->header.next; + + switch (s->header.type) + { + case lang_input_section_enum: + if (is_inconsistent_linkonce_section (s->input_section.section)) + { + s->input_section.section->output_section = bfd_abs_section_ptr; + *s_p = s_next; + continue; + } + break; + + case lang_constructors_statement_enum: + xtensa_strip_inconsistent_linkonce_sections (&constructor_list); + break; + + case lang_output_section_statement_enum: + if (s->output_section_statement.children.head) + xtensa_strip_inconsistent_linkonce_sections + (&s->output_section_statement.children); + break; + + case lang_wild_statement_enum: + xtensa_strip_inconsistent_linkonce_sections + (&s->wild_statement.children); + break; + + case lang_group_statement_enum: + xtensa_strip_inconsistent_linkonce_sections + (&s->group_statement.children); + break; + + case lang_data_statement_enum: + case lang_reloc_statement_enum: + case lang_object_symbols_statement_enum: + case lang_output_statement_enum: + case lang_target_statement_enum: + case lang_input_statement_enum: + case lang_assignment_statement_enum: + case lang_padding_statement_enum: + case lang_address_statement_enum: + case lang_fill_statement_enum: + break; + + default: + FAIL (); + break; + } + + s_p = &(*s_p)->header.next; + } + + /* Reset the tail of the list, in case the last entry was removed. */ + if (s_p != slist->tail) + slist->tail = s_p; +} + + +static void +xtensa_wild_group_interleave_callback (lang_statement_union_type *statement) +{ + lang_wild_statement_type *w; + reloc_deps_graph *deps; + if (statement->header.type == lang_wild_statement_enum) + { +#if EXTRA_VALIDATION + size_t old_child_count; + size_t new_child_count; +#endif + bfd_boolean no_reorder; + + w = &statement->wild_statement; + + no_reorder = FALSE; + + /* If it has 0 or 1 section bound, then do not reorder. */ + if (w->children.head == NULL + || (w->children.head->header.type == lang_input_section_enum + && w->children.head->header.next == NULL)) + no_reorder = TRUE; + + if (w->filenames_sorted) + no_reorder = TRUE; + + /* Check for sorting in a section list wildcard spec as well. */ + if (!no_reorder) + { + struct wildcard_list *l; + for (l = w->section_list; l != NULL; l = l->next) + { + if (l->spec.sorted == TRUE) + { + no_reorder = TRUE; + break; + } + } + } + + /* Special case until the NOREORDER linker directive is supported: + *(.init) output sections and *(.fini) specs may NOT be reordered. */ + + /* Check for sorting in a section list wildcard spec as well. */ + if (!no_reorder) + { + struct wildcard_list *l; + for (l = w->section_list; l != NULL; l = l->next) + { + if (l->spec.name + && ((strcmp (".init", l->spec.name) == 0) + || (strcmp (".fini", l->spec.name) == 0))) + { + no_reorder = TRUE; + break; + } + } + } + +#if EXTRA_VALIDATION + old_child_count = ld_count_children (statement); +#endif + + /* It is now officially a target. Build the graph of source + section -> target section (kept as a list of edges). */ + deps = ld_build_required_section_dependence (statement); + + /* If this wildcard does not reorder.... */ + if (!no_reorder && deps->count != 0) + { + /* First check for reverse dependences. Fix if possible. */ + xtensa_layout_wild (deps, w); + + xtensa_move_dependencies_to_front (deps, w); +#if EXTRA_VALIDATION + new_child_count = ld_count_children (statement); + ASSERT (new_child_count == old_child_count); +#endif + + xtensa_colocate_literals (deps, statement); + +#if EXTRA_VALIDATION + new_child_count = ld_count_children (statement); + ASSERT (new_child_count == old_child_count); +#endif + } + + /* Clean up. */ + free_reloc_deps_graph (deps); + } +} + + +static void +xtensa_wild_group_interleave (lang_statement_union_type *s) +{ + lang_for_each_statement_worker (xtensa_wild_group_interleave_callback, s); +} + + +static void +xtensa_layout_wild (const reloc_deps_graph *deps, lang_wild_statement_type *w) +{ + /* If it does not fit initially, we need to do this step. Move all + of the wild literal sections to a new list, then move each of + them back in just before the first section they depend on. */ + lang_statement_union_type **s_p; +#if EXTRA_VALIDATION + size_t old_count, new_count; + size_t ct1, ct2; +#endif + + lang_wild_statement_type literal_wild; + literal_wild.header.next = NULL; + literal_wild.header.type = lang_wild_statement_enum; + literal_wild.filename = NULL; + literal_wild.filenames_sorted = FALSE; + literal_wild.section_list = NULL; + literal_wild.keep_sections = FALSE; + literal_wild.children.head = NULL; + literal_wild.children.tail = &literal_wild.children.head; + +#if EXTRA_VALIDATION + old_count = ld_count_children ((lang_statement_union_type*) w); +#endif + + s_p = &w->children.head; + while (*s_p != NULL) + { + lang_statement_union_type *l = *s_p; + if (l->header.type == lang_input_section_enum) + { + if (section_is_target (deps, l) + && ! section_is_source (deps, l)) + { + /* Detach. */ + *s_p = l->header.next; + if (*s_p == NULL) + w->children.tail = s_p; + l->header.next = NULL; + + /* Append. */ + *literal_wild.children.tail = l; + literal_wild.children.tail = &l->header.next; + continue; + } + } + s_p = &(*s_p)->header.next; + } + +#if EXTRA_VALIDATION + ct1 = ld_count_children ((lang_statement_union_type*) w); + ct2 = ld_count_children ((lang_statement_union_type*) &literal_wild); + + ASSERT (old_count == (ct1 + ct2)); +#endif + + /* Now place them back in front of their dependent sections. */ + + while (literal_wild.children.head != NULL) + { + lang_statement_union_type *lit = literal_wild.children.head; + bfd_boolean placed = FALSE; + +#if EXTRA_VALIDATION + ASSERT (ct2 > 0); + ct2--; +#endif + + /* Detach. */ + literal_wild.children.head = lit->header.next; + if (literal_wild.children.head == NULL) + literal_wild.children.tail = &literal_wild.children.head; + lit->header.next = NULL; + + /* Find a spot to place it. */ + for (s_p = &w->children.head; *s_p != NULL; s_p = &(*s_p)->header.next) + { + lang_statement_union_type *src = *s_p; + if (deps_has_edge (deps, src, lit)) + { + /* Place it here. */ + lit->header.next = *s_p; + *s_p = lit; + placed = TRUE; + break; + } + } + + if (!placed) + { + /* Put it at the end. */ + *w->children.tail = lit; + w->children.tail = &lit->header.next; + } + } + +#if EXTRA_VALIDATION + new_count = ld_count_children ((lang_statement_union_type*) w); + ASSERT (new_count == old_count); +#endif +} + + +static void +xtensa_colocate_output_literals_callback (lang_statement_union_type *statement) +{ + lang_output_section_statement_type *os; + reloc_deps_graph *deps; + if (statement->header.type == lang_output_section_statement_enum) + { + /* Now, we walk over the contours of the output section statement. + + First we build the literal section dependences as before. + + At the first uniquely_literal section, we mark it as a good + spot to place other literals. Continue walking (and counting + sizes) until we find the next literal section. If this + section can be moved to the first one, then we move it. If + we every find a modification of ".", start over. If we find + a labeling of the current location, start over. Finally, at + the end, if we require page alignment, add page alignments. */ + +#if EXTRA_VALIDATION + size_t old_child_count; + size_t new_child_count; +#endif + bfd_boolean no_reorder = FALSE; + + os = &statement->output_section_statement; + +#if EXTRA_VALIDATION + old_child_count = ld_count_children (statement); +#endif + + /* It is now officially a target. Build the graph of source + section -> target section (kept as a list of edges). */ + + deps = ld_build_required_section_dependence (statement); + + /* If this wildcard does not reorder.... */ + if (!no_reorder) + { + /* First check for reverse dependences. Fix if possible. */ + xtensa_colocate_literals (deps, statement); + +#if EXTRA_VALIDATION + new_child_count = ld_count_children (statement); + ASSERT (new_child_count == old_child_count); +#endif + } + + /* Insert align/offset assignment statement. */ + if (xtensa_use_literal_pages) + { + ld_xtensa_insert_page_offsets (0, statement, deps, + xtensa_use_literal_pages); + lang_for_each_statement_worker (xtensa_ldlang_clear_addresses, + statement); + } + + /* Clean up. */ + free_reloc_deps_graph (deps); + } +} + + +static void +xtensa_colocate_output_literals (lang_statement_union_type *s) +{ + lang_for_each_statement_worker (xtensa_colocate_output_literals_callback, s); +} + + +static void +xtensa_ldlang_clear_addresses (lang_statement_union_type *statement) +{ + switch (statement->header.type) + { + case lang_input_section_enum: + { + asection *bfd_section = statement->input_section.section; + bfd_section->output_offset = 0; + } + break; + default: + break; + } +} + + +static bfd_vma +ld_assign_relative_paged_dot (bfd_vma dot, + lang_statement_union_type *s, + const reloc_deps_graph *deps ATTRIBUTE_UNUSED, + bfd_boolean lit_align) +{ + /* Walk through all of the input statements in this wild statement + assign dot to all of them. */ + + xtensa_ld_iter_stack *stack = NULL; + xtensa_ld_iter_stack **stack_p = &stack; + + bfd_boolean first_section = FALSE; + bfd_boolean in_literals = FALSE; + + for (iter_stack_create (stack_p, s); + !iter_stack_empty (stack_p); + iter_stack_next (stack_p)) + { + lang_statement_union_type *l = iter_stack_current (stack_p); + + switch (l->header.type) + { + case lang_input_section_enum: + { + asection *section = l->input_section.section; + size_t align_pow = section->alignment_power; + bfd_boolean do_xtensa_alignment = FALSE; + + if (lit_align) + { + bfd_boolean sec_is_target = section_is_target (deps, l); + bfd_boolean sec_is_source = section_is_source (deps, l); + + if (section->size != 0 + && (first_section + || (in_literals && !sec_is_target) + || (!in_literals && sec_is_target))) + { + do_xtensa_alignment = TRUE; + } + first_section = FALSE; + if (section->size != 0) + in_literals = (sec_is_target && !sec_is_source); + } + + if (do_xtensa_alignment && xtensa_page_power != 0) + dot += (1 << xtensa_page_power); + + dot = align_power (dot, align_pow); + section->output_offset = dot; + dot += section->size; + } + break; + case lang_fill_statement_enum: + dot += l->fill_statement.size; + break; + case lang_padding_statement_enum: + dot += l->padding_statement.size; + break; + default: + break; + } + } + return dot; +} + + +static bfd_boolean +ld_local_file_relocations_fit (lang_statement_union_type *statement, + const reloc_deps_graph *deps ATTRIBUTE_UNUSED) +{ + /* Walk over all of the dependencies that we identified and make + sure that IF the source and target are here (addr != 0): + 1) target addr < source addr + 2) (roundup(source + source_size, 4) - rounddown(target, 4)) + < (256K - (1 << bad align)) + Need a worst-case proof.... */ + + xtensa_ld_iter_stack *stack = NULL; + xtensa_ld_iter_stack **stack_p = &stack; + size_t max_align_power = 0; + size_t align_penalty = 256; + reloc_deps_e *e; + size_t i; + + /* Find the worst-case alignment requirement for this set of statements. */ + for (iter_stack_create (stack_p, statement); + !iter_stack_empty (stack_p); + iter_stack_next (stack_p)) + { + lang_statement_union_type *l = iter_stack_current (stack_p); + if (l->header.type == lang_input_section_enum) + { + lang_input_section_type *input = &l->input_section; + asection *section = input->section; + if (section->alignment_power > max_align_power) + max_align_power = section->alignment_power; + } + } + + /* Now check that everything fits. */ + for (i = 0; i < deps->count; i++) + { + asection *sec = deps->sections[i]; + const reloc_deps_section *deps_section = + xtensa_get_section_deps (deps, sec); + if (deps_section) + { + /* We choose to walk through the successors. */ + for (e = deps_section->succs; e != NULL; e = e->next) + { + if (e->src != e->tgt + && e->src->output_section == e->tgt->output_section + && e->src->output_offset != 0 + && e->tgt->output_offset != 0) + { + bfd_vma l32r_addr = + align_power (e->src->output_offset + e->src->size, 2); + bfd_vma target_addr = e->tgt->output_offset & ~3; + if (l32r_addr < target_addr) + { + fprintf (stderr, "Warning: " + "l32r target section before l32r\n"); + return FALSE; + } + + if (l32r_addr - target_addr > 256 * 1024 - align_penalty) + return FALSE; + } + } + } + } + + return TRUE; +} + + +static bfd_vma +ld_xtensa_insert_page_offsets (bfd_vma dot, + lang_statement_union_type *s, + reloc_deps_graph *deps, + bfd_boolean lit_align) +{ + xtensa_ld_iter_stack *stack = NULL; + xtensa_ld_iter_stack **stack_p = &stack; + + bfd_boolean first_section = FALSE; + bfd_boolean in_literals = FALSE; + + if (!lit_align) + return FALSE; + + for (iter_stack_create (stack_p, s); + !iter_stack_empty (stack_p); + iter_stack_next (stack_p)) + { + lang_statement_union_type *l = iter_stack_current (stack_p); + + switch (l->header.type) + { + case lang_input_section_enum: + { + asection *section = l->input_section.section; + bfd_boolean do_xtensa_alignment = FALSE; + + if (lit_align) + { + if (section->size != 0 + && (first_section + || (in_literals && !section_is_target (deps, l)) + || (!in_literals && section_is_target (deps, l)))) + { + do_xtensa_alignment = TRUE; + } + first_section = FALSE; + if (section->size != 0) + { + in_literals = (section_is_target (deps, l) + && !section_is_source (deps, l)); + } + } + + if (do_xtensa_alignment && xtensa_page_power != 0) + { + /* Create an expression that increments the current address, + i.e., "dot", by (1 << xtensa_align_power). */ + etree_type *name_op = exp_nameop (NAME, "."); + etree_type *addend_op = exp_intop (1 << xtensa_page_power); + etree_type *add_op = exp_binop ('+', name_op, addend_op); + etree_type *assign_op = exp_assop ('=', ".", add_op); + + lang_assignment_statement_type *assign_stmt; + lang_statement_union_type *assign_union; + lang_statement_list_type tmplist; + lang_statement_list_type *old_stat_ptr = stat_ptr; + + /* There is hidden state in "lang_add_assignment". It + appends the new assignment statement to the stat_ptr + list. Thus, we swap it before and after the call. */ + + tmplist.head = NULL; + tmplist.tail = &tmplist.head; + + stat_ptr = &tmplist; + /* Warning: side effect; statement appended to stat_ptr. */ + assign_stmt = lang_add_assignment (assign_op); + assign_union = (lang_statement_union_type *) assign_stmt; + stat_ptr = old_stat_ptr; + + assign_union->header.next = l; + *(*stack_p)->iterloc.loc = assign_union; + iter_stack_next (stack_p); + } + } + break; + default: + break; + } + } + return dot; +} + +EOF + +# Define some shell vars to insert bits of code into the standard ELF +# parse_args and list_options functions. +# +PARSE_AND_LIST_PROLOGUE=' +#define OPTION_OPT_SIZEOPT (300) +#define OPTION_NO_RELAX (OPTION_OPT_SIZEOPT + 1) +#define OPTION_LITERAL_MOVEMENT (OPTION_NO_RELAX + 1) +#define OPTION_NO_LITERAL_MOVEMENT (OPTION_LITERAL_MOVEMENT + 1) +extern int elf32xtensa_size_opt; +extern int elf32xtensa_no_literal_movement; +' + +PARSE_AND_LIST_LONGOPTS=' + { "size-opt", no_argument, NULL, OPTION_OPT_SIZEOPT}, + { "no-relax", no_argument, NULL, OPTION_NO_RELAX}, + { "literal-movement", no_argument, NULL, OPTION_LITERAL_MOVEMENT}, + { "no-literal-movement", no_argument, NULL, OPTION_NO_LITERAL_MOVEMENT}, +' + +PARSE_AND_LIST_OPTIONS=' + fprintf (file, _("\ + --size-opt When relaxing longcalls, prefer size\n\ + optimization over branch target alignment\n")); + fprintf (file, _("\ + --no-relax Do not relax branches or coalesce literals\n")); +' + +PARSE_AND_LIST_ARGS_CASES=' + case OPTION_OPT_SIZEOPT: + elf32xtensa_size_opt = 1; + break; + case OPTION_NO_RELAX: + disable_relaxation = TRUE; + break; + case OPTION_LITERAL_MOVEMENT: + elf32xtensa_no_literal_movement = 0; + break; + case OPTION_NO_LITERAL_MOVEMENT: + elf32xtensa_no_literal_movement = 1; + break; +' + +# Replace some of the standard ELF functions with our own versions. +# +LDEMUL_BEFORE_PARSE=elf_xtensa_before_parse +LDEMUL_AFTER_OPEN=elf_xtensa_after_open +LDEMUL_CHOOSE_TARGET=elf_xtensa_choose_target +LDEMUL_BEFORE_ALLOCATION=elf_xtensa_before_allocation Index: ia64elf.em =================================================================== --- ia64elf.em (nonexistent) +++ ia64elf.em (revision 816) @@ -0,0 +1,63 @@ +# This shell script emits a C file. -*- C -*- +# Copyright 2003, 2007 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +# This file is sourced from elf32.em, and defines extra ia64-elf +# specific routines. +# +# Define some shell vars to insert bits of code into the standard elf +# parse_args and list_options functions. +# +fragment <> e${EMULATION_NAME}.c + +fragment <xvec == &bfd_elf32_spu_vec; +} + +/* Create our note section. */ + +static void +spu_after_open (void) +{ + if (is_spu_target () + && !link_info.relocatable + && link_info.input_bfds != NULL + && !spu_elf_create_sections (&link_info, + stack_analysis, emit_stack_syms)) + einfo ("%X%P: can not create note section: %E\n"); + + gld${EMULATION_NAME}_after_open (); +} + +/* If O is NULL, add section S at the end of output section OUTPUT_NAME. + If O is not NULL, add section S at the beginning of output section O. + + Really, we should be duplicating ldlang.c map_input_to_output_sections + logic here, ie. using the linker script to find where the section + goes. That's rather a lot of code, and we don't want to run + map_input_to_output_sections again because most sections are already + mapped. So cheat, and put the section in a fixed place, ignoring any + attempt via a linker script to put .stub, .ovtab, and built-in + overlay manager code somewhere else. */ + +static void +spu_place_special_section (asection *s, asection *o, const char *output_name) +{ + lang_output_section_statement_type *os; + + os = lang_output_section_find (o != NULL ? o->name : output_name); + if (os == NULL) + { + const char *save = s->name; + s->name = output_name; + gld${EMULATION_NAME}_place_orphan (s); + s->name = save; + } + else if (o != NULL && os->children.head != NULL) + { + lang_statement_list_type add; + + lang_list_init (&add); + lang_add_section (&add, s, os); + *add.tail = os->children.head; + os->children.head = add.head; + } + else + lang_add_section (&os->children, s, os); + + s->output_section->size += s->size; +} + +/* Load built-in overlay manager, and tweak overlay section alignment. */ + +static void +spu_elf_load_ovl_mgr (void) +{ + lang_output_section_statement_type *os; + struct elf_link_hash_entry *h; + + h = elf_link_hash_lookup (elf_hash_table (&link_info), + "__ovly_load", FALSE, FALSE, FALSE); + + if (h != NULL + && (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + && h->def_regular) + { + /* User supplied __ovly_load. */ + } + else if (ovl_mgr_stream.start == ovl_mgr_stream.end) + einfo ("%F%P: no built-in overlay manager\n"); + else + { + lang_input_statement_type *ovl_is; + + ovl_is = lang_add_input_file ("builtin ovl_mgr", + lang_input_file_is_file_enum, + NULL); + + if (!spu_elf_open_builtin_lib (&ovl_is->the_bfd, &ovl_mgr_stream)) + einfo ("%X%P: can not open built-in overlay manager: %E\n"); + else + { + asection *in; + + if (!load_symbols (ovl_is, NULL)) + einfo ("%X%P: can not load built-in overlay manager: %E\n"); + + /* Map overlay manager sections to output sections. */ + for (in = ovl_is->the_bfd->sections; in != NULL; in = in->next) + if ((in->flags & (SEC_ALLOC | SEC_LOAD)) + == (SEC_ALLOC | SEC_LOAD)) + spu_place_special_section (in, NULL, ".text"); + } + } + + /* Ensure alignment of overlay sections is sufficient. */ + for (os = &lang_output_section_statement.head->output_section_statement; + os != NULL; + os = os->next) + if (os->bfd_section != NULL + && spu_elf_section_data (os->bfd_section) != NULL + && spu_elf_section_data (os->bfd_section)->u.o.ovl_index != 0) + { + if (os->bfd_section->alignment_power < 4) + os->bfd_section->alignment_power = 4; + + /* Also ensure size rounds up. */ + os->block_value = 16; + } +} + +/* Go find if we need to do anything special for overlays. */ + +static void +spu_before_allocation (void) +{ + if (is_spu_target () + && !link_info.relocatable + && !no_overlays) + { + /* Size the sections. This is premature, but we need to know the + rough layout so that overlays can be found. */ + expld.phase = lang_mark_phase_enum; + expld.dataseg.phase = exp_dataseg_none; + one_lang_size_sections_pass (NULL, TRUE); + + /* Find overlays by inspecting section vmas. */ + if (spu_elf_find_overlays (&link_info)) + { + int ret; + + if (auto_overlay != 0) + { + einfo ("%P: --auto-overlay ignored with user overlay script\n"); + auto_overlay = 0; + } + + ret = spu_elf_size_stubs (&link_info, + spu_place_special_section, + non_overlay_stubs); + if (ret == 0) + einfo ("%X%P: can not size overlay stubs: %E\n"); + else if (ret == 2) + spu_elf_load_ovl_mgr (); + } + + /* We must not cache anything from the preliminary sizing. */ + lang_reset_memory_regions (); + } + + gld${EMULATION_NAME}_before_allocation (); +} + +struct tflist { + struct tflist *next; + char name[9]; +}; + +static struct tflist *tmp_file_list; + +static void clean_tmp (void) +{ + for (; tmp_file_list != NULL; tmp_file_list = tmp_file_list->next) + unlink (tmp_file_list->name); +} + +static int +new_tmp_file (char **fname) +{ + struct tflist *tf; + int fd; + + if (tmp_file_list == NULL) + atexit (clean_tmp); + tf = xmalloc (sizeof (*tf)); + tf->next = tmp_file_list; + tmp_file_list = tf; + memcpy (tf->name, "ldXXXXXX", sizeof (tf->name)); + *fname = tf->name; +#ifdef HAVE_MKSTEMP + fd = mkstemp (*fname); +#else + *fname = mktemp (*fname); + if (*fname == NULL) + return -1; + fd = open (fname, O_RDWR | O_CREAT | O_EXCL, 0600); +#endif + return fd; +} + +static FILE * +spu_elf_open_overlay_script (void) +{ + FILE *script = NULL; + + if (auto_overlay_file == NULL) + { + int fd = new_tmp_file (&auto_overlay_file); + if (fd == -1) + goto file_err; + script = fdopen (fd, "w"); + } + else + script = fopen (auto_overlay_file, "w"); + + if (script == NULL) + { + file_err: + einfo ("%F%P: can not open script: %E\n"); + } + return script; +} + +static void +spu_elf_relink (void) +{ + char **argv = xmalloc ((my_argc + 5) * sizeof (*argv)); + + memcpy (argv, my_argv, my_argc * sizeof (*argv)); + argv[my_argc++] = "--no-auto-overlay"; + if (tmp_file_list->name == auto_overlay_file) + argv[my_argc++] = auto_overlay_file; + argv[my_argc++] = "-T"; + argv[my_argc++] = auto_overlay_file; + argv[my_argc] = 0; + execvp (argv[0], (char *const *) argv); + perror (argv[0]); + _exit (127); +} + +/* Final emulation specific call. */ + +static void +gld${EMULATION_NAME}_finish (void) +{ + int need_laying_out; + + need_laying_out = bfd_elf_discard_info (link_info.output_bfd, &link_info); + + gld${EMULATION_NAME}_map_segments (need_laying_out); + + if (is_spu_target ()) + { + if (local_store_lo < local_store_hi) + { + asection *s; + + s = spu_elf_check_vma (&link_info, auto_overlay, + local_store_lo, local_store_hi, + auto_overlay_fixed, auto_overlay_reserved, + spu_elf_load_ovl_mgr, + spu_elf_open_overlay_script, + spu_elf_relink); + if (s != NULL && !auto_overlay) + einfo ("%X%P: %A exceeds local store range\n", s); + } + else if (auto_overlay) + einfo ("%P: --auto-overlay ignored with zero local store range\n"); + + if (!spu_elf_build_stubs (&link_info, + emit_stub_syms || link_info.emitrelocations)) + einfo ("%X%P: can not build overlay stubs: %E\n"); + } + + finish_default (); +} + +static char * +gld${EMULATION_NAME}_choose_target (int argc, char *argv[]) +{ + my_argc = argc; + my_argv = argv; + return ldemul_default_target (argc, argv); +} + +EOF + +if grep -q 'ld_elf.*ppc.*_emulation' ldemul-list.h; then + fragment < +#include + +static const char * +base_name (const char *path) +{ + const char *file = strrchr (path, '/'); +#ifdef HAVE_DOS_BASED_FILE_SYSTEM + { + char *bslash = strrchr (path, '\\\\'); + + if (file == NULL || (bslash != NULL && bslash > file)) + file = bslash; + if (file == NULL + && path[0] != '\0' + && path[1] == ':') + file = path + 1; + } +#endif + if (file == NULL) + file = path; + else + ++file; + return file; +} + +/* This function is called when building a ppc32 or ppc64 executable + to handle embedded spu images. */ +extern bfd_boolean embedded_spu_file (lang_input_statement_type *, const char *); + +bfd_boolean +embedded_spu_file (lang_input_statement_type *entry, const char *flags) +{ + const char *cmd[6]; + const char *sym; + char *handle, *p; + char *oname; + int fd; + pid_t pid; + int status; + union lang_statement_union **old_stat_tail; + union lang_statement_union **old_file_tail; + union lang_statement_union *new_ent; + lang_input_statement_type *search; + + if (entry->the_bfd->format != bfd_object + || strcmp (entry->the_bfd->xvec->name, "elf32-spu") != 0 + || (entry->the_bfd->tdata.elf_obj_data->elf_header->e_type != ET_EXEC + && entry->the_bfd->tdata.elf_obj_data->elf_header->e_type != ET_DYN)) + return FALSE; + + /* Use the filename as the symbol marking the program handle struct. */ + sym = base_name (entry->the_bfd->filename); + + handle = xstrdup (sym); + for (p = handle; *p; ++p) + if (!(ISALNUM (*p) || *p == '$' || *p == '.')) + *p = '_'; + + fd = new_tmp_file (&oname); + if (fd == -1) + return FALSE; + close (fd); + + for (search = (lang_input_statement_type *) input_file_chain.head; + search != NULL; + search = (lang_input_statement_type *) search->next_real_file) + if (search->filename != NULL) + { + const char *infile = base_name (search->filename); + + if (strncmp (infile, "crtbegin", 8) == 0) + { + if (infile[8] == 'S') + flags = concat (flags, " -fPIC", (const char *) NULL); + else if (infile[8] == 'T') + flags = concat (flags, " -fpie", (const char *) NULL); + break; + } + } + + /* Use fork() and exec() rather than system() so that we don't + need to worry about quoting args. */ + cmd[0] = EMBEDSPU; + cmd[1] = flags; + cmd[2] = handle; + cmd[3] = entry->the_bfd->filename; + cmd[4] = oname; + cmd[5] = NULL; + if (trace_file_tries) + { + info_msg (_("running: %s \"%s\" \"%s\" \"%s\" \"%s\"\n"), + cmd[0], cmd[1], cmd[2], cmd[3], cmd[4]); + fflush (stdout); + } + + pid = fork (); + if (pid == -1) + return FALSE; + if (pid == 0) + { + execvp (cmd[0], (char *const *) cmd); + if (strcmp ("embedspu", EMBEDSPU) != 0) + { + cmd[0] = "embedspu"; + execvp (cmd[0], (char *const *) cmd); + } + perror (cmd[0]); + _exit (127); + } +#ifdef HAVE_WAITPID +#define WAITFOR(PID, STAT) waitpid (PID, STAT, 0) +#else +#define WAITFOR(PID, STAT) wait (STAT) +#endif + if (WAITFOR (pid, &status) != pid + || !WIFEXITED (status) + || WEXITSTATUS (status) != 0) + return FALSE; +#undef WAITFOR + + old_stat_tail = stat_ptr->tail; + old_file_tail = input_file_chain.tail; + if (lang_add_input_file (oname, lang_input_file_is_file_enum, NULL) == NULL) + return FALSE; + + /* lang_add_input_file put the new list entry at the end of the statement + and input file lists. Move it to just after the current entry. */ + new_ent = *old_stat_tail; + *old_stat_tail = NULL; + stat_ptr->tail = old_stat_tail; + *old_file_tail = NULL; + input_file_chain.tail = old_file_tail; + new_ent->header.next = entry->header.next; + entry->header.next = new_ent; + new_ent->input_statement.next_real_file = entry->next_real_file; + entry->next_real_file = new_ent; + + /* Ensure bfd sections are excluded from the output. */ + bfd_section_list_clear (entry->the_bfd); + entry->loaded = TRUE; + return TRUE; +} + +EOF +fi + +# Define some shell vars to insert bits of code into the standard elf +# parse_args and list_options functions. +# +PARSE_AND_LIST_PROLOGUE=' +#define OPTION_SPU_PLUGIN 301 +#define OPTION_SPU_NO_OVERLAYS (OPTION_SPU_PLUGIN + 1) +#define OPTION_SPU_STUB_SYMS (OPTION_SPU_NO_OVERLAYS + 1) +#define OPTION_SPU_NON_OVERLAY_STUBS (OPTION_SPU_STUB_SYMS + 1) +#define OPTION_SPU_LOCAL_STORE (OPTION_SPU_NON_OVERLAY_STUBS + 1) +#define OPTION_SPU_STACK_ANALYSIS (OPTION_SPU_LOCAL_STORE + 1) +#define OPTION_SPU_STACK_SYMS (OPTION_SPU_STACK_ANALYSIS + 1) +#define OPTION_SPU_AUTO_OVERLAY (OPTION_SPU_STACK_SYMS + 1) +#define OPTION_SPU_AUTO_RELINK (OPTION_SPU_AUTO_OVERLAY + 1) +#define OPTION_SPU_OVERLAY_RODATA (OPTION_SPU_AUTO_RELINK + 1) +#define OPTION_SPU_FIXED_SPACE (OPTION_SPU_OVERLAY_RODATA + 1) +#define OPTION_SPU_RESERVED_SPACE (OPTION_SPU_FIXED_SPACE + 1) +#define OPTION_SPU_NO_AUTO_OVERLAY (OPTION_SPU_RESERVED_SPACE + 1) +' + +PARSE_AND_LIST_LONGOPTS=' + { "plugin", no_argument, NULL, OPTION_SPU_PLUGIN }, + { "no-overlays", no_argument, NULL, OPTION_SPU_NO_OVERLAYS }, + { "emit-stub-syms", no_argument, NULL, OPTION_SPU_STUB_SYMS }, + { "extra-overlay-stubs", no_argument, NULL, OPTION_SPU_NON_OVERLAY_STUBS }, + { "local-store", required_argument, NULL, OPTION_SPU_LOCAL_STORE }, + { "stack-analysis", no_argument, NULL, OPTION_SPU_STACK_ANALYSIS }, + { "emit-stack-syms", no_argument, NULL, OPTION_SPU_STACK_SYMS }, + { "auto-overlay", optional_argument, NULL, OPTION_SPU_AUTO_OVERLAY }, + { "auto-relink", no_argument, NULL, OPTION_SPU_AUTO_RELINK }, + { "overlay-rodata", no_argument, NULL, OPTION_SPU_OVERLAY_RODATA }, + { "fixed-space", required_argument, NULL, OPTION_SPU_FIXED_SPACE }, + { "reserved-space", required_argument, NULL, OPTION_SPU_RESERVED_SPACE }, + { "no-auto-overlay", optional_argument, NULL, OPTION_SPU_NO_AUTO_OVERLAY }, +' + +PARSE_AND_LIST_OPTIONS=' + fprintf (file, _("\ + --plugin Make SPU plugin.\n\ + --no-overlays No overlay handling.\n\ + --emit-stub-syms Add symbols on overlay call stubs.\n\ + --extra-overlay-stubs Add stubs on all calls out of overlay regions.\n\ + --local-store=lo:hi Valid address range.\n\ + --stack-analysis Estimate maximum stack requirement.\n\ + --emit-stack-syms Add sym giving stack needed for each func.\n\ + --auto-overlay [=filename] Create an overlay script in filename if\n\ + executable does not fit in local store.\n\ + --auto-relink Rerun linker using auto-overlay script.\n\ + --overlay-rodata Place read-only data with associated function\n\ + code in overlays.\n\ + --fixed-space=bytes Local store for non-overlay code and data.\n\ + --reserved-space=bytes Local store for stack and heap.\n" + )); +' + +PARSE_AND_LIST_ARGS_CASES=' + case OPTION_SPU_PLUGIN: + spu_elf_plugin (1); + break; + + case OPTION_SPU_NO_OVERLAYS: + no_overlays = 1; + break; + + case OPTION_SPU_STUB_SYMS: + emit_stub_syms = 1; + break; + + case OPTION_SPU_NON_OVERLAY_STUBS: + non_overlay_stubs = 1; + break; + + case OPTION_SPU_LOCAL_STORE: + { + char *end; + local_store_lo = strtoul (optarg, &end, 0); + if (*end == '\'':'\'') + { + local_store_hi = strtoul (end + 1, &end, 0); + if (*end == 0) + break; + } + einfo (_("%P%F: invalid --local-store address range `%s'\''\n"), optarg); + } + break; + + case OPTION_SPU_STACK_ANALYSIS: + stack_analysis = 1; + break; + + case OPTION_SPU_STACK_SYMS: + emit_stack_syms = 1; + break; + + case OPTION_SPU_AUTO_OVERLAY: + auto_overlay |= 1; + if (optarg != NULL) + { + auto_overlay_file = optarg; + break; + } + /* Fall thru */ + + case OPTION_SPU_AUTO_RELINK: + auto_overlay |= 2; + break; + + case OPTION_SPU_OVERLAY_RODATA: + auto_overlay |= 4; + break; + + case OPTION_SPU_FIXED_SPACE: + { + char *end; + auto_overlay_fixed = strtoul (optarg, &end, 0); + if (*end != 0) + einfo (_("%P%F: invalid --fixed-space value `%s'\''\n"), optarg); + } + break; + + case OPTION_SPU_RESERVED_SPACE: + { + char *end; + auto_overlay_reserved = strtoul (optarg, &end, 0); + if (*end != 0) + einfo (_("%P%F: invalid --reserved-space value `%s'\''\n"), optarg); + } + break; + + case OPTION_SPU_NO_AUTO_OVERLAY: + auto_overlay = 0; + if (optarg != NULL) + { + struct tflist *tf; + size_t len; + + if (tmp_file_list == NULL) + atexit (clean_tmp); + + len = strlen (optarg) + 1; + tf = xmalloc (sizeof (*tf) - sizeof (tf->name) + len); + memcpy (tf->name, optarg, len); + tf->next = tmp_file_list; + tmp_file_list = tf; + break; + } + break; +' + +LDEMUL_AFTER_OPEN=spu_after_open +LDEMUL_BEFORE_ALLOCATION=spu_before_allocation +LDEMUL_FINISH=gld${EMULATION_NAME}_finish +LDEMUL_CHOOSE_TARGET=gld${EMULATION_NAME}_choose_target Index: mipself.em =================================================================== --- mipself.em (nonexistent) +++ mipself.em (revision 816) @@ -0,0 +1,38 @@ +# This shell script emits a C file. -*- C -*- +# Copyright 2006, 2007 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. + +fragment < + Linux support by Eric Youngdale + + This file is part of the GNU Binutils. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#define TARGET_IS_${EMULATION_NAME} + +#include "sysdep.h" +#include "bfd.h" +#include "bfdlink.h" + +#include "ld.h" +#include "ldmain.h" +#include "ldmisc.h" +#include "ldexp.h" +#include "ldlang.h" +#include "ldfile.h" +#include "ldemul.h" + +static void +gld${EMULATION_NAME}_before_parse (void) +{ + ldfile_set_output_arch ("${OUTPUT_ARCH}", bfd_arch_`echo ${ARCH} | sed -e 's/:.*//'`); + config.dynamic_link = TRUE; + config.has_shared = TRUE; +} + +/* Try to open a dynamic archive. This is where we know that Linux + dynamic libraries have an extension of .sa. */ + +static bfd_boolean +gld${EMULATION_NAME}_open_dynamic_archive + (const char *arch, search_dirs_type *search, lang_input_statement_type *entry) +{ + char *string; + + if (! entry->is_archive) + return FALSE; + + string = (char *) xmalloc (strlen (search->name) + + strlen (entry->filename) + + strlen (arch) + + sizeof "/lib.sa"); + + sprintf (string, "%s/lib%s%s.sa", search->name, entry->filename, arch); + + if (! ldfile_try_open_bfd (string, entry)) + { + free (string); + return FALSE; + } + + entry->filename = string; + + return TRUE; +} + +/* This is called by the create_output_section_statements routine via + lang_for_each_statement. It locates any address assignment to + .text, and modifies it to include the size of the headers. This + causes -Ttext to mean the starting address of the header, rather + than the starting address of .text, which is compatible with other + Linux tools. */ + +static void +gld${EMULATION_NAME}_find_address_statement (lang_statement_union_type *s) +{ + if (s->header.type == lang_address_statement_enum + && strcmp (s->address_statement.section_name, ".text") == 0) + { + ASSERT (s->address_statement.address->type.node_class == etree_value); + s->address_statement.address->value.value += 0x20; + } +} + +/* This is called before opening the input BFD's. */ + +static void +gld${EMULATION_NAME}_create_output_section_statements (void) +{ + lang_for_each_statement (gld${EMULATION_NAME}_find_address_statement); +} + +/* This is called after the sections have been attached to output + sections, but before any sizes or addresses have been set. */ + +static void +gld${EMULATION_NAME}_before_allocation (void) +{ + if (link_info.relocatable) + return; + + /* Let the backend work out the sizes of any sections required by + dynamic linking. */ + if (! bfd_${EMULATION_NAME}_size_dynamic_sections (link_info.output_bfd, + &link_info)) + einfo ("%P%F: failed to set dynamic section sizes: %E\n"); + + before_allocation_default (); +} + +static char * +gld${EMULATION_NAME}_get_script (int *isfile) +EOF + +if test -n "$COMPILE_IN" +then +# Scripts compiled in. + +# sed commands to quote an ld script as a C string. +sc="-f stringify.sed" + +fragment <> e${EMULATION_NAME}.c +echo ' ; else if (link_info.relocatable) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xr >> e${EMULATION_NAME}.c +echo ' ; else if (!config.text_read_only) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xbn >> e${EMULATION_NAME}.c +echo ' ; else if (!config.magic_demand_paged) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xn >> e${EMULATION_NAME}.c +echo ' ; else return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.x >> e${EMULATION_NAME}.c +echo '; }' >> e${EMULATION_NAME}.c + +else +# Scripts read from the filesystem. + +fragment <e${EMULATION_NAME}.c # there, now line numbers match ;-) +fragment < +#include "ldlex.h" +#include "ldmisc.h" +#include "ldctor.h" +#include "coff/internal.h" + +/* FIXME: See bfd/peXXigen.c for why we include an architecture specific + header in generic PE code. */ +#include "coff/i386.h" +#include "coff/pe.h" + +/* FIXME: This is a BFD internal header file, and we should not be + using it here. */ +#include "../bfd/libcoff.h" + +#include "deffile.h" +#include "pe-dll.h" +#include "safe-ctype.h" + +/* Permit the emulation parameters to override the default section + alignment by setting OVERRIDE_SECTION_ALIGNMENT. FIXME: This makes + it seem that include/coff/internal.h should not define + PE_DEF_SECTION_ALIGNMENT. */ +#if PE_DEF_SECTION_ALIGNMENT != ${OVERRIDE_SECTION_ALIGNMENT:-PE_DEF_SECTION_ALIGNMENT} +#undef PE_DEF_SECTION_ALIGNMENT +#define PE_DEF_SECTION_ALIGNMENT ${OVERRIDE_SECTION_ALIGNMENT} +#endif + +#if defined(TARGET_IS_i386pe) \ + || defined(TARGET_IS_shpe) \ + || defined(TARGET_IS_mipspe) \ + || defined(TARGET_IS_armpe) \ + || defined(TARGET_IS_arm_epoc_pe) \ + || defined(TARGET_IS_arm_wince_pe) +#define DLL_SUPPORT +#endif + +#if defined(TARGET_IS_i386pe) || ! defined(DLL_SUPPORT) +#define PE_DEF_SUBSYSTEM 3 +#else +#undef NT_EXE_IMAGE_BASE +#undef PE_DEF_SECTION_ALIGNMENT +#undef PE_DEF_FILE_ALIGNMENT +#define NT_EXE_IMAGE_BASE 0x00010000 + +#if defined(TARGET_IS_armpe) || defined(TARGET_IS_arm_wince_pe) +#define PE_DEF_SECTION_ALIGNMENT 0x00001000 +#define PE_DEF_SUBSYSTEM 9 +#else +#define PE_DEF_SECTION_ALIGNMENT 0x00000400 +#define PE_DEF_SUBSYSTEM 2 +#endif +#define PE_DEF_FILE_ALIGNMENT 0x00000200 +#endif + +#define U(S) ${INITIAL_SYMBOL_CHAR} S + +static struct internal_extra_pe_aouthdr pe; +static int dll; +static flagword real_flags = 0; +static int support_old_code = 0; +static char * thumb_entry_symbol = NULL; +static lang_assignment_statement_type *image_base_statement = 0; + +#ifdef DLL_SUPPORT +static int pe_enable_stdcall_fixup = -1; /* 0=disable 1=enable. */ +static char *pe_out_def_filename = NULL; +static char *pe_implib_filename = NULL; +static int pe_enable_auto_image_base = 0; +static char *pe_dll_search_prefix = NULL; +#endif + +extern const char *output_filename; + +static void +gld_${EMULATION_NAME}_before_parse (void) +{ + ldfile_set_output_arch ("${OUTPUT_ARCH}", bfd_arch_`echo ${ARCH} | sed -e 's/:.*//'`); + output_filename = "${EXECUTABLE_NAME:-a.exe}"; +#ifdef DLL_SUPPORT + config.dynamic_link = TRUE; + config.has_shared = 1; + link_info.pei386_auto_import = -1; + link_info.pei386_runtime_pseudo_reloc = -1; + +#if (PE_DEF_SUBSYSTEM == 9) || (PE_DEF_SUBSYSTEM == 2) +#if defined TARGET_IS_mipspe || defined TARGET_IS_armpe || defined TARGET_IS_arm_wince_pe + lang_default_entry ("WinMainCRTStartup"); +#else + lang_default_entry ("_WinMainCRTStartup"); +#endif +#else + lang_default_entry ("${ENTRY}"); +#endif +#endif +} + +/* PE format extra command line options. */ + +/* Used for setting flags in the PE header. */ +#define OPTION_BASE_FILE (300 + 1) +#define OPTION_DLL (OPTION_BASE_FILE + 1) +#define OPTION_FILE_ALIGNMENT (OPTION_DLL + 1) +#define OPTION_IMAGE_BASE (OPTION_FILE_ALIGNMENT + 1) +#define OPTION_MAJOR_IMAGE_VERSION (OPTION_IMAGE_BASE + 1) +#define OPTION_MAJOR_OS_VERSION (OPTION_MAJOR_IMAGE_VERSION + 1) +#define OPTION_MAJOR_SUBSYSTEM_VERSION (OPTION_MAJOR_OS_VERSION + 1) +#define OPTION_MINOR_IMAGE_VERSION (OPTION_MAJOR_SUBSYSTEM_VERSION + 1) +#define OPTION_MINOR_OS_VERSION (OPTION_MINOR_IMAGE_VERSION + 1) +#define OPTION_MINOR_SUBSYSTEM_VERSION (OPTION_MINOR_OS_VERSION + 1) +#define OPTION_SECTION_ALIGNMENT (OPTION_MINOR_SUBSYSTEM_VERSION + 1) +#define OPTION_STACK (OPTION_SECTION_ALIGNMENT + 1) +#define OPTION_SUBSYSTEM (OPTION_STACK + 1) +#define OPTION_HEAP (OPTION_SUBSYSTEM + 1) +#define OPTION_SUPPORT_OLD_CODE (OPTION_HEAP + 1) +#define OPTION_OUT_DEF (OPTION_SUPPORT_OLD_CODE + 1) +#define OPTION_EXPORT_ALL (OPTION_OUT_DEF + 1) +#define OPTION_EXCLUDE_SYMBOLS (OPTION_EXPORT_ALL + 1) +#define OPTION_KILL_ATS (OPTION_EXCLUDE_SYMBOLS + 1) +#define OPTION_STDCALL_ALIASES (OPTION_KILL_ATS + 1) +#define OPTION_ENABLE_STDCALL_FIXUP (OPTION_STDCALL_ALIASES + 1) +#define OPTION_DISABLE_STDCALL_FIXUP (OPTION_ENABLE_STDCALL_FIXUP + 1) +#define OPTION_IMPLIB_FILENAME (OPTION_DISABLE_STDCALL_FIXUP + 1) +#define OPTION_THUMB_ENTRY (OPTION_IMPLIB_FILENAME + 1) +#define OPTION_WARN_DUPLICATE_EXPORTS (OPTION_THUMB_ENTRY + 1) +#define OPTION_IMP_COMPAT (OPTION_WARN_DUPLICATE_EXPORTS + 1) +#define OPTION_ENABLE_AUTO_IMAGE_BASE (OPTION_IMP_COMPAT + 1) +#define OPTION_DISABLE_AUTO_IMAGE_BASE (OPTION_ENABLE_AUTO_IMAGE_BASE + 1) +#define OPTION_DLL_SEARCH_PREFIX (OPTION_DISABLE_AUTO_IMAGE_BASE + 1) +#define OPTION_NO_DEFAULT_EXCLUDES (OPTION_DLL_SEARCH_PREFIX + 1) +#define OPTION_DLL_ENABLE_AUTO_IMPORT (OPTION_NO_DEFAULT_EXCLUDES + 1) +#define OPTION_DLL_DISABLE_AUTO_IMPORT (OPTION_DLL_ENABLE_AUTO_IMPORT + 1) +#define OPTION_ENABLE_EXTRA_PE_DEBUG (OPTION_DLL_DISABLE_AUTO_IMPORT + 1) +#define OPTION_EXCLUDE_LIBS (OPTION_ENABLE_EXTRA_PE_DEBUG + 1) +#define OPTION_DLL_ENABLE_RUNTIME_PSEUDO_RELOC \ + (OPTION_EXCLUDE_LIBS + 1) +#define OPTION_DLL_DISABLE_RUNTIME_PSEUDO_RELOC \ + (OPTION_DLL_ENABLE_RUNTIME_PSEUDO_RELOC + 1) +#define OPTION_LARGE_ADDRESS_AWARE \ + (OPTION_DLL_DISABLE_RUNTIME_PSEUDO_RELOC + 1) + +static void +gld${EMULATION_NAME}_add_options + (int ns ATTRIBUTE_UNUSED, char **shortopts ATTRIBUTE_UNUSED, int nl, + struct option **longopts, int nrl ATTRIBUTE_UNUSED, + struct option **really_longopts ATTRIBUTE_UNUSED) +{ + static const struct option xtra_long[] = { + /* PE options */ + {"base-file", required_argument, NULL, OPTION_BASE_FILE}, + {"dll", no_argument, NULL, OPTION_DLL}, + {"file-alignment", required_argument, NULL, OPTION_FILE_ALIGNMENT}, + {"heap", required_argument, NULL, OPTION_HEAP}, + {"image-base", required_argument, NULL, OPTION_IMAGE_BASE}, + {"major-image-version", required_argument, NULL, OPTION_MAJOR_IMAGE_VERSION}, + {"major-os-version", required_argument, NULL, OPTION_MAJOR_OS_VERSION}, + {"major-subsystem-version", required_argument, NULL, OPTION_MAJOR_SUBSYSTEM_VERSION}, + {"minor-image-version", required_argument, NULL, OPTION_MINOR_IMAGE_VERSION}, + {"minor-os-version", required_argument, NULL, OPTION_MINOR_OS_VERSION}, + {"minor-subsystem-version", required_argument, NULL, OPTION_MINOR_SUBSYSTEM_VERSION}, + {"section-alignment", required_argument, NULL, OPTION_SECTION_ALIGNMENT}, + {"stack", required_argument, NULL, OPTION_STACK}, + {"subsystem", required_argument, NULL, OPTION_SUBSYSTEM}, + {"support-old-code", no_argument, NULL, OPTION_SUPPORT_OLD_CODE}, + {"thumb-entry", required_argument, NULL, OPTION_THUMB_ENTRY}, +#ifdef DLL_SUPPORT + /* getopt allows abbreviations, so we do this to stop it from treating -o + as an abbreviation for this option */ + {"output-def", required_argument, NULL, OPTION_OUT_DEF}, + {"output-def", required_argument, NULL, OPTION_OUT_DEF}, + {"export-all-symbols", no_argument, NULL, OPTION_EXPORT_ALL}, + {"exclude-symbols", required_argument, NULL, OPTION_EXCLUDE_SYMBOLS}, + {"exclude-libs", required_argument, NULL, OPTION_EXCLUDE_LIBS}, + {"kill-at", no_argument, NULL, OPTION_KILL_ATS}, + {"add-stdcall-alias", no_argument, NULL, OPTION_STDCALL_ALIASES}, + {"enable-stdcall-fixup", no_argument, NULL, OPTION_ENABLE_STDCALL_FIXUP}, + {"disable-stdcall-fixup", no_argument, NULL, OPTION_DISABLE_STDCALL_FIXUP}, + {"out-implib", required_argument, NULL, OPTION_IMPLIB_FILENAME}, + {"warn-duplicate-exports", no_argument, NULL, OPTION_WARN_DUPLICATE_EXPORTS}, + /* getopt() allows abbreviations, so we do this to stop it from + treating -c as an abbreviation for these --compat-implib. */ + {"compat-implib", no_argument, NULL, OPTION_IMP_COMPAT}, + {"compat-implib", no_argument, NULL, OPTION_IMP_COMPAT}, + {"enable-auto-image-base", no_argument, NULL, OPTION_ENABLE_AUTO_IMAGE_BASE}, + {"disable-auto-image-base", no_argument, NULL, OPTION_DISABLE_AUTO_IMAGE_BASE}, + {"dll-search-prefix", required_argument, NULL, OPTION_DLL_SEARCH_PREFIX}, + {"no-default-excludes", no_argument, NULL, OPTION_NO_DEFAULT_EXCLUDES}, + {"enable-auto-import", no_argument, NULL, OPTION_DLL_ENABLE_AUTO_IMPORT}, + {"disable-auto-import", no_argument, NULL, OPTION_DLL_DISABLE_AUTO_IMPORT}, + {"enable-extra-pe-debug", no_argument, NULL, OPTION_ENABLE_EXTRA_PE_DEBUG}, + {"enable-runtime-pseudo-reloc", no_argument, NULL, OPTION_DLL_ENABLE_RUNTIME_PSEUDO_RELOC}, + {"disable-runtime-pseudo-reloc", no_argument, NULL, OPTION_DLL_DISABLE_RUNTIME_PSEUDO_RELOC}, +#endif + {"large-address-aware", no_argument, NULL, OPTION_LARGE_ADDRESS_AWARE}, + {NULL, no_argument, NULL, 0} + }; + + *longopts = (struct option *) + xrealloc (*longopts, nl * sizeof (struct option) + sizeof (xtra_long)); + memcpy (*longopts + nl, &xtra_long, sizeof (xtra_long)); +} + +/* PE/WIN32; added routines to get the subsystem type, heap and/or stack + parameters which may be input from the command line. */ + +typedef struct +{ + void *ptr; + int size; + int value; + char *symbol; + int inited; +} definfo; + +#define D(field,symbol,def) {&pe.field,sizeof(pe.field), def, symbol,0} + +static definfo init[] = +{ + /* imagebase must be first */ +#define IMAGEBASEOFF 0 + D(ImageBase,"__image_base__", NT_EXE_IMAGE_BASE), +#define DLLOFF 1 + {&dll, sizeof(dll), 0, "__dll__", 0}, +#define MSIMAGEBASEOFF 2 + D(ImageBase, U ("__ImageBase"), NT_EXE_IMAGE_BASE), + D(SectionAlignment,"__section_alignment__", PE_DEF_SECTION_ALIGNMENT), + D(FileAlignment,"__file_alignment__", PE_DEF_FILE_ALIGNMENT), + D(MajorOperatingSystemVersion,"__major_os_version__", 4), + D(MinorOperatingSystemVersion,"__minor_os_version__", 0), + D(MajorImageVersion,"__major_image_version__", 1), + D(MinorImageVersion,"__minor_image_version__", 0), +#if defined(TARGET_IS_armpe) || defined(TARGET_IS_arm_wince_pe) + D(MajorSubsystemVersion,"__major_subsystem_version__", 3), +#else + D(MajorSubsystemVersion,"__major_subsystem_version__", 4), +#endif + D(MinorSubsystemVersion,"__minor_subsystem_version__", 0), + D(Subsystem,"__subsystem__", ${SUBSYSTEM}), + D(SizeOfStackReserve,"__size_of_stack_reserve__", 0x200000), + D(SizeOfStackCommit,"__size_of_stack_commit__", 0x1000), + D(SizeOfHeapReserve,"__size_of_heap_reserve__", 0x100000), + D(SizeOfHeapCommit,"__size_of_heap_commit__", 0x1000), + D(LoaderFlags,"__loader_flags__", 0x0), + { NULL, 0, 0, NULL, 0 } +}; + + +static void +gld_${EMULATION_NAME}_list_options (FILE *file) +{ + fprintf (file, _(" --base_file Generate a base file for relocatable DLLs\n")); + fprintf (file, _(" --dll Set image base to the default for DLLs\n")); + fprintf (file, _(" --file-alignment Set file alignment\n")); + fprintf (file, _(" --heap Set initial size of the heap\n")); + fprintf (file, _(" --image-base
Set start address of the executable\n")); + fprintf (file, _(" --major-image-version Set version number of the executable\n")); + fprintf (file, _(" --major-os-version Set minimum required OS version\n")); + fprintf (file, _(" --major-subsystem-version Set minimum required OS subsystem version\n")); + fprintf (file, _(" --minor-image-version Set revision number of the executable\n")); + fprintf (file, _(" --minor-os-version Set minimum required OS revision\n")); + fprintf (file, _(" --minor-subsystem-version Set minimum required OS subsystem revision\n")); + fprintf (file, _(" --section-alignment Set section alignment\n")); + fprintf (file, _(" --stack Set size of the initial stack\n")); + fprintf (file, _(" --subsystem [:] Set required OS subsystem [& version]\n")); + fprintf (file, _(" --support-old-code Support interworking with old code\n")); + fprintf (file, _(" --thumb-entry= Set the entry point to be Thumb \n")); +#ifdef DLL_SUPPORT + fprintf (file, _(" --add-stdcall-alias Export symbols with and without @nn\n")); + fprintf (file, _(" --disable-stdcall-fixup Don't link _sym to _sym@nn\n")); + fprintf (file, _(" --enable-stdcall-fixup Link _sym to _sym@nn without warnings\n")); + fprintf (file, _(" --exclude-symbols sym,sym,... Exclude symbols from automatic export\n")); + fprintf (file, _(" --exclude-libs lib,lib,... Exclude libraries from automatic export\n")); + fprintf (file, _(" --export-all-symbols Automatically export all globals to DLL\n")); + fprintf (file, _(" --kill-at Remove @nn from exported symbols\n")); + fprintf (file, _(" --out-implib Generate import library\n")); + fprintf (file, _(" --output-def Generate a .DEF file for the built DLL\n")); + fprintf (file, _(" --warn-duplicate-exports Warn about duplicate exports.\n")); + fprintf (file, _(" --compat-implib Create backward compatible import libs;\n\ + create __imp_ as well.\n")); + fprintf (file, _(" --enable-auto-image-base Automatically choose image base for DLLs\n\ + unless user specifies one\n")); + fprintf (file, _(" --disable-auto-image-base Do not auto-choose image base. (default)\n")); + fprintf (file, _(" --dll-search-prefix= When linking dynamically to a dll without\n\ + an importlib, use .dll\n\ + in preference to lib.dll \n")); + fprintf (file, _(" --enable-auto-import Do sophistcated linking of _sym to\n\ + __imp_sym for DATA references\n")); + fprintf (file, _(" --disable-auto-import Do not auto-import DATA items from DLLs\n")); + fprintf (file, _(" --enable-runtime-pseudo-reloc Work around auto-import limitations by\n\ + adding pseudo-relocations resolved at\n\ + runtime.\n")); + fprintf (file, _(" --disable-runtime-pseudo-reloc Do not add runtime pseudo-relocations for\n\ + auto-imported DATA.\n")); + fprintf (file, _(" --enable-extra-pe-debug Enable verbose debug output when building\n\ + or linking to DLLs (esp. auto-import)\n")); +#endif + fprintf (file, _(" --large-address-aware Executable supports virtual addresses\n\ + greater than 2 gigabytes\n")); +} + + +static void +set_pe_name (char *name, long val) +{ + int i; + + /* Find the name and set it. */ + for (i = 0; init[i].ptr; i++) + { + if (strcmp (name, init[i].symbol) == 0) + { + init[i].value = val; + init[i].inited = 1; + if (strcmp (name,"__image_base__") == 0) + set_pe_name (U ("__ImageBase"), val); + return; + } + } + abort (); +} + + +static void +set_pe_subsystem (void) +{ + const char *sver; + const char *entry; + const char *initial_symbol_char; + char *end; + int len; + int i; + int subsystem; + unsigned long temp_subsystem; + static const struct + { + const char *name; + const int value; + const char *entry; + } + v[] = + { + { "native", 1, "NtProcessStartup" }, + { "windows", 2, "WinMainCRTStartup" }, + { "console", 3, "mainCRTStartup" }, + { "posix", 7, "__PosixProcessStartup"}, + { "wince", 9, "WinMainCRTStartup" }, + { "xbox", 14, "mainCRTStartup" }, + { NULL, 0, NULL } + }; + /* Entry point name for arbitrary subsystem numbers. */ + static const char default_entry[] = "mainCRTStartup"; + + /* Check for the presence of a version number. */ + sver = strchr (optarg, ':'); + if (sver == NULL) + len = strlen (optarg); + else + { + len = sver - optarg; + set_pe_name ("__major_subsystem_version__", + strtoul (sver + 1, &end, 0)); + if (*end == '.') + set_pe_name ("__minor_subsystem_version__", + strtoul (end + 1, &end, 0)); + if (*end != '\0') + einfo (_("%P: warning: bad version number in -subsystem option\n")); + } + + /* Check for numeric subsystem. */ + temp_subsystem = strtoul (optarg, & end, 0); + if ((*end == ':' || *end == '\0') && (temp_subsystem < 65536)) + { + /* Search list for a numeric match to use its entry point. */ + for (i = 0; v[i].name; i++) + if (v[i].value == (int) temp_subsystem) + break; + + /* If no match, use the default. */ + if (v[i].name != NULL) + entry = v[i].entry; + else + entry = default_entry; + + /* Use this subsystem. */ + subsystem = (int) temp_subsystem; + } + else + { + /* Search for subsystem by name. */ + for (i = 0; v[i].name; i++) + if (strncmp (optarg, v[i].name, len) == 0 + && v[i].name[len] == '\0') + break; + + if (v[i].name == NULL) + { + einfo (_("%P%F: invalid subsystem type %s\n"), optarg); + return; + } + + entry = v[i].entry; + subsystem = v[i].value; + } + + set_pe_name ("__subsystem__", subsystem); + + initial_symbol_char = ${INITIAL_SYMBOL_CHAR}; + if (*initial_symbol_char != '\0') + { + char *alc_entry; + + /* lang_default_entry expects its argument to be permanently + allocated, so we don't free this string. */ + alc_entry = xmalloc (strlen (initial_symbol_char) + + strlen (entry) + + 1); + strcpy (alc_entry, initial_symbol_char); + strcat (alc_entry, entry); + entry = alc_entry; + } + + lang_default_entry (entry); + + return; +} + + +static void +set_pe_value (char *name) +{ + char *end; + + set_pe_name (name, strtoul (optarg, &end, 0)); + + if (end == optarg) + einfo (_("%P%F: invalid hex number for PE parameter '%s'\n"), optarg); + + optarg = end; +} + + +static void +set_pe_stack_heap (char *resname, char *comname) +{ + set_pe_value (resname); + + if (*optarg == ',') + { + optarg++; + set_pe_value (comname); + } + else if (*optarg) + einfo (_("%P%F: strange hex info for PE parameter '%s'\n"), optarg); +} + + +static bfd_boolean +gld${EMULATION_NAME}_handle_option (int optc) +{ + switch (optc) + { + default: + return FALSE; + + case OPTION_BASE_FILE: + link_info.base_file = fopen (optarg, FOPEN_WB); + if (link_info.base_file == NULL) + { + /* xgettext:c-format */ + fprintf (stderr, _("%s: Can't open base file %s\n"), + program_name, optarg); + xexit (1); + } + break; + + /* PE options. */ + case OPTION_HEAP: + set_pe_stack_heap ("__size_of_heap_reserve__", "__size_of_heap_commit__"); + break; + case OPTION_STACK: + set_pe_stack_heap ("__size_of_stack_reserve__", "__size_of_stack_commit__"); + break; + case OPTION_SUBSYSTEM: + set_pe_subsystem (); + break; + case OPTION_MAJOR_OS_VERSION: + set_pe_value ("__major_os_version__"); + break; + case OPTION_MINOR_OS_VERSION: + set_pe_value ("__minor_os_version__"); + break; + case OPTION_MAJOR_SUBSYSTEM_VERSION: + set_pe_value ("__major_subsystem_version__"); + break; + case OPTION_MINOR_SUBSYSTEM_VERSION: + set_pe_value ("__minor_subsystem_version__"); + break; + case OPTION_MAJOR_IMAGE_VERSION: + set_pe_value ("__major_image_version__"); + break; + case OPTION_MINOR_IMAGE_VERSION: + set_pe_value ("__minor_image_version__"); + break; + case OPTION_FILE_ALIGNMENT: + set_pe_value ("__file_alignment__"); + break; + case OPTION_SECTION_ALIGNMENT: + set_pe_value ("__section_alignment__"); + break; + case OPTION_DLL: + set_pe_name ("__dll__", 1); + break; + case OPTION_IMAGE_BASE: + set_pe_value ("__image_base__"); + break; + case OPTION_SUPPORT_OLD_CODE: + support_old_code = 1; + break; + case OPTION_THUMB_ENTRY: + thumb_entry_symbol = optarg; + break; +#ifdef DLL_SUPPORT + case OPTION_OUT_DEF: + pe_out_def_filename = xstrdup (optarg); + break; + case OPTION_EXPORT_ALL: + pe_dll_export_everything = 1; + break; + case OPTION_EXCLUDE_SYMBOLS: + pe_dll_add_excludes (optarg, 0); + break; + case OPTION_EXCLUDE_LIBS: + pe_dll_add_excludes (optarg, 1); + break; + case OPTION_KILL_ATS: + pe_dll_kill_ats = 1; + break; + case OPTION_STDCALL_ALIASES: + pe_dll_stdcall_aliases = 1; + break; + case OPTION_ENABLE_STDCALL_FIXUP: + pe_enable_stdcall_fixup = 1; + break; + case OPTION_DISABLE_STDCALL_FIXUP: + pe_enable_stdcall_fixup = 0; + break; + case OPTION_IMPLIB_FILENAME: + pe_implib_filename = xstrdup (optarg); + break; + case OPTION_WARN_DUPLICATE_EXPORTS: + pe_dll_warn_dup_exports = 1; + break; + case OPTION_IMP_COMPAT: + pe_dll_compat_implib = 1; + break; + case OPTION_ENABLE_AUTO_IMAGE_BASE: + pe_enable_auto_image_base = 1; + break; + case OPTION_DISABLE_AUTO_IMAGE_BASE: + pe_enable_auto_image_base = 0; + break; + case OPTION_DLL_SEARCH_PREFIX: + pe_dll_search_prefix = xstrdup (optarg); + break; + case OPTION_NO_DEFAULT_EXCLUDES: + pe_dll_do_default_excludes = 0; + break; + case OPTION_DLL_ENABLE_AUTO_IMPORT: + link_info.pei386_auto_import = 1; + break; + case OPTION_DLL_DISABLE_AUTO_IMPORT: + link_info.pei386_auto_import = 0; + break; + case OPTION_DLL_ENABLE_RUNTIME_PSEUDO_RELOC: + link_info.pei386_runtime_pseudo_reloc = 1; + break; + case OPTION_DLL_DISABLE_RUNTIME_PSEUDO_RELOC: + link_info.pei386_runtime_pseudo_reloc = 0; + break; + case OPTION_ENABLE_EXTRA_PE_DEBUG: + pe_dll_extra_pe_debug = 1; + break; +#endif + case OPTION_LARGE_ADDRESS_AWARE: + real_flags |= IMAGE_FILE_LARGE_ADDRESS_AWARE; + break; + } + return TRUE; +} + + +#ifdef DLL_SUPPORT +static unsigned long +strhash (const char *str) +{ + const unsigned char *s; + unsigned long hash; + unsigned int c; + unsigned int len; + + hash = 0; + len = 0; + s = (const unsigned char *) str; + while ((c = *s++) != '\0') + { + hash += c + (c << 17); + hash ^= hash >> 2; + ++len; + } + hash += len + (len << 17); + hash ^= hash >> 2; + + return hash; +} + +/* Use the output file to create a image base for relocatable DLLs. */ + +static unsigned long +compute_dll_image_base (const char *ofile) +{ + unsigned long hash = strhash (ofile); + return 0x61300000 + ((hash << 16) & 0x0FFC0000); +} +#endif + +/* Assign values to the special symbols before the linker script is + read. */ + +static void +gld_${EMULATION_NAME}_set_symbols (void) +{ + /* Run through and invent symbols for all the + names and insert the defaults. */ + int j; + lang_statement_list_type *save; + + if (!init[IMAGEBASEOFF].inited) + { + if (link_info.relocatable) + init[IMAGEBASEOFF].value = 0; + else if (init[DLLOFF].value || (link_info.shared && !link_info.pie)) +#ifdef DLL_SUPPORT + init[IMAGEBASEOFF].value = (pe_enable_auto_image_base) ? + compute_dll_image_base (output_filename) : NT_DLL_IMAGE_BASE; +#else + init[IMAGEBASEOFF].value = NT_DLL_IMAGE_BASE; +#endif + else + init[IMAGEBASEOFF].value = NT_EXE_IMAGE_BASE; + init[MSIMAGEBASEOFF].value = init[IMAGEBASEOFF].value; + } + + /* Don't do any symbol assignments if this is a relocatable link. */ + if (link_info.relocatable) + return; + + /* Glue the assignments into the abs section. */ + save = stat_ptr; + + stat_ptr = &(abs_output_section->children); + + for (j = 0; init[j].ptr; j++) + { + long val = init[j].value; + lang_assignment_statement_type *rv; + rv = lang_add_assignment (exp_assop ('=', init[j].symbol, + exp_intop (val))); + if (init[j].size == sizeof (short)) + *(short *) init[j].ptr = val; + else if (init[j].size == sizeof (int)) + *(int *) init[j].ptr = val; + else if (init[j].size == sizeof (long)) + *(long *) init[j].ptr = val; + /* This might be a long long or other special type. */ + else if (init[j].size == sizeof (bfd_vma)) + *(bfd_vma *) init[j].ptr = val; + else abort (); + if (j == IMAGEBASEOFF) + image_base_statement = rv; + } + /* Restore the pointer. */ + stat_ptr = save; + + if (pe.FileAlignment > + pe.SectionAlignment) + { + einfo (_("%P: warning, file alignment > section alignment.\n")); + } +} + +/* This is called after the linker script and the command line options + have been read. */ + +static void +gld_${EMULATION_NAME}_after_parse (void) +{ + /* The Windows libraries are designed for the linker to treat the + entry point as an undefined symbol. Otherwise, the .obj that + defines mainCRTStartup is brought in because it is the first + encountered in libc.lib and it has other symbols in it which will + be pulled in by the link process. To avoid this, we act as + though the user specified -u with the entry point symbol. + + This function is called after the linker script and command line + options have been read, so at this point we know the right entry + point. This function is called before the input files are + opened, so registering the symbol as undefined will make a + difference. */ + + if (! link_info.relocatable && entry_symbol.name != NULL) + ldlang_add_undef (entry_symbol.name); +} + +/* pe-dll.c directly accesses pe_data_import_dll, + so it must be defined outside of #ifdef DLL_SUPPORT. + Note - this variable is deliberately not initialised. + This allows it to be treated as a common varaible, and only + exist in one incarnation in a multiple target enabled linker. */ +char * pe_data_import_dll; + +#ifdef DLL_SUPPORT +static struct bfd_link_hash_entry *pe_undef_found_sym; + +static bfd_boolean +pe_undef_cdecl_match (struct bfd_link_hash_entry *h, void *inf) +{ + int sl; + char *string = inf; + + sl = strlen (string); + if (h->type == bfd_link_hash_defined + && strncmp (h->root.string, string, sl) == 0 + && h->root.string[sl] == '@') + { + pe_undef_found_sym = h; + return FALSE; + } + return TRUE; +} + +static void +pe_fixup_stdcalls (void) +{ + static int gave_warning_message = 0; + struct bfd_link_hash_entry *undef, *sym; + + if (pe_dll_extra_pe_debug) + printf ("%s\n", __FUNCTION__); + + for (undef = link_info.hash->undefs; undef; undef=undef->u.undef.next) + if (undef->type == bfd_link_hash_undefined) + { + char* at = strchr (undef->root.string, '@'); + int lead_at = (*undef->root.string == '@'); + /* For now, don't try to fixup fastcall symbols. */ + + if (at && !lead_at) + { + /* The symbol is a stdcall symbol, so let's look for a + cdecl symbol with the same name and resolve to that. */ + char *cname = xstrdup (undef->root.string /* + lead_at */); + at = strchr (cname, '@'); + *at = 0; + sym = bfd_link_hash_lookup (link_info.hash, cname, 0, 0, 1); + + if (sym && sym->type == bfd_link_hash_defined) + { + undef->type = bfd_link_hash_defined; + undef->u.def.value = sym->u.def.value; + undef->u.def.section = sym->u.def.section; + + if (pe_enable_stdcall_fixup == -1) + { + einfo (_("Warning: resolving %s by linking to %s\n"), + undef->root.string, cname); + if (! gave_warning_message) + { + gave_warning_message = 1; + einfo (_("Use --enable-stdcall-fixup to disable these warnings\n")); + einfo (_("Use --disable-stdcall-fixup to disable these fixups\n")); + } + } + } + } + else + { + /* The symbol is a cdecl symbol, so we look for stdcall + symbols - which means scanning the whole symbol table. */ + pe_undef_found_sym = 0; + bfd_link_hash_traverse (link_info.hash, pe_undef_cdecl_match, + (char *) undef->root.string); + sym = pe_undef_found_sym; + if (sym) + { + undef->type = bfd_link_hash_defined; + undef->u.def.value = sym->u.def.value; + undef->u.def.section = sym->u.def.section; + + if (pe_enable_stdcall_fixup == -1) + { + einfo (_("Warning: resolving %s by linking to %s\n"), + undef->root.string, sym->root.string); + if (! gave_warning_message) + { + gave_warning_message = 1; + einfo (_("Use --enable-stdcall-fixup to disable these warnings\n")); + einfo (_("Use --disable-stdcall-fixup to disable these fixups\n")); + } + } + } + } + } +} + +static int +make_import_fixup (arelent *rel, asection *s) +{ + struct bfd_symbol *sym = *rel->sym_ptr_ptr; + char addend[4]; + + if (pe_dll_extra_pe_debug) + printf ("arelent: %s@%#lx: add=%li\n", sym->name, + (long) rel->address, (long) rel->addend); + + if (! bfd_get_section_contents (s->owner, s, addend, rel->address, sizeof (addend))) + einfo (_("%C: Cannot get section contents - auto-import exception\n"), + s->owner, s, rel->address); + + pe_create_import_fixup (rel, s, bfd_get_32 (s->owner, addend)); + + return 1; +} + +static void +pe_find_data_imports (void) +{ + struct bfd_link_hash_entry *undef, *sym; + + if (link_info.pei386_auto_import == 0) + return; + + for (undef = link_info.hash->undefs; undef; undef=undef->u.undef.next) + { + if (undef->type == bfd_link_hash_undefined) + { + /* C++ symbols are *long*. */ + char buf[4096]; + + if (pe_dll_extra_pe_debug) + printf ("%s:%s\n", __FUNCTION__, undef->root.string); + + sprintf (buf, "__imp_%s", undef->root.string); + + sym = bfd_link_hash_lookup (link_info.hash, buf, 0, 0, 1); + + if (sym && sym->type == bfd_link_hash_defined) + { + bfd *b = sym->u.def.section->owner; + asymbol **symbols; + int nsyms, symsize, i; + + if (link_info.pei386_auto_import == -1) + { + static bfd_boolean warned = FALSE; + + info_msg (_("Info: resolving %s by linking to %s (auto-import)\n"), + undef->root.string, buf); + + /* PR linker/4844. */ + if (! warned) + { + warned = TRUE; + einfo (_("%P: warning: auto-importing has been activated without --enable-auto-import specified on the command line.\n\ +This should work unless it involves constant data structures referencing symbols from auto-imported DLLs.")); + } + } + + symsize = bfd_get_symtab_upper_bound (b); + symbols = (asymbol **) xmalloc (symsize); + nsyms = bfd_canonicalize_symtab (b, symbols); + + for (i = 0; i < nsyms; i++) + { + if (! CONST_STRNEQ (symbols[i]->name, U ("_head_"))) + continue; + + if (pe_dll_extra_pe_debug) + printf ("->%s\n", symbols[i]->name); + + pe_data_import_dll = (char*) (symbols[i]->name + + sizeof (U ("_head_")) - 1); + break; + } + + pe_walk_relocs_of_symbol (&link_info, undef->root.string, + make_import_fixup); + + /* Let's differentiate it somehow from defined. */ + undef->type = bfd_link_hash_defweak; + /* We replace original name with __imp_ prefixed, this + 1) may trash memory 2) leads to duplicate symbol generation. + Still, IMHO it's better than having name poluted. */ + undef->root.string = sym->root.string; + undef->u.def.value = sym->u.def.value; + undef->u.def.section = sym->u.def.section; + } + } + } +} + +static bfd_boolean +pr_sym (struct bfd_hash_entry *h, void *inf ATTRIBUTE_UNUSED) +{ + if (pe_dll_extra_pe_debug) + printf ("+%s\n", h->string); + + return TRUE; +} +#endif /* DLL_SUPPORT */ + + +static void +gld_${EMULATION_NAME}_after_open (void) +{ +#ifdef DLL_SUPPORT + if (pe_dll_extra_pe_debug) + { + bfd *a; + struct bfd_link_hash_entry *sym; + + printf ("%s()\n", __FUNCTION__); + + for (sym = link_info.hash->undefs; sym; sym=sym->u.undef.next) + printf ("-%s\n", sym->root.string); + bfd_hash_traverse (&link_info.hash->table, pr_sym, NULL); + + for (a = link_info.input_bfds; a; a = a->link_next) + printf ("*%s\n",a->filename); + } +#endif + + /* Pass the wacky PE command line options into the output bfd. + FIXME: This should be done via a function, rather than by + including an internal BFD header. */ + + if (coff_data (link_info.output_bfd) == NULL + || coff_data (link_info.output_bfd)->pe == 0) + einfo (_("%F%P: cannot perform PE operations on non PE output file '%B'.\n"), + link_info.output_bfd); + + pe_data (link_info.output_bfd)->pe_opthdr = pe; + pe_data (link_info.output_bfd)->dll = init[DLLOFF].value; + pe_data (link_info.output_bfd)->real_flags |= real_flags; + +#ifdef DLL_SUPPORT + if (pe_enable_stdcall_fixup) /* -1=warn or 1=disable */ + pe_fixup_stdcalls (); + + pe_process_import_defs (link_info.output_bfd, &link_info); + + pe_find_data_imports (); + +#if defined (TARGET_IS_i386pe) \ + || defined (TARGET_IS_armpe) \ + || defined (TARGET_IS_arm_epoc_pe) \ + || defined (TARGET_IS_arm_wince_pe) + if (!link_info.relocatable) + pe_dll_build_sections (link_info.output_bfd, &link_info); + else + pe_exe_build_sections (link_info.output_bfd, &link_info); +#else + if (link_info.shared) + pe_dll_build_sections (link_info.output_bfd, &link_info); +#endif +#endif /* DLL_SUPPORT */ + +#if defined(TARGET_IS_armpe) || defined(TARGET_IS_arm_epoc_pe) || defined(TARGET_IS_arm_wince_pe) + if (strstr (bfd_get_target (link_info.output_bfd), "arm") == NULL) + { + /* The arm backend needs special fields in the output hash structure. + These will only be created if the output format is an arm format, + hence we do not support linking and changing output formats at the + same time. Use a link followed by objcopy to change output formats. */ + einfo ("%F%X%P: error: cannot change output format whilst linking ARM binaries\n"); + return; + } + { + /* Find a BFD that can hold the interworking stubs. */ + LANG_FOR_EACH_INPUT_STATEMENT (is) + { + if (bfd_arm_get_bfd_for_interworking (is->the_bfd, & link_info)) + break; + } + } +#endif + + { + /* This next chunk of code tries to detect the case where you have + two import libraries for the same DLL (specifically, + symbolically linking libm.a and libc.a in cygwin to + libcygwin.a). In those cases, it's possible for function + thunks from the second implib to be used but without the + head/tail objects, causing an improper import table. We detect + those cases and rename the "other" import libraries to match + the one the head/tail come from, so that the linker will sort + things nicely and produce a valid import table. */ + + LANG_FOR_EACH_INPUT_STATEMENT (is) + { + if (is->the_bfd->my_archive) + { + int idata2 = 0, reloc_count=0, is_imp = 0; + asection *sec; + + /* See if this is an import library thunk. */ + for (sec = is->the_bfd->sections; sec; sec = sec->next) + { + if (strcmp (sec->name, ".idata\$2") == 0) + idata2 = 1; + if (CONST_STRNEQ (sec->name, ".idata\$")) + is_imp = 1; + reloc_count += sec->reloc_count; + } + + if (is_imp && !idata2 && reloc_count) + { + /* It is, look for the reference to head and see if it's + from our own library. */ + for (sec = is->the_bfd->sections; sec; sec = sec->next) + { + int i; + long symsize; + long relsize; + asymbol **symbols; + arelent **relocs; + int nrelocs; + + symsize = bfd_get_symtab_upper_bound (is->the_bfd); + if (symsize < 1) + break; + relsize = bfd_get_reloc_upper_bound (is->the_bfd, sec); + if (relsize < 1) + break; + + symbols = (asymbol **) xmalloc (symsize); + symsize = bfd_canonicalize_symtab (is->the_bfd, symbols); + if (symsize < 0) + { + einfo ("%X%P: unable to process symbols: %E"); + return; + } + + relocs = (arelent **) xmalloc ((size_t) relsize); + nrelocs = bfd_canonicalize_reloc (is->the_bfd, sec, + relocs, symbols); + if (nrelocs < 0) + { + free (relocs); + einfo ("%X%P: unable to process relocs: %E"); + return; + } + + for (i = 0; i < nrelocs; i++) + { + struct bfd_symbol *s; + struct bfd_link_hash_entry * blhe; + char *other_bfd_filename; + char *n; + + s = (relocs[i]->sym_ptr_ptr)[0]; + + if (s->flags & BSF_LOCAL) + continue; + + /* Thunk section with reloc to another bfd. */ + blhe = bfd_link_hash_lookup (link_info.hash, + s->name, + FALSE, FALSE, TRUE); + + if (blhe == NULL + || blhe->type != bfd_link_hash_defined) + continue; + + other_bfd_filename + = blhe->u.def.section->owner->my_archive + ? bfd_get_filename (blhe->u.def.section->owner->my_archive) + : bfd_get_filename (blhe->u.def.section->owner); + + if (strcmp (bfd_get_filename (is->the_bfd->my_archive), + other_bfd_filename) == 0) + continue; + + /* Rename this implib to match the other one. */ + n = xmalloc (strlen (other_bfd_filename) + 1); + strcpy (n, other_bfd_filename); + is->the_bfd->my_archive->filename = n; + } + + free (relocs); + /* Note - we do not free the symbols, + they are now cached in the BFD. */ + } + } + } + } + } + + { + int is_ms_arch = 0; + bfd *cur_arch = 0; + lang_input_statement_type *is2; + lang_input_statement_type *is3; + + /* Careful - this is a shell script. Watch those dollar signs! */ + /* Microsoft import libraries have every member named the same, + and not in the right order for us to link them correctly. We + must detect these and rename the members so that they'll link + correctly. There are three types of objects: the head, the + thunks, and the sentinel(s). The head is easy; it's the one + with idata2. We assume that the sentinels won't have relocs, + and the thunks will. It's easier than checking the symbol + table for external references. */ + LANG_FOR_EACH_INPUT_STATEMENT (is) + { + if (is->the_bfd->my_archive) + { + char *pnt; + bfd *arch = is->the_bfd->my_archive; + + if (cur_arch != arch) + { + cur_arch = arch; + is_ms_arch = 1; + + for (is3 = is; + is3 && is3->the_bfd->my_archive == arch; + is3 = (lang_input_statement_type *) is3->next) + { + /* A MS dynamic import library can also contain static + members, so look for the first element with a .dll + extension, and use that for the remainder of the + comparisons. */ + pnt = strrchr (is3->the_bfd->filename, '.'); + if (pnt != NULL && strcmp (pnt, ".dll") == 0) + break; + } + + if (is3 == NULL) + is_ms_arch = 0; + else + { + /* OK, found one. Now look to see if the remaining + (dynamic import) members use the same name. */ + for (is2 = is; + is2 && is2->the_bfd->my_archive == arch; + is2 = (lang_input_statement_type *) is2->next) + { + /* Skip static members, ie anything with a .obj + extension. */ + pnt = strrchr (is2->the_bfd->filename, '.'); + if (pnt != NULL && strcmp (pnt, ".obj") == 0) + continue; + + if (strcmp (is3->the_bfd->filename, + is2->the_bfd->filename)) + { + is_ms_arch = 0; + break; + } + } + } + } + + /* This fragment might have come from an .obj file in a Microsoft + import, and not an actual import record. If this is the case, + then leave the filename alone. */ + pnt = strrchr (is->the_bfd->filename, '.'); + + if (is_ms_arch && (strcmp (pnt, ".dll") == 0)) + { + int idata2 = 0, reloc_count=0; + asection *sec; + char *new_name, seq; + + for (sec = is->the_bfd->sections; sec; sec = sec->next) + { + if (strcmp (sec->name, ".idata\$2") == 0) + idata2 = 1; + reloc_count += sec->reloc_count; + } + + if (idata2) /* .idata2 is the TOC */ + seq = 'a'; + else if (reloc_count > 0) /* thunks */ + seq = 'b'; + else /* sentinel */ + seq = 'c'; + + new_name = xmalloc (strlen (is->the_bfd->filename) + 3); + sprintf (new_name, "%s.%c", is->the_bfd->filename, seq); + is->the_bfd->filename = new_name; + + new_name = xmalloc (strlen (is->filename) + 3); + sprintf (new_name, "%s.%c", is->filename, seq); + is->filename = new_name; + } + } + } + } + + { + /* The following chunk of code tries to identify jump stubs in + import libraries which are dead code and eliminates them + from the final link. For each exported symbol , there + is a object file in the import library with a .text section + and several .idata$* sections. The .text section contains the + symbol definition for which is a jump stub of the form + jmp *__imp_. The .idata$5 contains the symbol definition + for __imp_ which is the address of the slot for in + the import address table. When a symbol is imported explicitly + using __declspec(dllimport) declaration, the compiler generates + a reference to __imp_ which directly resolves to the + symbol in .idata$5, in which case the jump stub code is not + needed. The following code tries to identify jump stub sections + in import libraries which are not referred to by anyone and + marks them for exclusion from the final link. */ + LANG_FOR_EACH_INPUT_STATEMENT (is) + { + if (is->the_bfd->my_archive) + { + int is_imp = 0; + asection *sec, *stub_sec = NULL; + + /* See if this is an import library thunk. */ + for (sec = is->the_bfd->sections; sec; sec = sec->next) + { + if (strncmp (sec->name, ".idata\$", 7) == 0) + is_imp = 1; + /* The section containing the jmp stub has code + and has a reloc. */ + if ((sec->flags & SEC_CODE) && sec->reloc_count) + stub_sec = sec; + } + + if (is_imp && stub_sec) + { + long symsize; + asymbol **symbols; + long src_count; + struct bfd_link_hash_entry * blhe; + + symsize = bfd_get_symtab_upper_bound (is->the_bfd); + symbols = xmalloc (symsize); + symsize = bfd_canonicalize_symtab (is->the_bfd, symbols); + + for (src_count = 0; src_count < symsize; src_count++) + { + if (symbols[src_count]->section->id == stub_sec->id) + { + /* This symbol belongs to the section containing + the stub. */ + blhe = bfd_link_hash_lookup (link_info.hash, + symbols[src_count]->name, + FALSE, FALSE, TRUE); + /* If the symbol in the stub section has no other + undefined references, exclude the stub section + from the final link. */ + if (blhe && (blhe->type == bfd_link_hash_defined) + && (blhe->u.undef.next == NULL)) + stub_sec->flags |= SEC_EXCLUDE; + } + } + free (symbols); + } + } + } + } +} + +static void +gld_${EMULATION_NAME}_before_allocation (void) +{ +#ifdef TARGET_IS_ppcpe + /* Here we rummage through the found bfds to collect toc information. */ + { + LANG_FOR_EACH_INPUT_STATEMENT (is) + { + if (!ppc_process_before_allocation (is->the_bfd, &link_info)) + { + /* xgettext:c-format */ + einfo (_("Errors encountered processing file %s\n"), is->filename); + } + } + } + + /* We have seen it all. Allocate it, and carry on. */ + ppc_allocate_toc_section (&link_info); +#endif /* TARGET_IS_ppcpe */ + +#if defined(TARGET_IS_armpe) || defined(TARGET_IS_arm_epoc_pe) || defined(TARGET_IS_arm_wince_pe) + /* FIXME: we should be able to set the size of the interworking stub + section. + + Here we rummage through the found bfds to collect glue + information. FIXME: should this be based on a command line + option? krk@cygnus.com. */ + { + LANG_FOR_EACH_INPUT_STATEMENT (is) + { + if (! bfd_arm_process_before_allocation + (is->the_bfd, & link_info, support_old_code)) + { + /* xgettext:c-format */ + einfo (_("Errors encountered processing file %s for interworking"), + is->filename); + } + } + } + + /* We have seen it all. Allocate it, and carry on. */ + bfd_arm_allocate_interworking_sections (& link_info); +#endif /* TARGET_IS_armpe || TARGET_IS_arm_epoc_pe || TARGET_IS_arm_wince_pe */ + + before_allocation_default (); +} + +#ifdef DLL_SUPPORT +/* This is called when an input file isn't recognized as a BFD. We + check here for .DEF files and pull them in automatically. */ + +static int +saw_option (char *option) +{ + int i; + + for (i = 0; init[i].ptr; i++) + if (strcmp (init[i].symbol, option) == 0) + return init[i].inited; + return 0; +} +#endif /* DLL_SUPPORT */ + +static bfd_boolean +gld_${EMULATION_NAME}_unrecognized_file (lang_input_statement_type *entry ATTRIBUTE_UNUSED) +{ +#ifdef DLL_SUPPORT + const char *ext = entry->filename + strlen (entry->filename) - 4; + + if (strcmp (ext, ".def") == 0 || strcmp (ext, ".DEF") == 0) + { + pe_def_file = def_file_parse (entry->filename, pe_def_file); + + if (pe_def_file) + { + int i, buflen=0, len; + char *buf; + + for (i = 0; i < pe_def_file->num_exports; i++) + { + len = strlen (pe_def_file->exports[i].internal_name); + if (buflen < len + 2) + buflen = len + 2; + } + + buf = (char *) xmalloc (buflen); + + for (i = 0; i < pe_def_file->num_exports; i++) + { + struct bfd_link_hash_entry *h; + + sprintf (buf, "%s%s", U (""), pe_def_file->exports[i].internal_name); + + h = bfd_link_hash_lookup (link_info.hash, buf, TRUE, TRUE, TRUE); + if (h == (struct bfd_link_hash_entry *) NULL) + einfo (_("%P%F: bfd_link_hash_lookup failed: %E\n")); + if (h->type == bfd_link_hash_new) + { + h->type = bfd_link_hash_undefined; + h->u.undef.abfd = NULL; + bfd_link_add_undef (link_info.hash, h); + } + } + free (buf); + + /* def_file_print (stdout, pe_def_file); */ + if (pe_def_file->is_dll == 1) + link_info.shared = 1; + + if (pe_def_file->base_address != (bfd_vma)(-1)) + { + pe.ImageBase + = pe_data (link_info.output_bfd)->pe_opthdr.ImageBase + = init[IMAGEBASEOFF].value + = pe_def_file->base_address; + init[IMAGEBASEOFF].inited = 1; + if (image_base_statement) + image_base_statement->exp = exp_assop ('=', "__image_base__", + exp_intop (pe.ImageBase)); + } + + if (pe_def_file->stack_reserve != -1 + && ! saw_option ("__size_of_stack_reserve__")) + { + pe.SizeOfStackReserve = pe_def_file->stack_reserve; + if (pe_def_file->stack_commit != -1) + pe.SizeOfStackCommit = pe_def_file->stack_commit; + } + if (pe_def_file->heap_reserve != -1 + && ! saw_option ("__size_of_heap_reserve__")) + { + pe.SizeOfHeapReserve = pe_def_file->heap_reserve; + if (pe_def_file->heap_commit != -1) + pe.SizeOfHeapCommit = pe_def_file->heap_commit; + } + return TRUE; + } + } +#endif + return FALSE; +} + +static bfd_boolean +gld_${EMULATION_NAME}_recognized_file (lang_input_statement_type *entry ATTRIBUTE_UNUSED) +{ +#ifdef DLL_SUPPORT +#ifdef TARGET_IS_i386pe + pe_dll_id_target ("pei-i386"); +#endif +#ifdef TARGET_IS_shpe + pe_dll_id_target ("pei-shl"); +#endif +#ifdef TARGET_IS_mipspe + pe_dll_id_target ("pei-mips"); +#endif +#ifdef TARGET_IS_armpe + pe_dll_id_target ("pei-arm-little"); +#endif +#ifdef TARGET_IS_arm_epoc_pe + pe_dll_id_target ("epoc-pei-arm-little"); +#endif +#ifdef TARGET_IS_arm_wince_pe + pe_dll_id_target ("pei-arm-wince-little"); +#endif + if (pe_bfd_is_dll (entry->the_bfd)) + return pe_implied_import_dll (entry->filename); +#endif + return FALSE; +} + +static void +gld_${EMULATION_NAME}_finish (void) +{ +#if defined(TARGET_IS_armpe) || defined(TARGET_IS_arm_epoc_pe) || defined(TARGET_IS_arm_wince_pe) + struct bfd_link_hash_entry * h; + + if (thumb_entry_symbol != NULL) + { + h = bfd_link_hash_lookup (link_info.hash, thumb_entry_symbol, + FALSE, FALSE, TRUE); + + if (h != (struct bfd_link_hash_entry *) NULL + && (h->type == bfd_link_hash_defined + || h->type == bfd_link_hash_defweak) + && h->u.def.section->output_section != NULL) + { + static char buffer[32]; + bfd_vma val; + + /* Special procesing is required for a Thumb entry symbol. The + bottom bit of its address must be set. */ + val = (h->u.def.value + + bfd_get_section_vma (link_info.output_bfd, + h->u.def.section->output_section) + + h->u.def.section->output_offset); + + val |= 1; + + /* Now convert this value into a string and store it in entry_symbol + where the lang_finish() function will pick it up. */ + buffer[0] = '0'; + buffer[1] = 'x'; + + sprintf_vma (buffer + 2, val); + + if (entry_symbol.name != NULL && entry_from_cmdline) + einfo (_("%P: warning: '--thumb-entry %s' is overriding '-e %s'\n"), + thumb_entry_symbol, entry_symbol.name); + entry_symbol.name = buffer; + } + else + einfo (_("%P: warning: cannot find thumb start symbol %s\n"), thumb_entry_symbol); + } +#endif /* defined(TARGET_IS_armpe) || defined(TARGET_IS_arm_epoc_pe) || defined(TARGET_IS_arm_wince_pe) */ + + finish_default (); + +#ifdef DLL_SUPPORT + if (link_info.shared +#if !defined(TARGET_IS_shpe) && !defined(TARGET_IS_mipspe) + || (!link_info.relocatable && pe_def_file->num_exports != 0) +#endif + ) + { + pe_dll_fill_sections (link_info.output_bfd, &link_info); + if (pe_implib_filename) + pe_dll_generate_implib (pe_def_file, pe_implib_filename); + } +#if defined(TARGET_IS_shpe) || defined(TARGET_IS_mipspe) + /* ARM doesn't need relocs. */ + else + { + pe_exe_fill_sections (link_info.output_bfd, &link_info); + } +#endif + + if (pe_out_def_filename) + pe_dll_generate_def_file (pe_out_def_filename); +#endif /* DLL_SUPPORT */ + + /* I don't know where .idata gets set as code, but it shouldn't be. */ + { + asection *asec = bfd_get_section_by_name (link_info.output_bfd, ".idata"); + + if (asec) + { + asec->flags &= ~SEC_CODE; + asec->flags |= SEC_DATA; + } + } +} + + +/* Place an orphan section. + + We use this to put sections in a reasonable place in the file, and + to ensure that they are aligned as required. + + We handle grouped sections here as well. A section named .foo$nn + goes into the output section .foo. All grouped sections are sorted + by name. + + Grouped sections for the default sections are handled by the + default linker script using wildcards, and are sorted by + sort_sections. */ + +static bfd_boolean +gld_${EMULATION_NAME}_place_orphan (asection *s) +{ + const char *secname; + const char *orig_secname; + char *dollar = NULL; + lang_output_section_statement_type *os; + lang_statement_list_type add_child; + + secname = bfd_get_section_name (s->owner, s); + + /* Look through the script to see where to place this section. */ + orig_secname = secname; + if (!link_info.relocatable + && (dollar = strchr (secname, '$')) != NULL) + { + size_t len = dollar - orig_secname; + char *newname = xmalloc (len + 1); + memcpy (newname, orig_secname, len); + newname[len] = '\0'; + secname = newname; + } + + os = lang_output_section_find (secname); + + lang_list_init (&add_child); + + if (os != NULL + && (os->bfd_section == NULL + || os->bfd_section->flags == 0 + || ((s->flags ^ os->bfd_section->flags) + & (SEC_LOAD | SEC_ALLOC)) == 0)) + { + /* We already have an output section statement with this + name, and its bfd section, if any, has compatible flags. + If the section already exists but does not have any flags set, + then it has been created by the linker, probably as a result of + a --section-start command line switch. */ + lang_add_section (&add_child, s, os); + } + else + { + static struct orphan_save hold[] = + { + { ".text", + SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE, + 0, 0, 0, 0 }, + { ".rdata", + SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA, + 0, 0, 0, 0 }, + { ".data", + SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_DATA, + 0, 0, 0, 0 }, + { ".bss", + SEC_ALLOC, + 0, 0, 0, 0 } + }; + enum orphan_save_index + { + orphan_text = 0, + orphan_rodata, + orphan_data, + orphan_bss + }; + static int orphan_init_done = 0; + struct orphan_save *place; + lang_output_section_statement_type *after; + etree_type *address; + + if (!orphan_init_done) + { + struct orphan_save *ho; + for (ho = hold; ho < hold + sizeof (hold) / sizeof (hold[0]); ++ho) + if (ho->name != NULL) + { + ho->os = lang_output_section_find (ho->name); + if (ho->os != NULL && ho->os->flags == 0) + ho->os->flags = ho->flags; + } + orphan_init_done = 1; + } + + /* Try to put the new output section in a reasonable place based + on the section name and section flags. */ + + place = NULL; + if ((s->flags & SEC_ALLOC) == 0) + ; + else if ((s->flags & (SEC_LOAD | SEC_HAS_CONTENTS)) == 0) + place = &hold[orphan_bss]; + else if ((s->flags & SEC_READONLY) == 0) + place = &hold[orphan_data]; + else if ((s->flags & SEC_CODE) == 0) + place = &hold[orphan_rodata]; + else + place = &hold[orphan_text]; + + after = NULL; + if (place != NULL) + { + if (place->os == NULL) + place->os = lang_output_section_find (place->name); + after = place->os; + if (after == NULL) + after = lang_output_section_find_by_flags (s, &place->os, NULL); + if (after == NULL) + /* *ABS* is always the first output section statement. */ + after = (&lang_output_section_statement.head + ->output_section_statement); + } + + /* Choose a unique name for the section. This will be needed if the + same section name appears in the input file with different + loadable or allocatable characteristics. */ + if (bfd_get_section_by_name (link_info.output_bfd, secname) != NULL) + { + static int count = 1; + secname = bfd_get_unique_section_name (link_info.output_bfd, + secname, &count); + if (secname == NULL) + einfo ("%F%P: place_orphan failed: %E\n"); + } + + /* All sections in an executable must be aligned to a page boundary. */ + address = exp_unop (ALIGN_K, exp_nameop (NAME, "__section_alignment__")); + os = lang_insert_orphan (s, secname, after, place, address, &add_child); + } + + { + lang_statement_union_type **pl = &os->children.head; + + if (dollar != NULL) + { + bfd_boolean found_dollar; + + /* The section name has a '$'. Sort it with the other '$' + sections. */ + found_dollar = FALSE; + for ( ; *pl != NULL; pl = &(*pl)->header.next) + { + lang_input_section_type *ls; + const char *lname; + + if ((*pl)->header.type != lang_input_section_enum) + continue; + + ls = &(*pl)->input_section; + + lname = bfd_get_section_name (ls->section->owner, ls->section); + if (strchr (lname, '$') == NULL) + { + if (found_dollar) + break; + } + else + { + found_dollar = TRUE; + if (strcmp (orig_secname, lname) < 0) + break; + } + } + } + + if (add_child.head != NULL) + { + add_child.head->header.next = *pl; + *pl = add_child.head; + } + } + + return TRUE; +} + +static bfd_boolean +gld_${EMULATION_NAME}_open_dynamic_archive + (const char *arch ATTRIBUTE_UNUSED, search_dirs_type *search, + lang_input_statement_type *entry) +{ + static const struct + { + const char * format; + bfd_boolean use_prefix; + } + libname_fmt [] = + { + /* Preferred explicit import library for dll's. */ + { "lib%s.dll.a", FALSE }, + /* Alternate explicit import library for dll's. */ + { "%s.dll.a", FALSE }, + /* "libfoo.a" could be either an import lib or a static lib. + For backwards compatibility, libfoo.a needs to precede + libfoo.dll and foo.dll in the search. */ + { "lib%s.a", FALSE }, + /* The 'native' spelling of an import lib name is "foo.lib". */ + { "%s.lib", FALSE }, +#ifdef DLL_SUPPORT + /* Try "foo.dll" (preferred dll name, if specified). */ + { "%s%s.dll", TRUE }, +#endif + /* Try "libfoo.dll" (default preferred dll name). */ + { "lib%s.dll", FALSE }, + /* Finally try 'native' dll name "foo.dll". */ + { "%s.dll", FALSE }, + /* Note: If adding more formats to this table, make sure to check to + see if their length is longer than libname_fmt[0].format, and if + so, update the call to xmalloc() below. */ + { NULL, FALSE } + }; + static unsigned int format_max_len = 0; + const char * filename; + char * full_string; + char * base_string; + unsigned int i; + + + if (! entry->is_archive) + return FALSE; + + filename = entry->filename; + + if (format_max_len == 0) + /* We need to allow space in the memory that we are going to allocate + for the characters in the format string. Since the format array is + static we only need to calculate this information once. In theory + this value could also be computed statically, but this introduces + the possibility for a discrepancy and hence a possible memory + corruption. The lengths we compute here will be too long because + they will include any formating characters (%s) in the strings, but + this will not matter. */ + for (i = 0; libname_fmt[i].format; i++) + if (format_max_len < strlen (libname_fmt[i].format)) + format_max_len = strlen (libname_fmt[i].format); + + full_string = xmalloc (strlen (search->name) + + strlen (filename) + + format_max_len +#ifdef DLL_SUPPORT + + (pe_dll_search_prefix + ? strlen (pe_dll_search_prefix) : 0) +#endif + /* Allow for the terminating NUL and for the path + separator character that is inserted between + search->name and the start of the format string. */ + + 2); + + sprintf (full_string, "%s/", search->name); + base_string = full_string + strlen (full_string); + + for (i = 0; libname_fmt[i].format; i++) + { +#ifdef DLL_SUPPORT + if (libname_fmt[i].use_prefix) + { + if (!pe_dll_search_prefix) + continue; + sprintf (base_string, libname_fmt[i].format, pe_dll_search_prefix, filename); + } + else +#endif + sprintf (base_string, libname_fmt[i].format, filename); + + if (ldfile_try_open_bfd (full_string, entry)) + break; + } + + if (!libname_fmt[i].format) + { + free (full_string); + return FALSE; + } + + entry->filename = full_string; + + return TRUE; +} + +static int +gld_${EMULATION_NAME}_find_potential_libraries + (char *name, lang_input_statement_type *entry) +{ + return ldfile_open_file_search (name, entry, "", ".lib"); +} + +static char * +gld_${EMULATION_NAME}_get_script (int *isfile) +EOF +# Scripts compiled in. +# sed commands to quote an ld script as a C string. +sc="-f stringify.sed" + +fragment <> e${EMULATION_NAME}.c +echo ' ; else if (link_info.relocatable) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xr >> e${EMULATION_NAME}.c +echo ' ; else if (!config.text_read_only) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xbn >> e${EMULATION_NAME}.c +echo ' ; else if (!config.magic_demand_paged) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xn >> e${EMULATION_NAME}.c +if test -n "$GENERATE_AUTO_IMPORT_SCRIPT" ; then +echo ' ; else if (link_info.pei386_auto_import == 1) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xa >> e${EMULATION_NAME}.c +fi +echo ' ; else return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.x >> e${EMULATION_NAME}.c +echo '; }' >> e${EMULATION_NAME}.c + +fragment <> e${EMULATION_NAME}.c +echo ' ; else if (link_info.relocatable) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xr >> e${EMULATION_NAME}.c +echo ' ; else if (!config.text_read_only) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xbn >> e${EMULATION_NAME}.c +echo ' ; else if (!config.magic_demand_paged) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xn >> e${EMULATION_NAME}.c +echo ' ; else return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.x >> e${EMULATION_NAME}.c +echo '; }' >> e${EMULATION_NAME}.c + +else +# Scripts read from the filesystem. + +fragment < + SunOS shared library support by Ian Lance Taylor + + This file is part of the GNU Binutils. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#define TARGET_IS_${EMULATION_NAME} + +#include "sysdep.h" +#include "bfd.h" +#include "bfdlink.h" +#include "libiberty.h" +#include "safe-ctype.h" + +#include "ld.h" +#include "ldmain.h" +#include "ldmisc.h" +#include "ldexp.h" +#include "ldlang.h" +#include "ldfile.h" +#include "ldemul.h" + +#ifdef HAVE_DIRENT_H +# include +#else +# define dirent direct +# ifdef HAVE_SYS_NDIR_H +# include +# endif +# ifdef HAVE_SYS_DIR_H +# include +# endif +# ifdef HAVE_NDIR_H +# include +# endif +#endif + +static void gld${EMULATION_NAME}_find_so + (lang_input_statement_type *); +static char *gld${EMULATION_NAME}_search_dir + (const char *, const char *, bfd_boolean *); +static void gld${EMULATION_NAME}_check_needed + (lang_input_statement_type *); +static bfd_boolean gld${EMULATION_NAME}_search_needed + (const char *, const char *); +static bfd_boolean gld${EMULATION_NAME}_try_needed + (const char *, const char *); +static void gld${EMULATION_NAME}_find_assignment + (lang_statement_union_type *); +static void gld${EMULATION_NAME}_find_exp_assignment + (etree_type *); +static void gld${EMULATION_NAME}_count_need + (lang_input_statement_type *); +static void gld${EMULATION_NAME}_set_need + (lang_input_statement_type *); + +static void +gld${EMULATION_NAME}_before_parse (void) +{ + ldfile_set_output_arch ("${OUTPUT_ARCH}", bfd_arch_`echo ${ARCH} | sed -e 's/:.*//'`); + config.dynamic_link = TRUE; + config.has_shared = TRUE; +} + +/* This is called after the command line arguments have been parsed, + but before the linker script has been read. If this is a native + linker, we add the directories in LD_LIBRARY_PATH to the search + list. */ + +static void +gld${EMULATION_NAME}_set_symbols (void) +{ +EOF +if [ "x${host}" = "x${target}" ] ; then + case " ${EMULATION_LIBPATH} " in + *" ${EMULATION_NAME} "*) +fragment <search_dirs_flag + || ! inp->is_archive + || ! inp->dynamic) + return; + + ASSERT (CONST_STRNEQ (inp->local_sym_name, "-l")); + + for (search = search_head; search != NULL; search = search->next) + { + bfd_boolean found_static; + + found = gld${EMULATION_NAME}_search_dir (search->name, inp->filename, + &found_static); + if (found != NULL || found_static) + break; + } + + if (found == NULL) + { + /* We did not find a matching .so file. This isn't an error, + since there might still be a matching .a file, which will be + found by the usual search. */ + return; + } + + /* Replace the filename with the one we have found. */ + alc = (char *) xmalloc (strlen (search->name) + strlen (found) + 2); + sprintf (alc, "%s/%s", search->name, found); + inp->filename = alc; + + /* Turn off the search_dirs_flag to prevent ldfile_open_file from + searching for this file again. */ + inp->search_dirs_flag = FALSE; + + free (found); + + /* Now look for the same file name, but with .sa instead of .so. If + found, add it to the list of input files. */ + alc = (char *) xmalloc (strlen (inp->filename) + 1); + strcpy (alc, inp->filename); + strstr (alc + strlen (search->name), ".so")[2] = 'a'; + if (stat (alc, &st) != 0) + free (alc); + else + { + lang_input_statement_type *sa; + + /* Add the .sa file to the statement list just before the .so + file. This is really a hack. */ + sa = ((lang_input_statement_type *) + xmalloc (sizeof (lang_input_statement_type))); + *sa = *inp; + + inp->filename = alc; + inp->local_sym_name = alc; + + inp->header.next = (lang_statement_union_type *) sa; + inp->next_real_file = (lang_statement_union_type *) sa; + } +} + +/* Search a directory for a .so file. */ + +static char * +gld${EMULATION_NAME}_search_dir + (const char *dirname, const char *filename, bfd_boolean *found_static) +{ + int force_maj, force_min; + const char *dot; + unsigned int len; + char *alc; + char *found; + int max_maj, max_min; + DIR *dir; + struct dirent *entry; + unsigned int dirnamelen; + char *full_path; + int statval; + struct stat st; + + *found_static = FALSE; + + force_maj = -1; + force_min = -1; + dot = strchr (filename, '.'); + if (dot == NULL) + { + len = strlen (filename); + alc = NULL; + } + else + { + force_maj = atoi (dot + 1); + + len = dot - filename; + alc = (char *) xmalloc (len + 1); + strncpy (alc, filename, len); + alc[len] = '\0'; + filename = alc; + + dot = strchr (dot + 1, '.'); + if (dot != NULL) + force_min = atoi (dot + 1); + } + + found = NULL; + max_maj = max_min = 0; + + dir = opendir (dirname); + if (dir == NULL) + return NULL; + dirnamelen = strlen (dirname); + + while ((entry = readdir (dir)) != NULL) + { + const char *s; + int found_maj, found_min; + + if (! CONST_STRNEQ (entry->d_name, "lib") + || strncmp (entry->d_name + 3, filename, len) != 0) + continue; + + if (dot == NULL + && strcmp (entry->d_name + 3 + len, ".a") == 0) + { + *found_static = TRUE; + continue; + } + + /* We accept libfoo.so without a version number, even though the + native linker does not. This is more convenient for packages + which just generate .so files for shared libraries, as on ELF + systems. */ + if (! CONST_STRNEQ (entry->d_name + 3 + len, ".so")) + continue; + if (entry->d_name[6 + len] == '\0') + ; + else if (entry->d_name[6 + len] == '.' + && ISDIGIT (entry->d_name[7 + len])) + ; + else + continue; + + for (s = entry->d_name + 6 + len; *s != '\0'; s++) + if (*s != '.' && ! ISDIGIT (*s)) + break; + if (*s != '\0') + continue; + + /* We've found a .so file. Work out the major and minor + version numbers. */ + found_maj = 0; + found_min = 0; + sscanf (entry->d_name + 3 + len, ".so.%d.%d", + &found_maj, &found_min); + + if ((force_maj != -1 && force_maj != found_maj) + || (force_min != -1 && force_min != found_min)) + continue; + + /* Make sure the file really exists (ignore broken symlinks). */ + full_path = xmalloc (dirnamelen + 1 + strlen (entry->d_name) + 1); + sprintf (full_path, "%s/%s", dirname, entry->d_name); + statval = stat (full_path, &st); + free (full_path); + if (statval != 0) + continue; + + /* We've found a match for the name we are searching for. See + if this is the version we should use. If the major and minor + versions match, we use the last entry in alphabetical order; + I don't know if this is how SunOS distinguishes libc.so.1.8 + from libc.so.1.8.1, but it ought to suffice. */ + if (found == NULL + || (found_maj > max_maj) + || (found_maj == max_maj + && (found_min > max_min + || (found_min == max_min + && strcmp (entry->d_name, found) > 0)))) + { + if (found != NULL) + free (found); + found = (char *) xmalloc (strlen (entry->d_name) + 1); + strcpy (found, entry->d_name); + max_maj = found_maj; + max_min = found_min; + } + } + + closedir (dir); + + if (alc != NULL) + free (alc); + + return found; +} + +/* These variables are required to pass information back and forth + between after_open and check_needed. */ + +static struct bfd_link_needed_list *global_needed; +static bfd_boolean global_found; + +/* This is called after all the input files have been opened. */ + +static void +gld${EMULATION_NAME}_after_open (void) +{ + struct bfd_link_needed_list *needed, *l; + + /* We only need to worry about this when doing a final link. */ + if (link_info.relocatable || link_info.shared) + return; + + /* Get the list of files which appear in ld_need entries in dynamic + objects included in the link. For each such file, we want to + track down the corresponding library, and include the symbol + table in the link. This is what the runtime dynamic linker will + do. Tracking the files down here permits one dynamic object to + include another without requiring special action by the person + doing the link. Note that the needed list can actually grow + while we are stepping through this loop. */ + needed = bfd_sunos_get_needed_list (link_info.output_bfd, &link_info); + for (l = needed; l != NULL; l = l->next) + { + struct bfd_link_needed_list *ll; + const char *lname; + search_dirs_type *search; + + lname = l->name; + + /* If we've already seen this file, skip it. */ + for (ll = needed; ll != l; ll = ll->next) + if (strcmp (ll->name, lname) == 0) + break; + if (ll != l) + continue; + + /* See if this file was included in the link explicitly. */ + global_needed = l; + global_found = FALSE; + lang_for_each_input_file (gld${EMULATION_NAME}_check_needed); + if (global_found) + continue; + + if (! CONST_STRNEQ (lname, "-l")) + { + bfd *abfd; + + abfd = bfd_openr (lname, bfd_get_target (link_info.output_bfd)); + if (abfd != NULL) + { + if (! bfd_check_format (abfd, bfd_object)) + { + (void) bfd_close (abfd); + abfd = NULL; + } + } + if (abfd != NULL) + { + if ((bfd_get_file_flags (abfd) & DYNAMIC) == 0) + { + (void) bfd_close (abfd); + abfd = NULL; + } + } + if (abfd != NULL) + { + /* We've found the needed dynamic object. */ + if (! bfd_link_add_symbols (abfd, &link_info)) + einfo ("%F%B: could not read symbols: %E\n", abfd); + } + else + { + einfo ("%P: warning: %s, needed by %B, not found\n", + lname, l->by); + } + + continue; + } + + lname += 2; + + /* We want to search for the file in the same way that the + dynamic linker will search. That means that we want to use + rpath_link, rpath or -L, then the environment variable + LD_LIBRARY_PATH (native only), then (if rpath was used) the + linker script LIB_SEARCH_DIRS. */ + if (gld${EMULATION_NAME}_search_needed (command_line.rpath_link, + lname)) + continue; + if (command_line.rpath != NULL) + { + if (gld${EMULATION_NAME}_search_needed (command_line.rpath, lname)) + continue; + } + else + { + for (search = search_head; search != NULL; search = search->next) + if (gld${EMULATION_NAME}_try_needed (search->name, lname)) + break; + if (search != NULL) + continue; + } +EOF +if [ "x${host}" = "x${target}" ] ; then + case " ${EMULATION_LIBPATH} " in + *" ${EMULATION_NAME} "*) +fragment <next) + { + if (search->cmdline) + continue; + if (gld${EMULATION_NAME}_try_needed (search->name, lname)) + break; + } + if (search != NULL) + continue; + } + + einfo ("%P: warning: %s, needed by %B, not found\n", + l->name, l->by); + } +} + +/* Search for a needed file in a path. */ + +static bfd_boolean +gld${EMULATION_NAME}_search_needed (const char *path, const char *name) +{ + const char *s; + + if (path == NULL || *path == '\0') + return FALSE; + while (1) + { + const char *dir; + char *dircopy; + + s = strchr (path, ':'); + if (s == NULL) + { + dircopy = NULL; + dir = path; + } + else + { + dircopy = (char *) xmalloc (s - path + 1); + memcpy (dircopy, path, s - path); + dircopy[s - path] = '\0'; + dir = dircopy; + } + + if (gld${EMULATION_NAME}_try_needed (dir, name)) + return TRUE; + + if (dircopy != NULL) + free (dircopy); + + if (s == NULL) + break; + path = s + 1; + } + + return FALSE; +} + +/* This function is called for each possible directory for a needed + dynamic object. */ + +static bfd_boolean +gld${EMULATION_NAME}_try_needed (const char *dir, const char *name) +{ + char *file; + char *alc; + bfd_boolean ignore; + bfd *abfd; + + file = gld${EMULATION_NAME}_search_dir (dir, name, &ignore); + if (file == NULL) + return FALSE; + + alc = (char *) xmalloc (strlen (dir) + strlen (file) + 2); + sprintf (alc, "%s/%s", dir, file); + free (file); + abfd = bfd_openr (alc, bfd_get_target (link_info.output_bfd)); + if (abfd == NULL) + return FALSE; + if (! bfd_check_format (abfd, bfd_object)) + { + (void) bfd_close (abfd); + return FALSE; + } + if ((bfd_get_file_flags (abfd) & DYNAMIC) == 0) + { + (void) bfd_close (abfd); + return FALSE; + } + + /* We've found the needed dynamic object. */ + + /* Add this file into the symbol table. */ + if (! bfd_link_add_symbols (abfd, &link_info)) + einfo ("%F%B: could not read symbols: %E\n", abfd); + + return TRUE; +} + +/* See if we have already included a needed object in the link. This + does not have to be precise, as it does no harm to include a + dynamic object more than once. */ + +static void +gld${EMULATION_NAME}_check_needed (lang_input_statement_type *s) +{ + if (s->filename == NULL) + return; + if (! CONST_STRNEQ (global_needed->name, "-l")) + { + if (strcmp (s->filename, global_needed->name) == 0) + global_found = TRUE; + } + else + { + const char *sname, *lname; + const char *sdot, *ldot; + int lmaj, lmin, smaj, smin; + + lname = global_needed->name + 2; + + sname = strrchr (s->filename, '/'); + if (sname == NULL) + sname = s->filename; + else + ++sname; + + if (! CONST_STRNEQ (sname, "lib")) + return; + sname += 3; + + ldot = strchr (lname, '.'); + if (ldot == NULL) + ldot = lname + strlen (lname); + + sdot = strstr (sname, ".so."); + if (sdot == NULL) + return; + + if (sdot - sname != ldot - lname + || strncmp (lname, sname, sdot - sname) != 0) + return; + + lmaj = lmin = -1; + sscanf (ldot, ".%d.%d", &lmaj, &lmin); + smaj = smin = -1; + sscanf (sdot, ".so.%d.%d", &smaj, &smin); + if ((smaj != lmaj && smaj != -1 && lmaj != -1) + || (smin != lmin && smin != -1 && lmin != -1)) + return; + + global_found = TRUE; + } +} + +/* We need to use static variables to pass information around the call + to lang_for_each_statement. Ick. */ + +static const char *find_assign; +static bfd_boolean found_assign; + +/* We need to use static variables to pass information around the call + to lang_for_each_input_file. Ick. */ + +static bfd_size_type need_size; +static bfd_size_type need_entries; +static bfd_byte *need_contents; +static bfd_byte *need_pinfo; +static bfd_byte *need_pnames; + +/* The size of one entry in the .need section, not including the file + name. */ + +#define NEED_ENTRY_SIZE (16) + +/* This is called after the sections have been attached to output + sections, but before any sizes or addresses have been set. */ + +static void +gld${EMULATION_NAME}_before_allocation (void) +{ + struct bfd_link_hash_entry *hdyn = NULL; + asection *sneed; + asection *srules; + asection *sdyn; + + /* The SunOS native linker creates a shared library whenever there + are any undefined symbols in a link, unless -e is used. This is + pretty weird, but we are compatible. */ + if (! link_info.shared && ! link_info.relocatable && ! entry_from_cmdline) + { + struct bfd_link_hash_entry *h; + + for (h = link_info.hash->undefs; h != NULL; h = h->u.undef.next) + { + if (h->type == bfd_link_hash_undefined + && h->u.undef.abfd != NULL + && (h->u.undef.abfd->flags & DYNAMIC) == 0 + && strcmp (h->root.string, "__DYNAMIC") != 0 + && strcmp (h->root.string, "__GLOBAL_OFFSET_TABLE_") != 0) + { + find_assign = h->root.string; + found_assign = FALSE; + lang_for_each_statement (gld${EMULATION_NAME}_find_assignment); + if (! found_assign) + { + link_info.shared = TRUE; + break; + } + } + } + } + + if (link_info.shared) + { + lang_output_section_statement_type *os; + + /* Set the .text section to start at 0x20, not 0x2020. FIXME: + This is too magical. */ + os = lang_output_section_statement_lookup (".text"); + if (os->addr_tree == NULL) + os->addr_tree = exp_intop (0x20); + } + + /* We need to create a __DYNAMIC symbol. We don't do this in the + linker script because we want to set the value to the start of + the dynamic section if there is one, or to zero if there isn't + one. We need to create the symbol before calling + size_dynamic_sections, although we can't set the value until + afterward. */ + if (! link_info.relocatable) + { + hdyn = bfd_link_hash_lookup (link_info.hash, "__DYNAMIC", TRUE, FALSE, + FALSE); + if (hdyn == NULL) + einfo ("%P%F: bfd_link_hash_lookup: %E\n"); + if (! bfd_sunos_record_link_assignment (link_info.output_bfd, &link_info, + "__DYNAMIC")) + einfo ("%P%F: failed to record assignment to __DYNAMIC: %E\n"); + } + + /* If we are going to make any variable assignments, we need to let + the backend linker know about them in case the variables are + referred to by dynamic objects. */ + lang_for_each_statement (gld${EMULATION_NAME}_find_assignment); + + /* Let the backend linker work out the sizes of any sections + required by dynamic linking. */ + if (! bfd_sunos_size_dynamic_sections (link_info.output_bfd, &link_info, + &sdyn, &sneed, &srules)) + einfo ("%P%F: failed to set dynamic section sizes: %E\n"); + + if (sneed != NULL) + { + /* Set up the .need section. See the description of the ld_need + field in include/aout/sun4.h. */ + + need_entries = 0; + need_size = 0; + + lang_for_each_input_file (gld${EMULATION_NAME}_count_need); + + /* We should only have a .need section if we have at least one + dynamic object. */ + ASSERT (need_entries != 0); + + sneed->size = need_size; + sneed->contents = (bfd_byte *) xmalloc (need_size); + + need_contents = sneed->contents; + need_pinfo = sneed->contents; + need_pnames = sneed->contents + need_entries * 16; + + lang_for_each_input_file (gld${EMULATION_NAME}_set_need); + + ASSERT ((bfd_size_type) (need_pnames - sneed->contents) == need_size); + } + + if (srules != NULL) + { + /* Set up the .rules section. This is just a PATH like string + of the -L arguments given on the command line. We permit the + user to specify the directories using the -rpath command line + option. */ + if (command_line.rpath) + { + srules->size = strlen (command_line.rpath); + srules->contents = (bfd_byte *) command_line.rpath; + } + else + { + unsigned int size; + search_dirs_type *search; + + size = 0; + for (search = search_head; search != NULL; search = search->next) + if (search->cmdline) + size += strlen (search->name) + 1; + srules->size = size; + if (size > 0) + { + char *p; + + srules->contents = (bfd_byte *) xmalloc (size); + p = (char *) srules->contents; + *p = '\0'; + for (search = search_head; search != NULL; search = search->next) + { + if (search->cmdline) + { + if (p != (char *) srules->contents) + *p++ = ':'; + strcpy (p, search->name); + p += strlen (p); + } + } + } + } + } + + /* We must assign a value to __DYNAMIC. It should be zero if we are + not doing a dynamic link, or the start of the .dynamic section if + we are doing one. */ + if (! link_info.relocatable) + { + hdyn->type = bfd_link_hash_defined; + hdyn->u.def.value = 0; + if (sdyn != NULL) + hdyn->u.def.section = sdyn; + else + hdyn->u.def.section = bfd_abs_section_ptr; + } + + before_allocation_default (); +} + +/* This is called by the before_allocation routine via + lang_for_each_statement. It does one of two things: if the + variable find_assign is set, it sets found_assign if it finds an + assignment to that variable; otherwise it tells the backend linker + about all assignment statements, in case they are assignments to + symbols which are referred to by dynamic objects. */ + +static void +gld${EMULATION_NAME}_find_assignment (lang_statement_union_type *s) +{ + if (s->header.type == lang_assignment_statement_enum + && (find_assign == NULL || ! found_assign)) + gld${EMULATION_NAME}_find_exp_assignment (s->assignment_statement.exp); +} + +/* Look through an expression for an assignment statement. */ + +static void +gld${EMULATION_NAME}_find_exp_assignment (etree_type *exp) +{ + switch (exp->type.node_class) + { + case etree_assign: + if (find_assign != NULL) + { + if (strcmp (find_assign, exp->assign.dst) == 0) + found_assign = TRUE; + return; + } + + if (strcmp (exp->assign.dst, ".") != 0) + { + if (! bfd_sunos_record_link_assignment (link_info.output_bfd, + &link_info, + exp->assign.dst)) + einfo ("%P%F: failed to record assignment to %s: %E\n", + exp->assign.dst); + } + gld${EMULATION_NAME}_find_exp_assignment (exp->assign.src); + break; + + case etree_binary: + gld${EMULATION_NAME}_find_exp_assignment (exp->binary.lhs); + gld${EMULATION_NAME}_find_exp_assignment (exp->binary.rhs); + break; + + case etree_trinary: + gld${EMULATION_NAME}_find_exp_assignment (exp->trinary.cond); + gld${EMULATION_NAME}_find_exp_assignment (exp->trinary.lhs); + gld${EMULATION_NAME}_find_exp_assignment (exp->trinary.rhs); + break; + + case etree_unary: + gld${EMULATION_NAME}_find_exp_assignment (exp->unary.child); + break; + + default: + break; + } +} + +/* Work out the size of the .need section, and the number of entries. + The backend will set the ld_need field of the dynamic linking + information to point to the .need section. See include/aout/sun4.h + for more information. */ + +static void +gld${EMULATION_NAME}_count_need (lang_input_statement_type *inp) +{ + if (inp->the_bfd != NULL + && (inp->the_bfd->flags & DYNAMIC) != 0) + { + ++need_entries; + need_size += NEED_ENTRY_SIZE; + if (! inp->is_archive) + need_size += strlen (inp->filename) + 1; + else + { + ASSERT (inp->local_sym_name[0] == '-' + && inp->local_sym_name[1] == 'l'); + need_size += strlen (inp->local_sym_name + 2) + 1; + } + } +} + +/* Fill in the contents of the .need section. */ + +static void +gld${EMULATION_NAME}_set_need (lang_input_statement_type *inp) +{ + if (inp->the_bfd != NULL + && (inp->the_bfd->flags & DYNAMIC) != 0) + { + bfd_size_type c; + + /* To really fill in the .need section contents, we need to know + the final file position of the section, but we don't. + Instead, we use offsets, and rely on the BFD backend to + finish the section up correctly. FIXME: Talk about lack of + referential locality. */ + bfd_put_32 (link_info.output_bfd, need_pnames - need_contents, + need_pinfo); + if (! inp->is_archive) + { + bfd_put_32 (link_info.output_bfd, (bfd_vma) 0, need_pinfo + 4); + bfd_put_16 (link_info.output_bfd, (bfd_vma) 0, need_pinfo + 8); + bfd_put_16 (link_info.output_bfd, (bfd_vma) 0, need_pinfo + 10); + strcpy ((char *) need_pnames, inp->filename); + } + else + { + char *verstr; + int maj, min; + + bfd_put_32 (link_info.output_bfd, (bfd_vma) 0x80000000, + need_pinfo + 4); + maj = 0; + min = 0; + verstr = strstr (inp->filename, ".so."); + if (verstr != NULL) + sscanf (verstr, ".so.%d.%d", &maj, &min); + bfd_put_16 (link_info.output_bfd, (bfd_vma) maj, need_pinfo + 8); + bfd_put_16 (link_info.output_bfd, (bfd_vma) min, need_pinfo + 10); + strcpy ((char *) need_pnames, inp->local_sym_name + 2); + } + + c = (need_pinfo - need_contents) / NEED_ENTRY_SIZE; + if (c + 1 >= need_entries) + bfd_put_32 (link_info.output_bfd, (bfd_vma) 0, need_pinfo + 12); + else + bfd_put_32 (link_info.output_bfd, (bfd_vma) (c + 1) * NEED_ENTRY_SIZE, + need_pinfo + 12); + + need_pinfo += NEED_ENTRY_SIZE; + need_pnames += strlen ((char *) need_pnames) + 1; + } +} + +static char * +gld${EMULATION_NAME}_get_script (int *isfile) +EOF + +if test -n "$COMPILE_IN" +then +# Scripts compiled in. + +# sed commands to quote an ld script as a C string. +sc="-f stringify.sed" + +fragment <> e${EMULATION_NAME}.c +echo ' ; else if (link_info.relocatable) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xr >> e${EMULATION_NAME}.c +echo ' ; else if (!config.text_read_only) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xbn >> e${EMULATION_NAME}.c +echo ' ; else if (!config.magic_demand_paged) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xn >> e${EMULATION_NAME}.c +echo ' ; else return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.x >> e${EMULATION_NAME}.c +echo '; }' >> e${EMULATION_NAME}.c + +else +# Scripts read from the filesystem. + +fragment <> e${EMULATION_NAME}.c +echo ' ; else if (link_info.relocatable) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xr >> e${EMULATION_NAME}.c +echo ' ; else if (!config.text_read_only) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xbn >> e${EMULATION_NAME}.c +echo ' ; else if (!config.magic_demand_paged) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xn >> e${EMULATION_NAME}.c +echo ' ; else return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.x >> e${EMULATION_NAME}.c +echo '; }' >> e${EMULATION_NAME}.c + +else +# Scripts read from the filesystem. + +fragment <e${EMULATION_NAME}.c # there, now line numbers match ;-) +fragment <the_bfd, + SH64_CRANGES_SECTION_NAME); + if (input_cranges != NULL) + einfo (" %I\n", f); + } + } + + command_line.relax = FALSE; + } + + /* We wouldn't need to do anything when there's already a .cranges + section (and have a return here), except that we need to set the + section flags right for output sections that *don't* need a + .cranges section. */ + } + + if (command_line.relax) + { + LANG_FOR_EACH_INPUT_STATEMENT (f) + { + if (bfd_get_flavour (f->the_bfd) == bfd_target_elf_flavour) + { + asection *isec; + for (isec = f->the_bfd->sections; + isec != NULL; + isec = isec->next) + { + if (elf_section_data (isec)->this_hdr.sh_flags + & (SHF_SH5_ISA32 | SHF_SH5_ISA32_MIXED)) + { + einfo (_("%P: Sorry, turning off relaxing: SHmedia sections present.\n")); + einfo (" %I\n", f); + command_line.relax = FALSE; + goto done_scanning_shmedia_sections; + } + } + } + } + } + done_scanning_shmedia_sections: + + /* For each non-empty input section in each output section, check if it + has the same SH64-specific flags. If some input section differs, we + need a .cranges section. */ + for (osec = link_info.output_bfd->sections; + osec != NULL; + osec = osec->next) + { + struct sh64_section_data *sh64_sec_data; + bfd_vma oflags_isa = 0; + bfd_vma iflags_isa = 0; + + if (bfd_get_flavour (link_info.output_bfd) != bfd_target_elf_flavour) + einfo (_("%FError: non-ELF output formats are not supported by this target's linker.\n")); + + sh64_sec_data = sh64_elf_section_data (osec)->sh64_info; + + /* Omit excluded or garbage-collected sections. */ + if (bfd_get_section_flags (link_info.output_bfd, osec) & SEC_EXCLUDE) + continue; + + /* Make sure we have the target section data initialized. */ + if (sh64_sec_data == NULL) + { + sh64_sec_data = xcalloc (1, sizeof (struct sh64_section_data)); + sh64_elf_section_data (osec)->sh64_info = sh64_sec_data; + } + + /* First find an input section so we have flags to compare with; the + flags in the output section are not valid. */ + { + LANG_FOR_EACH_INPUT_STATEMENT (f) + { + asection *isec; + + for (isec = f->the_bfd->sections; + isec != NULL; + isec = isec->next) + { + if (isec->output_section == osec + && isec->size != 0 + && (bfd_get_section_flags (isec->owner, isec) + & SEC_EXCLUDE) == 0) + { + oflags_isa + = (elf_section_data (isec)->this_hdr.sh_flags + & (SHF_SH5_ISA32 | SHF_SH5_ISA32_MIXED)); + goto break_1; + } + } + } + } + + break_1: + + /* Check that all input sections have the same contents-type flags + as the first input section. */ + { + LANG_FOR_EACH_INPUT_STATEMENT (f) + { + asection *isec; + + for (isec = f->the_bfd->sections; + isec != NULL; + isec = isec->next) + { + if (isec->output_section == osec + && isec->size != 0 + && (bfd_get_section_flags (isec->owner, isec) + & SEC_EXCLUDE) == 0) + { + iflags_isa + = (elf_section_data (isec)->this_hdr.sh_flags + & (SHF_SH5_ISA32 | SHF_SH5_ISA32_MIXED)); + + /* If flags don't agree, we need a .cranges section. + Create it here if it did not exist through input + sections. */ + if (iflags_isa != oflags_isa) + { + if (cranges == NULL) + { + /* This section will be *appended* to + sections, so the outer iteration will reach + it in due time and set + sh64_elf_section_data; no need to set it + specifically here. */ + cranges + = bfd_make_section (link_info.output_bfd, + SH64_CRANGES_SECTION_NAME); + if (cranges == NULL + || !bfd_set_section_flags (link_info.output_bfd, + cranges, + SEC_LINKER_CREATED + | SEC_KEEP + | SEC_HAS_CONTENTS + | SEC_DEBUGGING)) + einfo + (_("%P%E%F: Can't make .cranges section\n")); + } + + /* We don't need to look at more input sections, + and we know this section will have mixed + contents. */ + goto break_2; + } + } + } + } + } + + /* If we got here, then all input sections in this output section + have the same contents flag. Put that where we expect to see + contents flags. We don't need to do this for sections that will + need additional, linker-generated .cranges entries. */ + sh64_sec_data->contents_flags = iflags_isa; + + break_2: + ; + } +} + +/* Size up and extend the .cranges section, merging generated entries. */ + +static void +sh64_elf_${EMULATION_NAME}_after_allocation (void) +{ + bfd_vma new_cranges = 0; + bfd_vma cranges_growth = 0; + asection *osec; + bfd_byte *crangesp; + + asection *cranges = bfd_get_section_by_name (link_info.output_bfd, + SH64_CRANGES_SECTION_NAME); + + /* If this ever starts doing something, we will pick it up. */ + after_allocation_default (); + + /* If there is no .cranges section, it is because it was seen earlier on + that none was needed. Otherwise it must have been created then, or + be present in input. */ + if (cranges == NULL) + return; + + /* First, we set the ISA flags for each output section according to the + first non-discarded section. For each input section in osec, we + check if it has the same flags. If it does not, we set flags to mark + a mixed section (and exit the loop early). */ + for (osec = link_info.output_bfd->sections; + osec != NULL; + osec = osec->next) + { + bfd_vma oflags_isa = 0; + bfd_boolean need_check_cranges = FALSE; + + /* Omit excluded or garbage-collected sections. */ + if (bfd_get_section_flags (link_info.output_bfd, osec) & SEC_EXCLUDE) + continue; + + /* First find an input section so we have flags to compare with; the + flags in the output section are not valid. */ + { + LANG_FOR_EACH_INPUT_STATEMENT (f) + { + asection *isec; + + for (isec = f->the_bfd->sections; + isec != NULL; + isec = isec->next) + { + if (isec->output_section == osec + && isec->size != 0 + && (bfd_get_section_flags (isec->owner, isec) + & SEC_EXCLUDE) == 0) + { + oflags_isa + = (elf_section_data (isec)->this_hdr.sh_flags + & (SHF_SH5_ISA32 | SHF_SH5_ISA32_MIXED)); + goto break_1; + } + } + } + } + + break_1: + + /* Check that all input sections have the same contents-type flags + as the first input section. */ + { + LANG_FOR_EACH_INPUT_STATEMENT (f) + { + asection *isec; + + for (isec = f->the_bfd->sections; + isec != NULL; + isec = isec->next) + { + if (isec->output_section == osec + && isec->size != 0 + && (bfd_get_section_flags (isec->owner, isec) + & SEC_EXCLUDE) == 0) + { + bfd_vma iflags_isa + = (elf_section_data (isec)->this_hdr.sh_flags + & (SHF_SH5_ISA32 | SHF_SH5_ISA32_MIXED)); + + /* If flags don't agree, set the target-specific data + of the section to mark that this section needs to + be have .cranges section entries added. Don't + bother setting ELF section flags in output section; + they will be cleared later and will have to be + re-initialized before the linked file is written. */ + if (iflags_isa != oflags_isa) + { + oflags_isa = SHF_SH5_ISA32_MIXED; + + BFD_ASSERT (sh64_elf_section_data (osec)->sh64_info); + + sh64_elf_section_data (osec)->sh64_info->contents_flags + = SHF_SH5_ISA32_MIXED; + need_check_cranges = TRUE; + goto break_2; + } + } + } + } + } + + break_2: + + /* If there were no new ranges for this output section, we don't + need to iterate over the input sections to check how many are + needed. */ + if (! need_check_cranges) + continue; + + /* If we found a section with differing contents type, we need more + ranges to mark the sections that are not mixed (and already have + .cranges descriptors). Calculate the maximum number of new + entries here. We may merge some of them, so that number is not + final; it can shrink. */ + { + LANG_FOR_EACH_INPUT_STATEMENT (f) + { + asection *isec; + + for (isec = f->the_bfd->sections; + isec != NULL; + isec = isec->next) + { + if (isec->output_section == osec + && isec->size != 0 + && (bfd_get_section_flags (isec->owner, isec) + & SEC_EXCLUDE) == 0 + && ((elf_section_data (isec)->this_hdr.sh_flags + & (SHF_SH5_ISA32 | SHF_SH5_ISA32_MIXED)) + != SHF_SH5_ISA32_MIXED)) + new_cranges++; + } + } + } + } + + /* ldemul_after_allocation may be called twice. First directly from + lang_process, and the second time when lang_process calls ldemul_finish, + which calls gld${EMULATION_NAME}_finish, e.g. gldshelf32_finish, which + is defined in emultempl/elf32.em and calls ldemul_after_allocation, + if bfd_elf_discard_info returned true. */ + if (cranges->contents != NULL) + free (cranges->contents); + + BFD_ASSERT (sh64_elf_section_data (cranges)->sh64_info != NULL); + + /* Make sure we have .cranges in memory even if there were only + assembler-generated .cranges. */ + cranges_growth = new_cranges * SH64_CRANGE_SIZE; + cranges->contents = xcalloc (cranges->size + cranges_growth, 1); + bfd_set_section_flags (cranges->owner, cranges, + bfd_get_section_flags (cranges->owner, cranges) + | SEC_IN_MEMORY); + + /* If we don't need to grow the .cranges section beyond what was in the + input sections, we have nothing more to do here. We then only got + here because there was a .cranges section coming from input. Zero + out the number of generated .cranges. */ + if (new_cranges == 0) + { + sh64_elf_section_data (cranges)->sh64_info->cranges_growth = 0; + return; + } + + crangesp = cranges->contents + cranges->size; + + /* Now pass over the sections again, and make reloc orders for the new + .cranges entries. Constants are set as we go. */ + for (osec = link_info.output_bfd->sections; + osec != NULL; + osec = osec->next) + { + struct bfd_link_order *cr_addr_order = NULL; + enum sh64_elf_cr_type last_cr_type = CRT_NONE; + bfd_vma last_cr_size = 0; + bfd_vma continuation_vma = 0; + + /* Omit excluded or garbage-collected sections, and output sections + which were not marked as needing further processing. */ + if ((bfd_get_section_flags (link_info.output_bfd, osec) & SEC_EXCLUDE) != 0 + || (sh64_elf_section_data (osec)->sh64_info->contents_flags + != SHF_SH5_ISA32_MIXED)) + continue; + + { + LANG_FOR_EACH_INPUT_STATEMENT (f) + { + asection *isec; + + for (isec = f->the_bfd->sections; + isec != NULL; + isec = isec->next) + { + /* Allow only sections that have (at least initially) a + non-zero size, and are not excluded, and are not marked + as containing mixed data, thus already having .cranges + entries. */ + if (isec->output_section == osec + && isec->size != 0 + && (bfd_get_section_flags (isec->owner, isec) + & SEC_EXCLUDE) == 0 + && ((elf_section_data (isec)->this_hdr.sh_flags + & (SHF_SH5_ISA32 | SHF_SH5_ISA32_MIXED)) + != SHF_SH5_ISA32_MIXED)) + { + enum sh64_elf_cr_type cr_type; + bfd_vma cr_size; + bfd_vma isa_flags + = (elf_section_data (isec)->this_hdr.sh_flags + & (SHF_SH5_ISA32 | SHF_SH5_ISA32_MIXED)); + + if (isa_flags == SHF_SH5_ISA32) + cr_type = CRT_SH5_ISA32; + else if ((bfd_get_section_flags (isec->owner, isec) + & SEC_CODE) == 0) + cr_type = CRT_DATA; + else + cr_type = CRT_SH5_ISA16; + + cr_size = isec->size; + + /* Sections can be empty, like .text in a file that + only contains other sections. Ranges shouldn't be + emitted for them. This can presumably happen after + relaxing and is not be caught at the "raw size" + test above. */ + if (cr_size == 0) + continue; + + /* See if this is a continuation of the previous range + for the same output section. If so, just change + the size of the last range and continue. */ + if (cr_type == last_cr_type + && (continuation_vma + == osec->vma + isec->output_offset)) + { + last_cr_size += cr_size; + bfd_put_32 (link_info.output_bfd, last_cr_size, + crangesp - SH64_CRANGE_SIZE + + SH64_CRANGE_CR_SIZE_OFFSET); + + continuation_vma += cr_size; + continue; + } + + /* If we emit relocatable contents, we need a + relocation for the start address. */ + if (link_info.relocatable || link_info.emitrelocations) + { + /* FIXME: We could perhaps use lang_add_reloc and + friends here, but I'm not really sure that + would leave us free to do some optimizations + later. */ + cr_addr_order + = bfd_new_link_order (link_info.output_bfd, cranges); + + if (cr_addr_order == NULL) + { + einfo (_("%P%F: bfd_new_link_order failed\n")); + return; + } + + cr_addr_order->type = bfd_section_reloc_link_order; + cr_addr_order->offset + = (cranges->output_offset + + crangesp + SH64_CRANGE_CR_ADDR_OFFSET + - cranges->contents); + cr_addr_order->size = 4; + cr_addr_order->u.reloc.p + = xmalloc (sizeof (struct bfd_link_order_reloc)); + + cr_addr_order->u.reloc.p->reloc = BFD_RELOC_32; + cr_addr_order->u.reloc.p->u.section = osec; + + /* Since SH, unlike normal RELA-targets, uses a + "partial inplace" REL-like relocation for this, + we put the addend in the contents and specify 0 + for the reloc. */ + bfd_put_32 (link_info.output_bfd, isec->output_offset, + crangesp + SH64_CRANGE_CR_ADDR_OFFSET); + cr_addr_order->u.reloc.p->addend = 0; + } + else + bfd_put_32 (link_info.output_bfd, + osec->vma + isec->output_offset, + crangesp + SH64_CRANGE_CR_ADDR_OFFSET); + + /* If we could make a reloc for cr_size we would do + it, but we would have to have a symbol for the size + of the _input_ section and there's no way to + generate that. */ + bfd_put_32 (link_info.output_bfd, cr_size, + crangesp + SH64_CRANGE_CR_SIZE_OFFSET); + + bfd_put_16 (link_info.output_bfd, cr_type, + crangesp + SH64_CRANGE_CR_TYPE_OFFSET); + + last_cr_type = cr_type; + last_cr_size = cr_size; + continuation_vma + = osec->vma + isec->output_offset + cr_size; + crangesp += SH64_CRANGE_SIZE; + } + } + } + } + } + + /* The .cranges section will have this size, no larger or smaller. + Since relocs (if relocatable linking) will be emitted into the + "extended" size, we must set the raw size to the total. We have to + keep track of the number of new .cranges entries. + + Sorting before writing is done by sh64_elf_final_write_processing. */ + + sh64_elf_section_data (cranges)->sh64_info->cranges_growth + = crangesp - cranges->contents - cranges->size; + cranges->size = crangesp - cranges->contents; + cranges->rawsize = cranges->size; +} Index: elf32.em =================================================================== --- elf32.em (nonexistent) +++ elf32.em (revision 816) @@ -0,0 +1,2339 @@ +# This shell script emits a C file. -*- C -*- +# It does some substitutions. +# This file is now misnamed, because it supports both 32 bit and 64 bit +# ELF emulations. +test -z "${ELFSIZE}" && ELFSIZE=32 +if [ -z "$MACHINE" ]; then + OUTPUT_ARCH=${ARCH} +else + OUTPUT_ARCH=${ARCH}:${MACHINE} +fi +fragment < + ELF support by Ian Lance Taylor + + This file is part of the GNU Binutils. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#define TARGET_IS_${EMULATION_NAME} + +#include "sysdep.h" +#include "bfd.h" +#include "libiberty.h" +#include "safe-ctype.h" +#include "getopt.h" +#include "md5.h" +#include "sha1.h" +#include + +#include "bfdlink.h" + +#include "ld.h" +#include "ldmain.h" +#include "ldmisc.h" +#include "ldexp.h" +#include "ldlang.h" +#include "ldfile.h" +#include "ldemul.h" +#include +#include "elf/common.h" +#include "elf-bfd.h" + +/* Declare functions used by various EXTRA_EM_FILEs. */ +static void gld${EMULATION_NAME}_before_parse (void); +static void gld${EMULATION_NAME}_after_open (void); +static void gld${EMULATION_NAME}_before_allocation (void); +static bfd_boolean gld${EMULATION_NAME}_place_orphan (asection *s); +static void gld${EMULATION_NAME}_finish (void); + +EOF + +if [ "x${USE_LIBPATH}" = xyes ] ; then + case ${target} in + *-*-linux-* | *-*-k*bsd*-*) + fragment < +#endif +EOF + ;; + esac +fi + +# Import any needed special functions and/or overrides. +# +source_em ${srcdir}/emultempl/elf-generic.em +if test -n "$EXTRA_EM_FILE" ; then + source_em ${srcdir}/emultempl/${EXTRA_EM_FILE}.em +fi + +# Functions in this file can be overridden by setting the LDEMUL_* shell +# variables. If the name of the overriding function is the same as is +# defined in this file, then don't output this file's version. +# If a different overriding name is given then output the standard function +# as presumably it is called from the overriding function. +# +if test x"$LDEMUL_BEFORE_PARSE" != xgld"$EMULATION_NAME"_before_parse; then +fragment <as_needed) + class = DYN_AS_NEEDED; + + /* Tell the ELF linker that we don't want the output file to have a + DT_NEEDED entry for any dynamic library in DT_NEEDED tags from + this file at all. */ + if (!entry->add_needed) + class |= DYN_NO_ADD_NEEDED; + + if (entry->just_syms_flag + && (bfd_get_file_flags (entry->the_bfd) & DYNAMIC) != 0) + einfo (_("%P%F: --just-symbols may not be used on DSO: %B\n"), + entry->the_bfd); + + if (!class + || (bfd_get_file_flags (entry->the_bfd) & DYNAMIC) == 0) + return FALSE; + + bfd_elf_set_dyn_lib_class (entry->the_bfd, class); + + /* Continue on with normal load_symbols processing. */ + return FALSE; +} +EOF +fi + +fragment <the_bfd == NULL + || (bfd_get_file_flags (s->the_bfd) & DYNAMIC) == 0) + return; + + soname = bfd_elf_get_dt_soname (s->the_bfd); + if (soname == NULL) + soname = lbasename (bfd_get_filename (s->the_bfd)); + + for (l = global_vercheck_needed; l != NULL; l = l->next) + { + const char *suffix; + + if (strcmp (soname, l->name) == 0) + { + /* Probably can't happen, but it's an easy check. */ + continue; + } + + if (strchr (l->name, '/') != NULL) + continue; + + suffix = strstr (l->name, ".so."); + if (suffix == NULL) + continue; + + suffix += sizeof ".so." - 1; + + if (strncmp (soname, l->name, suffix - l->name) == 0) + { + /* Here we know that S is a dynamic object FOO.SO.VER1, and + the object we are considering needs a dynamic object + FOO.SO.VER2, and VER1 and VER2 are different. This + appears to be a version mismatch, so we tell the caller + to try a different version of this library. */ + global_vercheck_failed = TRUE; + return; + } + } +} + + +/* See if an input file matches a DT_NEEDED entry by running stat on + the file. */ + +static void +gld${EMULATION_NAME}_stat_needed (lang_input_statement_type *s) +{ + struct stat st; + const char *suffix; + const char *soname; + + if (global_found != NULL) + return; + if (s->the_bfd == NULL) + return; + + /* If this input file was an as-needed entry, and wasn't found to be + needed at the stage it was linked, then don't say we have loaded it. */ + if ((bfd_elf_get_dyn_lib_class (s->the_bfd) & DYN_AS_NEEDED) != 0) + return; + + if (bfd_stat (s->the_bfd, &st) != 0) + { + einfo ("%P:%B: bfd_stat failed: %E\n", s->the_bfd); + return; + } + + /* Some operating systems, e.g. Windows, do not provide a meaningful + st_ino; they always set it to zero. (Windows does provide a + meaningful st_dev.) Do not indicate a duplicate library in that + case. While there is no guarantee that a system that provides + meaningful inode numbers will never set st_ino to zero, this is + merely an optimization, so we do not need to worry about false + negatives. */ + if (st.st_dev == global_stat.st_dev + && st.st_ino == global_stat.st_ino + && st.st_ino != 0) + { + global_found = s; + return; + } + + /* We issue a warning if it looks like we are including two + different versions of the same shared library. For example, + there may be a problem if -lc picks up libc.so.6 but some other + shared library has a DT_NEEDED entry of libc.so.5. This is a + heuristic test, and it will only work if the name looks like + NAME.so.VERSION. FIXME: Depending on file names is error-prone. + If we really want to issue warnings about mixing version numbers + of shared libraries, we need to find a better way. */ + + if (strchr (global_needed->name, '/') != NULL) + return; + suffix = strstr (global_needed->name, ".so."); + if (suffix == NULL) + return; + suffix += sizeof ".so." - 1; + + soname = bfd_elf_get_dt_soname (s->the_bfd); + if (soname == NULL) + soname = lbasename (s->filename); + + if (strncmp (soname, global_needed->name, suffix - global_needed->name) == 0) + einfo ("%P: warning: %s, needed by %B, may conflict with %s\n", + global_needed->name, global_needed->by, soname); +} + +struct dt_needed +{ + bfd *by; + const char *name; +}; + +/* This function is called for each possible name for a dynamic object + named by a DT_NEEDED entry. The FORCE parameter indicates whether + to skip the check for a conflicting version. */ + +static bfd_boolean +gld${EMULATION_NAME}_try_needed (struct dt_needed *needed, + int force) +{ + bfd *abfd; + const char *name = needed->name; + const char *soname; + int class; + + abfd = bfd_openr (name, bfd_get_target (link_info.output_bfd)); + if (abfd == NULL) + return FALSE; + if (! bfd_check_format (abfd, bfd_object)) + { + bfd_close (abfd); + return FALSE; + } + if ((bfd_get_file_flags (abfd) & DYNAMIC) == 0) + { + bfd_close (abfd); + return FALSE; + } + + /* For DT_NEEDED, they have to match. */ + if (abfd->xvec != link_info.output_bfd->xvec) + { + bfd_close (abfd); + return FALSE; + } + + /* Check whether this object would include any conflicting library + versions. If FORCE is set, then we skip this check; we use this + the second time around, if we couldn't find any compatible + instance of the shared library. */ + + if (! force) + { + struct bfd_link_needed_list *needed; + + if (! bfd_elf_get_bfd_needed_list (abfd, &needed)) + einfo ("%F%P:%B: bfd_elf_get_bfd_needed_list failed: %E\n", abfd); + + if (needed != NULL) + { + global_vercheck_needed = needed; + global_vercheck_failed = FALSE; + lang_for_each_input_file (gld${EMULATION_NAME}_vercheck); + if (global_vercheck_failed) + { + bfd_close (abfd); + /* Return FALSE to force the caller to move on to try + another file on the search path. */ + return FALSE; + } + + /* But wait! It gets much worse. On Linux, if a shared + library does not use libc at all, we are supposed to skip + it the first time around in case we encounter a shared + library later on with the same name which does use the + version of libc that we want. This is much too horrible + to use on any system other than Linux. */ + +EOF +case ${target} in + *-*-linux-* | *-*-k*bsd*-*) + fragment <next) + if (CONST_STRNEQ (l->name, "libc.so")) + break; + if (l == NULL) + { + bfd_close (abfd); + return FALSE; + } + } + +EOF + ;; +esac +fragment <filename); + + if (trace_file_tries) + info_msg (_("found %s at %s\n"), soname, name); + + global_found = NULL; + lang_for_each_input_file (gld${EMULATION_NAME}_stat_needed); + if (global_found != NULL) + { + /* Return TRUE to indicate that we found the file, even though + we aren't going to do anything with it. */ + return TRUE; + } + + /* Specify the soname to use. */ + bfd_elf_set_dt_needed_name (abfd, soname); + + /* Tell the ELF linker that we don't want the output file to have a + DT_NEEDED entry for this file, unless it is used to resolve + references in a regular object. */ + class = DYN_DT_NEEDED; + + /* Tell the ELF linker that we don't want the output file to have a + DT_NEEDED entry for this file at all if the entry is from a file + with DYN_NO_ADD_NEEDED. */ + if (needed->by != NULL + && (bfd_elf_get_dyn_lib_class (needed->by) & DYN_NO_ADD_NEEDED) != 0) + class |= DYN_NO_NEEDED | DYN_NO_ADD_NEEDED; + + bfd_elf_set_dyn_lib_class (abfd, class); + + /* Add this file into the symbol table. */ + if (! bfd_link_add_symbols (abfd, &link_info)) + einfo ("%F%B: could not read symbols: %E\n", abfd); + + return TRUE; +} + + +/* Search for a needed file in a path. */ + +static bfd_boolean +gld${EMULATION_NAME}_search_needed (const char *path, + struct dt_needed *n, int force) +{ + const char *s; + const char *name = n->name; + size_t len; + struct dt_needed needed; + + if (name[0] == '/') + return gld${EMULATION_NAME}_try_needed (n, force); + + if (path == NULL || *path == '\0') + return FALSE; + + needed.by = n->by; + needed.name = n->name; + + len = strlen (name); + while (1) + { + char *filename, *sset; + + s = strchr (path, config.rpath_separator); + if (s == NULL) + s = path + strlen (path); + + filename = (char *) xmalloc (s - path + len + 2); + if (s == path) + sset = filename; + else + { + memcpy (filename, path, s - path); + filename[s - path] = '/'; + sset = filename + (s - path) + 1; + } + strcpy (sset, name); + + needed.name = filename; + if (gld${EMULATION_NAME}_try_needed (&needed, force)) + return TRUE; + + free (filename); + + if (*s == '\0') + break; + path = s + 1; + } + + return FALSE; +} + +EOF +if [ "x${USE_LIBPATH}" = xyes ] ; then + fragment < +#else +#include "elf-hints-local.h" +#endif + +static bfd_boolean +gld${EMULATION_NAME}_check_ld_elf_hints (const char *name, int force) +{ + static bfd_boolean initialized; + static char *ld_elf_hints; + struct dt_needed needed; + + if (!initialized) + { + FILE *f; + char *tmppath; + + tmppath = concat (ld_sysroot, _PATH_ELF_HINTS, (const char *) NULL); + f = fopen (tmppath, FOPEN_RB); + free (tmppath); + if (f != NULL) + { + struct elfhints_hdr hdr; + + if (fread (&hdr, 1, sizeof (hdr), f) == sizeof (hdr) + && hdr.magic == ELFHINTS_MAGIC + && hdr.version == 1) + { + if (fseek (f, hdr.strtab + hdr.dirlist, SEEK_SET) != -1) + { + char *b; + + b = xmalloc (hdr.dirlistlen + 1); + if (fread (b, 1, hdr.dirlistlen + 1, f) == + hdr.dirlistlen + 1) + ld_elf_hints = gld${EMULATION_NAME}_add_sysroot (b); + + free (b); + } + } + fclose (f); + } + + initialized = TRUE; + } + + if (ld_elf_hints == NULL) + return FALSE; + + needed.by = NULL; + needed.name = name; + return gld${EMULATION_NAME}_search_needed (ld_elf_hints, & needed, + force); +} +EOF + # FreeBSD + ;; + + *-*-linux-* | *-*-k*bsd*-*) + fragment <path == NULL) + { + info->alloc = p - dir + 1 + 256; + info->path = xmalloc (info->alloc); + info->len = 0; + } + else + { + if (info->len + 1 + (p - dir) >= info->alloc) + { + info->alloc += p - dir + 256; + info->path = xrealloc (info->path, info->alloc); + } + info->path[info->len++] = config.rpath_separator; + } + memcpy (info->path + info->len, dir, p - dir); + info->len += p - dir; + info->path[info->len] = '\0'; + } + } + while (! feof (f)); + free (line); + fclose (f); + return TRUE; +} + +static bfd_boolean +gld${EMULATION_NAME}_check_ld_so_conf (const char *name, int force) +{ + static bfd_boolean initialized; + static char *ld_so_conf; + struct dt_needed needed; + + if (! initialized) + { + char *tmppath; + struct gld${EMULATION_NAME}_ld_so_conf info; + + info.path = NULL; + info.len = info.alloc = 0; + tmppath = concat (ld_sysroot, "${prefix}/etc/ld.so.conf", + (const char *) NULL); + if (!gld${EMULATION_NAME}_parse_ld_so_conf (&info, tmppath)) + { + free (tmppath); + tmppath = concat (ld_sysroot, "/etc/ld.so.conf", + (const char *) NULL); + gld${EMULATION_NAME}_parse_ld_so_conf (&info, tmppath); + } + free (tmppath); + + if (info.path) + { + char *d = gld${EMULATION_NAME}_add_sysroot (info.path); + free (info.path); + ld_so_conf = d; + } + initialized = TRUE; + } + + if (ld_so_conf == NULL) + return FALSE; + + + needed.by = NULL; + needed.name = name; + return gld${EMULATION_NAME}_search_needed (ld_so_conf, &needed, force); +} + +EOF + # Linux + ;; + esac +fi +fragment <the_bfd) + & DYN_AS_NEEDED) == 0) + return; + + if (s->filename == NULL || s->the_bfd == NULL) + return; + + /* Don't look for a second non-loaded as-needed lib. */ + if (global_found != NULL + && (bfd_elf_get_dyn_lib_class (s->the_bfd) & DYN_AS_NEEDED) != 0) + return; + + if (strcmp (s->filename, global_needed->name) == 0) + { + global_found = s; + return; + } + + if (s->search_dirs_flag) + { + const char *f = strrchr (s->filename, '/'); + if (f != NULL + && strcmp (f + 1, global_needed->name) == 0) + { + global_found = s; + return; + } + } + + soname = bfd_elf_get_dt_soname (s->the_bfd); + if (soname != NULL + && strcmp (soname, global_needed->name) == 0) + { + global_found = s; + return; + } +} + +EOF + +if test x"$LDEMUL_AFTER_OPEN" != xgld"$EMULATION_NAME"_after_open; then +fragment <emit_note_gnu_build_id; + bfd_size_type size; + + abfd = abfd; + + size = offsetof (Elf_External_Note, name[sizeof "GNU"]); + size = (size + 3) & -(bfd_size_type) 4; + + if (!strcmp (style, "md5") || !strcmp (style, "uuid")) + size += 128 / 8; + else if (!strcmp (style, "sha1")) + size += 160 / 8; + else if (!strncmp (style, "0x", 2)) + { + /* ID is in string form (hex). Convert to bits. */ + const char *id = style + 2; + do + { + if (ISXDIGIT (id[0]) && ISXDIGIT (id[1])) + { + ++size; + id += 2; + } + else if (*id == '-' || *id == ':') + ++id; + else + { + size = 0; + break; + } + } while (*id != '\0'); + } + else + size = 0; + + return size; +} + +static unsigned char +read_hex (const char xdigit) +{ + if (ISDIGIT (xdigit)) + return xdigit - '0'; + if (ISUPPER (xdigit)) + return xdigit - 'A' + 0xa; + if (ISLOWER (xdigit)) + return xdigit - 'a' + 0xa; + abort (); + return 0; +} + +struct build_id_info +{ + const char *style; + asection *sec; +}; + +static bfd_boolean +gld${EMULATION_NAME}_write_build_id_section (bfd *abfd) +{ + const struct elf_backend_data *bed = get_elf_backend_data (abfd); + struct build_id_info *info = + elf_tdata (abfd)->after_write_object_contents_info; + asection *asec; + Elf_Internal_Shdr *i_shdr; + unsigned char *contents, *id_bits; + bfd_size_type size; + Elf_External_Note *e_note; + + asec = info->sec; + if (bfd_is_abs_section (asec->output_section)) + { + einfo (_("%P: warning: .note.gnu.build-id section discarded," + " --build-id ignored.\n")); + return TRUE; + } + i_shdr = &elf_section_data (asec->output_section)->this_hdr; + + if (i_shdr->contents == NULL) + { + if (asec->contents == NULL) + asec->contents = xmalloc (asec->size); + contents = asec->contents; + } + else + contents = i_shdr->contents + asec->output_offset; + + e_note = (void *) contents; + size = offsetof (Elf_External_Note, name[sizeof "GNU"]); + size = (size + 3) & -(bfd_size_type) 4; + id_bits = contents + size; + size = asec->size - size; + + bfd_h_put_32 (abfd, sizeof "GNU", &e_note->namesz); + bfd_h_put_32 (abfd, size, &e_note->descsz); + bfd_h_put_32 (abfd, NT_GNU_BUILD_ID, &e_note->type); + memcpy (e_note->name, "GNU", sizeof "GNU"); + + if (!strcmp (info->style, "md5")) + { + struct md5_ctx ctx; + md5_init_ctx (&ctx); + if (bed->s->checksum_contents (abfd, + (void (*) (const void *, size_t, void *)) + &md5_process_bytes, + &ctx)) + md5_finish_ctx (&ctx, id_bits); + else + return FALSE; + } + else if (!strcmp (info->style, "sha1")) + { + struct sha1_ctx ctx; + sha1_init_ctx (&ctx); + if (bed->s->checksum_contents (abfd, + (void (*) (const void *, size_t, void *)) + &sha1_process_bytes, + &ctx)) + sha1_finish_ctx (&ctx, id_bits); + else + return FALSE; + } + else if (!strcmp (info->style, "uuid")) + { + int n; + int fd = open ("/dev/urandom", O_RDONLY); + if (fd < 0) + return FALSE; + n = read (fd, id_bits, size); + close (fd); + if (n < (int) size) + return FALSE; + } + else if (!strncmp (info->style, "0x", 2)) + { + /* ID is in string form (hex). Convert to bits. */ + const char *id = info->style + 2; + size_t n = 0; + do + { + if (ISXDIGIT (id[0]) && ISXDIGIT (id[1])) + { + id_bits[n] = read_hex (*id++) << 4; + id_bits[n++] |= read_hex (*id++); + } + else if (*id == '-' || *id == ':') + ++id; + else + abort (); /* Should have been validated earlier. */ + } while (*id != '\0'); + } + else + abort (); /* Should have been validated earlier. */ + + size = asec->size; + return (bfd_seek (abfd, + i_shdr->sh_offset + asec->output_offset, SEEK_SET) == 0 + && bfd_bwrite (contents, size, abfd) == size); +} + + +/* This is called after all the input files have been opened. */ + +static void +gld${EMULATION_NAME}_after_open (void) +{ + struct bfd_link_needed_list *needed, *l; + struct elf_link_hash_table *htab; + + htab = elf_hash_table (&link_info); + if (!is_elf_hash_table (htab)) + return; + + if (link_info.emit_note_gnu_build_id) + { + bfd *abfd; + asection *s; + bfd_size_type size; + + abfd = link_info.input_bfds; + + size = gld${EMULATION_NAME}_id_note_section_size (abfd, &link_info); + if (size == 0) + { + einfo ("%P: warning: unrecognized --build-id style ignored.\n"); + free (link_info.emit_note_gnu_build_id); + link_info.emit_note_gnu_build_id = NULL; + } + else + { + s = bfd_make_section_with_flags (abfd, ".note.gnu.build-id", + SEC_ALLOC | SEC_LOAD + | SEC_IN_MEMORY | SEC_LINKER_CREATED + | SEC_READONLY | SEC_DATA); + if (s != NULL && bfd_set_section_alignment (abfd, s, 2)) + { + struct elf_obj_tdata *t = elf_tdata (link_info.output_bfd); + struct build_id_info *b = xmalloc (sizeof *b); + b->style = link_info.emit_note_gnu_build_id; + b->sec = s; + elf_section_type (s) = SHT_NOTE; + s->size = size; + t->after_write_object_contents + = &gld${EMULATION_NAME}_write_build_id_section; + t->after_write_object_contents_info = b; + } + else + { + einfo ("%P: warning: Cannot create .note.gnu.build-id section," + " --build-id ignored.\n"); + free (link_info.emit_note_gnu_build_id); + link_info.emit_note_gnu_build_id = NULL; + } + } + } + + if (link_info.relocatable) + return; + + if (link_info.eh_frame_hdr + && !link_info.traditional_format) + { + bfd *abfd; + asection *s; + + for (abfd = link_info.input_bfds; abfd; abfd = abfd->link_next) + { + s = bfd_get_section_by_name (abfd, ".eh_frame"); + if (s && s->size > 8 && !bfd_is_abs_section (s->output_section)) + break; + } + if (abfd) + { + const struct elf_backend_data *bed; + + bed = get_elf_backend_data (abfd); + s = bfd_make_section_with_flags (abfd, ".eh_frame_hdr", + bed->dynamic_sec_flags + | SEC_READONLY); + if (s != NULL + && bfd_set_section_alignment (abfd, s, 2)) + htab->eh_info.hdr_sec = s; + else + einfo ("%P: warning: Cannot create .eh_frame_hdr section," + " --eh-frame-hdr ignored.\n"); + } + } + + /* Get the list of files which appear in DT_NEEDED entries in + dynamic objects included in the link (often there will be none). + For each such file, we want to track down the corresponding + library, and include the symbol table in the link. This is what + the runtime dynamic linker will do. Tracking the files down here + permits one dynamic object to include another without requiring + special action by the person doing the link. Note that the + needed list can actually grow while we are stepping through this + loop. */ + if (!link_info.executable) + return; + needed = bfd_elf_get_needed_list (link_info.output_bfd, &link_info); + for (l = needed; l != NULL; l = l->next) + { + struct bfd_link_needed_list *ll; + struct dt_needed n, nn; + int force; + + /* If the lib that needs this one was --as-needed and wasn't + found to be needed, then this lib isn't needed either. */ + if (l->by != NULL + && (bfd_elf_get_dyn_lib_class (l->by) & DYN_AS_NEEDED) != 0) + continue; + + /* If we've already seen this file, skip it. */ + for (ll = needed; ll != l; ll = ll->next) + if ((ll->by == NULL + || (bfd_elf_get_dyn_lib_class (ll->by) & DYN_AS_NEEDED) == 0) + && strcmp (ll->name, l->name) == 0) + break; + if (ll != l) + continue; + + /* See if this file was included in the link explicitly. */ + global_needed = l; + global_found = NULL; + lang_for_each_input_file (gld${EMULATION_NAME}_check_needed); + if (global_found != NULL + && (bfd_elf_get_dyn_lib_class (global_found->the_bfd) + & DYN_AS_NEEDED) == 0) + continue; + + n.by = l->by; + n.name = l->name; + nn.by = l->by; + if (trace_file_tries) + info_msg (_("%s needed by %B\n"), l->name, l->by); + + /* As-needed libs specified on the command line (or linker script) + take priority over libs found in search dirs. */ + if (global_found != NULL) + { + nn.name = global_found->filename; + if (gld${EMULATION_NAME}_try_needed (&nn, TRUE)) + continue; + } + + /* We need to find this file and include the symbol table. We + want to search for the file in the same way that the dynamic + linker will search. That means that we want to use + rpath_link, rpath, then the environment variable + LD_LIBRARY_PATH (native only), then the DT_RPATH/DT_RUNPATH + entries (native only), then the linker script LIB_SEARCH_DIRS. + We do not search using the -L arguments. + + We search twice. The first time, we skip objects which may + introduce version mismatches. The second time, we force + their use. See gld${EMULATION_NAME}_vercheck comment. */ + for (force = 0; force < 2; force++) + { + size_t len; + search_dirs_type *search; +EOF +if [ "x${NATIVE}" = xyes ] ; then +fragment <next) + { + char *tmpname = gld${EMULATION_NAME}_add_sysroot (rp->name); + found = (rp->by == l->by + && gld${EMULATION_NAME}_search_needed (tmpname, + &n, + force)); + free (tmpname); + } + if (found) + break; + +EOF +fi +if [ "x${USE_LIBPATH}" = xyes ] ; then + case ${target} in + *-*-freebsd* | *-*-dragonfly*) + fragment <name, force)) + break; +EOF + # FreeBSD + ;; + + *-*-linux-* | *-*-k*bsd*-*) + # Linux + fragment <name, force)) + break; + +EOF + ;; + esac +fi +fragment <name); + for (search = search_head; search != NULL; search = search->next) + { + char *filename; + + if (search->cmdline) + continue; + filename = (char *) xmalloc (strlen (search->name) + len + 2); + sprintf (filename, "%s/%s", search->name, l->name); + nn.name = filename; + if (gld${EMULATION_NAME}_try_needed (&nn, force)) + break; + free (filename); + } + if (search != NULL) + break; +EOF +fragment <name, l->by); + } +} + +EOF +fi + +fragment <type.node_class) + { + case etree_provide: + provide = TRUE; + /* Fall thru */ + case etree_assign: + /* We call record_link_assignment even if the symbol is defined. + This is because if it is defined by a dynamic object, we + actually want to use the value defined by the linker script, + not the value from the dynamic object (because we are setting + symbols like etext). If the symbol is defined by a regular + object, then, as it happens, calling record_link_assignment + will do no harm. */ + if (strcmp (exp->assign.dst, ".") != 0) + { + if (!bfd_elf_record_link_assignment (link_info.output_bfd, + &link_info, + exp->assign.dst, provide, + exp->assign.hidden)) + einfo ("%P%F: failed to record assignment to %s: %E\n", + exp->assign.dst); + } + gld${EMULATION_NAME}_find_exp_assignment (exp->assign.src); + break; + + case etree_binary: + gld${EMULATION_NAME}_find_exp_assignment (exp->binary.lhs); + gld${EMULATION_NAME}_find_exp_assignment (exp->binary.rhs); + break; + + case etree_trinary: + gld${EMULATION_NAME}_find_exp_assignment (exp->trinary.cond); + gld${EMULATION_NAME}_find_exp_assignment (exp->trinary.lhs); + gld${EMULATION_NAME}_find_exp_assignment (exp->trinary.rhs); + break; + + case etree_unary: + gld${EMULATION_NAME}_find_exp_assignment (exp->unary.child); + break; + + default: + break; + } +} + + +/* This is called by the before_allocation routine via + lang_for_each_statement. It locates any assignment statements, and + tells the ELF backend about them, in case they are assignments to + symbols which are referred to by dynamic objects. */ + +static void +gld${EMULATION_NAME}_find_statement_assignment (lang_statement_union_type *s) +{ + if (s->header.type == lang_assignment_statement_enum) + gld${EMULATION_NAME}_find_exp_assignment (s->assignment_statement.exp); +} + +EOF + +if test x"$LDEMUL_BEFORE_ALLOCATION" != xgld"$EMULATION_NAME"_before_allocation; then + if test x"${ELF_INTERPRETER_NAME+set}" = xset; then + ELF_INTERPRETER_SET_DEFAULT=" + if (sinterp != NULL) + { + sinterp->contents = (unsigned char *) ${ELF_INTERPRETER_NAME}; + sinterp->size = strlen ((char *) sinterp->contents) + 1; + } + +" + else + ELF_INTERPRETER_SET_DEFAULT= + fi +fragment <type == bfd_link_elf_hash_table) + _bfd_elf_tls_setup (link_info.output_bfd, &link_info); + + /* If we are going to make any variable assignments, we need to let + the ELF backend know about them in case the variables are + referred to by dynamic objects. */ + lang_for_each_statement (gld${EMULATION_NAME}_find_statement_assignment); + + /* Let the ELF backend work out the sizes of any sections required + by dynamic linking. */ + rpath = command_line.rpath; + if (rpath == NULL) + rpath = (const char *) getenv ("LD_RUN_PATH"); + if (! (bfd_elf_size_dynamic_sections + (link_info.output_bfd, command_line.soname, rpath, + command_line.filter_shlib, + (const char * const *) command_line.auxiliary_filters, + &link_info, &sinterp, lang_elf_version_info))) + einfo ("%P%F: failed to set dynamic section sizes: %E\n"); + +${ELF_INTERPRETER_SET_DEFAULT} + /* Let the user override the dynamic linker we are using. */ + if (command_line.interpreter != NULL + && sinterp != NULL) + { + sinterp->contents = (bfd_byte *) command_line.interpreter; + sinterp->size = strlen (command_line.interpreter) + 1; + } + + /* Look for any sections named .gnu.warning. As a GNU extensions, + we treat such sections as containing warning messages. We print + out the warning message, and then zero out the section size so + that it does not get copied into the output file. */ + + { + LANG_FOR_EACH_INPUT_STATEMENT (is) + { + asection *s; + bfd_size_type sz; + char *msg; + bfd_boolean ret; + + if (is->just_syms_flag) + continue; + + s = bfd_get_section_by_name (is->the_bfd, ".gnu.warning"); + if (s == NULL) + continue; + + sz = s->size; + msg = xmalloc ((size_t) (sz + 1)); + if (! bfd_get_section_contents (is->the_bfd, s, msg, + (file_ptr) 0, sz)) + einfo ("%F%B: Can't read contents of section .gnu.warning: %E\n", + is->the_bfd); + msg[sz] = '\0'; + ret = link_info.callbacks->warning (&link_info, msg, + (const char *) NULL, + is->the_bfd, (asection *) NULL, + (bfd_vma) 0); + ASSERT (ret); + free (msg); + + /* Clobber the section size, so that we don't waste space + copying the warning into the output file. If we've already + sized the output section, adjust its size. The adjustment + is on rawsize because targets that size sections early will + have called lang_reset_memory_regions after sizing. */ + if (s->output_section != NULL + && s->output_section->rawsize >= s->size) + s->output_section->rawsize -= s->size; + + s->size = 0; + + /* Also set SEC_EXCLUDE, so that local symbols defined in the + warning section don't get copied to the output. */ + s->flags |= SEC_EXCLUDE | SEC_KEEP; + } + } + + before_allocation_default (); + + if (!bfd_elf_size_dynsym_hash_dynstr (link_info.output_bfd, &link_info)) + einfo ("%P%F: failed to set dynamic section sizes: %E\n"); +} + +EOF +fi + +if test x"$LDEMUL_OPEN_DYNAMIC_ARCHIVE" != xgld"$EMULATION_NAME"_open_dynamic_archive; then +fragment <is_archive) + return FALSE; + + filename = entry->filename; + + /* This allocates a few bytes too many when EXTRA_SHLIB_EXTENSION + is defined, but it does not seem worth the headache to optimize + away those two bytes of space. */ + string = (char *) xmalloc (strlen (search->name) + + strlen (filename) + + strlen (arch) +#ifdef EXTRA_SHLIB_EXTENSION + + strlen (EXTRA_SHLIB_EXTENSION) +#endif + + sizeof "/lib.so"); + + sprintf (string, "%s/lib%s%s.so", search->name, filename, arch); + +#ifdef EXTRA_SHLIB_EXTENSION + /* Try the .so extension first. If that fails build a new filename + using EXTRA_SHLIB_EXTENSION. */ + if (! ldfile_try_open_bfd (string, entry)) + sprintf (string, "%s/lib%s%s%s", search->name, + filename, arch, EXTRA_SHLIB_EXTENSION); +#endif + + if (! ldfile_try_open_bfd (string, entry)) + { + free (string); + return FALSE; + } + + entry->filename = string; + + /* We have found a dynamic object to include in the link. The ELF + backend linker will create a DT_NEEDED entry in the .dynamic + section naming this file. If this file includes a DT_SONAME + entry, it will be used. Otherwise, the ELF linker will just use + the name of the file. For an archive found by searching, like + this one, the DT_NEEDED entry should consist of just the name of + the file, without the path information used to find it. Note + that we only need to do this if we have a dynamic object; an + archive will never be referenced by a DT_NEEDED entry. + + FIXME: This approach--using bfd_elf_set_dt_needed_name--is not + very pretty. I haven't been able to think of anything that is + pretty, though. */ + if (bfd_check_format (entry->the_bfd, bfd_object) + && (entry->the_bfd->flags & DYNAMIC) != 0) + { + ASSERT (entry->is_archive && entry->search_dirs_flag); + + /* Rather than duplicating the logic above. Just use the + filename we recorded earlier. */ + + filename = lbasename (entry->filename); + bfd_elf_set_dt_needed_name (entry->the_bfd, filename); + } + + return TRUE; +} + +EOF +fi + +if test x"$LDEMUL_PLACE_ORPHAN" != xgld"$EMULATION_NAME"_place_orphan; then +fragment <name[4] == 'a'; + + for (lookup = &lang_output_section_statement.head->output_section_statement; + lookup != NULL; + lookup = lookup->next) + { + if (lookup->constraint != -1 + && CONST_STRNEQ (lookup->name, ".rel")) + { + int lookrela = lookup->name[4] == 'a'; + + /* .rel.dyn must come before all other reloc sections, to suit + GNU ld.so. */ + if (isdyn) + break; + + /* Don't place after .rel.plt as doing so results in wrong + dynamic tags. */ + if (strcmp (".plt", lookup->name + 4 + lookrela) == 0) + break; + + if (rela == lookrela || last_rel == NULL) + last_rel = lookup; + if ((rela == lookrela || last_rel_alloc == NULL) + && lookup->bfd_section != NULL + && (lookup->bfd_section->flags & SEC_ALLOC) != 0) + last_rel_alloc = lookup; + } + + last = lookup; + if (lookup->bfd_section != NULL + && (lookup->bfd_section->flags & SEC_ALLOC) != 0) + { + last_alloc = lookup; + if ((lookup->bfd_section->flags & SEC_READONLY) != 0) + last_ro_alloc = lookup; + } + } + + if (last_rel_alloc) + return last_rel_alloc; + + if (last_rel) + return last_rel; + + if (last_ro_alloc) + return last_ro_alloc; + + if (last_alloc) + return last_alloc; + + return last; +} + +/* Place an orphan section. We use this to put random SHF_ALLOC + sections in the right segment. */ + +static bfd_boolean +gld${EMULATION_NAME}_place_orphan (asection *s) +{ + static struct orphan_save hold[] = + { + { ".text", + SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE, + 0, 0, 0, 0 }, + { ".rodata", + SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA, + 0, 0, 0, 0 }, + { ".data", + SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_DATA, + 0, 0, 0, 0 }, + { ".bss", + SEC_ALLOC, + 0, 0, 0, 0 }, + { 0, + SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA, + 0, 0, 0, 0 }, + { ".interp", + SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA, + 0, 0, 0, 0 }, + { ".sdata", + SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_DATA | SEC_SMALL_DATA, + 0, 0, 0, 0 } + }; + enum orphan_save_index + { + orphan_text = 0, + orphan_rodata, + orphan_data, + orphan_bss, + orphan_rel, + orphan_interp, + orphan_sdata + }; + static int orphan_init_done = 0; + struct orphan_save *place; + const char *secname; + lang_output_section_statement_type *after; + lang_output_section_statement_type *os; + int isdyn = 0; + int iself = s->owner->xvec->flavour == bfd_target_elf_flavour; + unsigned int sh_type = iself ? elf_section_type (s) : SHT_NULL; + + secname = bfd_get_section_name (s->owner, s); + + if (! link_info.relocatable + && link_info.combreloc + && (s->flags & SEC_ALLOC)) + { + if (iself) + switch (sh_type) + { + case SHT_RELA: + secname = ".rela.dyn"; + isdyn = 1; + break; + case SHT_REL: + secname = ".rel.dyn"; + isdyn = 1; + break; + default: + break; + } + else if (CONST_STRNEQ (secname, ".rel")) + { + secname = secname[4] == 'a' ? ".rela.dyn" : ".rel.dyn"; + isdyn = 1; + } + } + + if (isdyn || (!config.unique_orphan_sections && !unique_section_p (s))) + { + /* Look through the script to see where to place this section. */ + os = lang_output_section_find (secname); + + if (os != NULL + && (os->bfd_section == NULL + || os->bfd_section->flags == 0 + || (_bfd_elf_match_sections_by_type (link_info.output_bfd, + os->bfd_section, + s->owner, s) + && ((s->flags ^ os->bfd_section->flags) + & (SEC_LOAD | SEC_ALLOC)) == 0))) + { + /* We already have an output section statement with this + name, and its bfd section, if any, has compatible flags. + If the section already exists but does not have any flags + set, then it has been created by the linker, probably as a + result of a --section-start command line switch. */ + lang_add_section (&os->children, s, os); + return TRUE; + } + } + + if (!orphan_init_done) + { + struct orphan_save *ho; + for (ho = hold; ho < hold + sizeof (hold) / sizeof (hold[0]); ++ho) + if (ho->name != NULL) + { + ho->os = lang_output_section_find (ho->name); + if (ho->os != NULL && ho->os->flags == 0) + ho->os->flags = ho->flags; + } + orphan_init_done = 1; + } + + /* If this is a final link, then always put .gnu.warning.SYMBOL + sections into the .text section to get them out of the way. */ + if (link_info.executable + && ! link_info.relocatable + && CONST_STRNEQ (secname, ".gnu.warning.") + && hold[orphan_text].os != NULL) + { + lang_add_section (&hold[orphan_text].os->children, s, + hold[orphan_text].os); + return TRUE; + } + + /* Decide which segment the section should go in based on the + section name and section flags. We put loadable .note sections + right after the .interp section, so that the PT_NOTE segment is + stored right after the program headers where the OS can read it + in the first page. */ + + place = NULL; + if ((s->flags & SEC_ALLOC) == 0) + ; + else if ((s->flags & SEC_LOAD) != 0 + && ((iself && sh_type == SHT_NOTE) + || (!iself && CONST_STRNEQ (secname, ".note")))) + place = &hold[orphan_interp]; + else if ((s->flags & (SEC_LOAD | SEC_HAS_CONTENTS)) == 0) + place = &hold[orphan_bss]; + else if ((s->flags & SEC_SMALL_DATA) != 0) + place = &hold[orphan_sdata]; + else if ((s->flags & SEC_READONLY) == 0) + place = &hold[orphan_data]; + else if (((iself && (sh_type == SHT_RELA || sh_type == SHT_REL)) + || (!iself && CONST_STRNEQ (secname, ".rel"))) + && (s->flags & SEC_LOAD) != 0) + place = &hold[orphan_rel]; + else if ((s->flags & SEC_CODE) == 0) + place = &hold[orphan_rodata]; + else + place = &hold[orphan_text]; + + after = NULL; + if (place != NULL) + { + if (place->os == NULL) + { + if (place->name != NULL) + place->os = lang_output_section_find (place->name); + else + place->os = output_rel_find (s, isdyn); + } + after = place->os; + if (after == NULL) + after = lang_output_section_find_by_flags + (s, &place->os, _bfd_elf_match_sections_by_type); + if (after == NULL) + /* *ABS* is always the first output section statement. */ + after = &lang_output_section_statement.head->output_section_statement; + } + + /* Choose a unique name for the section. This will be needed if the + same section name appears in the input file with different + loadable or allocatable characteristics. */ + if (bfd_get_section_by_name (link_info.output_bfd, secname) != NULL) + { + static int count = 1; + secname = bfd_get_unique_section_name (link_info.output_bfd, + secname, &count); + if (secname == NULL) + einfo ("%F%P: place_orphan failed: %E\n"); + } + + lang_insert_orphan (s, secname, after, place, NULL, NULL); + + return TRUE; +} +EOF +fi + +if test x"$LDEMUL_FINISH" != xgld"$EMULATION_NAME"_finish; then +fragment <> e${EMULATION_NAME}.c +echo ' ; else if (link_info.relocatable) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xr >> e${EMULATION_NAME}.c +echo ' ; else if (!config.text_read_only) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xbn >> e${EMULATION_NAME}.c +if cmp -s ldscripts/${EMULATION_NAME}.x ldscripts/${EMULATION_NAME}.xn; then : ; else +echo ' ; else if (!config.magic_demand_paged) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xn >> e${EMULATION_NAME}.c +fi +if test -n "$GENERATE_PIE_SCRIPT" ; then +if test -n "$GENERATE_COMBRELOC_SCRIPT" ; then +echo ' ; else if (link_info.pie && link_info.combreloc' >> e${EMULATION_NAME}.c +echo ' && link_info.relro' >> e${EMULATION_NAME}.c +echo ' && (link_info.flags & DF_BIND_NOW)) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xdw >> e${EMULATION_NAME}.c +echo ' ; else if (link_info.pie && link_info.combreloc) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xdc >> e${EMULATION_NAME}.c +fi +echo ' ; else if (link_info.pie) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xd >> e${EMULATION_NAME}.c +fi +if test -n "$GENERATE_SHLIB_SCRIPT" ; then +if test -n "$GENERATE_COMBRELOC_SCRIPT" ; then +echo ' ; else if (link_info.shared && link_info.combreloc' >> e${EMULATION_NAME}.c +echo ' && link_info.relro' >> e${EMULATION_NAME}.c +echo ' && (link_info.flags & DF_BIND_NOW)) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xsw >> e${EMULATION_NAME}.c +echo ' ; else if (link_info.shared && link_info.combreloc) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xsc >> e${EMULATION_NAME}.c +fi +echo ' ; else if (link_info.shared) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xs >> e${EMULATION_NAME}.c +fi +if test -n "$GENERATE_COMBRELOC_SCRIPT" ; then +echo ' ; else if (link_info.combreloc && link_info.relro' >> e${EMULATION_NAME}.c +echo ' && (link_info.flags & DF_BIND_NOW)) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xw >> e${EMULATION_NAME}.c +echo ' ; else if (link_info.combreloc) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xc >> e${EMULATION_NAME}.c +fi +echo ' ; else return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.x >> e${EMULATION_NAME}.c +echo '; }' >> e${EMULATION_NAME}.c + +else +# Scripts read from the filesystem. + +fragment <, based on armelf.em +# +# This file is part of the GNU Binutils. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. + + +# This file is sourced from elf32.em, and defines some extra routines for m68k +# embedded systems using ELF and for some other systems using m68k ELF. While +# it is sourced from elf32.em for all m68k ELF configurations, here we include +# only the features we want depending on the configuration. + +case ${target} in + m68*-*-elf) + echo "#define SUPPORT_EMBEDDED_RELOCS" >>e${EMULATION_NAME}.c + ;; +esac + +fragment <link_next) + { + asection *datasec; + + /* As first-order business, make sure that each input BFD is either + COFF or ELF. We need to call a special BFD backend function to + generate the embedded relocs, and we have such functions only for + COFF and ELF. */ + if (bfd_get_flavour (abfd) != bfd_target_coff_flavour + && bfd_get_flavour (abfd) != bfd_target_elf_flavour) + einfo ("%F%B: all input objects must be COFF or ELF for --embedded-relocs\n"); + + datasec = bfd_get_section_by_name (abfd, ".data"); + + /* Note that we assume that the reloc_count field has already + been set up. We could call bfd_get_reloc_upper_bound, but + that returns the size of a memory buffer rather than a reloc + count. We do not want to call bfd_canonicalize_reloc, + because although it would always work it would force us to + read in the relocs into BFD canonical form, which would waste + a significant amount of time and memory. */ + if (datasec != NULL && datasec->reloc_count > 0) + { + asection *relsec; + + relsec = bfd_make_section (abfd, ".emreloc"); + if (relsec == NULL + || ! bfd_set_section_flags (abfd, relsec, + (SEC_ALLOC + | SEC_LOAD + | SEC_HAS_CONTENTS + | SEC_IN_MEMORY)) + || ! bfd_set_section_alignment (abfd, relsec, 2) + || ! bfd_set_section_size (abfd, relsec, + datasec->reloc_count * 12)) + einfo ("%F%B: can not create .emreloc section: %E\n"); + } + + /* Double check that all other data sections are empty, as is + required for embedded PIC code. */ + bfd_map_over_sections (abfd, check_sections, datasec); + } + } +#endif /* SUPPORT_EMBEDDED_RELOCS */ +} + +#ifdef SUPPORT_EMBEDDED_RELOCS +/* Check that of the data sections, only the .data section has + relocs. This is called via bfd_map_over_sections. */ + +static void +check_sections (bfd *abfd, asection *sec, void *datasec) +{ + if ((bfd_get_section_flags (abfd, sec) & SEC_DATA) + && sec != datasec + && sec->reloc_count != 0) + einfo ("%B%X: section %s has relocs; can not use --embedded-relocs\n", + abfd, bfd_get_section_name (abfd, sec)); +} + +#endif /* SUPPORT_EMBEDDED_RELOCS */ + +/* This function is called after the section sizes and offsets have + been set. */ + +static void +m68k_elf_after_allocation (void) +{ + /* Call the standard elf routine. */ + after_allocation_default (); + +#ifdef SUPPORT_EMBEDDED_RELOCS + if (command_line.embedded_relocs + && (! link_info.relocatable)) + { + bfd *abfd; + + /* If we are generating embedded relocs, call a special BFD backend + routine to do the work. */ + for (abfd = link_info.input_bfds; abfd != NULL; abfd = abfd->link_next) + { + asection *datasec, *relsec; + char *errmsg; + + datasec = bfd_get_section_by_name (abfd, ".data"); + + if (datasec == NULL || datasec->reloc_count == 0) + continue; + + relsec = bfd_get_section_by_name (abfd, ".emreloc"); + ASSERT (relsec != NULL); + + if (bfd_get_flavour (abfd) == bfd_target_coff_flavour) + { + if (! bfd_m68k_coff_create_embedded_relocs (abfd, &link_info, + datasec, relsec, + &errmsg)) + { + if (errmsg == NULL) + einfo ("%B%X: can not create runtime reloc information: %E\n", + abfd); + else + einfo ("%X%B: can not create runtime reloc information: %s\n", + abfd, errmsg); + } + } + else if (bfd_get_flavour (abfd) == bfd_target_elf_flavour) + { + if (! bfd_m68k_elf32_create_embedded_relocs (abfd, &link_info, + datasec, relsec, + &errmsg)) + { + if (errmsg == NULL) + einfo ("%B%X: can not create runtime reloc information: %E\n", + abfd); + else + einfo ("%X%B: can not create runtime reloc information: %s\n", + abfd, errmsg); + } + } + else + abort (); + } + } +#endif /* SUPPORT_EMBEDDED_RELOCS */ +} + +EOF + +# We have our own after_open and after_allocation functions, but they call +# the standard routines, so give them a different name. +LDEMUL_AFTER_OPEN=m68k_elf_after_open +LDEMUL_AFTER_ALLOCATION=m68k_elf_after_allocation Index: armcoff.em =================================================================== --- armcoff.em (nonexistent) +++ armcoff.em (revision 816) @@ -0,0 +1,283 @@ +# This shell script emits a C file. -*- C -*- +# It does some substitutions. +fragment < Set the entry point to be Thumb symbol \n")); +} + +static bfd_boolean +gld${EMULATION_NAME}_handle_option (int optc) +{ + switch (optc) + { + default: + return FALSE; + + case OPTION_SUPPORT_OLD_CODE: + support_old_code = 1; + break; + + case OPTION_THUMB_ENTRY: + thumb_entry_symbol = optarg; + break; + } + + return TRUE; +} + +static void +gld${EMULATION_NAME}_before_parse (void) +{ +#ifndef TARGET_ /* I.e., if not generic. */ + ldfile_set_output_arch ("`echo ${ARCH}`", bfd_arch_unknown); +#endif /* not TARGET_ */ +} + +/* This is called after the sections have been attached to output + sections, but before any sizes or addresses have been set. */ + +static void +gld${EMULATION_NAME}_before_allocation (void) +{ + /* we should be able to set the size of the interworking stub section */ + + /* Here we rummage through the found bfds to collect glue information */ + /* FIXME: should this be based on a command line option? krk@cygnus.com */ + { + LANG_FOR_EACH_INPUT_STATEMENT (is) + { + if (! bfd_arm_process_before_allocation + (is->the_bfd, & link_info, support_old_code)) + { + /* xgettext:c-format */ + einfo (_("Errors encountered processing file %s"), is->filename); + } + } + } + + /* We have seen it all. Allocate it, and carry on */ + bfd_arm_allocate_interworking_sections (& link_info); + + before_allocation_default (); +} + +static void +gld${EMULATION_NAME}_after_open (void) +{ + if (strstr (bfd_get_target (link_info.output_bfd), "arm") == NULL) + { + /* The arm backend needs special fields in the output hash structure. + These will only be created if the output format is an arm format, + hence we do not support linking and changing output formats at the + same time. Use a link followed by objcopy to change output formats. */ + einfo ("%F%X%P: error: cannot change output format whilst linking ARM binaries\n"); + return; + } + + { + LANG_FOR_EACH_INPUT_STATEMENT (is) + { + if (bfd_arm_get_bfd_for_interworking (is->the_bfd, & link_info)) + break; + } + } +} + +static void +gld${EMULATION_NAME}_finish (void) +{ + if (thumb_entry_symbol != NULL) + { + struct bfd_link_hash_entry * h; + + h = bfd_link_hash_lookup (link_info.hash, thumb_entry_symbol, + FALSE, FALSE, TRUE); + + if (h != (struct bfd_link_hash_entry *) NULL + && (h->type == bfd_link_hash_defined + || h->type == bfd_link_hash_defweak) + && h->u.def.section->output_section != NULL) + { + static char buffer[32]; + bfd_vma val; + + /* Special procesing is required for a Thumb entry symbol. The + bottom bit of its address must be set. */ + val = (h->u.def.value + + bfd_get_section_vma (link_info.output_bfd, + h->u.def.section->output_section) + + h->u.def.section->output_offset); + + val |= 1; + + /* Now convert this value into a string and store it in entry_symbol + where the lang_finish() function will pick it up. */ + buffer[0] = '0'; + buffer[1] = 'x'; + + sprintf_vma (buffer + 2, val); + + if (entry_symbol.name != NULL && entry_from_cmdline) + einfo (_("%P: warning: '--thumb-entry %s' is overriding '-e %s'\n"), + thumb_entry_symbol, entry_symbol.name); + entry_symbol.name = buffer; + } + else + einfo (_("%P: warning: cannot find thumb start symbol %s\n"), + thumb_entry_symbol); + } + + finish_default (); +} + +static char * +gld${EMULATION_NAME}_get_script (int *isfile) +EOF + +if test -n "$COMPILE_IN" +then +# Scripts compiled in. + +# sed commands to quote an ld script as a C string. +sc="-f stringify.sed" + +fragment <> e${EMULATION_NAME}.c +echo ' ; else if (link_info.relocatable) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xr >> e${EMULATION_NAME}.c +echo ' ; else if (!config.text_read_only) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xbn >> e${EMULATION_NAME}.c +echo ' ; else if (!config.magic_demand_paged) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xn >> e${EMULATION_NAME}.c +echo ' ; else return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.x >> e${EMULATION_NAME}.c +echo '; }' >> e${EMULATION_NAME}.c + +else +# Scripts read from the filesystem. + +fragment <the_bfd = bfd_create ("linker stubs", link_info.output_bfd); + if (stub_file->the_bfd == NULL + || ! bfd_set_arch_mach (stub_file->the_bfd, + bfd_get_arch (link_info.output_bfd), + bfd_get_mach (link_info.output_bfd))) + { + einfo ("%X%P: can not create BFD %E\n"); + return; + } + + stub_file->the_bfd->flags |= BFD_LINKER_CREATED; + ldlang_add_file (stub_file); +} + + +struct hook_stub_info +{ + lang_statement_list_type add; + asection *input_section; +}; + +/* Traverse the linker tree to find the spot where the stub goes. */ + +static bfd_boolean +hook_in_stub (struct hook_stub_info *info, lang_statement_union_type **lp) +{ + lang_statement_union_type *l; + bfd_boolean ret; + + for (; (l = *lp) != NULL; lp = &l->header.next) + { + switch (l->header.type) + { + case lang_constructors_statement_enum: + ret = hook_in_stub (info, &constructor_list.head); + if (ret) + return ret; + break; + + case lang_output_section_statement_enum: + ret = hook_in_stub (info, + &l->output_section_statement.children.head); + if (ret) + return ret; + break; + + case lang_wild_statement_enum: + ret = hook_in_stub (info, &l->wild_statement.children.head); + if (ret) + return ret; + break; + + case lang_group_statement_enum: + ret = hook_in_stub (info, &l->group_statement.children.head); + if (ret) + return ret; + break; + + case lang_input_section_enum: + if (l->input_section.section == info->input_section) + { + /* We've found our section. Insert the stub immediately + before its associated input section. */ + *lp = info->add.head; + *(info->add.tail) = l; + return TRUE; + } + break; + + case lang_data_statement_enum: + case lang_reloc_statement_enum: + case lang_object_symbols_statement_enum: + case lang_output_statement_enum: + case lang_target_statement_enum: + case lang_input_statement_enum: + case lang_assignment_statement_enum: + case lang_padding_statement_enum: + case lang_address_statement_enum: + case lang_fill_statement_enum: + break; + + default: + FAIL (); + break; + } + } + return FALSE; +} + + +/* Call-back for elf32_hppa_size_stubs. */ + +/* Create a new stub section, and arrange for it to be linked + immediately before INPUT_SECTION. */ + +static asection * +hppaelf_add_stub_section (const char *stub_sec_name, asection *input_section) +{ + asection *stub_sec; + flagword flags; + asection *output_section; + const char *secname; + lang_output_section_statement_type *os; + struct hook_stub_info info; + + stub_sec = bfd_make_section_anyway (stub_file->the_bfd, stub_sec_name); + if (stub_sec == NULL) + goto err_ret; + + flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE + | SEC_HAS_CONTENTS | SEC_RELOC | SEC_IN_MEMORY | SEC_KEEP); + if (!bfd_set_section_flags (stub_file->the_bfd, stub_sec, flags)) + goto err_ret; + + output_section = input_section->output_section; + secname = bfd_get_section_name (output_section->owner, output_section); + os = lang_output_section_find (secname); + + info.input_section = input_section; + lang_list_init (&info.add); + lang_add_section (&info.add, stub_sec, os); + + if (info.add.head == NULL) + goto err_ret; + + if (hook_in_stub (&info, &os->children.head)) + return stub_sec; + + err_ret: + einfo ("%X%P: can not make stub section: %E\n"); + return NULL; +} + + +/* Another call-back for elf32_hppa_size_stubs. */ + +static void +hppaelf_layout_sections_again (void) +{ + /* If we have changed sizes of the stub sections, then we need + to recalculate all the section offsets. This may mean we need to + add even more stubs. */ + gld${EMULATION_NAME}_map_segments (TRUE); + need_laying_out = -1; +} + + +static void +build_section_lists (lang_statement_union_type *statement) +{ + if (statement->header.type == lang_input_section_enum) + { + asection *i = statement->input_section.section; + + if (!((lang_input_statement_type *) i->owner->usrdata)->just_syms_flag + && (i->flags & SEC_EXCLUDE) == 0 + && i->output_section != NULL + && i->output_section->owner == link_info.output_bfd) + { + elf32_hppa_next_input_section (&link_info, i); + } + } +} + + +/* Final emulation specific call. For the PA we use this opportunity + to build linker stubs. */ + +static void +gld${EMULATION_NAME}_finish (void) +{ + /* bfd_elf_discard_info just plays with debugging sections, + ie. doesn't affect any code, so we can delay resizing the + sections. It's likely we'll resize everything in the process of + adding stubs. */ + if (bfd_elf_discard_info (link_info.output_bfd, &link_info)) + need_laying_out = 1; + + /* If generating a relocatable output file, then we don't + have to examine the relocs. */ + if (stub_file != NULL && !link_info.relocatable) + { + int ret = elf32_hppa_setup_section_lists (link_info.output_bfd, + &link_info); + + if (ret != 0) + { + if (ret < 0) + { + einfo ("%X%P: can not size stub section: %E\n"); + return; + } + + lang_for_each_statement (build_section_lists); + + /* Call into the BFD backend to do the real work. */ + if (! elf32_hppa_size_stubs (link_info.output_bfd, + stub_file->the_bfd, + &link_info, + multi_subspace, + group_size, + &hppaelf_add_stub_section, + &hppaelf_layout_sections_again)) + { + einfo ("%X%P: can not size stub section: %E\n"); + return; + } + } + } + + if (need_laying_out != -1) + gld${EMULATION_NAME}_map_segments (need_laying_out); + + if (! link_info.relocatable) + { + /* Set the global data pointer. */ + if (! elf32_hppa_set_gp (link_info.output_bfd, &link_info)) + { + einfo ("%X%P: can not set gp\n"); + return; + } + + /* Now build the linker stubs. */ + if (stub_file != NULL && stub_file->the_bfd->sections != NULL) + { + if (! elf32_hppa_build_stubs (&link_info)) + einfo ("%X%P: can not build stubs: %E\n"); + } + } + + finish_default (); +} + + +/* Avoid processing the fake stub_file in vercheck, stat_needed and + check_needed routines. */ + +static void (*real_func) (lang_input_statement_type *); + +static void hppa_for_each_input_file_wrapper (lang_input_statement_type *l) +{ + if (l != stub_file) + (*real_func) (l); +} + +static void +hppa_lang_for_each_input_file (void (*func) (lang_input_statement_type *)) +{ + real_func = func; + lang_for_each_input_file (&hppa_for_each_input_file_wrapper); +} + +#define lang_for_each_input_file hppa_lang_for_each_input_file + +EOF + +# Define some shell vars to insert bits of code into the standard elf +# parse_args and list_options functions. +# +PARSE_AND_LIST_PROLOGUE=' +#define OPTION_MULTI_SUBSPACE 301 +#define OPTION_STUBGROUP_SIZE (OPTION_MULTI_SUBSPACE + 1) +' + +PARSE_AND_LIST_LONGOPTS=' + { "multi-subspace", no_argument, NULL, OPTION_MULTI_SUBSPACE }, + { "stub-group-size", required_argument, NULL, OPTION_STUBGROUP_SIZE }, +' + +PARSE_AND_LIST_OPTIONS=' + fprintf (file, _("\ + --multi-subspace Generate import and export stubs to support\n\ + multiple sub-space shared libraries\n" + )); + fprintf (file, _("\ + --stub-group-size=N Maximum size of a group of input sections that\n\ + can be handled by one stub section. A negative\n\ + value locates all stubs before their branches\n\ + (with a group size of -N), while a positive\n\ + value allows two groups of input sections, one\n\ + before, and one after each stub section.\n\ + Values of +/-1 indicate the linker should\n\ + choose suitable defaults.\n" + )); +' + +PARSE_AND_LIST_ARGS_CASES=' + case OPTION_MULTI_SUBSPACE: + multi_subspace = 1; + break; + + case OPTION_STUBGROUP_SIZE: + { + const char *end; + group_size = bfd_scan_vma (optarg, &end, 0); + if (*end) + einfo (_("%P%F: invalid number `%s'\''\n"), optarg); + } + break; +' + +# Put these extra hppaelf routines in ld_${EMULATION_NAME}_emulation +# +LDEMUL_AFTER_PARSE=hppaelf_after_parse +LDEMUL_FINISH=gld${EMULATION_NAME}_finish +LDEMUL_CREATE_OUTPUT_SECTION_STATEMENTS=hppaelf_create_output_section_statements Index: astring.sed =================================================================== --- astring.sed (nonexistent) +++ astring.sed (revision 816) @@ -0,0 +1,13 @@ +s/["\\]/\\&/g +s/$/\\n\\/ +1 s/^/"/ +25s/\\$/"/ +26s/^/"/ +50s/\\$/"/ +51s/^/"/ +75s/\\$/"/ +76s/^/"/ +100s/\\$/"/ +101s/^/"/ +$ s/$/n"/ +$ s/\\n"n"$/\\n"/ Index: ppc64elf.em =================================================================== --- ppc64elf.em (nonexistent) +++ ppc64elf.em (revision 816) @@ -0,0 +1,605 @@ +# This shell script emits a C file. -*- C -*- +# Copyright 2002, 2003, 2004, 2005, 2007, 2008 +# Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +# This file is sourced from elf32.em, and defines extra powerpc64-elf +# specific routines. +# +fragment <the_bfd = bfd_create ("linker stubs", link_info.output_bfd); + if (stub_file->the_bfd == NULL + || !bfd_set_arch_mach (stub_file->the_bfd, + bfd_get_arch (link_info.output_bfd), + bfd_get_mach (link_info.output_bfd))) + { + einfo ("%F%P: can not create BFD %E\n"); + return; + } + + stub_file->the_bfd->flags |= BFD_LINKER_CREATED; + ldlang_add_file (stub_file); + ppc64_elf_init_stub_bfd (stub_file->the_bfd, &link_info); +} + +static void +ppc_before_allocation (void) +{ + if (stub_file != NULL) + { + if (!no_opd_opt + && !ppc64_elf_edit_opd (link_info.output_bfd, &link_info, + non_overlapping_opd)) + einfo ("%X%P: can not edit %s %E\n", "opd"); + + if (ppc64_elf_tls_setup (link_info.output_bfd, &link_info) + && !no_tls_opt) + { + /* Size the sections. This is premature, but we want to know the + TLS segment layout so that certain optimizations can be done. */ + expld.phase = lang_mark_phase_enum; + expld.dataseg.phase = exp_dataseg_none; + one_lang_size_sections_pass (NULL, TRUE); + + if (!ppc64_elf_tls_optimize (link_info.output_bfd, &link_info)) + einfo ("%X%P: TLS problem %E\n"); + + /* We must not cache anything from the preliminary sizing. */ + lang_reset_memory_regions (); + } + + if (!no_toc_opt + && !link_info.relocatable + && !ppc64_elf_edit_toc (link_info.output_bfd, &link_info)) + einfo ("%X%P: can not edit %s %E\n", "toc"); + } + + gld${EMULATION_NAME}_before_allocation (); +} + +struct hook_stub_info +{ + lang_statement_list_type add; + asection *input_section; +}; + +/* Traverse the linker tree to find the spot where the stub goes. */ + +static bfd_boolean +hook_in_stub (struct hook_stub_info *info, lang_statement_union_type **lp) +{ + lang_statement_union_type *l; + bfd_boolean ret; + + for (; (l = *lp) != NULL; lp = &l->header.next) + { + switch (l->header.type) + { + case lang_constructors_statement_enum: + ret = hook_in_stub (info, &constructor_list.head); + if (ret) + return ret; + break; + + case lang_output_section_statement_enum: + ret = hook_in_stub (info, + &l->output_section_statement.children.head); + if (ret) + return ret; + break; + + case lang_wild_statement_enum: + ret = hook_in_stub (info, &l->wild_statement.children.head); + if (ret) + return ret; + break; + + case lang_group_statement_enum: + ret = hook_in_stub (info, &l->group_statement.children.head); + if (ret) + return ret; + break; + + case lang_input_section_enum: + if (l->input_section.section == info->input_section) + { + /* We've found our section. Insert the stub immediately + before its associated input section. */ + *lp = info->add.head; + *(info->add.tail) = l; + return TRUE; + } + break; + + case lang_data_statement_enum: + case lang_reloc_statement_enum: + case lang_object_symbols_statement_enum: + case lang_output_statement_enum: + case lang_target_statement_enum: + case lang_input_statement_enum: + case lang_assignment_statement_enum: + case lang_padding_statement_enum: + case lang_address_statement_enum: + case lang_fill_statement_enum: + break; + + default: + FAIL (); + break; + } + } + return FALSE; +} + + +/* Call-back for ppc64_elf_size_stubs. */ + +/* Create a new stub section, and arrange for it to be linked + immediately before INPUT_SECTION. */ + +static asection * +ppc_add_stub_section (const char *stub_sec_name, asection *input_section) +{ + asection *stub_sec; + flagword flags; + asection *output_section; + const char *secname; + lang_output_section_statement_type *os; + struct hook_stub_info info; + + stub_sec = bfd_make_section_anyway (stub_file->the_bfd, stub_sec_name); + if (stub_sec == NULL) + goto err_ret; + + flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE + | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_KEEP); + if (!bfd_set_section_flags (stub_file->the_bfd, stub_sec, flags)) + goto err_ret; + + output_section = input_section->output_section; + secname = bfd_get_section_name (output_section->owner, output_section); + os = lang_output_section_find (secname); + + info.input_section = input_section; + lang_list_init (&info.add); + lang_add_section (&info.add, stub_sec, os); + + if (info.add.head == NULL) + goto err_ret; + + stub_added = 1; + if (hook_in_stub (&info, &os->children.head)) + return stub_sec; + + err_ret: + einfo ("%X%P: can not make stub section: %E\n"); + return NULL; +} + + +/* Another call-back for ppc64_elf_size_stubs. */ + +static void +ppc_layout_sections_again (void) +{ + /* If we have changed sizes of the stub sections, then we need + to recalculate all the section offsets. This may mean we need to + add even more stubs. */ + gld${EMULATION_NAME}_map_segments (TRUE); + need_laying_out = -1; +} + + +/* Call the back-end function to set TOC base after we have placed all + the sections. */ +static void +gld${EMULATION_NAME}_after_allocation (void) +{ + if (!link_info.relocatable) + _bfd_set_gp_value (link_info.output_bfd, + ppc64_elf_toc (link_info.output_bfd)); +} + + +static void +build_toc_list (lang_statement_union_type *statement) +{ + if (statement->header.type == lang_input_section_enum) + { + asection *i = statement->input_section.section; + + if (!((lang_input_statement_type *) i->owner->usrdata)->just_syms_flag + && (i->flags & SEC_EXCLUDE) == 0 + && i->output_section == toc_section) + ppc64_elf_next_toc_section (&link_info, i); + } +} + + +static void +build_section_lists (lang_statement_union_type *statement) +{ + if (statement->header.type == lang_input_section_enum) + { + asection *i = statement->input_section.section; + + if (!((lang_input_statement_type *) i->owner->usrdata)->just_syms_flag + && (i->flags & SEC_EXCLUDE) == 0 + && i->output_section != NULL + && i->output_section->owner == link_info.output_bfd) + { + if (!ppc64_elf_next_input_section (&link_info, i)) + einfo ("%X%P: can not size stub section: %E\n"); + } + } +} + + +/* Final emulation specific call. */ + +static void +gld${EMULATION_NAME}_finish (void) +{ + /* e_entry on PowerPC64 points to the function descriptor for + _start. If _start is missing, default to the first function + descriptor in the .opd section. */ + entry_section = ".opd"; + + /* bfd_elf_discard_info just plays with debugging sections, + ie. doesn't affect any code, so we can delay resizing the + sections. It's likely we'll resize everything in the process of + adding stubs. */ + if (bfd_elf_discard_info (link_info.output_bfd, &link_info)) + need_laying_out = 1; + + /* If generating a relocatable output file, then we don't have any + stubs. */ + if (stub_file != NULL && !link_info.relocatable) + { + int ret = ppc64_elf_setup_section_lists (link_info.output_bfd, + &link_info, + no_multi_toc); + if (ret < 0) + einfo ("%X%P: can not size stub section: %E\n"); + else if (ret > 0) + { + toc_section = bfd_get_section_by_name (link_info.output_bfd, ".got"); + if (toc_section != NULL) + lang_for_each_statement (build_toc_list); + + ppc64_elf_reinit_toc (link_info.output_bfd, &link_info); + + lang_for_each_statement (build_section_lists); + + /* Call into the BFD backend to do the real work. */ + if (!ppc64_elf_size_stubs (link_info.output_bfd, + &link_info, + group_size, + &ppc_add_stub_section, + &ppc_layout_sections_again)) + einfo ("%X%P: can not size stub section: %E\n"); + } + } + + if (need_laying_out != -1) + gld${EMULATION_NAME}_map_segments (need_laying_out); + + if (link_info.relocatable) + { + asection *toc = bfd_get_section_by_name (link_info.output_bfd, ".toc"); + if (toc != NULL + && bfd_section_size (link_info.output_bfd, toc) > 0x10000) + einfo ("%X%P: TOC section size exceeds 64k\n"); + } + + if (stub_added) + { + char *msg = NULL; + char *line, *endline; + + emit_stub_syms |= link_info.emitrelocations; + if (!ppc64_elf_build_stubs (emit_stub_syms, &link_info, + config.stats ? &msg : NULL)) + einfo ("%X%P: can not build stubs: %E\n"); + + for (line = msg; line != NULL; line = endline) + { + endline = strchr (line, '\n'); + if (endline != NULL) + *endline++ = '\0'; + fprintf (stderr, "%s: %s\n", program_name, line); + } + if (msg != NULL) + free (msg); + } + + ppc64_elf_restore_symbols (&link_info); + finish_default (); +} + + +/* Add a pattern matching ".foo" for every "foo" in a version script. + + The reason for doing this is that many shared library version + scripts export a selected set of functions or data symbols, forcing + others local. eg. + + . VERS_1 { + . global: + . this; that; some; thing; + . local: + . *; + . }; + + To make the above work for PowerPC64, we need to export ".this", + ".that" and so on, otherwise only the function descriptor syms are + exported. Lack of an exported function code sym may cause a + definition to be pulled in from a static library. */ + +static struct bfd_elf_version_expr * +gld${EMULATION_NAME}_new_vers_pattern (struct bfd_elf_version_expr *entry) +{ + struct bfd_elf_version_expr *dot_entry; + unsigned int len; + char *dot_pat; + + if (!dotsyms + || (entry->pattern != NULL + && (entry->pattern[0] == '*' || entry->pattern[0] == '.'))) + return entry; + + dot_entry = xmalloc (sizeof *dot_entry); + *dot_entry = *entry; + dot_entry->next = entry; + if (entry->pattern != NULL) + { + len = strlen (entry->pattern) + 2; + dot_pat = xmalloc (len); + dot_pat[0] = '.'; + memcpy (dot_pat + 1, entry->pattern, len - 1); + dot_entry->pattern = dot_pat; + } + if (entry->symbol != NULL) + { + len = strlen (entry->symbol) + 2; + dot_pat = xmalloc (len); + dot_pat[0] = '.'; + memcpy (dot_pat + 1, entry->symbol, len - 1); + dot_entry->symbol = dot_pat; + } + return dot_entry; +} + + +/* Avoid processing the fake stub_file in vercheck, stat_needed and + check_needed routines. */ + +static void (*real_func) (lang_input_statement_type *); + +static void ppc_for_each_input_file_wrapper (lang_input_statement_type *l) +{ + if (l != stub_file) + (*real_func) (l); +} + +static void +ppc_lang_for_each_input_file (void (*func) (lang_input_statement_type *)) +{ + real_func = func; + lang_for_each_input_file (&ppc_for_each_input_file_wrapper); +} + +#define lang_for_each_input_file ppc_lang_for_each_input_file + +EOF + +if grep -q 'ld_elf32_spu_emulation' ldemul-list.h; then + fragment <name = name; + element->next = (lib_list_type *) NULL; + **list = element; + *list = &element->next; + +} + +static bfd_boolean had_hll = FALSE; +static bfd_boolean had_hll_name = FALSE; + +static void +lnk960_hll (char *name) +{ + had_hll = TRUE; + if (name != (char *) NULL) + { + had_hll_name = TRUE; + append (&hll_list_tail, name); + } +} + +static void +lnk960_syslib (char *name) +{ + append (&syslib_list_tail, name); +} + + +static void +lnk960_before_parse (void) +{ + char *name = getenv ("I960BASE"); + + if (name == (char *) NULL) + { + name = getenv("G960BASE"); + if (name == (char *) NULL) + einfo ("%P%F I960BASE and G960BASE not set\n"); + } + + ldfile_add_library_path (concat (name, "/lib", (const char *) NULL), FALSE); + ldfile_output_architecture = bfd_arch_i960; + ldfile_output_machine = bfd_mach_i960_core; +} + +static void +add_on (lib_list_type *list, lang_input_file_enum_type search) +{ + while (list) + { + lang_add_input_file (list->name, search, (char *) NULL); + list = list->next; + } +} + +static void +lnk960_after_parse (void) +{ + /* If there has been no arch, default to -KB */ + if (ldfile_output_machine_name[0] == 0) + ldfile_add_arch ("KB"); + + /* if there has been no hll list then add our own */ + + if (had_hll && !had_hll_name) + { + append (&hll_list_tail, "cg"); + if (ldfile_output_machine == bfd_mach_i960_ka_sa + || ldfile_output_machine == bfd_mach_i960_ca) + append (&hll_list_tail, "fpg"); + } + + add_on (hll_list, lang_input_file_is_l_enum); + add_on (syslib_list, lang_input_file_is_search_file_enum); +} + +static void +lnk960_after_allocation (void) +{ + if (!link_info.relocatable) + { + lang_abs_symbol_at_end_of (".text", "_etext"); + lang_abs_symbol_at_end_of (".data", "_edata"); + lang_abs_symbol_at_beginning_of (".bss", "_bss_start"); + lang_abs_symbol_at_end_of (".bss", "_end"); + } +} + + +static struct + { + unsigned long number; + char *name; + } +machine_table[] = +{ + { bfd_mach_i960_core ,"CORE" }, + { bfd_mach_i960_kb_sb ,"KB" }, + { bfd_mach_i960_kb_sb ,"SB" }, + { bfd_mach_i960_mc ,"MC" }, + { bfd_mach_i960_xa ,"XA" }, + { bfd_mach_i960_ca ,"CA" }, + { bfd_mach_i960_ka_sa ,"KA" }, + { bfd_mach_i960_ka_sa ,"SA" }, + { bfd_mach_i960_jx ,"JX" }, + { bfd_mach_i960_hx ,"HX" }, + + { bfd_mach_i960_core ,"core" }, + { bfd_mach_i960_kb_sb ,"kb" }, + { bfd_mach_i960_kb_sb ,"sb" }, + { bfd_mach_i960_mc ,"mc" }, + { bfd_mach_i960_xa ,"xa" }, + { bfd_mach_i960_ca ,"ca" }, + { bfd_mach_i960_ka_sa ,"ka" }, + { bfd_mach_i960_ka_sa ,"sa" }, + { bfd_mach_i960_jx ,"jx" }, + { bfd_mach_i960_hx ,"hx" }, + + { 0, (char *) NULL } +}; + +static void +lnk960_set_output_arch (void) +{ + /* Set the output architecture and machine if possible */ + unsigned int i; + ldfile_output_machine = bfd_mach_i960_core; + for (i= 0; machine_table[i].name != (char*) NULL; i++) + { + if (strcmp (ldfile_output_machine_name, machine_table[i].name) == 0) + { + ldfile_output_machine = machine_table[i].number; + break; + } + } + bfd_set_arch_mach (link_info.output_bfd, ldfile_output_architecture, + ldfile_output_machine); +} + +static char * +lnk960_choose_target (int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) +{ + char *from_outside = getenv (TARGET_ENVIRON); + if (from_outside != (char *) NULL) + return from_outside; +#ifdef LNK960_LITTLE + return "coff-Intel-little"; +#else + return "coff-Intel-big"; +#endif +} + +static char * +lnk960_get_script (int *isfile) +EOF + +if test -n "$COMPILE_IN" +then +# Scripts compiled in. + +# sed commands to quote an ld script as a C string. +sc="-f stringify.sed" + +fragment <> e${EMULATION_NAME}.c +echo ' ; else if (link_info.relocatable) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xr >> e${EMULATION_NAME}.c +echo ' ; else if (!config.text_read_only) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xbn >> e${EMULATION_NAME}.c +echo ' ; else if (!config.magic_demand_paged) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xn >> e${EMULATION_NAME}.c +echo ' ; else return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.x >> e${EMULATION_NAME}.c +echo '; }' >> e${EMULATION_NAME}.c + +else +# Scripts read from the filesystem. + +fragment <e${EMULATION_NAME}.c # there, now line numbers match ;-) +fragment < +#include "ldlex.h" +#include "ldmisc.h" +#include "ldctor.h" +#include "coff/internal.h" + +/* FIXME: See bfd/peXXigen.c for why we include an architecture specific + header in generic PE code. */ +#include "coff/x86_64.h" +#include "coff/pe.h" + +/* FIXME: This is a BFD internal header file, and we should not be + using it here. */ +#include "../bfd/libcoff.h" + +#undef AOUTSZ +#define AOUTSZ PEPAOUTSZ +#define PEAOUTHDR PEPAOUTHDR + +#include "deffile.h" +#include "pep-dll.h" +#include "safe-ctype.h" + +/* Permit the emulation parameters to override the default section + alignment by setting OVERRIDE_SECTION_ALIGNMENT. FIXME: This makes + it seem that include/coff/internal.h should not define + PE_DEF_SECTION_ALIGNMENT. */ +#if PE_DEF_SECTION_ALIGNMENT != ${OVERRIDE_SECTION_ALIGNMENT:-PE_DEF_SECTION_ALIGNMENT} +#undef PE_DEF_SECTION_ALIGNMENT +#define PE_DEF_SECTION_ALIGNMENT ${OVERRIDE_SECTION_ALIGNMENT} +#endif + +#ifdef TARGET_IS_i386pep +#define DLL_SUPPORT +#endif + +#if defined(TARGET_IS_i386pep) || ! defined(DLL_SUPPORT) +#define PE_DEF_SUBSYSTEM 3 +#else +#undef NT_EXE_IMAGE_BASE +#define NT_EXE_IMAGE_BASE 0x00010000 +#undef PE_DEF_SECTION_ALIGNMENT +#define PE_DEF_SUBSYSTEM 2 +#undef PE_DEF_FILE_ALIGNMENT +#define PE_DEF_FILE_ALIGNMENT 0x00000200 +#define PE_DEF_SECTION_ALIGNMENT 0x00000400 +#endif + + +static struct internal_extra_pe_aouthdr pep; +static int dll; +static flagword real_flags = IMAGE_FILE_LARGE_ADDRESS_AWARE; +static int support_old_code = 0; +static lang_assignment_statement_type *image_base_statement = 0; + +#ifdef DLL_SUPPORT +static int pep_enable_stdcall_fixup = 1; /* 0=disable 1=enable (default). */ +static char * pep_out_def_filename = NULL; +static char * pep_implib_filename = NULL; +static int pep_enable_auto_image_base = 0; +static char * pep_dll_search_prefix = NULL; +#endif + +extern const char *output_filename; + +static void +gld_${EMULATION_NAME}_before_parse (void) +{ + ldfile_set_output_arch ("${OUTPUT_ARCH}", bfd_arch_`echo ${ARCH} | sed -e 's/:.*//'`); + output_filename = "${EXECUTABLE_NAME:-a.exe}"; +#ifdef DLL_SUPPORT + config.dynamic_link = TRUE; + config.has_shared = 1; + link_info.pei386_auto_import = -1; + link_info.pei386_runtime_pseudo_reloc = -1; + +#if (PE_DEF_SUBSYSTEM == 9) || (PE_DEF_SUBSYSTEM == 2) + lang_default_entry ("_WinMainCRTStartup"); +#else + lang_default_entry ("${ENTRY}"); +#endif +#endif +} + +/* PE format extra command line options. */ + +/* Used for setting flags in the PE header. */ +enum options +{ + OPTION_BASE_FILE = 300 + 1, + OPTION_DLL, + OPTION_FILE_ALIGNMENT, + OPTION_IMAGE_BASE, + OPTION_MAJOR_IMAGE_VERSION, + OPTION_MAJOR_OS_VERSION, + OPTION_MAJOR_SUBSYSTEM_VERSION, + OPTION_MINOR_IMAGE_VERSION, + OPTION_MINOR_OS_VERSION, + OPTION_MINOR_SUBSYSTEM_VERSION, + OPTION_SECTION_ALIGNMENT, + OPTION_STACK, + OPTION_SUBSYSTEM, + OPTION_HEAP, + OPTION_SUPPORT_OLD_CODE, + OPTION_OUT_DEF, + OPTION_EXPORT_ALL, + OPTION_EXCLUDE_SYMBOLS, + OPTION_KILL_ATS, + OPTION_STDCALL_ALIASES, + OPTION_ENABLE_STDCALL_FIXUP, + OPTION_DISABLE_STDCALL_FIXUP, + OPTION_IMPLIB_FILENAME, + OPTION_WARN_DUPLICATE_EXPORTS, + OPTION_IMP_COMPAT, + OPTION_ENABLE_AUTO_IMAGE_BASE, + OPTION_DISABLE_AUTO_IMAGE_BASE, + OPTION_DLL_SEARCH_PREFIX, + OPTION_NO_DEFAULT_EXCLUDES, + OPTION_DLL_ENABLE_AUTO_IMPORT, + OPTION_DLL_DISABLE_AUTO_IMPORT, + OPTION_ENABLE_EXTRA_PE_DEBUG, + OPTION_EXCLUDE_LIBS, + OPTION_DLL_ENABLE_RUNTIME_PSEUDO_RELOC, + OPTION_DLL_DISABLE_RUNTIME_PSEUDO_RELOC +}; + +static void +gld${EMULATION_NAME}_add_options + (int ns ATTRIBUTE_UNUSED, + char **shortopts ATTRIBUTE_UNUSED, + int nl, + struct option **longopts, + int nrl ATTRIBUTE_UNUSED, + struct option **really_longopts ATTRIBUTE_UNUSED) +{ + static const struct option xtra_long[] = + { + /* PE options */ + {"base-file", required_argument, NULL, OPTION_BASE_FILE}, + {"dll", no_argument, NULL, OPTION_DLL}, + {"file-alignment", required_argument, NULL, OPTION_FILE_ALIGNMENT}, + {"heap", required_argument, NULL, OPTION_HEAP}, + {"image-base", required_argument, NULL, OPTION_IMAGE_BASE}, + {"major-image-version", required_argument, NULL, OPTION_MAJOR_IMAGE_VERSION}, + {"major-os-version", required_argument, NULL, OPTION_MAJOR_OS_VERSION}, + {"major-subsystem-version", required_argument, NULL, OPTION_MAJOR_SUBSYSTEM_VERSION}, + {"minor-image-version", required_argument, NULL, OPTION_MINOR_IMAGE_VERSION}, + {"minor-os-version", required_argument, NULL, OPTION_MINOR_OS_VERSION}, + {"minor-subsystem-version", required_argument, NULL, OPTION_MINOR_SUBSYSTEM_VERSION}, + {"section-alignment", required_argument, NULL, OPTION_SECTION_ALIGNMENT}, + {"stack", required_argument, NULL, OPTION_STACK}, + {"subsystem", required_argument, NULL, OPTION_SUBSYSTEM}, + {"support-old-code", no_argument, NULL, OPTION_SUPPORT_OLD_CODE}, +#ifdef DLL_SUPPORT + /* getopt allows abbreviations, so we do this to stop it + from treating -o as an abbreviation for this option. */ + {"output-def", required_argument, NULL, OPTION_OUT_DEF}, + {"output-def", required_argument, NULL, OPTION_OUT_DEF}, + {"export-all-symbols", no_argument, NULL, OPTION_EXPORT_ALL}, + {"exclude-symbols", required_argument, NULL, OPTION_EXCLUDE_SYMBOLS}, + {"exclude-libs", required_argument, NULL, OPTION_EXCLUDE_LIBS}, + {"kill-at", no_argument, NULL, OPTION_KILL_ATS}, + {"add-stdcall-alias", no_argument, NULL, OPTION_STDCALL_ALIASES}, + {"enable-stdcall-fixup", no_argument, NULL, OPTION_ENABLE_STDCALL_FIXUP}, + {"disable-stdcall-fixup", no_argument, NULL, OPTION_DISABLE_STDCALL_FIXUP}, + {"out-implib", required_argument, NULL, OPTION_IMPLIB_FILENAME}, + {"warn-duplicate-exports", no_argument, NULL, OPTION_WARN_DUPLICATE_EXPORTS}, + /* getopt() allows abbreviations, so we do this to stop it from + treating -c as an abbreviation for these --compat-implib. */ + {"compat-implib", no_argument, NULL, OPTION_IMP_COMPAT}, + {"compat-implib", no_argument, NULL, OPTION_IMP_COMPAT}, + {"enable-auto-image-base", no_argument, NULL, OPTION_ENABLE_AUTO_IMAGE_BASE}, + {"disable-auto-image-base", no_argument, NULL, OPTION_DISABLE_AUTO_IMAGE_BASE}, + {"dll-search-prefix", required_argument, NULL, OPTION_DLL_SEARCH_PREFIX}, + {"no-default-excludes", no_argument, NULL, OPTION_NO_DEFAULT_EXCLUDES}, + {"enable-auto-import", no_argument, NULL, OPTION_DLL_ENABLE_AUTO_IMPORT}, + {"disable-auto-import", no_argument, NULL, OPTION_DLL_DISABLE_AUTO_IMPORT}, + {"enable-extra-pep-debug", no_argument, NULL, OPTION_ENABLE_EXTRA_PE_DEBUG}, + {"enable-runtime-pseudo-reloc", no_argument, NULL, OPTION_DLL_ENABLE_RUNTIME_PSEUDO_RELOC}, + {"disable-runtime-pseudo-reloc", no_argument, NULL, OPTION_DLL_DISABLE_RUNTIME_PSEUDO_RELOC}, +#endif + {NULL, no_argument, NULL, 0} + }; + + *longopts = xrealloc (*longopts, nl * sizeof (struct option) + sizeof (xtra_long)); + memcpy (*longopts + nl, &xtra_long, sizeof (xtra_long)); +} + +/* PE/WIN32; added routines to get the subsystem type, heap and/or stack + parameters which may be input from the command line. */ + +typedef struct +{ + void *ptr; + int size; + int value; + char *symbol; + int inited; +} definfo; + +#define D(field,symbol,def) {&pep.field,sizeof(pep.field), def, symbol,0} + +static definfo init[] = +{ + /* imagebase must be first */ +#define IMAGEBASEOFF 0 + D(ImageBase,"__image_base__", NT_EXE_IMAGE_BASE), +#define DLLOFF 1 + {&dll, sizeof(dll), 0, "__dll__", 0}, +#define MSIMAGEBASEOFF 2 + D(ImageBase,"___ImageBase", NT_EXE_IMAGE_BASE), + D(SectionAlignment,"__section_alignment__", PE_DEF_SECTION_ALIGNMENT), + D(FileAlignment,"__file_alignment__", PE_DEF_FILE_ALIGNMENT), + D(MajorOperatingSystemVersion,"__major_os_version__", 4), + D(MinorOperatingSystemVersion,"__minor_os_version__", 0), + D(MajorImageVersion,"__major_image_version__", 0), + D(MinorImageVersion,"__minor_image_version__", 0), + D(MajorSubsystemVersion,"__major_subsystem_version__", 5), + D(MinorSubsystemVersion,"__minor_subsystem_version__", 2), + D(Subsystem,"__subsystem__", ${SUBSYSTEM}), + D(SizeOfStackReserve,"__size_of_stack_reserve__", 0x200000), + D(SizeOfStackCommit,"__size_of_stack_commit__", 0x1000), + D(SizeOfHeapReserve,"__size_of_heap_reserve__", 0x100000), + D(SizeOfHeapCommit,"__size_of_heap_commit__", 0x1000), + D(LoaderFlags,"__loader_flags__", 0x0), + { NULL, 0, 0, NULL, 0 } +}; + + +static void +gld_${EMULATION_NAME}_list_options (FILE *file) +{ + fprintf (file, _(" --base_file Generate a base file for relocatable DLLs\n")); + fprintf (file, _(" --dll Set image base to the default for DLLs\n")); + fprintf (file, _(" --file-alignment Set file alignment\n")); + fprintf (file, _(" --heap Set initial size of the heap\n")); + fprintf (file, _(" --image-base
Set start address of the executable\n")); + fprintf (file, _(" --major-image-version Set version number of the executable\n")); + fprintf (file, _(" --major-os-version Set minimum required OS version\n")); + fprintf (file, _(" --major-subsystem-version Set minimum required OS subsystem version\n")); + fprintf (file, _(" --minor-image-version Set revision number of the executable\n")); + fprintf (file, _(" --minor-os-version Set minimum required OS revision\n")); + fprintf (file, _(" --minor-subsystem-version Set minimum required OS subsystem revision\n")); + fprintf (file, _(" --section-alignment Set section alignment\n")); + fprintf (file, _(" --stack Set size of the initial stack\n")); + fprintf (file, _(" --subsystem [:] Set required OS subsystem [& version]\n")); + fprintf (file, _(" --support-old-code Support interworking with old code\n")); +#ifdef DLL_SUPPORT + fprintf (file, _(" --add-stdcall-alias Export symbols with and without @nn\n")); + fprintf (file, _(" --disable-stdcall-fixup Don't link _sym to _sym@nn\n")); + fprintf (file, _(" --enable-stdcall-fixup Link _sym to _sym@nn without warnings\n")); + fprintf (file, _(" --exclude-symbols sym,sym,... Exclude symbols from automatic export\n")); + fprintf (file, _(" --exclude-libs lib,lib,... Exclude libraries from automatic export\n")); + fprintf (file, _(" --export-all-symbols Automatically export all globals to DLL\n")); + fprintf (file, _(" --kill-at Remove @nn from exported symbols\n")); + fprintf (file, _(" --out-implib Generate import library\n")); + fprintf (file, _(" --output-def Generate a .DEF file for the built DLL\n")); + fprintf (file, _(" --warn-duplicate-exports Warn about duplicate exports.\n")); + fprintf (file, _(" --compat-implib Create backward compatible import libs;\n\ + create __imp_ as well.\n")); + fprintf (file, _(" --enable-auto-image-base Automatically choose image base for DLLs\n\ + unless user specifies one\n")); + fprintf (file, _(" --disable-auto-image-base Do not auto-choose image base. (default)\n")); + fprintf (file, _(" --dll-search-prefix= When linking dynamically to a dll without\n\ + an importlib, use .dll\n\ + in preference to lib.dll \n")); + fprintf (file, _(" --enable-auto-import Do sophistcated linking of _sym to\n\ + __imp_sym for DATA references\n")); + fprintf (file, _(" --disable-auto-import Do not auto-import DATA items from DLLs\n")); + fprintf (file, _(" --enable-runtime-pseudo-reloc Work around auto-import limitations by\n\ + adding pseudo-relocations resolved at\n\ + runtime.\n")); + fprintf (file, _(" --disable-runtime-pseudo-reloc Do not add runtime pseudo-relocations for\n\ + auto-imported DATA.\n")); + fprintf (file, _(" --enable-extra-pep-debug Enable verbose debug output when building\n\ + or linking to DLLs (esp. auto-import)\n")); +#endif +} + + +static void +set_pep_name (char *name, long val) +{ + int i; + + /* Find the name and set it. */ + for (i = 0; init[i].ptr; i++) + { + if (strcmp (name, init[i].symbol) == 0) + { + init[i].value = val; + init[i].inited = 1; + if (strcmp (name,"__image_base__") == 0) + set_pep_name ("___ImageBase", val); + return; + } + } + abort (); +} + + +static void +set_pep_subsystem (void) +{ + const char *sver; + const char *entry; + const char *initial_symbol_char; + char *end; + int len; + int i; + int subsystem; + unsigned long temp_subsystem; + static const struct + { + const char *name; + const int value; + const char *entry; + } + v[] = + { + { "native", 1, "NtProcessStartup" }, + { "windows", 2, "WinMainCRTStartup" }, + { "console", 3, "mainCRTStartup" }, + { "posix", 7, "__PosixProcessStartup"}, + { "wince", 9, "_WinMainCRTStartup" }, + { "xbox", 14, "mainCRTStartup" }, + { NULL, 0, NULL } + }; + /* Entry point name for arbitrary subsystem numbers. */ + static const char default_entry[] = "mainCRTStartup"; + + /* Check for the presence of a version number. */ + sver = strchr (optarg, ':'); + if (sver == NULL) + len = strlen (optarg); + else + { + len = sver - optarg; + set_pep_name ("__major_subsystem_version__", + strtoul (sver + 1, &end, 0)); + if (*end == '.') + set_pep_name ("__minor_subsystem_version__", + strtoul (end + 1, &end, 0)); + if (*end != '\0') + einfo (_("%P: warning: bad version number in -subsystem option\n")); + } + + /* Check for numeric subsystem. */ + temp_subsystem = strtoul (optarg, & end, 0); + if ((*end == ':' || *end == '\0') && (temp_subsystem < 65536)) + { + /* Search list for a numeric match to use its entry point. */ + for (i = 0; v[i].name; i++) + if (v[i].value == (int) temp_subsystem) + break; + + /* If no match, use the default. */ + if (v[i].name != NULL) + entry = v[i].entry; + else + entry = default_entry; + + /* Use this subsystem. */ + subsystem = (int) temp_subsystem; + } + else + { + /* Search for subsystem by name. */ + for (i = 0; v[i].name; i++) + if (strncmp (optarg, v[i].name, len) == 0 + && v[i].name[len] == '\0') + break; + + if (v[i].name == NULL) + { + einfo (_("%P%F: invalid subsystem type %s\n"), optarg); + return; + } + + entry = v[i].entry; + subsystem = v[i].value; + } + + set_pep_name ("__subsystem__", subsystem); + + initial_symbol_char = ${INITIAL_SYMBOL_CHAR}; + if (*initial_symbol_char != '\0') + { + char *alc_entry; + + /* lang_default_entry expects its argument to be permanently + allocated, so we don't free this string. */ + alc_entry = xmalloc (strlen (initial_symbol_char) + + strlen (entry) + + 1); + strcpy (alc_entry, initial_symbol_char); + strcat (alc_entry, entry); + entry = alc_entry; + } + + lang_default_entry (entry); + + return; +} + + +static void +set_pep_value (char *name) +{ + char *end; + + set_pep_name (name, strtoul (optarg, &end, 0)); + + if (end == optarg) + einfo (_("%P%F: invalid hex number for PE parameter '%s'\n"), optarg); + + optarg = end; +} + + +static void +set_pep_stack_heap (char *resname, char *comname) +{ + set_pep_value (resname); + + if (*optarg == ',') + { + optarg++; + set_pep_value (comname); + } + else if (*optarg) + einfo (_("%P%F: strange hex info for PE parameter '%s'\n"), optarg); +} + + +static bfd_boolean +gld${EMULATION_NAME}_handle_option (int optc) +{ + switch (optc) + { + default: + return FALSE; + + case OPTION_BASE_FILE: + link_info.base_file = fopen (optarg, FOPEN_WB); + if (link_info.base_file == NULL) + { + /* xgettext:c-format */ + fprintf (stderr, _("%s: Can't open base file %s\n"), + program_name, optarg); + xexit (1); + } + break; + + /* PE options. */ + case OPTION_HEAP: + set_pep_stack_heap ("__size_of_heap_reserve__", "__size_of_heap_commit__"); + break; + case OPTION_STACK: + set_pep_stack_heap ("__size_of_stack_reserve__", "__size_of_stack_commit__"); + break; + case OPTION_SUBSYSTEM: + set_pep_subsystem (); + break; + case OPTION_MAJOR_OS_VERSION: + set_pep_value ("__major_os_version__"); + break; + case OPTION_MINOR_OS_VERSION: + set_pep_value ("__minor_os_version__"); + break; + case OPTION_MAJOR_SUBSYSTEM_VERSION: + set_pep_value ("__major_subsystem_version__"); + break; + case OPTION_MINOR_SUBSYSTEM_VERSION: + set_pep_value ("__minor_subsystem_version__"); + break; + case OPTION_MAJOR_IMAGE_VERSION: + set_pep_value ("__major_image_version__"); + break; + case OPTION_MINOR_IMAGE_VERSION: + set_pep_value ("__minor_image_version__"); + break; + case OPTION_FILE_ALIGNMENT: + set_pep_value ("__file_alignment__"); + break; + case OPTION_SECTION_ALIGNMENT: + set_pep_value ("__section_alignment__"); + break; + case OPTION_DLL: + set_pep_name ("__dll__", 1); + break; + case OPTION_IMAGE_BASE: + set_pep_value ("__image_base__"); + break; + case OPTION_SUPPORT_OLD_CODE: + support_old_code = 1; + break; +#ifdef DLL_SUPPORT + case OPTION_OUT_DEF: + pep_out_def_filename = xstrdup (optarg); + break; + case OPTION_EXPORT_ALL: + pep_dll_export_everything = 1; + break; + case OPTION_EXCLUDE_SYMBOLS: + pep_dll_add_excludes (optarg, 0); + break; + case OPTION_EXCLUDE_LIBS: + pep_dll_add_excludes (optarg, 1); + break; + case OPTION_KILL_ATS: + pep_dll_kill_ats = 1; + break; + case OPTION_STDCALL_ALIASES: + pep_dll_stdcall_aliases = 1; + break; + case OPTION_ENABLE_STDCALL_FIXUP: + pep_enable_stdcall_fixup = 1; + break; + case OPTION_DISABLE_STDCALL_FIXUP: + pep_enable_stdcall_fixup = 0; + break; + case OPTION_IMPLIB_FILENAME: + pep_implib_filename = xstrdup (optarg); + break; + case OPTION_WARN_DUPLICATE_EXPORTS: + pep_dll_warn_dup_exports = 1; + break; + case OPTION_IMP_COMPAT: + pep_dll_compat_implib = 1; + break; + case OPTION_ENABLE_AUTO_IMAGE_BASE: + pep_enable_auto_image_base = 1; + break; + case OPTION_DISABLE_AUTO_IMAGE_BASE: + pep_enable_auto_image_base = 0; + break; + case OPTION_DLL_SEARCH_PREFIX: + pep_dll_search_prefix = xstrdup (optarg); + break; + case OPTION_NO_DEFAULT_EXCLUDES: + pep_dll_do_default_excludes = 0; + break; + case OPTION_DLL_ENABLE_AUTO_IMPORT: + link_info.pei386_auto_import = 1; + break; + case OPTION_DLL_DISABLE_AUTO_IMPORT: + link_info.pei386_auto_import = 0; + break; + case OPTION_DLL_ENABLE_RUNTIME_PSEUDO_RELOC: + link_info.pei386_runtime_pseudo_reloc = 1; + break; + case OPTION_DLL_DISABLE_RUNTIME_PSEUDO_RELOC: + link_info.pei386_runtime_pseudo_reloc = 0; + break; + case OPTION_ENABLE_EXTRA_PE_DEBUG: + pep_dll_extra_pe_debug = 1; + break; +#endif + } + return TRUE; +} + + +#ifdef DLL_SUPPORT +static unsigned long +strhash (const char *str) +{ + const unsigned char *s; + unsigned long hash; + unsigned int c; + unsigned int len; + + hash = 0; + len = 0; + s = (const unsigned char *) str; + while ((c = *s++) != '\0') + { + hash += c + (c << 17); + hash ^= hash >> 2; + ++len; + } + hash += len + (len << 17); + hash ^= hash >> 2; + + return hash; +} + +/* Use the output file to create a image base for relocatable DLLs. */ + +static unsigned long +compute_dll_image_base (const char *ofile) +{ + unsigned long hash = strhash (ofile); + return 0x61300000 + ((hash << 16) & 0x0FFC0000); +} +#endif + +/* Assign values to the special symbols before the linker script is + read. */ + +static void +gld_${EMULATION_NAME}_set_symbols (void) +{ + /* Run through and invent symbols for all the + names and insert the defaults. */ + int j; + lang_statement_list_type *save; + + if (!init[IMAGEBASEOFF].inited) + { + if (link_info.relocatable) + init[IMAGEBASEOFF].value = 0; + else if (init[DLLOFF].value || (link_info.shared && !link_info.pie)) +#ifdef DLL_SUPPORT + init[IMAGEBASEOFF].value = (pep_enable_auto_image_base) ? + compute_dll_image_base (output_filename) : NT_DLL_IMAGE_BASE; +#else + init[IMAGEBASEOFF].value = NT_DLL_IMAGE_BASE; +#endif + else + init[IMAGEBASEOFF].value = NT_EXE_IMAGE_BASE; + init[MSIMAGEBASEOFF].value = init[IMAGEBASEOFF].value; + } + + /* Don't do any symbol assignments if this is a relocatable link. */ + if (link_info.relocatable) + return; + + /* Glue the assignments into the abs section. */ + save = stat_ptr; + + stat_ptr = &(abs_output_section->children); + + for (j = 0; init[j].ptr; j++) + { + long val = init[j].value; + lang_assignment_statement_type *rv; + rv = lang_add_assignment (exp_assop ('=', init[j].symbol, + exp_intop (val))); + if (init[j].size == sizeof (short)) + *(short *) init[j].ptr = val; + else if (init[j].size == sizeof (int)) + *(int *) init[j].ptr = val; + else if (init[j].size == sizeof (long)) + *(long *) init[j].ptr = val; + /* This might be a long long or other special type. */ + else if (init[j].size == sizeof (bfd_vma)) + *(bfd_vma *) init[j].ptr = val; + else abort (); + if (j == IMAGEBASEOFF) + image_base_statement = rv; + } + /* Restore the pointer. */ + stat_ptr = save; + + if (pep.FileAlignment > pep.SectionAlignment) + { + einfo (_("%P: warning, file alignment > section alignment.\n")); + } +} + +/* This is called after the linker script and the command line options + have been read. */ + +static void +gld_${EMULATION_NAME}_after_parse (void) +{ + /* The Windows libraries are designed for the linker to treat the + entry point as an undefined symbol. Otherwise, the .obj that + defines mainCRTStartup is brought in because it is the first + encountered in libc.lib and it has other symbols in it which will + be pulled in by the link process. To avoid this, we act as + though the user specified -u with the entry point symbol. + + This function is called after the linker script and command line + options have been read, so at this point we know the right entry + point. This function is called before the input files are + opened, so registering the symbol as undefined will make a + difference. */ + + if (! link_info.relocatable && entry_symbol.name != NULL) + ldlang_add_undef (entry_symbol.name); +} + +/* pep-dll.c directly accesses pep_data_import_dll, + so it must be defined outside of #ifdef DLL_SUPPORT. + Note - this variable is deliberately not initialised. + This allows it to be treated as a common varaible, and only + exist in one incarnation in a multiple target enabled linker. */ +char * pep_data_import_dll; + +#ifdef DLL_SUPPORT +static struct bfd_link_hash_entry *pep_undef_found_sym; + +static bfd_boolean +pep_undef_cdecl_match (struct bfd_link_hash_entry *h, void *inf) +{ + int sl; + char *string = inf; + + sl = strlen (string); + if (h->type == bfd_link_hash_defined + && strncmp (h->root.string, string, sl) == 0 + && h->root.string[sl] == '@') + { + pep_undef_found_sym = h; + return FALSE; + } + return TRUE; +} + +static void +pep_fixup_stdcalls (void) +{ + static int gave_warning_message = 0; + struct bfd_link_hash_entry *undef, *sym; + + if (pep_dll_extra_pe_debug) + printf ("%s\n", __FUNCTION__); + + for (undef = link_info.hash->undefs; undef; undef=undef->u.undef.next) + if (undef->type == bfd_link_hash_undefined) + { + char* at = strchr (undef->root.string, '@'); + int lead_at = (*undef->root.string == '@'); + /* For now, don't try to fixup fastcall symbols. */ + + if (at && !lead_at) + { + /* The symbol is a stdcall symbol, so let's look for a + cdecl symbol with the same name and resolve to that. */ + char *cname = xstrdup (undef->root.string /* + lead_at */); + at = strchr (cname, '@'); + *at = 0; + sym = bfd_link_hash_lookup (link_info.hash, cname, 0, 0, 1); + + if (sym && sym->type == bfd_link_hash_defined) + { + undef->type = bfd_link_hash_defined; + undef->u.def.value = sym->u.def.value; + undef->u.def.section = sym->u.def.section; + + if (pep_enable_stdcall_fixup == -1) + { + einfo (_("Warning: resolving %s by linking to %s\n"), + undef->root.string, cname); + if (! gave_warning_message) + { + gave_warning_message = 1; + einfo (_("Use --enable-stdcall-fixup to disable these warnings\n")); + einfo (_("Use --disable-stdcall-fixup to disable these fixups\n")); + } + } + } + } + else + { + /* The symbol is a cdecl symbol, so we look for stdcall + symbols - which means scanning the whole symbol table. */ + pep_undef_found_sym = 0; + bfd_link_hash_traverse (link_info.hash, pep_undef_cdecl_match, + (char *) undef->root.string); + sym = pep_undef_found_sym; + if (sym) + { + undef->type = bfd_link_hash_defined; + undef->u.def.value = sym->u.def.value; + undef->u.def.section = sym->u.def.section; + + if (pep_enable_stdcall_fixup == -1) + { + einfo (_("Warning: resolving %s by linking to %s\n"), + undef->root.string, sym->root.string); + if (! gave_warning_message) + { + gave_warning_message = 1; + einfo (_("Use --enable-stdcall-fixup to disable these warnings\n")); + einfo (_("Use --disable-stdcall-fixup to disable these fixups\n")); + } + } + } + } + } +} + +static int +make_import_fixup (arelent *rel, asection *s) +{ + struct bfd_symbol *sym = *rel->sym_ptr_ptr; + char addend[4]; + + if (pep_dll_extra_pe_debug) + printf ("arelent: %s@%#lx: add=%li\n", sym->name, + (long) rel->address, (long) rel->addend); + + if (! bfd_get_section_contents (s->owner, s, addend, rel->address, sizeof (addend))) + einfo (_("%C: Cannot get section contents - auto-import exception\n"), + s->owner, s, rel->address); + + pep_create_import_fixup (rel, s, bfd_get_32 (s->owner, addend)); + + return 1; +} + +static void +pep_find_data_imports (void) +{ + struct bfd_link_hash_entry *undef, *sym; + + if (link_info.pei386_auto_import == 0) + return; + + for (undef = link_info.hash->undefs; undef; undef=undef->u.undef.next) + { + if (undef->type == bfd_link_hash_undefined) + { + /* C++ symbols are *long*. */ + char buf[4096]; + + if (pep_dll_extra_pe_debug) + printf ("%s:%s\n", __FUNCTION__, undef->root.string); + + sprintf (buf, "__imp_%s", undef->root.string); + + sym = bfd_link_hash_lookup (link_info.hash, buf, 0, 0, 1); + + if (sym && sym->type == bfd_link_hash_defined) + { + bfd *b = sym->u.def.section->owner; + asymbol **symbols; + int nsyms, symsize, i; + + if (link_info.pei386_auto_import == -1) + { + static bfd_boolean warned = FALSE; + + info_msg (_("Info: resolving %s by linking to %s (auto-import)\n"), + undef->root.string, buf); + + /* PR linker/4844. */ + if (! warned) + { + warned = TRUE; + einfo (_("%P: warning: auto-importing has been activated without --enable-auto-import specified on the command line.\n\ +This should work unless it involves constant data structures referencing symbols from auto-imported DLLs.")); + } + } + + symsize = bfd_get_symtab_upper_bound (b); + symbols = xmalloc (symsize); + nsyms = bfd_canonicalize_symtab (b, symbols); + + for (i = 0; i < nsyms; i++) + { + if (! CONST_STRNEQ (symbols[i]->name, "__head_")) + continue; + + if (pep_dll_extra_pe_debug) + printf ("->%s\n", symbols[i]->name); + + pep_data_import_dll = (char*) (symbols[i]->name + + sizeof ("__head_") - 1); + break; + } + + pep_walk_relocs_of_symbol (&link_info, undef->root.string, + make_import_fixup); + + /* Let's differentiate it somehow from defined. */ + undef->type = bfd_link_hash_defweak; + /* We replace original name with __imp_ prefixed, this + 1) may trash memory 2) leads to duplicate symbol generation. + Still, IMHO it's better than having name poluted. */ + undef->root.string = sym->root.string; + undef->u.def.value = sym->u.def.value; + undef->u.def.section = sym->u.def.section; + } + } + } +} + +static bfd_boolean +pr_sym (struct bfd_hash_entry *h, void *inf ATTRIBUTE_UNUSED) +{ + if (pep_dll_extra_pe_debug) + printf ("+%s\n", h->string); + + return TRUE; +} +#endif /* DLL_SUPPORT */ + + +static void +gld_${EMULATION_NAME}_after_open (void) +{ +#ifdef DLL_SUPPORT + if (pep_dll_extra_pe_debug) + { + bfd *a; + struct bfd_link_hash_entry *sym; + + printf ("%s()\n", __FUNCTION__); + + for (sym = link_info.hash->undefs; sym; sym=sym->u.undef.next) + printf ("-%s\n", sym->root.string); + bfd_hash_traverse (&link_info.hash->table, pr_sym, NULL); + + for (a = link_info.input_bfds; a; a = a->link_next) + printf ("*%s\n",a->filename); + } +#endif + + /* Pass the wacky PE command line options into the output bfd. + FIXME: This should be done via a function, rather than by + including an internal BFD header. */ + + if (coff_data (link_info.output_bfd) == NULL + || coff_data (link_info.output_bfd)->pe == 0) + einfo (_("%F%P: cannot perform PE operations on non PE output file '%B'.\n"), + link_info.output_bfd); + + pe_data (link_info.output_bfd)->pe_opthdr = pep; + pe_data (link_info.output_bfd)->dll = init[DLLOFF].value; + pe_data (link_info.output_bfd)->real_flags |= real_flags; + +#ifdef DLL_SUPPORT + if (pep_enable_stdcall_fixup) /* -1=warn or 1=disable */ + pep_fixup_stdcalls (); + + pep_process_import_defs (link_info.output_bfd, &link_info); + + pep_find_data_imports (); + +#ifndef TARGET_IS_i386pep + if (link_info.shared) +#else + if (!link_info.relocatable) +#endif + pep_dll_build_sections (link_info.output_bfd, &link_info); + +#ifndef TARGET_IS_i386pep + else + pep_exe_build_sections (link_info.output_bfd, &link_info); +#endif +#endif /* DLL_SUPPORT */ + + { + /* This next chunk of code tries to detect the case where you have + two import libraries for the same DLL (specifically, + symbolically linking libm.a and libc.a in cygwin to + libcygwin.a). In those cases, it's possible for function + thunks from the second implib to be used but without the + head/tail objects, causing an improper import table. We detect + those cases and rename the "other" import libraries to match + the one the head/tail come from, so that the linker will sort + things nicely and produce a valid import table. */ + + LANG_FOR_EACH_INPUT_STATEMENT (is) + { + if (is->the_bfd->my_archive) + { + int idata2 = 0, reloc_count=0, is_imp = 0; + asection *sec; + + /* See if this is an import library thunk. */ + for (sec = is->the_bfd->sections; sec; sec = sec->next) + { + if (strcmp (sec->name, ".idata\$2") == 0) + idata2 = 1; + if (CONST_STRNEQ (sec->name, ".idata\$")) + is_imp = 1; + reloc_count += sec->reloc_count; + } + + if (is_imp && !idata2 && reloc_count) + { + /* It is, look for the reference to head and see if it's + from our own library. */ + for (sec = is->the_bfd->sections; sec; sec = sec->next) + { + int i; + long symsize; + long relsize; + asymbol **symbols; + arelent **relocs; + int nrelocs; + + symsize = bfd_get_symtab_upper_bound (is->the_bfd); + if (symsize < 1) + break; + relsize = bfd_get_reloc_upper_bound (is->the_bfd, sec); + if (relsize < 1) + break; + + symbols = xmalloc (symsize); + symsize = bfd_canonicalize_symtab (is->the_bfd, symbols); + if (symsize < 0) + { + einfo ("%X%P: unable to process symbols: %E"); + return; + } + + relocs = xmalloc ((size_t) relsize); + nrelocs = bfd_canonicalize_reloc (is->the_bfd, sec, + relocs, symbols); + if (nrelocs < 0) + { + free (relocs); + einfo ("%X%P: unable to process relocs: %E"); + return; + } + + for (i = 0; i < nrelocs; i++) + { + struct bfd_symbol *s; + struct bfd_link_hash_entry * blhe; + char *other_bfd_filename; + char *n; + + s = (relocs[i]->sym_ptr_ptr)[0]; + + if (s->flags & BSF_LOCAL) + continue; + + /* Thunk section with reloc to another bfd. */ + blhe = bfd_link_hash_lookup (link_info.hash, + s->name, + FALSE, FALSE, TRUE); + + if (blhe == NULL + || blhe->type != bfd_link_hash_defined) + continue; + + other_bfd_filename + = blhe->u.def.section->owner->my_archive + ? bfd_get_filename (blhe->u.def.section->owner->my_archive) + : bfd_get_filename (blhe->u.def.section->owner); + + if (strcmp (bfd_get_filename (is->the_bfd->my_archive), + other_bfd_filename) == 0) + continue; + + /* Rename this implib to match the other one. */ + n = xmalloc (strlen (other_bfd_filename) + 1); + strcpy (n, other_bfd_filename); + is->the_bfd->my_archive->filename = n; + } + + free (relocs); + /* Note - we do not free the symbols, + they are now cached in the BFD. */ + } + } + } + } + } + + { + int is_ms_arch = 0; + bfd *cur_arch = 0; + lang_input_statement_type *is2; + lang_input_statement_type *is3; + + /* Careful - this is a shell script. Watch those dollar signs! */ + /* Microsoft import libraries have every member named the same, + and not in the right order for us to link them correctly. We + must detect these and rename the members so that they'll link + correctly. There are three types of objects: the head, the + thunks, and the sentinel(s). The head is easy; it's the one + with idata2. We assume that the sentinels won't have relocs, + and the thunks will. It's easier than checking the symbol + table for external references. */ + LANG_FOR_EACH_INPUT_STATEMENT (is) + { + if (is->the_bfd->my_archive) + { + char *pnt; + bfd *arch = is->the_bfd->my_archive; + + if (cur_arch != arch) + { + cur_arch = arch; + is_ms_arch = 1; + + for (is3 = is; + is3 && is3->the_bfd->my_archive == arch; + is3 = (lang_input_statement_type *) is3->next) + { + /* A MS dynamic import library can also contain static + members, so look for the first element with a .dll + extension, and use that for the remainder of the + comparisons. */ + pnt = strrchr (is3->the_bfd->filename, '.'); + if (pnt != NULL && strcmp (pnt, ".dll") == 0) + break; + } + + if (is3 == NULL) + is_ms_arch = 0; + else + { + /* OK, found one. Now look to see if the remaining + (dynamic import) members use the same name. */ + for (is2 = is; + is2 && is2->the_bfd->my_archive == arch; + is2 = (lang_input_statement_type *) is2->next) + { + /* Skip static members, ie anything with a .obj + extension. */ + pnt = strrchr (is2->the_bfd->filename, '.'); + if (pnt != NULL && strcmp (pnt, ".obj") == 0) + continue; + + if (strcmp (is3->the_bfd->filename, + is2->the_bfd->filename)) + { + is_ms_arch = 0; + break; + } + } + } + } + + /* This fragment might have come from an .obj file in a Microsoft + import, and not an actual import record. If this is the case, + then leave the filename alone. */ + pnt = strrchr (is->the_bfd->filename, '.'); + + if (is_ms_arch && (strcmp (pnt, ".dll") == 0)) + { + int idata2 = 0, reloc_count=0; + asection *sec; + char *new_name, seq; + + for (sec = is->the_bfd->sections; sec; sec = sec->next) + { + if (strcmp (sec->name, ".idata\$2") == 0) + idata2 = 1; + reloc_count += sec->reloc_count; + } + + if (idata2) /* .idata2 is the TOC */ + seq = 'a'; + else if (reloc_count > 0) /* thunks */ + seq = 'b'; + else /* sentinel */ + seq = 'c'; + + new_name = xmalloc (strlen (is->the_bfd->filename) + 3); + sprintf (new_name, "%s.%c", is->the_bfd->filename, seq); + is->the_bfd->filename = new_name; + + new_name = xmalloc (strlen (is->filename) + 3); + sprintf (new_name, "%s.%c", is->filename, seq); + is->filename = new_name; + } + } + } + } +} + +static void +gld_${EMULATION_NAME}_before_allocation (void) +{ + before_allocation_default (); +} + +#ifdef DLL_SUPPORT +/* This is called when an input file isn't recognized as a BFD. We + check here for .DEF files and pull them in automatically. */ + +static int +saw_option (char *option) +{ + int i; + + for (i = 0; init[i].ptr; i++) + if (strcmp (init[i].symbol, option) == 0) + return init[i].inited; + return 0; +} +#endif /* DLL_SUPPORT */ + +static bfd_boolean +gld_${EMULATION_NAME}_unrecognized_file (lang_input_statement_type *entry ATTRIBUTE_UNUSED) +{ +#ifdef DLL_SUPPORT + const char *ext = entry->filename + strlen (entry->filename) - 4; + + if (strcmp (ext, ".def") == 0 || strcmp (ext, ".DEF") == 0) + { + pep_def_file = def_file_parse (entry->filename, pep_def_file); + + if (pep_def_file) + { + int i, buflen=0, len; + char *buf; + + for (i = 0; i < pep_def_file->num_exports; i++) + { + len = strlen (pep_def_file->exports[i].internal_name); + if (buflen < len + 2) + buflen = len + 2; + } + + buf = xmalloc (buflen); + + for (i = 0; i < pep_def_file->num_exports; i++) + { + struct bfd_link_hash_entry *h; + + sprintf (buf, "_%s", pep_def_file->exports[i].internal_name); + + h = bfd_link_hash_lookup (link_info.hash, buf, TRUE, TRUE, TRUE); + if (h == (struct bfd_link_hash_entry *) NULL) + einfo (_("%P%F: bfd_link_hash_lookup failed: %E\n")); + if (h->type == bfd_link_hash_new) + { + h->type = bfd_link_hash_undefined; + h->u.undef.abfd = NULL; + bfd_link_add_undef (link_info.hash, h); + } + } + free (buf); + + /* def_file_print (stdout, pep_def_file); */ + if (pep_def_file->is_dll == 1) + link_info.shared = 1; + + if (pep_def_file->base_address != (bfd_vma)(-1)) + { + pep.ImageBase + = pe_data (link_info.output_bfd)->pe_opthdr.ImageBase + = init[IMAGEBASEOFF].value + = pep_def_file->base_address; + init[IMAGEBASEOFF].inited = 1; + if (image_base_statement) + image_base_statement->exp = exp_assop ('=', "__image_base__", + exp_intop (pep.ImageBase)); + } + + if (pep_def_file->stack_reserve != -1 + && ! saw_option ("__size_of_stack_reserve__")) + { + pep.SizeOfStackReserve = pep_def_file->stack_reserve; + if (pep_def_file->stack_commit != -1) + pep.SizeOfStackCommit = pep_def_file->stack_commit; + } + if (pep_def_file->heap_reserve != -1 + && ! saw_option ("__size_of_heap_reserve__")) + { + pep.SizeOfHeapReserve = pep_def_file->heap_reserve; + if (pep_def_file->heap_commit != -1) + pep.SizeOfHeapCommit = pep_def_file->heap_commit; + } + return TRUE; + } + } +#endif + return FALSE; +} + +static bfd_boolean +gld_${EMULATION_NAME}_recognized_file (lang_input_statement_type *entry ATTRIBUTE_UNUSED) +{ +#ifdef DLL_SUPPORT +#ifdef TARGET_IS_i386pep + pep_dll_id_target ("pei-x86-64"); +#endif + if (pep_bfd_is_dll (entry->the_bfd)) + return pep_implied_import_dll (entry->filename); +#endif + return FALSE; +} + +static void +gld_${EMULATION_NAME}_finish (void) +{ + finish_default (); + +#ifdef DLL_SUPPORT + if (link_info.shared + || (!link_info.relocatable && pep_def_file->num_exports != 0)) + { + pep_dll_fill_sections (link_info.output_bfd, &link_info); + if (pep_implib_filename) + pep_dll_generate_implib (pep_def_file, pep_implib_filename); + } + + if (pep_out_def_filename) + pep_dll_generate_def_file (pep_out_def_filename); +#endif /* DLL_SUPPORT */ + + /* I don't know where .idata gets set as code, but it shouldn't be. */ + { + asection *asec = bfd_get_section_by_name (link_info.output_bfd, ".idata"); + + if (asec) + { + asec->flags &= ~SEC_CODE; + asec->flags |= SEC_DATA; + } + } +} + + +/* Place an orphan section. + + We use this to put sections in a reasonable place in the file, and + to ensure that they are aligned as required. + + We handle grouped sections here as well. A section named .foo$nn + goes into the output section .foo. All grouped sections are sorted + by name. + + Grouped sections for the default sections are handled by the + default linker script using wildcards, and are sorted by + sort_sections. */ + +static bfd_boolean +gld_${EMULATION_NAME}_place_orphan (asection *s) +{ + const char *secname; + const char *orig_secname; + char *dollar = NULL; + lang_output_section_statement_type *os; + lang_statement_list_type add_child; + + secname = bfd_get_section_name (s->owner, s); + + /* Look through the script to see where to place this section. */ + orig_secname = secname; + if (!link_info.relocatable + && (dollar = strchr (secname, '$')) != NULL) + { + size_t len = dollar - orig_secname; + char *newname = xmalloc (len + 1); + memcpy (newname, orig_secname, len); + newname[len] = '\0'; + secname = newname; + } + + os = lang_output_section_find (secname); + + lang_list_init (&add_child); + + if (os != NULL + && (os->bfd_section == NULL + || os->bfd_section->flags == 0 + || ((s->flags ^ os->bfd_section->flags) + & (SEC_LOAD | SEC_ALLOC)) == 0)) + { + /* We already have an output section statement with this + name, and its bfd section, if any, has compatible flags. + If the section already exists but does not have any flags set, + then it has been created by the linker, probably as a result of + a --section-start command line switch. */ + lang_add_section (&add_child, s, os); + } + else + { + static struct orphan_save hold[] = + { + { ".text", + SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE, + 0, 0, 0, 0 }, + { ".rdata", + SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA, + 0, 0, 0, 0 }, + { ".data", + SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_DATA, + 0, 0, 0, 0 }, + { ".bss", + SEC_ALLOC, + 0, 0, 0, 0 } + }; + enum orphan_save_index + { + orphan_text = 0, + orphan_rodata, + orphan_data, + orphan_bss + }; + static int orphan_init_done = 0; + struct orphan_save *place; + lang_output_section_statement_type *after; + etree_type *address; + + if (!orphan_init_done) + { + struct orphan_save *ho; + for (ho = hold; ho < hold + sizeof (hold) / sizeof (hold[0]); ++ho) + if (ho->name != NULL) + { + ho->os = lang_output_section_find (ho->name); + if (ho->os != NULL && ho->os->flags == 0) + ho->os->flags = ho->flags; + } + orphan_init_done = 1; + } + + /* Try to put the new output section in a reasonable place based + on the section name and section flags. */ + + place = NULL; + if ((s->flags & SEC_ALLOC) == 0) + ; + else if ((s->flags & (SEC_LOAD | SEC_HAS_CONTENTS)) == 0) + place = &hold[orphan_bss]; + else if ((s->flags & SEC_READONLY) == 0) + place = &hold[orphan_data]; + else if ((s->flags & SEC_CODE) == 0) + place = &hold[orphan_rodata]; + else + place = &hold[orphan_text]; + + after = NULL; + if (place != NULL) + { + if (place->os == NULL) + place->os = lang_output_section_find (place->name); + after = place->os; + if (after == NULL) + after = lang_output_section_find_by_flags (s, &place->os, NULL); + if (after == NULL) + /* *ABS* is always the first output section statement. */ + after = (&lang_output_section_statement.head + ->output_section_statement); + } + + /* Choose a unique name for the section. This will be needed if the + same section name appears in the input file with different + loadable or allocatable characteristics. */ + if (bfd_get_section_by_name (link_info.output_bfd, secname) != NULL) + { + static int count = 1; + secname = bfd_get_unique_section_name (link_info.output_bfd, + secname, &count); + if (secname == NULL) + einfo ("%F%P: place_orphan failed: %E\n"); + } + + /* All sections in an executable must be aligned to a page boundary. */ + address = exp_unop (ALIGN_K, exp_nameop (NAME, "__section_alignment__")); + os = lang_insert_orphan (s, secname, after, place, address, &add_child); + } + + { + lang_statement_union_type **pl = &os->children.head; + + if (dollar != NULL) + { + bfd_boolean found_dollar; + + /* The section name has a '$'. Sort it with the other '$' + sections. */ + found_dollar = FALSE; + for ( ; *pl != NULL; pl = &(*pl)->header.next) + { + lang_input_section_type *ls; + const char *lname; + + if ((*pl)->header.type != lang_input_section_enum) + continue; + + ls = &(*pl)->input_section; + + lname = bfd_get_section_name (ls->section->owner, ls->section); + if (strchr (lname, '$') == NULL) + { + if (found_dollar) + break; + } + else + { + found_dollar = TRUE; + if (strcmp (orig_secname, lname) < 0) + break; + } + } + } + + if (add_child.head != NULL) + { + add_child.head->header.next = *pl; + *pl = add_child.head; + } + } + + return TRUE; +} + +static bfd_boolean +gld_${EMULATION_NAME}_open_dynamic_archive + (const char *arch ATTRIBUTE_UNUSED, + search_dirs_type *search, + lang_input_statement_type *entry) +{ + static const struct + { + const char * format; + bfd_boolean use_prefix; + } + libname_fmt [] = + { + /* Preferred explicit import library for dll's. */ + { "lib%s.dll.a", FALSE }, + /* Alternate explicit import library for dll's. */ + { "%s.dll.a", FALSE }, + /* "libfoo.a" could be either an import lib or a static lib. + For backwards compatibility, libfoo.a needs to precede + libfoo.dll and foo.dll in the search. */ + { "lib%s.a", FALSE }, + /* The 'native' spelling of an import lib name is "foo.lib". */ + { "%s.lib", FALSE }, +#ifdef DLL_SUPPORT + /* Try "foo.dll" (preferred dll name, if specified). */ + { "%s%s.dll", TRUE }, +#endif + /* Try "libfoo.dll" (default preferred dll name). */ + { "lib%s.dll", FALSE }, + /* Finally try 'native' dll name "foo.dll". */ + { "%s.dll", FALSE }, + /* Note: If adding more formats to this table, make sure to check to + see if their length is longer than libname_fmt[0].format, and if + so, update the call to xmalloc() below. */ + { NULL, FALSE } + }; + static unsigned int format_max_len = 0; + const char * filename; + char * full_string; + char * base_string; + unsigned int i; + + + if (! entry->is_archive) + return FALSE; + + filename = entry->filename; + + if (format_max_len == 0) + /* We need to allow space in the memory that we are going to allocate + for the characters in the format string. Since the format array is + static we only need to calculate this information once. In theory + this value could also be computed statically, but this introduces + the possibility for a discrepancy and hence a possible memory + corruption. The lengths we compute here will be too long because + they will include any formating characters (%s) in the strings, but + this will not matter. */ + for (i = 0; libname_fmt[i].format; i++) + if (format_max_len < strlen (libname_fmt[i].format)) + format_max_len = strlen (libname_fmt[i].format); + + full_string = xmalloc (strlen (search->name) + + strlen (filename) + + format_max_len +#ifdef DLL_SUPPORT + + (pep_dll_search_prefix + ? strlen (pep_dll_search_prefix) : 0) +#endif + /* Allow for the terminating NUL and for the path + separator character that is inserted between + search->name and the start of the format string. */ + + 2); + + sprintf (full_string, "%s/", search->name); + base_string = full_string + strlen (full_string); + + for (i = 0; libname_fmt[i].format; i++) + { +#ifdef DLL_SUPPORT + if (libname_fmt[i].use_prefix) + { + if (!pep_dll_search_prefix) + continue; + sprintf (base_string, libname_fmt[i].format, pep_dll_search_prefix, filename); + } + else +#endif + sprintf (base_string, libname_fmt[i].format, filename); + + if (ldfile_try_open_bfd (full_string, entry)) + break; + } + + if (!libname_fmt[i].format) + { + free (full_string); + return FALSE; + } + + entry->filename = full_string; + + return TRUE; +} + +static int +gld_${EMULATION_NAME}_find_potential_libraries + (char *name, lang_input_statement_type *entry) +{ + return ldfile_open_file_search (name, entry, "", ".lib"); +} + +static char * +gld_${EMULATION_NAME}_get_script (int *isfile) +EOF +# Scripts compiled in. +# sed commands to quote an ld script as a C string. +sc="-f stringify.sed" + +fragment <> e${EMULATION_NAME}.c +echo ' ; else if (link_info.relocatable) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xr >> e${EMULATION_NAME}.c +echo ' ; else if (!config.text_read_only) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xbn >> e${EMULATION_NAME}.c +echo ' ; else if (!config.magic_demand_paged) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xn >> e${EMULATION_NAME}.c +if test -n "$GENERATE_AUTO_IMPORT_SCRIPT" ; then +echo ' ; else if (link_info.pei386_auto_import == 1) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xa >> e${EMULATION_NAME}.c +fi +echo ' ; else return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.x >> e${EMULATION_NAME}.c +echo '; }' >> e${EMULATION_NAME}.c + +fragment <the_bfd, + &link_info, + &m68hc11elf_add_stub_section)) + { + einfo ("%X%P: can not size stub section: %E\n"); + return; + } + } + + if (bank_window_name == 0) + return; + + /* The 'bank_window_name' memory region is a special region that describes + the memory bank window to access to paged memory. For 68HC12 + this is fixed and should be: + + window (rx) : ORIGIN = 0x8000, LENGTH = 16K + + But for 68HC11 this is board specific. The definition of such + memory region allows to control how this paged memory is accessed. */ + region = lang_memory_region_lookup (bank_window_name, FALSE); + + /* Check the length to see if it was defined in the script. */ + if (region->length != 0) + { + struct m68hc11_page_info *pinfo; + unsigned i; + + /* Get default values */ + m68hc11_elf_get_bank_parameters (&link_info); + pinfo = &m68hc11_elf_hash_table (&link_info)->pinfo; + + /* And override them with the region definition. */ + pinfo->bank_size = region->length; + pinfo->bank_shift = 0; + for (i = pinfo->bank_size; i != 0; i >>= 1) + pinfo->bank_shift++; + pinfo->bank_shift--; + pinfo->bank_size = 1L << pinfo->bank_shift; + pinfo->bank_mask = (1 << pinfo->bank_shift) - 1; + pinfo->bank_physical = region->origin; + pinfo->bank_physical_end = region->origin + pinfo->bank_size; + + if (pinfo->bank_size != region->length) + { + einfo (_("warning: the size of the 'window' memory region " + "is not a power of 2\n")); + einfo (_("warning: its size %d is truncated to %d\n"), + region->length, pinfo->bank_size); + } + } +} + +/* This is called before the input files are opened. We create a new + fake input file to hold the stub sections. */ + +static void +m68hc11elf_create_output_section_statements (void) +{ + stub_file = lang_add_input_file ("linker stubs", + lang_input_file_is_fake_enum, + NULL); + stub_file->the_bfd = bfd_create ("linker stubs", link_info.output_bfd); + if (stub_file->the_bfd == NULL + || !bfd_set_arch_mach (stub_file->the_bfd, + bfd_get_arch (link_info.output_bfd), + bfd_get_mach (link_info.output_bfd))) + { + einfo ("%X%P: can not create BFD %E\n"); + return; + } + + ldlang_add_file (stub_file); +} + + +struct hook_stub_info +{ + lang_statement_list_type add; + asection *input_section; +}; + +/* Traverse the linker tree to find the spot where the stub goes. */ + +static bfd_boolean +hook_in_stub (struct hook_stub_info *info, lang_statement_union_type **lp) +{ + lang_statement_union_type *l; + bfd_boolean ret; + + for (; (l = *lp) != NULL; lp = &l->header.next) + { + switch (l->header.type) + { + case lang_constructors_statement_enum: + ret = hook_in_stub (info, &constructor_list.head); + if (ret) + return ret; + break; + + case lang_output_section_statement_enum: + ret = hook_in_stub (info, + &l->output_section_statement.children.head); + if (ret) + return ret; + break; + + case lang_wild_statement_enum: + ret = hook_in_stub (info, &l->wild_statement.children.head); + if (ret) + return ret; + break; + + case lang_group_statement_enum: + ret = hook_in_stub (info, &l->group_statement.children.head); + if (ret) + return ret; + break; + + case lang_input_section_enum: + if (l->input_section.section == info->input_section + || strcmp (bfd_get_section_name (output_section, + l->input_section.section), + bfd_get_section_name (output_section, + info->input_section)) == 0) + { + /* We've found our section. Insert the stub immediately + before its associated input section. */ + *lp = info->add.head; + *(info->add.tail) = l; + return TRUE; + } + break; + + case lang_data_statement_enum: + case lang_reloc_statement_enum: + case lang_object_symbols_statement_enum: + case lang_output_statement_enum: + case lang_target_statement_enum: + case lang_input_statement_enum: + case lang_assignment_statement_enum: + case lang_padding_statement_enum: + case lang_address_statement_enum: + case lang_fill_statement_enum: + break; + + default: + FAIL (); + break; + } + } + return FALSE; +} + + +/* Call-back for elf32_m68hc11_size_stubs. */ + +/* Create a new stub section, and arrange for it to be linked + immediately before INPUT_SECTION. */ + +static asection * +m68hc11elf_add_stub_section (const char *stub_sec_name, + asection *tramp_section) +{ + asection *stub_sec; + flagword flags; + asection *output_section; + const char *secname; + lang_output_section_statement_type *os; + struct hook_stub_info info; + + stub_sec = bfd_make_section_anyway (stub_file->the_bfd, stub_sec_name); + if (stub_sec == NULL) + goto err_ret; + + flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE + | SEC_HAS_CONTENTS | SEC_RELOC | SEC_IN_MEMORY | SEC_KEEP); + if (!bfd_set_section_flags (stub_file->the_bfd, stub_sec, flags)) + goto err_ret; + + output_section = tramp_section->output_section; + secname = bfd_get_section_name (output_section->owner, output_section); + os = lang_output_section_find (secname); + + /* Try to put the new section at the same place as an existing + .tramp section. Such .tramp section exists in most cases and + contains the trampoline code. This way we put the generated trampoline + at the correct place. */ + info.input_section = tramp_section; + lang_list_init (&info.add); + lang_add_section (&info.add, stub_sec, os); + + if (info.add.head == NULL) + goto err_ret; + + if (hook_in_stub (&info, &os->children.head)) + return stub_sec; + + err_ret: + einfo ("%X%P: can not make stub section: %E\n"); + return NULL; +} + +/* Final emulation specific call. For the 68HC12 we use this opportunity + to build linker stubs. */ + +static void +m68hc11elf_finish (void) +{ + /* Now build the linker stubs. */ + if (stub_file->the_bfd->sections != NULL) + { + /* Call again the trampoline analyzer to initialize the trampoline + stubs with the correct symbol addresses. Since there could have + been relaxation, the symbol addresses that were found during + first call may no longer be correct. */ + if (!elf32_m68hc11_size_stubs (link_info.output_bfd, + stub_file->the_bfd, + &link_info, 0)) + { + einfo ("%X%P: can not size stub section: %E\n"); + return; + } + if (!elf32_m68hc11_build_stubs (link_info.output_bfd, &link_info)) + einfo ("%X%P: can not build stubs: %E\n"); + } + + gld${EMULATION_NAME}_finish (); +} + + +/* Avoid processing the fake stub_file in vercheck, stat_needed and + check_needed routines. */ + +static void (*real_func) (lang_input_statement_type *); + +static void m68hc11_for_each_input_file_wrapper (lang_input_statement_type *l) +{ + if (l != stub_file) + (*real_func) (l); +} + +static void +m68hc11_lang_for_each_input_file (void (*func) (lang_input_statement_type *)) +{ + real_func = func; + lang_for_each_input_file (&m68hc11_for_each_input_file_wrapper); +} + +#define lang_for_each_input_file m68hc11_lang_for_each_input_file + +EOF + +# Define some shell vars to insert bits of code into the standard elf +# parse_args and list_options functions. +# +PARSE_AND_LIST_PROLOGUE=' +#define OPTION_NO_TRAMPOLINE 300 +#define OPTION_BANK_WINDOW 301 +' + +# The options are repeated below so that no abbreviations are allowed. +# Otherwise -s matches stub-group-size +PARSE_AND_LIST_LONGOPTS=' + { "no-trampoline", no_argument, NULL, OPTION_NO_TRAMPOLINE }, + { "bank-window", required_argument, NULL, OPTION_BANK_WINDOW }, +' + +PARSE_AND_LIST_OPTIONS=' + fprintf (file, _( +" --no-trampoline Do not generate the far trampolines used to call\n" +" a far function using 'jsr' or 'bsr'.\n" +" --bank-window NAME Specify the name of the memory region describing\n" +" the layout of the memory bank window.\n" + )); +' + +PARSE_AND_LIST_ARGS_CASES=' + case OPTION_NO_TRAMPOLINE: + no_trampoline = 1; + break; + case OPTION_BANK_WINDOW: + bank_window_name = optarg; + break; +' + +# Put these extra m68hc11elf routines in ld_${EMULATION_NAME}_emulation +# +LDEMUL_BEFORE_ALLOCATION=m68hc11_elf_${EMULATION_NAME}_before_allocation +LDEMUL_FINISH=m68hc11elf_finish +LDEMUL_CREATE_OUTPUT_SECTION_STATEMENTS=m68hc11elf_create_output_section_statements Index: vanilla.em =================================================================== --- vanilla.em (nonexistent) +++ vanilla.em (revision 816) @@ -0,0 +1,87 @@ +# This shell script emits a C file. -*- C -*- +# It does some substitutions. +fragment <flags & (SEC_EXCLUDE | SEC_LOAD)) != SEC_LOAD) + return FALSE; + + /* Only care for sections we're going to load. */ + secname = s->name; + os = lang_output_section_find (secname); + + /* We have an output section by this name. Place the section inside it + (regardless of whether the linker script lists it as input). */ + if (os != NULL) + { + lang_add_section (&os->children, s, os); + return TRUE; + } + + /* If this section does not have .text-type section flags or there's no + MMO_TEXT_SECTION_NAME, we don't have anything to say. */ + if ((s->flags & (SEC_CODE | SEC_READONLY)) == 0) + return FALSE; + + if (hold_text.os == NULL) + hold_text.os = lang_output_section_find (hold_text.name); + + place = &hold_text; + if (hold_text.os != NULL) + after = hold_text.os; + else + after = &lang_output_section_statement.head->output_section_statement; + + /* If there's an output section by this name, we'll use it, regardless + of section flags, in contrast to what's done in elf32.em. */ + os = lang_insert_orphan (s, secname, after, place, NULL, NULL); + + /* We need an output section for .text as a root, so if there was none + (might happen with a peculiar linker script such as in "map + addresses", map-address.exp), we grab the output section created + above. */ + if (hold_text.os == NULL) + hold_text.os = os; + + return TRUE; +} + +/* Remove the spurious settings of SEC_RELOC that make it to the output at + link time. We are as confused as elflink.h:elf_bfd_final_link, and + paper over the bug similarly. */ + +static void +mmo_wipe_sec_reloc_flag (bfd *abfd, asection *sec, void *ptr ATTRIBUTE_UNUSED) +{ + bfd_set_section_flags (abfd, sec, + bfd_get_section_flags (abfd, sec) & ~SEC_RELOC); +} + +/* Iterate with bfd_map_over_sections over mmo_wipe_sec_reloc_flag... */ + +static void +mmo_finish (void) +{ + bfd_map_over_sections (link_info.output_bfd, mmo_wipe_sec_reloc_flag, NULL); + gld${EMULATION_NAME}_map_segments (FALSE); + finish_default (); +} + +/* To get on-demand global register allocation right, we need to parse the + relocs, like what happens when linking to ELF. It needs to be done + before all input sections are supposed to be present. When linking to + ELF, it's done when reading symbols. When linking to mmo, we do it + when all input files are seen, which is equivalent. */ + +static void +mmo_after_open (void) +{ + /* When there's a mismatch between the output format and the emulation + (using weird combinations like "-m mmo --oformat elf64-mmix" for + example), we'd count relocs twice because they'd also be counted + along the usual route for ELF-only linking, which would lead to an + internal accounting error. */ + if (bfd_get_flavour (link_info.output_bfd) != bfd_target_elf_flavour) + { + LANG_FOR_EACH_INPUT_STATEMENT (is) + { + if (bfd_get_flavour (is->the_bfd) == bfd_target_elf_flavour + && !_bfd_mmix_check_all_relocs (is->the_bfd, &link_info)) + einfo ("%X%P: Internal problems scanning %B after opening it", + is->the_bfd); + } + } +} +EOF + +LDEMUL_PLACE_ORPHAN=mmo_place_orphan +LDEMUL_FINISH=mmo_finish +LDEMUL_AFTER_OPEN=mmo_after_open Index: spu_ovl.S =================================================================== --- spu_ovl.S (nonexistent) +++ spu_ovl.S (revision 816) @@ -0,0 +1,430 @@ +/* Overlay manager for SPU. + + Copyright 2006, 2007, 2008 Free Software Foundation, Inc. + + This file is part of the GNU Binutils. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* MFC DMA defn's. */ +#define MFC_GET_CMD 0x40 +#define MFC_MAX_DMA_SIZE 0x4000 +#define MFC_TAG_UPDATE_ALL 2 +#define MFC_TAG_ID 0 + +/* Register usage. */ +#define reserved1 $75 +#define parm $75 +#define tab1 reserved1 +#define tab2 reserved1 +#define vma reserved1 +#define oldvma reserved1 +#define newmask reserved1 +#define map reserved1 + +#define reserved2 $76 +#define off1 reserved2 +#define off2 reserved2 +#define present1 reserved2 +#define present2 reserved2 +#define sz reserved2 +#define cmp reserved2 +#define add64 reserved2 +#define cgbits reserved2 +#define off3 reserved2 +#define off4 reserved2 +#define addr4 reserved2 +#define off5 reserved2 +#define tagstat reserved2 + +#define reserved3 $77 +#define size1 reserved3 +#define size2 reserved3 +#define rv3 reserved3 +#define ealo reserved3 +#define cmd reserved3 +#define off64 reserved3 +#define tab3 reserved3 +#define tab4 reserved3 +#define tab5 reserved3 + +#define reserved4 $78 +#define ovl reserved4 +#define rv2 reserved4 +#define rv5 reserved4 +#define cgshuf reserved4 +#define newovl reserved4 + +#define reserved5 $79 +#define target reserved5 + +#define save1 $72 +#define rv4 save1 +#define rv7 save1 +#define tagid save1 +#define maxsize save1 +#define pbyte save1 +#define pbit save1 + +#define save2 $73 +#define cur save2 +#define rv6 save2 +#define osize save2 +#define zovl save2 +#define oldovl save2 +#define newvma save2 + +#define save3 $74 +#define rv1 save3 +#define ea64 save3 +#define buf3 save3 +#define genwi save3 +#define newmap save3 +#define oldmask save3 + + + .text + .align 4 + .type __rv_pattern, @object + .size __rv_pattern, 16 +__rv_pattern: + .word 0x00010203, 0x10111213, 0x80808080, 0x80808080 + + .type __cg_pattern, @object + .size __cg_pattern, 16 +__cg_pattern: + .word 0x04050607, 0x80808080, 0x80808080, 0x80808080 + + .type __ovly_current, @object + .size __ovly_current, 16 +__ovly_current: + .space 16 + +/* + * __ovly_return - stub for returning from overlay functions. + * + * On entry the four slots of $lr are: + * __ovly_return, prev ovl index, caller return addr, undefined. + * + * Load the previous overlay and jump to the caller return address. + * Updates __ovly_current. + */ + .align 4 + .global __ovly_return + .type __ovly_return, @function +__ovly_return: + ila tab1, _ovly_table - 16 # 0,2 0 + shlqbyi ovl, $lr, 4 # 1,4 0 +#nop + shlqbyi target, $lr, 8 # 1,4 1 +#nop; lnop +#nop; lnop + shli off1, ovl, 4 # 0,4 4 +#lnop +#nop + hbr ovly_ret9, target # 1,15 5 +#nop; lnop +#nop; lnop +#nop + lqx vma, tab1, off1 # 1,6 8 +#nop; lnop +#nop; lnop +#nop; lnop +#nop; lnop +#nop; lnop +#nop + rotqbyi size1, vma, 4 # 1,4 14 +#nop + stqd save3, -48($sp) # 1,6 15 +#nop + stqd save2, -32($sp) # 1,6 16 +#nop + stqd save1, -16($sp) # 1,6 17 + andi present1, size1, 1 # 0,2 18 + stqr ovl, __ovly_current # 1,6 18 +#nop; lnop +#nop + brz present1, do_load # 1,4 20 +ovly_ret9: +#nop + bi target # 1,4 21 + +/* + * __ovly_load - copy an overlay partion to local store. + * + * On entry $75 points to a word consisting of the overlay index in + * the top 14 bits, and the target address in the bottom 18 bits. + * + * Sets up $lr to return via __ovly_return. + * Updates __ovly_current. + */ + .align 3 + .global __ovly_load + .type __ovly_load, @function +__ovly_load: +#if OVL_STUB_SIZE == 8 +######## +#nop + lqd target, 0(parm) # 1,6 -11 +#nop; lnop +#nop; lnop +#nop; lnop +#nop; lnop +#nop; lnop +#nop + rotqby target, target, parm # 1,4 -5 + ila tab2, _ovly_table - 16 # 0,2 -4 + stqd save3, -48($sp) # 1,6 -4 +#nop + stqd save2, -32($sp) # 1,6 -3 +#nop + stqd save1, -16($sp) # 1,6 -2 + rotmi ovl, target, -18 # 0,4 -1 + hbr ovly_load9, target # 1,15 -1 + ila rv1, __ovly_return # 0,2 0 +#lnop +#nop; lnop +#nop + lqr cur, __ovly_current # 1,6 2 + shli off2, ovl, 4 # 0,4 3 + stqr ovl, __ovly_current # 1,6 3 + ceq rv2, $lr, rv1 # 0,2 4 + lqr rv3, __rv_pattern # 1,6 4 +#nop; lnop +#nop; lnop +#nop + lqx vma, tab2, off2 # 1,6 7 +######## +#else /* OVL_STUB_SIZE == 16 */ +######## + ila tab2, _ovly_table - 16 # 0,2 0 + stqd save3, -48($sp) # 1,6 0 + ila rv1, __ovly_return # 0,2 1 + stqd save2, -32($sp) # 1,6 1 + shli off2, ovl, 4 # 0,4 2 + lqr cur, __ovly_current # 1,6 2 + nop + stqr ovl, __ovly_current # 1,6 3 + ceq rv2, $lr, rv1 # 0,2 4 + lqr rv3, __rv_pattern # 1,6 4 +#nop + hbr ovly_load9, target # 1,15 5 +#nop + lqx vma, tab2, off2 # 1,6 6 +#nop + stqd save1, -16($sp) # 1,6 7 +######## +#endif + +#nop; lnop +#nop; lnop +#nop + shufb rv4, rv1, cur, rv3 # 1,4 10 +#nop + fsmb rv5, rv2 # 1,4 11 +#nop + rotqmbyi rv6, $lr, -8 # 1,4 12 +#nop + rotqbyi size2, vma, 4 # 1,4 13 +#nop + lqd save3, -48($sp) # 1,6 14 +#nop; lnop + or rv7, rv4, rv6 # 0,2 16 + lqd save2, -32($sp) # 1,6 16 + andi present2, size2, 1 # 0,2 17 + lnop # 1,0 17 + selb $lr, rv7, $lr, rv5 # 0,2 18 + lqd save1, -16($sp) # 1,6 18 +#nop + brz present2, do_load # 1,4 19 +ovly_load9: +#nop + bi target # 1,4 20 + +/* If we get here, we are about to load a new overlay. + * "vma" contains the relevant entry from _ovly_table[]. + * extern struct { + * u32 vma; + * u32 size; + * u32 file_offset; + * u32 buf; + * } _ovly_table[]; + */ + .align 3 + .global __ovly_load_event + .type __ovly_load_event, @function +__ovly_load_event: +do_load: +#nop + rotqbyi sz, vma, 8 # 1,4 0 +#nop + rotqbyi osize, vma, 4 # 1,4 1 +#nop + lqa ea64, _EAR_ # 1,6 2 +#nop + lqr cgshuf, __cg_pattern # 1,6 3 + +/* We could predict the branch at the end of this loop by adding a few + instructions, and there are plenty of free cycles to do so without + impacting loop execution time. However, it doesn't make a great + deal of sense since we need to wait for the dma to complete anyway. */ +__ovly_xfer_loop: +#nop + rotqmbyi off64, sz, -4 # 1,4 4 +#nop; lnop +#nop; lnop +#nop; lnop + cg cgbits, ea64, off64 # 0,2 8 +#lnop +#nop; lnop +#nop + shufb add64, cgbits, cgbits, cgshuf # 1,4 10 +#nop; lnop +#nop; lnop +#nop; lnop + addx add64, ea64, off64 # 0,2 14 +#lnop + ila maxsize, MFC_MAX_DMA_SIZE # 0,2 15 + lnop + ori ea64, add64, 0 # 0,2 16 + rotqbyi ealo, add64, 4 # 1,4 16 + cgt cmp, osize, maxsize # 0,2 17 + wrch $MFC_LSA, vma # 1,6 17 +#nop; lnop + selb sz, osize, maxsize, cmp # 0,2 19 + wrch $MFC_EAH, ea64 # 1,6 19 + ila tagid, MFC_TAG_ID # 0,2 20 + wrch $MFC_EAL, ealo # 1,6 20 + ila cmd, MFC_GET_CMD # 0,2 21 + wrch $MFC_Size, sz # 1,6 21 + sf osize, sz, osize # 0,2 22 + wrch $MFC_TagId, tagid # 1,6 22 + a vma, vma, sz # 0,2 23 + wrch $MFC_Cmd, cmd # 1,6 23 +#nop + brnz osize, __ovly_xfer_loop # 1,4 24 + +/* Now update our data structions while waiting for DMA to complete. + Low bit of .size needs to be cleared on the _ovly_table entry + corresponding to the evicted overlay, and set on the entry for the + newly loaded overlay. Note that no overlay may in fact be evicted + as _ovly_buf_table[] starts with all zeros. Don't zap .size entry + for zero index! Also of course update the _ovly_buf_table entry. */ +#nop + lqr newovl, __ovly_current # 1,6 25 +#nop; lnop +#nop; lnop +#nop; lnop +#nop; lnop +#nop; lnop + shli off3, newovl, 4 # 0,4 31 +#lnop + ila tab3, _ovly_table - 16 # 0,2 32 +#lnop +#nop + fsmbi pbyte, 0x100 # 1,4 33 +#nop; lnop +#nop + lqx vma, tab3, off3 # 1,6 35 +#nop; lnop + andi pbit, pbyte, 1 # 0,2 37 + lnop +#nop; lnop +#nop; lnop +#nop; lnop + or newvma, vma, pbit # 0,2 41 + rotqbyi buf3, vma, 12 # 1,4 41 +#nop; lnop +#nop + stqx newvma, tab3, off3 # 1,6 43 +#nop; lnop + shli off4, buf3, 2 # 1,4 45 +#lnop + ila tab4, _ovly_buf_table - 4 # 0,2 46 +#lnop +#nop; lnop +#nop; lnop +#nop + lqx map, tab4, off4 # 1,6 49 +#nop + cwx genwi, tab4, off4 # 1,4 50 + a addr4, tab4, off4 # 0,2 51 +#lnop +#nop; lnop +#nop; lnop +#nop; lnop +#nop + rotqby oldovl, map, addr4 # 1,4 55 +#nop + shufb newmap, newovl, map, genwi # 0,4 56 +#if MFC_TAG_ID < 16 + ila newmask, 1 << MFC_TAG_ID # 0,2 57 +#else + ilhu newmask, 1 << (MFC_TAG_ID - 16) # 0,2 57 +#endif +#lnop +#nop; lnop +#nop; lnop + stqd newmap, 0(addr4) # 1,6 60 + +/* Save app's tagmask, wait for DMA complete, restore mask. */ + ila tagstat, MFC_TAG_UPDATE_ALL # 0,2 61 + rdch oldmask, $MFC_RdTagMask # 1,6 61 +#nop + wrch $MFC_WrTagMask, newmask # 1,6 62 +#nop + wrch $MFC_WrTagUpdate, tagstat # 1,6 63 +#nop + rdch tagstat, $MFC_RdTagStat # 1,6 64 +#nop + sync # 1,4 65 +/* Any hint prior to the sync is lost. A hint here allows the branch + to complete 15 cycles after the hint. With no hint the branch will + take 18 or 19 cycles. */ + ila tab5, _ovly_table - 16 # 0,2 66 + hbr do_load99, target # 1,15 66 + shli off5, oldovl, 4 # 0,4 67 + wrch $MFC_WrTagMask, oldmask # 1,6 67 + ceqi zovl, oldovl, 0 # 0,2 68 +#lnop +#nop; lnop +#nop + fsm zovl, zovl # 1,4 70 +#nop + lqx oldvma, tab5, off5 # 1,6 71 +#nop + lqd save3, -48($sp) # 1,6 72 +#nop; lnop + andc pbit, pbit, zovl # 0,2 74 + lqd save2, -32($sp) # 1,6 74 +#nop; lnop +#nop; lnop + andc oldvma, oldvma, pbit # 0,2 77 + lqd save1, -16($sp) # 1,6 77 +#nop; lnop + nop + stqx oldvma, tab5, off5 # 1,6 79 +#nop; lnop + + .global _ovly_debug_event + .type _ovly_debug_event, @function +_ovly_debug_event: + nop +/* Branch to target address. */ +do_load99: + bi target # 1,4 81 + + .size __ovly_load, . - __ovly_load Index: README =================================================================== --- README (nonexistent) +++ README (revision 816) @@ -0,0 +1,3 @@ +The files in this directory are sourced by genscripts.sh, after +setting some variables to substitute in, to produce +C source files that contain jump tables for each emulation. Index: ostring.sed =================================================================== --- ostring.sed (nonexistent) +++ ostring.sed (revision 816) @@ -0,0 +1,4 @@ +s/["\\]/\\&/g +s/$/\\n\\/ +1 s/^/"/ +$ s/$/n"/ Index: mmixelf.em =================================================================== --- mmixelf.em (nonexistent) +++ mmixelf.em (revision 816) @@ -0,0 +1,44 @@ +# This shell script emits a C file. -*- C -*- +# Copyright 2001, 2002, 2003, 2007 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +# This file is sourced from elf32.em and used to define MMIX and ELF +# specific things. First include what we have in common with mmo. + +source_em ${srcdir}/emultempl/mmix-elfnmmo.em + +fragment < +# +# This file is part of the GNU Binutils. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +# This file is sourced from elf32.em, and defines extra cr16-elf +# specific routines. +# +fragment <size - 8; + + /* If we start on a local register, we have too many global registers. + We treat this error as nonfatal (meaning processing will continue in + search for other errors), because it's a link error in the same way + as an undefined symbol. */ + if (regvma < 32 * 8) + { + einfo ("%X%P: Too many global registers: %u, max 223\n", + (unsigned) sec->size / 8); + regvma = 32 * 8; + } + + /* Set vma to correspond to first such register number * 8. */ + bfd_set_section_vma (link_info.output_bfd, sec, (bfd_vma) regvma); + + /* Simplify symbol output for the register section (without contents; + created for register symbols) by setting the output offset to 0. + This section is only present when there are register symbols. */ + sec = bfd_get_section_by_name (link_info.output_bfd, MMIX_REG_SECTION_NAME); + if (sec != NULL) + bfd_set_section_vma (abfd, sec, 0); + + if (!_bfd_mmix_after_linker_allocation (link_info.output_bfd, &link_info)) + { + /* This is a fatal error; make einfo call not return. */ + einfo ("%F%P: Can't finalize linker-allocated global registers\n"); + } +} +EOF + +LDEMUL_AFTER_ALLOCATION=mmix_after_allocation +LDEMUL_BEFORE_ALLOCATION=mmix_before_allocation Index: armelf.em =================================================================== --- armelf.em (nonexistent) +++ armelf.em (revision 816) @@ -0,0 +1,370 @@ +# This shell script emits a C file. -*- C -*- +# Copyright 1991, 1993, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, +# 2004, 2005, 2007, 2008 +# Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +# This file is sourced from elf32.em, and defines extra arm-elf +# specific routines. +# +test -z "$TARGET2_TYPE" && TARGET2_TYPE="rel" +fragment <the_bfd, & link_info); + } + } + + /* Call the standard elf routine. */ + gld${EMULATION_NAME}_after_open (); +} + +static void +arm_elf_set_bfd_for_interworking (lang_statement_union_type *statement) +{ + if (statement->header.type == lang_input_section_enum) + { + asection *i = statement->input_section.section; + + if (!((lang_input_statement_type *) i->owner->usrdata)->just_syms_flag + && (i->flags & SEC_EXCLUDE) == 0) + { + asection *output_section = i->output_section; + + ASSERT (output_section->owner == link_info.output_bfd); + + /* Don't attach the interworking stubs to a dynamic object, to + an empty section, etc. */ + if ((output_section->flags & SEC_HAS_CONTENTS) != 0 + && (i->flags & SEC_NEVER_LOAD) == 0 + && ! (i->owner->flags & DYNAMIC) + && ! i->owner->output_has_begun) + { + bfd_for_interwork = i->owner; + bfd_for_interwork->output_has_begun = TRUE; + } + } + } +} + +static void +arm_elf_before_allocation (void) +{ + bfd *tem; + + if (link_info.input_bfds != NULL) + { + /* The interworking bfd must be the last one in the link. */ + bfd_for_interwork = NULL; + for (tem = link_info.input_bfds; tem != NULL; tem = tem->link_next) + tem->output_has_begun = FALSE; + + lang_for_each_statement (arm_elf_set_bfd_for_interworking); + for (tem = link_info.input_bfds; tem != NULL; tem = tem->link_next) + tem->output_has_begun = FALSE; + + /* If bfd_for_interwork is NULL, then there are no loadable sections + with real contents to be linked, so we are not going to have to + create any interworking stubs, so it is OK not to call + bfd_elf32_arm_get_bfd_for_interworking. */ + if (bfd_for_interwork != NULL) + bfd_elf32_arm_get_bfd_for_interworking (bfd_for_interwork, &link_info); + } + + bfd_elf32_arm_set_byteswap_code (&link_info, byteswap_code); + + /* Choose type of VFP11 erratum fix, or warn if specified fix is unnecessary + due to architecture version. */ + bfd_elf32_arm_set_vfp11_fix (link_info.output_bfd, &link_info); + + /* We should be able to set the size of the interworking stub section. We + can't do it until later if we have dynamic sections, though. */ + if (! elf_hash_table (&link_info)->dynamic_sections_created) + { + /* Here we rummage through the found bfds to collect glue information. */ + LANG_FOR_EACH_INPUT_STATEMENT (is) + { + /* Initialise mapping tables for code/data. */ + bfd_elf32_arm_init_maps (is->the_bfd); + + if (!bfd_elf32_arm_process_before_allocation (is->the_bfd, + &link_info) + || !bfd_elf32_arm_vfp11_erratum_scan (is->the_bfd, &link_info)) + /* xgettext:c-format */ + einfo (_("Errors encountered processing file %s"), is->filename); + } + } + + /* Call the standard elf routine. */ + gld${EMULATION_NAME}_before_allocation (); + + /* We have seen it all. Allocate it, and carry on. */ + bfd_elf32_arm_allocate_interworking_sections (& link_info); +} + +static void +arm_elf_after_allocation (void) +{ + /* Call the standard elf routine. */ + after_allocation_default (); + + { + LANG_FOR_EACH_INPUT_STATEMENT (is) + { + /* Figure out where VFP11 erratum veneers (and the labels returning + from same) have been placed. */ + bfd_elf32_arm_vfp11_fix_veneer_locations (is->the_bfd, &link_info); + } + } +} + +static void +arm_elf_finish (void) +{ + struct bfd_link_hash_entry * h; + + /* Call the elf32.em routine. */ + gld${EMULATION_NAME}_finish (); + + if (thumb_entry_symbol) + { + h = bfd_link_hash_lookup (link_info.hash, thumb_entry_symbol, + FALSE, FALSE, TRUE); + } + else + { + struct elf_link_hash_entry * eh; + + if (!entry_symbol.name) + return; + + h = bfd_link_hash_lookup (link_info.hash, entry_symbol.name, + FALSE, FALSE, TRUE); + eh = (struct elf_link_hash_entry *)h; + if (!h || ELF_ST_TYPE(eh->type) != STT_ARM_TFUNC) + return; + } + + + if (h != (struct bfd_link_hash_entry *) NULL + && (h->type == bfd_link_hash_defined + || h->type == bfd_link_hash_defweak) + && h->u.def.section->output_section != NULL) + { + static char buffer[32]; + bfd_vma val; + + /* Special procesing is required for a Thumb entry symbol. The + bottom bit of its address must be set. */ + val = (h->u.def.value + + bfd_get_section_vma (link_info.output_bfd, + h->u.def.section->output_section) + + h->u.def.section->output_offset); + + val |= 1; + + /* Now convert this value into a string and store it in entry_symbol + where the lang_finish() function will pick it up. */ + buffer[0] = '0'; + buffer[1] = 'x'; + + sprintf_vma (buffer + 2, val); + + if (thumb_entry_symbol != NULL && entry_symbol.name != NULL + && entry_from_cmdline) + einfo (_("%P: warning: '--thumb-entry %s' is overriding '-e %s'\n"), + thumb_entry_symbol, entry_symbol.name); + entry_symbol.name = buffer; + } + else + einfo (_("%P: warning: cannot find thumb start symbol %s\n"), + thumb_entry_symbol); +} + +/* This is a convenient point to tell BFD about target specific flags. + After the output has been created, but before inputs are read. */ +static void +arm_elf_create_output_section_statements (void) +{ + bfd_elf32_arm_set_target_relocs (link_info.output_bfd, &link_info, + target1_is_rel, + target2_type, fix_v4bx, use_blx, + vfp11_denorm_fix, no_enum_size_warning, + pic_veneer); +} + +EOF + +# Define some shell vars to insert bits of code into the standard elf +# parse_args and list_options functions. +# +PARSE_AND_LIST_PROLOGUE=' +#define OPTION_THUMB_ENTRY 301 +#define OPTION_BE8 302 +#define OPTION_TARGET1_REL 303 +#define OPTION_TARGET1_ABS 304 +#define OPTION_TARGET2 305 +#define OPTION_FIX_V4BX 306 +#define OPTION_USE_BLX 307 +#define OPTION_VFP11_DENORM_FIX 308 +#define OPTION_NO_ENUM_SIZE_WARNING 309 +#define OPTION_PIC_VENEER 310 +#define OPTION_FIX_V4BX_INTERWORKING 311 +' + +PARSE_AND_LIST_SHORTOPTS=p + +PARSE_AND_LIST_LONGOPTS=' + { "no-pipeline-knowledge", no_argument, NULL, '\'p\''}, + { "thumb-entry", required_argument, NULL, OPTION_THUMB_ENTRY}, + { "be8", no_argument, NULL, OPTION_BE8}, + { "target1-rel", no_argument, NULL, OPTION_TARGET1_REL}, + { "target1-abs", no_argument, NULL, OPTION_TARGET1_ABS}, + { "target2", required_argument, NULL, OPTION_TARGET2}, + { "fix-v4bx", no_argument, NULL, OPTION_FIX_V4BX}, + { "fix-v4bx-interworking", no_argument, NULL, OPTION_FIX_V4BX_INTERWORKING}, + { "use-blx", no_argument, NULL, OPTION_USE_BLX}, + { "vfp11-denorm-fix", required_argument, NULL, OPTION_VFP11_DENORM_FIX}, + { "no-enum-size-warning", no_argument, NULL, OPTION_NO_ENUM_SIZE_WARNING}, + { "pic-veneer", no_argument, NULL, OPTION_PIC_VENEER}, +' + +PARSE_AND_LIST_OPTIONS=' + fprintf (file, _(" --thumb-entry= Set the entry point to be Thumb symbol \n")); + fprintf (file, _(" --be8 Oputput BE8 format image\n")); + fprintf (file, _(" --target1=rel Interpret R_ARM_TARGET1 as R_ARM_REL32\n")); + fprintf (file, _(" --target1=abs Interpret R_ARM_TARGET1 as R_ARM_ABS32\n")); + fprintf (file, _(" --target2= Specify definition of R_ARM_TARGET2\n")); + fprintf (file, _(" --fix-v4bx Rewrite BX rn as MOV pc, rn for ARMv4\n")); + fprintf (file, _(" --fix-v4bx-interworking Rewrite BX rn branch to ARMv4 interworking veneer\n")); + fprintf (file, _(" --use-blx Enable use of BLX instructions\n")); + fprintf (file, _(" --vfp11-denorm-fix Specify how to fix VFP11 denorm erratum\n")); + fprintf (file, _(" --no-enum-size-warning Don'\''t warn about objects with incompatible" + " enum sizes\n")); + fprintf (file, _(" --pic-veneer Always generate PIC interworking veneers\n")); +' + +PARSE_AND_LIST_ARGS_CASES=' + case '\'p\'': + /* Only here for backwards compatibility. */ + break; + + case OPTION_THUMB_ENTRY: + thumb_entry_symbol = optarg; + break; + + case OPTION_BE8: + byteswap_code = 1; + break; + + case OPTION_TARGET1_REL: + target1_is_rel = 1; + break; + + case OPTION_TARGET1_ABS: + target1_is_rel = 0; + break; + + case OPTION_TARGET2: + target2_type = optarg; + break; + + case OPTION_FIX_V4BX: + fix_v4bx = 1; + break; + + case OPTION_FIX_V4BX_INTERWORKING: + fix_v4bx = 2; + break; + + case OPTION_USE_BLX: + use_blx = 1; + break; + + case OPTION_VFP11_DENORM_FIX: + if (strcmp (optarg, "none") == 0) + vfp11_denorm_fix = BFD_ARM_VFP11_FIX_NONE; + else if (strcmp (optarg, "scalar") == 0) + vfp11_denorm_fix = BFD_ARM_VFP11_FIX_SCALAR; + else if (strcmp (optarg, "vector") == 0) + vfp11_denorm_fix = BFD_ARM_VFP11_FIX_VECTOR; + else + einfo (_("Unrecognized VFP11 fix type '\''%s'\''.\n"), optarg); + break; + + case OPTION_NO_ENUM_SIZE_WARNING: + no_enum_size_warning = 1; + break; + + case OPTION_PIC_VENEER: + pic_veneer = 1; + break; +' + +# We have our own after_open and before_allocation functions, but they call +# the standard routines, so give them a different name. +LDEMUL_AFTER_OPEN=arm_elf_after_open +LDEMUL_BEFORE_ALLOCATION=arm_elf_before_allocation +LDEMUL_AFTER_ALLOCATION=arm_elf_after_allocation +LDEMUL_CREATE_OUTPUT_SECTION_STATEMENTS=arm_elf_create_output_section_statements + +# Replace the elf before_parse function with our own. +LDEMUL_BEFORE_PARSE=gld"${EMULATION_NAME}"_before_parse + +# Call the extra arm-elf function +LDEMUL_FINISH=arm_elf_finish Index: needrelax.em =================================================================== --- needrelax.em (nonexistent) +++ needrelax.em (revision 816) @@ -0,0 +1,39 @@ +# This shell script emits a C file. -*- C -*- +# Copyright 2001, 2002, 2003, 2007 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +# This file is sourced from elf32.em. It is used by targets for +# which relaxation is not just an optimization, but for correctness. + +LDEMUL_BEFORE_ALLOCATION=need_relax_${EMULATION_NAME}_before_allocation + +fragment <output_section_statement; + os != NULL; + os = os->next) + { + if (os->constraint == SPECIAL && strcmp (os->name, ".plt") == 0) + { + if (num_plt < 2) + plt_os[num_plt] = os; + ++num_plt; + } + } + + if (num_plt == 2) + { + plt_os[0]->constraint = elf64_alpha_use_secureplt ? 0 : -1; + plt_os[1]->constraint = elf64_alpha_use_secureplt ? -1 : 0; + } + } + + gld${EMULATION_NAME}_after_open (); +} + +static void +alpha_after_parse (void) +{ + if (limit_32bit && !link_info.shared && !link_info.relocatable) + lang_section_start (".interp", + exp_binop ('+', + exp_intop (ALPHA_TEXT_START_32BIT), + exp_nameop (SIZEOF_HEADERS, NULL)), + NULL); +} + +static void +alpha_before_allocation (void) +{ + /* Call main function; we're just extending it. */ + gld${EMULATION_NAME}_before_allocation (); + + /* Add -relax if -O, not -r, and not explicitly disabled. */ + if (link_info.optimize && !link_info.relocatable && !disable_relaxation) + command_line.relax = TRUE; +} + +static void +alpha_finish (void) +{ + if (limit_32bit) + elf_elfheader (link_info.output_bfd)->e_flags |= EF_ALPHA_32BIT; + + gld${EMULATION_NAME}_finish (); +} +EOF + +# Define some shell vars to insert bits of code into the standard elf +# parse_args and list_options functions. +# +PARSE_AND_LIST_PROLOGUE=' +#define OPTION_TASO 300 +#define OPTION_NO_RELAX (OPTION_TASO + 1) +#define OPTION_SECUREPLT (OPTION_NO_RELAX + 1) +#define OPTION_NO_SECUREPLT (OPTION_SECUREPLT + 1) +' + +PARSE_AND_LIST_LONGOPTS=' + { "taso", no_argument, NULL, OPTION_TASO }, + { "no-relax", no_argument, NULL, OPTION_NO_RELAX }, + { "secureplt", no_argument, NULL, OPTION_SECUREPLT }, + { "no-secureplt", no_argument, NULL, OPTION_NO_SECUREPLT }, +' + +PARSE_AND_LIST_OPTIONS=' + fprintf (file, _("\ + --taso Load executable in the lower 31-bit addressable\n\ + virtual address range.\n\ + --no-relax Do not relax call and gp sequences.\n\ + --secureplt Force PLT in text segment.\n\ + --no-secureplt Force PLT in data segment.\n\ +")); +' + +PARSE_AND_LIST_ARGS_CASES=' + case OPTION_TASO: + limit_32bit = 1; + break; + case OPTION_NO_RELAX: + disable_relaxation = TRUE; + break; + case OPTION_SECUREPLT: + elf64_alpha_use_secureplt = TRUE; + break; + case OPTION_NO_SECUREPLT: + elf64_alpha_use_secureplt = FALSE; + break; +' + +# Put these extra alpha routines in ld_${EMULATION_NAME}_emulation +# +LDEMUL_AFTER_OPEN=alpha_after_open +LDEMUL_AFTER_PARSE=alpha_after_parse +LDEMUL_BEFORE_ALLOCATION=alpha_before_allocation +LDEMUL_FINISH=alpha_finish Index: generic.em =================================================================== --- generic.em (nonexistent) +++ generic.em (revision 816) @@ -0,0 +1,149 @@ +# This shell script emits a C file. -*- C -*- +# It does some substitutions. +fragment <> e${EMULATION_NAME}.c +echo ' ; else if (link_info.relocatable) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xr >> e${EMULATION_NAME}.c +echo ' ; else if (!config.text_read_only) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xbn >> e${EMULATION_NAME}.c +echo ' ; else if (!config.magic_demand_paged) return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.xn >> e${EMULATION_NAME}.c +echo ' ; else return' >> e${EMULATION_NAME}.c +sed $sc ldscripts/${EMULATION_NAME}.x >> e${EMULATION_NAME}.c +echo '; }' >> e${EMULATION_NAME}.c + +else +# Scripts read from the filesystem. + +fragment <output_section_statement; + os != NULL; + os = os->next) + { + if (os->constraint == SPECIAL && strcmp (os->name, ".plt") == 0) + { + if (num_plt < 2) + plt_os[num_plt] = os; + ++num_plt; + } + if (os->constraint == SPECIAL && strcmp (os->name, ".got") == 0) + { + if (num_got < 2) + got_os[num_got] = os; + ++num_got; + } + } + + keep_new = new_plt == 1 ? 0 : -1; + if (num_plt == 2) + { + plt_os[0]->constraint = keep_new; + plt_os[1]->constraint = ~keep_new; + } + if (num_got == 2) + { + if (old_got) + keep_new = -1; + got_os[0]->constraint = keep_new; + got_os[1]->constraint = ~keep_new; + } + } + + gld${EMULATION_NAME}_after_open (); +} + +static void +ppc_before_allocation (void) +{ + if (is_ppc_elf (link_info.output_bfd)) + { + if (ppc_elf_tls_setup (link_info.output_bfd, &link_info) && !notlsopt) + { + if (!ppc_elf_tls_optimize (link_info.output_bfd, &link_info)) + { + einfo ("%X%P: TLS problem %E\n"); + return; + } + } + } + gld${EMULATION_NAME}_before_allocation (); +} + +EOF + +if grep -q 'ld_elf32_spu_emulation' ldemul-list.h; then + fragment <

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.