Example #1
0
/*
 * Create an archive descriptor.  By maintaining a list of archives any
 * duplicate occurrences of the same archive specified by the user enable us to
 * pick off where the last processing finished.
 */
Ar_desc *
ld_ar_setup(const char *name, Elf *elf, Ofl_desc *ofl)
{
	Ar_desc *	adp;
	size_t		number;
	Elf_Arsym *	start;

	/*
	 * Unless, -z allextract is specified, get the archive symbol table
	 * if one exists, and ignore the file with a warning message otherwise.
	 */
	if (ofl->ofl_flags1 & FLG_OF1_ALLEXRT) {
		start = NULL;
	} else  if ((start = elf_getarsym(elf, &number)) == NULL) {
		if (elf_errno())
			ld_eprintf(ofl, ERR_ELF, MSG_INTL(MSG_ELF_GETARSYM),
			    name);
		else
			ld_eprintf(ofl, ERR_WARNING, MSG_INTL(MSG_ELF_ARSYM),
			    name);
		return (0);
	}

	/*
	 * As this is a new archive reference establish a new descriptor.
	 */
	if ((adp = libld_malloc(sizeof (Ar_desc))) == NULL)
		return ((Ar_desc *)S_ERROR);
	adp->ad_name = name;
	adp->ad_elf = elf;
	adp->ad_start = start;
	if (start) {
		adp->ad_aux = libld_calloc(sizeof (Ar_aux), number);
		if (adp->ad_aux == NULL)
			return ((Ar_desc *)S_ERROR);
	} else {
		adp->ad_aux = NULL;
	}

	/*
	 * Retain any command line options that are applicable to archive
	 * extraction in case we have to rescan this archive later.
	 */
	adp->ad_flags = ofl->ofl_flags1 & MSK_OF1_ARCHIVE;

	ofl->ofl_arscnt++;

	/*
	 * Add this new descriptor to the list of archives.
	 */
	if (aplist_append(&ofl->ofl_ars, adp, AL_CNT_OFL_LIBS) == NULL)
		return ((Ar_desc *)S_ERROR);
	else
		return (adp);
}
Example #2
0
Group_desc *
ld_get_group(Ofl_desc *ofl, Is_desc *isp)
{
	Ifl_desc	*ifl = isp->is_file;
	uint_t		scnndx = isp->is_scnndx;
	Group_desc	*gdp;
	Aliste		idx;

	/*
	 * Scan the GROUP sections associated with this file to find the
	 * matching group section.
	 */
	for (ALIST_TRAVERSE(ifl->ifl_groups, idx, gdp)) {
		size_t	ndx;
		Word	*data;

		if (isp->is_shdr->sh_type == SHT_GROUP) {
			if (isp->is_scnndx == gdp->gd_isc->is_scnndx)
				return (gdp);
			continue;
		}

		data = gdp->gd_data;
		for (ndx = 1; ndx < gdp->gd_cnt; ndx++) {
			if (data[ndx] == scnndx)
				return (gdp);
		}
	}

	ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_ELF_NOGROUPSECT),
	    ifl->ifl_name, EC_WORD(isp->is_scnndx), isp->is_name);
	return (NULL);
}
Example #3
0
/*
 * Exit after cleaning up.
 */
