mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	libbpf: add btf__parse_elf API to load .BTF and .BTF.ext
Loading BTF and BTF.ext from ELF file is a common need. Instead of requiring every user to re-implement it, let's provide this API from libbpf itself. It's mostly copy/paste from `bpftool btf dump` implementation, which will be switched to libbpf's version in next patch. btf__parse_elf allows to load BTF and optionally BTF.ext. This is also useful for tests that need to load/work with BTF, loaded from test ELF files. Signed-off-by: Andrii Nakryiko <andriin@fb.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
		
							parent
							
								
									1d7a08b3bd
								
							
						
					
					
						commit
						e6c64855fd
					
				
					 3 changed files with 135 additions and 0 deletions
				
			
		| 
						 | 
					@ -4,10 +4,12 @@
 | 
				
			||||||
#include <stdio.h>
 | 
					#include <stdio.h>
 | 
				
			||||||
#include <stdlib.h>
 | 
					#include <stdlib.h>
 | 
				
			||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <fcntl.h>
 | 
				
			||||||
#include <unistd.h>
 | 
					#include <unistd.h>
 | 
				
			||||||
#include <errno.h>
 | 
					#include <errno.h>
 | 
				
			||||||
#include <linux/err.h>
 | 
					#include <linux/err.h>
 | 
				
			||||||
#include <linux/btf.h>
 | 
					#include <linux/btf.h>
 | 
				
			||||||
 | 
					#include <gelf.h>
 | 
				
			||||||
#include "btf.h"
 | 
					#include "btf.h"
 | 
				
			||||||
#include "bpf.h"
 | 
					#include "bpf.h"
 | 
				
			||||||
#include "libbpf.h"
 | 
					#include "libbpf.h"
 | 
				
			||||||
