Linus observed that the symbol_request(utf8_data_table) call fails when
CONFIG_UNICODE=y and CONFIG_TRIM_UNUSED_KSYMS=y.
symbol_get() relies on the symbol data being present in the ksymtab for
symbol lookups. However, EXPORT_SYMBOL_GPL(utf8_data_table) is dropped
due to CONFIG_TRIM_UNUSED_KSYMS, as no module references it in this case.
Probably, this has been broken since commit dbacb0ef67
("kconfig option
for TRIM_UNUSED_KSYMS").
This commit addresses the issue by leveraging modpost. Symbol names
passed to symbol_get() are recorded in the special .no_trim_symbol
section, which is then parsed by modpost to forcibly keep such symbols.
The .no_trim_symbol section is discarded by the linker scripts, so there
is no impact on the size of the final vmlinux or modules.
This commit cannot resolve the issue for direct calls to __symbol_get()
because the symbol name is not known at compile-time.
Although symbol_get() may eventually be deprecated, this workaround
should be good enough meanwhile.
Reported-by: Linus Torvalds <torvalds@linux-foundation.org>
Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>
241 lines
6.5 KiB
C
241 lines
6.5 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#include <byteswap.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/mman.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <elf.h>
|
|
#include "../../include/linux/module_symbol.h"
|
|
|
|
#include <list_types.h>
|
|
#include "elfconfig.h"
|
|
|
|
/* On BSD-alike OSes elf.h defines these according to host's word size */
|
|
#undef ELF_ST_BIND
|
|
#undef ELF_ST_TYPE
|
|
#undef ELF_R_SYM
|
|
#undef ELF_R_TYPE
|
|
|
|
#if KERNEL_ELFCLASS == ELFCLASS32
|
|
|
|
#define Elf_Ehdr Elf32_Ehdr
|
|
#define Elf_Shdr Elf32_Shdr
|
|
#define Elf_Sym Elf32_Sym
|
|
#define Elf_Addr Elf32_Addr
|
|
#define Elf_Section Elf32_Half
|
|
#define ELF_ST_BIND ELF32_ST_BIND
|
|
#define ELF_ST_TYPE ELF32_ST_TYPE
|
|
|
|
#define Elf_Rel Elf32_Rel
|
|
#define Elf_Rela Elf32_Rela
|
|
#define ELF_R_SYM ELF32_R_SYM
|
|
#define ELF_R_TYPE ELF32_R_TYPE
|
|
#else
|
|
|
|
#define Elf_Ehdr Elf64_Ehdr
|
|
#define Elf_Shdr Elf64_Shdr
|
|
#define Elf_Sym Elf64_Sym
|
|
#define Elf_Addr Elf64_Addr
|
|
#define Elf_Section Elf64_Half
|
|
#define ELF_ST_BIND ELF64_ST_BIND
|
|
#define ELF_ST_TYPE ELF64_ST_TYPE
|
|
|
|
#define Elf_Rel Elf64_Rel
|
|
#define Elf_Rela Elf64_Rela
|
|
#define ELF_R_SYM ELF64_R_SYM
|
|
#define ELF_R_TYPE ELF64_R_TYPE
|
|
#endif
|
|
|
|
#define bswap(x) \
|
|
({ \
|
|
_Static_assert(sizeof(x) == 1 || sizeof(x) == 2 || \
|
|
sizeof(x) == 4 || sizeof(x) == 8, "bug"); \
|
|
(typeof(x))(sizeof(x) == 2 ? bswap_16(x) : \
|
|
sizeof(x) == 4 ? bswap_32(x) : \
|
|
sizeof(x) == 8 ? bswap_64(x) : \
|
|
x); \
|
|
})
|
|
|
|
#define TO_NATIVE(x) \
|
|
(target_is_big_endian == host_is_big_endian ? x : bswap(x))
|
|
|
|
#define __get_unaligned_t(type, ptr) ({ \
|
|
const struct { type x; } __attribute__((__packed__)) *__pptr = \
|
|
(typeof(__pptr))(ptr); \
|
|
__pptr->x; \
|
|
})
|
|
|
|
#define get_unaligned(ptr) __get_unaligned_t(typeof(*(ptr)), (ptr))
|
|
|
|
#define get_unaligned_native(ptr) \
|
|
({ \
|
|
typeof(*(ptr)) _val = get_unaligned(ptr); \
|
|
TO_NATIVE(_val); \
|
|
})
|
|
|
|
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
|
|
|
|
#define strstarts(str, prefix) (strncmp(str, prefix, strlen(prefix)) == 0)
|
|
|
|
struct buffer {
|
|
char *p;
|
|
int pos;
|
|
int size;
|
|
};
|
|
|
|
void __attribute__((format(printf, 2, 3)))
|
|
buf_printf(struct buffer *buf, const char *fmt, ...);
|
|
|
|
void
|
|
buf_write(struct buffer *buf, const char *s, int len);
|
|
|
|
/**
|
|
* struct module_alias - auto-generated MODULE_ALIAS()
|
|
*
|
|
* @node: linked to module::aliases
|
|
* @str: a string for MODULE_ALIAS()
|
|
*/
|
|
struct module_alias {
|
|
struct list_head node;
|
|
char str[];
|
|
};
|
|
|
|
/**
|
|
* struct module - represent a module (vmlinux or *.ko)
|
|
*
|
|
* @dump_file: path to the .symvers file if loaded from a file
|
|
* @aliases: list head for module_aliases
|
|
* @no_trim_symbol: .no_trim_symbol section data
|
|
* @no_trim_symbol_len: length of the .no_trim_symbol section
|
|
*/
|
|
struct module {
|
|
struct list_head list;
|
|
struct list_head exported_symbols;
|
|
struct list_head unresolved_symbols;
|
|
const char *dump_file;
|
|
bool is_gpl_compatible;
|
|
bool is_vmlinux;
|
|
bool seen;
|
|
bool has_init;
|
|
bool has_cleanup;
|
|
char srcversion[25];
|
|
// Missing namespace dependencies
|
|
struct list_head missing_namespaces;
|
|
// Actual imported namespaces
|
|
struct list_head imported_namespaces;
|
|
struct list_head aliases;
|
|
char *no_trim_symbol;
|
|
unsigned int no_trim_symbol_len;
|
|
char name[];
|
|
};
|
|
|
|
struct elf_info {
|
|
size_t size;
|
|
Elf_Ehdr *hdr;
|
|
Elf_Shdr *sechdrs;
|
|
Elf_Sym *symtab_start;
|
|
Elf_Sym *symtab_stop;
|
|
unsigned int export_symbol_secndx; /* .export_symbol section */
|
|
char *strtab;
|
|
char *modinfo;
|
|
unsigned int modinfo_len;
|
|
char *no_trim_symbol;
|
|
unsigned int no_trim_symbol_len;
|
|
|
|
/* support for 32bit section numbers */
|
|
|
|
unsigned int num_sections; /* max_secindex + 1 */
|
|
unsigned int secindex_strings;
|
|
/* if Nth symbol table entry has .st_shndx = SHN_XINDEX,
|
|
* take shndx from symtab_shndx_start[N] instead */
|
|
Elf32_Word *symtab_shndx_start;
|
|
Elf32_Word *symtab_shndx_stop;
|
|
|
|
struct symsearch *symsearch;
|
|
};
|
|
|
|
/* Accessor for sym->st_shndx, hides ugliness of "64k sections" */
|
|
static inline unsigned int get_secindex(const struct elf_info *info,
|
|
const Elf_Sym *sym)
|
|
{
|
|
unsigned int index = sym->st_shndx;
|
|
|
|
/*
|
|
* Elf{32,64}_Sym::st_shndx is 2 byte. Big section numbers are available
|
|
* in the .symtab_shndx section.
|
|
*/
|
|
if (index == SHN_XINDEX)
|
|
return info->symtab_shndx_start[sym - info->symtab_start];
|
|
|
|
/*
|
|
* Move reserved section indices SHN_LORESERVE..SHN_HIRESERVE out of
|
|
* the way to UINT_MAX-255..UINT_MAX, to avoid conflicting with real
|
|
* section indices.
|
|
*/
|
|
if (index >= SHN_LORESERVE && index <= SHN_HIRESERVE)
|
|
return index - SHN_HIRESERVE - 1;
|
|
|
|
return index;
|
|
}
|
|
|
|
/*
|
|
* If there's no name there, ignore it; likewise, ignore it if it's
|
|
* one of the magic symbols emitted used by current tools.
|
|
*
|
|
* Internal symbols created by tools should be ignored by modpost.
|
|
*/
|
|
static inline bool is_valid_name(struct elf_info *elf, Elf_Sym *sym)
|
|
{
|
|
const char *name = elf->strtab + sym->st_name;
|
|
|
|
if (!name || !strlen(name))
|
|
return false;
|
|
return !is_mapping_symbol(name);
|
|
}
|
|
|
|
/* symsearch.c */
|
|
void symsearch_init(struct elf_info *elf);
|
|
void symsearch_finish(struct elf_info *elf);
|
|
Elf_Sym *symsearch_find_nearest(struct elf_info *elf, Elf_Addr addr,
|
|
unsigned int secndx, bool allow_negative,
|
|
Elf_Addr min_distance);
|
|
|
|
/* file2alias.c */
|
|
void handle_moddevtable(struct module *mod, struct elf_info *info,
|
|
Elf_Sym *sym, const char *symname);
|
|
|
|
/* sumversion.c */
|
|
void get_src_version(const char *modname, char sum[], unsigned sumlen);
|
|
|
|
/* from modpost.c */
|
|
extern bool target_is_big_endian;
|
|
extern bool host_is_big_endian;
|
|
char *read_text_file(const char *filename);
|
|
char *get_line(char **stringp);
|
|
void *sym_get_data(const struct elf_info *info, const Elf_Sym *sym);
|
|
|
|
void __attribute__((format(printf, 2, 3)))
|
|
modpost_log(bool is_error, const char *fmt, ...);
|
|
|
|
/*
|
|
* warn - show the given message, then let modpost continue running, still
|
|
* allowing modpost to exit successfully. This should be used when
|
|
* we still allow to generate vmlinux and modules.
|
|
*
|
|
* error - show the given message, then let modpost continue running, but fail
|
|
* in the end. This should be used when we should stop building vmlinux
|
|
* or modules, but we can continue running modpost to catch as many
|
|
* issues as possible.
|
|
*
|
|
* fatal - show the given message, and bail out immediately. This should be
|
|
* used when there is no point to continue running modpost.
|
|
*/
|
|
#define warn(fmt, args...) modpost_log(false, fmt, ##args)
|
|
#define error(fmt, args...) modpost_log(true, fmt, ##args)
|
|
#define fatal(fmt, args...) do { error(fmt, ##args); exit(1); } while (1)
|