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