Home Home > GIT Browse
summaryrefslogtreecommitdiff
blob: 5347af059af0469e1160c24fdbc0f42034224fb1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/*
 * kallsyms_relocs.c - resolve non-exported symbols
 *
 * Copyright (C) 2018 SUSE
 * Author: Nicolai Stange <nstange@suse.de>
 *
 * 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/>.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kallsyms.h>
#include "kallsyms_relocs.h"

struct find_args
{
	struct klp_kallsyms_reloc reloc;
	unsigned long match_count;
};

static int __find_callback(void *data, const char *name,
			   struct module *mod, unsigned long addr)
{
	struct find_args *args = data;

	if ((mod && !args->reloc.objname) || (!mod && args->reloc.objname))
		return 0;

	if (strcmp(args->reloc.symname, name))
		return 0;

	if (args->reloc.objname && strcmp(args->reloc.objname, mod->name))
		return 0;

	args->match_count++;

	/*
	 * Finish the search when the symbol is found for the desired
	 * position or the position is not defined.
	 */
	if (!args->reloc.sympos || args->match_count == args->reloc.sympos) {
		*args->reloc.addr = (void *)addr;
		return 1;
	}

	return 0;
}

static int (*klp_module_kallsyms_on_each_symbol)(int (*fn)(void *, const char *,
							   struct module *,
							   unsigned long),
						 void *data);

/* Bootstrap: resolve non-exported module_kallsyms_on_each_symbol() */
static int __kallsyms_relocs_init(void)
{
	const char symname[] = "module_kallsyms_on_each_symbol";

	if (klp_module_kallsyms_on_each_symbol)
		return 0;

	klp_module_kallsyms_on_each_symbol =
		(void *)kallsyms_lookup_name(symname);

	if (!klp_module_kallsyms_on_each_symbol) {
		pr_err("livepatch: symbol %s not resolved\n", symname);
		return -ENOENT;
	}

	return 0;
}

/*
 * Must be called with module_mutex held if any of the relocs'
 * ->objname can be non-NULL.
 */
int __klp_resolve_kallsyms_relocs(struct klp_kallsyms_reloc *relocs,
				  unsigned long count)
{
	int ret;
	unsigned long i;
	struct find_args args;

	ret = __kallsyms_relocs_init();
	if (ret)
		return ret;

	for (i = 0; i < count; ++i) {
		*relocs[i].addr = NULL;
		args.reloc = relocs[i];
		args.match_count = 0;

		if (args.reloc.objname) {
			klp_module_kallsyms_on_each_symbol(__find_callback,
							   &args);
		} else {
			kallsyms_on_each_symbol(__find_callback, &args);
		}

		if (!*relocs[i].addr) {
			if (relocs[i].objname) {
				pr_err("livepatch: symbol %s:%s not resolved\n",
				       relocs[i].objname, relocs[i].symname);
			} else {
				pr_err("livepatch: symbol %s not resolved\n",
				       relocs[i].symname);
			}

			return -ENOENT;
		}
	}

	return 0;
}