162306a36Sopenharmony_ci/* Postprocess module symbol versions 262306a36Sopenharmony_ci * 362306a36Sopenharmony_ci * Copyright 2003 Kai Germaschewski 462306a36Sopenharmony_ci * Copyright 2002-2004 Rusty Russell, IBM Corporation 562306a36Sopenharmony_ci * Copyright 2006-2008 Sam Ravnborg 662306a36Sopenharmony_ci * Based in part on module-init-tools/depmod.c,file2alias 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This software may be used and distributed according to the terms 962306a36Sopenharmony_ci * of the GNU General Public License, incorporated herein by reference. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Usage: modpost vmlinux module1.o module2.o ... 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define _GNU_SOURCE 1562306a36Sopenharmony_ci#include <elf.h> 1662306a36Sopenharmony_ci#include <fnmatch.h> 1762306a36Sopenharmony_ci#include <stdio.h> 1862306a36Sopenharmony_ci#include <ctype.h> 1962306a36Sopenharmony_ci#include <string.h> 2062306a36Sopenharmony_ci#include <limits.h> 2162306a36Sopenharmony_ci#include <stdbool.h> 2262306a36Sopenharmony_ci#include <errno.h> 2362306a36Sopenharmony_ci#include "modpost.h" 2462306a36Sopenharmony_ci#include "../../include/linux/license.h" 2562306a36Sopenharmony_ci#include "../../include/linux/module_symbol.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic bool module_enabled; 2862306a36Sopenharmony_ci/* Are we using CONFIG_MODVERSIONS? */ 2962306a36Sopenharmony_cistatic bool modversions; 3062306a36Sopenharmony_ci/* Is CONFIG_MODULE_SRCVERSION_ALL set? */ 3162306a36Sopenharmony_cistatic bool all_versions; 3262306a36Sopenharmony_ci/* If we are modposting external module set to 1 */ 3362306a36Sopenharmony_cistatic bool external_module; 3462306a36Sopenharmony_ci/* Only warn about unresolved symbols */ 3562306a36Sopenharmony_cistatic bool warn_unresolved; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic int sec_mismatch_count; 3862306a36Sopenharmony_cistatic bool sec_mismatch_warn_only = true; 3962306a36Sopenharmony_ci/* Trim EXPORT_SYMBOLs that are unused by in-tree modules */ 4062306a36Sopenharmony_cistatic bool trim_unused_exports; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* ignore missing files */ 4362306a36Sopenharmony_cistatic bool ignore_missing_files; 4462306a36Sopenharmony_ci/* If set to 1, only warn (instead of error) about missing ns imports */ 4562306a36Sopenharmony_cistatic bool allow_missing_ns_imports; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic bool error_occurred; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic bool extra_warn; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* 5262306a36Sopenharmony_ci * Cut off the warnings when there are too many. This typically occurs when 5362306a36Sopenharmony_ci * vmlinux is missing. ('make modules' without building vmlinux.) 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_ci#define MAX_UNRESOLVED_REPORTS 10 5662306a36Sopenharmony_cistatic unsigned int nr_unresolved; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* In kernel, this size is defined in linux/module.h; 5962306a36Sopenharmony_ci * here we use Elf_Addr instead of long for covering cross-compile 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#define MODULE_NAME_LEN (64 - sizeof(Elf_Addr)) 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_civoid __attribute__((format(printf, 2, 3))) 6562306a36Sopenharmony_cimodpost_log(enum loglevel loglevel, const char *fmt, ...) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci va_list arglist; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci switch (loglevel) { 7062306a36Sopenharmony_ci case LOG_WARN: 7162306a36Sopenharmony_ci fprintf(stderr, "WARNING: "); 7262306a36Sopenharmony_ci break; 7362306a36Sopenharmony_ci case LOG_ERROR: 7462306a36Sopenharmony_ci fprintf(stderr, "ERROR: "); 7562306a36Sopenharmony_ci break; 7662306a36Sopenharmony_ci case LOG_FATAL: 7762306a36Sopenharmony_ci fprintf(stderr, "FATAL: "); 7862306a36Sopenharmony_ci break; 7962306a36Sopenharmony_ci default: /* invalid loglevel, ignore */ 8062306a36Sopenharmony_ci break; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci fprintf(stderr, "modpost: "); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci va_start(arglist, fmt); 8662306a36Sopenharmony_ci vfprintf(stderr, fmt, arglist); 8762306a36Sopenharmony_ci va_end(arglist); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (loglevel == LOG_FATAL) 9062306a36Sopenharmony_ci exit(1); 9162306a36Sopenharmony_ci if (loglevel == LOG_ERROR) 9262306a36Sopenharmony_ci error_occurred = true; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic inline bool strends(const char *str, const char *postfix) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci if (strlen(str) < strlen(postfix)) 9862306a36Sopenharmony_ci return false; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return strcmp(str + strlen(str) - strlen(postfix), postfix) == 0; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_civoid *do_nofail(void *ptr, const char *expr) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci if (!ptr) 10662306a36Sopenharmony_ci fatal("Memory allocation failure: %s.\n", expr); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci return ptr; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cichar *read_text_file(const char *filename) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci struct stat st; 11462306a36Sopenharmony_ci size_t nbytes; 11562306a36Sopenharmony_ci int fd; 11662306a36Sopenharmony_ci char *buf; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci fd = open(filename, O_RDONLY); 11962306a36Sopenharmony_ci if (fd < 0) { 12062306a36Sopenharmony_ci perror(filename); 12162306a36Sopenharmony_ci exit(1); 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (fstat(fd, &st) < 0) { 12562306a36Sopenharmony_ci perror(filename); 12662306a36Sopenharmony_ci exit(1); 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci buf = NOFAIL(malloc(st.st_size + 1)); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci nbytes = st.st_size; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci while (nbytes) { 13462306a36Sopenharmony_ci ssize_t bytes_read; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci bytes_read = read(fd, buf, nbytes); 13762306a36Sopenharmony_ci if (bytes_read < 0) { 13862306a36Sopenharmony_ci perror(filename); 13962306a36Sopenharmony_ci exit(1); 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci nbytes -= bytes_read; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci buf[st.st_size] = '\0'; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci close(fd); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci return buf; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cichar *get_line(char **stringp) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci char *orig = *stringp, *next; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* do not return the unwanted extra line at EOF */ 15662306a36Sopenharmony_ci if (!orig || *orig == '\0') 15762306a36Sopenharmony_ci return NULL; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci /* don't use strsep here, it is not available everywhere */ 16062306a36Sopenharmony_ci next = strchr(orig, '\n'); 16162306a36Sopenharmony_ci if (next) 16262306a36Sopenharmony_ci *next++ = '\0'; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci *stringp = next; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return orig; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/* A list of all modules we processed */ 17062306a36Sopenharmony_ciLIST_HEAD(modules); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic struct module *find_module(const char *modname) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct module *mod; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci list_for_each_entry(mod, &modules, list) { 17762306a36Sopenharmony_ci if (strcmp(mod->name, modname) == 0) 17862306a36Sopenharmony_ci return mod; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci return NULL; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic struct module *new_module(const char *name, size_t namelen) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct module *mod; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci mod = NOFAIL(malloc(sizeof(*mod) + namelen + 1)); 18862306a36Sopenharmony_ci memset(mod, 0, sizeof(*mod)); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci INIT_LIST_HEAD(&mod->exported_symbols); 19162306a36Sopenharmony_ci INIT_LIST_HEAD(&mod->unresolved_symbols); 19262306a36Sopenharmony_ci INIT_LIST_HEAD(&mod->missing_namespaces); 19362306a36Sopenharmony_ci INIT_LIST_HEAD(&mod->imported_namespaces); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci memcpy(mod->name, name, namelen); 19662306a36Sopenharmony_ci mod->name[namelen] = '\0'; 19762306a36Sopenharmony_ci mod->is_vmlinux = (strcmp(mod->name, "vmlinux") == 0); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* 20062306a36Sopenharmony_ci * Set mod->is_gpl_compatible to true by default. If MODULE_LICENSE() 20162306a36Sopenharmony_ci * is missing, do not check the use for EXPORT_SYMBOL_GPL() becasue 20262306a36Sopenharmony_ci * modpost will exit wiht error anyway. 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_ci mod->is_gpl_compatible = true; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci list_add_tail(&mod->list, &modules); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci return mod; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci/* A hash of all exported symbols, 21262306a36Sopenharmony_ci * struct symbol is also used for lists of unresolved symbols */ 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci#define SYMBOL_HASH_SIZE 1024 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistruct symbol { 21762306a36Sopenharmony_ci struct symbol *next; 21862306a36Sopenharmony_ci struct list_head list; /* link to module::exported_symbols or module::unresolved_symbols */ 21962306a36Sopenharmony_ci struct module *module; 22062306a36Sopenharmony_ci char *namespace; 22162306a36Sopenharmony_ci unsigned int crc; 22262306a36Sopenharmony_ci bool crc_valid; 22362306a36Sopenharmony_ci bool weak; 22462306a36Sopenharmony_ci bool is_func; 22562306a36Sopenharmony_ci bool is_gpl_only; /* exported by EXPORT_SYMBOL_GPL */ 22662306a36Sopenharmony_ci bool used; /* there exists a user of this symbol */ 22762306a36Sopenharmony_ci char name[]; 22862306a36Sopenharmony_ci}; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic struct symbol *symbolhash[SYMBOL_HASH_SIZE]; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci/* This is based on the hash algorithm from gdbm, via tdb */ 23362306a36Sopenharmony_cistatic inline unsigned int tdb_hash(const char *name) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci unsigned value; /* Used to compute the hash value. */ 23662306a36Sopenharmony_ci unsigned i; /* Used to cycle through random values. */ 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* Set the initial value from the key size. */ 23962306a36Sopenharmony_ci for (value = 0x238F13AF * strlen(name), i = 0; name[i]; i++) 24062306a36Sopenharmony_ci value = (value + (((unsigned char *)name)[i] << (i*5 % 24))); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci return (1103515243 * value + 12345); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci/** 24662306a36Sopenharmony_ci * Allocate a new symbols for use in the hash of exported symbols or 24762306a36Sopenharmony_ci * the list of unresolved symbols per module 24862306a36Sopenharmony_ci **/ 24962306a36Sopenharmony_cistatic struct symbol *alloc_symbol(const char *name) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci struct symbol *s = NOFAIL(malloc(sizeof(*s) + strlen(name) + 1)); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci memset(s, 0, sizeof(*s)); 25462306a36Sopenharmony_ci strcpy(s->name, name); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return s; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci/* For the hash of exported symbols */ 26062306a36Sopenharmony_cistatic void hash_add_symbol(struct symbol *sym) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci unsigned int hash; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci hash = tdb_hash(sym->name) % SYMBOL_HASH_SIZE; 26562306a36Sopenharmony_ci sym->next = symbolhash[hash]; 26662306a36Sopenharmony_ci symbolhash[hash] = sym; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic void sym_add_unresolved(const char *name, struct module *mod, bool weak) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci struct symbol *sym; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci sym = alloc_symbol(name); 27462306a36Sopenharmony_ci sym->weak = weak; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci list_add_tail(&sym->list, &mod->unresolved_symbols); 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic struct symbol *sym_find_with_module(const char *name, struct module *mod) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci struct symbol *s; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* For our purposes, .foo matches foo. PPC64 needs this. */ 28462306a36Sopenharmony_ci if (name[0] == '.') 28562306a36Sopenharmony_ci name++; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci for (s = symbolhash[tdb_hash(name) % SYMBOL_HASH_SIZE]; s; s = s->next) { 28862306a36Sopenharmony_ci if (strcmp(s->name, name) == 0 && (!mod || s->module == mod)) 28962306a36Sopenharmony_ci return s; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci return NULL; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic struct symbol *find_symbol(const char *name) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci return sym_find_with_module(name, NULL); 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistruct namespace_list { 30062306a36Sopenharmony_ci struct list_head list; 30162306a36Sopenharmony_ci char namespace[]; 30262306a36Sopenharmony_ci}; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic bool contains_namespace(struct list_head *head, const char *namespace) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct namespace_list *list; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* 30962306a36Sopenharmony_ci * The default namespace is null string "", which is always implicitly 31062306a36Sopenharmony_ci * contained. 31162306a36Sopenharmony_ci */ 31262306a36Sopenharmony_ci if (!namespace[0]) 31362306a36Sopenharmony_ci return true; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci list_for_each_entry(list, head, list) { 31662306a36Sopenharmony_ci if (!strcmp(list->namespace, namespace)) 31762306a36Sopenharmony_ci return true; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci return false; 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic void add_namespace(struct list_head *head, const char *namespace) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci struct namespace_list *ns_entry; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (!contains_namespace(head, namespace)) { 32862306a36Sopenharmony_ci ns_entry = NOFAIL(malloc(sizeof(*ns_entry) + 32962306a36Sopenharmony_ci strlen(namespace) + 1)); 33062306a36Sopenharmony_ci strcpy(ns_entry->namespace, namespace); 33162306a36Sopenharmony_ci list_add_tail(&ns_entry->list, head); 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic void *sym_get_data_by_offset(const struct elf_info *info, 33662306a36Sopenharmony_ci unsigned int secindex, unsigned long offset) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci Elf_Shdr *sechdr = &info->sechdrs[secindex]; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci return (void *)info->hdr + sechdr->sh_offset + offset; 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_civoid *sym_get_data(const struct elf_info *info, const Elf_Sym *sym) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci return sym_get_data_by_offset(info, get_secindex(info, sym), 34662306a36Sopenharmony_ci sym->st_value); 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic const char *sech_name(const struct elf_info *info, Elf_Shdr *sechdr) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci return sym_get_data_by_offset(info, info->secindex_strings, 35262306a36Sopenharmony_ci sechdr->sh_name); 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic const char *sec_name(const struct elf_info *info, unsigned int secindex) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci /* 35862306a36Sopenharmony_ci * If sym->st_shndx is a special section index, there is no 35962306a36Sopenharmony_ci * corresponding section header. 36062306a36Sopenharmony_ci * Return "" if the index is out of range of info->sechdrs[] array. 36162306a36Sopenharmony_ci */ 36262306a36Sopenharmony_ci if (secindex >= info->num_sections) 36362306a36Sopenharmony_ci return ""; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci return sech_name(info, &info->sechdrs[secindex]); 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci#define strstarts(str, prefix) (strncmp(str, prefix, strlen(prefix)) == 0) 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic struct symbol *sym_add_exported(const char *name, struct module *mod, 37162306a36Sopenharmony_ci bool gpl_only, const char *namespace) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci struct symbol *s = find_symbol(name); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci if (s && (!external_module || s->module->is_vmlinux || s->module == mod)) { 37662306a36Sopenharmony_ci error("%s: '%s' exported twice. Previous export was in %s%s\n", 37762306a36Sopenharmony_ci mod->name, name, s->module->name, 37862306a36Sopenharmony_ci s->module->is_vmlinux ? "" : ".ko"); 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci s = alloc_symbol(name); 38262306a36Sopenharmony_ci s->module = mod; 38362306a36Sopenharmony_ci s->is_gpl_only = gpl_only; 38462306a36Sopenharmony_ci s->namespace = NOFAIL(strdup(namespace)); 38562306a36Sopenharmony_ci list_add_tail(&s->list, &mod->exported_symbols); 38662306a36Sopenharmony_ci hash_add_symbol(s); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci return s; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic void sym_set_crc(struct symbol *sym, unsigned int crc) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci sym->crc = crc; 39462306a36Sopenharmony_ci sym->crc_valid = true; 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic void *grab_file(const char *filename, size_t *size) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci struct stat st; 40062306a36Sopenharmony_ci void *map = MAP_FAILED; 40162306a36Sopenharmony_ci int fd; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci fd = open(filename, O_RDONLY); 40462306a36Sopenharmony_ci if (fd < 0) 40562306a36Sopenharmony_ci return NULL; 40662306a36Sopenharmony_ci if (fstat(fd, &st)) 40762306a36Sopenharmony_ci goto failed; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci *size = st.st_size; 41062306a36Sopenharmony_ci map = mmap(NULL, *size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cifailed: 41362306a36Sopenharmony_ci close(fd); 41462306a36Sopenharmony_ci if (map == MAP_FAILED) 41562306a36Sopenharmony_ci return NULL; 41662306a36Sopenharmony_ci return map; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic void release_file(void *file, size_t size) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci munmap(file, size); 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic int parse_elf(struct elf_info *info, const char *filename) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci unsigned int i; 42762306a36Sopenharmony_ci Elf_Ehdr *hdr; 42862306a36Sopenharmony_ci Elf_Shdr *sechdrs; 42962306a36Sopenharmony_ci Elf_Sym *sym; 43062306a36Sopenharmony_ci const char *secstrings; 43162306a36Sopenharmony_ci unsigned int symtab_idx = ~0U, symtab_shndx_idx = ~0U; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci hdr = grab_file(filename, &info->size); 43462306a36Sopenharmony_ci if (!hdr) { 43562306a36Sopenharmony_ci if (ignore_missing_files) { 43662306a36Sopenharmony_ci fprintf(stderr, "%s: %s (ignored)\n", filename, 43762306a36Sopenharmony_ci strerror(errno)); 43862306a36Sopenharmony_ci return 0; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci perror(filename); 44162306a36Sopenharmony_ci exit(1); 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci info->hdr = hdr; 44462306a36Sopenharmony_ci if (info->size < sizeof(*hdr)) { 44562306a36Sopenharmony_ci /* file too small, assume this is an empty .o file */ 44662306a36Sopenharmony_ci return 0; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci /* Is this a valid ELF file? */ 44962306a36Sopenharmony_ci if ((hdr->e_ident[EI_MAG0] != ELFMAG0) || 45062306a36Sopenharmony_ci (hdr->e_ident[EI_MAG1] != ELFMAG1) || 45162306a36Sopenharmony_ci (hdr->e_ident[EI_MAG2] != ELFMAG2) || 45262306a36Sopenharmony_ci (hdr->e_ident[EI_MAG3] != ELFMAG3)) { 45362306a36Sopenharmony_ci /* Not an ELF file - silently ignore it */ 45462306a36Sopenharmony_ci return 0; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci /* Fix endianness in ELF header */ 45762306a36Sopenharmony_ci hdr->e_type = TO_NATIVE(hdr->e_type); 45862306a36Sopenharmony_ci hdr->e_machine = TO_NATIVE(hdr->e_machine); 45962306a36Sopenharmony_ci hdr->e_version = TO_NATIVE(hdr->e_version); 46062306a36Sopenharmony_ci hdr->e_entry = TO_NATIVE(hdr->e_entry); 46162306a36Sopenharmony_ci hdr->e_phoff = TO_NATIVE(hdr->e_phoff); 46262306a36Sopenharmony_ci hdr->e_shoff = TO_NATIVE(hdr->e_shoff); 46362306a36Sopenharmony_ci hdr->e_flags = TO_NATIVE(hdr->e_flags); 46462306a36Sopenharmony_ci hdr->e_ehsize = TO_NATIVE(hdr->e_ehsize); 46562306a36Sopenharmony_ci hdr->e_phentsize = TO_NATIVE(hdr->e_phentsize); 46662306a36Sopenharmony_ci hdr->e_phnum = TO_NATIVE(hdr->e_phnum); 46762306a36Sopenharmony_ci hdr->e_shentsize = TO_NATIVE(hdr->e_shentsize); 46862306a36Sopenharmony_ci hdr->e_shnum = TO_NATIVE(hdr->e_shnum); 46962306a36Sopenharmony_ci hdr->e_shstrndx = TO_NATIVE(hdr->e_shstrndx); 47062306a36Sopenharmony_ci sechdrs = (void *)hdr + hdr->e_shoff; 47162306a36Sopenharmony_ci info->sechdrs = sechdrs; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci /* modpost only works for relocatable objects */ 47462306a36Sopenharmony_ci if (hdr->e_type != ET_REL) 47562306a36Sopenharmony_ci fatal("%s: not relocatable object.", filename); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci /* Check if file offset is correct */ 47862306a36Sopenharmony_ci if (hdr->e_shoff > info->size) { 47962306a36Sopenharmony_ci fatal("section header offset=%lu in file '%s' is bigger than filesize=%zu\n", 48062306a36Sopenharmony_ci (unsigned long)hdr->e_shoff, filename, info->size); 48162306a36Sopenharmony_ci return 0; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if (hdr->e_shnum == SHN_UNDEF) { 48562306a36Sopenharmony_ci /* 48662306a36Sopenharmony_ci * There are more than 64k sections, 48762306a36Sopenharmony_ci * read count from .sh_size. 48862306a36Sopenharmony_ci */ 48962306a36Sopenharmony_ci info->num_sections = TO_NATIVE(sechdrs[0].sh_size); 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci else { 49262306a36Sopenharmony_ci info->num_sections = hdr->e_shnum; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci if (hdr->e_shstrndx == SHN_XINDEX) { 49562306a36Sopenharmony_ci info->secindex_strings = TO_NATIVE(sechdrs[0].sh_link); 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci else { 49862306a36Sopenharmony_ci info->secindex_strings = hdr->e_shstrndx; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci /* Fix endianness in section headers */ 50262306a36Sopenharmony_ci for (i = 0; i < info->num_sections; i++) { 50362306a36Sopenharmony_ci sechdrs[i].sh_name = TO_NATIVE(sechdrs[i].sh_name); 50462306a36Sopenharmony_ci sechdrs[i].sh_type = TO_NATIVE(sechdrs[i].sh_type); 50562306a36Sopenharmony_ci sechdrs[i].sh_flags = TO_NATIVE(sechdrs[i].sh_flags); 50662306a36Sopenharmony_ci sechdrs[i].sh_addr = TO_NATIVE(sechdrs[i].sh_addr); 50762306a36Sopenharmony_ci sechdrs[i].sh_offset = TO_NATIVE(sechdrs[i].sh_offset); 50862306a36Sopenharmony_ci sechdrs[i].sh_size = TO_NATIVE(sechdrs[i].sh_size); 50962306a36Sopenharmony_ci sechdrs[i].sh_link = TO_NATIVE(sechdrs[i].sh_link); 51062306a36Sopenharmony_ci sechdrs[i].sh_info = TO_NATIVE(sechdrs[i].sh_info); 51162306a36Sopenharmony_ci sechdrs[i].sh_addralign = TO_NATIVE(sechdrs[i].sh_addralign); 51262306a36Sopenharmony_ci sechdrs[i].sh_entsize = TO_NATIVE(sechdrs[i].sh_entsize); 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci /* Find symbol table. */ 51562306a36Sopenharmony_ci secstrings = (void *)hdr + sechdrs[info->secindex_strings].sh_offset; 51662306a36Sopenharmony_ci for (i = 1; i < info->num_sections; i++) { 51762306a36Sopenharmony_ci const char *secname; 51862306a36Sopenharmony_ci int nobits = sechdrs[i].sh_type == SHT_NOBITS; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (!nobits && sechdrs[i].sh_offset > info->size) { 52162306a36Sopenharmony_ci fatal("%s is truncated. sechdrs[i].sh_offset=%lu > sizeof(*hrd)=%zu\n", 52262306a36Sopenharmony_ci filename, (unsigned long)sechdrs[i].sh_offset, 52362306a36Sopenharmony_ci sizeof(*hdr)); 52462306a36Sopenharmony_ci return 0; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci secname = secstrings + sechdrs[i].sh_name; 52762306a36Sopenharmony_ci if (strcmp(secname, ".modinfo") == 0) { 52862306a36Sopenharmony_ci if (nobits) 52962306a36Sopenharmony_ci fatal("%s has NOBITS .modinfo\n", filename); 53062306a36Sopenharmony_ci info->modinfo = (void *)hdr + sechdrs[i].sh_offset; 53162306a36Sopenharmony_ci info->modinfo_len = sechdrs[i].sh_size; 53262306a36Sopenharmony_ci } else if (!strcmp(secname, ".export_symbol")) { 53362306a36Sopenharmony_ci info->export_symbol_secndx = i; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (sechdrs[i].sh_type == SHT_SYMTAB) { 53762306a36Sopenharmony_ci unsigned int sh_link_idx; 53862306a36Sopenharmony_ci symtab_idx = i; 53962306a36Sopenharmony_ci info->symtab_start = (void *)hdr + 54062306a36Sopenharmony_ci sechdrs[i].sh_offset; 54162306a36Sopenharmony_ci info->symtab_stop = (void *)hdr + 54262306a36Sopenharmony_ci sechdrs[i].sh_offset + sechdrs[i].sh_size; 54362306a36Sopenharmony_ci sh_link_idx = sechdrs[i].sh_link; 54462306a36Sopenharmony_ci info->strtab = (void *)hdr + 54562306a36Sopenharmony_ci sechdrs[sh_link_idx].sh_offset; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* 32bit section no. table? ("more than 64k sections") */ 54962306a36Sopenharmony_ci if (sechdrs[i].sh_type == SHT_SYMTAB_SHNDX) { 55062306a36Sopenharmony_ci symtab_shndx_idx = i; 55162306a36Sopenharmony_ci info->symtab_shndx_start = (void *)hdr + 55262306a36Sopenharmony_ci sechdrs[i].sh_offset; 55362306a36Sopenharmony_ci info->symtab_shndx_stop = (void *)hdr + 55462306a36Sopenharmony_ci sechdrs[i].sh_offset + sechdrs[i].sh_size; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci if (!info->symtab_start) 55862306a36Sopenharmony_ci fatal("%s has no symtab?\n", filename); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* Fix endianness in symbols */ 56162306a36Sopenharmony_ci for (sym = info->symtab_start; sym < info->symtab_stop; sym++) { 56262306a36Sopenharmony_ci sym->st_shndx = TO_NATIVE(sym->st_shndx); 56362306a36Sopenharmony_ci sym->st_name = TO_NATIVE(sym->st_name); 56462306a36Sopenharmony_ci sym->st_value = TO_NATIVE(sym->st_value); 56562306a36Sopenharmony_ci sym->st_size = TO_NATIVE(sym->st_size); 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci if (symtab_shndx_idx != ~0U) { 56962306a36Sopenharmony_ci Elf32_Word *p; 57062306a36Sopenharmony_ci if (symtab_idx != sechdrs[symtab_shndx_idx].sh_link) 57162306a36Sopenharmony_ci fatal("%s: SYMTAB_SHNDX has bad sh_link: %u!=%u\n", 57262306a36Sopenharmony_ci filename, sechdrs[symtab_shndx_idx].sh_link, 57362306a36Sopenharmony_ci symtab_idx); 57462306a36Sopenharmony_ci /* Fix endianness */ 57562306a36Sopenharmony_ci for (p = info->symtab_shndx_start; p < info->symtab_shndx_stop; 57662306a36Sopenharmony_ci p++) 57762306a36Sopenharmony_ci *p = TO_NATIVE(*p); 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci return 1; 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_cistatic void parse_elf_finish(struct elf_info *info) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci release_file(info->hdr, info->size); 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_cistatic int ignore_undef_symbol(struct elf_info *info, const char *symname) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci /* ignore __this_module, it will be resolved shortly */ 59162306a36Sopenharmony_ci if (strcmp(symname, "__this_module") == 0) 59262306a36Sopenharmony_ci return 1; 59362306a36Sopenharmony_ci /* ignore global offset table */ 59462306a36Sopenharmony_ci if (strcmp(symname, "_GLOBAL_OFFSET_TABLE_") == 0) 59562306a36Sopenharmony_ci return 1; 59662306a36Sopenharmony_ci if (info->hdr->e_machine == EM_PPC) 59762306a36Sopenharmony_ci /* Special register function linked on all modules during final link of .ko */ 59862306a36Sopenharmony_ci if (strstarts(symname, "_restgpr_") || 59962306a36Sopenharmony_ci strstarts(symname, "_savegpr_") || 60062306a36Sopenharmony_ci strstarts(symname, "_rest32gpr_") || 60162306a36Sopenharmony_ci strstarts(symname, "_save32gpr_") || 60262306a36Sopenharmony_ci strstarts(symname, "_restvr_") || 60362306a36Sopenharmony_ci strstarts(symname, "_savevr_")) 60462306a36Sopenharmony_ci return 1; 60562306a36Sopenharmony_ci if (info->hdr->e_machine == EM_PPC64) 60662306a36Sopenharmony_ci /* Special register function linked on all modules during final link of .ko */ 60762306a36Sopenharmony_ci if (strstarts(symname, "_restgpr0_") || 60862306a36Sopenharmony_ci strstarts(symname, "_savegpr0_") || 60962306a36Sopenharmony_ci strstarts(symname, "_restvr_") || 61062306a36Sopenharmony_ci strstarts(symname, "_savevr_") || 61162306a36Sopenharmony_ci strcmp(symname, ".TOC.") == 0) 61262306a36Sopenharmony_ci return 1; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (info->hdr->e_machine == EM_S390) 61562306a36Sopenharmony_ci /* Expoline thunks are linked on all kernel modules during final link of .ko */ 61662306a36Sopenharmony_ci if (strstarts(symname, "__s390_indirect_jump_r")) 61762306a36Sopenharmony_ci return 1; 61862306a36Sopenharmony_ci /* Do not ignore this symbol */ 61962306a36Sopenharmony_ci return 0; 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cistatic void handle_symbol(struct module *mod, struct elf_info *info, 62362306a36Sopenharmony_ci const Elf_Sym *sym, const char *symname) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci switch (sym->st_shndx) { 62662306a36Sopenharmony_ci case SHN_COMMON: 62762306a36Sopenharmony_ci if (strstarts(symname, "__gnu_lto_")) { 62862306a36Sopenharmony_ci /* Should warn here, but modpost runs before the linker */ 62962306a36Sopenharmony_ci } else 63062306a36Sopenharmony_ci warn("\"%s\" [%s] is COMMON symbol\n", symname, mod->name); 63162306a36Sopenharmony_ci break; 63262306a36Sopenharmony_ci case SHN_UNDEF: 63362306a36Sopenharmony_ci /* undefined symbol */ 63462306a36Sopenharmony_ci if (ELF_ST_BIND(sym->st_info) != STB_GLOBAL && 63562306a36Sopenharmony_ci ELF_ST_BIND(sym->st_info) != STB_WEAK) 63662306a36Sopenharmony_ci break; 63762306a36Sopenharmony_ci if (ignore_undef_symbol(info, symname)) 63862306a36Sopenharmony_ci break; 63962306a36Sopenharmony_ci if (info->hdr->e_machine == EM_SPARC || 64062306a36Sopenharmony_ci info->hdr->e_machine == EM_SPARCV9) { 64162306a36Sopenharmony_ci /* Ignore register directives. */ 64262306a36Sopenharmony_ci if (ELF_ST_TYPE(sym->st_info) == STT_SPARC_REGISTER) 64362306a36Sopenharmony_ci break; 64462306a36Sopenharmony_ci if (symname[0] == '.') { 64562306a36Sopenharmony_ci char *munged = NOFAIL(strdup(symname)); 64662306a36Sopenharmony_ci munged[0] = '_'; 64762306a36Sopenharmony_ci munged[1] = toupper(munged[1]); 64862306a36Sopenharmony_ci symname = munged; 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci sym_add_unresolved(symname, mod, 65362306a36Sopenharmony_ci ELF_ST_BIND(sym->st_info) == STB_WEAK); 65462306a36Sopenharmony_ci break; 65562306a36Sopenharmony_ci default: 65662306a36Sopenharmony_ci if (strcmp(symname, "init_module") == 0) 65762306a36Sopenharmony_ci mod->has_init = true; 65862306a36Sopenharmony_ci if (strcmp(symname, "cleanup_module") == 0) 65962306a36Sopenharmony_ci mod->has_cleanup = true; 66062306a36Sopenharmony_ci break; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci/** 66562306a36Sopenharmony_ci * Parse tag=value strings from .modinfo section 66662306a36Sopenharmony_ci **/ 66762306a36Sopenharmony_cistatic char *next_string(char *string, unsigned long *secsize) 66862306a36Sopenharmony_ci{ 66962306a36Sopenharmony_ci /* Skip non-zero chars */ 67062306a36Sopenharmony_ci while (string[0]) { 67162306a36Sopenharmony_ci string++; 67262306a36Sopenharmony_ci if ((*secsize)-- <= 1) 67362306a36Sopenharmony_ci return NULL; 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci /* Skip any zero padding. */ 67762306a36Sopenharmony_ci while (!string[0]) { 67862306a36Sopenharmony_ci string++; 67962306a36Sopenharmony_ci if ((*secsize)-- <= 1) 68062306a36Sopenharmony_ci return NULL; 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci return string; 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_cistatic char *get_next_modinfo(struct elf_info *info, const char *tag, 68662306a36Sopenharmony_ci char *prev) 68762306a36Sopenharmony_ci{ 68862306a36Sopenharmony_ci char *p; 68962306a36Sopenharmony_ci unsigned int taglen = strlen(tag); 69062306a36Sopenharmony_ci char *modinfo = info->modinfo; 69162306a36Sopenharmony_ci unsigned long size = info->modinfo_len; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (prev) { 69462306a36Sopenharmony_ci size -= prev - modinfo; 69562306a36Sopenharmony_ci modinfo = next_string(prev, &size); 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci for (p = modinfo; p; p = next_string(p, &size)) { 69962306a36Sopenharmony_ci if (strncmp(p, tag, taglen) == 0 && p[taglen] == '=') 70062306a36Sopenharmony_ci return p + taglen + 1; 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci return NULL; 70362306a36Sopenharmony_ci} 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_cistatic char *get_modinfo(struct elf_info *info, const char *tag) 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci return get_next_modinfo(info, tag, NULL); 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_cistatic const char *sym_name(struct elf_info *elf, Elf_Sym *sym) 71262306a36Sopenharmony_ci{ 71362306a36Sopenharmony_ci if (sym) 71462306a36Sopenharmony_ci return elf->strtab + sym->st_name; 71562306a36Sopenharmony_ci else 71662306a36Sopenharmony_ci return "(unknown)"; 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci/* 72062306a36Sopenharmony_ci * Check whether the 'string' argument matches one of the 'patterns', 72162306a36Sopenharmony_ci * an array of shell wildcard patterns (glob). 72262306a36Sopenharmony_ci * 72362306a36Sopenharmony_ci * Return true is there is a match. 72462306a36Sopenharmony_ci */ 72562306a36Sopenharmony_cistatic bool match(const char *string, const char *const patterns[]) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci const char *pattern; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci while ((pattern = *patterns++)) { 73062306a36Sopenharmony_ci if (!fnmatch(pattern, string, 0)) 73162306a36Sopenharmony_ci return true; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci return false; 73562306a36Sopenharmony_ci} 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci/* useful to pass patterns to match() directly */ 73862306a36Sopenharmony_ci#define PATTERNS(...) \ 73962306a36Sopenharmony_ci ({ \ 74062306a36Sopenharmony_ci static const char *const patterns[] = {__VA_ARGS__, NULL}; \ 74162306a36Sopenharmony_ci patterns; \ 74262306a36Sopenharmony_ci }) 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci/* sections that we do not want to do full section mismatch check on */ 74562306a36Sopenharmony_cistatic const char *const section_white_list[] = 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci ".comment*", 74862306a36Sopenharmony_ci ".debug*", 74962306a36Sopenharmony_ci ".zdebug*", /* Compressed debug sections. */ 75062306a36Sopenharmony_ci ".GCC.command.line", /* record-gcc-switches */ 75162306a36Sopenharmony_ci ".mdebug*", /* alpha, score, mips etc. */ 75262306a36Sopenharmony_ci ".pdr", /* alpha, score, mips etc. */ 75362306a36Sopenharmony_ci ".stab*", 75462306a36Sopenharmony_ci ".note*", 75562306a36Sopenharmony_ci ".got*", 75662306a36Sopenharmony_ci ".toc*", 75762306a36Sopenharmony_ci ".xt.prop", /* xtensa */ 75862306a36Sopenharmony_ci ".xt.lit", /* xtensa */ 75962306a36Sopenharmony_ci ".arcextmap*", /* arc */ 76062306a36Sopenharmony_ci ".gnu.linkonce.arcext*", /* arc : modules */ 76162306a36Sopenharmony_ci ".cmem*", /* EZchip */ 76262306a36Sopenharmony_ci ".fmt_slot*", /* EZchip */ 76362306a36Sopenharmony_ci ".gnu.lto*", 76462306a36Sopenharmony_ci ".discard.*", 76562306a36Sopenharmony_ci ".llvm.call-graph-profile", /* call graph */ 76662306a36Sopenharmony_ci NULL 76762306a36Sopenharmony_ci}; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci/* 77062306a36Sopenharmony_ci * This is used to find sections missing the SHF_ALLOC flag. 77162306a36Sopenharmony_ci * The cause of this is often a section specified in assembler 77262306a36Sopenharmony_ci * without "ax" / "aw". 77362306a36Sopenharmony_ci */ 77462306a36Sopenharmony_cistatic void check_section(const char *modname, struct elf_info *elf, 77562306a36Sopenharmony_ci Elf_Shdr *sechdr) 77662306a36Sopenharmony_ci{ 77762306a36Sopenharmony_ci const char *sec = sech_name(elf, sechdr); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci if (sechdr->sh_type == SHT_PROGBITS && 78062306a36Sopenharmony_ci !(sechdr->sh_flags & SHF_ALLOC) && 78162306a36Sopenharmony_ci !match(sec, section_white_list)) { 78262306a36Sopenharmony_ci warn("%s (%s): unexpected non-allocatable section.\n" 78362306a36Sopenharmony_ci "Did you forget to use \"ax\"/\"aw\" in a .S file?\n" 78462306a36Sopenharmony_ci "Note that for example <linux/init.h> contains\n" 78562306a36Sopenharmony_ci "section definitions for use in .S files.\n\n", 78662306a36Sopenharmony_ci modname, sec); 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci} 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci#define ALL_INIT_DATA_SECTIONS \ 79362306a36Sopenharmony_ci ".init.setup", ".init.rodata", ".meminit.rodata", \ 79462306a36Sopenharmony_ci ".init.data", ".meminit.data" 79562306a36Sopenharmony_ci#define ALL_EXIT_DATA_SECTIONS \ 79662306a36Sopenharmony_ci ".exit.data", ".memexit.data" 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci#define ALL_INIT_TEXT_SECTIONS \ 79962306a36Sopenharmony_ci ".init.text", ".meminit.text" 80062306a36Sopenharmony_ci#define ALL_EXIT_TEXT_SECTIONS \ 80162306a36Sopenharmony_ci ".exit.text" 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci#define ALL_PCI_INIT_SECTIONS \ 80462306a36Sopenharmony_ci ".pci_fixup_early", ".pci_fixup_header", ".pci_fixup_final", \ 80562306a36Sopenharmony_ci ".pci_fixup_enable", ".pci_fixup_resume", \ 80662306a36Sopenharmony_ci ".pci_fixup_resume_early", ".pci_fixup_suspend" 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci#define ALL_XXXINIT_SECTIONS MEM_INIT_SECTIONS 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci#define ALL_INIT_SECTIONS INIT_SECTIONS, ALL_XXXINIT_SECTIONS 81162306a36Sopenharmony_ci#define ALL_EXIT_SECTIONS EXIT_SECTIONS 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci#define DATA_SECTIONS ".data", ".data.rel" 81462306a36Sopenharmony_ci#define TEXT_SECTIONS ".text", ".text.*", ".sched.text", \ 81562306a36Sopenharmony_ci ".kprobes.text", ".cpuidle.text", ".noinstr.text", \ 81662306a36Sopenharmony_ci ".ltext", ".ltext.*" 81762306a36Sopenharmony_ci#define OTHER_TEXT_SECTIONS ".ref.text", ".head.text", ".spinlock.text", \ 81862306a36Sopenharmony_ci ".fixup", ".entry.text", ".exception.text", \ 81962306a36Sopenharmony_ci ".coldtext", ".softirqentry.text" 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci#define INIT_SECTIONS ".init.*" 82262306a36Sopenharmony_ci#define MEM_INIT_SECTIONS ".meminit.*" 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci#define EXIT_SECTIONS ".exit.*" 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci#define ALL_TEXT_SECTIONS ALL_INIT_TEXT_SECTIONS, ALL_EXIT_TEXT_SECTIONS, \ 82762306a36Sopenharmony_ci TEXT_SECTIONS, OTHER_TEXT_SECTIONS 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_cienum mismatch { 83062306a36Sopenharmony_ci TEXT_TO_ANY_INIT, 83162306a36Sopenharmony_ci DATA_TO_ANY_INIT, 83262306a36Sopenharmony_ci TEXTDATA_TO_ANY_EXIT, 83362306a36Sopenharmony_ci XXXINIT_TO_SOME_INIT, 83462306a36Sopenharmony_ci ANY_INIT_TO_ANY_EXIT, 83562306a36Sopenharmony_ci ANY_EXIT_TO_ANY_INIT, 83662306a36Sopenharmony_ci EXTABLE_TO_NON_TEXT, 83762306a36Sopenharmony_ci}; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci/** 84062306a36Sopenharmony_ci * Describe how to match sections on different criteria: 84162306a36Sopenharmony_ci * 84262306a36Sopenharmony_ci * @fromsec: Array of sections to be matched. 84362306a36Sopenharmony_ci * 84462306a36Sopenharmony_ci * @bad_tosec: Relocations applied to a section in @fromsec to a section in 84562306a36Sopenharmony_ci * this array is forbidden (black-list). Can be empty. 84662306a36Sopenharmony_ci * 84762306a36Sopenharmony_ci * @good_tosec: Relocations applied to a section in @fromsec must be 84862306a36Sopenharmony_ci * targeting sections in this array (white-list). Can be empty. 84962306a36Sopenharmony_ci * 85062306a36Sopenharmony_ci * @mismatch: Type of mismatch. 85162306a36Sopenharmony_ci */ 85262306a36Sopenharmony_cistruct sectioncheck { 85362306a36Sopenharmony_ci const char *fromsec[20]; 85462306a36Sopenharmony_ci const char *bad_tosec[20]; 85562306a36Sopenharmony_ci const char *good_tosec[20]; 85662306a36Sopenharmony_ci enum mismatch mismatch; 85762306a36Sopenharmony_ci}; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_cistatic const struct sectioncheck sectioncheck[] = { 86062306a36Sopenharmony_ci/* Do not reference init/exit code/data from 86162306a36Sopenharmony_ci * normal code and data 86262306a36Sopenharmony_ci */ 86362306a36Sopenharmony_ci{ 86462306a36Sopenharmony_ci .fromsec = { TEXT_SECTIONS, NULL }, 86562306a36Sopenharmony_ci .bad_tosec = { ALL_INIT_SECTIONS, NULL }, 86662306a36Sopenharmony_ci .mismatch = TEXT_TO_ANY_INIT, 86762306a36Sopenharmony_ci}, 86862306a36Sopenharmony_ci{ 86962306a36Sopenharmony_ci .fromsec = { DATA_SECTIONS, NULL }, 87062306a36Sopenharmony_ci .bad_tosec = { ALL_XXXINIT_SECTIONS, INIT_SECTIONS, NULL }, 87162306a36Sopenharmony_ci .mismatch = DATA_TO_ANY_INIT, 87262306a36Sopenharmony_ci}, 87362306a36Sopenharmony_ci{ 87462306a36Sopenharmony_ci .fromsec = { TEXT_SECTIONS, DATA_SECTIONS, NULL }, 87562306a36Sopenharmony_ci .bad_tosec = { ALL_EXIT_SECTIONS, NULL }, 87662306a36Sopenharmony_ci .mismatch = TEXTDATA_TO_ANY_EXIT, 87762306a36Sopenharmony_ci}, 87862306a36Sopenharmony_ci/* Do not reference init code/data from meminit code/data */ 87962306a36Sopenharmony_ci{ 88062306a36Sopenharmony_ci .fromsec = { ALL_XXXINIT_SECTIONS, NULL }, 88162306a36Sopenharmony_ci .bad_tosec = { INIT_SECTIONS, NULL }, 88262306a36Sopenharmony_ci .mismatch = XXXINIT_TO_SOME_INIT, 88362306a36Sopenharmony_ci}, 88462306a36Sopenharmony_ci/* Do not use exit code/data from init code */ 88562306a36Sopenharmony_ci{ 88662306a36Sopenharmony_ci .fromsec = { ALL_INIT_SECTIONS, NULL }, 88762306a36Sopenharmony_ci .bad_tosec = { ALL_EXIT_SECTIONS, NULL }, 88862306a36Sopenharmony_ci .mismatch = ANY_INIT_TO_ANY_EXIT, 88962306a36Sopenharmony_ci}, 89062306a36Sopenharmony_ci/* Do not use init code/data from exit code */ 89162306a36Sopenharmony_ci{ 89262306a36Sopenharmony_ci .fromsec = { ALL_EXIT_SECTIONS, NULL }, 89362306a36Sopenharmony_ci .bad_tosec = { ALL_INIT_SECTIONS, NULL }, 89462306a36Sopenharmony_ci .mismatch = ANY_EXIT_TO_ANY_INIT, 89562306a36Sopenharmony_ci}, 89662306a36Sopenharmony_ci{ 89762306a36Sopenharmony_ci .fromsec = { ALL_PCI_INIT_SECTIONS, NULL }, 89862306a36Sopenharmony_ci .bad_tosec = { INIT_SECTIONS, NULL }, 89962306a36Sopenharmony_ci .mismatch = ANY_INIT_TO_ANY_EXIT, 90062306a36Sopenharmony_ci}, 90162306a36Sopenharmony_ci{ 90262306a36Sopenharmony_ci .fromsec = { "__ex_table", NULL }, 90362306a36Sopenharmony_ci /* If you're adding any new black-listed sections in here, consider 90462306a36Sopenharmony_ci * adding a special 'printer' for them in scripts/check_extable. 90562306a36Sopenharmony_ci */ 90662306a36Sopenharmony_ci .bad_tosec = { ".altinstr_replacement", NULL }, 90762306a36Sopenharmony_ci .good_tosec = {ALL_TEXT_SECTIONS , NULL}, 90862306a36Sopenharmony_ci .mismatch = EXTABLE_TO_NON_TEXT, 90962306a36Sopenharmony_ci} 91062306a36Sopenharmony_ci}; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_cistatic const struct sectioncheck *section_mismatch( 91362306a36Sopenharmony_ci const char *fromsec, const char *tosec) 91462306a36Sopenharmony_ci{ 91562306a36Sopenharmony_ci int i; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci /* 91862306a36Sopenharmony_ci * The target section could be the SHT_NUL section when we're 91962306a36Sopenharmony_ci * handling relocations to un-resolved symbols, trying to match it 92062306a36Sopenharmony_ci * doesn't make much sense and causes build failures on parisc 92162306a36Sopenharmony_ci * architectures. 92262306a36Sopenharmony_ci */ 92362306a36Sopenharmony_ci if (*tosec == '\0') 92462306a36Sopenharmony_ci return NULL; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sectioncheck); i++) { 92762306a36Sopenharmony_ci const struct sectioncheck *check = §ioncheck[i]; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci if (match(fromsec, check->fromsec)) { 93062306a36Sopenharmony_ci if (check->bad_tosec[0] && match(tosec, check->bad_tosec)) 93162306a36Sopenharmony_ci return check; 93262306a36Sopenharmony_ci if (check->good_tosec[0] && !match(tosec, check->good_tosec)) 93362306a36Sopenharmony_ci return check; 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci } 93662306a36Sopenharmony_ci return NULL; 93762306a36Sopenharmony_ci} 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci/** 94062306a36Sopenharmony_ci * Whitelist to allow certain references to pass with no warning. 94162306a36Sopenharmony_ci * 94262306a36Sopenharmony_ci * Pattern 1: 94362306a36Sopenharmony_ci * If a module parameter is declared __initdata and permissions=0 94462306a36Sopenharmony_ci * then this is legal despite the warning generated. 94562306a36Sopenharmony_ci * We cannot see value of permissions here, so just ignore 94662306a36Sopenharmony_ci * this pattern. 94762306a36Sopenharmony_ci * The pattern is identified by: 94862306a36Sopenharmony_ci * tosec = .init.data 94962306a36Sopenharmony_ci * fromsec = .data* 95062306a36Sopenharmony_ci * atsym =__param* 95162306a36Sopenharmony_ci * 95262306a36Sopenharmony_ci * Pattern 1a: 95362306a36Sopenharmony_ci * module_param_call() ops can refer to __init set function if permissions=0 95462306a36Sopenharmony_ci * The pattern is identified by: 95562306a36Sopenharmony_ci * tosec = .init.text 95662306a36Sopenharmony_ci * fromsec = .data* 95762306a36Sopenharmony_ci * atsym = __param_ops_* 95862306a36Sopenharmony_ci * 95962306a36Sopenharmony_ci * Pattern 3: 96062306a36Sopenharmony_ci * Whitelist all references from .head.text to any init section 96162306a36Sopenharmony_ci * 96262306a36Sopenharmony_ci * Pattern 4: 96362306a36Sopenharmony_ci * Some symbols belong to init section but still it is ok to reference 96462306a36Sopenharmony_ci * these from non-init sections as these symbols don't have any memory 96562306a36Sopenharmony_ci * allocated for them and symbol address and value are same. So even 96662306a36Sopenharmony_ci * if init section is freed, its ok to reference those symbols. 96762306a36Sopenharmony_ci * For ex. symbols marking the init section boundaries. 96862306a36Sopenharmony_ci * This pattern is identified by 96962306a36Sopenharmony_ci * refsymname = __init_begin, _sinittext, _einittext 97062306a36Sopenharmony_ci * 97162306a36Sopenharmony_ci * Pattern 5: 97262306a36Sopenharmony_ci * GCC may optimize static inlines when fed constant arg(s) resulting 97362306a36Sopenharmony_ci * in functions like cpumask_empty() -- generating an associated symbol 97462306a36Sopenharmony_ci * cpumask_empty.constprop.3 that appears in the audit. If the const that 97562306a36Sopenharmony_ci * is passed in comes from __init, like say nmi_ipi_mask, we get a 97662306a36Sopenharmony_ci * meaningless section warning. May need to add isra symbols too... 97762306a36Sopenharmony_ci * This pattern is identified by 97862306a36Sopenharmony_ci * tosec = init section 97962306a36Sopenharmony_ci * fromsec = text section 98062306a36Sopenharmony_ci * refsymname = *.constprop.* 98162306a36Sopenharmony_ci * 98262306a36Sopenharmony_ci **/ 98362306a36Sopenharmony_cistatic int secref_whitelist(const char *fromsec, const char *fromsym, 98462306a36Sopenharmony_ci const char *tosec, const char *tosym) 98562306a36Sopenharmony_ci{ 98662306a36Sopenharmony_ci /* Check for pattern 1 */ 98762306a36Sopenharmony_ci if (match(tosec, PATTERNS(ALL_INIT_DATA_SECTIONS)) && 98862306a36Sopenharmony_ci match(fromsec, PATTERNS(DATA_SECTIONS)) && 98962306a36Sopenharmony_ci strstarts(fromsym, "__param")) 99062306a36Sopenharmony_ci return 0; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci /* Check for pattern 1a */ 99362306a36Sopenharmony_ci if (strcmp(tosec, ".init.text") == 0 && 99462306a36Sopenharmony_ci match(fromsec, PATTERNS(DATA_SECTIONS)) && 99562306a36Sopenharmony_ci strstarts(fromsym, "__param_ops_")) 99662306a36Sopenharmony_ci return 0; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci /* symbols in data sections that may refer to any init/exit sections */ 99962306a36Sopenharmony_ci if (match(fromsec, PATTERNS(DATA_SECTIONS)) && 100062306a36Sopenharmony_ci match(tosec, PATTERNS(ALL_INIT_SECTIONS, ALL_EXIT_SECTIONS)) && 100162306a36Sopenharmony_ci match(fromsym, PATTERNS("*_template", // scsi uses *_template a lot 100262306a36Sopenharmony_ci "*_timer", // arm uses ops structures named _timer a lot 100362306a36Sopenharmony_ci "*_sht", // scsi also used *_sht to some extent 100462306a36Sopenharmony_ci "*_ops", 100562306a36Sopenharmony_ci "*_probe", 100662306a36Sopenharmony_ci "*_probe_one", 100762306a36Sopenharmony_ci "*_console"))) 100862306a36Sopenharmony_ci return 0; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci /* symbols in data sections that may refer to meminit sections */ 101162306a36Sopenharmony_ci if (match(fromsec, PATTERNS(DATA_SECTIONS)) && 101262306a36Sopenharmony_ci match(tosec, PATTERNS(ALL_XXXINIT_SECTIONS)) && 101362306a36Sopenharmony_ci match(fromsym, PATTERNS("*driver"))) 101462306a36Sopenharmony_ci return 0; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci /* 101762306a36Sopenharmony_ci * symbols in data sections must not refer to .exit.*, but there are 101862306a36Sopenharmony_ci * quite a few offenders, so hide these unless for W=1 builds until 101962306a36Sopenharmony_ci * these are fixed. 102062306a36Sopenharmony_ci */ 102162306a36Sopenharmony_ci if (!extra_warn && 102262306a36Sopenharmony_ci match(fromsec, PATTERNS(DATA_SECTIONS)) && 102362306a36Sopenharmony_ci match(tosec, PATTERNS(EXIT_SECTIONS)) && 102462306a36Sopenharmony_ci match(fromsym, PATTERNS("*driver"))) 102562306a36Sopenharmony_ci return 0; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci /* Check for pattern 3 */ 102862306a36Sopenharmony_ci if (strstarts(fromsec, ".head.text") && 102962306a36Sopenharmony_ci match(tosec, PATTERNS(ALL_INIT_SECTIONS))) 103062306a36Sopenharmony_ci return 0; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci /* Check for pattern 4 */ 103362306a36Sopenharmony_ci if (match(tosym, PATTERNS("__init_begin", "_sinittext", "_einittext"))) 103462306a36Sopenharmony_ci return 0; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci /* Check for pattern 5 */ 103762306a36Sopenharmony_ci if (match(fromsec, PATTERNS(ALL_TEXT_SECTIONS)) && 103862306a36Sopenharmony_ci match(tosec, PATTERNS(ALL_INIT_SECTIONS)) && 103962306a36Sopenharmony_ci match(fromsym, PATTERNS("*.constprop.*"))) 104062306a36Sopenharmony_ci return 0; 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci return 1; 104362306a36Sopenharmony_ci} 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci/* 104662306a36Sopenharmony_ci * If there's no name there, ignore it; likewise, ignore it if it's 104762306a36Sopenharmony_ci * one of the magic symbols emitted used by current tools. 104862306a36Sopenharmony_ci * 104962306a36Sopenharmony_ci * Otherwise if find_symbols_between() returns those symbols, they'll 105062306a36Sopenharmony_ci * fail the whitelist tests and cause lots of false alarms ... fixable 105162306a36Sopenharmony_ci * only by merging __exit and __init sections into __text, bloating 105262306a36Sopenharmony_ci * the kernel (which is especially evil on embedded platforms). 105362306a36Sopenharmony_ci */ 105462306a36Sopenharmony_cistatic inline int is_valid_name(struct elf_info *elf, Elf_Sym *sym) 105562306a36Sopenharmony_ci{ 105662306a36Sopenharmony_ci const char *name = elf->strtab + sym->st_name; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci if (!name || !strlen(name)) 105962306a36Sopenharmony_ci return 0; 106062306a36Sopenharmony_ci return !is_mapping_symbol(name); 106162306a36Sopenharmony_ci} 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci/* Look up the nearest symbol based on the section and the address */ 106462306a36Sopenharmony_cistatic Elf_Sym *find_nearest_sym(struct elf_info *elf, Elf_Addr addr, 106562306a36Sopenharmony_ci unsigned int secndx, bool allow_negative, 106662306a36Sopenharmony_ci Elf_Addr min_distance) 106762306a36Sopenharmony_ci{ 106862306a36Sopenharmony_ci Elf_Sym *sym; 106962306a36Sopenharmony_ci Elf_Sym *near = NULL; 107062306a36Sopenharmony_ci Elf_Addr sym_addr, distance; 107162306a36Sopenharmony_ci bool is_arm = (elf->hdr->e_machine == EM_ARM); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci for (sym = elf->symtab_start; sym < elf->symtab_stop; sym++) { 107462306a36Sopenharmony_ci if (get_secindex(elf, sym) != secndx) 107562306a36Sopenharmony_ci continue; 107662306a36Sopenharmony_ci if (!is_valid_name(elf, sym)) 107762306a36Sopenharmony_ci continue; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci sym_addr = sym->st_value; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci /* 108262306a36Sopenharmony_ci * For ARM Thumb instruction, the bit 0 of st_value is set 108362306a36Sopenharmony_ci * if the symbol is STT_FUNC type. Mask it to get the address. 108462306a36Sopenharmony_ci */ 108562306a36Sopenharmony_ci if (is_arm && ELF_ST_TYPE(sym->st_info) == STT_FUNC) 108662306a36Sopenharmony_ci sym_addr &= ~1; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci if (addr >= sym_addr) 108962306a36Sopenharmony_ci distance = addr - sym_addr; 109062306a36Sopenharmony_ci else if (allow_negative) 109162306a36Sopenharmony_ci distance = sym_addr - addr; 109262306a36Sopenharmony_ci else 109362306a36Sopenharmony_ci continue; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci if (distance <= min_distance) { 109662306a36Sopenharmony_ci min_distance = distance; 109762306a36Sopenharmony_ci near = sym; 109862306a36Sopenharmony_ci } 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci if (min_distance == 0) 110162306a36Sopenharmony_ci break; 110262306a36Sopenharmony_ci } 110362306a36Sopenharmony_ci return near; 110462306a36Sopenharmony_ci} 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_cistatic Elf_Sym *find_fromsym(struct elf_info *elf, Elf_Addr addr, 110762306a36Sopenharmony_ci unsigned int secndx) 110862306a36Sopenharmony_ci{ 110962306a36Sopenharmony_ci return find_nearest_sym(elf, addr, secndx, false, ~0); 111062306a36Sopenharmony_ci} 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_cistatic Elf_Sym *find_tosym(struct elf_info *elf, Elf_Addr addr, Elf_Sym *sym) 111362306a36Sopenharmony_ci{ 111462306a36Sopenharmony_ci /* If the supplied symbol has a valid name, return it */ 111562306a36Sopenharmony_ci if (is_valid_name(elf, sym)) 111662306a36Sopenharmony_ci return sym; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci /* 111962306a36Sopenharmony_ci * Strive to find a better symbol name, but the resulting name may not 112062306a36Sopenharmony_ci * match the symbol referenced in the original code. 112162306a36Sopenharmony_ci */ 112262306a36Sopenharmony_ci return find_nearest_sym(elf, addr, get_secindex(elf, sym), true, 20); 112362306a36Sopenharmony_ci} 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_cistatic bool is_executable_section(struct elf_info *elf, unsigned int secndx) 112662306a36Sopenharmony_ci{ 112762306a36Sopenharmony_ci if (secndx >= elf->num_sections) 112862306a36Sopenharmony_ci return false; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci return (elf->sechdrs[secndx].sh_flags & SHF_EXECINSTR) != 0; 113162306a36Sopenharmony_ci} 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_cistatic void default_mismatch_handler(const char *modname, struct elf_info *elf, 113462306a36Sopenharmony_ci const struct sectioncheck* const mismatch, 113562306a36Sopenharmony_ci Elf_Sym *tsym, 113662306a36Sopenharmony_ci unsigned int fsecndx, const char *fromsec, Elf_Addr faddr, 113762306a36Sopenharmony_ci const char *tosec, Elf_Addr taddr) 113862306a36Sopenharmony_ci{ 113962306a36Sopenharmony_ci Elf_Sym *from; 114062306a36Sopenharmony_ci const char *tosym; 114162306a36Sopenharmony_ci const char *fromsym; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci from = find_fromsym(elf, faddr, fsecndx); 114462306a36Sopenharmony_ci fromsym = sym_name(elf, from); 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci tsym = find_tosym(elf, taddr, tsym); 114762306a36Sopenharmony_ci tosym = sym_name(elf, tsym); 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci /* check whitelist - we may ignore it */ 115062306a36Sopenharmony_ci if (!secref_whitelist(fromsec, fromsym, tosec, tosym)) 115162306a36Sopenharmony_ci return; 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci sec_mismatch_count++; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci warn("%s: section mismatch in reference: %s+0x%x (section: %s) -> %s (section: %s)\n", 115662306a36Sopenharmony_ci modname, fromsym, (unsigned int)(faddr - from->st_value), fromsec, tosym, tosec); 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci if (mismatch->mismatch == EXTABLE_TO_NON_TEXT) { 115962306a36Sopenharmony_ci if (match(tosec, mismatch->bad_tosec)) 116062306a36Sopenharmony_ci fatal("The relocation at %s+0x%lx references\n" 116162306a36Sopenharmony_ci "section \"%s\" which is black-listed.\n" 116262306a36Sopenharmony_ci "Something is seriously wrong and should be fixed.\n" 116362306a36Sopenharmony_ci "You might get more information about where this is\n" 116462306a36Sopenharmony_ci "coming from by using scripts/check_extable.sh %s\n", 116562306a36Sopenharmony_ci fromsec, (long)faddr, tosec, modname); 116662306a36Sopenharmony_ci else if (is_executable_section(elf, get_secindex(elf, tsym))) 116762306a36Sopenharmony_ci warn("The relocation at %s+0x%lx references\n" 116862306a36Sopenharmony_ci "section \"%s\" which is not in the list of\n" 116962306a36Sopenharmony_ci "authorized sections. If you're adding a new section\n" 117062306a36Sopenharmony_ci "and/or if this reference is valid, add \"%s\" to the\n" 117162306a36Sopenharmony_ci "list of authorized sections to jump to on fault.\n" 117262306a36Sopenharmony_ci "This can be achieved by adding \"%s\" to\n" 117362306a36Sopenharmony_ci "OTHER_TEXT_SECTIONS in scripts/mod/modpost.c.\n", 117462306a36Sopenharmony_ci fromsec, (long)faddr, tosec, tosec, tosec); 117562306a36Sopenharmony_ci else 117662306a36Sopenharmony_ci error("%s+0x%lx references non-executable section '%s'\n", 117762306a36Sopenharmony_ci fromsec, (long)faddr, tosec); 117862306a36Sopenharmony_ci } 117962306a36Sopenharmony_ci} 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_cistatic void check_export_symbol(struct module *mod, struct elf_info *elf, 118262306a36Sopenharmony_ci Elf_Addr faddr, const char *secname, 118362306a36Sopenharmony_ci Elf_Sym *sym) 118462306a36Sopenharmony_ci{ 118562306a36Sopenharmony_ci static const char *prefix = "__export_symbol_"; 118662306a36Sopenharmony_ci const char *label_name, *name, *data; 118762306a36Sopenharmony_ci Elf_Sym *label; 118862306a36Sopenharmony_ci struct symbol *s; 118962306a36Sopenharmony_ci bool is_gpl; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci label = find_fromsym(elf, faddr, elf->export_symbol_secndx); 119262306a36Sopenharmony_ci label_name = sym_name(elf, label); 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci if (!strstarts(label_name, prefix)) { 119562306a36Sopenharmony_ci error("%s: .export_symbol section contains strange symbol '%s'\n", 119662306a36Sopenharmony_ci mod->name, label_name); 119762306a36Sopenharmony_ci return; 119862306a36Sopenharmony_ci } 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci if (ELF_ST_BIND(sym->st_info) != STB_GLOBAL && 120162306a36Sopenharmony_ci ELF_ST_BIND(sym->st_info) != STB_WEAK) { 120262306a36Sopenharmony_ci error("%s: local symbol '%s' was exported\n", mod->name, 120362306a36Sopenharmony_ci label_name + strlen(prefix)); 120462306a36Sopenharmony_ci return; 120562306a36Sopenharmony_ci } 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci name = sym_name(elf, sym); 120862306a36Sopenharmony_ci if (strcmp(label_name + strlen(prefix), name)) { 120962306a36Sopenharmony_ci error("%s: .export_symbol section references '%s', but it does not seem to be an export symbol\n", 121062306a36Sopenharmony_ci mod->name, name); 121162306a36Sopenharmony_ci return; 121262306a36Sopenharmony_ci } 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci data = sym_get_data(elf, label); /* license */ 121562306a36Sopenharmony_ci if (!strcmp(data, "GPL")) { 121662306a36Sopenharmony_ci is_gpl = true; 121762306a36Sopenharmony_ci } else if (!strcmp(data, "")) { 121862306a36Sopenharmony_ci is_gpl = false; 121962306a36Sopenharmony_ci } else { 122062306a36Sopenharmony_ci error("%s: unknown license '%s' was specified for '%s'\n", 122162306a36Sopenharmony_ci mod->name, data, name); 122262306a36Sopenharmony_ci return; 122362306a36Sopenharmony_ci } 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci data += strlen(data) + 1; /* namespace */ 122662306a36Sopenharmony_ci s = sym_add_exported(name, mod, is_gpl, data); 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci /* 122962306a36Sopenharmony_ci * We need to be aware whether we are exporting a function or 123062306a36Sopenharmony_ci * a data on some architectures. 123162306a36Sopenharmony_ci */ 123262306a36Sopenharmony_ci s->is_func = (ELF_ST_TYPE(sym->st_info) == STT_FUNC); 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci /* 123562306a36Sopenharmony_ci * For parisc64, symbols prefixed $$ from the library have the symbol type 123662306a36Sopenharmony_ci * STT_LOPROC. They should be handled as functions too. 123762306a36Sopenharmony_ci */ 123862306a36Sopenharmony_ci if (elf->hdr->e_ident[EI_CLASS] == ELFCLASS64 && 123962306a36Sopenharmony_ci elf->hdr->e_machine == EM_PARISC && 124062306a36Sopenharmony_ci ELF_ST_TYPE(sym->st_info) == STT_LOPROC) 124162306a36Sopenharmony_ci s->is_func = true; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci if (match(secname, PATTERNS(INIT_SECTIONS))) 124462306a36Sopenharmony_ci warn("%s: %s: EXPORT_SYMBOL used for init symbol. Remove __init or EXPORT_SYMBOL.\n", 124562306a36Sopenharmony_ci mod->name, name); 124662306a36Sopenharmony_ci else if (match(secname, PATTERNS(EXIT_SECTIONS))) 124762306a36Sopenharmony_ci warn("%s: %s: EXPORT_SYMBOL used for exit symbol. Remove __exit or EXPORT_SYMBOL.\n", 124862306a36Sopenharmony_ci mod->name, name); 124962306a36Sopenharmony_ci} 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_cistatic void check_section_mismatch(struct module *mod, struct elf_info *elf, 125262306a36Sopenharmony_ci Elf_Sym *sym, 125362306a36Sopenharmony_ci unsigned int fsecndx, const char *fromsec, 125462306a36Sopenharmony_ci Elf_Addr faddr, Elf_Addr taddr) 125562306a36Sopenharmony_ci{ 125662306a36Sopenharmony_ci const char *tosec = sec_name(elf, get_secindex(elf, sym)); 125762306a36Sopenharmony_ci const struct sectioncheck *mismatch; 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci if (module_enabled && elf->export_symbol_secndx == fsecndx) { 126062306a36Sopenharmony_ci check_export_symbol(mod, elf, faddr, tosec, sym); 126162306a36Sopenharmony_ci return; 126262306a36Sopenharmony_ci } 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci mismatch = section_mismatch(fromsec, tosec); 126562306a36Sopenharmony_ci if (!mismatch) 126662306a36Sopenharmony_ci return; 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci default_mismatch_handler(mod->name, elf, mismatch, sym, 126962306a36Sopenharmony_ci fsecndx, fromsec, faddr, 127062306a36Sopenharmony_ci tosec, taddr); 127162306a36Sopenharmony_ci} 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_cistatic Elf_Addr addend_386_rel(uint32_t *location, unsigned int r_type) 127462306a36Sopenharmony_ci{ 127562306a36Sopenharmony_ci switch (r_type) { 127662306a36Sopenharmony_ci case R_386_32: 127762306a36Sopenharmony_ci return TO_NATIVE(*location); 127862306a36Sopenharmony_ci case R_386_PC32: 127962306a36Sopenharmony_ci return TO_NATIVE(*location) + 4; 128062306a36Sopenharmony_ci } 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci return (Elf_Addr)(-1); 128362306a36Sopenharmony_ci} 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci#ifndef R_ARM_CALL 128662306a36Sopenharmony_ci#define R_ARM_CALL 28 128762306a36Sopenharmony_ci#endif 128862306a36Sopenharmony_ci#ifndef R_ARM_JUMP24 128962306a36Sopenharmony_ci#define R_ARM_JUMP24 29 129062306a36Sopenharmony_ci#endif 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci#ifndef R_ARM_THM_CALL 129362306a36Sopenharmony_ci#define R_ARM_THM_CALL 10 129462306a36Sopenharmony_ci#endif 129562306a36Sopenharmony_ci#ifndef R_ARM_THM_JUMP24 129662306a36Sopenharmony_ci#define R_ARM_THM_JUMP24 30 129762306a36Sopenharmony_ci#endif 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci#ifndef R_ARM_MOVW_ABS_NC 130062306a36Sopenharmony_ci#define R_ARM_MOVW_ABS_NC 43 130162306a36Sopenharmony_ci#endif 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci#ifndef R_ARM_MOVT_ABS 130462306a36Sopenharmony_ci#define R_ARM_MOVT_ABS 44 130562306a36Sopenharmony_ci#endif 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci#ifndef R_ARM_THM_MOVW_ABS_NC 130862306a36Sopenharmony_ci#define R_ARM_THM_MOVW_ABS_NC 47 130962306a36Sopenharmony_ci#endif 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci#ifndef R_ARM_THM_MOVT_ABS 131262306a36Sopenharmony_ci#define R_ARM_THM_MOVT_ABS 48 131362306a36Sopenharmony_ci#endif 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci#ifndef R_ARM_THM_JUMP19 131662306a36Sopenharmony_ci#define R_ARM_THM_JUMP19 51 131762306a36Sopenharmony_ci#endif 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_cistatic int32_t sign_extend32(int32_t value, int index) 132062306a36Sopenharmony_ci{ 132162306a36Sopenharmony_ci uint8_t shift = 31 - index; 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci return (int32_t)(value << shift) >> shift; 132462306a36Sopenharmony_ci} 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_cistatic Elf_Addr addend_arm_rel(void *loc, Elf_Sym *sym, unsigned int r_type) 132762306a36Sopenharmony_ci{ 132862306a36Sopenharmony_ci uint32_t inst, upper, lower, sign, j1, j2; 132962306a36Sopenharmony_ci int32_t offset; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci switch (r_type) { 133262306a36Sopenharmony_ci case R_ARM_ABS32: 133362306a36Sopenharmony_ci case R_ARM_REL32: 133462306a36Sopenharmony_ci inst = TO_NATIVE(*(uint32_t *)loc); 133562306a36Sopenharmony_ci return inst + sym->st_value; 133662306a36Sopenharmony_ci case R_ARM_MOVW_ABS_NC: 133762306a36Sopenharmony_ci case R_ARM_MOVT_ABS: 133862306a36Sopenharmony_ci inst = TO_NATIVE(*(uint32_t *)loc); 133962306a36Sopenharmony_ci offset = sign_extend32(((inst & 0xf0000) >> 4) | (inst & 0xfff), 134062306a36Sopenharmony_ci 15); 134162306a36Sopenharmony_ci return offset + sym->st_value; 134262306a36Sopenharmony_ci case R_ARM_PC24: 134362306a36Sopenharmony_ci case R_ARM_CALL: 134462306a36Sopenharmony_ci case R_ARM_JUMP24: 134562306a36Sopenharmony_ci inst = TO_NATIVE(*(uint32_t *)loc); 134662306a36Sopenharmony_ci offset = sign_extend32((inst & 0x00ffffff) << 2, 25); 134762306a36Sopenharmony_ci return offset + sym->st_value + 8; 134862306a36Sopenharmony_ci case R_ARM_THM_MOVW_ABS_NC: 134962306a36Sopenharmony_ci case R_ARM_THM_MOVT_ABS: 135062306a36Sopenharmony_ci upper = TO_NATIVE(*(uint16_t *)loc); 135162306a36Sopenharmony_ci lower = TO_NATIVE(*((uint16_t *)loc + 1)); 135262306a36Sopenharmony_ci offset = sign_extend32(((upper & 0x000f) << 12) | 135362306a36Sopenharmony_ci ((upper & 0x0400) << 1) | 135462306a36Sopenharmony_ci ((lower & 0x7000) >> 4) | 135562306a36Sopenharmony_ci (lower & 0x00ff), 135662306a36Sopenharmony_ci 15); 135762306a36Sopenharmony_ci return offset + sym->st_value; 135862306a36Sopenharmony_ci case R_ARM_THM_JUMP19: 135962306a36Sopenharmony_ci /* 136062306a36Sopenharmony_ci * Encoding T3: 136162306a36Sopenharmony_ci * S = upper[10] 136262306a36Sopenharmony_ci * imm6 = upper[5:0] 136362306a36Sopenharmony_ci * J1 = lower[13] 136462306a36Sopenharmony_ci * J2 = lower[11] 136562306a36Sopenharmony_ci * imm11 = lower[10:0] 136662306a36Sopenharmony_ci * imm32 = SignExtend(S:J2:J1:imm6:imm11:'0') 136762306a36Sopenharmony_ci */ 136862306a36Sopenharmony_ci upper = TO_NATIVE(*(uint16_t *)loc); 136962306a36Sopenharmony_ci lower = TO_NATIVE(*((uint16_t *)loc + 1)); 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci sign = (upper >> 10) & 1; 137262306a36Sopenharmony_ci j1 = (lower >> 13) & 1; 137362306a36Sopenharmony_ci j2 = (lower >> 11) & 1; 137462306a36Sopenharmony_ci offset = sign_extend32((sign << 20) | (j2 << 19) | (j1 << 18) | 137562306a36Sopenharmony_ci ((upper & 0x03f) << 12) | 137662306a36Sopenharmony_ci ((lower & 0x07ff) << 1), 137762306a36Sopenharmony_ci 20); 137862306a36Sopenharmony_ci return offset + sym->st_value + 4; 137962306a36Sopenharmony_ci case R_ARM_THM_CALL: 138062306a36Sopenharmony_ci case R_ARM_THM_JUMP24: 138162306a36Sopenharmony_ci /* 138262306a36Sopenharmony_ci * Encoding T4: 138362306a36Sopenharmony_ci * S = upper[10] 138462306a36Sopenharmony_ci * imm10 = upper[9:0] 138562306a36Sopenharmony_ci * J1 = lower[13] 138662306a36Sopenharmony_ci * J2 = lower[11] 138762306a36Sopenharmony_ci * imm11 = lower[10:0] 138862306a36Sopenharmony_ci * I1 = NOT(J1 XOR S) 138962306a36Sopenharmony_ci * I2 = NOT(J2 XOR S) 139062306a36Sopenharmony_ci * imm32 = SignExtend(S:I1:I2:imm10:imm11:'0') 139162306a36Sopenharmony_ci */ 139262306a36Sopenharmony_ci upper = TO_NATIVE(*(uint16_t *)loc); 139362306a36Sopenharmony_ci lower = TO_NATIVE(*((uint16_t *)loc + 1)); 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci sign = (upper >> 10) & 1; 139662306a36Sopenharmony_ci j1 = (lower >> 13) & 1; 139762306a36Sopenharmony_ci j2 = (lower >> 11) & 1; 139862306a36Sopenharmony_ci offset = sign_extend32((sign << 24) | 139962306a36Sopenharmony_ci ((~(j1 ^ sign) & 1) << 23) | 140062306a36Sopenharmony_ci ((~(j2 ^ sign) & 1) << 22) | 140162306a36Sopenharmony_ci ((upper & 0x03ff) << 12) | 140262306a36Sopenharmony_ci ((lower & 0x07ff) << 1), 140362306a36Sopenharmony_ci 24); 140462306a36Sopenharmony_ci return offset + sym->st_value + 4; 140562306a36Sopenharmony_ci } 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci return (Elf_Addr)(-1); 140862306a36Sopenharmony_ci} 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_cistatic Elf_Addr addend_mips_rel(uint32_t *location, unsigned int r_type) 141162306a36Sopenharmony_ci{ 141262306a36Sopenharmony_ci uint32_t inst; 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci inst = TO_NATIVE(*location); 141562306a36Sopenharmony_ci switch (r_type) { 141662306a36Sopenharmony_ci case R_MIPS_LO16: 141762306a36Sopenharmony_ci return inst & 0xffff; 141862306a36Sopenharmony_ci case R_MIPS_26: 141962306a36Sopenharmony_ci return (inst & 0x03ffffff) << 2; 142062306a36Sopenharmony_ci case R_MIPS_32: 142162306a36Sopenharmony_ci return inst; 142262306a36Sopenharmony_ci } 142362306a36Sopenharmony_ci return (Elf_Addr)(-1); 142462306a36Sopenharmony_ci} 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci#ifndef EM_RISCV 142762306a36Sopenharmony_ci#define EM_RISCV 243 142862306a36Sopenharmony_ci#endif 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci#ifndef R_RISCV_SUB32 143162306a36Sopenharmony_ci#define R_RISCV_SUB32 39 143262306a36Sopenharmony_ci#endif 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci#ifndef EM_LOONGARCH 143562306a36Sopenharmony_ci#define EM_LOONGARCH 258 143662306a36Sopenharmony_ci#endif 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci#ifndef R_LARCH_SUB32 143962306a36Sopenharmony_ci#define R_LARCH_SUB32 55 144062306a36Sopenharmony_ci#endif 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_cistatic void get_rel_type_and_sym(struct elf_info *elf, uint64_t r_info, 144362306a36Sopenharmony_ci unsigned int *r_type, unsigned int *r_sym) 144462306a36Sopenharmony_ci{ 144562306a36Sopenharmony_ci typedef struct { 144662306a36Sopenharmony_ci Elf64_Word r_sym; /* Symbol index */ 144762306a36Sopenharmony_ci unsigned char r_ssym; /* Special symbol for 2nd relocation */ 144862306a36Sopenharmony_ci unsigned char r_type3; /* 3rd relocation type */ 144962306a36Sopenharmony_ci unsigned char r_type2; /* 2nd relocation type */ 145062306a36Sopenharmony_ci unsigned char r_type; /* 1st relocation type */ 145162306a36Sopenharmony_ci } Elf64_Mips_R_Info; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci bool is_64bit = (elf->hdr->e_ident[EI_CLASS] == ELFCLASS64); 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci if (elf->hdr->e_machine == EM_MIPS && is_64bit) { 145662306a36Sopenharmony_ci Elf64_Mips_R_Info *mips64_r_info = (void *)&r_info; 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci *r_type = mips64_r_info->r_type; 145962306a36Sopenharmony_ci *r_sym = TO_NATIVE(mips64_r_info->r_sym); 146062306a36Sopenharmony_ci return; 146162306a36Sopenharmony_ci } 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci if (is_64bit) { 146462306a36Sopenharmony_ci Elf64_Xword r_info64 = r_info; 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci r_info = TO_NATIVE(r_info64); 146762306a36Sopenharmony_ci } else { 146862306a36Sopenharmony_ci Elf32_Word r_info32 = r_info; 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci r_info = TO_NATIVE(r_info32); 147162306a36Sopenharmony_ci } 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci *r_type = ELF_R_TYPE(r_info); 147462306a36Sopenharmony_ci *r_sym = ELF_R_SYM(r_info); 147562306a36Sopenharmony_ci} 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_cistatic void section_rela(struct module *mod, struct elf_info *elf, 147862306a36Sopenharmony_ci Elf_Shdr *sechdr) 147962306a36Sopenharmony_ci{ 148062306a36Sopenharmony_ci Elf_Rela *rela; 148162306a36Sopenharmony_ci unsigned int fsecndx = sechdr->sh_info; 148262306a36Sopenharmony_ci const char *fromsec = sec_name(elf, fsecndx); 148362306a36Sopenharmony_ci Elf_Rela *start = (void *)elf->hdr + sechdr->sh_offset; 148462306a36Sopenharmony_ci Elf_Rela *stop = (void *)start + sechdr->sh_size; 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci /* if from section (name) is know good then skip it */ 148762306a36Sopenharmony_ci if (match(fromsec, section_white_list)) 148862306a36Sopenharmony_ci return; 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci for (rela = start; rela < stop; rela++) { 149162306a36Sopenharmony_ci Elf_Sym *tsym; 149262306a36Sopenharmony_ci Elf_Addr taddr, r_offset; 149362306a36Sopenharmony_ci unsigned int r_type, r_sym; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci r_offset = TO_NATIVE(rela->r_offset); 149662306a36Sopenharmony_ci get_rel_type_and_sym(elf, rela->r_info, &r_type, &r_sym); 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci tsym = elf->symtab_start + r_sym; 149962306a36Sopenharmony_ci taddr = tsym->st_value + TO_NATIVE(rela->r_addend); 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci switch (elf->hdr->e_machine) { 150262306a36Sopenharmony_ci case EM_RISCV: 150362306a36Sopenharmony_ci if (!strcmp("__ex_table", fromsec) && 150462306a36Sopenharmony_ci r_type == R_RISCV_SUB32) 150562306a36Sopenharmony_ci continue; 150662306a36Sopenharmony_ci break; 150762306a36Sopenharmony_ci case EM_LOONGARCH: 150862306a36Sopenharmony_ci if (!strcmp("__ex_table", fromsec) && 150962306a36Sopenharmony_ci r_type == R_LARCH_SUB32) 151062306a36Sopenharmony_ci continue; 151162306a36Sopenharmony_ci break; 151262306a36Sopenharmony_ci } 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci check_section_mismatch(mod, elf, tsym, 151562306a36Sopenharmony_ci fsecndx, fromsec, r_offset, taddr); 151662306a36Sopenharmony_ci } 151762306a36Sopenharmony_ci} 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_cistatic void section_rel(struct module *mod, struct elf_info *elf, 152062306a36Sopenharmony_ci Elf_Shdr *sechdr) 152162306a36Sopenharmony_ci{ 152262306a36Sopenharmony_ci Elf_Rel *rel; 152362306a36Sopenharmony_ci unsigned int fsecndx = sechdr->sh_info; 152462306a36Sopenharmony_ci const char *fromsec = sec_name(elf, fsecndx); 152562306a36Sopenharmony_ci Elf_Rel *start = (void *)elf->hdr + sechdr->sh_offset; 152662306a36Sopenharmony_ci Elf_Rel *stop = (void *)start + sechdr->sh_size; 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci /* if from section (name) is know good then skip it */ 152962306a36Sopenharmony_ci if (match(fromsec, section_white_list)) 153062306a36Sopenharmony_ci return; 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci for (rel = start; rel < stop; rel++) { 153362306a36Sopenharmony_ci Elf_Sym *tsym; 153462306a36Sopenharmony_ci Elf_Addr taddr = 0, r_offset; 153562306a36Sopenharmony_ci unsigned int r_type, r_sym; 153662306a36Sopenharmony_ci void *loc; 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci r_offset = TO_NATIVE(rel->r_offset); 153962306a36Sopenharmony_ci get_rel_type_and_sym(elf, rel->r_info, &r_type, &r_sym); 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci loc = sym_get_data_by_offset(elf, fsecndx, r_offset); 154262306a36Sopenharmony_ci tsym = elf->symtab_start + r_sym; 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci switch (elf->hdr->e_machine) { 154562306a36Sopenharmony_ci case EM_386: 154662306a36Sopenharmony_ci taddr = addend_386_rel(loc, r_type); 154762306a36Sopenharmony_ci break; 154862306a36Sopenharmony_ci case EM_ARM: 154962306a36Sopenharmony_ci taddr = addend_arm_rel(loc, tsym, r_type); 155062306a36Sopenharmony_ci break; 155162306a36Sopenharmony_ci case EM_MIPS: 155262306a36Sopenharmony_ci taddr = addend_mips_rel(loc, r_type); 155362306a36Sopenharmony_ci break; 155462306a36Sopenharmony_ci default: 155562306a36Sopenharmony_ci fatal("Please add code to calculate addend for this architecture\n"); 155662306a36Sopenharmony_ci } 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci check_section_mismatch(mod, elf, tsym, 155962306a36Sopenharmony_ci fsecndx, fromsec, r_offset, taddr); 156062306a36Sopenharmony_ci } 156162306a36Sopenharmony_ci} 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci/** 156462306a36Sopenharmony_ci * A module includes a number of sections that are discarded 156562306a36Sopenharmony_ci * either when loaded or when used as built-in. 156662306a36Sopenharmony_ci * For loaded modules all functions marked __init and all data 156762306a36Sopenharmony_ci * marked __initdata will be discarded when the module has been initialized. 156862306a36Sopenharmony_ci * Likewise for modules used built-in the sections marked __exit 156962306a36Sopenharmony_ci * are discarded because __exit marked function are supposed to be called 157062306a36Sopenharmony_ci * only when a module is unloaded which never happens for built-in modules. 157162306a36Sopenharmony_ci * The check_sec_ref() function traverses all relocation records 157262306a36Sopenharmony_ci * to find all references to a section that reference a section that will 157362306a36Sopenharmony_ci * be discarded and warns about it. 157462306a36Sopenharmony_ci **/ 157562306a36Sopenharmony_cistatic void check_sec_ref(struct module *mod, struct elf_info *elf) 157662306a36Sopenharmony_ci{ 157762306a36Sopenharmony_ci int i; 157862306a36Sopenharmony_ci Elf_Shdr *sechdrs = elf->sechdrs; 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci /* Walk through all sections */ 158162306a36Sopenharmony_ci for (i = 0; i < elf->num_sections; i++) { 158262306a36Sopenharmony_ci check_section(mod->name, elf, &elf->sechdrs[i]); 158362306a36Sopenharmony_ci /* We want to process only relocation sections and not .init */ 158462306a36Sopenharmony_ci if (sechdrs[i].sh_type == SHT_RELA) 158562306a36Sopenharmony_ci section_rela(mod, elf, &elf->sechdrs[i]); 158662306a36Sopenharmony_ci else if (sechdrs[i].sh_type == SHT_REL) 158762306a36Sopenharmony_ci section_rel(mod, elf, &elf->sechdrs[i]); 158862306a36Sopenharmony_ci } 158962306a36Sopenharmony_ci} 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_cistatic char *remove_dot(char *s) 159262306a36Sopenharmony_ci{ 159362306a36Sopenharmony_ci size_t n = strcspn(s, "."); 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci if (n && s[n]) { 159662306a36Sopenharmony_ci size_t m = strspn(s + n + 1, "0123456789"); 159762306a36Sopenharmony_ci if (m && (s[n + m + 1] == '.' || s[n + m + 1] == 0)) 159862306a36Sopenharmony_ci s[n] = 0; 159962306a36Sopenharmony_ci } 160062306a36Sopenharmony_ci return s; 160162306a36Sopenharmony_ci} 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci/* 160462306a36Sopenharmony_ci * The CRCs are recorded in .*.cmd files in the form of: 160562306a36Sopenharmony_ci * #SYMVER <name> <crc> 160662306a36Sopenharmony_ci */ 160762306a36Sopenharmony_cistatic void extract_crcs_for_object(const char *object, struct module *mod) 160862306a36Sopenharmony_ci{ 160962306a36Sopenharmony_ci char cmd_file[PATH_MAX]; 161062306a36Sopenharmony_ci char *buf, *p; 161162306a36Sopenharmony_ci const char *base; 161262306a36Sopenharmony_ci int dirlen, ret; 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci base = strrchr(object, '/'); 161562306a36Sopenharmony_ci if (base) { 161662306a36Sopenharmony_ci base++; 161762306a36Sopenharmony_ci dirlen = base - object; 161862306a36Sopenharmony_ci } else { 161962306a36Sopenharmony_ci dirlen = 0; 162062306a36Sopenharmony_ci base = object; 162162306a36Sopenharmony_ci } 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci ret = snprintf(cmd_file, sizeof(cmd_file), "%.*s.%s.cmd", 162462306a36Sopenharmony_ci dirlen, object, base); 162562306a36Sopenharmony_ci if (ret >= sizeof(cmd_file)) { 162662306a36Sopenharmony_ci error("%s: too long path was truncated\n", cmd_file); 162762306a36Sopenharmony_ci return; 162862306a36Sopenharmony_ci } 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci buf = read_text_file(cmd_file); 163162306a36Sopenharmony_ci p = buf; 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci while ((p = strstr(p, "\n#SYMVER "))) { 163462306a36Sopenharmony_ci char *name; 163562306a36Sopenharmony_ci size_t namelen; 163662306a36Sopenharmony_ci unsigned int crc; 163762306a36Sopenharmony_ci struct symbol *sym; 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci name = p + strlen("\n#SYMVER "); 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci p = strchr(name, ' '); 164262306a36Sopenharmony_ci if (!p) 164362306a36Sopenharmony_ci break; 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci namelen = p - name; 164662306a36Sopenharmony_ci p++; 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci if (!isdigit(*p)) 164962306a36Sopenharmony_ci continue; /* skip this line */ 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci crc = strtoul(p, &p, 0); 165262306a36Sopenharmony_ci if (*p != '\n') 165362306a36Sopenharmony_ci continue; /* skip this line */ 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci name[namelen] = '\0'; 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci /* 165862306a36Sopenharmony_ci * sym_find_with_module() may return NULL here. 165962306a36Sopenharmony_ci * It typically occurs when CONFIG_TRIM_UNUSED_KSYMS=y. 166062306a36Sopenharmony_ci * Since commit e1327a127703, genksyms calculates CRCs of all 166162306a36Sopenharmony_ci * symbols, including trimmed ones. Ignore orphan CRCs. 166262306a36Sopenharmony_ci */ 166362306a36Sopenharmony_ci sym = sym_find_with_module(name, mod); 166462306a36Sopenharmony_ci if (sym) 166562306a36Sopenharmony_ci sym_set_crc(sym, crc); 166662306a36Sopenharmony_ci } 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci free(buf); 166962306a36Sopenharmony_ci} 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci/* 167262306a36Sopenharmony_ci * The symbol versions (CRC) are recorded in the .*.cmd files. 167362306a36Sopenharmony_ci * Parse them to retrieve CRCs for the current module. 167462306a36Sopenharmony_ci */ 167562306a36Sopenharmony_cistatic void mod_set_crcs(struct module *mod) 167662306a36Sopenharmony_ci{ 167762306a36Sopenharmony_ci char objlist[PATH_MAX]; 167862306a36Sopenharmony_ci char *buf, *p, *obj; 167962306a36Sopenharmony_ci int ret; 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci if (mod->is_vmlinux) { 168262306a36Sopenharmony_ci strcpy(objlist, ".vmlinux.objs"); 168362306a36Sopenharmony_ci } else { 168462306a36Sopenharmony_ci /* objects for a module are listed in the *.mod file. */ 168562306a36Sopenharmony_ci ret = snprintf(objlist, sizeof(objlist), "%s.mod", mod->name); 168662306a36Sopenharmony_ci if (ret >= sizeof(objlist)) { 168762306a36Sopenharmony_ci error("%s: too long path was truncated\n", objlist); 168862306a36Sopenharmony_ci return; 168962306a36Sopenharmony_ci } 169062306a36Sopenharmony_ci } 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci buf = read_text_file(objlist); 169362306a36Sopenharmony_ci p = buf; 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci while ((obj = strsep(&p, "\n")) && obj[0]) 169662306a36Sopenharmony_ci extract_crcs_for_object(obj, mod); 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci free(buf); 169962306a36Sopenharmony_ci} 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_cistatic void read_symbols(const char *modname) 170262306a36Sopenharmony_ci{ 170362306a36Sopenharmony_ci const char *symname; 170462306a36Sopenharmony_ci char *version; 170562306a36Sopenharmony_ci char *license; 170662306a36Sopenharmony_ci char *namespace; 170762306a36Sopenharmony_ci struct module *mod; 170862306a36Sopenharmony_ci struct elf_info info = { }; 170962306a36Sopenharmony_ci Elf_Sym *sym; 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci if (!parse_elf(&info, modname)) 171262306a36Sopenharmony_ci return; 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci if (!strends(modname, ".o")) { 171562306a36Sopenharmony_ci error("%s: filename must be suffixed with .o\n", modname); 171662306a36Sopenharmony_ci return; 171762306a36Sopenharmony_ci } 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci /* strip trailing .o */ 172062306a36Sopenharmony_ci mod = new_module(modname, strlen(modname) - strlen(".o")); 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci if (!mod->is_vmlinux) { 172362306a36Sopenharmony_ci license = get_modinfo(&info, "license"); 172462306a36Sopenharmony_ci if (!license) 172562306a36Sopenharmony_ci error("missing MODULE_LICENSE() in %s\n", modname); 172662306a36Sopenharmony_ci while (license) { 172762306a36Sopenharmony_ci if (!license_is_gpl_compatible(license)) { 172862306a36Sopenharmony_ci mod->is_gpl_compatible = false; 172962306a36Sopenharmony_ci break; 173062306a36Sopenharmony_ci } 173162306a36Sopenharmony_ci license = get_next_modinfo(&info, "license", license); 173262306a36Sopenharmony_ci } 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ci namespace = get_modinfo(&info, "import_ns"); 173562306a36Sopenharmony_ci while (namespace) { 173662306a36Sopenharmony_ci add_namespace(&mod->imported_namespaces, namespace); 173762306a36Sopenharmony_ci namespace = get_next_modinfo(&info, "import_ns", 173862306a36Sopenharmony_ci namespace); 173962306a36Sopenharmony_ci } 174062306a36Sopenharmony_ci } 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci if (extra_warn && !get_modinfo(&info, "description")) 174362306a36Sopenharmony_ci warn("missing MODULE_DESCRIPTION() in %s\n", modname); 174462306a36Sopenharmony_ci for (sym = info.symtab_start; sym < info.symtab_stop; sym++) { 174562306a36Sopenharmony_ci symname = remove_dot(info.strtab + sym->st_name); 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci handle_symbol(mod, &info, sym, symname); 174862306a36Sopenharmony_ci handle_moddevtable(mod, &info, sym, symname); 174962306a36Sopenharmony_ci } 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_ci check_sec_ref(mod, &info); 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci if (!mod->is_vmlinux) { 175462306a36Sopenharmony_ci version = get_modinfo(&info, "version"); 175562306a36Sopenharmony_ci if (version || all_versions) 175662306a36Sopenharmony_ci get_src_version(mod->name, mod->srcversion, 175762306a36Sopenharmony_ci sizeof(mod->srcversion) - 1); 175862306a36Sopenharmony_ci } 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci parse_elf_finish(&info); 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci if (modversions) { 176362306a36Sopenharmony_ci /* 176462306a36Sopenharmony_ci * Our trick to get versioning for module struct etc. - it's 176562306a36Sopenharmony_ci * never passed as an argument to an exported function, so 176662306a36Sopenharmony_ci * the automatic versioning doesn't pick it up, but it's really 176762306a36Sopenharmony_ci * important anyhow. 176862306a36Sopenharmony_ci */ 176962306a36Sopenharmony_ci sym_add_unresolved("module_layout", mod, false); 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci mod_set_crcs(mod); 177262306a36Sopenharmony_ci } 177362306a36Sopenharmony_ci} 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_cistatic void read_symbols_from_files(const char *filename) 177662306a36Sopenharmony_ci{ 177762306a36Sopenharmony_ci FILE *in = stdin; 177862306a36Sopenharmony_ci char fname[PATH_MAX]; 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci in = fopen(filename, "r"); 178162306a36Sopenharmony_ci if (!in) 178262306a36Sopenharmony_ci fatal("Can't open filenames file %s: %m", filename); 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci while (fgets(fname, PATH_MAX, in) != NULL) { 178562306a36Sopenharmony_ci if (strends(fname, "\n")) 178662306a36Sopenharmony_ci fname[strlen(fname)-1] = '\0'; 178762306a36Sopenharmony_ci read_symbols(fname); 178862306a36Sopenharmony_ci } 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci fclose(in); 179162306a36Sopenharmony_ci} 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci#define SZ 500 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci/* We first write the generated file into memory using the 179662306a36Sopenharmony_ci * following helper, then compare to the file on disk and 179762306a36Sopenharmony_ci * only update the later if anything changed */ 179862306a36Sopenharmony_ci 179962306a36Sopenharmony_civoid __attribute__((format(printf, 2, 3))) buf_printf(struct buffer *buf, 180062306a36Sopenharmony_ci const char *fmt, ...) 180162306a36Sopenharmony_ci{ 180262306a36Sopenharmony_ci char tmp[SZ]; 180362306a36Sopenharmony_ci int len; 180462306a36Sopenharmony_ci va_list ap; 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci va_start(ap, fmt); 180762306a36Sopenharmony_ci len = vsnprintf(tmp, SZ, fmt, ap); 180862306a36Sopenharmony_ci buf_write(buf, tmp, len); 180962306a36Sopenharmony_ci va_end(ap); 181062306a36Sopenharmony_ci} 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_civoid buf_write(struct buffer *buf, const char *s, int len) 181362306a36Sopenharmony_ci{ 181462306a36Sopenharmony_ci if (buf->size - buf->pos < len) { 181562306a36Sopenharmony_ci buf->size += len + SZ; 181662306a36Sopenharmony_ci buf->p = NOFAIL(realloc(buf->p, buf->size)); 181762306a36Sopenharmony_ci } 181862306a36Sopenharmony_ci strncpy(buf->p + buf->pos, s, len); 181962306a36Sopenharmony_ci buf->pos += len; 182062306a36Sopenharmony_ci} 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_cistatic void check_exports(struct module *mod) 182362306a36Sopenharmony_ci{ 182462306a36Sopenharmony_ci struct symbol *s, *exp; 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci list_for_each_entry(s, &mod->unresolved_symbols, list) { 182762306a36Sopenharmony_ci const char *basename; 182862306a36Sopenharmony_ci exp = find_symbol(s->name); 182962306a36Sopenharmony_ci if (!exp) { 183062306a36Sopenharmony_ci if (!s->weak && nr_unresolved++ < MAX_UNRESOLVED_REPORTS) 183162306a36Sopenharmony_ci modpost_log(warn_unresolved ? LOG_WARN : LOG_ERROR, 183262306a36Sopenharmony_ci "\"%s\" [%s.ko] undefined!\n", 183362306a36Sopenharmony_ci s->name, mod->name); 183462306a36Sopenharmony_ci continue; 183562306a36Sopenharmony_ci } 183662306a36Sopenharmony_ci if (exp->module == mod) { 183762306a36Sopenharmony_ci error("\"%s\" [%s.ko] was exported without definition\n", 183862306a36Sopenharmony_ci s->name, mod->name); 183962306a36Sopenharmony_ci continue; 184062306a36Sopenharmony_ci } 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_ci exp->used = true; 184362306a36Sopenharmony_ci s->module = exp->module; 184462306a36Sopenharmony_ci s->crc_valid = exp->crc_valid; 184562306a36Sopenharmony_ci s->crc = exp->crc; 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci basename = strrchr(mod->name, '/'); 184862306a36Sopenharmony_ci if (basename) 184962306a36Sopenharmony_ci basename++; 185062306a36Sopenharmony_ci else 185162306a36Sopenharmony_ci basename = mod->name; 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_ci if (!contains_namespace(&mod->imported_namespaces, exp->namespace)) { 185462306a36Sopenharmony_ci modpost_log(allow_missing_ns_imports ? LOG_WARN : LOG_ERROR, 185562306a36Sopenharmony_ci "module %s uses symbol %s from namespace %s, but does not import it.\n", 185662306a36Sopenharmony_ci basename, exp->name, exp->namespace); 185762306a36Sopenharmony_ci add_namespace(&mod->missing_namespaces, exp->namespace); 185862306a36Sopenharmony_ci } 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci if (!mod->is_gpl_compatible && exp->is_gpl_only) 186162306a36Sopenharmony_ci error("GPL-incompatible module %s.ko uses GPL-only symbol '%s'\n", 186262306a36Sopenharmony_ci basename, exp->name); 186362306a36Sopenharmony_ci } 186462306a36Sopenharmony_ci} 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_cistatic void handle_white_list_exports(const char *white_list) 186762306a36Sopenharmony_ci{ 186862306a36Sopenharmony_ci char *buf, *p, *name; 186962306a36Sopenharmony_ci 187062306a36Sopenharmony_ci buf = read_text_file(white_list); 187162306a36Sopenharmony_ci p = buf; 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci while ((name = strsep(&p, "\n"))) { 187462306a36Sopenharmony_ci struct symbol *sym = find_symbol(name); 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci if (sym) 187762306a36Sopenharmony_ci sym->used = true; 187862306a36Sopenharmony_ci } 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci free(buf); 188162306a36Sopenharmony_ci} 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_cistatic void check_modname_len(struct module *mod) 188462306a36Sopenharmony_ci{ 188562306a36Sopenharmony_ci const char *mod_name; 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_ci mod_name = strrchr(mod->name, '/'); 188862306a36Sopenharmony_ci if (mod_name == NULL) 188962306a36Sopenharmony_ci mod_name = mod->name; 189062306a36Sopenharmony_ci else 189162306a36Sopenharmony_ci mod_name++; 189262306a36Sopenharmony_ci if (strlen(mod_name) >= MODULE_NAME_LEN) 189362306a36Sopenharmony_ci error("module name is too long [%s.ko]\n", mod->name); 189462306a36Sopenharmony_ci} 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_ci/** 189762306a36Sopenharmony_ci * Header for the generated file 189862306a36Sopenharmony_ci **/ 189962306a36Sopenharmony_cistatic void add_header(struct buffer *b, struct module *mod) 190062306a36Sopenharmony_ci{ 190162306a36Sopenharmony_ci buf_printf(b, "#include <linux/module.h>\n"); 190262306a36Sopenharmony_ci /* 190362306a36Sopenharmony_ci * Include build-salt.h after module.h in order to 190462306a36Sopenharmony_ci * inherit the definitions. 190562306a36Sopenharmony_ci */ 190662306a36Sopenharmony_ci buf_printf(b, "#define INCLUDE_VERMAGIC\n"); 190762306a36Sopenharmony_ci buf_printf(b, "#include <linux/build-salt.h>\n"); 190862306a36Sopenharmony_ci buf_printf(b, "#include <linux/elfnote-lto.h>\n"); 190962306a36Sopenharmony_ci buf_printf(b, "#include <linux/export-internal.h>\n"); 191062306a36Sopenharmony_ci buf_printf(b, "#include <linux/vermagic.h>\n"); 191162306a36Sopenharmony_ci buf_printf(b, "#include <linux/compiler.h>\n"); 191262306a36Sopenharmony_ci buf_printf(b, "\n"); 191362306a36Sopenharmony_ci buf_printf(b, "#ifdef CONFIG_UNWINDER_ORC\n"); 191462306a36Sopenharmony_ci buf_printf(b, "#include <asm/orc_header.h>\n"); 191562306a36Sopenharmony_ci buf_printf(b, "ORC_HEADER;\n"); 191662306a36Sopenharmony_ci buf_printf(b, "#endif\n"); 191762306a36Sopenharmony_ci buf_printf(b, "\n"); 191862306a36Sopenharmony_ci buf_printf(b, "BUILD_SALT;\n"); 191962306a36Sopenharmony_ci buf_printf(b, "BUILD_LTO_INFO;\n"); 192062306a36Sopenharmony_ci buf_printf(b, "\n"); 192162306a36Sopenharmony_ci buf_printf(b, "MODULE_INFO(vermagic, VERMAGIC_STRING);\n"); 192262306a36Sopenharmony_ci buf_printf(b, "MODULE_INFO(name, KBUILD_MODNAME);\n"); 192362306a36Sopenharmony_ci buf_printf(b, "\n"); 192462306a36Sopenharmony_ci buf_printf(b, "__visible struct module __this_module\n"); 192562306a36Sopenharmony_ci buf_printf(b, "__section(\".gnu.linkonce.this_module\") = {\n"); 192662306a36Sopenharmony_ci buf_printf(b, "\t.name = KBUILD_MODNAME,\n"); 192762306a36Sopenharmony_ci if (mod->has_init) 192862306a36Sopenharmony_ci buf_printf(b, "\t.init = init_module,\n"); 192962306a36Sopenharmony_ci if (mod->has_cleanup) 193062306a36Sopenharmony_ci buf_printf(b, "#ifdef CONFIG_MODULE_UNLOAD\n" 193162306a36Sopenharmony_ci "\t.exit = cleanup_module,\n" 193262306a36Sopenharmony_ci "#endif\n"); 193362306a36Sopenharmony_ci buf_printf(b, "\t.arch = MODULE_ARCH_INIT,\n"); 193462306a36Sopenharmony_ci buf_printf(b, "};\n"); 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_ci if (!external_module) 193762306a36Sopenharmony_ci buf_printf(b, "\nMODULE_INFO(intree, \"Y\");\n"); 193862306a36Sopenharmony_ci 193962306a36Sopenharmony_ci buf_printf(b, 194062306a36Sopenharmony_ci "\n" 194162306a36Sopenharmony_ci "#ifdef CONFIG_RETPOLINE\n" 194262306a36Sopenharmony_ci "MODULE_INFO(retpoline, \"Y\");\n" 194362306a36Sopenharmony_ci "#endif\n"); 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_ci if (strstarts(mod->name, "drivers/staging")) 194662306a36Sopenharmony_ci buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n"); 194762306a36Sopenharmony_ci 194862306a36Sopenharmony_ci if (strstarts(mod->name, "tools/testing")) 194962306a36Sopenharmony_ci buf_printf(b, "\nMODULE_INFO(test, \"Y\");\n"); 195062306a36Sopenharmony_ci} 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_cistatic void add_exported_symbols(struct buffer *buf, struct module *mod) 195362306a36Sopenharmony_ci{ 195462306a36Sopenharmony_ci struct symbol *sym; 195562306a36Sopenharmony_ci 195662306a36Sopenharmony_ci /* generate struct for exported symbols */ 195762306a36Sopenharmony_ci buf_printf(buf, "\n"); 195862306a36Sopenharmony_ci list_for_each_entry(sym, &mod->exported_symbols, list) { 195962306a36Sopenharmony_ci if (trim_unused_exports && !sym->used) 196062306a36Sopenharmony_ci continue; 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci buf_printf(buf, "KSYMTAB_%s(%s, \"%s\", \"%s\");\n", 196362306a36Sopenharmony_ci sym->is_func ? "FUNC" : "DATA", sym->name, 196462306a36Sopenharmony_ci sym->is_gpl_only ? "_gpl" : "", sym->namespace); 196562306a36Sopenharmony_ci } 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci if (!modversions) 196862306a36Sopenharmony_ci return; 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_ci /* record CRCs for exported symbols */ 197162306a36Sopenharmony_ci buf_printf(buf, "\n"); 197262306a36Sopenharmony_ci list_for_each_entry(sym, &mod->exported_symbols, list) { 197362306a36Sopenharmony_ci if (trim_unused_exports && !sym->used) 197462306a36Sopenharmony_ci continue; 197562306a36Sopenharmony_ci 197662306a36Sopenharmony_ci if (!sym->crc_valid) 197762306a36Sopenharmony_ci warn("EXPORT symbol \"%s\" [%s%s] version generation failed, symbol will not be versioned.\n" 197862306a36Sopenharmony_ci "Is \"%s\" prototyped in <asm/asm-prototypes.h>?\n", 197962306a36Sopenharmony_ci sym->name, mod->name, mod->is_vmlinux ? "" : ".ko", 198062306a36Sopenharmony_ci sym->name); 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_ci buf_printf(buf, "SYMBOL_CRC(%s, 0x%08x, \"%s\");\n", 198362306a36Sopenharmony_ci sym->name, sym->crc, sym->is_gpl_only ? "_gpl" : ""); 198462306a36Sopenharmony_ci } 198562306a36Sopenharmony_ci} 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci/** 198862306a36Sopenharmony_ci * Record CRCs for unresolved symbols 198962306a36Sopenharmony_ci **/ 199062306a36Sopenharmony_cistatic void add_versions(struct buffer *b, struct module *mod) 199162306a36Sopenharmony_ci{ 199262306a36Sopenharmony_ci struct symbol *s; 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci if (!modversions) 199562306a36Sopenharmony_ci return; 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_ci buf_printf(b, "\n"); 199862306a36Sopenharmony_ci buf_printf(b, "static const struct modversion_info ____versions[]\n"); 199962306a36Sopenharmony_ci buf_printf(b, "__used __section(\"__versions\") = {\n"); 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_ci list_for_each_entry(s, &mod->unresolved_symbols, list) { 200262306a36Sopenharmony_ci if (!s->module) 200362306a36Sopenharmony_ci continue; 200462306a36Sopenharmony_ci if (!s->crc_valid) { 200562306a36Sopenharmony_ci warn("\"%s\" [%s.ko] has no CRC!\n", 200662306a36Sopenharmony_ci s->name, mod->name); 200762306a36Sopenharmony_ci continue; 200862306a36Sopenharmony_ci } 200962306a36Sopenharmony_ci if (strlen(s->name) >= MODULE_NAME_LEN) { 201062306a36Sopenharmony_ci error("too long symbol \"%s\" [%s.ko]\n", 201162306a36Sopenharmony_ci s->name, mod->name); 201262306a36Sopenharmony_ci break; 201362306a36Sopenharmony_ci } 201462306a36Sopenharmony_ci buf_printf(b, "\t{ %#8x, \"%s\" },\n", 201562306a36Sopenharmony_ci s->crc, s->name); 201662306a36Sopenharmony_ci } 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_ci buf_printf(b, "};\n"); 201962306a36Sopenharmony_ci} 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_cistatic void add_depends(struct buffer *b, struct module *mod) 202262306a36Sopenharmony_ci{ 202362306a36Sopenharmony_ci struct symbol *s; 202462306a36Sopenharmony_ci int first = 1; 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_ci /* Clear ->seen flag of modules that own symbols needed by this. */ 202762306a36Sopenharmony_ci list_for_each_entry(s, &mod->unresolved_symbols, list) { 202862306a36Sopenharmony_ci if (s->module) 202962306a36Sopenharmony_ci s->module->seen = s->module->is_vmlinux; 203062306a36Sopenharmony_ci } 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_ci buf_printf(b, "\n"); 203362306a36Sopenharmony_ci buf_printf(b, "MODULE_INFO(depends, \""); 203462306a36Sopenharmony_ci list_for_each_entry(s, &mod->unresolved_symbols, list) { 203562306a36Sopenharmony_ci const char *p; 203662306a36Sopenharmony_ci if (!s->module) 203762306a36Sopenharmony_ci continue; 203862306a36Sopenharmony_ci 203962306a36Sopenharmony_ci if (s->module->seen) 204062306a36Sopenharmony_ci continue; 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_ci s->module->seen = true; 204362306a36Sopenharmony_ci p = strrchr(s->module->name, '/'); 204462306a36Sopenharmony_ci if (p) 204562306a36Sopenharmony_ci p++; 204662306a36Sopenharmony_ci else 204762306a36Sopenharmony_ci p = s->module->name; 204862306a36Sopenharmony_ci buf_printf(b, "%s%s", first ? "" : ",", p); 204962306a36Sopenharmony_ci first = 0; 205062306a36Sopenharmony_ci } 205162306a36Sopenharmony_ci buf_printf(b, "\");\n"); 205262306a36Sopenharmony_ci} 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_cistatic void add_srcversion(struct buffer *b, struct module *mod) 205562306a36Sopenharmony_ci{ 205662306a36Sopenharmony_ci if (mod->srcversion[0]) { 205762306a36Sopenharmony_ci buf_printf(b, "\n"); 205862306a36Sopenharmony_ci buf_printf(b, "MODULE_INFO(srcversion, \"%s\");\n", 205962306a36Sopenharmony_ci mod->srcversion); 206062306a36Sopenharmony_ci } 206162306a36Sopenharmony_ci} 206262306a36Sopenharmony_ci 206362306a36Sopenharmony_cistatic void write_buf(struct buffer *b, const char *fname) 206462306a36Sopenharmony_ci{ 206562306a36Sopenharmony_ci FILE *file; 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_ci if (error_occurred) 206862306a36Sopenharmony_ci return; 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_ci file = fopen(fname, "w"); 207162306a36Sopenharmony_ci if (!file) { 207262306a36Sopenharmony_ci perror(fname); 207362306a36Sopenharmony_ci exit(1); 207462306a36Sopenharmony_ci } 207562306a36Sopenharmony_ci if (fwrite(b->p, 1, b->pos, file) != b->pos) { 207662306a36Sopenharmony_ci perror(fname); 207762306a36Sopenharmony_ci exit(1); 207862306a36Sopenharmony_ci } 207962306a36Sopenharmony_ci if (fclose(file) != 0) { 208062306a36Sopenharmony_ci perror(fname); 208162306a36Sopenharmony_ci exit(1); 208262306a36Sopenharmony_ci } 208362306a36Sopenharmony_ci} 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_cistatic void write_if_changed(struct buffer *b, const char *fname) 208662306a36Sopenharmony_ci{ 208762306a36Sopenharmony_ci char *tmp; 208862306a36Sopenharmony_ci FILE *file; 208962306a36Sopenharmony_ci struct stat st; 209062306a36Sopenharmony_ci 209162306a36Sopenharmony_ci file = fopen(fname, "r"); 209262306a36Sopenharmony_ci if (!file) 209362306a36Sopenharmony_ci goto write; 209462306a36Sopenharmony_ci 209562306a36Sopenharmony_ci if (fstat(fileno(file), &st) < 0) 209662306a36Sopenharmony_ci goto close_write; 209762306a36Sopenharmony_ci 209862306a36Sopenharmony_ci if (st.st_size != b->pos) 209962306a36Sopenharmony_ci goto close_write; 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci tmp = NOFAIL(malloc(b->pos)); 210262306a36Sopenharmony_ci if (fread(tmp, 1, b->pos, file) != b->pos) 210362306a36Sopenharmony_ci goto free_write; 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_ci if (memcmp(tmp, b->p, b->pos) != 0) 210662306a36Sopenharmony_ci goto free_write; 210762306a36Sopenharmony_ci 210862306a36Sopenharmony_ci free(tmp); 210962306a36Sopenharmony_ci fclose(file); 211062306a36Sopenharmony_ci return; 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_ci free_write: 211362306a36Sopenharmony_ci free(tmp); 211462306a36Sopenharmony_ci close_write: 211562306a36Sopenharmony_ci fclose(file); 211662306a36Sopenharmony_ci write: 211762306a36Sopenharmony_ci write_buf(b, fname); 211862306a36Sopenharmony_ci} 211962306a36Sopenharmony_ci 212062306a36Sopenharmony_cistatic void write_vmlinux_export_c_file(struct module *mod) 212162306a36Sopenharmony_ci{ 212262306a36Sopenharmony_ci struct buffer buf = { }; 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_ci buf_printf(&buf, 212562306a36Sopenharmony_ci "#include <linux/export-internal.h>\n"); 212662306a36Sopenharmony_ci 212762306a36Sopenharmony_ci add_exported_symbols(&buf, mod); 212862306a36Sopenharmony_ci write_if_changed(&buf, ".vmlinux.export.c"); 212962306a36Sopenharmony_ci free(buf.p); 213062306a36Sopenharmony_ci} 213162306a36Sopenharmony_ci 213262306a36Sopenharmony_ci/* do sanity checks, and generate *.mod.c file */ 213362306a36Sopenharmony_cistatic void write_mod_c_file(struct module *mod) 213462306a36Sopenharmony_ci{ 213562306a36Sopenharmony_ci struct buffer buf = { }; 213662306a36Sopenharmony_ci char fname[PATH_MAX]; 213762306a36Sopenharmony_ci int ret; 213862306a36Sopenharmony_ci 213962306a36Sopenharmony_ci add_header(&buf, mod); 214062306a36Sopenharmony_ci add_exported_symbols(&buf, mod); 214162306a36Sopenharmony_ci add_versions(&buf, mod); 214262306a36Sopenharmony_ci add_depends(&buf, mod); 214362306a36Sopenharmony_ci add_moddevtable(&buf, mod); 214462306a36Sopenharmony_ci add_srcversion(&buf, mod); 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci ret = snprintf(fname, sizeof(fname), "%s.mod.c", mod->name); 214762306a36Sopenharmony_ci if (ret >= sizeof(fname)) { 214862306a36Sopenharmony_ci error("%s: too long path was truncated\n", fname); 214962306a36Sopenharmony_ci goto free; 215062306a36Sopenharmony_ci } 215162306a36Sopenharmony_ci 215262306a36Sopenharmony_ci write_if_changed(&buf, fname); 215362306a36Sopenharmony_ci 215462306a36Sopenharmony_cifree: 215562306a36Sopenharmony_ci free(buf.p); 215662306a36Sopenharmony_ci} 215762306a36Sopenharmony_ci 215862306a36Sopenharmony_ci/* parse Module.symvers file. line format: 215962306a36Sopenharmony_ci * 0x12345678<tab>symbol<tab>module<tab>export<tab>namespace 216062306a36Sopenharmony_ci **/ 216162306a36Sopenharmony_cistatic void read_dump(const char *fname) 216262306a36Sopenharmony_ci{ 216362306a36Sopenharmony_ci char *buf, *pos, *line; 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_ci buf = read_text_file(fname); 216662306a36Sopenharmony_ci if (!buf) 216762306a36Sopenharmony_ci /* No symbol versions, silently ignore */ 216862306a36Sopenharmony_ci return; 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_ci pos = buf; 217162306a36Sopenharmony_ci 217262306a36Sopenharmony_ci while ((line = get_line(&pos))) { 217362306a36Sopenharmony_ci char *symname, *namespace, *modname, *d, *export; 217462306a36Sopenharmony_ci unsigned int crc; 217562306a36Sopenharmony_ci struct module *mod; 217662306a36Sopenharmony_ci struct symbol *s; 217762306a36Sopenharmony_ci bool gpl_only; 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_ci if (!(symname = strchr(line, '\t'))) 218062306a36Sopenharmony_ci goto fail; 218162306a36Sopenharmony_ci *symname++ = '\0'; 218262306a36Sopenharmony_ci if (!(modname = strchr(symname, '\t'))) 218362306a36Sopenharmony_ci goto fail; 218462306a36Sopenharmony_ci *modname++ = '\0'; 218562306a36Sopenharmony_ci if (!(export = strchr(modname, '\t'))) 218662306a36Sopenharmony_ci goto fail; 218762306a36Sopenharmony_ci *export++ = '\0'; 218862306a36Sopenharmony_ci if (!(namespace = strchr(export, '\t'))) 218962306a36Sopenharmony_ci goto fail; 219062306a36Sopenharmony_ci *namespace++ = '\0'; 219162306a36Sopenharmony_ci 219262306a36Sopenharmony_ci crc = strtoul(line, &d, 16); 219362306a36Sopenharmony_ci if (*symname == '\0' || *modname == '\0' || *d != '\0') 219462306a36Sopenharmony_ci goto fail; 219562306a36Sopenharmony_ci 219662306a36Sopenharmony_ci if (!strcmp(export, "EXPORT_SYMBOL_GPL")) { 219762306a36Sopenharmony_ci gpl_only = true; 219862306a36Sopenharmony_ci } else if (!strcmp(export, "EXPORT_SYMBOL")) { 219962306a36Sopenharmony_ci gpl_only = false; 220062306a36Sopenharmony_ci } else { 220162306a36Sopenharmony_ci error("%s: unknown license %s. skip", symname, export); 220262306a36Sopenharmony_ci continue; 220362306a36Sopenharmony_ci } 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_ci mod = find_module(modname); 220662306a36Sopenharmony_ci if (!mod) { 220762306a36Sopenharmony_ci mod = new_module(modname, strlen(modname)); 220862306a36Sopenharmony_ci mod->from_dump = true; 220962306a36Sopenharmony_ci } 221062306a36Sopenharmony_ci s = sym_add_exported(symname, mod, gpl_only, namespace); 221162306a36Sopenharmony_ci sym_set_crc(s, crc); 221262306a36Sopenharmony_ci } 221362306a36Sopenharmony_ci free(buf); 221462306a36Sopenharmony_ci return; 221562306a36Sopenharmony_cifail: 221662306a36Sopenharmony_ci free(buf); 221762306a36Sopenharmony_ci fatal("parse error in symbol dump file\n"); 221862306a36Sopenharmony_ci} 221962306a36Sopenharmony_ci 222062306a36Sopenharmony_cistatic void write_dump(const char *fname) 222162306a36Sopenharmony_ci{ 222262306a36Sopenharmony_ci struct buffer buf = { }; 222362306a36Sopenharmony_ci struct module *mod; 222462306a36Sopenharmony_ci struct symbol *sym; 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_ci list_for_each_entry(mod, &modules, list) { 222762306a36Sopenharmony_ci if (mod->from_dump) 222862306a36Sopenharmony_ci continue; 222962306a36Sopenharmony_ci list_for_each_entry(sym, &mod->exported_symbols, list) { 223062306a36Sopenharmony_ci if (trim_unused_exports && !sym->used) 223162306a36Sopenharmony_ci continue; 223262306a36Sopenharmony_ci 223362306a36Sopenharmony_ci buf_printf(&buf, "0x%08x\t%s\t%s\tEXPORT_SYMBOL%s\t%s\n", 223462306a36Sopenharmony_ci sym->crc, sym->name, mod->name, 223562306a36Sopenharmony_ci sym->is_gpl_only ? "_GPL" : "", 223662306a36Sopenharmony_ci sym->namespace); 223762306a36Sopenharmony_ci } 223862306a36Sopenharmony_ci } 223962306a36Sopenharmony_ci write_buf(&buf, fname); 224062306a36Sopenharmony_ci free(buf.p); 224162306a36Sopenharmony_ci} 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_cistatic void write_namespace_deps_files(const char *fname) 224462306a36Sopenharmony_ci{ 224562306a36Sopenharmony_ci struct module *mod; 224662306a36Sopenharmony_ci struct namespace_list *ns; 224762306a36Sopenharmony_ci struct buffer ns_deps_buf = {}; 224862306a36Sopenharmony_ci 224962306a36Sopenharmony_ci list_for_each_entry(mod, &modules, list) { 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_ci if (mod->from_dump || list_empty(&mod->missing_namespaces)) 225262306a36Sopenharmony_ci continue; 225362306a36Sopenharmony_ci 225462306a36Sopenharmony_ci buf_printf(&ns_deps_buf, "%s.ko:", mod->name); 225562306a36Sopenharmony_ci 225662306a36Sopenharmony_ci list_for_each_entry(ns, &mod->missing_namespaces, list) 225762306a36Sopenharmony_ci buf_printf(&ns_deps_buf, " %s", ns->namespace); 225862306a36Sopenharmony_ci 225962306a36Sopenharmony_ci buf_printf(&ns_deps_buf, "\n"); 226062306a36Sopenharmony_ci } 226162306a36Sopenharmony_ci 226262306a36Sopenharmony_ci write_if_changed(&ns_deps_buf, fname); 226362306a36Sopenharmony_ci free(ns_deps_buf.p); 226462306a36Sopenharmony_ci} 226562306a36Sopenharmony_ci 226662306a36Sopenharmony_cistruct dump_list { 226762306a36Sopenharmony_ci struct list_head list; 226862306a36Sopenharmony_ci const char *file; 226962306a36Sopenharmony_ci}; 227062306a36Sopenharmony_ci 227162306a36Sopenharmony_ciint main(int argc, char **argv) 227262306a36Sopenharmony_ci{ 227362306a36Sopenharmony_ci struct module *mod; 227462306a36Sopenharmony_ci char *missing_namespace_deps = NULL; 227562306a36Sopenharmony_ci char *unused_exports_white_list = NULL; 227662306a36Sopenharmony_ci char *dump_write = NULL, *files_source = NULL; 227762306a36Sopenharmony_ci int opt; 227862306a36Sopenharmony_ci LIST_HEAD(dump_lists); 227962306a36Sopenharmony_ci struct dump_list *dl, *dl2; 228062306a36Sopenharmony_ci 228162306a36Sopenharmony_ci while ((opt = getopt(argc, argv, "ei:MmnT:to:au:WwENd:")) != -1) { 228262306a36Sopenharmony_ci switch (opt) { 228362306a36Sopenharmony_ci case 'e': 228462306a36Sopenharmony_ci external_module = true; 228562306a36Sopenharmony_ci break; 228662306a36Sopenharmony_ci case 'i': 228762306a36Sopenharmony_ci dl = NOFAIL(malloc(sizeof(*dl))); 228862306a36Sopenharmony_ci dl->file = optarg; 228962306a36Sopenharmony_ci list_add_tail(&dl->list, &dump_lists); 229062306a36Sopenharmony_ci break; 229162306a36Sopenharmony_ci case 'M': 229262306a36Sopenharmony_ci module_enabled = true; 229362306a36Sopenharmony_ci break; 229462306a36Sopenharmony_ci case 'm': 229562306a36Sopenharmony_ci modversions = true; 229662306a36Sopenharmony_ci break; 229762306a36Sopenharmony_ci case 'n': 229862306a36Sopenharmony_ci ignore_missing_files = true; 229962306a36Sopenharmony_ci break; 230062306a36Sopenharmony_ci case 'o': 230162306a36Sopenharmony_ci dump_write = optarg; 230262306a36Sopenharmony_ci break; 230362306a36Sopenharmony_ci case 'a': 230462306a36Sopenharmony_ci all_versions = true; 230562306a36Sopenharmony_ci break; 230662306a36Sopenharmony_ci case 'T': 230762306a36Sopenharmony_ci files_source = optarg; 230862306a36Sopenharmony_ci break; 230962306a36Sopenharmony_ci case 't': 231062306a36Sopenharmony_ci trim_unused_exports = true; 231162306a36Sopenharmony_ci break; 231262306a36Sopenharmony_ci case 'u': 231362306a36Sopenharmony_ci unused_exports_white_list = optarg; 231462306a36Sopenharmony_ci break; 231562306a36Sopenharmony_ci case 'W': 231662306a36Sopenharmony_ci extra_warn = true; 231762306a36Sopenharmony_ci break; 231862306a36Sopenharmony_ci case 'w': 231962306a36Sopenharmony_ci warn_unresolved = true; 232062306a36Sopenharmony_ci break; 232162306a36Sopenharmony_ci case 'E': 232262306a36Sopenharmony_ci sec_mismatch_warn_only = false; 232362306a36Sopenharmony_ci break; 232462306a36Sopenharmony_ci case 'N': 232562306a36Sopenharmony_ci allow_missing_ns_imports = true; 232662306a36Sopenharmony_ci break; 232762306a36Sopenharmony_ci case 'd': 232862306a36Sopenharmony_ci missing_namespace_deps = optarg; 232962306a36Sopenharmony_ci break; 233062306a36Sopenharmony_ci default: 233162306a36Sopenharmony_ci exit(1); 233262306a36Sopenharmony_ci } 233362306a36Sopenharmony_ci } 233462306a36Sopenharmony_ci 233562306a36Sopenharmony_ci list_for_each_entry_safe(dl, dl2, &dump_lists, list) { 233662306a36Sopenharmony_ci read_dump(dl->file); 233762306a36Sopenharmony_ci list_del(&dl->list); 233862306a36Sopenharmony_ci free(dl); 233962306a36Sopenharmony_ci } 234062306a36Sopenharmony_ci 234162306a36Sopenharmony_ci while (optind < argc) 234262306a36Sopenharmony_ci read_symbols(argv[optind++]); 234362306a36Sopenharmony_ci 234462306a36Sopenharmony_ci if (files_source) 234562306a36Sopenharmony_ci read_symbols_from_files(files_source); 234662306a36Sopenharmony_ci 234762306a36Sopenharmony_ci list_for_each_entry(mod, &modules, list) { 234862306a36Sopenharmony_ci if (mod->from_dump || mod->is_vmlinux) 234962306a36Sopenharmony_ci continue; 235062306a36Sopenharmony_ci 235162306a36Sopenharmony_ci check_modname_len(mod); 235262306a36Sopenharmony_ci check_exports(mod); 235362306a36Sopenharmony_ci } 235462306a36Sopenharmony_ci 235562306a36Sopenharmony_ci if (unused_exports_white_list) 235662306a36Sopenharmony_ci handle_white_list_exports(unused_exports_white_list); 235762306a36Sopenharmony_ci 235862306a36Sopenharmony_ci list_for_each_entry(mod, &modules, list) { 235962306a36Sopenharmony_ci if (mod->from_dump) 236062306a36Sopenharmony_ci continue; 236162306a36Sopenharmony_ci 236262306a36Sopenharmony_ci if (mod->is_vmlinux) 236362306a36Sopenharmony_ci write_vmlinux_export_c_file(mod); 236462306a36Sopenharmony_ci else 236562306a36Sopenharmony_ci write_mod_c_file(mod); 236662306a36Sopenharmony_ci } 236762306a36Sopenharmony_ci 236862306a36Sopenharmony_ci if (missing_namespace_deps) 236962306a36Sopenharmony_ci write_namespace_deps_files(missing_namespace_deps); 237062306a36Sopenharmony_ci 237162306a36Sopenharmony_ci if (dump_write) 237262306a36Sopenharmony_ci write_dump(dump_write); 237362306a36Sopenharmony_ci if (sec_mismatch_count && !sec_mismatch_warn_only) 237462306a36Sopenharmony_ci error("Section mismatches detected.\n" 237562306a36Sopenharmony_ci "Set CONFIG_SECTION_MISMATCH_WARN_ONLY=y to allow them.\n"); 237662306a36Sopenharmony_ci 237762306a36Sopenharmony_ci if (nr_unresolved > MAX_UNRESOLVED_REPORTS) 237862306a36Sopenharmony_ci warn("suppressed %u unresolved symbol warnings because there were too many)\n", 237962306a36Sopenharmony_ci nr_unresolved - MAX_UNRESOLVED_REPORTS); 238062306a36Sopenharmony_ci 238162306a36Sopenharmony_ci return error_occurred ? 1 : 0; 238262306a36Sopenharmony_ci} 2383