freebsd-dev/gnu/usr.bin/ld/warnings.c
Jordan K. Hubbard 09e3d49d92 This is Paul K's latest set of ld changes. A commit was necessary at this
late stage due to the fact that link.h was copyright Sun Microsystems.

This version of ld sync's us up with NetBSD's ld and supports compatablily
with NetBSD's -[zZ] flags (which we had reversed).  Compiling with this
new ld will give you RRS warnings for libraries which do not contain .type
infomation - these wsarnings are harmless and will go away as soon as you
recompile your libraries (cd /usr/src; make libraries).
1994-02-13 20:43:13 +00:00

751 lines
19 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* $Id: warnings.c,v 1.5 1994/01/12 23:14:07 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;
}