Example #1
0
/*
 * Delete the existing runpath from the object.
 *
 * entry:
 *	dynsec - Dynamic section
 *	numdyn - # of elements in dynamic array
 *
 * exit:
 *	Returns True (1) if the dynamic section was modified, and
 *	False (0) otherwise.
 *
 * note:
 *	The way this works is that we look at each item in the
 *	dynamic section and simply remove any DT_RPATH or
 *	DT_RUNPATH entries, copying up anything following in order
 *	to fill the hole. This is all that is needed to completely
 *	remove a runpath from an object. Note that the string is left
 *	in the string table. There is no safe way to remove it, since
 *	we cannot know that it is not used by any other item in the file,
 *	and no way to track or reuse the space if we did remove it.
 *	On the plus side, this means we can always restore the old
 *	runpath without having to add a new string.
 */
static int
remove_runpath(Cache *dynsec, GElf_Word numdyn)
{
	GElf_Dyn	dyn;
	GElf_Word	ndx, cpndx;
	int		changed = 0;

	for (ndx = cpndx = 0; ndx < numdyn; ndx++) {
		if (gelf_getdyn(dynsec->c_data, ndx, &dyn) == NULL)
			msg_elf("gelf_getdyn");

		/* Skip over any runpath element */
		if ((dyn.d_tag == DT_RPATH) || (dyn.d_tag == DT_RUNPATH)) {
			msg_debug("[%d]%s[%d]: skip runpath\n",
			    dynsec->c_ndx, dynsec->c_name, ndx);
			continue;
		}

		/*
		 * If cpndx and ndx differ, it means that we
		 * have skipped over a runpath in an earlier
		 * iteration. In this case, we need to copy
		 * the item in dynamic[ndx] back to dynamic[cpndx].
		 */
		if (ndx != cpndx) {
			msg_debug("[%d]%s[%d]: copy from [%d]\n",
			    dynsec->c_ndx, dynsec->c_name, cpndx, ndx);
			if (gelf_update_dyn(dynsec->c_data, cpndx, &dyn) == 0)
				msg_elf("gelf_update_dyn");
			changed = 1;
		}

		/* Advance copy index to receive next item */
		cpndx++;
	}

	/*
	 * If we removed a runpath element, then we should zero
	 * the slots left at the end. This is not strictly
	 * necessary, since the linker should not look past the
	 * first DT_NULL, but it is good to not leave garbage
	 * lying around.
	 */
	bzero(&dyn, sizeof (dyn));	/* Note: DT_NULL is 0 */
	for (; cpndx < numdyn; cpndx++) {
		msg_debug("[%d]%s[%d]: Set to DT_NULL\n",
		    dynsec->c_ndx, dynsec->c_name, cpndx);
		if (gelf_update_dyn(dynsec->c_data, cpndx, &dyn) == 0)
			msg_elf("gelf_update_dyn");
		changed = 1;
	}

	return (changed);
}
Example #2
0
/*
 * Returns the offset of the specified string from within
 * the given string table.
 *
 * entry:
 *	dynsec - Dynamic section descriptor
 *	strsec - Descriptor for string table associated with dynamic section
 *	dyn_strpad - DT_SUNW_STRPAD element from dynamic section
 *	str - String we are looking for.
 *
 * exit:
 *	On success, the offset of the given string within the string
 *	table is returned. If the string does not exist within the table,
 *	but there is a valid DT_SUNW_STRPAD reserved section, then we
 *	add the string, and update the dynamic section STRPAD element
 *	to reflect the space we use.
 *
 *	This routine does not return on failure.
 */
