162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci#include <string.h>
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <objtool/special.h>
562306a36Sopenharmony_ci#include <objtool/builtin.h>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#define X86_FEATURE_POPCNT (4 * 32 + 23)
862306a36Sopenharmony_ci#define X86_FEATURE_SMAP   (9 * 32 + 20)
962306a36Sopenharmony_ci
1062306a36Sopenharmony_civoid arch_handle_alternative(unsigned short feature, struct special_alt *alt)
1162306a36Sopenharmony_ci{
1262306a36Sopenharmony_ci	switch (feature) {
1362306a36Sopenharmony_ci	case X86_FEATURE_SMAP:
1462306a36Sopenharmony_ci		/*
1562306a36Sopenharmony_ci		 * If UACCESS validation is enabled; force that alternative;
1662306a36Sopenharmony_ci		 * otherwise force it the other way.
1762306a36Sopenharmony_ci		 *
1862306a36Sopenharmony_ci		 * What we want to avoid is having both the original and the
1962306a36Sopenharmony_ci		 * alternative code flow at the same time, in that case we can
2062306a36Sopenharmony_ci		 * find paths that see the STAC but take the NOP instead of
2162306a36Sopenharmony_ci		 * CLAC and the other way around.
2262306a36Sopenharmony_ci		 */
2362306a36Sopenharmony_ci		if (opts.uaccess)
2462306a36Sopenharmony_ci			alt->skip_orig = true;
2562306a36Sopenharmony_ci		else
2662306a36Sopenharmony_ci			alt->skip_alt = true;
2762306a36Sopenharmony_ci		break;
2862306a36Sopenharmony_ci	case X86_FEATURE_POPCNT:
2962306a36Sopenharmony_ci		/*
3062306a36Sopenharmony_ci		 * It has been requested that we don't validate the !POPCNT
3162306a36Sopenharmony_ci		 * feature path which is a "very very small percentage of
3262306a36Sopenharmony_ci		 * machines".
3362306a36Sopenharmony_ci		 */
3462306a36Sopenharmony_ci		alt->skip_orig = true;
3562306a36Sopenharmony_ci		break;
3662306a36Sopenharmony_ci	default:
3762306a36Sopenharmony_ci		break;
3862306a36Sopenharmony_ci	}
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cibool arch_support_alt_relocation(struct special_alt *special_alt,
4262306a36Sopenharmony_ci				 struct instruction *insn,
4362306a36Sopenharmony_ci				 struct reloc *reloc)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	return true;
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/*
4962306a36Sopenharmony_ci * There are 3 basic jump table patterns:
5062306a36Sopenharmony_ci *
5162306a36Sopenharmony_ci * 1. jmpq *[rodata addr](,%reg,8)
5262306a36Sopenharmony_ci *
5362306a36Sopenharmony_ci *    This is the most common case by far.  It jumps to an address in a simple
5462306a36Sopenharmony_ci *    jump table which is stored in .rodata.
5562306a36Sopenharmony_ci *
5662306a36Sopenharmony_ci * 2. jmpq *[rodata addr](%rip)
5762306a36Sopenharmony_ci *
5862306a36Sopenharmony_ci *    This is caused by a rare GCC quirk, currently only seen in three driver
5962306a36Sopenharmony_ci *    functions in the kernel, only with certain obscure non-distro configs.
6062306a36Sopenharmony_ci *
6162306a36Sopenharmony_ci *    As part of an optimization, GCC makes a copy of an existing switch jump
6262306a36Sopenharmony_ci *    table, modifies it, and then hard-codes the jump (albeit with an indirect
6362306a36Sopenharmony_ci *    jump) to use a single entry in the table.  The rest of the jump table and
6462306a36Sopenharmony_ci *    some of its jump targets remain as dead code.
6562306a36Sopenharmony_ci *
6662306a36Sopenharmony_ci *    In such a case we can just crudely ignore all unreachable instruction
6762306a36Sopenharmony_ci *    warnings for the entire object file.  Ideally we would just ignore them
6862306a36Sopenharmony_ci *    for the function, but that would require redesigning the code quite a
6962306a36Sopenharmony_ci *    bit.  And honestly that's just not worth doing: unreachable instruction
7062306a36Sopenharmony_ci *    warnings are of questionable value anyway, and this is such a rare issue.
7162306a36Sopenharmony_ci *
7262306a36Sopenharmony_ci * 3. mov [rodata addr],%reg1
7362306a36Sopenharmony_ci *    ... some instructions ...
7462306a36Sopenharmony_ci *    jmpq *(%reg1,%reg2,8)
7562306a36Sopenharmony_ci *
7662306a36Sopenharmony_ci *    This is a fairly uncommon pattern which is new for GCC 6.  As of this
7762306a36Sopenharmony_ci *    writing, there are 11 occurrences of it in the allmodconfig kernel.
7862306a36Sopenharmony_ci *
7962306a36Sopenharmony_ci *    As of GCC 7 there are quite a few more of these and the 'in between' code
8062306a36Sopenharmony_ci *    is significant. Esp. with KASAN enabled some of the code between the mov
8162306a36Sopenharmony_ci *    and jmpq uses .rodata itself, which can confuse things.
8262306a36Sopenharmony_ci *
8362306a36Sopenharmony_ci *    TODO: Once we have DWARF CFI and smarter instruction decoding logic,
8462306a36Sopenharmony_ci *    ensure the same register is used in the mov and jump instructions.
8562306a36Sopenharmony_ci *
8662306a36Sopenharmony_ci *    NOTE: RETPOLINE made it harder still to decode dynamic jumps.
8762306a36Sopenharmony_ci */
8862306a36Sopenharmony_cistruct reloc *arch_find_switch_table(struct objtool_file *file,
8962306a36Sopenharmony_ci				    struct instruction *insn)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	struct reloc  *text_reloc, *rodata_reloc;
9262306a36Sopenharmony_ci	struct section *table_sec;
9362306a36Sopenharmony_ci	unsigned long table_offset;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	/* look for a relocation which references .rodata */
9662306a36Sopenharmony_ci	text_reloc = find_reloc_by_dest_range(file->elf, insn->sec,
9762306a36Sopenharmony_ci					      insn->offset, insn->len);
9862306a36Sopenharmony_ci	if (!text_reloc || text_reloc->sym->type != STT_SECTION ||
9962306a36Sopenharmony_ci	    !text_reloc->sym->sec->rodata)
10062306a36Sopenharmony_ci		return NULL;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	table_offset = reloc_addend(text_reloc);
10362306a36Sopenharmony_ci	table_sec = text_reloc->sym->sec;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	if (reloc_type(text_reloc) == R_X86_64_PC32)
10662306a36Sopenharmony_ci		table_offset += 4;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	/*
10962306a36Sopenharmony_ci	 * Make sure the .rodata address isn't associated with a
11062306a36Sopenharmony_ci	 * symbol.  GCC jump tables are anonymous data.
11162306a36Sopenharmony_ci	 *
11262306a36Sopenharmony_ci	 * Also support C jump tables which are in the same format as
11362306a36Sopenharmony_ci	 * switch jump tables.  For objtool to recognize them, they
11462306a36Sopenharmony_ci	 * need to be placed in the C_JUMP_TABLE_SECTION section.  They
11562306a36Sopenharmony_ci	 * have symbols associated with them.
11662306a36Sopenharmony_ci	 */
11762306a36Sopenharmony_ci	if (find_symbol_containing(table_sec, table_offset) &&
11862306a36Sopenharmony_ci	    strcmp(table_sec->name, C_JUMP_TABLE_SECTION))
11962306a36Sopenharmony_ci		return NULL;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	/*
12262306a36Sopenharmony_ci	 * Each table entry has a rela associated with it.  The rela
12362306a36Sopenharmony_ci	 * should reference text in the same function as the original
12462306a36Sopenharmony_ci	 * instruction.
12562306a36Sopenharmony_ci	 */
12662306a36Sopenharmony_ci	rodata_reloc = find_reloc_by_dest(file->elf, table_sec, table_offset);
12762306a36Sopenharmony_ci	if (!rodata_reloc)
12862306a36Sopenharmony_ci		return NULL;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	/*
13162306a36Sopenharmony_ci	 * Use of RIP-relative switch jumps is quite rare, and
13262306a36Sopenharmony_ci	 * indicates a rare GCC quirk/bug which can leave dead
13362306a36Sopenharmony_ci	 * code behind.
13462306a36Sopenharmony_ci	 */
13562306a36Sopenharmony_ci	if (reloc_type(text_reloc) == R_X86_64_PC32)
13662306a36Sopenharmony_ci		file->ignore_unreachables = true;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	return rodata_reloc;
13962306a36Sopenharmony_ci}
140