int
ld_exit(Ofl_desc *ofl)
{
	/*
	 * If we have created an output file remove it.
	 */
	if ((ofl->ofl_fd > 0) && ((ofl->ofl_flags1 & FLG_OF1_NONREG) == 0))
		(void) unlink(ofl->ofl_name);

	/*
	 * Inform any support library that the link-edit has failed.
	 */
	ld_sup_atexit(ofl, 1);

	/*
	 * 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));

	return (1);
}
Example #4
0
/*
 * Return the archive member's name.
 *
 * entry:
 *	name - Name of archive
 *	arelf - ELF descriptor for archive member.
 *	ofl - output descriptor
 *
 * exit:
 *	Returns pointer to archive member name on success, NULL on error.
 */
static const char *
ar_member_name(const char *name, Elf *arelf, Ofl_desc *ofl)
{
	Elf_Arhdr	*arhdr;

	if ((arhdr = elf_getarhdr(arelf)) == NULL) {
		ld_eprintf(ofl, ERR_ELF, MSG_INTL(MSG_ELF_GETARHDR), name);
		return (NULL);
	}
	return (arhdr->ar_name);
}
Example #5
0
/*
 * Process the given archive and extract objects for inclusion into
 * the link.
 *
 * entry:
 *	name - Name of archive
 *	fd - Open file descriptor for archive
 *	adp - Archive descriptor
 *	ofl - output descriptor
 *
 * exit:
 *	Returns FALSE on fatal error, TRUE otherwise.
 */
Boolean
ld_process_archive(const char *name, int fd, Ar_desc *adp, Ofl_desc *ofl)
{
	Boolean		found = FALSE;
	Rej_desc	rej = { 0 };

	/*
	 * If a fatal error condition has been set there's really no point in
	 * processing the archive further.  Having got to this point we have at
	 * least established that the archive exists (thus verifying that the
	 * command line options that got us to this archive are correct).  Very
	 * large archives can take a significant time to process, therefore
	 * continuing on from here may significantly delay the fatal error
	 * message the user is already set to receive.
	 */
	if (ofl->ofl_flags & FLG_OF_FATAL)
		return (TRUE);

	/*
	 * If this archive was processed with -z allextract, then all members
	 * have already been extracted.
	 */
	if (adp->ad_elf == NULL)
		return (TRUE);

	if (ofl->ofl_flags1 & FLG_OF1_ALLEXRT) {
		if (!ar_extract_all(name, fd, adp, ofl, &found, &rej))
			return (FALSE);
	} else {
		if (!ar_extract_bysym(name, fd, adp, ofl, &found, &rej))
			return (FALSE);
	}

	/*
	 * If no objects have been found in the archive test for any rejections
	 * and if one had occurred issue a warning - its possible a user has
	 * pointed at an archive containing the wrong class of elf members.
	 */
	if ((found == 0) && rej.rej_type) {
		Conv_reject_desc_buf_t rej_buf;

		ld_eprintf(ofl, ERR_WARNING, 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 (TRUE);
}
Example #6
0
/*
 * Define our signal handler.
 */
static void
/* ARGSUSED2 */
handler(int sig, siginfo_t *sip, void *utp)
{
	struct sigaction	nact;
	Signals *		sigs;

	/*
	 * Reset all ignore handlers regardless of how we got here.
	 */
	nact.sa_handler = SIG_IGN;
	nact.sa_flags = 0;
	(void) sigemptyset(&nact.sa_mask);

	for (sigs = signals; sigs->signo; sigs++) {
		if (sigs->defhdl == SIG_IGN)
			(void) sigaction(sigs->signo, &nact, NULL);
	}

	/*
	 * The model for creating an output file is to ftruncate() it to the
	 * required size and mmap() a mapping into which the new contents are
	 * written.  Neither of these operations guarantee that the required
	 * disk blocks exist, and should we run out of disk space a bus error
	 * is generated.
	 * Other situations have been reported to result in ld catching a bus
	 * error (one instance was a stale NFS handle from an unstable server).
	 * Thus we catch all bus errors and hope we can decode a better error.
	 */
	if ((sig == SIGBUS) && sip && Ofl->ofl_name) {
		ld_eprintf(Ofl, ERR_FATAL, MSG_INTL(MSG_FIL_INTERRUPT),
		    Ofl->ofl_name, strerror(sip->si_errno));
	}
	/*
	 * This assert(0) causes DEBUG enabled linkers to produce a core file.
	 */
	if ((sig != SIGHUP) && (sig != SIGINT))
		assert(0);

	exit(ld_exit(Ofl));
}
Example #7
0
uintptr_t
ld_group_process(Is_desc *gisc, Ofl_desc *ofl)
{
	Ifl_desc	*gifl = gisc->is_file;
	Shdr		*sshdr, *gshdr = gisc->is_shdr;
	Is_desc		*isc;
	Sym		*sym;
	const char	*str;
	Group_desc	gd;
	size_t		ndx;
	int		gnu_stt_section;

	/*
	 * Confirm that the sh_link points to a valid section.
	 */
	if ((gshdr->sh_link == SHN_UNDEF) ||
	    (gshdr->sh_link >= gifl->ifl_shnum) ||
	    ((isc = gifl->ifl_isdesc[gshdr->sh_link]) == NULL)) {
		ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_FIL_INVSHLINK),
		    gifl->ifl_name, EC_WORD(gisc->is_scnndx),
		    gisc->is_name, EC_XWORD(gshdr->sh_link));
		return (0);
	}
	if (gshdr->sh_entsize == 0) {
		ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_FIL_INVSHENTSIZE),
		    gifl->ifl_name, EC_WORD(gisc->is_scnndx), gisc->is_name,
		    EC_XWORD(gshdr->sh_entsize));
		return (0);
	}

	/*
	 * Get the associated symbol table.  Sanity check the sh_info field
	 * (which points to the signature symbol table entry) against the size
	 * of the symbol table.
	 */
	sshdr = isc->is_shdr;
	sym = (Sym *)isc->is_indata->d_buf;

	if ((sshdr->sh_info == SHN_UNDEF) ||
	    (gshdr->sh_info >= (Word)(sshdr->sh_size / sshdr->sh_entsize)) ||
	    ((isc = gifl->ifl_isdesc[sshdr->sh_link]) == NULL)) {
		ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_FIL_INVSHINFO),
		    gifl->ifl_name, EC_WORD(gisc->is_scnndx), gisc->is_name,
		    EC_XWORD(gshdr->sh_info));
		return (0);
	}

	sym += gshdr->sh_info;

	/*
	 * Get the symbol name from the associated string table.
	 */
	str = (char *)isc->is_indata->d_buf;
	str += sym->st_name;

	/*
	 * The GNU assembler can use section symbols as the signature symbol
	 * as described by this comment in the gold linker (found via google):
	 *
	 *	It seems that some versions of gas will create a section group
	 *	associated with a section symbol, and then fail to give a name
	 *	to the section symbol.  In such a case, use the name of the
	 *	section.
	 *
	 * In order to support such objects, we do the same.
	 */
	gnu_stt_section = ((sym->st_name == 0) || (*str == '\0')) &&
	    (ELF_ST_TYPE(sym->st_info) == STT_SECTION);
	if (gnu_stt_section)
		str = gisc->is_name;


	/*
	 * Generate a group descriptor.
	 */
	gd.gd_isc = gisc;
	gd.gd_oisc = NULL;
	gd.gd_name = str;
	gd.gd_data = gisc->is_indata->d_buf;
	gd.gd_cnt = gisc->is_indata->d_size / sizeof (Word);

	/*
	 * If this group is a COMDAT group, validate the signature symbol.
	 */
	if ((gd.gd_data[0] & GRP_COMDAT) && !gnu_stt_section &&
	    ((ELF_ST_BIND(sym->st_info) == STB_LOCAL) ||
	    (sym->st_shndx == SHN_UNDEF))) {
		/* If section symbol, construct a printable name for it */
		if (ELF_ST_TYPE(sym->st_info) == STT_SECTION) {
			if (gisc->is_sym_name == NULL)
				(void) ld_stt_section_sym_name(gisc);

			if (gisc->is_sym_name != NULL)
				str = gisc->is_sym_name;
		}

		ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_GRP_INVALSYM),
		    gifl->ifl_name, EC_WORD(gisc->is_scnndx),
		    gisc->is_name, str);
		return (0);
	}

	/*
	 * If the signature symbol is a name generated by the GNU compiler to
	 * refer to a header, we need sloppy relocation.
	 */
	if (is_header_gensym(str)) {
		if ((ofl->ofl_flags1 & FLG_OF1_NRLXREL) == 0)
			ofl->ofl_flags1 |= FLG_OF1_RLXREL;
		DBG_CALL(Dbg_sec_gnu_comdat(ofl->ofl_lml, gisc, TRUE,
		    (ofl->ofl_flags1 & FLG_OF1_RLXREL) != 0));
	}

	/*
	 * Validate the section indices within the group.  If this is a COMDAT
	 * group, mark each section as COMDAT.
	 */
	for (ndx = 1; ndx < gd.gd_cnt; ndx++) {
		Word	gndx;

		if ((gndx = gd.gd_data[ndx]) >= gifl->ifl_shnum) {
			ld_eprintf(ofl, ERR_FATAL,
			    MSG_INTL(MSG_GRP_INVALNDX), gifl->ifl_name,
			    EC_WORD(gisc->is_scnndx), gisc->is_name, ndx, gndx);
			return (0);
		}

		if (gd.gd_data[0] & GRP_COMDAT)
			gifl->ifl_isdesc[gndx]->is_flags |= FLG_IS_COMDAT;
	}

	/*
	 * If this is a COMDAT group, determine whether this group has already
	 * been encountered, or whether this is the first instance of the group.
	 */
	if ((gd.gd_data[0] & GRP_COMDAT) &&
	    (gpavl_loaded(ofl, &gd) == S_ERROR))
		return (S_ERROR);

	/*
	 * Associate the group descriptor with this input file.
	 */
	if (alist_append(&(gifl->ifl_groups), &gd, sizeof (Group_desc),
	    AL_CNT_IFL_GROUPS) == NULL)
		return (S_ERROR);

	return (1);
}
Example #8
0
/*
 * Archive members are typically extracted to resolve an existing undefined
 * reference.  However, other symbol definitions can cause archive members to
 * be processed to determine if the archive member provides a more appropriate
 * definition.  This routine processes the archive member to determine if the
 * member is really required.
 *
 *  i.	Tentative symbols may cause the extraction of an archive member.
 *	If the archive member has a strong defined symbol it will be used.
 *	If the archive member simply contains another tentative definition,
 *	or a defined function symbol, then it will not be used.
 *
 *  ii.	A symbol reference may define a hidden or protected visibility.  The
 *	reference can only be bound to a definition within a relocatable object
 *	for this restricted visibility to be satisfied.  If the archive member
 * 	provides a definition of the same symbol type, this definition is
 *	taken.  The visibility of the defined symbol is irrelevant, as the most
 *	restrictive visibility of the reference and the definition will be
 *	applied to the final symbol.
 *
 * exit:
 *	Returns 1 if there is a match, 0 if no match is seen, and S_ERROR if an
 *	error occurred.
 */
