xref: /illumos-gate/usr/src/cmd/sgs/libld/common/support.c (revision a724c049b7e0dd8612bc3aaec84e96e80511050d)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include	<stdio.h>
28 #include	<dlfcn.h>
29 #include	<libelf.h>
30 #include	<link.h>
31 #include	<debug.h>
32 #include	"msg.h"
33 #include	"_libld.h"
34 
35 /*
36  * Table which defines the default functions to be called by the library
37  * SUPPORT (-S <libname>).  These functions can be redefined by the
38  * ld_support_loadso() routine.
39  */
40 static Support_list support[LDS_NUM] = {
41 	{MSG_ORIG(MSG_SUP_VERSION),	{ 0, 0 }},	/* LDS_VERSION */
42 	{MSG_ORIG(MSG_SUP_INPUT_DONE),	{ 0, 0 }},	/* LDS_INPUT_DONE */
43 #if	defined(_ELF64)
44 	{MSG_ORIG(MSG_SUP_START_64),	{ 0, 0 }},	/* LDS_START */
45 	{MSG_ORIG(MSG_SUP_ATEXIT_64),	{ 0, 0 }},	/* LDS_ATEXIT */
46 	{MSG_ORIG(MSG_SUP_OPEN_64),	{ 0, 0 }},	/* LDS_OPEN */
47 	{MSG_ORIG(MSG_SUP_FILE_64),	{ 0, 0 }},	/* LDS_FILE */
48 	{MSG_ORIG(MSG_SUP_INSEC_64),	{ 0, 0 }},	/* LDS_INSEC */
49 	{MSG_ORIG(MSG_SUP_SEC_64),	{ 0, 0 }}	/* LDS_SEC */
50 #else	/* Elf32 */
51 	{MSG_ORIG(MSG_SUP_START),	{ 0, 0 }},	/* LDS_START */
52 	{MSG_ORIG(MSG_SUP_ATEXIT),	{ 0, 0 }},	/* LDS_ATEXIT */
53 	{MSG_ORIG(MSG_SUP_OPEN),	{ 0, 0 }},	/* LDS_OPEN */
54 	{MSG_ORIG(MSG_SUP_FILE),	{ 0, 0 }},	/* LDS_FILE */
55 	{MSG_ORIG(MSG_SUP_INSEC),	{ 0, 0 }},	/* LDS_INSEC */
56 	{MSG_ORIG(MSG_SUP_SEC),		{ 0, 0 }}	/* LDS_SEC */
57 #endif
58 };
59 
60 /*
61  * Loads in a support shared object specified using the SGS_SUPPORT environment
62  * variable or the -S ld option, and determines which interface functions are
63  * provided by that object.
64  */
65 uintptr_t
66 ld_sup_loadso(Ofl_desc *ofl, const char *obj)
67 {
68 	void		*handle, (*fptr)();
69 	Func_list	*flp;
70 	uint_t		interface, version = LD_SUP_VERSION1;
71 
72 	/*
73 	 * Load the required support library.  If we are unable to load it fail
74 	 * with a fatal error.
75 	 */
76 	if ((handle = dlopen(obj, (RTLD_LAZY | RTLD_FIRST))) == NULL) {
77 		eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_SUP_NOLOAD),
78 		    obj, dlerror());
79 		return (S_ERROR);
80 	}
81 
82 	for (interface = 0; interface < LDS_NUM; interface++) {
83 		if ((fptr = (void (*)())dlsym(handle,
84 		    support[interface].sup_name)) == NULL)
85 			continue;
86 
87 		DBG_CALL(Dbg_support_load(ofl->ofl_lml, obj,
88 		    support[interface].sup_name));
89 
90 		if (interface == LDS_VERSION) {
91 			DBG_CALL(Dbg_support_action(ofl->ofl_lml, obj,
92 			    support[LDS_VERSION].sup_name, LDS_VERSION, 0));
93 
94 			version = ((uint_t(*)())fptr)(LD_SUP_VCURRENT);
95 
96 			/*
97 			 * If version is LD_SUP_VNONE, unload the support
98 			 * library and act as if it was never requested.
99 			 * This provides the support library with a mechanism
100 			 * for opting out of the process.
101 			 *
102 			 * Note that this depends on LDS_VERSION being the
103 			 * first item in support[]. If this were not true,
104 			 * then other functions from the library might be
105 			 * entered into the function lists, and unloading
106 			 * the object would cause corruption.
107 			 */
108 			assert(LDS_VERSION == 0);
109 			if (version == LD_SUP_VNONE) {
110 				DBG_CALL(Dbg_support_vnone(ofl->ofl_lml,
111 				    obj));
112 				(void) dlclose(handle);
113 				return (1);
114 			}
115 
116 			/*
117 			 * If the support library has a version higher
118 			 * than we support, we are unable to accept it.
119 			 */
120 			if (version > LD_SUP_VCURRENT) {
121 				eprintf(ofl->ofl_lml, ERR_FATAL,
122 				    MSG_INTL(MSG_SUP_BADVERSION), obj,
123 				    LD_SUP_VCURRENT, version);
124 				(void) dlclose(handle);
125 				return (S_ERROR);
126 			}
127 		}
128 
129 
130 		if ((flp = libld_malloc(sizeof (Func_list))) == NULL) {
131 			(void) dlclose(handle);
132 			return (S_ERROR);
133 		}
134 		flp->fl_obj = obj;
135 		flp->fl_fptr = fptr;
136 		flp->fl_version = version;
137 		if (list_appendc(&support[interface].sup_funcs, flp) == 0)
138 			return (S_ERROR);
139 	}
140 	return (1);
141 }
142 
143 /*
144  * Wrapper routines for the ld support library calls.
145  */
146 void
147 ld_sup_start(Ofl_desc *ofl, const Half etype, const char *caller)
148 {
149 	Func_list	*flp;
150 	Listnode	*lnp;
151 
152 	for (LIST_TRAVERSE(&support[LDS_START].sup_funcs, lnp, flp)) {
153 		DBG_CALL(Dbg_support_action(ofl->ofl_lml, flp->fl_obj,
154 		    support[LDS_START].sup_name, LDS_START, ofl->ofl_name));
155 		(*flp->fl_fptr)(ofl->ofl_name, etype, caller);
156 	}
157 }
158 
159 void
160 ld_sup_atexit(Ofl_desc *ofl, int ecode)
161 {
162 	Func_list	*flp;
163 	Listnode	*lnp;
164 
165 	for (LIST_TRAVERSE(&support[LDS_ATEXIT].sup_funcs, lnp, flp)) {
166 		DBG_CALL(Dbg_support_action(ofl->ofl_lml, flp->fl_obj,
167 		    support[LDS_ATEXIT].sup_name, LDS_ATEXIT, 0));
168 		(*flp->fl_fptr)(ecode);
169 	}
170 }
171 
172 void
173 ld_sup_open(Ofl_desc *ofl, const char **opath, const char **ofile, int *ofd,
174     int flags, Elf **oelf, Elf *ref, size_t off, const Elf_Kind ekind)
175 {
176 	Func_list	*flp;
177 	Listnode	*lnp;
178 	const char	*npath = *opath;
179 	const char	*nfile = *ofile;
180 	Elf		*nelf = *oelf;
181 	int		nfd = *ofd;
182 
183 	for (LIST_TRAVERSE(&support[LDS_OPEN].sup_funcs, lnp, flp)) {
184 		int	_flags = 0;
185 
186 		/*
187 		 * This interface was introduced in VERSION3.  Only call this
188 		 * function for libraries reporting support for version 3 or
189 		 * above.
190 		 */
191 		if (flp->fl_version < LD_SUP_VERSION3)
192 			continue;
193 
194 		if (!(flags & FLG_IF_CMDLINE))
195 			_flags |= LD_SUP_DERIVED;
196 		if (!(flags & FLG_IF_NEEDED))
197 			_flags |= LD_SUP_INHERITED;
198 		if (flags & FLG_IF_EXTRACT)
199 			_flags |= LD_SUP_EXTRACTED;
200 
201 		/*
202 		 * If the present object is an extracted archive member, make
203 		 * sure the archive offset is reset so that the caller can
204 		 * obtain an ELF descriptor to the same member (an elf_begin()
205 		 * moves the offset to the next member).
206 		 */
207 		if (flags & FLG_IF_EXTRACT)
208 			(void) elf_rand(ref, off);
209 
210 		DBG_CALL(Dbg_support_action(ofl->ofl_lml, flp->fl_obj,
211 		    support[LDS_OPEN].sup_name, LDS_OPEN, *opath));
212 		(*flp->fl_fptr)(&npath, &nfile, &nfd, _flags, &nelf, ref, off,
213 		    ekind);
214 	}
215 
216 	/*
217 	 * If the file descriptor, ELF descriptor, or file names have been
218 	 * modified, then diagnose the differences and return the new data.
219 	 * As a basic test, make sure the support library hasn't nulled out
220 	 * data ld(1) will try and dereference.
221 	 */
222 	if ((npath != *opath) || (nfd != *ofd) || (nelf != *oelf)) {
223 		Dbg_file_modified(ofl->ofl_lml, flp->fl_obj, *opath, npath,
224 		    *ofd, nfd, *oelf, nelf);
225 		if (npath)
226 			*opath = npath;
227 		if (nfile)
228 			*ofile = nfile;
229 		*ofd = nfd;
230 		*oelf = nelf;
231 	}
232 }
233 
234 void
235 ld_sup_file(Ofl_desc *ofl, const char *ifile, const Elf_Kind ekind, int flags,
236     Elf *elf)
237 {
238 	Func_list	*flp;
239 	Listnode	*lnp;
240 
241 	for (LIST_TRAVERSE(&support[LDS_FILE].sup_funcs, lnp, flp)) {
242 		int	_flags = 0;
243 
244 		if (!(flags & FLG_IF_CMDLINE))
245 			_flags |= LD_SUP_DERIVED;
246 		if (!(flags & FLG_IF_NEEDED))
247 			_flags |= LD_SUP_INHERITED;
248 		if (flags & FLG_IF_EXTRACT)
249 			_flags |= LD_SUP_EXTRACTED;
250 
251 		DBG_CALL(Dbg_support_action(ofl->ofl_lml, flp->fl_obj,
252 		    support[LDS_FILE].sup_name, LDS_FILE, ifile));
253 		(*flp->fl_fptr)(ifile, ekind, _flags, elf);
254 	}
255 }
256 
257 uintptr_t
258 ld_sup_input_section(Ofl_desc *ofl, Ifl_desc *ifl, const char *sname,
259     Shdr **oshdr, Word ndx, Elf_Scn *scn, Elf *elf)
260 {
261 	Func_list	*flp;
262 	Listnode	*lnp;
263 	uint_t		flags = 0;
264 	Elf_Data	*data = NULL;
265 	Shdr		*nshdr = *oshdr;
266 
267 	for (LIST_TRAVERSE(&support[LDS_INSEC].sup_funcs, lnp, flp)) {
268 		/*
269 		 * This interface was introduced in VERSION2.  Only call this
270 		 * function for libraries reporting support for version 2 or
271 		 * above.
272 		 */
273 		if (flp->fl_version < LD_SUP_VERSION2)
274 			continue;
275 
276 		if ((data == NULL) &&
277 		    ((data = elf_getdata(scn, NULL)) == NULL)) {
278 			eprintf(ofl->ofl_lml, ERR_ELF,
279 			    MSG_INTL(MSG_ELF_GETDATA), ifl->ifl_name);
280 			ofl->ofl_flags |= FLG_OF_FATAL;
281 			return (S_ERROR);
282 		}
283 
284 		DBG_CALL(Dbg_support_action(ofl->ofl_lml, flp->fl_obj,
285 		    support[LDS_INSEC].sup_name, LDS_INSEC, sname));
286 		(*flp->fl_fptr)(sname, &nshdr, ndx, data, elf, &flags);
287 	}
288 
289 	/*
290 	 * If the section header has been re-allocated (known to occur with
291 	 * libCCexcept.so), then diagnose the section header difference and
292 	 * return the new section header.
293 	 */
294 	if (nshdr != *oshdr) {
295 		Dbg_shdr_modified(ofl->ofl_lml, flp->fl_obj,
296 		    ifl->ifl_ehdr->e_machine, *oshdr, nshdr, sname);
297 		*oshdr = nshdr;
298 	}
299 	return (0);
300 }
301 
302 void
303 ld_sup_section(Ofl_desc *ofl, const char *scn, Shdr *shdr, Word ndx,
304     Elf_Data *data, Elf *elf)
305 {
306 	Func_list	*flp;
307 	Listnode	*lnp;
308 
309 	for (LIST_TRAVERSE(&support[LDS_SEC].sup_funcs, lnp, flp)) {
310 		DBG_CALL(Dbg_support_action(ofl->ofl_lml, flp->fl_obj,
311 		    support[LDS_SEC].sup_name, LDS_SEC, scn));
312 		(*flp->fl_fptr)(scn, shdr, ndx, data, elf);
313 	}
314 }
315 
316 void
317 ld_sup_input_done(Ofl_desc *ofl)
318 {
319 	Func_list	*flp;
320 	Listnode	*lnp;
321 	uint_t		flags = 0;
322 
323 	for (LIST_TRAVERSE(&support[LDS_INPUT_DONE].sup_funcs, lnp, flp)) {
324 		/*
325 		 * This interface was introduced in VERSION2.  Only call this
326 		 * function for libraries reporting support for version 2 or
327 		 * above.
328 		 */
329 		if (flp->fl_version < LD_SUP_VERSION2)
330 			continue;
331 
332 		DBG_CALL(Dbg_support_action(ofl->ofl_lml, flp->fl_obj,
333 		    support[LDS_INPUT_DONE].sup_name, LDS_INPUT_DONE, 0));
334 		(*flp->fl_fptr)(&flags);
335 	}
336 }
337