static GElf_Word
dynstr_insert(Cache *dynsec, Cache *strsec, dyn_elt_t *dyn_strpad,
    const char *str)
{
	GElf_Word	ins_off;	/* Table offset to 1st reserved byte */
	char		*s;		/* ptr to strings within table */
	GElf_Word	len;		/* Length of str inc. NULL byte */
	GElf_Word	tail_ign;	/* # reserved bytes at end of strtab */


	tail_ign = dyn_strpad->seen ? dyn_strpad->dyn.d_un.d_val : 0;

	/* Does the string already exist in the string table? */
	if (sec_findstr(strsec, tail_ign, str, &len))
		return (len);

	/*
	 * The desired string does not already exist. Do we have
	 * room to add it?
	 */
	len = strlen(str) + 1;
	if (!dyn_strpad->seen || (len > dyn_strpad->dyn.d_un.d_val))
		msg_fatal("[%d]%s: String table does not have room "
		    "to add string\n", strsec->c_shdr.sh_link,
		    strsec->c_name);


	/*
	 * We will add the string at the first byte of the reserved NULL
	 * area at the end. The DT_SUNW_STRPAD dynamic element gives us
	 * the size of that reserved space.
	 */
	ins_off = strsec->c_shdr.sh_size - tail_ign;
	s = ((char *)strsec->c_data->d_buf) + ins_off;

	/* Announce the operation */
	msg_debug("[%d]%s[%d]: Using %d/%d byes from reserved area to "
	    "add string: %s\n", strsec->c_shdr.sh_link, strsec->c_name,
	    ins_off, len, (int)dyn_strpad->dyn.d_un.d_val, str);

	/*
	 * Copy the string into the pad area at the end, and
	 * mark the data area as dirty so libelf will flush our
	 * changes to the string data.
	 */
	(void) strncpy(s, str, dyn_strpad->dyn.d_un.d_val);
	(void) elf_flagdata(strsec->c_data, ELF_C_SET, ELF_F_DIRTY);

	/* Update the DT_STRPAD dynamic entry */
	dyn_strpad->dyn.d_un.d_val -= len;
	if (gelf_update_dyn(dynsec->c_data, dyn_strpad->ndx,
	    &dyn_strpad->dyn) == 0)
		msg_elf("gelf_update_dyn");

	return (ins_off);
}
Example #3
0
static void
set_flag(char *ifile, ulong_t flval)
{
	Elf *elf;
	Elf_Scn *scn;
	Elf_Data *data;
	GElf_Shdr shdr;
	GElf_Dyn dyn;
	int fd, secidx, nent, i;

	(void) elf_version(EV_CURRENT);

	if ((fd = open(ifile, O_RDWR)) < 0)
		die("Can't open %s", ifile);

	if ((elf = elf_begin(fd, ELF_C_RDWR, NULL)) == NULL)
		elfdie("Can't start ELF for %s", ifile);

	if ((secidx = findelfsecidx(elf, ".dynamic")) == -1)
		die("Can't find .dynamic section in %s\n", ifile);

	if ((scn = elf_getscn(elf, secidx)) == NULL)
		elfdie("elf_getscn (%d)", secidx);

	if (gelf_getshdr(scn, &shdr) == NULL)
		elfdie("gelf_shdr");

	if ((data = elf_getdata(scn, NULL)) == NULL)
		elfdie("elf_getdata");

	nent = shdr.sh_size / shdr.sh_entsize;
	for (i = 0; i < nent; i++) {
		if (gelf_getdyn(data, i, &dyn) == NULL)
			elfdie("gelf_getdyn");

		if (dyn.d_tag == DT_FLAGS_1) {
			dyn.d_un.d_val |= (Elf64_Xword)flval;

			if (gelf_update_dyn(data, i, &dyn) == 0)
				elfdie("gelf_update_dyn");

			break;
		}
	}

	if (i == nent) {
		die("%s's .dynamic section doesn't have a DT_FLAGS_1 "
		    "field\n", ifile);
	}

	if (elf_update(elf, ELF_C_WRITE) == -1)
		elfdie("Couldn't update %s with changes", ifile);

	(void) elf_end(elf);
	(void) close(fd);
}
Example #4
0
static void rel_dyn_fixup_fn(ElfCreator *ctor, Elf64_Sxword d_tag, Elf_Scn *scn)
{
	GElf_Shdr *shdr, shdr_mem;
	GElf_Dyn *dyn, dyn_mem;
	size_t idx;

	dyn = get_dyn_by_tag(ctor, d_tag, &dyn_mem, &idx);
	shdr = gelf_getshdr(scn, &shdr_mem);
	if (shdr) {
		dyn->d_un.d_ptr = shdr->sh_addr;
		gelf_update_dyn(ctor->dyndata, idx, dyn);
	} else {
		remove_dyn(ctor, idx);
		dyn = get_dyn_by_tag(ctor, DT_RELSZ, &dyn_mem, &idx);
		if (dyn) {
			dyn->d_un.d_val = 0;
			gelf_update_dyn(ctor->dyndata, idx, dyn);
		}
	}
}
Example #5
0
static void remove_dyn(ElfCreator *ctor, size_t idx)
{
	size_t cnt;

	for (cnt = idx; cnt < ctor->dynshdr->sh_size/ctor->dynshdr->sh_entsize;
			cnt++) {
		GElf_Dyn *dyn, dyn_mem;

		if (cnt+1 == ctor->dynshdr->sh_size/ctor->dynshdr->sh_entsize) {
			memset(&dyn_mem, '\0', sizeof(dyn_mem));
			gelf_update_dyn(ctor->dyndata, cnt, &dyn_mem);
			break;
		}

		dyn = gelf_getdyn(ctor->dyndata, cnt+1, &dyn_mem);
		gelf_update_dyn(ctor->dyndata, cnt, dyn);
	}
	ctor->dynshdr->sh_size--;
	gelf_update_shdr(ctor->dynscn, ctor->dynshdr);
	update_dyn_cache(ctor);
}
Example #6
0
int
main(int argc, char **argv)
{
	int		c, ndx;
	int		readonly;
	int		fd;
	const char	*file;
	const char	*runpath;
	Elf		*elf;
	GElf_Ehdr	ehdr;
	size_t		shstrndx, shnum;
	Elf_Scn		*scn;
	Elf_Data	*data;
	char		*shnames = NULL;
	Cache		*cache, *_cache;
	Cache		*dynsec, *strsec;
	GElf_Word	numdyn;
	dyn_elt_t	rpath_elt;
	dyn_elt_t	runpath_elt;
	dyn_elt_t	strpad_elt;
	dyn_elt_t	flags_1_elt;
	dyn_elt_t	null_elt;
	int		changed = 0;


	opterr = 0;
	while ((c = getopt(argc, argv, "dr")) != EOF) {
		switch (c) {
		case 'd':
			d_flg = 1;
			break;

		case 'r':
			r_flg = 1;
			break;

		case '?':
			msg_usage();
		}
	}

	/*
	 * The first plain argument is the file name, and is required.
	 * The second plain argument is the runpath, and is optional.
	 * If no runpath is given, we print the current runpath to stdout
	 * and exit. If it is present, we modify the ELF file to use it.
	 */
	argc = argc - optind;
	argv += optind;
	if ((argc < 1) || (argc > 2))
		msg_usage();
	if ((argc == 2) && r_flg)
		msg_usage();

	readonly = (argc == 1) && !r_flg;
	file = argv[0];
	if (!readonly)
		runpath = argv[1];

	if ((fd = open(file, readonly ? O_RDONLY : O_RDWR)) == -1)
		msg_fatal("unable to open file: %s: %s\n",
		    file, strerror(errno));

	(void) elf_version(EV_CURRENT);
	elf = elf_begin(fd, readonly ? ELF_C_READ : ELF_C_RDWR, NULL);
	if (elf == NULL)
		msg_elf("elf_begin");

	/* We only handle standalone ELF files */
	switch (elf_kind(elf)) {
	case ELF_K_AR:
		msg_fatal("unable to edit ELF archive: %s\n", file);
		break;
	case ELF_K_ELF:
		break;
	default:
		msg_fatal("unable to edit non-ELF file: %s\n", file);
		break;
	}

	if (gelf_getehdr(elf, &ehdr) == NULL)
		msg_elf("gelf_getehdr");

	if (elf_getshnum(elf, &shnum) == 0)
		msg_elf("elf_getshnum");

	if (elf_getshstrndx(elf, &shstrndx) == 0)
		msg_elf("elf_getshstrndx");

	/*
	 * Obtain the .shstrtab data buffer to provide the required section
	 * name strings.
	 */
	if ((scn = elf_getscn(elf, shstrndx)) == NULL)
		msg_elf("elf_getscn");
	if ((data = elf_getdata(scn, NULL)) == NULL)
		msg_elf("elf_getdata");
	shnames = data->d_buf;

	/*
	 * Allocate a cache to maintain a descriptor for each section.
	 */
	if ((cache = malloc(shnum * sizeof (Cache))) == NULL)
		msg_fatal("unable to allocate section cache: %s\n",
		    strerror(errno));
	bzero(cache, sizeof (cache[0]));
	cache->c_name = "";
	_cache = cache + 1;

	/*
	 * Fill in cache with information for each section, and
	 * locate the dynamic section.
	 */
	dynsec = strsec = NULL;
	for (ndx = 1, scn = NULL; scn = elf_nextscn(elf, scn);
	    ndx++, _cache++) {
		_cache->c_ndx = ndx;
		if (gelf_getshdr(scn, &_cache->c_shdr) == NULL)
			msg_elf("gelf_getshdr");
		_cache->c_data = elf_getdata(scn, NULL);
		_cache->c_name = shnames + _cache->c_shdr.sh_name;
		if (_cache->c_shdr.sh_type == SHT_DYNAMIC) {
			dynsec = _cache;
			numdyn = dynsec->c_shdr.sh_size /
			    dynsec->c_shdr.sh_entsize;
			msg_debug("[%d]%s: dynamic section\n",
			    ndx, _cache->c_name);
		}
	}

	/*
	 * If we got a dynamic section, locate the string table.
	 * If not, we can't continue.
	 */
	if (dynsec == NULL)
		msg_fatal("file lacks a dynamic section: %s\n", file);
	strsec = &cache[dynsec->c_shdr.sh_link];
	msg_debug("[%d]%s: dynamic string table section\n",
	    strsec->c_ndx, strsec->c_name);

	/*
	 * History Lesson And Strategy:
	 *
	 * This routine handles both DT_RPATH and DT_RUNPATH entries, altering
	 * either or both if they are present.
	 *
	 * The original SYSV ABI only had DT_RPATH, and the runtime loader used
	 * it to search for things in the following order:
	 *
	 *	DT_RPATH, LD_LIBRARY_PATH, defaults
	 *
	 * Solaris did not follow this rule. Environment variables should
	 * supersede everything else, so we have always deviated from the
	 * ABI and and instead search in the order
	 *
	 *	LD_LIBRARY_PATH, DT_RPATH, defaults
	 *
	 * Other Unix variants initially followed the ABI, but in recent years
	 * realized that it was a mistake. Hence, DT_RUNPATH was invented,
	 * with the search order:
	 *
	 *	LD_LIBRARY_PATH, DT_RUNPATH, defaults
	 *
	 * So for Solaris, DT_RPATH and DT_RUNPATH mean the same thing. If both
	 * are present (which does happen), we set them both to the new
	 * value. If either one is present, we set that one. If neither is
	 * present, and we have a spare DT_NULL slot, we create a DT_RUNPATH.
	 */

	/*
	 * Examine the dynamic section to determine the index for
	 *	- DT_RPATH
	 *	- DT_RUNPATH
	 *	- DT_SUNW_STRPAD
	 *	- DT_NULL, and whether there are any extra DT_NULL slots
	 */
	dyn_elt_init(&rpath_elt);
	dyn_elt_init(&runpath_elt);
	dyn_elt_init(&strpad_elt);
	dyn_elt_init(&flags_1_elt);
	dyn_elt_init(&null_elt);
	for (ndx = 0; ndx < numdyn; ndx++) {
		GElf_Dyn	dyn;

		if (gelf_getdyn(dynsec->c_data, ndx, &dyn) == NULL)
			msg_elf("gelf_getdyn");

		switch (dyn.d_tag) {
		case DT_NULL:
			/*
			 * Remember the state of the first DT_NULL. If there
			 * are more than one (i.e. the first one is not
			 * in the final spot), and there is no runpath, then
			 * we will turn the first one into a DT_RUNPATH.
			 */
			if (!null_elt.seen) {
				dyn_elt_save(&null_elt, ndx, &dyn);
				msg_debug("[%d]%s[%d]: DT_NULL\n",
				    dynsec->c_ndx, dynsec->c_name, ndx);
			}
			break;

		case DT_RPATH:
			dyn_elt_save(&rpath_elt, ndx, &dyn);
			msg_debug("[%d]%s[%d]: DT_RPATH: %s\n",
			    dynsec->c_ndx, dynsec->c_name, ndx,
			    DYN_ELT_STRING(&rpath_elt, strsec));
			break;

		case DT_RUNPATH:
			dyn_elt_save(&runpath_elt, ndx, &dyn);
			msg_debug("[%d]%s[%d]: DT_RUNPATH: %s\n",
			    dynsec->c_ndx, dynsec->c_name, ndx,
			    DYN_ELT_STRING(&runpath_elt, strsec));
			break;

		case DT_SUNW_STRPAD:
			dyn_elt_save(&strpad_elt, ndx, &dyn);
			msg_debug("[%d]%s[%d]: DT_STRPAD: %d\n",
			    dynsec->c_ndx, dynsec->c_name, ndx,
			    (int)strpad_elt.dyn.d_un.d_val);
			break;

		case DT_FLAGS_1:
			dyn_elt_save(&flags_1_elt, ndx, &dyn);
			break;
		}
	}

	/*
	 * If this is a readonly session, then print the existing
	 * runpath and exit. DT_RPATH and DT_RUNPATH should have
	 * the same value, so we arbitrarily favor DT_RUNPATH.
	 */
	if (readonly) {
		if (runpath_elt.seen)
			(void) printf("%s\n",
			    DYN_ELT_STRING(&runpath_elt, strsec));
		else if (rpath_elt.seen)
			(void) printf("%s\n",
			    DYN_ELT_STRING(&rpath_elt, strsec));
		else
			msg_debug("ELF file does not have a runpath: %s\n",
			    file);
		return (0);
	}


	/* Edit the file, either to remove the runpath or to add/modify it */
	if (r_flg) {
		if (!(runpath_elt.seen || rpath_elt.seen))
			msg_debug("[%d]%s: no runpath found\n",
			    dynsec->c_ndx, dynsec->c_name);
		else
			changed = remove_runpath(dynsec, numdyn);
	} else {
		changed = new_runpath(runpath, dynsec, numdyn, strsec,
		    &rpath_elt, &runpath_elt, &strpad_elt, &null_elt);
	}

	if (changed) {
		/*
		 * If possible, set the DF_1_EDITED flag, indicating that
		 * this file has been edited after the fact.
		 */
		if (flags_1_elt.seen) {
			flags_1_elt.dyn.d_un.d_val |= DF_1_EDITED;
		} else if (null_elt.seen && (null_elt.ndx < (numdyn - 1))) {
			msg_debug("[%d]%s: No existing flags_1 entry to "
			    "modify. Will use extra DT_NULL in slot [%d] \n",
			    dynsec->c_ndx, dynsec->c_name, null_elt.ndx);
			flags_1_elt.seen = 1;
			flags_1_elt.ndx = null_elt.ndx;
			flags_1_elt.dyn.d_tag = DT_FLAGS_1;
			flags_1_elt.dyn.d_un.d_val = DF_1_EDITED;
		}
		if (flags_1_elt.seen) {
			msg_debug("[%d]%s[%d]: Set DF_1_EDITED flag\n",
			    dynsec->c_ndx, dynsec->c_name, flags_1_elt.ndx);
			if (gelf_update_dyn(dynsec->c_data, flags_1_elt.ndx,
			    &flags_1_elt.dyn) == 0)
				msg_elf("gelf_update_dyn");
		}

		/*
		 * Mark the data area as dirty so libelf will flush our
		 * changes to the dynamic section data.
		 */
		(void) elf_flagdata(dynsec->c_data, ELF_C_SET, ELF_F_DIRTY);

		/* Flush the file to disk */
		if (elf_update(elf, ELF_C_WRITE) == -1)
			msg_elf("elf_update");
		(void) close(fd);
		(void) elf_end(elf);
	}
	return (0);
}
Example #7
0
/*
 * Add/modify the runpath for the object.
 *
 * entry:
 *	dynsec - Dynamic section
 *	numdyn - # of elements in dynamic array
 *
 * exit:
 *	Returns True (1) if the dynamic section was modified, and
 *	False (0) otherwise.
 */