static uintptr_t
process_member(Ar_mem *amp, const char *name, Sym_desc *sdp, Ofl_desc *ofl)
{
	Sym	*syms, *osym = sdp->sd_sym;
	Xword	symn, cnt;
	char 	*strs;

	/*
	 * Find the first symbol table in the archive member, obtain its
	 * data buffer and determine the number of global symbols (Note,
	 * there must be a symbol table present otherwise the archive would
	 * never have been able to generate its own symbol entry for this
	 * member).
	 */
	if (amp->am_syms == NULL) {
		Elf_Scn		*scn = NULL;
		Shdr		*shdr;
		Elf_Data	*data;

		while (scn = elf_nextscn(amp->am_elf, scn)) {
			if ((shdr = elf_getshdr(scn)) == NULL) {
				ld_eprintf(ofl, ERR_ELF,
				    MSG_INTL(MSG_ELF_GETSHDR), amp->am_path);
				return (S_ERROR);
			}
			if ((shdr->sh_type == SHT_SYMTAB) ||
			    (shdr->sh_type == SHT_DYNSYM))
				break;
		}
		if ((data = elf_getdata(scn, NULL)) == NULL) {
			ld_eprintf(ofl, ERR_ELF, MSG_INTL(MSG_ELF_GETDATA),
			    amp->am_path);
			return (S_ERROR);
		}
		syms = (Sym *)data->d_buf;
		syms += shdr->sh_info;
		symn = shdr->sh_size / shdr->sh_entsize;
		symn -= shdr->sh_info;

		/*
		 * Get the data for the associated string table.
		 */
		if ((scn = elf_getscn(amp->am_elf, (size_t)shdr->sh_link)) ==
		    NULL) {
			ld_eprintf(ofl, ERR_ELF, MSG_INTL(MSG_ELF_GETSCN),
			    amp->am_path);
			return (S_ERROR);
		}
		if ((data = elf_getdata(scn, NULL)) == NULL) {
			ld_eprintf(ofl, ERR_ELF, MSG_INTL(MSG_ELF_GETDATA),
			    amp->am_path);
			return (S_ERROR);
		}
		strs = data->d_buf;

		/*
		 * Initialize the archive member structure in case we have to
		 * come through here again.
		 */
		amp->am_syms = syms;
		amp->am_strs = strs;
		amp->am_symn = symn;
	} else {
		syms = amp->am_syms;
		strs = amp->am_strs;
		symn = amp->am_symn;
	}

	/*
	 * Loop through the symbol table entries looking for a match for the
	 * original symbol.
	 */
	for (cnt = 0; cnt < symn; syms++, cnt++) {
		Word	shndx;

		if ((shndx = syms->st_shndx) == SHN_UNDEF)
			continue;

		if (osym->st_shndx == SHN_COMMON) {
			/*
			 * Determine whether a tentative symbol definition
			 * should be overridden.
			 */
			if ((shndx == SHN_ABS) || (shndx == SHN_COMMON) ||
			    (ELF_ST_TYPE(syms->st_info) == STT_FUNC))
				continue;

			/*
			 * A historic detail requires that a weak definition
			 * within an archive will not override a strong
			 * definition (see sym_realtent() resolution and ABI
			 * symbol binding description - page 4-27).
			 */
			if ((ELF_ST_BIND(syms->st_info) == STB_WEAK) &&
			    (ELF_ST_BIND(osym->st_info) != STB_WEAK))
				continue;
		} else {
			/*
			 * Determine whether a restricted visibility reference
			 * should be overridden.  Don't worry about the
			 * visibility of the archive member definition, nor
			 * whether it is weak or global.  Any definition is
			 * better than a binding to an external shared object
			 * (which is the only event that must presently exist
			 * for us to be here looking for a better alternative).
			 */
			if (ELF_ST_TYPE(syms->st_info) !=
			    ELF_ST_TYPE(osym->st_info))
				continue;
		}

		if (strcmp(strs + syms->st_name, name) == 0)
			return (1);
	}
	return (0);
}
Example #9
0
/*
 * Read the archive symbol table.  For each symbol in the table, determine
 * whether that symbol satisfies an unresolved reference, tentative reference,
 * or a reference that expects hidden or protected visibility.  If so, the
 * corresponding object from the archive is processed.  The archive symbol
 * table is searched until we go through a complete pass without satisfying any
 * unresolved symbols
 *
 * entry:
 *	name - Name of archive
 *	fd - Open file descriptor for archive
 *	adp - Archive descriptor
 *	ofl - output descriptor
 *	found - Address of variable to set to TRUE if any objects are extracted
 *	rej - Rejection descriptor to pass to ld_process_ifl().
 *
 * exit:
 *	Returns FALSE on fatal error. On success, *found will be TRUE
 *	if any object was extracted, rej will be set if any object
 *	was rejected, and TRUE is returned.
 */
