root/usr/src/cmd/sgs/libld/common/ldmain.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 *      Copyright (c) 1988 AT&T
 *        All Rights Reserved
 *
 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
 *
 * Copyright 2020 Joyent, Inc.
 */

/*
 * Processing of relocatable objects and shared objects.
 */

/*
 * ld -- link/editor main program
 */
#include        <sys/types.h>
#include        <sys/time.h>
#include        <sys/mman.h>
#include        <string.h>
#include        <stdio.h>
#include        <locale.h>
#include        <stdarg.h>
#include        <debug.h>
#include        "msg.h"
#include        "_libld.h"

/*
 * All target specific code is referenced via this global variable, which
 * is initialized in ld_main(). This allows the linker to function as
 * a cross linker, by vectoring to the target-specific code for the
 * current target machine.
 */
Target          ld_targ;

/*
 * A default library search path is used if one was not supplied on the command
 * line.  Note: these strings can not use MSG_ORIG() since they are modified as
 * part of the path processing.
 */
#if     defined(_ELF64)
static char     def_Plibpath[] = "/lib/64:/usr/lib/64";
#else
static char     def_Plibpath[] = "/usr/ccs/lib:/lib:/usr/lib";
#endif

/*
 * A default elf header provides for simplifying diagnostic processing.
 */
static Ehdr     def_ehdr = { { ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3,
                            ELFCLASSNONE, ELFDATANONE }, 0, EM_NONE,
                            EV_CURRENT };

/*
 * ld-centric wrapper on top of veprintf():
 * - Accepts output descriptor rather than linkmap list
 * - Sets the FLG_OF_FATAL/FLG_OF_WARN flags as necessary
 */
void
ld_eprintf(Ofl_desc *ofl, Error error, const char *format, ...)
{
        va_list args;

        /* Set flag indicating type of error being issued */
        switch (error) {
        case ERR_NONE:
        case ERR_WARNING_NF:
                break;
        case ERR_WARNING:
                ofl->ofl_flags |= FLG_OF_WARN;
                break;
        case ERR_GUIDANCE:
                ofl->ofl_guideflags |= FLG_OFG_ISSUED;
                ofl->ofl_flags |= FLG_OF_WARN;
                break;
        default:
                ofl->ofl_flags |= FLG_OF_FATAL;
        }

        /* Issue the error */
        va_start(args, format);
        veprintf(ofl->ofl_lml, error, format, args);
        va_end(args);
}

/*
 * Establish the global state necessary to link the desired machine
 * target, as reflected by the ld_targ global variable.
 */
int
ld_init_target(Lm_list *lml, Half mach)
{
        switch (mach) {
        case EM_386:
        case EM_AMD64:
                ld_targ = *ld_targ_init_x86();
                break;

        case EM_SPARC:
        case EM_SPARC32PLUS:
        case EM_SPARCV9:
                ld_targ = *ld_targ_init_sparc();
                break;

        default:
                {
                        Conv_inv_buf_t  inv_buf;

                        eprintf(lml, ERR_FATAL, MSG_INTL(MSG_TARG_UNSUPPORTED),
                            conv_ehdr_mach(mach, 0, &inv_buf));
                        return (1);
                }
        }

        return (0);
}


/*
 * The main program
 */
