Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 15ce2658 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'modules-next-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux

Pull module updates from Rusty Russell:
 "Quentin opened a can of worms by adding extable entry checking to
  modpost, but most architectures seem fixed now.  Thanks to all
  involved.

  Last minute rebase because I noticed a "[PATCH]" had snuck into a
  commit message somehow"

* tag 'modules-next-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux:
  modpost: don't emit section mismatch warnings for compiler optimizations
  modpost: expand pattern matching to support substring matches
  modpost: do not try to match the SHT_NUL section.
  modpost: fix extable entry size calculation.
  modpost: fix inverted logic in is_extable_fault_address().
  modpost: handle -ffunction-sections
  modpost: Whitelist .text.fixup and .exception.text
  params: handle quotes properly for values not of form foo="bar".
  modpost: document the use of struct section_check.
  modpost: handle relocations mismatch in __ex_table.
  scripts: add check_extable.sh script.
  modpost: mismatch_handler: retrieve tosym information only when needed.
  modpost: factorize symbol pretty print in get_pretty_name().
  modpost: add handler function pointer to sectioncheck.
  modpost: add .sched.text and .kprobes.text to the TEXT_SECTIONS list.
  modpost: add strict white-listing when referencing sections.
  module: do not print allocation-fail warning on bogus user buffer size
  kernel/module.c: fix typos in message about unused symbols
parents f3ca10dd 4a3893d0
Loading
Loading
Loading
Loading
+5 −4
Original line number Diff line number Diff line
@@ -387,8 +387,8 @@ static bool check_symbol(const struct symsearch *syms,
		pr_warn("Symbol %s is marked as UNUSED, however this module is "
			"using it.\n", fsa->name);
		pr_warn("This symbol will go away in the future.\n");
		pr_warn("Please evalute if this is the right api to use and if "
			"it really is, submit a report the linux kernel "
		pr_warn("Please evaluate if this is the right api to use and "
			"if it really is, submit a report to the linux kernel "
			"mailing list together with submitting your code for "
			"inclusion.\n");
	}
@@ -2511,7 +2511,8 @@ static int copy_module_from_user(const void __user *umod, unsigned long len,
		return err;

	/* Suck in entire file: we'll want most of it. */
	info->hdr = vmalloc(info->len);
	info->hdr = __vmalloc(info->len,
			GFP_KERNEL | __GFP_HIGHMEM | __GFP_NOWARN, PAGE_KERNEL);
	if (!info->hdr)
		return -ENOMEM;

+2 −2
Original line number Diff line number Diff line
@@ -173,9 +173,9 @@ static char *next_arg(char *args, char **param, char **val)
			if (args[i-1] == '"')
				args[i-1] = '\0';
		}
	}
	if (quoted && args[i-1] == '"')
		args[i-1] = '\0';
	}

	if (args[i]) {
		args[i] = '\0';
+146 −0
Original line number Diff line number Diff line
#! /bin/bash
# (c) 2015, Quentin Casasnovas <quentin.casasnovas@oracle.com>

obj=$1

file ${obj} | grep -q ELF || (echo "${obj} is not and ELF file." 1>&2 ; exit 0)

# Bail out early if there isn't an __ex_table section in this object file.
objdump -hj __ex_table ${obj} 2> /dev/null > /dev/null
[ $? -ne 0 ] && exit 0

white_list=.text,.fixup

suspicious_relocs=$(objdump -rj __ex_table ${obj}  | tail -n +6 |
			grep -v $(eval echo -e{${white_list}}) | awk '{print $3}')

# No suspicious relocs in __ex_table, jobs a good'un
[ -z "${suspicious_relocs}" ] && exit 0


# After this point, something is seriously wrong since we just found out we
# have some relocations in __ex_table which point to sections which aren't
# white listed.  If you're adding a new section in the Linux kernel, and
# you're expecting this section to contain code which can fault (i.e. the
# __ex_table relocation to your new section is expected), simply add your
# new section to the white_list variable above.  If not, you're probably
# doing something wrong and the rest of this code is just trying to print
# you more information about it.

function find_section_offset_from_symbol()
{
    eval $(objdump -t ${obj} | grep ${1} | sed 's/\([0-9a-f]\+\) .\{7\} \([^ \t]\+\).*/section="\2"; section_offset="0x\1" /')

    # addr2line takes addresses in hexadecimal...
    section_offset=$(printf "0x%016x" $(( ${section_offset} + $2 )) )
}

function find_symbol_and_offset_from_reloc()
{
    # Extract symbol and offset from the objdump output
    eval $(echo $reloc | sed 's/\([^+]\+\)+\?\(0x[0-9a-f]\+\)\?/symbol="\1"; symbol_offset="\2"/')

    # When the relocation points to the begining of a symbol or section, it
    # won't print the offset since it is zero.
    if [ -z "${symbol_offset}" ]; then
	symbol_offset=0x0
    fi
}

function find_alt_replacement_target()
{
    # The target of the .altinstr_replacement is the relocation just before
    # the .altinstr_replacement one.
    eval $(objdump -rj .altinstructions ${obj} | grep -B1 "${section}+${section_offset}" | head -n1 | awk '{print $3}' |
	   sed 's/\([^+]\+\)+\(0x[0-9a-f]\+\)/alt_target_section="\1"; alt_target_offset="\2"/')
}

function handle_alt_replacement_reloc()
{
    # This will define alt_target_section and alt_target_section_offset
    find_alt_replacement_target ${section} ${section_offset}

    echo "Error: found a reference to .altinstr_replacement in __ex_table:"
    addr2line -fip -j ${alt_target_section} -e ${obj} ${alt_target_offset} | awk '{print "\t" $0}'

    error=true
}

function is_executable_section()
{
    objdump -hwj ${section} ${obj} | grep -q CODE
    return $?
}

function handle_suspicious_generic_reloc()
{
    if is_executable_section ${section}; then
	# We've got a relocation to a non white listed _executable_
	# section, print a warning so the developper adds the section to
	# the white list or fix his code.  We try to pretty-print the file
	# and line number where that relocation was added.
	echo "Warning: found a reference to section \"${section}\" in __ex_table:"
	addr2line -fip -j ${section} -e ${obj} ${section_offset} | awk '{print "\t" $0}'
    else
	# Something is definitively wrong here since we've got a relocation
	# to a non-executable section, there's no way this would ever be
	# running in the kernel.
	echo "Error: found a reference to non-executable section \"${section}\" in __ex_table at offset ${section_offset}"
	error=true
    fi
}

function handle_suspicious_reloc()
{
    case "${section}" in
	".altinstr_replacement")
	    handle_alt_replacement_reloc ${section} ${section_offset}
	    ;;
	*)
	    handle_suspicious_generic_reloc ${section} ${section_offset}
	    ;;
    esac
}

