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 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25/*
26 * Copyright (c) 2015, Joyent, Inc.
27 */
28
29/*
30 * Routines for writing ctf data to elf files.
31 */
32
33#include <libctf_impl.h>
34#include <libctf.h>
35#include <gelf.h>
36#include <sys/stat.h>
37#include <sys/types.h>
38#include <fcntl.h>
39#include <errno.h>
40#include <unistd.h>
41#include <libelf.h>
42
43static int
44ctf_write_elf(ctf_file_t *fp, Elf *src, Elf *dst, int flags)
45{
46	GElf_Ehdr sehdr, dehdr;
47	Elf_Scn *sscn, *dscn;
48	Elf_Data *sdata, *ddata;
49	GElf_Shdr shdr;
50	int symtab_idx = -1;
51	off_t new_offset = 0;
52	off_t ctfnameoff = 0;
53	int compress = (flags & CTF_ELFWRITE_F_COMPRESS);
54	int *secxlate = NULL;
55	int srcidx, dstidx, pad, i;
56	int curnmoff = 0;
57	int changing = 0;
58	int ret;
59	size_t nshdr, nphdr, strndx;
60	void *strdatabuf = NULL, *symdatabuf = NULL;
61	size_t strdatasz = 0, symdatasz = 0;
62
63	void *cdata = NULL;
64	size_t elfsize, asize;
65
66	if ((flags & ~(CTF_ELFWRITE_F_COMPRESS)) != 0) {
67		ret = ctf_set_errno(fp, EINVAL);
68		goto out;
69	}
70
71	if (gelf_newehdr(dst, gelf_getclass(src)) == 0) {
72		ret = ctf_set_errno(fp, ECTF_ELF);
73		goto out;
74	}
75	if (gelf_getehdr(src, &sehdr) == NULL) {
76		ret = ctf_set_errno(fp, ECTF_ELF);
77		goto out;
78	}
79	(void) memcpy(&dehdr, &sehdr, sizeof (GElf_Ehdr));
80	if (gelf_update_ehdr(dst, &dehdr) == 0) {
81		ret = ctf_set_errno(fp, ECTF_ELF);
82		goto out;
83	}
84
85	/*
86	 * Use libelf to get the number of sections and the string section to
87	 * deal with ELF files that may have a large number of sections. We just
88	 * always use this to make our live easier.
89	 */
90	if (elf_getphdrnum(src, &nphdr) != 0) {
91		ret = ctf_set_errno(fp, ECTF_ELF);
92		goto out;
93	}
94	if (elf_getshdrnum(src, &nshdr) != 0) {
95		ret = ctf_set_errno(fp, ECTF_ELF);
96		goto out;
97	}
98	if (elf_getshdrstrndx(src, &strndx) != 0) {
99		ret = ctf_set_errno(fp, ECTF_ELF);
100		goto out;
101	}
102
103	/*
104	 * Neither the existing debug sections nor the SUNW_ctf sections (new or
105	 * existing) are SHF_ALLOC'd, so they won't be in areas referenced by
106	 * program headers.  As such, we can just blindly copy the program
107	 * headers from the existing file to the new file.
108	 */
109	if (nphdr != 0) {
110		(void) elf_flagelf(dst, ELF_C_SET, ELF_F_LAYOUT);
111		if (gelf_newphdr(dst, nphdr) == 0) {
112			ret = ctf_set_errno(fp, ECTF_ELF);
113			goto out;
114		}
115
116		for (i = 0; i < nphdr; i++) {
117			GElf_Phdr phdr;
118
119			if (gelf_getphdr(src, i, &phdr) == NULL) {
120				ret = ctf_set_errno(fp, ECTF_ELF);
121				goto out;
122			}
123			if (gelf_update_phdr(dst, i, &phdr) == 0) {
124				ret = ctf_set_errno(fp, ECTF_ELF);
125				goto out;
126			}
127		}
128	}
129
130	secxlate = ctf_alloc(sizeof (int) * nshdr);
131	for (srcidx = dstidx = 0; srcidx < nshdr; srcidx++) {
132		Elf_Scn *scn = elf_getscn(src, srcidx);
133		GElf_Shdr shdr;
134		char *sname;
135
136		if (gelf_getshdr(scn, &shdr) == NULL) {
137			ret = ctf_set_errno(fp, ECTF_ELF);
138			goto out;
139		}
140		sname = elf_strptr(src, strndx, shdr.sh_name);
141		if (sname == NULL) {
142			ret = ctf_set_errno(fp, ECTF_ELF);
143			goto out;
144		}
145
146		if (strcmp(sname, CTF_ELF_SCN_NAME) == 0) {
147			secxlate[srcidx] = -1;
148		} else {
149			secxlate[srcidx] = dstidx++;
150			curnmoff += strlen(sname) + 1;
151		}
152
153		new_offset = (off_t)dehdr.e_phoff;
154	}
155
156	for (srcidx = 1; srcidx < nshdr; srcidx++) {
157		char *sname;
158
159		sscn = elf_getscn(src, srcidx);
160		if (gelf_getshdr(sscn, &shdr) == NULL) {
161			ret = ctf_set_errno(fp, ECTF_ELF);
162			goto out;
163		}
164
165		if (secxlate[srcidx] == -1) {
166			changing = 1;
167			continue;
168		}
169
170		dscn = elf_newscn(dst);
171		if (dscn == NULL) {
172			ret = ctf_set_errno(fp, ECTF_ELF);
173			goto out;
174		}
175
176		/*
177		 * If this file has program headers, we need to explicitly lay
178		 * out sections.  If none of the sections prior to this one have
179		 * been removed, then we can just use the existing location.  If
180		 * one or more sections have been changed, then we need to
181		 * adjust this one to avoid holes.
182		 */
183		if (changing && nphdr != 0) {
184			pad = new_offset % shdr.sh_addralign;
185
186			if (pad != 0)
187				new_offset += shdr.sh_addralign - pad;
188			shdr.sh_offset = new_offset;
189		}
190
191		shdr.sh_link = secxlate[shdr.sh_link];
192
193		if (shdr.sh_type == SHT_REL || shdr.sh_type == SHT_RELA)
194			shdr.sh_info = secxlate[shdr.sh_info];
195
196		sname = elf_strptr(src, strndx, shdr.sh_name);
197		if (sname == NULL) {
198			ret = ctf_set_errno(fp, ECTF_ELF);
199			goto out;
200		}
201		if ((sdata = elf_getdata(sscn, NULL)) == NULL) {
202			ret = ctf_set_errno(fp, ECTF_ELF);
203			goto out;
204		}
205		if ((ddata = elf_newdata(dscn)) == NULL) {
206			ret = ctf_set_errno(fp, ECTF_ELF);
207			goto out;
208		}
209		bcopy(sdata, ddata, sizeof (Elf_Data));
210
211		if (srcidx == strndx) {
212			char seclen = strlen(CTF_ELF_SCN_NAME);
213
214			strdatasz = ddata->d_size + shdr.sh_size +
215			    seclen + 1;
216			ddata->d_buf = strdatabuf = ctf_alloc(strdatasz);
217			if (ddata->d_buf == NULL) {
218				ret = ctf_set_errno(fp, ECTF_ELF);
219				goto out;
220			}
221			bcopy(sdata->d_buf, ddata->d_buf, shdr.sh_size);
222			(void) strcpy((caddr_t)ddata->d_buf + shdr.sh_size,
223			    CTF_ELF_SCN_NAME);
224			ctfnameoff = (off_t)shdr.sh_size;
225			shdr.sh_size += seclen + 1;
226			ddata->d_size += seclen + 1;
227
228			if (nphdr != 0)
229				changing = 1;
230		}
231
232		if (shdr.sh_type == SHT_SYMTAB && shdr.sh_entsize != 0) {
233			int nsym = shdr.sh_size / shdr.sh_entsize;
234
235			symtab_idx = secxlate[srcidx];
236
237			symdatasz = shdr.sh_size;
238			ddata->d_buf = symdatabuf = ctf_alloc(symdatasz);
239			if (ddata->d_buf == NULL) {
240				ret = ctf_set_errno(fp, ECTF_ELF);
241				goto out;
242			}
243			(void) bcopy(sdata->d_buf, ddata->d_buf, shdr.sh_size);
244
245			for (i = 0; i < nsym; i++) {
246				GElf_Sym sym;
247				short newscn;
248
249				(void) gelf_getsym(ddata, i, &sym);
250
251				if (sym.st_shndx >= SHN_LORESERVE)
252					continue;
253
254				if ((newscn = secxlate[sym.st_shndx]) !=
255				    sym.st_shndx) {
256					sym.st_shndx =
257					    (newscn == -1 ? 1 : newscn);
258
259					if (gelf_update_sym(ddata, i, &sym) ==
260					    0) {
261						ret = ctf_set_errno(fp,
262						    ECTF_ELF);
263						goto out;
264					}
265				}
266			}
267		}
268
269		if (gelf_update_shdr(dscn, &shdr) == 0) {
270			ret = ctf_set_errno(fp, ECTF_ELF);
271			goto out;
272		}
273
274		new_offset = (off_t)shdr.sh_offset;
275		if (shdr.sh_type != SHT_NOBITS)
276			new_offset += shdr.sh_size;
277	}
278
279	if (symtab_idx == -1) {
280		ret = ctf_set_errno(fp, ECTF_ELF);
281		goto out;
282	}
283
284	/* Add the ctf section */
285	if ((dscn = elf_newscn(dst)) == NULL) {
286		ret = ctf_set_errno(fp, ECTF_ELF);
287		goto out;
288	}
289	if (gelf_getshdr(dscn, &shdr) == NULL) {
290		ret = ctf_set_errno(fp, ECTF_ELF);
291		goto out;
292	}
293	shdr.sh_name = ctfnameoff;
294	shdr.sh_type = SHT_PROGBITS;
295	shdr.sh_size = fp->ctf_size;
296	shdr.sh_link = symtab_idx;
297	shdr.sh_addralign = 4;
298	if (changing && nphdr != 0) {
299		pad = new_offset % shdr.sh_addralign;
300
301		if (pad)
302			new_offset += shdr.sh_addralign - pad;
303
304		shdr.sh_offset = new_offset;
305		new_offset += shdr.sh_size;
306	}
307
308	if ((ddata = elf_newdata(dscn)) == NULL) {
309		ret = ctf_set_errno(fp, ECTF_ELF);
310		goto out;
311	}
312
313	if (compress != 0) {
314		int err;
315
316		if (ctf_zopen(&err) == NULL) {
317			ret = ctf_set_errno(fp, err);
318			goto out;
319		}
320
321		if ((err = ctf_compress(fp, &cdata, &asize, &elfsize)) != 0) {
322			ret = ctf_set_errno(fp, err);
323			goto out;
324		}
325		ddata->d_buf = cdata;
326		ddata->d_size = elfsize;
327	} else {
328		ddata->d_buf = (void *)fp->ctf_base;
329		ddata->d_size = fp->ctf_size;
330	}
331	ddata->d_align = shdr.sh_addralign;
332
333	if (gelf_update_shdr(dscn, &shdr) == 0) {
334		ret = ctf_set_errno(fp, ECTF_ELF);
335		goto out;
336	}
337
338	/* update the section header location */
339	if (nphdr != 0) {
340		size_t align = gelf_fsize(dst, ELF_T_ADDR, 1, EV_CURRENT);
341		size_t r = new_offset % align;
342
343		if (r)
344			new_offset += align - r;
345
346		dehdr.e_shoff = new_offset;
347	}
348
349	/* commit to disk */
350	if (sehdr.e_shstrndx == SHN_XINDEX)
351		dehdr.e_shstrndx = SHN_XINDEX;
352	else
353		dehdr.e_shstrndx = secxlate[sehdr.e_shstrndx];
354	if (gelf_update_ehdr(dst, &dehdr) == 0) {
355		ret = ctf_set_errno(fp, ECTF_ELF);
356		goto out;
357	}
358	if (elf_update(dst, ELF_C_WRITE) < 0) {
359		ret = ctf_set_errno(fp, ECTF_ELF);
360		goto out;
361	}
362
363	ret = 0;
364
365out:
366	if (strdatabuf != NULL)
367		ctf_free(strdatabuf, strdatasz);
368	if (symdatabuf != NULL)
369		ctf_free(symdatabuf, symdatasz);
370	if (cdata != NULL)
371		ctf_data_free(cdata, fp->ctf_size);
372	if (secxlate != NULL)
373		ctf_free(secxlate, sizeof (int) * nshdr);
374
375	return (ret);
376}
377
378int
379ctf_elffdwrite(ctf_file_t *fp, int ifd, int ofd, int flags)
380{
381	int ret;
382	Elf *ielf, *oelf;
383
384	(void) elf_version(EV_CURRENT);
385	if ((ielf = elf_begin(ifd, ELF_C_READ, NULL)) == NULL)
386		return (ctf_set_errno(fp, ECTF_ELF));
387
388	if ((oelf = elf_begin(ofd, ELF_C_WRITE, NULL)) == NULL)
389		return (ctf_set_errno(fp, ECTF_ELF));
390
391	ret = ctf_write_elf(fp, ielf, oelf, flags);
392
393	(void) elf_end(ielf);
394	(void) elf_end(oelf);
395
396	return (ret);
397}
398
399int
400ctf_elfwrite(ctf_file_t *fp, const char *input, const char *output, int flags)
401{
402	struct stat st;
403	int ifd, ofd, ret;
404
405	if ((ifd = open(input, O_RDONLY)) < 0)
406		return (ctf_set_errno(fp, errno));
407
408	if (fstat(ifd, &st) < 0)
409		return (ctf_set_errno(fp, errno));
410
411	if ((ofd = open(output, O_RDWR | O_CREAT | O_TRUNC, st.st_mode)) < 0)
412		return (ctf_set_errno(fp, errno));
413
414	ret = ctf_elffdwrite(fp, ifd, ofd, flags);
415
416	if (close(ifd) != 0 && ret == 0)
417		ret = ctf_set_errno(fp, errno);
418	if (close(ofd) != 0 && ret == 0)
419		ret = ctf_set_errno(fp, errno);
420
421	return (ret);
422}
423