aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeffrey A Law <law@cygnus.com>1997-08-11 20:23:53 +0000
committerJeffrey A Law <law@cygnus.com>1997-08-11 20:23:53 +0000
commitcc94826bb787bcdac89169d50c2dc09c2558d6b1 (patch)
treefd4878ed0cdfb269f8e71f6bb4319c96a290414b
parent426f6993455a6172a788ef213608397476fda556 (diff)
* 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: https://gcc.gnu.org/svn/gcc/trunk@14769 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r--gcc/ChangeLog16
-rw-r--r--gcc/Makefile.in7
-rw-r--r--gcc/collect2.c15
-rwxr-xr-xgcc/configure14
-rw-r--r--gcc/configure.in8
-rw-r--r--gcc/gcc.c97
-rw-r--r--gcc/hash.c208
-rw-r--r--gcc/hash.h131
-rw-r--r--gcc/tlink.c631
9 files changed, 1073 insertions, 54 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index cdf71eb59d8..ff0aa85597e 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,19 @@
+Mon Aug 11 14:15:02 1997 Jeffrey A Law (law@cygnus.com)
+
+ * 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.
+
Mon Aug 11 10:04:49 1997 Jeffrey A Law (law@cygnus.com)
* Integrate alias analysis changes from jfc@mit.edu
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 7d7e6daa684..76d83967f48 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -313,6 +313,7 @@ USE_COLLECT2 = @will_use_collect2@
MAYBE_USE_COLLECT2 = @maybe_use_collect2@
# It is convenient for configure to add the assignment at the beginning,
# so don't override it here.
+USE_COLLECT2 = ld
# List of extra C and assembler files to add to libgcc1.a.
# Assembler files should have names ending in `.asm'.
@@ -1169,11 +1170,11 @@ ld: collect2
ln collect2$(exeext) ld$(exeext) > /dev/null 2>&1 \
|| cp collect2$(exeext) ld$(exeext)
-collect2 : collect2.o cplus-dem.o underscore.o version.o \
+collect2: collect2.o tlink.o hash.o cplus-dem.o underscore.o version.o \
choose-temp.o $(LIBDEPS)
# Don't try modifying collect2 (aka ld) in place--it might be linking this.
-rm -f collect2$(exeext)
- $(CC) $(ALL_CFLAGS) $(LDFLAGS) -o $@ collect2.o \
+ $(CC) $(ALL_CFLAGS) $(LDFLAGS) -o $@ collect2.o tlink.o hash.o \
cplus-dem.o underscore.o version.o choose-temp.o $(LIBS)
collect2.o : collect2.c $(CONFIG_H) gstab.h obstack.h demangle.h
@@ -1181,6 +1182,8 @@ collect2.o : collect2.c $(CONFIG_H) gstab.h obstack.h demangle.h
-DTARGET_MACHINE=\"$(target_alias)\" $(MAYBE_USE_COLLECT2) \
-c `echo $(srcdir)/collect2.c | sed 's,^\./,,'`
+tlink.o: tlink.c demangle.h hash.h $(CONFIG_H)
+hash.o: hash.c hash.h
cplus-dem.o: cplus-dem.c demangle.h
underscore.c: stamp-under ; @true
diff --git a/gcc/collect2.c b/gcc/collect2.c
index e5775192f6c..64d98d78db7 100644
--- a/gcc/collect2.c
+++ b/gcc/collect2.c
@@ -1368,17 +1368,20 @@ main (argc, argv)
fprintf (stderr, "\n");
}
- /* Load the program, searching all libraries. */
+ /* Load the program, searching all libraries and attempting to provide
+ undefined symbols from repository information. */
- collect_execute ("ld", ld1_argv, ldout);
- do_wait ("ld");
- dump_file (ldout);
- unlink (ldout);
+ do_tlink (ld1_argv, object_lst);
/* If -r or they'll be run via some other method, don't build the
constructor or destructor list, just return now. */
if (rflag || ! do_collecting)
- return 0;
+ {
+ /* But make sure we delete the export file we may have created. */
+ if (export_file != 0 && export_file[0])
+ maybe_unlink (export_file);
+ return 0;
+ }
/* Examine the namelist with nm and search it for static constructors
and destructors to call.
diff --git a/gcc/configure b/gcc/configure
index b3181529acc..64999d034ab 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -4219,13 +4219,13 @@ if [ -f ../gas/Makefile ]; then
fi
# If we have ld in the build tree, make a link to it.
-if [ -f ../ld/Makefile ]; then
- if [ x$use_collect2 = x ]; then
- rm -f ld; $symbolic_link ../ld/ld.new ld 2>/dev/null
- else
- rm -f collect-ld; $symbolic_link ../ld/ld.new collect-ld 2>/dev/null
- fi
-fi
+#if [ -f ../ld/Makefile ]; then
+# if [ x$use_collect2 = x ]; then
+# rm -f ld; $symbolic_link ../ld/ld.new ld 2>/dev/null
+# else
+# rm -f collect-ld; $symbolic_link ../ld/ld.new collect-ld 2>/dev/null
+# fi
+#fi
# Figure out what language subdirectories are present.
subdirs=
diff --git a/gcc/configure.in b/gcc/configure.in
index 6656d096245..da6bdb630ea 100644
--- a/gcc/configure.in
+++ b/gcc/configure.in
@@ -2886,11 +2886,11 @@ fi
# If we have ld in the build tree, make a link to it.
if [[ -f ../ld/Makefile ]]; then
- if [[ x$use_collect2 = x ]]; then
- rm -f ld; $symbolic_link ../ld/ld.new ld 2>/dev/null
- else
+# if [[ x$use_collect2 = x ]]; then
+# rm -f ld; $symbolic_link ../ld/ld.new ld 2>/dev/null
+# else
rm -f collect-ld; $symbolic_link ../ld/ld.new collect-ld 2>/dev/null
- fi
+# fi
fi
# Figure out what language subdirectories are present.
diff --git a/gcc/gcc.c b/gcc/gcc.c
index 2027d28c513..4f6237cde63 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -525,11 +525,12 @@ static struct user_specs *user_specs_head, *user_specs_tail;
/* This defines which switch letters take arguments. */
-#define DEFAULT_SWITCH_TAKES_ARG(CHAR) \
+#define DEFAULT_SWITCH_TAKES_ARG(CHAR) \
((CHAR) == 'D' || (CHAR) == 'U' || (CHAR) == 'o' \
|| (CHAR) == 'e' || (CHAR) == 'T' || (CHAR) == 'u' \
|| (CHAR) == 'I' || (CHAR) == 'm' || (CHAR) == 'x' \
- || (CHAR) == 'L' || (CHAR) == 'A')
+ || (CHAR) == 'L' || (CHAR) == 'A' || (CHAR) == 'V' \
+ || (CHAR) == 'B' || (CHAR) == 'b')
#ifndef SWITCH_TAKES_ARG
#define SWITCH_TAKES_ARG(CHAR) DEFAULT_SWITCH_TAKES_ARG(CHAR)
@@ -2391,6 +2392,9 @@ process_command (argc, argv)
else
nstore[endp-startp] = 0;
add_prefix (&exec_prefixes, nstore, 0, 0, NULL_PTR);
+ add_prefix (&include_prefixes,
+ concat (nstore, "include", NULL_PTR),
+ 0, 0, NULL_PTR);
if (*endp == 0)
break;
endp = startp = endp + 1;
@@ -2630,6 +2634,7 @@ process_command (argc, argv)
switch (c)
{
case 'b':
+ n_switches++;
if (p[1] == 0 && i + 1 == argc)
fatal ("argument to `-b' is missing");
if (p[1] == 0)
@@ -2681,6 +2686,7 @@ process_command (argc, argv)
}
}
}
+ n_switches++;
}
break;
@@ -2694,6 +2700,7 @@ process_command (argc, argv)
break;
case 'V':
+ n_switches++;
if (p[1] == 0 && i + 1 == argc)
fatal ("argument to `-V' is missing");
if (p[1] == 0)
@@ -2884,13 +2891,6 @@ process_command (argc, argv)
register char *p = &argv[i][1];
register int c = *p;
- if (c == 'B' || c == 'b' || c == 'V')
- {
- /* Skip a separate arg, if any. */
- if (p[1] == 0)
- i++;
- continue;
- }
if (c == 'x')
{
if (p[1] == 0 && i + 1 == argc)
@@ -2952,6 +2952,12 @@ process_command (argc, argv)
/* This is always valid, since gcc.c itself understands it. */
if (!strcmp (p, "save-temps"))
switches[n_switches].valid = 1;
+ else
+ {
+ char ch = switches[n_switches].part1[0];
+ if (ch == 'V' || ch == 'b' || ch == 'B')
+ switches[n_switches].valid = 1;
+ }
n_switches++;
}
else
@@ -4373,6 +4379,53 @@ main (argc, argv)
process_command (argc, argv);
+ {
+ int i;
+ int first_time;
+
+ /* Build COLLECT_GCC_OPTIONS to have all of the options specified to
+ the compiler. */
+ obstack_grow (&collect_obstack, "COLLECT_GCC_OPTIONS=",
+ sizeof ("COLLECT_GCC_OPTIONS=")-1);
+
+ first_time = TRUE;
+ for (i = 0; i < n_switches; i++)
+ {
+ char **args;
+ char *p, *q;
+ if (!first_time)
+ obstack_grow (&collect_obstack, " ", 1);
+
+ first_time = FALSE;
+ obstack_grow (&collect_obstack, "'-", 2);
+ q = switches[i].part1;
+ while (p = (char *) index (q,'\''))
+ {
+ obstack_grow (&collect_obstack, q, p-q);
+ obstack_grow (&collect_obstack, "'\\''", 4);
+ q = ++p;
+ }
+ obstack_grow (&collect_obstack, q, strlen (q));
+ obstack_grow (&collect_obstack, "'", 1);
+
+ for (args = switches[i].args; args && *args; args++)
+ {
+ obstack_grow (&collect_obstack, " '", 2);
+ q = *args;
+ while (p = (char *) index (q,'\''))
+ {
+ obstack_grow (&collect_obstack, q, p-q);
+ obstack_grow (&collect_obstack, "'\\''", 4);
+ q = ++p;
+ }
+ obstack_grow (&collect_obstack, q, strlen (q));
+ obstack_grow (&collect_obstack, "'", 1);
+ }
+ }
+ obstack_grow (&collect_obstack, "\0", 1);
+ putenv (obstack_finish (&collect_obstack));
+ }
+
/* Initialize the vector of specs to just the default.
This means one element containing 0s, as a terminator. */
@@ -4676,32 +4729,6 @@ main (argc, argv)
putenv_from_prefixes (&exec_prefixes, "COMPILER_PATH=");
putenv_from_prefixes (&startfile_prefixes, "LIBRARY_PATH=");
- /* Build COLLECT_GCC_OPTIONS to have all of the options specified to
- the compiler. */
- obstack_grow (&collect_obstack, "COLLECT_GCC_OPTIONS=",
- sizeof ("COLLECT_GCC_OPTIONS=")-1);
-
- first_time = TRUE;
- for (i = 0; i < n_switches; i++)
- {
- char **args;
- if (!first_time)
- obstack_grow (&collect_obstack, " ", 1);
-
- first_time = FALSE;
- obstack_grow (&collect_obstack, "-", 1);
- obstack_grow (&collect_obstack, switches[i].part1,
- strlen (switches[i].part1));
-
- for (args = switches[i].args; args && *args; args++)
- {
- obstack_grow (&collect_obstack, " ", 1);
- obstack_grow (&collect_obstack, *args, strlen (*args));
- }
- }
- obstack_grow (&collect_obstack, "\0", 1);
- putenv (obstack_finish (&collect_obstack));
-
value = do_spec (link_command_spec);
if (value < 0)
error_count = 1;
diff --git a/gcc/hash.c b/gcc/hash.c
new file mode 100644
index 00000000000..155ffbf70f8
--- /dev/null
+++ b/gcc/hash.c
@@ -0,0 +1,208 @@
+/* CYGNUS LOCAL: whole file jason */
+/* hash.c -- hash table routines
+ Copyright (C) 1993, 94 Free Software Foundation, Inc.
+ Written by Steve Chamberlain <sac@cygnus.com>
+
+This file was lifted from BFD, the Binary File Descriptor library.
+
+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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "config.h"
+#include "hash.h"
+#include "obstack.h"
+
+extern void free PARAMS ((PTR));
+
+/* Obstack allocation and deallocation routines. */
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+extern char * xmalloc ();
+
+/* The default number of entries to use when creating a hash table. */
+#define DEFAULT_SIZE (1009)
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+/* Create a new hash table, given a number of entries. */
+
+boolean
+hash_table_init_n (table, newfunc, size)
+ struct hash_table *table;
+ struct hash_entry *(*newfunc) PARAMS ((struct hash_entry *,
+ struct hash_table *,
+ const char *));
+ unsigned int size;
+{
+ unsigned int alloc;
+
+ alloc = size * sizeof (struct hash_entry *);
+ if (!obstack_begin (&table->memory, alloc))
+ {
+ error ("no memory");
+ return false;
+ }
+ table->table = ((struct hash_entry **)
+ obstack_alloc (&table->memory, alloc));
+ if (!table->table)
+ {
+ error ("no memory");
+ return false;
+ }
+ memset ((PTR) table->table, 0, alloc);
+ table->size = size;
+ table->newfunc = newfunc;
+ return true;
+}
+
+/* Create a new hash table with the default number of entries. */
+
+boolean
+hash_table_init (table, newfunc)
+ struct hash_table *table;
+ struct hash_entry *(*newfunc) PARAMS ((struct hash_entry *,
+ struct hash_table *,
+ const char *));
+{
+ return hash_table_init_n (table, newfunc, DEFAULT_SIZE);
+}
+
+/* Free a hash table. */
+
+void
+hash_table_free (table)
+ struct hash_table *table;
+{
+ obstack_free (&table->memory, (PTR) NULL);
+}
+
+/* Look up a string in a hash table. */
+
+struct hash_entry *
+hash_lookup (table, string, create, copy)
+ struct hash_table *table;
+ const char *string;
+ boolean create;
+ boolean copy;
+{
+ register const unsigned char *s;
+ register unsigned long hash;
+ register unsigned int c;
+ struct hash_entry *hashp;
+ unsigned int len;
+ unsigned int index;
+
+ hash = 0;
+ len = 0;
+ s = (const unsigned char *) string;
+ while ((c = *s++) != '\0')
+ {
+ hash += c + (c << 17);
+ hash ^= hash >> 2;
+ ++len;
+ }
+ hash += len + (len << 17);
+ hash ^= hash >> 2;
+
+ index = hash % table->size;
+ for (hashp = table->table[index];
+ hashp != (struct hash_entry *) NULL;
+ hashp = hashp->next)
+ {
+ if (hashp->hash == hash
+ && strcmp (hashp->string, string) == 0)
+ return hashp;
+ }
+
+ if (! create)
+ return (struct hash_entry *) NULL;
+
+ hashp = (*table->newfunc) ((struct hash_entry *) NULL, table, string);
+ if (hashp == (struct hash_entry *) NULL)
+ return (struct hash_entry *) NULL;
+ if (copy)
+ {
+ char *new;
+
+ new = (char *) obstack_alloc (&table->memory, len + 1);
+ if (!new)
+ {
+ error ("no memory");
+ return (struct hash_entry *) NULL;
+ }
+ strcpy (new, string);
+ string = new;
+ }
+ hashp->string = string;
+ hashp->hash = hash;
+ hashp->next = table->table[index];
+ table->table[index] = hashp;
+
+ return hashp;
+}
+
+/* Base method for creating a new hash table entry. */
+
+/*ARGSUSED*/
+struct hash_entry *
+hash_newfunc (entry, table, string)
+ struct hash_entry *entry;
+ struct hash_table *table;
+ const char *string;
+{
+ if (entry == (struct hash_entry *) NULL)
+ entry = ((struct hash_entry *)
+ hash_allocate (table, sizeof (struct hash_entry)));
+ return entry;
+}
+
+/* Allocate space in a hash table. */
+
+PTR
+hash_allocate (table, size)
+ struct hash_table *table;
+ unsigned int size;
+{
+ PTR ret;
+
+ ret = obstack_alloc (&table->memory, size);
+ if (ret == NULL && size != 0)
+ error ("no memory");
+ return ret;
+}
+
+/* Traverse a hash table. */
+
+void
+hash_traverse (table, func, info)
+ struct hash_table *table;
+ boolean (*func) PARAMS ((struct hash_entry *, PTR));
+ PTR info;
+{
+ unsigned int i;
+
+ for (i = 0; i < table->size; i++)
+ {
+ struct hash_entry *p;
+
+ for (p = table->table[i]; p != NULL; p = p->next)
+ {
+ if (! (*func) (p, info))
+ return;
+ }
+ }
+}
diff --git a/gcc/hash.h b/gcc/hash.h
new file mode 100644
index 00000000000..388532abd65
--- /dev/null
+++ b/gcc/hash.h
@@ -0,0 +1,131 @@
+/* CYGNUS LOCAL: whole file jason */
+/* Header file for generic hash table support.
+ Copyright (C) 1993, 94 Free Software Foundation, Inc.
+ Written by Steve Chamberlain <sac@cygnus.com>
+
+This file was lifted from BFD, the Binary File Descriptor library.
+
+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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifdef IN_GCC
+
+/* Add prototype support. */
+#ifndef PROTO
+#if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__)
+#define PROTO(ARGS) ARGS
+#else
+#define PROTO(ARGS) ()
+#endif
+#endif
+
+#define PARAMS(ARGS) PROTO(ARGS)
+
+#ifdef __STDC__
+#define PTR void *
+#else
+#ifndef const
+#define const
+#endif
+#define PTR char *
+#endif
+
+#else /* ! IN_GCC */
+#include <ansidecl.h>
+#endif /* IN_GCC */
+
+#include "obstack.h"
+
+typedef enum {false, true} boolean;
+
+/* Hash table routines. There is no way to free up a hash table. */
+
+/* An element in the hash table. Most uses will actually use a larger
+ structure, and an instance of this will be the first field. */
+
+struct hash_entry
+{
+ /* Next entry for this hash code. */
+ struct hash_entry *next;
+ /* String being hashed. */
+ const char *string;
+ /* Hash code. This is the full hash code, not the index into the
+ table. */
+ unsigned long hash;
+};
+
+/* A hash table. */
+
+struct hash_table
+{
+ /* The hash array. */
+ struct hash_entry **table;
+ /* The number of slots in the hash table. */
+ unsigned int size;
+ /* A function used to create new elements in the hash table. The
+ first entry is itself a pointer to an element. When this
+ function is first invoked, this pointer will be NULL. However,
+ having the pointer permits a hierarchy of method functions to be
+ built each of which calls the function in the superclass. Thus
+ each function should be written to allocate a new block of memory
+ only if the argument is NULL. */
+ struct hash_entry *(*newfunc) PARAMS ((struct hash_entry *,
+ struct hash_table *,
+ const char *));
+ /* An obstack for this hash table. */
+ struct obstack memory;
+};
+
+/* Initialize a hash table. */
+extern boolean hash_table_init
+ PARAMS ((struct hash_table *,
+ struct hash_entry *(*) (struct hash_entry *,
+ struct hash_table *,
+ const char *)));
+
+/* Initialize a hash table specifying a size. */
+extern boolean hash_table_init_n
+ PARAMS ((struct hash_table *,
+ struct hash_entry *(*) (struct hash_entry *,
+ struct hash_table *,
+ const char *),
+ unsigned int size));
+
+/* Free up a hash table. */
+extern void hash_table_free PARAMS ((struct hash_table *));
+
+/* Look up a string in a hash table. If CREATE is true, a new entry
+ will be created for this string if one does not already exist. The
+ COPY argument must be true if this routine should copy the string
+ into newly allocated memory when adding an entry. */
+extern struct hash_entry *hash_lookup
+ PARAMS ((struct hash_table *, const char *, boolean create,
+ boolean copy));
+
+/* Base method for creating a hash table entry. */
+extern struct hash_entry *hash_newfunc
+ PARAMS ((struct hash_entry *, struct hash_table *,
+ const char *));
+
+/* Grab some space for a hash table entry. */
+extern PTR hash_allocate PARAMS ((struct hash_table *,
+ unsigned int));
+
+/* Traverse a hash table in a random order, calling a function on each
+ element. If the function returns false, the traversal stops. The
+ INFO argument is passed to the function. */
+extern void hash_traverse PARAMS ((struct hash_table *,
+ boolean (*) (struct hash_entry *,
+ PTR),
+ PTR info));
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);
+ }
+}