18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci#include <string.h>
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include "../../special.h"
58c2ecf20Sopenharmony_ci#include "../../builtin.h"
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#define X86_FEATURE_POPCNT (4 * 32 + 23)
88c2ecf20Sopenharmony_ci#define X86_FEATURE_SMAP   (9 * 32 + 20)
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_civoid arch_handle_alternative(unsigned short feature, struct special_alt *alt)
118c2ecf20Sopenharmony_ci{
128c2ecf20Sopenharmony_ci	switch (feature) {
138c2ecf20Sopenharmony_ci	case X86_FEATURE_SMAP:
148c2ecf20Sopenharmony_ci		/*
158c2ecf20Sopenharmony_ci		 * If UACCESS validation is enabled; force that alternative;
168c2ecf20Sopenharmony_ci		 * otherwise force it the other way.
178c2ecf20Sopenharmony_ci		 *
188c2ecf20Sopenharmony_ci		 * What we want to avoid is having both the original and the
198c2ecf20Sopenharmony_ci		 * alternative code flow at the same time, in that case we can
208c2ecf20Sopenharmony_ci		 * find paths that see the STAC but take the NOP instead of
218c2ecf20Sopenharmony_ci		 * CLAC and the other way around.
228c2ecf20Sopenharmony_ci		 */
238c2ecf20Sopenharmony_ci		if (uaccess)
248c2ecf20Sopenharmony_ci			alt->skip_orig = true;
258c2ecf20Sopenharmony_ci		else
268c2ecf20Sopenharmony_ci			alt->skip_alt = true;
278c2ecf20Sopenharmony_ci		break;
288c2ecf20Sopenharmony_ci	case X86_FEATURE_POPCNT:
298c2ecf20Sopenharmony_ci		/*
308c2ecf20Sopenharmony_ci		 * It has been requested that we don't validate the !POPCNT
318c2ecf20Sopenharmony_ci		 * feature path which is a "very very small percentage of
328c2ecf20Sopenharmony_ci		 * machines".
338c2ecf20Sopenharmony_ci		 */
348c2ecf20Sopenharmony_ci		alt->skip_orig = true;
358c2ecf20Sopenharmony_ci		break;
368c2ecf20Sopenharmony_ci	default:
378c2ecf20Sopenharmony_ci		break;
388c2ecf20Sopenharmony_ci	}
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cibool arch_support_alt_relocation(struct special_alt *special_alt,
428c2ecf20Sopenharmony_ci				 struct instruction *insn,
438c2ecf20Sopenharmony_ci				 struct reloc *reloc)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	/*
468c2ecf20Sopenharmony_ci	 * The x86 alternatives code adjusts the offsets only when it
478c2ecf20Sopenharmony_ci	 * encounters a branch instruction at the very beginning of the
488c2ecf20Sopenharmony_ci	 * replacement group.
498c2ecf20Sopenharmony_ci	 */
508c2ecf20Sopenharmony_ci	return insn->offset == special_alt->new_off &&
518c2ecf20Sopenharmony_ci	       (insn->type == INSN_CALL || is_jump(insn));
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci/*
558c2ecf20Sopenharmony_ci * There are 3 basic jump table patterns:
568c2ecf20Sopenharmony_ci *
578c2ecf20Sopenharmony_ci * 1. jmpq *[rodata addr](,%reg,8)
588c2ecf20Sopenharmony_ci *
598c2ecf20Sopenharmony_ci *    This is the most common case by far.  It jumps to an address in a simple
608c2ecf20Sopenharmony_ci *    jump table which is stored in .rodata.
618c2ecf20Sopenharmony_ci *
628c2ecf20Sopenharmony_ci * 2. jmpq *[rodata addr](%rip)
638c2ecf20Sopenharmony_ci *
648c2ecf20Sopenharmony_ci *    This is caused by a rare GCC quirk, currently only seen in three driver
658c2ecf20Sopenharmony_ci *    functions in the kernel, only with certain obscure non-distro configs.
668c2ecf20Sopenharmony_ci *
678c2ecf20Sopenharmony_ci *    As part of an optimization, GCC makes a copy of an existing switch jump
688c2ecf20Sopenharmony_ci *    table, modifies it, and then hard-codes the jump (albeit with an indirect
698c2ecf20Sopenharmony_ci *    jump) to use a single entry in the table.  The rest of the jump table and
708c2ecf20Sopenharmony_ci *    some of its jump targets remain as dead code.
718c2ecf20Sopenharmony_ci *
728c2ecf20Sopenharmony_ci *    In such a case we can just crudely ignore all unreachable instruction
738c2ecf20Sopenharmony_ci *    warnings for the entire object file.  Ideally we would just ignore them
748c2ecf20Sopenharmony_ci *    for the function, but that would require redesigning the code quite a
758c2ecf20Sopenharmony_ci *    bit.  And honestly that's just not worth doing: unreachable instruction
768c2ecf20Sopenharmony_ci *    warnings are of questionable value anyway, and this is such a rare issue.
778c2ecf20Sopenharmony_ci *
788c2ecf20Sopenharmony_ci * 3. mov [rodata addr],%reg1
798c2ecf20Sopenharmony_ci *    ... some instructions ...
808c2ecf20Sopenharmony_ci *    jmpq *(%reg1,%reg2,8)
818c2ecf20Sopenharmony_ci *
828c2ecf20Sopenharmony_ci *    This is a fairly uncommon pattern which is new for GCC 6.  As of this
838c2ecf20Sopenharmony_ci *    writing, there are 11 occurrences of it in the allmodconfig kernel.
848c2ecf20Sopenharmony_ci *
858c2ecf20Sopenharmony_ci *    As of GCC 7 there are quite a few more of these and the 'in between' code
868c2ecf20Sopenharmony_ci *    is significant. Esp. with KASAN enabled some of the code between the mov
878c2ecf20Sopenharmony_ci *    and jmpq uses .rodata itself, which can confuse things.
888c2ecf20Sopenharmony_ci *
898c2ecf20Sopenharmony_ci *    TODO: Once we have DWARF CFI and smarter instruction decoding logic,
908c2ecf20Sopenharmony_ci *    ensure the same register is used in the mov and jump instructions.
918c2ecf20Sopenharmony_ci *
928c2ecf20Sopenharmony_ci *    NOTE: RETPOLINE made it harder still to decode dynamic jumps.
938c2ecf20Sopenharmony_ci */
948c2ecf20Sopenharmony_cistruct reloc *arch_find_switch_table(struct objtool_file *file,
958c2ecf20Sopenharmony_ci				    struct instruction *insn)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	struct reloc  *text_reloc, *rodata_reloc;
988c2ecf20Sopenharmony_ci	struct section *table_sec;
998c2ecf20Sopenharmony_ci	unsigned long table_offset;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	/* look for a relocation which references .rodata */
1028c2ecf20Sopenharmony_ci	text_reloc = find_reloc_by_dest_range(file->elf, insn->sec,
1038c2ecf20Sopenharmony_ci					      insn->offset, insn->len);
1048c2ecf20Sopenharmony_ci	if (!text_reloc || text_reloc->sym->type != STT_SECTION ||
1058c2ecf20Sopenharmony_ci	    !text_reloc->sym->sec->rodata)
1068c2ecf20Sopenharmony_ci		return NULL;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	table_offset = text_reloc->addend;
1098c2ecf20Sopenharmony_ci	table_sec = text_reloc->sym->sec;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	if (text_reloc->type == R_X86_64_PC32)
1128c2ecf20Sopenharmony_ci		table_offset += 4;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	/*
1158c2ecf20Sopenharmony_ci	 * Make sure the .rodata address isn't associated with a
1168c2ecf20Sopenharmony_ci	 * symbol.  GCC jump tables are anonymous data.
1178c2ecf20Sopenharmony_ci	 *
1188c2ecf20Sopenharmony_ci	 * Also support C jump tables which are in the same format as
1198c2ecf20Sopenharmony_ci	 * switch jump tables.  For objtool to recognize them, they
1208c2ecf20Sopenharmony_ci	 * need to be placed in the C_JUMP_TABLE_SECTION section.  They
1218c2ecf20Sopenharmony_ci	 * have symbols associated with them.
1228c2ecf20Sopenharmony_ci	 */
1238c2ecf20Sopenharmony_ci	if (find_symbol_containing(table_sec, table_offset) &&
1248c2ecf20Sopenharmony_ci	    strcmp(table_sec->name, C_JUMP_TABLE_SECTION))
1258c2ecf20Sopenharmony_ci		return NULL;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	/*
1288c2ecf20Sopenharmony_ci	 * Each table entry has a rela associated with it.  The rela
1298c2ecf20Sopenharmony_ci	 * should reference text in the same function as the original
1308c2ecf20Sopenharmony_ci	 * instruction.
1318c2ecf20Sopenharmony_ci	 */
1328c2ecf20Sopenharmony_ci	rodata_reloc = find_reloc_by_dest(file->elf, table_sec, table_offset);
1338c2ecf20Sopenharmony_ci	if (!rodata_reloc)
1348c2ecf20Sopenharmony_ci		return NULL;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	/*
1378c2ecf20Sopenharmony_ci	 * Use of RIP-relative switch jumps is quite rare, and
1388c2ecf20Sopenharmony_ci	 * indicates a rare GCC quirk/bug which can leave dead
1398c2ecf20Sopenharmony_ci	 * code behind.
1408c2ecf20Sopenharmony_ci	 */
1418c2ecf20Sopenharmony_ci	if (text_reloc->type == R_X86_64_PC32)
1428c2ecf20Sopenharmony_ci		file->ignore_unreachables = true;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	return rodata_reloc;
1458c2ecf20Sopenharmony_ci}
146