static Boolean
ar_extract_bysym(const char *name, int fd, Ar_desc *adp,
    Ofl_desc *ofl, Boolean *found, Rej_desc *rej)
{
	Elf_Arsym *	arsym;
	Elf *		arelf;
	Ar_aux *	aup;
	Sym_desc *	sdp;
	const char	*arname, *arpath;
	Boolean		again = FALSE;
	uintptr_t	err;

	/*
	 * An archive without a symbol table should not reach this function,
	 * because it can only get past ld_ar_setup() in the case where
	 * the archive is first seen under the influence of '-z allextract'.
	 * That will cause the entire archive to be extracted, and any
	 * subsequent reference to the archive will be ignored by
	 * ld_process_archive().
	 */
	if (adp->ad_start == NULL) {
		assert(adp->ad_start != NULL);
		return (TRUE);
	}

	/*
	 * Loop through archive symbol table until we make a complete pass
	 * without satisfying an unresolved reference.  For each archive
	 * symbol, see if there is a symbol with the same name in ld's
	 * symbol table.  If so, and if that symbol is still unresolved or
	 * tentative, process the corresponding archive member.
	 */
	do {
		DBG_CALL(Dbg_file_ar(ofl->ofl_lml, name, again));
		DBG_CALL(Dbg_syms_ar_title(ofl->ofl_lml, name, again));
		again = FALSE;

		for (arsym = adp->ad_start, aup = adp->ad_aux; arsym->as_name;
		    ++arsym, ++aup) {
			Ar_mem		*amp;
			Sym		*sym;
			Boolean		visible = TRUE;
			Boolean		vers;
			Ifl_desc	*ifl;

			/*
			 * If the auxiliary members value indicates that this
			 * member has been processed then this symbol will have
			 * been added to the output file image already or the
			 * object was rejected in which case we don't want to
			 * process it again.
			 */
			if (aup->au_mem == FLG_ARMEM_PROC)
				continue;

			/*
			 * If the auxiliary symbol element is non-zero lookup
			 * the symbol from the internal symbol table.
			 */
			if ((sdp = aup->au_syms) == NULL) {
				if ((sdp = ld_sym_find(arsym->as_name,
				    /* LINTED */
				    (Word)arsym->as_hash, NULL, ofl)) == NULL) {
					DBG_CALL(Dbg_syms_ar_skip(ofl->ofl_lml,
					    name, arsym));
					continue;
				}
				aup->au_syms = sdp;
			}

			/*
			 * With '-z allextract', all members will be extracted.
			 *
			 * This archive member is a candidate for extraction if
			 * the internal symbol originates from an explicit file,
			 * and represents an undefined or tentative symbol.
			 *
			 * By default, weak references do not cause archive
			 * extraction, however the -zweakextract flag overrides
			 * this default.
			 *
			 * If this symbol has already been bound to a versioned
			 * shared object, but the shared objects version is not
			 * available, then a definition of this symbol from
			 * within the archive is a better candidate.  Similarly,
			 * if this symbol has been bound to a shared object, but
			 * the original reference expected hidden or protected
			 * visibility, then a definition of this symbol from
			 * within the archive is a better candidate.
			 */
			vers = TRUE;
			ifl = sdp->sd_file;

			sym = sdp->sd_sym;

			if (sdp->sd_ref == REF_DYN_NEED) {
				uchar_t	vis;

				if (ifl->ifl_vercnt) {
					Word		vndx;
					Ver_index	*vip;

					vndx = sdp->sd_aux->sa_dverndx;
					vip = &ifl->ifl_verndx[vndx];
					if (!(vip->vi_flags & FLG_VER_AVAIL))
						vers = FALSE;
				}

				vis = ELF_ST_VISIBILITY(sym->st_other);
				visible = sym_vis[vis];
			}

			if (((ifl->ifl_flags & FLG_IF_NEEDED) == 0) ||
			    (visible && vers && (sym->st_shndx != SHN_UNDEF) &&
			    (sym->st_shndx != SHN_COMMON)) ||
			    ((ELF_ST_BIND(sym->st_info) == STB_WEAK) &&
			    (!(ofl->ofl_flags1 & FLG_OF1_WEAKEXT)))) {
				DBG_CALL(Dbg_syms_ar_skip(ofl->ofl_lml,
				    name, arsym));
				continue;
			}

			/*
			 * Determine if we have already extracted this member,
			 * and if so reuse the Ar_mem information.
			 */
			if ((amp = aup->au_mem) != 0) {
				arelf = amp->am_elf;
				arname = amp->am_name;
				arpath = amp->am_path;
			} else {
				/*
				 * Set up a new elf descriptor for this member.
				 */
				if (elf_rand(adp->ad_elf, arsym->as_off) !=
				    arsym->as_off) {
					ld_eprintf(ofl, ERR_ELF,
					    MSG_INTL(MSG_ELF_ARMEM), name,
					    EC_WORD(arsym->as_off),
					    demangle(arsym->as_name));
					return (FALSE);
				}

				if ((arelf = elf_begin(fd, ELF_C_READ,
				    adp->ad_elf)) == NULL) {
					ld_eprintf(ofl, ERR_ELF,
					    MSG_INTL(MSG_ELF_BEGIN), name);
					return (FALSE);
				}

				/* Get member filename */
				if ((arname = ar_member_name(name, arelf,
				    ofl)) == NULL)
					return (FALSE);

				/* Construct the member's full pathname */
				if ((arpath = ar_member_path(name, arname)) ==
				    NULL)
					return (S_ERROR);

				/*
				 * Determine whether the support libraries wish
				 * to process this open. See comments in
				 * ld_process_open().
				 */
				ld_sup_open(ofl, &arpath, &arname, &fd,
				    (FLG_IF_EXTRACT | FLG_IF_NEEDED),
				    &arelf, adp->ad_elf, arsym->as_off,
				    elf_kind(arelf));
				if (arelf == NULL) {
					/* Ignore this archive member */
					aup->au_mem = FLG_ARMEM_PROC;
					continue;
				}
			}

			/*
			 * The symbol for which this archive member is being
			 * processed may provide a better alternative to the
			 * symbol that is presently known.  Two cases are
			 * covered:
			 *
			 *  i.	The present symbol represents tentative data.
			 *	The archive member may provide a data
			 *	definition symbol.
			 *  ii.	The present symbol represents a reference that
			 *	has seen a definition within a shared object
			 *	dependency, but the reference expects to be
			 *	reduced to hidden or protected visibility.
			 */
			if ((sym->st_shndx == SHN_COMMON) ||
			    (visible == FALSE)) {
				/*
				 * If we don't already have a member structure
				 * allocate one.
				 */
				if (!amp) {
					if ((amp = libld_calloc(sizeof (Ar_mem),
					    1)) == NULL)
						return (FALSE);
					amp->am_elf = arelf;
					amp->am_name = arname;
					amp->am_path = arpath;
				}
				DBG_CALL(Dbg_syms_ar_checking(ofl->ofl_lml,
				    name, arname, arsym));
				if ((err = process_member(amp, arsym->as_name,
				    sdp, ofl)) == S_ERROR)
					return (FALSE);

				/*
				 * If it turns out that we don't need this
				 * member simply initialize all other auxiliary
				 * entries that match this offset with this
				 * members address.  In this way we can resuse
				 * this information if we recurse back to this
				 * symbol.
				 */
				if (err == 0) {
					if (aup->au_mem == NULL)
						ld_ar_member(adp, arsym,
						    aup, amp);
					continue;
				}
			}

			/*
			 * Process the archive member.  Retain any error for
			 * return to the caller.
			 */
			DBG_CALL(Dbg_syms_ar_resolve(ofl->ofl_lml,
			    name, arname, arsym));
			switch (ar_input(fd, adp, ofl, arelf, arpath,
			    rej)) {
			case S_ERROR:
				return (FALSE);
			case 0:
				/*
				 * Mark the member as extracted so that we
				 * don't try and process it again on a rescan.
				 */
				ld_ar_member(adp, arsym, aup, FLG_ARMEM_PROC);
				continue;
			}

			/*
			 * Note that this archive has contributed something
			 * during this specific operation, and also signal
			 * the need to rescan the archive.
			 */
			*found = again = TRUE;

			ld_ar_member(adp, arsym, aup, FLG_ARMEM_PROC);
		}
	} while (again);

	return (TRUE);
}
Example #10
0
/*
 * Assign move descriptors with the associated target symbol.
 */
