From 252b5132c753830d5fd56823373aed85f2a0db63 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 3 May 1999 07:29:11 +0000 Subject: 19990502 sourceware import --- ld/emultempl/README | 3 + ld/emultempl/aix.em | 1054 +++++++++++++++++++++++++++++++++++++++ ld/emultempl/armcoff.em | 226 +++++++++ ld/emultempl/armelf.em | 168 +++++++ ld/emultempl/armelf_oabi.em | 175 +++++++ ld/emultempl/beos.em | 837 +++++++++++++++++++++++++++++++ ld/emultempl/elf32.em | 1143 +++++++++++++++++++++++++++++++++++++++++++ ld/emultempl/generic.em | 118 +++++ ld/emultempl/gld960.em | 176 +++++++ ld/emultempl/gld960c.em | 192 ++++++++ ld/emultempl/hppaelf.em | 283 +++++++++++ ld/emultempl/linux.em | 208 ++++++++ ld/emultempl/lnk960.em | 327 +++++++++++++ ld/emultempl/mipsecoff.em | 229 +++++++++ ld/emultempl/pe.em | 1104 +++++++++++++++++++++++++++++++++++++++++ ld/emultempl/stringify.sed | 4 + ld/emultempl/sunos.em | 1037 +++++++++++++++++++++++++++++++++++++++ ld/emultempl/vanilla.em | 69 +++ 18 files changed, 7353 insertions(+) create mode 100644 ld/emultempl/README create mode 100644 ld/emultempl/aix.em create mode 100644 ld/emultempl/armcoff.em create mode 100644 ld/emultempl/armelf.em create mode 100644 ld/emultempl/armelf_oabi.em create mode 100644 ld/emultempl/beos.em create mode 100644 ld/emultempl/elf32.em create mode 100644 ld/emultempl/generic.em create mode 100644 ld/emultempl/gld960.em create mode 100644 ld/emultempl/gld960c.em create mode 100644 ld/emultempl/hppaelf.em create mode 100644 ld/emultempl/linux.em create mode 100644 ld/emultempl/lnk960.em create mode 100644 ld/emultempl/mipsecoff.em create mode 100644 ld/emultempl/pe.em create mode 100644 ld/emultempl/stringify.sed create mode 100644 ld/emultempl/sunos.em create mode 100644 ld/emultempl/vanilla.em (limited to 'ld/emultempl') diff --git a/ld/emultempl/README b/ld/emultempl/README new file mode 100644 index 0000000..30ec0ab --- /dev/null +++ b/ld/emultempl/README @@ -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. diff --git a/ld/emultempl/aix.em b/ld/emultempl/aix.em new file mode 100644 index 0000000..c77857f --- /dev/null +++ b/ld/emultempl/aix.em @@ -0,0 +1,1054 @@ +# This shell script emits a C file. -*- C -*- +# It does some substitutions. +cat >e${EMULATION_NAME}.c < + AIX support by Ian Lance Taylor + +This file is part of GLD, the Gnu Linker. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#define TARGET_IS_${EMULATION_NAME} + +#include "bfd.h" +#include "sysdep.h" +#include "libiberty.h" +#include "getopt.h" +#include "obstack.h" +#include "bfdlink.h" + +#include + +#include "ld.h" +#include "ldmain.h" +#include "ldemul.h" +#include "ldfile.h" +#include "ldmisc.h" +#include "ldexp.h" +#include "ldlang.h" +#include "ldctor.h" +#include "ldgram.h" + +static void gld${EMULATION_NAME}_before_parse PARAMS ((void)); +static int gld${EMULATION_NAME}_parse_args PARAMS ((int, char **)); +static void gld${EMULATION_NAME}_after_open PARAMS ((void)); +static void gld${EMULATION_NAME}_before_allocation PARAMS ((void)); +static void gld${EMULATION_NAME}_read_file PARAMS ((const char *, boolean)); +static void gld${EMULATION_NAME}_free PARAMS ((PTR)); +static void gld${EMULATION_NAME}_find_relocs + PARAMS ((lang_statement_union_type *)); +static void gld${EMULATION_NAME}_find_exp_assignment PARAMS ((etree_type *)); +static char *gld${EMULATION_NAME}_get_script PARAMS ((int *isfile)); + +/* 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; + boolean syscall; +}; + +static struct export_symbol_list *export_symbols; + +/* This routine is called before anything else is done. */ + +static void +gld${EMULATION_NAME}_before_parse() +{ +#ifndef TARGET_ /* I.e., if not generic. */ + ldfile_output_architecture = bfd_arch_${ARCH}; +#endif /* not TARGET_ */ + config.has_shared = true; +} + +/* Handle AIX specific options. */ + +static int +gld${EMULATION_NAME}_parse_args (argc, argv) + int argc; + char **argv; +{ + int prevoptind = optind; + int prevopterr = opterr; + int indx; + int longind; + int optc; + long val; + char *end; + +#define OPTION_IGNORE (300) +#define OPTION_AUTOIMP (OPTION_IGNORE + 1) +#define OPTION_ERNOTOK (OPTION_AUTOIMP + 1) +#define OPTION_EROK (OPTION_ERNOTOK + 1) +#define OPTION_EXPORT (OPTION_EROK + 1) +#define OPTION_IMPORT (OPTION_EXPORT + 1) +#define OPTION_LOADMAP (OPTION_IMPORT + 1) +#define OPTION_MAXDATA (OPTION_LOADMAP + 1) +#define OPTION_MAXSTACK (OPTION_MAXDATA + 1) +#define OPTION_MODTYPE (OPTION_MAXSTACK + 1) +#define OPTION_NOAUTOIMP (OPTION_MODTYPE + 1) +#define OPTION_NOSTRCMPCT (OPTION_NOAUTOIMP + 1) +#define OPTION_PD (OPTION_NOSTRCMPCT + 1) +#define OPTION_PT (OPTION_PD + 1) +#define OPTION_STRCMPCT (OPTION_PT + 1) +#define OPTION_UNIX (OPTION_STRCMPCT + 1) + + static struct option longopts[] = { + {"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}, + {"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}, + {"bS", required_argument, NULL, OPTION_MAXSTACK}, + {"bso", no_argument, NULL, OPTION_AUTOIMP}, + {"bstrcmpct", no_argument, NULL, OPTION_STRCMPCT}, + {"btextro", no_argument, &textro, 1}, + {"static", no_argument, NULL, OPTION_NOAUTOIMP}, + {"unix", no_argument, NULL, OPTION_UNIX}, + {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. */ + + /* 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 && strncmp (argv[indx], "-b", 2) == 0) + { + char *s; + + for (s = argv[indx]; *s != '\0'; s++) + { + if (*s == ':') + { + *s = '='; + break; + } + } + } + + /* We add s and u so to the short options list so that -s and -u on + the command line do not match -static and -unix. */ + + opterr = 0; + optc = getopt_long_only (argc, argv, "-D:H:KT:zsu", longopts, &longind); + opterr = prevopterr; + + switch (optc) + { + case 's': + case 'u': + default: + optind = prevoptind; + return 0; + + case 0: + /* Long option which just sets a flag. */ + break; + + case 'D': + val = strtol (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)); + break; + + case 'H': + val = strtoul (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 = strtoul (optarg, &end, 0); + if (*end != '\0') + { + optind = prevoptind; + return 0; + } + lang_section_start (".text", exp_intop (val)); + break; + + case OPTION_IGNORE: + 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 = strtoul (optarg, &end, 0); + if (*end != '\0') + einfo ("%P: warning: ignoring invalid -bmaxdata number %s\n", + optarg); + else + maxdata = val; + break; + + case OPTION_MAXSTACK: + val = strtoul (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 = strtoul (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); + } + 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 = strtoul (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); + } + break; + + case OPTION_STRCMPCT: + link_info.traditional_format = false; + break; + + case OPTION_UNIX: + unix_ld = true; + break; + } + + return 1; +} + +/* 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 boolean +gld${EMULATION_NAME}_unrecognized_file (entry) + lang_input_statement_type *entry; +{ + FILE *e; + 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 () +{ + boolean r; + struct set_info *p; + + /* Call ldctor_build_sets, after pretending that this is a + relocateable 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.relocateable; + if (strstr (bfd_get_target (output_bfd), "xcoff") != NULL) + link_info.relocateable = true; + ldctor_build_sets (); + link_info.relocateable = 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 (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 () +{ + struct filelist *fl; + struct export_symbol_list *el; + char *libpath; + asection *special_sections[6]; + 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 (output_bfd, &link_info, h, el->syscall)) + 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); + + /* We need to build LIBPATH from the -L arguments. If any -rpath + arguments were used, though, we use -rpath instead, as a GNU + extension. */ + if (command_line.rpath != NULL) + libpath = command_line.rpath; + else if (search_head == NULL) + libpath = (char *) ""; + else + { + size_t len; + search_dirs_type *search; + + 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 (output_bfd, &link_info, libpath, + entry_symbol, file_align, + maxstack, maxdata, + gc && ! unix_ld ? true : false, + modtype, + textro ? true : false, + unix_ld, + special_sections)) + 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 < 6; i++) + { + asection *sec; + lang_output_section_statement_type *os; + lang_statement_union_type **pls; + lang_input_section_type *is; + const char *oname; + 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)->next) + { + if ((*pls)->header.type == lang_input_section_enum + && (*pls)->input_section.section == sec) + { + is = (lang_input_section_type *) *pls; + *pls = (*pls)->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)->next) + { + if ((*pwls)->header.type == lang_input_section_enum + && (*pwls)->input_section.section == sec) + { + is = (lang_input_section_type *) *pwls; + *pwls = (*pwls)->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 0: + /* _text */ + oname = ".text"; + start = true; + break; + case 1: + /* _etext */ + oname = ".text"; + start = false; + break; + case 2: + /* _data */ + oname = ".data"; + start = true; + break; + case 3: + /* _edata */ + oname = ".data"; + start = false; + break; + case 4: + case 5: + /* _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); + } + } +} + +/* 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 parse_args emulation routine. */ + +static void +gld${EMULATION_NAME}_read_file (filename, import) + const char *filename; + boolean import; +{ + struct obstack *o; + FILE *f; + int lineno; + int c; + 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; + while ((c = getc (f)) != EOF) + { + char *s; + char *symname; + boolean syscall; + 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 ((unsigned char) *s)) + ++s; + if (*s == '\0' + || *s == '*' + || (*s == '#' && s[1] == ' ') + || (! import && *s == '#' && s[1] == '!')) + { + obstack_free (o, obstack_base (o)); + continue; + } + + if (*s == '#' && s[1] == '!') + { + s += 2; + while (isspace ((unsigned char) *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 ((unsigned char) *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 ((unsigned char) 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; + } + + /* This is a symbol to be imported or exported. */ + symname = s; + syscall = false; + address = (bfd_vma) -1; + + while (! isspace ((unsigned char) *s) && *s != '\0') + ++s; + if (*s != '\0') + { + char *se; + + *s++ = '\0'; + + while (isspace ((unsigned char) *s)) + ++s; + + se = s; + while (! isspace ((unsigned char) *se) && *se != '\0') + ++se; + if (*se != '\0') + { + *se++ = '\0'; + while (isspace ((unsigned char) *se)) + ++se; + if (*se != '\0') + einfo ("%s%d: warning: syntax error in import/export file\n", + filename, lineno); + } + + if (s == se) + { + /* There was no address after all. */ + } + else if (strcasecmp (s, "svc") == 0 + || strcasecmp (s, "syscall") == 0) + syscall = true; + else + { + char *end; + + address = strtoul (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 = buystring (symname); + n->syscall = syscall; + 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 (output_bfd, &link_info, h, + address, imppath, impfile, + impmember)) + 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 (p) + PTR 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 (s) + 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 (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 (exp) + 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 (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(isfile) + 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/stringify.sed" + +cat >>e${EMULATION_NAME}.c <> e${EMULATION_NAME}.c +echo ' ; else if (link_info.relocateable == true) 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. + +cat >>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <e${EMULATION_NAME}.c <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); +} + +static void +gld${EMULATION_NAME}_after_open () +{ + LANG_FOR_EACH_INPUT_STATEMENT (is) + { + if (bfd_arm_get_bfd_for_interworking (is->the_bfd, & link_info)) + break; + } +} + +static char * +gld${EMULATION_NAME}_get_script (isfile) + 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/stringify.sed" + +cat >>e${EMULATION_NAME}.c <> e${EMULATION_NAME}.c +echo ' ; else if (link_info.relocateable == true) 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. + +cat >>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <e${EMULATION_NAME}.c <the_bfd, &link_info)) + { + /* xgettext:c-format */ + einfo (_("Errors encountered processing file %s"), is->filename); + } + } + } + + /* We have seen it all. Allocate it, and carry on */ + bfd_elf32_arm_allocate_interworking_sections (& link_info); +} + +static void +gld${EMULATION_NAME}_after_open () +{ + + LANG_FOR_EACH_INPUT_STATEMENT (is) + { + /* The interworking bfd must be the last one to be processed */ + if (!is->next) + bfd_elf32_arm_get_bfd_for_interworking (is->the_bfd, & link_info); + } +} + +static char * +gld${EMULATION_NAME}_get_script (isfile) + 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/stringify.sed" + +cat >>e${EMULATION_NAME}.c <> e${EMULATION_NAME}.c +echo ' ; else if (link_info.relocateable == true) 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. + +cat >>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <e${EMULATION_NAME}.c <the_bfd, &link_info)) + { + /* xgettext:c-format */ + einfo (_("Errors encountered processing file %s"), is->filename); + } + } + } + + /* We have seen it all. Allocate it, and carry on */ + bfd_elf32_arm_allocate_interworking_sections (& link_info); +} + +static void +gld${EMULATION_NAME}_after_open () +{ + + LANG_FOR_EACH_INPUT_STATEMENT (is) + { + /* The interworking bfd must be the last one to be processed */ + if (!is->next) + bfd_elf32_arm_get_bfd_for_interworking (is->the_bfd, & link_info); + } +} + +static char * +gld${EMULATION_NAME}_get_script (isfile) + 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/stringify.sed" + +cat >>e${EMULATION_NAME}.c <> e${EMULATION_NAME}.c +echo ' ; else if (link_info.relocateable == true) 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. + +cat >>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <e${EMULATION_NAME}.c <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() +{ + /* 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(output_bfd)->pe) + { + einfo ("%F%P: PE operations on non PE file.\n"); + } + + pe_data(output_bfd)->pe_opthdr = pe; + pe_data(output_bfd)->dll = init[DLLOFF].value; + +} + +/* Callback functions for qsort in sort_sections. */ + +static int +sort_by_file_name (a, b) + void *a; + void *b; +{ + lang_statement_union_type **ra = a; + lang_statement_union_type **rb = b; + int i, a_sec, b_sec; + + i = strcmp ((*ra)->input_section.ifile->the_bfd->my_archive->filename, + (*rb)->input_section.ifile->the_bfd->my_archive->filename); + if (i != 0) + return i; + + i = strcmp ((*ra)->input_section.ifile->filename, + (*rb)->input_section.ifile->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.ifile->the_bfd, + (*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.ifile->the_bfd); + + if (! bfd_get_section_contents ((*rb)->input_section.ifile->the_bfd, + (*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.ifile->the_bfd); + + 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 (a, b) + void *a; + void *b; +{ + lang_statement_union_type **ra = a; + lang_statement_union_type **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 ((strncmp ((*ra)->input_section.section->name, ".stab", 5) == 0) + && (strncmp ((*rb)->input_section.section->name, ".stab", 5) != 0)) + return 1; + return i; + } + 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 (startptr, next_after, count, sort_func) + lang_statement_union_type **startptr,*next_after; + int count; + int (*sort_func) (); +{ + 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->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 (s) + lang_statement_union_type *s; +{ + for (; s ; s = s->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; + + /* Is this the .idata section? */ + if (s->wild_statement.section_name != NULL + && strncmp (s->wild_statement.section_name, ".idata", 6) == 0) + { + /* 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.ifile->the_bfd->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->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 (s->wild_statement.section_name != NULL) + { + char *q = strchr (s->wild_statement.section_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->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() +{ + 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); +} + +/* 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. */ + +/*ARGSUSED*/ +static boolean +gld${EMULATION_NAME}_place_orphan (file, s) + lang_input_statement_type *file; + 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.relocateable) + 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 = buystring (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->next) + { + if (l->header.type == lang_wild_statement_enum + && strcmp (l->wild_statement.section_name, output_secname) == 0) + break; + } + ps[0] = 0; + if (l == NULL) +#if 1 + einfo ("%P%F: *(%s\$) missing from linker script\n", output_secname); +#else /* FIXME: This block is untried. It exists to convey the intent, + should one decide to not require *(.foo\$) to appear in the linker + script. */ + { + lang_wild_statement_type *new = new_stat (lang_wild_statement, + &os->children); + new->section_name = xmalloc (strlen (output_secname) + 2); + sprintf (new->section_name, "%s\$", output_secname); + new->filename = NULL; + lang_list_init (&new->children); + l = new; + } +#endif + + /* 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. */ + wild_doit (&l->wild_statement.children, s, os, file); + + return true; +} + +static char * +gld_${EMULATION_NAME}_get_script(isfile) + int *isfile; +EOF +# Scripts compiled in. +# sed commands to quote an ld script as a C string. +sc="-f ${srcdir}/emultempl/stringify.sed" + +cat >>e${EMULATION_NAME}.c <> e${EMULATION_NAME}.c +echo ' ; else if (link_info.relocateable == true) 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 + +cat >>e${EMULATION_NAME}.c <e${EMULATION_NAME}.c < + ELF support by Ian Lance Taylor + +This file is part of GLD, the Gnu Linker. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#define TARGET_IS_${EMULATION_NAME} + +#include "bfd.h" +#include "sysdep.h" + +#include + +#include "bfdlink.h" + +#include "ld.h" +#include "ldmain.h" +#include "ldemul.h" +#include "ldfile.h" +#include "ldmisc.h" +#include "ldexp.h" +#include "ldlang.h" +#include "ldgram.h" + +static void gld${EMULATION_NAME}_before_parse PARAMS ((void)); +static boolean gld${EMULATION_NAME}_open_dynamic_archive + PARAMS ((const char *, search_dirs_type *, lang_input_statement_type *)); +static void gld${EMULATION_NAME}_after_open PARAMS ((void)); +static void gld${EMULATION_NAME}_check_needed + PARAMS ((lang_input_statement_type *)); +static void gld${EMULATION_NAME}_stat_needed + PARAMS ((lang_input_statement_type *)); +static boolean gld${EMULATION_NAME}_search_needed + PARAMS ((const char *, const char *, int)); +static boolean gld${EMULATION_NAME}_try_needed PARAMS ((const char *, int)); +static void gld${EMULATION_NAME}_vercheck + PARAMS ((lang_input_statement_type *)); +static void gld${EMULATION_NAME}_before_allocation PARAMS ((void)); +static void gld${EMULATION_NAME}_find_statement_assignment + PARAMS ((lang_statement_union_type *)); +static void gld${EMULATION_NAME}_find_exp_assignment PARAMS ((etree_type *)); +static boolean gld${EMULATION_NAME}_place_orphan + PARAMS ((lang_input_statement_type *, asection *)); +static void gld${EMULATION_NAME}_place_section + PARAMS ((lang_statement_union_type *)); +static char *gld${EMULATION_NAME}_get_script PARAMS ((int *isfile)); + +static void +gld${EMULATION_NAME}_before_parse() +{ + ldfile_output_architecture = bfd_arch_`echo ${ARCH} | sed -e 's/:.*//'`; + config.dynamic_link = ${DYNAMIC_LINK-true}; + config.has_shared = `if test -n "$GENERATE_SHLIB_SCRIPT" ; then echo true ; else echo false ; fi`; +} + +/* Try to open a dynamic archive. This is where we know that ELF + dynamic libraries have an extension of .so. */ + +static boolean +gld${EMULATION_NAME}_open_dynamic_archive (arch, search, entry) + const char *arch; + search_dirs_type *search; + lang_input_statement_type *entry; +{ + const char *filename; + char *string; + + if (! entry->is_archive) + return false; + + filename = entry->filename; + + string = (char *) xmalloc (strlen (search->name) + + strlen (filename) + + strlen (arch) + + sizeof "/lib.so"); + + sprintf (string, "%s/lib%s%s.so", search->name, filename, arch); + + 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) + { + char *needed_name; + + ASSERT (entry->is_archive && entry->search_dirs_flag); + needed_name = (char *) xmalloc (strlen (filename) + + strlen (arch) + + sizeof "lib.so"); + sprintf (needed_name, "lib%s%s.so", filename, arch); + bfd_elf_set_dt_needed_name (entry->the_bfd, needed_name); + } + + return true; +} + +EOF +if [ "x${host}" = "x${target}" ] ; then + if [ "x${DEFAULT_EMULATION}" = "x${EMULATION_NAME}" ] ; then +cat >>e${EMULATION_NAME}.c <= alloc) + { + alloc *= 2; + b = (char *) xrealloc (b, alloc); + } + if (c != ':' + && c != ' ' + && c != '\t' + && c != '\n' + && c != ',') + { + b[len] = c; + ++len; + } + else + { + if (len > 0 && b[len - 1] != ':') + { + b[len] = ':'; + ++len; + } + } + } + + if (len > 0 && b[len - 1] == ':') + --len; + + if (len > 0) + b[len] = '\0'; + else + { + free (b); + b = NULL; + } + + fclose (f); + + ld_so_conf = b; + } + + initialized = true; + } + + if (ld_so_conf == NULL) + return false; + + return gld${EMULATION_NAME}_search_needed (ld_so_conf, name, force); +} + +EOF + fi +fi +cat >>e${EMULATION_NAME}.c <next) + { + struct bfd_link_needed_list *ll; + int force; + + /* If we've already seen this file, skip it. */ + for (ll = needed; ll != l; ll = ll->next) + if (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 = false; + lang_for_each_input_file (gld${EMULATION_NAME}_check_needed); + if (global_found) + 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 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++) + { + const char *lib_path; + size_t len; + search_dirs_type *search; + + if (gld${EMULATION_NAME}_search_needed (command_line.rpath_link, + l->name, force)) + break; + if (gld${EMULATION_NAME}_search_needed (command_line.rpath, + l->name, force)) + break; + if (command_line.rpath_link == NULL + && command_line.rpath == NULL) + { + lib_path = (const char *) getenv ("LD_RUN_PATH"); + if (gld${EMULATION_NAME}_search_needed (lib_path, l->name, + force)) + break; + } +EOF +if [ "x${host}" = "x${target}" ] ; then + if [ "x${DEFAULT_EMULATION}" = "x${EMULATION_NAME}" ] ; then +cat >>e${EMULATION_NAME}.c <name, force)) + break; +EOF + fi +fi +cat >>e${EMULATION_NAME}.c <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); + if (gld${EMULATION_NAME}_try_needed (filename, force)) + break; + free (filename); + } + if (search != NULL) + break; +EOF +if [ "x${host}" = "x${target}" ] ; then + if [ "x${DEFAULT_EMULATION}" = "x${EMULATION_NAME}" ] ; then +cat >>e${EMULATION_NAME}.c <name, force)) + break; +EOF + fi +fi +cat >>e${EMULATION_NAME}.c <name, l->by); + } +} + +/* Search for a needed file in a path. */ + +static boolean +gld${EMULATION_NAME}_search_needed (path, name, force) + const char *path; + const char *name; + int force; +{ + const char *s; + size_t len; + + if (path == NULL || *path == '\0') + return false; + len = strlen (name); + while (1) + { + char *filename, *sset; + + s = strchr (path, ':'); + 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); + + if (gld${EMULATION_NAME}_try_needed (filename, force)) + return true; + + free (filename); + + if (*s == '\0') + break; + path = s + 1; + } + + return false; +} + +/* 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 boolean +gld${EMULATION_NAME}_try_needed (name, force) + const char *name; + int force; +{ + bfd *abfd; + + abfd = bfd_openr (name, bfd_get_target (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; + } + + /* 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) + { + (void) 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-gnu*) + cat >>e${EMULATION_NAME}.c <next) + if (strncmp (l->name, "libc.so", 7) == 0) + break; + if (l == NULL) + { + (void) bfd_close (abfd); + return false; + } + } + +EOF + ;; +esac +cat >>e${EMULATION_NAME}.c <filename != NULL + && strcmp (s->filename, global_needed->name) == 0) + { + global_found = true; + return; + } + + if (s->the_bfd != NULL) + { + const char *soname; + + soname = bfd_elf_get_dt_soname (s->the_bfd); + if (soname != NULL + && strcmp (soname, global_needed->name) == 0) + { + global_found = true; + return; + } + } + + if (s->search_dirs_flag + && s->filename != NULL + && strchr (global_needed->name, '/') == NULL) + { + const char *f; + + f = strrchr (s->filename, '/'); + if (f != NULL + && strcmp (f + 1, global_needed->name) == 0) + { + global_found = 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 (s) + lang_input_statement_type *s; +{ + struct stat st; + const char *suffix; + const char *soname; + const char *f; + + if (global_found) + return; + if (s->the_bfd == NULL) + return; + + if (bfd_stat (s->the_bfd, &st) != 0) + { + einfo ("%P:%B: bfd_stat failed: %E\n", s->the_bfd); + return; + } + + if (st.st_dev == global_stat.st_dev + && st.st_ino == global_stat.st_ino) + { + global_found = true; + 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 + hueristic 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 = s->filename; + + f = strrchr (soname, '/'); + if (f != NULL) + ++f; + else + f = soname; + + if (strncmp (f, 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, f); +} + +/* On Linux, it's possible to have different versions of the same + shared library linked against different versions of libc. The + dynamic linker somehow tags which libc version to use in + /etc/ld.so.cache, and, based on the libc that it sees in the + executable, chooses which version of the shared library to use. + + We try to do a similar check here by checking whether this shared + library needs any other shared libraries which may conflict with + libraries we have already included in the link. If it does, we + skip it, and try to find another shared library farther on down the + link path. + + This is called via lang_for_each_input_file. + GLOBAL_VERCHECK_NEEDED is the list of objects needed by the object + which we ar checking. This sets GLOBAL_VERCHECK_FAILED if we find + a conflicting version. */ + +static void +gld${EMULATION_NAME}_vercheck (s) + lang_input_statement_type *s; +{ + const char *soname, *f; + struct bfd_link_needed_list *l; + + if (global_vercheck_failed) + return; + if (s->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 = bfd_get_filename (s->the_bfd); + + f = strrchr (soname, '/'); + if (f != NULL) + ++f; + else + f = soname; + + for (l = global_vercheck_needed; l != NULL; l = l->next) + { + const char *suffix; + + if (strcmp (f, 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 (f, 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; + } + } +} + +/* 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 () +{ + const char *rpath; + asection *sinterp; + + /* 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${ELFSIZE}_size_dynamic_sections + (output_bfd, command_line.soname, rpath, + command_line.export_dynamic, 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"); + + /* 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->_raw_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; + boolean ret; + + if (is->just_syms_flag) + continue; + + s = bfd_get_section_by_name (is->the_bfd, ".gnu.warning"); + if (s == NULL) + continue; + + sz = bfd_section_size (is->the_bfd, s); + 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 copying the + warning into the output file. */ + s->_raw_size = 0; + } + } +} + +/* 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 (s) + lang_statement_union_type *s; +{ + 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 (exp) + 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; + + /* 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. */ + + /* Fall through. */ + case etree_assign: + if (strcmp (exp->assign.dst, ".") != 0) + { + if (! (bfd_elf${ELFSIZE}_record_link_assignment + (output_bfd, &link_info, exp->assign.dst, + exp->type.node_class == etree_provide ? true : false))) + 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; + } +} + +/* Place an orphan section. We use this to put random SHF_ALLOC + sections in the right segment. */ + +static asection *hold_section; +static lang_output_section_statement_type *hold_use; +static lang_output_section_statement_type *hold_text; +static lang_output_section_statement_type *hold_rodata; +static lang_output_section_statement_type *hold_data; +static lang_output_section_statement_type *hold_bss; +static lang_output_section_statement_type *hold_rel; +static lang_output_section_statement_type *hold_interp; + +/*ARGSUSED*/ +static boolean +gld${EMULATION_NAME}_place_orphan (file, s) + lang_input_statement_type *file; + asection *s; +{ + lang_output_section_statement_type *place; + asection *snew, **pps; + lang_statement_list_type *old; + lang_statement_list_type add; + etree_type *address; + const char *secname, *ps; + const char *outsecname; + lang_output_section_statement_type *os; + + if ((s->flags & SEC_ALLOC) == 0) + return false; + + /* Look through the script to see where to place this section. */ + hold_section = s; + hold_use = NULL; + lang_for_each_statement (gld${EMULATION_NAME}_place_section); + + if (hold_use != NULL) + { + /* We have already placed a section with this name. */ + wild_doit (&hold_use->children, s, hold_use, file); + return true; + } + + secname = bfd_get_section_name (s->owner, s); + + /* 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.shared + && ! link_info.relocateable + && strncmp (secname, ".gnu.warning.", sizeof ".gnu.warning." - 1) == 0 + && hold_text != NULL) + { + wild_doit (&hold_text->children, s, hold_text, file); + 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_EXCLUDE) + return false; + else if ((s->flags & SEC_LOAD) != 0 + && strncmp (secname, ".note", 4) == 0 + && hold_interp != NULL) + place = hold_interp; + else if ((s->flags & SEC_HAS_CONTENTS) == 0 + && hold_bss != NULL) + place = hold_bss; + else if ((s->flags & SEC_READONLY) == 0 + && hold_data != NULL) + place = hold_data; + else if (strncmp (secname, ".rel", 4) == 0 + && hold_rel != NULL) + place = hold_rel; + else if ((s->flags & SEC_CODE) == 0 + && (s->flags & SEC_READONLY) != 0 + && hold_rodata != NULL) + place = hold_rodata; + else if ((s->flags & SEC_READONLY) != 0 + && hold_text != NULL) + place = hold_text; + if (place == NULL) + return false; + + /* 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 allocateable characteristics. */ + outsecname = secname; + if (bfd_get_section_by_name (output_bfd, outsecname) != NULL) + { + unsigned int len; + char *newname; + unsigned int i; + + len = strlen (outsecname); + newname = xmalloc (len + 5); + strcpy (newname, outsecname); + i = 0; + do + { + sprintf (newname + len, "%d", i); + ++i; + } + while (bfd_get_section_by_name (output_bfd, newname) != NULL); + + outsecname = newname; + } + + /* Create the section in the output file, and put it in the right + place. This shuffling is to make the output file look neater. */ + snew = bfd_make_section (output_bfd, outsecname); + if (snew == NULL) + einfo ("%P%F: output format %s cannot represent section called %s\n", + output_bfd->xvec->name, outsecname); + if (place->bfd_section != NULL) + { + for (pps = &output_bfd->sections; *pps != snew; pps = &(*pps)->next) + ; + *pps = snew->next; + snew->next = place->bfd_section->next; + place->bfd_section->next = snew; + } + + /* Start building a list of statements for this section. */ + old = stat_ptr; + stat_ptr = &add; + lang_list_init (stat_ptr); + + /* If the name of the section is representable in C, then create + symbols to mark the start and the end of the section. */ + for (ps = outsecname; *ps != '\0'; ps++) + if (! isalnum ((unsigned char) *ps) && *ps != '_') + break; + if (*ps == '\0' && config.build_constructors) + { + char *symname; + + symname = (char *) xmalloc (ps - outsecname + sizeof "__start_"); + sprintf (symname, "__start_%s", outsecname); + lang_add_assignment (exp_assop ('=', symname, + exp_unop (ALIGN_K, + exp_intop ((bfd_vma) 1 + << s->alignment_power)))); + } + + if (! link_info.relocateable) + address = NULL; + else + address = exp_intop ((bfd_vma) 0); + + lang_enter_output_section_statement (outsecname, address, 0, + (bfd_vma) 0, + (etree_type *) NULL, + (etree_type *) NULL, + (etree_type *) NULL); + + os = lang_output_section_statement_lookup (outsecname); + wild_doit (&os->children, s, os, file); + + lang_leave_output_section_statement + ((bfd_vma) 0, "*default*", (struct lang_output_section_phdr_list *) NULL); + stat_ptr = &add; + + if (*ps == '\0' && config.build_constructors) + { + char *symname; + + symname = (char *) xmalloc (ps - outsecname + sizeof "__stop_"); + sprintf (symname, "__stop_%s", outsecname); + lang_add_assignment (exp_assop ('=', symname, + exp_nameop (NAME, "."))); + } + + /* Now stick the new statement list right after PLACE. */ + *add.tail = place->header.next; + place->header.next = add.head; + + stat_ptr = old; + + return true; +} + +static void +gld${EMULATION_NAME}_place_section (s) + lang_statement_union_type *s; +{ + lang_output_section_statement_type *os; + + if (s->header.type != lang_output_section_statement_enum) + return; + + os = &s->output_section_statement; + + if (strcmp (os->name, hold_section->name) == 0 + && os->bfd_section != NULL + && ((hold_section->flags & (SEC_LOAD | SEC_ALLOC)) + == (os->bfd_section->flags & (SEC_LOAD | SEC_ALLOC)))) + hold_use = os; + + if (strcmp (os->name, ".text") == 0) + hold_text = os; + else if (strcmp (os->name, ".rodata") == 0) + hold_rodata = os; + else if (strcmp (os->name, ".data") == 0) + hold_data = os; + else if (strcmp (os->name, ".bss") == 0) + hold_bss = os; + else if (hold_rel == NULL + && os->bfd_section != NULL + && (os->bfd_section->flags & SEC_ALLOC) != 0 + && strncmp (os->name, ".rel", 4) == 0) + hold_rel = os; + else if (strcmp (os->name, ".interp") == 0) + hold_interp = os; +} + +static char * +gld${EMULATION_NAME}_get_script(isfile) + int *isfile; +EOF + +if test -n "$COMPILE_IN" +then +# Scripts compiled in. + +# sed commands to quote an ld script as a C string. +sc='s/["\\]/\\&/g +s/$/\\n\\/ +1s/^/"/ +$s/$/n"/ +' + +cat >>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <> e${EMULATION_NAME}.c +echo ' ; else if (link_info.relocateable == true) 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. + +cat >>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <e${EMULATION_NAME}.c < +#include "bfd.h" +#include "sysdep.h" +#include "libiberty.h" +#include "bfdlink.h" + +#include "ld.h" +#include "ldemul.h" +#include "ldfile.h" +#include "ldmisc.h" +#include "ldmain.h" + +#ifdef GNU960 + +static void +gld960_before_parse() +{ + static char *env_variables[] = { "G960LIB", "G960BASE", 0 }; + char **p; + char *env ; + + for ( p = env_variables; *p; p++ ){ + env = (char *) getenv(*p); + if (env) { + ldfile_add_library_path (concat (env, + "/lib/libcoff", + (const char *) NULL), + false); + } + } + ldfile_output_architecture = bfd_arch_i960; +} + +#else /* not GNU960 */ + +static void gld960_before_parse() +{ + char *env ; + env = getenv("G960LIB"); + if (env) { + ldfile_add_library_path(env, false); + } + env = getenv("G960BASE"); + if (env) + ldfile_add_library_path (concat (env, "/lib", (const char *) NULL), + false); + ldfile_output_architecture = bfd_arch_i960; +} + +#endif /* GNU960 */ + + +static void +gld960_set_output_arch() +{ + if (ldfile_output_machine_name != NULL + && *ldfile_output_machine_name != '\0') + { + char *s, *s1; + + s = concat ("i960:", ldfile_output_machine_name, (char *) NULL); + for (s1 = s; *s1 != '\0'; s1++) + if (isupper ((unsigned char) *s1)) + *s1 = tolower ((unsigned char) *s1); + ldfile_set_output_arch (s); + free (s); + } + + set_output_arch_default (); +} + +static char * +gld960_choose_target() +{ +#ifdef GNU960 + + output_filename = "b.out"; + return bfd_make_targ_name(BFD_BOUT_FORMAT, 0); + +#else + + char *from_outside = getenv(TARGET_ENVIRON); + output_filename = "b.out"; + + if (from_outside != (char *)NULL) + return from_outside; + + return "coff-Intel-little"; + +#endif +} + +static char * +gld960_get_script(isfile) + int *isfile; +EOF + +if test -n "$COMPILE_IN" +then +# Scripts compiled in. + +# sed commands to quote an ld script as a C string. +sc='s/["\\]/\\&/g +s/$/\\n\\/ +1s/^/"/ +$s/$/n"/ +' + +cat >>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <e${EMULATION_NAME}.c <the_bfd = bfd_create ("linker stubs", output_bfd); + if (stub_file->the_bfd == NULL + || ! bfd_set_arch_mach (stub_file->the_bfd, + bfd_get_arch (output_bfd), + bfd_get_mach (output_bfd))) + { + einfo ("%X%P: can not create BFD %E\n"); + return; + } + + stub_sec = bfd_make_section_old_way (stub_file->the_bfd, ".text"); + /* Don't set SEC_RELOC until we actually have relocations in this + section. */ + if (stub_sec == NULL + || ! bfd_set_section_flags (stub_file->the_bfd, stub_sec, + (SEC_HAS_CONTENTS + | SEC_ALLOC + | SEC_LOAD + | SEC_CODE + | SEC_IN_MEMORY))) + { + einfo ("%X%P: can not create stub section: %E\n"); + return; + } + + ldlang_add_file (stub_file); +} + +/* Walk all the lang statements splicing out any padding statements from + the list. */ + +static void +hppaelf_delete_padding_statements (s, prev) + lang_statement_union_type *s; + lang_statement_union_type **prev; +{ + lang_statement_union_type *sprev = NULL; + for (; s != NULL; s = s->next) + { + switch (s->header.type) + { + + /* We want recursively walk these sections. */ + case lang_constructors_statement_enum: + hppaelf_delete_padding_statements (constructor_list.head, + &constructor_list.head); + break; + + case lang_output_section_statement_enum: + hppaelf_delete_padding_statements (s->output_section_statement. + children.head, + &s->output_section_statement. + children.head); + break; + + /* Huh? What is a lang_wild_statement? */ + case lang_wild_statement_enum: + hppaelf_delete_padding_statements (s->wild_statement. + children.head, + &s->wild_statement. + children.head); + break; + + /* Here's what we are really looking for. Splice these out of + the list. */ + case lang_padding_statement_enum: + if (sprev) + sprev->header.next = s->header.next; + else + **prev = *s; + break; + + /* We don't care about these cases. */ + case lang_data_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_address_statement_enum: + break; + + default: + abort (); + break; + } + sprev = s; + } +} + +/* Final emulation specific call. For the PA we use this opportunity + to build linker stubs. */ + +static void +hppaelf_finish () +{ + /* Call into the BFD backend to do the real work. */ + if (elf32_hppa_size_stubs (stub_file->the_bfd, output_bfd, &link_info) + == false) + { + einfo ("%X%P: can not size stub section: %E\n"); + return; + } + + /* If the size of the stub section is nonzero, then we need + to resize the sections, recompute the assignments, and finally + build the stubs. */ + if (bfd_section_size (stub_file->the_bfd, stub_file->the_bfd->sections) != 0) + { + /* Delete all the padding statements, they're no longer valid. */ + hppaelf_delete_padding_statements (stat_ptr->head, &stat_ptr->head); + + /* Resize the sections. */ + lang_size_sections (stat_ptr->head, abs_output_section, + &stat_ptr->head, 0, (bfd_vma) 0, false); + + /* Redo special stuff. */ + ldemul_after_allocation (); + + /* Do the assignments again. */ + lang_do_assignments (stat_ptr->head, + abs_output_section, + (fill_type) 0, (bfd_vma) 0); + + /* Now build the linker stubs. */ + if (elf32_hppa_build_stubs (stub_file->the_bfd, &link_info) == false) + { + einfo ("%X%P: can not build stubs: %E\n"); + return; + } + } +} + +/* The script itself gets inserted here. */ + +static char * +hppaelf_get_script(isfile) + int *isfile; +EOF + +if test -n "$COMPILE_IN" +then +# Scripts compiled in. + +# sed commands to quote an ld script as a C string. +sc='s/["\\]/\\&/g +s/$/\\n\\/ +1s/^/"/ +$s/$/n"/ +' + +cat >>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <e${EMULATION_NAME}.c < + Linux support by Eric Youngdale + +This file is part of GLD, the Gnu Linker. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#define TARGET_IS_${EMULATION_NAME} + +#include "bfd.h" +#include "sysdep.h" +#include "bfdlink.h" + +#include "ld.h" +#include "ldmain.h" +#include "ldemul.h" +#include "ldfile.h" +#include "ldmisc.h" +#include "ldexp.h" +#include "ldlang.h" + +static void gld${EMULATION_NAME}_before_parse PARAMS ((void)); +static boolean gld${EMULATION_NAME}_open_dynamic_archive + PARAMS ((const char *, search_dirs_type *, lang_input_statement_type *)); +static void gld${EMULATION_NAME}_find_address_statement + PARAMS ((lang_statement_union_type *)); +static void gld${EMULATION_NAME}_create_output_section_statements + PARAMS ((void)); +static void gld${EMULATION_NAME}_before_allocation PARAMS ((void)); +static char *gld${EMULATION_NAME}_get_script PARAMS ((int *isfile)); + +static void +gld${EMULATION_NAME}_before_parse() +{ + ldfile_output_architecture = bfd_arch_${ARCH}; + 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 boolean +gld${EMULATION_NAME}_open_dynamic_archive (arch, search, entry) + 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 (s) + 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 () +{ + 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 () +{ + if (link_info.relocateable) + return; + + /* Let the backend work out the sizes of any sections required by + dynamic linking. */ + if (! bfd_${EMULATION_NAME}_size_dynamic_sections (output_bfd, &link_info)) + einfo ("%P%F: failed to set dynamic section sizes: %E\n"); +} + +static char * +gld${EMULATION_NAME}_get_script(isfile) + int *isfile; +EOF + +if test -n "$COMPILE_IN" +then +# Scripts compiled in. + +# sed commands to quote an ld script as a C string. +sc='s/["\\]/\\&/g +s/$/\\n\\/ +1s/^/"/ +$s/$/n"/ +' + +cat >>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <e${EMULATION_NAME}.c <name = name; + element->next = (lib_list_type *)NULL; + **list = element; + *list = &element->next; + +} + +static boolean had_hll = false; +static boolean had_hll_name = false; + +static void +lnk960_hll(name) + char *name; +{ + had_hll = true; + if (name != (char *)NULL) { + had_hll_name = true; + append(&hll_list_tail, name); + } +} + +static void +lnk960_syslib(name) + char *name; +{ + append(&syslib_list_tail,name); +} + + +#ifdef GNU960 + +static void +lnk960_before_parse() +{ + static char *env_variables[] = { "G960LIB", "G960BASE", 0 }; + char **p; + char *env ; + + for ( p = env_variables; *p; p++ ){ + env = (char *) getenv(*p); + if (env) { + ldfile_add_library_path(concat(env,"/lib/libcoff",""), false); + } + } + + env= (char *) getenv("I960BASE"); + if ( env ) { + ldfile_add_library_path(concat(env,"/lib",""), false); + } + + ldfile_output_architecture = bfd_arch_i960; + ldfile_output_machine = bfd_mach_i960_core; +} + +#else /* not GNU960 */ + +static void +lnk960_before_parse() +{ + 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",""), false); + ldfile_output_architecture = bfd_arch_i960; + ldfile_output_machine = bfd_mach_i960_core; +} + +#endif /* GNU960 */ + + +static void +add_on(list, search) + 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() +{ + /* 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_before_allocation() +{ +} + +static void +lnk960_after_allocation() +{ + if (link_info.relocateable == false) { + 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() +{ + /* 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(output_bfd, ldfile_output_architecture, ldfile_output_machine); +} + +static char * +lnk960_choose_target() +{ +#ifdef GNU960 + + return bfd_make_targ_name(BFD_COFF_FORMAT, 0); + +#else + + 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 +#endif + +} + +static char * +lnk960_get_script(isfile) + int *isfile; +EOF + +if test -n "$COMPILE_IN" +then +# Scripts compiled in. + +# sed commands to quote an ld script as a C string. +sc='s/["\\]/\\&/g +s/$/\\n\\/ +1s/^/"/ +$s/$/n"/ +' + +cat >>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <e${EMULATION_NAME}.c < based on generic.em. + +This file is part of GLD, the Gnu Linker. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#define TARGET_IS_${EMULATION_NAME} + +#include "bfd.h" +#include "sysdep.h" +#include "bfdlink.h" + +#include "ld.h" +#include "ldmain.h" +#include "ldemul.h" +#include "ldfile.h" +#include "ldmisc.h" + +static void gld${EMULATION_NAME}_before_parse PARAMS ((void)); +static void gld${EMULATION_NAME}_after_open PARAMS ((void)); +static void check_sections PARAMS ((bfd *, asection *, PTR)); +static void gld${EMULATION_NAME}_after_allocation PARAMS ((void)); +static char *gld${EMULATION_NAME}_get_script PARAMS ((int *isfile)); + +static void +gld${EMULATION_NAME}_before_parse() +{ +#ifndef TARGET_ /* I.e., if not generic. */ + ldfile_output_architecture = bfd_arch_${ARCH}; +#endif /* not TARGET_ */ +} + +/* This function is run after all the input files have been opened. + We create a .rel.sdata section for each input file with a non zero + .sdata section. The BFD backend will fill in these sections with + magic numbers which can be used to relocate the data section at run + time. This will only do the right thing if all the input files + have been compiled using -membedded-pic. */ + +static void +gld${EMULATION_NAME}_after_open () +{ + bfd *abfd; + + if (! command_line.embedded_relocs + || link_info.relocateable) + return; + + for (abfd = link_info.input_bfds; abfd != NULL; abfd = abfd->link_next) + { + asection *datasec; + + datasec = bfd_get_section_by_name (abfd, ".sdata"); + + /* 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, ".rel.sdata"); + 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 * 4)) + einfo ("%F%B: can not create .rel.sdata 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, (PTR) datasec); + } +} + +/* Check that of the data sections, only the .sdata section has + relocs. This is called via bfd_map_over_sections. */ + +static void +check_sections (abfd, sec, sdatasec) + bfd *abfd; + asection *sec; + PTR sdatasec; +{ + if ((bfd_get_section_flags (abfd, sec) & SEC_CODE) == 0 + && sec != (asection *) sdatasec + && sec->reloc_count != 0) + einfo ("%P%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 () +{ + bfd *abfd; + + if (! command_line.embedded_relocs + || link_info.relocateable) + 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, ".sdata"); + + if (datasec == NULL || datasec->reloc_count == 0) + continue; + + relsec = bfd_get_section_by_name (abfd, ".rel.sdata"); + ASSERT (relsec != NULL); + + if (! bfd_mips_ecoff_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(isfile) + 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/stringify.sed" + +cat >>e${EMULATION_NAME}.c <> e${EMULATION_NAME}.c +echo ' ; else if (link_info.relocateable == true) 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. + +cat >>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <e${EMULATION_NAME}.c < 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 TARGET_IS_i386pe + 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, _(" --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")); +#endif +} + +static void +set_pe_name (name, val) + 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 () +{ + 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" }, + { "console", 3, "_mainCRTStartup" }, +#if 0 + /* The Microsoft linker does not recognize this. */ + { "os2", 5, "" }, +#endif + { "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); + + lang_add_entry (v[i].entry, 1); + + return; + } + } + + einfo (_("%P%F: invalid subsystem type %s\n"), optarg); +} + + + +static void +set_pe_value (name) + 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 (resname, comname) + 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 int +gld_${EMULATION_NAME}_parse_args(argc, argv) + int argc; + char **argv; +{ + int longind; + int optc; + int prevoptind = optind; + int prevopterr = opterr; + int wanterror; + static int lastoptind = -1; + + if (lastoptind != optind) + opterr = 0; + wanterror = opterr; + + lastoptind = optind; + + optc = getopt_long_only (argc, argv, "-", longopts, &longind); + opterr = prevopterr; + + switch (optc) + { + default: + if (wanterror) + xexit (1); + optind = prevoptind; + return 0; + + case OPTION_BASE_FILE: + link_info.base_file = (PTR) 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_OUT_DEF: + pe_out_def_filename = xstrdup (optarg); + break; + case OPTION_EXPORT_ALL: + pe_dll_export_everything = 1; + break; + case OPTION_EXCLUDE_SYMBOLS: +#ifdef TARGET_IS_i386pe + pe_dll_add_excludes (optarg); +#endif + 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; + } + return 1; +} + +/* Assign values to the special symbols before the linker script is + read. */ + +static void +gld_${EMULATION_NAME}_set_symbols () +{ + /* 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.relocateable) + init[IMAGEBASEOFF].value = 0; + else if (init[DLLOFF].value || link_info.shared) + init[IMAGEBASEOFF].value = NT_DLL_IMAGE_BASE; + else + init[IMAGEBASEOFF].value = NT_EXE_IMAGE_BASE; + } + + /* Don't do any symbol assignments if this is a relocateable link. */ + if (link_info.relocateable) + 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 () +{ + /* 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 (entry_symbol) + ldlang_add_undef (entry_symbol); +} + +static struct bfd_link_hash_entry *pe_undef_found_sym; + +static boolean +pe_undef_cdecl_match (h, string) + struct bfd_link_hash_entry *h; + PTR string; +{ + int 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 () +{ + static int gave_warning_message = 0; + struct bfd_link_hash_entry *undef, *sym; + char *at; + for (undef = link_info.hash->undefs; undef; undef=undef->next) + if (undef->type == bfd_link_hash_undefined) + { + at = strchr (undef->root.string, '@'); + if (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); + 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, + (PTR) 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 void +gld_${EMULATION_NAME}_after_open () +{ + /* 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 (output_bfd)->pe) + einfo (_("%F%P: PE operations on non PE file.\n")); + + pe_data (output_bfd)->pe_opthdr = pe; + pe_data (output_bfd)->dll = init[DLLOFF].value; + +#ifdef TARGET_IS_i386pe + if (pe_enable_stdcall_fixup) /* -1=warn or 1=disable */ + pe_fixup_stdcalls (); + + pe_process_import_defs(output_bfd, &link_info); + if (link_info.shared) + pe_dll_build_sections (output_bfd, &link_info); +#endif + +#ifdef TARGET_IS_armpe + { + /* 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 +} + +static void +gld_${EMULATION_NAME}_before_allocation() +{ +#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 */ + +#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 (! 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 */ +} + + +/* 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; +} + +static boolean +gld_${EMULATION_NAME}_unrecognized_file(entry) + lang_input_statement_type *entry; +{ +#ifdef TARGET_IS_i386pe + const char *ext = entry->filename + strlen (entry->filename) - 4; + + if (strcmp (ext, ".def") == 0 || strcmp (ext, ".DEF") == 0) + { + if (pe_def_file == 0) + pe_def_file = def_file_empty (); + def_file_parse (entry->filename, pe_def_file); + if (pe_def_file) + { + int i, buflen=0, len; + char *buf; + for (i=0; inum_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; inum_exports; i++) + { + struct bfd_link_hash_entry *h; + sprintf(buf, "_%s", 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 (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 0 + /* Not sure if these *should* be set */ + if (pe_def_file->version_major != -1) + { + pe.MajorImageVersion = pe_def_file->version_major; + pe.MinorImageVersion = pe_def_file->version_minor; + } +#endif + 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 boolean +gld_${EMULATION_NAME}_recognized_file(entry) + lang_input_statement_type *entry; +{ +#ifdef TARGET_IS_i386pe + if (bfd_get_format (entry->the_bfd) == bfd_object) + { + const char *ext = entry->filename + strlen (entry->filename) - 4; + if (strcmp (ext, ".dll") == 0 || strcmp (ext, ".DLL") == 0) + return pe_implied_import_dll (entry->filename); + } +#endif + return false; +} + +static void +gld_${EMULATION_NAME}_finish () +{ +#ifdef TARGET_IS_i386pe + if (link_info.shared) + { + pe_dll_fill_sections (output_bfd, &link_info); + if (pe_implib_filename) + pe_dll_generate_implib (pe_def_file, pe_implib_filename); + } + if (pe_out_def_filename) + pe_dll_generate_def_file (pe_out_def_filename); +#endif +} + + +/* 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 asection *hold_section; +static char *hold_section_name; +static lang_output_section_statement_type *hold_use; +static lang_output_section_statement_type *hold_text; +static lang_output_section_statement_type *hold_rdata; +static lang_output_section_statement_type *hold_data; +static lang_output_section_statement_type *hold_bss; + +/* Place an orphan section. We use this to put random SHF_ALLOC + sections in the right segment. */ + +/*ARGSUSED*/ +static boolean +gld_${EMULATION_NAME}_place_orphan (file, s) + lang_input_statement_type *file; + asection *s; +{ + const char *secname; + char *dollar; + + if ((s->flags & SEC_ALLOC) == 0) + return false; + + secname = bfd_get_section_name (s->owner, s); + + /* Look through the script to see where to place this section. */ + + hold_section = s; + + hold_section_name = xstrdup (secname); + dollar = strchr (hold_section_name, '$'); + if (dollar != NULL) + *dollar = '\0'; + + hold_use = NULL; + lang_for_each_statement (gld${EMULATION_NAME}_place_section); + + if (hold_use == NULL) + { + lang_output_section_statement_type *place; + char *outsecname; + asection *snew, **pps; + lang_statement_list_type *old; + lang_statement_list_type add; + etree_type *address; + + /* 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_HAS_CONTENTS) == 0 + && hold_bss != NULL) + place = hold_bss; + else if ((s->flags & SEC_READONLY) == 0 + && hold_data != NULL) + place = hold_data; + else if ((s->flags & SEC_CODE) == 0 + && (s->flags & SEC_READONLY) != 0 + && hold_rdata != NULL) + place = hold_rdata; + else if ((s->flags & SEC_READONLY) != 0 + && hold_text != NULL) + place = hold_text; + + /* 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 allocateable characteristics. */ + outsecname = xstrdup (hold_section_name); + if (bfd_get_section_by_name (output_bfd, outsecname) != NULL) + { + unsigned int len; + char *newname; + unsigned int i; + + len = strlen (outsecname); + newname = xmalloc (len + 5); + strcpy (newname, outsecname); + i = 0; + do + { + sprintf (newname + len, "%d", i); + ++i; + } + while (bfd_get_section_by_name (output_bfd, newname) != NULL); + + free (outsecname); + outsecname = newname; + } + + /* We don't want to free OUTSECNAME, as it may get attached to + the output section statement. */ + + /* Create the section in the output file, and put it in the + right place. This shuffling is to make the output file look + neater. */ + snew = bfd_make_section (output_bfd, outsecname); + if (snew == NULL) + einfo ("%P%F: output format %s cannot represent section called %s\n", + output_bfd->xvec->name, outsecname); + if (place != NULL && place->bfd_section != NULL) + { + for (pps = &output_bfd->sections; *pps != snew; pps = &(*pps)->next) + ; + *pps = snew->next; + snew->next = place->bfd_section->next; + place->bfd_section->next = snew; + } + + /* Start building a list of statements for this section. */ + old = stat_ptr; + stat_ptr = &add; + lang_list_init (stat_ptr); + + if (link_info.relocateable) + address = NULL; + else + { + /* All sections in an executable must be aligned to a page + boundary. */ + address = exp_unop (ALIGN_K, + exp_nameop (NAME, "__section_alignment__")); + } + + lang_enter_output_section_statement (outsecname, address, 0, + (bfd_vma) 0, + (etree_type *) NULL, + (etree_type *) NULL, + (etree_type *) NULL); + + hold_use = lang_output_section_statement_lookup (outsecname); + + lang_leave_output_section_statement + ((bfd_vma) 0, "*default*", + (struct lang_output_section_phdr_list *) NULL); + + /* Now stick the new statement list right after PLACE. */ + if (place != NULL) + { + *add.tail = place->header.next; + place->header.next = add.head; + } + + stat_ptr = old; + } + + if (dollar == NULL) + wild_doit (&hold_use->children, s, hold_use, file); + else + { + lang_statement_union_type **pl; + boolean found_dollar; + lang_statement_list_type list; + + /* The section name has a '$'. Sort it with the other '$' + sections. */ + + found_dollar = false; + for (pl = &hold_use->children.head; *pl != NULL; pl = &(*pl)->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->ifile->the_bfd, ls->section); + if (strchr (lname, '$') == NULL) + { + if (found_dollar) + break; + } + else + { + found_dollar = true; + if (strcmp (secname, lname) < 0) + break; + } + } + + lang_list_init (&list); + wild_doit (&list, s, hold_use, file); + if (list.head != NULL) + { + ASSERT (list.head->next == NULL); + list.head->next = *pl; + *pl = list.head; + } + } + + free (hold_section_name); + + return true; +} + +static void +gld${EMULATION_NAME}_place_section (s) + lang_statement_union_type *s; +{ + lang_output_section_statement_type *os; + + if (s->header.type != lang_output_section_statement_enum) + return; + + os = &s->output_section_statement; + + if (strcmp (os->name, hold_section_name) == 0 + && os->bfd_section != NULL + && ((hold_section->flags & (SEC_LOAD | SEC_ALLOC)) + == (os->bfd_section->flags & (SEC_LOAD | SEC_ALLOC)))) + hold_use = os; + + if (strcmp (os->name, ".text") == 0) + hold_text = os; + else if (strcmp (os->name, ".rdata") == 0) + hold_rdata = os; + else if (strcmp (os->name, ".data") == 0) + hold_data = os; + else if (strcmp (os->name, ".bss") == 0) + hold_bss = os; +} + +static char * +gld_${EMULATION_NAME}_get_script(isfile) + int *isfile; +EOF +# Scripts compiled in. +# sed commands to quote an ld script as a C string. +sc="-f ${srcdir}/emultempl/stringify.sed" + +cat >>e${EMULATION_NAME}.c <> e${EMULATION_NAME}.c +echo ' ; else if (link_info.relocateable == true) 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 + +cat >>e${EMULATION_NAME}.c <e${EMULATION_NAME}.c < + SunOS shared library support by Ian Lance Taylor + +This file is part of GLD, the Gnu Linker. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#define TARGET_IS_${EMULATION_NAME} + +#include + +#include "bfd.h" +#include "sysdep.h" +#include "bfdlink.h" +#include "libiberty.h" + +#include "ld.h" +#include "ldmain.h" +#include "ldemul.h" +#include "ldfile.h" +#include "ldmisc.h" +#include "ldexp.h" +#include "ldlang.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}_before_parse PARAMS ((void)); +static void gld${EMULATION_NAME}_set_symbols PARAMS ((void)); +static void gld${EMULATION_NAME}_create_output_section_statements + PARAMS ((void)); +static void gld${EMULATION_NAME}_find_so + PARAMS ((lang_input_statement_type *)); +static char *gld${EMULATION_NAME}_search_dir + PARAMS ((const char *, const char *, boolean *)); +static void gld${EMULATION_NAME}_after_open PARAMS ((void)); +static void gld${EMULATION_NAME}_check_needed + PARAMS ((lang_input_statement_type *)); +static boolean gld${EMULATION_NAME}_search_needed + PARAMS ((const char *, const char *)); +static boolean gld${EMULATION_NAME}_try_needed + PARAMS ((const char *, const char *)); +static void gld${EMULATION_NAME}_before_allocation PARAMS ((void)); +static void gld${EMULATION_NAME}_find_assignment + PARAMS ((lang_statement_union_type *)); +static void gld${EMULATION_NAME}_find_exp_assignment PARAMS ((etree_type *)); +static void gld${EMULATION_NAME}_count_need + PARAMS ((lang_input_statement_type *)); +static void gld${EMULATION_NAME}_set_need + PARAMS ((lang_input_statement_type *)); +static char *gld${EMULATION_NAME}_get_script PARAMS ((int *isfile)); + +static void +gld${EMULATION_NAME}_before_parse() +{ + ldfile_output_architecture = bfd_arch_${ARCH}; + 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 () +{ +EOF +if [ "x${host}" = "x${target}" ] ; then + if [ "x${DEFAULT_EMULATION}" = "x${EMULATION_NAME}" ] ; then +cat >>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <search_dirs_flag + || ! inp->is_archive + || ! inp->dynamic) + return; + + ASSERT (strncmp (inp->local_sym_name, "-l", 2) == 0); + + for (search = search_head; search != NULL; search = search->next) + { + 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 (dirname, filename, found_static) + const char *dirname; + const char *filename; + 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 (strncmp (entry->d_name, "lib", 3) != 0 + || 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 (strncmp (entry->d_name + 3 + len, ".so", 3) != 0) + continue; + if (entry->d_name[6 + len] == '\0') + ; + else if (entry->d_name[6 + len] == '.' + && isdigit ((unsigned char) entry->d_name[7 + len])) + ; + else + continue; + + for (s = entry->d_name + 6 + len; *s != '\0'; s++) + if (*s != '.' && ! isdigit ((unsigned char) *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 boolean global_found; + +/* This is called after all the input files have been opened. */ + +static void +gld${EMULATION_NAME}_after_open () +{ + struct bfd_link_needed_list *needed, *l; + + /* We only need to worry about this when doing a final link. */ + if (link_info.relocateable || 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 (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 (strncmp (lname, "-l", 2) != 0) + { + bfd *abfd; + + abfd = bfd_openr (lname, bfd_get_target (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 + if [ "x${DEFAULT_EMULATION}" = "x${EMULATION_NAME}" ] ; then +cat >>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <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 boolean +gld${EMULATION_NAME}_search_needed (path, name) + 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 boolean +gld${EMULATION_NAME}_try_needed (dir, name) + const char *dir; + const char *name; +{ + char *file; + char *alc; + 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 (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 (s) + lang_input_statement_type *s; +{ + if (s->filename == NULL) + return; + if (strncmp (global_needed->name, "-l", 2) != 0) + { + 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 (strncmp (sname, "lib", 3) != 0) + 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 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 () +{ + 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.relocateable && ! entry_from_cmdline) + { + struct bfd_link_hash_entry *h; + + for (h = link_info.hash->undefs; h != NULL; h = h->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.relocateable) + { + 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 (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 (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->_raw_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->_raw_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->_raw_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.relocateable) + { + 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; + } +} + +/* 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 (s) + 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 (exp) + 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 (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 (inp) + 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 (inp) + 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 (output_bfd, need_pnames - need_contents, need_pinfo); + if (! inp->is_archive) + { + bfd_put_32 (output_bfd, (bfd_vma) 0, need_pinfo + 4); + bfd_put_16 (output_bfd, (bfd_vma) 0, need_pinfo + 8); + bfd_put_16 (output_bfd, (bfd_vma) 0, need_pinfo + 10); + strcpy (need_pnames, inp->filename); + } + else + { + char *verstr; + int maj, min; + + bfd_put_32 (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 (output_bfd, (bfd_vma) maj, need_pinfo + 8); + bfd_put_16 (output_bfd, (bfd_vma) min, need_pinfo + 10); + strcpy (need_pnames, inp->local_sym_name + 2); + } + + c = (need_pinfo - need_contents) / NEED_ENTRY_SIZE; + if (c + 1 >= need_entries) + bfd_put_32 (output_bfd, (bfd_vma) 0, need_pinfo + 12); + else + bfd_put_32 (output_bfd, (bfd_vma) (c + 1) * NEED_ENTRY_SIZE, + need_pinfo + 12); + + need_pinfo += NEED_ENTRY_SIZE; + need_pnames += strlen (need_pnames) + 1; + } +} + +static char * +gld${EMULATION_NAME}_get_script(isfile) + int *isfile; +EOF + +if test -n "$COMPILE_IN" +then +# Scripts compiled in. + +# sed commands to quote an ld script as a C string. +sc='s/["\\]/\\&/g +s/$/\\n\\/ +1s/^/"/ +$s/$/n"/ +' + +cat >>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <e${EMULATION_NAME}.c <