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