/* * $Id: lib.c,v 1.3 1993/11/01 16:26:17 pk Exp $ - library routines */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; }