xref: /illumos-gate/usr/src/lib/libctf/common/ctf_elfwrite.c (revision bc1f688b4872ace323eaddbb1a6365d054e7bf56)
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 
43 static int
44 ctf_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)) == NULL) {
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) == NULL) {
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) == NULL) {
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) == NULL) {
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 
365 out:
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 
378 int
379 ctf_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 
399 int
400 ctf_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