summaryrefslogtreecommitdiff
path: root/usr/src/cmd/sgs/elfedit/common/elfedit_machelf.c
blob: b5094e39c36a41d36306019bc18d894d5e5fc799 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
/*
 * 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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
 */

/*
 * ELFCLASS specific code for elfedit, built once for each class
 */
#include	<stdlib.h>
#include	<stdio.h>
#include	<unistd.h>
#include	<_machelf.h>
#include	<libelf.h>
#include	<strings.h>
#include	<sgs.h>
#include	"msg.h"
#include	"_elfedit.h"



/*
 * Look up the elfedit_symtab_t that corresponds to the symbol table
 * referenced by the sh_link field of the given auxiliary section.
 *
 * entry:
 *	obj_state - Partially constructed object state from
 *		elfedit_init_obj_state().
 *	auxsec - Section that is associated with the symbol table section
 *
 * exit:
 *	Returns the pointer to the elfedit_symtab_t entry that is
 *	referenced by the auxiliary section. If not found,
 *	outputs a debug message, and returns NULL.
 */
static elfedit_symtab_t *
get_symtab(elfedit_obj_state_t *obj_state, elfedit_section_t *auxsec)
{
	elfedit_symtab_t *symtab = obj_state->os_symtab;
	Word	sh_link = auxsec->sec_shdr->sh_link;
	Word	i;

	for (i = 0; i < obj_state->os_symtabnum; i++, symtab++)
		if (symtab->symt_shndx == sh_link)
			return (symtab);

	/*
	 * If we don't return above, it doesn't reference a valid
	 * symbol table. Issue warning.
	 */
	elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_AUX_LINK),
	    EC_WORD(auxsec->sec_shndx), auxsec->sec_name,
	    EC_WORD(sh_link));

	return (NULL);
}


/*
 * Fill in state.elf.obj_state with a a dynamically allocated
 * elfedit_obj_state_t struct of the appropriate ELFCLASS.
 * This pre-chewed form is fed to each command, reducing the amount
 * of ELF boilerplate code each command needs to contain.
 *
 * entry:
 *	file - Name of file to process
 *	fd - Descriptor of open file which has been successfully
 *		processed by elf_begin().
 *	elf - Elf handle returned by elf_begin
 *
 * exit:
 *	An elfedit_obj_state_t struct of the appropriate ELFCLASS has
 *	been dynamically allocated, and state.elf.obj_state references it.
 *	On failure, this routine does not return to the caller.
 *
 * note: The resulting elfedit_obj_state_t is allocated from a single
 *	piece of memory, such that a single call to free() suffices
 *	to release it as well as any memory it references.
 */
