xref: /illumos-gate/usr/src/lib/libctf/common/ctf_elfwrite.c (revision e4caeab9084da2fca73539909c80a44baaf3fa67)
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)) == 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  
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