8e0a22a5fb
Weak symbols are to replace indirect (type N_INDR) symbols and will help to clean up name spaces in libraries. Also, some misc. bug fixes, including PR 586, and use stdio for the output file entirely.
3691 lines
91 KiB
C
3691 lines
91 KiB
C
/*-
|
|
* This code is derived from software copyrighted by the Free Software
|
|
* Foundation.
|
|
*
|
|
* Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
|
|
*
|
|
* Modified 1993 by Paul Kranenburg, Erasmus University
|
|
*/
|
|
|
|
#ifndef lint
|
|
static char sccsid[] = "@(#)ld.c 6.10 (Berkeley) 5/22/91";
|
|
#endif /* not lint */
|
|
|
|
/* Linker `ld' for GNU
|
|
Copyright (C) 1988 Free Software Foundation, Inc.
|
|
|
|
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 1, 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. */
|
|
|
|
/* Written by Richard Stallman with some help from Eric Albert.
|
|
Set, indirect, and warning symbol features added by Randy Smith. */
|
|
|
|
/*
|
|
* $Id: ld.c,v 1.39 1994/12/23 20:32:51 pk Exp $
|
|
*/
|
|
|
|
/* Define how to initialize system-dependent header fields. */
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/file.h>
|
|
#include <sys/time.h>
|
|
#include <sys/resource.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <err.h>
|
|
#include <fcntl.h>
|
|
#include <ar.h>
|
|
#include <ranlib.h>
|
|
#include <a.out.h>
|
|
#include <stab.h>
|
|
#include <string.h>
|
|
|
|
#include "ld.h"
|
|
|
|
/* Vector of entries for input files specified by arguments.
|
|
These are all the input files except for members of specified libraries. */
|
|
struct file_entry *file_table;
|
|
int number_of_files;
|
|
|
|
/* 1 => write relocation into output file so can re-input it later. */
|
|
int relocatable_output;
|
|
|
|
/* 1 => building a shared object, set by `-Bshareable'. */
|
|
int building_shared_object;
|
|
|
|
/* 1 => create the output executable. */
|
|
int make_executable;
|
|
|
|
/* Force the executable to be output, even if there are non-fatal errors */
|
|
int force_executable;
|
|
|
|
/* 1 => assign space to common symbols even if `relocatable_output'. */
|
|
int force_common_definition;
|
|
|
|
/* 1 => assign jmp slots to text symbols in shared objects even if non-PIC */
|
|
int force_alias_definition;
|
|
|
|
/* 1 => some files contain PIC code, affects relocation bits
|
|
if `relocatable_output'. */
|
|
int pic_code_seen;
|
|
|
|
/* 1 => segments must be page aligned (ZMAGIC, QMAGIC) */
|
|
int page_align_segments;
|
|
|
|
/* 1 => data segment must be page aligned, even if `-n' or `-N' */
|
|
int page_align_data;
|
|
|
|
/* 1 => do not use standard library search path */
|
|
int nostdlib;
|
|
|
|
/* Version number to put in __DYNAMIC (set by -V) */
|
|
int soversion;
|
|
|
|
int text_size; /* total size of text. */
|
|
int text_start; /* start of text */
|
|
int text_pad; /* clear space between text and data */
|
|
int data_size; /* total size of data. */
|
|
int data_start; /* start of data */
|
|
int data_pad; /* part of bss segment as part of data */
|
|
|
|
int bss_size; /* total size of bss. */
|
|
int bss_start; /* start of bss */
|
|
|
|
int text_reloc_size; /* total size of text relocation. */
|
|
int data_reloc_size; /* total size of data relocation. */
|
|
|
|
int rrs_section_type; /* What's in the RRS section */
|
|
int rrs_text_size; /* Size of RRS text additions */
|
|
int rrs_text_start; /* Location of above */
|
|
int rrs_data_size; /* Size of RRS data additions */
|
|
int rrs_data_start; /* Location of above */
|
|
|
|
/* Specifications of start and length of the area reserved at the end
|
|
of the data segment for the set vectors. Computed in 'digest_symbols' */
|
|
int set_sect_start; /* start of set element vectors */
|
|
int set_sect_size; /* size of above */
|
|
|
|
int link_mode; /* Current link mode */
|
|
|
|
/*
|
|
* When loading the text and data, we can avoid doing a close
|
|
* and another open between members of the same library.
|
|
*
|
|
* These two variables remember the file that is currently open.
|
|
* Both are zero if no file is open.
|
|
*
|
|
* See `each_file' and `file_close'.
|
|
*/
|
|
struct file_entry *input_file;
|
|
int input_desc;
|
|
|
|
/* The name of the file to write; "a.out" by default. */
|
|
char *output_filename; /* Output file name. */
|
|
FILE *outstream; /* Output file descriptor. */
|
|
struct exec outheader; /* Output file header. */
|
|
int magic; /* Output file magic. */
|
|
int oldmagic;
|
|
int relocatable_output; /* `-r'-ed output */
|
|
|
|
symbol *entry_symbol; /* specified by `-e' */
|
|
int entry_offset; /* program entry if no `-e' given */
|
|
|
|
int page_size; /* Size of a page (machine dependent) */
|
|
|
|
/*
|
|
* Keep a list of any symbols referenced from the command line (so
|
|
* that error messages for these guys can be generated). This list is
|
|
* zero terminated.
|
|
*/
|
|
symbol **cmdline_references;
|
|
int cl_refs_allocated;
|
|
|
|
/*
|
|
* Which symbols should be stripped (omitted from the output): none, all, or
|
|
* debugger symbols.
|
|
*/
|
|
enum {
|
|
STRIP_NONE, STRIP_ALL, STRIP_DEBUGGER
|
|
} strip_symbols;
|
|
|
|
/*
|
|
* Which local symbols should be omitted: none, all, or those starting with L.
|
|
* This is irrelevant if STRIP_NONE.
|
|
*/
|
|
enum {
|
|
DISCARD_NONE, DISCARD_ALL, DISCARD_L
|
|
} discard_locals;
|
|
|
|
int global_sym_count; /* # of nlist entries for global symbols */
|
|
int size_sym_count; /* # of N_SIZE nlist entries for output
|
|
(relocatable_output only) */
|
|
int local_sym_count; /* # of nlist entries for local symbols. */
|
|
int non_L_local_sym_count; /* # of nlist entries for non-L symbols */
|
|
int debugger_sym_count; /* # of nlist entries for debugger info. */
|
|
int undefined_global_sym_count; /* # of global symbols referenced and
|
|
not defined. */
|
|
int undefined_shobj_sym_count; /* # of undefined symbols referenced
|
|
by shared objects */
|
|
int multiple_def_count; /* # of multiply defined symbols. */
|
|
int defined_global_sym_count; /* # of defined global symbols. */
|
|
int common_defined_global_count; /* # of common symbols. */
|
|
int undefined_weak_sym_count; /* # of weak symbols referenced and
|
|
not defined. */
|
|
|
|
#if notused
|
|
int special_sym_count; /* # of linker defined symbols. */
|
|
/* XXX - Currently, only __DYNAMIC and _G_O_T_ go here if required,
|
|
* perhaps _etext, _edata and _end should go here too.
|
|
*/
|
|
#endif
|
|
int global_alias_count; /* # of aliased symbols */
|
|
int set_symbol_count; /* # of N_SET* symbols. */
|
|
int set_vector_count; /* # of set vectors in output. */
|
|
int warn_sym_count; /* # of warning symbols encountered. */
|
|
int list_warning_symbols; /* 1 => warning symbols referenced */
|
|
|
|
struct string_list_element *set_element_prefixes;
|
|
|
|
int trace_files; /* print names of input files as processed (`-t'). */
|
|
int write_map; /* write a load map (`-M') */
|
|
|
|
/*
|
|
* `text-start' address is normally this much plus a page boundary.
|
|
* This is not a user option; it is fixed for each system.
|
|
*/
|
|
int text_start_alignment;
|
|
|
|
/*
|
|
* Nonzero if -T was specified in the command line.
|
|
* This prevents text_start from being set later to default values.
|
|
*/
|
|
int T_flag_specified;
|
|
|
|
/*
|
|
* Nonzero if -Tdata was specified in the command line.
|
|
* This prevents data_start from being set later to default values.
|
|
*/
|
|
int Tdata_flag_specified;
|
|
|
|
/*
|
|
* Size to pad data section up to.
|
|
* We simply increase the size of the data section, padding with zeros,
|
|
* and reduce the size of the bss section to match.
|
|
*/
|
|
int specified_data_size;
|
|
|
|
long *set_vectors;
|
|
int setv_fill_count;
|
|
|
|
static void decode_option __P((char *, char *));
|
|
static void decode_command __P((int, char **));
|
|
static int classify_arg __P((char *));
|
|
static void load_symbols __P((void));
|
|
static void enter_global_ref __P((struct localsymbol *,
|
|
char *, struct file_entry *));
|
|
static void digest_symbols __P((void));
|
|
static void digest_pass1 __P((void)), digest_pass2 __P((void));
|
|
static void consider_file_section_lengths __P((struct file_entry *));
|
|
static void relocate_file_addresses __P((struct file_entry *));
|
|
static void consider_relocation __P((struct file_entry *, int));
|
|
static void consider_local_symbols __P((struct file_entry *));
|
|
static void perform_relocation __P((char *, int,
|
|
struct relocation_info *, int,
|
|
struct file_entry *, int));
|
|
static void copy_text __P((struct file_entry *));
|
|
static void copy_data __P((struct file_entry *));
|
|
static void coptxtrel __P((struct file_entry *));
|
|
static void copdatrel __P((struct file_entry *));
|
|
static void write_output __P((void));
|
|
static void write_header __P((void));
|
|
static void write_text __P((void));
|
|
static void write_data __P((void));
|
|
static void write_rel __P((void));
|
|
static void write_syms __P((void));
|
|
static void assign_symbolnums __P((struct file_entry *, int *));
|
|
static void cleanup __P((void));
|
|
static int parse __P((char *, char *, char *));
|
|
|
|
|
|
int
|
|
main(argc, argv)
|
|
int argc;
|
|
char *argv[];
|
|
{
|
|
|
|
/* Added this to stop ld core-dumping on very large .o files. */
|
|
#ifdef RLIMIT_STACK
|
|
/* Get rid of any avoidable limit on stack size. */
|
|
{
|
|
struct rlimit rlim;
|
|
|
|
/* Set the stack limit huge so that alloca does not fail. */
|
|
if (getrlimit(RLIMIT_STACK, &rlim) != 0)
|
|
warn("getrlimit");
|
|
else {
|
|
rlim.rlim_cur = rlim.rlim_max;
|
|
if (setrlimit(RLIMIT_STACK, &rlim) != 0)
|
|
warn("setrlimit");
|
|
}
|
|
}
|
|
#endif /* RLIMIT_STACK */
|
|
|
|
page_size = PAGSIZ;
|
|
|
|
/* Clear the cumulative info on the output file. */
|
|
|
|
text_size = 0;
|
|
data_size = 0;
|
|
bss_size = 0;
|
|
text_reloc_size = 0;
|
|
data_reloc_size = 0;
|
|
|
|
data_pad = 0;
|
|
text_pad = 0;
|
|
page_align_segments = 0;
|
|
page_align_data = 0;
|
|
|
|
/* Initialize the data about options. */
|
|
|
|
specified_data_size = 0;
|
|
strip_symbols = STRIP_NONE;
|
|
trace_files = 0;
|
|
discard_locals = DISCARD_NONE;
|
|
entry_symbol = 0;
|
|
write_map = 0;
|
|
relocatable_output = 0;
|
|
force_common_definition = 0;
|
|
T_flag_specified = 0;
|
|
Tdata_flag_specified = 0;
|
|
magic = DEFAULT_MAGIC;
|
|
make_executable = 1;
|
|
force_executable = 0;
|
|
link_mode = DYNAMIC;
|
|
#ifdef SUNOS4
|
|
link_mode |= SILLYARCHIVE;
|
|
#endif
|
|
soversion = DEFAULT_SOVERSION;
|
|
|
|
/* Initialize the cumulative counts of symbols. */
|
|
|
|
local_sym_count = 0;
|
|
non_L_local_sym_count = 0;
|
|
debugger_sym_count = 0;
|
|
undefined_global_sym_count = 0;
|
|
warn_sym_count = 0;
|
|
list_warning_symbols = 0;
|
|
multiple_def_count = 0;
|
|
common_defined_global_count = 0;
|
|
|
|
/* Keep a list of symbols referenced from the command line */
|
|
cl_refs_allocated = 10;
|
|
cmdline_references = (symbol **)
|
|
xmalloc(cl_refs_allocated * sizeof(symbol *));
|
|
*cmdline_references = 0;
|
|
|
|
/* Completely decode ARGV. */
|
|
decode_command(argc, argv);
|
|
|
|
building_shared_object =
|
|
(!relocatable_output && (link_mode & SHAREABLE));
|
|
|
|
if (building_shared_object && entry_symbol) {
|
|
errx(1,"`-Bshareable' and `-e' options are mutually exclusive");
|
|
}
|
|
|
|
/* Create the symbols `etext', `edata' and `end'. */
|
|
symtab_init(relocatable_output);
|
|
|
|
/*
|
|
* Determine whether to count the header as part of the text size,
|
|
* and initialize the text size accordingly. This depends on the kind
|
|
* of system and on the output format selected.
|
|
*/
|
|
|
|
if (magic == ZMAGIC || magic == QMAGIC)
|
|
page_align_segments = 1;
|
|
|
|
md_init_header(&outheader, magic, 0);
|
|
|
|
text_size = sizeof(struct exec);
|
|
text_size -= N_TXTOFF(outheader);
|
|
|
|
if (text_size < 0)
|
|
text_size = 0;
|
|
entry_offset = text_size;
|
|
|
|
if (!T_flag_specified && !relocatable_output)
|
|
text_start = TEXT_START(outheader);
|
|
|
|
/* The text-start address is normally this far past a page boundary. */
|
|
text_start_alignment = text_start % page_size;
|
|
|
|
/*
|
|
* Load symbols of all input files. Also search all libraries and
|
|
* decide which library members to load.
|
|
*/
|
|
load_symbols();
|
|
|
|
/* Compute where each file's sections go, and relocate symbols. */
|
|
digest_symbols();
|
|
|
|
/*
|
|
* Print error messages for any missing symbols, for any warning
|
|
* symbols, and possibly multiple definitions
|
|
*/
|
|
make_executable &= do_warnings(stderr);
|
|
|
|
/* Print a map, if requested. */
|
|
if (write_map)
|
|
print_symbols(stdout);
|
|
|
|
/* Write the output file. */
|
|
if (make_executable || force_executable)
|
|
write_output();
|
|
|
|
exit(!make_executable);
|
|
}
|
|
|
|
/*
|
|
* Analyze a command line argument. Return 0 if the argument is a filename.
|
|
* Return 1 if the argument is a option complete in itself. Return 2 if the
|
|
* argument is a option which uses an argument.
|
|
*
|
|
* Thus, the value is the number of consecutive arguments that are part of
|
|
* options.
|
|
*/
|
|
|
|
static int
|
|
classify_arg(arg)
|
|
register char *arg;
|
|
{
|
|
if (*arg != '-')
|
|
return 0;
|
|
switch (arg[1]) {
|
|
case 'a':
|
|
if (!strcmp(&arg[2], "ssert"))
|
|
return 2;
|
|
case 'A':
|
|
case 'D':
|
|
case 'e':
|
|
case 'L':
|
|
case 'l':
|
|
case 'o':
|
|
case 'u':
|
|
case 'V':
|
|
case 'y':
|
|
if (arg[2])
|
|
return 1;
|
|
return 2;
|
|
|
|
case 'B':
|
|
if (!strcmp(&arg[2], "static"))
|
|
return 1;
|
|
if (!strcmp(&arg[2], "dynamic"))
|
|
return 1;
|
|
|
|
case 'T':
|
|
if (arg[2] == 0)
|
|
return 2;
|
|
if (!strcmp(&arg[2], "text"))
|
|
return 2;
|
|
if (!strcmp(&arg[2], "data"))
|
|
return 2;
|
|
return 1;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Process the command arguments, setting up file_table with an entry for
|
|
* each input file, and setting variables according to the options.
|
|
*/
|
|
|
|
static void
|
|
decode_command(argc, argv)
|
|
int argc;
|
|
char **argv;
|
|
{
|
|
register int i;
|
|
register struct file_entry *p;
|
|
|
|
number_of_files = 0;
|
|
output_filename = "a.out";
|
|
|
|
/*
|
|
* First compute number_of_files so we know how long to make
|
|
* file_table.
|
|
* Also process most options completely.
|
|
*/
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
register int code = classify_arg(argv[i]);
|
|
if (code) {
|
|
if (i + code > argc)
|
|
errx(1, "no argument following %s", argv[i]);
|
|
|
|
decode_option(argv[i], argv[i + 1]);
|
|
|
|
if (argv[i][1] == 'l' || argv[i][1] == 'A')
|
|
number_of_files++;
|
|
|
|
i += code - 1;
|
|
} else
|
|
number_of_files++;
|
|
}
|
|
|
|
if (!number_of_files)
|
|
errx(1, "no input files");
|
|
|
|
p = file_table = (struct file_entry *)
|
|
xmalloc(number_of_files * sizeof(struct file_entry));
|
|
bzero(p, number_of_files * sizeof(struct file_entry));
|
|
|
|
/* Now scan again and fill in file_table. */
|
|
/* All options except -A and -l are ignored here. */
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
char *string;
|
|
register int code = classify_arg(argv[i]);
|
|
|
|
if (code == 0) {
|
|
p->filename = argv[i];
|
|
p->local_sym_name = argv[i];
|
|
p++;
|
|
continue;
|
|
}
|
|
if (code == 2)
|
|
string = argv[i + 1];
|
|
else
|
|
string = &argv[i][2];
|
|
|
|
if (argv[i][1] == 'B') {
|
|
if (strcmp(string, "static") == 0)
|
|
link_mode &= ~DYNAMIC;
|
|
else if (strcmp(string, "dynamic") == 0)
|
|
link_mode |= DYNAMIC;
|
|
else if (strcmp(string, "symbolic") == 0)
|
|
link_mode |= SYMBOLIC;
|
|
else if (strcmp(string, "forcearchive") == 0)
|
|
link_mode |= FORCEARCHIVE;
|
|
else if (strcmp(string, "shareable") == 0)
|
|
link_mode |= SHAREABLE;
|
|
#ifdef SUN_COMPAT
|
|
else if (strcmp(string, "silly") == 0)
|
|
link_mode |= SILLYARCHIVE;
|
|
else if (strcmp(string, "~silly") == 0)
|
|
link_mode &= ~SILLYARCHIVE;
|
|
#endif
|
|
}
|
|
if (argv[i][1] == 'A') {
|
|
if (p != file_table)
|
|
errx(1, "-A specified before an input file other than the first");
|
|
p->filename = string;
|
|
p->local_sym_name = string;
|
|
p->flags |= E_JUST_SYMS;
|
|
link_mode &= ~DYNAMIC;
|
|
p++;
|
|
}
|
|
if (argv[i][1] == 'l') {
|
|
p->filename = string;
|
|
p->local_sym_name = concat("-l", string, "");
|
|
p->flags |= E_SEARCH_DIRS;
|
|
if (link_mode & DYNAMIC && !relocatable_output)
|
|
p->flags |= E_SEARCH_DYNAMIC;
|
|
p++;
|
|
}
|
|
i += code - 1;
|
|
}
|
|
|
|
/* Now check some option settings for consistency. */
|
|
|
|
if (page_align_segments &&
|
|
(text_start - text_start_alignment) & (page_size - 1))
|
|
errx(1, "incorrect alignment of text start address");
|
|
|
|
/* Append the standard search directories to the user-specified ones. */
|
|
add_search_path(getenv("LD_LIBRARY_PATH"));
|
|
if (!nostdlib && getenv("LD_NOSTD_PATH") == NULL)
|
|
std_search_path();
|
|
}
|
|
|
|
void
|
|
add_cmdline_ref(sp)
|
|
symbol *sp;
|
|
{
|
|
symbol **ptr;
|
|
|
|
for (ptr = cmdline_references;
|
|
ptr < cmdline_references + cl_refs_allocated && *ptr;
|
|
ptr++);
|
|
|
|
if (ptr >= cmdline_references + cl_refs_allocated - 1) {
|
|
int diff = ptr - cmdline_references;
|
|
|
|
cl_refs_allocated *= 2;
|
|
cmdline_references = (symbol **)
|
|
xrealloc(cmdline_references,
|
|
cl_refs_allocated * sizeof(symbol *));
|
|
ptr = cmdline_references + diff;
|
|
}
|
|
*ptr++ = sp;
|
|
*ptr = (symbol *)0;
|
|
}
|
|
|
|
int
|
|
set_element_prefixed_p(name)
|
|
char *name;
|
|
{
|
|
struct string_list_element *p;
|
|
int i;
|
|
|
|
for (p = set_element_prefixes; p; p = p->next) {
|
|
|
|
for (i = 0; p->str[i] != '\0' && (p->str[i] == name[i]); i++);
|
|
if (p->str[i] == '\0')
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Record an option and arrange to act on it later. ARG should be the
|
|
* following command argument, which may or may not be used by this option.
|
|
*
|
|
* The `l' and `A' options are ignored here since they actually specify input
|
|
* files.
|
|
*/
|
|
|
|
static void
|
|
decode_option(swt, arg)
|
|
register char *swt, *arg;
|
|
{
|
|
if (!strcmp(swt + 1, "Bstatic"))
|
|
return;
|
|
if (!strcmp(swt + 1, "Bdynamic"))
|
|
return;
|
|
if (!strcmp(swt + 1, "Bsymbolic"))
|
|
return;
|
|
if (!strcmp(swt + 1, "Bforcearchive"))
|
|
return;
|
|
if (!strcmp(swt + 1, "Bshareable"))
|
|
return;
|
|
if (!strcmp(swt + 1, "assert"))
|
|
return;
|
|
#ifdef SUN_COMPAT
|
|
if (!strcmp(swt + 1, "Bsilly"))
|
|
return;
|
|
#endif
|
|
if (!strcmp(swt + 1, "Ttext")) {
|
|
text_start = parse(arg, "%x", "invalid argument to -Ttext");
|
|
T_flag_specified = 1;
|
|
return;
|
|
}
|
|
if (!strcmp(swt + 1, "Tdata")) {
|
|
rrs_data_start = parse(arg, "%x", "invalid argument to -Tdata");
|
|
Tdata_flag_specified = 1;
|
|
return;
|
|
}
|
|
if (!strcmp(swt + 1, "noinhibit-exec")) {
|
|
force_executable = 1;
|
|
return;
|
|
}
|
|
if (!strcmp(swt + 1, "nostdlib")) {
|
|
nostdlib = 1;
|
|
return;
|
|
}
|
|
if (swt[2] != 0)
|
|
arg = &swt[2];
|
|
|
|
switch (swt[1]) {
|
|
case 'A':
|
|
return;
|
|
|
|
case 'D':
|
|
specified_data_size = parse(arg, "%x", "invalid argument to -D");
|
|
return;
|
|
|
|
case 'd':
|
|
if (swt[2] == 0 || *arg == 'c')
|
|
force_common_definition = 1;
|
|
else if (*arg == 'p')
|
|
force_alias_definition = 1;
|
|
else
|
|
errx(1, "-d option takes 'c' or 'p' argument");
|
|
return;
|
|
|
|
case 'e':
|
|
entry_symbol = getsym(arg);
|
|
if (!entry_symbol->defined &&
|
|
!(entry_symbol->flags & GS_REFERENCED))
|
|
undefined_global_sym_count++;
|
|
entry_symbol->flags |= GS_REFERENCED;
|
|
add_cmdline_ref(entry_symbol);
|
|
return;
|
|
|
|
case 'l':
|
|
return;
|
|
|
|
case 'L':
|
|
add_search_dir(arg);
|
|
return;
|
|
|
|
case 'M':
|
|
write_map = 1;
|
|
return;
|
|
|
|
case 'N':
|
|
magic = OMAGIC;
|
|
return;
|
|
|
|
case 'n':
|
|
magic = NMAGIC;
|
|
return;
|
|
|
|
case 'o':
|
|
output_filename = arg;
|
|
return;
|
|
|
|
case 'p':
|
|
page_align_data = 1;
|
|
return;
|
|
|
|
#ifdef QMAGIC
|
|
case 'Q':
|
|
magic = QMAGIC;
|
|
return;
|
|
#endif
|
|
|
|
case 'r':
|
|
relocatable_output = 1;
|
|
magic = OMAGIC;
|
|
text_start = 0;
|
|
return;
|
|
|
|
case 'S':
|
|
strip_symbols = STRIP_DEBUGGER;
|
|
return;
|
|
|
|
case 's':
|
|
strip_symbols = STRIP_ALL;
|
|
return;
|
|
|
|
case 'T':
|
|
text_start = parse(arg, "%x", "invalid argument to -T");
|
|
T_flag_specified = 1;
|
|
return;
|
|
|
|
case 't':
|
|
trace_files = 1;
|
|
return;
|
|
|
|
case 'u':
|
|
{
|
|
register symbol *sp = getsym(arg);
|
|
|
|
if (!sp->defined && !(sp->flags & GS_REFERENCED))
|
|
undefined_global_sym_count++;
|
|
sp->flags |= GS_REFERENCED;
|
|
add_cmdline_ref(sp);
|
|
}
|
|
return;
|
|
|
|
#if 1
|
|
case 'V':
|
|
soversion = parse(arg, "%d", "invalid argument to -V");
|
|
return;
|
|
#endif
|
|
|
|
case 'X':
|
|
discard_locals = DISCARD_L;
|
|
return;
|
|
|
|
case 'x':
|
|
discard_locals = DISCARD_ALL;
|
|
return;
|
|
|
|
case 'y':
|
|
{
|
|
register symbol *sp = getsym(&swt[2]);
|
|
sp->flags |= GS_TRACE;
|
|
}
|
|
return;
|
|
|
|
case 'z':
|
|
magic = ZMAGIC;
|
|
oldmagic = 0;
|
|
#ifdef FreeBSD
|
|
netzmagic = 1;
|
|
#endif
|
|
return;
|
|
|
|
case 'Z':
|
|
magic = oldmagic = ZMAGIC;
|
|
#ifdef FreeBSD
|
|
netzmagic = 0;
|
|
#endif
|
|
return;
|
|
|
|
default:
|
|
errx(1, "invalid command option `%s'", swt);
|
|
}
|
|
}
|
|
|
|
/* Convenient functions for operating on one or all files being loaded. */
|
|
|
|
/*
|
|
* Call FUNCTION on each input file entry. Do not call for entries for
|
|
* libraries; instead, call once for each library member that is being
|
|
* loaded.
|
|
*
|
|
* FUNCTION receives two arguments: the entry, and ARG.
|
|
*/
|
|
|
|
void
|
|
each_file(function, arg)
|
|
register void (*function)();
|
|
register void *arg;
|
|
{
|
|
register int i;
|
|
|
|
for (i = 0; i < number_of_files; i++) {
|
|
register struct file_entry *entry = &file_table[i];
|
|
register struct file_entry *subentry;
|
|
|
|
if (entry->flags & E_SCRAPPED)
|
|
continue;
|
|
|
|
if (!(entry->flags & E_IS_LIBRARY))
|
|
(*function)(entry, arg);
|
|
|
|
subentry = entry->subfiles;
|
|
for (; subentry; subentry = subentry->chain) {
|
|
if (subentry->flags & E_SCRAPPED)
|
|
continue;
|
|
(*function)(subentry, arg);
|
|
}
|
|
|
|
#ifdef SUN_COMPAT
|
|
if (entry->silly_archive) {
|
|
|
|
if (!(entry->flags & E_DYNAMIC))
|
|
warnx("Silly");
|
|
|
|
if (!(entry->silly_archive->flags & E_IS_LIBRARY))
|
|
warnx("Sillier");
|
|
|
|
subentry = entry->silly_archive->subfiles;
|
|
for (; subentry; subentry = subentry->chain) {
|
|
if (subentry->flags & E_SCRAPPED)
|
|
continue;
|
|
(*function)(subentry, arg);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Call FUNCTION on each input file entry until it returns a non-zero value.
|
|
* Return this value. Do not call for entries for libraries; instead, call
|
|
* once for each library member that is being loaded.
|
|
*
|
|
* FUNCTION receives two arguments: the entry, and ARG. It must be a function
|
|
* returning unsigned long (though this can probably be fudged).
|
|
*/
|
|
|
|
unsigned long
|
|
check_each_file(function, arg)
|
|
register unsigned long (*function)();
|
|
register void *arg;
|
|
{
|
|
register int i;
|
|
register unsigned long return_val;
|
|
|
|
for (i = 0; i < number_of_files; i++) {
|
|
register struct file_entry *entry = &file_table[i];
|
|
if (entry->flags & E_SCRAPPED)
|
|
continue;
|
|
if (entry->flags & E_IS_LIBRARY) {
|
|
register struct file_entry *subentry = entry->subfiles;
|
|
for (; subentry; subentry = subentry->chain) {
|
|
if (subentry->flags & E_SCRAPPED)
|
|
continue;
|
|
if (return_val = (*function)(subentry, arg))
|
|
return return_val;
|
|
}
|
|
} else if (return_val = (*function)(entry, arg))
|
|
return return_val;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Like `each_file' but ignore files that were just for symbol definitions. */
|
|
|
|
void
|
|
each_full_file(function, arg)
|
|
register void (*function)();
|
|
register void *arg;
|
|
{
|
|
register int i;
|
|
|
|
for (i = 0; i < number_of_files; i++) {
|
|
register struct file_entry *entry = &file_table[i];
|
|
register struct file_entry *subentry;
|
|
|
|
if (entry->flags & (E_SCRAPPED | E_JUST_SYMS))
|
|
continue;
|
|
|
|
#ifdef SUN_COMPAT
|
|
if (entry->silly_archive) {
|
|
|
|
if (!(entry->flags & E_DYNAMIC))
|
|
warnx("Silly");
|
|
|
|
if (!(entry->silly_archive->flags & E_IS_LIBRARY))
|
|
warnx("Sillier");
|
|
|
|
subentry = entry->silly_archive->subfiles;
|
|
for (; subentry; subentry = subentry->chain) {
|
|
if (subentry->flags & E_SCRAPPED)
|
|
continue;
|
|
(*function)(subentry, arg);
|
|
}
|
|
}
|
|
#endif
|
|
if (entry->flags & E_DYNAMIC)
|
|
continue;
|
|
|
|
if (!(entry->flags & E_IS_LIBRARY))
|
|
(*function)(entry, arg);
|
|
|
|
subentry = entry->subfiles;
|
|
for (; subentry; subentry = subentry->chain) {
|
|
if (subentry->flags & E_SCRAPPED)
|
|
continue;
|
|
(*function)(subentry, arg);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/* Close the input file that is now open. */
|
|
|
|
void
|
|
file_close()
|
|
{
|
|
close(input_desc);
|
|
input_desc = 0;
|
|
input_file = 0;
|
|
}
|
|
|
|
/*
|
|
* Open the input file specified by 'entry', and return a descriptor. The
|
|
* open file is remembered; if the same file is opened twice in a row, a new
|
|
* open is not actually done.
|
|
*/
|
|
int
|
|
file_open(entry)
|
|
register struct file_entry *entry;
|
|
{
|
|
register int fd;
|
|
|
|
if (entry->superfile && (entry->superfile->flags & E_IS_LIBRARY))
|
|
return file_open(entry->superfile);
|
|
|
|
if (entry == input_file)
|
|
return input_desc;
|
|
|
|
if (input_file)
|
|
file_close();
|
|
|
|
if (entry->flags & E_SEARCH_DIRS) {
|
|
fd = findlib(entry);
|
|
} else
|
|
fd = open(entry->filename, O_RDONLY, 0);
|
|
|
|
if (fd > 0) {
|
|
input_file = entry;
|
|
input_desc = fd;
|
|
return fd;
|
|
}
|
|
|
|
if (entry->flags & E_SEARCH_DIRS)
|
|
errx(1, "%s: no match", entry->local_sym_name);
|
|
else
|
|
err(1, "%s", entry->filename);
|
|
return fd;
|
|
}
|
|
|
|
int
|
|
text_offset(entry)
|
|
struct file_entry *entry;
|
|
{
|
|
return entry->starting_offset + N_TXTOFF (entry->header);
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/*
|
|
* Read a file's header into the proper place in the file_entry. FD is the
|
|
* descriptor on which the file is open. ENTRY is the file's entry.
|
|
*/
|
|
void
|
|
read_header(fd, entry)
|
|
int fd;
|
|
struct file_entry *entry;
|
|
{
|
|
register int len;
|
|
|
|
if (lseek(fd, entry->starting_offset, L_SET) !=
|
|
entry->starting_offset)
|
|
err(1, "%s: read_header: lseek", get_file_name(entry));
|
|
|
|
len = read(fd, &entry->header, sizeof(struct exec));
|
|
if (len != sizeof (struct exec))
|
|
err(1, "%s: read_header: read", get_file_name(entry));
|
|
|
|
md_swapin_exec_hdr(&entry->header);
|
|
|
|
if (N_BADMAG (entry->header))
|
|
errx(1, "%s: bad magic", get_file_name(entry));
|
|
|
|
if (N_BADMID(entry->header))
|
|
errx(1, "%s: non-native input file", get_file_name(entry));
|
|
|
|
entry->flags |= E_HEADER_VALID;
|
|
}
|
|
|
|
/*
|
|
* Read the symbols of file ENTRY into core. Assume it is already open, on
|
|
* descriptor FD. Also read the length of the string table, which follows
|
|
* the symbol table, but don't read the contents of the string table.
|
|
*/
|
|
|
|
void
|
|
read_entry_symbols(fd, entry)
|
|
struct file_entry *entry;
|
|
int fd;
|
|
{
|
|
int str_size;
|
|
struct nlist *np;
|
|
int i;
|
|
|
|
if (!(entry->flags & E_HEADER_VALID))
|
|
read_header(fd, entry);
|
|
|
|
np = (struct nlist *)alloca(entry->header.a_syms);
|
|
entry->nsymbols = entry->header.a_syms / sizeof(struct nlist);
|
|
if (entry->nsymbols == 0)
|
|
return;
|
|
|
|
entry->symbols = (struct localsymbol *)
|
|
xmalloc(entry->nsymbols * sizeof(struct localsymbol));
|
|
|
|
if (lseek(fd, N_SYMOFF(entry->header) + entry->starting_offset, L_SET)
|
|
!= N_SYMOFF(entry->header) + entry->starting_offset)
|
|
err(1, "%s: read_symbols: lseek(syms)", get_file_name(entry));
|
|
|
|
if (entry->header.a_syms != read(fd, np, entry->header.a_syms))
|
|
errx(1, "%s: read_symbols: premature end of file in symbols",
|
|
get_file_name(entry));
|
|
|
|
md_swapin_symbols(np, entry->header.a_syms / sizeof(struct nlist));
|
|
|
|
for (i = 0; i < entry->nsymbols; i++) {
|
|
entry->symbols[i].nzlist.nlist = *np++;
|
|
entry->symbols[i].nzlist.nz_size = 0;
|
|
entry->symbols[i].symbol = NULL;
|
|
entry->symbols[i].next = NULL;
|
|
entry->symbols[i].entry = entry;
|
|
entry->symbols[i].gotslot_offset = -1;
|
|
entry->symbols[i].flags = 0;
|
|
}
|
|
|
|
entry->strings_offset = N_STROFF(entry->header) +
|
|
entry->starting_offset;
|
|
if (lseek(fd, entry->strings_offset, 0) == (off_t)-1)
|
|
err(1, "%s: read_symbols: lseek(strings)",
|
|
get_file_name(entry));
|
|
if (sizeof str_size != read(fd, &str_size, sizeof str_size))
|
|
errx(1, "%s: read_symbols: cannot read string table size",
|
|
get_file_name(entry));
|
|
|
|
entry->string_size = md_swap_long(str_size);
|
|
}
|
|
|
|
/*
|
|
* Read the string table of file ENTRY open on descriptor FD, into core.
|
|
*/
|
|
void
|
|
read_entry_strings(fd, entry)
|
|
struct file_entry *entry;
|
|
int fd;
|
|
{
|
|
|
|
if (entry->string_size == 0)
|
|
return;
|
|
|
|
if (!(entry->flags & E_HEADER_VALID) || !entry->strings_offset)
|
|
errx(1, "%s: read_strings: string table unavailable",
|
|
get_file_name(entry));
|
|
|
|
if (lseek(fd, entry->strings_offset, L_SET) !=
|
|
entry->strings_offset)
|
|
err(1, "%s: read_strings: lseek",
|
|
get_file_name(entry));
|
|
|
|
if (read(fd, entry->strings, entry->string_size) !=
|
|
entry->string_size)
|
|
errx(1, "%s: read_strings: premature end of file in strings",
|
|
get_file_name(entry));
|
|
|
|
return;
|
|
}
|
|
|
|
/* Read in the relocation sections of ENTRY if necessary */
|
|
|
|
void
|
|
read_entry_relocation(fd, entry)
|
|
int fd;
|
|
struct file_entry *entry;
|
|
{
|
|
register struct relocation_info *reloc;
|
|
off_t pos;
|
|
|
|
if (!entry->textrel) {
|
|
|
|
reloc = (struct relocation_info *)
|
|
xmalloc(entry->header.a_trsize);
|
|
|
|
pos = text_offset(entry) +
|
|
entry->header.a_text + entry->header.a_data;
|
|
|
|
if (lseek(fd, pos, L_SET) != pos)
|
|
err(1, "%s: read_reloc(text): lseek",
|
|
get_file_name(entry));
|
|
|
|
if (read(fd, reloc, entry->header.a_trsize) !=
|
|
entry->header.a_trsize)
|
|
errx(1, "%s: read_reloc(text): premature EOF",
|
|
get_file_name(entry));
|
|
|
|
md_swapin_reloc(reloc, entry->header.a_trsize / sizeof(*reloc));
|
|
entry->textrel = reloc;
|
|
entry->ntextrel = entry->header.a_trsize / sizeof(*reloc);
|
|
|
|
}
|
|
|
|
if (!entry->datarel) {
|
|
|
|
reloc = (struct relocation_info *)
|
|
xmalloc(entry->header.a_drsize);
|
|
|
|
pos = text_offset(entry) + entry->header.a_text +
|
|
entry->header.a_data + entry->header.a_trsize;
|
|
|
|
if (lseek(fd, pos, L_SET) != pos)
|
|
err(1, "%s: read_reloc(data): lseek",
|
|
get_file_name(entry));
|
|
|
|
if (read(fd, reloc, entry->header.a_drsize) !=
|
|
entry->header.a_drsize)
|
|
errx(1, "%s: read_reloc(data): premature EOF",
|
|
get_file_name(entry));
|
|
|
|
md_swapin_reloc(reloc, entry->header.a_drsize / sizeof(*reloc));
|
|
entry->datarel = reloc;
|
|
entry->ndatarel = entry->header.a_drsize / sizeof(*reloc);
|
|
|
|
}
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/*
|
|
* Read in the symbols of all input files.
|
|
*/
|
|
static void
|
|
load_symbols()
|
|
{
|
|
register int i;
|
|
|
|
if (trace_files)
|
|
fprintf(stderr, "Loading symbols:\n\n");
|
|
|
|
for (i = 0; i < number_of_files; i++)
|
|
read_file_symbols(&file_table[i]);
|
|
|
|
if (trace_files)
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
/*
|
|
* If ENTRY is a rel file, read its symbol and string sections into core. If
|
|
* it is a library, search it and load the appropriate members (which means
|
|
* calling this function recursively on those members).
|
|
*/
|
|
|
|
void
|
|
read_file_symbols(entry)
|
|
register struct file_entry *entry;
|
|
{
|
|
register int fd;
|
|
register int len;
|
|
struct exec hdr;
|
|
|
|
fd = file_open(entry);
|
|
|
|
len = read(fd, &hdr, sizeof hdr);
|
|
if (len != sizeof hdr)
|
|
errx(1, "%s: read_file_symbols(header): premature EOF",
|
|
get_file_name(entry));
|
|
|
|
md_swapin_exec_hdr(&hdr);
|
|
|
|
if (!N_BADMAG (hdr)) {
|
|
if (N_IS_DYNAMIC(hdr) && !(entry->flags & E_JUST_SYMS)) {
|
|
if (relocatable_output) {
|
|
errx(1,
|
|
"%s: -r and shared objects currently not supported",
|
|
get_file_name(entry));
|
|
return;
|
|
}
|
|
#if notyet /* Compatibility */
|
|
if (!(N_GETFLAG(hdr) & EX_PIC))
|
|
warnx("%s: EX_PIC not set",
|
|
get_file_name(entry));
|
|
#endif
|
|
entry->flags |= E_DYNAMIC;
|
|
if (entry->superfile || rrs_add_shobj(entry))
|
|
read_shared_object(fd, entry);
|
|
else
|
|
entry->flags |= E_SCRAPPED;
|
|
} else {
|
|
if (N_GETFLAG(hdr) & EX_PIC)
|
|
pic_code_seen = 1;
|
|
read_entry_symbols(fd, entry);
|
|
entry->strings = (char *)alloca(entry->string_size);
|
|
read_entry_strings(fd, entry);
|
|
read_entry_relocation(fd, entry);
|
|
enter_file_symbols(entry);
|
|
entry->strings = 0;
|
|
}
|
|
} else {
|
|
char armag[SARMAG];
|
|
|
|
lseek (fd, 0, 0);
|
|
if (SARMAG != read(fd, armag, SARMAG) ||
|
|
strncmp (armag, ARMAG, SARMAG))
|
|
errx(1,
|
|
"%s: malformed input file (not rel or archive)",
|
|
get_file_name(entry));
|
|
entry->flags |= E_IS_LIBRARY;
|
|
search_library(fd, entry);
|
|
}
|
|
|
|
file_close();
|
|
}
|
|
|
|
|
|
/*
|
|
* Enter the external symbol defs and refs of ENTRY in the hash table.
|
|
*/
|
|
|
|
void
|
|
enter_file_symbols(entry)
|
|
struct file_entry *entry;
|
|
{
|
|
struct localsymbol *lsp, *lspend;
|
|
|
|
if (trace_files)
|
|
prline_file_name(entry, stderr);
|
|
|
|
lspend = entry->symbols + entry->nsymbols;
|
|
|
|
for (lsp = entry->symbols; lsp < lspend; lsp++) {
|
|
register struct nlist *p = &lsp->nzlist.nlist;
|
|
|
|
if (p->n_type == (N_SETV | N_EXT))
|
|
continue;
|
|
|
|
/*
|
|
* Turn magically prefixed symbols into set symbols of
|
|
* a corresponding type.
|
|
*/
|
|
if (set_element_prefixes &&
|
|
set_element_prefixed_p(entry->strings+lsp->nzlist.nz_strx))
|
|
lsp->nzlist.nz_type += (N_SETA - N_ABS);
|
|
|
|
if (SET_ELEMENT_P(p->n_type)) {
|
|
set_symbol_count++;
|
|
if (!relocatable_output)
|
|
enter_global_ref(lsp,
|
|
p->n_un.n_strx + entry->strings, entry);
|
|
} else if (p->n_type == N_WARNING) {
|
|
char *msg = p->n_un.n_strx + entry->strings;
|
|
|
|
/* Grab the next entry. */
|
|
lsp++;
|
|
p = &lsp->nzlist.nlist;
|
|
if (p->n_type != (N_UNDF | N_EXT)) {
|
|
warnx(
|
|
"%s: Warning symbol without external reference following.",
|
|
get_file_name(entry));
|
|
make_executable = 0;
|
|
lsp--; /* Process normally. */
|
|
} else {
|
|
symbol *sp;
|
|
char *name = p->n_un.n_strx + entry->strings;
|
|
/* Deal with the warning symbol. */
|
|
lsp->flags |= LS_WARNING;
|
|
enter_global_ref(lsp, name, entry);
|
|
sp = getsym(name);
|
|
if (sp->warning == NULL) {
|
|
sp->warning = (char *)
|
|
xmalloc(strlen(msg)+1);
|
|
strcpy(sp->warning, msg);
|
|
warn_sym_count++;
|
|
} else if (strcmp(sp->warning, msg))
|
|
warnx(
|
|
"%s: multiple definitions for warning symbol `%s'",
|
|
get_file_name(entry), sp->name);
|
|
}
|
|
} else if (p->n_type & N_EXT) {
|
|
enter_global_ref(lsp,
|
|
p->n_un.n_strx + entry->strings, entry);
|
|
} else if (p->n_un.n_strx &&
|
|
(p->n_un.n_strx + entry->strings)[0] == LPREFIX)
|
|
lsp->flags |= LS_L_SYMBOL;
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* Enter one global symbol in the hash table. LSP points to the `struct
|
|
* localsymbol' from the file that describes the global symbol. NAME is the
|
|
* symbol's name. ENTRY is the file entry for the file the symbol comes from.
|
|
*
|
|
* LSP is put on the chain of all such structs that refer to the same symbol.
|
|
* This chain starts in the `refs' for symbols from relocatable objects. A
|
|
* backpointer to the global symbol is kept in LSP.
|
|
*
|
|
* Symbols from shared objects are linked through `soref'. For such symbols
|
|
* that's all we do at this stage, with the exception of the case where the
|
|
* symbol is a common. The `referenced' bit is only set for references from
|
|
* relocatable objects.
|
|
*
|
|
*/
|
|
|
|
static void
|
|
enter_global_ref(lsp, name, entry)
|
|
struct localsymbol *lsp;
|
|
char *name;
|
|
struct file_entry *entry;
|
|
{
|
|
register struct nzlist *nzp = &lsp->nzlist;
|
|
register symbol *sp = getsym(name);
|
|
register int type = nzp->nz_type;
|
|
int oldref = (sp->flags & GS_REFERENCED);
|
|
int olddef = sp->defined;
|
|
int com = sp->defined && sp->common_size;
|
|
|
|
if (type == (N_INDR | N_EXT) && !olddef) {
|
|
sp->alias = getsym(entry->strings + (lsp + 1)->nzlist.nz_strx);
|
|
if (sp == sp->alias) {
|
|
warnx("%s: %s is alias for itself",
|
|
get_file_name(entry), name);
|
|
/* Rewrite symbol as global text symbol with value 0 */
|
|
lsp->nzlist.nz_type = N_TEXT|N_EXT;
|
|
lsp->nzlist.nz_value = 0;
|
|
make_executable = 0;
|
|
} else {
|
|
global_alias_count++;
|
|
}
|
|
#if 0
|
|
if (sp->flags & GS_REFERENCED)
|
|
sp->alias->flags |= GS_REFERENCED;
|
|
#endif
|
|
}
|
|
|
|
if (entry->flags & E_DYNAMIC) {
|
|
lsp->next = sp->sorefs;
|
|
sp->sorefs = lsp;
|
|
lsp->symbol = sp;
|
|
|
|
/*
|
|
* Handle commons from shared objects:
|
|
* 1) If symbol hitherto undefined, turn it into a common.
|
|
* 2) If symbol already common, update size if necessary.
|
|
*/
|
|
/*XXX - look at case where commons are only in shared objects */
|
|
if (type == (N_UNDF | N_EXT) && nzp->nz_value) {
|
|
if (!olddef) {
|
|
if (oldref)
|
|
undefined_global_sym_count--;
|
|
common_defined_global_count++;
|
|
sp->common_size = nzp->nz_value;
|
|
sp->defined = N_UNDF | N_EXT;
|
|
} else if (com && sp->common_size < nzp->nz_value) {
|
|
sp->common_size = nzp->nz_value;
|
|
}
|
|
} else if (type != (N_UNDF | N_EXT) && !oldref) {
|
|
/*
|
|
* This is an ex common...
|
|
*/
|
|
if (com)
|
|
common_defined_global_count--;
|
|
sp->common_size = 0;
|
|
sp->defined = 0;
|
|
}
|
|
|
|
/*
|
|
* Handle size information in shared objects.
|
|
*/
|
|
if (nzp->nz_size > sp->size)
|
|
sp->size = nzp->nz_size;
|
|
|
|
if ((lsp->flags & LS_WARNING) && (sp->flags & GS_REFERENCED))
|
|
/*
|
|
* Prevent warning symbols from getting
|
|
* gratuitously referenced.
|
|
*/
|
|
list_warning_symbols = 1;
|
|
return;
|
|
}
|
|
|
|
lsp->next = sp->refs;
|
|
sp->refs = lsp;
|
|
lsp->symbol = sp;
|
|
|
|
if (lsp->flags & LS_WARNING) {
|
|
/*
|
|
* Prevent warning symbols from getting
|
|
* gratuitously referenced.
|
|
*/
|
|
if (sp->flags & GS_REFERENCED)
|
|
list_warning_symbols = 1;
|
|
return;
|
|
}
|
|
|
|
if (sp->warning)
|
|
list_warning_symbols = 1;
|
|
|
|
sp->flags |= GS_REFERENCED;
|
|
|
|
if (sp == dynamic_symbol || sp == got_symbol) {
|
|
if (type != (N_UNDF | N_EXT) && !(entry->flags & E_JUST_SYMS))
|
|
errx(1,"Linker reserved symbol %s defined as type %x ",
|
|
name, type);
|
|
return;
|
|
}
|
|
|
|
if (olddef && N_ISWEAK(&nzp->nlist) && !(sp->flags & GS_WEAK)) {
|
|
#ifdef DEBUG
|
|
printf("%s: not overridden by weak symbol from %s\n",
|
|
sp->name, get_file_name(entry));
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
if (type == (N_SIZE | N_EXT)) {
|
|
|
|
if (relocatable_output && nzp->nz_value != 0 && sp->size == 0)
|
|
size_sym_count++;
|
|
if (sp->size < nzp->nz_value)
|
|
sp->size = nzp->nz_value;
|
|
|
|
} else if (type != (N_UNDF | N_EXT) || nzp->nz_value) {
|
|
|
|
/*
|
|
* Set `->defined' here, so commons and undefined globals
|
|
* can be counted correctly.
|
|
*/
|
|
if (!sp->defined || sp->defined == (N_UNDF | N_EXT)) {
|
|
sp->defined = type;
|
|
}
|
|
|
|
if ((sp->flags & GS_WEAK) && !N_ISWEAK(&nzp->nlist)) {
|
|
/*
|
|
* Upgrade an existing weak definition.
|
|
* We fake it by pretending the symbol is undefined;
|
|
* must undo any common fiddling, however.
|
|
*/
|
|
if (!oldref)
|
|
errx(1, "internal error: enter_glob_ref: "
|
|
"weak symbol not referenced");
|
|
if (!olddef && !com)
|
|
undefined_weak_sym_count--;
|
|
undefined_global_sym_count++;
|
|
sp->defined = type;
|
|
sp->flags &= ~GS_WEAK;
|
|
olddef = 0;
|
|
if (com)
|
|
common_defined_global_count--;
|
|
com = 0;
|
|
sp->common_size = 0;
|
|
}
|
|
if (oldref && !olddef) {
|
|
/*
|
|
* It used to be undefined and we're defining it.
|
|
*/
|
|
undefined_global_sym_count--;
|
|
if (sp->flags & GS_WEAK)
|
|
/* Used to be a weak reference */
|
|
undefined_weak_sym_count--;
|
|
if (undefined_global_sym_count < 0 ||
|
|
undefined_weak_sym_count < 0)
|
|
errx(1, "internal error: enter_glob_ref: "
|
|
"undefined_global_sym_count = %d, "
|
|
"undefined_weak_sym_count = %d",
|
|
undefined_global_sym_count,
|
|
undefined_weak_sym_count);
|
|
|
|
}
|
|
|
|
if (N_ISWEAK(&nzp->nlist))
|
|
/* The definition is weak */
|
|
sp->flags |= GS_WEAK;
|
|
|
|
if (!olddef && type == (N_UNDF | N_EXT) && nzp->nz_value) {
|
|
/*
|
|
* First definition and it's common.
|
|
*/
|
|
common_defined_global_count++;
|
|
sp->common_size = nzp->nz_value;
|
|
} else if (com && type != (N_UNDF | N_EXT)) {
|
|
/*
|
|
* It used to be common and we're defining
|
|
* it as something else.
|
|
*/
|
|
common_defined_global_count--;
|
|
sp->common_size = 0;
|
|
} else if (com && type == (N_UNDF | N_EXT) &&
|
|
sp->common_size < nzp->nz_value)
|
|
/*
|
|
* It used to be common and this is a new common entry
|
|
* to which we need to pay attention.
|
|
*/
|
|
sp->common_size = nzp->nz_value;
|
|
|
|
if (SET_ELEMENT_P(type) && (!olddef || com))
|
|
set_vector_count++;
|
|
|
|
} else if (!oldref && !com) {
|
|
/*
|
|
* An unreferenced symbol can already be defined
|
|
* as common by shared objects.
|
|
*/
|
|
undefined_global_sym_count++;
|
|
if (N_ISWEAK(&nzp->nlist)) {
|
|
/* The reference is weak */
|
|
sp->flags |= GS_WEAK;
|
|
undefined_weak_sym_count++;
|
|
}
|
|
}
|
|
|
|
if (sp == end_symbol && (entry->flags & E_JUST_SYMS) &&
|
|
!T_flag_specified)
|
|
text_start = nzp->nz_value;
|
|
|
|
if (sp->flags & GS_TRACE) {
|
|
register char *reftype;
|
|
switch (type & N_TYPE) {
|
|
case N_UNDF:
|
|
reftype = nzp->nz_value
|
|
? "defined as common" : "referenced";
|
|
break;
|
|
|
|
case N_ABS:
|
|
reftype = "defined as absolute";
|
|
break;
|
|
|
|
case N_TEXT:
|
|
reftype = "defined in text section";
|
|
break;
|
|
|
|
case N_DATA:
|
|
reftype = "defined in data section";
|
|
break;
|
|
|
|
case N_BSS:
|
|
reftype = "defined in BSS section";
|
|
break;
|
|
|
|
case N_INDR:
|
|
reftype = "alias";
|
|
break;
|
|
|
|
case N_SIZE:
|
|
reftype = "size spec";
|
|
break;
|
|
|
|
default:
|
|
reftype = "I don't know this type";
|
|
break;
|
|
}
|
|
|
|
fprintf(stderr, "symbol %s %s%s in ", sp->name,
|
|
(N_ISWEAK(&nzp->nlist))?"weakly ":"", reftype);
|
|
print_file_name (entry, stderr);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This returns 0 if the given file entry's symbol table does *not* contain
|
|
* the nlist point entry, and it returns the files entry pointer (cast to
|
|
* unsigned long) if it does.
|
|
*/
|
|
|
|
unsigned long
|
|
contains_symbol(entry, np)
|
|
struct file_entry *entry;
|
|
register struct nlist *np;
|
|
{
|
|
if (np >= &entry->symbols->nzlist.nlist &&
|
|
np < &(entry->symbols + entry->nsymbols)->nzlist.nlist)
|
|
return (unsigned long) entry;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Having entered all the global symbols and found the sizes of sections of
|
|
* all files to be linked, make all appropriate deductions from this data.
|
|
*
|
|
* We propagate global symbol values from definitions to references. We compute
|
|
* the layout of the output file and where each input file's contents fit
|
|
* into it.
|
|
*
|
|
* This is now done in several stages.
|
|
*
|
|
* 1) All global symbols are examined for definitions in relocatable (.o)
|
|
* files. The symbols' type is set according to the definition found,
|
|
* but its value can not yet be determined. In stead, we keep a pointer
|
|
* to the file entry's localsymbol that bequeathed the global symbol with
|
|
* its definition. Also, multiple (incompatible) definitions are checked
|
|
* for in this pass. If no definition comes forward, the set of local
|
|
* symbols originating from shared objects is searched for a definition.
|
|
*
|
|
* 2) Then the relocation information of each relocatable file is examined
|
|
* for possible contributions to the RRS section.
|
|
*
|
|
* 3) When this is done, the sizes and start addresses are set of all segments
|
|
* that will appear in the output file (including the RRS segment).
|
|
*
|
|
* 4) Finally, all symbols are relocated according according to the start
|
|
* of the entry they are part of. Then global symbols are assigned their
|
|
* final values. Also, space for commons and imported data are allocated
|
|
* during this pass, if the link mode in effect so demands.
|
|
*
|
|
*/
|
|
|
|
static void
|
|
digest_symbols()
|
|
{
|
|
|
|
if (trace_files)
|
|
fprintf(stderr, "Digesting symbol information:\n\n");
|
|
|
|
if (!relocatable_output) {
|
|
/*
|
|
* The set sector size is the number of set elements + a word
|
|
* for each symbol for the length word at the beginning of
|
|
* the vector, plus a word for each symbol for a zero at the
|
|
* end of the vector (for incremental linking).
|
|
*/
|
|
set_sect_size = (set_symbol_count + 2 * set_vector_count) *
|
|
sizeof (unsigned long);
|
|
set_vectors = (long *)xmalloc (set_sect_size);
|
|
setv_fill_count = 0;
|
|
}
|
|
|
|
/* Pass 1: check and define symbols */
|
|
defined_global_sym_count = 0;
|
|
digest_pass1();
|
|
|
|
each_full_file(consider_relocation, (void *)0); /* Text */
|
|
each_full_file(consider_relocation, (void *)1); /* Data */
|
|
|
|
each_file(consider_local_symbols, (void *)0);
|
|
|
|
/*
|
|
* Compute total size of sections.
|
|
* RRS data is the first output data section, RRS text is the last
|
|
* text section. Thus, DATA_START is calculated from RRS_DATA_START
|
|
* and RRS_DATA_SIZE, while RRS_TEXT_START is derived from TEXT_START
|
|
* and TEXT_SIZE.
|
|
*/
|
|
consider_rrs_section_lengths();
|
|
each_full_file(consider_file_section_lengths, 0);
|
|
rrs_text_start = text_start + text_size;
|
|
text_size += rrs_text_size;
|
|
data_size += rrs_data_size;
|
|
|
|
/*
|
|
* If necessary, pad text section to full page in the file. Include
|
|
* the padding in the text segment size.
|
|
*/
|
|
|
|
if (page_align_segments || page_align_data) {
|
|
int text_end = text_size + N_TXTOFF(outheader);
|
|
text_pad = PALIGN(text_end, page_size) - text_end;
|
|
text_size += text_pad;
|
|
}
|
|
outheader.a_text = text_size;
|
|
|
|
/*
|
|
* Make the data segment address start in memory on a suitable
|
|
* boundary.
|
|
*/
|
|
|
|
if (!Tdata_flag_specified)
|
|
rrs_data_start = text_start +
|
|
DATA_START(outheader) - TEXT_START(outheader);
|
|
|
|
data_start = rrs_data_start + rrs_data_size;
|
|
if (!relocatable_output) {
|
|
set_sect_start = rrs_data_start + data_size;
|
|
data_size += MALIGN(set_sect_size);
|
|
}
|
|
bss_start = rrs_data_start + data_size;
|
|
|
|
#ifdef DEBUG
|
|
printf("textstart = %#x, textsize = %#x, rrs_text_start = %#x, rrs_text_size %#x\n",
|
|
text_start, text_size, rrs_text_start, rrs_text_size);
|
|
printf("datastart = %#x, datasize = %#x, rrs_data_start %#x, rrs_data_size %#x\n",
|
|
data_start, data_size, rrs_data_start, rrs_data_size);
|
|
printf("bssstart = %#x, bsssize = %#x\n",
|
|
bss_start, bss_size);
|
|
printf("set_sect_start = %#x, set_sect_size = %#x\n",
|
|
set_sect_start, set_sect_size);
|
|
#endif
|
|
|
|
/* Compute start addresses of each file's sections and symbols. */
|
|
|
|
each_full_file(relocate_file_addresses, 0);
|
|
relocate_rrs_addresses();
|
|
|
|
/* Pass 2: assign values to symbols */
|
|
digest_pass2();
|
|
|
|
if (end_symbol) { /* These are null if -r. */
|
|
etext_symbol->value = text_start + text_size - text_pad;
|
|
edata_symbol->value = rrs_data_start + data_size;
|
|
end_symbol->value = rrs_data_start + data_size + bss_size;
|
|
}
|
|
/*
|
|
* Figure the data_pad now, so that it overlaps with the bss
|
|
* addresses.
|
|
*/
|
|
|
|
if (specified_data_size && specified_data_size > data_size)
|
|
data_pad = specified_data_size - data_size;
|
|
|
|
if (page_align_segments)
|
|
data_pad = PALIGN(data_pad + data_size, page_size) - data_size;
|
|
|
|
bss_size -= data_pad;
|
|
if (bss_size < 0)
|
|
bss_size = 0;
|
|
|
|
data_size += data_pad;
|
|
|
|
/*
|
|
* Calculate total number of symbols that will go into
|
|
* the output symbol table (barring DISCARD_* settings).
|
|
*/
|
|
global_sym_count = defined_global_sym_count +
|
|
undefined_global_sym_count;
|
|
|
|
if (dynamic_symbol->flags & GS_REFERENCED)
|
|
global_sym_count++;
|
|
|
|
if (got_symbol->flags & GS_REFERENCED)
|
|
global_sym_count++;
|
|
|
|
if (relocatable_output || building_shared_object) {
|
|
/* For each alias we write out two struct nlists */
|
|
global_sym_count += global_alias_count;
|
|
/* Propagate warning symbols; costs two extra struct nlists */
|
|
global_sym_count += 2 * warn_sym_count;
|
|
}
|
|
|
|
if (relocatable_output)
|
|
/* We write out the original N_SIZE symbols */
|
|
global_sym_count += size_sym_count;
|
|
|
|
#ifdef DEBUG
|
|
printf(
|
|
"global symbols %d "
|
|
"(defined %d, undefined %d, weak %d, aliases %d, warnings 2 * %d, "
|
|
"size symbols %d)\ncommons %d, locals: %d, debug symbols: %d, set_symbols %d\n",
|
|
global_sym_count,
|
|
defined_global_sym_count, undefined_global_sym_count,
|
|
undefined_weak_sym_count,
|
|
global_alias_count, warn_sym_count, size_sym_count,
|
|
common_defined_global_count, local_sym_count,
|
|
debugger_sym_count, set_symbol_count);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Determine the definition of each global symbol.
|
|
*/
|
|
static void
|
|
digest_pass1()
|
|
{
|
|
|
|
/*
|
|
* For each symbol, verify that it is defined globally at most
|
|
* once within relocatable files (except when building a shared lib).
|
|
* and set the `defined' field if there is a definition.
|
|
*
|
|
* Then check the shared object symbol chain for any remaining
|
|
* undefined symbols. Set the `so_defined' field for any
|
|
* definition find this way.
|
|
*/
|
|
FOR_EACH_SYMBOL(i, sp) {
|
|
symbol *spsave;
|
|
struct localsymbol *lsp;
|
|
int defs = 0;
|
|
|
|
if (!(sp->flags & GS_REFERENCED)) {
|
|
#if 0
|
|
/* Check for undefined symbols in shared objects */
|
|
int type;
|
|
for (lsp = sp->sorefs; lsp; lsp = lsp->next) {
|
|
type = lsp->nzlist.nlist.n_type;
|
|
if ((type & N_EXT) && type != (N_UNDF | N_EXT))
|
|
break;
|
|
}
|
|
if ((type & N_EXT) && type == (N_UNDF | N_EXT))
|
|
undefined_shobj_sym_count++;
|
|
#endif
|
|
|
|
/* Superfluous symbol from shared object */
|
|
continue;
|
|
}
|
|
if (sp->so_defined)
|
|
/* Already examined; must have been an alias */
|
|
continue;
|
|
|
|
if (sp == got_symbol || sp == dynamic_symbol)
|
|
continue;
|
|
|
|
for (lsp = sp->refs; lsp; lsp = lsp->next) {
|
|
register struct nlist *p = &lsp->nzlist.nlist;
|
|
register int type = p->n_type;
|
|
|
|
if (SET_ELEMENT_P(type)) {
|
|
if (relocatable_output)
|
|
errx(1,
|
|
"internal error: global ref to set el %s with -r",
|
|
sp->name);
|
|
if (!defs++) {
|
|
sp->defined = N_SETV | N_EXT;
|
|
sp->value =
|
|
setv_fill_count++ * sizeof(long);
|
|
} else if ((sp->defined & N_TYPE) != N_SETV) {
|
|
sp->mult_defs = 1;
|
|
multiple_def_count++;
|
|
}
|
|
/* Keep count and remember symbol */
|
|
sp->setv_count++;
|
|
set_vectors[setv_fill_count++] = (long)p;
|
|
if (building_shared_object) {
|
|
struct relocation_info reloc;
|
|
|
|
/*
|
|
* Make sure to relocate the contents
|
|
* of this set vector.
|
|
*/
|
|
bzero(&reloc, sizeof(reloc));
|
|
RELOC_INIT_SEGMENT_RELOC(&reloc);
|
|
RELOC_ADDRESS(&reloc) =
|
|
setv_fill_count * sizeof(long);
|
|
alloc_rrs_segment_reloc(NULL, &reloc);
|
|
}
|
|
|
|
} else if ((type & N_EXT) && type != (N_UNDF | N_EXT)
|
|
&& (type & N_TYPE) != N_FN
|
|
&& (type & N_TYPE) != N_SIZE) {
|
|
/* non-common definition */
|
|
if (!N_ISWEAK(p))
|
|
++defs;
|
|
if (defs > 1) {
|
|
sp->mult_defs = 1;
|
|
multiple_def_count++;
|
|
} else if (!N_ISWEAK(p) ||
|
|
(!sp->def_lsp && !sp->common_size)) {
|
|
sp->def_lsp = lsp;
|
|
lsp->entry->flags |= E_SYMBOLS_USED;
|
|
sp->defined = type;
|
|
sp->aux = N_AUX(p);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sp->defined) {
|
|
if ((sp->defined & N_TYPE) == N_SETV)
|
|
/* Allocate zero entry in set vector */
|
|
setv_fill_count++;
|
|
/*
|
|
* At this stage, we do not know whether an alias
|
|
* is going to be defined for real here, or whether
|
|
* it refers to a shared object symbol. The decision
|
|
* is deferred until digest_pass2().
|
|
*/
|
|
if (!sp->alias)
|
|
defined_global_sym_count++;
|
|
continue;
|
|
}
|
|
|
|
if (relocatable_output)
|
|
/* We're done */
|
|
continue;
|
|
|
|
/*
|
|
* Still undefined, search the shared object symbols for a
|
|
* definition. This symbol must go into the RRS.
|
|
*/
|
|
if (building_shared_object) {
|
|
/* Just punt for now */
|
|
undefined_global_sym_count--;
|
|
if (undefined_global_sym_count < 0)
|
|
errx(1,
|
|
"internal error: digest_pass1,1: %s: undefined_global_sym_count = %d",
|
|
sp->name, undefined_global_sym_count);
|
|
continue;
|
|
}
|
|
|
|
spsave=sp;
|
|
again:
|
|
for (lsp = sp->sorefs; lsp; lsp = lsp->next) {
|
|
register struct nlist *p = &lsp->nzlist.nlist;
|
|
register int type = p->n_type;
|
|
|
|
if ((type & N_EXT) && type != (N_UNDF | N_EXT) &&
|
|
(type & N_TYPE) != N_FN) {
|
|
/* non-common definition */
|
|
sp->def_lsp = lsp;
|
|
sp->so_defined = type;
|
|
sp->aux = N_AUX(p);
|
|
if (lsp->entry->flags & E_SECONDCLASS)
|
|
/* Keep looking for something better */
|
|
continue;
|
|
if (N_ISWEAK(p))
|
|
/* Keep looking for something better */
|
|
continue;
|
|
break;
|
|
}
|
|
}
|
|
if (sp->def_lsp) {
|
|
#ifdef DEBUG
|
|
printf("pass1: SO definition for %s, type %x in %s at %#x\n",
|
|
sp->name, sp->so_defined, get_file_name(sp->def_lsp->entry),
|
|
sp->def_lsp->nzlist.nz_value);
|
|
#endif
|
|
sp->def_lsp->entry->flags |= E_SYMBOLS_USED;
|
|
if (sp->flags & GS_REFERENCED)
|
|
undefined_global_sym_count--;
|
|
else
|
|
sp->flags |= GS_REFERENCED;
|
|
if (undefined_global_sym_count < 0)
|
|
errx(1, "internal error: digest_pass1,2: "
|
|
"%s: undefined_global_sym_count = %d",
|
|
sp->name, undefined_global_sym_count);
|
|
if (sp->alias &&
|
|
!(sp->alias->flags & GS_REFERENCED)) {
|
|
sp = sp->alias;
|
|
goto again;
|
|
}
|
|
}
|
|
sp=spsave;
|
|
} END_EACH_SYMBOL;
|
|
|
|
if (setv_fill_count != set_sect_size/sizeof(long))
|
|
errx(1, "internal error: allocated set symbol space (%d) "
|
|
"doesn't match actual (%d)",
|
|
set_sect_size/sizeof(long), setv_fill_count);
|
|
}
|
|
|
|
|
|
/*
|
|
* Scan relocation info in ENTRY for contributions to the RRS section
|
|
* of the output file.
|
|
*/
|
|
static void
|
|
consider_relocation(entry, dataseg)
|
|
struct file_entry *entry;
|
|
int dataseg;
|
|
{
|
|
struct relocation_info *reloc, *end;
|
|
struct localsymbol *lsp;
|
|
symbol *sp;
|
|
|
|
if (dataseg == 0) {
|
|
/* Text relocations */
|
|
reloc = entry->textrel;
|
|
end = entry->textrel + entry->ntextrel;
|
|
} else {
|
|
/* Data relocations */
|
|
reloc = entry->datarel;
|
|
end = entry->datarel + entry->ndatarel;
|
|
}
|
|
|
|
for (; reloc < end; reloc++) {
|
|
|
|
if (relocatable_output) {
|
|
lsp = &entry->symbols[reloc->r_symbolnum];
|
|
if (RELOC_BASEREL_P(reloc)) {
|
|
pic_code_seen = 1; /* Compatibility */
|
|
if (!RELOC_EXTERN_P(reloc))
|
|
lsp->flags |= LS_RENAME;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* First, do the PIC specific relocs.
|
|
* r_relative and r_copy should not occur at this point
|
|
* (we do output them). The others break down to these
|
|
* combinations:
|
|
*
|
|
* jmptab: extern: needs jmp slot
|
|
* !extern: "intersegment" jump/call,
|
|
* should get resolved in output
|
|
*
|
|
* baserel: extern: need GOT entry
|
|
* !extern: may need GOT entry,
|
|
* machine dependent
|
|
*
|
|
* baserel's always refer to symbol through `r_symbolnum'
|
|
* whether extern or not. Internal baserels refer to statics
|
|
* that must be accessed either *through* the GOT table like
|
|
* global data, or by means of an offset from the GOT table.
|
|
* The macro RELOC_STATICS_THROUGH_GOT_P() determines which
|
|
* applies, since this is a machine (compiler?) dependent
|
|
* addressing mode.
|
|
*/
|
|
|
|
if (RELOC_JMPTAB_P(reloc)) {
|
|
|
|
if (!RELOC_EXTERN_P(reloc))
|
|
continue;
|
|
|
|
lsp = &entry->symbols[reloc->r_symbolnum];
|
|
sp = lsp->symbol;
|
|
if (sp->alias)
|
|
sp = sp->alias;
|
|
if (sp->flags & GS_TRACE) {
|
|
fprintf(stderr, "symbol %s has jmpslot in %s\n",
|
|
sp->name, get_file_name(entry));
|
|
}
|
|
alloc_rrs_jmpslot(entry, sp);
|
|
|
|
} else if (RELOC_BASEREL_P(reloc)) {
|
|
|
|
lsp = &entry->symbols[reloc->r_symbolnum];
|
|
alloc_rrs_gotslot(entry, reloc, lsp);
|
|
|
|
} else if (RELOC_EXTERN_P(reloc)) {
|
|
|
|
/*
|
|
* Non-PIC relocations.
|
|
* If the definition comes from a shared object
|
|
* we need a relocation entry in RRS.
|
|
*
|
|
* If the .so definition is N_TEXT a jmpslot is
|
|
* allocated.
|
|
*
|
|
* If it is N_DATA we allocate an address in BSS (?)
|
|
* and arrange for the data to be copied at run-time.
|
|
* The symbol is temporarily marked with N_SIZE in
|
|
* the `defined' field, so we know what to do in
|
|
* pass2() and during actual relocation. We convert
|
|
* the type back to something real again when writing
|
|
* out the symbols.
|
|
*
|
|
*/
|
|
lsp = &entry->symbols[reloc->r_symbolnum];
|
|
sp = lsp->symbol;
|
|
if (sp == NULL)
|
|
errx(1, "%s: bogus relocation record",
|
|
get_file_name(entry));
|
|
|
|
if (sp->alias)
|
|
sp = sp->alias;
|
|
|
|
/*
|
|
* Skip refs to _GLOBAL_OFFSET_TABLE_ and __DYNAMIC
|
|
*/
|
|
if (sp == got_symbol) {
|
|
if (!CHECK_GOT_RELOC(reloc))
|
|
errx(1,
|
|
"%s: Unexpected relocation type for GOT symbol",
|
|
get_file_name(entry));
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* This symbol gives rise to a RRS entry
|
|
*/
|
|
|
|
if (building_shared_object) {
|
|
if (sp->flags & GS_TRACE) {
|
|
fprintf(stderr,
|
|
"symbol %s RRS entry in %s\n",
|
|
sp->name, get_file_name(entry));
|
|
}
|
|
alloc_rrs_reloc(entry, sp);
|
|
continue;
|
|
}
|
|
|
|
if (force_alias_definition && sp->so_defined &&
|
|
sp->aux == AUX_FUNC) {
|
|
|
|
/* Call to shared library procedure */
|
|
alloc_rrs_jmpslot(entry, sp);
|
|
|
|
} else if (sp->size && sp->so_defined &&
|
|
sp->aux == AUX_OBJECT) {
|
|
|
|
/* Reference to shared library data */
|
|
alloc_rrs_cpy_reloc(entry, sp);
|
|
sp->defined = N_SIZE;
|
|
|
|
} else if (!sp->defined && sp->common_size == 0 &&
|
|
sp->so_defined)
|
|
alloc_rrs_reloc(entry, sp);
|
|
|
|
} else {
|
|
/*
|
|
* Segment relocation.
|
|
* Prepare an RRS relocation as these are load
|
|
* address dependent.
|
|
*/
|
|
if (building_shared_object && !RELOC_PCREL_P(reloc)) {
|
|
alloc_rrs_segment_reloc(entry, reloc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Determine the disposition of each local symbol.
|
|
*/
|
|
static void
|
|
consider_local_symbols(entry)
|
|
register struct file_entry *entry;
|
|
{
|
|
register struct localsymbol *lsp, *lspend;
|
|
|
|
if (entry->flags & E_DYNAMIC)
|
|
return;
|
|
|
|
lspend = entry->symbols + entry->nsymbols;
|
|
|
|
/*
|
|
* For each symbol determine whether it should go
|
|
* in the output symbol table.
|
|
*/
|
|
|
|
for (lsp = entry->symbols; lsp < lspend; lsp++) {
|
|
register struct nlist *p = &lsp->nzlist.nlist;
|
|
register int type = p->n_type;
|
|
|
|
if (type == N_WARNING)
|
|
continue;
|
|
|
|
if (SET_ELEMENT_P (type)) {
|
|
/*
|
|
* This occurs even if global. These types of
|
|
* symbols are never written globally, though
|
|
* they are stored globally.
|
|
*/
|
|
if (relocatable_output)
|
|
lsp->flags |= LS_WRITE;
|
|
|
|
} else if (!(type & (N_STAB | N_EXT))) {
|
|
|
|
/*
|
|
* Ordinary local symbol
|
|
*/
|
|
if ((lsp->flags & LS_RENAME) || (
|
|
discard_locals != DISCARD_ALL &&
|
|
!(discard_locals == DISCARD_L &&
|
|
(lsp->flags & LS_L_SYMBOL))) ) {
|
|
|
|
lsp->flags |= LS_WRITE;
|
|
local_sym_count++;
|
|
}
|
|
|
|
} else if (!(type & N_EXT)) {
|
|
|
|
/*
|
|
* Debugger symbol
|
|
*/
|
|
if (strip_symbols == STRIP_NONE) {
|
|
lsp->flags |= LS_WRITE;
|
|
debugger_sym_count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Count one for the local symbol that we generate,
|
|
* whose name is the file's name (usually) and whose address
|
|
* is the start of the file's text.
|
|
*/
|
|
if (discard_locals != DISCARD_ALL)
|
|
local_sym_count++;
|
|
}
|
|
|
|
/*
|
|
* Accumulate the section sizes of input file ENTRY into the section sizes of
|
|
* the output file.
|
|
*/
|
|
static void
|
|
consider_file_section_lengths(entry)
|
|
register struct file_entry *entry;
|
|
{
|
|
|
|
entry->text_start_address = text_size;
|
|
/* If there were any vectors, we need to chop them off */
|
|
text_size += entry->header.a_text;
|
|
entry->data_start_address = data_size;
|
|
data_size += entry->header.a_data;
|
|
entry->bss_start_address = bss_size;
|
|
bss_size += entry->header.a_bss;
|
|
|
|
text_reloc_size += entry->header.a_trsize;
|
|
data_reloc_size += entry->header.a_drsize;
|
|
}
|
|
|
|
/*
|
|
* Determine where the sections of ENTRY go into the output file,
|
|
* whose total section sizes are already known.
|
|
* Also relocate the addresses of the file's local and debugger symbols.
|
|
*/
|
|
static void
|
|
relocate_file_addresses(entry)
|
|
register struct file_entry *entry;
|
|
{
|
|
register struct localsymbol *lsp, *lspend;
|
|
|
|
entry->text_start_address += text_start;
|
|
/*
|
|
* Note that `data_start' and `data_size' have not yet been
|
|
* adjusted for `data_pad'. If they had been, we would get the wrong
|
|
* results here.
|
|
*/
|
|
entry->data_start_address += data_start;
|
|
entry->bss_start_address += bss_start;
|
|
#ifdef DEBUG
|
|
printf("%s: datastart: %#x, bss %#x\n", get_file_name(entry),
|
|
entry->data_start_address, entry->bss_start_address);
|
|
#endif
|
|
|
|
lspend = entry->symbols + entry->nsymbols;
|
|
|
|
for (lsp = entry->symbols; lsp < lspend; lsp++) {
|
|
register struct nlist *p = &lsp->nzlist.nlist;
|
|
register int type = p->n_type;
|
|
|
|
/*
|
|
* If this belongs to a section, update it
|
|
* by the section's start address
|
|
*/
|
|
|
|
switch (type & N_TYPE) {
|
|
case N_TEXT:
|
|
case N_SETT:
|
|
p->n_value += entry->text_start_address;
|
|
break;
|
|
case N_DATA:
|
|
case N_SETD:
|
|
case N_SETV:
|
|
/*
|
|
* A symbol whose value is in the data section is
|
|
* present in the input file as if the data section
|
|
* started at an address equal to the length of the
|
|
* file's text.
|
|
*/
|
|
p->n_value += entry->data_start_address -
|
|
entry->header.a_text;
|
|
break;
|
|
case N_BSS:
|
|
case N_SETB:
|
|
/* likewise for symbols with value in BSS. */
|
|
p->n_value += entry->bss_start_address -
|
|
(entry->header.a_text +
|
|
entry->header.a_data);
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* Assign a value to each global symbol.
|
|
*/
|
|
static void
|
|
digest_pass2()
|
|
{
|
|
FOR_EACH_SYMBOL(i, sp) {
|
|
int size;
|
|
int align = sizeof(int);
|
|
|
|
if (!(sp->flags & GS_REFERENCED))
|
|
continue;
|
|
|
|
if (sp->alias &&
|
|
(relocatable_output || building_shared_object ||
|
|
(sp->alias->defined && !sp->alias->so_defined))) {
|
|
/*
|
|
* The alias points at a defined symbol, so it
|
|
* must itself be counted as one too, in order to
|
|
* compute the correct number of symbol table entries.
|
|
*/
|
|
if (!sp->defined) {
|
|
/*
|
|
* Change aliased symbol's definition too.
|
|
* These things happen if shared object commons
|
|
* or data is going into our symbol table.
|
|
*/
|
|
if (sp->so_defined != (N_INDR+N_EXT))
|
|
warnx( "pass2: %s: alias isn't",
|
|
sp->name);
|
|
sp->defined = sp->so_defined;
|
|
sp->so_defined = 0;
|
|
}
|
|
defined_global_sym_count++;
|
|
}
|
|
|
|
if ((sp->defined & N_TYPE) == N_SETV) {
|
|
/*
|
|
* Set length word at front of vector and zero byte
|
|
* at end. Reverse the vector itself to put it in
|
|
* file order.
|
|
*/
|
|
unsigned long i, *p, *q;
|
|
unsigned long length_word_index =
|
|
sp->value / sizeof(long);
|
|
|
|
/* Relocate symbol value */
|
|
sp->value += set_sect_start;
|
|
|
|
set_vectors[length_word_index] = sp->setv_count;
|
|
|
|
/*
|
|
* Relocate vector to final address.
|
|
*/
|
|
for (i = 0; i < sp->setv_count; i++) {
|
|
struct nlist *p = (struct nlist *)
|
|
set_vectors[1+i+length_word_index];
|
|
|
|
set_vectors[1+i+length_word_index] = p->n_value;
|
|
if (building_shared_object) {
|
|
struct relocation_info reloc;
|
|
|
|
bzero(&reloc, sizeof(reloc));
|
|
RELOC_INIT_SEGMENT_RELOC(&reloc);
|
|
RELOC_ADDRESS(&reloc) =
|
|
(1 + i + length_word_index) *
|
|
sizeof(long)
|
|
+ set_sect_start;
|
|
RELOC_TYPE(&reloc) =
|
|
(p->n_type - (N_SETA - N_ABS)) & N_TYPE;
|
|
claim_rrs_segment_reloc(NULL, &reloc);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Reverse the vector.
|
|
*/
|
|
p = &set_vectors[length_word_index + 1];
|
|
q = &set_vectors[length_word_index + sp->setv_count];
|
|
while (p < q) {
|
|
unsigned long tmp = *p;
|
|
*p++ = *q;
|
|
*q-- = tmp;
|
|
}
|
|
|
|
/* Clear terminating entry */
|
|
set_vectors[length_word_index + sp->setv_count + 1] = 0;
|
|
continue;
|
|
}
|
|
|
|
if (sp->def_lsp) {
|
|
if (sp->defined && (sp->defined & ~N_EXT) != N_SETV)
|
|
sp->value = sp->def_lsp->nzlist.nz_value;
|
|
if (sp->so_defined &&
|
|
(sp->def_lsp->entry->flags & E_SECONDCLASS))
|
|
/* Flag second-hand definitions */
|
|
undefined_global_sym_count++;
|
|
if (sp->flags & GS_TRACE)
|
|
printf("symbol %s assigned to location %#x\n",
|
|
sp->name, sp->value);
|
|
}
|
|
|
|
/*
|
|
* If not -r'ing, allocate common symbols in the BSS section.
|
|
*/
|
|
if (building_shared_object && !(link_mode & SYMBOLIC))
|
|
/* No common allocation in shared objects */
|
|
continue;
|
|
|
|
if ((size = sp->common_size) != 0) {
|
|
/*
|
|
* It's a common.
|
|
*/
|
|
if (sp->defined != (N_UNDF + N_EXT))
|
|
errx(1, "%s: common isn't", sp->name);
|
|
|
|
} else if ((size = sp->size) != 0 && sp->defined == N_SIZE) {
|
|
/*
|
|
* It's data from shared object with size info.
|
|
*/
|
|
if (!sp->so_defined)
|
|
errx(1, "%s: Bogus N_SIZE item", sp->name);
|
|
|
|
} else
|
|
/*
|
|
* It's neither
|
|
*/
|
|
continue;
|
|
|
|
|
|
if (relocatable_output && !force_common_definition) {
|
|
sp->defined = 0;
|
|
undefined_global_sym_count++;
|
|
defined_global_sym_count--;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Round up to nearest sizeof (int). I don't know whether
|
|
* this is necessary or not (given that alignment is taken
|
|
* care of later), but it's traditional, so I'll leave it in.
|
|
* Note that if this size alignment is ever removed, ALIGN
|
|
* above will have to be initialized to 1 instead of sizeof
|
|
* (int).
|
|
*/
|
|
|
|
size = PALIGN(size, sizeof(int));
|
|
|
|
while (align < MAX_ALIGNMENT && !(size & align))
|
|
align <<= 1;
|
|
|
|
bss_size = PALIGN(bss_size + data_size + rrs_data_start, align)
|
|
- (data_size + rrs_data_start);
|
|
|
|
sp->value = rrs_data_start + data_size + bss_size;
|
|
if (sp->defined == (N_UNDF | N_EXT))
|
|
sp->defined = N_BSS | N_EXT;
|
|
else {
|
|
sp->so_defined = 0;
|
|
defined_global_sym_count++;
|
|
}
|
|
bss_size += size;
|
|
if (write_map)
|
|
printf("Allocating %s %s: %x at %x\n",
|
|
sp->defined==(N_BSS|N_EXT)?"common":"data",
|
|
sp->name, size, sp->value);
|
|
|
|
} END_EACH_SYMBOL;
|
|
}
|
|
|
|
|
|
/* -------------------------------------------------------------------*/
|
|
|
|
/* Write the output file */
|
|
void
|
|
write_output()
|
|
{
|
|
struct stat statbuf;
|
|
int filemode;
|
|
|
|
if (lstat(output_filename, &statbuf) == 0) {
|
|
if (S_ISREG(statbuf.st_mode))
|
|
(void)unlink(output_filename);
|
|
}
|
|
|
|
outstream = fopen(output_filename, "w");
|
|
if (outstream == NULL)
|
|
err(1, "open: %s", output_filename);
|
|
|
|
if (atexit(cleanup))
|
|
err(1, "atexit");
|
|
|
|
if (fstat(fileno(outstream), &statbuf) < 0)
|
|
err(1, "fstat: %s", output_filename);
|
|
|
|
filemode = statbuf.st_mode;
|
|
|
|
if (S_ISREG(statbuf.st_mode) &&
|
|
chmod(output_filename, filemode & ~0111) == -1)
|
|
err(1, "chmod: %s", output_filename);
|
|
|
|
/* Output the a.out header. */
|
|
write_header();
|
|
|
|
/* Output the text and data segments, relocating as we go. */
|
|
write_text();
|
|
write_data();
|
|
|
|
/* Output the merged relocation info, if requested with `-r'. */
|
|
if (relocatable_output)
|
|
write_rel();
|
|
|
|
/* Output the symbol table (both globals and locals). */
|
|
write_syms();
|
|
|
|
/* Output the RSS section */
|
|
write_rrs();
|
|
|
|
if (chmod (output_filename, filemode | 0111) == -1)
|
|
err(1, "chmod: %s", output_filename);
|
|
|
|
fflush(outstream);
|
|
/* Report I/O error such as disk full. */
|
|
if (ferror(outstream) || fclose(outstream) != 0)
|
|
err(1, "write_output: %s", output_filename);
|
|
outstream = 0;
|
|
}
|
|
|
|
/* Total number of symbols to be written in the output file. */
|
|
static int nsyms;
|
|
|
|
void
|
|
write_header()
|
|
{
|
|
int flags;
|
|
|
|
if (link_mode & SHAREABLE)
|
|
flags = EX_DYNAMIC | EX_PIC;
|
|
else if (pic_code_seen)
|
|
flags = EX_PIC;
|
|
else if (rrs_section_type == RRS_FULL)
|
|
flags = EX_DYNAMIC;
|
|
else
|
|
flags = 0;
|
|
|
|
if (oldmagic && (flags & EX_DPMASK))
|
|
warnx("Cannot set flag in old magic headers\n");
|
|
|
|
N_SET_FLAG (outheader, flags);
|
|
|
|
outheader.a_text = text_size;
|
|
outheader.a_data = data_size;
|
|
outheader.a_bss = bss_size;
|
|
outheader.a_entry = (entry_symbol ? entry_symbol->value
|
|
: text_start + entry_offset);
|
|
|
|
if (strip_symbols == STRIP_ALL)
|
|
nsyms = 0;
|
|
else
|
|
nsyms = global_sym_count + local_sym_count + debugger_sym_count;
|
|
|
|
if (relocatable_output)
|
|
nsyms += set_symbol_count;
|
|
|
|
outheader.a_syms = nsyms * sizeof (struct nlist);
|
|
|
|
if (relocatable_output) {
|
|
outheader.a_trsize = text_reloc_size;
|
|
outheader.a_drsize = data_reloc_size;
|
|
} else {
|
|
outheader.a_trsize = 0;
|
|
outheader.a_drsize = 0;
|
|
}
|
|
|
|
md_swapout_exec_hdr(&outheader);
|
|
mywrite(&outheader, sizeof (struct exec), 1, outstream);
|
|
md_swapin_exec_hdr(&outheader);
|
|
|
|
/*
|
|
* Output whatever padding is required in the executable file
|
|
* between the header and the start of the text.
|
|
*/
|
|
|
|
#ifndef COFF_ENCAPSULATE
|
|
padfile(N_TXTOFF(outheader) - sizeof outheader, outstream);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Relocate the text segment of each input file
|
|
* and write to the output file.
|
|
*/
|
|
void
|
|
write_text()
|
|
{
|
|
|
|
if (trace_files)
|
|
fprintf(stderr, "Copying and relocating text:\n\n");
|
|
|
|
each_full_file(copy_text, 0);
|
|
file_close();
|
|
|
|
if (trace_files)
|
|
fprintf(stderr, "\n");
|
|
|
|
padfile(text_pad, outstream);
|
|
}
|
|
|
|
/*
|
|
* Read the text segment contents of ENTRY, relocate them, and write the
|
|
* result to the output file. If `-r', save the text relocation for later
|
|
* reuse.
|
|
*/
|
|
void
|
|
copy_text(entry)
|
|
struct file_entry *entry;
|
|
{
|
|
register char *bytes;
|
|
register int fd;
|
|
|
|
if (trace_files)
|
|
prline_file_name(entry, stderr);
|
|
|
|
fd = file_open(entry);
|
|
|
|
/* Allocate space for the file's text section */
|
|
bytes = (char *)alloca(entry->header.a_text);
|
|
|
|
/* Deal with relocation information however is appropriate */
|
|
if (entry->textrel == NULL)
|
|
errx(1, "%s: no text relocation", get_file_name(entry));
|
|
|
|
/* Read the text section into core. */
|
|
if (lseek(fd, text_offset(entry), L_SET) == (off_t)-1)
|
|
err(1, "%s: copy_text: lseek", get_file_name(entry));
|
|
if (entry->header.a_text != read(fd, bytes, entry->header.a_text))
|
|
errx(1, "%s: copy_text: premature EOF", get_file_name(entry));
|
|
|
|
/* Relocate the text according to the text relocation. */
|
|
perform_relocation (bytes, entry->header.a_text,
|
|
entry->textrel, entry->ntextrel, entry, 0);
|
|
|
|
/* Write the relocated text to the output file. */
|
|
mywrite(bytes, 1, entry->header.a_text, outstream);
|
|
}
|
|
|
|
/*
|
|
* Relocate the data segment of each input file
|
|
* and write to the output file.
|
|
*/
|
|
|
|
void
|
|
write_data()
|
|
{
|
|
off_t pos;
|
|
|
|
if (trace_files)
|
|
fprintf(stderr, "Copying and relocating data:\n\n");
|
|
|
|
pos = N_DATOFF(outheader) + data_start - rrs_data_start;
|
|
if (fseek(outstream, pos, SEEK_SET) != 0)
|
|
errx(1, "write_data: fseek");
|
|
|
|
each_full_file(copy_data, 0);
|
|
file_close();
|
|
|
|
/*
|
|
* Write out the set element vectors. See digest symbols for
|
|
* description of length of the set vector section.
|
|
*/
|
|
|
|
if (set_vector_count) {
|
|
swap_longs(set_vectors, set_symbol_count + 2*set_vector_count);
|
|
mywrite(set_vectors, set_symbol_count + 2*set_vector_count,
|
|
sizeof (unsigned long), outstream);
|
|
}
|
|
|
|
if (trace_files)
|
|
fprintf(stderr, "\n");
|
|
|
|
padfile(data_pad, outstream);
|
|
}
|
|
|
|
/*
|
|
* Read the data segment contents of ENTRY, relocate them, and write the
|
|
* result to the output file. If `-r', save the data relocation for later
|
|
* reuse. See comments in `copy_text'.
|
|
*/
|
|
void
|
|
copy_data(entry)
|
|
struct file_entry *entry;
|
|
{
|
|
register char *bytes;
|
|
register int fd;
|
|
|
|
if (trace_files)
|
|
prline_file_name (entry, stderr);
|
|
|
|
fd = file_open(entry);
|
|
|
|
bytes = (char *)alloca(entry->header.a_data);
|
|
|
|
if (entry->datarel == NULL)
|
|
errx(1, "%s: no data relocation", get_file_name(entry));
|
|
|
|
if (lseek(fd, text_offset(entry) + entry->header.a_text, L_SET) ==
|
|
(off_t)-1)
|
|
err(1, "%s: copy_data: lseek", get_file_name(entry));
|
|
if (entry->header.a_data != read(fd, bytes, entry->header.a_data))
|
|
errx(1, "%s: copy_data: premature EOF", get_file_name(entry));
|
|
|
|
perform_relocation(bytes, entry->header.a_data,
|
|
entry->datarel, entry->ndatarel, entry, 1);
|
|
|
|
mywrite(bytes, 1, entry->header.a_data, outstream);
|
|
}
|
|
|
|
/*
|
|
* Relocate ENTRY's text or data section contents. DATA is the address of the
|
|
* contents, in core. DATA_SIZE is the length of the contents. PC_RELOCATION
|
|
* is the difference between the address of the contents in the output file
|
|
* and its address in the input file. RELOC is the address of the
|
|
* relocation info, in core. NRELOC says how many there are.
|
|
*/
|
|
|
|
/* HACK: md.c may need access to this */
|
|
int pc_relocation;
|
|
|
|
void
|
|
perform_relocation(data, data_size, reloc, nreloc, entry, dataseg)
|
|
char *data;
|
|
int data_size;
|
|
struct relocation_info *reloc;
|
|
int nreloc;
|
|
struct file_entry *entry;
|
|
int dataseg;
|
|
{
|
|
|
|
register struct relocation_info *r = reloc;
|
|
struct relocation_info *end = reloc + nreloc;
|
|
|
|
int text_relocation = entry->text_start_address;
|
|
int data_relocation = entry->data_start_address - entry->header.a_text;
|
|
int bss_relocation = entry->bss_start_address -
|
|
entry->header.a_text - entry->header.a_data;
|
|
pc_relocation = dataseg?
|
|
entry->data_start_address - entry->header.a_text:
|
|
entry->text_start_address;
|
|
|
|
for (; r < end; r++) {
|
|
int addr = RELOC_ADDRESS(r);
|
|
long addend = md_get_addend(r, data+addr);
|
|
long relocation;
|
|
|
|
/*
|
|
* Loop over the relocations again as we did in
|
|
* consider_relocation(), claiming the reserved RRS
|
|
* relocations.
|
|
*/
|
|
|
|
if (addr >= data_size)
|
|
errx(1, "%s: relocation address out of range",
|
|
get_file_name(entry));
|
|
|
|
if (RELOC_JMPTAB_P(r)) {
|
|
|
|
int symindex = RELOC_SYMBOL(r);
|
|
struct localsymbol *lsp = &entry->symbols[symindex];
|
|
symbol *sp;
|
|
|
|
if (symindex >= entry->nsymbols)
|
|
errx(1, "%s: relocation symbolnum out of range",
|
|
get_file_name(entry));
|
|
|
|
sp = lsp->symbol;
|
|
if (sp == NULL)
|
|
errx(1, "%s: bogus relocation record",
|
|
get_file_name(entry));
|
|
if (sp->alias)
|
|
sp = sp->alias;
|
|
|
|
if (relocatable_output)
|
|
relocation = addend;
|
|
else if (!RELOC_EXTERN_P(r)) {
|
|
relocation = addend +
|
|
data_relocation - text_relocation;
|
|
} else
|
|
relocation = addend +
|
|
claim_rrs_jmpslot(entry, r, sp, addend);
|
|
|
|
} else if (RELOC_BASEREL_P(r)) {
|
|
|
|
int symindex = RELOC_SYMBOL(r);
|
|
struct localsymbol *lsp = &entry->symbols[symindex];
|
|
|
|
if (symindex >= entry->nsymbols)
|
|
errx(1, "%s: relocation symbolnum out of range",
|
|
get_file_name(entry));
|
|
|
|
if (relocatable_output)
|
|
relocation = addend;
|
|
else if (!RELOC_EXTERN_P(r))
|
|
relocation = claim_rrs_internal_gotslot(
|
|
entry, r, lsp, addend);
|
|
else
|
|
relocation = claim_rrs_gotslot(
|
|
entry, r, lsp, addend);
|
|
|
|
} else if (RELOC_EXTERN_P(r)) {
|
|
|
|
int symindex = RELOC_SYMBOL(r);
|
|
symbol *sp;
|
|
|
|
if (symindex >= entry->nsymbols)
|
|
errx(1, "%s: relocation symbolnum out of range",
|
|
get_file_name(entry));
|
|
|
|
sp = entry->symbols[symindex].symbol;
|
|
if (sp == NULL)
|
|
errx(1, "%s: bogus relocation record",
|
|
get_file_name(entry));
|
|
if (sp->alias)
|
|
sp = sp->alias;
|
|
|
|
if (relocatable_output) {
|
|
relocation = addend;
|
|
/*
|
|
* In PIC code, we keep the reference to the
|
|
* external symbol, even if defined now.
|
|
*/
|
|
if (!pic_code_seen)
|
|
relocation += sp->value;
|
|
} else if (sp->defined) {
|
|
if (sp->flags & GS_TRACE) {
|
|
fprintf(stderr,
|
|
"symbol %s defined as %x in %s\n",
|
|
sp->name, sp->defined,
|
|
get_file_name(entry) );
|
|
}
|
|
if (sp == got_symbol) {
|
|
/* Handle _GOT_ refs */
|
|
relocation = addend + sp->value
|
|
+ md_got_reloc(r);
|
|
} else if (building_shared_object) {
|
|
/*
|
|
* Normal (non-PIC) relocation needs
|
|
* to be converted into an RRS reloc
|
|
* when building a shared object.
|
|
*/
|
|
r->r_address += dataseg?
|
|
entry->data_start_address:
|
|
entry->text_start_address;
|
|
relocation = addend;
|
|
if (claim_rrs_reloc(
|
|
entry, r, sp, &relocation))
|
|
continue;
|
|
} else if (sp->defined == N_SIZE) {
|
|
/*
|
|
* If size is known, arrange a
|
|
* run-time copy.
|
|
*/
|
|
if (!sp->size)
|
|
errx(1, "Copy item isn't: %s",
|
|
sp->name);
|
|
|
|
relocation = addend + sp->value;
|
|
r->r_address = sp->value;
|
|
claim_rrs_cpy_reloc(entry, r, sp);
|
|
} else
|
|
/* Plain old relocation */
|
|
relocation = addend + sp->value;
|
|
} else {
|
|
/*
|
|
* If the symbol is undefined, we relocate it
|
|
* in a way similar to -r case. We use an
|
|
* RRS relocation to resolve the symbol at
|
|
* run-time. The r_address field is updated
|
|
* to reflect the changed position in the
|
|
* output file.
|
|
*/
|
|
if (sp->flags & GS_TRACE) {
|
|
fprintf(stderr,
|
|
"symbol %s claims RRS in %s%s\n",
|
|
sp->name, get_file_name(entry),
|
|
(sp->so_defined == (N_TEXT+N_EXT) &&
|
|
sp->flags & GS_HASJMPSLOT)?
|
|
" (JMPSLOT)":"");
|
|
}
|
|
if (sp->so_defined == (N_TEXT+N_EXT) &&
|
|
sp->flags & GS_HASJMPSLOT) {
|
|
/*
|
|
* Claim a jmpslot if one was allocated.
|
|
*
|
|
* At this point, a jmpslot can only
|
|
* result from a shared object reference
|
|
* while `force_alias' is in effect.
|
|
*/
|
|
relocation = addend +
|
|
claim_rrs_jmpslot(
|
|
entry, r, sp, addend);
|
|
} else {
|
|
r->r_address += dataseg?
|
|
entry->data_start_address:
|
|
entry->text_start_address;
|
|
relocation = addend;
|
|
if ((building_shared_object ||
|
|
sp->so_defined) &&
|
|
claim_rrs_reloc(entry, r, sp,
|
|
&relocation))
|
|
continue;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
switch (RELOC_TYPE(r)) {
|
|
case N_TEXT:
|
|
case N_TEXT | N_EXT:
|
|
relocation = addend + text_relocation;
|
|
break;
|
|
|
|
case N_DATA:
|
|
case N_DATA | N_EXT:
|
|
/*
|
|
* A word that points to beginning of the the
|
|
* data section initially contains not 0 but
|
|
* rather the "address" of that section in
|
|
* the input file, which is the length of the
|
|
* file's text.
|
|
*/
|
|
relocation = addend + data_relocation;
|
|
break;
|
|
|
|
case N_BSS:
|
|
case N_BSS | N_EXT:
|
|
/*
|
|
* Similarly, an input word pointing to the
|
|
* beginning of the bss initially contains
|
|
* the length of text plus data of the file.
|
|
*/
|
|
relocation = addend + bss_relocation;
|
|
break;
|
|
|
|
case N_ABS:
|
|
case N_ABS | N_EXT:
|
|
/*
|
|
* Don't know why this code would occur, but
|
|
* apparently it does.
|
|
*/
|
|
break;
|
|
|
|
default:
|
|
errx(1, "%s: nonexternal relocation invalid",
|
|
get_file_name(entry));
|
|
}
|
|
|
|
/*
|
|
* When building a shared object, these segment
|
|
* relocations need a "load address relative"
|
|
* RRS fixup.
|
|
*/
|
|
if (building_shared_object && !RELOC_PCREL_P(r)) {
|
|
r->r_address += dataseg?
|
|
entry->data_start_address:
|
|
entry->text_start_address;
|
|
claim_rrs_segment_reloc(entry, r);
|
|
}
|
|
}
|
|
|
|
if (RELOC_PCREL_P(r))
|
|
relocation -= pc_relocation;
|
|
|
|
md_relocate(r, relocation, data+addr, relocatable_output);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* For relocatable_output only: write out the relocation,
|
|
* relocating the addresses-to-be-relocated.
|
|
*/
|
|
void
|
|
write_rel()
|
|
{
|
|
int count = 0;
|
|
|
|
if (trace_files)
|
|
fprintf(stderr, "Writing text relocation:\n\n");
|
|
|
|
/*
|
|
* Assign each global symbol a sequence number, giving the order
|
|
* in which `write_syms' will write it.
|
|
* This is so we can store the proper symbolnum fields
|
|
* in relocation entries we write.
|
|
*/
|
|
|
|
/* BLECH - Assign number 0 to __DYNAMIC (!! Sun compatibility) */
|
|
|
|
if (dynamic_symbol->flags & GS_REFERENCED)
|
|
dynamic_symbol->symbolnum = count++;
|
|
FOR_EACH_SYMBOL(i, sp) {
|
|
if (sp == dynamic_symbol)
|
|
continue;
|
|
if (sp->warning)
|
|
count += 2;
|
|
if (!(sp->flags & GS_REFERENCED))
|
|
continue;
|
|
sp->symbolnum = count++;
|
|
if (sp->size)
|
|
count++;
|
|
if (sp->alias)
|
|
count++;
|
|
} END_EACH_SYMBOL;
|
|
|
|
if (count != global_sym_count)
|
|
errx(1, "internal error: write_rel: count = %d", count);
|
|
|
|
each_full_file(assign_symbolnums, &count);
|
|
|
|
/* Write out the relocations of all files, remembered from copy_text. */
|
|
each_full_file(coptxtrel, 0);
|
|
|
|
if (trace_files)
|
|
fprintf(stderr, "\nWriting data relocation:\n\n");
|
|
|
|
each_full_file(copdatrel, 0);
|
|
|
|
if (trace_files)
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
|
|
/*
|
|
* Assign symbol ordinal numbers to local symbols in each entry.
|
|
*/
|
|
static void
|
|
assign_symbolnums(entry, countp)
|
|
struct file_entry *entry;
|
|
int *countp;
|
|
{
|
|
struct localsymbol *lsp, *lspend;
|
|
int n = *countp;
|
|
|
|
lspend = entry->symbols + entry->nsymbols;
|
|
|
|
if (discard_locals != DISCARD_ALL)
|
|
/* Count the N_FN symbol for this entry */
|
|
n++;
|
|
|
|
for (lsp = entry->symbols; lsp < lspend; lsp++) {
|
|
if (lsp->flags & LS_WRITE)
|
|
lsp->symbolnum = n++;
|
|
}
|
|
*countp = n;
|
|
}
|
|
|
|
static void
|
|
coptxtrel(entry)
|
|
struct file_entry *entry;
|
|
{
|
|
register struct relocation_info *r, *end;
|
|
register int reloc = entry->text_start_address;
|
|
|
|
r = entry->textrel;
|
|
end = r + entry->ntextrel;
|
|
|
|
for (; r < end; r++) {
|
|
register int symindex;
|
|
struct localsymbol *lsp;
|
|
symbol *sp;
|
|
|
|
RELOC_ADDRESS(r) += reloc;
|
|
|
|
symindex = RELOC_SYMBOL(r);
|
|
lsp = &entry->symbols[symindex];
|
|
|
|
if (!RELOC_EXTERN_P(r)) {
|
|
if (!pic_code_seen)
|
|
continue;
|
|
if (RELOC_BASEREL_P(r))
|
|
RELOC_SYMBOL(r) = lsp->symbolnum;
|
|
continue;
|
|
}
|
|
|
|
if (symindex >= entry->nsymbols)
|
|
errx(1, "%s: relocation symbolnum out of range",
|
|
get_file_name(entry));
|
|
|
|
sp = lsp->symbol;
|
|
|
|
#ifdef N_INDR
|
|
/* Resolve indirection. */
|
|
if ((sp->defined & ~N_EXT) == N_INDR) {
|
|
if (sp->alias == NULL)
|
|
errx(1, "internal error: alias in hyperspace");
|
|
sp = sp->alias;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* If the symbol is now defined, change the external
|
|
* relocation to an internal one.
|
|
*/
|
|
|
|
if (sp->defined) {
|
|
if (!pic_code_seen) {
|
|
RELOC_EXTERN_P(r) = 0;
|
|
RELOC_SYMBOL(r) = (sp->defined & N_TYPE);
|
|
} else
|
|
RELOC_SYMBOL(r) = sp->symbolnum;
|
|
#ifdef RELOC_ADD_EXTRA
|
|
/*
|
|
* If we aren't going to be adding in the
|
|
* value in memory on the next pass of the
|
|
* loader, then we need to add it in from the
|
|
* relocation entry, unless the symbol remains
|
|
* external in our output. Otherwise the work we
|
|
* did in this pass is lost.
|
|
*/
|
|
if (!RELOC_MEMORY_ADD_P(r) && !RELOC_EXTERN_P(r))
|
|
RELOC_ADD_EXTRA(r) += sp->value;
|
|
#endif
|
|
} else
|
|
/*
|
|
* Global symbols come first.
|
|
*/
|
|
RELOC_SYMBOL(r) = sp->symbolnum;
|
|
}
|
|
md_swapout_reloc(entry->textrel, entry->ntextrel);
|
|
mywrite(entry->textrel, entry->ntextrel,
|
|
sizeof(struct relocation_info), outstream);
|
|
}
|
|
|
|
static void
|
|
copdatrel(entry)
|
|
struct file_entry *entry;
|
|
{
|
|
register struct relocation_info *r, *end;
|
|
/*
|
|
* Relocate the address of the relocation. Old address is relative to
|
|
* start of the input file's data section. New address is relative to
|
|
* start of the output file's data section.
|
|
*/
|
|
register int reloc = entry->data_start_address - text_size;
|
|
|
|
r = entry->datarel;
|
|
end = r + entry->ndatarel;
|
|
|
|
for (; r < end; r++) {
|
|
register int symindex;
|
|
symbol *sp;
|
|
int symtype;
|
|
|
|
RELOC_ADDRESS(r) += reloc;
|
|
|
|
if (!RELOC_EXTERN_P(r)) {
|
|
if (RELOC_BASEREL_P(r))
|
|
errx(1, "%s: Unsupported relocation type",
|
|
get_file_name(entry));
|
|
continue;
|
|
}
|
|
|
|
symindex = RELOC_SYMBOL(r);
|
|
sp = entry->symbols[symindex].symbol;
|
|
|
|
if (symindex >= entry->header.a_syms)
|
|
errx(1, "%s: relocation symbolnum out of range",
|
|
get_file_name(entry));
|
|
|
|
#ifdef N_INDR
|
|
/* Resolve indirection. */
|
|
if ((sp->defined & ~N_EXT) == N_INDR) {
|
|
if (sp->alias == NULL)
|
|
errx(1, "internal error: alias in hyperspace");
|
|
sp = sp->alias;
|
|
}
|
|
#endif
|
|
|
|
symtype = sp->defined & N_TYPE;
|
|
|
|
if (!pic_code_seen && ( symtype == N_BSS ||
|
|
symtype == N_DATA ||
|
|
symtype == N_TEXT ||
|
|
symtype == N_ABS)) {
|
|
RELOC_EXTERN_P(r) = 0;
|
|
RELOC_SYMBOL(r) = symtype;
|
|
} else
|
|
/*
|
|
* Global symbols come first.
|
|
*/
|
|
RELOC_SYMBOL(r) =
|
|
entry->symbols[symindex].symbol->symbolnum;
|
|
}
|
|
md_swapout_reloc(entry->datarel, entry->ndatarel);
|
|
mywrite(entry->datarel, entry->ndatarel,
|
|
sizeof(struct relocation_info), outstream);
|
|
}
|
|
|
|
void write_file_syms __P((struct file_entry *, int *));
|
|
void write_string_table __P((void));
|
|
|
|
/* Offsets and current lengths of symbol and string tables in output file. */
|
|
|
|
static int symtab_offset;
|
|
static int symtab_len;
|
|
|
|
/* Address in output file where string table starts. */
|
|
static int strtab_offset;
|
|
|
|
/* Offset within string table
|
|
where the strings in `strtab_vector' should be written. */
|
|
static int strtab_len;
|
|
|
|
/* Total size of string table strings allocated so far,
|
|
including strings in `strtab_vector'. */
|
|
static int strtab_size;
|
|
|
|
/* Vector whose elements are strings to be added to the string table. */
|
|
static char **strtab_vector;
|
|
|
|
/* Vector whose elements are the lengths of those strings. */
|
|
static int *strtab_lens;
|
|
|
|
/* Index in `strtab_vector' at which the next string will be stored. */
|
|
static int strtab_index;
|
|
|
|
/*
|
|
* Add the string NAME to the output file string table. Record it in
|
|
* `strtab_vector' to be output later. Return the index within the string
|
|
* table that this string will have.
|
|
*/
|
|
|
|
static int
|
|
assign_string_table_index(name)
|
|
char *name;
|
|
{
|
|
register int index = strtab_size;
|
|
register int len = strlen(name) + 1;
|
|
|
|
strtab_size += len;
|
|
strtab_vector[strtab_index] = name;
|
|
strtab_lens[strtab_index++] = len;
|
|
|
|
return index;
|
|
}
|
|
|
|
/*
|
|
* Write the contents of `strtab_vector' into the string table. This is done
|
|
* once for each file's local&debugger symbols and once for the global
|
|
* symbols.
|
|
*/
|
|
void
|
|
write_string_table()
|
|
{
|
|
register int i;
|
|
|
|
if (fseek(outstream, strtab_offset + strtab_len, SEEK_SET) != 0)
|
|
err(1, "write_string_table: %s: fseek", output_filename);
|
|
|
|
for (i = 0; i < strtab_index; i++) {
|
|
mywrite(strtab_vector[i], 1, strtab_lens[i], outstream);
|
|
strtab_len += strtab_lens[i];
|
|
}
|
|
}
|
|
|
|
/* Write the symbol table and string table of the output file. */
|
|
|
|
void
|
|
write_syms()
|
|
{
|
|
/* Number of symbols written so far. */
|
|
int syms_written = 0;
|
|
struct nlist nl;
|
|
|
|
/*
|
|
* Buffer big enough for all the global symbols. One extra struct
|
|
* for each indirect symbol to hold the extra reference following.
|
|
*/
|
|
struct nlist *buf = (struct nlist *)
|
|
alloca(global_sym_count * sizeof(struct nlist));
|
|
/* Pointer for storing into BUF. */
|
|
register struct nlist *bufp = buf;
|
|
|
|
/* Size of string table includes the bytes that store the size. */
|
|
strtab_size = sizeof strtab_size;
|
|
|
|
symtab_offset = N_SYMOFF(outheader);
|
|
symtab_len = 0;
|
|
strtab_offset = N_STROFF(outheader);
|
|
strtab_len = strtab_size;
|
|
|
|
if (strip_symbols == STRIP_ALL)
|
|
return;
|
|
|
|
/* First, write out the global symbols. */
|
|
|
|
/*
|
|
* Allocate two vectors that record the data to generate the string
|
|
* table from the global symbols written so far. This must include
|
|
* extra space for the references following indirect outputs.
|
|
*/
|
|
|
|
strtab_vector = (char **)alloca((global_sym_count) * sizeof(char *));
|
|
strtab_lens = (int *)alloca((global_sym_count) * sizeof(int));
|
|
strtab_index = 0;
|
|
|
|
/*
|
|
* __DYNAMIC symbol *must* be first for Sun compatibility, as Sun's
|
|
* ld.so reads the shared object's first symbol. This means that
|
|
* (Sun's) shared libraries cannot be stripped! (We only assume
|
|
* that __DYNAMIC is the first item in the data segment)
|
|
*
|
|
* If defined (ie. not relocatable_output), make it look
|
|
* like an internal symbol.
|
|
*/
|
|
if (dynamic_symbol->flags & GS_REFERENCED) {
|
|
nl.n_other = 0;
|
|
nl.n_desc = 0;
|
|
nl.n_type = dynamic_symbol->defined;
|
|
if (nl.n_type == N_UNDF)
|
|
nl.n_type |= N_EXT;
|
|
else
|
|
nl.n_type &= ~N_EXT;
|
|
nl.n_value = dynamic_symbol->value;
|
|
nl.n_un.n_strx = assign_string_table_index(dynamic_symbol->name);
|
|
*bufp++ = nl;
|
|
syms_written++;
|
|
}
|
|
|
|
/* Scan the symbol hash table, bucket by bucket. */
|
|
|
|
FOR_EACH_SYMBOL(i, sp) {
|
|
|
|
if (sp == dynamic_symbol)
|
|
/* Already dealt with above */
|
|
continue;
|
|
|
|
/*
|
|
* Propagate N_WARNING symbols.
|
|
*/
|
|
if ((relocatable_output || building_shared_object)
|
|
&& sp->warning) {
|
|
nl.n_type = N_WARNING;
|
|
nl.n_un.n_strx = assign_string_table_index(sp->warning);
|
|
nl.n_value = 0;
|
|
nl.n_other = 0;
|
|
nl.n_desc = 0;
|
|
*bufp++ = nl;
|
|
syms_written++;
|
|
|
|
nl.n_type = N_UNDF + N_EXT;
|
|
nl.n_un.n_strx = assign_string_table_index(sp->name);
|
|
nl.n_value = 0;
|
|
nl.n_other = 0;
|
|
nl.n_desc = 0;
|
|
*bufp++ = nl;
|
|
syms_written++;
|
|
}
|
|
|
|
if (!(sp->flags & GS_REFERENCED))
|
|
/* Came from shared object but was not used */
|
|
continue;
|
|
|
|
if (sp->so_defined || (sp->alias && sp->alias->so_defined))
|
|
/*
|
|
* Definition came from shared object,
|
|
* don't mention it here
|
|
*/
|
|
continue;
|
|
|
|
if (!sp->defined && !relocatable_output) {
|
|
/*
|
|
* We're building a shared object and there
|
|
* are still undefined symbols. Don't output
|
|
* these, symbol was discounted in digest_pass1()
|
|
* (they are in the RRS symbol table).
|
|
*/
|
|
if (building_shared_object)
|
|
continue;
|
|
if (!(sp->flags & GS_WEAK))
|
|
warnx("symbol %s remains undefined", sp->name);
|
|
}
|
|
|
|
if (syms_written >= global_sym_count)
|
|
errx(1,
|
|
"internal error: number of symbols exceeds alloc'd %d",
|
|
global_sym_count);
|
|
|
|
/*
|
|
* Construct a `struct nlist' for the symbol.
|
|
*/
|
|
nl.n_other = 0;
|
|
nl.n_desc = 0;
|
|
|
|
if (sp->defined > 1) {
|
|
/*
|
|
* defined with known type
|
|
*/
|
|
if (!relocatable_output && !building_shared_object &&
|
|
sp->alias && sp->alias->defined > 1) {
|
|
/*
|
|
* If the target of an indirect symbol has
|
|
* been defined and we are outputting an
|
|
* executable, resolve the indirection; it's
|
|
* no longer needed.
|
|
*/
|
|
nl.n_type = sp->alias->defined;
|
|
nl.n_value = sp->alias->value;
|
|
nl.n_other = N_OTHER(0, sp->alias->aux);
|
|
} else {
|
|
int bind = 0;
|
|
|
|
if (sp->defined == N_SIZE)
|
|
nl.n_type = N_DATA | N_EXT;
|
|
else
|
|
nl.n_type = sp->defined;
|
|
if (nl.n_type == (N_INDR|N_EXT) &&
|
|
sp->value != 0)
|
|
errx(1, "%s: N_INDR has value %#x",
|
|
sp->name, sp->value);
|
|
nl.n_value = sp->value;
|
|
if (sp->def_lsp)
|
|
bind = N_BIND(&sp->def_lsp->nzlist.nlist);
|
|
nl.n_other = N_OTHER(bind, sp->aux);
|
|
}
|
|
|
|
} else if (sp->common_size) {
|
|
/*
|
|
* defined as common but not allocated,
|
|
* happens only with -r and not -d, write out
|
|
* a common definition.
|
|
*
|
|
* common condition needs to be before undefined
|
|
* condition because unallocated commons are set
|
|
* undefined in digest_symbols.
|
|
*/
|
|
nl.n_type = N_UNDF | N_EXT;
|
|
nl.n_value = sp->common_size;
|
|
} else if (!sp->defined) {
|
|
/* undefined -- legit only if -r */
|
|
nl.n_type = N_UNDF | N_EXT;
|
|
nl.n_value = 0;
|
|
} else
|
|
errx(1,
|
|
"internal error: %s defined in mysterious way",
|
|
sp->name);
|
|
|
|
/*
|
|
* Allocate string table space for the symbol name.
|
|
*/
|
|
|
|
nl.n_un.n_strx = assign_string_table_index(sp->name);
|
|
|
|
/* Output to the buffer and count it. */
|
|
|
|
*bufp++ = nl;
|
|
syms_written++;
|
|
|
|
/*
|
|
* Write second symbol of an alias pair.
|
|
*/
|
|
if (nl.n_type == N_INDR + N_EXT) {
|
|
if (sp->alias == NULL)
|
|
errx(1, "internal error: alias in hyperspace");
|
|
nl.n_type = N_UNDF + N_EXT;
|
|
nl.n_un.n_strx =
|
|
assign_string_table_index(sp->alias->name);
|
|
nl.n_value = 0;
|
|
nl.n_other = 0;
|
|
nl.n_desc = 0;
|
|
*bufp++ = nl;
|
|
syms_written++;
|
|
}
|
|
|
|
/*
|
|
* Write N_SIZE symbol for a symbol with a known size.
|
|
*/
|
|
if (relocatable_output && sp->size) {
|
|
nl.n_type = N_SIZE + N_EXT;
|
|
nl.n_un.n_strx = assign_string_table_index(sp->name);
|
|
nl.n_value = sp->size;
|
|
nl.n_other = 0;
|
|
nl.n_desc = 0;
|
|
*bufp++ = nl;
|
|
syms_written++;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printf("writesym(#%d): %s, type %x\n", syms_written, sp->name, sp->defined);
|
|
#endif
|
|
} END_EACH_SYMBOL;
|
|
|
|
if (syms_written != strtab_index || strtab_index != global_sym_count)
|
|
errx(1, "internal error: wrong number (%d) of global symbols "
|
|
"written into output file, should be %d",
|
|
syms_written, global_sym_count);
|
|
|
|
/* Output the buffer full of `struct nlist's. */
|
|
|
|
if (fseek(outstream, symtab_offset + symtab_len, SEEK_SET) != 0)
|
|
err(1, "write_syms: fseek");
|
|
md_swapout_symbols(buf, bufp - buf);
|
|
mywrite(buf, bufp - buf, sizeof(struct nlist), outstream);
|
|
symtab_len += sizeof(struct nlist) * (bufp - buf);
|
|
|
|
/* Write the strings for the global symbols. */
|
|
write_string_table();
|
|
|
|
/* Write the local symbols defined by the various files. */
|
|
each_file(write_file_syms, (void *)&syms_written);
|
|
file_close();
|
|
|
|
if (syms_written != nsyms)
|
|
errx(1, "internal error: wrong number of symbols (%d) "
|
|
"written into output file, should be %d",
|
|
syms_written, nsyms);
|
|
|
|
if (symtab_offset + symtab_len != strtab_offset)
|
|
errx(1,
|
|
"internal error: inconsistent symbol table length: %d vs %s",
|
|
symtab_offset + symtab_len, strtab_offset);
|
|
|
|
if (fseek(outstream, strtab_offset, SEEK_SET) != 0)
|
|
err(1, "write_syms: fseek");
|
|
strtab_size = md_swap_long(strtab_size);
|
|
mywrite(&strtab_size, sizeof(int), 1, outstream);
|
|
}
|
|
|
|
|
|
/*
|
|
* Write the local and debugger symbols of file ENTRY. Increment
|
|
* *SYMS_WRITTEN_ADDR for each symbol that is written.
|
|
*/
|
|
|
|
/*
|
|
* Note that we do not combine identical names of local symbols. dbx or gdb
|
|
* would be confused if we did that.
|
|
*/
|
|
void
|
|
write_file_syms(entry, syms_written_addr)
|
|
struct file_entry *entry;
|
|
int *syms_written_addr;
|
|
{
|
|
struct localsymbol *lsp, *lspend;
|
|
|
|
/* Upper bound on number of syms to be written here. */
|
|
int max_syms = entry->nsymbols + 1;
|
|
|
|
/*
|
|
* Buffer to accumulate all the syms before writing them. It has one
|
|
* extra slot for the local symbol we generate here.
|
|
*/
|
|
struct nlist *buf = (struct nlist *)
|
|
alloca(max_syms * sizeof(struct nlist));
|
|
|
|
register struct nlist *bufp = buf;
|
|
|
|
if (entry->flags & E_DYNAMIC)
|
|
return;
|
|
|
|
/*
|
|
* Make tables that record, for each symbol, its name and its name's
|
|
* length. The elements are filled in by `assign_string_table_index'.
|
|
*/
|
|
|
|
strtab_vector = (char **)alloca(max_syms * sizeof(char *));
|
|
strtab_lens = (int *)alloca(max_syms * sizeof(int));
|
|
strtab_index = 0;
|
|
|
|
/* Generate a local symbol for the start of this file's text. */
|
|
|
|
if (discard_locals != DISCARD_ALL) {
|
|
struct nlist nl;
|
|
|
|
nl.n_type = N_FN | N_EXT;
|
|
nl.n_un.n_strx =
|
|
assign_string_table_index(entry->local_sym_name);
|
|
nl.n_value = entry->text_start_address;
|
|
nl.n_desc = 0;
|
|
nl.n_other = 0;
|
|
*bufp++ = nl;
|
|
(*syms_written_addr)++;
|
|
}
|
|
/* Read the file's string table. */
|
|
|
|
entry->strings = (char *)alloca(entry->string_size);
|
|
read_entry_strings(file_open(entry), entry);
|
|
|
|
lspend = entry->symbols + entry->nsymbols;
|
|
|
|
for (lsp = entry->symbols; lsp < lspend; lsp++) {
|
|
register struct nlist *p = &lsp->nzlist.nlist;
|
|
char *name;
|
|
|
|
if (!(lsp->flags & LS_WRITE))
|
|
continue;
|
|
|
|
if (p->n_un.n_strx == 0)
|
|
name = NULL;
|
|
else if (!(lsp->flags & LS_RENAME))
|
|
name = p->n_un.n_strx + entry->strings;
|
|
else {
|
|
char *cp = p->n_un.n_strx + entry->strings;
|
|
name = (char *)alloca(
|
|
strlen(entry->local_sym_name) +
|
|
strlen(cp) + 2 );
|
|
(void)sprintf(name, "%s.%s", entry->local_sym_name, cp);
|
|
}
|
|
|
|
/*
|
|
* If this symbol has a name, allocate space for it
|
|
* in the output string table.
|
|
*/
|
|
|
|
if (name)
|
|
p->n_un.n_strx = assign_string_table_index(name);
|
|
|
|
/* Output this symbol to the buffer and count it. */
|
|
|
|
*bufp++ = *p;
|
|
(*syms_written_addr)++;
|
|
}
|
|
|
|
/* All the symbols are now in BUF; write them. */
|
|
|
|
if (fseek(outstream, symtab_offset + symtab_len, SEEK_SET) != 0)
|
|
err(1, "write local symbols: fseek");
|
|
md_swapout_symbols(buf, bufp - buf);
|
|
mywrite(buf, bufp - buf, sizeof(struct nlist), outstream);
|
|
symtab_len += sizeof(struct nlist) * (bufp - buf);
|
|
|
|
/*
|
|
* Write the string-table data for the symbols just written, using
|
|
* the data in vectors `strtab_vector' and `strtab_lens'.
|
|
*/
|
|
|
|
write_string_table();
|
|
entry->strings = 0; /* Since it will disappear anyway. */
|
|
}
|
|
|
|
/*
|
|
* Parse the string ARG using scanf format FORMAT, and return the result.
|
|
* If it does not parse, report fatal error
|
|
* generating the error message using format string ERROR and ARG as arg.
|
|
*/
|
|
|
|
static int
|
|
parse(arg, format, error)
|
|
char *arg, *format, *error;
|
|
{
|
|
int x;
|
|
|
|
if (1 != sscanf(arg, format, &x))
|
|
errx(1, error, arg);
|
|
return x;
|
|
}
|
|
|
|
/*
|
|
* Output COUNT*ELTSIZE bytes of data at BUF to the descriptor FD.
|
|
*/
|
|
void
|
|
mywrite(buf, count, eltsize, fd)
|
|
void *buf;
|
|
int count;
|
|
int eltsize;
|
|
FILE *fd;
|
|
{
|
|
|
|
if (fwrite(buf, eltsize, count, fd) != count)
|
|
err(1, "write");
|
|
}
|
|
|
|
static void
|
|
cleanup()
|
|
{
|
|
struct stat statbuf;
|
|
|
|
if (outstream == 0)
|
|
return;
|
|
|
|
if (fstat(fileno(outstream), &statbuf) == 0) {
|
|
if (S_ISREG(statbuf.st_mode))
|
|
(void)unlink(output_filename);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Output PADDING zero-bytes to descriptor FD.
|
|
* PADDING may be negative; in that case, do nothing.
|
|
*/
|
|
void
|
|
padfile(padding, fd)
|
|
int padding;
|
|
FILE *fd;
|
|
{
|
|
register char *buf;
|
|
if (padding <= 0)
|
|
return;
|
|
|
|
buf = (char *)alloca(padding);
|
|
bzero(buf, padding);
|
|
mywrite(buf, padding, 1, fd);
|
|
}
|