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 = &sectioncheck[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