cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

main.c (16295B)


      1// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
      2
      3/*
      4 * resolve_btfids scans Elf object for .BTF_ids section and resolves
      5 * its symbols with BTF ID values.
      6 *
      7 * Each symbol points to 4 bytes data and is expected to have
      8 * following name syntax:
      9 *
     10 * __BTF_ID__<type>__<symbol>[__<id>]
     11 *
     12 * type is:
     13 *
     14 *   func    - lookup BTF_KIND_FUNC symbol with <symbol> name
     15 *             and store its ID into the data:
     16 *
     17 *             __BTF_ID__func__vfs_close__1:
     18 *             .zero 4
     19 *
     20 *   struct  - lookup BTF_KIND_STRUCT symbol with <symbol> name
     21 *             and store its ID into the data:
     22 *
     23 *             __BTF_ID__struct__sk_buff__1:
     24 *             .zero 4
     25 *
     26 *   union   - lookup BTF_KIND_UNION symbol with <symbol> name
     27 *             and store its ID into the data:
     28 *
     29 *             __BTF_ID__union__thread_union__1:
     30 *             .zero 4
     31 *
     32 *   typedef - lookup BTF_KIND_TYPEDEF symbol with <symbol> name
     33 *             and store its ID into the data:
     34 *
     35 *             __BTF_ID__typedef__pid_t__1:
     36 *             .zero 4
     37 *
     38 *   set     - store symbol size into first 4 bytes and sort following
     39 *             ID list
     40 *
     41 *             __BTF_ID__set__list:
     42 *             .zero 4
     43 *             list:
     44 *             __BTF_ID__func__vfs_getattr__3:
     45 *             .zero 4
     46 *             __BTF_ID__func__vfs_fallocate__4:
     47 *             .zero 4
     48 */
     49
     50#define  _GNU_SOURCE
     51#include <stdio.h>
     52#include <string.h>
     53#include <unistd.h>
     54#include <stdlib.h>
     55#include <libelf.h>
     56#include <gelf.h>
     57#include <sys/stat.h>
     58#include <fcntl.h>
     59#include <errno.h>
     60#include <linux/rbtree.h>
     61#include <linux/zalloc.h>
     62#include <linux/err.h>
     63#include <bpf/btf.h>
     64#include <bpf/libbpf.h>
     65#include <parse-options.h>
     66
     67#define BTF_IDS_SECTION	".BTF_ids"
     68#define BTF_ID		"__BTF_ID__"
     69
     70#define BTF_STRUCT	"struct"
     71#define BTF_UNION	"union"
     72#define BTF_TYPEDEF	"typedef"
     73#define BTF_FUNC	"func"
     74#define BTF_SET		"set"
     75
     76#define ADDR_CNT	100
     77
     78struct btf_id {
     79	struct rb_node	 rb_node;
     80	char		*name;
     81	union {
     82		int	 id;
     83		int	 cnt;
     84	};
     85	int		 addr_cnt;
     86	bool		 is_set;
     87	Elf64_Addr	 addr[ADDR_CNT];
     88};
     89
     90struct object {
     91	const char *path;
     92	const char *btf;
     93	const char *base_btf_path;
     94
     95	struct {
     96		int		 fd;
     97		Elf		*elf;
     98		Elf_Data	*symbols;
     99		Elf_Data	*idlist;
    100		int		 symbols_shndx;
    101		int		 idlist_shndx;
    102		size_t		 strtabidx;
    103		unsigned long	 idlist_addr;
    104	} efile;
    105
    106	struct rb_root	sets;
    107	struct rb_root	structs;
    108	struct rb_root	unions;
    109	struct rb_root	typedefs;
    110	struct rb_root	funcs;
    111
    112	int nr_funcs;
    113	int nr_structs;
    114	int nr_unions;
    115	int nr_typedefs;
    116};
    117
    118static int verbose;
    119
    120static int eprintf(int level, int var, const char *fmt, ...)
    121{
    122	va_list args;
    123	int ret = 0;
    124
    125	if (var >= level) {
    126		va_start(args, fmt);
    127		ret = vfprintf(stderr, fmt, args);
    128		va_end(args);
    129	}
    130	return ret;
    131}
    132
    133#ifndef pr_fmt
    134#define pr_fmt(fmt) fmt
    135#endif
    136
    137#define pr_debug(fmt, ...) \
    138	eprintf(1, verbose, pr_fmt(fmt), ##__VA_ARGS__)
    139#define pr_debugN(n, fmt, ...) \
    140	eprintf(n, verbose, pr_fmt(fmt), ##__VA_ARGS__)
    141#define pr_debug2(fmt, ...) pr_debugN(2, pr_fmt(fmt), ##__VA_ARGS__)
    142#define pr_err(fmt, ...) \
    143	eprintf(0, verbose, pr_fmt(fmt), ##__VA_ARGS__)
    144#define pr_info(fmt, ...) \
    145	eprintf(0, verbose, pr_fmt(fmt), ##__VA_ARGS__)
    146
    147static bool is_btf_id(const char *name)
    148{
    149	return name && !strncmp(name, BTF_ID, sizeof(BTF_ID) - 1);
    150}
    151
    152static struct btf_id *btf_id__find(struct rb_root *root, const char *name)
    153{
    154	struct rb_node *p = root->rb_node;
    155	struct btf_id *id;
    156	int cmp;
    157
    158	while (p) {
    159		id = rb_entry(p, struct btf_id, rb_node);
    160		cmp = strcmp(id->name, name);
    161		if (cmp < 0)
    162			p = p->rb_left;
    163		else if (cmp > 0)
    164			p = p->rb_right;
    165		else
    166			return id;
    167	}
    168	return NULL;
    169}
    170
    171static struct btf_id *
    172btf_id__add(struct rb_root *root, char *name, bool unique)
    173{
    174	struct rb_node **p = &root->rb_node;
    175	struct rb_node *parent = NULL;
    176	struct btf_id *id;
    177	int cmp;
    178
    179	while (*p != NULL) {
    180		parent = *p;
    181		id = rb_entry(parent, struct btf_id, rb_node);
    182		cmp = strcmp(id->name, name);
    183		if (cmp < 0)
    184			p = &(*p)->rb_left;
    185		else if (cmp > 0)
    186			p = &(*p)->rb_right;
    187		else
    188			return unique ? NULL : id;
    189	}
    190
    191	id = zalloc(sizeof(*id));
    192	if (id) {
    193		pr_debug("adding symbol %s\n", name);
    194		id->name = name;
    195		rb_link_node(&id->rb_node, parent, p);
    196		rb_insert_color(&id->rb_node, root);
    197	}
    198	return id;
    199}
    200
    201static char *get_id(const char *prefix_end)
    202{
    203	/*
    204	 * __BTF_ID__func__vfs_truncate__0
    205	 * prefix_end =  ^
    206	 * pos        =    ^
    207	 */
    208	int len = strlen(prefix_end);
    209	int pos = sizeof("__") - 1;
    210	char *p, *id;
    211
    212	if (pos >= len)
    213		return NULL;
    214
    215	id = strdup(prefix_end + pos);
    216	if (id) {
    217		/*
    218		 * __BTF_ID__func__vfs_truncate__0
    219		 * id =            ^
    220		 *
    221		 * cut the unique id part
    222		 */
    223		p = strrchr(id, '_');
    224		p--;
    225		if (*p != '_') {
    226			free(id);
    227			return NULL;
    228		}
    229		*p = '\0';
    230	}
    231	return id;
    232}
    233
    234static struct btf_id *add_set(struct object *obj, char *name)
    235{
    236	/*
    237	 * __BTF_ID__set__name
    238	 * name =    ^
    239	 * id   =         ^
    240	 */
    241	char *id = name + sizeof(BTF_SET "__") - 1;
    242	int len = strlen(name);
    243
    244	if (id >= name + len) {
    245		pr_err("FAILED to parse set name: %s\n", name);
    246		return NULL;
    247	}
    248
    249	return btf_id__add(&obj->sets, id, true);
    250}
    251
    252static struct btf_id *add_symbol(struct rb_root *root, char *name, size_t size)
    253{
    254	char *id;
    255
    256	id = get_id(name + size);
    257	if (!id) {
    258		pr_err("FAILED to parse symbol name: %s\n", name);
    259		return NULL;
    260	}
    261
    262	return btf_id__add(root, id, false);
    263}
    264
    265/* Older libelf.h and glibc elf.h might not yet define the ELF compression types. */
    266#ifndef SHF_COMPRESSED
    267#define SHF_COMPRESSED (1 << 11) /* Section with compressed data. */
    268#endif
    269
    270/*
    271 * The data of compressed section should be aligned to 4
    272 * (for 32bit) or 8 (for 64 bit) bytes. The binutils ld
    273 * sets sh_addralign to 1, which makes libelf fail with
    274 * misaligned section error during the update:
    275 *    FAILED elf_update(WRITE): invalid section alignment
    276 *
    277 * While waiting for ld fix, we fix the compressed sections
    278 * sh_addralign value manualy.
    279 */
    280static int compressed_section_fix(Elf *elf, Elf_Scn *scn, GElf_Shdr *sh)
    281{
    282	int expected = gelf_getclass(elf) == ELFCLASS32 ? 4 : 8;
    283
    284	if (!(sh->sh_flags & SHF_COMPRESSED))
    285		return 0;
    286
    287	if (sh->sh_addralign == expected)
    288		return 0;
    289
    290	pr_debug2(" - fixing wrong alignment sh_addralign %u, expected %u\n",
    291		  sh->sh_addralign, expected);
    292
    293	sh->sh_addralign = expected;
    294
    295	if (gelf_update_shdr(scn, sh) == 0) {
    296		pr_err("FAILED cannot update section header: %s\n",
    297			elf_errmsg(-1));
    298		return -1;
    299	}
    300	return 0;
    301}
    302
    303static int elf_collect(struct object *obj)
    304{
    305	Elf_Scn *scn = NULL;
    306	size_t shdrstrndx;
    307	int idx = 0;
    308	Elf *elf;
    309	int fd;
    310
    311	fd = open(obj->path, O_RDWR, 0666);
    312	if (fd == -1) {
    313		pr_err("FAILED cannot open %s: %s\n",
    314			obj->path, strerror(errno));
    315		return -1;
    316	}
    317
    318	elf_version(EV_CURRENT);
    319
    320	elf = elf_begin(fd, ELF_C_RDWR_MMAP, NULL);
    321	if (!elf) {
    322		close(fd);
    323		pr_err("FAILED cannot create ELF descriptor: %s\n",
    324			elf_errmsg(-1));
    325		return -1;
    326	}
    327
    328	obj->efile.fd  = fd;
    329	obj->efile.elf = elf;
    330
    331	elf_flagelf(elf, ELF_C_SET, ELF_F_LAYOUT);
    332
    333	if (elf_getshdrstrndx(elf, &shdrstrndx) != 0) {
    334		pr_err("FAILED cannot get shdr str ndx\n");
    335		return -1;
    336	}
    337
    338	/*
    339	 * Scan all the elf sections and look for save data
    340	 * from .BTF_ids section and symbols.
    341	 */
    342	while ((scn = elf_nextscn(elf, scn)) != NULL) {
    343		Elf_Data *data;
    344		GElf_Shdr sh;
    345		char *name;
    346
    347		idx++;
    348		if (gelf_getshdr(scn, &sh) != &sh) {
    349			pr_err("FAILED get section(%d) header\n", idx);
    350			return -1;
    351		}
    352
    353		name = elf_strptr(elf, shdrstrndx, sh.sh_name);
    354		if (!name) {
    355			pr_err("FAILED get section(%d) name\n", idx);
    356			return -1;
    357		}
    358
    359		data = elf_getdata(scn, 0);
    360		if (!data) {
    361			pr_err("FAILED to get section(%d) data from %s\n",
    362				idx, name);
    363			return -1;
    364		}
    365
    366		pr_debug2("section(%d) %s, size %ld, link %d, flags %lx, type=%d\n",
    367			  idx, name, (unsigned long) data->d_size,
    368			  (int) sh.sh_link, (unsigned long) sh.sh_flags,
    369			  (int) sh.sh_type);
    370
    371		if (sh.sh_type == SHT_SYMTAB) {
    372			obj->efile.symbols       = data;
    373			obj->efile.symbols_shndx = idx;
    374			obj->efile.strtabidx     = sh.sh_link;
    375		} else if (!strcmp(name, BTF_IDS_SECTION)) {
    376			obj->efile.idlist       = data;
    377			obj->efile.idlist_shndx = idx;
    378			obj->efile.idlist_addr  = sh.sh_addr;
    379		}
    380
    381		if (compressed_section_fix(elf, scn, &sh))
    382			return -1;
    383	}
    384
    385	return 0;
    386}
    387
    388static int symbols_collect(struct object *obj)
    389{
    390	Elf_Scn *scn = NULL;
    391	int n, i;
    392	GElf_Shdr sh;
    393	char *name;
    394
    395	scn = elf_getscn(obj->efile.elf, obj->efile.symbols_shndx);
    396	if (!scn)
    397		return -1;
    398
    399	if (gelf_getshdr(scn, &sh) != &sh)
    400		return -1;
    401
    402	n = sh.sh_size / sh.sh_entsize;
    403
    404	/*
    405	 * Scan symbols and look for the ones starting with
    406	 * __BTF_ID__* over .BTF_ids section.
    407	 */
    408	for (i = 0; i < n; i++) {
    409		char *prefix;
    410		struct btf_id *id;
    411		GElf_Sym sym;
    412
    413		if (!gelf_getsym(obj->efile.symbols, i, &sym))
    414			return -1;
    415
    416		if (sym.st_shndx != obj->efile.idlist_shndx)
    417			continue;
    418
    419		name = elf_strptr(obj->efile.elf, obj->efile.strtabidx,
    420				  sym.st_name);
    421
    422		if (!is_btf_id(name))
    423			continue;
    424
    425		/*
    426		 * __BTF_ID__TYPE__vfs_truncate__0
    427		 * prefix =  ^
    428		 */
    429		prefix = name + sizeof(BTF_ID) - 1;
    430
    431		/* struct */
    432		if (!strncmp(prefix, BTF_STRUCT, sizeof(BTF_STRUCT) - 1)) {
    433			obj->nr_structs++;
    434			id = add_symbol(&obj->structs, prefix, sizeof(BTF_STRUCT) - 1);
    435		/* union  */
    436		} else if (!strncmp(prefix, BTF_UNION, sizeof(BTF_UNION) - 1)) {
    437			obj->nr_unions++;
    438			id = add_symbol(&obj->unions, prefix, sizeof(BTF_UNION) - 1);
    439		/* typedef */
    440		} else if (!strncmp(prefix, BTF_TYPEDEF, sizeof(BTF_TYPEDEF) - 1)) {
    441			obj->nr_typedefs++;
    442			id = add_symbol(&obj->typedefs, prefix, sizeof(BTF_TYPEDEF) - 1);
    443		/* func */
    444		} else if (!strncmp(prefix, BTF_FUNC, sizeof(BTF_FUNC) - 1)) {
    445			obj->nr_funcs++;
    446			id = add_symbol(&obj->funcs, prefix, sizeof(BTF_FUNC) - 1);
    447		/* set */
    448		} else if (!strncmp(prefix, BTF_SET, sizeof(BTF_SET) - 1)) {
    449			id = add_set(obj, prefix);
    450			/*
    451			 * SET objects store list's count, which is encoded
    452			 * in symbol's size, together with 'cnt' field hence
    453			 * that - 1.
    454			 */
    455			if (id) {
    456				id->cnt = sym.st_size / sizeof(int) - 1;
    457				id->is_set = true;
    458			}
    459		} else {
    460			pr_err("FAILED unsupported prefix %s\n", prefix);
    461			return -1;
    462		}
    463
    464		if (!id)
    465			return -ENOMEM;
    466
    467		if (id->addr_cnt >= ADDR_CNT) {
    468			pr_err("FAILED symbol %s crossed the number of allowed lists\n",
    469				id->name);
    470			return -1;
    471		}
    472		id->addr[id->addr_cnt++] = sym.st_value;
    473	}
    474
    475	return 0;
    476}
    477
    478static int symbols_resolve(struct object *obj)
    479{
    480	int nr_typedefs = obj->nr_typedefs;
    481	int nr_structs  = obj->nr_structs;
    482	int nr_unions   = obj->nr_unions;
    483	int nr_funcs    = obj->nr_funcs;
    484	struct btf *base_btf = NULL;
    485	int err, type_id;
    486	struct btf *btf;
    487	__u32 nr_types;
    488
    489	if (obj->base_btf_path) {
    490		base_btf = btf__parse(obj->base_btf_path, NULL);
    491		err = libbpf_get_error(base_btf);
    492		if (err) {
    493			pr_err("FAILED: load base BTF from %s: %s\n",
    494			       obj->base_btf_path, strerror(-err));
    495			return -1;
    496		}
    497	}
    498
    499	btf = btf__parse_split(obj->btf ?: obj->path, base_btf);
    500	err = libbpf_get_error(btf);
    501	if (err) {
    502		pr_err("FAILED: load BTF from %s: %s\n",
    503			obj->btf ?: obj->path, strerror(-err));
    504		goto out;
    505	}
    506
    507	err = -1;
    508	nr_types = btf__type_cnt(btf);
    509
    510	/*
    511	 * Iterate all the BTF types and search for collected symbol IDs.
    512	 */
    513	for (type_id = 1; type_id < nr_types; type_id++) {
    514		const struct btf_type *type;
    515		struct rb_root *root;
    516		struct btf_id *id;
    517		const char *str;
    518		int *nr;
    519
    520		type = btf__type_by_id(btf, type_id);
    521		if (!type) {
    522			pr_err("FAILED: malformed BTF, can't resolve type for ID %d\n",
    523				type_id);
    524			goto out;
    525		}
    526
    527		if (btf_is_func(type) && nr_funcs) {
    528			nr   = &nr_funcs;
    529			root = &obj->funcs;
    530		} else if (btf_is_struct(type) && nr_structs) {
    531			nr   = &nr_structs;
    532			root = &obj->structs;
    533		} else if (btf_is_union(type) && nr_unions) {
    534			nr   = &nr_unions;
    535			root = &obj->unions;
    536		} else if (btf_is_typedef(type) && nr_typedefs) {
    537			nr   = &nr_typedefs;
    538			root = &obj->typedefs;
    539		} else
    540			continue;
    541
    542		str = btf__name_by_offset(btf, type->name_off);
    543		if (!str) {
    544			pr_err("FAILED: malformed BTF, can't resolve name for ID %d\n",
    545				type_id);
    546			goto out;
    547		}
    548
    549		id = btf_id__find(root, str);
    550		if (id) {
    551			if (id->id) {
    552				pr_info("WARN: multiple IDs found for '%s': %d, %d - using %d\n",
    553					str, id->id, type_id, id->id);
    554			} else {
    555				id->id = type_id;
    556				(*nr)--;
    557			}
    558		}
    559	}
    560
    561	err = 0;
    562out:
    563	btf__free(base_btf);
    564	btf__free(btf);
    565	return err;
    566}
    567
    568static int id_patch(struct object *obj, struct btf_id *id)
    569{
    570	Elf_Data *data = obj->efile.idlist;
    571	int *ptr = data->d_buf;
    572	int i;
    573
    574	if (!id->id && !id->is_set)
    575		pr_err("WARN: resolve_btfids: unresolved symbol %s\n", id->name);
    576
    577	for (i = 0; i < id->addr_cnt; i++) {
    578		unsigned long addr = id->addr[i];
    579		unsigned long idx = addr - obj->efile.idlist_addr;
    580
    581		pr_debug("patching addr %5lu: ID %7d [%s]\n",
    582			 idx, id->id, id->name);
    583
    584		if (idx >= data->d_size) {
    585			pr_err("FAILED patching index %lu out of bounds %lu\n",
    586				idx, data->d_size);
    587			return -1;
    588		}
    589
    590		idx = idx / sizeof(int);
    591		ptr[idx] = id->id;
    592	}
    593
    594	return 0;
    595}
    596
    597static int __symbols_patch(struct object *obj, struct rb_root *root)
    598{
    599	struct rb_node *next;
    600	struct btf_id *id;
    601
    602	next = rb_first(root);
    603	while (next) {
    604		id = rb_entry(next, struct btf_id, rb_node);
    605
    606		if (id_patch(obj, id))
    607			return -1;
    608
    609		next = rb_next(next);
    610	}
    611	return 0;
    612}
    613
    614static int cmp_id(const void *pa, const void *pb)
    615{
    616	const int *a = pa, *b = pb;
    617
    618	return *a - *b;
    619}
    620
    621static int sets_patch(struct object *obj)
    622{
    623	Elf_Data *data = obj->efile.idlist;
    624	int *ptr = data->d_buf;
    625	struct rb_node *next;
    626
    627	next = rb_first(&obj->sets);
    628	while (next) {
    629		unsigned long addr, idx;
    630		struct btf_id *id;
    631		int *base;
    632		int cnt;
    633
    634		id   = rb_entry(next, struct btf_id, rb_node);
    635		addr = id->addr[0];
    636		idx  = addr - obj->efile.idlist_addr;
    637
    638		/* sets are unique */
    639		if (id->addr_cnt != 1) {
    640			pr_err("FAILED malformed data for set '%s'\n",
    641				id->name);
    642			return -1;
    643		}
    644
    645		idx = idx / sizeof(int);
    646		base = &ptr[idx] + 1;
    647		cnt = ptr[idx];
    648
    649		pr_debug("sorting  addr %5lu: cnt %6d [%s]\n",
    650			 (idx + 1) * sizeof(int), cnt, id->name);
    651
    652		qsort(base, cnt, sizeof(int), cmp_id);
    653
    654		next = rb_next(next);
    655	}
    656	return 0;
    657}
    658
    659static int symbols_patch(struct object *obj)
    660{
    661	int err;
    662
    663	if (__symbols_patch(obj, &obj->structs)  ||
    664	    __symbols_patch(obj, &obj->unions)   ||
    665	    __symbols_patch(obj, &obj->typedefs) ||
    666	    __symbols_patch(obj, &obj->funcs)    ||
    667	    __symbols_patch(obj, &obj->sets))
    668		return -1;
    669
    670	if (sets_patch(obj))
    671		return -1;
    672
    673	/* Set type to ensure endian translation occurs. */
    674	obj->efile.idlist->d_type = ELF_T_WORD;
    675
    676	elf_flagdata(obj->efile.idlist, ELF_C_SET, ELF_F_DIRTY);
    677
    678	err = elf_update(obj->efile.elf, ELF_C_WRITE);
    679	if (err < 0) {
    680		pr_err("FAILED elf_update(WRITE): %s\n",
    681			elf_errmsg(-1));
    682	}
    683
    684	pr_debug("update %s for %s\n",
    685		 err >= 0 ? "ok" : "failed", obj->path);
    686	return err < 0 ? -1 : 0;
    687}
    688
    689static const char * const resolve_btfids_usage[] = {
    690	"resolve_btfids [<options>] <ELF object>",
    691	NULL
    692};
    693
    694int main(int argc, const char **argv)
    695{
    696	struct object obj = {
    697		.efile = {
    698			.idlist_shndx  = -1,
    699			.symbols_shndx = -1,
    700		},
    701		.structs  = RB_ROOT,
    702		.unions   = RB_ROOT,
    703		.typedefs = RB_ROOT,
    704		.funcs    = RB_ROOT,
    705		.sets     = RB_ROOT,
    706	};
    707	struct option btfid_options[] = {
    708		OPT_INCR('v', "verbose", &verbose,
    709			 "be more verbose (show errors, etc)"),
    710		OPT_STRING(0, "btf", &obj.btf, "BTF data",
    711			   "BTF data"),
    712		OPT_STRING('b', "btf_base", &obj.base_btf_path, "file",
    713			   "path of file providing base BTF"),
    714		OPT_END()
    715	};
    716	int err = -1;
    717
    718	argc = parse_options(argc, argv, btfid_options, resolve_btfids_usage,
    719			     PARSE_OPT_STOP_AT_NON_OPTION);
    720	if (argc != 1)
    721		usage_with_options(resolve_btfids_usage, btfid_options);
    722
    723	obj.path = argv[0];
    724
    725	if (elf_collect(&obj))
    726		goto out;
    727
    728	/*
    729	 * We did not find .BTF_ids section or symbols section,
    730	 * nothing to do..
    731	 */
    732	if (obj.efile.idlist_shndx == -1 ||
    733	    obj.efile.symbols_shndx == -1) {
    734		pr_debug("Cannot find .BTF_ids or symbols sections, nothing to do\n");
    735		err = 0;
    736		goto out;
    737	}
    738
    739	if (symbols_collect(&obj))
    740		goto out;
    741
    742	if (symbols_resolve(&obj))
    743		goto out;
    744
    745	if (symbols_patch(&obj))
    746		goto out;
    747
    748	err = 0;
    749out:
    750	if (obj.efile.elf) {
    751		elf_end(obj.efile.elf);
    752		close(obj.efile.fd);
    753	}
    754	return err;
    755}