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