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