Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

objtool: Support per-function rodata sections

Add support for processing switch jump tables in objects with multiple
.rodata sections, such as those created by '-ffunction-sections' and
'-fdata-sections'. Currently, objtool always looks in .rodata for jump
table information, which results in many "sibling call from callable
instruction with modified stack frame" warnings with objects compiled
using those flags.

The fix is comprised of three parts:

1. Flagging all .rodata sections when importing ELF information for
easier checking later.

2. Keeping a reference to the section each relocation is from in order
to get the list_head for the other relocations in that section.

3. Finding jump tables by following relocations to .rodata sections,
rather than always referencing a single global .rodata section.

The patch has been tested without data sections enabled and no
differences in the resulting orc unwind information were seen.

Note that as objtool adds terminators to end of each .text section the
unwind information generated between a function+data sections build and
a normal build aren't directly comparable. Manual inspection suggests
that objtool is now generating the correct information, or at least
making more of an effort to do so than it did previously.

Signed-off-by: Allan Xavier <allan.x.xavier@oracle.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lkml.kernel.org/r/099bdc375195c490dda04db777ee0b95d566ded1.1536325914.git.jpoimboe@redhat.com

authored by

Allan Xavier and committed by
Thomas Gleixner
4a60aa05 db44bf4b

+37 -9
+32 -6
tools/objtool/check.c
··· 836 836 struct symbol *pfunc = insn->func->pfunc; 837 837 unsigned int prev_offset = 0; 838 838 839 - list_for_each_entry_from(rela, &file->rodata->rela->rela_list, list) { 839 + list_for_each_entry_from(rela, &table->rela_sec->rela_list, list) { 840 840 if (rela == next_table) 841 841 break; 842 842 ··· 926 926 { 927 927 struct rela *text_rela, *rodata_rela; 928 928 struct instruction *orig_insn = insn; 929 + struct section *rodata_sec; 929 930 unsigned long table_offset; 930 931 931 932 /* ··· 954 953 /* look for a relocation which references .rodata */ 955 954 text_rela = find_rela_by_dest_range(insn->sec, insn->offset, 956 955 insn->len); 957 - if (!text_rela || text_rela->sym != file->rodata->sym) 956 + if (!text_rela || text_rela->sym->type != STT_SECTION || 957 + !text_rela->sym->sec->rodata) 958 958 continue; 959 959 960 960 table_offset = text_rela->addend; 961 + rodata_sec = text_rela->sym->sec; 962 + 961 963 if (text_rela->type == R_X86_64_PC32) 962 964 table_offset += 4; 963 965 ··· 968 964 * Make sure the .rodata address isn't associated with a 969 965 * symbol. gcc jump tables are anonymous data. 970 966 */ 971 - if (find_symbol_containing(file->rodata, table_offset)) 967 + if (find_symbol_containing(rodata_sec, table_offset)) 972 968 continue; 973 969 974 - rodata_rela = find_rela_by_dest(file->rodata, table_offset); 970 + rodata_rela = find_rela_by_dest(rodata_sec, table_offset); 975 971 if (rodata_rela) { 976 972 /* 977 973 * Use of RIP-relative switch jumps is quite rare, and ··· 1056 1052 struct symbol *func; 1057 1053 int ret; 1058 1054 1059 - if (!file->rodata || !file->rodata->rela) 1055 + if (!file->rodata) 1060 1056 return 0; 1061 1057 1062 1058 for_each_sec(file, sec) { ··· 1202 1198 return 0; 1203 1199 } 1204 1200 1201 + static void mark_rodata(struct objtool_file *file) 1202 + { 1203 + struct section *sec; 1204 + bool found = false; 1205 + 1206 + /* 1207 + * This searches for the .rodata section or multiple .rodata.func_name 1208 + * sections if -fdata-sections is being used. The .str.1.1 and .str.1.8 1209 + * rodata sections are ignored as they don't contain jump tables. 1210 + */ 1211 + for_each_sec(file, sec) { 1212 + if (!strncmp(sec->name, ".rodata", 7) && 1213 + !strstr(sec->name, ".str1.")) { 1214 + sec->rodata = true; 1215 + found = true; 1216 + } 1217 + } 1218 + 1219 + file->rodata = found; 1220 + } 1221 + 1205 1222 static int decode_sections(struct objtool_file *file) 1206 1223 { 1207 1224 int ret; 1225 + 1226 + mark_rodata(file); 1208 1227 1209 1228 ret = decode_instructions(file); 1210 1229 if (ret) ··· 2198 2171 INIT_LIST_HEAD(&file.insn_list); 2199 2172 hash_init(file.insn_hash); 2200 2173 file.whitelist = find_section_by_name(file.elf, ".discard.func_stack_frame_non_standard"); 2201 - file.rodata = find_section_by_name(file.elf, ".rodata"); 2202 2174 file.c_file = find_section_by_name(file.elf, ".comment"); 2203 2175 file.ignore_unreachables = no_unreachable; 2204 2176 file.hints = false;
+2 -2
tools/objtool/check.h
··· 60 60 struct elf *elf; 61 61 struct list_head insn_list; 62 62 DECLARE_HASHTABLE(insn_hash, 16); 63 - struct section *rodata, *whitelist; 64 - bool ignore_unreachables, c_file, hints; 63 + struct section *whitelist; 64 + bool ignore_unreachables, c_file, hints, rodata; 65 65 }; 66 66 67 67 int check(const char *objname, bool orc);
+1
tools/objtool/elf.c
··· 379 379 rela->offset = rela->rela.r_offset; 380 380 symndx = GELF_R_SYM(rela->rela.r_info); 381 381 rela->sym = find_symbol_by_index(elf, symndx); 382 + rela->rela_sec = sec; 382 383 if (!rela->sym) { 383 384 WARN("can't find rela entry symbol %d for %s", 384 385 symndx, sec->name);
+2 -1
tools/objtool/elf.h
··· 48 48 char *name; 49 49 int idx; 50 50 unsigned int len; 51 - bool changed, text; 51 + bool changed, text, rodata; 52 52 }; 53 53 54 54 struct symbol { ··· 68 68 struct list_head list; 69 69 struct hlist_node hash; 70 70 GElf_Rela rela; 71 + struct section *rela_sec; 71 72 struct symbol *sym; 72 73 unsigned int type; 73 74 unsigned long offset;