1/*- 2 * SPDX-License-Identifier: BSD-4-Clause 3 * 4 * Copyright (c) 2000, Boris Popov 5 * Copyright (c) 1998-2000 Doug Rabson 6 * Copyright (c) 2004 Peter Wemm 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by Boris Popov. 20 * 4. Neither the name of the author nor the names of any co-contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37#include <sys/param.h> 38 39#include <err.h> 40#include <errno.h> 41#include <gelf.h> 42#include <stdio.h> 43#include <stdlib.h> 44#include <string.h> 45 46#include "ef.h" 47 48typedef struct { 49 GElf_Addr addr; 50 GElf_Off offset; 51 GElf_Off size; 52 int flags; 53 int sec; /* Original section */ 54 char *name; 55} Elf_progent; 56 57typedef struct { 58 GElf_Rel *rel; 59 long nrel; 60 int sec; 61} Elf_relent; 62 63typedef struct { 64 GElf_Rela *rela; 65 long nrela; 66 int sec; 67} Elf_relaent; 68 69struct ef_file { 70 char *ef_name; 71 struct elf_file *ef_efile; 72 73 Elf_progent *progtab; 74 int nprogtab; 75 76 Elf_relaent *relatab; 77 int nrela; 78 79 Elf_relent *reltab; 80 int nrel; 81 82 GElf_Sym *ddbsymtab; /* The symbol table we are using */ 83 size_t ddbsymcnt; /* Number of symbols */ 84 caddr_t ddbstrtab; /* String table */ 85 long ddbstrcnt; /* number of bytes in string table */ 86 87 caddr_t shstrtab; /* Section name string table */ 88 long shstrcnt; /* number of bytes in string table */ 89 90 int ef_verbose; 91}; 92 93static void ef_obj_close(elf_file_t ef); 94 95static int ef_obj_seg_read_rel(elf_file_t ef, GElf_Addr address, 96 size_t len, void *dest); 97static int ef_obj_seg_read_string(elf_file_t ef, GElf_Addr address, 98 size_t len, char *dest); 99 100static GElf_Addr ef_obj_symaddr(elf_file_t ef, GElf_Size symidx); 101static int ef_obj_lookup_set(elf_file_t ef, const char *name, 102 GElf_Addr *startp, GElf_Addr *stopp, long *countp); 103static int ef_obj_lookup_symbol(elf_file_t ef, const char *name, 104 GElf_Sym **sym); 105 106static struct elf_file_ops ef_obj_file_ops = { 107 .close = ef_obj_close, 108 .seg_read_rel = ef_obj_seg_read_rel, 109 .seg_read_string = ef_obj_seg_read_string, 110 .symaddr = ef_obj_symaddr, 111 .lookup_set = ef_obj_lookup_set, 112}; 113 114static GElf_Off 115ef_obj_get_offset(elf_file_t ef, GElf_Addr addr) 116{ 117 Elf_progent *pt; 118 int i; 119 120 for (i = 0; i < ef->nprogtab; i++) { 121 pt = &ef->progtab[i]; 122 if (pt->offset == (GElf_Off)-1) 123 continue; 124 if (addr >= pt->addr && addr < pt->addr + pt->size) 125 return (pt->offset + (addr - pt->addr)); 126 } 127 return (0); 128} 129 130static int 131ef_obj_lookup_symbol(elf_file_t ef, const char *name, GElf_Sym **sym) 132{ 133 GElf_Sym *symp; 134 const char *strp; 135 int i; 136 137 for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { 138 strp = ef->ddbstrtab + symp->st_name; 139 if (symp->st_shndx != SHN_UNDEF && strcmp(name, strp) == 0) { 140 *sym = symp; 141 return (0); 142 } 143 } 144 return (ENOENT); 145} 146 147static int 148ef_obj_lookup_set(elf_file_t ef, const char *name, GElf_Addr *startp, 149 GElf_Addr *stopp, long *countp) 150{ 151 int i; 152 153 for (i = 0; i < ef->nprogtab; i++) { 154 if ((strncmp(ef->progtab[i].name, "set_", 4) == 0) && 155 strcmp(ef->progtab[i].name + 4, name) == 0) { 156 *startp = ef->progtab[i].addr; 157 *stopp = ef->progtab[i].addr + ef->progtab[i].size; 158 *countp = (*stopp - *startp) / 159 elf_pointer_size(ef->ef_efile); 160 return (0); 161 } 162 } 163 return (ESRCH); 164} 165 166static GElf_Addr 167ef_obj_symaddr(elf_file_t ef, GElf_Size symidx) 168{ 169 const GElf_Sym *sym; 170 171 if (symidx >= ef->ddbsymcnt) 172 return (0); 173 sym = ef->ddbsymtab + symidx; 174 175 if (sym->st_shndx != SHN_UNDEF) 176 return (sym->st_value); 177 return (0); 178} 179 180static int 181ef_obj_seg_read_rel(elf_file_t ef, GElf_Addr address, size_t len, void *dest) 182{ 183 GElf_Off secofs; 184 GElf_Rel *r; 185 GElf_Rela *a; 186 GElf_Addr secbase, dataoff; 187 int error, i, sec; 188 189 /* Find out which section contains the data. */ 190 sec = -1; 191 for (i = 0; i < ef->nprogtab; i++) { 192 if (address < ef->progtab[i].addr) 193 continue; 194 195 dataoff = address - ef->progtab[i].addr; 196 if (dataoff + len > ef->progtab[i].size) 197 continue; 198 199 sec = ef->progtab[i].sec; 200 secbase = ef->progtab[i].addr; 201 secofs = ef->progtab[i].offset; 202 break; 203 } 204 205 if (sec == -1) { 206 if (ef->ef_verbose) 207 warnx("ef_obj_seg_read_rel(%s): bad address (%jx)", 208 ef->ef_name, (uintmax_t)address); 209 return (EFAULT); 210 } 211 212 if (secofs == (GElf_Off)-1) { 213 memset(dest, 0, len); 214 } else { 215 error = elf_read_raw_data(ef->ef_efile, secofs + dataoff, dest, 216 len); 217 if (error != 0) 218 return (error); 219 } 220 221 /* Now do the relocations. */ 222 for (i = 0; i < ef->nrel; i++) { 223 if (ef->reltab[i].sec != sec) 224 continue; 225 for (r = ef->reltab[i].rel; 226 r < &ef->reltab[i].rel[ef->reltab[i].nrel]; r++) { 227 error = elf_reloc(ef->ef_efile, r, ELF_T_REL, secbase, 228 address, len, dest); 229 if (error != 0) 230 return (error); 231 } 232 } 233 for (i = 0; i < ef->nrela; i++) { 234 if (ef->relatab[i].sec != sec) 235 continue; 236 for (a = ef->relatab[i].rela; 237 a < &ef->relatab[i].rela[ef->relatab[i].nrela]; a++) { 238 error = elf_reloc(ef->ef_efile, a, ELF_T_RELA, secbase, 239 address, len, dest); 240 if (error != 0) 241 return (error); 242 } 243 } 244 return (0); 245} 246 247static int 248ef_obj_seg_read_string(elf_file_t ef, GElf_Addr address, size_t len, char *dest) 249{ 250 GElf_Off ofs; 251 252 ofs = ef_obj_get_offset(ef, address); 253 if (ofs == 0) { 254 if (ef->ef_verbose) 255 warnx("ef_obj_seg_read_string(%s): bad address (%jx)", 256 ef->ef_name, (uintmax_t)address); 257 return (EFAULT); 258 } 259 260 return (elf_read_raw_string(ef->ef_efile, ofs, dest, len)); 261} 262 263int 264ef_obj_open(struct elf_file *efile, int verbose) 265{ 266 elf_file_t ef; 267 GElf_Ehdr *hdr; 268 GElf_Shdr *shdr; 269 GElf_Sym *es; 270 GElf_Addr mapbase; 271 size_t i, nshdr; 272 int error, pb, ra, rl; 273 int j, nsym, symstrindex, symtabindex; 274 275 hdr = &efile->ef_hdr; 276 if (hdr->e_type != ET_REL || hdr->e_shnum == 0 || hdr->e_shoff == 0 || 277 hdr->e_shentsize != elf_object_size(efile, ELF_T_SHDR)) 278 return (EFTYPE); 279 280 ef = calloc(1, sizeof(*ef)); 281 if (ef == NULL) 282 return (errno); 283 284 efile->ef_ef = ef; 285 efile->ef_ops = &ef_obj_file_ops; 286 287 ef->ef_verbose = verbose; 288 ef->ef_name = strdup(efile->ef_filename); 289 ef->ef_efile = efile; 290 291 error = elf_read_shdrs(efile, &nshdr, &shdr); 292 if (error != 0) { 293 shdr = NULL; 294 goto out; 295 } 296 297 /* Scan the section headers for information and table sizing. */ 298 nsym = 0; 299 symtabindex = -1; 300 symstrindex = -1; 301 for (i = 0; i < nshdr; i++) { 302 switch (shdr[i].sh_type) { 303 case SHT_PROGBITS: 304 case SHT_NOBITS: 305 ef->nprogtab++; 306 break; 307 case SHT_SYMTAB: 308 nsym++; 309 symtabindex = i; 310 symstrindex = shdr[i].sh_link; 311 break; 312 case SHT_REL: 313 ef->nrel++; 314 break; 315 case SHT_RELA: 316 ef->nrela++; 317 break; 318 case SHT_STRTAB: 319 break; 320 } 321 } 322 323 if (ef->nprogtab == 0) { 324 warnx("%s: file has no contents", ef->ef_name); 325 goto out; 326 } 327 if (nsym != 1) { 328 warnx("%s: file has no valid symbol table", ef->ef_name); 329 goto out; 330 } 331 if (symstrindex < 0 || symstrindex > nshdr || 332 shdr[symstrindex].sh_type != SHT_STRTAB) { 333 warnx("%s: file has invalid symbol strings", ef->ef_name); 334 goto out; 335 } 336 337 /* Allocate space for tracking the load chunks */ 338 if (ef->nprogtab != 0) 339 ef->progtab = calloc(ef->nprogtab, sizeof(*ef->progtab)); 340 if (ef->nrel != 0) 341 ef->reltab = calloc(ef->nrel, sizeof(*ef->reltab)); 342 if (ef->nrela != 0) 343 ef->relatab = calloc(ef->nrela, sizeof(*ef->relatab)); 344 if ((ef->nprogtab != 0 && ef->progtab == NULL) || 345 (ef->nrel != 0 && ef->reltab == NULL) || 346 (ef->nrela != 0 && ef->relatab == NULL)) { 347 printf("malloc failed\n"); 348 error = ENOMEM; 349 goto out; 350 } 351 352 if (elf_read_symbols(efile, symtabindex, &ef->ddbsymcnt, 353 &ef->ddbsymtab) != 0) { 354 printf("elf_read_symbols failed\n"); 355 goto out; 356 } 357 358 if (elf_read_string_table(efile, &shdr[symstrindex], &ef->ddbstrcnt, 359 &ef->ddbstrtab) != 0) { 360 printf("elf_read_string_table failed\n"); 361 goto out; 362 } 363 364 /* Do we have a string table for the section names? */ 365 if (hdr->e_shstrndx != 0 && 366 shdr[hdr->e_shstrndx].sh_type == SHT_STRTAB) { 367 if (elf_read_string_table(efile, &shdr[hdr->e_shstrndx], 368 &ef->shstrcnt, &ef->shstrtab) != 0) { 369 printf("elf_read_string_table failed\n"); 370 goto out; 371 } 372 } 373 374 /* 375 * Now allocate address space for code/data(progbits) and 376 * bss(nobits) and allocate space for and load relocs. 377 */ 378 pb = 0; 379 rl = 0; 380 ra = 0; 381 mapbase = 0; 382 for (i = 0; i < nshdr; i++) { 383 switch (shdr[i].sh_type) { 384 case SHT_PROGBITS: 385 case SHT_NOBITS: 386 mapbase = roundup2(mapbase, shdr[i].sh_addralign); 387 ef->progtab[pb].addr = mapbase; 388 if (shdr[i].sh_type == SHT_PROGBITS) { 389 ef->progtab[pb].name = "<<PROGBITS>>"; 390 ef->progtab[pb].offset = shdr[i].sh_offset; 391 } else { 392 ef->progtab[pb].name = "<<NOBITS>>"; 393 ef->progtab[pb].offset = (GElf_Off)-1; 394 } 395 ef->progtab[pb].size = shdr[i].sh_size; 396 ef->progtab[pb].sec = i; 397 if (ef->shstrtab && shdr[i].sh_name != 0) 398 ef->progtab[pb].name = 399 ef->shstrtab + shdr[i].sh_name; 400 401 /* Update all symbol values with the offset. */ 402 for (j = 0; j < ef->ddbsymcnt; j++) { 403 es = &ef->ddbsymtab[j]; 404 if (es->st_shndx != i) 405 continue; 406 es->st_value += ef->progtab[pb].addr; 407 } 408 mapbase += shdr[i].sh_size; 409 pb++; 410 break; 411 case SHT_REL: 412 ef->reltab[rl].sec = shdr[i].sh_info; 413 if (elf_read_rel(efile, i, &ef->reltab[rl].nrel, 414 &ef->reltab[rl].rel) != 0) { 415 printf("elf_read_rel failed\n"); 416 goto out; 417 } 418 rl++; 419 break; 420 case SHT_RELA: 421 ef->relatab[ra].sec = shdr[i].sh_info; 422 if (elf_read_rela(efile, i, &ef->relatab[ra].nrela, 423 &ef->relatab[ra].rela) != 0) { 424 printf("elf_read_rela failed\n"); 425 goto out; 426 } 427 ra++; 428 break; 429 } 430 } 431 error = 0; 432out: 433 free(shdr); 434 if (error != 0) 435 ef_obj_close(ef); 436 return (error); 437} 438 439static void 440ef_obj_close(elf_file_t ef) 441{ 442 int i; 443 444 if (ef->ef_name) 445 free(ef->ef_name); 446 if (ef->nprogtab != 0) 447 free(ef->progtab); 448 if (ef->nrel != 0) { 449 for (i = 0; i < ef->nrel; i++) 450 if (ef->reltab[i].rel != NULL) 451 free(ef->reltab[i].rel); 452 free(ef->reltab); 453 } 454 if (ef->nrela != 0) { 455 for (i = 0; i < ef->nrela; i++) 456 if (ef->relatab[i].rela != NULL) 457 free(ef->relatab[i].rela); 458 free(ef->relatab); 459 } 460 if (ef->ddbsymtab != NULL) 461 free(ef->ddbsymtab); 462 if (ef->ddbstrtab != NULL) 463 free(ef->ddbstrtab); 464 if (ef->shstrtab != NULL) 465 free(ef->shstrtab); 466 ef->ef_efile->ef_ops = NULL; 467 ef->ef_efile->ef_ef = NULL; 468 free(ef); 469} 470