function diagnose()
{

    for reloc in ${suspicious_relocs}; do
	# Let's find out where the target of the relocation in __ex_table
	# is, this will define ${symbol} and ${symbol_offset}
	find_symbol_and_offset_from_reloc ${reloc}

	# When there's a global symbol at the place of the relocation,
	# objdump will use it instead of giving us a section+offset, so
	# let's find out which section is this symbol in and the total
	# offset withing that section.
	find_section_offset_from_symbol ${symbol} ${symbol_offset}

	# In this case objdump was presenting us with a reloc to a symbol
	# rather than a section. Now that we've got the actual section,
	# we can skip it if it's in the white_list.
	if [ -z "$( echo $section | grep -v $(eval echo -e{${white_list}}))" ]; then
	    continue;
	fi

	# Will either print a warning if the relocation happens to be in a
	# section we do not know but has executable bit set, or error out.
	handle_suspicious_reloc
    done
}

function check_debug_info() {
    objdump -hj .debug_info ${obj} 2> /dev/null > /dev/null ||
	echo -e "${obj} does not contain debug information, the addr2line output will be limited.\n" \
	     "Recompile ${obj} with CONFIG_DEBUG_INFO to get a more useful output."
}

check_debug_info

diagnose

if [ "${error}" ]; then
    exit 1
fi

exit 0
+287 −54
Original line number Diff line number Diff line
@@ -776,6 +776,7 @@ static const char *sech_name(struct elf_info *elf, Elf_Shdr *sechdr)
 * "foo" will match an exact string equal to "foo"
 * "*foo" will match a string that ends with "foo"
 * "foo*" will match a string that begins with "foo"
 * "*foo*" will match a string that contains "foo"
 */
