Skip to content

Commit ead165f

Browse files
Peter Zijlstrasuryasaimadhu
authored andcommitted
objtool: Fix symbol creation
Nathan reported objtool failing with the following messages: warning: objtool: no non-local symbols !? warning: objtool: gelf_update_symshndx: invalid section index The problem is due to commit 4abff6d ("objtool: Fix code relocs vs weak symbols") failing to consider the case where an object would have no non-local symbols. The problem that commit tries to address is adding a STB_LOCAL symbol to the symbol table in light of the ELF spec's requirement that: In each symbol table, all symbols with STB_LOCAL binding preced the weak and global symbols. As ``Sections'' above describes, a symbol table section's sh_info section header member holds the symbol table index for the first non-local symbol. The approach taken is to find this first non-local symbol, move that to the end and then re-use the freed spot to insert a new local symbol and increment sh_info. Except it never considered the case of object files without global symbols and got a whole bunch of details wrong -- so many in fact that it is a wonder it ever worked :/ Specifically: - It failed to re-hash the symbol on the new index, so a subsequent find_symbol_by_index() would not find it at the new location and a query for the old location would now return a non-deterministic choice between the old and new symbol. - It failed to appreciate that the GElf wrappers are not a valid disk format (it works because GElf is basically Elf64 and we only support x86_64 atm.) - It failed to fully appreciate how horrible the libelf API really is and got the gelf_update_symshndx() call pretty much completely wrong; with the direct consequence that if inserting a second STB_LOCAL symbol would require moving the same STB_GLOBAL symbol again it would completely come unstuck. Write a new elf_update_symbol() function that wraps all the magic required to update or create a new symbol at a given index. Specifically, gelf_update_sym*() require an @ndx argument that is relative to the @DaTa argument; this means you have to manually iterate the section data descriptor list and update @ndx. Fixes: 4abff6d ("objtool: Fix code relocs vs weak symbols") Reported-by: Nathan Chancellor <nathan@kernel.org> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Signed-off-by: Borislav Petkov <bp@suse.de> Acked-by: Josh Poimboeuf <jpoimboe@kernel.org> Tested-by: Nathan Chancellor <nathan@kernel.org> Cc: <stable@vger.kernel.org> Link: https://lkml.kernel.org/r/YoPCTEYjoPqE4ZxB@hirez.programming.kicks-ass.net
1 parent 1d1a0e7 commit ead165f

File tree

1 file changed

+129
-69
lines changed

1 file changed

+129
-69
lines changed

tools/objtool/elf.c

Lines changed: 129 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,9 @@ static void elf_add_symbol(struct elf *elf, struct symbol *sym)
374374
struct list_head *entry;
375375
struct rb_node *pnode;
376376

377+
INIT_LIST_HEAD(&sym->pv_target);
378+
sym->alias = sym;
379+
377380
sym->type = GELF_ST_TYPE(sym->sym.st_info);
378381
sym->bind = GELF_ST_BIND(sym->sym.st_info);
379382

@@ -438,8 +441,6 @@ static int read_symbols(struct elf *elf)
438441
return -1;
439442
}
440443
memset(sym, 0, sizeof(*sym));
441-
INIT_LIST_HEAD(&sym->pv_target);
442-
sym->alias = sym;
443444

444445
sym->idx = i;
445446

@@ -603,104 +604,146 @@ static void elf_dirty_reloc_sym(struct elf *elf, struct symbol *sym)
603604
}
604605

