diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
commit | d6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch) | |
tree | ece253b876159b39c620e62b6c9b1174642e070e /gnu/usr.bin/ld/warnings.c |
initial import of NetBSD tree
Diffstat (limited to 'gnu/usr.bin/ld/warnings.c')
-rw-r--r-- | gnu/usr.bin/ld/warnings.c | 758 |
1 files changed, 758 insertions, 0 deletions
diff --git a/gnu/usr.bin/ld/warnings.c b/gnu/usr.bin/ld/warnings.c new file mode 100644 index 00000000000..e5dce20fe44 --- /dev/null +++ b/gnu/usr.bin/ld/warnings.c @@ -0,0 +1,758 @@ +/* + * $Id: warnings.c,v 1.1 1995/10/18 08:40:56 deraadt Exp $ + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <sys/time.h> +#include <sys/errno.h> +#include <err.h> +#include <stdio.h> +#include <stdlib.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" + +static int reported_undefineds; + +/* + * 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); + (void)sprintf(result, "%s(%s)", supfile, entry->filename); + free(supfile); + + } else { + result = (char *)xmalloc(strlen(entry->filename) + 1); + strcpy(result, entry->filename); + } + return result; +} + +/* 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, (void *)outfile); + + fprintf(outfile, "\nGlobal symbols:\n\n"); + FOR_EACH_SYMBOL(i, sp) { + fprintf(outfile, " %s: ", sp->name); + if (!(sp->flags & GS_REFERENCED)) + fprintf(outfile, "unreferenced"); + else if (sp->so_defined) + fprintf(outfile, "sodefined"); + else if (!sp->defined) + fprintf(outfile, "undefined"); + else if (sp->defined == (N_UNDF|N_EXT)) + fprintf(outfile, "common: size %#x", sp->common_size); + else + fprintf(outfile, "type %d, value %#x, size %#x", + sp->defined, sp->value, sp->size); + if (sp->alias) + fprintf(outfile, ", aliased to %s", sp->alias->name); + fprintf(outfile, "\n"); + } END_EACH_SYMBOL; + + each_file(list_file_locals, (void *)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"); + 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_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 +reloc_cmp(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 *lspend = entry->symbols + entry->nsymbols; + + + current->sym = next->sym; + current->line = next->line; + current->filename = next->filename; + + while (++(next->sym) < lspend) { + + 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; +{ + register struct localsymbol *lsp, *lspend; + struct line_debug_entry *state_pointer, *current, *next, *source; + + state_pointer = (struct line_debug_entry *) + xmalloc(3 * sizeof(*state_pointer)); + + current = state_pointer, + next = state_pointer + 1, + source = state_pointer + 2; /* Used to store source file */ + + lspend = entry->symbols+entry->nsymbols; + + for (lsp = entry->symbols; lsp < lspend; lsp++) + if (lsp->nzlist.nlist.n_type == N_SO) + break; + + if (lsp >= lspend) { + /* 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, *next, *tmp_pointer; + int use_data_symbols; + + current = state_pointer; + next = state_pointer + 1; + + 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 *rp, *erp; + int start_of_segment; + struct localsymbol *start_of_syms; + struct line_debug_entry *state_pointer, *current; + /* 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; + + rp = data_segment ? entry->datarel : entry->textrel; + erp = data_segment ? (rp + entry->ndatarel) : (rp + entry->ntextrel); + start_of_syms = entry->symbols; + start_of_segment = (data_segment ? + entry->data_start_address : + entry->text_start_address); + state_pointer = init_debug_scan(data_segment != 0, entry); + current = state_pointer; + + /* + * We need to sort the relocation info here. Sheesh, so much effort + * for one lousy error optimization. + */ + qsort(rp, erp - rp, sizeof(rp[0]), reloc_cmp); + + for (; rp < erp; rp++) { + register struct localsymbol *lsp; + register symbol *g; + + /* + * If the relocation isn't resolved through a symbol, continue. + */ + if (!RELOC_EXTERN_P(rp)) + continue; + + lsp = &entry->symbols[RELOC_SYMBOL(rp)]; + + /* + * 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 ((g = lsp->symbol) == NULL) + continue; + + if (!(lsp->nzlist.nz_type & N_EXT) && + !SET_ELEMENT_P(lsp->nzlist.nz_type)) { + warnx("internal error: `%s' N_EXT not set", g->name); + continue; + } + + errmsg = 0; + + if (!g->defined && !g->so_defined && list_unresolved_refs) { + /* Mark as being noted by relocation warning pass. */ + SET_BIT(nlist_bitvector, lsp - start_of_syms); + + if (g->undef_refs == 0) + reported_undefineds++; + 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; + + if (BIT_SET_P(nlist_bitvector, lsp - start_of_syms)) + continue; + + /* Mark as being noted by relocation warning pass. */ + SET_BIT(nlist_bitvector, lsp - 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(rp) + 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 nsym; + int i; + char *errfmt, *file_name; + int line_number; + int dont_allow_symbol_name; + u_char *nlist_bitvector; + struct line_debug_entry *text_scan, *data_scan; + + nsym = entry->nsymbols; + nlist_bitvector = (u_char *)alloca((nsym >> 3) + 1); + bzero(nlist_bitvector, (nsym >> 3) + 1); + + /* Read in the strings */ + entry->strings = (char *)alloca(entry->string_size); + read_entry_strings(file_open(entry), entry); + + if (!(entry->flags & E_DYNAMIC)) { + /* Do text warnings based on a scan through the reloc info. */ + do_relocation_warnings(entry, 0, outfile, nlist_bitvector); + + /* Do data warnings based on a scan through the reloc 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 < nsym; i++) { + struct nlist *np; + symbol *g; + + g = entry->symbols[i].symbol; + np = &entry->symbols[i].nzlist.nlist; + + if (g == NULL) + continue; + + if (!(np->n_type & N_EXT) && !SET_ELEMENT_P(np->n_type)) { + warnx("internal error: `%s' N_EXT not set", g->name); + 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 (np->n_type) { + case N_TEXT | N_EXT: + line_number = + address_to_line(np->n_value, text_scan); + file_name = text_scan[0].filename; + break; + + case N_DATA | N_EXT: + line_number = + address_to_line(np->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)"; + line_number = -1; + break; + + case N_SIZE | N_EXT: + errfmt = + "Size element definition of symbol `%s' (multiply defined)"; + line_number = -1; + break; + + case N_INDR | N_EXT: + errfmt = + "Alias definition of symbol `%s' (multiply defined)"; + line_number = -1; + break; + + case N_UNDF | N_EXT: + /* Don't print out multiple defs at references.*/ + continue; + + default: + warnx("%s: unexpected multiple definitions " + "of symbol `%s', type %#x", + get_file_name(entry), + g->name, np->n_type); + break; + } + + } else if (BIT_SET_P(nlist_bitvector, i)) { + continue; + } else if (list_unresolved_refs && + !g->defined && !g->so_defined) { + + if (g->undef_refs == 0) + reported_undefineds++; + 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->def_lsp && g->def_lsp->entry != entry && + !(entry->flags & E_DYNAMIC) && + g->def_lsp->entry->flags & E_SECONDCLASS) { + fprintf(outfile, + "%s: Undefined symbol `%s' referenced (use %s ?)\n", + get_file_name(entry), + g->name, + g->def_lsp->entry->local_sym_name); + continue; + } 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 + * of a reference. The second is if it's the reference + * used by the warning stabs itself. + */ + if (np->n_type != (N_EXT | N_UNDF) || + (entry->symbols[i].flags & LS_WARNING)) + continue; + + errfmt = g->warning; + line_number = -1; + dont_allow_symbol_name = 1; + } else + continue; + + if (line_number == -1) + fprintf(outfile, "%s: ", get_file_name(entry)); + 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 disappear anyway. */ +} + +int +do_warnings(outfile) + FILE *outfile; +{ + + list_unresolved_refs = !relocatable_output && + ( (undefined_global_sym_count - undefined_weak_sym_count) > 0 + || undefined_shobj_sym_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, (void *)outfile); + + if (list_unresolved_refs && + reported_undefineds != + (undefined_global_sym_count - undefined_weak_sym_count)) + warnx("Spurious undefined symbols: " + "# undefined symbols %d, reported %d", + (undefined_global_sym_count - undefined_weak_sym_count), + reported_undefineds); + + if (list_unresolved_refs || list_multiple_defs) + return 0; + + return 1; +} + |