xref: /illumos-gate/usr/src/cmd/sgs/elfwrap/common/elfwrap.c (revision d48be21240dfd051b689384ce2b23479d757f2d8)
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	<sys/types.h>
28 #include	<sys/stat.h>
29 #include	<sys/mman.h>
30 #include	<unistd.h>
31 #include	<fcntl.h>
32 #include	<libgen.h>
33 #include	<errno.h>
34 #include	<libelf.h>
35 #include	<stdio.h>
36 #include	<strings.h>
37 #include	<msg.h>
38 #include	<machdep.h>
39 #include	<_libelf.h>
40 #include	<_elfwrap.h>
41 
42 /*
43  * This module is compiled to support 32-bit and 64-bit class objects.  Define
44  * the necessary interfaces for these classes.
45  */
46 #if	defined(_ELF64)
47 #define	input	input64
48 #define	output	output64
49 #else
50 #define	input	input32
51 #define	output	output32
52 #endif
53 
54 static StdSec_t	StdSecs[] = {
55 	{ MSG_ORIG(MSG_SCN_SYMTAB),	SHT_SYMTAB,	0 },
56 	{ MSG_ORIG(MSG_SCN_STRTAB),	SHT_STRTAB,	SHF_STRINGS},
57 	{ MSG_ORIG(MSG_SCN_SHSTRTAB),	SHT_STRTAB,	SHF_STRINGS},
58 	{ NULL,				0,		0 }
59 };
60 
61 /*
62  * Process all input files.  These contain the data that will be assigned to a
63  * new ELF section.
64  */
65 int
66 input(int argc, char **argv, const char *prog, const char *ofile,
67     ObjDesc_t *odp)
68 {
69 	OutSec_t	outsec;
70 	StdSec_t	*stdsecs;
71 	size_t		ndx, cnt;
72 	int		ret = 0, fd = -1;
73 
74 	/*
75 	 * Make sure we have access to read each input file, and prepare an
76 	 * output section descriptor for each.  Note, we assign section indexes
77 	 * starting at 1, as section index 0 is special, and is created by
78 	 * libelf.
79 	 */
80 	for (ndx = 1; argc; argc--, argv++, ndx++) {
81 		char		*file = *argv;
82 		struct stat	status;
83 		size_t		namesz;
84 
85 		/*
86 		 * Close any previously opened file.
87 		 */
88 		if (fd != -1)
89 			(void) close(fd);
90 
91 		/*
92 		 * Identify the section.
93 		 */
94 		outsec.os_name = basename(file);
95 		outsec.os_type = SHT_PROGBITS;
96 		outsec.os_flags = SHF_ALLOC;
97 		outsec.os_ndx = ndx;
98 
99 		if ((fd = open(file, O_RDONLY)) == -1) {
100 			int err = errno;
101 			(void) fprintf(stderr, MSG_INTL(MSG_ERR_OPEN),
102 			    prog, file, strerror(err));
103 			ret = 1;
104 			continue;
105 		}
106 		if (fstat(fd, &status) == -1) {
107 			int err = errno;
108 			(void) fprintf(stderr, MSG_INTL(MSG_ERR_FSTAT),
109 			    prog, file, strerror(err));
110 			ret = 1;
111 			continue;
112 		}
113 
114 		if ((outsec.os_size = status.st_size) == 0) {
115 			(void) fprintf(stderr, MSG_INTL(MSG_WARN_ZERO),
116 			    prog, file);
117 			continue;
118 		}
119 
120 		if ((outsec.os_addr = mmap(0, outsec.os_size, PROT_READ,
121 		    MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
122 			int err = errno;
123 			(void) fprintf(stderr, MSG_INTL(MSG_ERR_MMAP),
124 			    prog, file, strerror(err));
125 			ret = 1;
126 			continue;
127 		}
128 
129 		if (alist_append(&(odp->od_outsecs), &outsec, sizeof (OutSec_t),
130 		    AL_CNT_WOSECS) == 0) {
131 			int err = errno;
132 			(void) fprintf(stderr, MSG_INTL(MSG_ERR_ALLOC),
133 			    prog, file, strerror(err));
134 			return (1);
135 		}
136 
137 		/*
138 		 * Each data section contributes:
139 		 *
140 		 * i.	its basename, prefixed with a "dot", to the .shstrtab.
141 		 * ii.	a section symbol.
142 		 * iii.	a data symbol, using the basename, with an
143 		 *	appended "_data" string.
144 		 * iv.	a data size symbol, using the basename with an
145 		 *	appended "_size" string.
146 		 */
147 		namesz = strlen(outsec.os_name) + 1;
148 
149 		odp->od_symtabno += 3;
150 		odp->od_strtabsz += (namesz + MSG_STR_START_SIZE);
151 		odp->od_strtabsz += (namesz + MSG_STR_END_SIZE);
152 		odp->od_shstrtabsz += (namesz + MSG_STR_DOT_SIZE);
153 	}
154 
155 	if (fd != -1)
156 		(void) close(fd);
157 
158 	/*
159 	 * If an error occurred, or no input files contributed data, bail now.
160 	 */
161 	if (ret || (odp->od_outsecs == NULL))
162 		return (1);
163 
164 	/*
165 	 * Create section descriptors for .symtab, .strtab, and .shstrtab.
166 	 */
167 	for (cnt = 0, stdsecs = &StdSecs[cnt]; stdsecs->ss_name; cnt++,
168 	    ndx++, stdsecs = &StdSecs[cnt]) {
169 
170 		/*
171 		 * Identify the section.
172 		 */
173 		outsec.os_name = stdsecs->ss_name;
174 		outsec.os_type = stdsecs->ss_type;
175 		outsec.os_flags = stdsecs->ss_flags;
176 		outsec.os_ndx = ndx;
177 		outsec.os_size = 0;
178 		outsec.os_addr = 0;
179 
180 		if (alist_append(&(odp->od_outsecs), &outsec, sizeof (OutSec_t),
181 		    AL_CNT_WOSECS) == 0) {
182 			int err = errno;
183 			(void) fprintf(stderr, MSG_INTL(MSG_ERR_ALLOC),
184 			    prog, outsec.os_name, strerror(err));
185 			return (1);
186 		}
187 
188 		/*
189 		 * Each standard section contributes:
190 		 *
191 		 * i.	its section name to the .shstrtab.
192 		 * ii.	a section symbol.
193 		 */
194 		odp->od_symtabno++;
195 		odp->od_shstrtabsz += (strlen(outsec.os_name) + 1);
196 	}
197 
198 	/*
199 	 * The symbol table requires an initial NULL entry and a following
200 	 * FILE entry.  Both string tables require an initial NULL byte.
201 	 * The .strtab requires room for the output file name (STT_FILE).
202 	 */
203 	odp->od_symtabno += 2;
204 	odp->od_strtabsz += strlen(ofile) + 2;
205 	odp->od_shstrtabsz++;
206 
207 	return (0);
208 }
209 
210 /*
211  * Having captured all input data, create the output file.
212  */
213 int
214 output(const char *prog, int fd, const char *ofile, ushort_t mach,
215     ObjDesc_t *odp)
216 {
217 	Aliste		off;
218 	Elf		*melf, *oelf;
219 	Ehdr		*ehdr;
220 	Sym		*symtab, *secsymtabent, *glbsymtabent;
221 	char		*strtab, *strtabent, *shstrtab, *shstrtabent;
222 	OutSec_t	*outsec, *outsymtab, *outstrtab, *outshstrtab;
223 	size_t		len;
224 	TargDesc_t	tdesc;
225 
226 	/*
227 	 * Obtain any target specific ELF information.
228 	 */
229 	if (mach == 0)
230 		mach = M_MACH;
231 
232 	switch (mach) {
233 #if	!defined(lint)
234 		case EM_SPARC:
235 			target_init_sparc(&tdesc);
236 			break;
237 		case EM_SPARCV9:
238 			target_init_sparcv9(&tdesc);
239 			break;
240 		case EM_386:
241 			target_init_i386(&tdesc);
242 			break;
243 		case EM_AMD64:
244 			target_init_amd64(&tdesc);
245 			break;
246 #else
247 		default:
248 			target_init(&tdesc);
249 			break;
250 #endif
251 	}
252 	/*
253 	 * Create a new ELF descriptor for the new output file.
254 	 */
255 	if ((oelf = elf_begin(fd, ELF_C_WRITE, 0)) == NULL) {
256 		(void) fprintf(stderr, MSG_INTL(MSG_ELF_BEGIN), prog,
257 		    elf_errmsg(elf_errno()));
258 		return (1);
259 	}
260 
261 	/*
262 	 * Create and initialize the new ELF header.
263 	 */
264 	if ((ehdr = elf_newehdr(oelf)) == NULL) {
265 		(void) fprintf(stderr, MSG_INTL(MSG_ELF_NEWEHDR), prog,
266 		    elf_errmsg(elf_errno()));
267 		return (1);
268 	}
269 
270 	/*
271 	 * Note, the ELF header is initialized to reflect the host running
272 	 * elfwrap(1) rather than the target.  Using host byte order allows
273 	 * elfwrap(1) to create the object data.  Prior to the final update,
274 	 * the output ELF header is modified to reflect the target, causing
275 	 * libelf to produce the output object using the correct byte order
276 	 * and other target information.
277 	 */
278 	ehdr->e_ident[EI_DATA] = M_DATA;
279 	ehdr->e_type = ET_REL;
280 	ehdr->e_version = EV_CURRENT;
281 
282 	/*
283 	 * Create the required number of new sections, their associated section
284 	 * header, and an initial data buffer.
285 	 */
286 	for (ALIST_TRAVERSE(odp->od_outsecs, off, outsec)) {
287 		Elf_Scn		*scn;
288 		Elf_Data	*data;
289 		Shdr		*shdr;
290 
291 		if ((scn = elf_newscn(oelf)) == NULL) {
292 			(void) fprintf(stderr, MSG_INTL(MSG_ELF_NEWSCN),
293 			    prog, outsec->os_name, elf_errmsg(elf_errno()));
294 			return (1);
295 		}
296 		if ((shdr = elf_getshdr(scn)) == NULL) {
297 			(void) fprintf(stderr, MSG_INTL(MSG_ELF_GETSHDR),
298 			    prog, outsec->os_name, elf_errmsg(elf_errno()));
299 			return (1);
300 		}
301 
302 		/*
303 		 * Assign the section type and flags.
304 		 */
305 		shdr->sh_type = outsec->os_type;
306 		shdr->sh_flags = outsec->os_flags;
307 
308 		if ((data = elf_newdata(scn)) == NULL) {
309 			(void) fprintf(stderr, MSG_INTL(MSG_ELF_NEWDATA),
310 			    prog, outsec->os_name, elf_errmsg(elf_errno()));
311 			return (1);
312 		}
313 
314 		switch (shdr->sh_type) {
315 		case SHT_PROGBITS:
316 			/*
317 			 * If this is a PROGBITS section, then the data
318 			 * originates from an input file.  Assign the data
319 			 * buffer to this input file and provide a default
320 			 * alignment.
321 			 */
322 			data->d_buf = outsec->os_addr;
323 			data->d_type = ELF_T_BYTE;
324 			data->d_size = outsec->os_size;
325 			data->d_align = tdesc.td_align;
326 			break;
327 
328 		case SHT_SYMTAB:
329 			/*
330 			 * If this is the symbol table, use the symbol count to
331 			 * reserve sufficient space for the symbols we need.
332 			 */
333 			data->d_buf = 0;
334 			data->d_type = ELF_T_SYM;
335 			data->d_size = (odp->od_symtabno * tdesc.td_symsz);
336 			data->d_align = tdesc.td_align;
337 			break;
338 
339 		case SHT_STRTAB:
340 			/*
341 			 * If this is a string table, use the table size to
342 			 * reserve sufficient space for the strings we need.
343 			 */
344 			data->d_buf = 0;
345 			data->d_type = ELF_T_BYTE;
346 			if (strcmp(outsec->os_name, MSG_ORIG(MSG_SCN_STRTAB)))
347 				data->d_size = odp->od_shstrtabsz;
348 			else
349 				data->d_size = odp->od_strtabsz;
350 			data->d_align = 1;
351 			break;
352 		}
353 	}
354 
355 	/*
356 	 * Write the ELF data into a memory image.
357 	 */
358 	if ((elf_update(oelf, ELF_C_WRIMAGE)) == -1) {
359 		(void) fprintf(stderr, MSG_INTL(MSG_ELF_UPDATE), prog,
360 		    elf_errmsg(elf_errno()));
361 		return (1);
362 	}
363 
364 	/*
365 	 * Assign an ELF descriptor to the memory image.
366 	 */
367 	if ((melf = elf_begin(0, ELF_C_IMAGE, oelf)) == NULL) {
368 		(void) fprintf(stderr, MSG_INTL(MSG_ELF_BEGIN), prog,
369 		    elf_errmsg(elf_errno()));
370 		return (1);
371 	}
372 
373 	/*
374 	 * Get the ELF header from the memory image.
375 	 */
376 	if ((ehdr = elf_getehdr(melf)) == NULL) {
377 		(void) fprintf(stderr, MSG_INTL(MSG_ELF_GETEHDR), prog,
378 		    elf_errmsg(elf_errno()));
379 		return (1);
380 	}
381 
382 	/*
383 	 * Read the section header and data from the new sections of the
384 	 * memory image.
385 	 */
386 	for (ALIST_TRAVERSE(odp->od_outsecs, off, outsec)) {
387 		Elf_Scn		*scn;
388 		Shdr		*shdr;
389 
390 		if ((scn = elf_getscn(melf, outsec->os_ndx)) == NULL) {
391 			(void) fprintf(stderr, MSG_INTL(MSG_ELF_GETSCN),
392 			    prog, outsec->os_name, elf_errmsg(elf_errno()));
393 			return (1);
394 		}
395 		if ((outsec->os_shdr = shdr = elf_getshdr(scn)) == NULL) {
396 			(void) fprintf(stderr, MSG_INTL(MSG_ELF_GETSHDR),
397 			    prog, outsec->os_name, elf_errmsg(elf_errno()));
398 			return (1);
399 		}
400 		if ((outsec->os_data = elf_getdata(scn, NULL)) == NULL) {
401 			(void) fprintf(stderr, MSG_INTL(MSG_ELF_GETDATA),
402 			    prog, outsec->os_name, elf_errmsg(elf_errno()));
403 			return (1);
404 		}
405 
406 		if (shdr->sh_type == SHT_PROGBITS)
407 			continue;
408 
409 		/*
410 		 * Remember the symbol table and string tables, so that they
411 		 * can be filled in later.
412 		 */
413 		if (shdr->sh_type == SHT_SYMTAB) {
414 			outsymtab = outsec;
415 			symtab = (Sym *)outsec->os_data->d_buf;
416 		} else if (shdr->sh_type == SHT_STRTAB) {
417 			if (strcmp(outsec->os_name, MSG_ORIG(MSG_SCN_STRTAB))) {
418 				outshstrtab = outsec;
419 				shstrtab = (char *)outsec->os_data->d_buf;
420 			} else {
421 				outstrtab = outsec;
422 				strtab = (char *)outsec->os_data->d_buf;
423 			}
424 		}
425 	}
426 
427 	/*
428 	 * Update the ELF header with the .shstrtab index.
429 	 */
430 	ehdr->e_shstrndx = outshstrtab->os_ndx;
431 
432 	/*
433 	 * Set up the string table entries, and skip the first byte.
434 	 */
435 	strtabent = strtab;
436 	strtabent++;
437 
438 	shstrtabent = shstrtab;
439 	shstrtabent++;
440 
441 	/*
442 	 * Skip the first symbol table entry.  Write a FILE entry, and set
443 	 * up for adding sections and data symbols.  Associate the symbol
444 	 * table with the string table.
445 	 */
446 	secsymtabent = symtab;
447 	secsymtabent++;
448 	secsymtabent->st_name = (strtabent - strtab);
449 	secsymtabent->st_info = ELF_ST_INFO(STB_LOCAL, STT_NOTYPE);
450 	secsymtabent->st_shndx = SHN_ABS;
451 	secsymtabent++;
452 
453 	glbsymtabent = secsymtabent;
454 	glbsymtabent += alist_nitems(odp->od_outsecs);
455 
456 	outsymtab->os_shdr->sh_link = outstrtab->os_ndx;
457 
458 	/*
459 	 * Write the output file name to the .strtab.
460 	 */
461 	len = strlen(ofile) + 1;
462 	(void) memcpy(strtabent, ofile, len);
463 	strtabent += len;
464 
465 	/*
466 	 * Rescan all the new sections, adding symbols and strings as required.
467 	 */
468 	for (ALIST_TRAVERSE(odp->od_outsecs, off, outsec)) {
469 		size_t	alen;
470 
471 		/*
472 		 * Create a section symbol.
473 		 */
474 		secsymtabent->st_info = ELF_ST_INFO(STB_LOCAL, STT_SECTION);
475 		secsymtabent->st_shndx = outsec->os_ndx;
476 		secsymtabent++;
477 
478 		/*
479 		 * Store the section name, (with an appended "." if the section
480 		 * name is derived from the input file name), and point the
481 		 * section header to this name.
482 		 */
483 		outsec->os_shdr->sh_name = (shstrtabent - shstrtab);
484 
485 		if (outsec->os_shdr->sh_type == SHT_PROGBITS) {
486 			(void) memcpy(shstrtabent, MSG_ORIG(MSG_STR_DOT),
487 			    MSG_STR_DOT_SIZE);
488 			shstrtabent += MSG_STR_DOT_SIZE;
489 		}
490 
491 		len = strlen(outsec->os_name) + 1;
492 		(void) memcpy(shstrtabent, outsec->os_name, len);
493 		shstrtabent += len;
494 
495 		if (outsec->os_shdr->sh_type != SHT_PROGBITS)
496 			continue;
497 
498 		/*
499 		 * Add a symbol pointing to this PROGBITS section.  The value
500 		 * is the base offset of this section, which can only be 0.
501 		 * The size of the symbol can be taken straight from the section
502 		 * header information (that libelf generated).
503 		 */
504 		glbsymtabent->st_name = (strtabent - strtab);
505 		glbsymtabent->st_info = ELF_ST_INFO(STB_GLOBAL, STT_OBJECT);
506 		glbsymtabent->st_shndx = outsec->os_ndx;
507 		glbsymtabent->st_size = outsec->os_shdr->sh_size;
508 		glbsymtabent++;
509 
510 		/*
511 		 * Store this symbol name (with an appended "_data") in the
512 		 * string table.
513 		 */
514 		len--;
515 		(void) memcpy(strtabent, outsec->os_name, len);
516 		strtabent += len;
517 		alen = (MSG_STR_START_SIZE + 1);
518 		(void) memcpy(strtabent, MSG_ORIG(MSG_STR_START), alen);
519 		strtabent += alen;
520 
521 		/*
522 		 * Add a symbol indicating the size of this PROGBITS section.
523 		 */
524 		glbsymtabent->st_name = (strtabent - strtab);
525 		glbsymtabent->st_info = ELF_ST_INFO(STB_GLOBAL, STT_OBJECT);
526 		glbsymtabent->st_shndx = outsec->os_ndx;
527 		glbsymtabent->st_value = outsec->os_shdr->sh_size;
528 		glbsymtabent++;
529 
530 		/*
531 		 * Store this symbol name (with an appended "_end") in the
532 		 * string table.
533 		 */
534 		(void) memcpy(strtabent, outsec->os_name, len);
535 		strtabent += len;
536 		alen = (MSG_STR_END_SIZE + 1);
537 		(void) memcpy(strtabent, MSG_ORIG(MSG_STR_END), alen);
538 		strtabent += alen;
539 	}
540 
541 	/*
542 	 * Update the .symtab section header with the index of the first
543 	 * non-local symbol.  The only locals written are the section symbols.
544 	 */
545 	outsymtab->os_shdr->sh_info = (secsymtabent - symtab);
546 
547 	/*
548 	 * Having updated the image following the byte order of elfwrap(), seed
549 	 * the ELF header with the appropriate target information.
550 	 */
551 	ehdr->e_ident[EI_CLASS] = tdesc.td_class;
552 	ehdr->e_ident[EI_DATA] = tdesc.td_data;
553 	ehdr->e_machine = tdesc.td_mach;
554 
555 	/*
556 	 * If the output relocatable object is targeted to a machine with a
557 	 * different byte order than the host running elfwrap(1), swap the data
558 	 * to the target byte order.
559 	 */
560 	if ((_elf_sys_encoding() != ehdr->e_ident[EI_DATA]) &&
561 	    (_elf_swap_wrimage(melf) != 0)) {
562 		(void) fprintf(stderr, MSG_INTL(MSG_ELF_SWAP_WRIMAGE), prog,
563 		    elf_errmsg(elf_errno()));
564 		return (1);
565 	}
566 	(void) elf_end(melf);
567 
568 	/*
569 	 * Finally, write the updated memory image out to disc.
570 	 */
571 	if ((elf_update(oelf, ELF_C_WRITE)) == -1) {
572 		(void) fprintf(stderr, MSG_INTL(MSG_ELF_UPDATE), prog,
573 		    elf_errmsg(elf_errno()));
574 		return (1);
575 	}
576 	(void) elf_end(oelf);
577 
578 	return (0);
579 }
580