static int match(const char *sym, const char * const pat[])
{
@@ -784,8 +785,17 @@ static int match(const char *sym, const char * const pat[])
		p = *pat++;
		const char *endp = p + strlen(p) - 1;

		/* "*foo*" */
		if (*p == '*' && *endp == '*') {
			char *here, *bare = strndup(p + 1, strlen(p) - 2);

			here = strstr(sym, bare);
			free(bare);
			if (here != NULL)
				return 1;
		}
		/* "*foo" */
		if (*p == '*') {
		else if (*p == '*') {
			if (strrcmp(sym, p + 1) == 0)
				return 1;
		}
@@ -873,7 +883,10 @@ static void check_section(const char *modname, struct elf_info *elf,
#define ALL_EXIT_SECTIONS EXIT_SECTIONS, ALL_XXXEXIT_SECTIONS

#define DATA_SECTIONS ".data", ".data.rel"
#define TEXT_SECTIONS ".text", ".text.unlikely"
#define TEXT_SECTIONS ".text", ".text.unlikely", ".sched.text", \
		".kprobes.text"
#define OTHER_TEXT_SECTIONS ".ref.text", ".head.text", ".spinlock.text", \
		".fixup", ".entry.text", ".exception.text", ".text.*"

#define INIT_SECTIONS      ".init.*"
#define MEM_INIT_SECTIONS  ".meminit.*"
@@ -881,6 +894,9 @@ static void check_section(const char *modname, struct elf_info *elf,
#define EXIT_SECTIONS      ".exit.*"
#define MEM_EXIT_SECTIONS  ".memexit.*"

#define ALL_TEXT_SECTIONS  ALL_INIT_TEXT_SECTIONS, ALL_EXIT_TEXT_SECTIONS, \
		TEXT_SECTIONS, OTHER_TEXT_SECTIONS

/* init data sections */
static const char *const init_data_sections[] =
	{ ALL_INIT_DATA_SECTIONS, NULL };
@@ -892,6 +908,9 @@ static const char *const init_sections[] = { ALL_INIT_SECTIONS, NULL };
static const char *const init_exit_sections[] =
	{ALL_INIT_SECTIONS, ALL_EXIT_SECTIONS, NULL };

/* all text sections */
static const char *const text_sections[] = { ALL_TEXT_SECTIONS, NULL };

/* data section */
static const char *const data_sections[] = { DATA_SECTIONS, NULL };

@@ -910,6 +929,7 @@ static const char *const data_sections[] = { DATA_SECTIONS, NULL };
static const char *const head_sections[] = { ".head.text*", NULL };
static const char *const linker_symbols[] =
	{ "__init_begin", "_sinittext", "_einittext", NULL };
static const char *const optim_symbols[] = { "*.constprop.*", NULL };

enum mismatch {
	TEXT_TO_ANY_INIT,
@@ -921,34 +941,65 @@ enum mismatch {
	ANY_INIT_TO_ANY_EXIT,
	ANY_EXIT_TO_ANY_INIT,
	EXPORT_TO_INIT_EXIT,
	EXTABLE_TO_NON_TEXT,
};

/**
 * Describe how to match sections on different criterias:
 *
 * @fromsec: Array of sections to be matched.
 *
 * @bad_tosec: Relocations applied to a section in @fromsec to a section in
 * this array is forbidden (black-list).  Can be empty.
 *
 * @good_tosec: Relocations applied to a section in @fromsec must be
 * targetting sections in this array (white-list).  Can be empty.
 *
 * @mismatch: Type of mismatch.
 *
 * @symbol_white_list: Do not match a relocation to a symbol in this list
 * even if it is targetting a section in @bad_to_sec.
 *
 * @handler: Specific handler to call when a match is found.  If NULL,
 * default_mismatch_handler() will be called.
 *
 */
struct sectioncheck {
	const char *fromsec[20];
	const char *tosec[20];
	const char *bad_tosec[20];
	const char *good_tosec[20];
	enum mismatch mismatch;
	const char *symbol_white_list[20];
	void (*handler)(const char *modname, struct elf_info *elf,
			const struct sectioncheck* const mismatch,
			Elf_Rela *r, Elf_Sym *sym, const char *fromsec);

};

static void extable_mismatch_handler(const char *modname, struct elf_info *elf,
				     const struct sectioncheck* const mismatch,
				     Elf_Rela *r, Elf_Sym *sym,
				     const char *fromsec);

static const struct sectioncheck sectioncheck[] = {
/* Do not reference init/exit code/data from
 * normal code and data
 */
{
	.fromsec = { TEXT_SECTIONS, NULL },
	.tosec   = { ALL_INIT_SECTIONS, NULL },
	.bad_tosec = { ALL_INIT_SECTIONS, NULL },
	.mismatch = TEXT_TO_ANY_INIT,
	.symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL },
},
{
	.fromsec = { DATA_SECTIONS, NULL },
	.tosec   = { ALL_XXXINIT_SECTIONS, NULL },
	.bad_tosec = { ALL_XXXINIT_SECTIONS, NULL },
	.mismatch = DATA_TO_ANY_INIT,
	.symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL },
},
{
	.fromsec = { DATA_SECTIONS, NULL },
	.tosec   = { INIT_SECTIONS, NULL },
	.bad_tosec = { INIT_SECTIONS, NULL },
	.mismatch = DATA_TO_ANY_INIT,
	.symbol_white_list = {
		"*_template", "*_timer", "*_sht", "*_ops",
@@ -957,56 +1008,66 @@ static const struct sectioncheck sectioncheck[] = {
},
{
	.fromsec = { TEXT_SECTIONS, NULL },
	.tosec   = { ALL_EXIT_SECTIONS, NULL },
	.bad_tosec = { ALL_EXIT_SECTIONS, NULL },
	.mismatch = TEXT_TO_ANY_EXIT,
	.symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL },
},
{
	.fromsec = { DATA_SECTIONS, NULL },
	.tosec   = { ALL_EXIT_SECTIONS, NULL },
	.bad_tosec = { ALL_EXIT_SECTIONS, NULL },
	.mismatch = DATA_TO_ANY_EXIT,
	.symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL },
},
/* Do not reference init code/data from meminit code/data */
{
	.fromsec = { ALL_XXXINIT_SECTIONS, NULL },
	.tosec   = { INIT_SECTIONS, NULL },
	.bad_tosec = { INIT_SECTIONS, NULL },
	.mismatch = XXXINIT_TO_SOME_INIT,
	.symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL },
},
/* Do not reference exit code/data from memexit code/data */
{
	.fromsec = { ALL_XXXEXIT_SECTIONS, NULL },
	.tosec   = { EXIT_SECTIONS, NULL },
	.bad_tosec = { EXIT_SECTIONS, NULL },
	.mismatch = XXXEXIT_TO_SOME_EXIT,
	.symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL },
},
/* Do not use exit code/data from init code */
{
	.fromsec = { ALL_INIT_SECTIONS, NULL },
	.tosec   = { ALL_EXIT_SECTIONS, NULL },
	.bad_tosec = { ALL_EXIT_SECTIONS, NULL },
	.mismatch = ANY_INIT_TO_ANY_EXIT,
	.symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL },
},
/* Do not use init code/data from exit code */
{
	.fromsec = { ALL_EXIT_SECTIONS, NULL },
	.tosec   = { ALL_INIT_SECTIONS, NULL },
	.bad_tosec = { ALL_INIT_SECTIONS, NULL },
	.mismatch = ANY_EXIT_TO_ANY_INIT,
	.symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL },
},
{
	.fromsec = { ALL_PCI_INIT_SECTIONS, NULL },
	.tosec   = { INIT_SECTIONS, NULL },
	.bad_tosec = { INIT_SECTIONS, NULL },
	.mismatch = ANY_INIT_TO_ANY_EXIT,
	.symbol_white_list = { NULL },
},
/* Do not export init/exit functions or data */
{
	.fromsec = { "__ksymtab*", NULL },
	.tosec   = { INIT_SECTIONS, EXIT_SECTIONS, NULL },
	.bad_tosec = { INIT_SECTIONS, EXIT_SECTIONS, NULL },
	.mismatch = EXPORT_TO_INIT_EXIT,
	.symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL },
},
{
	.fromsec = { "__ex_table", NULL },
	/* If you're adding any new black-listed sections in here, consider
	 * adding a special 'printer' for them in scripts/check_extable.
	 */
	.bad_tosec = { ".altinstr_replacement", NULL },
	.good_tosec = {ALL_TEXT_SECTIONS , NULL},
	.mismatch = EXTABLE_TO_NON_TEXT,
	.handler = extable_mismatch_handler,
}
};

