diff options
Diffstat (limited to 'src/ld.c')
-rw-r--r-- | src/ld.c | 1506 |
1 files changed, 1506 insertions, 0 deletions
diff --git a/src/ld.c b/src/ld.c new file mode 100644 index 00000000..2aece00e --- /dev/null +++ b/src/ld.c @@ -0,0 +1,1506 @@ +/* Copyright (C) 2001, 2002, 2003, 2004, 2005 Red Hat, Inc. + Written by Ulrich Drepper <drepper@redhat.com>, 2001. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <argp.h> +#include <assert.h> +#include <error.h> +#include <fcntl.h> +#include <libelf.h> +#include <libintl.h> +#include <locale.h> +#include <mcheck.h> +#include <stdio.h> +#include <stdio_ext.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <system.h> +#include "ld.h" +#include "list.h" + + +/* Name and version of program. */ +static void print_version (FILE *stream, struct argp_state *state); +void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; + +/* Bug report address. */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + + +/* Values for the various options. */ +enum + { + ARGP_whole_archive = 300, + ARGP_no_whole_archive, + ARGP_static, + ARGP_dynamic, + ARGP_pagesize, + ARGP_rpath, + ARGP_rpath_link, + ARGP_runpath, + ARGP_runpath_link, + ARGP_version_script, + ARGP_gc_sections, + ARGP_no_gc_sections, + ARGP_no_undefined, + ARGP_conserve, +#if YYDEBUG + ARGP_yydebug, +#endif + }; + + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + /* XXX This list will be reordered and section names will be added. + Just not right now. */ + { "whole-archive", ARGP_whole_archive, NULL, 0, + N_("Include whole archives in the output from now on."), 0 }, + { "no-whole-archive", ARGP_no_whole_archive, NULL, 0, + N_("Stop including the whole arhives in the output."), 0 }, + + { "output", 'o', N_("FILE"), 0, N_("Place output in FILE."), 0 }, + + { NULL, 'O', N_("LEVEL"), OPTION_ARG_OPTIONAL, + N_("Set optimization level to LEVEL."), 0 }, + + { "verbose", 'v', NULL, 0, N_("Verbose messages."), 0 }, + { "trace", 't', NULL, 0, N_("Trace file opens."), 0 }, + { "conserve-memory", ARGP_conserve, NULL, 0, + N_("Trade speed for less memory usage"), 0 }, + + { NULL, 'z', "KEYWORD", OPTION_HIDDEN, NULL, 0 }, + { "-z nodefaultlib", '\0', NULL, OPTION_DOC, + N_("Object is marked to not use default search path at runtime."), 0 }, + { "-z allextract", '\0', NULL, OPTION_DOC, + N_("Same as --whole-archive."), 0 }, + { "-z defaultextract", '\0', NULL, OPTION_DOC, N_("\ +Default rules of extracting from archive; weak references are not enough."), + 0 }, + { "-z weakextract", '\0', NULL, OPTION_DOC, + N_("Weak references cause extraction from archive."), 0 }, + { "-z muldefs", '\0', NULL, OPTION_DOC, + N_("Allow multiple definitions; first is used."), 0 }, + { "-z defs | nodefs", '\0', NULL, OPTION_DOC, + N_("Disallow/allow undefined symbols in DSOs."), 0 }, + { "no-undefined", ARGP_no_undefined, NULL, OPTION_HIDDEN, NULL, 0 }, + { "-z origin", '\0', NULL, OPTION_DOC, + N_("Object requires immediate handling of $ORIGIN."), 0 }, + { "-z now", '\0', NULL, OPTION_DOC, + N_("Relocation will not be processed lazily."), 0 }, + { "-z nodelete", '\0', NULL, OPTION_DOC, + N_("Object cannot be unloaded at runtime."), 0 }, + { "-z initfirst", '\0', NULL, OPTION_DOC, + N_("Mark object to be initialized first."), 0 }, + { "-z lazyload | nolazyload", '\0', NULL, OPTION_DOC, + N_("Enable/disable lazy-loading flag for following dependencies."), 0 }, + { "-z nodlopen", '\0', NULL, OPTION_DOC, + N_("Mark object as not loadable with 'dlopen'."), 0 }, + { "-z ignore | record", '\0', NULL, OPTION_DOC, + N_("Ignore/record dependencies on unused DSOs."), 0 }, + { "-z systemlibrary", '\0', NULL, OPTION_DOC, + N_("Generated DSO will be a system library."), 0 }, + + { NULL, 'l', N_("FILE"), OPTION_HIDDEN, NULL, 0 }, + + { NULL, '(', NULL, 0, N_("Start a group."), 0 }, + { NULL, ')', NULL, 0, N_("End a group."), 0 }, + + { NULL, 'L', N_("PATH"), 0, + N_("Add PATH to list of directories files are searched in."), 0 }, + + { NULL, 'c', N_("FILE"), 0, N_("Use linker script in FILE."), 0 }, + + { "entry", 'e', N_("ADDRESS"), 0, N_("Set entry point address."), 0 }, + + { "static", ARGP_static, NULL, OPTION_HIDDEN, NULL, 0 }, + { "-B static", ARGP_static, NULL, OPTION_DOC, + N_("Do not link against shared libraries."), 0 }, + { "dynamic", ARGP_dynamic, NULL, OPTION_HIDDEN, NULL, 0 }, + { "-B dynamic", ARGP_dynamic, NULL, OPTION_DOC, + N_("Prefer linking against shared libraries."), 0 }, + + { "export-dynamic", 'E', NULL, 0, N_("Export all dynamic symbols."), 0 }, + + { "strip-all", 's', NULL, 0, N_("Strip all symbols."), 0 }, + { "strip-debug", 'S', NULL, 0, N_("Strip debugging symbols."), 0 }, + + { "pagesize", ARGP_pagesize, "SIZE", 0, + N_("Assume pagesize for the target system to be SIZE."), 0 }, + + { "rpath", ARGP_rpath, "PATH", OPTION_HIDDEN, NULL, 0 }, + { "rpath-link", ARGP_rpath_link, "PATH", OPTION_HIDDEN, NULL, 0 }, + + { "runpath", ARGP_runpath, "PATH", 0, N_("Set runtime DSO search path."), + 0 }, + { "runpath-link", ARGP_runpath_link, "PATH", 0, + N_("Set link time DSO search path."), 0 }, + + { NULL, 'i', NULL, 0, N_("Ignore LD_LIBRARY_PATH environment variable."), + 0 }, + + { "version-script", ARGP_version_script, "FILE", 0, + N_("Read version information from FILE."), 0 }, + + { "emulation", 'm', "NAME", 0, N_("Set emulation to NAME."), 0 }, + + { "shared", 'G', NULL, 0, N_("Generate dynamic shared object."), 0 }, + { NULL, 'r', NULL, 0L, N_("Generate relocatable object."), 0 }, + + { NULL, 'B', "KEYWORD", OPTION_HIDDEN, "", 0 }, + { "-B local", 'B', NULL, OPTION_DOC, + N_("Causes symbol not assigned to a version be reduced to local."), 0 }, + + { "gc-sections", ARGP_gc_sections, NULL, 0, N_("Remove unused sections."), + 0 }, + { "no-gc-sections", ARGP_no_gc_sections, NULL, 0, + N_("Don't remove unused sections."), 0 }, + + { "soname", 'h', "NAME", 0, N_("Set soname of shared object."), 0 }, + { "dynamic-linker", 'I', "NAME", 0, N_("Set the dynamic linker name."), 0 }, + + { NULL, 'Q', "YN", OPTION_HIDDEN, NULL, 0 }, + { "-Q y | n", 'Q', NULL, OPTION_DOC, + N_("Add/suppress addition indentifying link-editor to .comment section"), + 0 }, + +#if YYDEBUG + { "yydebug", ARGP_yydebug, NULL, 0, + N_("Select to get parser debug information"), 0 }, +#endif + + { NULL, 0, NULL, 0, NULL, 0 } +}; + +/* Short description of program. */ +static const char doc[] = N_("Combine object and archive files."); + +/* Strings for arguments in help texts. */ +static const char args_doc[] = N_("[FILE]..."); + +/* Prototype for option handler. */ +static error_t parse_opt_1st (int key, char *arg, struct argp_state *state); +static error_t parse_opt_2nd (int key, char *arg, struct argp_state *state); + +/* Data structure to communicate with argp functions. */ +static struct argp argp_1st = +{ + options, parse_opt_1st, args_doc, doc, NULL, NULL, NULL +}; +static struct argp argp_2nd = +{ + options, parse_opt_2nd, args_doc, doc, NULL, NULL, NULL +}; + + +/* Linker state. This contains all global information. */ +struct ld_state ld_state; + +/* List of the input files. */ +static struct file_list +{ + const char *name; + struct file_list *next; +} *input_file_list; + +/* If nonzero be verbose. */ +int verbose; + +/* If nonzero, trade speed for less memory/address space usage. */ +int conserve_memory; + +/* The emulation name to use. */ +static const char *emulation; + +/* Keep track of the nesting level. Even though we don't handle nested + groups we still keep track to improve the error messages. */ +static int group_level; + +/* The last file we processed. */ +static struct usedfiles *last_file; + +/* The default linker script. */ +/* XXX We'll do this a bit different in the real solution. */ +static const char *linker_script = SRCDIR "/elf32-i386.script"; + +/* Nonzero if an error occurred while loading the input files. */ +static int error_loading; + + +/* Intermediate storage for the LD_LIBRARY_PATH information from the + environment. */ +static char *ld_library_path1; + +/* Flag used to communicate with the scanner. */ +int ld_scan_version_script; + +/* Name of the input file. */ +const char *ldin_fname; + +/* Define by parser if required. */ +extern int lddebug; + + +/* Prototypes for local functions. */ +static void parse_z_option (const char *arg); +static void parse_z_option_2 (const char *arg); +static void parse_B_option (const char *arg); +static void parse_B_option_2 (const char *arg); +static void determine_output_format (void); +static void load_needed (void); +static void collect_sections (void); +static void add_rxxpath (struct pathelement **pathp, const char *str); +static void gen_rxxpath_data (void); +static void read_version_script (const char *fname); +static void create_lscript_symbols (void); +static void create_special_section_symbol (struct symbol **symp, + const char *name); + + +int +main (int argc, char *argv[]) +{ + int remaining; + int err; + +#ifndef NDEBUG + /* Enable memory debugging. */ + mtrace (); +#endif + + /* Sanity check. We always want to use the LFS functionality. */ + if (sizeof (off_t) != sizeof (off64_t)) + abort (); + + /* We use no threads here which can interfere with handling a stream. */ + __fsetlocking (stdin, FSETLOCKING_BYCALLER); + __fsetlocking (stdout, FSETLOCKING_BYCALLER); + __fsetlocking (stderr, FSETLOCKING_BYCALLER); + + /* Set locale. */ + setlocale (LC_ALL, ""); + + /* Make sure the message catalog can be found. */ + bindtextdomain (PACKAGE, LOCALEDIR); + + /* Initialize the message catalog. */ + textdomain (PACKAGE); + + /* Before we start tell the ELF library which version we are using. */ + elf_version (EV_CURRENT); + + /* The user can use the LD_LIBRARY_PATH environment variable to add + additional lookup directories. */ + ld_library_path1 = getenv ("LD_LIBRARY_PATH"); + + /* Initialize the memory handling. */ +#define obstack_chunk_alloc xmalloc +#define obstack_chunk_free free + obstack_init (&ld_state.smem); + + /* One quick pass over the parameters which allows us to scan for options + with global effect which influence the rest of the processing. */ + argp_parse (&argp_1st, argc, argv, ARGP_IN_ORDER, &remaining, NULL); + + /* We need at least one input file. */ + if (input_file_list == NULL) + { + error (0, 0, gettext ("At least one input file needed")); + argp_help (&argp_1st, stderr, ARGP_HELP_SEE, "ld"); + exit (EXIT_FAILURE); + } + + /* Determine which ELF backend to use. */ + determine_output_format (); + + /* Prepare state. */ + err = ld_prepare_state (emulation); + if (err != 0) + error (EXIT_FAILURE, 0, gettext ("error while preparing linking")); + + /* XXX Read the linker script now. Since we later will have the linker + script built in we don't go into trouble to make sure we handle GROUP + statements in the script. This simply must not happen. */ + ldin = fopen (linker_script, "r"); + if (ldin == NULL) + error (EXIT_FAILURE, errno, gettext ("cannot open linker script \"%s\""), + linker_script); + /* No need for locking. */ + __fsetlocking (ldin, FSETLOCKING_BYCALLER); + + ld_state.srcfiles = NULL; + ldlineno = 1; + ld_scan_version_script = 0; + ldin_fname = linker_script; + if (ldparse () != 0) + /* Something went wrong during parsing. */ + exit (EXIT_FAILURE); + fclose (ldin); + + /* We now might have a list of directories to look for libraries in + named by the linker script. Put them in a different list so that + they are searched after all paths given by the user on the + command line. */ + ld_state.default_paths = ld_state.paths; + ld_state.paths = ld_state.tailpaths = NULL; + + /* Get runpath/rpath information in usable form. */ + gen_rxxpath_data (); + + /* Parse and process arguments for real. */ + argp_parse (&argp_2nd, argc, argv, ARGP_IN_ORDER, &remaining, NULL); + /* All options should have been processed by the argp parser. */ + assert (remaining == argc); + + /* Process the last file. */ + while (last_file != NULL) + /* Try to open the file. */ + error_loading |= FILE_PROCESS (-1, last_file, &ld_state, &last_file); + + /* Stop if there has been a problem while reading the input files. */ + if (error_loading) + exit (error_loading); + + /* See whether all opened -( were closed. */ + if (group_level > 0) + { + error (0, 0, gettext ("-( without matching -)")); + argp_help (&argp_1st, stderr, ARGP_HELP_SEE, "ld"); + exit (EXIT_FAILURE); + } + + /* When we create a relocatable file we don't have to look for the + DT_NEEDED DSOs and we also don't test for undefined symbols. */ + if (ld_state.file_type != relocatable_file_type) + { + /* At this point we have loaded all the direct dependencies. What + remains to be done is find the indirect dependencies. These are + DSOs which are referenced by the DT_NEEDED entries in the DSOs + which are direct dependencies. We have to transitively find and + load all these dependencies. */ + load_needed (); + + /* At this point all object files and DSOs are read. If there + are still undefined symbols left they might have to be + synthesized from the linker script. */ + create_lscript_symbols (); + + /* Now that we have loaded all the object files we can determine + whether we have any non-weak unresolved references left. If + there are any we stop. If the user used the '-z nodefs' option + and we are creating a DSO don't perform the tests. */ + if (FLAG_UNRESOLVED (&ld_state) != 0) + exit (1); + } + + /* Collect information about the relocations which will be carried + forward into the output. We have to do this here and now since + we need to know which sections have to be created. */ + if (ld_state.file_type != relocatable_file_type) + { + void *p ; + struct scnhead *h; + + p = NULL; + while ((h = ld_section_tab_iterate (&ld_state.section_tab, &p)) != NULL) + if (h->type == SHT_REL || h->type == SHT_RELA) + { + struct scninfo *runp = h->last; + do + { + /* If we are processing the relocations determine how + many will be in the output file. Also determine + how many GOT entries are needed. */ + COUNT_RELOCATIONS (&ld_state, runp); + + ld_state.relsize_total += runp->relsize; + } + while ((runp = runp->next) != h->last); + } + } + + /* Not part of the gABI, but part of every psABI: the symbols for the + GOT section. Add the symbol if necessary. */ + if (ld_state.need_got) + create_special_section_symbol (&ld_state.got_symbol, + "_GLOBAL_OFFSET_TABLE_"); + /* Similarly for the _DYNAMIC symbol which points to the dynamic + section. */ + if (dynamically_linked_p ()) + create_special_section_symbol (&ld_state.dyn_symbol, "_DYNAMIC"); + + /* We are ready to start working on the output file. Not all + information has been gather or created yet. This will be done as + we go. Open the file now. */ + if (OPEN_OUTFILE (&ld_state, EM_NONE, ELFCLASSNONE, ELFDATANONE) != 0) + exit (1); + + /* Create the sections which are generated by the linker and are not + present in the input file. The output file must already have + been opened since we need the ELF descriptor to deduce type + sizes. */ + GENERATE_SECTIONS (&ld_state); + + /* At this point we have read all the files and know all the + sections which have to be linked into the application. We do now + create an array listing all the sections. We will than pass this + array to a system specific function which can reorder it at will. + The functions can also merge sections if this is what is + wanted. */ + collect_sections (); + + /* Create the output sections now. This may requires sorting them + first. */ + CREATE_SECTIONS (&ld_state); + + /* Create the output file data. Appropriate code for the selected + output file type is called. */ + if (CREATE_OUTFILE (&ld_state) != 0) + exit (1); + + /* Finalize the output file, write the data out. */ + err |= FINALIZE (&ld_state); + + /* Return with an non-zero exit status also if any error message has + been printed. */ + return err | (error_message_count != 0); +} + + +/* Quick scan of the parameter list for options with global effect. */ +static error_t +parse_opt_1st (int key, char *arg, + struct argp_state *state __attribute__ ((unused))) +{ + switch (key) + { + case 'B': + parse_B_option (arg); + break; + + case 'c': + linker_script = arg; + break; + + case 'E': + ld_state.export_all_dynamic = true; + break; + + case 'G': + if (ld_state.file_type != no_file_type) + error (EXIT_FAILURE, 0, + gettext ("only one option of -G and -r is allowed")); + ld_state.file_type = dso_file_type; + + /* If we generate a DSO we have to export all symbols. */ + ld_state.export_all_dynamic = true; + break; + + case 'h': + ld_state.soname = arg; + break; + + case 'i': + /* Discard the LD_LIBRARY_PATH value we found. */ + ld_library_path1 = NULL; + break; + + case 'I': + ld_state.interp = arg; + break; + + case 'm': + if (emulation != NULL) + error (EXIT_FAILURE, 0, gettext ("more than one '-m' parameter")); + emulation = arg; + break; + + case 'Q': + if (arg[1] == '\0' && (arg[0] == 'y' || arg[0] == 'Y')) + ld_state.add_ld_comment = true; + else if (arg[1] == '\0' && (arg[0] == 'n' || arg[0] == 'N')) + ld_state.add_ld_comment = true; + else + error (EXIT_FAILURE, 0, gettext ("unknown option `-%c %s'"), 'Q', arg); + break; + + case 'r': + if (ld_state.file_type != no_file_type) + error (EXIT_FAILURE, 0, + gettext ("only one option of -G and -r is allowed")); + ld_state.file_type = relocatable_file_type; + break; + + case 'S': + ld_state.strip = strip_debug; + break; + + case 't': + ld_state.trace_files = true; + break; + + case 'v': + verbose = 1; + break; + + case 'z': + /* The SysV linker used 'z' to pass various flags to the linker. + We follow this. See 'parse_z_option' for the options we + recognize. */ + parse_z_option (arg); + break; + + case ARGP_pagesize: + { + char *endp; + ld_state.pagesize = strtoul (arg, &endp, 0); + if (*endp != '\0') + { + if (endp[1] == '\0' && tolower (*endp) == 'k') + ld_state.pagesize *= 1024; + else if (endp[1] == '\0' && tolower (*endp) == 'm') + ld_state.pagesize *= 1024 * 1024; + else + { + error (0, 0, + gettext ("invalid page size value \"%s\": ignored"), + arg); + ld_state.pagesize = 0; + } + } + } + break; + + case ARGP_rpath: + add_rxxpath (&ld_state.rpath, arg); + break; + + case ARGP_rpath_link: + add_rxxpath (&ld_state.rpath_link, arg); + break; + + case ARGP_runpath: + add_rxxpath (&ld_state.runpath, arg); + break; + + case ARGP_runpath_link: + add_rxxpath (&ld_state.runpath_link, arg); + break; + + case ARGP_gc_sections: + case ARGP_no_gc_sections: + ld_state.gc_sections = key == ARGP_gc_sections; + break; + + case 's': + if (arg == NULL) + { + if (ld_state.strip == strip_all) + ld_state.strip = strip_everything; + else + ld_state.strip = strip_all; + break; + } + /* FALLTHROUGH */ + + case 'e': + case 'o': + case 'O': + case ARGP_whole_archive: + case ARGP_no_whole_archive: + case 'L': + case '(': + case ')': + case 'l': + case ARGP_static: + case ARGP_dynamic: + case ARGP_version_script: + /* We'll handle these in the second pass. */ + break; + + case ARGP_KEY_ARG: + { + struct file_list *newp; + + newp = (struct file_list *) xmalloc (sizeof (struct file_list)); + newp->name = arg; +#ifndef NDEBUG + newp->next = NULL; +#endif + CSNGL_LIST_ADD_REAR (input_file_list, newp); + } + break; + +#if YYDEBUG + case ARGP_yydebug: + lddebug = 1; + break; +#endif + + case ARGP_no_undefined: + ld_state.nodefs = false; + break; + + case ARGP_conserve: + conserve_memory = 1; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + + +/* Handle program arguments for real. */ +static error_t +parse_opt_2nd (int key, char *arg, + struct argp_state *state __attribute__ ((unused))) +{ + static bool group_start_requested; + static bool group_end_requested; + + switch (key) + { + case 'B': + parse_B_option_2 (arg); + break; + + case 'e': + ld_state.entry = arg; + break; + + case 'o': + if (ld_state.outfname != NULL) + { + error (0, 0, gettext ("More than one output file name given.")); + see_help: + argp_help (&argp_2nd, stderr, ARGP_HELP_SEE, "ld"); + exit (EXIT_FAILURE); + } + ld_state.outfname = arg; + break; + + case 'O': + if (arg == NULL) + ld_state.optlevel = 1; + else + { + char *endp; + unsigned long int level = strtoul (arg, &endp, 10); + if (*endp != '\0') + { + error (0, 0, gettext ("Invalid optimization level `%s'"), arg); + goto see_help; + } + ld_state.optlevel = level; + } + break; + + case ARGP_whole_archive: + ld_state.extract_rule = allextract; + break; + case ARGP_no_whole_archive: + ld_state.extract_rule = defaultextract; + break; + + case ARGP_static: + case ARGP_dynamic: + /* Enable/disable use for DSOs. */ + ld_state.statically = key == ARGP_static; + break; + + case 'z': + /* The SysV linker used 'z' to pass various flags to the linker. + We follow this. See 'parse_z_option' for the options we + recognize. */ + parse_z_option_2 (arg); + break; + + case ARGP_version_script: + read_version_script (arg); + break; + + case 'L': + /* Add a new search directory. */ + ld_new_searchdir (arg); + break; + + case '(': + /* Start a link group. We have to be able to determine the object + file which is named next. Do this by remembering a pointer to + the pointer which will point to the next object. */ + if (verbose && (group_start_requested || !group_end_requested)) + error (0, 0, gettext ("nested -( -) groups are not allowed")); + + /* Increment the nesting level. */ + ++group_level; + + /* Record group start. */ + group_start_requested = true; + group_end_requested = false; + break; + + case ')': + /* End a link group. If there is no group open this is clearly + a bug. If there is a group open insert a back reference + pointer in the record for the last object of the group. If + there is no new object or just one don't do anything. */ + if (!group_end_requested) + { + if (group_level == 0) + { + error (0, 0, gettext ("-) without matching -(")); + goto see_help; + } + } + else + last_file->group_end = true; + + if (group_level > 0) + --group_level; + break; + + case 'l': + case ARGP_KEY_ARG: + { + while (last_file != NULL) + /* Try to open the file. */ + error_loading |= FILE_PROCESS (-1, last_file, &ld_state, &last_file); + + last_file = ld_new_inputfile (arg, + key == 'l' + ? archive_file_type + : relocatable_file_type); + if (group_start_requested) + { + last_file->group_start = true; + + group_start_requested = false; + group_end_requested = true; + } + } + break; + + default: + /* We can catch all other options here. They either have + already been handled or, if the parameter was not correct, + the error has been reported. */ + break; + } + return 0; +} + + +/* Load all the DSOs named as dependencies in other DSOs we already + loaded. */ +static void +load_needed (void) +{ + struct usedfiles *first; + struct usedfiles *runp; + + /* XXX There is one problem here: do we allow references from + regular object files to be satisfied by these implicit + dependencies? The old linker allows this and several libraries + depend on this. Solaris' linker does not allow this; it provides + the user with a comprehensive error message explaining the + situation. + + XXX IMO the old ld behavior is correct since this is also how the + dynamic linker will work. It will look for unresolved references + in all loaded DSOs. + + XXX Should we add an option to get Solaris compatibility? */ + if (ld_state.needed == NULL) + return; + + runp = first = ld_state.needed->next; + do + { + struct usedfiles *ignore; + struct usedfiles *next = runp->next; + int err; + + err = FILE_PROCESS (-1, runp, &ld_state, &ignore); + if (err != 0) + /* Something went wrong. */ + exit (err); + + runp = next; + } + while (runp != first); +} + + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) +{ + fprintf (stream, "ld (%s) %s\n", PACKAGE_NAME, VERSION); + fprintf (stream, gettext ("\ +Copyright (C) %s Red Hat, Inc.\n\ +This is free software; see the source for copying conditions. There is NO\n\ +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ +"), "2005"); + fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); +} + + +/* There are a lot of -z options, parse them here. Some of them have + to be parsed in the first pass, others must be handled in the + second pass. */ +static void +parse_z_option (const char *arg) +{ + if (strcmp (arg, "nodefaultlib") == 0 + /* This is only meaningful if we create a DSO. */ + && ld_state.file_type == dso_file_type) + ld_state.dt_flags_1 |= DF_1_NODEFLIB; + else if (strcmp (arg, "muldefs") == 0) + ld_state.muldefs = true; + else if (strcmp (arg, "nodefs") == 0) + ld_state.nodefs = true; + else if (strcmp (arg, "defs") == 0) + ld_state.nodefs = false; + else if (strcmp (arg, "now") == 0) + /* We could also set the DF_1_NOW flag in DT_FLAGS_1 but this isn't + necessary. */ + ld_state.dt_flags |= DF_BIND_NOW; + else if (strcmp (arg, "origin") == 0) + /* We could also set the DF_1_ORIGIN flag in DT_FLAGS_1 but this isn't + necessary. */ + ld_state.dt_flags |= DF_ORIGIN; + else if (strcmp (arg, "nodelete") == 0 + /* This is only meaningful if we create a DSO. */ + && ld_state.file_type == dso_file_type) + ld_state.dt_flags_1 |= DF_1_NODELETE; + else if (strcmp (arg, "initfirst") == 0) + ld_state.dt_flags_1 |= DF_1_INITFIRST; + else if (strcmp (arg, "nodlopen") == 0 + /* This is only meaningful if we create a DSO. */ + && ld_state.file_type == dso_file_type) + ld_state.dt_flags_1 |= DF_1_NOOPEN; + else if (strcmp (arg, "ignore") == 0) + ld_state.ignore_unused_dsos = true; + else if (strcmp (arg, "record") == 0) + ld_state.ignore_unused_dsos = false; + else if (strcmp (arg, "systemlibrary") == 0) + ld_state.is_system_library = true; + else if (strcmp (arg, "allextract") != 0 + && strcmp (arg, "defaultextract") != 0 + && strcmp (arg, "weakextract") != 0 + && strcmp (arg, "lazyload") != 0 + && strcmp (arg, "nolazyload") != 0) + error (0, 0, gettext ("unknown option `-%c %s'"), 'z', arg); +} + + +static void +parse_z_option_2 (const char *arg) +{ + if (strcmp (arg, "allextract") == 0) + ld_state.extract_rule = allextract; + else if (strcmp (arg, "defaultextract") == 0) + ld_state.extract_rule = defaultextract; + else if (strcmp (arg, "weakextract") == 0) + ld_state.extract_rule = weakextract; + else if (strcmp (arg, "lazyload") == 0) + ld_state.lazyload = true; + else if (strcmp (arg, "nolazyload") == 0) + ld_state.lazyload = false; +} + + +/* There are a lot of -B options, parse them here. */ +static void +parse_B_option (const char *arg) +{ + if (strcmp (arg, "local") == 0) + ld_state.default_bind_local = true; + else if (strcmp (arg, "symbolic") != 0 + && strcmp (arg, "static") != 0 + && strcmp (arg, "dynamic") != 0) + error (0, 0, gettext ("unknown option '-%c %s'"), 'B', arg); +} + + +/* The same functionality, but called in the second pass over the + parameters. */ +static void +parse_B_option_2 (const char *arg) +{ + if (strcmp (arg, "static") == 0) + ld_state.statically = true; + else if (strcmp (arg, "dynamic") == 0) + ld_state.statically = false; + else if (strcmp (arg, "symbolic") == 0 + /* This is only meaningful if we create a DSO. */ + && ld_state.file_type == dso_file_type) + ld_state.dt_flags |= DF_SYMBOLIC; +} + + +static void +determine_output_format (void) +{ + /* First change the 'input_file_list' variable in a simple + single-linked list. */ + struct file_list *last = input_file_list; + input_file_list = input_file_list->next; + last->next = NULL; + + /* Determine the target configuration which we are supposed to use. + The user can use the '-m' option to select one. If this is + missing we are trying to load one file and determine the + architecture from that. */ + if (emulation != NULL) + { + ld_state.ebl = ebl_openbackend_emulation (emulation); + + assert (ld_state.ebl != NULL); + } + else + { + /* Find an ELF input file and let it determine the ELf backend. */ + struct file_list *runp = input_file_list; + + while (runp != NULL) + { + int fd = open (runp->name, O_RDONLY); + if (fd != -1) + { + int try (Elf *elf) + { + int result = 0; + + if (elf == NULL) + return 0; + + if (elf_kind (elf) == ELF_K_ELF) + { + /* We have an ELF file. We now can find out + what the output format should be. */ + XElf_Ehdr_vardef(ehdr); + + /* Get the ELF header of the object. */ + xelf_getehdr (elf, ehdr); + if (ehdr != NULL) + ld_state.ebl = + ebl_openbackend_machine (ehdr->e_machine); + + result = 1; + } + else if (elf_kind (elf) == ELF_K_AR) + { + /* Try the archive members. This could + potentially lead to wrong results if the + archive contains files for more than one + architecture. But this is the user's + problem. */ + Elf *subelf; + Elf_Cmd cmd = ELF_C_READ_MMAP; + + while ((subelf = elf_begin (fd, cmd, elf)) != NULL) + { + cmd = elf_next (subelf); + + if (try (subelf) != 0) + break; + } + } + + elf_end (elf); + + return result; + } + + if (try (elf_begin (fd, ELF_C_READ_MMAP, NULL)) != 0) + /* Found a file. */ + break; + } + + runp = runp->next; + } + + if (ld_state.ebl == NULL) + { + error (0, 0, gettext ("\ +could not find input file to determine output file format")); + error (EXIT_FAILURE, 0, gettext ("\ +try again with an appropriate '-m' parameter")); + } + } + + /* We don't need the list of input files anymore. The second run over + the parameters will handle them. */ + while (input_file_list != NULL) + { + struct file_list *oldp = input_file_list; + input_file_list = input_file_list->next; + free (oldp); + } + + /* We also know now what kind of file we are supposed to create. If + the user hasn't selected anythign we create and executable. */ + if (ld_state.file_type == no_file_type) + ld_state.file_type = executable_file_type; +} + +/* Add DIR to the list of directories searched for object files and + libraries. */ +void +ld_new_searchdir (const char *dir) +{ + struct pathelement *newpath; + + newpath = (struct pathelement *) + obstack_calloc (&ld_state.smem, sizeof (struct pathelement)); + + newpath->pname = dir; + + /* Enqueue the file. */ + if (ld_state.tailpaths == NULL) + ld_state.paths = ld_state.tailpaths = newpath; + else + { + ld_state.tailpaths->next = newpath; + ld_state.tailpaths = newpath; + } +} + + +struct usedfiles * +ld_new_inputfile (const char *fname, enum file_type type) +{ + struct usedfiles *newfile = (struct usedfiles *) + obstack_calloc (&ld_state.smem, sizeof (struct usedfiles)); + + newfile->soname = newfile->fname = newfile->rfname = fname; + newfile->file_type = type; + newfile->extract_rule = ld_state.extract_rule; + newfile->lazyload = ld_state.lazyload; + newfile->status = not_opened; + + return newfile; +} + + +/* Create an array listing all the sections. We will than pass this + array to a system specific function which can reorder it at will. + The functions can also merge sections if this is what is + wanted. */ +static void +collect_sections (void) +{ + void *p ; + struct scnhead *h; + size_t cnt; + + /* We have that many sections. At least for now. */ + ld_state.nallsections = ld_state.section_tab.filled; + + /* Allocate the array. We allocate one more entry than computed so + far since we might need a new section for the copy relocations. */ + ld_state.allsections = + (struct scnhead **) obstack_alloc (&ld_state.smem, + (ld_state.nallsections + 1) + * sizeof (struct scnhead *)); + + /* Fill the array. We rely here on the hash table iterator to + return the entries in the order they were added. */ + cnt = 0; + p = NULL; + while ((h = ld_section_tab_iterate (&ld_state.section_tab, &p)) != NULL) + { + struct scninfo *runp; + bool used = false; + + if (h->kind == scn_normal) + { + runp = h->last; + do + { + if (h->type == SHT_REL || h->type == SHT_RELA) + { + if (runp->used) + /* This is a relocation section. If the section + it is relocating is used in the result so must + the relocation section. */ + runp->used + = runp->fileinfo->scninfo[SCNINFO_SHDR (runp->shdr).sh_info].used; + } + + /* Accumulate the result. */ + used |= runp->used; + + /* Next input section. */ + runp = runp->next; + } + while (runp != h->last); + + h->used = used; + } + + ld_state.allsections[cnt++] = h; + } + ld_state.nusedsections = cnt; + + assert (cnt == ld_state.nallsections); +} + + +/* Add given path to the end of list. */ +static void +add_rxxpath (struct pathelement **pathp, const char *str) +{ + struct pathelement *newp; + + /* The path elements can in theory be freed after we read all the + files. But the amount of memory we are talking about is small + and the cost of free() calls is not neglectable. */ + newp = (struct pathelement *) obstack_alloc (&ld_state.smem, sizeof (*newp)); + newp->pname = str; + newp->exist = 0; +#ifndef NDEBUG + newp->next = NULL; +#endif + + CSNGL_LIST_ADD_REAR (*pathp, newp); +} + + +/* Convert lists of possibly colon-separated directory lists into lists + where each entry is for a single directory. */ +static void +normalize_dirlist (struct pathelement **pathp) +{ + struct pathelement *firstp = *pathp; + + do + { + const char *pname = (*pathp)->pname; + const char *colonp = strchrnul (pname, ':'); + + if (colonp != NULL) + { + struct pathelement *lastp = *pathp; + struct pathelement *newp; + + while (1) + { + if (colonp == pname) + lastp->pname = "."; + else + lastp->pname = obstack_strndup (&ld_state.smem, pname, + colonp - pname); + + if (*colonp == '\0') + break; + pname = colonp + 1; + + newp = (struct pathelement *) obstack_alloc (&ld_state.smem, + sizeof (*newp)); + newp->next = lastp->next; + newp->exist = 0; + lastp = lastp->next = newp; + + colonp = strchrnul (pname, ':'); + } + + pathp = &lastp->next; + } + else + pathp = &(*pathp)->next; + } + while (*pathp != firstp); +} + + +/* Called after all parameters are parsed to bring the runpath/rpath + information into a usable form. */ +static void +gen_rxxpath_data (void) +{ + char *ld_library_path2; + + /* Convert the information in true single-linked lists for easy use. + At this point we also discard the rpath information if runpath + information is provided. rpath is deprecated and should not be + used (or ever be invented for that matter). */ + if (ld_state.rpath != NULL) + { + struct pathelement *endp = ld_state.rpath; + ld_state.rpath = ld_state.rpath->next; + endp->next = NULL; + } + if (ld_state.rpath_link != NULL) + { + struct pathelement *endp = ld_state.rpath_link; + ld_state.rpath_link = ld_state.rpath_link->next; + endp->next = NULL; + } + + if (ld_state.runpath != NULL) + { + struct pathelement *endp = ld_state.runpath; + ld_state.runpath = ld_state.runpath->next; + endp->next = NULL; + + /* If rpath information is also available discard it. + XXX Should there be a possibility to avoid this? */ + while (ld_state.rpath != NULL) + { + struct pathelement *old = ld_state.rpath; + ld_state.rpath = ld_state.rpath->next; + free (old); + } + } + if (ld_state.runpath_link != NULL) + { + struct pathelement *endp = ld_state.runpath_link; + ld_state.runpath_link = ld_state.runpath_link->next; + endp->next = NULL; + + /* If rpath information is also available discard it. + XXX Should there be a possibility to avoid this? */ + while (ld_state.rpath_link != NULL) + { + struct pathelement *old = ld_state.rpath_link; + ld_state.rpath_link = ld_state.rpath_link->next; + free (old); + } + + /* The information in the strings in the list can actually be + directory lists themselves, with entries separated by colons. + Convert the list now to a list with one list entry for each + directory. */ + normalize_dirlist (&ld_state.runpath_link); + } + else if (ld_state.rpath_link != NULL) + /* Same as for the runpath_link above. */ + normalize_dirlist (&ld_state.rpath_link); + + + /* As a related task, handle the LD_LIBRARY_PATH value here. First + we have to possibly split the value found (if it contains a + semicolon). Then we have to split the value in list of + directories, i.e., split at the colons. */ + if (ld_library_path1 != NULL) + { + ld_library_path2 = strchr (ld_library_path1, ';'); + if (ld_library_path2 == NULL) + { + /* If no semicolon is present the directories are looked at + after the -L parameters (-> ld_library_path2). */ + ld_library_path2 = ld_library_path1; + ld_library_path1 = NULL; + } + else + { + /* NUL terminate the first part. */ + *ld_library_path2++ = '\0'; + + /* Convert the string value in a list. */ + add_rxxpath (&ld_state.ld_library_path1, ld_library_path1); + normalize_dirlist (&ld_state.ld_library_path1); + } + + add_rxxpath (&ld_state.ld_library_path2, ld_library_path2); + normalize_dirlist (&ld_state.ld_library_path2); + } +} + + +static void +read_version_script (const char *fname) +{ + /* Open the file. The name is supposed to be the complete (relative + or absolute) path. No search along a path will be performed. */ + ldin = fopen (fname, "r"); + if (ldin == NULL) + error (EXIT_FAILURE, errno, gettext ("cannot read version script \"%s\""), + fname); + /* No need for locking. */ + __fsetlocking (ldin, FSETLOCKING_BYCALLER); + + /* Tell the parser that this is a version script. */ + ld_scan_version_script = 1; + + ldlineno = 1; + ldin_fname = fname; + if (ldparse () != 0) + /* Something went wrong during parsing. */ + exit (EXIT_FAILURE); + + fclose (ldin); +} + + +static void +create_lscript_symbols (void) +{ + /* Walk through the data from the linker script and generate all the + symbols which are required to be present and and those marked + with PROVIDE if there is a undefined reference. */ + if (ld_state.output_segments == NULL) + return; + + struct output_segment *segment = ld_state.output_segments->next; + do + { + struct output_rule *orule; + + for (orule = segment->output_rules; orule != NULL; orule = orule->next) + if (orule->tag == output_assignment + /* The assignments to "." (i.e., the PC) have to be + ignored here. */ + && strcmp (orule->val.assignment->variable, ".") != 0) + { + struct symbol *s = ld_state.unresolved; + + /* Check whether the symbol is needed. */ + if (likely (s != NULL)) + { + struct symbol *first = s; + const char *providename = orule->val.assignment->variable; + + /* Determine whether the provided symbol is still + undefined. */ + // XXX TODO Loop inside a loop. Gag! Must rewrite. */ + do + if (strcmp (s->name, providename) == 0) + { + /* Not defined but referenced. */ + if (unlikely (!s->defined)) + { + /* Put on the list of symbols. First remove it from + whatever list it currently is on. */ + CDBL_LIST_DEL (ld_state.unresolved, s); + --ld_state.nunresolved; + goto use_it; + } + + if (unlikely (!orule->val.assignment->provide_flag)) + { + /* The symbol is already defined and now again + in the linker script. This is an error. */ + error (0, 0, gettext ("\ +duplicate definition of '%s' in linker script"), + providename); + goto next_rule; + } + } + while ((s = s->next) != first); + } + + /* If the symbol only has to be provided if it is needed, + ignore it here since it is not undefined. */ + if (orule->val.assignment->provide_flag) + continue; + + /* Allocate memory for this new symbol. */ + s = (struct symbol *) + obstack_calloc (&ld_state.smem, sizeof (struct symbol)); + + /* Initialize it. */ + s->name = orule->val.assignment->variable; + + /* Insert it into the symbol hash table. */ + unsigned long int hval = elf_hash (s->name); + if (unlikely (ld_symbol_tab_insert (&ld_state.symbol_tab, + hval, s) != 0)) + { + /* This means the symbol is defined somewhere else. + Maybe it comes from a DSO or so. Get the + definition. */ + free (s); + struct symbol *old = ld_symbol_tab_find (&ld_state.symbol_tab, + hval, s); + assert (old != NULL); + free (s); + + /* If this is a definition from the application itself + this means a duplicate definition. */ + if (! old->in_dso) + { + error (0, 0, gettext ("\ +duplicate definition of '%s' in linker script"), + s->name); + goto next_rule; + } + + /* We use the definition from the linker script. */ + s = old; + } + + use_it: + /* The symbol is (now) defined. */ + s->defined = 1; + s->type = STT_NOTYPE; + + /* Add a reference to the symbol record. We will come + across it when creating the output file. */ + orule->val.assignment->sym = s; + + SNGL_LIST_PUSH (ld_state.lscript_syms, s); + ++ld_state.nlscript_syms; + + next_rule: + ; + } + + segment = segment->next; + } + while (segment != ld_state.output_segments->next); +} + + +/* Create creation of spection section symbols representing sections in the + output file. This is done for symbols like _GLOBAL_OFFSET_TABLE_ and + _DYNAMIC. */ +static void +create_special_section_symbol (struct symbol **symp, const char *name) +{ + if (*symp == NULL) + { + /* No symbol defined found yet. Create one. */ + struct symbol *newsym = (struct symbol *) + obstack_calloc (&ld_state.smem, sizeof (*newsym)); + + newsym->name = name; + // XXX Should we mark the symbol hidden? They are hardly useful + // used outside the current object. + + /* Add to the symbol table. */ + if (unlikely (ld_symbol_tab_insert (&ld_state.symbol_tab, + elf_hash (name), newsym) != 0)) + abort (); + + *symp = newsym; + } + else if ((*symp)->defined) + /* Cannot happen. We do use this symbol from any input file. */ + abort (); + + (*symp)->defined = 1; + (*symp)->type = STT_OBJECT; + + ++ld_state.nsymtab; +} |