#ifdef _ELF64
void
elfedit64_init_obj_state(const char *file, int fd, Elf *elf)
#else
void
elfedit32_init_obj_state(const char *file, int fd, Elf *elf)
#endif
{
#define	INITIAL_SYMTABNDX_ALLOC	5

	/*
	 * These macros are used to call functions from libelf.
	 *
	 * LIBELF_FAIL encapsulates the common way in which we handle
	 * all of these errors: libelf_fail_name is set and execution
	 * jumps to the libelf_failure label for handling.
	 *
	 * LIBELF is used for the common case in which the function returns
	 * NULL for failure and something else for success.
	 */
#define	LIBELF_FAIL(_name) { libelf_fail_name = _name; goto libelf_failure; }
#define	LIBELF(_libelf_expr, _name) \
	if ((_libelf_expr) == NULL) \
		LIBELF_FAIL(_name)

	const char *libelf_fail_name;	/* Used for LIBELF errors */

	Elf_Scn			*scn;
	Elf_Data		*data;
	uint_t			ndx;
	size_t			len, os_size, secarr_size;
	char			*names = 0;
	size_t			names_len;
	elfedit_section_t	*_cache;
	elfedit_obj_state_t	tstate;
	elfedit_obj_state_t	*obj_state = NULL;
	Word			*symtabndx = NULL;
	Word			symtabndx_size = 0;
	elfedit_symtab_t	*symtab;

	tstate.os_file = file;
	tstate.os_fd = fd;
	tstate.os_elf = elf;
	tstate.os_dynndx = SHN_UNDEF;
	tstate.os_symtabnum = 0;

	LIBELF(tstate.os_ehdr = elf_getehdr(tstate.os_elf),
	    MSG_ORIG(MSG_ELF_GETEHDR))

	/* Program header array count and address */
	if (elf_getphdrnum(tstate.os_elf, &tstate.os_phnum) == -1)
		LIBELF_FAIL(MSG_ORIG(MSG_ELF_GETPHDRNUM))
	if (tstate.os_phnum > 0) {
		LIBELF((tstate.os_phdr = elf_getphdr(tstate.os_elf)),
		    MSG_ORIG(MSG_ELF_GETPHDR))
	} else {
		tstate.os_phdr = NULL;
	}

	if (elf_getshdrnum(tstate.os_elf, &tstate.os_shnum) == -1)
		LIBELF_FAIL(MSG_ORIG(MSG_ELF_GETSHDRNUM))

	/*
	 * Obtain the .shstrtab data buffer to provide the required section
	 * name strings.
	 */
	if (elf_getshdrstrndx(tstate.os_elf, &tstate.os_shstrndx) == -1)
		LIBELF_FAIL(MSG_ORIG(MSG_ELF_GETSHDRSTRNDX))
	LIBELF((scn = elf_getscn(tstate.os_elf, tstate.os_shstrndx)),
	    MSG_ORIG(MSG_ELF_GETSCN))
	LIBELF((data = elf_getdata(scn, NULL)), MSG_ORIG(MSG_ELF_GETDATA))
	names = data->d_buf;
	names_len = (names == NULL) ? 0 : data->d_size;

	/*
	 * Count the number of symbol tables and capture their indexes.
	 * Find the dynamic section.
	 */
	for (ndx = 1, scn = NULL; scn = elf_nextscn(tstate.os_elf, scn);
	    ndx++) {
		Shdr *shdr;

		LIBELF(shdr = elf_getshdr(scn), MSG_ORIG(MSG_ELF_GETSHDR));

		switch (shdr->sh_type) {
		case SHT_DYNAMIC:
			/* Save index of dynamic section for use below */
			tstate.os_dynndx = ndx;
			break;

		case SHT_SYMTAB:
		case SHT_DYNSYM:
		case SHT_SUNW_LDYNSYM:
			if (symtabndx_size <= tstate.os_symtabnum) {
				symtabndx_size = (symtabndx_size == 0) ?
				    INITIAL_SYMTABNDX_ALLOC :
				    (symtabndx_size * 2);
				symtabndx = elfedit_realloc(
				    MSG_INTL(MSG_ALLOC_SYMTABOS), symtabndx,
				    symtabndx_size * sizeof (symtabndx[0]));
			}
			symtabndx[tstate.os_symtabnum++] = ndx;
			break;
		}
	}

	/*
	 * Allocate space to hold the state. We allocate space for everything
	 * in one chunk to make releasing it easy:
	 *	(1) elfedit_obj_state_t struct
	 *	(2) The array of elfedit_section_t items referenced from
	 *		the elfedit_obj_state_t struct.
	 *	(3) The array of elfedit_symtab_t items referenced from
	 *		the elfedit_obj_state_t struct.
	 *	(4) The file name.
	 *
	 * Note that we round up the size of (1) and (2) to a double boundary
	 * to ensure proper alignment of (2) and (3). (4) can align on any
	 * boundary.
	 */
	os_size = S_DROUND(sizeof (tstate));
	secarr_size = (tstate.os_shnum * sizeof (elfedit_section_t));
	secarr_size = S_DROUND(secarr_size);
	len = strlen(tstate.os_file) + 1;
	obj_state = elfedit_malloc(MSG_INTL(MSG_ALLOC_OBJSTATE),
	    os_size + secarr_size +
	    (tstate.os_symtabnum * sizeof (elfedit_symtab_t)) + len);
	*obj_state = tstate;

	/*LINTED E_BAD_PTR_CAST_ALIGN*/
	obj_state->os_secarr = (elfedit_section_t *)
	    ((char *)obj_state + os_size);
	if (obj_state->os_symtabnum == 0) {
		obj_state->os_symtab = NULL;
	} else {
		/*LINTED E_BAD_PTR_CAST_ALIGN*/
		obj_state->os_symtab = (elfedit_symtab_t *)
		    ((char *)obj_state->os_secarr + secarr_size);
		obj_state->os_file =
		    (char *)(obj_state->os_symtab + tstate.os_symtabnum);
		(void) strncpy((char *)obj_state->os_file, tstate.os_file, len);
	}

	/*
	 * Fill in obj_state->os_secarr with information for each section.
	 * At the same time, fill in obj_state->os_symtab with the symbol
	 * table related data.
	 */
	bzero(obj_state->os_secarr, sizeof (obj_state->os_secarr[0]));
	_cache = obj_state->os_secarr;
	LIBELF(scn = elf_getscn(tstate.os_elf, 0),
	    MSG_ORIG(MSG_ELF_GETSCN));
	_cache->sec_scn = scn;
	LIBELF(_cache->sec_shdr = elf_getshdr(scn), MSG_ORIG(MSG_ELF_GETSHDR));
	_cache->sec_name = (_cache->sec_shdr->sh_name < names_len) ?
	    (names + _cache->sec_shdr->sh_name) : MSG_INTL(MSG_UNKNOWNSECNAM);
	_cache++;

	if (obj_state->os_symtab != NULL) {
		bzero(obj_state->os_symtab,
		    sizeof (obj_state->os_symtab[0]) * obj_state->os_symtabnum);
		for (ndx = 0; ndx < obj_state->os_symtabnum; ndx++)
			obj_state->os_symtab[ndx].symt_shndx = symtabndx[ndx];
		free(symtabndx);
	}

	for (ndx = 1, scn = NULL; scn = elf_nextscn(tstate.os_elf, scn);
	    ndx++, _cache++) {
		_cache->sec_shndx = ndx;
		_cache->sec_scn = scn;
		LIBELF(_cache->sec_shdr = elf_getshdr(scn),
		    MSG_ORIG(MSG_ELF_GETSHDR))
		_cache->sec_data = elf_getdata(scn, NULL);
		_cache->sec_name = (_cache->sec_shdr->sh_name < names_len) ?
		    (names + _cache->sec_shdr->sh_name) :
		    MSG_INTL(MSG_UNKNOWNSECNAM);

		switch (_cache->sec_shdr->sh_type) {
		case SHT_SYMTAB_SHNDX:
			symtab = get_symtab(obj_state, _cache);
			symtab->symt_xshndx = ndx;
			break;

		case SHT_SUNW_syminfo:
			symtab = get_symtab(obj_state, _cache);
			symtab->symt_syminfo = ndx;
			break;

		case SHT_SUNW_versym:
			symtab = get_symtab(obj_state, _cache);
			symtab->symt_versym = ndx;
			break;
		}
	}

	/*
	 * Sanity check the symbol tables, and discard any auxiliary
	 * sections without enough elements.
	 */
	symtab = obj_state->os_symtab;
	for (ndx = 0; ndx < obj_state->os_symtabnum; ndx++, symtab++) {
		elfedit_section_t	*symsec;
		Word			symsec_cnt, aux_cnt;

		symsec = &obj_state->os_secarr[symtab->symt_shndx];
		symsec_cnt = symsec->sec_shdr->sh_size / sizeof (Sym);

		/* Extended section indexes */
		if (symtab->symt_xshndx != SHN_UNDEF) {
			_cache = &obj_state->os_secarr[symtab->symt_xshndx];
			aux_cnt = _cache->sec_shdr->sh_size / sizeof (Word);
			if (symsec_cnt > aux_cnt)
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_AUX_SIZE),
				    EC_WORD(ndx), _cache->sec_name,
				    EC_WORD(aux_cnt),
				    EC_WORD(symsec->sec_shndx),
				    symsec->sec_name, EC_WORD(aux_cnt));
		}

		/* Syminfo */
		if (symtab->symt_syminfo != SHN_UNDEF) {
			_cache = &obj_state->os_secarr[symtab->symt_syminfo];
			aux_cnt = _cache->sec_shdr->sh_size / sizeof (Syminfo);
			if (symsec_cnt > aux_cnt)
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_AUX_SIZE),
				    EC_WORD(ndx), _cache->sec_name,
				    EC_WORD(aux_cnt),
				    EC_WORD(symsec->sec_shndx),
				    symsec->sec_name, EC_WORD(aux_cnt));
		}

		/* Versym */
		if (symtab->symt_versym != SHN_UNDEF) {
			_cache = &obj_state->os_secarr[symtab->symt_versym];
			aux_cnt = _cache->sec_shdr->sh_size / sizeof (Versym);
			if (symsec_cnt > aux_cnt)
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_AUX_SIZE),
				    EC_WORD(ndx), _cache->sec_name,
				    EC_WORD(aux_cnt),
				    EC_WORD(symsec->sec_shndx),
				    symsec->sec_name, EC_WORD(aux_cnt));
		}
	}

	/*
	 * If this object has a dynsym section with a FLAGS_1 field,
	 * then set the DF_1_EDITED bit. elfedit allows changes that
	 * can break the resulting program, so knowing that a file was
	 * edited can be helpful when encountering a core file or other
	 * unexpected failure in the field. A single bit can't tell you
	 * what was changed, but it will alert you to the possibility that
	 * some additional questions might be in order.
	 */
	if (obj_state->os_dynndx != SHN_UNDEF) {
		Word			i;
		Word			numdyn;
		elfedit_section_t	*dynsec;
		elfedit_dyn_elt_t	flags_1_elt;
		elfedit_dyn_elt_t	null_elt;
		Dyn			*dyn;

		dynsec = &obj_state->os_secarr[obj_state->os_dynndx];
		dyn = (Dyn *) dynsec->sec_data->d_buf;
		numdyn = dynsec->sec_shdr->sh_size /
		    dynsec->sec_shdr->sh_entsize;
		elfedit_dyn_elt_init(&flags_1_elt);
		elfedit_dyn_elt_init(&null_elt);
		for (i = 0; i < numdyn; i++) {

			switch (dyn[i].d_tag) {
			case DT_NULL:
				/*
				 * Remember 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 flags1,
				 * then we will turn the first one into a
				 * DT_FLAGS_1.
				 */
				if (!null_elt.dn_seen)
					elfedit_dyn_elt_save(&null_elt, i,
					    &dyn[i]);
				break;

			case DT_FLAGS_1:
				elfedit_dyn_elt_save(&flags_1_elt, i, &dyn[i]);
				break;
			}
		}
		/* If don't have a flags1 field, can we make one from a NULL? */
		if (!flags_1_elt.dn_seen && null_elt.dn_seen &&
		    (null_elt.dn_ndx < (numdyn - 1))) {
			elfedit_msg(ELFEDIT_MSG_DEBUG,
			    MSG_INTL(MSG_DEBUG_NULL2DYNFL1),
			    EC_WORD(obj_state->os_dynndx),
			    dynsec->sec_name, EC_WORD(null_elt.dn_ndx));
			flags_1_elt.dn_seen = 1;
			flags_1_elt.dn_ndx = null_elt.dn_ndx;
			flags_1_elt.dn_dyn.d_tag = DT_FLAGS_1;
			flags_1_elt.dn_dyn.d_un.d_val = 0;
		}
		/*
		 * If there is a flags 1 field, add the edit flag if
		 * it is not present, and report it's presence otherwise.
		 */
		if (flags_1_elt.dn_seen) {
			if (flags_1_elt.dn_dyn.d_un.d_val & DF_1_EDITED) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_SEEDYNFLG),
				    EC_WORD(obj_state->os_dynndx),
				    dynsec->sec_name,
				    EC_WORD(flags_1_elt.dn_ndx));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_ADDDYNFLG),
				    EC_WORD(obj_state->os_dynndx),
				    dynsec->sec_name,
				    EC_WORD(flags_1_elt.dn_ndx));
				flags_1_elt.dn_dyn.d_un.d_val |= DF_1_EDITED;
				dyn[flags_1_elt.dn_ndx] = flags_1_elt.dn_dyn;
				elfedit_modified_data(dynsec);
			}
		}
	}

#ifdef _ELF64
	state.elf.obj_state.s64 = obj_state;
#else
	state.elf.obj_state.s32 = obj_state;
#endif
	return;

libelf_failure:
	/*
	 * Control comes here if there is an error with LIBELF.
	 *
	 * entry:
	 *	libelf_fail_name - Name of failing libelf function
	 *	tstate.os_file - Name of ELF file being processed
	 *	tstate.os_fd - Descriptor of open ELF file
	 *
	 * exit:
	 *	- dynamic memory is released if necessary
	 *	- The error issued
	 */
	if (obj_state != NULL)
		free(obj_state);
	(void) close(tstate.os_fd);
	elfedit_elferr(tstate.os_file, libelf_fail_name);
#undef INITIAL_SYMTABNDX_ALLOC
#undef LIBELF_FAIL
#undef LIBELF
}