xref: /illumos-gate/usr/src/cmd/sgs/elfedit/common/elfedit_machelf.c (revision ed5289f91b9bf164dccd6c75398362be77a4478d)
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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * ELFCLASS specific code for elfedit, built once for each class
30  */
31 #include	<stdlib.h>
32 #include	<stdio.h>
33 #include	<unistd.h>
34 #include	<_machelf.h>
35 #include	<libelf.h>
36 #include	<strings.h>
37 #include	<sgs.h>
38 #include	"msg.h"
39 #include	"_elfedit.h"
40 
41 
42 
43 /*
44  * Look up the elfedit_symtab_t that corresponds to the symbol table
45  * referenced by the sh_link field of the given auxiliary section.
46  *
47  * entry:
48  *	obj_state - Partially constructed object state from
49  *		elfedit_init_obj_state().
50  *	auxsec - Section that is associated with the symbol table section
51  *
52  * exit:
53  *	Returns the pointer to the elfedit_symtab_t entry that is
54  *	referenced by the auxiliary section. If not found,
55  *	outputs a debug message, and returns NULL.
56  */
57 static elfedit_symtab_t *
58 get_symtab(elfedit_obj_state_t *obj_state, elfedit_section_t *auxsec)
59 {
60 	elfedit_symtab_t *symtab = obj_state->os_symtab;
61 	Word	sh_link = auxsec->sec_shdr->sh_link;
62 	Word	i;
63 
64 	for (i = 0; i < obj_state->os_symtabnum; i++, symtab++)
65 		if (symtab->symt_shndx == sh_link)
66 			return (symtab);
67 
68 	/*
69 	 * If we don't return above, it doesn't reference a valid
70 	 * symbol table. Issue warning.
71 	 */
72 	elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_AUX_LINK),
73 	    EC_WORD(auxsec->sec_shndx), auxsec->sec_name,
74 	    EC_WORD(sh_link));
75 
76 	return (NULL);
77 }
78 
79 
80 /*
81  * Fill in state.elf.obj_state with a a dynamically allocated
82  * elfedit_obj_state_t struct of the appropriate ELFCLASS.
83  * This pre-chewed form is fed to each command, reducing the amount
84  * of ELF boilerplate code each command needs to contain.
85  *
86  * entry:
87  *	file - Name of file to process
88  *	fd - Descriptor of open file which has been successfully
89  *		processed by elf_begin().
90  *	elf - Elf handle returned by elf_begin
91  *
92  * exit:
93  *	An elfedit_obj_state_t struct of the appropriate ELFCLASS has
94  *	been dynamically allocated, and state.elf.obj_state references it.
95  *	On failure, this routine does not return to the caller.
96  *
97  * note: The resulting elfedit_obj_state_t is allocated from a single
98  *	piece of memory, such that a single call to free() suffices
99  *	to release it as well as any memory it references.
100  */
101 #ifdef _ELF64
102 void
103 elfedit64_init_obj_state(const char *file, int fd, Elf *elf)
104 #else
105 void
106 elfedit32_init_obj_state(const char *file, int fd, Elf *elf)
107 #endif
108 {
109 #define	INITIAL_SYMTABNDX_ALLOC	5
110 
111 	/*
112 	 * This macro is used to call functions from libelf, all of which
113 	 * return NULL for failure and something else for success. On error,
114 	 * libelf_fail_name is set and execution jumps to the libelf_failure
115 	 * label for handling. Otherwise, the results of the call are ready
116 	 * for use by the caller.
117 	 */
118 #define	LIBELF(_libelf_expr, _name) \
119 	if ((_libelf_expr) == NULL) { \
120 		libelf_fail_name = _name; \
121 		goto libelf_failure; \
122 	}
123 
124 	const char *libelf_fail_name;	/* Used for LIBELF errors */
125 
126 	Elf_Scn			*scn;
127 	Elf_Data		*data;
128 	uint_t			ndx;
129 	size_t			len, os_size, secarr_size;
130 	char			*names = 0;
131 	size_t			names_len;
132 	elfedit_section_t	*_cache;
133 	elfedit_obj_state_t	tstate;
134 	elfedit_obj_state_t	*obj_state = NULL;
135 	Word			*symtabndx = NULL;
136 	Word			symtabndx_size = 0;
137 	elfedit_symtab_t	*symtab;
138 
139 	tstate.os_file = file;
140 	tstate.os_fd = fd;
141 	tstate.os_elf = elf;
142 	tstate.os_dynndx = SHN_UNDEF;
143 	tstate.os_symtabnum = 0;
144 
145 	LIBELF(tstate.os_ehdr = elf_getehdr(tstate.os_elf),
146 	    MSG_ORIG(MSG_ELF_GETEHDR))
147 
148 	/* Program header array count and address */
149 	LIBELF(elf_getphnum(tstate.os_elf, &tstate.os_phnum),
150 	    MSG_ORIG(MSG_ELF_GETPHNUM))
151 	if (tstate.os_phnum > 0) {
152 		LIBELF((tstate.os_phdr = elf_getphdr(tstate.os_elf)),
153 		    MSG_ORIG(MSG_ELF_GETPHDR))
154 	} else {
155 		tstate.os_phdr = NULL;
156 	}
157 
158 
159 	LIBELF(elf_getshnum(tstate.os_elf, &tstate.os_shnum),
160 	    MSG_ORIG(MSG_ELF_GETSHNUM))
161 
162 
163 	/*
164 	 * Obtain the .shstrtab data buffer to provide the required section
165 	 * name strings.
166 	 */
167 	LIBELF(elf_getshstrndx(tstate.os_elf, &tstate.os_shstrndx),
168 	    MSG_ORIG(MSG_ELF_GETSHSTRNDX))
169 	LIBELF((scn = elf_getscn(tstate.os_elf, tstate.os_shstrndx)),
170 	    MSG_ORIG(MSG_ELF_GETSCN))
171 	LIBELF((data = elf_getdata(scn, NULL)), MSG_ORIG(MSG_ELF_GETDATA))
172 	names = data->d_buf;
173 	names_len = (names == NULL) ? 0 : data->d_size;
174 
175 	/*
176 	 * Count the number of symbol tables and capture their indexes.
177 	 * Find the dynamic section.
178 	 */
179 	for (ndx = 1, scn = NULL; scn = elf_nextscn(tstate.os_elf, scn);
180 	    ndx++) {
181 		Shdr *shdr;
182 
183 		LIBELF(shdr = elf_getshdr(scn), MSG_ORIG(MSG_ELF_GETSHDR));
184 
185 		switch (shdr->sh_type) {
186 		case SHT_DYNAMIC:
187 			/* Save index of dynamic section for use below */
188 			tstate.os_dynndx = ndx;
189 			break;
190 
191 		case SHT_SYMTAB:
192 		case SHT_DYNSYM:
193 		case SHT_SUNW_LDYNSYM:
194 			if (symtabndx_size <= tstate.os_symtabnum) {
195 				symtabndx_size = (symtabndx_size == 0) ?
196 				    INITIAL_SYMTABNDX_ALLOC :
197 				    (symtabndx_size * 2);
198 				symtabndx = elfedit_realloc(
199 				    MSG_INTL(MSG_ALLOC_SYMTABOS), symtabndx,
200 				    symtabndx_size * sizeof (symtabndx[0]));
201 			}
202 			symtabndx[tstate.os_symtabnum++] = ndx;
203 			break;
204 		}
205 	}
206 
207 	/*
208 	 * Allocate space to hold the state. We allocate space for everything
209 	 * in one chunk to make releasing it easy:
210 	 *	(1) elfedit_obj_state_t struct
211 	 *	(2) The array of elfedit_section_t items referenced from
212 	 *		the elfedit_obj_state_t struct.
213 	 *	(3) The array of elfedit_symtab_t items referenced from
214 	 *		the elfedit_obj_state_t struct.
215 	 *	(4) The file name.
216 	 *
217 	 * Note that we round up the size of (1) and (2) to a double boundary
218 	 * to ensure proper alignment of (2) and (3). (4) can align on any
219 	 * boundary.
220 	 */
221 	os_size = S_DROUND(sizeof (tstate));
222 	secarr_size = (tstate.os_shnum * sizeof (elfedit_section_t));
223 	secarr_size = S_DROUND(secarr_size);
224 	len = strlen(tstate.os_file) + 1;
225 	obj_state = elfedit_malloc(MSG_INTL(MSG_ALLOC_OBJSTATE),
226 	    os_size + secarr_size +
227 	    (tstate.os_symtabnum * sizeof (elfedit_symtab_t)) + len);
228 	*obj_state = tstate;
229 
230 	/*LINTED E_BAD_PTR_CAST_ALIGN*/
231 	obj_state->os_secarr = (elfedit_section_t *)
232 	    ((char *)obj_state + os_size);
233 	if (obj_state->os_symtabnum == 0)
234 		obj_state->os_symtab = NULL;
235 	else
236 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
237 		obj_state->os_symtab = (elfedit_symtab_t *)
238 		    ((char *)obj_state->os_secarr + secarr_size);
239 	obj_state->os_file =
240 	    (char *)(obj_state->os_symtab + tstate.os_symtabnum);
241 	(void) strncpy((char *)obj_state->os_file, tstate.os_file, len);
242 
243 	/*
244 	 * Fill in obj_state->os_secarr with information for each section.
245 	 * At the same time, fill in obj_state->os_symtab with the symbol
246 	 * table related data.
247 	 */
248 	bzero(obj_state->os_secarr, sizeof (obj_state->os_secarr[0]));
249 	_cache = obj_state->os_secarr;
250 	LIBELF(scn = elf_getscn(tstate.os_elf, 0),
251 	    MSG_ORIG(MSG_ELF_GETSCN));
252 	_cache->sec_scn = scn;
253 	LIBELF(_cache->sec_shdr = elf_getshdr(scn), MSG_ORIG(MSG_ELF_GETSHDR));
254 	_cache->sec_name = (_cache->sec_shdr->sh_name < names_len) ?
255 	    (names + _cache->sec_shdr->sh_name) : MSG_INTL(MSG_UNKNOWNSECNAM);
256 	_cache++;
257 
258 	if (obj_state->os_symtab != NULL) {
259 		bzero(obj_state->os_symtab,
260 		    sizeof (obj_state->os_symtab[0]) * obj_state->os_symtabnum);
261 		for (ndx = 0; ndx < obj_state->os_symtabnum; ndx++)
262 			obj_state->os_symtab[ndx].symt_shndx = symtabndx[ndx];
263 		free(symtabndx);
264 	}
265 
266 	for (ndx = 1, scn = NULL; scn = elf_nextscn(tstate.os_elf, scn);
267 	    ndx++, _cache++) {
268 		_cache->sec_shndx = ndx;
269 		_cache->sec_scn = scn;
270 		LIBELF(_cache->sec_shdr = elf_getshdr(scn),
271 		    MSG_ORIG(MSG_ELF_GETSHDR))
272 		_cache->sec_data = elf_getdata(scn, NULL);
273 		_cache->sec_name = (_cache->sec_shdr->sh_name < names_len) ?
274 		    (names + _cache->sec_shdr->sh_name) :
275 		    MSG_INTL(MSG_UNKNOWNSECNAM);
276 
277 		switch (_cache->sec_shdr->sh_type) {
278 		case SHT_SYMTAB_SHNDX:
279 			symtab = get_symtab(obj_state, _cache);
280 			symtab->symt_xshndx = ndx;
281 			break;
282 
283 		case SHT_SUNW_syminfo:
284 			symtab = get_symtab(obj_state, _cache);
285 			symtab->symt_syminfo = ndx;
286 			break;
287 
288 		case SHT_SUNW_versym:
289 			symtab = get_symtab(obj_state, _cache);
290 			symtab->symt_versym = ndx;
291 			break;
292 		}
293 	}
294 
295 	/*
296 	 * Sanity check the symbol tables, and discard any auxiliary
297 	 * sections without enough elements.
298 	 */
299 	symtab = obj_state->os_symtab;
300 	for (ndx = 0; ndx < obj_state->os_symtabnum; ndx++, symtab++) {
301 		elfedit_section_t	*symsec;
302 		Word			symsec_cnt, aux_cnt;
303 
304 		symsec = &obj_state->os_secarr[symtab->symt_shndx];
305 		symsec_cnt = symsec->sec_shdr->sh_size / sizeof (Sym);
306 
307 		/* Extended section indexes */
308 		if (symtab->symt_xshndx != SHN_UNDEF) {
309 			_cache = &obj_state->os_secarr[symtab->symt_xshndx];
310 			aux_cnt = _cache->sec_shdr->sh_size / sizeof (Word);
311 			if (symsec_cnt > aux_cnt)
312 				elfedit_msg(ELFEDIT_MSG_DEBUG,
313 				    MSG_INTL(MSG_DEBUG_AUX_SIZE),
314 				    EC_WORD(ndx), _cache->sec_name,
315 				    EC_WORD(aux_cnt),
316 				    EC_WORD(symsec->sec_shndx),
317 				    symsec->sec_name, EC_WORD(aux_cnt));
318 		}
319 
320 		/* Syminfo */
321 		if (symtab->symt_syminfo != SHN_UNDEF) {
322 			_cache = &obj_state->os_secarr[symtab->symt_syminfo];
323 			aux_cnt = _cache->sec_shdr->sh_size / sizeof (Syminfo);
324 			if (symsec_cnt > aux_cnt)
325 				elfedit_msg(ELFEDIT_MSG_DEBUG,
326 				    MSG_INTL(MSG_DEBUG_AUX_SIZE),
327 				    EC_WORD(ndx), _cache->sec_name,
328 				    EC_WORD(aux_cnt),
329 				    EC_WORD(symsec->sec_shndx),
330 				    symsec->sec_name, EC_WORD(aux_cnt));
331 		}
332 
333 		/* Versym */
334 		if (symtab->symt_versym != SHN_UNDEF) {
335 			_cache = &obj_state->os_secarr[symtab->symt_versym];
336 			aux_cnt = _cache->sec_shdr->sh_size / sizeof (Versym);
337 			if (symsec_cnt > aux_cnt)
338 				elfedit_msg(ELFEDIT_MSG_DEBUG,
339 				    MSG_INTL(MSG_DEBUG_AUX_SIZE),
340 				    EC_WORD(ndx), _cache->sec_name,
341 				    EC_WORD(aux_cnt),
342 				    EC_WORD(symsec->sec_shndx),
343 				    symsec->sec_name, EC_WORD(aux_cnt));
344 		}
345 	}
346 
347 	/*
348 	 * If this object has a dynsym section with a FLAGS_1 field,
349 	 * then set the DF_1_EDITED bit. elfedit allows changes that
350 	 * can break the resulting program, so knowing that a file was
351 	 * edited can be helpful when encountering a core file or other
352 	 * unexpected failure in the field. A single bit can't tell you
353 	 * what was changed, but it will alert you to the possibility that
354 	 * some additional questions might be in order.
355 	 */
356 	if (obj_state->os_dynndx != SHN_UNDEF) {
357 		Word			i;
358 		Word			numdyn;
359 		elfedit_section_t	*dynsec;
360 		elfedit_dyn_elt_t	flags_1_elt;
361 		elfedit_dyn_elt_t	null_elt;
362 		Dyn			*dyn;
363 
364 		dynsec = &obj_state->os_secarr[obj_state->os_dynndx];
365 		dyn = (Dyn *) dynsec->sec_data->d_buf;
366 		numdyn = dynsec->sec_shdr->sh_size /
367 		    dynsec->sec_shdr->sh_entsize;
368 		elfedit_dyn_elt_init(&flags_1_elt);
369 		elfedit_dyn_elt_init(&null_elt);
370 		for (i = 0; i < numdyn; i++) {
371 
372 			switch (dyn[i].d_tag) {
373 			case DT_NULL:
374 				/*
375 				 * Remember state of the first DT_NULL. If there
376 				 * are more than one (i.e. the first one is not
377 				 * in the final spot), and there is no flags1,
378 				 * then we will turn the first one into a
379 				 * DT_FLAGS_1.
380 				 */
381 				if (!null_elt.dn_seen)
382 					elfedit_dyn_elt_save(&null_elt, i,
383 					    &dyn[i]);
384 				break;
385 
386 			case DT_FLAGS_1:
387 				elfedit_dyn_elt_save(&flags_1_elt, i, &dyn[i]);
388 				break;
389 			}
390 		}
391 		/* If don't have a flags1 field, can we make one from a NULL? */
392 		if (!flags_1_elt.dn_seen && null_elt.dn_seen &&
393 		    (null_elt.dn_ndx < (numdyn - 1))) {
394 			elfedit_msg(ELFEDIT_MSG_DEBUG,
395 			    MSG_INTL(MSG_DEBUG_NULL2DYNFL1),
396 			    EC_WORD(obj_state->os_dynndx),
397 			    dynsec->sec_name, EC_WORD(null_elt.dn_ndx));
398 			flags_1_elt.dn_seen = 1;
399 			flags_1_elt.dn_ndx = null_elt.dn_ndx;
400 			flags_1_elt.dn_dyn.d_tag = DT_FLAGS_1;
401 			flags_1_elt.dn_dyn.d_un.d_val = 0;
402 		}
403 		/*
404 		 * If there is a flags 1 field, add the edit flag if
405 		 * it is not present, and report it's presence otherwise.
406 		 */
407 		if (flags_1_elt.dn_seen) {
408 			if (flags_1_elt.dn_dyn.d_un.d_val & DF_1_EDITED) {
409 				elfedit_msg(ELFEDIT_MSG_DEBUG,
410 				    MSG_INTL(MSG_DEBUG_SEEDYNFLG),
411 				    EC_WORD(obj_state->os_dynndx),
412 				    dynsec->sec_name,
413 				    EC_WORD(flags_1_elt.dn_ndx));
414 			} else {
415 				elfedit_msg(ELFEDIT_MSG_DEBUG,
416 				    MSG_INTL(MSG_DEBUG_ADDDYNFLG),
417 				    EC_WORD(obj_state->os_dynndx),
418 				    dynsec->sec_name,
419 				    EC_WORD(flags_1_elt.dn_ndx));
420 				flags_1_elt.dn_dyn.d_un.d_val |= DF_1_EDITED;
421 				dyn[flags_1_elt.dn_ndx] = flags_1_elt.dn_dyn;
422 				elfedit_modified_data(dynsec);
423 			}
424 		}
425 	}
426 
427 #ifdef _ELF64
428 	state.elf.obj_state.s64 = obj_state;
429 #else
430 	state.elf.obj_state.s32 = obj_state;
431 #endif
432 	return;
433 
434 libelf_failure:
435 	/*
436 	 * Control comes here if there is an error with LIBELF.
437 	 *
438 	 * entry:
439 	 *	libelf_fail_name - Name of failing libelf function
440 	 *	tstate.os_file - Name of ELF file being processed
441 	 *	tstate.os_fd - Descriptor of open ELF file
442 	 *
443 	 * exit:
444 	 *	- dynamic memory is released if necessary
445 	 *	- The error issued
446 	 */
447 	if (obj_state != NULL)
448 		free(obj_state);
449 	(void) close(tstate.os_fd);
450 	elfedit_elferr(tstate.os_file, libelf_fail_name);
451 #undef LIBELF_FAILURE
452 #undef INITIAL_SYMTABNDX_ALLOC
453 }
454