From 3b1b01cf2503d8d3faf35cf5b0cfb82fce7812ce Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Thu, 24 May 2001 20:59:23 +0000 Subject: Support for xcoff64 --- ld/emultempl/aix.em | 681 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 443 insertions(+), 238 deletions(-) (limited to 'ld/emultempl/aix.em') diff --git a/ld/emultempl/aix.em b/ld/emultempl/aix.em index 0dc332b..88d13b2 100644 --- a/ld/emultempl/aix.em +++ b/ld/emultempl/aix.em @@ -4,10 +4,10 @@ cat >e${EMULATION_NAME}.c < AIX support by Ian Lance Taylor + AIX 64 bit support by Tom Rix This file is part of GLD, the Gnu Linker. @@ -38,14 +38,17 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "ld.h" #include "ldmain.h" +#include "ldemul.h" +#include "ldfile.h" #include "ldmisc.h" #include "ldexp.h" #include "ldlang.h" -#include "ldfile.h" -#include "ldemul.h" #include "ldctor.h" #include "ldgram.h" +#include "coff/internal.h" +#include "coff/xcoff.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)); @@ -102,6 +105,9 @@ struct export_symbol_list static struct export_symbol_list *export_symbols; +/* Maintains the 32 or 64 bit mode state of import file */ +static unsigned int symbol_mode = 0x04; + /* This routine is called before anything else is done. */ static void @@ -111,6 +117,15 @@ gld${EMULATION_NAME}_before_parse() ldfile_output_architecture = bfd_arch_${ARCH}; #endif /* not TARGET_ */ config.has_shared = true; + + /* + * The link_info.[init|fini]_functions are initialized in ld/lexsup.c. + * Override them here so we can use the link_info.init_function as a + * state flag that lets the backend know that -binitfini has been done. + */ + link_info.init_function = NULL; + link_info.fini_function = NULL; + } /* Handle AIX specific options. */ @@ -125,27 +140,81 @@ gld${EMULATION_NAME}_parse_args (argc, argv) int indx; int longind; int optc; - long val; + bfd_signed_vma 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[] = { + enum { + OPTION_IGNORE = 300, + OPTION_AUTOIMP, + OPTION_ERNOTOK, + OPTION_EROK, + OPTION_EXPORT, + OPTION_IMPORT, + OPTION_INITFINI, + OPTION_LOADMAP, + OPTION_MAXDATA, + OPTION_MAXSTACK, + OPTION_MODTYPE, + OPTION_NOAUTOIMP, + OPTION_NOSTRCMPCT, + OPTION_PD, + OPTION_PT, + OPTION_STRCMPCT, + OPTION_UNIX + }; + + /* + b64 is an empty option. The native linker uses -b64 for xcoff64 support + Our linker uses -m aixppc64 for xcoff64 support. The choice for the + correct emulation is done in collect2.c via the environmental varible + LDEMULATION. + + binitfini has special handling in the linker backend. The native linker + uses the arguemnts to generate a table of init and fini functions for + the executable. The important use for this option is to support aix 4.2+ + c++ constructors and destructors. This is tied into gcc via collect2.c. + The function table is accessed by the runtime linker/loader by checking if + the first symbol in the loader symbol table is "__rtinit". The native + linker generates this table and the loader symbol. The gnu linker looks + for the symbol "__rtinit" and makes it the first loader symbol. It is the + responsiblity of the user to define the __rtinit symbol. The format for + __rtinit is given by the aix system file /usr/include/rtinit.h. You can + look at collect2.c to see an example of how this is done for 32 and 64 bit. + Below is an exmaple of a 32 bit assembly file that defines __rtinit. + + .file "my_rtinit.s" + + .csect .data[RW],3 + .globl __rtinit + .extern init_function + .extern fini_function + + __rtinit: + .long 0 + .long f1i - __rtinit + .long f1f - __rtinit + .long f2i - f1i + .align 3 + f1i: .long init_function + .long s1i - __rtinit + .long 0 + f2i: .long 0 + .long 0 + .long 0 + f1f: .long fini_function + .long s1f - __rtinit + .long 0 + f2f: .long 0 + .long 0 + .long 0 + .align 3 + s1i: .string "init_function" + .align 3 + s1f: .string "fini_function" + + */ + + static const struct option longopts[] = { {"basis", no_argument, NULL, OPTION_IGNORE}, {"bautoimp", no_argument, NULL, OPTION_AUTOIMP}, {"bcomprld", no_argument, NULL, OPTION_IGNORE}, @@ -163,6 +232,7 @@ gld${EMULATION_NAME}_parse_args (argc, argv) {"bhalt", required_argument, NULL, OPTION_IGNORE}, {"bI", required_argument, NULL, OPTION_IMPORT}, {"bimport", required_argument, NULL, OPTION_IMPORT}, + {"binitfini", required_argument, NULL, OPTION_INITFINI}, {"bl", required_argument, NULL, OPTION_LOADMAP}, {"bloadmap", required_argument, NULL, OPTION_LOADMAP}, {"bmaxdata", required_argument, NULL, OPTION_MAXDATA}, @@ -184,6 +254,7 @@ gld${EMULATION_NAME}_parse_args (argc, argv) {"bso", no_argument, NULL, OPTION_AUTOIMP}, {"bstrcmpct", no_argument, NULL, OPTION_STRCMPCT}, {"btextro", no_argument, &textro, 1}, + {"b64", no_argument, NULL, 0}, {"static", no_argument, NULL, OPTION_NOAUTOIMP}, {"unix", no_argument, NULL, OPTION_UNIX}, {NULL, no_argument, NULL, 0} @@ -202,21 +273,21 @@ gld${EMULATION_NAME}_parse_args (argc, argv) 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) + 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; - } - } + } + + 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. */ @@ -238,7 +309,7 @@ gld${EMULATION_NAME}_parse_args (argc, argv) break; case 'D': - val = strtol (optarg, &end, 0); + val = strtoll (optarg, &end, 0); if (*end != '\0') einfo ("%P: warning: ignoring invalid -D number %s\n", optarg); else if (val != -1) @@ -265,7 +336,7 @@ gld${EMULATION_NAME}_parse_args (argc, argv) 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); + val = strtoull (optarg, &end, 0); if (*end != '\0') { optind = prevoptind; @@ -277,6 +348,41 @@ gld${EMULATION_NAME}_parse_args (argc, argv) case OPTION_IGNORE: break; + case OPTION_INITFINI: { + /* + * The aix linker init fini has the format : + * + * -binitfini:[ Initial][:Termination][:Priority] + * + * it allows the Termination and Priority to be optional. + * + * Since we support only one init/fini pair, we ignore the Priority. + * + * Define the special symbol __rtinit. + * + * strtok does not correctly handle the case of -binitfini::fini: so + * do it by hand + */ + char *t, *i, *f; + + i = t = optarg; + while (*t && ':' != *t) t++; + if (*t) *t++ = 0; + + if (0 != strlen(i)) { + link_info.init_function = i; + } + + f = t; + while (*t && ':' != *t) t++; + *t = 0; + + if (0 != strlen(f)) { + link_info.fini_function = f; + } + } + break; + case OPTION_AUTOIMP: link_info.static_link = false; break; @@ -313,7 +419,7 @@ gld${EMULATION_NAME}_parse_args (argc, argv) break; case OPTION_MAXDATA: - val = strtoul (optarg, &end, 0); + val = strtoull (optarg, &end, 0); if (*end != '\0') einfo ("%P: warning: ignoring invalid -bmaxdata number %s\n", optarg); @@ -322,7 +428,7 @@ gld${EMULATION_NAME}_parse_args (argc, argv) break; case OPTION_MAXSTACK: - val = strtoul (optarg, &end, 0); + val = strtoull (optarg, &end, 0); if (*end != '\0') einfo ("%P: warning: ignoring invalid -bmaxstack number %s\n", optarg); @@ -355,7 +461,7 @@ gld${EMULATION_NAME}_parse_args (argc, argv) 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); + val = strtoull (optarg, &end, 0); if (*end != '\0') einfo ("%P: warning: ignoring invalid -pD number %s\n", optarg); else @@ -378,7 +484,7 @@ gld${EMULATION_NAME}_parse_args (argc, argv) /* 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); + val = strtoull (optarg, &end, 0); if (*end != '\0') einfo ("%P: warning: ignoring invalid -pT number %s\n", optarg); else @@ -499,22 +605,21 @@ gld${EMULATION_NAME}_before_allocation () struct filelist *fl; struct export_symbol_list *el; char *libpath; - asection *special_sections[6]; + asection *special_sections[XCOFF_NUMBER_OF_SPECIAL_SECTIONS]; int i; /* Handle the import and export files, if any. */ for (fl = import_files; fl != NULL; fl = fl->next) gld${EMULATION_NAME}_read_file (fl->name, true); - for (el = export_symbols; el != NULL; el = el->next) - { - struct bfd_link_hash_entry *h; - - h = bfd_link_hash_lookup (link_info.hash, el->name, false, false, false); - if (h == NULL) - einfo ("%P%F: bfd_link_hash_lookup of export symbol failed: %E\n"); - if (! bfd_xcoff_export_symbol (output_bfd, &link_info, h, el->syscall)) - einfo ("%P%F: bfd_xcoff_export_symbol failed: %E\n"); - } + 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 @@ -562,109 +667,186 @@ gld${EMULATION_NAME}_before_allocation () /* 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; + for (i = 0; i < XCOFF_NUMBER_OF_SPECIAL_SECTIONS; i++) { + asection *sec; + lang_output_section_statement_type *os; + lang_statement_union_type **pls; + lang_input_section_type *is; + const char *oname; + 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); + } - /* 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; + 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 ((*pls)->header.type == lang_wild_statement_enum) - { - lang_statement_union_type **pwls; + } + } + + if (is != NULL) + break; + } + } - 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 XCOFF_SPECIAL_SECTION_TEXT: + /* _text */ + oname = ".text"; + start = true; + break; + + case XCOFF_SPECIAL_SECTION_ETEXT: + /* _etext */ + oname = ".text"; + start = false; + break; - if (is == NULL) - einfo ("%P%F: can't find %s in output section\n", - bfd_get_section_name (sec->owner, sec)); + case XCOFF_SPECIAL_SECTION_DATA: + /* _data */ + oname = ".data"; + start = true; + break; + + case XCOFF_SPECIAL_SECTION_EDATA: + /* _edata */ + oname = ".data"; + start = false; + break; - /* 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; - } + case XCOFF_SPECIAL_SECTION_END: + case XCOFF_SPECIAL_SECTION_END2: + /* _end and end */ + oname = ".bss"; + start = false; + break; + } - os = lang_output_section_find (oname); + 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); - } + 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); + } + } +} + +static int change_symbol_mode (char *input) +{ + /* + * 1 : state changed + * 0 : no change + */ + + char *symbol_mode_string[] = { + "# 32", /* 0x01 */ + "# 64", /* 0x02 */ + "# no32", /* 0x04 */ + "# no64", /* 0x08 */ + NULL, + }; + unsigned int bit; + char *string; + + for (bit = 0; ; bit++) { + string = symbol_mode_string[bit]; + if (NULL == string) + return 0; + + if (0 == strcmp(input, string)) { + symbol_mode = (1 << bit); + return 1; + } + } + /* should not be here */ + return 0; +} + + +static int is_syscall(char *input) +{ + /* + * 1 : yes + * 0 : ignore + * -1 : error, try something else + */ + unsigned int bit; + char *string; + + char *syscall_string[] = { + "svc", /* 0x01 */ + "svc32", /* 0x02 */ + "svc3264", /* 0x04 */ + "svc64", /* 0x08 */ + "syscall", /* 0x10 */ + "syscall32", /* 0x20 */ + "syscall3264", /* 0x40 */ + "syscall64", /* 0x80 */ + NULL + }; + + for (bit = 0; ;bit++) { + + string = syscall_string[bit]; + if (NULL == string) { + return -1; + } + + if (0 == strcmp(input, string)) { + if (1 << bit & ${SYSCALL_MASK}) { + return 1; + } else { + return 0; + } } + } + /* should not be here */ + return -1; } + /* Read an import or export file. For an import file, this is called by the before_allocation emulation routine. For an export file, this is called by the parse_args emulation routine. */ @@ -700,6 +882,15 @@ gld${EMULATION_NAME}_read_file (filename, import) impmember = NULL; lineno = 0; + + /* + * default to 32 and 64 bit mode + * symbols at top of /lib/syscalls.exp do not have a mode modifier and they + * are not repeated, assume 64 bit routines also want to use them. + * See the routine change_symbol_mode for more information. + */ + symbol_mode = 0x04; + while ((c = getc (f)) != EOF) { char *s; @@ -720,10 +911,11 @@ gld${EMULATION_NAME}_read_file (filename, import) s = (char *) obstack_base (o); while (isspace ((unsigned char) *s)) ++s; - if (*s == '\0' - || *s == '*' - || (*s == '#' && s[1] == ' ') - || (! import && *s == '#' && s[1] == '!')) + if (*s == '\0' || + *s == '*' || + change_symbol_mode (s) || + (*s == '#' && s[1] == ' ') || + (! import && *s == '#' && s[1] == '!')) { obstack_free (o, obstack_base (o)); continue; @@ -802,100 +994,111 @@ gld${EMULATION_NAME}_read_file (filename, import) 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 - { + if (symbol_mode & ${SYMBOL_MODE_MASK}) { + + /* 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) { + int status; 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 = xstrdup (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); + + status = is_syscall(s); + + switch (status) { + case 1: + /* this is a system call */ + syscall = true; + break; + + case 0: + /* ignore this system call */ + break; + + default: + /* not a system call, check for address */ + 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 = xstrdup (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)); - } + 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); + if (! keep) + { + obstack_free (o, NULL); + free (o); + } } -} /* This routine saves us from worrying about declaring free. */ @@ -986,7 +1189,7 @@ then # Scripts compiled in. # sed commands to quote an ld script as a C string. -sc="-f stringify.sed" +sc="-f ${srcdir}/emultempl/stringify.sed" cat >>e${EMULATION_NAME}.c <