mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	This is a major rewrite of objtool. Instead of only tracking frame pointer changes, it now tracks all stack-related operations, including all register saves/restores. In addition to making stack validation more robust, this also paves the way for undwarf generation. Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com> Cc: Andy Lutomirski <luto@kernel.org> Cc: Jiri Slaby <jslaby@suse.cz> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: live-patching@vger.kernel.org Link: http://lkml.kernel.org/r/678bd94c0566c6129bcc376cddb259c4c5633004.1498659915.git.jpoimboe@redhat.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
		
			
				
	
	
		
			193 lines
		
	
	
	
		
			4.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			193 lines
		
	
	
	
		
			4.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (C) 2015 Josh Poimboeuf <jpoimboe@redhat.com>
 | 
						|
 *
 | 
						|
 * This program is free software; you can redistribute it and/or
 | 
						|
 * modify it under the terms of the GNU General Public License
 | 
						|
 * as published by the Free Software Foundation; either version 2
 | 
						|
 * of the License, or (at your option) any later version.
 | 
						|
 *
 | 
						|
 * This program is distributed in the hope that it will be useful,
 | 
						|
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
 * GNU General Public License for more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU General Public License
 | 
						|
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 | 
						|
 */
 | 
						|
 | 
						|
/*
 | 
						|
 * This file reads all the special sections which have alternate instructions
 | 
						|
 * which can be patched in or redirected to at runtime.
 | 
						|
 */
 | 
						|
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
 | 
						|
#include "special.h"
 | 
						|
#include "warn.h"
 | 
						|
 | 
						|
#define EX_ENTRY_SIZE		12
 | 
						|
#define EX_ORIG_OFFSET		0
 | 
						|
#define EX_NEW_OFFSET		4
 | 
						|
 | 
						|
#define JUMP_ENTRY_SIZE		24
 | 
						|
#define JUMP_ORIG_OFFSET	0
 | 
						|
#define JUMP_NEW_OFFSET		8
 | 
						|
 | 
						|
#define ALT_ENTRY_SIZE		13
 | 
						|
#define ALT_ORIG_OFFSET		0
 | 
						|
#define ALT_NEW_OFFSET		4
 | 
						|
#define ALT_FEATURE_OFFSET	8
 | 
						|
#define ALT_ORIG_LEN_OFFSET	10
 | 
						|
#define ALT_NEW_LEN_OFFSET	11
 | 
						|
 | 
						|
#define X86_FEATURE_POPCNT (4*32+23)
 | 
						|
 | 
						|
struct special_entry {
 | 
						|
	const char *sec;
 | 
						|
	bool group, jump_or_nop;
 | 
						|
	unsigned char size, orig, new;
 | 
						|
	unsigned char orig_len, new_len; /* group only */
 | 
						|
	unsigned char feature; /* ALTERNATIVE macro CPU feature */
 | 
						|
};
 | 
						|
 | 
						|
struct special_entry entries[] = {
 | 
						|
	{
 | 
						|
		.sec = ".altinstructions",
 | 
						|
		.group = true,
 | 
						|
		.size = ALT_ENTRY_SIZE,
 | 
						|
		.orig = ALT_ORIG_OFFSET,
 | 
						|
		.orig_len = ALT_ORIG_LEN_OFFSET,
 | 
						|
		.new = ALT_NEW_OFFSET,
 | 
						|
		.new_len = ALT_NEW_LEN_OFFSET,
 | 
						|
		.feature = ALT_FEATURE_OFFSET,
 | 
						|
	},
 | 
						|
	{
 | 
						|
		.sec = "__jump_table",
 | 
						|
		.jump_or_nop = true,
 | 
						|
		.size = JUMP_ENTRY_SIZE,
 | 
						|
		.orig = JUMP_ORIG_OFFSET,
 | 
						|
		.new = JUMP_NEW_OFFSET,
 | 
						|
	},
 | 
						|
	{
 | 
						|
		.sec = "__ex_table",
 | 
						|
		.size = EX_ENTRY_SIZE,
 | 
						|
		.orig = EX_ORIG_OFFSET,
 | 
						|
		.new = EX_NEW_OFFSET,
 | 
						|
	},
 | 
						|
	{},
 | 
						|
};
 | 
						|
 | 
						|
static int get_alt_entry(struct elf *elf, struct special_entry *entry,
 | 
						|
			 struct section *sec, int idx,
 | 
						|
			 struct special_alt *alt)
 | 
						|
{
 | 
						|
	struct rela *orig_rela, *new_rela;
 | 
						|
	unsigned long offset;
 | 
						|
 | 
						|
	offset = idx * entry->size;
 | 
						|
 | 
						|
	alt->group = entry->group;
 | 
						|
	alt->jump_or_nop = entry->jump_or_nop;
 | 
						|
 | 
						|
	if (alt->group) {
 | 
						|
		alt->orig_len = *(unsigned char *)(sec->data->d_buf + offset +
 | 
						|
						   entry->orig_len);
 | 
						|
		alt->new_len = *(unsigned char *)(sec->data->d_buf + offset +
 | 
						|
						  entry->new_len);
 | 
						|
	}
 | 
						|
 | 
						|
	if (entry->feature) {
 | 
						|
		unsigned short feature;
 | 
						|
 | 
						|
		feature = *(unsigned short *)(sec->data->d_buf + offset +
 | 
						|
					      entry->feature);
 | 
						|
 | 
						|
		/*
 | 
						|
		 * It has been requested that we don't validate the !POPCNT
 | 
						|
		 * feature path which is a "very very small percentage of
 | 
						|
		 * machines".
 | 
						|
		 */
 | 
						|
		if (feature == X86_FEATURE_POPCNT)
 | 
						|
			alt->skip_orig = true;
 | 
						|
	}
 | 
						|
 | 
						|
	orig_rela = find_rela_by_dest(sec, offset + entry->orig);
 | 
						|
	if (!orig_rela) {
 | 
						|
		WARN_FUNC("can't find orig rela", sec, offset + entry->orig);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
	if (orig_rela->sym->type != STT_SECTION) {
 | 
						|
		WARN_FUNC("don't know how to handle non-section rela symbol %s",
 | 
						|
			   sec, offset + entry->orig, orig_rela->sym->name);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	alt->orig_sec = orig_rela->sym->sec;
 | 
						|
	alt->orig_off = orig_rela->addend;
 | 
						|
 | 
						|
	if (!entry->group || alt->new_len) {
 | 
						|
		new_rela = find_rela_by_dest(sec, offset + entry->new);
 | 
						|
		if (!new_rela) {
 | 
						|
			WARN_FUNC("can't find new rela",
 | 
						|
				  sec, offset + entry->new);
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
 | 
						|
		alt->new_sec = new_rela->sym->sec;
 | 
						|
		alt->new_off = (unsigned int)new_rela->addend;
 | 
						|
 | 
						|
		/* _ASM_EXTABLE_EX hack */
 | 
						|
		if (alt->new_off >= 0x7ffffff0)
 | 
						|
			alt->new_off -= 0x7ffffff0;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Read all the special sections and create a list of special_alt structs which
 | 
						|
 * describe all the alternate instructions which can be patched in or
 | 
						|
 * redirected to at runtime.
 | 
						|
 */
 | 
						|
int special_get_alts(struct elf *elf, struct list_head *alts)
 | 
						|
{
 | 
						|
	struct special_entry *entry;
 | 
						|
	struct section *sec;
 | 
						|
	unsigned int nr_entries;
 | 
						|
	struct special_alt *alt;
 | 
						|
	int idx, ret;
 | 
						|
 | 
						|
	INIT_LIST_HEAD(alts);
 | 
						|
 | 
						|
	for (entry = entries; entry->sec; entry++) {
 | 
						|
		sec = find_section_by_name(elf, entry->sec);
 | 
						|
		if (!sec)
 | 
						|
			continue;
 | 
						|
 | 
						|
		if (sec->len % entry->size != 0) {
 | 
						|
			WARN("%s size not a multiple of %d",
 | 
						|
			     sec->name, entry->size);
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
 | 
						|
		nr_entries = sec->len / entry->size;
 | 
						|
 | 
						|
		for (idx = 0; idx < nr_entries; idx++) {
 | 
						|
			alt = malloc(sizeof(*alt));
 | 
						|
			if (!alt) {
 | 
						|
				WARN("malloc failed");
 | 
						|
				return -1;
 | 
						|
			}
 | 
						|
			memset(alt, 0, sizeof(*alt));
 | 
						|
 | 
						|
			ret = get_alt_entry(elf, entry, sec, idx, alt);
 | 
						|
			if (ret)
 | 
						|
				return ret;
 | 
						|
 | 
						|
			list_add_tail(&alt->list, alts);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 |