static uintptr_t
append_move_desc(Ofl_desc *ofl, Sym_desc *sdp, Elf64_Move *mvp, Is_desc *isp)
{
	int 	i, cnt = mvp->m_repeat;

	for (i = 0; i < cnt; i++) {
		size_t		idx;
		Mv_desc		*omdp, nmd;

		/* LINTED */
		nmd.md_len = ELF_M_SIZE(mvp->m_info);
		nmd.md_start = mvp->m_poffset + i *
		    ((mvp->m_stride + 1) * nmd.md_len);
		nmd.md_move = mvp;

		/*
		 * Verify that this move descriptor doesn't overlap any existing
		 * move descriptors.
		 */
		for (ALIST_TRAVERSE(sdp->sd_move, idx, omdp)) {
			Mv_desc	*smdp, *lmdp;

			if (nmd.md_start > omdp->md_start) {
				smdp = omdp;
				lmdp = &nmd;
			} else {
				smdp = &nmd;
				lmdp = omdp;
			}

			/*
			 * If this move entry is exactly the same as that of
			 * a symbol that has overridden this symbol (for example
			 * should two identical COMMON definitions be associated
			 * with the same move data), simply ignore this move
			 * element.
			 */
			if ((nmd.md_start == omdp->md_start) &&
			    ((nmd.md_len == smdp->md_len) &&
			    sdp->sd_file != isp->is_file))
				continue;

			if ((nmd.md_start != omdp->md_start) &&
			    ((smdp->md_start + smdp->md_len) <= lmdp->md_start))
				continue;

			ld_eprintf(ofl, ERR_FATAL, MSG_MOVE_OVERLAP,
			    sdp->sd_file->ifl_name, EC_WORD(isp->is_scnndx),
			    isp->is_name, demangle(sdp->sd_name),
			    EC_XWORD(nmd.md_start), EC_XWORD(nmd.md_len),
			    EC_XWORD(omdp->md_start), EC_XWORD(omdp->md_len));

			/*
			 * Indicate that an error has occurred, so that
			 * processing can be terminated once all move errors
			 * are flushed out.
			 */
			sdp->sd_flags |= FLG_SY_OVERLAP;
			return (1);
		}

		if (alist_append(&sdp->sd_move, &nmd, sizeof (Mv_desc),
		    AL_CNT_SDP_MOVE) == NULL)
			return (S_ERROR);
	}
	return (1);
}
Example #11
0
/*
 * Validate a SHT_SUNW_move section.  These are only processed from input
 * relocatable objects.  The move section entries are validated and any data
 * structures required for later processing are created.
 */
uintptr_t
ld_process_move(Ofl_desc *ofl)
{
	size_t		idx;
	Is_desc		*isp;
	int 		errcnt = 0;

	for (APLIST_TRAVERSE(ofl->ofl_ismove, idx, isp)) {
		Ifl_desc	*ifile = isp->is_file;
		Elf64_Move		*mvp;
		Elf64_Xword		i, num;

		mvp = (Elf64_Move *)isp->is_indata->d_buf;

		if (isp->is_shdr->sh_entsize == 0) {
			ld_eprintf(ofl, ERR_FATAL,
			    MSG_FIL_INVSHENTSIZE,
			    isp->is_file->ifl_name, EC_WORD(isp->is_scnndx),
			    isp->is_name, EC_XWORD(0));
			return (S_ERROR);
		}
		num = isp->is_shdr->sh_size / isp->is_shdr->sh_entsize;

		for (i = 0; i < num; i++) {
			Elf64_Xword 		ndx = ELF_M_SYM(mvp->m_info);
			Sym_desc	*sdp;
			Elf64_Sym	*sym;

			if ((ndx >= (Elf64_Xword) isp->is_file->ifl_symscnt) ||
			    (ndx == 0)) {
				ld_eprintf(ofl, ERR_FATAL,
				    MSG_PSYM_INVMINFO1,
				    isp->is_file->ifl_name,
				    EC_WORD(isp->is_scnndx), isp->is_name, i,
				    EC_XWORD(mvp->m_info));
				return (S_ERROR);
			}
			if (mvp->m_repeat == 0) {
				ld_eprintf(ofl, ERR_FATAL,
				    MSG_PSYM_INVMREPEAT,
				    isp->is_file->ifl_name,
				    EC_WORD(isp->is_scnndx), isp->is_name, i,
				    EC_XWORD(mvp->m_repeat));
				return (S_ERROR);
			}

			sdp = isp->is_file->ifl_oldndx[ndx];

			/*
			 * Validate that this entry has a valid size.
			 */
			/* LINTED */
			switch (ELF_M_SIZE(mvp->m_info)) {
			case 1: case 2: case 4: case 8:
				break;
			default:
				ld_eprintf(ofl, ERR_FATAL,
				    MSG_PSYM_INVMINFO2,
				    isp->is_file->ifl_name,
				    EC_WORD(isp->is_scnndx), isp->is_name, i,
				    EC_XWORD(mvp->m_info));
				return (S_ERROR);
			}

			/*
			 * If this is a global symbol, adjust the visibility.
			 */
			if (sdp->sd_aux &&
			    ((sdp->sd_flags & FLG_SY_VISIBLE) == 0))
				ld_sym_adjust_vis(sdp, ofl);

			sym = sdp->sd_sym;

			if (sdp->sd_move == NULL) {
				/*
				 * If this is the first move entry associated
				 * with this symbol, save the symbol on the
				 * partial symbol list, and initialize various
				 * state regarding this symbol.
				 */
				if (aplist_append(&ofl->ofl_parsyms, sdp,
				    AL_CNT_OFL_PARSYMS) == NULL)
					return (S_ERROR);

				/*
				 * Even if -zredlocsym is in effect, the local
				 * symbol used for partial initialization is
				 * kept.
				 */
				if ((ofl->ofl_flags & FLG_OF_REDLSYM) &&
				    (ELF_ST_BIND(sym->st_info) == STB_LOCAL) &&
				    (ELF_ST_TYPE(sym->st_info) == STT_OBJECT)) {
					ofl->ofl_locscnt++;
					if (st_insert(ofl->ofl_strtab,
					    sdp->sd_name) == -1)
						return (S_ERROR);
				}

				/*
				 * Mark the input section associated with this
				 * partially initialized symbol.
				 * This is needed when the symbol
				 * the relocation entry uses symbol information
				 * not from the symbol entry.
				 *
				 * For executable, the following is
				 * needed only for expanded symbol. However,
				 * for shared object any partially non
				 * expanded symbols are moved from
				 * .bss/COMMON to .sunwbss. So the following are
				 * needed.
				 */
				if ((sym->st_shndx != SHN_UNDEF) &&
				    (sym->st_shndx < SHN_LOPROC)) {
					Is_desc	*isc;

					isc = ifile->ifl_isdesc[ sym->st_shndx];
					isc->is_flags |= FLG_IS_RELUPD;

					if (sdp->sd_osym == NULL) {
						if ((sdp->sd_osym =
						    libld_calloc(sizeof(Elf64_Sym),
						    1)) == NULL)
							return (S_ERROR);
						*(sdp->sd_osym) =
						    *(sdp->sd_sym);
					}
				}
			}

			if (append_move_desc(ofl, sdp, mvp, isp) == S_ERROR)
				return (S_ERROR);

			if (sdp->sd_flags & FLG_SY_OVERLAP)
				errcnt++;

			/*
			 * If this symbol is marked to be expanded, go to the
			 * next move entry.
			 */
			if (sdp->sd_flags & FLG_SY_PAREXPN) {
				mvp++;
				continue;
			}

			/*
			 * Decide whether this partial symbol is to be expanded
			 * or not.
			 *
			 * The symbol will be expanded if:
			 *	a) '-z nopartial' is specified
			 *	b) move entries covered entire symbol
			 *
			 * To expand an move entry, size of the symbol to be
			 * expanded need to be known to generate a file space.
			 * (see make_movesections().)
			 *
			 * Therefore the move entry can not be expanded
			 * if the partial symbol is a section symbol.
			 * (The size of the symbol may be unknown.)
			 * This may happen, for example, when a local symbol is
			 * reduced by the -zredlocsym.
			 *
			 * The following two if statements checks the
			 * if the move entry can be expanded or not.
			 */
			if (OFL_IS_STATIC_EXEC(ofl)) {
				if (ELF_ST_TYPE(sym->st_info) == STT_SECTION) {
					errcnt++;
					ld_eprintf(ofl, ERR_FATAL,
					    MSG_PSYM_CANNOTEXPND,
					    sdp->sd_file->ifl_name,
					    EC_WORD(isp->is_scnndx),
					    isp->is_name, i,
					    MSG_PSYM_NOSTATIC);
				} else {
					sdp->sd_flags |= FLG_SY_PAREXPN;
				}
			} else if ((ofl->ofl_flags1 & FLG_OF1_NOPARTI) != 0) {
				if (ELF_ST_TYPE(sym->st_info) == STT_SECTION) {
					ld_eprintf(ofl, ERR_WARNING,
					    MSG_PSYM_CANNOTEXPND,
					    sdp->sd_file->ifl_name,
					    EC_WORD(isp->is_scnndx),
					    isp->is_name, i,
					    MSG_STR_EMPTY);
				} else {
					sdp->sd_flags |= FLG_SY_PAREXPN;
				}
			} else if (((Elf64_Xword)((sizeof (Elf64_Move)) *
			    alist_nitems(sdp->sd_move)) > sym->st_size) &&
			    (ELF_ST_TYPE(sym->st_info) == STT_OBJECT)) {
				sdp->sd_flags |= FLG_SY_PAREXPN;
			}

			/*
			 * If a move entry exists that references a local
			 * symbol, and this symbol reference will eventually
			 * be assigned to the associated section, make sure the
			 * section symbol is available for relocating against
			 * at runtime.
			 */
			if ((ELF_ST_BIND(sym->st_info) == STB_LOCAL) &&
			    (((ofl->ofl_flags & FLG_OF_RELOBJ) == 0) ||
			    (ofl->ofl_flags & FLG_OF_REDLSYM))) {
				Os_desc *osp = sdp->sd_isc->is_osdesc;

				if (osp &&
				    ((osp->os_flags & FLG_OS_OUTREL) == 0)) {
					ofl->ofl_dynshdrcnt++;
					osp->os_flags |= FLG_OS_OUTREL;
				} else if ((sdp->sd_flags &
				    FLG_SY_PAREXPN) == 0)
					ofl->ofl_flags1 |= FLG_OF1_BSSOREL;
			}
			mvp++;
		}
	}

	if (errcnt != 0)
		return (S_ERROR);
	if (make_mvsections(ofl) == S_ERROR)
		return (S_ERROR);

	return (1);
}
Example #12
0
/*
 * Initialize new entrance and segment descriptors and add them as lists to
 * the output file descriptor.
 */
