* This code is derived from software copyrighted by the Free Software
* Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
* Modified 1993 by Paul Kranenburg, Erasmus University
static char sccsid
[] = "@(#)ld.c 6.10 (Berkeley) 5/22/91";
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)
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.11 1993/11/05 12:47:11 pk Exp $
/* Define how to initialize system-dependent header fields. */
#include <sys/resource.h>
int building_shared_object
;
/* 1 => write relocation into output file so can re-input it later. */
/* Non zero means to create the output executable. */
/* Cleared by nonfatal errors. */
/* Force the executable to be output, even if there are non-fatal errors */
/* 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
;
* Which symbols should be stripped (omitted from the output): none, all, or
STRIP_NONE
, STRIP_ALL
, STRIP_DEBUGGER
* Which local symbols should be omitted: none, all, or those starting with L.
* This is irrelevant if STRIP_NONE.
DISCARD_NONE
, DISCARD_ALL
, DISCARD_L
/* Nonzero means print names of input files as processed. */
/* Magic number to use for the output file, set by switch. */
* `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.
* 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.
if ((progname
= strrchr(argv
[0], '/')) == NULL
)
/* Added this to stop ld core-dumping on very large .o files. */
/* Get rid of any avoidable limit on stack size. */
/* Set the stack limit huge so that alloca does not fail. */
getrlimit(RLIMIT_STACK
, &rlim
);
rlim
.rlim_cur
= rlim
.rlim_max
;
setrlimit(RLIMIT_STACK
, &rlim
);
#endif /* RLIMIT_STACK */
/* Clear the cumulative info on the output file. */
/* Initialize the data about options. */
strip_symbols
= STRIP_NONE
;
discard_locals
= DISCARD_NONE
;
force_common_definition
= 0;
Tdata_flag_specified
= 0;
soversion
= LD_VERSION_BSD
;
/* Initialize the cumulative counts of symbols. */
non_L_local_sym_count
= 0;
undefined_global_sym_count
= 0;
common_defined_global_count
= 0;
/* Keep a list of symbols referenced from the command line */
= (struct glosym
**) xmalloc(cl_refs_allocated
* sizeof(struct glosym
*));
/* Completely decode ARGV. */
decode_command(argc
, argv
);
(!relocatable_output
&& (link_mode
& SHAREABLE
));
/* Create the symbols `etext', `edata' and `end'. */
symtab_init(relocatable_output
);
/* Prepare for the run-time linking support. */
* 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.
md_init_header(&outheader
, magic
, 0);
text_size
= sizeof(struct exec
);
text_size
-= N_TXTOFF(outheader
);
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.
/* Compute where each file's sections go, and relocate 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. */
/* Write the output file. */
if (make_executable
|| force_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
if (!strcmp(&arg
[2], "ssert"))
if (!strcmp(&arg
[2], "static"))
if (!strcmp(&arg
[2], "dynamic"))
if (!strcmp(&arg
[2], "text"))
if (!strcmp(&arg
[2], "data"))
* Process the command arguments, setting up file_table with an entry for
* each input file, and setting variables according to the options.
decode_command(argc
, argv
)
register struct file_entry
*p
;
output_filename
= "a.out";
* First compute number_of_files so we know how long to make
* Also process most options completely.
for (i
= 1; i
< argc
; i
++) {
register int code
= classify_arg(argv
[i
]);
fatal("no argument following %s\n", argv
[i
]);
decode_option(argv
[i
], argv
[i
+ 1]);
if (argv
[i
][1] == 'l' || argv
[i
][1] == 'A')
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
++) {
register int code
= classify_arg(argv
[i
]);
p
->local_sym_name
= argv
[i
];
if (strcmp(string
, "static") == 0)
else if (strcmp(string
, "dynamic") == 0)
else if (strcmp(string
, "symbolic") == 0)
else if (strcmp(string
, "forcearchive") == 0)
link_mode
|= FORCEARCHIVE
;
else if (strcmp(string
, "shareable") == 0)
fatal("-A specified before an input file other than the first");
p
->local_sym_name
= string
;
p
->local_sym_name
= concat("-l", string
, "");
if (link_mode
& DYNAMIC
&& !relocatable_output
)
p
->search_dynamic_flag
= 1;
/* Now check some option settings for consistency. */
&& (text_start
- text_start_alignment
) & (page_size
- 1))
fatal("-T argument not multiple of page size, with sharable output");
/* Append the standard search directories to the user-specified ones. */
std_search_dirs(getenv("LD_LIBRARY_PATH"));
for (ptr
= cmdline_references
;
ptr
< cmdline_references
+ cl_refs_allocated
&& *ptr
;
if (ptr
>= cmdline_references
+ cl_refs_allocated
- 1) {
int diff
= ptr
- cmdline_references
;
cmdline_references
= (struct glosym
**)
xrealloc(cmdline_references
,
cl_refs_allocated
* sizeof(struct glosym
*));
ptr
= cmdline_references
+ diff
;
*ptr
= (struct glosym
*) 0;
set_element_prefixed_p(name
)
struct string_list_element
*p
;
for (p
= set_element_prefixes
; p
; p
= p
->next
) {
for (i
= 0; p
->str
[i
] != '\0' && (p
->str
[i
] == name
[i
]); i
++);
* 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
register char *swt
, *arg
;
/* We get Bstatic from gcc on suns. */
if (!strcmp(swt
+ 1, "Bstatic"))
if (!strcmp(swt
+ 1, "Bdynamic"))
if (!strcmp(swt
+ 1, "Bsymbolic"))
if (!strcmp(swt
+ 1, "Bforcearchive"))
if (!strcmp(swt
+ 1, "Bshareable"))
if (!strcmp(swt
+ 1, "assert"))
if (!strcmp(swt
+ 1, "Ttext")) {
text_start
= parse(arg
, "%x", "invalid argument to -Ttext");
if (!strcmp(swt
+ 1, "Tdata")) {
rrs_data_start
= parse(arg
, "%x", "invalid argument to -Tdata");
Tdata_flag_specified
= 1;
if (!strcmp(swt
+ 1, "noinhibit-exec")) {
specified_data_size
= parse(arg
, "%x", "invalid argument to -D");
force_common_definition
= 1;
force_alias_definition
= 1;
fatal("-d option takes 'c' or 'p' argument");
entry_symbol
= getsym(arg
);
if (!entry_symbol
->defined
&& !entry_symbol
->referenced
)
undefined_global_sym_count
++;
entry_symbol
->referenced
= 1;
add_cmdline_ref(entry_symbol
);
magic
= oldmagic
= QMAGIC
;
magic
= oldmagic
= ZMAGIC
;
strip_symbols
= STRIP_DEBUGGER
;
strip_symbols
= STRIP_ALL
;
text_start
= parse(arg
, "%x", "invalid argument to -T");
register symbol
*sp
= getsym(arg
);
if (!sp
->defined
&& !sp
->referenced
)
undefined_global_sym_count
++;
soversion
= parse(arg
, "%d", "invalid argument to -V");
discard_locals
= DISCARD_L
;
discard_locals
= DISCARD_ALL
;
register symbol
*sp
= getsym(&swt
[2]);
fatal("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
* FUNCTION receives two arguments: the entry, and ARG.
register void (*function
) ();
for (i
= 0; i
< number_of_files
; i
++) {
register struct file_entry
*entry
= &file_table
[i
];
if (entry
->library_flag
) {
register struct file_entry
*subentry
= entry
->subfiles
;
for (; subentry
; subentry
= subentry
->chain
)
(*function
) (subentry
, arg
);
(*function
) (entry
, arg
);
* 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).
check_each_file(function
, arg
)
register unsigned long (*function
) ();
register unsigned long return_val
;
for (i
= 0; i
< number_of_files
; i
++) {
register struct file_entry
*entry
= &file_table
[i
];
if (entry
->library_flag
) {
register struct file_entry
*subentry
= entry
->subfiles
;
for (; subentry
; subentry
= subentry
->chain
)
if (return_val
= (*function
) (subentry
, arg
))
} else if (return_val
= (*function
) (entry
, arg
))
/* Like `each_file' but ignore files that were just for symbol definitions. */
each_full_file(function
, arg
)
register void (*function
) ();
for (i
= 0; i
< number_of_files
; i
++) {
register struct file_entry
*entry
= &file_table
[i
];
if (entry
->just_syms_flag
|| entry
->is_dynamic
)
if (entry
->library_flag
) {
register struct file_entry
*subentry
= entry
->subfiles
;
for (; subentry
; subentry
= subentry
->chain
)
(*function
) (subentry
, arg
);
(*function
) (entry
, arg
);
/* Close the input file that is now open. */
* 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.
register struct file_entry
*entry
;
return file_open (entry
->superfile
);
if (input_file
) file_close ();
if (entry
->search_dirs_flag
) {
desc
= open (entry
->filename
, O_RDONLY
, 0);
struct file_entry
*entry
;
return entry
->starting_offset
+ N_TXTOFF (entry
->header
);
/* Medium-level input routines for rel files. */
* Read a file's header into the proper place in the file_entry. DESC is the
* descriptor on which the file is open. ENTRY is the file's entry.
read_header (desc
, entry
)
register struct file_entry
*entry
;
struct exec
*loc
= (struct exec
*) &entry
->header
;
if (lseek (desc
, entry
->starting_offset
, L_SET
) !=
fatal_with_file("read_header: lseek failure ", entry
);
len
= read (desc
, &entry
->header
, sizeof (struct exec
));
if (len
!= sizeof (struct exec
))
fatal_with_file ("failure reading header of ", entry
);
md_swapin_exec_hdr(&entry
->header
);
fatal_with_file ("bad magic number in ", entry
);
entry
->header_read_flag
= 1;
* Read the symbols of file ENTRY into core. Assume it is already open, on
* descriptor DESC. Also read the length of the string table, which follows
* the symbol table, but don't read the contents of the string table.
read_entry_symbols (desc
, entry
)
struct file_entry
*entry
;
if (!entry
->header_read_flag
)
read_header (desc
, entry
);
np
= (struct nlist
*) alloca (entry
->header
.a_syms
);
entry
->nsymbols
= entry
->header
.a_syms
/ sizeof(struct nlist
);
entry
->symbols
= (struct localsymbol
*)
xmalloc(entry
->nsymbols
* sizeof(struct localsymbol
));
if (lseek(desc
, N_SYMOFF(entry
->header
) + entry
->starting_offset
, L_SET
)
!= N_SYMOFF(entry
->header
) + entry
->starting_offset
)
fatal_with_file ("read_symbols(h): lseek failure ", entry
);
if (entry
->header
.a_syms
!= read (desc
, np
, entry
->header
.a_syms
))
fatal_with_file ("premature end of file in symbols of ", 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
].gotslot_offset
= -1;
entry
->symbols
[i
].gotslot_claimed
= 0;
entry
->strings_offset
= N_STROFF(entry
->header
) +
if (lseek(desc
, entry
->strings_offset
, 0) == (off_t
)-1)
fatal_with_file ("read_symbols(s): lseek failure ", entry
);
if (sizeof str_size
!= read (desc
, &str_size
, sizeof str_size
))
fatal_with_file ("bad string table size in ", entry
);
entry
->string_size
= md_swap_long(str_size
);
* Read the string table of file ENTRY into core. Assume it is already open,
read_entry_strings (desc
, entry
)
struct file_entry
*entry
;
if (!entry
->header_read_flag
|| !entry
->strings_offset
)
fatal("internal error: %s", "cannot read string table");
if (lseek (desc
, entry
->strings_offset
, L_SET
) != entry
->strings_offset
)
fatal_with_file ("read_strings: lseek failure ", entry
);
if (entry
->string_size
!=
read (desc
, entry
->strings
, entry
->string_size
))
fatal_with_file ("premature end of file in strings of ", entry
);
/* DEAD - Read in all of the relocation information */
each_full_file (read_entry_relocation
, 0);
/* Read in the relocation sections of ENTRY if necessary */
read_entry_relocation (desc
, entry
)
struct file_entry
*entry
;
register struct relocation_info
*reloc
;
reloc
= (struct relocation_info
*)
xmalloc(entry
->header
.a_trsize
);
pos
= text_offset(entry
) +
entry
->header
.a_text
+ entry
->header
.a_data
;
if (lseek(desc
, pos
, L_SET
) != pos
)
fatal_with_file("read_reloc(t): lseek failure ", entry
);
if (entry
->header
.a_trsize
!=
read(desc
, reloc
, entry
->header
.a_trsize
)) {
"premature eof in text relocation of ", entry
);
md_swapin_reloc(reloc
, entry
->header
.a_trsize
/ sizeof(*reloc
));
entry
->ntextrel
= entry
->header
.a_trsize
/ sizeof(*reloc
);
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(desc
, pos
, L_SET
) != pos
)
fatal_with_file("read_reloc(d): lseek failure ", entry
);
if (entry
->header
.a_drsize
!=
read (desc
, reloc
, entry
->header
.a_drsize
)) {
"premature eof in data relocation of ", entry
);
md_swapin_reloc(reloc
, entry
->header
.a_drsize
/ sizeof(*reloc
));
entry
->ndatarel
= entry
->header
.a_drsize
/ sizeof(*reloc
);
/* Read in the symbols of all input files. */
void read_file_symbols (), read_entry_symbols (), read_entry_strings ();
void enter_file_symbols (), enter_global_ref ();
if (trace_files
) fprintf (stderr
, "Loading symbols:\n\n");
for (i
= 0; i
< number_of_files
; i
++) {
register struct file_entry
*entry
= &file_table
[i
];
read_file_symbols (entry
);
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).
read_file_symbols (entry
)
register struct file_entry
*entry
;
desc
= file_open (entry
);
len
= read (desc
, &hdr
, sizeof hdr
);
fatal_with_file ("failure reading header of ", entry
);
md_swapin_exec_hdr(&hdr
);
if (relocatable_output
) {
"-r and shared objects currently not supported ",
read_shared_object(desc
, entry
);
read_entry_symbols (desc
, entry
);
entry
->strings
= (char *) alloca (entry
->string_size
);
read_entry_strings (desc
, entry
);
read_entry_relocation(desc
, entry
);
enter_file_symbols (entry
);
if (SARMAG
!= read (desc
, armag
, SARMAG
) ||
strncmp (armag
, ARMAG
, SARMAG
))
"malformed input file (not rel or archive) ", entry
);
search_library (desc
, entry
);
/* Enter the external symbol defs and refs of ENTRY in the hash table. */
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
))
* Turn magically prefixed symbols into set symbols of
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
)) {
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. */
if (p
->n_type
!= (N_UNDF
| N_EXT
)) {
error("Warning symbol found in %s without external reference following.",
p
--; /* Process normally. */
char *sname
= p
->n_un
.n_strx
+ entry
->strings
;
/* Deal with the warning symbol. */
p
->n_un
.n_strx
+ entry
->strings
, entry
);
sp
->warning
= (char *)xmalloc(strlen(name
)+1);
strcpy (sp
->warning
, name
);
} else if (p
->n_type
& N_EXT
) {
p
->n_un
.n_strx
+ entry
->strings
, entry
);
} else if (p
->n_un
.n_strx
&& !(p
->n_type
& (N_STAB
| N_EXT
))
if ((p
->n_un
.n_strx
+ entry
->strings
)[0] != LPREFIX
)
} else if (!entry
->is_dynamic
)
* 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 (!entry
->is_dynamic
) {
* 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 `dynref'. 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
enter_global_ref (lsp
, name
, entry
)
struct file_entry
*entry
;
register struct nzlist
*nzp
= &lsp
->nzlist
;
register symbol
*sp
= getsym (name
);
register int type
= nzp
->nz_type
;
int oldref
= sp
->referenced
;
int olddef
= sp
->defined
;
int com
= sp
->defined
&& sp
->max_common_size
;
if (type
== (N_INDR
| N_EXT
)) {
sp
->alias
= getsym(entry
->strings
+ (lsp
+ 1)->nzlist
.nz_strx
);
error("%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;
* 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
) {
undefined_global_sym_count
--;
common_defined_global_count
++;
sp
->max_common_size
= nzp
->nz_value
;
sp
->defined
= N_UNDF
| N_EXT
;
} else if (com
&& sp
->max_common_size
< nzp
->nz_value
) {
sp
->max_common_size
= nzp
->nz_value
;
* Handle size information in shared objects.
if (nzp
->nz_size
> sp
->size
)
if (sp
== dynamic_symbol
|| sp
== got_symbol
) {
if (type
!= (N_UNDF
| N_EXT
) && !entry
->just_syms_flag
)
fatal("Linker reserved symbol %s defined as type %x ",
if (type
== (N_SIZE
| N_EXT
)) {
if (sp
->size
< nzp
->nz_value
)
sp
->size
= nzp
->nz_value
;
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
))
* It used to be undefined and we're defining it.
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
->max_common_size
= nzp
->nz_value
;
} else if (com
&& type
!= (N_UNDF
| N_EXT
)) {
* It used to be common and we're defining
common_defined_global_count
--;
} else if (com
&& type
== (N_UNDF
| N_EXT
)
&& sp
->max_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
->max_common_size
= nzp
->nz_value
;
if (SET_ELEMENT_P(type
) && (!olddef
|| com
))
undefined_global_sym_count
++;
if (sp
== end_symbol
&& entry
->just_syms_flag
&& !T_flag_specified
)
text_start
= nzp
->nz_value
;
"defined as common":"referenced";
reftype
= "defined as absolute";
reftype
= "defined in text section";
reftype
= "defined in data section";
reftype
= "defined in BSS section";
reftype
= "I don't know this type";
fprintf (stderr
, "symbol %s %s in ", sp
->name
, reftype
);
print_file_name (entry
, stderr
);
* This return 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.
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
;
void consider_file_section_lengths (), relocate_file_addresses ();
void consider_relocation();
* 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
* 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 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.
void digest_pass1(), digest_pass2();
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
= (2 * set_symbol_count
+ set_vector_count
) *
set_vectors
= (unsigned long *) xmalloc (set_sect_size
);
/* Pass 1: check and define symbols */
defined_global_sym_count
= 0;
if (!relocatable_output
) {
each_full_file(consider_relocation
, 0); /* Text */
each_full_file(consider_relocation
, 1); /* Data */
* 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
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.
int text_end
= text_size
+ N_TXTOFF(outheader
);
text_pad
= PALIGN(text_end
, page_size
) - text_end
;
outheader
.a_text
= text_size
;
* Make the data segment address start in memory on a suitable
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
+= set_sect_size
;
bss_start
= rrs_data_start
+ data_size
;
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",
/* 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 */
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
if (specified_data_size
&& specified_data_size
> data_size
)
data_pad
= specified_data_size
- data_size
;
data_pad
= PALIGN(data_pad
+ data_size
, page_size
) - data_size
;
* Now, 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.
/* Check for undefined symbols in shared objects */
for (lsp
= sp
->sorefs
; lsp
; lsp
= lsp
->next
) {
type
= lsp
->nzlist
.nlist
.n_type
;
if ((type
& N_EXT
) && type
!= (N_UNDF
| N_EXT
))
if ((type
& N_EXT
) && type
== (N_UNDF
| N_EXT
))
undefined_shobj_sym_count
++;
/* Superfluous symbol from shared object */
if (sp
== got_symbol
|| sp
== dynamic_symbol
)
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
)) {
"internal error: global ref to set el %s with -r",
sp
->defined
= N_SETV
| N_EXT
;
setv_fill_count
++ * sizeof(long);
} else if ((sp
->defined
& N_TYPE
) != N_SETV
) {
sp
->multiply_defined
= 1;
/* Keep count and remember symbol */
set_vectors
[setv_fill_count
++] = (long)p
;
} 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
sp
->multiply_defined
= 1;
if ((sp
->defined
& N_TYPE
) == N_SETV
)
/* Allocate zero entry in set vector */
defined_global_sym_count
++;
* Still undefined, search the shared object symbols for a
* definition. This symbol must go into the RRS.
if (building_shared_object
) {
undefined_global_sym_count
--;
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 */
undefined_global_sym_count
--;
printf("shr: %s gets defined to %x with value %x\n", sp
->name
, type
, sp
->value
);
* Assign each symbol its final value.
* If not -r'ing, allocate common symbols in the BSS section.
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
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
;
for (i
= 1; i
< (sp
->setv_count
- 1)/2 + 1; i
++) {
tmp
= set_vectors
[length_word_index
+ i
];
set_vectors
[length_word_index
+ i
] =
set_vectors
[length_word_index
+ sp
->setv_count
+ 1 - i
];
set_vectors
[length_word_index
+ sp
->setv_count
+ 1 - i
] = tmp
;
/* Clear terminating entry */
set_vectors
[length_word_index
+ sp
->setv_count
+ 1] = 0;
if (sp
->defined
&& sp
->def_nlist
&&
((sp
->defined
& ~N_EXT
) != N_SETV
))
sp
->value
= sp
->def_nlist
->n_value
;
if (building_shared_object
&& !(link_mode
& SYMBOLIC
))
/* No common allocation in shared objects */
if ((size
= sp
->max_common_size
) != 0) {
if (sp
->defined
!= (N_UNDF
+ N_EXT
))
fatal("%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.
fatal("%s: Bogus N_SIZE item", sp
->name
);
if (relocatable_output
&& !force_common_definition
) {
undefined_global_sym_count
++;
defined_global_sym_count
--;
* 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
size
= PALIGN(size
, sizeof(int));
align
= align
> MAX_ALIGNMENT
?
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
;
defined_global_sym_count
++;
printf("Allocating %s %s: %x at %x\n",
sp
->defined
==(N_BSS
|N_EXT
)?"common":"data",
sp
->name
, size
, sp
->value
);
* Scan relocation info in ENTRY for contributions to the dynamic section of
consider_relocation (entry
, dataseg
)
struct file_entry
*entry
;
struct relocation_info
*reloc
, *end
;
end
= entry
->textrel
+ entry
->ntextrel
;
end
= entry
->datarel
+ entry
->ndatarel
;
for (; reloc
< end
; reloc
++) {
* 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
* jmptab: extern: needs jmp slot
* !extern: "intersegment" jump/call,
* should get resolved in output
* baserel: extern: need GOT entry
* !extern: may need GOT entry,
* 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
if (RELOC_JMPTAB_P(reloc
)) {
if (!RELOC_EXTERN_P(reloc
))
lsp
= &entry
->symbols
[reloc
->r_symbolnum
];
} else if (RELOC_BASEREL_P(reloc
)) {
lsp
= &entry
->symbols
[reloc
->r_symbolnum
];
alloc_rrs_gotslot(reloc
, lsp
);
} else if (RELOC_EXTERN_P(reloc
)) {
* 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
* 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
lsp
= &entry
->symbols
[reloc
->r_symbolnum
];
"internal error, sp==NULL", entry
);
* Skip refs to _GLOBAL_OFFSET_TABLE_ and __DYNAMIC
if (!CHECK_GOT_RELOC(reloc
))
"Unexpected relocation type ", entry
);
* This symbol gives rise to a RRS entry
if (building_shared_object
) {
* Only allocate an alias for function calls. Use
* sp->size here as a heuristic to discriminate
* between function definitions and data residing
* NOTE THAT THE COMPILER MUST NOT GENERATE ".size"
* DIRECTIVES FOR FUNCTIONS.
* In the future we might go for ".type" directives.
if (force_alias_definition
&& sp
->size
== 0 &&
sp
->so_defined
== N_TEXT
+ N_EXT
) {
/* Call to shared library procedure */
(sp
->so_defined
== N_DATA
+ N_EXT
||
sp
->so_defined
== N_TEXT
+ N_EXT
)) {
/* Reference to shared library data */
} else if (!sp
->defined
&& sp
->max_common_size
== 0)
* Prepare an RRS relocation as these are load
if (building_shared_object
) {
alloc_rrs_segment_reloc(reloc
);
* Accumulate the section sizes of input file ENTRY into the section sizes of
consider_file_section_lengths (entry
)
register struct file_entry
*entry
;
if (entry
->just_syms_flag
)
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.
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
entry
->data_start_address
+= data_start
;
entry
->bss_start_address
+= bss_start
;
printf("%s: datastart: %#x, bss %#x\n", get_file_name(entry
),
entry
->data_start_address
, entry
->bss_start_address
);
lspend
= entry
->symbols
+ entry
->nsymbols
;
for (lsp
= entry
->symbols
; lsp
< lspend
; lsp
++) {
register struct nlist
*p
= &lsp
->nzlist
.nlist
;
* If this belongs to a section, update it
* by the section's start address
register int type
= p
->n_type
& N_TYPE
;
p
->n_value
+= entry
->text_start_address
;
* 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
p
->n_value
+= entry
->data_start_address
-
/* likewise for symbols with value in BSS. */
p
->n_value
+= entry
->bss_start_address
- entry
->header
.a_text
- entry
->header
.a_data
;
/* Write the output file */
if (lstat(output_filename
, &statbuf
) != -1) {
if (!S_ISDIR(statbuf
.st_mode
))
(void)unlink(output_filename
);
outdesc
= open (output_filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0666);
perror_name (output_filename
);
if (fstat (outdesc
, &statbuf
) < 0)
perror_name (output_filename
);
filemode
= statbuf
.st_mode
;
chmod (output_filename
, filemode
& ~0111);
/* Output the a.out header. */
/* Output the text and data segments, relocating as we go. */
/* Output the merged relocation info, if requested with `-r'. */
/* Output the symbol table (both globals and locals). */
/* Output the RSS section */
if (chmod (output_filename
, filemode
| 0111) == -1)
perror_name (output_filename
);
void modify_location (), perform_relocation (), copy_text (), copy_data ();
/* Total number of symbols to be written in the output file. */
int flags
= (rrs_section_type
== RRS_FULL
) ? EX_DYNAMIC
: 0;
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
= (defined_global_sym_count
+ undefined_global_sym_count
);
if (discard_locals
== DISCARD_L
)
nsyms
+= non_L_local_sym_count
;
else if (discard_locals
== DISCARD_NONE
)
nsyms
+= local_sym_count
;
/* For each alias we write out two struct nlists */
nsyms
+= set_symbol_count
+ global_alias_count
;
if (dynamic_symbol
->referenced
)
nsyms
++, special_sym_count
++;
if (got_symbol
->referenced
)
nsyms
++, special_sym_count
++;
if (strip_symbols
== STRIP_NONE
)
nsyms
+= debugger_sym_count
;
printf("defined globals: %d, undefined globals %d, locals: %d (non_L: %d), \
debug symbols: %d, special: %d --> nsyms %d\n",
defined_global_sym_count
, undefined_global_sym_count
,
local_sym_count
, non_L_local_sym_count
, debugger_sym_count
,
special_sym_count
, nsyms
);
outheader
.a_syms
= nsyms
* sizeof (struct nlist
);
if (relocatable_output
) {
outheader
.a_trsize
= text_reloc_size
;
outheader
.a_drsize
= data_reloc_size
;
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.
padfile (N_TXTOFF(outheader
) - sizeof outheader
, outdesc
);
/* Relocate the text segment of each input file
and write to the output file. */
fprintf (stderr
, "Copying and relocating text:\n\n");
each_full_file (copy_text
, 0);
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
struct file_entry
*entry
;
prline_file_name (entry
, stderr
);
desc
= 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
)
fatal_with_file("no text relocation of ", entry
);
/* Read the text section into core. */
lseek (desc
, text_offset (entry
), 0);
if (entry
->header
.a_text
!= read (desc
, bytes
, entry
->header
.a_text
))
fatal_with_file ("premature eof in text section of ", 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. */
fprintf (stderr
, "Copying and relocating data:\n\n");
pos
= N_DATOFF(outheader
) + data_start
- rrs_data_start
;
if (lseek(outdesc
, pos
, L_SET
) != pos
)
fatal("write_data: lseek: cant position data offset");
each_full_file (copy_data
, 0);
* Write out the set element vectors. See digest symbols for
* description of length of the set vector section.
swap_longs(set_vectors
, 2 * set_symbol_count
+ set_vector_count
);
mywrite (set_vectors
, 2 * set_symbol_count
+ set_vector_count
,
sizeof (unsigned long), outdesc
);
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'.
struct file_entry
*entry
;
prline_file_name (entry
, stderr
);
desc
= file_open (entry
);
bytes
= (char *)alloca(entry
->header
.a_data
);
if (entry
->datarel
== NULL
)
fatal_with_file("no data relocation of ", entry
);
lseek (desc
, text_offset (entry
) + entry
->header
.a_text
, 0);
if (entry
->header
.a_data
!= read(desc
, bytes
, entry
->header
.a_data
))
fatal_with_file ("premature eof in data section of ", 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.
perform_relocation(data
, data_size
, reloc
, nreloc
, entry
, dataseg
)
struct relocation_info
*reloc
;
struct file_entry
*entry
;
register struct relocation_info
*r
= reloc
;
struct relocation_info
*end
= reloc
+ nreloc
;
text_relocation
= entry
->text_start_address
;
data_relocation
= entry
->data_start_address
- entry
->header
.a_text
;
bss_relocation
= entry
->bss_start_address
-
entry
->header
.a_text
- entry
->header
.a_data
;
entry
->data_start_address
- entry
->header
.a_text
:
entry
->text_start_address
;
int addr
= RELOC_ADDRESS(r
);
long addend
= md_get_addend(r
, data
+addr
);
* Loop over the relocations again as we did in
* consider_relocation(), claiming the reserved RRS
"relocation address out of range in ", entry
);
int symindex
= RELOC_SYMBOL(r
);
struct localsymbol
*lsp
= &entry
->symbols
[symindex
];
if (symindex
>= entry
->nsymbols
)
"relocation symbolnum out of range in ", entry
);
else if (!RELOC_EXTERN_P(r
)) {
data_relocation
- text_relocation
;
claim_rrs_jmpslot(r
, sp
, addend
);
} else if (RELOC_BASEREL_P(r
)) {
int symindex
= RELOC_SYMBOL(r
);
struct localsymbol
*lsp
= &entry
->symbols
[symindex
];
if (symindex
>= entry
->nsymbols
)
"relocation symbolnum out of range in ", entry
);
else if (!RELOC_EXTERN_P(r
))
relocation
= claim_rrs_internal_gotslot(entry
,
relocation
= claim_rrs_gotslot(r
, lsp
, addend
);
} else if (RELOC_EXTERN_P(r
)) {
int symindex
= RELOC_SYMBOL(r
);
if (symindex
>= entry
->nsymbols
)
"relocation symbolnum out of range in ", entry
);
sp
= entry
->symbols
[symindex
].symbol
;
if (relocatable_output
) {
relocation
= addend
+ sp
->value
;
} else if (sp
->defined
) {
relocation
= addend
+ sp
->value
} else if (building_shared_object
) {
* Normal (non-PIC) relocation needs
* to be converted into an RRS reloc
* when building a shared object.
entry
->data_start_address
:
entry
->text_start_address
;
if (claim_rrs_reloc(r
, sp
, &relocation
))
} else if (sp
->defined
== N_SIZE
) {
* If size is known, arrange a
fatal("Copy item isn't: %s",
relocation
= addend
+ sp
->value
;
r
->r_address
= sp
->value
;
claim_rrs_cpy_reloc(r
, sp
);
/* Plain old relocation */
relocation
= addend
+ sp
->value
;
* 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
* In case the symbol is defined in a shared
* object as N_TEXT or N_DATA, an appropriate
* jmpslot or copy relocation is generated.
switch (sp
->so_defined
) {
* Claim a jmpslot if one was
* allocated (dependent on
if (sp
->jmpslot_offset
== -1)
claim_rrs_jmpslot(r
, sp
, addend
);
entry
->data_start_address
:
entry
->text_start_address
;
if (claim_rrs_reloc(r
, sp
, &relocation
))
printf("%s: BSS found in so_defined\n", sp
->name
);
fatal("%s: shobj symbol with unknown type %#x", sp
->name
, sp
->so_defined
);
relocation
= addend
+ text_relocation
;
* 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
relocation
= addend
+ data_relocation
;
* 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
;
* Don't know why this code would occur, but
"nonexternal relocation code invalid in ", entry
);
* When building a shared object, these segment
* relocations need a "load address relative"
if (building_shared_object
) {
entry
->data_start_address
:
entry
->text_start_address
;
claim_rrs_segment_reloc(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 coptxtrel (), copdatrel ();
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
->referenced
)
dynamic_symbol
->symbolnum
= count
++;
if (sp
!= dynamic_symbol
&& sp
->referenced
) {
/* Correct, because if (relocatable_output), we will also be writing
whatever indirect blocks we have. */
if (count
!= defined_global_sym_count
+ undefined_global_sym_count
fatal ("internal error: write_rel: count = %d", count
);
/* Write out the relocations of all files, remembered from copy_text. */
each_full_file (coptxtrel
, 0);
fprintf (stderr
, "\nWriting data relocation:\n\n");
each_full_file (copdatrel
, 0);
struct file_entry
*entry
;
register struct relocation_info
*r
, *end
;
register int reloc
= entry
->text_start_address
;
end
= r
+ entry
->ntextrel
;
RELOC_ADDRESS(r
) += reloc
;
symindex
= RELOC_SYMBOL(r
);
sp
= entry
->symbols
[symindex
].symbol
;
if (symindex
>= entry
->nsymbols
)
"relocation symbolnum out of range in ", entry
);
/* Resolve indirection. */
if ((sp
->defined
& ~N_EXT
) == N_INDR
) {
fatal("internal error: alias in hyperspace");
* If the symbol is now defined, change the external
* relocation to an internal one.
RELOC_SYMBOL(r
) = (sp
->defined
& N_TYPE
);
* 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. Otherwise the work we
* did in this pass is lost.
if (!RELOC_MEMORY_ADD_P(r
))
RELOC_ADD_EXTRA(r
) += sp
->value
;
* 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
);
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
;
end
= r
+ entry
->ndatarel
;
RELOC_ADDRESS(r
) += reloc
;
symindex
= RELOC_SYMBOL(r
);
sp
= entry
->symbols
[symindex
].symbol
;
if (symindex
>= entry
->header
.a_syms
)
"relocation symbolnum out of range in ", entry
);
/* Resolve indirection. */
if ((sp
->defined
& ~N_EXT
) == N_INDR
) {
fatal("internal error: alias in hyperspace");
symtype
= sp
->defined
& N_TYPE
;
if (force_common_definition
||
RELOC_SYMBOL(r
) = symtype
;
* Global symbols come first.
entry
->symbols
[symindex
].symbol
->symbolnum
;
md_swapout_reloc(entry
->datarel
, entry
->ndatarel
);
mywrite(entry
->datarel
, entry
->ndatarel
,
sizeof(struct relocation_info
), outdesc
);
void write_string_table ();
/* Offsets and current lengths of symbol and string tables in output file. */
/* Address in output file where string table starts. */
/* Offset within string table
where the strings in `strtab_vector' should be written. */
/* Total size of string table strings allocated so far,
including strings in `strtab_vector'. */
/* Vector whose elements are strings to be added to the string table. */
/* Vector whose elements are the lengths of those strings. */
/* Index in `strtab_vector' at which the next string will be stored. */
* 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.
assign_string_table_index(name
)
register int index
= strtab_size
;
register int len
= strlen(name
) + 1;
strtab_vector
[strtab_index
] = name
;
strtab_lens
[strtab_index
++] = len
;
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
lseek (outdesc
, string_table_offset
+ string_table_len
, 0);
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
];
/* Report I/O error such as disk full. */
perror_name (output_filename
);
/* Write the symbol table and string table of the output file. */
/* Number of symbols written so far. */
int non_local_syms
= defined_global_sym_count
+ undefined_global_sym_count
* Buffer big enough for all the global symbols. One extra struct
* for each indirect symbol to hold the extra reference following.
= (struct nlist
*)alloca(non_local_syms
* 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
);
string_table_offset
= N_STROFF(outheader
);
string_table_len
= strtab_size
;
if (strip_symbols
== STRIP_ALL
)
/* 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((non_local_syms
) * sizeof(char *));
strtab_lens
= (int *) alloca((non_local_syms
) * sizeof(int));
* __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
->referenced
) {
nl
.n_type
= dynamic_symbol
->defined
;
nl
.n_value
= dynamic_symbol
->value
;
nl
.n_un
.n_strx
= assign_string_table_index(dynamic_symbol
->name
);
/* Scan the symbol hash table, bucket by bucket. */
if (sp
== dynamic_symbol
)
/* Already dealt with above */
/* Came from shared object but was not used */
* Definition came from shared object,
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
)
error("symbol %s remains undefined", sp
->name
);
/* Construct a `struct nlist' for the symbol. */
* common condition needs to be before undefined
* condition because unallocated commons are set
* undefined in digest_symbols
/* defined with known type */
if (!relocatable_output
&& 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
nl
.n_type
= sp
->alias
->defined
;
nl
.n_type
= sp
->alias
->value
;
} else if (sp
->defined
== N_SIZE
)
nl
.n_type
= N_DATA
| N_EXT
;
} else if (sp
->max_common_size
) {
* defined as common but not allocated,
* happens only with -r and not -d, write out
nl
.n_type
= N_UNDF
| N_EXT
;
nl
.n_value
= sp
->max_common_size
;
} else if (!sp
->defined
) {
/* undefined -- legit only if -r */
nl
.n_type
= N_UNDF
| N_EXT
;
"internal error: %s defined in mysterious way",
* 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. */
if (syms_written
>= non_local_syms
)
"internal error: number of symbols exceeds allocated %d",
if (nl
.n_type
== N_INDR
+ N_EXT
) {
fatal("internal error: alias in hyperspace");
nl
.n_type
= N_UNDF
+ N_EXT
;
assign_string_table_index(sp
->alias
->name
);
printf("writesym(#%d): %s, type %x\n", syms_written
, sp
->name
, sp
->defined
);
if (syms_written
!= strtab_index
|| strtab_index
!= non_local_syms
)
wrong number (%d) of global symbols written into output file, should be %d",
syms_written
, non_local_syms
);
/* Output the buffer full of `struct nlist's. */
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 strings for the global symbols. */
/* Write the local symbols defined by the various files. */
each_file(write_file_syms
, &syms_written
);
if (syms_written
!= nsyms
)
wrong number of symbols (%d) written into output file, should be %d",
if (symbol_table_offset
+ symbol_table_len
!= string_table_offset
)
"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.
write_file_syms(entry
, syms_written_addr
)
struct file_entry
*entry
;
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
;
* 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));
/* Generate a local symbol for the start of this file's text. */
if (discard_locals
!= DISCARD_ALL
) {
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
;
entry
->local_syms_offset
= *syms_written_addr
* sizeof(struct nlist
);
/* 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
;
register int type
= p
->n_type
;
* WRITE gets 1 for a non-global symbol that should be
if (SET_ELEMENT_P (type
))
* This occurs even if global. These types of
* symbols are never written globally, though
* they are stored globally.
write
= relocatable_output
;
else if (!(type
& (N_STAB
| N_EXT
)))
/* ordinary local symbol */
write
= ((discard_locals
!= DISCARD_ALL
)
&& !(discard_locals
== DISCARD_L
&&
(p
->n_un
.n_strx
+ entry
->strings
)[0] == LPREFIX
)
else if (!(type
& N_EXT
))
write
= (strip_symbols
== STRIP_NONE
) &&
!(discard_locals
== DISCARD_L
&&
(p
->n_un
.n_strx
+ entry
->strings
)[0] == LPREFIX
);
* If this symbol has a name, allocate space for it
* in the output string table.
p
->n_un
.n_strx
= assign_string_table_index(
p
->n_un
.n_strx
+ entry
->strings
);
/* Output this symbol to the buffer and count it. */
/* 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'.
entry
->strings
= 0; /* Since it will disappear anyway. */