History

objtool: Refactor jump table code to support other architectures

The way to identify jump tables and retrieve all the data necessary to handle the different execution branches is not the same on all architectures. In order to be able to add other architecture support, define an arch-dependent function to process jump-tables.

Raphael Gault committed 6 years ago

Commit d871f7b1 parent 45245f54 files · +103 −87Browse files

Unified · Split

4 files
  1. tools/objtool/arch/x86/special.c +95 −0
  2. tools/objtool/check.c +4 −86
  3. tools/objtool/check.h +0 −1
  4. tools/objtool/special.h +4 −0
tools/objtool/arch/x86/special.c+95−0
@@ -1,4 +1,6 @@
1// SPDX-License-Identifier: GPL-2.0-or-later1// SPDX-License-Identifier: GPL-2.0-or-later
2#include <string.h>
3
2#include "../../special.h"4#include "../../special.h"
3#include "../../builtin.h"5#include "../../builtin.h"
4 6
@@ -48,3 +50,96 @@ bool arch_support_alt_relocation(struct special_alt *special_alt, · 43 unmodified lines
48 return insn->offset == special_alt->new_off &&50 return insn->offset == special_alt->new_off &&
49 (insn->type == INSN_CALL || is_static_jump(insn));51 (insn->type == INSN_CALL || is_static_jump(insn));
50}52}
53
54/*
55 * There are 3 basic jump table patterns:
56 *
57 * 1. jmpq *[rodata addr](,%reg,8)
58 *
59 * This is the most common case by far. It jumps to an address in a simple
60 * jump table which is stored in .rodata.
61 *
62 * 2. jmpq *[rodata addr](%rip)
63 *
64 * This is caused by a rare GCC quirk, currently only seen in three driver
65 * functions in the kernel, only with certain obscure non-distro configs.
66 *
67 * As part of an optimization, GCC makes a copy of an existing switch jump
68 * table, modifies it, and then hard-codes the jump (albeit with an indirect
69 * jump) to use a single entry in the table. The rest of the jump table and
70 * some of its jump targets remain as dead code.
71 *
72 * In such a case we can just crudely ignore all unreachable instruction
73 * warnings for the entire object file. Ideally we would just ignore them
74 * for the function, but that would require redesigning the code quite a
75 * bit. And honestly that's just not worth doing: unreachable instruction
76 * warnings are of questionable value anyway, and this is such a rare issue.
77 *
78 * 3. mov [rodata addr],%reg1
79 * ... some instructions ...
80 * jmpq *(%reg1,%reg2,8)
81 *
82 * This is a fairly uncommon pattern which is new for GCC 6. As of this
83 * writing, there are 11 occurrences of it in the allmodconfig kernel.
84 *
85 * As of GCC 7 there are quite a few more of these and the 'in between' code
86 * is significant. Esp. with KASAN enabled some of the code between the mov
87 * and jmpq uses .rodata itself, which can confuse things.
88 *
89 * TODO: Once we have DWARF CFI and smarter instruction decoding logic,
90 * ensure the same register is used in the mov and jump instructions.
91 *
92 * NOTE: RETPOLINE made it harder still to decode dynamic jumps.
93 */
94struct reloc *arch_find_switch_table(struct objtool_file *file,
95 struct instruction *insn)
96{
97 struct reloc *text_reloc, *rodata_reloc;
98 struct section *table_sec;
99 unsigned long table_offset;
100
101 /* look for a relocation which references .rodata */
102 text_reloc = find_reloc_by_dest_range(file->elf, insn->sec,
103 insn->offset, insn->len);
104 if (!text_reloc || text_reloc->sym->type != STT_SECTION ||
105 !text_reloc->sym->sec->rodata)
106 return NULL;
107
108 table_offset = text_reloc->addend;
109 table_sec = text_reloc->sym->sec;
110
111 if (text_reloc->type == R_X86_64_PC32)
112 table_offset += 4;
113
114 /*
115 * Make sure the .rodata address isn't associated with a
116 * symbol. GCC jump tables are anonymous data.
117 *
118 * Also support C jump tables which are in the same format as
119 * switch jump tables. For objtool to recognize them, they
120 * need to be placed in the C_JUMP_TABLE_SECTION section. They
121 * have symbols associated with them.
122 */
123 if (find_symbol_containing(table_sec, table_offset) &&
124 strcmp(table_sec->name, C_JUMP_TABLE_SECTION))
125 return NULL;
126
127 /*
128 * Each table entry has a rela associated with it. The rela
129 * should reference text in the same function as the original
130 * instruction.
131 */
132 rodata_reloc = find_reloc_by_dest(file->elf, table_sec, table_offset);
133 if (!rodata_reloc)
134 return NULL;
135
136 /*
137 * Use of RIP-relative switch jumps is quite rare, and
138 * indicates a rare GCC quirk/bug which can leave dead
139 * code behind.
140 */
141 if (text_reloc->type == R_X86_64_PC32)
142 file->ignore_unreachables = true;
143
144 return rodata_reloc;
145}
tools/objtool/check.c+4−86
@@ -20,8 +20,6 @@ · 19 unmodified lines
20 20
21#define FAKE_JUMP_OFFSET -121#define FAKE_JUMP_OFFSET -1
22 22
23#define C_JUMP_TABLE_SECTION ".rodata..c_jump_table"
24
25struct alternative {23struct alternative {
26 struct list_head list;24 struct list_head list;
27 struct instruction *insn;25 struct instruction *insn;
@@ -1190,56 +1188,15 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn, · 1162 unmodified lines
1190}1188}
1191 1189
1192/*1190/*
1193 * find_jump_table() - Given a dynamic jump, find the switch jump table in1191 * find_jump_table() - Given a dynamic jump, find the switch jump table
1194 * .rodata associated with it.1192 * associated with it.
1195 *
1196 * There are 3 basic patterns:
1197 *
1198 * 1. jmpq *[rodata addr](,%reg,8)
1199 *
1200 * This is the most common case by far. It jumps to an address in a simple
1201 * jump table which is stored in .rodata.
1202 *
1203 * 2. jmpq *[rodata addr](%rip)
1204 *
1205 * This is caused by a rare GCC quirk, currently only seen in three driver
1206 * functions in the kernel, only with certain obscure non-distro configs.
1207 *
1208 * As part of an optimization, GCC makes a copy of an existing switch jump
1209 * table, modifies it, and then hard-codes the jump (albeit with an indirect
1210 * jump) to use a single entry in the table. The rest of the jump table and
1211 * some of its jump targets remain as dead code.
1212 *
1213 * In such a case we can just crudely ignore all unreachable instruction
1214 * warnings for the entire object file. Ideally we would just ignore them
1215 * for the function, but that would require redesigning the code quite a
1216 * bit. And honestly that's just not worth doing: unreachable instruction
1217 * warnings are of questionable value anyway, and this is such a rare issue.
1218 *
1219 * 3. mov [rodata addr],%reg1
1220 * ... some instructions ...
1221 * jmpq *(%reg1,%reg2,8)
1222 *
1223 * This is a fairly uncommon pattern which is new for GCC 6. As of this
1224 * writing, there are 11 occurrences of it in the allmodconfig kernel.
1225 *
1226 * As of GCC 7 there are quite a few more of these and the 'in between' code
1227 * is significant. Esp. with KASAN enabled some of the code between the mov
1228 * and jmpq uses .rodata itself, which can confuse things.
1229 *
1230 * TODO: Once we have DWARF CFI and smarter instruction decoding logic,
1231 * ensure the same register is used in the mov and jump instructions.
1232 *
1233 * NOTE: RETPOLINE made it harder still to decode dynamic jumps.
1234 */1193 */
1235static struct reloc *find_jump_table(struct objtool_file *file,1194static struct reloc *find_jump_table(struct objtool_file *file,
1236 struct symbol *func,1195 struct symbol *func,
1237 struct instruction *insn)1196 struct instruction *insn)
1238{1197{
1239 struct reloc *text_reloc, *table_reloc;1198 struct reloc *table_reloc;
1240 struct instruction *dest_insn, *orig_insn = insn;1199 struct instruction *dest_insn, *orig_insn = insn;
1241 struct section *table_sec;
1242 unsigned long table_offset;
1243 1200
1244 /*1201 /*
1245 * Backward search using the @first_jump_src links, these help avoid1202 * Backward search using the @first_jump_src links, these help avoid
@@ -1260,52 +1217,13 @@ static struct reloc *find_jump_table(struct objtool_file *file, · 14 unmodified lines
1260 insn->jump_dest->offset > orig_insn->offset))1217 insn->jump_dest->offset > orig_insn->offset))
1261 break;1218 break;
1262 1219
1263 /* look for a relocation which references .rodata */1220 table_reloc = arch_find_switch_table(file, insn);
1264 text_reloc = find_reloc_by_dest_range(file->elf, insn->sec,
1265 insn->offset, insn->len);
1266 if (!text_reloc || text_reloc->sym->type != STT_SECTION ||
1267 !text_reloc->sym->sec->rodata)
1268 continue;
1269
1270 table_offset = text_reloc->addend;
1271 table_sec = text_reloc->sym->sec;
1272
1273 if (text_reloc->type == R_X86_64_PC32)
1274 table_offset += 4;
1275
1276 /*
1277 * Make sure the .rodata address isn't associated with a
1278 * symbol. GCC jump tables are anonymous data.
1279 *
1280 * Also support C jump tables which are in the same format as
1281 * switch jump tables. For objtool to recognize them, they
1282 * need to be placed in the C_JUMP_TABLE_SECTION section. They
1283 * have symbols associated with them.
1284 */
1285 if (find_symbol_containing(table_sec, table_offset) &&
1286 strcmp(table_sec->name, C_JUMP_TABLE_SECTION))
1287 continue;
1288
1289 /*
1290 * Each table entry has a reloc associated with it. The reloc
1291 * should reference text in the same function as the original
1292 * instruction.
1293 */
1294 table_reloc = find_reloc_by_dest(file->elf, table_sec, table_offset);
1295 if (!table_reloc)1221 if (!table_reloc)
1296 continue;1222 continue;
1297 dest_insn = find_insn(file, table_reloc->sym->sec, table_reloc->addend);1223 dest_insn = find_insn(file, table_reloc->sym->sec, table_reloc->addend);
1298 if (!dest_insn || !dest_insn->func || dest_insn->func->pfunc != func)1224 if (!dest_insn || !dest_insn->func || dest_insn->func->pfunc != func)
1299 continue;1225 continue;
1300 1226
1301 /*
1302 * Use of RIP-relative switch jumps is quite rare, and
1303 * indicates a rare GCC quirk/bug which can leave dead code
1304 * behind.
1305 */
1306 if (text_reloc->type == R_X86_64_PC32)
1307 file->ignore_unreachables = true;
1308
1309 return table_reloc;1227 return table_reloc;
1310 }1228 }
1311 1229
tools/objtool/check.h+0−1
@@ -66,5 +66,4 @@ struct instruction *find_insn(struct objtool_file *file, · 65 unmodified lines
66 insn->sec == sec; \66 insn->sec == sec; \
67 insn = list_next_entry(insn, list))67 insn = list_next_entry(insn, list))
68 68
69
70#endif /* _CHECK_H */69#endif /* _CHECK_H */
tools/objtool/special.h+4−0
@@ -10,6 +10,8 @@ · 9 unmodified lines
10#include "check.h"10#include "check.h"
11#include "elf.h"11#include "elf.h"
12 12
13#define C_JUMP_TABLE_SECTION ".rodata..c_jump_table"
14
13struct special_alt {15struct special_alt {
14 struct list_head list;16 struct list_head list;
15 17
@@ -34,4 +36,6 @@ void arch_handle_alternative(unsigned short feature, struct special_alt *alt); · 18 unmodified lines
34bool arch_support_alt_relocation(struct special_alt *special_alt,36bool arch_support_alt_relocation(struct special_alt *special_alt,
35 struct instruction *insn,37 struct instruction *insn,
36 struct reloc *reloc);38 struct reloc *reloc);
39struct reloc *arch_find_switch_table(struct objtool_file *file,
40 struct instruction *insn);
37#endif /* _SPECIAL_H */41#endif /* _SPECIAL_H */

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t