605606
/*
606-
* Move the first global symbol, as per sh_info, into a new, higher symbol
607-
* index. This fees up the shndx for a new local symbol.
607+
* The libelf API is terrible; gelf_update_sym*() takes a data block relative
608+
* index value, *NOT* the symbol index. As such, iterate the data blocks and
609+
* adjust index until it fits.
610+
*
611+
* If no data block is found, allow adding a new data block provided the index
612+
* is only one past the end.
608613
*/
609-
static int elf_move_global_symbol(struct elf *elf, struct section *symtab,
610-
struct section *symtab_shndx)
614+
static int elf_update_symbol(struct elf *elf, struct section *symtab,
615+
struct section *symtab_shndx, struct symbol *sym)
611616
{
612-
Elf_Data *data, *shndx_data = NULL;
613-
Elf32_Word first_non_local;
614-
struct symbol *sym;
615-
Elf_Scn *s;
616-
617-
first_non_local = symtab->sh.sh_info;
618-
619-
sym = find_symbol_by_index(elf, first_non_local);
620-
if (!sym) {
621-
WARN("no non-local symbols !?");
622-
return first_non_local;
623-
}
617+
Elf32_Word shndx = sym->sec ? sym->sec->idx : SHN_UNDEF;
618+
Elf_Data *symtab_data = NULL, *shndx_data = NULL;
619+
Elf64_Xword entsize = symtab->sh.sh_entsize;
620+
int max_idx, idx = sym->idx;
621+
Elf_Scn *s, *t = NULL;
624622

625623
s = elf_getscn(elf->elf, symtab->idx);
626624
if (!s) {
627625
WARN_ELF("elf_getscn");
628626
return -1;
629627
}
630628

631-
data = elf_newdata(s);
632-
if (!data) {
633-
WARN_ELF("elf_newdata");
634-
return -1;
629+
if (symtab_shndx) {
630+
t = elf_getscn(elf->elf, symtab_shndx->idx);
631+
if (!t) {
632+
WARN_ELF("elf_getscn");
633+
return -1;
634+
}
635635
}
636636

637-
data->d_buf = &sym->sym;
638-
data->d_size = sizeof(sym->sym);
639-
data->d_align = 1;
640-
data->d_type = ELF_T_SYM;
637+
for (;;) {
638+
/* get next data descriptor for the relevant sections */
639+
symtab_data = elf_getdata(s, symtab_data);
640+
if (t)
641+
shndx_data = elf_getdata(t, shndx_data);
641642

642-
sym->idx = symtab->sh.sh_size / sizeof(sym->sym);
643-
elf_dirty_reloc_sym(elf, sym);
643+
/* end-of-list */
644+
if (!symtab_data) {
645+
void *buf;
644646

645-
symtab->sh.sh_info += 1;
646-
symtab->sh.sh_size += data->d_size;
647-
symtab->changed = true;
647+
if (idx) {
648+
/* we don't do holes in symbol tables */
649+
WARN("index out of range");
650+
return -1;
651+
}
648652

649-
if (symtab_shndx) {
650-
s = elf_getscn(elf->elf, symtab_shndx->idx);
651-
if (!s) {
652-
WARN_ELF("elf_getscn");
653+
/* if @idx == 0, it's the next contiguous entry, create it */
654+
symtab_data = elf_newdata(s);
655+
if (t)
656+
shndx_data = elf_newdata(t);
657+
658+
buf = calloc(1, entsize);
659+
if (!buf) {
660+
WARN("malloc");
661+
return -1;
662+
}
663+
664+
symtab_data->d_buf = buf;
665+
symtab_data->d_size = entsize;
666+
symtab_data->d_align = 1;
667+
symtab_data->d_type = ELF_T_SYM;
668+
669+
symtab->sh.sh_size += entsize;
670+
symtab->changed = true;
671+
672+
if (t) {
673+
shndx_data->d_buf = &sym->sec->idx;
674+
shndx_data->d_size = sizeof(Elf32_Word);
675+
shndx_data->d_align = sizeof(Elf32_Word);
676+
shndx_data->d_type = ELF_T_WORD;
677+
678+
symtab_shndx->sh.sh_size += sizeof(Elf32_Word);
679+
symtab_shndx->changed = true;
680+
}
681+
682+
break;
683+
}
684+
685+
/* empty blocks should not happen */
686+
if (!symtab_data->d_size) {
687+
WARN("zero size data");
653688
return -1;
654689
}
655690

656-
shndx_data = elf_newdata(s);
691+
/* is this the right block? */
692+
max_idx = symtab_data->d_size / entsize;
693+
if (idx < max_idx)
694+
break;
695+
696+
/* adjust index and try again */
697+
idx -= max_idx;
698+
}
699+
700+
/* something went side-ways */
701+
if (idx < 0) {
702+
WARN("negative index");
703+
return -1;
704+
}
705+
706+
/* setup extended section index magic and write the symbol */
707+
if (shndx >= SHN_UNDEF && shndx < SHN_LORESERVE) {
708+
sym->sym.st_shndx = shndx;
709+
if (!shndx_data)
710+
shndx = 0;
711+
} else {
712+
sym->sym.st_shndx = SHN_XINDEX;
657713
if (!shndx_data) {
658-
WARN_ELF("elf_newshndx_data");
714+
WARN("no .symtab_shndx");
659715
return -1;
660716
}
717+
}
661718

662-
shndx_data->d_buf = &sym->sec->idx;
663-
shndx_data->d_size = sizeof(Elf32_Word);
664-
shndx_data->d_align = 4;
665-
shndx_data->d_type = ELF_T_WORD;
666-
667-
symtab_shndx->sh.sh_size += 4;
668-
symtab_shndx->changed = true;
719+
if (!gelf_update_symshndx(symtab_data, shndx_data, idx, &sym->sym, shndx)) {
720+
WARN_ELF("gelf_update_symshndx");
721+
return -1;
669722
}
670723

671-
return first_non_local;
724+
return 0;
672725
}
673726

674727
static struct symbol *
675728
elf_create_section_symbol(struct elf *elf, struct section *sec)
676729
{
677730
struct section *symtab, *symtab_shndx;
678-
Elf_Data *shndx_data = NULL;
679-
struct symbol *sym;
680-
Elf32_Word shndx;
731+
Elf32_Word first_non_local, new_idx;
732+
struct symbol *sym, *old;
681733

682734
symtab = find_section_by_name(elf, ".symtab");
683735
if (symtab) {
684736
symtab_shndx = find_section_by_name(elf, ".symtab_shndx");
685-
if (symtab_shndx)
686-
shndx_data = symtab_shndx->data;
687737
} else {
688738
WARN("no .symtab");
689739
return NULL;
690740
}
691741

692-
sym = malloc(sizeof(*sym));
742+
sym = calloc(1, sizeof(*sym));
693743
if (!sym) {
694744
perror("malloc");
695745
return NULL;
696746
}
697-
memset(sym, 0, sizeof(*sym));
698-
699-
sym->idx = elf_move_global_symbol(elf, symtab, symtab_shndx);
700-
if (sym->idx < 0) {
701-
WARN("elf_move_global_symbol");
702-
return NULL;
703-
}
704747

705748
sym->name = sec->name;
706749
sym->sec = sec;
@@ -710,24 +753,41 @@ elf_create_section_symbol(struct elf *elf, struct section *sec)
710753
// st_other 0
711754
// st_value 0
712755
// st_size 0
713-
shndx = sec->idx;
714-
if (shndx >= SHN_UNDEF && shndx < SHN_LORESERVE) {
715-
sym->sym.st_shndx = shndx;
716-
if (!shndx_data)
717-
shndx = 0;
718-
} else {
719-
sym->sym.st_shndx = SHN_XINDEX;
720-
if (!shndx_data) {
721-
WARN("no .symtab_shndx");
756+
757+
/*
758+
* Move the first global symbol, as per sh_info, into a new, higher
759+
* symbol index. This fees up a spot for a new local symbol.
760+
*/
761+
first_non_local = symtab->sh.sh_info;
762+
new_idx = symtab->sh.sh_size / symtab->sh.sh_entsize;
763+
old = find_symbol_by_index(elf, first_non_local);
764+
if (old) {
765+
old->idx = new_idx;
766+
767+
hlist_del(&old->hash);
768+
elf_hash_add(symbol, &old->hash, old->idx);
769+
770+
elf_dirty_reloc_sym(elf, old);
771+
772+
if (elf_update_symbol(elf, symtab, symtab_shndx, old)) {
773+
WARN("elf_update_symbol move");
722774
return NULL;
723775
}
776+
777+
new_idx = first_non_local;
724778
}
725779

726-
if (!gelf_update_symshndx(symtab->data, shndx_data, sym->idx, &sym->sym, shndx)) {
727-
WARN_ELF("gelf_update_symshndx");
780+
sym->idx = new_idx;
781+
if (elf_update_symbol(elf, symtab, symtab_shndx, sym)) {
782+
WARN("elf_update_symbol");
728783
return NULL;
729784
}
730785

786+
/*
787+
* Either way, we added a LOCAL symbol.
788+
*/
789+
symtab->sh.sh_info += 1;
790+
731791
elf_add_symbol(elf, sym);
732792

733793
return sym;

0 commit comments

Comments
 (0)