* 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. From-SVN: r14769
This commit is contained in:
parent
9ae8ffe751
commit
aa32d84158
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
14
gcc/configure
vendored
14
gcc/configure
vendored
@ -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=
|
||||
|
@ -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.
|
||||
|
97
gcc/gcc.c
97
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;
|
||||
|
208
gcc/hash.c
Normal file
208
gcc/hash.c
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
131
gcc/hash.h
Normal file
131
gcc/hash.h
Normal file
@ -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));
|
631
gcc/tlink.c
Normal file
631
gcc/tlink.c
Normal file
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user