1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2
3 #ifndef _GNU_SOURCE
4 #define _GNU_SOURCE
5 #endif
6 #include <libelf.h>
7 #include <gelf.h>
8 #include <fcntl.h>
9 #include <linux/kernel.h>
10
11 #include "libbpf_internal.h"
12 #include "str_error.h"
13
14 #define STRERR_BUFSIZE 128
15
16 /* A SHT_GNU_versym section holds 16-bit words. This bit is set if
17 * the symbol is hidden and can only be seen when referenced using an
18 * explicit version number. This is a GNU extension.
19 */
20 #define VERSYM_HIDDEN 0x8000
21
22 /* This is the mask for the rest of the data in a word read from a
23 * SHT_GNU_versym section.
24 */
25 #define VERSYM_VERSION 0x7fff
26
elf_open(const char * binary_path,struct elf_fd * elf_fd)27 int elf_open(const char *binary_path, struct elf_fd *elf_fd)
28 {
29 char errmsg[STRERR_BUFSIZE];
30 int fd, ret;
31 Elf *elf;
32
33 if (elf_version(EV_CURRENT) == EV_NONE) {
34 pr_warn("elf: failed to init libelf for %s\n", binary_path);
35 return -LIBBPF_ERRNO__LIBELF;
36 }
37 fd = open(binary_path, O_RDONLY | O_CLOEXEC);
38 if (fd < 0) {
39 ret = -errno;
40 pr_warn("elf: failed to open %s: %s\n", binary_path,
41 libbpf_strerror_r(ret, errmsg, sizeof(errmsg)));
42 return ret;
43 }
44 elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
45 if (!elf) {
46 pr_warn("elf: could not read elf from %s: %s\n", binary_path, elf_errmsg(-1));
47 close(fd);
48 return -LIBBPF_ERRNO__FORMAT;
49 }
50 elf_fd->fd = fd;
51 elf_fd->elf = elf;
52 return 0;
53 }
54
elf_close(struct elf_fd * elf_fd)55 void elf_close(struct elf_fd *elf_fd)
56 {
57 if (!elf_fd)
58 return;
59 elf_end(elf_fd->elf);
60 close(elf_fd->fd);
61 }
62
63 /* Return next ELF section of sh_type after scn, or first of that type if scn is NULL. */
elf_find_next_scn_by_type(Elf * elf,int sh_type,Elf_Scn * scn)64 static Elf_Scn *elf_find_next_scn_by_type(Elf *elf, int sh_type, Elf_Scn *scn)
65 {
66 while ((scn = elf_nextscn(elf, scn)) != NULL) {
67 GElf_Shdr sh;
68
69 if (!gelf_getshdr(scn, &sh))
70 continue;
71 if (sh.sh_type == sh_type)
72 return scn;
73 }
74 return NULL;
75 }
76
77 struct elf_sym {
78 const char *name;
79 GElf_Sym sym;
80 GElf_Shdr sh;
81 int ver;
82 bool hidden;
83 };
84
85 struct elf_sym_iter {
86 Elf *elf;
87 Elf_Data *syms;
88 Elf_Data *versyms;
89 Elf_Data *verdefs;
90 size_t nr_syms;
91 size_t strtabidx;
92 size_t verdef_strtabidx;
93 size_t next_sym_idx;
94 struct elf_sym sym;
95 int st_type;
96 };
97
elf_sym_iter_new(struct elf_sym_iter * iter,Elf * elf,const char * binary_path,int sh_type,int st_type)98 static int elf_sym_iter_new(struct elf_sym_iter *iter,
99 Elf *elf, const char *binary_path,
100 int sh_type, int st_type)
101 {
102 Elf_Scn *scn = NULL;
103 GElf_Ehdr ehdr;
104 GElf_Shdr sh;
105
106 memset(iter, 0, sizeof(*iter));
107
108 if (!gelf_getehdr(elf, &ehdr)) {
109 pr_warn("elf: failed to get ehdr from %s: %s\n", binary_path, elf_errmsg(-1));
110 return -EINVAL;
111 }
112
113 scn = elf_find_next_scn_by_type(elf, sh_type, NULL);
114 if (!scn) {
115 pr_debug("elf: failed to find symbol table ELF sections in '%s'\n",
116 binary_path);
117 return -ENOENT;
118 }
119
120 if (!gelf_getshdr(scn, &sh))
121 return -EINVAL;
122
123 iter->strtabidx = sh.sh_link;
124 iter->syms = elf_getdata(scn, 0);
125 if (!iter->syms) {
126 pr_warn("elf: failed to get symbols for symtab section in '%s': %s\n",
127 binary_path, elf_errmsg(-1));
128 return -EINVAL;
129 }
130 iter->nr_syms = iter->syms->d_size / sh.sh_entsize;
131 iter->elf = elf;
132 iter->st_type = st_type;
133
134 /* Version symbol table is meaningful to dynsym only */
135 if (sh_type != SHT_DYNSYM)
136 return 0;
137
138 scn = elf_find_next_scn_by_type(elf, SHT_GNU_versym, NULL);
139 if (!scn)
140 return 0;
141 iter->versyms = elf_getdata(scn, 0);
142
143 scn = elf_find_next_scn_by_type(elf, SHT_GNU_verdef, NULL);
144 if (!scn) {
145 pr_debug("elf: failed to find verdef ELF sections in '%s'\n", binary_path);
146 return -ENOENT;
147 }
148 if (!gelf_getshdr(scn, &sh))
149 return -EINVAL;
150 iter->verdef_strtabidx = sh.sh_link;
151 iter->verdefs = elf_getdata(scn, 0);
152
153 return 0;
154 }
155
elf_sym_iter_next(struct elf_sym_iter * iter)156 static struct elf_sym *elf_sym_iter_next(struct elf_sym_iter *iter)
157 {
158 struct elf_sym *ret = &iter->sym;
159 GElf_Sym *sym = &ret->sym;
160 const char *name = NULL;
161 GElf_Versym versym;
162 Elf_Scn *sym_scn;
163 size_t idx;
164
165 for (idx = iter->next_sym_idx; idx < iter->nr_syms; idx++) {
166 if (!gelf_getsym(iter->syms, idx, sym))
167 continue;
168 if (GELF_ST_TYPE(sym->st_info) != iter->st_type)
169 continue;
170 name = elf_strptr(iter->elf, iter->strtabidx, sym->st_name);
171 if (!name)
172 continue;
173 sym_scn = elf_getscn(iter->elf, sym->st_shndx);
174 if (!sym_scn)
175 continue;
176 if (!gelf_getshdr(sym_scn, &ret->sh))
177 continue;
178
179 iter->next_sym_idx = idx + 1;
180 ret->name = name;
181 ret->ver = 0;
182 ret->hidden = false;
183
184 if (iter->versyms) {
185 if (!gelf_getversym(iter->versyms, idx, &versym))
186 continue;
187 ret->ver = versym & VERSYM_VERSION;
188 ret->hidden = versym & VERSYM_HIDDEN;
189 }
190 return ret;
191 }
192
193 return NULL;
194 }
195
elf_get_vername(struct elf_sym_iter * iter,int ver)196 static const char *elf_get_vername(struct elf_sym_iter *iter, int ver)
197 {
198 GElf_Verdaux verdaux;
199 GElf_Verdef verdef;
200 int offset;
201
202 offset = 0;
203 while (gelf_getverdef(iter->verdefs, offset, &verdef)) {
204 if (verdef.vd_ndx != ver) {
205 if (!verdef.vd_next)
206 break;
207
208 offset += verdef.vd_next;
209 continue;
210 }
211
212 if (!gelf_getverdaux(iter->verdefs, offset + verdef.vd_aux, &verdaux))
213 break;
214
215 return elf_strptr(iter->elf, iter->verdef_strtabidx, verdaux.vda_name);
216
217 }
218 return NULL;
219 }
220
symbol_match(struct elf_sym_iter * iter,int sh_type,struct elf_sym * sym,const char * name,size_t name_len,const char * lib_ver)221 static bool symbol_match(struct elf_sym_iter *iter, int sh_type, struct elf_sym *sym,
222 const char *name, size_t name_len, const char *lib_ver)
223 {
224 const char *ver_name;
225
226 /* Symbols are in forms of func, func@LIB_VER or func@@LIB_VER
227 * make sure the func part matches the user specified name
228 */
229 if (strncmp(sym->name, name, name_len) != 0)
230 return false;
231
232 /* ...but we don't want a search for "foo" to match 'foo2" also, so any
233 * additional characters in sname should be of the form "@@LIB".
234 */
235 if (sym->name[name_len] != '\0' && sym->name[name_len] != '@')
236 return false;
237
238 /* If user does not specify symbol version, then we got a match */
239 if (!lib_ver)
240 return true;
241
242 /* If user specifies symbol version, for dynamic symbols,
243 * get version name from ELF verdef section for comparison.
244 */
245 if (sh_type == SHT_DYNSYM) {
246 ver_name = elf_get_vername(iter, sym->ver);
247 if (!ver_name)
248 return false;
249 return strcmp(ver_name, lib_ver) == 0;
250 }
251
252 /* For normal symbols, it is already in form of func@LIB_VER */
253 return strcmp(sym->name, name) == 0;
254 }
255
256 /* Transform symbol's virtual address (absolute for binaries and relative
257 * for shared libs) into file offset, which is what kernel is expecting
258 * for uprobe/uretprobe attachment.
259 * See Documentation/trace/uprobetracer.rst for more details. This is done
260 * by looking up symbol's containing section's header and using iter's virtual
261 * address (sh_addr) and corresponding file offset (sh_offset) to transform
262 * sym.st_value (virtual address) into desired final file offset.
263 */
elf_sym_offset(struct elf_sym * sym)264 static unsigned long elf_sym_offset(struct elf_sym *sym)
265 {
266 return sym->sym.st_value - sym->sh.sh_addr + sym->sh.sh_offset;
267 }
268
269 /* Find offset of function name in the provided ELF object. "binary_path" is
270 * the path to the ELF binary represented by "elf", and only used for error
271 * reporting matters. "name" matches symbol name or name@@LIB for library
272 * functions.
273 */
elf_find_func_offset(Elf * elf,const char * binary_path,const char * name)274 long elf_find_func_offset(Elf *elf, const char *binary_path, const char *name)
275 {
276 int i, sh_types[2] = { SHT_DYNSYM, SHT_SYMTAB };
277 const char *at_symbol, *lib_ver;
278 bool is_shared_lib;
279 long ret = -ENOENT;
280 size_t name_len;
281 GElf_Ehdr ehdr;
282
283 if (!gelf_getehdr(elf, &ehdr)) {
284 pr_warn("elf: failed to get ehdr from %s: %s\n", binary_path, elf_errmsg(-1));
285 ret = -LIBBPF_ERRNO__FORMAT;
286 goto out;
287 }
288 /* for shared lib case, we do not need to calculate relative offset */
289 is_shared_lib = ehdr.e_type == ET_DYN;
290
291 /* Does name specify "@@LIB_VER" or "@LIB_VER" ? */
292 at_symbol = strchr(name, '@');
293 if (at_symbol) {
294 name_len = at_symbol - name;
295 /* skip second @ if it's @@LIB_VER case */
296 if (at_symbol[1] == '@')
297 at_symbol++;
298 lib_ver = at_symbol + 1;
299 } else {
300 name_len = strlen(name);
301 lib_ver = NULL;
302 }
303
304 /* Search SHT_DYNSYM, SHT_SYMTAB for symbol. This search order is used because if
305 * a binary is stripped, it may only have SHT_DYNSYM, and a fully-statically
306 * linked binary may not have SHT_DYMSYM, so absence of a section should not be
307 * reported as a warning/error.
308 */
309 for (i = 0; i < ARRAY_SIZE(sh_types); i++) {
310 struct elf_sym_iter iter;
311 struct elf_sym *sym;
312 int last_bind = -1;
313 int cur_bind;
314
315 ret = elf_sym_iter_new(&iter, elf, binary_path, sh_types[i], STT_FUNC);
316 if (ret == -ENOENT)
317 continue;
318 if (ret)
319 goto out;
320
321 while ((sym = elf_sym_iter_next(&iter))) {
322 if (!symbol_match(&iter, sh_types[i], sym, name, name_len, lib_ver))
323 continue;
324
325 cur_bind = GELF_ST_BIND(sym->sym.st_info);
326
327 if (ret > 0) {
328 /* handle multiple matches */
329 if (elf_sym_offset(sym) == ret) {
330 /* same offset, no problem */
331 continue;
332 } else if (last_bind != STB_WEAK && cur_bind != STB_WEAK) {
333 /* Only accept one non-weak bind. */
334 pr_warn("elf: ambiguous match for '%s', '%s' in '%s'\n",
335 sym->name, name, binary_path);
336 ret = -LIBBPF_ERRNO__FORMAT;
337 goto out;
338 } else if (cur_bind == STB_WEAK) {
339 /* already have a non-weak bind, and
340 * this is a weak bind, so ignore.
341 */
342 continue;
343 }
344 }
345
346 ret = elf_sym_offset(sym);
347 last_bind = cur_bind;
348 }
349 if (ret > 0)
350 break;
351 }
352
353 if (ret > 0) {
354 pr_debug("elf: symbol address match for '%s' in '%s': 0x%lx\n", name, binary_path,
355 ret);
356 } else {
357 if (ret == 0) {
358 pr_warn("elf: '%s' is 0 in symtab for '%s': %s\n", name, binary_path,
359 is_shared_lib ? "should not be 0 in a shared library" :
360 "try using shared library path instead");
361 ret = -ENOENT;
362 } else {
363 pr_warn("elf: failed to find symbol '%s' in '%s'\n", name, binary_path);
364 }
365 }
366 out:
367 return ret;
368 }
369
370 /* Find offset of function name in ELF object specified by path. "name" matches
371 * symbol name or name@@LIB for library functions.
372 */
elf_find_func_offset_from_file(const char * binary_path,const char * name)373 long elf_find_func_offset_from_file(const char *binary_path, const char *name)
374 {
375 struct elf_fd elf_fd;
376 long ret = -ENOENT;
377
378 ret = elf_open(binary_path, &elf_fd);
379 if (ret)
380 return ret;
381 ret = elf_find_func_offset(elf_fd.elf, binary_path, name);
382 elf_close(&elf_fd);
383 return ret;
384 }
385
386 struct symbol {
387 const char *name;
388 int bind;
389 int idx;
390 };
391
symbol_cmp(const void * a,const void * b)392 static int symbol_cmp(const void *a, const void *b)
393 {
394 const struct symbol *sym_a = a;
395 const struct symbol *sym_b = b;
396
397 return strcmp(sym_a->name, sym_b->name);
398 }
399
400 /*
401 * Return offsets in @poffsets for symbols specified in @syms array argument.
402 * On success returns 0 and offsets are returned in allocated array with @cnt
403 * size, that needs to be released by the caller.
404 */
elf_resolve_syms_offsets(const char * binary_path,int cnt,const char ** syms,unsigned long ** poffsets)405 int elf_resolve_syms_offsets(const char *binary_path, int cnt,
406 const char **syms, unsigned long **poffsets)
407 {
408 int sh_types[2] = { SHT_DYNSYM, SHT_SYMTAB };
409 int err = 0, i, cnt_done = 0;
410 unsigned long *offsets;
411 struct symbol *symbols;
412 struct elf_fd elf_fd;
413
414 err = elf_open(binary_path, &elf_fd);
415 if (err)
416 return err;
417
418 offsets = calloc(cnt, sizeof(*offsets));
419 symbols = calloc(cnt, sizeof(*symbols));
420
421 if (!offsets || !symbols) {
422 err = -ENOMEM;
423 goto out;
424 }
425
426 for (i = 0; i < cnt; i++) {
427 symbols[i].name = syms[i];
428 symbols[i].idx = i;
429 }
430
431 qsort(symbols, cnt, sizeof(*symbols), symbol_cmp);
432
433 for (i = 0; i < ARRAY_SIZE(sh_types); i++) {
434 struct elf_sym_iter iter;
435 struct elf_sym *sym;
436
437 err = elf_sym_iter_new(&iter, elf_fd.elf, binary_path, sh_types[i], STT_FUNC);
438 if (err == -ENOENT)
439 continue;
440 if (err)
441 goto out;
442
443 while ((sym = elf_sym_iter_next(&iter))) {
444 unsigned long sym_offset = elf_sym_offset(sym);
445 int bind = GELF_ST_BIND(sym->sym.st_info);
446 struct symbol *found, tmp = {
447 .name = sym->name,
448 };
449 unsigned long *offset;
450
451 found = bsearch(&tmp, symbols, cnt, sizeof(*symbols), symbol_cmp);
452 if (!found)
453 continue;
454
455 offset = &offsets[found->idx];
456 if (*offset > 0) {
457 /* same offset, no problem */
458 if (*offset == sym_offset)
459 continue;
460 /* handle multiple matches */
461 if (found->bind != STB_WEAK && bind != STB_WEAK) {
462 /* Only accept one non-weak bind. */
463 pr_warn("elf: ambiguous match found '%s@%lu' in '%s' previous offset %lu\n",
464 sym->name, sym_offset, binary_path, *offset);
465 err = -ESRCH;
466 goto out;
467 } else if (bind == STB_WEAK) {
468 /* already have a non-weak bind, and
469 * this is a weak bind, so ignore.
470 */
471 continue;
472 }
473 } else {
474 cnt_done++;
475 }
476 *offset = sym_offset;
477 found->bind = bind;
478 }
479 }
480
481 if (cnt != cnt_done) {
482 err = -ENOENT;
483 goto out;
484 }
485
486 *poffsets = offsets;
487
488 out:
489 free(symbols);
490 if (err)
491 free(offsets);
492 elf_close(&elf_fd);
493 return err;
494 }
495
496 /*
497 * Return offsets in @poffsets for symbols specified by @pattern argument.
498 * On success returns 0 and offsets are returned in allocated @poffsets
499 * array with the @pctn size, that needs to be released by the caller.
500 */
elf_resolve_pattern_offsets(const char * binary_path,const char * pattern,unsigned long ** poffsets,size_t * pcnt)501 int elf_resolve_pattern_offsets(const char *binary_path, const char *pattern,
502 unsigned long **poffsets, size_t *pcnt)
503 {
504 int sh_types[2] = { SHT_SYMTAB, SHT_DYNSYM };
505 unsigned long *offsets = NULL;
506 size_t cap = 0, cnt = 0;
507 struct elf_fd elf_fd;
508 int err = 0, i;
509
510 err = elf_open(binary_path, &elf_fd);
511 if (err)
512 return err;
513
514 for (i = 0; i < ARRAY_SIZE(sh_types); i++) {
515 struct elf_sym_iter iter;
516 struct elf_sym *sym;
517
518 err = elf_sym_iter_new(&iter, elf_fd.elf, binary_path, sh_types[i], STT_FUNC);
519 if (err == -ENOENT)
520 continue;
521 if (err)
522 goto out;
523
524 while ((sym = elf_sym_iter_next(&iter))) {
525 if (!glob_match(sym->name, pattern))
526 continue;
527
528 err = libbpf_ensure_mem((void **) &offsets, &cap, sizeof(*offsets),
529 cnt + 1);
530 if (err)
531 goto out;
532
533 offsets[cnt++] = elf_sym_offset(sym);
534 }
535
536 /* If we found anything in the first symbol section,
537 * do not search others to avoid duplicates.
538 */
539 if (cnt)
540 break;
541 }
542
543 if (cnt) {
544 *poffsets = offsets;
545 *pcnt = cnt;
546 } else {
547 err = -ENOENT;
548 }
549
550 out:
551 if (err)
552 free(offsets);
553 elf_close(&elf_fd);
554 return err;
555 }
556