/* * 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 (c) 2012, Joyent, Inc. All rights reserved. * Copyright 2017 RackTop Systems. */ /* * Publicly available flags are defined in ld(1). The following flags are * private, and may be removed at any time. * * OPTION MEANING * * -z dtrace=symbol assigns symbol to PT_SUNWDTRACE segment, * providing scratch area for dtrace processing. * * -z noreloc suppress relocation processing. This provides * a mechanism for validating kernel module symbol * resolution that would normally incur fatal * relocation errors. * * -z rtldinfo=symbol assigns symbol to SUNW_RTLDINF dynamic tag, * providing pre-initialization specific routines * for TLS initialization. * * -z nointerp suppress the addition of an interpreter * section. This is used to generate the kernel, * but makes no sense to be used by anyone else. * * -z norelaxreloc suppress the automatic addition of relaxed * relocations to GNU linkonce/COMDAT sections. * * -z nosighandler suppress the registration of the signal handler * used to manage SIGBUS. */ /* * The following flags are committed, and will not be removed, but are * not publically documented, either because they are obsolete, or because * they exist to work around defects in other software and are not of * sufficient interest otherwise. * * OPTION MEANING * * -Wl,... compiler drivers and configuration tools * have been known to pass this compiler option * to ld(1). Strip off the "-Wl," prefix and * process the remainder (...) as a normal option. */ #include #include #include #include #include #include #include #include #include "msg.h" #include "_libld.h" /* * Define a set of local argument flags, the settings of these will be * verified in check_flags() and lead to the appropriate output file flags * being initialized. */ typedef enum { SET_UNKNOWN = -1, SET_FALSE = 0, SET_TRUE = 1 } Setstate; static Setstate dflag = SET_UNKNOWN; static Setstate zdflag = SET_UNKNOWN; static Setstate Qflag = SET_UNKNOWN; static Setstate Bdflag = SET_UNKNOWN; static Setstate zfwflag = SET_UNKNOWN; static Boolean aflag = FALSE; static Boolean bflag = FALSE; static Boolean sflag = FALSE; static Boolean zinflag = FALSE; static Boolean zlflag = FALSE; static Boolean Bgflag = FALSE; static Boolean Blflag = FALSE; static Boolean Beflag = FALSE; static Boolean Bsflag = FALSE; static Boolean Dflag = FALSE; static Boolean Vflag = FALSE; enum output_type { OT_RELOC, /* relocatable object */ OT_SHARED, /* shared object */ OT_EXEC, /* dynamic executable */ OT_KMOD, /* kernel module */ }; static enum output_type otype = OT_EXEC; /* * ztflag's state is set by pointing it to the matching string: * text | textoff | textwarn */ static const char *ztflag = NULL; /* * Remember the guidance flags that result from the initial -z guidance * option, so that they can be compared to any that follow. We only want * to issue a warning when they differ. */ static ofl_guideflag_t initial_guidance_flags = 0; static uintptr_t process_files_com(Ofl_desc *, int, char **); static uintptr_t process_flags_com(Ofl_desc *, int, char **, int *); /* * Print usage message to stderr - 2 modes, summary message only, * and full usage message. */ static void usage_mesg(Boolean detail) { (void) fprintf(stderr, MSG_INTL(MSG_ARG_USAGE), MSG_ORIG(MSG_STR_OPTIONS)); if (detail == FALSE) return; (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_3)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_6)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_A)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_B)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_CBDR)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_CBDY)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_CBE)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_CBG)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_CBL)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_CBR)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_CBS)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_C)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_CC)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_D)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_CD)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_E)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_F)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_CF)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_CG)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_H)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_I)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_CI)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_L)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_CL)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_M)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_CM)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_CN)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_O)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_P)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_CP)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_CQ)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_R)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_CR)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_S)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_CS)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_T)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_U)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_CV)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_CY)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZA)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZAE)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZADLIB)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZC)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZDEF)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZDFS)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZDRS)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZE)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZFATW)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZFA)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZGP)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZGUIDE)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZH)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZIG)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZINA)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZINI)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZINT)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZLAZY)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZLD32)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZLD64)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZLO)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZM)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZNC)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZNDFS)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZNDEF)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZNDEL)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZNDLO)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZNDU)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZNLD)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZNOW)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZNPA)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZNV)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZO)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZPIA)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZRL)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZRREL)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZRS)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZRSN)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZRSGRP)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZSCAP)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZTARG)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZT)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZTO)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZTW)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZTY)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZWRAP)); (void) fprintf(stderr, MSG_INTL(MSG_ARG_DETAIL_ZVER)); } /* * Rescan the archives seen on the command line in order * to handle circularly dependent archives, stopping when * no further member extraction occurs. * * entry: * ofl - Output file descriptor * isgrp - True if this is a an archive group search, False * to search starting with argv[1] through end_arg_ndx * end_arg_ndx - Index of final argv element to consider. */ static uintptr_t ld_rescan_archives(Ofl_desc *ofl, int isgrp, int end_arg_ndx) { ofl->ofl_flags1 |= FLG_OF1_EXTRACT; while (ofl->ofl_flags1 & FLG_OF1_EXTRACT) { Aliste idx; Ar_desc *adp; Word start_ndx = isgrp ? ofl->ofl_ars_gsndx : 0; Word ndx = 0; ofl->ofl_flags1 &= ~FLG_OF1_EXTRACT; DBG_CALL(Dbg_file_ar_rescan(ofl->ofl_lml, isgrp ? ofl->ofl_ars_gsandx : 1, end_arg_ndx)); for (APLIST_TRAVERSE(ofl->ofl_ars, idx, adp)) { /* If not to starting index yet, skip it */ if (ndx++ < start_ndx) continue; /* * If this archive was processed with -z allextract, * then all members have already been extracted. */ if (adp->ad_allextract == TRUE) continue; /* * Reestablish any archive specific command line flags. */ ofl->ofl_flags1 &= ~MSK_OF1_ARCHIVE; ofl->ofl_flags1 |= (adp->ad_flags & MSK_OF1_ARCHIVE); /* * Re-process the archive. Note that a file descriptor * is unnecessary, as the file is already available in * memory. */ if (!ld_process_archive(adp->ad_name, -1, adp, ofl)) return (S_ERROR); if (ofl->ofl_flags & FLG_OF_FATAL) return (1); } } return (1); } /* * Checks the command line option flags for consistency. */ static uintptr_t check_flags(Ofl_desc * ofl, int argc) { /* * If the user specified -zguidance=noall, then we can safely disable * the entire feature. The purpose of -zguidance=noall is to allow * the user to override guidance specified from a makefile via * the LD_OPTIONS environment variable, and so, we want to behave * in exactly the same manner we would have if no option were present. */ if ((ofl->ofl_guideflags & (FLG_OFG_ENABLE | FLG_OFG_NO_ALL)) == (FLG_OFG_ENABLE | FLG_OFG_NO_ALL)) ofl->ofl_guideflags &= ~FLG_OFG_ENABLE; if (Plibpath && (Llibdir || Ulibdir)) ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_YP), Llibdir ? 'L' : 'U'); if ((otype == OT_RELOC) || (otype == OT_KMOD)) { if (otype == OT_RELOC) { if (dflag == SET_UNKNOWN) dflag = SET_FALSE; } else if (otype == OT_KMOD) { if (dflag != SET_UNKNOWN) { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_MARG_INCOMP), MSG_INTL(MSG_MARG_TYPE_KMOD), MSG_ORIG(MSG_ARG_D)); } dflag = SET_TRUE; } /* * Combining relocations when building a relocatable * object isn't allowed. Warn the user, but proceed. */ if (ofl->ofl_flags & FLG_OF_COMREL) { const char *msg; if (otype == OT_RELOC) { msg = MSG_INTL(MSG_MARG_REL); } else { msg = MSG_INTL(MSG_MARG_TYPE_KMOD); } ld_eprintf(ofl, ERR_WARNING, MSG_INTL(MSG_MARG_INCOMP), msg, MSG_ORIG(MSG_ARG_ZCOMBRELOC)); } ofl->ofl_flags |= FLG_OF_RELOBJ; if (otype == OT_KMOD) ofl->ofl_flags |= FLG_OF_KMOD; } else { /* * Translating object capabilities to symbol capabilities is * only meaningful when creating a relocatable object. */ if (ofl->ofl_flags & FLG_OF_OTOSCAP) ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_MARG_ONLY), MSG_ORIG(MSG_ARG_ZSYMBOLCAP), MSG_INTL(MSG_MARG_REL)); /* * If the user hasn't explicitly requested that relocations * not be combined, combine them by default. */ if ((ofl->ofl_flags & FLG_OF_NOCOMREL) == 0) ofl->ofl_flags |= FLG_OF_COMREL; } if (zdflag == SET_TRUE) ofl->ofl_flags |= FLG_OF_NOUNDEF; if (zinflag) ofl->ofl_dtflags_1 |= DF_1_INTERPOSE; if (sflag) ofl->ofl_flags |= FLG_OF_STRIP; if (Qflag == SET_TRUE) ofl->ofl_flags |= FLG_OF_ADDVERS; if (Blflag) ofl->ofl_flags |= FLG_OF_AUTOLCL; if (Beflag) ofl->ofl_flags |= FLG_OF_AUTOELM; if (Blflag && Beflag) ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_INCOMP), MSG_ORIG(MSG_ARG_BELIMINATE), MSG_ORIG(MSG_ARG_BLOCAL)); if (ofl->ofl_interp && (ofl->ofl_flags1 & FLG_OF1_NOINTRP)) ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_INCOMP), MSG_ORIG(MSG_ARG_CI), MSG_ORIG(MSG_ARG_ZNOINTERP)); if ((ofl->ofl_flags1 & (FLG_OF1_NRLXREL | FLG_OF1_RLXREL)) == (FLG_OF1_NRLXREL | FLG_OF1_RLXREL)) ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_INCOMP), MSG_ORIG(MSG_ARG_ZRELAXRELOC), MSG_ORIG(MSG_ARG_ZNORELAXRELOC)); if (ofl->ofl_filtees && (otype != OT_SHARED)) ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_MARG_ST_ONLYAVL), ((ofl->ofl_flags & FLG_OF_AUX) ? MSG_INTL(MSG_MARG_FILTER_AUX) : MSG_INTL(MSG_MARG_FILTER))); if (dflag != SET_FALSE) { /* * Set -Bdynamic on by default, setting is rechecked as input * files are processed. */ ofl->ofl_flags |= (FLG_OF_DYNAMIC | FLG_OF_DYNLIBS | FLG_OF_PROCRED); if (aflag) ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_INCOMP), MSG_ORIG(MSG_ARG_DY), MSG_ORIG(MSG_ARG_A)); if (bflag) ofl->ofl_flags |= FLG_OF_BFLAG; if (Bgflag == TRUE) { if (zdflag == SET_FALSE) ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_INCOMP), MSG_ORIG(MSG_ARG_BGROUP), MSG_ORIG(MSG_ARG_ZNODEF)); ofl->ofl_dtflags_1 |= DF_1_GROUP; ofl->ofl_flags |= FLG_OF_NOUNDEF; } /* * If the use of default library searching has been suppressed * but no runpaths have been provided we're going to have a hard * job running this object. */ if ((ofl->ofl_dtflags_1 & DF_1_NODEFLIB) && !ofl->ofl_rpath) ld_eprintf(ofl, ERR_WARNING, MSG_INTL(MSG_ARG_NODEFLIB), MSG_INTL(MSG_MARG_RPATH)); /* * By default, text relocation warnings are given when building * an executable unless the -b flag is specified. This option * implies that unclean text can be created, so no warnings are * generated unless specifically asked for. */ if ((ztflag == MSG_ORIG(MSG_ARG_ZTEXTOFF)) || ((ztflag == NULL) && bflag)) { ofl->ofl_flags1 |= FLG_OF1_TEXTOFF; ofl->ofl_guideflags |= FLG_OFG_NO_TEXT; } else if (ztflag == MSG_ORIG(MSG_ARG_ZTEXT)) { ofl->ofl_flags |= FLG_OF_PURETXT; ofl->ofl_guideflags |= FLG_OFG_NO_TEXT; } if ((otype == OT_SHARED) || (otype == OT_EXEC)) { /* * Create a dynamic object. -Bdirect indicates that all * references should be bound directly. This also * enables lazyloading. Individual symbols can be * bound directly (or not) using mapfiles and the * DIRECT (NODIRECT) qualifier. With this capability, * each syminfo entry is tagged SYMINFO_FLG_DIRECTBIND. * Prior to this per-symbol direct binding, runtime * direct binding was controlled via the DF_1_DIRECT * flag. This flag affected all references from the * object. -Bdirect continues to set this flag, and * thus provides a means of taking a newly built * direct binding object back to older systems. * * NOTE, any use of per-symbol NODIRECT bindings, or * -znodirect, will disable the creation of the * DF_1_DIRECT flag. Older runtime linkers do not * have the capability to do per-symbol direct bindings. */ if (Bdflag == SET_TRUE) { ofl->ofl_dtflags_1 |= DF_1_DIRECT; ofl->ofl_flags1 |= FLG_OF1_LAZYLD; ofl->ofl_guideflags |= FLG_OFG_NO_LAZY; ofl->ofl_flags |= FLG_OF_SYMINFO; } /* * -Bnodirect disables directly binding to any symbols * exported from the object being created. Individual * references to external objects can still be affected * by -zdirect or mapfile DIRECT directives. */ if (Bdflag == SET_FALSE) { ofl->ofl_flags1 |= (FLG_OF1_NDIRECT | FLG_OF1_NGLBDIR | FLG_OF1_ALNODIR); ofl->ofl_flags |= FLG_OF_SYMINFO; } } if (otype == OT_EXEC) { /* * Dynamically linked executable. */ ofl->ofl_flags |= FLG_OF_EXEC; if (zdflag != SET_FALSE) ofl->ofl_flags |= FLG_OF_NOUNDEF; /* * -z textwarn is the default for executables, and * only an explicit -z text* option can change that, * so there's no need to provide additional guidance. */ ofl->ofl_guideflags |= FLG_OFG_NO_TEXT; if (Bsflag) ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_DY_INCOMP), MSG_ORIG(MSG_ARG_BSYMBOLIC)); if (ofl->ofl_soname) ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_MARG_DY_INCOMP), MSG_INTL(MSG_MARG_SONAME)); } else if (otype == OT_SHARED) { /* * Shared library. */ ofl->ofl_flags |= FLG_OF_SHAROBJ; /* * By default, print text relocation warnings for * executables but *not* for shared objects. However, * if -z guidance is on, issue warnings for shared * objects as well. * * If -z textwarn is explicitly specified, also issue * guidance messages if -z guidance is on, but not * for -z text or -z textoff. */ if (ztflag == NULL) { if (!OFL_GUIDANCE(ofl, FLG_OFG_NO_TEXT)) ofl->ofl_flags1 |= FLG_OF1_TEXTOFF; } else if ((ofl->ofl_flags & FLG_OF_PURETXT) || (ofl->ofl_flags1 & FLG_OF1_TEXTOFF)) { ofl->ofl_guideflags |= FLG_OFG_NO_TEXT; } if (Bsflag) { /* * -Bsymbolic, and -Bnodirect make no sense. */ if (Bdflag == SET_FALSE) ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_INCOMP), MSG_ORIG(MSG_ARG_BSYMBOLIC), MSG_ORIG(MSG_ARG_BNODIRECT)); ofl->ofl_flags |= FLG_OF_SYMBOLIC; ofl->ofl_dtflags |= DF_SYMBOLIC; } } else { /* * Dynamic relocatable object. */ if (ztflag == NULL) ofl->ofl_flags1 |= FLG_OF1_TEXTOFF; ofl->ofl_guideflags |= FLG_OFG_NO_TEXT; if (ofl->ofl_interp) ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_MARG_INCOMP), MSG_INTL(MSG_MARG_REL), MSG_ORIG(MSG_ARG_CI)); } assert((ofl->ofl_flags & (FLG_OF_SHAROBJ|FLG_OF_EXEC)) != (FLG_OF_SHAROBJ|FLG_OF_EXEC)); } else { ofl->ofl_flags |= FLG_OF_STATIC; if (bflag) ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_ST_INCOMP), MSG_ORIG(MSG_ARG_B)); if (ofl->ofl_soname) ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_MARG_ST_INCOMP), MSG_INTL(MSG_MARG_SONAME)); if (ofl->ofl_depaudit) ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_ST_INCOMP), MSG_ORIG(MSG_ARG_CP)); if (ofl->ofl_audit) ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_ST_INCOMP), MSG_ORIG(MSG_ARG_P)); if (ofl->ofl_config) ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_ST_INCOMP), MSG_ORIG(MSG_ARG_C)); if (ztflag) ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_ST_INCOMP), MSG_ORIG(MSG_ARG_ZTEXTALL)); if (otype == OT_SHARED) ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_MARG_ST_INCOMP), MSG_INTL(MSG_MARG_SO)); if (aflag && (otype == OT_RELOC)) ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_MARG_INCOMP), MSG_ORIG(MSG_ARG_A), MSG_INTL(MSG_MARG_REL)); if (otype == OT_RELOC) { /* * We can only strip the symbol table and string table * if no output relocations will refer to them. */ if (sflag) ld_eprintf(ofl, ERR_WARNING, MSG_INTL(MSG_ARG_STRIP), MSG_INTL(MSG_MARG_REL), MSG_INTL(MSG_MARG_STRIP)); if (ztflag == NULL) ofl->ofl_flags1 |= FLG_OF1_TEXTOFF; ofl->ofl_guideflags |= FLG_OFG_NO_TEXT; if (ofl->ofl_interp) ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_MARG_INCOMP), MSG_INTL(MSG_MARG_REL), MSG_ORIG(MSG_ARG_CI)); } else { /* * Static executable. */ ofl->ofl_flags |= FLG_OF_EXEC | FLG_OF_PROCRED; if (zdflag != SET_FALSE) ofl->ofl_flags |= FLG_OF_NOUNDEF; } } /* * If the user didn't supply an output file name supply a default. */ if (ofl->ofl_name == NULL) ofl->ofl_name = MSG_ORIG(MSG_STR_AOUT); /* * We set the entrance criteria after all input argument processing as * it is only at this point we're sure what the output image will be * (static or dynamic). */ if (ld_ent_setup(ofl, ld_targ.t_m.m_segm_align) == S_ERROR) return (S_ERROR); /* * Does the host currently running the linker have the same * byte order as the target for which the object is being produced? * If not, set FLG_OF1_ENCDIFF so relocation code will know * to check. */ if (_elf_sys_encoding() != ld_targ.t_m.m_data) ofl->ofl_flags1 |= FLG_OF1_ENCDIFF; /* * If the target has special executable section filling requirements, * register the fill function with libelf */ if (ld_targ.t_ff.ff_execfill != NULL) _elf_execfill(ld_targ.t_ff.ff_execfill); /* * Initialize string tables. Symbol definitions within mapfiles can * result in the creation of input sections. */ if (ld_init_strings(ofl) == S_ERROR) return (S_ERROR); /* * Process mapfiles. Mapfile can redefine or add sections/segments, * so this must come after the default entrance criteria are established * (above). */ if (ofl->ofl_maps) { const char *name; Aliste idx; for (APLIST_TRAVERSE(ofl->ofl_maps, idx, name)) if (!ld_map_parse(name, ofl)) return (S_ERROR); if (!ld_map_post_process(ofl)) return (S_ERROR); } /* * If a mapfile has been used to define a single symbolic scope of * interfaces, -Bsymbolic is established. This global setting goes * beyond individual symbol protection, and ensures all relocations * (even those that reference section symbols) are processed within * the object being built. */ if (((ofl->ofl_flags & (FLG_OF_MAPSYMB | FLG_OF_MAPGLOB)) == FLG_OF_MAPSYMB) && (ofl->ofl_flags & (FLG_OF_AUTOLCL | FLG_OF_AUTOELM))) { ofl->ofl_flags |= FLG_OF_SYMBOLIC; ofl->ofl_dtflags |= DF_SYMBOLIC; } /* * If -zloadfltr is set, verify that filtering is in effect. Filters * are either established from the command line, and affect the whole * object, or are set on a per-symbol basis from a mapfile. */ if (zlflag) { if ((ofl->ofl_filtees == NULL) && (ofl->ofl_dtsfltrs == NULL)) ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_NOFLTR), MSG_ORIG(MSG_ARG_ZLOADFLTR)); ofl->ofl_dtflags_1 |= DF_1_LOADFLTR; } /* * Check that we have something to work with. This check is carried out * after mapfile processing as its possible a mapfile is being used to * define symbols, in which case it would be sufficient to build the * output file purely from the mapfile. */ if ((ofl->ofl_objscnt == 0) && (ofl->ofl_soscnt == 0)) { if ((Vflag || (Dflag && (dbg_desc->d_extra & DBG_E_HELP_EXIT))) && (argc == 2)) { ofl->ofl_flags1 |= FLG_OF1_DONE; } else { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_NOFILES)); return (S_ERROR); } } return (1); } /* * Decompose the string pointed by optarg into argv[][] so that argv[][] can be * used as an argument to getopt(). * * If the second argument 'usage' is not NULL, then this is called from the * first pass. Else this is called from the second pass. */ static uintptr_t createargv(Ofl_desc *ofl, int *usage) { int argc = 0, idx = 0, ooptind; uintptr_t ret; char **argv, *p0; /* * The argument being examined is either: * ld32= or * ld64= */ #if defined(_LP64) if (optarg[2] == '3') return (0); #else if (optarg[2] == '6') return (0); #endif p0 = &optarg[5]; /* * Count the number of arguments. */ while (*p0) { /* * Pointing at non-separator character. */ if (*p0 != ',') { argc++; while (*p0 && (*p0 != ',')) p0++; continue; } /* * Pointing at a separator character. */ if (*p0 == ',') { while (*p0 == ',') p0++; continue; } } if (argc == 0) return (0); /* * Allocate argument vector. */ if ((p0 = (char *)strdup(&optarg[5])) == NULL) return (S_ERROR); if ((argv = libld_malloc((sizeof (char *)) * (argc + 1))) == NULL) return (S_ERROR); while (*p0) { char *p; /* * Pointing at the beginning of non-separator character string. */ if (*p0 != ',') { p = p0; while (*p0 && (*p0 != ',')) p0++; argv[idx++] = p; if (*p0) { *p0 = '\0'; p0++; } continue; } /* * Pointing at the beginining of separator character string. */ if (*p0 == ',') { while (*p0 == ',') p0++; continue; } } argv[idx] = 0; ooptind = optind; optind = 0; /* * Dispatch to pass1 or pass2 */ if (usage) ret = process_flags_com(ofl, argc, argv, usage); else ret = process_files_com(ofl, argc, argv); optind = ooptind; return (ret); } /* * Parse the items in a '-z guidance' value, and set the ofl_guideflags. * A guidance option looks like this: * * -z guidance[=item1,item2,...] * * Where each item specifies categories of guidance messages to suppress, * each starting with the prefix 'no'. We allow arbitrary whitespace between * the items, allow multiple ',' delimiters without an intervening item, and * quietly ignore any items we don't recognize. * * - Such items are likely to be known to newer versions of the linker, * and we do not want an older version of the linker to * complain about them. * * - Times and standards can change, and so we wish to reserve the * right to make an old item that no longer makes sense go away. * Quietly ignoring unrecognized items facilitates this. * * However, we always display unrecognized items in debug output. * * entry: * ofl - Output descriptor * optarg - option string to be processed. This will either be a NULL * terminated 'guidance', or it will be 'guidance=' followed * by the item tokens as described above. * * exit: * Returns TRUE (1) on success, FALSE (0) on failure. * */ static Boolean guidance_parse(Ofl_desc *ofl, char *optarg) { typedef struct { const char *name; ofl_guideflag_t flag; } item_desc; static item_desc items[] = { { MSG_ORIG(MSG_ARG_GUIDE_NO_ALL), FLG_OFG_NO_ALL }, { MSG_ORIG(MSG_ARG_GUIDE_NO_DEFS), FLG_OFG_NO_DEFS }, { MSG_ORIG(MSG_ARG_GUIDE_NO_DIRECT), FLG_OFG_NO_DB }, { MSG_ORIG(MSG_ARG_GUIDE_NO_LAZYLOAD), FLG_OFG_NO_LAZY }, { MSG_ORIG(MSG_ARG_GUIDE_NO_MAPFILE), FLG_OFG_NO_MF }, { MSG_ORIG(MSG_ARG_GUIDE_NO_TEXT), FLG_OFG_NO_TEXT }, { MSG_ORIG(MSG_ARG_GUIDE_NO_UNUSED), FLG_OFG_NO_UNUSED }, { MSG_ORIG(MSG_ARG_GUIDE_NO_ASSERTS), FLG_OFG_NO_ASSERTS }, { NULL, 0 } }; char *lasts, *name; item_desc *item; ofl_guideflag_t ofl_guideflags = FLG_OFG_ENABLE; /* * Skip the 'guidance' prefix. If NULL terminated, there are no * item values to parse. Otherwise, skip the '=' and parse the items. */ optarg += MSG_ARG_GUIDE_SIZE; if (*optarg == '=') { optarg++; if ((name = libld_malloc(strlen(optarg) + 1)) == NULL) return (FALSE); (void) strcpy(name, optarg); if ((name = strtok_r(name, MSG_ORIG(MSG_ARG_GUIDE_DELIM), &lasts)) != NULL) { do { for (item = items; item->name != NULL; item++) if (strcasecmp(name, item->name) == 0) break; if (item->name == NULL) { ld_eprintf(ofl, ERR_GUIDANCE, MSG_INTL(MSG_GUIDE_UNKNOWN), name); continue; } ofl_guideflags |= item->flag; } while ((name = strtok_r(NULL, MSG_ORIG(MSG_ARG_GUIDE_DELIM), &lasts)) != NULL); } } /* * If -zguidance is used more than once, we take the first one. We * do this quietly if they have identical options, and with a warning * otherwise. */ if ((initial_guidance_flags & FLG_OFG_ENABLE) && (ofl_guideflags != initial_guidance_flags)) { ld_eprintf(ofl, ERR_WARNING_NF, MSG_INTL(MSG_ARG_MTONCE), MSG_ORIG(MSG_ARG_ZGUIDE)); return (TRUE); } /* * First time: Save the flags for comparison to any subsequent * -z guidance that comes along, and OR the resulting flags into * the flags kept in the output descriptor. */ initial_guidance_flags = ofl_guideflags; ofl->ofl_guideflags |= ofl_guideflags; return (TRUE); } /* * Parse the -z assert-deflib option. This option can appear in two different * forms: * -z assert-deflib * -z assert-deflib=libfred.so * * Either form enables this option, the latter form marks libfred.so as an * exempt library from the check. It is valid to have multiple invocations of * the second form. We silently ignore mulitple occurrences of the first form * and multiple invocations of the first form when the second form also occurs. * * We only return false when we have an internal error, such as the failure of * aplist_append. Every other time we return true, but we have the appropriate * fatal flags set beacuse of the ld_eprintf. */ static int assdeflib_parse(Ofl_desc *ofl, char *optarg) { size_t olen, mlen; ofl->ofl_flags |= FLG_OF_ADEFLIB; olen = strlen(optarg); /* Minimum size of assert-deflib=lib%s.so */ mlen = MSG_ARG_ASSDEFLIB_SIZE + 1 + MSG_STR_LIB_SIZE + MSG_STR_SOEXT_SIZE; if (olen > MSG_ARG_ASSDEFLIB_SIZE) { if (optarg[MSG_ARG_ASSDEFLIB_SIZE] != '=') { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_ILLEGAL), MSG_ORIG(MSG_ARG_ASSDEFLIB), optarg); return (TRUE); } if (strncmp(optarg + MSG_ARG_ASSDEFLIB_SIZE + 1, MSG_ORIG(MSG_STR_LIB), MSG_STR_LIB_SIZE) != 0 || strcmp(optarg + olen - MSG_STR_SOEXT_SIZE, MSG_ORIG(MSG_STR_SOEXT)) != 0 || olen <= mlen) { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_ASSDEFLIB_MALFORMED), optarg); return (TRUE); } if (aplist_append(&ofl->ofl_assdeflib, optarg + MSG_ARG_ASSDEFLIB_SIZE + 1, AL_CNT_ASSDEFLIB) == NULL) return (FALSE); } return (TRUE); } static int optitle = 0; /* * Parsing options pass1 for process_flags(). */ static uintptr_t parseopt_pass1(Ofl_desc *ofl, int argc, char **argv, int *usage) { int c, ndx = optind; /* * The -32, -64 and -ztarget options are special, in that we validate * them, but otherwise ignore them. libld.so (this code) is called * from the ld front end program. ld has already examined the * arguments to determine the output class and machine type of the * output object, as reflected in the version (32/64) of ld_main() * that was called and the value of the 'mach' argument passed. * By time execution reaches this point, these options have already * been seen and acted on. */ while ((c = ld_getopt(ofl->ofl_lml, ndx, argc, argv)) != -1) { switch (c) { case '3': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); /* * -32 is processed by ld to determine the output class. * Here we sanity check the option incase some other * -3* option is mistakenly passed to us. */ if (optarg[0] != '2') ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_ILLEGAL), MSG_ORIG(MSG_ARG_3), optarg); continue; case '6': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); /* * -64 is processed by ld to determine the output class. * Here we sanity check the option incase some other * -6* option is mistakenly passed to us. */ if (optarg[0] != '4') ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_ILLEGAL), MSG_ORIG(MSG_ARG_6), optarg); continue; case 'a': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, NULL)); aflag = TRUE; break; case 'b': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, NULL)); bflag = TRUE; /* * This is a hack, and may be undone later. * The -b option is only used to build the Unix * kernel and its related kernel-mode modules. * We do not want those files to get a .SUNW_ldynsym * section. At least for now, the kernel makes no * use of .SUNW_ldynsym, and we do not want to use * the space to hold it. Therefore, we overload * the use of -b to also imply -znoldynsym. */ ofl->ofl_flags |= FLG_OF_NOLDYNSYM; break; case 'c': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); if (ofl->ofl_config) ld_eprintf(ofl, ERR_WARNING_NF, MSG_INTL(MSG_ARG_MTONCE), MSG_ORIG(MSG_ARG_C)); else ofl->ofl_config = optarg; break; case 'C': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, NULL)); demangle_flag = 1; break; case 'd': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); if ((optarg[0] == 'n') && (optarg[1] == '\0')) { if (dflag != SET_UNKNOWN) ld_eprintf(ofl, ERR_WARNING_NF, MSG_INTL(MSG_ARG_MTONCE), MSG_ORIG(MSG_ARG_D)); else dflag = SET_FALSE; } else if ((optarg[0] == 'y') && (optarg[1] == '\0')) { if (dflag != SET_UNKNOWN) ld_eprintf(ofl, ERR_WARNING_NF, MSG_INTL(MSG_ARG_MTONCE), MSG_ORIG(MSG_ARG_D)); else dflag = SET_TRUE; } else { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_ILLEGAL), MSG_ORIG(MSG_ARG_D), optarg); } break; case 'e': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); if (ofl->ofl_entry) ld_eprintf(ofl, ERR_WARNING_NF, MSG_INTL(MSG_MARG_MTONCE), MSG_INTL(MSG_MARG_ENTRY)); else ofl->ofl_entry = (void *)optarg; break; case 'f': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); if (ofl->ofl_filtees && (!(ofl->ofl_flags & FLG_OF_AUX))) { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_MARG_INCOMP), MSG_INTL(MSG_MARG_FILTER_AUX), MSG_INTL(MSG_MARG_FILTER)); } else { if ((ofl->ofl_filtees = add_string(ofl->ofl_filtees, optarg)) == (const char *)S_ERROR) return (S_ERROR); ofl->ofl_flags |= FLG_OF_AUX; } break; case 'F': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); if (ofl->ofl_filtees && (ofl->ofl_flags & FLG_OF_AUX)) { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_MARG_INCOMP), MSG_INTL(MSG_MARG_FILTER), MSG_INTL(MSG_MARG_FILTER_AUX)); } else { if ((ofl->ofl_filtees = add_string(ofl->ofl_filtees, optarg)) == (const char *)S_ERROR) return (S_ERROR); } break; case 'h': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); if (ofl->ofl_soname) ld_eprintf(ofl, ERR_WARNING_NF, MSG_INTL(MSG_MARG_MTONCE), MSG_INTL(MSG_MARG_SONAME)); else ofl->ofl_soname = (const char *)optarg; break; case 'i': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, NULL)); ofl->ofl_flags |= FLG_OF_IGNENV; break; case 'I': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); if (ofl->ofl_interp) ld_eprintf(ofl, ERR_WARNING_NF, MSG_INTL(MSG_ARG_MTONCE), MSG_ORIG(MSG_ARG_CI)); else ofl->ofl_interp = (const char *)optarg; break; case 'l': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); /* * For now, count any library as a shared object. This * is used to size the internal symbol cache. This * value is recalculated later on actual file processing * to get an accurate shared object count. */ ofl->ofl_soscnt++; break; case 'm': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, NULL)); ofl->ofl_flags |= FLG_OF_GENMAP; break; case 'o': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); if (ofl->ofl_name) ld_eprintf(ofl, ERR_WARNING_NF, MSG_INTL(MSG_MARG_MTONCE), MSG_INTL(MSG_MARG_OUTFILE)); else ofl->ofl_name = (const char *)optarg; break; case 'p': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); /* * Multiple instances of this option may occur. Each * additional instance is effectively concatenated to * the previous separated by a colon. */ if (*optarg != '\0') { if ((ofl->ofl_audit = add_string(ofl->ofl_audit, optarg)) == (const char *)S_ERROR) return (S_ERROR); } break; case 'P': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); /* * Multiple instances of this option may occur. Each * additional instance is effectively concatenated to * the previous separated by a colon. */ if (*optarg != '\0') { if ((ofl->ofl_depaudit = add_string(ofl->ofl_depaudit, optarg)) == (const char *)S_ERROR) return (S_ERROR); } break; case 'r': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, NULL)); otype = OT_RELOC; break; case 'R': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); /* * Multiple instances of this option may occur. Each * additional instance is effectively concatenated to * the previous separated by a colon. */ if (*optarg != '\0') { if ((ofl->ofl_rpath = add_string(ofl->ofl_rpath, optarg)) == (const char *)S_ERROR) return (S_ERROR); } break; case 's': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, NULL)); sflag = TRUE; break; case 't': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, NULL)); ofl->ofl_flags |= FLG_OF_NOWARN; break; case 'u': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); break; case 'z': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); /* * Skip comma that might be present between -z and its * argument (e.g. if -Wl,-z,assert-deflib was passed). */ if (strncmp(optarg, MSG_ORIG(MSG_STR_COMMA), MSG_STR_COMMA_SIZE) == 0) optarg++; /* * For specific help, print our usage message and exit * immediately to ensure a 0 return code. */ if (strncmp(optarg, MSG_ORIG(MSG_ARG_HELP), MSG_ARG_HELP_SIZE) == 0) { usage_mesg(TRUE); exit(0); } /* * For some options set a flag - further consistancy * checks will be carried out in check_flags(). */ if ((strncmp(optarg, MSG_ORIG(MSG_ARG_LD32), MSG_ARG_LD32_SIZE) == 0) || (strncmp(optarg, MSG_ORIG(MSG_ARG_LD64), MSG_ARG_LD64_SIZE) == 0)) { if (createargv(ofl, usage) == S_ERROR) return (S_ERROR); } else if ( strcmp(optarg, MSG_ORIG(MSG_ARG_DEFS)) == 0) { if (zdflag != SET_UNKNOWN) ld_eprintf(ofl, ERR_WARNING_NF, MSG_INTL(MSG_ARG_MTONCE), MSG_ORIG(MSG_ARG_ZDEFNODEF)); else zdflag = SET_TRUE; ofl->ofl_guideflags |= FLG_OFG_NO_DEFS; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_NODEFS)) == 0) { if (zdflag != SET_UNKNOWN) ld_eprintf(ofl, ERR_WARNING_NF, MSG_INTL(MSG_ARG_MTONCE), MSG_ORIG(MSG_ARG_ZDEFNODEF)); else zdflag = SET_FALSE; ofl->ofl_guideflags |= FLG_OFG_NO_DEFS; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_TEXT)) == 0) { if (ztflag && (ztflag != MSG_ORIG(MSG_ARG_ZTEXT))) ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_INCOMP), MSG_ORIG(MSG_ARG_ZTEXT), ztflag); ztflag = MSG_ORIG(MSG_ARG_ZTEXT); } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_TEXTOFF)) == 0) { if (ztflag && (ztflag != MSG_ORIG(MSG_ARG_ZTEXTOFF))) ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_INCOMP), MSG_ORIG(MSG_ARG_ZTEXTOFF), ztflag); ztflag = MSG_ORIG(MSG_ARG_ZTEXTOFF); } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_TEXTWARN)) == 0) { if (ztflag && (ztflag != MSG_ORIG(MSG_ARG_ZTEXTWARN))) ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_INCOMP), MSG_ORIG(MSG_ARG_ZTEXTWARN), ztflag); ztflag = MSG_ORIG(MSG_ARG_ZTEXTWARN); /* * For other options simply set the ofl flags directly. */ } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_RESCAN)) == 0) { ofl->ofl_flags1 |= FLG_OF1_RESCAN; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_ABSEXEC)) == 0) { ofl->ofl_flags1 |= FLG_OF1_ABSEXEC; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_LOADFLTR)) == 0) { zlflag = TRUE; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_NORELOC)) == 0) { ofl->ofl_dtflags_1 |= DF_1_NORELOC; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_NOVERSION)) == 0) { ofl->ofl_flags |= FLG_OF_NOVERSEC; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_MULDEFS)) == 0) { ofl->ofl_flags |= FLG_OF_MULDEFS; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_REDLOCSYM)) == 0) { ofl->ofl_flags |= FLG_OF_REDLSYM; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_INITFIRST)) == 0) { ofl->ofl_dtflags_1 |= DF_1_INITFIRST; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_NODELETE)) == 0) { ofl->ofl_dtflags_1 |= DF_1_NODELETE; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_NOPARTIAL)) == 0) { ofl->ofl_flags1 |= FLG_OF1_NOPARTI; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_NOOPEN)) == 0) { ofl->ofl_dtflags_1 |= DF_1_NOOPEN; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_NOW)) == 0) { ofl->ofl_dtflags_1 |= DF_1_NOW; ofl->ofl_dtflags |= DF_BIND_NOW; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_ORIGIN)) == 0) { ofl->ofl_dtflags_1 |= DF_1_ORIGIN; ofl->ofl_dtflags |= DF_ORIGIN; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_NODEFAULTLIB)) == 0) { ofl->ofl_dtflags_1 |= DF_1_NODEFLIB; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_NODUMP)) == 0) { ofl->ofl_dtflags_1 |= DF_1_NODUMP; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_ENDFILTEE)) == 0) { ofl->ofl_dtflags_1 |= DF_1_ENDFILTEE; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_VERBOSE)) == 0) { ofl->ofl_flags |= FLG_OF_VERBOSE; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_COMBRELOC)) == 0) { ofl->ofl_flags |= FLG_OF_COMREL; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_NOCOMBRELOC)) == 0) { ofl->ofl_flags |= FLG_OF_NOCOMREL; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_NOCOMPSTRTAB)) == 0) { ofl->ofl_flags1 |= FLG_OF1_NCSTTAB; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_NOINTERP)) == 0) { ofl->ofl_flags1 |= FLG_OF1_NOINTRP; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_INTERPOSE)) == 0) { zinflag = TRUE; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_IGNORE)) == 0) { ofl->ofl_flags1 |= FLG_OF1_IGNPRC; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_RELAXRELOC)) == 0) { ofl->ofl_flags1 |= FLG_OF1_RLXREL; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_NORELAXRELOC)) == 0) { ofl->ofl_flags1 |= FLG_OF1_NRLXREL; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_NOLDYNSYM)) == 0) { ofl->ofl_flags |= FLG_OF_NOLDYNSYM; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_GLOBAUDIT)) == 0) { ofl->ofl_dtflags_1 |= DF_1_GLOBAUDIT; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_NOSIGHANDLER)) == 0) { ofl->ofl_flags1 |= FLG_OF1_NOSGHND; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_SYMBOLCAP)) == 0) { ofl->ofl_flags |= FLG_OF_OTOSCAP; /* * Check archive group usage * -z rescan-start ... -z rescan-end * to ensure they don't overlap and are well formed. */ } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_RESCAN_START)) == 0) { if (ofl->ofl_ars_gsandx == 0) { ofl->ofl_ars_gsandx = ndx; } else if (ofl->ofl_ars_gsandx > 0) { /* Another group is still open */ ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_AR_GRP_OLAP), MSG_INTL(MSG_MARG_AR_GRPS)); /* Don't report cascading errors */ ofl->ofl_ars_gsandx = -1; } } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_RESCAN_END)) == 0) { if (ofl->ofl_ars_gsandx > 0) { ofl->ofl_ars_gsandx = 0; } else if (ofl->ofl_ars_gsandx == 0) { /* There was no matching begin */ ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_AR_GRP_BAD), MSG_INTL(MSG_MARG_AR_GRP_END), MSG_INTL(MSG_MARG_AR_GRP_START)); /* Don't report cascading errors */ ofl->ofl_ars_gsandx = -1; } /* * If -z wrap is seen, enter the symbol to be wrapped * into the wrap AVL tree. */ } else if (strncmp(optarg, MSG_ORIG(MSG_ARG_WRAP), MSG_ARG_WRAP_SIZE) == 0) { if (ld_wrap_enter(ofl, optarg + MSG_ARG_WRAP_SIZE) == NULL) return (S_ERROR); } else if (strncmp(optarg, MSG_ORIG(MSG_ARG_ASLR), MSG_ARG_ASLR_SIZE) == 0) { char *p = optarg + MSG_ARG_ASLR_SIZE; if (*p == '\0') { ofl->ofl_aslr = 1; } else if (*p == '=') { p++; if ((strcmp(p, MSG_ORIG(MSG_ARG_ENABLED)) == 0) || (strcmp(p, MSG_ORIG(MSG_ARG_ENABLE)) == 0)) { ofl->ofl_aslr = 1; } else if ((strcmp(p, MSG_ORIG(MSG_ARG_DISABLED)) == 0) || (strcmp(p, MSG_ORIG(MSG_ARG_DISABLE)) == 0)) { ofl->ofl_aslr = -1; } else { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_ILLEGAL), MSG_ORIG(MSG_ARG_ZASLR), p); return (S_ERROR); } } else { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_ILLEGAL), MSG_ORIG(MSG_ARG_Z), optarg); return (S_ERROR); } } else if ((strncmp(optarg, MSG_ORIG(MSG_ARG_GUIDE), MSG_ARG_GUIDE_SIZE) == 0) && ((optarg[MSG_ARG_GUIDE_SIZE] == '=') || (optarg[MSG_ARG_GUIDE_SIZE] == '\0'))) { if (!guidance_parse(ofl, optarg)) return (S_ERROR); } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_FATWARN)) == 0) { if (zfwflag == SET_FALSE) { ld_eprintf(ofl, ERR_WARNING_NF, MSG_INTL(MSG_ARG_MTONCE), MSG_ORIG(MSG_ARG_ZFATWNOFATW)); } else { zfwflag = SET_TRUE; ofl->ofl_flags |= FLG_OF_FATWARN; } } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_NOFATWARN)) == 0) { if (zfwflag == SET_TRUE) ld_eprintf(ofl, ERR_WARNING_NF, MSG_INTL(MSG_ARG_MTONCE), MSG_ORIG(MSG_ARG_ZFATWNOFATW)); else zfwflag = SET_FALSE; /* * Process everything related to -z assert-deflib. This * must be done in pass 1 because it gets used in pass * 2. */ } else if (strncmp(optarg, MSG_ORIG(MSG_ARG_ASSDEFLIB), MSG_ARG_ASSDEFLIB_SIZE) == 0) { if (assdeflib_parse(ofl, optarg) != TRUE) return (S_ERROR); /* * Process new-style output type specification, which * we'll use in pass 2 and throughout. */ } else if (strncmp(optarg, MSG_ORIG(MSG_ARG_TYPE), MSG_ARG_TYPE_SIZE) == 0) { char *p = optarg + MSG_ARG_TYPE_SIZE; if (*p != '=') { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_ILLEGAL), MSG_ORIG(MSG_ARG_Z), optarg); return (S_ERROR); } p++; if (strcmp(p, MSG_ORIG(MSG_ARG_TYPE_RELOC)) == 0) { otype = OT_RELOC; } else if (strcmp(p, MSG_ORIG(MSG_ARG_TYPE_EXEC)) == 0) { otype = OT_EXEC; } else if (strcmp(p, MSG_ORIG(MSG_ARG_TYPE_SHARED)) == 0) { otype = OT_SHARED; } else if (strcmp(p, MSG_ORIG(MSG_ARG_TYPE_KMOD)) == 0) { otype = OT_KMOD; } else { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_ILLEGAL), MSG_ORIG(MSG_ARG_Z), optarg); return (S_ERROR); } /* * The following options just need validation as they * are interpreted either on the second pass through * the command line arguments, by ld(1) directly, or * are merely accepted for compatibility. */ } else if ( strncmp(optarg, MSG_ORIG(MSG_ARG_INITARRAY), MSG_ARG_INITARRAY_SIZE) && strncmp(optarg, MSG_ORIG(MSG_ARG_FINIARRAY), MSG_ARG_FINIARRAY_SIZE) && strncmp(optarg, MSG_ORIG(MSG_ARG_PREINITARRAY), MSG_ARG_PREINITARRAY_SIZE) && strncmp(optarg, MSG_ORIG(MSG_ARG_RTLDINFO), MSG_ARG_RTLDINFO_SIZE) && strncmp(optarg, MSG_ORIG(MSG_ARG_DTRACE), MSG_ARG_DTRACE_SIZE) && strcmp(optarg, MSG_ORIG(MSG_ARG_ALLEXTRT)) && strcmp(optarg, MSG_ORIG(MSG_ARG_DFLEXTRT)) && strcmp(optarg, MSG_ORIG(MSG_ARG_DIRECT)) && strcmp(optarg, MSG_ORIG(MSG_ARG_NODIRECT)) && strcmp(optarg, MSG_ORIG(MSG_ARG_GROUPPERM)) && strcmp(optarg, MSG_ORIG(MSG_ARG_LAZYLOAD)) && strcmp(optarg, MSG_ORIG(MSG_ARG_NOGROUPPERM)) && strcmp(optarg, MSG_ORIG(MSG_ARG_NOLAZYLOAD)) && strcmp(optarg, MSG_ORIG(MSG_ARG_NODEFERRED)) && strcmp(optarg, MSG_ORIG(MSG_ARG_RECORD)) && strcmp(optarg, MSG_ORIG(MSG_ARG_ALTEXEC64)) && strcmp(optarg, MSG_ORIG(MSG_ARG_WEAKEXT)) && strncmp(optarg, MSG_ORIG(MSG_ARG_TARGET), MSG_ARG_TARGET_SIZE) && strcmp(optarg, MSG_ORIG(MSG_ARG_RESCAN_NOW)) && strcmp(optarg, MSG_ORIG(MSG_ARG_DEFERRED))) { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_ILLEGAL), MSG_ORIG(MSG_ARG_Z), optarg); } break; case 'D': /* * If we have not yet read any input files go ahead * and process any debugging options (this allows any * argument processing, entrance criteria and library * initialization to be displayed). Otherwise, if an * input file has been seen, skip interpretation until * process_files (this allows debugging to be turned * on and off around individual groups of files). */ Dflag = 1; if (ofl->ofl_objscnt == 0) { if (dbg_setup(ofl, optarg, 2) == 0) return (S_ERROR); } /* * A diagnostic can only be provided after dbg_setup(). * As this is the first diagnostic that can be produced * by ld(1), issue a title for timing and basic output. */ if ((optitle == 0) && DBG_ENABLED) { optitle++; DBG_CALL(Dbg_basic_options(ofl->ofl_lml)); } DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); break; case 'B': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); if (strcmp(optarg, MSG_ORIG(MSG_ARG_DIRECT)) == 0) { if (Bdflag == SET_FALSE) { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_INCOMP), MSG_ORIG(MSG_ARG_BNODIRECT), MSG_ORIG(MSG_ARG_BDIRECT)); } else { Bdflag = SET_TRUE; ofl->ofl_guideflags |= FLG_OFG_NO_DB; } } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_NODIRECT)) == 0) { if (Bdflag == SET_TRUE) { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_INCOMP), MSG_ORIG(MSG_ARG_BDIRECT), MSG_ORIG(MSG_ARG_BNODIRECT)); } else { Bdflag = SET_FALSE; ofl->ofl_guideflags |= FLG_OFG_NO_DB; } } else if (strcmp(optarg, MSG_ORIG(MSG_STR_SYMBOLIC)) == 0) Bsflag = TRUE; else if (strcmp(optarg, MSG_ORIG(MSG_ARG_REDUCE)) == 0) ofl->ofl_flags |= FLG_OF_PROCRED; else if (strcmp(optarg, MSG_ORIG(MSG_STR_LOCAL)) == 0) Blflag = TRUE; else if (strcmp(optarg, MSG_ORIG(MSG_ARG_GROUP)) == 0) Bgflag = TRUE; else if (strcmp(optarg, MSG_ORIG(MSG_STR_ELIMINATE)) == 0) Beflag = TRUE; else if (strcmp(optarg, MSG_ORIG(MSG_ARG_TRANSLATOR)) == 0) { ld_eprintf(ofl, ERR_WARNING, MSG_INTL(MSG_ARG_UNSUPPORTED), MSG_ORIG(MSG_ARG_BTRANSLATOR)); } else if (strcmp(optarg, MSG_ORIG(MSG_STR_LD_DYNAMIC)) && strcmp(optarg, MSG_ORIG(MSG_ARG_STATIC))) { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_ILLEGAL), MSG_ORIG(MSG_ARG_CB), optarg); } break; case 'G': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, NULL)); otype = OT_SHARED; break; case 'L': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); break; case 'M': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); if (aplist_append(&(ofl->ofl_maps), optarg, AL_CNT_OFL_MAPFILES) == NULL) return (S_ERROR); break; case 'N': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); break; case 'Q': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); if ((optarg[0] == 'n') && (optarg[1] == '\0')) { if (Qflag != SET_UNKNOWN) ld_eprintf(ofl, ERR_WARNING_NF, MSG_INTL(MSG_ARG_MTONCE), MSG_ORIG(MSG_ARG_CQ)); else Qflag = SET_FALSE; } else if ((optarg[0] == 'y') && (optarg[1] == '\0')) { if (Qflag != SET_UNKNOWN) ld_eprintf(ofl, ERR_WARNING_NF, MSG_INTL(MSG_ARG_MTONCE), MSG_ORIG(MSG_ARG_CQ)); else Qflag = SET_TRUE; } else { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_ILLEGAL), MSG_ORIG(MSG_ARG_CQ), optarg); } break; case 'S': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); if (aplist_append(&lib_support, optarg, AL_CNT_SUPPORT) == NULL) return (S_ERROR); break; case 'V': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, NULL)); if (!Vflag) (void) fprintf(stderr, MSG_ORIG(MSG_STR_STRNL), ofl->ofl_sgsid); Vflag = TRUE; break; case 'Y': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); if (strncmp(optarg, MSG_ORIG(MSG_ARG_LCOM), 2) == 0) { if (Llibdir) ld_eprintf(ofl, ERR_WARNING_NF, MSG_INTL(MSG_ARG_MTONCE), MSG_ORIG(MSG_ARG_CYL)); else Llibdir = optarg + 2; } else if (strncmp(optarg, MSG_ORIG(MSG_ARG_UCOM), 2) == 0) { if (Ulibdir) ld_eprintf(ofl, ERR_WARNING_NF, MSG_INTL(MSG_ARG_MTONCE), MSG_ORIG(MSG_ARG_CYU)); else Ulibdir = optarg + 2; } else if (strncmp(optarg, MSG_ORIG(MSG_ARG_PCOM), 2) == 0) { if (Plibpath) ld_eprintf(ofl, ERR_WARNING_NF, MSG_INTL(MSG_ARG_MTONCE), MSG_ORIG(MSG_ARG_CYP)); else Plibpath = optarg + 2; } else { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_ILLEGAL), MSG_ORIG(MSG_ARG_CY), optarg); } break; case '?': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, NULL)); /* * If the option character is '-', we're looking at a * long option which couldn't be translated, display a * more useful error. */ if (optopt == '-') { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_ARG_LONG_UNKNOWN), argv[optind-1]); } else { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_ARG_UNKNOWN), optopt); } (*usage)++; break; default: break; } /* * Update the argument index for the next getopt() iteration. */ ndx = optind; } return (1); } /* * Parsing options pass2 for */ static uintptr_t parseopt_pass2(Ofl_desc *ofl, int argc, char **argv) { int c, ndx = optind; while ((c = ld_getopt(ofl->ofl_lml, ndx, argc, argv)) != -1) { Ifl_desc *ifl; Sym_desc *sdp; switch (c) { case 'l': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); if (ld_find_library(optarg, ofl) == S_ERROR) return (S_ERROR); break; case 'B': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); if (strcmp(optarg, MSG_ORIG(MSG_STR_LD_DYNAMIC)) == 0) { if (ofl->ofl_flags & FLG_OF_DYNAMIC) ofl->ofl_flags |= FLG_OF_DYNLIBS; else { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_ST_INCOMP), MSG_ORIG(MSG_ARG_BDYNAMIC)); } } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_STATIC)) == 0) ofl->ofl_flags &= ~FLG_OF_DYNLIBS; break; case 'L': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); if (ld_add_libdir(ofl, optarg) == S_ERROR) return (S_ERROR); break; case 'N': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); /* * Record DT_NEEDED string */ if (!(ofl->ofl_flags & FLG_OF_DYNAMIC)) ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_ST_INCOMP), MSG_ORIG(MSG_ARG_CN)); if (((ifl = libld_calloc(1, sizeof (Ifl_desc))) == NULL) || (aplist_append(&ofl->ofl_sos, ifl, AL_CNT_OFL_LIBS) == NULL)) return (S_ERROR); ifl->ifl_name = MSG_INTL(MSG_STR_COMMAND); ifl->ifl_soname = optarg; ifl->ifl_flags = (FLG_IF_NEEDSTR | FLG_IF_FILEREF | FLG_IF_DEPREQD); break; case 'D': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); (void) dbg_setup(ofl, optarg, 3); break; case 'u': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); if (ld_sym_add_u(optarg, ofl, MSG_STR_COMMAND) == (Sym_desc *)S_ERROR) return (S_ERROR); break; case 'z': DBG_CALL(Dbg_args_option(ofl->ofl_lml, ndx, c, optarg)); if ((strncmp(optarg, MSG_ORIG(MSG_ARG_LD32), MSG_ARG_LD32_SIZE) == 0) || (strncmp(optarg, MSG_ORIG(MSG_ARG_LD64), MSG_ARG_LD64_SIZE) == 0)) { if (createargv(ofl, 0) == S_ERROR) return (S_ERROR); } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_ALLEXTRT)) == 0) { ofl->ofl_flags1 |= FLG_OF1_ALLEXRT; ofl->ofl_flags1 &= ~FLG_OF1_WEAKEXT; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_WEAKEXT)) == 0) { ofl->ofl_flags1 |= FLG_OF1_WEAKEXT; ofl->ofl_flags1 &= ~FLG_OF1_ALLEXRT; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_DFLEXTRT)) == 0) { ofl->ofl_flags1 &= ~(FLG_OF1_ALLEXRT | FLG_OF1_WEAKEXT); } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_DIRECT)) == 0) { ofl->ofl_flags1 |= FLG_OF1_ZDIRECT; ofl->ofl_guideflags |= FLG_OFG_NO_DB; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_NODIRECT)) == 0) { ofl->ofl_flags1 &= ~FLG_OF1_ZDIRECT; ofl->ofl_guideflags |= FLG_OFG_NO_DB; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_IGNORE)) == 0) { ofl->ofl_flags1 |= FLG_OF1_IGNORE; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_RECORD)) == 0) { ofl->ofl_flags1 &= ~FLG_OF1_IGNORE; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_LAZYLOAD)) == 0) { ofl->ofl_flags1 |= FLG_OF1_LAZYLD; ofl->ofl_guideflags |= FLG_OFG_NO_LAZY; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_NOLAZYLOAD)) == 0) { ofl->ofl_flags1 &= ~ FLG_OF1_LAZYLD; ofl->ofl_guideflags |= FLG_OFG_NO_LAZY; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_GROUPPERM)) == 0) { ofl->ofl_flags1 |= FLG_OF1_GRPPRM; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_NOGROUPPERM)) == 0) { ofl->ofl_flags1 &= ~FLG_OF1_GRPPRM; } else if (strncmp(optarg, MSG_ORIG(MSG_ARG_INITARRAY), MSG_ARG_INITARRAY_SIZE) == 0) { if (((sdp = ld_sym_add_u(optarg + MSG_ARG_INITARRAY_SIZE, ofl, MSG_STR_COMMAND)) == (Sym_desc *)S_ERROR) || (aplist_append(&ofl->ofl_initarray, sdp, AL_CNT_OFL_ARRAYS) == NULL)) return (S_ERROR); } else if (strncmp(optarg, MSG_ORIG(MSG_ARG_FINIARRAY), MSG_ARG_FINIARRAY_SIZE) == 0) { if (((sdp = ld_sym_add_u(optarg + MSG_ARG_FINIARRAY_SIZE, ofl, MSG_STR_COMMAND)) == (Sym_desc *)S_ERROR) || (aplist_append(&ofl->ofl_finiarray, sdp, AL_CNT_OFL_ARRAYS) == NULL)) return (S_ERROR); } else if (strncmp(optarg, MSG_ORIG(MSG_ARG_PREINITARRAY), MSG_ARG_PREINITARRAY_SIZE) == 0) { if (((sdp = ld_sym_add_u(optarg + MSG_ARG_PREINITARRAY_SIZE, ofl, MSG_STR_COMMAND)) == (Sym_desc *)S_ERROR) || (aplist_append(&ofl->ofl_preiarray, sdp, AL_CNT_OFL_ARRAYS) == NULL)) return (S_ERROR); } else if (strncmp(optarg, MSG_ORIG(MSG_ARG_RTLDINFO), MSG_ARG_RTLDINFO_SIZE) == 0) { if (((sdp = ld_sym_add_u(optarg + MSG_ARG_RTLDINFO_SIZE, ofl, MSG_STR_COMMAND)) == (Sym_desc *)S_ERROR) || (aplist_append(&ofl->ofl_rtldinfo, sdp, AL_CNT_OFL_ARRAYS) == NULL)) return (S_ERROR); } else if (strncmp(optarg, MSG_ORIG(MSG_ARG_DTRACE), MSG_ARG_DTRACE_SIZE) == 0) { if ((sdp = ld_sym_add_u(optarg + MSG_ARG_DTRACE_SIZE, ofl, MSG_STR_COMMAND)) == (Sym_desc *)S_ERROR) return (S_ERROR); ofl->ofl_dtracesym = sdp; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_RESCAN_NOW)) == 0) { if (ld_rescan_archives(ofl, 0, ndx) == S_ERROR) return (S_ERROR); } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_RESCAN_START)) == 0) { ofl->ofl_ars_gsndx = ofl->ofl_arscnt; ofl->ofl_ars_gsandx = ndx; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_RESCAN_END)) == 0) { if (ld_rescan_archives(ofl, 1, ndx) == S_ERROR) return (S_ERROR); } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_DEFERRED)) == 0) { ofl->ofl_flags1 |= FLG_OF1_DEFERRED; } else if (strcmp(optarg, MSG_ORIG(MSG_ARG_NODEFERRED)) == 0) { ofl->ofl_flags1 &= ~FLG_OF1_DEFERRED; } default: break; } /* * Update the argument index for the next getopt() iteration. */ ndx = optind; } return (1); } /* * * Pass 1 -- process_flags: collects all options and sets flags */ static uintptr_t process_flags_com(Ofl_desc *ofl, int argc, char **argv, int *usage) { for (; optind < argc; optind++) { /* * If we detect some more options return to getopt(). * Checking argv[optind][1] against null prevents a forever * loop if an unadorned `-' argument is passed to us. */ while ((optind < argc) && (argv[optind][0] == '-')) { if (argv[optind][1] != '\0') { if (parseopt_pass1(ofl, argc, argv, usage) == S_ERROR) return (S_ERROR); } else if (++optind < argc) continue; } if (optind >= argc) break; ofl->ofl_objscnt++; } /* Did an unterminated archive group run off the end? */ if (ofl->ofl_ars_gsandx > 0) { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ARG_AR_GRP_BAD), MSG_INTL(MSG_MARG_AR_GRP_START), MSG_INTL(MSG_MARG_AR_GRP_END)); return (S_ERROR); } return (1); } uintptr_t ld_process_flags(Ofl_desc *ofl, int argc, char **argv) { int usage = 0; /* Collect all argument errors before exit */ if (argc < 2) { usage_mesg(FALSE); return (S_ERROR); } /* * Option handling */ opterr = 0; optind = 1; if (process_flags_com(ofl, argc, argv, &usage) == S_ERROR) return (S_ERROR); /* * Having parsed everything, did we have any usage errors. */ if (usage) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_ARG_USEHELP)); return (S_ERROR); } return (check_flags(ofl, argc)); } /* * Pass 2 -- process_files: skips the flags collected in pass 1 and processes * files. */ static uintptr_t process_files_com(Ofl_desc *ofl, int argc, char **argv) { for (; optind < argc; optind++) { int fd; uintptr_t open_ret; char *path; Rej_desc rej = { 0 }; /* * If we detect some more options return to getopt(). * Checking argv[optind][1] against null prevents a forever * loop if an unadorned `-' argument is passed to us. */ while ((optind < argc) && (argv[optind][0] == '-')) { if (argv[optind][1] != '\0') { if (parseopt_pass2(ofl, argc, argv) == S_ERROR) return (S_ERROR); } else if (++optind < argc) continue; } if (optind >= argc) break; path = argv[optind]; if ((fd = open(path, O_RDONLY)) == -1) { int err = errno; ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_SYS_OPEN), path, strerror(err)); continue; } DBG_CALL(Dbg_args_file(ofl->ofl_lml, optind, path)); open_ret = ld_process_open(path, path, &fd, ofl, (FLG_IF_CMDLINE | FLG_IF_NEEDED), &rej, NULL); if (fd != -1) (void) close(fd); if (open_ret == S_ERROR) return (S_ERROR); /* * Check for mismatched input. */ if (rej.rej_type) { Conv_reject_desc_buf_t rej_buf; ld_eprintf(ofl, ERR_FATAL, MSG_INTL(reject[rej.rej_type]), rej.rej_name ? rej.rej_name : MSG_INTL(MSG_STR_UNKNOWN), conv_reject_desc(&rej, &rej_buf, ld_targ.t_m.m_mach)); return (1); } } return (1); } uintptr_t ld_process_files(Ofl_desc *ofl, int argc, char **argv) { DBG_CALL(Dbg_basic_files(ofl->ofl_lml)); /* * Process command line files (taking into account any applicable * preceding flags). Return if any fatal errors have occurred. */ opterr = 0; optind = 1; if (process_files_com(ofl, argc, argv) == S_ERROR) return (S_ERROR); if (ofl->ofl_flags & FLG_OF_FATAL) return (1); /* * Guidance: Use -B direct/nodirect or -z direct/nodirect. * * This is a backstop for the case where the link had no dependencies. * Otherwise, it will get caught by ld_process_ifl(). We need both, * because -z direct is positional, and its value at the time where * the first dependency is seen might be different than it is now. */ if ((ofl->ofl_flags & FLG_OF_DYNAMIC) && OFL_GUIDANCE(ofl, FLG_OFG_NO_DB)) { ld_eprintf(ofl, ERR_GUIDANCE, MSG_INTL(MSG_GUIDE_DIRECT)); ofl->ofl_guideflags |= FLG_OFG_NO_DB; } /* * Now that all command line files have been processed see if there are * any additional `needed' shared object dependencies. */ if (ofl->ofl_soneed) if (ld_finish_libs(ofl) == S_ERROR) return (S_ERROR); /* * If rescanning archives is enabled, do so now to determine whether * there might still be members extracted to satisfy references from any * explicit objects. Continue until no new objects are extracted. Note * that this pass is carried out *after* processing any implicit objects * (above) as they may already have resolved any undefined references * from any explicit dependencies. */ if (ofl->ofl_flags1 & FLG_OF1_RESCAN) { if (ld_rescan_archives(ofl, 0, argc) == S_ERROR) return (S_ERROR); if (ofl->ofl_flags & FLG_OF_FATAL) return (1); } /* * If debugging, provide statistics on each archives extraction, or flag * any archive that has provided no members. Note that this could be a * nice place to free up much of the archive infrastructure, as we've * extracted any members we need. However, as we presently don't free * anything under ld(1) there's not much point in proceeding further. */ DBG_CALL(Dbg_statistics_ar(ofl)); /* * If any version definitions have been established, either via input * from a mapfile or from the input relocatable objects, make sure any * version dependencies are satisfied, and version symbols created. */ if (ofl->ofl_verdesc) if (ld_vers_check_defs(ofl) == S_ERROR) return (S_ERROR); /* * If input section ordering was specified within some segment * using a mapfile, verify that the expected sections were seen. */ if (ofl->ofl_flags & FLG_OF_IS_ORDER) ld_ent_check(ofl); return (1); } uintptr_t ld_init_strings(Ofl_desc *ofl) { uint_t stflags; if (ofl->ofl_flags1 & FLG_OF1_NCSTTAB) stflags = 0; else stflags = FLG_STNEW_COMPRESS; if (((ofl->ofl_shdrsttab = st_new(stflags)) == NULL) || ((ofl->ofl_strtab = st_new(stflags)) == NULL) || ((ofl->ofl_dynstrtab = st_new(stflags)) == NULL)) return (S_ERROR); return (0); }