root/tools/objtool/elf.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. find_section_by_name
  2. find_section_by_index
  3. find_symbol_by_index
  4. find_symbol_by_offset
  5. find_symbol_by_name
  6. find_symbol_containing
  7. find_rela_by_dest_range
  8. find_rela_by_dest
  9. find_containing_func
  10. read_sections
  11. read_symbols
  12. read_relas
  13. elf_read
  14. elf_create_section
  15. elf_create_rela_section
  16. elf_rebuild_rela_section
  17. elf_write
  18. elf_close

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * elf.c - ELF access library
   4  *
   5  * Adapted from kpatch (https://github.com/dynup/kpatch):
   6  * Copyright (C) 2013-2015 Josh Poimboeuf <jpoimboe@redhat.com>
   7  * Copyright (C) 2014 Seth Jennings <sjenning@redhat.com>
   8  */
   9 
  10 #include <sys/types.h>
  11 #include <sys/stat.h>
  12 #include <fcntl.h>
  13 #include <stdio.h>
  14 #include <stdlib.h>
  15 #include <string.h>
  16 #include <unistd.h>
  17 #include <errno.h>
  18 
  19 #include "elf.h"
  20 #include "warn.h"
  21 
  22 #define MAX_NAME_LEN 128
  23 
  24 struct section *find_section_by_name(struct elf *elf, const char *name)
  25 {
  26         struct section *sec;
  27 
  28         list_for_each_entry(sec, &elf->sections, list)
  29                 if (!strcmp(sec->name, name))
  30                         return sec;
  31 
  32         return NULL;
  33 }
  34 
  35 static struct section *find_section_by_index(struct elf *elf,
  36                                              unsigned int idx)
  37 {
  38         struct section *sec;
  39 
  40         list_for_each_entry(sec, &elf->sections, list)
  41                 if (sec->idx == idx)
  42                         return sec;
  43 
  44         return NULL;
  45 }
  46 
  47 static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx)
  48 {
  49         struct section *sec;
  50         struct symbol *sym;
  51 
  52         list_for_each_entry(sec, &elf->sections, list)
  53                 hash_for_each_possible(sec->symbol_hash, sym, hash, idx)
  54                         if (sym->idx == idx)
  55                                 return sym;
  56 
  57         return NULL;
  58 }
  59 
  60 struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset)
  61 {
  62         struct symbol *sym;
  63 
  64         list_for_each_entry(sym, &sec->symbol_list, list)
  65                 if (sym->type != STT_SECTION &&
  66                     sym->offset == offset)
  67                         return sym;
  68 
  69         return NULL;
  70 }
  71 
  72 struct symbol *find_symbol_by_name(struct elf *elf, const char *name)
  73 {
  74         struct section *sec;
  75         struct symbol *sym;
  76 
  77         list_for_each_entry(sec, &elf->sections, list)
  78                 list_for_each_entry(sym, &sec->symbol_list, list)
  79                         if (!strcmp(sym->name, name))
  80                                 return sym;
  81 
  82         return NULL;
  83 }
  84 
  85 struct symbol *find_symbol_containing(struct section *sec, unsigned long offset)
  86 {
  87         struct symbol *sym;
  88 
  89         list_for_each_entry(sym, &sec->symbol_list, list)
  90                 if (sym->type != STT_SECTION &&
  91                     offset >= sym->offset && offset < sym->offset + sym->len)
  92                         return sym;
  93 
  94         return NULL;
  95 }
  96 
  97 struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset,
  98                                      unsigned int len)
  99 {
 100         struct rela *rela;
 101         unsigned long o;
 102 
 103         if (!sec->rela)
 104                 return NULL;
 105 
 106         for (o = offset; o < offset + len; o++)
 107                 hash_for_each_possible(sec->rela->rela_hash, rela, hash, o)
 108                         if (rela->offset == o)
 109                                 return rela;
 110 
 111         return NULL;
 112 }
 113 
 114 struct rela *find_rela_by_dest(struct section *sec, unsigned long offset)
 115 {
 116         return find_rela_by_dest_range(sec, offset, 1);
 117 }
 118 
 119 struct symbol *find_containing_func(struct section *sec, unsigned long offset)
 120 {
 121         struct symbol *func;
 122 
 123         list_for_each_entry(func, &sec->symbol_list, list)
 124                 if (func->type == STT_FUNC && offset >= func->offset &&
 125                     offset < func->offset + func->len)
 126                         return func;
 127 
 128         return NULL;
 129 }
 130 
 131 static int read_sections(struct elf *elf)
 132 {
 133         Elf_Scn *s = NULL;
 134         struct section *sec;
 135         size_t shstrndx, sections_nr;
 136         int i;
 137 
 138         if (elf_getshdrnum(elf->elf, &sections_nr)) {
 139                 WARN_ELF("elf_getshdrnum");
 140                 return -1;
 141         }
 142 
 143         if (elf_getshdrstrndx(elf->elf, &shstrndx)) {
 144                 WARN_ELF("elf_getshdrstrndx");
 145                 return -1;
 146         }
 147 
 148         for (i = 0; i < sections_nr; i++) {
 149                 sec = malloc(sizeof(*sec));
 150                 if (!sec) {
 151                         perror("malloc");
 152                         return -1;
 153                 }
 154                 memset(sec, 0, sizeof(*sec));
 155 
 156                 INIT_LIST_HEAD(&sec->symbol_list);
 157                 INIT_LIST_HEAD(&sec->rela_list);
 158                 hash_init(sec->rela_hash);
 159                 hash_init(sec->symbol_hash);
 160 
 161                 list_add_tail(&sec->list, &elf->sections);
 162 
 163                 s = elf_getscn(elf->elf, i);
 164                 if (!s) {
 165                         WARN_ELF("elf_getscn");
 166                         return -1;
 167                 }
 168 
 169                 sec->idx = elf_ndxscn(s);
 170 
 171                 if (!gelf_getshdr(s, &sec->sh)) {
 172                         WARN_ELF("gelf_getshdr");
 173                         return -1;
 174                 }
 175 
 176                 sec->name = elf_strptr(elf->elf, shstrndx, sec->sh.sh_name);
 177                 if (!sec->name) {
 178                         WARN_ELF("elf_strptr");
 179                         return -1;
 180                 }
 181 
 182                 if (sec->sh.sh_size != 0) {
 183                         sec->data = elf_getdata(s, NULL);
 184                         if (!sec->data) {
 185                                 WARN_ELF("elf_getdata");
 186                                 return -1;
 187                         }
 188                         if (sec->data->d_off != 0 ||
 189                             sec->data->d_size != sec->sh.sh_size) {
 190                                 WARN("unexpected data attributes for %s",
 191                                      sec->name);
 192                                 return -1;
 193                         }
 194                 }
 195                 sec->len = sec->sh.sh_size;
 196         }
 197 
 198         /* sanity check, one more call to elf_nextscn() should return NULL */
 199         if (elf_nextscn(elf->elf, s)) {
 200                 WARN("section entry mismatch");
 201                 return -1;
 202         }
 203 
 204         return 0;
 205 }
 206 
 207 static int read_symbols(struct elf *elf)
 208 {
 209         struct section *symtab, *sec;
 210         struct symbol *sym, *pfunc, *alias;
 211         struct list_head *entry, *tmp;
 212         int symbols_nr, i;
 213         char *coldstr;
 214 
 215         symtab = find_section_by_name(elf, ".symtab");
 216         if (!symtab) {
 217                 WARN("missing symbol table");
 218                 return -1;
 219         }
 220 
 221         symbols_nr = symtab->sh.sh_size / symtab->sh.sh_entsize;
 222 
 223         for (i = 0; i < symbols_nr; i++) {
 224                 sym = malloc(sizeof(*sym));
 225                 if (!sym) {
 226                         perror("malloc");
 227                         return -1;
 228                 }
 229                 memset(sym, 0, sizeof(*sym));
 230                 alias = sym;
 231 
 232                 sym->idx = i;
 233 
 234                 if (!gelf_getsym(symtab->data, i, &sym->sym)) {
 235                         WARN_ELF("gelf_getsym");
 236                         goto err;
 237                 }
 238 
 239                 sym->name = elf_strptr(elf->elf, symtab->sh.sh_link,
 240                                        sym->sym.st_name);
 241                 if (!sym->name) {
 242                         WARN_ELF("elf_strptr");
 243                         goto err;
 244                 }
 245 
 246                 sym->type = GELF_ST_TYPE(sym->sym.st_info);
 247                 sym->bind = GELF_ST_BIND(sym->sym.st_info);
 248 
 249                 if (sym->sym.st_shndx > SHN_UNDEF &&
 250                     sym->sym.st_shndx < SHN_LORESERVE) {
 251                         sym->sec = find_section_by_index(elf,
 252                                                          sym->sym.st_shndx);
 253                         if (!sym->sec) {
 254                                 WARN("couldn't find section for symbol %s",
 255                                      sym->name);
 256                                 goto err;
 257                         }
 258                         if (sym->type == STT_SECTION) {
 259                                 sym->name = sym->sec->name;
 260                                 sym->sec->sym = sym;
 261                         }
 262                 } else
 263                         sym->sec = find_section_by_index(elf, 0);
 264 
 265                 sym->offset = sym->sym.st_value;
 266                 sym->len = sym->sym.st_size;
 267 
 268                 /* sorted insert into a per-section list */
 269                 entry = &sym->sec->symbol_list;
 270                 list_for_each_prev(tmp, &sym->sec->symbol_list) {
 271                         struct symbol *s;
 272 
 273                         s = list_entry(tmp, struct symbol, list);
 274 
 275                         if (sym->offset > s->offset) {
 276                                 entry = tmp;
 277                                 break;
 278                         }
 279 
 280                         if (sym->offset == s->offset) {
 281                                 if (sym->len && sym->len == s->len && alias == sym)
 282                                         alias = s;
 283 
 284                                 if (sym->len >= s->len) {
 285                                         entry = tmp;
 286                                         break;
 287                                 }
 288                         }
 289                 }
 290                 sym->alias = alias;
 291                 list_add(&sym->list, entry);
 292                 hash_add(sym->sec->symbol_hash, &sym->hash, sym->idx);
 293         }
 294 
 295         /* Create parent/child links for any cold subfunctions */
 296         list_for_each_entry(sec, &elf->sections, list) {
 297                 list_for_each_entry(sym, &sec->symbol_list, list) {
 298                         char pname[MAX_NAME_LEN + 1];
 299                         size_t pnamelen;
 300                         if (sym->type != STT_FUNC)
 301                                 continue;
 302                         sym->pfunc = sym->cfunc = sym;
 303                         coldstr = strstr(sym->name, ".cold");
 304                         if (!coldstr)
 305                                 continue;
 306 
 307                         pnamelen = coldstr - sym->name;
 308                         if (pnamelen > MAX_NAME_LEN) {
 309                                 WARN("%s(): parent function name exceeds maximum length of %d characters",
 310                                      sym->name, MAX_NAME_LEN);
 311                                 return -1;
 312                         }
 313 
 314                         strncpy(pname, sym->name, pnamelen);
 315                         pname[pnamelen] = '\0';
 316                         pfunc = find_symbol_by_name(elf, pname);
 317 
 318                         if (!pfunc) {
 319                                 WARN("%s(): can't find parent function",
 320                                      sym->name);
 321                                 return -1;
 322                         }
 323 
 324                         sym->pfunc = pfunc;
 325                         pfunc->cfunc = sym;
 326 
 327                         /*
 328                          * Unfortunately, -fnoreorder-functions puts the child
 329                          * inside the parent.  Remove the overlap so we can
 330                          * have sane assumptions.
 331                          *
 332                          * Note that pfunc->len now no longer matches
 333                          * pfunc->sym.st_size.
 334                          */
 335                         if (sym->sec == pfunc->sec &&
 336                             sym->offset >= pfunc->offset &&
 337                             sym->offset + sym->len == pfunc->offset + pfunc->len) {
 338                                 pfunc->len -= sym->len;
 339                         }
 340                 }
 341         }
 342 
 343         return 0;
 344 
 345 err:
 346         free(sym);
 347         return -1;
 348 }
 349 
 350 static int read_relas(struct elf *elf)
 351 {
 352         struct section *sec;
 353         struct rela *rela;
 354         int i;
 355         unsigned int symndx;
 356 
 357         list_for_each_entry(sec, &elf->sections, list) {
 358                 if (sec->sh.sh_type != SHT_RELA)
 359                         continue;
 360 
 361                 sec->base = find_section_by_name(elf, sec->name + 5);
 362                 if (!sec->base) {
 363                         WARN("can't find base section for rela section %s",
 364                              sec->name);
 365                         return -1;
 366                 }
 367 
 368                 sec->base->rela = sec;
 369 
 370                 for (i = 0; i < sec->sh.sh_size / sec->sh.sh_entsize; i++) {
 371                         rela = malloc(sizeof(*rela));
 372                         if (!rela) {
 373                                 perror("malloc");
 374                                 return -1;
 375                         }
 376                         memset(rela, 0, sizeof(*rela));
 377 
 378                         if (!gelf_getrela(sec->data, i, &rela->rela)) {
 379                                 WARN_ELF("gelf_getrela");
 380                                 return -1;
 381                         }
 382 
 383                         rela->type = GELF_R_TYPE(rela->rela.r_info);
 384                         rela->addend = rela->rela.r_addend;
 385                         rela->offset = rela->rela.r_offset;
 386                         symndx = GELF_R_SYM(rela->rela.r_info);
 387                         rela->sym = find_symbol_by_index(elf, symndx);
 388                         rela->sec = sec;
 389                         if (!rela->sym) {
 390                                 WARN("can't find rela entry symbol %d for %s",
 391                                      symndx, sec->name);
 392                                 return -1;
 393                         }
 394 
 395                         list_add_tail(&rela->list, &sec->rela_list);
 396                         hash_add(sec->rela_hash, &rela->hash, rela->offset);
 397 
 398                 }
 399         }
 400 
 401         return 0;
 402 }
 403 
 404 struct elf *elf_read(const char *name, int flags)
 405 {
 406         struct elf *elf;
 407         Elf_Cmd cmd;
 408 
 409         elf_version(EV_CURRENT);
 410 
 411         elf = malloc(sizeof(*elf));
 412         if (!elf) {
 413                 perror("malloc");
 414                 return NULL;
 415         }
 416         memset(elf, 0, sizeof(*elf));
 417 
 418         INIT_LIST_HEAD(&elf->sections);
 419 
 420         elf->fd = open(name, flags);
 421         if (elf->fd == -1) {
 422                 fprintf(stderr, "objtool: Can't open '%s': %s\n",
 423                         name, strerror(errno));
 424                 goto err;
 425         }
 426 
 427         if ((flags & O_ACCMODE) == O_RDONLY)
 428                 cmd = ELF_C_READ_MMAP;
 429         else if ((flags & O_ACCMODE) == O_RDWR)
 430                 cmd = ELF_C_RDWR;
 431         else /* O_WRONLY */
 432                 cmd = ELF_C_WRITE;
 433 
 434         elf->elf = elf_begin(elf->fd, cmd, NULL);
 435         if (!elf->elf) {
 436                 WARN_ELF("elf_begin");
 437                 goto err;
 438         }
 439 
 440         if (!gelf_getehdr(elf->elf, &elf->ehdr)) {
 441                 WARN_ELF("gelf_getehdr");
 442                 goto err;
 443         }
 444 
 445         if (read_sections(elf))
 446                 goto err;
 447 
 448         if (read_symbols(elf))
 449                 goto err;
 450 
 451         if (read_relas(elf))
 452                 goto err;
 453 
 454         return elf;
 455 
 456 err:
 457         elf_close(elf);
 458         return NULL;
 459 }
 460 
 461 struct section *elf_create_section(struct elf *elf, const char *name,
 462                                    size_t entsize, int nr)
 463 {
 464         struct section *sec, *shstrtab;
 465         size_t size = entsize * nr;
 466         Elf_Scn *s;
 467         Elf_Data *data;
 468 
 469         sec = malloc(sizeof(*sec));
 470         if (!sec) {
 471                 perror("malloc");
 472                 return NULL;
 473         }
 474         memset(sec, 0, sizeof(*sec));
 475 
 476         INIT_LIST_HEAD(&sec->symbol_list);
 477         INIT_LIST_HEAD(&sec->rela_list);
 478         hash_init(sec->rela_hash);
 479         hash_init(sec->symbol_hash);
 480 
 481         list_add_tail(&sec->list, &elf->sections);
 482 
 483         s = elf_newscn(elf->elf);
 484         if (!s) {
 485                 WARN_ELF("elf_newscn");
 486                 return NULL;
 487         }
 488 
 489         sec->name = strdup(name);
 490         if (!sec->name) {
 491                 perror("strdup");
 492                 return NULL;
 493         }
 494 
 495         sec->idx = elf_ndxscn(s);
 496         sec->len = size;
 497         sec->changed = true;
 498 
 499         sec->data = elf_newdata(s);
 500         if (!sec->data) {
 501                 WARN_ELF("elf_newdata");
 502                 return NULL;
 503         }
 504 
 505         sec->data->d_size = size;
 506         sec->data->d_align = 1;
 507 
 508         if (size) {
 509                 sec->data->d_buf = malloc(size);
 510                 if (!sec->data->d_buf) {
 511                         perror("malloc");
 512                         return NULL;
 513                 }
 514                 memset(sec->data->d_buf, 0, size);
 515         }
 516 
 517         if (!gelf_getshdr(s, &sec->sh)) {
 518                 WARN_ELF("gelf_getshdr");
 519                 return NULL;
 520         }
 521 
 522         sec->sh.sh_size = size;
 523         sec->sh.sh_entsize = entsize;
 524         sec->sh.sh_type = SHT_PROGBITS;
 525         sec->sh.sh_addralign = 1;
 526         sec->sh.sh_flags = SHF_ALLOC;
 527 
 528 
 529         /* Add section name to .shstrtab (or .strtab for Clang) */
 530         shstrtab = find_section_by_name(elf, ".shstrtab");
 531         if (!shstrtab)
 532                 shstrtab = find_section_by_name(elf, ".strtab");
 533         if (!shstrtab) {
 534                 WARN("can't find .shstrtab or .strtab section");
 535                 return NULL;
 536         }
 537 
 538         s = elf_getscn(elf->elf, shstrtab->idx);
 539         if (!s) {
 540                 WARN_ELF("elf_getscn");
 541                 return NULL;
 542         }
 543 
 544         data = elf_newdata(s);
 545         if (!data) {
 546                 WARN_ELF("elf_newdata");
 547                 return NULL;
 548         }
 549 
 550         data->d_buf = sec->name;
 551         data->d_size = strlen(name) + 1;
 552         data->d_align = 1;
 553 
 554         sec->sh.sh_name = shstrtab->len;
 555 
 556         shstrtab->len += strlen(name) + 1;
 557         shstrtab->changed = true;
 558 
 559         return sec;
 560 }
 561 
 562 struct section *elf_create_rela_section(struct elf *elf, struct section *base)
 563 {
 564         char *relaname;
 565         struct section *sec;
 566 
 567         relaname = malloc(strlen(base->name) + strlen(".rela") + 1);
 568         if (!relaname) {
 569                 perror("malloc");
 570                 return NULL;
 571         }
 572         strcpy(relaname, ".rela");
 573         strcat(relaname, base->name);
 574 
 575         sec = elf_create_section(elf, relaname, sizeof(GElf_Rela), 0);
 576         free(relaname);
 577         if (!sec)
 578                 return NULL;
 579 
 580         base->rela = sec;
 581         sec->base = base;
 582 
 583         sec->sh.sh_type = SHT_RELA;
 584         sec->sh.sh_addralign = 8;
 585         sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx;
 586         sec->sh.sh_info = base->idx;
 587         sec->sh.sh_flags = SHF_INFO_LINK;
 588 
 589         return sec;
 590 }
 591 
 592 int elf_rebuild_rela_section(struct section *sec)
 593 {
 594         struct rela *rela;
 595         int nr, idx = 0, size;
 596         GElf_Rela *relas;
 597 
 598         nr = 0;
 599         list_for_each_entry(rela, &sec->rela_list, list)
 600                 nr++;
 601 
 602         size = nr * sizeof(*relas);
 603         relas = malloc(size);
 604         if (!relas) {
 605                 perror("malloc");
 606                 return -1;
 607         }
 608 
 609         sec->data->d_buf = relas;
 610         sec->data->d_size = size;
 611 
 612         sec->sh.sh_size = size;
 613 
 614         idx = 0;
 615         list_for_each_entry(rela, &sec->rela_list, list) {
 616                 relas[idx].r_offset = rela->offset;
 617                 relas[idx].r_addend = rela->addend;
 618                 relas[idx].r_info = GELF_R_INFO(rela->sym->idx, rela->type);
 619                 idx++;
 620         }
 621 
 622         return 0;
 623 }
 624 
 625 int elf_write(struct elf *elf)
 626 {
 627         struct section *sec;
 628         Elf_Scn *s;
 629 
 630         /* Update section headers for changed sections: */
 631         list_for_each_entry(sec, &elf->sections, list) {
 632                 if (sec->changed) {
 633                         s = elf_getscn(elf->elf, sec->idx);
 634                         if (!s) {
 635                                 WARN_ELF("elf_getscn");
 636                                 return -1;
 637                         }
 638                         if (!gelf_update_shdr(s, &sec->sh)) {
 639                                 WARN_ELF("gelf_update_shdr");
 640                                 return -1;
 641                         }
 642                 }
 643         }
 644 
 645         /* Make sure the new section header entries get updated properly. */
 646         elf_flagelf(elf->elf, ELF_C_SET, ELF_F_DIRTY);
 647 
 648         /* Write all changes to the file. */
 649         if (elf_update(elf->elf, ELF_C_WRITE) < 0) {
 650                 WARN_ELF("elf_update");
 651                 return -1;
 652         }
 653 
 654         return 0;
 655 }
 656 
 657 void elf_close(struct elf *elf)
 658 {
 659         struct section *sec, *tmpsec;
 660         struct symbol *sym, *tmpsym;
 661         struct rela *rela, *tmprela;
 662 
 663         if (elf->elf)
 664                 elf_end(elf->elf);
 665 
 666         if (elf->fd > 0)
 667                 close(elf->fd);
 668 
 669         list_for_each_entry_safe(sec, tmpsec, &elf->sections, list) {
 670                 list_for_each_entry_safe(sym, tmpsym, &sec->symbol_list, list) {
 671                         list_del(&sym->list);
 672                         hash_del(&sym->hash);
 673                         free(sym);
 674                 }
 675                 list_for_each_entry_safe(rela, tmprela, &sec->rela_list, list) {
 676                         list_del(&rela->list);
 677                         hash_del(&rela->hash);
 678                         free(rela);
 679                 }
 680                 list_del(&sec->list);
 681                 free(sec);
 682         }
 683 
 684         free(elf);
 685 }

/* [<][>][^][v][top][bottom][index][help] */