c79eac4c4c
NetBSD ld code except for local changes for dlopen() and friends and the hashing on the minor value of the shlibs. We should be binary compatible now with all their libraries. Obtained from: NetBSD
759 lines
19 KiB
C
759 lines
19 KiB
C
/*
|
|
* $Id: warnings.c,v 1.9 1994/12/23 22:30:57 nate 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;
|
|
}
|
|
|