mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-16 10:20:30 +00:00
699e1b82fb
handling of errors through the standard err() and warn() more fixes for Geoff Rehmet's NULL pointer bug. fixes NULL pointer bugs when linking mono and nested X servers. supports a `-nostdlib' option. accept object files without a symbol table don't attempt dynamic linking when `-A' is given a few variable names have chaged (desc -> fd), and the formatting has changed which should make it much easier to track his sources. I tested 'make world' for /usr/src and X twice with these changes.
3514 lines
87 KiB
C
3514 lines
87 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.21 1994/02/17 03:57:00 davidg 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. */
|
||
int outdesc; /* 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 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.
|
||
*/
|
||
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 warning_count; /* # of warning symbols encountered. */
|
||
|
||
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;
|
||
warning_count = 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);
|
||
|
||
/* Prepare for the run-time linking support. */
|
||
init_rrs();
|
||
|
||
/*
|
||
* 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, "-T argument not multiple of page size, with sharable output");
|
||
|
||
/* 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 number", 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) failed", 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) failed",
|
||
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 failed",
|
||
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 failed",
|
||
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;
|
||
}
|
||
entry->flags |= E_DYNAMIC;
|
||
if (entry->superfile || rrs_add_shobj(entry))
|
||
read_shared_object(fd, entry);
|
||
else
|
||
entry->flags |= E_SCRAPPED;
|
||
} else {
|
||
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 *name = p->n_un.n_strx + entry->strings;
|
||
|
||
/* Grab the next entry. */
|
||
p++;
|
||
if (p->n_type != (N_UNDF | N_EXT)) {
|
||
warnx(
|
||
"%s: Warning symbol without external reference following.",
|
||
get_file_name(entry));
|
||
make_executable = 0;
|
||
p--; /* Process normally. */
|
||
} else {
|
||
symbol *sp;
|
||
char *sname = p->n_un.n_strx + entry->strings;
|
||
/* Deal with the warning symbol. */
|
||
enter_global_ref(lsp,
|
||
p->n_un.n_strx + entry->strings, entry);
|
||
sp = getsym (sname);
|
||
sp->warning = (char *)xmalloc(strlen(name)+1);
|
||
strcpy (sp->warning, name);
|
||
warning_count++;
|
||
}
|
||
} 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)) {
|
||
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 (entry->flags & E_DYNAMIC) {
|
||
lsp->next = sp->sorefs;
|
||
sp->sorefs = lsp;
|
||
|
||
/*
|
||
* 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...
|
||
*/
|
||
sp->common_size = 0;
|
||
sp->defined = 0;
|
||
}
|
||
|
||
/*
|
||
* Handle size information in shared objects.
|
||
*/
|
||
if (nzp->nz_size > sp->size)
|
||
sp->size = nzp->nz_size;
|
||
|
||
lsp->symbol = sp;
|
||
return;
|
||
}
|
||
|
||
lsp->next = sp->refs;
|
||
sp->refs = lsp;
|
||
lsp->symbol = sp;
|
||
|
||
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;
|
||
}
|
||
|
||
#ifdef N_SIZE
|
||
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
|
||
#endif
|
||
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 (oldref && !olddef)
|
||
/*
|
||
* It used to be undefined and we're defining it.
|
||
*/
|
||
undefined_global_sym_count--;
|
||
if (undefined_global_sym_count < 0)
|
||
errx(1,
|
||
"internal error: enter_glob_ref: undefined_global_sym_count = %d",
|
||
undefined_global_sym_count);
|
||
|
||
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 (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;
|
||
|
||
default:
|
||
reftype = "I don't know this type";
|
||
break;
|
||
}
|
||
|
||
fprintf(stderr, "symbol %s %s in ", sp->name, 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;
|
||
|
||
if (relocatable_output)
|
||
/* We write out the original N_SET* symbols */
|
||
global_sym_count += size_sym_count;
|
||
|
||
#ifdef DEBUG
|
||
printf("global symbols %d (defined %d, undefined %d, aliases %d), locals: %d, \
|
||
debug symbols: %d, set_symbols %d\n",
|
||
global_sym_count,
|
||
defined_global_sym_count, undefined_global_sym_count, global_alias_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) {
|
||
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_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 (defs++ && sp->value != p->n_value
|
||
&& entry_symbol/*XXX*/) {
|
||
sp->mult_defs = 1;
|
||
multiple_def_count++;
|
||
}
|
||
sp->def_nlist = p;
|
||
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;
|
||
}
|
||
|
||
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_nlist = p;
|
||
lsp->entry->flags |= E_SYMBOLS_USED;
|
||
sp->so_defined = type;
|
||
sp->aux = N_AUX(p);
|
||
if (sp->flags & GS_REFERENCED)
|
||
undefined_global_sym_count--;
|
||
else
|
||
sp->flags |= GS_REFERENCED;
|
||
#ifdef DEBUG
|
||
printf("shr: %s gets defined to %x with value %x\n", sp->name, type, sp->value);
|
||
#endif
|
||
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;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
} 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;
|
||
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: internal error, sp==NULL",
|
||
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)
|
||
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.
|
||
*/
|
||
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_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->defined && sp->def_nlist &&
|
||
((sp->defined & ~N_EXT) != N_SETV))
|
||
sp->value = sp->def_nlist->n_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 (!(size & align))
|
||
align <<= 1;
|
||
|
||
align = align > MAX_ALIGNMENT ? MAX_ALIGNMENT : align;
|
||
|
||
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);
|
||
}
|
||
|
||
outdesc = open(output_filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
||
if (outdesc < 0)
|
||
err(1, "open: %s", output_filename);
|
||
|
||
if (atexit(cleanup))
|
||
err(1, "atexit");
|
||
|
||
if (fstat (outdesc, &statbuf) < 0)
|
||
err(1, "fstat: %s", output_filename);
|
||
|
||
filemode = statbuf.st_mode;
|
||
|
||
chmod (output_filename, filemode & ~0111);
|
||
|
||
/* 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);
|
||
|
||
close(outdesc);
|
||
outdesc = 0;
|
||
}
|
||
|
||
/* Total number of symbols to be written in the output file. */
|
||
static int nsyms;
|
||
|
||
void
|
||
write_header()
|
||
{
|
||
int flags = (rrs_section_type == RRS_FULL) ? EX_DYNAMIC : 0;
|
||
|
||
if (oldmagic && (flags & EX_DYNAMIC))
|
||
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, outdesc);
|
||
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, outdesc);
|
||
#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, outdesc);
|
||
}
|
||
|
||
/*
|
||
* 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 in text section", 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, outdesc);
|
||
}
|
||
|
||
/*
|
||
* 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 (lseek(outdesc, pos, L_SET) != pos)
|
||
errx(1, "write_data: failed to lseek to data offset");
|
||
|
||
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), outdesc);
|
||
}
|
||
|
||
if (trace_files)
|
||
fprintf(stderr, "\n");
|
||
|
||
padfile(data_pad, outdesc);
|
||
}
|
||
|
||
/*
|
||
* 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 in data section", get_file_name(entry));
|
||
|
||
perform_relocation(bytes, entry->header.a_data,
|
||
entry->datarel, entry->ndatarel, entry, 1);
|
||
|
||
mywrite(bytes, 1, entry->header.a_data, outdesc);
|
||
}
|
||
|
||
/*
|
||
* 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->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->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->jmpslot_offset != -1)?
|
||
" (JMPSLOT)":"");
|
||
}
|
||
if (sp->so_defined == (N_TEXT+N_EXT) &&
|
||
sp->jmpslot_offset != -1) {
|
||
/*
|
||
* 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 (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 && (sp->flags & GS_REFERENCED)) {
|
||
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), outdesc);
|
||
}
|
||
|
||
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), outdesc);
|
||
}
|
||
|
||
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 symbol_table_offset;
|
||
static int symbol_table_len;
|
||
|
||
/* Address in output file where string table starts. */
|
||
static int string_table_offset;
|
||
|
||
/* Offset within string table
|
||
where the strings in `strtab_vector' should be written. */
|
||
static int string_table_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;
|
||
}
|
||
|
||
FILE *outstream = (FILE *)0;
|
||
|
||
/*
|
||
* 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 (lseek(outdesc, string_table_offset + string_table_len, 0) ==
|
||
(off_t)-1)
|
||
err(1, "write_string_table: %s: lseek", output_filename);
|
||
|
||
if (!outstream)
|
||
outstream = fdopen(outdesc, "w");
|
||
|
||
for (i = 0; i < strtab_index; i++) {
|
||
fwrite (strtab_vector[i], 1, strtab_lens[i], outstream);
|
||
string_table_len += strtab_lens[i];
|
||
}
|
||
|
||
fflush(outstream);
|
||
|
||
/* Report I/O error such as disk full. */
|
||
if (ferror(outstream))
|
||
err(1, "write_string_table: %s", output_filename);
|
||
}
|
||
|
||
/* 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;
|
||
|
||
symbol_table_offset = N_SYMOFF(outheader);
|
||
symbol_table_len = 0;
|
||
string_table_offset = N_STROFF(outheader);
|
||
string_table_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;
|
||
|
||
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)
|
||
warnx("symbol %s remains undefined", sp->name);
|
||
continue;
|
||
}
|
||
|
||
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 {
|
||
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;
|
||
nl.n_other = N_OTHER(0, 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 (lseek(outdesc, symbol_table_offset + symbol_table_len, 0) ==
|
||
(off_t)-1)
|
||
err(1, "write_syms: lseek");
|
||
md_swapout_symbols(buf, bufp - buf);
|
||
mywrite(buf, bufp - buf, sizeof(struct nlist), outdesc);
|
||
symbol_table_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 (symbol_table_offset + symbol_table_len != string_table_offset)
|
||
errx(1,
|
||
"internal error: inconsistent symbol table length: %d vs %s",
|
||
symbol_table_offset + symbol_table_len, string_table_offset);
|
||
|
||
lseek(outdesc, string_table_offset, 0);
|
||
strtab_size = md_swap_long(strtab_size);
|
||
mywrite(&strtab_size, sizeof(int), 1, outdesc);
|
||
}
|
||
|
||
|
||
/*
|
||
* 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. */
|
||
|
||
lseek(outdesc, symbol_table_offset + symbol_table_len, 0);
|
||
md_swapout_symbols(buf, bufp - buf);
|
||
mywrite(buf, bufp - buf, sizeof(struct nlist), outdesc);
|
||
symbol_table_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;
|
||
int fd;
|
||
{
|
||
register int val;
|
||
register int bytes = count * eltsize;
|
||
|
||
while (bytes > 0) {
|
||
val = write(fd, buf, bytes);
|
||
if (val <= 0)
|
||
err(1, "write: %s", output_filename);
|
||
buf += val;
|
||
bytes -= val;
|
||
}
|
||
}
|
||
|
||
static void
|
||
cleanup()
|
||
{
|
||
struct stat statbuf;
|
||
|
||
if (outdesc <= 0)
|
||
return;
|
||
|
||
if (fstat(outdesc, &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;
|
||
int fd;
|
||
{
|
||
register char *buf;
|
||
if (padding <= 0)
|
||
return;
|
||
|
||
buf = (char *)alloca(padding);
|
||
bzero(buf, padding);
|
||
mywrite(buf, padding, 1, fd);
|
||
}
|