diff options
author | paul <paul@FreeBSD.org> | 1993-11-03 23:41:59 +0000 |
---|---|---|
committer | paul <paul@FreeBSD.org> | 1993-11-03 23:41:59 +0000 |
commit | 66fdbc00cfad3be604d07da78b916f4c04f96dbc (patch) | |
tree | ae9c655d7f7352058dd703d580c1f70430c1e5f7 /gnu/usr.bin/ld/lib.c | |
parent | 7f9e326f196003559252a070bd773ee08a76b244 (diff) | |
download | FreeBSD-src-66fdbc00cfad3be604d07da78b916f4c04f96dbc.zip FreeBSD-src-66fdbc00cfad3be604d07da78b916f4c04f96dbc.tar.gz |
Imported NetBSD's ld for shared libs.
Diffstat (limited to 'gnu/usr.bin/ld/lib.c')
-rw-r--r-- | gnu/usr.bin/ld/lib.c | 640 |
1 files changed, 640 insertions, 0 deletions
diff --git a/gnu/usr.bin/ld/lib.c b/gnu/usr.bin/ld/lib.c new file mode 100644 index 0000000..2ce8efb --- /dev/null +++ b/gnu/usr.bin/ld/lib.c @@ -0,0 +1,640 @@ +/* + * $Id: lib.c,v 1.3 1993/11/01 16:26:17 pk Exp $ - library routines + */ + +#include <sys/param.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <sys/time.h> +#include <fcntl.h> +#include <ar.h> +#include <ranlib.h> +#include <a.out.h> +#include <stab.h> +#include <string.h> +#include <dirent.h> + +#include "ld.h" + +char **search_dirs; + +/* Length of the vector `search_dirs'. */ +int n_search_dirs; + +struct file_entry *decode_library_subfile(); +void linear_library(), symdef_library(); + +/* + * Search the library ENTRY, already open on descriptor DESC. This means + * deciding which library members to load, making a chain of `struct + * file_entry' for those members, and entering their global symbols in the + * hash table. + */ + +void +search_library(desc, entry) + int desc; + struct file_entry *entry; +{ + int member_length; + register char *name; + register struct file_entry *subentry; + + if (!(link_mode & FORCEARCHIVE) && !undefined_global_sym_count) + return; + + /* Examine its first member, which starts SARMAG bytes in. */ + subentry = decode_library_subfile(desc, entry, SARMAG, &member_length); + if (!subentry) + return; + + name = subentry->filename; + free(subentry); + + /* Search via __.SYMDEF if that exists, else linearly. */ + + if (!strcmp(name, "__.SYMDEF")) + symdef_library(desc, entry, member_length); + else + linear_library(desc, entry); +} + +/* + * Construct and return a file_entry for a library member. The library's + * file_entry is library_entry, and the library is open on DESC. + * SUBFILE_OFFSET is the byte index in the library of this member's header. + * We store the length of the member into *LENGTH_LOC. + */ + +struct file_entry * +decode_library_subfile(desc, library_entry, subfile_offset, length_loc) + int desc; + struct file_entry *library_entry; + int subfile_offset; + int *length_loc; +{ + int bytes_read; + register int namelen; + int member_length; + register char *name; + struct ar_hdr hdr1; + register struct file_entry *subentry; + + lseek(desc, subfile_offset, 0); + + bytes_read = read(desc, &hdr1, sizeof hdr1); + if (!bytes_read) + return 0; /* end of archive */ + + if (sizeof hdr1 != bytes_read) + fatal_with_file("malformed library archive ", library_entry); + + if (sscanf(hdr1.ar_size, "%d", &member_length) != 1) + fatal_with_file("malformatted header of archive member in ", library_entry); + + subentry = (struct file_entry *) xmalloc(sizeof(struct file_entry)); + bzero(subentry, sizeof(struct file_entry)); + + for (namelen = 0; + namelen < sizeof hdr1.ar_name + && hdr1.ar_name[namelen] != 0 && hdr1.ar_name[namelen] != ' ' + && hdr1.ar_name[namelen] != '/'; + namelen++); + + name = (char *) xmalloc(namelen + 1); + strncpy(name, hdr1.ar_name, namelen); + name[namelen] = 0; + + subentry->filename = name; + subentry->local_sym_name = name; + subentry->symbols = 0; + subentry->strings = 0; + subentry->subfiles = 0; + subentry->starting_offset = subfile_offset + sizeof hdr1; + subentry->superfile = library_entry; + subentry->library_flag = 0; + subentry->header_read_flag = 0; + subentry->just_syms_flag = 0; + subentry->chain = 0; + subentry->total_size = member_length; + + (*length_loc) = member_length; + + return subentry; +} + +int subfile_wanted_p(); + +/* + * Search a library that has a __.SYMDEF member. DESC is a descriptor on + * which the library is open. The file pointer is assumed to point at the + * __.SYMDEF data. ENTRY is the library's file_entry. MEMBER_LENGTH is the + * length of the __.SYMDEF data. + */ + +void +symdef_library(desc, entry, member_length) + int desc; + struct file_entry *entry; + int member_length; +{ + int *symdef_data = (int *) xmalloc(member_length); + register struct ranlib *symdef_base; + char *sym_name_base; + int number_of_symdefs; + int length_of_strings; + int not_finished; + int bytes_read; + register int i; + struct file_entry *prev = 0; + int prev_offset = 0; + + bytes_read = read(desc, symdef_data, member_length); + if (bytes_read != member_length) + fatal_with_file("malformatted __.SYMDEF in ", entry); + + number_of_symdefs = md_swap_long(*symdef_data) / sizeof(struct ranlib); + if (number_of_symdefs < 0 || + number_of_symdefs * sizeof(struct ranlib) + 2 * sizeof(int) > member_length) + fatal_with_file("malformatted __.SYMDEF in ", entry); + + symdef_base = (struct ranlib *) (symdef_data + 1); + length_of_strings = md_swap_long(*(int *) (symdef_base + number_of_symdefs)); + + if (length_of_strings < 0 + || number_of_symdefs * sizeof(struct ranlib) + length_of_strings + + 2 * sizeof(int) > member_length) + fatal_with_file("malformatted __.SYMDEF in ", entry); + + sym_name_base = sizeof(int) + (char *) (symdef_base + number_of_symdefs); + + /* Check all the string indexes for validity. */ + md_swapin_ranlib_hdr(symdef_base, number_of_symdefs); + for (i = 0; i < number_of_symdefs; i++) { + register int index = symdef_base[i].ran_un.ran_strx; + if (index < 0 || index >= length_of_strings + || (index && *(sym_name_base + index - 1))) + fatal_with_file("malformatted __.SYMDEF in ", entry); + } + + /* + * Search the symdef data for members to load. Do this until one + * whole pass finds nothing to load. + */ + + not_finished = 1; + while (not_finished) { + + not_finished = 0; + + /* + * Scan all the symbols mentioned in the symdef for ones that + * we need. Load the library members that contain such + * symbols. + */ + + for (i = 0; (i < number_of_symdefs && + ((link_mode & FORCEARCHIVE) || + undefined_global_sym_count || + common_defined_global_count)); i++) { + + register symbol *sp; + int junk; + register int j; + register int offset = symdef_base[i].ran_off; + struct file_entry *subentry; + + + if (symdef_base[i].ran_un.ran_strx < 0) + continue; + + sp = getsym_soft(sym_name_base + + symdef_base[i].ran_un.ran_strx); + + /* + * If we find a symbol that appears to be needed, + * think carefully about the archive member that the + * symbol is in. + */ + + /* + * Per Mike Karels' recommendation, we no longer load + * library files if the only reference(s) that would + * be satisfied are 'common' references. This + * prevents some problems with name pollution (e.g. a + * global common 'utime' linked to a function). + */ + if (!(link_mode & FORCEARCHIVE) && + (!sp || !sp->referenced || sp->defined)) + continue; + + /* + * Don't think carefully about any archive member + * more than once in a given pass. + */ + + if (prev_offset == offset) + continue; + prev_offset = offset; + + /* + * Read the symbol table of the archive member. + */ + + subentry = decode_library_subfile(desc, + entry, offset, &junk); + if (subentry == 0) + fatal( + "invalid offset for %s in symbol table of %s", + sym_name_base + + symdef_base[i].ran_un.ran_strx, + entry->filename); + read_entry_symbols(desc, subentry); + subentry->strings = (char *) + malloc(subentry->string_size); + read_entry_strings(desc, subentry); + + /* + * Now scan the symbol table and decide whether to + * load. + */ + + if (!(link_mode & FORCEARCHIVE) && + !subfile_wanted_p(subentry)) { + free(subentry->symbols); + free(subentry); + } else { + /* + * This member is needed; load it. Since we + * are loading something on this pass, we + * must make another pass through the symdef + * data. + */ + + not_finished = 1; + + read_entry_relocation(desc, subentry); + enter_file_symbols(subentry); + + if (prev) + prev->chain = subentry; + else + entry->subfiles = subentry; + prev = subentry; + + /* + * Clear out this member's symbols from the + * symdef data so that following passes won't + * waste time on them. + */ + + for (j = 0; j < number_of_symdefs; j++) { + if (symdef_base[j].ran_off == offset) + symdef_base[j].ran_un.ran_strx = -1; + } + } + + /* + * We'll read the strings again if we need them + * again. + */ + free(subentry->strings); + subentry->strings = 0; + } + } + + free(symdef_data); +} + +/* + * Search a library that has no __.SYMDEF. ENTRY is the library's file_entry. + * DESC is the descriptor it is open on. + */ + +void +linear_library(desc, entry) + int desc; + struct file_entry *entry; +{ + register struct file_entry *prev = 0; + register int this_subfile_offset = SARMAG; + + while ((link_mode & FORCEARCHIVE) || + undefined_global_sym_count || common_defined_global_count) { + + int member_length; + register struct file_entry *subentry; + + subentry = decode_library_subfile(desc, entry, this_subfile_offset, + &member_length); + + if (!subentry) + return; + + read_entry_symbols(desc, subentry); + subentry->strings = (char *) alloca(subentry->string_size); + read_entry_strings(desc, subentry); + + if (!(link_mode & FORCEARCHIVE) && + !subfile_wanted_p(subentry)) { + free(subentry->symbols); + free(subentry); + } else { + read_entry_relocation(desc, subentry); + enter_file_symbols(subentry); + + if (prev) + prev->chain = subentry; + else + entry->subfiles = subentry; + prev = subentry; + subentry->strings = 0; /* Since space will dissapear + * on return */ + } + + this_subfile_offset += member_length + sizeof(struct ar_hdr); + if (this_subfile_offset & 1) + this_subfile_offset++; + } +} + +/* + * ENTRY is an entry for a library member. Its symbols have been read into + * core, but not entered. Return nonzero if we ought to load this member. + */ + +int +subfile_wanted_p(entry) + struct file_entry *entry; +{ + struct localsymbol *lsp, *lspend; +#ifdef DOLLAR_KLUDGE + register int dollar_cond = 0; +#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; + register char *name = p->n_un.n_strx + entry->strings; + register symbol *sp = getsym_soft(name); + + /* + * If the symbol has an interesting definition, we could + * potentially want it. + */ + if (! (type & N_EXT) + || (type == (N_UNDF | N_EXT) && p->n_value == 0 + +#ifdef DOLLAR_KLUDGE + && name[1] != '$' +#endif + ) +#ifdef SET_ELEMENT_P + || SET_ELEMENT_P(type) + || set_element_prefixed_p(name) +#endif + ) + continue; + + +#ifdef DOLLAR_KLUDGE + if (name[1] == '$') { + sp = getsym_soft(&name[2]); + dollar_cond = 1; + if (!sp) + continue; + if (sp->referenced) { + if (write_map) { + print_file_name(entry, stdout); + fprintf(stdout, " needed due to $-conditional %s\n", name); + } + return 1; + } + continue; + } +#endif + + /* + * If this symbol has not been hashed, we can't be + * looking for it. + */ + + if (!sp) + continue; + + /* + * We don't load a file if it merely satisfies a + * common reference (see explanation above in + * symdef_library()). + */ + if (sp->referenced && !sp->defined) { + /* + * This is a symbol we are looking for. It + * is either not yet defined or defined as a + * common. + */ +#ifdef DOLLAR_KLUDGE + if (dollar_cond) + continue; +#endif + if (type == (N_UNDF | N_EXT)) { + /* + * Symbol being defined as common. + * Remember this, but don't load + * subfile just for this. + */ + + /* + * If it didn't used to be common, up + * the count of common symbols. + */ + if (!sp->max_common_size) + common_defined_global_count++; + + if (sp->max_common_size < p->n_value) + sp->max_common_size = p->n_value; + if (!sp->defined) + undefined_global_sym_count--; + sp->defined = type; + continue; + } + if (write_map) { + print_file_name(entry, stdout); + fprintf(stdout, " needed due to %s\n", sp->name); + } + return 1; + } else { + struct localsymbol *lsp; + int defs = 0; + + /* 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_STAB) == 0 && + type != (N_UNDF | N_EXT)) + goto xxx; + } + if (write_map) { + print_file_name(entry, stdout); + fprintf(stdout, " needed due to shared lib ref %s\n", sp->name); + } + return 1; + xxx: ; + } + } + + return 0; +} + +/* + * Read the symbols of dynamic entity ENTRY into core. Assume it is already + * open, on descriptor DESC. + */ +void +read_shared_object (desc, entry) + struct file_entry *entry; + int desc; +{ + struct link_dynamic dyn; + struct link_dynamic_2 dyn2; + struct nlist *np; + struct nzlist *nzp; + int n, i, has_nz = 0; + + if (!entry->header_read_flag) + read_header (desc, entry); + + /* Read DYNAMIC structure (first in data segment) */ + lseek (desc, + text_offset (entry) + entry->header.a_text, + L_SET); + if (read(desc, &dyn, sizeof dyn) != sizeof dyn) { + fatal_with_file ( + "premature eof in data segment of ", entry); + } + md_swapin_link_dynamic(&dyn); + + /* Check version */ + switch (dyn.ld_version) { + default: + fatal_with_file( "unsupported _DYNAMIC version ", entry); + break; + case LD_VERSION_SUN: + break; + case LD_VERSION_BSD: + has_nz = 1; + break; + } + + /* Read link_dynamic_2 struct (from data segment) */ + lseek (desc, + text_offset(entry) + dyn.ld_un.ld_2, + L_SET); + if (read(desc, &dyn2, sizeof dyn2) != sizeof dyn2) { + fatal_with_file( "premature eof in data segment of ", entry); + } + md_swapin_link_dynamic_2(&dyn2); + + /* Read symbols (text segment) */ + n = dyn2.ld_strings - dyn2.ld_symbols; + entry->nsymbols = n / + (has_nz ? sizeof(struct nzlist) : sizeof(struct nlist)); + nzp = (struct nzlist *)(np = (struct nlist *) alloca (n)); + entry->symbols = (struct localsymbol *) + xmalloc(entry->nsymbols * sizeof(struct localsymbol)); + lseek(desc, text_offset (entry) + dyn2.ld_symbols, L_SET); + if (read(desc, (char *)nzp, n) != n) { + fatal_with_file( + "premature eof while reading dyn syms ", entry); + } + if (has_nz) + md_swapin_zsymbols(nzp, entry->nsymbols); + else + md_swapin_symbols(np, entry->nsymbols); + + /* Convert to structs localsymbol */ + for (i = 0; i < entry->nsymbols; i++) { + if (has_nz) { + entry->symbols[i].nzlist = *nzp++; + } else { + 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; + } + + /* Read strings (text segment) */ + n = entry->string_size = dyn2.ld_str_sz; + entry->strings = (char *) alloca(n); + entry->strings_offset = text_offset (entry) + dyn2.ld_strings; + lseek(desc, entry->strings_offset, L_SET); + if (read(desc, entry->strings, n) != n) { + fatal_with_file( + "premature eof while reading dyn strings ", entry); + } + enter_file_symbols (entry); + entry->strings = 0; + + /* TODO: examine needed shared objects */ + if (dyn2.ld_need) { + } +} + +#undef major +#undef minor + +int +findlib(p) +struct file_entry *p; +{ + int desc; + int i; + int len; + int major = -1, minor = -1; + char *cp, *fname = NULL; + + if (p->search_dynamic_flag == 0) + goto dot_a; + + fname = findshlib(p->filename, &major, &minor); + + if (fname && (desc = open (fname, O_RDONLY, 0)) > 0) { + p->filename = fname; + p->lib_major = major; + p->lib_minor = minor; + p->search_dirs_flag = 0; + return desc; + } + free (fname); + +dot_a: + p->search_dynamic_flag = 0; + if (cp = strrchr(p->filename, '/')) { + *cp++ = '\0'; + fname = concat(concat(p->filename, "/lib", cp), ".a", ""); + *(--cp) = '/'; + } else + fname = concat("lib", p->filename, ".a"); + + for (i = 0; i < n_search_dirs; i++) { + register char *string + = concat (search_dirs[i], "/", fname); + desc = open (string, O_RDONLY, 0); + if (desc > 0) { + p->filename = string; + p->search_dirs_flag = 0; + break; + } + free (string); + } + return desc; +} + |