[PATCH dwarves] btf: Generate btf for functions in the .BTF_ids section

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



BTF is currently generated for functions that are in ftrace list
or extern.

A recent use case also needs BTF generated for functions included in
allowlist.  In particular, the kernel
commit e78aea8b2170 ("bpf: tcp: Put some tcp cong functions in allowlist for bpf-tcp-cc")
allows bpf program to directly call a few tcp cc kernel functions.  Those
functions are specified under an ELF section .BTF_ids.  The symbols
in this ELF section is like __BTF_ID__func__<kernel_func>__[digit]+.
For example, __BTF_ID__func__cubictcp_init__1.  Those kernel
functions are currently allowed only if CONFIG_DYNAMIC_FTRACE is
set to ensure they are in the ftrace list but this kconfig dependency
is unnecessary.

pahole can generate BTF for those kernel functions if it knows they
are in the allowlist.  This patch is to capture those symbols
in the .BTF_ids section and generate BTF for them.

Cc: Andrii Nakryiko <andrii@xxxxxxxxxx>
Signed-off-by: Martin KaFai Lau <kafai@xxxxxx>
---
 btf_encoder.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++---
 libbtf.c      |  10 ++++
 libbtf.h      |   2 +
 3 files changed, 142 insertions(+), 6 deletions(-)

diff --git a/btf_encoder.c b/btf_encoder.c
index 80e896961d4e..48c183915ddd 100644
--- a/btf_encoder.c
+++ b/btf_encoder.c
@@ -106,6 +106,121 @@ static int collect_function(struct btf_elf *btfe, GElf_Sym *sym,
 	return 0;
 }
 
+#define BTF_ID_FUNC_PREFIX "__BTF_ID__func__"
+#define BTF_ID_FUNC_PREFIX_LEN (sizeof(BTF_ID_FUNC_PREFIX) - 1)
+
+static char **listed_functions;
+static int listed_functions_alloc;
+static int listed_functions_cnt;
+
+static void delete_listed_functions(void)
+{
+	int i;
+
+	for (i = 0; i < listed_functions_cnt; i++)
+		free(listed_functions[i]);
+
+	free(listed_functions);
+	/* In case btf_encoder__encode() will be called multiple times */
+	listed_functions_alloc = 0;
+	listed_functions_cnt = 0;
+	listed_functions = NULL;
+}
+
+static int listed_function_cmp(const void *_a, const void *_b)
+{
+	const char *a = *(const char **)_a;
+	const char *b = *(const char **)_b;
+
+	return strcmp(a, b);
+}
+
+static bool is_listed_func(const char *name)
+{
+	return !!bsearch(&name, listed_functions, listed_functions_cnt,
+			 sizeof(*listed_functions), listed_function_cmp);
+}
+
+static int collect_listed_functions(struct btf_elf *btfe, GElf_Sym *sym,
+				    size_t sym_sec_idx)
+{
+	int len, digits = 0, underscores = 0;
+	const char *name;
+	char *func_name;
+
+	if (!btfe->btf_ids_shndx ||
+	    btfe->btf_ids_shndx != sym_sec_idx)
+		return 0;
+
+	/* The kernel function in the btf id list will have symbol like:
+	 * __BTF_ID__func__<kernel_func_name>__[digit]+
+	 */
+	name = elf_sym__name(sym, btfe->symtab);
+	if (strncmp(name, BTF_ID_FUNC_PREFIX, BTF_ID_FUNC_PREFIX_LEN))
+		return 0;
+
+	name += BTF_ID_FUNC_PREFIX_LEN;
+
+	/* name: <kernel_func_name>__[digit]+
+	 * Strip the ending __[digit]+
+	 */
+	for (len = strlen(name); len && underscores != 2; len--) {
+		char c = name[len - 1];
+
+		if (c == '_') {
+			if (!digits)
+				return 0;
+			underscores++;
+		} else if (isdigit(c)) {
+			if (underscores)
+				return 0;
+			digits++;
+		} else {
+			return 0;
+		}
+	}
+
+	if (!len)
+		return 0;
+
+	func_name = strndup(name, len);
+	if (!func_name) {
+		fprintf(stderr,
+			"Failed to alloc memory for listed function %s%s\n",
+			BTF_ID_FUNC_PREFIX, name);
+		return -1;
+	}
+
+	if (is_listed_func(func_name)) {
+		/* already captured */
+		free(func_name);
+		return 0;
+	}
+
+	/* grow listed_functions */
+	if (listed_functions_cnt == listed_functions_alloc) {
+		char **new;
+
+		listed_functions_alloc = max(100,
+					     listed_functions_alloc * 3 / 2);
+		new = realloc(listed_functions,
+			      listed_functions_alloc * sizeof(*listed_functions));
+		if (!new) {
+			fprintf(stderr,
+				"Failed to alloc memory for listed function %s%s\n",
+				BTF_ID_FUNC_PREFIX, name);
+			free(func_name);
+			return -1;
+		}
+		listed_functions = new;
+	}
+
+	listed_functions[listed_functions_cnt++] = func_name;
+	qsort(listed_functions, listed_functions_cnt,
+	      sizeof(*listed_functions), listed_function_cmp);
+	return 0;
+}
+
 static int addrs_cmp(const void *_a, const void *_b)
 {
 	const __u64 *a = _a;
@@ -294,14 +409,15 @@ static int setup_functions(struct btf_elf *btfe, struct funcs_layout *fl)
 		kmod = true;
 	}
 