int
ld_main(int argc, char **argv, Half mach)
{
        char            *sgs_support;   /* SGS_SUPPORT environment string */
        Half            etype;
        Ofl_desc        *ofl;
        ofl_flag_t      save_flg_of_warn;

        /*
         * Establish a base time.  Total time diagnostics are relative to
         * entering the link-editor here.
         */
        (void) gettimeofday(&DBG_TOTALTIME, NULL);
        DBG_DELTATIME = DBG_TOTALTIME;

        /* Output file descriptor */
        if ((ofl = libld_calloc(1, sizeof (Ofl_desc))) == NULL)
                return (1);

        /* Initialize target state */
        if (ld_init_target(NULL, mach) != 0)
                return (1);

        /*
         * Set up the default output ELF header to satisfy diagnostic
         * requirements, and initialize the machine and class details.
         */
        ofl->ofl_dehdr = &def_ehdr;
        def_ehdr.e_ident[EI_CLASS] = ld_targ.t_m.m_class;
        def_ehdr.e_ident[EI_DATA] = ld_targ.t_m.m_data;
        def_ehdr.e_machine = ld_targ.t_m.m_mach;

        /*
         * Build up linker version string
         */
        if ((ofl->ofl_sgsid = libld_calloc(MSG_SGS_ID_SIZE +
            strlen(link_ver_string) + 1, 1)) == NULL)
                return (1);
        (void) strcpy(ofl->ofl_sgsid, MSG_ORIG(MSG_SGS_ID));
        (void) strcat(ofl->ofl_sgsid, link_ver_string);

        /*
         * Argument pass one.  Get all the input flags (skip any files) and
         * check for consistency.  Return from ld_process_flags() marks the
         * end of mapfile processing.  The entrance criteria and segment
         * descriptors are complete and in their final form.
         */
        if (ld_process_flags(ofl, argc, argv) == S_ERROR) {
                /* If any ERR_GUIDANCE messages were issued, add a summary */
                if (ofl->ofl_guideflags & FLG_OFG_ISSUED)
                        ld_eprintf(ofl, ERR_GUIDANCE,
                            MSG_INTL(MSG_GUIDE_SUMMARY));
                return (1);
        }

        if (ofl->ofl_flags & FLG_OF_FATAL) {
                ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_FLAGS));
                /* If any ERR_GUIDANCE messages were issued, add a summary */
                if (ofl->ofl_guideflags & FLG_OFG_ISSUED)
                        ld_eprintf(ofl, ERR_GUIDANCE,
                            MSG_INTL(MSG_GUIDE_SUMMARY));
                return (1);
        }

        /*
         * At this point a call such as ld -V is considered complete.
         */
        if (ofl->ofl_flags1 & FLG_OF1_DONE)
                return (0);

        /* Initialize signal handler */
        ld_init_sighandler(ofl);

        /*
         * Determine whether any support libraries should be loaded,
         * (either through the SGS_SUPPORT environment variable and/or
         * through the -S option).
         */
#if     defined(_LP64)
        if ((sgs_support = getenv(MSG_ORIG(MSG_SGS_SUPPORT_64))) == NULL)
#else
        if ((sgs_support = getenv(MSG_ORIG(MSG_SGS_SUPPORT_32))) == NULL)
#endif
                sgs_support = getenv(MSG_ORIG(MSG_SGS_SUPPORT));

        if (sgs_support && sgs_support[0]) {
                const char      *sep = MSG_ORIG(MSG_STR_COLON);
                char            *lib;
                char            *lasts;

                DBG_CALL(Dbg_support_req(ofl->ofl_lml, sgs_support,
                    DBG_SUP_ENVIRON));
                if ((lib = strtok_r(sgs_support, sep, &lasts)) != NULL) {
                        do {
                                if (ld_sup_loadso(ofl, lib) == S_ERROR)
                                        return (ld_exit(ofl));
                                DBG_CALL(Dbg_util_nl(ofl->ofl_lml, DBG_NL_STD));

                        } while ((lib = strtok_r(NULL, sep, &lasts)) != NULL);
                }
        }
        if (lib_support) {
                Aliste  idx;
                char    *lib;

                for (APLIST_TRAVERSE(lib_support, idx, lib)) {
                        DBG_CALL(Dbg_support_req(ofl->ofl_lml, lib,
                            DBG_SUP_CMDLINE));
                        if (ld_sup_loadso(ofl, lib) == S_ERROR)
                                return (ld_exit(ofl));
                        DBG_CALL(Dbg_util_nl(ofl->ofl_lml, DBG_NL_STD));
                }
        }

        DBG_CALL(Dbg_ent_print(ofl->ofl_lml,
            ofl->ofl_dehdr->e_ident[EI_OSABI], ofl->ofl_dehdr->e_machine,
            ofl->ofl_ents));
        DBG_CALL(Dbg_seg_list(ofl->ofl_lml,
            ofl->ofl_dehdr->e_ident[EI_OSABI], ofl->ofl_dehdr->e_machine,
            ofl->ofl_segs));

        /*
         * The objscnt and soscnt variables were used to estimate the expected
         * input files, and size the symbol hash buckets accordingly.  Reset
         * these values now, so as to gain an accurate count from pass two, for
         * later statistics diagnostics.
         */
        ofl->ofl_objscnt = ofl->ofl_soscnt = 0;

        /*
         * Determine whether we can create the file before going any further.
         */
        if (ld_open_outfile(ofl) == S_ERROR)
                return (ld_exit(ofl));

        /*
         * If the user didn't supply a library path supply a default.  And, if
         * no run-path has been specified (-R), see if the environment variable
         * is in use (historic).
         */
        if (Plibpath == NULL)
                Plibpath = def_Plibpath;

        if (ofl->ofl_rpath == NULL) {
                char    *rpath;

                if (((rpath = getenv(MSG_ORIG(MSG_LD_RUN_PATH))) != NULL) &&
                    rpath[0])
                        ofl->ofl_rpath = rpath;
        }

        /*
         * Argument pass two.  Input all libraries and objects.
         */
        if (ld_lib_setup(ofl) == S_ERROR)
                return (ld_exit(ofl));

        /*
         * Call ld_start() with the etype of our output file and the
         * output file name.
         */
        if (ofl->ofl_flags & FLG_OF_SHAROBJ)
                etype = ET_DYN;
        else if (ofl->ofl_flags & FLG_OF_RELOBJ)
                etype = ET_REL;
        else
                etype = ET_EXEC;

        ld_sup_start(ofl, etype, argv[0]);

        /*
         * Process all input files.
         */
        if (ld_process_files(ofl, argc, argv) == S_ERROR)
                return (ld_exit(ofl));
        if (ofl->ofl_flags & FLG_OF_FATAL) {
                ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_FILES),
                    ofl->ofl_name);
                return (ld_exit(ofl));
        }

        ld_sup_input_done(ofl);

        /*
         * Now that all input section processing is complete, validate and
         * process any SHT_SUNW_move sections.
         */
        if (ofl->ofl_ismove && (ld_process_move(ofl) == S_ERROR))
                return (ld_exit(ofl));

        /*
         * Before validating all symbols count the number of relocation entries.
         * If copy relocations exist, COMMON symbols must be generated which are
         * assigned to the executables .bss.  During sym_validate() the actual
         * size and alignment of the .bss is calculated.  Doing things in this
         * order reduces the number of symbol table traversals required (however
         * it does take a little longer for the user to be told of any undefined
         * symbol errors).
         */
        if (ld_reloc_init(ofl) == S_ERROR)
                return (ld_exit(ofl));

        /*
         * We need to know if FLG_OF_WARN is currently set, in case
         * we need to honor a -z fatal-warnings request. However, we also
         * need to know if a warning due to symbol validation results from
         * the upcoming call to ld_sym_validate() in order to issue the
         * appropriate message for it. So we save the current value,
         * and clear the main flag.
         */
        save_flg_of_warn = ofl->ofl_flags & FLG_OF_WARN;
        ofl->ofl_flags &= ~FLG_OF_WARN;

        if (ld_sym_validate(ofl) == S_ERROR)
                return (ld_exit(ofl));

        /*
         * Now that all symbol processing is complete see if any undefined
         * references still remain.  If we observed undefined symbols the
         * FLG_OF_FATAL bit will be set:  If creating a static executable, or a
         * dynamic executable or shared object with the -zdefs flag set, this
         * condition is fatal.  If creating a shared object with the -Bsymbolic
         * flag set, this condition is simply a warning.
         */
        if (ofl->ofl_flags & FLG_OF_FATAL)
                ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_SYM_FATAL),
                    ofl->ofl_name);
        else if (ofl->ofl_flags & FLG_OF_WARN)
                ld_eprintf(ofl, ERR_WARNING, MSG_INTL(MSG_ARG_SYM_WARN));

        /*
         * Guidance: Use -z defs|nodefs when building shared objects.
         *
         * ld_sym_validate() will mask this guidance message out unless we are
         * intended to send it here, so all we need to do is use OFL_GUIDANCE()
         * to decide whether to issue it or not.
         */
        if (OFL_GUIDANCE(ofl, FLG_OFG_NO_DEFS))
                ld_eprintf(ofl, ERR_GUIDANCE, MSG_INTL(MSG_GUIDE_DEFS));

        /*
         * Symbol processing was the final step before we start producing the
         * output object. At this time, if we've seen warnings and the
         * -z fatal-warnings option is specified, promote them to fatal, which
         * will cause us to exit without creating an object.
         *
         * We didn't do this as the warnings were reported in order to
         * maximize the number of problems a given link-editor invocation
         * can diagnose. This is safe, since warnings are by definition events
         * one can choose to ignore.
         */
        if (((ofl->ofl_flags | save_flg_of_warn) &
            (FLG_OF_WARN | FLG_OF_FATWARN)) ==
            (FLG_OF_WARN | FLG_OF_FATWARN))
                ofl->ofl_flags |= FLG_OF_FATAL;

        /*
         * If fatal errors occurred in symbol processing, or due to warnings
         * promoted by -z fatal-warnings, this is the end of the line.
         */
        if (ofl->ofl_flags & FLG_OF_FATAL)
                return (ld_exit(ofl));

        /*
         * Generate any necessary sections.
         */
        if (ld_make_sections(ofl) == S_ERROR)
                return (ld_exit(ofl));

        /*
         * Now that all sections have been added to the output file, determine
         * whether any mapfile section ordering was specified, and verify that
         * all mapfile ordering directives have been matched.  Issue a warning
         * for any directives that have not been matched.
         * Also, if SHF_ORDERED sections exist, set up sort key values.
         */
        if (ofl->ofl_flags & (FLG_OF_OS_ORDER | FLG_OF_KEY))
                ld_sec_validate(ofl);

        /*
         * Having collected all the input data create the initial output file
         * image, assign virtual addresses to the image, and generate a load
         * map if the user requested one.
         */
        if (ld_create_outfile(ofl) == S_ERROR)
                return (ld_exit(ofl));

        if (ld_update_outfile(ofl) == S_ERROR)
                return (ld_exit(ofl));
        if (ofl->ofl_flags & FLG_OF_GENMAP)
                ld_map_out(ofl);

        /*
         * Build relocation sections and perform any relocation updates.
         */
        if (ld_reloc_process(ofl) == S_ERROR)
                return (ld_exit(ofl));

        /*
         * Fill in contents for unwind header (.eh_frame_hdr)
         */
        if (ld_unwind_populate_hdr(ofl) == S_ERROR)
                return (ld_exit(ofl));

        /*
         * Finally create the files elf checksum.
         */
        if (ofl->ofl_checksum)
                *ofl->ofl_checksum = (Xword)elf_checksum(ofl->ofl_elf);

        /*
         * If this is a cross link to a target with a different byte
         * order than the linker, swap the data to the target byte order.
         */
        if (((ofl->ofl_flags1 & FLG_OF1_ENCDIFF) != 0) &&
            (_elf_swap_wrimage(ofl->ofl_elf) != 0)) {
                ld_eprintf(ofl, ERR_ELF, MSG_INTL(MSG_ELF_SWAP_WRIMAGE),
                    ofl->ofl_name);
                return (ld_exit(ofl));
        }

        /*
         * We're done, so make sure the updates are flushed to the output file.
         */
        if ((ofl->ofl_size = elf_update(ofl->ofl_welf, ELF_C_WRITE)) == 0) {
                ld_eprintf(ofl, ERR_ELF, MSG_INTL(MSG_ELF_UPDATE),
                    ofl->ofl_name);
                return (ld_exit(ofl));
        }

        ld_sup_atexit(ofl, 0);

        DBG_CALL(Dbg_statistics_ld(ofl));
        DBG_CALL(Dbg_basic_finish(ofl->ofl_lml));

        /*
         * Wrap up debug output file if one is open
         */
        dbg_cleanup();

        /* If any ERR_GUIDANCE messages were issued, add a summary */
        if (ofl->ofl_guideflags & FLG_OFG_ISSUED) {
                ld_eprintf(ofl, ERR_GUIDANCE, MSG_INTL(MSG_GUIDE_SUMMARY));
                ofl->ofl_guideflags &= ~FLG_OFG_ISSUED;
        }

        /*
         * One final check for any new warnings we found that should fail the
         * link edit.
         */
        if ((ofl->ofl_flags & (FLG_OF_WARN | FLG_OF_FATWARN)) ==
            (FLG_OF_WARN | FLG_OF_FATWARN))
                return (ld_exit(ofl));

        /*
         * For performance reasons we didn't used to actually free up the
         * memory we'd allocated, since it'll be freed on exit.
         *
         * These calls does not free nearly as much memory as you would think
         * they do, unfortunately.
         */
        ld_ofl_cleanup(ofl);
        libld_free(ofl);
        return (0);
}

/*
 * Cleanup an Ifl_desc.
 */
static void
ifl_list_cleanup(APlist *apl)
{
        Aliste          idx;
        Ifl_desc        *ifl;

        for (APLIST_TRAVERSE(apl, idx, ifl)) {
                if (ifl->ifl_elf)
                        (void) elf_end(ifl->ifl_elf);
        }
}

/*
 * Cleanup all memory that has been dynamically allocated during libld
 * processing and elf_end() all Elf descriptors that are still open.
 */
void
ld_ofl_cleanup(Ofl_desc *ofl)
{
        Ar_desc         *adp;
        Aliste          idx;

        ifl_list_cleanup(ofl->ofl_objs);
        ofl->ofl_objs = NULL;
        ifl_list_cleanup(ofl->ofl_sos);
        ofl->ofl_sos = NULL;

        for (APLIST_TRAVERSE(ofl->ofl_ars, idx, adp)) {
                Ar_aux          *aup;
                Elf_Arsym       *arsym;

                /*
                 * Free up member information for normally processed archives.
                 * Archives processed under -z allextract have no member
                 * information or symbol table, and members have already been
                 * dealt with as input files.
                 */
                if (adp->ad_allextract == TRUE)
                        continue;

                for (arsym = adp->ad_start, aup = adp->ad_aux;
                    (arsym->as_name != NULL); ++arsym, ++aup) {
                        if ((aup->au_mem != NULL) &&
                            (aup->au_mem != FLG_ARMEM_PROC)) {
                                (void) elf_end(aup->au_mem->am_elf);

                                /*
                                 * Null out all entries to this member so
                                 * that we don't attempt to elf_end() it again.
                                 */
                                ld_ar_member(adp, arsym, aup, 0);
                        }
                }
                (void) elf_end(adp->ad_elf);
        }
        ofl->ofl_ars = NULL;

        (void) elf_end(ofl->ofl_elf);
        (void) elf_end(ofl->ofl_welf);

        /*
         * Note that we don't free ofl itself here, just its contents.  The
         * ofl itself belongs to the caller.
         */
}