/* Scan linker error messages for missing template instantiations and provide
them.
- Copyright (C) 1995, 1998, 1999, 2000, 2001, 2003
- Free Software Foundation, Inc.
+ Copyright (C) 1995, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2007, 2008,
+ 2009, 2010, 2011 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 2, or (at your option) any later
+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
for more details.
You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING. If not, write to the Free
-Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA. */
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "hashtab.h"
#include "demangle.h"
#include "collect2.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
extern int prepends_underscore;
static int tlink_verbose;
+
+static char *initial_cwd;
\f
/* Hash table boilerplate for working with htab_t. We have hash tables
for symbol names, file names, and demangled symbols. */
int tweaking;
} file;
+typedef const char *str;
+DEF_VEC_P(str);
+DEF_VEC_ALLOC_P(str,heap);
+
typedef struct demangled_hash_entry
{
const char *key;
- const char *mangled;
+ VEC(str,heap) *mangled;
} demangled;
/* Hash and comparison functions for these hash tables. */
static void file_push (file *);
static file * file_pop (void);
static void tlink_init (void);
-static int tlink_execute (const char *, char **, const char *);
+static int tlink_execute (const char *, char **, const char *, const char *);
static char * frob_extension (const char *, const char *);
static char * obstack_fgets (FILE *, struct obstack *);
static char * tfgets (FILE *);
if (*e == NULL)
{
struct symbol_hash_entry *v;
- *e = v = xcalloc (1, sizeof (*v));
+ *e = v = XCNEW (struct symbol_hash_entry);
v->key = xstrdup (string);
}
- return *e;
+ return (struct symbol_hash_entry *) *e;
}
static htab_t file_table;
if (*e == NULL)
{
struct file_hash_entry *v;
- *e = v = xcalloc (1, sizeof (*v));
+ *e = v = XCNEW (struct file_hash_entry);
v->key = xstrdup (string);
}
- return *e;
+ return (struct file_hash_entry *) *e;
}
static htab_t demangled_table;
if (*e == NULL)
{
struct demangled_hash_entry *v;
- *e = v = xcalloc (1, sizeof (*v));
+ *e = v = XCNEW (struct demangled_hash_entry);
v->key = xstrdup (string);
}
- return *e;
+ return (struct demangled_hash_entry *) *e;
}
\f
/* Stack code. */
static void
symbol_push (symbol *p)
{
- struct symbol_stack_entry *ep = (struct symbol_stack_entry *) obstack_alloc
- (&symbol_stack_obstack, sizeof (struct symbol_stack_entry));
+ struct symbol_stack_entry *ep
+ = XOBNEW (&symbol_stack_obstack, struct symbol_stack_entry);
ep->value = p;
ep->next = symbol_stack;
symbol_stack = ep;
if (p->tweaking)
return;
- ep = (struct file_stack_entry *) obstack_alloc
- (&file_stack_obstack, sizeof (struct file_stack_entry));
+ ep = XOBNEW (&file_stack_obstack, struct file_stack_entry);
ep->value = p;
ep->next = file_stack;
file_stack = ep;
if (debug)
tlink_verbose = 3;
}
+
+ initial_cwd = getpwd ();
}
static int
-tlink_execute (const char *prog, char **argv, const char *redir)
+tlink_execute (const char *prog, char **argv, const char *outname,
+ const char *errname)
{
- collect_execute (prog, argv, redir);
- return collect_wait (prog);
+ struct pex_obj *pex;
+
+ pex = collect_execute (prog, argv, outname, errname, PEX_LAST | PEX_SEARCH);
+ return collect_wait (prog, pex);
}
static char *
frob_extension (const char *s, const char *ext)
{
- const char *p = strrchr (s, '/');
- if (! p)
- p = s;
- p = strrchr (p, '.');
+ const char *p;
+
+ p = strrchr (lbasename (s), '.');
if (! p)
p = s + strlen (s);
obstack_grow (&temporary_obstack, s, p - s);
- return obstack_copy0 (&temporary_obstack, ext, strlen (ext));
+ return (char *) obstack_copy0 (&temporary_obstack, ext, strlen (ext));
}
static char *
if (obstack_object_size (ob) == 0)
return NULL;
obstack_1grow (ob, '\0');
- return obstack_finish (ob);
+ return XOBFINISH (ob, char *);
}
static char *
sym->tweaked = 1;
if (line[0] == 'O')
- line[0] = 'C';
+ {
+ line[0] = 'C';
+ sym->chosen = 1;
+ }
else
- line[0] = 'O';
+ {
+ line[0] = 'O';
+ sym->chosen = 0;
+ }
}
}
/* Update the repo files for each of the object files we have adjusted and
- recompile.
-
- XXX Should this use collect_execute instead of system? */
+ recompile. */
static int
recompile_files (void)
while ((f = file_pop ()) != NULL)
{
- char *line, *command;
+ 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");
}
fclose (stream);
fclose (output);
- rename (outname, f->key);
+ /* 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 ("removing .rpo file: %m");
+ if (rename (outname, f->key) == -1)
+ fatal_error ("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_grow (&temporary_obstack, "cd ", 3);
- obstack_grow (&temporary_obstack, f->dir, strlen (f->dir));
- obstack_grow (&temporary_obstack, "; ", 2);
- obstack_grow (&temporary_obstack, c_file_name, strlen (c_file_name));
- obstack_1grow (&temporary_obstack, ' ');
- obstack_grow (&temporary_obstack, f->args, strlen (f->args));
- obstack_1grow (&temporary_obstack, ' ');
- command = obstack_copy0 (&temporary_obstack, f->main, strlen (f->main));
+ 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 (tlink_verbose >= 3)
- fprintf (stderr, "%s\n", command);
- if (system (command) != 0)
+ if (chdir (f->dir) != 0
+ || tlink_execute (c_file_name, argv, NULL, NULL) != 0
+ || chdir (initial_cwd) != 0)
return 0;
read_repo_file (f);
+ obstack_free (&arg_stack, NULL);
obstack_free (&temporary_obstack, temporary_firstobj);
}
return 1;
continue;
dem = demangled_hash_lookup (p, true);
- dem->mangled = sym->key;
+ VEC_safe_push (str, heap, dem->mangled, 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
{
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 (! sym && ! end)
/* Try a mangled name in quotes. */
{
- const char *oldq = q + 1;
- demangled *dem = 0;
+ 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)
/* 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);
/* We need to check for certain error keywords here, or we would
mistakenly use GNU ld's "In function `foo':" message. */
- if (q && (strstr (oldq, "ndefined")
+ 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)
- sym = symbol_hash_lookup (dem->mangled, false);
- else
+ if (!dem)
{
if (!strncmp (p, USER_LABEL_PREFIX,
strlen (USER_LABEL_PREFIX)))
}
}
- if (sym && sym->tweaked)
+ if (dem)
{
- fclose (stream);
- 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);
+ /* 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 suceed, 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 = VEC_length (str, dem->mangled);
+ ok = true;
+ FOR_EACH_VEC_ELT (str, 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);
void
do_tlink (char **ld_argv, char **object_lst ATTRIBUTE_UNUSED)
{
- int exit = tlink_execute ("ld", ld_argv, ldout);
+ int exit = tlink_execute ("ld", ld_argv, ldout, lderrout);
tlink_init ();
while (exit && i++ < MAX_ITERATIONS)
{
if (tlink_verbose >= 3)
- dump_file (ldout);
+ {
+ dump_file (ldout, stdout);
+ dump_file (lderrout, stderr);
+ }
demangle_new_symbols ();
- if (! scan_linker_output (ldout))
+ if (! scan_linker_output (ldout)
+ && ! scan_linker_output (lderrout))
break;
if (! recompile_files ())
break;
if (tlink_verbose)
fprintf (stderr, _("collect: relinking\n"));
- exit = tlink_execute ("ld", ld_argv, ldout);
+ exit = tlink_execute ("ld", ld_argv, ldout, lderrout);
}
}
- dump_file (ldout);
+ dump_file (ldout, stdout);
unlink (ldout);
+ dump_file (lderrout, stderr);
+ unlink (lderrout);
if (exit)
{
error ("ld returned %d exit status", exit);