• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
60 	if (snprintf(proc_pid_maps, sizeof(proc_pid_maps), "/proc/%d/maps", pid)
61 	    >= sizeof(proc_pid_maps)) {
62 		warn("snprintf /proc/PID/maps failed");
63 		return -1;
64 	}
65 	maps = fopen(proc_pid_maps, "r");
66 	if (!maps) {
67 		warn("No such pid %d\n", pid);
68 		return -1;
69 	}
70 	while (fgets(line_buf, sizeof(line_buf), maps)) {
71 		if (sscanf(line_buf, "%*x-%*x %*s %*x %*s %*u %s", path) != 1)
72 			continue;
73 		/* e.g. /usr/lib/x86_64-linux-gnu/libc-2.31.so */
74 		p = strrchr(path, '/');
75 		if (!p)
76 			continue;
77 		if (strncmp(p, "/lib", 4))
78 			continue;
79 		p += 4;
80 		if (strncmp(lib, p, strlen(lib)))
81 			continue;
82 		p += strlen(lib);
83 		/* libraries can have - or . after the name */
84 		if (*p != '.' && *p != '-')
85 			continue;
86 
87 		fclose(maps);
88 		return 0;
89 	}
90 
91 	warn("Cannot find library %s\n", lib);
92 	fclose(maps);
93 	return -1;
94 }
95 
96 /*
97  * Returns 0 on success; -1 on failure.  On success, returns via `path` the full
98  * path to the program.
99  */
which_program(const char * prog,char * path,size_t path_sz)100 static int which_program(const char *prog, char *path, size_t path_sz)
101 {
102 	FILE *which;
103 	char cmd[100];
104 
105 	if (snprintf(cmd, sizeof(cmd), "which %s", prog) >= sizeof(cmd)) {
106 		warn("snprintf which prog failed");
107 		return -1;
108 	}
109 	which = popen(cmd, "r");
110 	if (!which) {
111 		warn("which failed");
112 		return -1;
113 	}
114 	if (!fgets(path, path_sz, which)) {
115 		warn("fgets which failed");
116 		pclose(which);
117 		return -1;
118 	}
119 	/* which has a \n at the end of the string */
120 	path[strlen(path) - 1] = '\0';
121 	pclose(which);
122 	return 0;
123 }
124 
125 /*
126  * Returns 0 on success; -1 on failure.  On success, returns via `path` the full
127  * path to the binary for the given pid.
128  * 1) pid == x, binary == ""    : returns the path to x's program
129  * 2) pid == x, binary == "foo" : returns the path to libfoo linked in x
130  * 3) pid == 0, binary == ""    : failure: need a pid or a binary
131  * 4) pid == 0, binary == "bar" : returns the path to `which bar`
132  *
133  * For case 4), ideally we'd like to search for libbar too, but we don't support
134  * that yet.
135  */
resolve_binary_path(const char * binary,pid_t pid,char * path,size_t path_sz)136 int resolve_binary_path(const char *binary, pid_t pid, char *path, size_t path_sz)
137 {
138 	if (!strcmp(binary, "")) {
139 		if (!pid) {
140 			warn("Uprobes need a pid or a binary\n");
141 			return -1;
142 		}
143 		return get_pid_binary_path(pid, path, path_sz);
144 	}
145 	if (pid)
146 		return get_pid_lib_path(pid, binary, path, path_sz);
147 
148 	if (which_program(binary, path, path_sz)) {
149 		/*
150 		 * If the user is tracing a program by name, we can find it.
151 		 * But we can't find a library by name yet.  We'd need to parse
152 		 * ld.so.cache or something similar.
153 		 */
154 		warn("Can't find %s (Need a PID if this is a library)\n", binary);
155 		return -1;
156 	}
157 	return 0;
158 }
159 
160 /*
161  * Opens an elf at `path` of kind ELF_K_ELF.  Returns NULL on failure.  On
162  * success, close with close_elf(e, fd_close).
163  */
open_elf(const char * path,int * fd_close)164 Elf *open_elf(const char *path, int *fd_close)
165 {
166 	int fd;
167 	Elf *e;
168 
169 	if (elf_version(EV_CURRENT) == EV_NONE) {
170 		warn("elf init failed\n");
171 		return NULL;
172 	}
173 	fd = open(path, O_RDONLY);
174 	if (fd < 0) {
175 		warn("Could not open %s\n", path);
176 		return NULL;
177 	}
178 	e = elf_begin(fd, ELF_C_READ, NULL);
179 	if (!e) {
180 		warn("elf_begin failed: %s\n", elf_errmsg(-1));
181 		close(fd);
182 		return NULL;
183 	}
184 	if (elf_kind(e) != ELF_K_ELF) {
185 		warn("elf kind %d is not ELF_K_ELF\n", elf_kind(e));
186 		elf_end(e);
187 		close(fd);
188 		return NULL;
189 	}
190 	*fd_close = fd;
191 	return e;
192 }
193 
open_elf_by_fd(int fd)194 Elf *open_elf_by_fd(int fd)
195 {
196 	Elf *e;
197 
198 	if (elf_version(EV_CURRENT) == EV_NONE) {
199 		warn("elf init failed\n");
200 		return NULL;
201 	}
202 	e = elf_begin(fd, ELF_C_READ, NULL);
203 	if (!e) {
204 		warn("elf_begin failed: %s\n", elf_errmsg(-1));
205 		close(fd);
206 		return NULL;
207 	}
208 	if (elf_kind(e) != ELF_K_ELF) {
209 		warn("elf kind %d is not ELF_K_ELF\n", elf_kind(e));
210 		elf_end(e);
211 		close(fd);
212 		return NULL;
213 	}
214 	return e;
215 }
216 
close_elf(Elf * e,int fd_close)217 void close_elf(Elf *e, int fd_close)
218 {
219 	elf_end(e);
220 	close(fd_close);
221 }
222 
223 /* 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)224 off_t get_elf_func_offset(const char *path, const char *func)
225 {
226 	off_t ret = -1;
227 	int i, fd = -1;
228 	Elf *e;
229 	Elf_Scn *scn;
230 	Elf_Data *data;
231 	GElf_Ehdr ehdr;
232 	GElf_Shdr shdr[1];
233 	GElf_Phdr phdr;
234 	GElf_Sym sym[1];
235 	size_t shstrndx, nhdrs;
236 	char *n;
237 
238 	e = open_elf(path, &fd);
239 
240 	if (!gelf_getehdr(e, &ehdr))
241 		goto out;
242 
243 	if (elf_getshdrstrndx(e, &shstrndx) != 0)
244 		goto out;
245 
246 	scn = NULL;
247 	while ((scn = elf_nextscn(e, scn))) {
248 		if (!gelf_getshdr(scn, shdr))
249 			continue;
250 		if (!(shdr->sh_type == SHT_SYMTAB || shdr->sh_type == SHT_DYNSYM))
251 			continue;
252 		data = NULL;
253 		while ((data = elf_getdata(scn, data))) {
254 			for (i = 0; gelf_getsym(data, i, sym); i++) {
255 				n = elf_strptr(e, shdr->sh_link, sym->st_name);
256 				if (!n)
257 					continue;
258 				if (GELF_ST_TYPE(sym->st_info) != STT_FUNC)
259 					continue;
260 				if (!strcmp(n, func)) {
261 					ret = sym->st_value;
262 					goto check;
263 				}
264 			}
265 		}
266 	}
267 
268 check:
269 	if (ehdr.e_type == ET_EXEC || ehdr.e_type == ET_DYN) {
270 		if (elf_getphdrnum(e, &nhdrs) != 0) {
271 			ret = -1;
272 			goto out;
273 		}
274 		for (i = 0; i < (int)nhdrs; i++) {
275 			if (!gelf_getphdr(e, i, &phdr))
276 				continue;
277 			if (phdr.p_type != PT_LOAD || !(phdr.p_flags & PF_X))
278 				continue;
279 			if (phdr.p_vaddr <= ret && ret < (phdr.p_vaddr + phdr.p_memsz)) {
280 				ret = ret - phdr.p_vaddr + phdr.p_offset;
281 				goto out;
282 			}
283 		}
284 		ret = -1;
285 	}
286 out:
287 	close_elf(e, fd);
288 	return ret;
289 }
290