static int
new_runpath(const char *runpath, Cache *dynsec, GElf_Word numdyn,
    Cache *strsec, dyn_elt_t *rpath_elt, dyn_elt_t *runpath_elt,
    dyn_elt_t *strpad_elt, dyn_elt_t *null_elt)
{
	/*  Do we have an available dynamic section entry to use? */
	if (rpath_elt->seen || runpath_elt->seen) {
		/*
		 * We have seen a DT_RPATH, or a DT_RUNPATH, or both.
		 * If all of these have the same string as the desired
		 * new value, then we don't need to alter anything and can
		 * simply return. Otherwise, we'll modify them all to have
		 * the new string (below).
		 */
		if ((!rpath_elt->seen ||
		    (strcmp(DYN_ELT_STRING(rpath_elt, strsec),
		    runpath) == 0)) &&
		    (!runpath_elt->seen ||
		    (strcmp(DYN_ELT_STRING(runpath_elt, strsec),
		    runpath) == 0))) {
			if (rpath_elt->seen)
				msg_debug("[%d]%s[%d]: Existing DT_RPATH "
				    "already has desired value\n",
				    dynsec->c_ndx,  dynsec->c_name,
				    rpath_elt->ndx);
			if (runpath_elt->seen)
				msg_debug("[%d]%s[%d]: Existing DT_RUNPATH "
				    "already has desired value\n",
				    dynsec->c_ndx,  dynsec->c_name,
				    runpath_elt->ndx);
			return (0);
		}
	} else if (!null_elt->seen || (null_elt->ndx == (numdyn - 1))) {
		/*
		 * There is no DT_RPATH or DT_RUNPATH in the dynamic array,
		 * and there are no extra DT_NULL entries that we can
		 * convert into one. We cannot proceed.
		 */
		msg_debug("[%d]%s: Dynamic section does not have room "
		    "to add a runpath\n", dynsec->c_ndx,  dynsec->c_name);
		msg_fatal("unable to add runpath to file: %s\n", file);
	}


	/* Does the string exist in the table already, or can we add it? */
	rpath_elt->dyn.d_un.d_val = runpath_elt->dyn.d_un.d_val =
	    dynstr_insert(dynsec, strsec, strpad_elt, runpath);

	/* Update DT_RPATH entry if present */
	if (rpath_elt->seen) {
		msg_debug("[%d]%s[%d]: Reusing existing DT_RPATH entry: %s\n",
		    dynsec->c_ndx, dynsec->c_name, rpath_elt->ndx,
		    DYN_ELT_STRING(rpath_elt, strsec));
		if (gelf_update_dyn(dynsec->c_data, rpath_elt->ndx,
		    &rpath_elt->dyn) == 0)
			msg_elf("gelf_update_dyn");
	}

	/*
	 * Update the DT_RUNPATH entry in the dynamic section, if present.
	 * If one is not present, and there is also no DT_RPATH, then
	 * we use a spare DT_NULL entry to create a new DT_RUNPATH.
	 */
	if (runpath_elt->seen || !rpath_elt->seen) {
		if (runpath_elt->seen) {
			msg_debug("[%d]%s[%d]: Reusing existing DT_RUNPATH "
			    "entry: %s\n", dynsec->c_ndx, dynsec->c_name,
			    runpath_elt->ndx,
			    DYN_ELT_STRING(runpath_elt, strsec));
		} else {	/* Using a spare DT_NULL entry */
			msg_debug("[%d]%s: No existing runpath to modify. "
			    "Will use extra DT_NULL in slot [%d] \n",
			    dynsec->c_ndx, dynsec->c_name, null_elt->ndx);
			runpath_elt->seen = 1;
			runpath_elt->ndx = null_elt->ndx;
			runpath_elt->dyn.d_tag = DT_RUNPATH;
			null_elt->ndx++;
		}
		if (gelf_update_dyn(dynsec->c_data, runpath_elt->ndx,
		    &runpath_elt->dyn) == 0)
			msg_elf("gelf_update_dyn");
	}

	return (1);
}