summaryrefslogtreecommitdiff
path: root/gcc/tlink.c
diff options
context:
space:
mode:
authorlaw <law@138bc75d-0d04-0410-961f-82ee72b054a4>1997-08-11 20:23:53 +0000
committerlaw <law@138bc75d-0d04-0410-961f-82ee72b054a4>1997-08-11 20:23:53 +0000
commit94ca3aabc3c15be6abd61e91fc1ef184c9fe3dc5 (patch)
treefd4878ed0cdfb269f8e71f6bb4319c96a290414b /gcc/tlink.c
parentea0cb7ae5f6f719c7b651fd31451921e2528bea9 (diff)
downloadgcc-94ca3aabc3c15be6abd61e91fc1ef184c9fe3dc5.tar.gz
* Integrate tlink patch from jason@cygnus.com
* gcc.c (SWITCH_TAKES_ARG): Add 'V', 'B' and 'b'. (process_command): Increment n_switches for them. Don't discard their args. Validate them. (main): Escape " marks when creating COLLECT_GCC_OPTIONS. From Rohan Lenard. (process_command): Set include_prefixes from COMPILER_PATH. (main): Set COLLECT_GCC_OPTIONS sooner. * confiugre.in: Link ../ld/ld.new to collect-ld rather than real-ld. * tlink.c, hash.c, hash.h: New files. * Makefile.in (USE_COLLECT2): Always use collect2. (collect2): Depend on and link in hash.o and tlink.o. (tlink.o, hash.o): Add dependencies. tlink patches from Jason. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@14769 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/tlink.c')
-rw-r--r--gcc/tlink.c631
1 files changed, 631 insertions, 0 deletions
diff --git a/gcc/tlink.c b/gcc/tlink.c
new file mode 100644
index 00000000000..77b7875c193
--- /dev/null
+++ b/gcc/tlink.c
@@ -0,0 +1,631 @@
+/* CYGNUS LOCAL: whole file jason */
+/* Scan linker error messages for missing template instantiations and provide
+ them.
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Contributed by Jason Merrill (jason@cygnus.com).
+
+This file is part of GNU CC.
+
+GNU CC 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 version.
+
+GNU CC 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 GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include "config.h"
+#include "hash.h"
+#include "demangle.h"
+
+#define MAX_ITERATIONS 17
+
+/* Obstack allocation and deallocation routines. */
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+extern char * xmalloc PARAMS((unsigned));
+extern void free ();
+extern char * getenv ();
+
+/* Defined in collect2.c. */
+extern int vflag, debug;
+extern char *ldout;
+extern char *c_file_name;
+extern struct obstack temporary_obstack;
+extern struct obstack permanent_obstack;
+extern char * temporary_firstobj;
+
+/* Defined in the automatically-generated underscore.c. */
+extern int prepends_underscore;
+
+static int tlink_verbose;
+
+/* Hash table code. */
+
+typedef struct symbol_hash_entry
+{
+ struct hash_entry root;
+ struct file_hash_entry *file;
+ int chosen;
+ int tweaking;
+ int tweaked;
+} symbol;
+
+typedef struct file_hash_entry
+{
+ struct hash_entry root;
+ const char *args;
+ const char *dir;
+ const char *main;
+ int tweaking;
+} file;
+
+typedef struct demangled_hash_entry
+{
+ struct hash_entry root;
+ const char *mangled;
+} demangled;
+
+static struct hash_table symbol_table;
+
+static struct hash_entry *
+symbol_hash_newfunc (entry, table, string)
+ struct hash_entry *entry;
+ struct hash_table *table;
+ const char *string;
+{
+ struct symbol_hash_entry *ret = (struct symbol_hash_entry *) entry;
+ if (ret == NULL)
+ {
+ ret = ((struct symbol_hash_entry *)
+ hash_allocate (table, sizeof (struct symbol_hash_entry)));
+ if (ret == NULL)
+ return NULL;
+ }
+ ret = ((struct symbol_hash_entry *)
+ hash_newfunc ((struct hash_entry *) ret, table, string));
+ ret->file = NULL;
+ ret->chosen = 0;
+ ret->tweaking = 0;
+ ret->tweaked = 0;
+ return (struct hash_entry *) ret;
+}
+
+static struct symbol_hash_entry *
+symbol_hash_lookup (string, create)
+ const char *string;
+ boolean create;
+{
+ return ((struct symbol_hash_entry *)
+ hash_lookup (&symbol_table, string, create, true));
+}
+
+static struct hash_table file_table;
+
+static struct hash_entry *
+file_hash_newfunc (entry, table, string)
+ struct hash_entry *entry;
+ struct hash_table *table;
+ const char *string;
+{
+ struct file_hash_entry *ret = (struct file_hash_entry *) entry;
+ if (ret == NULL)
+ {
+ ret = ((struct file_hash_entry *)
+ hash_allocate (table, sizeof (struct file_hash_entry)));
+ if (ret == NULL)
+ return NULL;
+ }
+ ret = ((struct file_hash_entry *)
+ hash_newfunc ((struct hash_entry *) ret, table, string));
+ ret->args = NULL;
+ ret->dir = NULL;
+ ret->main = NULL;
+ ret->tweaking = 0;
+ return (struct hash_entry *) ret;
+}
+
+static struct file_hash_entry *
+file_hash_lookup (string)
+ const char *string;
+{
+ return ((struct file_hash_entry *)
+ hash_lookup (&file_table, string, true, true));
+}
+
+static struct hash_table demangled_table;
+
+static struct hash_entry *
+demangled_hash_newfunc (entry, table, string)
+ struct hash_entry *entry;
+ struct hash_table *table;
+ const char *string;
+{
+ struct demangled_hash_entry *ret = (struct demangled_hash_entry *) entry;
+ if (ret == NULL)
+ {
+ ret = ((struct demangled_hash_entry *)
+ hash_allocate (table, sizeof (struct demangled_hash_entry)));
+ if (ret == NULL)
+ return NULL;
+ }
+ ret = ((struct demangled_hash_entry *)
+ hash_newfunc ((struct hash_entry *) ret, table, string));
+ ret->mangled = NULL;
+ return (struct hash_entry *) ret;
+}
+
+static struct demangled_hash_entry *
+demangled_hash_lookup (string, create)
+ const char *string;
+ boolean create;
+{
+ return ((struct demangled_hash_entry *)
+ hash_lookup (&demangled_table, string, create, true));
+}
+
+/* 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 (p)
+ symbol *p;
+{
+ struct symbol_stack_entry *ep = (struct symbol_stack_entry *) obstack_alloc
+ (&symbol_stack_obstack, sizeof (struct symbol_stack_entry));
+ ep->value = p;
+ ep->next = symbol_stack;
+ symbol_stack = ep;
+}
+
+static symbol *
+symbol_pop ()
+{
+ 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 (p)
+ file *p;
+{
+ struct file_stack_entry *ep;
+
+ if (p->tweaking)
+ return;
+
+ ep = (struct file_stack_entry *) obstack_alloc
+ (&file_stack_obstack, sizeof (struct file_stack_entry));
+ ep->value = p;
+ ep->next = file_stack;
+ file_stack = ep;
+ p->tweaking = 1;
+}
+
+static file *
+file_pop ()
+{
+ 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. */
+
+static void
+tlink_init ()
+{
+ char *p;
+
+ hash_table_init (&symbol_table, symbol_hash_newfunc);
+ hash_table_init (&file_table, file_hash_newfunc);
+ hash_table_init (&demangled_table, demangled_hash_newfunc);
+ 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 (vflag)
+ tlink_verbose = 2;
+ if (debug)
+ tlink_verbose = 3;
+ }
+}
+
+static int
+tlink_execute (prog, argv, redir)
+ char *prog;
+ char **argv;
+ char *redir;
+{
+ collect_execute (prog, argv, redir);
+ return collect_wait (prog);
+}
+
+static char *
+frob_extension (s, ext)
+ char *s, *ext;
+{
+ char *p = (char *) rindex (s, '/');
+ if (! p)
+ p = s;
+ p = (char *) rindex (p, '.');
+ if (! p)
+ p = s + strlen (s);
+
+ obstack_grow (&temporary_obstack, s, p - s);
+ return obstack_copy0 (&temporary_obstack, ext, strlen (ext));
+}
+
+static char *
+obstack_fgets (stream, ob)
+ 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 obstack_finish (ob);
+}
+
+static char *
+tfgets (stream)
+ FILE *stream;
+{
+ return obstack_fgets (stream, &temporary_obstack);
+}
+
+static char *
+pfgets (stream)
+ FILE *stream;
+{
+ return obstack_fgets (stream, &permanent_obstack);
+}
+
+/* Real tlink code. */
+
+static void
+freadsym (stream, f, chosen)
+ FILE *stream;
+ file *f;
+ int chosen;
+{
+ symbol *sym;
+
+ {
+ char *name = tfgets (stream);
+ sym = symbol_hash_lookup (name, true);
+ }
+
+ if (sym->file == NULL)
+ {
+ symbol_push (sym);
+ sym->file = f;
+ sym->chosen = chosen;
+ }
+ else if (chosen)
+ {
+ 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;
+ }
+}
+
+static void
+read_repo_file (f)
+ file *f;
+{
+ char c;
+ FILE *stream = fopen (f->root.string, "r");
+
+ if (tlink_verbose >= 2)
+ fprintf (stderr, "collect: reading %s\n", f->root.string);
+
+ 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 = ".";
+}
+
+static void
+maybe_tweak (line, f)
+ 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';
+ else
+ line[0] = 'O';
+ }
+}
+
+static int
+recompile_files ()
+{
+ file *f;
+
+ while ((f = file_pop ()) != NULL)
+ {
+ char *line, *command;
+ FILE *stream = fopen (f->root.string, "r");
+ char *outname = frob_extension (f->root.string, ".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);
+ rename (outname, f->root.string);
+
+ 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));
+
+ if (tlink_verbose)
+ fprintf (stderr, "collect: recompiling %s\n", f->main);
+ if (tlink_verbose >= 3)
+ fprintf (stderr, "%s\n", command);
+
+ if (system (command) != 0)
+ return 0;
+
+ read_repo_file (f);
+
+ obstack_free (&temporary_obstack, temporary_firstobj);
+ }
+ return 1;
+}
+
+static int
+read_repo_files (object_lst)
+ char **object_lst;
+{
+ char **object = object_lst;
+
+ for (; *object; object++)
+ {
+ char *p = frob_extension (*object, ".rpo");
+ file *f;
+
+ 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);
+}
+
+static void
+demangle_new_symbols ()
+{
+ symbol *sym;
+
+ while ((sym = symbol_pop ()) != NULL)
+ {
+ demangled *dem;
+ char *p = cplus_demangle (sym->root.string, DMGL_PARAMS | DMGL_ANSI);
+
+ if (! p)
+ continue;
+
+ dem = demangled_hash_lookup (p, true);
+ dem->mangled = sym->root.string;
+ }
+}
+
+static int
+scan_linker_output (fname)
+ char *fname;
+{
+ FILE *stream = fopen (fname, "r");
+ char *line;
+
+ while ((line = tfgets (stream)) != NULL)
+ {
+ char *p = line, *q;
+ symbol *sym;
+ int end;
+
+ while (*p && isspace (*p))
+ ++p;
+
+ if (! *p)
+ continue;
+
+ for (q = p; *q && ! isspace (*q); ++q)
+ ;
+
+ /* Try the first word on the line. */
+ if (*p == '.')
+ ++p;
+ if (*p == '_' && prepends_underscore)
+ ++p;
+
+ end = ! *q;
+ *q = 0;
+ sym = symbol_hash_lookup (p, false);
+
+ if (! sym && ! end)
+ /* Try a mangled name in `quotes'. */
+ {
+ demangled *dem = 0;
+ p = (char *) index (q+1, '`');
+ q = 0;
+
+#define MUL "multiple definition of "
+#define UND "undefined reference to "
+
+ if (p && (p - line > sizeof (MUL)))
+ {
+ char *beg = p - sizeof (MUL) + 1;
+ *p = 0;
+ if (!strcmp (beg, MUL) || !strcmp (beg, UND))
+ p++, q = (char *) index (p, '\'');
+ }
+ if (q)
+ *q = 0, dem = demangled_hash_lookup (p, false);
+ if (dem)
+ sym = symbol_hash_lookup (dem->mangled, false);
+ }
+
+ if (sym && sym->tweaked)
+ return 0;
+ if (sym && !sym->tweaking)
+ {
+ if (tlink_verbose >= 2)
+ fprintf (stderr, "collect: tweaking %s in %s\n",
+ sym->root.string, sym->file->root.string);
+ sym->tweaking = 1;
+ file_push (sym->file);
+ }
+
+ obstack_free (&temporary_obstack, temporary_firstobj);
+ }
+
+ return (file_stack != NULL);
+}
+
+void
+do_tlink (ld_argv, object_lst)
+ char **ld_argv, **object_lst;
+{
+ int exit = tlink_execute ("ld", ld_argv, ldout);
+
+ tlink_init ();
+
+ if (exit)
+ {
+ 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 (exit && i++ < MAX_ITERATIONS)
+ {
+ if (tlink_verbose >= 3)
+ dump_file (ldout);
+ demangle_new_symbols ();
+ if (! scan_linker_output (ldout))
+ break;
+ if (! recompile_files ())
+ break;
+ if (tlink_verbose)
+ fprintf (stderr, "collect: relinking\n");
+ exit = tlink_execute ("ld", ld_argv, ldout);
+ }
+ }
+
+ dump_file (ldout);
+ unlink (ldout);
+ if (exit)
+ {
+ error ("ld returned %d exit status", exit);
+ collect_exit (exit);
+ }
+}