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 "kldelf.h" 47 48 typedef 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 57 typedef struct { 58 GElf_Rel *rel; 59 long nrel; 60 int sec; 61 } Elf_relent; 62 63 typedef struct { 64 GElf_Rela *rela; 65 long nrela; 66 int sec; 67 } Elf_relaent; 68 69 struct 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 93 static void ef_obj_close(elf_file_t ef); 94 95 static int ef_obj_seg_read_rel(elf_file_t ef, GElf_Addr address, 96 size_t len, void *dest); 97 static int ef_obj_seg_read_string(elf_file_t ef, GElf_Addr address, 98 size_t len, char *dest); 99 100 static GElf_Addr ef_obj_symaddr(elf_file_t ef, GElf_Size symidx); 101 static int ef_obj_lookup_set(elf_file_t ef, const char *name, 102 GElf_Addr *startp, GElf_Addr *stopp, long *countp); 103 static int ef_obj_lookup_symbol(elf_file_t ef, const char *name, 104 GElf_Sym **sym, bool see_local); 105 106 static 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 .lookup_symbol = ef_obj_lookup_symbol, 113 }; 114 115 static GElf_Off 116 ef_obj_get_offset(elf_file_t ef, GElf_Addr addr) 117 { 118 Elf_progent *pt; 119 int i; 120 121 for (i = 0; i < ef->nprogtab; i++) { 122 pt = &ef->progtab[i]; 123 if (pt->offset == (GElf_Off)-1) 124 continue; 125 if (addr >= pt->addr && addr < pt->addr + pt->size) 126 return (pt->offset + (addr - pt->addr)); 127 } 128 return (0); 129 } 130 131 static int 132 ef_obj_lookup_symbol(elf_file_t ef, const char *name, GElf_Sym **sym, 133 bool see_local) 134 { 135 GElf_Sym *symp; 136 const char *strp; 137 int i; 138 139 for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { 140 strp = ef->ddbstrtab + symp->st_name; 141 if (symp->st_shndx != SHN_UNDEF && strcmp(name, strp) == 0) { 142 if (see_local || 143 GELF_ST_BIND(symp->st_info) != STB_LOCAL) { 144 *sym = symp; 145 return (0); 146 } 147 } 148 } 149 return (ENOENT); 150 } 151 152 static int 153 ef_obj_lookup_set(elf_file_t ef, const char *name, GElf_Addr *startp, 154 GElf_Addr *stopp, long *countp) 155 { 156 int i; 157 158 for (i = 0; i < ef->nprogtab; i++) { 159 if ((strncmp(ef->progtab[i].name, "set_", 4) == 0) && 160 strcmp(ef->progtab[i].name + 4, name) == 0) { 161 *startp = ef->progtab[i].addr; 162 *stopp = ef->progtab[i].addr + ef->progtab[i].size; 163 *countp = (*stopp - *startp) / 164 elf_pointer_size(ef->ef_efile); 165 return (0); 166 } 167 } 168 return (ESRCH); 169 } 170 171 static GElf_Addr 172 ef_obj_symaddr(elf_file_t ef, GElf_Size symidx) 173 { 174 const GElf_Sym *sym; 175 176 if (symidx >= ef->ddbsymcnt) 177 return (0); 178 sym = ef->ddbsymtab + symidx; 179 180 if (sym->st_shndx != SHN_UNDEF) 181 return (sym->st_value); 182 return (0); 183 } 184 185 static int 186 ef_obj_seg_read_rel(elf_file_t ef, GElf_Addr address, size_t len, void *dest) 187 { 188 GElf_Off secofs; 189 GElf_Rel *r; 190 GElf_Rela *a; 191 GElf_Addr secbase, dataoff; 192 int error, i, sec; 193 194 /* Find out which section contains the data. */ 195 sec = -1; 196 for (i = 0; i < ef->nprogtab; i++) { 197 if (address < ef->progtab[i].addr) 198 continue; 199 200 dataoff = address - ef->progtab[i].addr; 201 if (dataoff + len > ef->progtab[i].size) 202 continue; 203 204 sec = ef->progtab[i].sec; 205 secbase = ef->progtab[i].addr; 206 secofs = ef->progtab[i].offset; 207 break; 208 } 209 210 if (sec == -1) { 211 if (ef->ef_verbose) 212 warnx("ef_obj_seg_read_rel(%s): bad address (%jx)", 213 ef->ef_name, (uintmax_t)address); 214 return (EFAULT); 215 } 216 217 if (secofs == (GElf_Off)-1) { 218 memset(dest, 0, len); 219 } else { 220 error = elf_read_raw_data(ef->ef_efile, secofs + dataoff, dest, 221 len); 222 if (error != 0) 223 return (error); 224 } 225 226 /* Now do the relocations. */ 227 for (i = 0; i < ef->nrel; i++) { 228 if (ef->reltab[i].sec != sec) 229 continue; 230 for (r = ef->reltab[i].rel; 231 r < &ef->reltab[i].rel[ef->reltab[i].nrel]; r++) { 232 error = elf_reloc(ef->ef_efile, r, ELF_T_REL, secbase, 233 address, len, dest); 234 if (error != 0) 235 return (error); 236 } 237 } 238 for (i = 0; i < ef->nrela; i++) { 239 if (ef->relatab[i].sec != sec) 240 continue; 241 for (a = ef->relatab[i].rela; 242 a < &ef->relatab[i].rela[ef->relatab[i].nrela]; a++) { 243 error = elf_reloc(ef->ef_efile, a, ELF_T_RELA, secbase, 244 address, len, dest); 245 if (error != 0) 246 return (error); 247 } 248 } 249 return (0); 250 } 251 252 static int 253 ef_obj_seg_read_string(elf_file_t ef, GElf_Addr address, size_t len, char *dest) 254 { 255 GElf_Off ofs; 256 257 ofs = ef_obj_get_offset(ef, address); 258 if (ofs == 0) { 259 if (ef->ef_verbose) 260 warnx("ef_obj_seg_read_string(%s): bad address (%jx)", 261 ef->ef_name, (uintmax_t)address); 262 return (EFAULT); 263 } 264 265 return (elf_read_raw_string(ef->ef_efile, ofs, dest, len)); 266 } 267 268 int 269 ef_obj_open(struct elf_file *efile, int verbose) 270 { 271 elf_file_t ef; 272 GElf_Ehdr *hdr; 273 GElf_Shdr *shdr; 274 GElf_Sym *es; 275 GElf_Addr mapbase; 276 size_t i, nshdr; 277 int error, pb, ra, rl; 278 int j, nsym, symstrindex, symtabindex; 279 280 hdr = &efile->ef_hdr; 281 if (hdr->e_type != ET_REL || hdr->e_shnum == 0 || hdr->e_shoff == 0 || 282 hdr->e_shentsize != elf_object_size(efile, ELF_T_SHDR)) 283 return (EFTYPE); 284 285 ef = calloc(1, sizeof(*ef)); 286 if (ef == NULL) 287 return (errno); 288 289 efile->ef_ef = ef; 290 efile->ef_ops = &ef_obj_file_ops; 291 292 ef->ef_verbose = verbose; 293 ef->ef_name = strdup(efile->ef_filename); 294 ef->ef_efile = efile; 295 296 error = elf_read_shdrs(efile, &nshdr, &shdr); 297 if (error != 0) { 298 shdr = NULL; 299 goto out; 300 } 301 302 /* Scan the section headers for information and table sizing. */ 303 nsym = 0; 304 symtabindex = -1; 305 symstrindex = -1; 306 for (i = 0; i < nshdr; i++) { 307 switch (shdr[i].sh_type) { 308 case SHT_PROGBITS: 309 case SHT_NOBITS: 310 ef->nprogtab++; 311 break; 312 case SHT_SYMTAB: 313 nsym++; 314 symtabindex = i; 315 symstrindex = shdr[i].sh_link; 316 break; 317 case SHT_REL: 318 ef->nrel++; 319 break; 320 case SHT_RELA: 321 ef->nrela++; 322 break; 323 case SHT_STRTAB: 324 break; 325 } 326 } 327 328 if (ef->nprogtab == 0) { 329 warnx("%s: file has no contents", ef->ef_name); 330 goto out; 331 } 332 if (nsym != 1) { 333 warnx("%s: file has no valid symbol table", ef->ef_name); 334 goto out; 335 } 336 if (symstrindex < 0 || symstrindex > nshdr || 337 shdr[symstrindex].sh_type != SHT_STRTAB) { 338 warnx("%s: file has invalid symbol strings", ef->ef_name); 339 goto out; 340 } 341 342 /* Allocate space for tracking the load chunks */ 343 if (ef->nprogtab != 0) 344 ef->progtab = calloc(ef->nprogtab, sizeof(*ef->progtab)); 345 if (ef->nrel != 0) 346 ef->reltab = calloc(ef->nrel, sizeof(*ef->reltab)); 347 if (ef->nrela != 0) 348 ef->relatab = calloc(ef->nrela, sizeof(*ef->relatab)); 349 if ((ef->nprogtab != 0 && ef->progtab == NULL) || 350 (ef->nrel != 0 && ef->reltab == NULL) || 351 (ef->nrela != 0 && ef->relatab == NULL)) { 352 warnx("malloc failed"); 353 error = ENOMEM; 354 goto out; 355 } 356 357 if (elf_read_symbols(efile, symtabindex, &ef->ddbsymcnt, 358 &ef->ddbsymtab) != 0) { 359 warnx("elf_read_symbols failed"); 360 goto out; 361 } 362 363 if (elf_read_string_table(efile, &shdr[symstrindex], &ef->ddbstrcnt, 364 &ef->ddbstrtab) != 0) { 365 warnx("elf_read_string_table failed"); 366 goto out; 367 } 368 369 /* Do we have a string table for the section names? */ 370 if (hdr->e_shstrndx != 0 && 371 shdr[hdr->e_shstrndx].sh_type == SHT_STRTAB) { 372 if (elf_read_string_table(efile, &shdr[hdr->e_shstrndx], 373 &ef->shstrcnt, &ef->shstrtab) != 0) { 374 warnx("elf_read_string_table failed"); 375 goto out; 376 } 377 } 378 379 /* 380 * Now allocate address space for code/data(progbits) and 381 * bss(nobits) and allocate space for and load relocs. 382 */ 383 pb = 0; 384 rl = 0; 385 ra = 0; 386 mapbase = 0; 387 for (i = 0; i < nshdr; i++) { 388 switch (shdr[i].sh_type) { 389 case SHT_PROGBITS: 390 case SHT_NOBITS: 391 mapbase = roundup2(mapbase, shdr[i].sh_addralign); 392 ef->progtab[pb].addr = mapbase; 393 if (shdr[i].sh_type == SHT_PROGBITS) { 394 ef->progtab[pb].name = "<<PROGBITS>>"; 395 ef->progtab[pb].offset = shdr[i].sh_offset; 396 } else { 397 ef->progtab[pb].name = "<<NOBITS>>"; 398 ef->progtab[pb].offset = (GElf_Off)-1; 399 } 400 ef->progtab[pb].size = shdr[i].sh_size; 401 ef->progtab[pb].sec = i; 402 if (ef->shstrtab && shdr[i].sh_name != 0) 403 ef->progtab[pb].name = 404 ef->shstrtab + shdr[i].sh_name; 405 406 /* Update all symbol values with the offset. */ 407 for (j = 0; j < ef->ddbsymcnt; j++) { 408 es = &ef->ddbsymtab[j]; 409 if (es->st_shndx != i) 410 continue; 411 es->st_value += ef->progtab[pb].addr; 412 } 413 mapbase += shdr[i].sh_size; 414 pb++; 415 break; 416 case SHT_REL: 417 ef->reltab[rl].sec = shdr[i].sh_info; 418 if (elf_read_rel(efile, i, &ef->reltab[rl].nrel, 419 &ef->reltab[rl].rel) != 0) { 420 warnx("elf_read_rel failed"); 421 goto out; 422 } 423 rl++; 424 break; 425 case SHT_RELA: 426 ef->relatab[ra].sec = shdr[i].sh_info; 427 if (elf_read_rela(efile, i, &ef->relatab[ra].nrela, 428 &ef->relatab[ra].rela) != 0) { 429 warnx("elf_read_rela failed"); 430 goto out; 431 } 432 ra++; 433 break; 434 } 435 } 436 error = 0; 437 out: 438 free(shdr); 439 if (error != 0) 440 ef_obj_close(ef); 441 return (error); 442 } 443 444 static void 445 ef_obj_close(elf_file_t ef) 446 { 447 int i; 448 449 if (ef->ef_name) 450 free(ef->ef_name); 451 if (ef->nprogtab != 0) 452 free(ef->progtab); 453 if (ef->nrel != 0) { 454 for (i = 0; i < ef->nrel; i++) 455 if (ef->reltab[i].rel != NULL) 456 free(ef->reltab[i].rel); 457 free(ef->reltab); 458 } 459 if (ef->nrela != 0) { 460 for (i = 0; i < ef->nrela; i++) 461 if (ef->relatab[i].rela != NULL) 462 free(ef->relatab[i].rela); 463 free(ef->relatab); 464 } 465 if (ef->ddbsymtab != NULL) 466 free(ef->ddbsymtab); 467 if (ef->ddbstrtab != NULL) 468 free(ef->ddbstrtab); 469 if (ef->shstrtab != NULL) 470 free(ef->shstrtab); 471 ef->ef_efile->ef_ops = NULL; 472 ef->ef_efile->ef_ef = NULL; 473 free(ef); 474 } 475