uintptr_t
ld_ent_setup(Ofl_desc *ofl, Elf64_Xword segalign)
{
	Ent_desc	*enp;
	predef_seg_t	*psegs;
	Sg_desc		*sgp;
	size_t		idx;

	/*
	 * Initialize the elf library.
	 */
	if (elf_version(EV_CURRENT) == EV_NONE) {
		ld_eprintf(ofl, ERR_FATAL, MSG_ELF_LIBELF, EV_CURRENT);
		return (S_ERROR);
	}

	/*
	 * Initialize internal Global Symbol Table AVL tree
	 */
	avl_create(&ofl->ofl_symavl, &ld_sym_avl_comp, sizeof (Sym_avlnode),
	    SGSOFFSETOF(Sym_avlnode, sav_node));

	/* Initialize segment AVL tree */
	avl_create(&ofl->ofl_segs_avl, ofl_segs_avl_cmp,
	    sizeof (Sg_desc), SGSOFFSETOF(Sg_desc, sg_avlnode));

	/* Initialize entrance criteria AVL tree */
	avl_create(&ofl->ofl_ents_avl, ofl_ents_avl_cmp, sizeof (Ent_desc),
	    SGSOFFSETOF(Ent_desc, ec_avlnode));


	/*
	 * Allocate and initialize writable copies of both the entrance and
	 * segment descriptors.
	 *
	 * Note that on non-amd64 targets, this allocates a few more
	 * elements than are needed. For now, we are willing to overallocate
	 * a small amount to simplify the code.
	 */
	if ((psegs = libld_malloc(sizeof (sg_desc))) == NULL)
		return (S_ERROR);
	(void) memcpy(psegs, &sg_desc, sizeof (sg_desc));
	sgp = (Sg_desc *) psegs;

	/*
	 * The data segment and stack permissions can differ:
	 *
	 *	- Architectural/ABI per-platform differences
	 *	- Whether the object is built statically or dynamically
	 *
	 * Those segments so affected have their program header flags
	 * set here at runtime, rather than in the sg_desc templates above.
	 */
	psegs->psg_data.sg_phdr.p_flags = ld_targ.t_m.m_dataseg_perm;
	psegs->psg_bss.sg_phdr.p_flags = ld_targ.t_m.m_dataseg_perm;
	psegs->psg_dynamic.sg_phdr.p_flags = ld_targ.t_m.m_dataseg_perm;
	psegs->psg_sunwdtrace.sg_phdr.p_flags = ld_targ.t_m.m_dataseg_perm;
#if	defined(_ELF64)
	psegs->psg_ldata.sg_phdr.p_flags = ld_targ.t_m.m_dataseg_perm;
	psegs->psg_sunwdtrace.sg_phdr.p_flags |= PF_X;
#endif
	psegs->psg_sunwstack.sg_phdr.p_flags = ld_targ.t_m.m_stack_perm;
	if ((ofl->ofl_flags & FLG_OF_DYNAMIC) == 0)
		psegs->psg_data.sg_phdr.p_flags |= PF_X;

	/*
	 * Traverse the new entrance descriptor list converting the segment
	 * pointer entries to the absolute address within the new segment
	 * descriptor list.  Add each entrance descriptor to the output file
	 * list.
	 */
	if ((enp = libld_malloc(sizeof (ent_desc))) == NULL)
		return (S_ERROR);
	(void) memcpy(enp, ent_desc, sizeof (ent_desc));
	for (idx = 0; idx < (sizeof (ent_desc) / sizeof (ent_desc[0])); idx++,
	    enp++) {

#if	defined(_ELF64)
		/* Don't use the amd64 entry conditions for non-amd64 targets */
		if ((enp->ec_attrmask & SHF_X86_64_LARGE) &&
		    (ld_targ.t_m.m_mach != EM_X86_64))
			continue;
#endif
		if (aplist_append(&ofl->ofl_ents, enp,
		    AL_CNT_OFL_ENTRANCE) == NULL)
			return (S_ERROR);

		/*
		 * The segment pointer is currently pointing at a template
		 * segment descriptor in sg_desc. Compute its array index,
		 * and then use that index to compute the address of the
		 * corresponding descriptor in the writable copy.
		 */
		enp->ec_segment =
		    &sgp[(enp->ec_segment - (Sg_desc *) &sg_desc)];
	}

	/*
	 * Add each segment descriptor to the segment descriptor list. The
	 * ones with non-NULL sg_name are also entered into the AVL tree.
	 * For each loadable segment initialize a default alignment. Note
	 * that ld(1) and ld.so.1 initialize this differently.
	 */
	for (idx = 0; idx < predef_seg_nelts; idx++, sgp++) {
		Elf64_Phdr	*phdr = &(sgp->sg_phdr);

		/* Ignore amd64 segment templates for non-amd64 targets */
		switch (sgp->sg_id) {
		case SGID_LRODATA:
		case SGID_LDATA:
			if ((ld_targ.t_m.m_mach != EM_X86_64))
				continue;
		}

		if (phdr->p_type == PT_LOAD)
			phdr->p_align = segalign;

		if ((aplist_append(&ofl->ofl_segs, sgp,
		    AL_CNT_SEGMENTS)) == NULL)
			return (S_ERROR);

#ifdef NDEBUG			/* assert() is enabled */
		/*
		 * Enforce the segment name rule: Any segment that can
		 * be referenced by an entrance descriptor must have
		 * a name. Any segment that cannot, must have a NULL
		 * name pointer.
		 */
		switch (phdr->p_type) {
		case PT_LOAD:
		case PT_NOTE:
		case PT_NULL:
			assert(sgp->sg_name != NULL);
			break;
		default:
			assert(sgp->sg_name == NULL);
			break;
		}
#endif

		/*
		 * Add named segment descriptors to the AVL tree to
		 * provide O(logN) lookups.
		 */
		if (sgp->sg_name != NULL)
			avl_add(&ofl->ofl_segs_avl, sgp);
	}

	return (1);
}
Example #13
0
/*
 * Create an unwind header (.eh_frame_hdr) output section.
 * The section is created and space reserved, but the data
 * is not copied into place. That is done by a later call
 * to ld_unwind_populate(), after active relocations have been
 * processed.
 *
 * When GNU linkonce processing is in effect, we can end up in a situation
 * where the FDEs related to discarded sections remain in the eh_frame
 * section. Ideally, we would remove these dead entries from eh_frame.
 * However, that optimization has not yet been implemented. In the current
 * implementation, the number of dead FDEs cannot be determined until
 * active relocations are processed, and that processing follows the
 * call to this function. This means that we are unable to detect dead FDEs
 * here, and the section created by this routine is sized for maximum case
 * where all FDEs are valid.
 */
