dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libctf / common / ctf_elfwrite.c
blob377eb637368995abac5a586349fa6e4ff9e3e7cd
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 * Copyright (c) 2015, Joyent, Inc.
30 * Routines for writing ctf data to elf files, originally from the ctf tools.
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>
43 static int
44 ctf_write_elf(ctf_file_t *fp, Elf *src, Elf *dst, int flags)
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;
63 void *cdata = NULL;
64 size_t elfsize, asize;
66 if ((flags & ~(CTF_ELFWRITE_F_COMPRESS)) != 0) {
67 ret = ctf_set_errno(fp, EINVAL);
68 goto out;
71 if (gelf_newehdr(dst, gelf_getclass(src)) == 0) {
72 ret = ctf_set_errno(fp, ECTF_ELF);
73 goto out;
75 if (gelf_getehdr(src, &sehdr) == NULL) {
76 ret = ctf_set_errno(fp, ECTF_ELF);
77 goto out;
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;
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.
90 if (elf_getphdrnum(src, &nphdr) != 0) {
91 ret = ctf_set_errno(fp, ECTF_ELF);
92 goto out;
94 if (elf_getshdrnum(src, &nshdr) != 0) {
95 ret = ctf_set_errno(fp, ECTF_ELF);
96 goto out;
98 if (elf_getshdrstrndx(src, &strndx) != 0) {
99 ret = ctf_set_errno(fp, ECTF_ELF);
100 goto out;
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.
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;
116 for (i = 0; i < nphdr; i++) {
117 GElf_Phdr phdr;
119 if (gelf_getphdr(src, i, &phdr) == NULL) {
120 ret = ctf_set_errno(fp, ECTF_ELF);
121 goto out;
123 if (gelf_update_phdr(dst, i, &phdr) == 0) {
124 ret = ctf_set_errno(fp, ECTF_ELF);
125 goto out;
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;
136 if (gelf_getshdr(scn, &shdr) == NULL) {
137 ret = ctf_set_errno(fp, ECTF_ELF);
138 goto out;
140 sname = elf_strptr(src, strndx, shdr.sh_name);
141 if (sname == NULL) {
142 ret = ctf_set_errno(fp, ECTF_ELF);
143 goto out;
146 if (strcmp(sname, CTF_ELF_SCN_NAME) == 0) {
147 secxlate[srcidx] = -1;
148 } else {
149 secxlate[srcidx] = dstidx++;
150 curnmoff += strlen(sname) + 1;
153 new_offset = (off_t)dehdr.e_phoff;
156 for (srcidx = 1; srcidx < nshdr; srcidx++) {
157 char *sname;
159 sscn = elf_getscn(src, srcidx);
160 if (gelf_getshdr(sscn, &shdr) == NULL) {
161 ret = ctf_set_errno(fp, ECTF_ELF);
162 goto out;
165 if (secxlate[srcidx] == -1) {
166 changing = 1;
167 continue;
170 dscn = elf_newscn(dst);
171 if (dscn == NULL) {
172 ret = ctf_set_errno(fp, ECTF_ELF);
173 goto out;
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.
183 if (changing && nphdr != 0) {
184 pad = new_offset % shdr.sh_addralign;
186 if (pad != 0)
187 new_offset += shdr.sh_addralign - pad;
188 shdr.sh_offset = new_offset;
191 shdr.sh_link = secxlate[shdr.sh_link];
193 if (shdr.sh_type == SHT_REL || shdr.sh_type == SHT_RELA)
194 shdr.sh_info = secxlate[shdr.sh_info];
196 sname = elf_strptr(src, strndx, shdr.sh_name);
197 if (sname == NULL) {
198 ret = ctf_set_errno(fp, ECTF_ELF);
199 goto out;
201 if ((sdata = elf_getdata(sscn, NULL)) == NULL) {
202 ret = ctf_set_errno(fp, ECTF_ELF);
203 goto out;
205 if ((ddata = elf_newdata(dscn)) == NULL) {
206 ret = ctf_set_errno(fp, ECTF_ELF);
207 goto out;
209 bcopy(sdata, ddata, sizeof (Elf_Data));
211 if (srcidx == strndx) {
212 char seclen = strlen(CTF_ELF_SCN_NAME);
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;
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;
228 if (nphdr != 0)
229 changing = 1;
232 if (shdr.sh_type == SHT_SYMTAB && shdr.sh_entsize != 0) {
233 int nsym = shdr.sh_size / shdr.sh_entsize;
235 symtab_idx = secxlate[srcidx];
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;
243 (void) bcopy(sdata->d_buf, ddata->d_buf, shdr.sh_size);
245 for (i = 0; i < nsym; i++) {
246 GElf_Sym sym;
247 short newscn;
249 (void) gelf_getsym(ddata, i, &sym);
251 if (sym.st_shndx >= SHN_LORESERVE)
252 continue;
254 if ((newscn = secxlate[sym.st_shndx]) !=
255 sym.st_shndx) {
256 sym.st_shndx =
257 (newscn == -1 ? 1 : newscn);
259 if (gelf_update_sym(ddata, i, &sym) ==
260 0) {
261 ret = ctf_set_errno(fp,
262 ECTF_ELF);
263 goto out;
269 if (gelf_update_shdr(dscn, &shdr) == 0) {
270 ret = ctf_set_errno(fp, ECTF_ELF);
271 goto out;
274 new_offset = (off_t)shdr.sh_offset;
275 if (shdr.sh_type != SHT_NOBITS)
276 new_offset += shdr.sh_size;
279 if (symtab_idx == -1) {
280 ret = ctf_set_errno(fp, ECTF_ELF);
281 goto out;
284 /* Add the ctf section */
285 if ((dscn = elf_newscn(dst)) == NULL) {
286 ret = ctf_set_errno(fp, ECTF_ELF);
287 goto out;
289 if (gelf_getshdr(dscn, &shdr) == NULL) {
290 ret = ctf_set_errno(fp, ECTF_ELF);
291 goto out;
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;
301 if (pad)
302 new_offset += shdr.sh_addralign - pad;
304 shdr.sh_offset = new_offset;
305 new_offset += shdr.sh_size;
308 if ((ddata = elf_newdata(dscn)) == NULL) {
309 ret = ctf_set_errno(fp, ECTF_ELF);
310 goto out;
313 if (compress != 0) {
314 int err;
316 if (ctf_zopen(&err) == NULL) {
317 ret = ctf_set_errno(fp, err);
318 goto out;
321 if ((err = ctf_compress(fp, &cdata, &asize, &elfsize)) != 0) {
322 ret = ctf_set_errno(fp, err);
323 goto out;
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;
331 ddata->d_align = shdr.sh_addralign;
333 if (gelf_update_shdr(dscn, &shdr) == 0) {
334 ret = ctf_set_errno(fp, ECTF_ELF);
335 goto out;
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;
343 if (r)
344 new_offset += align - r;
346 dehdr.e_shoff = new_offset;
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;
358 if (elf_update(dst, ELF_C_WRITE) < 0) {
359 ret = ctf_set_errno(fp, ECTF_ELF);
360 goto out;
363 ret = 0;
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);
375 return (ret);
379 ctf_elffdwrite(ctf_file_t *fp, int ifd, int ofd, int flags)
381 int ret;
382 Elf *ielf, *oelf;
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));
388 if ((oelf = elf_begin(ofd, ELF_C_WRITE, NULL)) == NULL)
389 return (ctf_set_errno(fp, ECTF_ELF));
391 ret = ctf_write_elf(fp, ielf, oelf, flags);
393 (void) elf_end(ielf);
394 (void) elf_end(oelf);
396 return (ret);
400 ctf_elfwrite(ctf_file_t *fp, const char *input, const char *output, int flags)
402 struct stat st;
403 int ifd, ofd, ret;
405 if ((ifd = open(input, O_RDONLY)) < 0)
406 return (ctf_set_errno(fp, errno));
408 if (fstat(ifd, &st) < 0)
409 return (ctf_set_errno(fp, errno));
411 if ((ofd = open(output, O_RDWR | O_CREAT | O_TRUNC, st.st_mode)) < 0)
412 return (ctf_set_errno(fp, errno));
414 ret = ctf_elffdwrite(fp, ifd, ofd, flags);
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);
421 return (ret);