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;