| 
						 | 
					@ -417,6 +419,132 @@ struct btf *btf__new(__u8 *data, __u32 size)
 | 
				
			||||||
	return btf;
 | 
						return btf;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool btf_check_endianness(const GElf_Ehdr *ehdr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
 | 
				
			||||||
 | 
						return ehdr->e_ident[EI_DATA] == ELFDATA2LSB;
 | 
				
			||||||
 | 
					#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
 | 
				
			||||||
 | 
						return ehdr->e_ident[EI_DATA] == ELFDATA2MSB;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					# error "Unrecognized __BYTE_ORDER__"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct btf *btf__parse_elf(const char *path, struct btf_ext **btf_ext)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						Elf_Data *btf_data = NULL, *btf_ext_data = NULL;
 | 
				
			||||||
 | 
						int err = 0, fd = -1, idx = 0;
 | 
				
			||||||
 | 
						struct btf *btf = NULL;
 | 
				
			||||||
 | 
						Elf_Scn *scn = NULL;
 | 
				
			||||||
 | 
						Elf *elf = NULL;
 | 
				
			||||||
 | 
						GElf_Ehdr ehdr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (elf_version(EV_CURRENT) == EV_NONE) {
 | 
				
			||||||
 | 
							pr_warning("failed to init libelf for %s\n", path);
 | 
				
			||||||
 | 
							return ERR_PTR(-LIBBPF_ERRNO__LIBELF);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fd = open(path, O_RDONLY);
 | 
				
			||||||
 | 
						if (fd < 0) {
 | 
				
			||||||
 | 
							err = -errno;
 | 
				
			||||||
 | 
							pr_warning("failed to open %s: %s\n", path, strerror(errno));
 | 
				
			||||||
 | 
							return ERR_PTR(err);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = -LIBBPF_ERRNO__FORMAT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						elf = elf_begin(fd, ELF_C_READ, NULL);
 | 
				
			||||||
 | 
						if (!elf) {
 | 
				
			||||||
 | 
							pr_warning("failed to open %s as ELF file\n", path);
 | 
				
			||||||
 | 
							goto done;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (!gelf_getehdr(elf, &ehdr)) {
 | 
				
			||||||
 | 
							pr_warning("failed to get EHDR from %s\n", path);
 | 
				
			||||||
 | 
							goto done;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (!btf_check_endianness(&ehdr)) {
 | 
				
			||||||
 | 
							pr_warning("non-native ELF endianness is not supported\n");
 | 
				
			||||||
 | 
							goto done;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (!elf_rawdata(elf_getscn(elf, ehdr.e_shstrndx), NULL)) {
 | 
				
			||||||
 | 
							pr_warning("failed to get e_shstrndx from %s\n", path);
 | 
				
			||||||
 | 
							goto done;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while ((scn = elf_nextscn(elf, scn)) != NULL) {
 | 
				
			||||||
 | 
							GElf_Shdr sh;
 | 
				
			||||||
 | 
							char *name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							idx++;
 | 
				
			||||||
 | 
							if (gelf_getshdr(scn, &sh) != &sh) {
 | 
				
			||||||
 | 
								pr_warning("failed to get section(%d) header from %s\n",
 | 
				
			||||||
 | 
									   idx, path);
 | 
				
			||||||
 | 
								goto done;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							name = elf_strptr(elf, ehdr.e_shstrndx, sh.sh_name);
 | 
				
			||||||
 | 
							if (!name) {
 | 
				
			||||||
 | 
								pr_warning("failed to get section(%d) name from %s\n",
 | 
				
			||||||
 | 
									   idx, path);
 | 
				
			||||||
 | 
								goto done;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (strcmp(name, BTF_ELF_SEC) == 0) {
 | 
				
			||||||
 | 
								btf_data = elf_getdata(scn, 0);
 | 
				
			||||||
 | 
								if (!btf_data) {
 | 
				
			||||||
 | 
									pr_warning("failed to get section(%d, %s) data from %s\n",
 | 
				
			||||||
 | 
										   idx, name, path);
 | 
				
			||||||
 | 
									goto done;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							} else if (btf_ext && strcmp(name, BTF_EXT_ELF_SEC) == 0) {
 | 
				
			||||||
 | 
								btf_ext_data = elf_getdata(scn, 0);
 | 
				
			||||||
 | 
								if (!btf_ext_data) {
 | 
				
			||||||
 | 
									pr_warning("failed to get section(%d, %s) data from %s\n",
 | 
				
			||||||
 | 
										   idx, name, path);
 | 
				
			||||||
 | 
									goto done;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!btf_data) {
 | 
				
			||||||
 | 
							err = -ENOENT;
 | 
				
			||||||
 | 
							goto done;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						btf = btf__new(btf_data->d_buf, btf_data->d_size);
 | 
				
			||||||
 | 
						if (IS_ERR(btf))
 | 
				
			||||||
 | 
							goto done;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (btf_ext && btf_ext_data) {
 | 
				
			||||||
 | 
							*btf_ext = btf_ext__new(btf_ext_data->d_buf,
 | 
				
			||||||
 | 
										btf_ext_data->d_size);
 | 
				
			||||||
 | 
							if (IS_ERR(*btf_ext))
 | 
				
			||||||
 | 
								goto done;
 | 
				
			||||||
 | 
						} else if (btf_ext) {
 | 
				
			||||||
 | 
							*btf_ext = NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					done:
 | 
				
			||||||
 | 
						if (elf)
 | 
				
			||||||
 | 
							elf_end(elf);
 | 
				
			||||||
 | 
						close(fd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							return ERR_PTR(err);
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * btf is always parsed before btf_ext, so no need to clean up
 | 
				
			||||||
 | 
						 * btf_ext, if btf loading failed
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (IS_ERR(btf))
 | 
				
			||||||
 | 
							return btf;
 | 
				
			||||||
 | 
						if (btf_ext && IS_ERR(*btf_ext)) {
 | 
				
			||||||
 | 
							btf__free(btf);
 | 
				
			||||||
 | 
							err = PTR_ERR(*btf_ext);
 | 
				
			||||||
 | 
							return ERR_PTR(err);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return btf;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int compare_vsi_off(const void *_a, const void *_b)
 | 
					static int compare_vsi_off(const void *_a, const void *_b)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	const struct btf_var_secinfo *a = _a;
 | 
						const struct btf_var_secinfo *a = _a;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -59,6 +59,8 @@ struct btf_ext_header {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LIBBPF_API void btf__free(struct btf *btf);
 | 
					LIBBPF_API void btf__free(struct btf *btf);
 | 
				
			||||||
LIBBPF_API struct btf *btf__new(__u8 *data, __u32 size);
 | 
					LIBBPF_API struct btf *btf__new(__u8 *data, __u32 size);
 | 
				
			||||||
 | 
					LIBBPF_API struct btf *btf__parse_elf(const char *path,
 | 
				
			||||||
 | 
									      struct btf_ext **btf_ext);
 | 
				
			||||||
LIBBPF_API int btf__finalize_data(struct bpf_object *obj, struct btf *btf);
 | 
					LIBBPF_API int btf__finalize_data(struct bpf_object *obj, struct btf *btf);
 | 
				
			||||||
LIBBPF_API int btf__load(struct btf *btf);
 | 
					LIBBPF_API int btf__load(struct btf *btf);
 | 
				
			||||||
LIBBPF_API __s32 btf__find_by_name(const struct btf *btf,
 | 
					LIBBPF_API __s32 btf__find_by_name(const struct btf *btf,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -164,3 +164,8 @@ LIBBPF_0.0.3 {
 | 
				
			||||||
		bpf_map_freeze;
 | 
							bpf_map_freeze;
 | 
				
			||||||
		btf__finalize_data;
 | 
							btf__finalize_data;
 | 
				
			||||||
} LIBBPF_0.0.2;
 | 
					} LIBBPF_0.0.2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LIBBPF_0.0.4 {
 | 
				
			||||||
 | 
						global:
 | 
				
			||||||
 | 
							btf__parse_elf;
 | 
				
			||||||
 | 
					} LIBBPF_0.0.3;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue