diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
commit | d6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch) | |
tree | ece253b876159b39c620e62b6c9b1174642e070e /gnu/usr.bin/ld/lib.c |
initial import of NetBSD tree
Diffstat (limited to 'gnu/usr.bin/ld/lib.c')
-rw-r--r-- | gnu/usr.bin/ld/lib.c | 846 |
1 files changed, 846 insertions, 0 deletions
diff --git a/gnu/usr.bin/ld/lib.c b/gnu/usr.bin/ld/lib.c new file mode 100644 index 00000000000..f51d4014475 --- /dev/null +++ b/gnu/usr.bin/ld/lib.c @@ -0,0 +1,846 @@ +/* + * $Id: lib.c,v 1.1 1995/10/18 08:40:55 deraadt Exp $ - library routines + */ + +#include <sys/param.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <sys/time.h> +#include <err.h> +#include <fcntl.h> +#include <ar.h> +#include <ranlib.h> +#include <a.out.h> +#include <stab.h> +#include <string.h> +#include <dirent.h> +#include <ctype.h> + +#include "ld.h" + +static void linear_library __P((int, struct file_entry *)); +static void symdef_library __P((int, struct file_entry *, int)); +static struct file_entry *decode_library_subfile __P((int, + struct file_entry *, + int, int *)); + +/* + * Search the library ENTRY, already open on descriptor FD. 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(fd, entry) + int fd; + 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(fd, 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(fd, entry, member_length); + else + linear_library(fd, 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 FD. + * SUBFILE_OFFSET is the byte index in the library of this member's header. + * We store the length of the member into *LENGTH_LOC. + */ + +static struct file_entry * +decode_library_subfile(fd, library_entry, subfile_offset, length_loc) + int fd; + struct file_entry *library_entry; + int subfile_offset; + int *length_loc; +{ + int bytes_read; + register int namelen; + int member_length, content_length; + int starting_offset; + register char *name; + struct ar_hdr hdr1; + register struct file_entry *subentry; + + lseek(fd, subfile_offset, 0); + + bytes_read = read(fd, &hdr1, sizeof hdr1); + if (!bytes_read) + return 0; /* end of archive */ + + if (sizeof hdr1 != bytes_read) + errx(1, "%s: malformed library archive", + get_file_name(library_entry)); + + if (sscanf(hdr1.ar_size, "%d", &member_length) != 1) + errx(1, "%s: malformatted header of archive member: %.*s", + get_file_name(library_entry), + sizeof(hdr1.ar_name), hdr1.ar_name); + + 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++); + + starting_offset = subfile_offset + sizeof hdr1; + content_length = member_length; + +#ifdef AR_EFMT1 + /* + * BSD 4.4 extended AR format: #1/<namelen>, with name as the + * first <namelen> bytes of the file + */ + if (strncmp(hdr1.ar_name, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 && + isdigit(hdr1.ar_name[sizeof(AR_EFMT1) - 1])) { + + namelen = atoi(&hdr1.ar_name[sizeof(AR_EFMT1) - 1]); + name = (char *)xmalloc(namelen + 1); + if (read(fd, name, namelen) != namelen) + errx(1, "%s: malformatted archive member: %.*s", + get_file_name(library_entry), + sizeof(hdr1.ar_name), hdr1.ar_name); + name[namelen] = 0; + content_length -= namelen; + starting_offset += namelen; + } else + +#endif + { + name = (char *)xmalloc(namelen + 1); + strncpy(name, hdr1.ar_name, namelen); + name[namelen] = 0; + } + + subentry->filename = name; + subentry->local_sym_name = name; + subentry->starting_offset = starting_offset; + subentry->superfile = library_entry; + subentry->total_size = content_length; +#if 0 + subentry->symbols = 0; + subentry->strings = 0; + subentry->subfiles = 0; + subentry->chain = 0; + subentry->flags = 0; +#endif + + (*length_loc) = member_length; + + return subentry; +} + +static int subfile_wanted_p __P((struct file_entry *)); + +/* + * Search a library that has a __.SYMDEF member. FD 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. + */ + +static void +symdef_library(fd, entry, member_length) + int fd; + struct file_entry *entry; + int member_length; +{ + int *symdef_data = (int *) xmalloc(member_length); + register struct ranlib *symdef_base; + char *sym_name_base; + int nsymdefs; + 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(fd, symdef_data, member_length); + if (bytes_read != member_length) + errx(1, "%s: malformatted __.SYMDEF", + get_file_name(entry)); + + nsymdefs = md_swap_long(*symdef_data) / sizeof(struct ranlib); + if (nsymdefs < 0 || + nsymdefs * sizeof(struct ranlib) + 2 * sizeof(int) > member_length) + errx(1, "%s: malformatted __.SYMDEF", + get_file_name(entry)); + + symdef_base = (struct ranlib *) (symdef_data + 1); + length_of_strings = md_swap_long(*(int *) (symdef_base + nsymdefs)); + + if (length_of_strings < 0 + || nsymdefs * sizeof(struct ranlib) + length_of_strings + + 2 * sizeof(int) > member_length) + errx(1, "%s: malformatted __.SYMDEF", + get_file_name(entry)); + + sym_name_base = sizeof(int) + (char *) (symdef_base + nsymdefs); + + /* Check all the string indexes for validity. */ + md_swapin_ranlib_hdr(symdef_base, nsymdefs); + for (i = 0; i < nsymdefs; i++) { + register int index = symdef_base[i].ran_un.ran_strx; + if (index < 0 || index >= length_of_strings + || (index && *(sym_name_base + index - 1))) + errx(1, "%s: malformatted __.SYMDEF", + get_file_name(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 < nsymdefs && + ((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 we're not forcing the archive in then we don't + * need to bother if: we've never heard of the symbol, + * or if it is already defined. The last clause causes + * archive members to be searched for definitions + * satisfying undefined shared object symbols. + */ + if (!(link_mode & FORCEARCHIVE) && + (!sp || sp->defined || + (!(sp->flags & GS_REFERENCED) && + !sp->sorefs))) + 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(fd, + entry, offset, &junk); + if (subentry == 0) + errx(1, + "invalid offset for %s in symbol table of %s", + sym_name_base + + symdef_base[i].ran_un.ran_strx, + entry->filename); + + read_entry_symbols(fd, subentry); + subentry->strings = (char *) + alloca(subentry->string_size); + read_entry_strings(fd, subentry); + + /* + * Now scan the symbol table and decide whether to + * load. + */ + + if (!(link_mode & FORCEARCHIVE) && + !subfile_wanted_p(subentry)) { + if (subentry->symbols) + 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(fd, 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 < nsymdefs; 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. + */ + subentry->strings = 0; + } + } + } + + free(symdef_data); +} + +/* + * Search a library that has no __.SYMDEF. ENTRY is the library's file_entry. + * FD is the descriptor it is open on. + */ + +static void +linear_library(fd, entry) + int fd; + 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(fd, entry, + this_subfile_offset, &member_length); + + if (!subentry) + return; + + read_entry_symbols(fd, subentry); + subentry->strings = (char *)alloca(subentry->string_size); + read_entry_strings(fd, subentry); + + if (!(link_mode & FORCEARCHIVE) && + !subfile_wanted_p(subentry)) { + if (subentry->symbols) + free(subentry->symbols); + free(subentry); + } else { + read_entry_relocation(fd, 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. + */ + +static 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->flags & 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->flags & GS_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->common_size) + common_defined_global_count++; + + if (sp->common_size < p->n_value) + sp->common_size = p->n_value; + if (!sp->defined) + undefined_global_sym_count--; + sp->defined = type; + continue; + } + if (sp->flags & GS_WEAK) + /* Weak symbols don't pull archive members */ + continue; + if (write_map) { + print_file_name(entry, stdout); + fprintf(stdout, " needed due to %s\n", sp->name); + } + return 1; + + } else if (!sp->defined && sp->sorefs) { + /* + * Check for undefined symbols or commons + * in shared objects. + */ + struct localsymbol *lsp; + + for (lsp = sp->sorefs; lsp; lsp = lsp->next) { + int type = lsp->nzlist.nlist.n_type; + if ( (type & N_EXT) && + (type & N_STAB) == 0 && + type != (N_UNDF | N_EXT)) + break; /* We don't need it */ + } + if (lsp != NULL) + /* + * We have a worthy definition in a shared + * object that was specified ahead of the + * archive we're examining now. So, punt. + */ + continue; + + /* + * At this point, we have an undefined shared + * object reference. Again, if the archive member + * defines a common we just note the its size. + * Otherwise, the member gets included. + */ + + if (type == (N_UNDF|N_EXT) && p->n_value) { + /* + * New symbol is common, just takes its + * size, but don't load. + */ + sp->common_size = p->n_value; + sp->defined = type; + continue; + } + + /* + * THIS STILL MISSES the case where one shared + * object defines a common and the next defines + * more strongly; fix this someday by making + * `struct glosym' and enter_global_ref() more + * symmetric. + */ + + if (write_map) { + print_file_name(entry, stdout); + fprintf(stdout, + " needed due to shared lib ref %s (%d)\n", + sp->name, + lsp ? lsp->nzlist.nlist.n_type : -1); + } + return 1; + } + } + + return 0; +} + +/* + * Read the symbols of dynamic entity ENTRY into core. Assume it is already + * open, on descriptor FD. + */ +void +read_shared_object(fd, entry) + struct file_entry *entry; + int fd; +{ + struct _dynamic dyn; + struct section_dispatch_table sdt; + struct nlist *np; + struct nzlist *nzp; + int n, i, has_nz = 0; + + if (!(entry->flags & E_HEADER_VALID)) + read_header(fd, entry); + + /* Read DYNAMIC structure (first in data segment) */ + if (lseek(fd, text_offset(entry) + entry->header.a_text, L_SET) == + (off_t)-1) + err(1, "%s: lseek", get_file_name(entry)); + if (read(fd, &dyn, sizeof dyn) != sizeof dyn) { + errx(1, "%s: premature EOF reading _dynamic", + get_file_name(entry)); + } + md_swapin__dynamic(&dyn); + + /* Check version */ + switch (dyn.d_version) { + default: + errx(1, "%s: unsupported _DYNAMIC version: %d", + get_file_name(entry), dyn.d_version); + break; + case LD_VERSION_SUN: + break; + case LD_VERSION_BSD: + has_nz = 1; + break; + } + + /* Read Section Dispatch Table (from data segment) */ + if (lseek(fd, + text_offset(entry) + (long)dyn.d_un.d_sdt - + (DATA_START(entry->header) - N_DATOFF(entry->header)), + L_SET) == (off_t)-1) + err(1, "%s: lseek", get_file_name(entry)); + if (read(fd, &sdt, sizeof sdt) != sizeof sdt) + errx(1, "%s: premature EOF reading sdt", + get_file_name(entry)); + md_swapin_section_dispatch_table(&sdt); + + /* Read symbols (text segment) */ + n = sdt.sdt_strings - sdt.sdt_nzlist; + 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)); + + if (lseek(fd, + text_offset(entry) + (long)sdt.sdt_nzlist - + (TEXT_START(entry->header) - N_TXTOFF(entry->header)), + L_SET) == (off_t)-1) + err(1, "%s: lseek", get_file_name(entry)); + if (read(fd, (char *)nzp, n) != n) + errx(1, "%s: premature EOF reading symbols ", + get_file_name(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].entry = entry; + entry->symbols[i].gotslot_offset = -1; + entry->symbols[i].flags = 0; + } + + /* Read strings (text segment) */ + n = entry->string_size = sdt.sdt_str_sz; + entry->strings = (char *)alloca(n); + entry->strings_offset = text_offset(entry) + sdt.sdt_strings; + if (lseek(fd, + entry->strings_offset - + (TEXT_START(entry->header) - N_TXTOFF(entry->header)), + L_SET) == (off_t)-1) + err(1, "%s: lseek", get_file_name(entry)); + if (read(fd, entry->strings, n) != n) + errx(1, "%s: premature EOF reading strings", + get_file_name(entry)); + enter_file_symbols (entry); + entry->strings = 0; + + /* + * Load any subsidiary shared objects. + */ + if (sdt.sdt_sods) { + struct sod sod; + off_t offset; + struct file_entry *prev = NULL; + + offset = (off_t)sdt.sdt_sods; + while (1) { + struct file_entry *subentry; + char *libname, name[MAXPATHLEN]; /*XXX*/ + + subentry = (struct file_entry *) + xmalloc(sizeof(struct file_entry)); + bzero(subentry, sizeof(struct file_entry)); + subentry->superfile = entry; + subentry->flags = E_SECONDCLASS; + + if (lseek(fd, + offset - (TEXT_START(entry->header) - + N_TXTOFF(entry->header)), + L_SET) == (off_t)-1) + err(1, "%s: lseek", get_file_name(entry)); + if (read(fd, &sod, sizeof(sod)) != sizeof(sod)) + errx(1, "%s: premature EOF reding sod", + get_file_name(entry)); + md_swapin_sod(&sod, 1); + if (lseek(fd, + (off_t)sod.sod_name - (TEXT_START(entry->header) - + N_TXTOFF(entry->header)), + L_SET) == (off_t)-1) + err(1, "%s: lseek", get_file_name(entry)); + (void)read(fd, name, sizeof(name)); /*XXX*/ + if (sod.sod_library) { + int sod_major = sod.sod_major; + int sod_minor = sod.sod_minor; + + libname = findshlib(name, + &sod_major, &sod_minor, 0); + if (libname == NULL) + errx(1,"no shared -l%s.%d.%d available", + name, sod.sod_major, sod.sod_minor); + subentry->filename = libname; + subentry->local_sym_name = concat("-l", name, ""); + } else { + subentry->filename = strdup(name); + subentry->local_sym_name = strdup(name); + } + read_file_symbols(subentry); + + if (prev) + prev->chain = subentry; + else + entry->subfiles = subentry; + prev = subentry; + fd = file_open(entry); + if ((offset = (off_t)sod.sod_next) == 0) + break; + } + } +#ifdef SUN_COMPAT + if (link_mode & SILLYARCHIVE) { + char *cp, *sa_name; + char armag[SARMAG]; + int fd; + struct file_entry *subentry; + + sa_name = strdup(entry->filename); + if (sa_name == NULL) + goto out; + cp = sa_name + strlen(sa_name) - 1; + while (cp > sa_name) { + if (!isdigit(*cp) && *cp != '.') + break; + --cp; + } + if (cp <= sa_name || *cp != 'o') { + /* Not in `libxxx.so.n.m' form */ + free(sa_name); + goto out; + } + + *cp = 'a'; + if ((fd = open(sa_name, O_RDONLY, 0)) < 0) + goto out; + + /* Read archive magic */ + bzero(armag, SARMAG); + (void)read(fd, armag, SARMAG); + (void)close(fd); + if (strncmp(armag, ARMAG, SARMAG) != 0) { + warnx("%s: malformed silly archive", + get_file_name(entry)); + goto out; + } + + subentry = (struct file_entry *) + xmalloc(sizeof(struct file_entry)); + bzero(subentry, sizeof(struct file_entry)); + + entry->silly_archive = subentry; + subentry->superfile = entry; + subentry->filename = sa_name; + subentry->local_sym_name = sa_name; + subentry->flags |= E_IS_LIBRARY; + search_library(file_open(subentry), subentry); +out: + ; + } +#endif +} + +#undef major +#undef minor + +int +findlib(p) +struct file_entry *p; +{ + int i; + int fd = -1; + int major = -1, minor = -1; + char *cp, *fname = NULL; + + if (!(p->flags & E_SEARCH_DYNAMIC)) + goto dot_a; + + fname = findshlib(p->filename, &major, &minor, 1); + + if (fname && (fd = open(fname, O_RDONLY, 0)) > 0) { + p->filename = fname; + p->lib_major = major; + p->lib_minor = minor; + p->flags &= ~E_SEARCH_DIRS; + return fd; + } + (void)free(fname); + +dot_a: + p->flags &= ~E_SEARCH_DYNAMIC; + 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 *path + = concat(search_dirs[i], "/", fname); + fd = open(path, O_RDONLY, 0); + if (fd > 0) { + p->filename = path; + p->flags &= ~E_SEARCH_DIRS; + break; + } + (void)free(path); + } + (void)free(fname); + return fd; +} + |