123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868 |
- /* Scan linker error messages for missing template instantiations and provide
- them.
- Copyright (C) 1995-2015 Free Software Foundation, Inc.
- Contributed by Jason Merrill (jason@cygnus.com).
- This file is part of GCC.
- GCC is free software; you can redistribute it and/or modify it under
- the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 3, or (at your option) any later
- version.
- GCC 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 GCC; see the file COPYING3. If not see
- <http://www.gnu.org/licenses/>. */
- #include "config.h"
- #include "system.h"
- #include "coretypes.h"
- #include "tm.h"
- #include "intl.h"
- #include "obstack.h"
- #include "hashtab.h"
- #include "demangle.h"
- #include "collect2.h"
- #include "collect-utils.h"
- #include "filenames.h"
- #include "diagnostic-core.h"
- #include "vec.h"
- /* TARGET_64BIT may be defined to use driver specific functionality. */
- #undef TARGET_64BIT
- #define TARGET_64BIT TARGET_64BIT_DEFAULT
- #define MAX_ITERATIONS 17
- /* Defined in the automatically-generated underscore.c. */
- extern int prepends_underscore;
- static int tlink_verbose;
- static char *initial_cwd;
- /* Hash table boilerplate for working with htab_t. We have hash tables
- for symbol names, file names, and demangled symbols. */
- typedef struct symbol_hash_entry
- {
- const char *key;
- struct file_hash_entry *file;
- int chosen;
- int tweaking;
- int tweaked;
- } symbol;
- typedef struct file_hash_entry
- {
- const char *key;
- const char *args;
- const char *dir;
- const char *main;
- int tweaking;
- } file;
- typedef const char *str;
- typedef struct demangled_hash_entry
- {
- const char *key;
- vec<str> mangled;
- } demangled;
- /* Hash and comparison functions for these hash tables. */
- static int hash_string_eq (const void *, const void *);
- static hashval_t hash_string_hash (const void *);
- static int
- hash_string_eq (const void *s1_p, const void *s2_p)
- {
- const char *const *s1 = (const char *const *) s1_p;
- const char *s2 = (const char *) s2_p;
- return strcmp (*s1, s2) == 0;
- }
- static hashval_t
- hash_string_hash (const void *s_p)
- {
- const char *const *s = (const char *const *) s_p;
- return (*htab_hash_string) (*s);
- }
- static htab_t symbol_table;
- static struct symbol_hash_entry * symbol_hash_lookup (const char *, int);
- static struct file_hash_entry * file_hash_lookup (const char *);
- static struct demangled_hash_entry *demangled_hash_lookup (const char *, int);
- static void symbol_push (symbol *);
- static symbol * symbol_pop (void);
- static void file_push (file *);
- static file * file_pop (void);
- static char * frob_extension (const char *, const char *);
- static char * obstack_fgets (FILE *, struct obstack *);
- static char * tfgets (FILE *);
- static char * pfgets (FILE *);
- static void freadsym (FILE *, file *, int);
- static void read_repo_file (file *);
- static void maybe_tweak (char *, file *);
- static int recompile_files (void);
- static int read_repo_files (char **);
- static void demangle_new_symbols (void);
- static int scan_linker_output (const char *);
- /* Look up an entry in the symbol hash table. */
- static struct symbol_hash_entry *
- symbol_hash_lookup (const char *string, int create)
- {
- void **e;
- e = htab_find_slot_with_hash (symbol_table, string,
- (*htab_hash_string) (string),
- create ? INSERT : NO_INSERT);
- if (e == NULL)
- return NULL;
- if (*e == NULL)
- {
- struct symbol_hash_entry *v;
- *e = v = XCNEW (struct symbol_hash_entry);
- v->key = xstrdup (string);
- }
- return (struct symbol_hash_entry *) *e;
- }
- static htab_t file_table;
- /* Look up an entry in the file hash table. */
- static struct file_hash_entry *
- file_hash_lookup (const char *string)
- {
- void **e;
- e = htab_find_slot_with_hash (file_table, string,
- (*htab_hash_string) (string),
- INSERT);
- if (*e == NULL)
- {
- struct file_hash_entry *v;
- *e = v = XCNEW (struct file_hash_entry);
- v->key = xstrdup (string);
- }
- return (struct file_hash_entry *) *e;
- }
- static htab_t demangled_table;
- /* Look up an entry in the demangled name hash table. */
- static struct demangled_hash_entry *
- demangled_hash_lookup (const char *string, int create)
- {
- void **e;
- e = htab_find_slot_with_hash (demangled_table, string,
- (*htab_hash_string) (string),
- create ? INSERT : NO_INSERT);
- if (e == NULL)
- return NULL;
- if (*e == NULL)
- {
- struct demangled_hash_entry *v;
- *e = v = XCNEW (struct demangled_hash_entry);
- v->key = xstrdup (string);
- }
- return (struct demangled_hash_entry *) *e;
- }
- /* Stack code. */
- struct symbol_stack_entry
- {
- symbol *value;
- struct symbol_stack_entry *next;
- };
- struct obstack symbol_stack_obstack;
- struct symbol_stack_entry *symbol_stack;
- struct file_stack_entry
- {
- file *value;
- struct file_stack_entry *next;
- };
- struct obstack file_stack_obstack;
- struct file_stack_entry *file_stack;
- static void
- symbol_push (symbol *p)
- {
- struct symbol_stack_entry *ep
- = XOBNEW (&symbol_stack_obstack, struct symbol_stack_entry);
- ep->value = p;
- ep->next = symbol_stack;
- symbol_stack = ep;
- }
- static symbol *
- symbol_pop (void)
- {
- struct symbol_stack_entry *ep = symbol_stack;
- symbol *p;
- if (ep == NULL)
- return NULL;
- p = ep->value;
- symbol_stack = ep->next;
- obstack_free (&symbol_stack_obstack, ep);
- return p;
- }
- static void
- file_push (file *p)
- {
- struct file_stack_entry *ep;
- if (p->tweaking)
- return;
- ep = XOBNEW (&file_stack_obstack, struct file_stack_entry);
- ep->value = p;
- ep->next = file_stack;
- file_stack = ep;
- p->tweaking = 1;
- }
- static file *
- file_pop (void)
- {
- struct file_stack_entry *ep = file_stack;
- file *p;
- if (ep == NULL)
- return NULL;
- p = ep->value;
- file_stack = ep->next;
- obstack_free (&file_stack_obstack, ep);
- p->tweaking = 0;
- return p;
- }
- /* Other machinery. */
- /* Initialize the tlink machinery. Called from do_tlink. */
- static void
- tlink_init (void)
- {
- const char *p;
- symbol_table = htab_create (500, hash_string_hash, hash_string_eq,
- NULL);
- file_table = htab_create (500, hash_string_hash, hash_string_eq,
- NULL);
- demangled_table = htab_create (500, hash_string_hash, hash_string_eq,
- NULL);
- obstack_begin (&symbol_stack_obstack, 0);
- obstack_begin (&file_stack_obstack, 0);
- p = getenv ("TLINK_VERBOSE");
- if (p)
- tlink_verbose = atoi (p);
- else
- {
- tlink_verbose = 1;
- if (verbose)
- tlink_verbose = 2;
- if (debug)
- tlink_verbose = 3;
- }
- initial_cwd = getpwd ();
- }
- static int
- tlink_execute (const char *prog, char **argv, const char *outname,
- const char *errname, bool use_atfile)
- {
- struct pex_obj *pex;
- pex = collect_execute (prog, argv, outname, errname,
- PEX_LAST | PEX_SEARCH, use_atfile);
- return collect_wait (prog, pex);
- }
- static char *
- frob_extension (const char *s, const char *ext)
- {
- const char *p;
- p = strrchr (lbasename (s), '.');
- if (! p)
- p = s + strlen (s);
- obstack_grow (&temporary_obstack, s, p - s);
- return (char *) obstack_copy0 (&temporary_obstack, ext, strlen (ext));
- }
- static char *
- obstack_fgets (FILE *stream, struct obstack *ob)
- {
- int c;
- while ((c = getc (stream)) != EOF && c != '\n')
- obstack_1grow (ob, c);
- if (obstack_object_size (ob) == 0)
- return NULL;
- obstack_1grow (ob, '\0');
- return XOBFINISH (ob, char *);
- }
- static char *
- tfgets (FILE *stream)
- {
- return obstack_fgets (stream, &temporary_obstack);
- }
- static char *
- pfgets (FILE *stream)
- {
- return xstrdup (tfgets (stream));
- }
- /* Real tlink code. */
- /* Subroutine of read_repo_file. We are reading the repo file for file F,
- which is coming in on STREAM, and the symbol that comes next in STREAM
- is offered, chosen or provided if CHOSEN is 0, 1 or 2, respectively.
- XXX "provided" is unimplemented, both here and in the compiler. */
- static void
- freadsym (FILE *stream, file *f, int chosen)
- {
- symbol *sym;
- {
- const char *name = tfgets (stream);
- sym = symbol_hash_lookup (name, true);
- }
- if (sym->file == NULL)
- {
- /* We didn't have this symbol already, so we choose this file. */
- symbol_push (sym);
- sym->file = f;
- sym->chosen = chosen;
- }
- else if (chosen)
- {
- /* We want this file; cast aside any pretender. */
- if (sym->chosen && sym->file != f)
- {
- if (sym->chosen == 1)
- file_push (sym->file);
- else
- {
- file_push (f);
- f = sym->file;
- chosen = sym->chosen;
- }
- }
- sym->file = f;
- sym->chosen = chosen;
- }
- }
- /* Read in the repo file denoted by F, and record all its information. */
- static void
- read_repo_file (file *f)
- {
- char c;
- FILE *stream = fopen (f->key, "r");
- if (tlink_verbose >= 2)
- fprintf (stderr, _("collect: reading %s\n"), f->key);
- while (fscanf (stream, "%c ", &c) == 1)
- {
- switch (c)
- {
- case 'A':
- f->args = pfgets (stream);
- break;
- case 'D':
- f->dir = pfgets (stream);
- break;
- case 'M':
- f->main = pfgets (stream);
- break;
- case 'P':
- freadsym (stream, f, 2);
- break;
- case 'C':
- freadsym (stream, f, 1);
- break;
- case 'O':
- freadsym (stream, f, 0);
- break;
- }
- obstack_free (&temporary_obstack, temporary_firstobj);
- }
- fclose (stream);
- if (f->args == NULL)
- f->args = getenv ("COLLECT_GCC_OPTIONS");
- if (f->dir == NULL)
- f->dir = ".";
- }
- /* We might want to modify LINE, which is a symbol line from file F. We do
- this if either we saw an error message referring to the symbol in
- question, or we have already allocated the symbol to another file and
- this one wants to emit it as well. */
- static void
- maybe_tweak (char *line, file *f)
- {
- symbol *sym = symbol_hash_lookup (line + 2, false);
- if ((sym->file == f && sym->tweaking)
- || (sym->file != f && line[0] == 'C'))
- {
- sym->tweaking = 0;
- sym->tweaked = 1;
- if (line[0] == 'O')
- {
- line[0] = 'C';
- sym->chosen = 1;
- }
- else
- {
- line[0] = 'O';
- sym->chosen = 0;
- }
- }
- }
- /* Update the repo files for each of the object files we have adjusted and
- recompile. */
- static int
- recompile_files (void)
- {
- file *f;
- putenv (xstrdup ("COMPILER_PATH="));
- putenv (xstrdup ("LIBRARY_PATH="));
- while ((f = file_pop ()) != NULL)
- {
- char *line;
- const char *p, *q;
- char **argv;
- struct obstack arg_stack;
- FILE *stream = fopen (f->key, "r");
- const char *const outname = frob_extension (f->key, ".rnw");
- FILE *output = fopen (outname, "w");
- while ((line = tfgets (stream)) != NULL)
- {
- switch (line[0])
- {
- case 'C':
- case 'O':
- maybe_tweak (line, f);
- }
- fprintf (output, "%s\n", line);
- }
- fclose (stream);
- fclose (output);
- /* On Windows "rename" returns -1 and sets ERRNO to EACCESS if
- the new file name already exists. Therefore, we explicitly
- remove the old file first. */
- if (remove (f->key) == -1)
- fatal_error (input_location, "removing .rpo file: %m");
- if (rename (outname, f->key) == -1)
- fatal_error (input_location, "renaming .rpo file: %m");
- if (!f->args)
- {
- error ("repository file '%s' does not contain command-line "
- "arguments", f->key);
- return 0;
- }
- /* Build a null-terminated argv array suitable for
- tlink_execute(). Manipulate arguments on the arg_stack while
- building argv on the temporary_obstack. */
- obstack_init (&arg_stack);
- obstack_ptr_grow (&temporary_obstack, c_file_name);
- for (p = f->args; *p != '\0'; p = q + 1)
- {
- /* Arguments are delimited by single-quotes. Find the
- opening quote. */
- p = strchr (p, '\'');
- if (!p)
- goto done;
- /* Find the closing quote. */
- q = strchr (p + 1, '\'');
- if (!q)
- goto done;
- obstack_grow (&arg_stack, p + 1, q - (p + 1));
- /* Replace '\'' with '. This is how set_collect_gcc_options
- encodes a single-quote. */
- while (q[1] == '\\' && q[2] == '\'' && q[3] == '\'')
- {
- const char *r;
- r = strchr (q + 4, '\'');
- if (!r)
- goto done;
- obstack_grow (&arg_stack, q + 3, r - (q + 3));
- q = r;
- }
- obstack_1grow (&arg_stack, '\0');
- obstack_ptr_grow (&temporary_obstack, obstack_finish (&arg_stack));
- }
- done:
- obstack_ptr_grow (&temporary_obstack, f->main);
- obstack_ptr_grow (&temporary_obstack, NULL);
- argv = XOBFINISH (&temporary_obstack, char **);
- if (tlink_verbose)
- fprintf (stderr, _("collect: recompiling %s\n"), f->main);
- if (chdir (f->dir) != 0
- || tlink_execute (c_file_name, argv, NULL, NULL, false) != 0
- || chdir (initial_cwd) != 0)
- return 0;
- read_repo_file (f);
- obstack_free (&arg_stack, NULL);
- obstack_free (&temporary_obstack, temporary_firstobj);
- }
- return 1;
- }
- /* The first phase of processing: determine which object files have
- .rpo files associated with them, and read in the information. */
- static int
- read_repo_files (char **object_lst)
- {
- char **object = object_lst;
- for (; *object; object++)
- {
- const char *p;
- file *f;
- /* Don't bother trying for ld flags. */
- if (*object[0] == '-')
- continue;
- p = frob_extension (*object, ".rpo");
- if (! file_exists (p))
- continue;
- f = file_hash_lookup (p);
- read_repo_file (f);
- }
- if (file_stack != NULL && ! recompile_files ())
- return 0;
- return (symbol_stack != NULL);
- }
- /* Add the demangled forms of any new symbols to the hash table. */
- static void
- demangle_new_symbols (void)
- {
- symbol *sym;
- while ((sym = symbol_pop ()) != NULL)
- {
- demangled *dem;
- const char *p = cplus_demangle (sym->key, DMGL_PARAMS | DMGL_ANSI);
- if (! p)
- continue;
- dem = demangled_hash_lookup (p, true);
- dem->mangled.safe_push (sym->key);
- }
- }
- /* We want to tweak symbol SYM. Return true if all is well, false on
- error. */
- static bool
- start_tweaking (symbol *sym)
- {
- if (sym && sym->tweaked)
- {
- error ("'%s' was assigned to '%s', but was not defined "
- "during recompilation, or vice versa",
- sym->key, sym->file->key);
- return 0;
- }
- if (sym && !sym->tweaking)
- {
- if (tlink_verbose >= 2)
- fprintf (stderr, _("collect: tweaking %s in %s\n"),
- sym->key, sym->file->key);
- sym->tweaking = 1;
- file_push (sym->file);
- }
- return true;
- }
- /* Step through the output of the linker, in the file named FNAME, and
- adjust the settings for each symbol encountered. */
- static int
- scan_linker_output (const char *fname)
- {
- FILE *stream = fopen (fname, "r");
- char *line;
- int skip_next_in_line = 0;
- while ((line = tfgets (stream)) != NULL)
- {
- char *p = line, *q;
- symbol *sym;
- demangled *dem = 0;
- int end;
- int ok = 0;
- unsigned ix;
- str s;
- /* On darwin9, we might have to skip " in " lines as well. */
- if (skip_next_in_line
- && strstr (p, " in "))
- continue;
- skip_next_in_line = 0;
- while (*p && ISSPACE ((unsigned char) *p))
- ++p;
- if (! *p)
- continue;
- for (q = p; *q && ! ISSPACE ((unsigned char) *q); ++q)
- ;
- /* Try the first word on the line. */
- if (*p == '.')
- ++p;
- if (!strncmp (p, USER_LABEL_PREFIX, strlen (USER_LABEL_PREFIX)))
- p += strlen (USER_LABEL_PREFIX);
- end = ! *q;
- *q = 0;
- sym = symbol_hash_lookup (p, false);
- /* Some SVR4 linkers produce messages like
- ld: 0711-317 ERROR: Undefined symbol: .g__t3foo1Zi
- */
- if (! sym && ! end && strstr (q + 1, "Undefined symbol: "))
- {
- char *p = strrchr (q + 1, ' ');
- p++;
- if (*p == '.')
- p++;
- if (!strncmp (p, USER_LABEL_PREFIX, strlen (USER_LABEL_PREFIX)))
- p += strlen (USER_LABEL_PREFIX);
- sym = symbol_hash_lookup (p, false);
- }
- if (! sym && ! end)
- /* Try a mangled name in quotes. */
- {
- char *oldq = q + 1;
- q = 0;
- /* On darwin9, we look for "foo" referenced from:\n\(.* in .*\n\)* */
- if (strcmp (oldq, "referenced from:") == 0)
- {
- /* We have to remember that we found a symbol to tweak. */
- ok = 1;
- /* We actually want to start from the first word on the
- line. */
- oldq = p;
- /* Since the format is multiline, we have to skip
- following lines with " in ". */
- skip_next_in_line = 1;
- }
- /* First try `GNU style'. */
- p = strchr (oldq, '`');
- if (p)
- p++, q = strchr (p, '\'');
- /* Then try "double quotes". */
- else if (p = strchr (oldq, '"'), p)
- p++, q = strchr (p, '"');
- /* Then try 'single quotes'. */
- else if (p = strchr (oldq, '\''), p)
- p++, q = strchr (p, '\'');
- else {
- /* Then try entire line. */
- q = strchr (oldq, 0);
- if (q != oldq)
- p = (char *)oldq;
- }
- if (p)
- {
- /* Don't let the strstr's below see the demangled name; we
- might get spurious matches. */
- p[-1] = '\0';
- /* powerpc64-linux references .foo when calling function foo. */
- if (*p == '.')
- p++;
- }
- /* We need to check for certain error keywords here, or we would
- mistakenly use GNU ld's "In function `foo':" message. */
- if (q && (ok
- || strstr (oldq, "ndefined")
- || strstr (oldq, "nresolved")
- || strstr (oldq, "nsatisfied")
- || strstr (oldq, "ultiple")))
- {
- *q = 0;
- dem = demangled_hash_lookup (p, false);
- if (!dem)
- {
- if (!strncmp (p, USER_LABEL_PREFIX,
- strlen (USER_LABEL_PREFIX)))
- p += strlen (USER_LABEL_PREFIX);
- sym = symbol_hash_lookup (p, false);
- }
- }
- }
- if (dem)
- {
- /* We found a demangled name. If this is the name of a
- constructor or destructor, there can be several mangled names
- that match it, so choose or unchoose all of them. If some are
- chosen and some not, leave the later ones that don't match
- alone for now; either this will cause the link to succeed, or
- on the next attempt we will switch all of them the other way
- and that will cause it to succeed. */
- int chosen = 0;
- int len = dem->mangled.length ();
- ok = true;
- FOR_EACH_VEC_ELT (dem->mangled, ix, s)
- {
- sym = symbol_hash_lookup (s, false);
- if (ix == 0)
- chosen = sym->chosen;
- else if (sym->chosen != chosen)
- /* Mismatch. */
- continue;
- /* Avoid an error about re-tweaking when we guess wrong in
- the case of mismatch. */
- if (len > 1)
- sym->tweaked = false;
- ok = start_tweaking (sym);
- }
- }
- else
- ok = start_tweaking (sym);
- obstack_free (&temporary_obstack, temporary_firstobj);
- if (!ok)
- {
- fclose (stream);
- return 0;
- }
- }
- fclose (stream);
- return (file_stack != NULL);
- }
- /* Entry point for tlink. Called from main in collect2.c.
- Iteratively try to provide definitions for all the unresolved symbols
- mentioned in the linker error messages.
- LD_ARGV is an array of arguments for the linker.
- OBJECT_LST is an array of object files that we may be able to recompile
- to provide missing definitions. Currently ignored. */
- void
- do_tlink (char **ld_argv, char **object_lst ATTRIBUTE_UNUSED)
- {
- int ret = tlink_execute ("ld", ld_argv, ldout, lderrout,
- HAVE_GNU_LD && at_file_supplied);
- tlink_init ();
- if (ret)
- {
- int i = 0;
- /* Until collect does a better job of figuring out which are object
- files, assume that everything on the command line could be. */
- if (read_repo_files (ld_argv))
- while (ret && i++ < MAX_ITERATIONS)
- {
- if (tlink_verbose >= 3)
- {
- dump_ld_file (ldout, stdout);
- dump_ld_file (lderrout, stderr);
- }
- demangle_new_symbols ();
- if (! scan_linker_output (ldout)
- && ! scan_linker_output (lderrout))
- break;
- if (! recompile_files ())
- break;
- if (tlink_verbose)
- fprintf (stderr, _("collect: relinking\n"));
- ret = tlink_execute ("ld", ld_argv, ldout, lderrout,
- HAVE_GNU_LD && at_file_supplied);
- }
- }
- dump_ld_file (ldout, stdout);
- unlink (ldout);
- dump_ld_file (lderrout, stderr);
- unlink (lderrout);
- if (ret)
- {
- error ("ld returned %d exit status", ret);
- exit (ret);
- }
- else
- {
- /* We have just successfully produced an output file, so assume that we
- may unlink it if need be for now on. */
- may_unlink_output_file = true;
- }
- }
|