-	if (!addrs) {
+	if (!addrs && !listed_functions_cnt) {
 		if (btf_elf__verbose)
-			printf("ftrace symbols not detected, falling back to DWARF data\n");
+			printf("ftrace symbols and btf listed functions are not detected, falling back to DWARF data\n");
 		delete_functions();
 		return 0;
 	}
 
-	qsort(addrs, count, sizeof(addrs[0]), addrs_cmp);
+	if (addrs)
+		qsort(addrs, count, sizeof(addrs[0]), addrs_cmp);
 	qsort(functions, functions_cnt, sizeof(functions[0]), functions_cmp);
 
 	/*
@@ -321,8 +437,12 @@ static int setup_functions(struct btf_elf *btfe, struct funcs_layout *fl)
 		if (kmod)
 			func->addr += func->sh_addr;
 
-		/* Make sure function is within ftrace addresses. */
-		if (is_ftrace_func(func, addrs, count)) {
+		/*
+		 * Make sure function is within ftrace addresses or
+		 * is a listed function in the .BTF_ids section.
+		 */
+		if (is_ftrace_func(func, addrs, count) ||
+		    is_listed_func(func->name)) {
 			/*
 			 * We iterate over sorted array, so we can easily skip
 			 * not valid item and move following valid field into
@@ -533,6 +653,7 @@ int btf_encoder__encode()
 
 	err = btf_elf__encode(btfe, 0);
 	delete_functions();
+	delete_listed_functions();
 	btf_elf__delete(btfe);
 	btfe = NULL;
 
@@ -650,6 +771,8 @@ static int collect_symbols(struct btf_elf *btfe, bool collect_percpu_vars)
 			return -1;
 		if (collect_function(btfe, &sym, sym_sec_idx))
 			return -1;
+		if (collect_listed_functions(btfe, &sym, sym_sec_idx))
+			return -1;
 		collect_symbol(&sym, &fl, sym_sec_idx);
 	}
 
@@ -764,7 +887,8 @@ int cu__encode_btf(struct cu *cu, int verbose, bool force,
 		 *   - are marked as declarations
 		 *   - do not have full argument names
 		 *   - are not in ftrace list (if it's available)
-		 *   - are not external (in case ftrace filter is not available)
+		 *   - are not in the .BTF_ids section
+		 *   - are not external (in case ftrace and .BTF_ids filter are not available)
 		 */
 		if (fn->declaration)
 			continue;
diff --git a/libbtf.c b/libbtf.c
index c2360ff1f804..d9729bd4680d 100644
--- a/libbtf.c
+++ b/libbtf.c
@@ -168,6 +168,16 @@ try_as_raw_btf:
 		return btfe;
 	}
 
+	sec = elf_section_by_name(btfe->elf, &btfe->ehdr, &shdr,
+				  BTF_IDS_SECTION, NULL);
+	if (!sec) {
+		if (btf_elf__verbose)
+			printf("%s: '%s' doesn't have '%s' section\n", __func__,
+			       btfe->filename, BTF_IDS_SECTION);
+	} else {
+		btfe->btf_ids_shndx = elf_ndxscn(sec);
+	}
+
 	/* find percpu section's shndx */
 	sec = elf_section_by_name(btfe->elf, &btfe->ehdr, &shdr, PERCPU_SECTION,
 				  NULL);
diff --git a/libbtf.h b/libbtf.h
index c7cbe6e17ee7..6caaac306f77 100644
--- a/libbtf.h
+++ b/libbtf.h
@@ -24,6 +24,7 @@ struct btf_elf {
 	uint8_t		  wordsize;
 	bool		  is_big_endian;
 	bool		  raw_btf; // "/sys/kernel/btf/vmlinux"
+	uint32_t	  btf_ids_shndx;
 	uint32_t	  percpu_shndx;
 	uint64_t	  percpu_base_addr;
 	uint64_t	  percpu_sec_sz;
@@ -38,6 +39,7 @@ extern uint8_t btf_elf__force;
 extern bool btf_gen_floats;
 
 #define PERCPU_SECTION ".data..percpu"
+#define BTF_IDS_SECTION ".BTF_ids"
 
 struct cu;
 struct base_type;
-- 
2.30.2





[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux