diff options
Diffstat (limited to 'gnu/usr.bin/ld/warnings.c')
| -rw-r--r-- | gnu/usr.bin/ld/warnings.c | 750 | 
1 files changed, 750 insertions, 0 deletions
| diff --git a/gnu/usr.bin/ld/warnings.c b/gnu/usr.bin/ld/warnings.c new file mode 100644 index 000000000000..9d2634f35f52 --- /dev/null +++ b/gnu/usr.bin/ld/warnings.c @@ -0,0 +1,750 @@ +/* + * $Id: warnings.c,v 1.6 1994/02/13 20:41:48 jkh Exp $ + */ + +#include <sys/param.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <sys/time.h> +#include <sys/errno.h> +#include <fcntl.h> +#include <ar.h> +#include <ranlib.h> +#include <a.out.h> +#include <stab.h> +#include <string.h> +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#include "ld.h" + +/* + * Print the filename of ENTRY on OUTFILE (a stdio stream), + * and then a newline. + */ + +void +prline_file_name (entry, outfile) +     struct file_entry *entry; +     FILE *outfile; +{ +	print_file_name (entry, outfile); +	fprintf (outfile, "\n"); +} + +/* + * Print the filename of ENTRY on OUTFILE (a stdio stream). + */ + +void +print_file_name (entry, outfile) +     struct file_entry *entry; +     FILE *outfile; +{ +	if (entry == NULL) { +		fprintf (outfile, "NULL"); +	} + +	if (entry->superfile) { +		print_file_name (entry->superfile, outfile); +		fprintf (outfile, "(%s)", entry->filename); +	} else +		fprintf (outfile, "%s", entry->filename); +} + +/* + * Return the filename of entry as a string (malloc'd for the purpose) + */ + +char * +get_file_name (entry) +     struct file_entry *entry; +{ +	char *result, *supfile; + +	if (entry == NULL) { +		return (char *)strdup("NULL"); +	} + +	if (entry->superfile) { +		supfile = get_file_name (entry->superfile); +		result = (char *) xmalloc (strlen(supfile) +					+ strlen(entry->filename) + 3); +		sprintf (result, "%s(%s)", supfile, entry->filename); +		free (supfile); + +	} else { +		result = (char *) xmalloc (strlen (entry->filename) + 1); +		strcpy (result, entry->filename); +	} +	return result; +} + +/* + * Report a fatal error.  The error message is STRING followed by the + * filename of ENTRY. + */ +void +#if __STDC__ +fatal_with_file (char *fmt, struct file_entry *entry, ...) +#else +fatal_with_file (fmt, entry, va_alist) +	char			*fmt; +	struct file_entry	*entry; +	va_dcl +#endif +{ +	va_list	ap; +#if __STDC__ +	va_start(ap, fmt); +#else +	va_start(ap); +#endif +	(void)fprintf(stderr, "%s: ", progname); +	(void)vfprintf(stderr, fmt, ap); +	print_file_name (entry, stderr); +	(void)fprintf(stderr, "\n"); + +	va_end(ap); +	exit (1); +} + +/* + * Report a fatal error using the message for the last failed system call, + * followed by the string NAME. + */ +void +perror_name (name) +	char *name; +{ +	char *s; + +	if (errno < sys_nerr) +		s = concat ("", sys_errlist[errno], " for %s"); +	else +		s = "cannot open %s"; +	fatal (s, name); +} + +/* + * Report a fatal error using the message for the last failed system call, + * followed by the name of file ENTRY. + */ +void +perror_file (entry) +     struct file_entry *entry; +{ +	char *s; + +	if (errno < sys_nerr) +		s = concat ("", sys_errlist[errno], " for "); +	else +		s = "cannot open "; +	fatal_with_file (s, entry); +} + + +/* Print a complete or partial map of the output file. */ + +static void	describe_file_sections __P((struct file_entry *, FILE *)); +static void	list_file_locals __P((struct file_entry *, FILE *)); + +void +print_symbols(outfile) +	FILE           *outfile; +{ +	fprintf(outfile, "\nFiles:\n\n"); +	each_file(describe_file_sections, outfile); + +	fprintf(outfile, "\nGlobal symbols:\n\n"); +	FOR_EACH_SYMBOL(i, sp) { +		if (sp->defined == (N_UNDF|N_EXT)) +			fprintf(outfile, "  %s: common, length %#x\n", +						sp->name, sp->common_size); +		if (!(sp->flags & GS_REFERENCED)) +			fprintf(outfile, "  %s: unreferenced\n", sp->name); +		else if (sp->so_defined) +			fprintf(outfile, "  %s: sodefined\n", sp->name); +		else if (!sp->defined) +			fprintf(outfile, "  %s: undefined\n", sp->name); +		else +			fprintf(outfile, "  %s: %#x, size %#x\n", +						sp->name, sp->value, sp->size); +	} END_EACH_SYMBOL; + +	each_file(list_file_locals, outfile); +} + +static void +describe_file_sections(entry, outfile) +	struct file_entry *entry; +	FILE           *outfile; +{ +	fprintf(outfile, "  "); +	print_file_name(entry, outfile); +	if (entry->flags & (E_JUST_SYMS | E_DYNAMIC)) +		fprintf(outfile, " symbols only\n", 0); +	else +		fprintf(outfile, " text %x(%x), data %x(%x), bss %x(%x) hex\n", +			entry->text_start_address, entry->header.a_text, +			entry->data_start_address, entry->header.a_data, +			entry->bss_start_address, entry->header.a_bss); +} + +static void +list_file_locals (entry, outfile) +     struct file_entry *entry; +     FILE *outfile; +{ +	struct localsymbol	*lsp, *lspend; + +	entry->strings = (char *) alloca (entry->string_size); +	read_entry_strings (file_open (entry), entry); + +	fprintf (outfile, "\nLocal symbols of "); +	print_file_name (entry, outfile); +	fprintf (outfile, ":\n\n"); + +	lspend = entry->symbols + entry->nsymbols; +	for (lsp = entry->symbols; lsp < lspend; lsp++) { +		register struct nlist *p = &lsp->nzlist.nlist; +		/* +		 * If this is a definition, +		 * update it if necessary by this file's start address. +		 */ +		if (!(p->n_type & (N_STAB | N_EXT))) +			fprintf(outfile, "  %s: 0x%x\n", +				entry->strings + p->n_un.n_strx, p->n_value); +	} + +	entry->strings = 0;		/* All done with them.  */ +} + + +/* Static vars for do_warnings and subroutines of it */ +static int list_unresolved_refs;	/* List unresolved refs */ +static int list_warning_symbols;	/* List warning syms */ +static int list_multiple_defs;		/* List multiple definitions */ + +static struct line_debug_entry *init_debug_scan __P((int, struct file_entry *)); +static int		next_debug_entry __P((int, struct line_debug_entry *)); + +/* + * Structure for communication between do_file_warnings and it's + * helper routines.  Will in practice be an array of three of these: + * 0) Current line, 1) Next line, 2) Source file info. + */ +struct line_debug_entry +{ +	int			line; +	char			*filename; +	struct localsymbol	*sym; +}; + +/* + * Helper routines for do_file_warnings. + */ + +/* Return an integer less than, equal to, or greater than 0 as per the +   relation between the two relocation entries.  Used by qsort.  */ + +static int +relocation_entries_relation (rel1, rel2) +     struct relocation_info *rel1, *rel2; +{ +	return RELOC_ADDRESS(rel1) - RELOC_ADDRESS(rel2); +} + +/* Moves to the next debugging symbol in the file.  USE_DATA_SYMBOLS +   determines the type of the debugging symbol to look for (DSLINE or +   SLINE).  STATE_POINTER keeps track of the old and new locatiosn in +   the file.  It assumes that state_pointer[1] is valid; ie +   that it.sym points into some entry in the symbol table.  If +   state_pointer[1].sym == 0, this routine should not be called.  */ + +static int +next_debug_entry (use_data_symbols, state_pointer) +     register int use_data_symbols; +     /* Next must be passed by reference! */ +     struct line_debug_entry state_pointer[3]; +{ +	register struct line_debug_entry +				*current = state_pointer, +				*next = state_pointer + 1, +				/* Used to store source file */ +				*source = state_pointer + 2; + +	struct file_entry	*entry = (struct file_entry *) source->sym; +	struct localsymbol	*endp = entry->symbols + entry->nsymbols; + + +	current->sym = next->sym; +	current->line = next->line; +	current->filename = next->filename; + +	while (++(next->sym) < endp) { + +		struct nlist	*np = &next->sym->nzlist.nlist; + +		/* +		 * n_type is a char, and N_SOL, N_EINCL and N_BINCL are > 0x80, +		 * so may look negative...therefore, must mask to low bits +		 */ +		switch (np->n_type & 0xff) { +		case N_SLINE: +			if (use_data_symbols) continue; +			next->line = np->n_desc; +			return 1; +		case N_DSLINE: +			if (!use_data_symbols) continue; +			next->line = np->n_desc; +			return 1; +#ifdef HAVE_SUN_STABS +		case N_EINCL: +			next->filename = source->filename; +			continue; +#endif +		case N_SO: +			source->filename = np->n_un.n_strx + entry->strings; +			source->line++; +#ifdef HAVE_SUN_STABS +		case N_BINCL: +#endif +		case N_SOL: +			next->filename = np->n_un.n_strx + entry->strings; +		default: +			continue; +		} +	} +	next->sym = (struct localsymbol *)0; +	return 0; +} + +/* + * Create a structure to save the state of a scan through the debug symbols. + * USE_DATA_SYMBOLS is set if we should be scanning for DSLINE's instead of + * SLINE's.  entry is the file entry which points at the symbols to use. + */ + +static struct line_debug_entry * +init_debug_scan(use_data_symbols, entry) +	int             use_data_symbols; +	struct file_entry *entry; +{ +	struct localsymbol	*lsp; +	struct line_debug_entry *state_pointer = (struct line_debug_entry *) +				xmalloc(3 * sizeof(struct line_debug_entry)); + +	register struct line_debug_entry +		*current = state_pointer, +		*next = state_pointer + 1, +		*source = state_pointer + 2;	/* Used to store source file */ + + +	for (lsp = entry->symbols; lsp < entry->symbols+entry->nsymbols; lsp++) +		if (lsp->nzlist.nlist.n_type == N_SO) +			break; + +	if (lsp >= entry->symbols + entry->nsymbols) { +		/* I believe this translates to "We lose" */ +		current->filename = next->filename = entry->filename; +		current->line = next->line = -1; +		current->sym = next->sym = (struct localsymbol *) 0; +		return state_pointer; +	} +	next->line = source->line = 0; +	next->filename = source->filename +			= (lsp->nzlist.nlist.n_un.n_strx + entry->strings); +	source->sym = (struct localsymbol *) entry; +	next->sym = lsp; + +	/* To setup next */ +	next_debug_entry(use_data_symbols, state_pointer); + +	if (!next->sym) {	/* No line numbers for this section; */ +		/* setup output results as appropriate */ +		if (source->line) { +			current->filename = source->filename = entry->filename; +			current->line = -1;	/* Don't print lineno */ +		} else { +			current->filename = source->filename; +			current->line = 0; +		} +		return state_pointer; +	} +	/* To setup current */ +	next_debug_entry(use_data_symbols, state_pointer); + +	return state_pointer; +} + +/* + * Takes an ADDRESS (in either text or data space) and a STATE_POINTER which + * describes the current location in the implied scan through the debug + * symbols within the file which ADDRESS is within, and returns the source + * line number which corresponds to ADDRESS. + */ + +static int +address_to_line(address, state_pointer) +	unsigned long   address; +/* Next must be passed by reference! */ +	struct line_debug_entry state_pointer[3]; +{ +	struct line_debug_entry +	               *current = state_pointer, *next = state_pointer + 1; +	struct line_debug_entry *tmp_pointer; + +	int             use_data_symbols; + +	if (next->sym) +		use_data_symbols = +			(next->sym->nzlist.nlist.n_type & N_TYPE) == N_DATA; +	else +		return current->line; + +	/* Go back to the beginning if we've already passed it.  */ +	if (current->sym->nzlist.nlist.n_value > address) { +		tmp_pointer = init_debug_scan(use_data_symbols, +					      (struct file_entry *) +					      ((state_pointer + 2)->sym)); +		state_pointer[0] = tmp_pointer[0]; +		state_pointer[1] = tmp_pointer[1]; +		state_pointer[2] = tmp_pointer[2]; +		free(tmp_pointer); +	} +	/* If we're still in a bad way, return -1, meaning invalid line.  */ +	if (current->sym->nzlist.nlist.n_value > address) +		return -1; + +	while (next->sym +	       && next->sym->nzlist.nlist.n_value <= address +	       && next_debug_entry(use_data_symbols, state_pointer)); +	return current->line; +} + + +/* Macros for manipulating bitvectors.  */ +#define	BIT_SET_P(bv, index)	((bv)[(index) >> 3] & 1 << ((index) & 0x7)) +#define	SET_BIT(bv, index)	((bv)[(index) >> 3] |= 1 << ((index) & 0x7)) + +/* + * This routine will scan through the relocation data of file ENTRY, printing + * out references to undefined symbols and references to symbols defined in + * files with N_WARNING symbols.  If DATA_SEGMENT is non-zero, it will scan + * the data relocation segment (and use N_DSLINE symbols to track line + * number); otherwise it will scan the text relocation segment.  Warnings + * will be printed on the output stream OUTFILE.  Eventually, every nlist + * symbol mapped through will be marked in the NLIST_BITVECTOR, so we don't + * repeat ourselves when we scan the nlists themselves. + */ + +static void +do_relocation_warnings(entry, data_segment, outfile, nlist_bitvector) +	struct file_entry *entry; +	int             data_segment; +	FILE           *outfile; +	unsigned char  *nlist_bitvector; +{ +	struct relocation_info *reloc, *reloc_start = +			data_segment ? entry->datarel : entry->textrel; + +	int	reloc_size = (data_segment ? entry->ndatarel : entry->ntextrel); +	int	start_of_segment = (data_segment ? +					entry->data_start_address : +					entry->text_start_address); +	struct localsymbol	*start_of_syms = entry->symbols; +	struct line_debug_entry	*state_pointer = +				init_debug_scan(data_segment != 0, entry); + +	register struct line_debug_entry	*current = state_pointer; + +	/* Assigned to generally static values; should not be written into.  */ +	char *errfmt; + +	/* +	 * Assigned to alloca'd values cand copied into; should be freed when +	 * done. +	 */ +	char *errmsg; +	int  invalidate_line_number; + +	/* +	 * We need to sort the relocation info here.  Sheesh, so much effort +	 * for one lousy error optimization. +	 */ + +	qsort(reloc_start, reloc_size, sizeof(struct relocation_info), +	      relocation_entries_relation); + +	for (reloc = reloc_start; +	     reloc < (reloc_start + reloc_size); +	     reloc++) { +		register struct localsymbol *s; +		register symbol *g; + +		/* +		 * If the relocation isn't resolved through a symbol, +		 * continue +		 */ +		if (!RELOC_EXTERN_P(reloc)) +			continue; + +		s = &entry->symbols[RELOC_SYMBOL(reloc)]; + +		/* +		 * Local symbols shouldn't ever be used by relocation info, +		 * so the next should be safe. This is, of course, wrong. +		 * References to local BSS symbols can be the targets of +		 * relocation info, and they can (must) be resolved through +		 * symbols.  However, these must be defined properly, (the +		 * assembler would have caught it otherwise), so we can +		 * ignore these cases. +		 */ +		if (!(s->nzlist.nz_type & N_EXT)) +			continue; + +		g = s->symbol; +		errmsg = 0; + +		if (!g->defined && !g->so_defined && list_unresolved_refs) {	/* Reference */ +			/* Mark as being noted by relocation warning pass.  */ +			SET_BIT(nlist_bitvector, s - start_of_syms); + +			if (g->undef_refs >= MAX_UREFS_PRINTED)	/* Listed too many */ +				continue; + +			/* Undefined symbol which we should mention */ + +			if (++(g->undef_refs) == MAX_UREFS_PRINTED) { +				errfmt = "More undefined symbol %s refs follow"; +				invalidate_line_number = 1; +			} else { +				errfmt = "Undefined symbol %s referenced from %s segment"; +				invalidate_line_number = 0; +			} +		} else {	/* Defined */ +			/* Potential symbol warning here */ +			if (!g->warning) +				continue; + +			/* Mark as being noted by relocation warning pass.  */ +			SET_BIT(nlist_bitvector, s - start_of_syms); + +			errfmt = 0; +			errmsg = g->warning; +			invalidate_line_number = 0; +		} + + +		/* If errfmt == 0, errmsg has already been defined.  */ +		if (errfmt != 0) { +			char           *nm; + +			nm = g->name; +			errmsg = (char *) xmalloc(strlen(errfmt) + strlen(nm) + 1); +			sprintf(errmsg, errfmt, nm, data_segment ? "data" : "text"); +			if (nm != g->name) +				free(nm); +		} +		address_to_line(RELOC_ADDRESS(reloc) + start_of_segment, +				state_pointer); + +		if (current->line >= 0) +			fprintf(outfile, "%s:%d: %s\n", current->filename, +			invalidate_line_number ? 0 : current->line, errmsg); +		else +			fprintf(outfile, "%s: %s\n", current->filename, errmsg); + +		if (errfmt != 0) +			free(errmsg); +	} + +	free(state_pointer); +} + +/* + * Print on OUTFILE a list of all warnings generated by references and/or + * definitions in the file ENTRY.  List source file and line number if + * possible, just the .o file if not. + */ + +void +do_file_warnings (entry, outfile) +     struct file_entry *entry; +     FILE *outfile; +{ +	int number_of_syms = entry->nsymbols; +	unsigned char *nlist_bitvector = (unsigned char *) +					alloca ((number_of_syms >> 3) + 1); +	struct line_debug_entry *text_scan, *data_scan; +	int	i; +	char	*errfmt, *file_name; +	int	line_number; +	int	dont_allow_symbol_name; + +	bzero (nlist_bitvector, (number_of_syms >> 3) + 1); + +	/* Read in the files strings if they aren't available */ +	if (!entry->strings) { +		int desc; + +		entry->strings = (char *) alloca (entry->string_size); +		desc = file_open (entry); +		read_entry_strings (desc, entry); +	} + +	if (!(entry->flags & E_DYNAMIC)) { +		/* Do text warnings based on a scan through the relocation info. */ +		do_relocation_warnings (entry, 0, outfile, nlist_bitvector); + +		/* Do data warnings based on a scan through the relocation info. */ +		do_relocation_warnings (entry, 1, outfile, nlist_bitvector); +	} + +	/* Scan through all of the nlist entries in this file and pick up +	anything that the scan through the relocation stuff didn't. */ + +	text_scan = init_debug_scan (0, entry); +	data_scan = init_debug_scan (1, entry); + +	for (i = 0; i < number_of_syms; i++) { +		struct nlist *s; +		symbol *g; + +	        g = entry->symbols[i].symbol; +		s = &entry->symbols[i].nzlist.nlist; + +		if (!(s->n_type & N_EXT)) +			continue; + +		if (!(g->flags & GS_REFERENCED)) { +#if 0 +			/* Check for undefined shobj symbols */ +			struct localsymbol	*lsp; +			register int		type; + +			for (lsp = g->sorefs; lsp; lsp = lsp->next) { +				type = lsp->nzlist.nz_type; +				if ((type & N_EXT) && +						type != (N_UNDF | N_EXT)) { +					break; +				} +			} +			if (type == (N_UNDF | N_EXT)) { +				fprintf(stderr, +					"Undefined symbol %s referenced from %s\n", +					g->name, +					get_file_name(entry)); +			} +#endif +			continue; +		} + +		dont_allow_symbol_name = 0; + +		if (list_multiple_defs && g->mult_defs) { +			errfmt = "Definition of symbol %s (multiply defined)"; +			switch (s->n_type) { + +			case N_TEXT | N_EXT: +				line_number = address_to_line (s->n_value, text_scan); +				file_name = text_scan[0].filename; +				break; + +			case N_DATA | N_EXT: +				line_number = address_to_line (s->n_value, data_scan); +				file_name = data_scan[0].filename; +				break; + +			case N_SETA | N_EXT: +			case N_SETT | N_EXT: +			case N_SETD | N_EXT: +			case N_SETB | N_EXT: +				if (g->mult_defs == 2) +					continue; +				errfmt = "First set element definition of symbol %s (multiply defined)"; +				break; + +			default: +printf("multiply defined: %s, type %#x\n", g->name, s->n_type); +				/* Don't print out multiple defs at references.*/ +				continue; +			} + +		} else if (BIT_SET_P (nlist_bitvector, i)) { +			continue; +		} else if (list_unresolved_refs && !g->defined && !g->so_defined) { +			if (g->undef_refs >= MAX_UREFS_PRINTED) +				continue; + +			if (++(g->undef_refs) == MAX_UREFS_PRINTED) +				errfmt = "More undefined \"%s\" refs follow"; +			else +				errfmt = "Undefined symbol \"%s\" referenced"; +			line_number = -1; +		} else if (g->warning) { +			/* +			 * There are two cases in which we don't want to do +			 * this. The first is if this is a definition instead +			 * do a reference. The second is if it's the reference +			 * used by the warning stabs itself. +			 */ +			if (s->n_type != (N_EXT | N_UNDF) +					|| (i && (s-1)->n_type == N_WARNING)) +				continue; + +			errfmt = g->warning; +			line_number = -1; +			dont_allow_symbol_name = 1; +		} else +			continue; + +		if (line_number == -1) +			fprintf (outfile, "%s: ", entry->filename); +		else +			fprintf (outfile, "%s:%d: ", file_name, line_number); + +		if (dont_allow_symbol_name) +			fprintf (outfile, "%s", errfmt); +		else +			fprintf (outfile, errfmt, g->name); + +		fputc ('\n', outfile); +	} +	free (text_scan); +	free (data_scan); +	entry->strings = 0;	/* Since it will dissapear anyway. */ +} + +int +do_warnings(outfile) +	FILE	*outfile; +{ +	list_unresolved_refs = !relocatable_output && +		(undefined_global_sym_count || undefined_shobj_sym_count); +	list_warning_symbols = warning_count; +	list_multiple_defs = multiple_def_count != 0; + +	if (!(list_unresolved_refs || +			list_warning_symbols || list_multiple_defs)) +		/* No need to run this routine */ +		return 1; + +	if (entry_symbol && !entry_symbol->defined) +		fprintf (outfile, "Undefined entry symbol %s\n", +						entry_symbol->name); +	each_file (do_file_warnings, outfile); + +	if (list_unresolved_refs || list_multiple_defs) +		return 0; + +	return 1; +} + | 
