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
ctf_write_elf(ctf_file_t * fp,Elf * src,Elf * dst,int flags)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
ctf_elffdwrite(ctf_file_t * fp,int ifd,int ofd,int flags)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
ctf_elfwrite(ctf_file_t * fp,const char * input,const char * output,int flags)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