mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-12-28 16:56:26 +00:00
kallsyms: Match symbols exactly with CONFIG_LTO_CLANG
With CONFIG_LTO_CLANG=y, the compiler may add .llvm.<hash> suffix to
function names to avoid duplication. APIs like kallsyms_lookup_name()
and kallsyms_on_each_match_symbol() tries to match these symbol names
without the .llvm.<hash> suffix, e.g., match "c_stop" with symbol
c_stop.llvm.17132674095431275852. This turned out to be problematic
for use cases that require exact match, for example, livepatch.
Fix this by making the APIs to match symbols exactly.
Also cleanup kallsyms_selftests accordingly.
Signed-off-by: Song Liu <song@kernel.org>
Fixes: 8cc32a9bbf
("kallsyms: strip LTO-only suffixes from promoted global functions")
Tested-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Reviewed-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Acked-by: Petr Mladek <pmladek@suse.com>
Reviewed-by: Sami Tolvanen <samitolvanen@google.com>
Reviewed-by: Luis Chamberlain <mcgrof@kernel.org>
Link: https://lore.kernel.org/r/20240807220513.3100483-3-song@kernel.org
Signed-off-by: Kees Cook <kees@kernel.org>
This commit is contained in:
parent
020925ce92
commit
fb6a421fb6
@ -160,38 +160,6 @@ unsigned long kallsyms_sym_address(int idx)
|
|||||||
return kallsyms_relative_base - 1 - kallsyms_offsets[idx];
|
return kallsyms_relative_base - 1 - kallsyms_offsets[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cleanup_symbol_name(char *s)
|
|
||||||
{
|
|
||||||
char *res;
|
|
||||||
|
|
||||||
if (!IS_ENABLED(CONFIG_LTO_CLANG))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* LLVM appends various suffixes for local functions and variables that
|
|
||||||
* must be promoted to global scope as part of LTO. This can break
|
|
||||||
* hooking of static functions with kprobes. '.' is not a valid
|
|
||||||
* character in an identifier in C. Suffixes only in LLVM LTO observed:
|
|
||||||
* - foo.llvm.[0-9a-f]+
|
|
||||||
*/
|
|
||||||
res = strstr(s, ".llvm.");
|
|
||||||
if (res)
|
|
||||||
*res = '\0';
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int compare_symbol_name(const char *name, char *namebuf)
|
|
||||||
{
|
|
||||||
/* The kallsyms_seqs_of_names is sorted based on names after
|
|
||||||
* cleanup_symbol_name() (see scripts/kallsyms.c) if clang lto is enabled.
|
|
||||||
* To ensure correct bisection in kallsyms_lookup_names(), do
|
|
||||||
* cleanup_symbol_name(namebuf) before comparing name and namebuf.
|
|
||||||
*/
|
|
||||||
cleanup_symbol_name(namebuf);
|
|
||||||
return strcmp(name, namebuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned int get_symbol_seq(int index)
|
static unsigned int get_symbol_seq(int index)
|
||||||
{
|
{
|
||||||
unsigned int i, seq = 0;
|
unsigned int i, seq = 0;
|
||||||
@ -219,7 +187,7 @@ static int kallsyms_lookup_names(const char *name,
|
|||||||
seq = get_symbol_seq(mid);
|
seq = get_symbol_seq(mid);
|
||||||
off = get_symbol_offset(seq);
|
off = get_symbol_offset(seq);
|
||||||
kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
|
kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
|
||||||
ret = compare_symbol_name(name, namebuf);
|
ret = strcmp(name, namebuf);
|
||||||
if (ret > 0)
|
if (ret > 0)
|
||||||
low = mid + 1;
|
low = mid + 1;
|
||||||
else if (ret < 0)
|
else if (ret < 0)
|
||||||
@ -236,7 +204,7 @@ static int kallsyms_lookup_names(const char *name,
|
|||||||
seq = get_symbol_seq(low - 1);
|
seq = get_symbol_seq(low - 1);
|
||||||
off = get_symbol_offset(seq);
|
off = get_symbol_offset(seq);
|
||||||
kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
|
kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
|
||||||
if (compare_symbol_name(name, namebuf))
|
if (strcmp(name, namebuf))
|
||||||
break;
|
break;
|
||||||
low--;
|
low--;
|
||||||
}
|
}
|
||||||
@ -248,7 +216,7 @@ static int kallsyms_lookup_names(const char *name,
|
|||||||
seq = get_symbol_seq(high + 1);
|
seq = get_symbol_seq(high + 1);
|
||||||
off = get_symbol_offset(seq);
|
off = get_symbol_offset(seq);
|
||||||
kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
|
kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
|
||||||
if (compare_symbol_name(name, namebuf))
|
if (strcmp(name, namebuf))
|
||||||
break;
|
break;
|
||||||
high++;
|
high++;
|
||||||
}
|
}
|
||||||
@ -407,8 +375,7 @@ static int kallsyms_lookup_buildid(unsigned long addr,
|
|||||||
if (modbuildid)
|
if (modbuildid)
|
||||||
*modbuildid = NULL;
|
*modbuildid = NULL;
|
||||||
|
|
||||||
ret = strlen(namebuf);
|
return strlen(namebuf);
|
||||||
goto found;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* See if it's in a module or a BPF JITed image. */
|
/* See if it's in a module or a BPF JITed image. */
|
||||||
@ -422,8 +389,6 @@ static int kallsyms_lookup_buildid(unsigned long addr,
|
|||||||
ret = ftrace_mod_address_lookup(addr, symbolsize,
|
ret = ftrace_mod_address_lookup(addr, symbolsize,
|
||||||
offset, modname, namebuf);
|
offset, modname, namebuf);
|
||||||
|
|
||||||
found:
|
|
||||||
cleanup_symbol_name(namebuf);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -450,8 +415,6 @@ const char *kallsyms_lookup(unsigned long addr,
|
|||||||
|
|
||||||
int lookup_symbol_name(unsigned long addr, char *symname)
|
int lookup_symbol_name(unsigned long addr, char *symname)
|
||||||
{
|
{
|
||||||
int res;
|
|
||||||
|
|
||||||
symname[0] = '\0';
|
symname[0] = '\0';
|
||||||
symname[KSYM_NAME_LEN - 1] = '\0';
|
symname[KSYM_NAME_LEN - 1] = '\0';
|
||||||
|
|
||||||
@ -462,16 +425,10 @@ int lookup_symbol_name(unsigned long addr, char *symname)
|
|||||||
/* Grab name */
|
/* Grab name */
|
||||||
kallsyms_expand_symbol(get_symbol_offset(pos),
|
kallsyms_expand_symbol(get_symbol_offset(pos),
|
||||||
symname, KSYM_NAME_LEN);
|
symname, KSYM_NAME_LEN);
|
||||||
goto found;
|
return 0;
|
||||||
}
|
}
|
||||||
/* See if it's in a module. */
|
/* See if it's in a module. */
|
||||||
res = lookup_module_symbol_name(addr, symname);
|
return lookup_module_symbol_name(addr, symname);
|
||||||
if (res)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
found:
|
|
||||||
cleanup_symbol_name(symname);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Look up a kernel symbol and return it in a text buffer. */
|
/* Look up a kernel symbol and return it in a text buffer. */
|
||||||
|
@ -187,31 +187,11 @@ static void test_perf_kallsyms_lookup_name(void)
|
|||||||
stat.min, stat.max, div_u64(stat.sum, stat.real_cnt));
|
stat.min, stat.max, div_u64(stat.sum, stat.real_cnt));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool match_cleanup_name(const char *s, const char *name)
|
|
||||||
{
|
|
||||||
char *p;
|
|
||||||
int len;
|
|
||||||
|
|
||||||
if (!IS_ENABLED(CONFIG_LTO_CLANG))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
p = strstr(s, ".llvm.");
|
|
||||||
if (!p)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
len = strlen(name);
|
|
||||||
if (p - s != len)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return !strncmp(s, name, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int find_symbol(void *data, const char *name, unsigned long addr)
|
static int find_symbol(void *data, const char *name, unsigned long addr)
|
||||||
{
|
{
|
||||||
struct test_stat *stat = (struct test_stat *)data;
|
struct test_stat *stat = (struct test_stat *)data;
|
||||||
|
|
||||||
if (strcmp(name, stat->name) == 0 ||
|
if (!strcmp(name, stat->name)) {
|
||||||
(!stat->perf && match_cleanup_name(name, stat->name))) {
|
|
||||||
stat->real_cnt++;
|
stat->real_cnt++;
|
||||||
stat->addr = addr;
|
stat->addr = addr;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user