xref: /aosp_15_r20/external/bcc/libbpf-tools/uprobe_helpers.c (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2 /* Copyright (c) 2021 Google LLC. */
3 #ifndef _GNU_SOURCE
4 #define _GNU_SOURCE
5 #endif
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <fcntl.h>
9 #include <stdio.h>
10 #include <stdarg.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <errno.h>
15 #include <limits.h>
16 #include <gelf.h>
17 
18 #define warn(...) fprintf(stderr, __VA_ARGS__)
19 
20 /*
21  * Returns 0 on success; -1 on failure.  On sucess, returns via `path` the full
22  * path to the program for pid.
23  */
get_pid_binary_path(pid_t pid,char * path,size_t path_sz)24 int get_pid_binary_path(pid_t pid, char *path, size_t path_sz)
25 {
26 	ssize_t ret;
27 	char proc_pid_exe[32];
28 
29 	if (snprintf(proc_pid_exe, sizeof(proc_pid_exe), "/proc/%d/exe", pid)
30 	    >= sizeof(proc_pid_exe)) {
31 		warn("snprintf /proc/PID/exe failed");
32 		return -1;
33 	}
34 	ret = readlink(proc_pid_exe, path, path_sz);
35 	if (ret < 0) {
36 		warn("No such pid %d\n", pid);
37 		return -1;
38 	}
39 	if (ret >= path_sz) {
40 		warn("readlink truncation");
41 		return -1;
42 	}
43 	path[ret] = '\0';
44 
45 	return 0;
46 }
47 
48 /*
49  * Returns 0 on success; -1 on failure.  On success, returns via `path` the full
50  * path to a library matching the name `lib` that is loaded into pid's address
51  * space.
52  */
get_pid_lib_path(pid_t pid,const char * lib,char * path,size_t path_sz)53 int get_pid_lib_path(pid_t pid, const char *lib, char *path, size_t path_sz)
54 {
55 	FILE *maps;
56 	char *p;
57 	char proc_pid_maps[32];
58 	char line_buf[1024];
59 	char path_buf[1024];
60 
61 	if (snprintf(proc_pid_maps, sizeof(proc_pid_maps), "/proc/%d/maps", pid)
62 	    >= sizeof(proc_pid_maps)) {
63 		warn("snprintf /proc/PID/maps failed");
64 		return -1;
65 	}
66 	maps = fopen(proc_pid_maps, "r");
67 	if (!maps) {
68 		warn("No such pid %d\n", pid);
69 		return -1;
70 	}
71 	while (fgets(line_buf, sizeof(line_buf), maps)) {
72 		if (sscanf(line_buf, "%*x-%*x %*s %*x %*s %*u %s", path_buf) != 1)
73 			continue;
74 		/* e.g. /usr/lib/x86_64-linux-gnu/libc-2.31.so */
75 		p = strrchr(path_buf, '/');
76 		if (!p)
77 			continue;
78 		if (strncmp(p, "/lib", 4))
79 			continue;
80 		p += 4;
81 		if (strncmp(lib, p, strlen(lib)))
82 			continue;
83 		p += strlen(lib);
84 		/* libraries can have - or . after the name */
85 		if (*p != '.' && *p != '-')
86 			continue;
87 		if (strnlen(path_buf, 1024) >= path_sz) {
88 			warn("path size too small\n");
89 			return -1;
90 		}
91 		strcpy(path, path_buf);
92 		fclose(maps);
93 		return 0;
94 	}
95 
96 	warn("Cannot find library %s\n", lib);
97 	fclose(maps);
98 	return -1;
99 }
100 
101 /*
102  * Returns 0 on success; -1 on failure.  On success, returns via `path` the full
103  * path to the program.
104  */
which_program(const char * prog,char * path,size_t path_sz)105 static int which_program(const char *prog, char *path, size_t path_sz)
106 {
107 	FILE *which;
108 	char cmd[100];
109 
110 	if (snprintf(cmd, sizeof(cmd), "which %s", prog) >= sizeof(cmd)) {
111 		warn("snprintf which prog failed");
112 		return -1;
113 	}
114 	which = popen(cmd, "r");
115 	if (!which) {
116 		warn("which failed");
117 		return -1;
118 	}
119 	if (!fgets(path, path_sz, which)) {
120 		warn("fgets which failed");
121 		pclose(which);
122 		return -1;
123 	}
124 	/* which has a \n at the end of the string */
125 	path[strlen(path) - 1] = '\0';
126 	pclose(which);
127 	return 0;
128 }
129 
130 /*
131  * Returns 0 on success; -1 on failure.  On success, returns via `path` the full
132  * path to the binary for the given pid.
133  * 1) pid == x, binary == ""    : returns the path to x's program
134  * 2) pid == x, binary == "foo" : returns the path to libfoo linked in x
135  * 3) pid == 0, binary == ""    : failure: need a pid or a binary
136  * 4) pid == 0, binary == "bar" : returns the path to `which bar`
137  *
138  * For case 4), ideally we'd like to search for libbar too, but we don't support
139  * that yet.
140  */
resolve_binary_path(const char * binary,pid_t pid,char * path,size_t path_sz)141 int resolve_binary_path(const char *binary, pid_t pid, char *path, size_t path_sz)
142 {
143 	if (!strcmp(binary, "")) {
144 		if (!pid) {
145 			warn("Uprobes need a pid or a binary\n");
146 			return -1;
147 		}
148 		return get_pid_binary_path(pid, path, path_sz);
149 	}
150 	if (pid)
151 		return get_pid_lib_path(pid, binary, path, path_sz);
152 
153 	if (which_program(binary, path, path_sz)) {
154 		/*
155 		 * If the user is tracing a program by name, we can find it.
156 		 * But we can't find a library by name yet.  We'd need to parse
157 		 * ld.so.cache or something similar.
158 		 */
159 		warn("Can't find %s (Need a PID if this is a library)\n", binary);
160 		return -1;
161 	}
162 	return 0;
163 }
164 
165 /*
166  * Opens an elf at `path` of kind ELF_K_ELF.  Returns NULL on failure.  On
167  * success, close with close_elf(e, fd_close).
168  */
open_elf(const char * path,int * fd_close)169 Elf *open_elf(const char *path, int *fd_close)
170 {
171 	int fd;
172 	Elf *e;
173 
174 	if (elf_version(EV_CURRENT) == EV_NONE) {
175 		warn("elf init failed\n");
176 		return NULL;
177 	}
178 	fd = open(path, O_RDONLY);
179 	if (fd < 0) {
180 		warn("Could not open %s\n", path);
181 		return NULL;
182 	}
183 	e = elf_begin(fd, ELF_C_READ, NULL);
184 	if (!e) {
185 		warn("elf_begin failed: %s\n", elf_errmsg(-1));
186 		close(fd);
187 		return NULL;
188 	}
189 	if (elf_kind(e) != ELF_K_ELF) {
190 		warn("elf kind %d is not ELF_K_ELF\n", elf_kind(e));
191 		elf_end(e);
192 		close(fd);
193 		return NULL;
194 	}
195 	*fd_close = fd;
196 	return e;
197 }
198 
open_elf_by_fd(int fd)199 Elf *open_elf_by_fd(int fd)
200 {
201 	Elf *e;
202 
203 	if (elf_version(EV_CURRENT) == EV_NONE) {
204 		warn("elf init failed\n");
205 		return NULL;
206 	}
207 	e = elf_begin(fd, ELF_C_READ, NULL);
208 	if (!e) {
209 		warn("elf_begin failed: %s\n", elf_errmsg(-1));
210 		close(fd);
211 		return NULL;
212 	}
213 	if (elf_kind(e) != ELF_K_ELF) {
214 		warn("elf kind %d is not ELF_K_ELF\n", elf_kind(e));
215 		elf_end(e);
216 		close(fd);
217 		return NULL;
218 	}
219 	return e;
220 }
221 
close_elf(Elf * e,int fd_close)222 void close_elf(Elf *e, int fd_close)
223 {
224 	elf_end(e);
225 	close(fd_close);
226 }
227 
228 /* Returns the offset of a function in the elf file `path`, or -1 on failure. */
get_elf_func_offset(const char * path,const char * func)229 off_t get_elf_func_offset(const char *path, const char *func)
230 {
231 	off_t ret = -1;
232 	int i, fd = -1;
233 	Elf *e;
234 	Elf_Scn *scn;
235 	Elf_Data *data;
236 	GElf_Ehdr ehdr;
237 	GElf_Shdr shdr[1];
238 	GElf_Phdr phdr;
239 	GElf_Sym sym[1];
240 	size_t shstrndx, nhdrs;
241 	char *n;
242 
243 	e = open_elf(path, &fd);
244 
245 	if (!gelf_getehdr(e, &ehdr))
246 		goto out;
247 
248 	if (elf_getshdrstrndx(e, &shstrndx) != 0)
249 		goto out;
250 
251 	scn = NULL;
252 	while ((scn = elf_nextscn(e, scn))) {
253 		if (!gelf_getshdr(scn, shdr))
254 			continue;
255 		if (!(shdr->sh_type == SHT_SYMTAB || shdr->sh_type == SHT_DYNSYM))
256 			continue;
257 		data = NULL;
258 		while ((data = elf_getdata(scn, data))) {
259 			for (i = 0; gelf_getsym(data, i, sym); i++) {
260 				n = elf_strptr(e, shdr->sh_link, sym->st_name);
261 				if (!n)
262 					continue;
263 				if (GELF_ST_TYPE(sym->st_info) != STT_FUNC)
264 					continue;
265 				if (!strcmp(n, func)) {
266 					ret = sym->st_value;
267 					goto check;
268 				}
269 			}
270 		}
271 	}
272 
273 check:
274 	if (ehdr.e_type == ET_EXEC || ehdr.e_type == ET_DYN) {
275 		if (elf_getphdrnum(e, &nhdrs) != 0) {
276 			ret = -1;
277 			goto out;
278 		}
279 		for (i = 0; i < (int)nhdrs; i++) {
280 			if (!gelf_getphdr(e, i, &phdr))
281 				continue;
282 			if (phdr.p_type != PT_LOAD || !(phdr.p_flags & PF_X))
283 				continue;
284 			if (phdr.p_vaddr <= ret && ret < (phdr.p_vaddr + phdr.p_memsz)) {
285 				ret = ret - phdr.p_vaddr + phdr.p_offset;
286 				goto out;
287 			}
288 		}
289 		ret = -1;
290 	}
291 out:
292 	close_elf(e, fd);
293 	return ret;
294 }
295