xref: /aosp_15_r20/external/bcc/libbpf-tools/btf_helpers.c (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1 /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
2 
3 #include <errno.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <sys/utsname.h>
8 #include <zlib.h>
9 
10 #include "trace_helpers.h"
11 #include "btf_helpers.h"
12 
13 extern unsigned char _binary_min_core_btfs_tar_gz_start[] __attribute__((weak));
14 extern unsigned char _binary_min_core_btfs_tar_gz_end[] __attribute__((weak));
15 
16 #define FIELD_LEN 65
17 #define ID_FMT "ID=%64s"
18 #define VERSION_FMT "VERSION_ID=\"%64s"
19 
20 struct os_info {
21 	char id[FIELD_LEN];
22 	char version[FIELD_LEN];
23 	char arch[FIELD_LEN];
24 	char kernel_release[FIELD_LEN];
25 };
26 
get_os_info()27 static struct os_info * get_os_info()
28 {
29 	struct os_info *info = NULL;
30 	struct utsname u;
31 	size_t len = 0;
32 	ssize_t read;
33 	char *line = NULL;
34 	FILE *f;
35 
36 	if (uname(&u) == -1)
37 		return NULL;
38 
39 	f = fopen("/etc/os-release", "r");
40 	if (!f)
41 		return NULL;
42 
43 	info = calloc(1, sizeof(*info));
44 	if (!info)
45 		goto out;
46 
47 	strncpy(info->kernel_release, u.release, FIELD_LEN);
48 	strncpy(info->arch, u.machine, FIELD_LEN);
49 
50 	while ((read = getline(&line, &len, f)) != -1) {
51 		if (sscanf(line, ID_FMT, info->id) == 1)
52 			continue;
53 
54 		if (sscanf(line, VERSION_FMT, info->version) == 1) {
55 			/* remove '"' suffix */
56 			info->version[strlen(info->version) - 1] = 0;
57 			continue;
58 		}
59 	}
60 
61 out:
62 	free(line);
63 	fclose(f);
64 
65 	return info;
66 }
67 
68 #define INITIAL_BUF_SIZE (1024 * 1024 * 4) /* 4MB */
69 
70 /* adapted from https://zlib.net/zlib_how.html */
71 static int
inflate_gz(unsigned char * src,int src_size,unsigned char ** dst,int * dst_size)72 inflate_gz(unsigned char *src, int src_size, unsigned char **dst, int *dst_size)
73 {
74 	size_t size = INITIAL_BUF_SIZE;
75 	size_t next_size = size;
76 	z_stream strm;
77 	void *tmp;
78 	int ret;
79 
80 	strm.zalloc = Z_NULL;
81 	strm.zfree = Z_NULL;
82 	strm.opaque = Z_NULL;
83 	strm.avail_in = 0;
84 	strm.next_in = Z_NULL;
85 
86 	ret = inflateInit2(&strm, 16 + MAX_WBITS);
87 	if (ret != Z_OK)
88 		return -EINVAL;
89 
90 	*dst = malloc(size);
91 	if (!*dst)
92 		return -ENOMEM;
93 
94 	strm.next_in = src;
95 	strm.avail_in = src_size;
96 
97 	/* run inflate() on input until it returns Z_STREAM_END */
98 	do {
99 		strm.next_out = *dst + strm.total_out;
100 		strm.avail_out = next_size;
101 		ret = inflate(&strm, Z_NO_FLUSH);
102 		if (ret != Z_OK && ret != Z_STREAM_END)
103 			goto out_err;
104 		/* we need more space */
105 		if (strm.avail_out == 0) {
106 			next_size = size;
107 			size *= 2;
108 			tmp = realloc(*dst, size);
109 			if (!tmp) {
110 				ret = -ENOMEM;
111 				goto out_err;
112 			}
113 			*dst = tmp;
114 		}
115 	} while (ret != Z_STREAM_END);
116 
117 	*dst_size = strm.total_out;
118 
119 	/* clean up and return */
120 	ret = inflateEnd(&strm);
121 	if (ret != Z_OK) {
122 		ret = -EINVAL;
123 		goto out_err;
124 	}
125 	return 0;
126 
127 out_err:
128 	free(*dst);
129 	*dst = NULL;
130 	return ret;
131 }
132 
133 /* tar header from https://github.com/tklauser/libtar/blob/v1.2.20/lib/libtar.h#L39-L60 */
134 struct tar_header {
135 	char name[100];
136 	char mode[8];
137 	char uid[8];
138 	char gid[8];
139 	char size[12];
140 	char mtime[12];
141 	char chksum[8];
142 	char typeflag;
143 	char linkname[100];
144 	char magic[6];
145 	char version[2];
146 	char uname[32];
147 	char gname[32];
148 	char devmajor[8];
149 	char devminor[8];
150 	char prefix[155];
151 	char padding[12];
152 };
153 
tar_file_start(struct tar_header * tar,const char * name,int * length)154 static char *tar_file_start(struct tar_header *tar, const char *name, int *length)
155 {
156 	while (tar->name[0]) {
157 		sscanf(tar->size, "%o", length);
158 		if (!strcmp(tar->name, name))
159 			return (char *)(tar + 1);
160 		tar += 1 + (*length + 511)/512;
161 	}
162 	return NULL;
163 }
164 
ensure_core_btf(struct bpf_object_open_opts * opts)165 int ensure_core_btf(struct bpf_object_open_opts *opts)
166 {
167 	char name_fmt[] = "./%s/%s/%s/%s.btf";
168 	char btf_path[] = "/tmp/bcc-libbpf-tools.btf.XXXXXX";
169 	struct os_info *info = NULL;
170 	unsigned char *dst_buf = NULL;
171 	char *file_start;
172 	int dst_size = 0;
173 	char name[100];
174 	FILE *dst = NULL;
175 	int ret;
176 
177 	/* do nothing if the system provides BTF */
178 	if (vmlinux_btf_exists())
179 		return 0;
180 
181 	/* compiled without min core btfs */
182 	if (!_binary_min_core_btfs_tar_gz_start)
183 		return -EOPNOTSUPP;
184 
185 	info = get_os_info();
186 	if (!info)
187 		return -errno;
188 
189 	ret = mkstemp(btf_path);
190 	if (ret < 0) {
191 		ret = -errno;
192 		goto out;
193 	}
194 
195 	dst = fdopen(ret, "wb");
196 	if (!dst) {
197 		ret = -errno;
198 		goto out;
199 	}
200 
201 	ret = snprintf(name, sizeof(name), name_fmt, info->id, info->version,
202 		       info->arch, info->kernel_release);
203 	if (ret < 0 || ret == sizeof(name)) {
204 		ret = -EINVAL;
205 		goto out;
206 	}
207 
208 	ret = inflate_gz(_binary_min_core_btfs_tar_gz_start,
209 			 _binary_min_core_btfs_tar_gz_end - _binary_min_core_btfs_tar_gz_start,
210 			 &dst_buf, &dst_size);
211 	if (ret < 0)
212 		goto out;
213 
214 	ret = 0;
215 	file_start = tar_file_start((struct tar_header *)dst_buf, name, &dst_size);
216 	if (!file_start) {
217 		ret = -EINVAL;
218 		goto out;
219 	}
220 
221 	if (fwrite(file_start, 1, dst_size, dst) != dst_size) {
222 		ret = -ferror(dst);
223 		goto out;
224 	}
225 
226 	opts->btf_custom_path = strdup(btf_path);
227 	if (!opts->btf_custom_path)
228 		ret = -ENOMEM;
229 
230 out:
231 	free(info);
232 	fclose(dst);
233 	free(dst_buf);
234 
235 	return ret;
236 }
237 
cleanup_core_btf(struct bpf_object_open_opts * opts)238 void cleanup_core_btf(struct bpf_object_open_opts *opts) {
239 	if (!opts)
240 		return;
241 
242 	if (!opts->btf_custom_path)
243 		return;
244 
245 	unlink(opts->btf_custom_path);
246 	free((void *)opts->btf_custom_path);
247 }
248