uintptr_t
ld_unwind_make_hdr(Ofl_desc *ofl)
{
	int		bswap = (ofl->ofl_flags1 & FLG_OF1_ENCDIFF) != 0;
	Shdr		*shdr;
	Elf_Data	*elfdata;
	Is_desc		*isp;
	size_t		size;
	Xword		fde_cnt;
	Aliste		idx1;
	Os_desc		*osp;

	/*
	 * we only build a unwind header if we have
	 * some unwind information in the file.
	 */
	if (ofl->ofl_unwind == NULL)
		return (1);

	/*
	 * Allocate and initialize the Elf_Data structure.
	 */
	if ((elfdata = libld_calloc(sizeof (Elf_Data), 1)) == NULL)
		return (S_ERROR);
	elfdata->d_type = ELF_T_BYTE;
	elfdata->d_align = ld_targ.t_m.m_word_align;
	elfdata->d_version = ofl->ofl_dehdr->e_version;

	/*
	 * Allocate and initialize the Shdr structure.
	 */
	if ((shdr = libld_calloc(sizeof (Shdr), 1)) == NULL)
		return (S_ERROR);
	shdr->sh_type = ld_targ.t_m.m_sht_unwind;
	shdr->sh_flags = SHF_ALLOC;
	shdr->sh_addralign = ld_targ.t_m.m_word_align;
	shdr->sh_entsize = 0;

	/*
	 * Allocate and initialize the Is_desc structure.
	 */
	if ((isp = libld_calloc(1, sizeof (Is_desc))) == NULL)
		return (S_ERROR);
	isp->is_name = MSG_ORIG(MSG_SCN_UNWINDHDR);
	isp->is_shdr = shdr;
	isp->is_indata = elfdata;

	if ((ofl->ofl_unwindhdr = ld_place_section(ofl, isp, NULL,
	    ld_targ.t_id.id_unwindhdr, NULL)) == (Os_desc *)S_ERROR)
		return (S_ERROR);

	/*
	 * Scan through all of the input Frame information, counting each FDE
	 * that requires an index.  Each fde_entry gets a corresponding entry
	 * in the binary search table.
	 */
	fde_cnt = 0;
	for (APLIST_TRAVERSE(ofl->ofl_unwind, idx1, osp)) {
		Aliste	idx2;
		int	os_isdescs_idx;

		OS_ISDESCS_TRAVERSE(os_isdescs_idx, osp, idx2, isp) {
			uchar_t		*data;
			uint64_t	off = 0;

			data = isp->is_indata->d_buf;
			size = isp->is_indata->d_size;

			while (off < size) {
				uint_t		length, id;
				uint64_t	ndx = 0;

				/*
				 * Extract length in lsb format.  A zero length
				 * indicates that this CIE is a terminator and
				 * that processing for unwind information is
				 * complete.
				 */
				length = extract_uint(data + off, &ndx, bswap);
				if (length == 0)
					break;

				/*
				 * Extract CIE id in lsb format.
				 */
				id = extract_uint(data + off, &ndx, bswap);

				/*
				 * A CIE record has a id of '0', otherwise
				 * this is a FDE entry and the 'id' is the
				 * CIE pointer.
				 */
				if (id == 0) {
					uint_t	cieversion;
					/*
					 * The only CIE version supported
					 * is '1' - quick sanity check
					 * here.
					 */
					cieversion = data[off + ndx];
					ndx += 1;
					/* BEGIN CSTYLED */
					if (cieversion != 1) {
					    ld_eprintf(ofl, ERR_FATAL,
						MSG_INTL(MSG_UNW_BADCIEVERS),
						isp->is_file->ifl_name,
						isp->is_name, off);
					    return (S_ERROR);
					}
					/* END CSTYLED */
				} else {
					fde_cnt++;
				}
				off += length + 4;
			}
		}
	}
Example #14
0
static uintptr_t
vers_visit_children(Ofl_desc *ofl, Ver_desc *vp, int flag)
{
	size_t			idx;
	Ver_desc		*vdp;
	static int		err = 0;
	static Ver_Stack	ver_stk = {0, 0, 0};
	int			tmp_sp;

	/*
	 * If there was any fatal error,
	 * just return.
	 */
	if (err == S_ERROR)
		return (err);

	/*
	 * if this is called from, ver_check_defs(), initialize sp.
	 */
	if (flag == 0)
		ver_stk.ver_sp = 0;

	/*
	 * Check if passed version pointer vp is already in the stack.
	 */
	for (tmp_sp = 0; tmp_sp < ver_stk.ver_sp; tmp_sp++) {
		Ver_desc *v;

		v = ver_stk.ver_stk[tmp_sp];
		if (v == vp) {
			/*
			 * cyclic dependency.
			 */
			if (err == 0) {
				ld_eprintf(ofl, ERR_FATAL,
				    (MSG_VER_CYCLIC));
				err = 1;
			}
			for (tmp_sp = 0; tmp_sp < ver_stk.ver_sp; tmp_sp++) {
				v = ver_stk.ver_stk[tmp_sp];
				if ((v->vd_flags & FLG_VER_CYCLIC) == 0) {
					v->vd_flags |= FLG_VER_CYCLIC;
					ld_eprintf(ofl, ERR_NONE,
					    (MSG_VER_ADDVER),
					    v->vd_name);
				}
			}
			if ((vp->vd_flags & FLG_VER_CYCLIC) == 0) {
				vp->vd_flags |= FLG_VER_CYCLIC;
				ld_eprintf(ofl, ERR_NONE,
				    (MSG_VER_ADDVER), vp->vd_name);
			}
			return (err);
		}
	}

	/*
	 * Push version on the stack.
	 */
	if (ver_stk.ver_sp >= ver_stk.ver_lmt) {
		ver_stk.ver_lmt += _NUM_OF_VERS_;
		if ((ver_stk.ver_stk = (Ver_desc **)
		    libld_realloc((void *)ver_stk.ver_stk,
		    ver_stk.ver_lmt * sizeof (Ver_desc *))) == NULL)
			return (S_ERROR);
	}
	ver_stk.ver_stk[(ver_stk.ver_sp)++] = vp;

	/*
	 * Now visit children.
	 */
	for (APLIST_TRAVERSE(vp->vd_deps, idx, vdp))
		if (vers_visit_children(ofl, vdp, 1) == S_ERROR)
			return (S_ERROR);

	/*
	 * Pop version from the stack.
	 */
	(ver_stk.ver_sp)--;

	return (err);
}
Example #15
0
uintptr_t
ld_vers_check_defs(Ofl_desc *ofl)
{
	size_t		idx1;
	Ver_desc	*vdp;
	uintptr_t 	is_cyclic = 0;

	//DBG_CALL(Dbg_ver_def_title(ofl->ofl_lml, ofl->ofl_name));

	/*
	 * First check if there are any cyclic dependency
	 */
	for (APLIST_TRAVERSE(ofl->ofl_verdesc, idx1, vdp))
		if ((is_cyclic = vers_visit_children(ofl, vdp, 0)) == S_ERROR)
			return (S_ERROR);

	if (is_cyclic)
		ofl->ofl_flags |= FLG_OF_FATAL;

	for (APLIST_TRAVERSE(ofl->ofl_verdesc, idx1, vdp)) {
		uint8_t	cnt;
		Elf64_Sym	*sym;
		Sym_desc	*sdp;
		const char	*name = vdp->vd_name;
		unsigned char	bind;
		Ver_desc	*_vdp;
		avl_index_t	where;
		size_t		idx2;

		if (vdp->vd_ndx == 0) {
			ld_eprintf(ofl, ERR_FATAL,
			    (MSG_VER_UNDEF), name, vdp->vd_ref->vd_name,
			    vdp->vd_ref->vd_file->ifl_name);
			continue;
		}

		//DBG_CALL(Dbg_ver_desc_entry(ofl->ofl_lml, vdp));

		/*
		 * If a version definition contains no symbols this is possibly
		 * a mapfile error.
		 */
		#if 0
		if ((vdp->vd_flags &
		    (VER_FLG_BASE | VER_FLG_WEAK | FLG_VER_REFER)) == 0)
			DBG_CALL(Dbg_ver_nointerface(ofl->ofl_lml,	
		    	    vdp->vd_name));
		#endif

		/*
		 * Update the version entry count to account for this new
		 * version descriptor (the count is the size in bytes).
		 */
		ofl->ofl_verdefsz += sizeof (Elf64_Verdef);

		/*
		 * Traverse this versions dependency list to determine what
		 * additional version dependencies we must account for against
		 * this descriptor.
		 */
		cnt = 1;
		for (APLIST_TRAVERSE(vdp->vd_deps, idx2, _vdp)) {
#if	defined(__lint)
			/* get lint to think `_vdp' is used... */
			vdp = _vdp;
#endif
			cnt++;
		}
		ofl->ofl_verdefsz += (cnt * sizeof (Elf64_Verdaux));

		/*
		 * Except for the base version descriptor, generate an absolute
		 * symbol to reflect this version.
		 */
		if (vdp->vd_flags & VER_FLG_BASE)
			continue;

		if (vdp->vd_flags & VER_FLG_WEAK)
			bind = STB_WEAK;
		else
			bind = STB_GLOBAL;

		if (sdp = ld_sym_find(name, vdp->vd_hash, &where, ofl)) {
			/*
			 * If the symbol already exists and is undefined or was
			 * defined in a shared library, convert it to an
			 * absolute.
			 */
			if ((sdp->sd_sym->st_shndx == SHN_UNDEF) ||
			    (sdp->sd_ref != REF_REL_NEED)) {
				sdp->sd_shndx = sdp->sd_sym->st_shndx = SHN_ABS;
				sdp->sd_sym->st_info =
				    ELF_ST_INFO(bind, STT_OBJECT);
				sdp->sd_ref = REF_REL_NEED;
				sdp->sd_flags |= (FLG_SY_SPECSEC |
				    FLG_SY_DEFAULT | FLG_SY_EXPDEF);
				sdp->sd_aux->sa_overndx = vdp->vd_ndx;

				/*
				 * If the reference originated from a mapfile
				 * insure we mark the symbol as used.
				 */
				if (sdp->sd_flags & FLG_SY_MAPREF)
					sdp->sd_flags |= FLG_SY_MAPUSED;

			} else if ((sdp->sd_flags & FLG_SY_SPECSEC) &&
			    (sdp->sd_sym->st_shndx != SHN_ABS) &&
			    (sdp->sd_ref == REF_REL_NEED)) {
				ld_eprintf(ofl, ERR_WARNING,
				    (MSG_VER_DEFINED), name,
				    sdp->sd_file->ifl_name);
			}
		} else {
			/*
			 * If the symbol does not exist create it.
			 */
			if ((sym = libld_calloc(sizeof (Elf64_Sym), 1)) == NULL)
				return (S_ERROR);

			sym->st_shndx = SHN_ABS;
			sym->st_info = ELF_ST_INFO(bind, STT_OBJECT);
			
			//DBG_CALL(Dbg_ver_symbol(ofl->ofl_lml, name));

			if ((sdp = ld_sym_enter(name, sym, vdp->vd_hash,
			    vdp->vd_file, ofl, 0, SHN_ABS,
			    (FLG_SY_SPECSEC | FLG_SY_DEFAULT | FLG_SY_EXPDEF),
			    &where)) == (Sym_desc *)S_ERROR)
				return (S_ERROR);

			sdp->sd_ref = REF_REL_NEED;
			sdp->sd_aux->sa_overndx = vdp->vd_ndx;
		}
	}
	return (1);
}