@@ -1017,10 +1078,22 @@ static const struct sectioncheck *section_mismatch(
	int elems = sizeof(sectioncheck) / sizeof(struct sectioncheck);
	const struct sectioncheck *check = &sectioncheck[0];

	/*
	 * The target section could be the SHT_NUL section when we're
	 * handling relocations to un-resolved symbols, trying to match it
	 * doesn't make much sense and causes build failures on parisc and
	 * mn10300 architectures.
	 */
	if (*tosec == '\0')
		return NULL;

	for (i = 0; i < elems; i++) {
		if (match(fromsec, check->fromsec) &&
		    match(tosec, check->tosec))
		if (match(fromsec, check->fromsec)) {
			if (check->bad_tosec[0] && match(tosec, check->bad_tosec))
				return check;
			if (check->good_tosec[0] && !match(tosec, check->good_tosec))
				return check;
		}
		check++;
	}
	return NULL;
@@ -1067,6 +1140,17 @@ static const struct sectioncheck *section_mismatch(
 *   This pattern is identified by
 *   refsymname = __init_begin, _sinittext, _einittext
 *
 * Pattern 5:
 *   GCC may optimize static inlines when fed constant arg(s) resulting
 *   in functions like cpumask_empty() -- generating an associated symbol
 *   cpumask_empty.constprop.3 that appears in the audit.  If the const that
 *   is passed in comes from __init, like say nmi_ipi_mask, we get a
 *   meaningless section warning.  May need to add isra symbols too...
 *   This pattern is identified by
 *   tosec   = init section
 *   fromsec = text section
 *   refsymname = *.constprop.*
 *
 **/
static int secref_whitelist(const struct sectioncheck *mismatch,
			    const char *fromsec, const char *fromsym,
@@ -1099,6 +1183,12 @@ static int secref_whitelist(const struct sectioncheck *mismatch,
	if (match(tosym, linker_symbols))
		return 0;

	/* Check for pattern 5 */
	if (match(fromsec, text_sections) &&
	    match(tosec, init_sections) &&
	    match(fromsym, optim_symbols))
		return 0;

	return 1;
}

@@ -1261,6 +1351,15 @@ static void print_section_list(const char * const list[20])
	fprintf(stderr, "\n");
}

static inline void get_pretty_name(int is_func, const char** name, const char** name_p)
{
	switch (is_func) {
	case 0:	*name = "variable"; *name_p = ""; break;
	case 1:	*name = "function"; *name_p = "()"; break;
	default: *name = "(unknown reference)"; *name_p = ""; break;
	}
}

/*
 * Print a warning about a section mismatch.
 * Try to find symbols near it so user can find it.
@@ -1280,21 +1379,13 @@ static void report_sec_mismatch(const char *modname,
	char *prl_from;
	char *prl_to;

	switch (from_is_func) {
	case 0: from = "variable"; from_p = "";   break;
	case 1: from = "function"; from_p = "()"; break;
	default: from = "(unknown reference)"; from_p = ""; break;
	}
	switch (to_is_func) {
	case 0: to = "variable"; to_p = "";   break;
	case 1: to = "function"; to_p = "()"; break;
	default: to = "(unknown reference)"; to_p = ""; break;
	}

	sec_mismatch_count++;
	if (!sec_mismatch_verbose)
		return;

	get_pretty_name(from_is_func, &from, &from_p);
	get_pretty_name(to_is_func, &to, &to_p);

	warn("%s(%s+0x%llx): Section mismatch in reference from the %s %s%s "
	     "to the %s %s:%s%s\n",
	     modname, fromsec, fromaddr, from, fromsym, from_p, to, tosec,
@@ -1408,19 +1499,19 @@ static void report_sec_mismatch(const char *modname,
		tosym, prl_to, prl_to, tosym);
		free(prl_to);
		break;
	case EXTABLE_TO_NON_TEXT:
		fatal("There's a special handler for this mismatch type, "
		      "we should never get here.");
		break;
	}
	fprintf(stderr, "\n");
}

static void check_section_mismatch(const char *modname, struct elf_info *elf,
static void default_mismatch_handler(const char *modname, struct elf_info *elf,
				     const struct sectioncheck* const mismatch,
				     Elf_Rela *r, Elf_Sym *sym, const char *fromsec)
{
	const char *tosec;
	const struct sectioncheck *mismatch;

	tosec = sec_name(elf, get_secindex(elf, sym));
	mismatch = section_mismatch(fromsec, tosec);
	if (mismatch) {
	Elf_Sym *to;
	Elf_Sym *from;
	const char *tosym;
@@ -1428,13 +1519,15 @@ static void check_section_mismatch(const char *modname, struct elf_info *elf,

	from = find_elf_symbol2(elf, r->r_offset, fromsec);
	fromsym = sym_name(elf, from);
		to = find_elf_symbol(elf, r->r_addend, sym);
		tosym = sym_name(elf, to);

	if (!strncmp(fromsym, "reference___initcall",
		     sizeof("reference___initcall")-1))
		return;

	tosec = sec_name(elf, get_secindex(elf, sym));
	to = find_elf_symbol(elf, r->r_addend, sym);
	tosym = sym_name(elf, to);

	/* check whitelist - we may ignore it */
	if (secref_whitelist(mismatch,
			     fromsec, fromsym, tosec, tosym)) {
@@ -1444,6 +1537,142 @@ static void check_section_mismatch(const char *modname, struct elf_info *elf,
				    is_function(to));
	}
}

static int is_executable_section(struct elf_info* elf, unsigned int section_index)
{
	if (section_index > elf->num_sections)
		fatal("section_index is outside elf->num_sections!\n");

	return ((elf->sechdrs[section_index].sh_flags & SHF_EXECINSTR) == SHF_EXECINSTR);
}

/*
 * We rely on a gross hack in section_rel[a]() calling find_extable_entry_size()
 * to know the sizeof(struct exception_table_entry) for the target architecture.
 */
static unsigned int extable_entry_size = 0;
static void find_extable_entry_size(const char* const sec, const Elf_Rela* r)
{
	/*
	 * If we're currently checking the second relocation within __ex_table,
	 * that relocation offset tells us the offsetof(struct
	 * exception_table_entry, fixup) which is equal to sizeof(struct
	 * exception_table_entry) divided by two.  We use that to our advantage
	 * since there's no portable way to get that size as every architecture
	 * seems to go with different sized types.  Not pretty but better than
	 * hard-coding the size for every architecture..
	 */
	if (!extable_entry_size)
		extable_entry_size = r->r_offset * 2;
}

static inline bool is_extable_fault_address(Elf_Rela *r)
{
	/*
	 * extable_entry_size is only discovered after we've handled the
	 * _second_ relocation in __ex_table, so only abort when we're not
	 * handling the first reloc and extable_entry_size is zero.
	 */
	if (r->r_offset && extable_entry_size == 0)
		fatal("extable_entry size hasn't been discovered!\n");

	return ((r->r_offset == 0) ||
		(r->r_offset % extable_entry_size == 0));
}

#define is_second_extable_reloc(Start, Cur, Sec)			\
	(((Cur) == (Start) + 1) && (strcmp("__ex_table", (Sec)) == 0))

static void report_extable_warnings(const char* modname, struct elf_info* elf,
				    const struct sectioncheck* const mismatch,
				    Elf_Rela* r, Elf_Sym* sym,
				    const char* fromsec, const char* tosec)
{
	Elf_Sym* fromsym = find_elf_symbol2(elf, r->r_offset, fromsec);
	const char* fromsym_name = sym_name(elf, fromsym);
	Elf_Sym* tosym = find_elf_symbol(elf, r->r_addend, sym);
	const char* tosym_name = sym_name(elf, tosym);
	const char* from_pretty_name;
	const char* from_pretty_name_p;
	const char* to_pretty_name;
	const char* to_pretty_name_p;

	get_pretty_name(is_function(fromsym),
			&from_pretty_name, &from_pretty_name_p);
	get_pretty_name(is_function(tosym),
			&to_pretty_name, &to_pretty_name_p);

	warn("%s(%s+0x%lx): Section mismatch in reference"
	     " from the %s %s%s to the %s %s:%s%s\n",
	     modname, fromsec, (long)r->r_offset, from_pretty_name,
	     fromsym_name, from_pretty_name_p,
	     to_pretty_name, tosec, tosym_name, to_pretty_name_p);

	if (!match(tosec, mismatch->bad_tosec) &&
	    is_executable_section(elf, get_secindex(elf, sym)))
		fprintf(stderr,
			"The relocation at %s+0x%lx references\n"
			"section \"%s\" which is not in the list of\n"
			"authorized sections.  If you're adding a new section\n"
			"and/or if this reference is valid, add \"%s\" to the\n"
			"list of authorized sections to jump to on fault.\n"
			"This can be achieved by adding \"%s\" to \n"
			"OTHER_TEXT_SECTIONS in scripts/mod/modpost.c.\n",
			fromsec, (long)r->r_offset, tosec, tosec, tosec);
}

static void extable_mismatch_handler(const char* modname, struct elf_info *elf,
				     const struct sectioncheck* const mismatch,
				     Elf_Rela* r, Elf_Sym* sym,
				     const char *fromsec)
{
	const char* tosec = sec_name(elf, get_secindex(elf, sym));

	sec_mismatch_count++;

	if (sec_mismatch_verbose)
		report_extable_warnings(modname, elf, mismatch, r, sym,
					fromsec, tosec);

	if (match(tosec, mismatch->bad_tosec))
		fatal("The relocation at %s+0x%lx references\n"
		      "section \"%s\" which is black-listed.\n"
		      "Something is seriously wrong and should be fixed.\n"
		      "You might get more information about where this is\n"
		      "coming from by using scripts/check_extable.sh %s\n",
		      fromsec, (long)r->r_offset, tosec, modname);
	else if (!is_executable_section(elf, get_secindex(elf, sym))) {
		if (is_extable_fault_address(r))
			fatal("The relocation at %s+0x%lx references\n"
			      "section \"%s\" which is not executable, IOW\n"
			      "it is not possible for the kernel to fault\n"
			      "at that address.  Something is seriously wrong\n"
			      "and should be fixed.\n",
			      fromsec, (long)r->r_offset, tosec);
		else
			fatal("The relocation at %s+0x%lx references\n"
			      "section \"%s\" which is not executable, IOW\n"
			      "the kernel will fault if it ever tries to\n"
			      "jump to it.  Something is seriously wrong\n"
			      "and should be fixed.\n",
			      fromsec, (long)r->r_offset, tosec);
	}
}

static void check_section_mismatch(const char *modname, struct elf_info *elf,
				   Elf_Rela *r, Elf_Sym *sym, const char *fromsec)
{
	const char *tosec = sec_name(elf, get_secindex(elf, sym));;
	const struct sectioncheck *mismatch = section_mismatch(fromsec, tosec);

	if (mismatch) {
		if (mismatch->handler)
			mismatch->handler(modname, elf,  mismatch,
					  r, sym, fromsec);
		else
			default_mismatch_handler(modname, elf, mismatch,
						 r, sym, fromsec);
	}
}

static unsigned int *reloc_location(struct elf_info *elf,
@@ -1582,6 +1811,8 @@ static void section_rela(const char *modname, struct elf_info *elf,
		/* Skip special sections */
		if (is_shndx_special(sym->st_shndx))
			continue;
		if (is_second_extable_reloc(start, rela, fromsec))
			find_extable_entry_size(fromsec, &r);
		check_section_mismatch(modname, elf, &r, sym, fromsec);
	}
}
@@ -1640,6 +1871,8 @@ static void section_rel(const char *modname, struct elf_info *elf,
		/* Skip special sections */
		if (is_shndx_special(sym->st_shndx))
			continue;
		if (is_second_extable_reloc(start, rel, fromsec))
			find_extable_entry_size(fromsec, &r);
		check_section_mismatch(modname, elf, &r, sym, fromsec);
	}
}