brintos

brintos / linux-shallow public Read only

0
0
Text · 4.1 KiB · 097a69d Raw
183 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2015 Josh Poimboeuf <jpoimboe@redhat.com>4 */5 6/*7 * This file reads all the special sections which have alternate instructions8 * which can be patched in or redirected to at runtime.9 */10 11#include <stdlib.h>12#include <string.h>13 14#include <arch/special.h>15#include <objtool/builtin.h>16#include <objtool/special.h>17#include <objtool/warn.h>18#include <objtool/endianness.h>19 20struct special_entry {21	const char *sec;22	bool group, jump_or_nop;23	unsigned char size, orig, new;24	unsigned char orig_len, new_len; /* group only */25	unsigned char feature; /* ALTERNATIVE macro CPU feature */26	unsigned char key; /* jump_label key */27};28 29static const struct special_entry entries[] = {30	{31		.sec = ".altinstructions",32		.group = true,33		.size = ALT_ENTRY_SIZE,34		.orig = ALT_ORIG_OFFSET,35		.orig_len = ALT_ORIG_LEN_OFFSET,36		.new = ALT_NEW_OFFSET,37		.new_len = ALT_NEW_LEN_OFFSET,38		.feature = ALT_FEATURE_OFFSET,39	},40	{41		.sec = "__jump_table",42		.jump_or_nop = true,43		.size = JUMP_ENTRY_SIZE,44		.orig = JUMP_ORIG_OFFSET,45		.new = JUMP_NEW_OFFSET,46		.key = JUMP_KEY_OFFSET,47	},48	{49		.sec = "__ex_table",50		.size = EX_ENTRY_SIZE,51		.orig = EX_ORIG_OFFSET,52		.new = EX_NEW_OFFSET,53	},54	{},55};56 57void __weak arch_handle_alternative(unsigned short feature, struct special_alt *alt)58{59}60 61static void reloc_to_sec_off(struct reloc *reloc, struct section **sec,62			     unsigned long *off)63{64	*sec = reloc->sym->sec;65	*off = reloc->sym->offset + reloc_addend(reloc);66}67 68static int get_alt_entry(struct elf *elf, const struct special_entry *entry,69			 struct section *sec, int idx,70			 struct special_alt *alt)71{72	struct reloc *orig_reloc, *new_reloc;73	unsigned long offset;74 75	offset = idx * entry->size;76 77	alt->group = entry->group;78	alt->jump_or_nop = entry->jump_or_nop;79 80	if (alt->group) {81		alt->orig_len = *(unsigned char *)(sec->data->d_buf + offset +82						   entry->orig_len);83		alt->new_len = *(unsigned char *)(sec->data->d_buf + offset +84						  entry->new_len);85	}86 87	orig_reloc = find_reloc_by_dest(elf, sec, offset + entry->orig);88	if (!orig_reloc) {89		WARN_FUNC("can't find orig reloc", sec, offset + entry->orig);90		return -1;91	}92 93	reloc_to_sec_off(orig_reloc, &alt->orig_sec, &alt->orig_off);94 95	if (entry->feature) {96		unsigned short feature;97 98		feature = bswap_if_needed(elf,99					  *(unsigned short *)(sec->data->d_buf +100							      offset +101							      entry->feature));102		arch_handle_alternative(feature, alt);103	}104 105	if (!entry->group || alt->new_len) {106		new_reloc = find_reloc_by_dest(elf, sec, offset + entry->new);107		if (!new_reloc) {108			WARN_FUNC("can't find new reloc",109				  sec, offset + entry->new);110			return -1;111		}112 113		reloc_to_sec_off(new_reloc, &alt->new_sec, &alt->new_off);114 115		/* _ASM_EXTABLE_EX hack */116		if (alt->new_off >= 0x7ffffff0)117			alt->new_off -= 0x7ffffff0;118	}119 120	if (entry->key) {121		struct reloc *key_reloc;122 123		key_reloc = find_reloc_by_dest(elf, sec, offset + entry->key);124		if (!key_reloc) {125			WARN_FUNC("can't find key reloc",126				  sec, offset + entry->key);127			return -1;128		}129		alt->key_addend = reloc_addend(key_reloc);130	}131 132	return 0;133}134 135/*136 * Read all the special sections and create a list of special_alt structs which137 * describe all the alternate instructions which can be patched in or138 * redirected to at runtime.139 */140int special_get_alts(struct elf *elf, struct list_head *alts)141{142	const struct special_entry *entry;143	struct section *sec;144	unsigned int nr_entries;145	struct special_alt *alt;146	int idx, ret;147 148	INIT_LIST_HEAD(alts);149 150	for (entry = entries; entry->sec; entry++) {151		sec = find_section_by_name(elf, entry->sec);152		if (!sec)153			continue;154 155		if (sec->sh.sh_size % entry->size != 0) {156			WARN("%s size not a multiple of %d",157			     sec->name, entry->size);158			return -1;159		}160 161		nr_entries = sec->sh.sh_size / entry->size;162 163		for (idx = 0; idx < nr_entries; idx++) {164			alt = malloc(sizeof(*alt));165			if (!alt) {166				WARN("malloc failed");167				return -1;168			}169			memset(alt, 0, sizeof(*alt));170 171			ret = get_alt_entry(elf, entry, sec, idx, alt);172			if (ret > 0)173				continue;174			if (ret < 0)175				return ret;176 177			list_add_tail(&alt->list, alts);178		}179	}180 181	return 0;182}183