/ libpkg / pkg_elf.c
pkg_elf.c
  1  /*-
  2   * Copyright (c) 2011-2024 Baptiste Daroussin <bapt@FreeBSD.org>
  3   * Copyright (c) 2012-2013 Matthew Seaman <matthew@FreeBSD.org>
  4   *
  5   * SPDX-License-Identifier: BSD-2-Clause
  6   */
  7  
  8  #ifdef HAVE_CONFIG_H
  9  #include "pkg_config.h"
 10  #endif
 11  
 12  #if __has_include(<sys/endian.h>)
 13  #include <sys/endian.h>
 14  #elif __has_include(<endian.h>)
 15  #include <endian.h>
 16  #elif __has_include(<machine/endian.h>)
 17  #include <machine/endian.h>
 18  #endif
 19  #include <sys/types.h>
 20  #if __has_include(<sys/elf_common.h>) && !defined(__DragonFly__)
 21  #include <sys/elf_common.h>
 22  #endif
 23  #include <sys/stat.h>
 24  
 25  #include <assert.h>
 26  #include <dlfcn.h>
 27  #include <fcntl.h>
 28  #include <gelf.h>
 29  #include <libgen.h>
 30  #if __has_include(<link.h>) && !defined(__DragonFly__) && defined(HAVE_LIBELF)
 31  #include <link.h>
 32  #endif
 33  #include <paths.h>
 34  #include <stdbool.h>
 35  #include <string.h>
 36  #include <unistd.h>
 37  #ifdef HAVE_LIBELF
 38  #include <libelf.h>
 39  #endif
 40  
 41  #include <bsd_compat.h>
 42  
 43  #include "pkg.h"
 44  #include "private/pkg.h"
 45  #include "private/pkg_abi.h"
 46  #include "private/event.h"
 47  #include "private/binfmt.h"
 48  
 49  #ifndef NT_ABI_TAG
 50  #define NT_ABI_TAG 1
 51  #endif
 52  
 53  #define NT_VERSION	1
 54  #define NT_ARCH		2
 55  #define NT_GNU_ABI_TAG	1
 56  
 57  #ifndef roundup2
 58  #define roundup2(x, y)	(((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */
 59  #endif
 60  
 61  static void elf_parse_abi(Elf *elf, GElf_Ehdr *ehdr, struct pkg_abi *abi);
 62  
 63  #ifndef HAVE_ELF_NOTE
 64  typedef Elf32_Nhdr Elf_Note;
 65  #endif
 66  
 67  static int
 68  analyse_elf(struct pkg *pkg, const char *fpath, char **provided,
 69      enum pkg_shlib_flags *provided_flags)
 70  {
 71  	assert(*provided == NULL);
 72  	assert(*provided_flags == PKG_SHLIB_FLAGS_NONE);
 73  
 74  	int ret = EPKG_OK;
 75  
 76  	pkg_debug(1, "analysing elf %s", fpath);
 77  
 78  	struct stat sb;
 79  	if (lstat(fpath, &sb) != 0)
 80  		pkg_emit_errno("fstat() failed for", fpath);
 81  	/* ignore empty files */
 82  	if (sb.st_size == 0)
 83  		return (EPKG_END); /* Empty file or sym-link: no results */
 84  
 85  	int fd = open(fpath, O_RDONLY, 0);
 86  	if (fd < 0) {
 87  		return (EPKG_FATAL);
 88  	}
 89  
 90  	if (elf_version(EV_CURRENT) == EV_NONE) {
 91  		pkg_emit_error("ELF library initialization failed: %s",
 92  		    elf_errmsg(-1));
 93  		return (EPKG_FATAL);
 94  	}
 95  
 96  	Elf *e = elf_begin(fd, ELF_C_READ, NULL);
 97  	if (e == NULL) {
 98  		ret = EPKG_FATAL;
 99  		pkg_debug(1, "elf_begin() for %s failed: %s", fpath,
100  		    elf_errmsg(-1));
101  		goto cleanup;
102  	}
103  
104  	if (elf_kind(e) == ELF_K_AR) {
105  		/*
106  		 * ar archive: check if it contains ELF objects.
107  		 * WASM .a archives contain non-ELF objects and
108  		 * should not be flagged as architecture-specific.
109  		 */
110  		if (ctx.developer_mode) {
111  			Elf_Cmd cmd = ELF_C_READ;
112  			Elf *member;
113  			while ((member = elf_begin(fd, cmd, e)) != NULL) {
114  				if (elf_kind(member) == ELF_K_ELF) {
115  					pkg->flags |= PKG_CONTAINS_STATIC_LIBS;
116  					elf_end(member);
117  					break;
118  				}
119  				cmd = elf_next(member);
120  				elf_end(member);
121  			}
122  		}
123  		ret = EPKG_END;
124  		goto cleanup;
125  	}
126  
127  	if (elf_kind(e) != ELF_K_ELF) {
128  		/* Not an elf file: no results */
129  		ret = EPKG_END;
130  		pkg_debug(1, "not an elf");
131  		goto cleanup;
132  	}
133  
134  	if (ctx.developer_mode)
135  		pkg->flags |= PKG_CONTAINS_ELF_OBJECTS;
136  
137  	GElf_Ehdr elfhdr;
138  	if (gelf_getehdr(e, &elfhdr) == NULL) {
139  		ret = EPKG_WARN;
140  		pkg_debug(1, "getehdr() failed: %s.", elf_errmsg(-1));
141  		goto cleanup;
142  	}
143  
144  	if (elfhdr.e_type != ET_DYN && elfhdr.e_type != ET_EXEC &&
145  	    elfhdr.e_type != ET_REL) {
146  		pkg_debug(1, "not an elf");
147  		ret = EPKG_END;
148  		goto cleanup;
149  	}
150  
151  	/* Parse the needed information from the dynamic section header */
152  	Elf_Scn *scn = NULL;
153  	Elf_Scn *dynamic = NULL;
154  	size_t numdyn = 0;
155  	size_t sh_link = 0;
156  	while ((scn = elf_nextscn(e, scn)) != NULL) {
157  		GElf_Shdr shdr;
158  		if (gelf_getshdr(scn, &shdr) != &shdr) {
159  			ret = EPKG_FATAL;
160  			pkg_emit_error("getshdr() for %s failed: %s", fpath,
161  					elf_errmsg(-1));
162  			goto cleanup;
163  		}
164  		if (shdr.sh_type == SHT_DYNAMIC) {
165  			dynamic = scn;
166  			sh_link = shdr.sh_link;
167  			if (shdr.sh_entsize == 0) {
168  				ret = EPKG_END;
169  				goto cleanup;
170  			}
171  			numdyn = shdr.sh_size / shdr.sh_entsize;
172  			break;
173  		}
174  	}
175  
176  	if (dynamic == NULL) {
177  		ret = EPKG_END;
178  		goto cleanup; /* not a dynamically linked elf: no results */
179  	}
180  
181  	struct pkg_abi elf_abi;
182  	elf_parse_abi(e, &elfhdr, &elf_abi);
183  	if (elf_abi.arch == PKG_ARCH_UNKNOWN) {
184  		ret = EPKG_END;
185  		goto cleanup;
186  	}
187  
188  	if (elf_abi.os == PKG_OS_UNKNOWN) {
189  		/* It is necessary to fall back to checking the ELF header if elf_parse_abi()
190  		 * was not able to determine the OS due to missing ELF notes. However, we
191  		 * only do this fallback when analyzing for shlibs rather than directly in
192  		 * elf_parse_abi() because we cannot determine the version without ELF notes.
193  		 * Since we do not need to check the osversion when analyzing shlibs, the
194  		 * fallback is fine here.
195  		 */
196  		if (elfhdr.e_ident[EI_OSABI] == ELFOSABI_FREEBSD) {
197  			elf_abi.os = PKG_OS_FREEBSD;
198  		} else if (ctx.abi.os == PKG_OS_LINUX || ctx.abi.os == PKG_OS_FREEBSD) {
199  			/* There is no reliable way to identify shared libraries targeting Linux.
200  			 * It would be possible to reliably identify Linux executables by checking
201  			 * the dynamic linker path in DT_INTERP. Shared libraries however do not
202  			 * have DT_INTERP set.
203  			 *
204  			 * Reading the notes section for NT_GNU_ABI_TAG is not sufficient either
205  			 * as this is only required for executables, not shared libraries.
206  			 * See https://refspecs.linuxfoundation.org/LSB_1.2.0/gLSB/noteabitag.html
207  			 *
208  			 * Therefore, if pkg is targeting Linux assume that ELF files with unknown
209  			 * target OS are also targeting Linux.
210  			 *
211  			 * Furthermore, if pkg is targeting FreeBSD also assume that ELF
212  			 * files with unknown target OS are targeting Linux. This is consistent
213  			 * with the behavior of the FreeBSD kernel, which falls back to Linux by
214  			 * default if it is unable to determine the target OS of an ELF file.
215  			 * (This behavior can be overridden with a fallback_brand sysctl.)
216  			 *
217  			 * We could add a pkg option to configure the fallback OS
218  			 * in the future if necessary.
219  			 */
220  			elf_abi.os = PKG_OS_LINUX;
221  		} else {
222  			ret = EPKG_END;
223  			goto cleanup;
224  		}
225  	}
226  
227  	enum pkg_shlib_flags flags = pkg_shlib_flags_from_abi(&elf_abi);
228  	if ((flags & PKG_SHLIB_FLAGS_COMPAT_LINUX) == 0 && elf_abi.os != ctx.abi.os) {
229  		ret = EPKG_END;
230  		goto cleanup; /* Incompatible OS */
231  	}
232  	if ((flags & PKG_SHLIB_FLAGS_COMPAT_32) == 0 && elf_abi.arch != ctx.abi.arch) {
233  		ret = EPKG_END;
234  		goto cleanup; /* Incompatible architecture */
235  	}
236  
237  	Elf_Data *data = elf_getdata(dynamic, NULL);
238  	if (data == NULL) {
239  		ret = EPKG_END; /* Some error occurred, ignore this file */
240  		goto cleanup;
241  	}
242  
243  	for (size_t dynidx = 0; dynidx < numdyn; dynidx++) {
244  		GElf_Dyn *dyn, dyn_mem;
245  		if ((dyn = gelf_getdyn(data, dynidx, &dyn_mem)) == NULL) {
246  			ret = EPKG_FATAL;
247  			pkg_emit_error("getdyn() failed for %s: %s", fpath,
248  			    elf_errmsg(-1));
249  			goto cleanup;
250  		}
251  
252  		const char *shlib = elf_strptr(e, sh_link, dyn->d_un.d_val);
253  		if (shlib == NULL || *shlib == '\0') {
254  			continue;
255  		}
256  
257  		if (strncmp(shlib, "lib", 3) != 0) {
258  			continue;
259  		}
260  
261  		if (dyn->d_tag == DT_SONAME) {
262  			if (*provided != NULL) {
263  				pkg_emit_error("malformed ELF file %s has "
264  				    "multiple DT_SONAME entries", fpath);
265  				goto cleanup;
266  			}
267  			*provided = xstrdup(shlib);
268  			*provided_flags = flags;
269  		} else if (dyn->d_tag == DT_NEEDED) {
270  			/*
271  			 * some packages record fullpath to a lib
272  			 * neovim is an example, skip them for now
273  			 */
274  			if (*shlib == '/')
275  				continue;
276  			pkg_addshlib_required(pkg, shlib, flags);
277  		}
278  	}
279  
280  cleanup:
281  	if (e != NULL)
282  		elf_end(e);
283  	close(fd);
284  
285  	return (ret);
286  }
287  
288  static int
289  analyse_fpath(struct pkg *pkg, const char *fpath)
290  {
291  	const char *dot;
292  
293  	dot = strrchr(fpath, '.');
294  
295  	if (dot == NULL)	/* No extension */
296  		return (EPKG_OK);
297  
298  	if ((dot[1] == 'l' && dot[2] == 'a' && dot[3] == '\0'))
299  		pkg->flags |= PKG_CONTAINS_LA;
300  
301  	return (EPKG_OK);
302  }
303  
304  static enum pkg_arch
305  aeabi_parse_arm_attributes(void *data, size_t length)
306  {
307  	uint32_t sect_len;
308  	uint8_t *section = data;
309  
310  #define	MOVE(len) do {		\
311  	assert(length >= (len)); \
312  	section += (len);	\
313  	length -= (len);	\
314  } while (0)
315  
316  	if (length == 0 || *section != 'A')
317  		return (PKG_ARCH_UNKNOWN);
318  	MOVE(1);
319  
320  	/* Read the section length */
321  	if (length < sizeof(sect_len))
322  		return (PKG_ARCH_UNKNOWN);
323  	memcpy(&sect_len, section, sizeof(sect_len));
324  
325  	/*
326  	 * The section length should be no longer than the section it is within
327  	 */
328  	if (sect_len > length)
329  		return (PKG_ARCH_UNKNOWN);
330  
331  	MOVE(sizeof(sect_len));
332  
333  	/* Skip the vendor name */
334  	while (length != 0) {
335  		if (*section == '\0')
336  			break;
337  		MOVE(1);
338  	}
339  	if (length == 0)
340  		return (PKG_ARCH_UNKNOWN);
341  	MOVE(1);
342  
343  	while (length != 0) {
344  		uint32_t tag_length;
345  
346  		switch(*section) {
347  		case 1: /* Tag_File */
348  			MOVE(1);
349  			if (length < sizeof(tag_length))
350  				return (PKG_ARCH_UNKNOWN);
351  			memcpy(&tag_length, section, sizeof(tag_length));
352  			break;
353  		case 2: /* Tag_Section */
354  		case 3: /* Tag_Symbol */
355  		default:
356  			return (PKG_ARCH_UNKNOWN);
357  		}
358  		/* At least space for the tag and size */
359  		if (tag_length <= 5)
360  			return (PKG_ARCH_UNKNOWN);
361  		tag_length--;
362  		/* Check the tag fits */
363  		if (tag_length > length)
364  			return (PKG_ARCH_UNKNOWN);
365  
366  #define	MOVE_TAG(len) do {		\
367  	assert(tag_length >= (len));	\
368  	MOVE(len);			\
369  	tag_length -= (len);		\
370  } while(0)
371  
372  		MOVE(sizeof(tag_length));
373  		tag_length -= sizeof(tag_length);
374  
375  		while (tag_length != 0) {
376  			uint8_t tag;
377  
378  			assert(tag_length >= length);
379  
380  			tag = *section;
381  			MOVE_TAG(1);
382  
383  			/*
384  			 * These tag values come from:
385  			 *
386  			 * Addenda to, and Errata in, the ABI for the
387  			 * ARM Architecture. Release 2.08, section 2.3.
388  			 */
389  			if (tag == 6) { /* == Tag_CPU_arch */
390  				uint8_t val;
391  
392  				val = *section;
393  				/*
394  				 * We don't support values that require
395  				 * more than one byte.
396  				 */
397  				if (val & (1 << 7))
398  					return (PKG_ARCH_UNKNOWN);
399  
400  				/* We have an ARMv4 or ARMv5 */
401  				if (val <= 5)
402  					return (PKG_ARCH_UNKNOWN);
403  				else if (val == 6) /* We have an ARMv6 */
404  					return (PKG_ARCH_ARMV6);
405  				else /* We have an ARMv7+ */
406  					return (PKG_ARCH_ARMV7);
407  			} else if (tag == 4 || tag == 5 || tag == 32 ||
408  			    tag == 65 || tag == 67) {
409  				while (*section != '\0' && length != 0)
410  					MOVE_TAG(1);
411  				if (tag_length == 0)
412  					return (PKG_ARCH_UNKNOWN);
413  				/* Skip the last byte */
414  				MOVE_TAG(1);
415  			} else if ((tag >= 7 && tag <= 31) || tag == 34 ||
416  			    tag == 36 || tag == 38 || tag == 42 || tag == 44 ||
417  			    tag == 64 || tag == 66 || tag == 68 || tag == 70) {
418  				/* Skip the uleb128 data */
419  				while (*section & (1 << 7) && length != 0)
420  					MOVE_TAG(1);
421  				if (tag_length == 0)
422  					return (PKG_ARCH_UNKNOWN);
423  				/* Skip the last byte */
424  				MOVE_TAG(1);
425  			} else
426  				return (PKG_ARCH_UNKNOWN);
427  #undef MOVE_TAG
428  		}
429  
430  		break;
431  	}
432  	return (PKG_ARCH_UNKNOWN);
433  #undef MOVE
434  }
435  
436  static enum pkg_arch
437  elf_parse_arch(Elf *elf, GElf_Ehdr *ehdr)
438  {
439  	switch (ehdr->e_machine) {
440  	case EM_386:
441  		return (PKG_ARCH_I386);
442  	case EM_X86_64:
443  		return (PKG_ARCH_AMD64);
444  	case EM_AARCH64:
445  		return (PKG_ARCH_AARCH64);
446  	case EM_ARM:
447  		/* Only support EABI */
448  		if ((ehdr->e_flags & EF_ARM_EABIMASK) == 0) {
449  			return (PKG_ARCH_UNKNOWN);
450  		}
451  
452  		size_t shstrndx;
453  		elf_getshdrstrndx(elf, &shstrndx);
454  
455  		GElf_Shdr shdr;
456  		Elf_Scn *scn = NULL;
457  		while ((scn = elf_nextscn(elf, scn)) != NULL) {
458  			if (gelf_getshdr(scn, &shdr) != &shdr) {
459  				break;
460  			}
461  			const char *sh_name = elf_strptr(elf, shstrndx, shdr.sh_name);
462  			if (sh_name == NULL) {
463  				continue;
464  			}
465  			if (STREQ(".ARM.attributes", sh_name)) {
466  				Elf_Data *data = elf_getdata(scn, NULL);
467  				return (aeabi_parse_arm_attributes(data->d_buf, data->d_size));
468  			}
469  		}
470  		break;
471  	case EM_PPC:
472  		return (PKG_ARCH_POWERPC);
473  	case EM_PPC64:
474  		switch (ehdr->e_ident[EI_DATA]) {
475  		case ELFDATA2MSB:
476  			return (PKG_ARCH_POWERPC64);
477  		case ELFDATA2LSB:
478  			return (PKG_ARCH_POWERPC64LE);
479  		}
480  		break;
481  	case EM_RISCV:
482  		switch (ehdr->e_ident[EI_CLASS]) {
483  		case ELFCLASS32:
484  			return (PKG_ARCH_RISCV32);
485  		case ELFCLASS64:
486  			return (PKG_ARCH_RISCV64);
487  		}
488  		break;
489  	}
490  
491  	return (PKG_ARCH_UNKNOWN);
492  }
493  
494  /* Returns true if the OS and version were successfully parsed */
495  static bool
496  elf_note_analyse(Elf_Data *data, GElf_Ehdr *elfhdr, struct pkg_abi *abi)
497  {
498  	Elf_Note note;
499  	char *src;
500  	uint32_t gnu_abi_tag[4];
501  	int note_ost[6] = {
502  		PKG_OS_LINUX,
503  		PKG_OS_UNKNOWN, /* GNU Hurd */
504  		PKG_OS_UNKNOWN, /* Solaris */
505  		PKG_OS_FREEBSD,
506  		PKG_OS_NETBSD,
507  		PKG_OS_UNKNOWN, /* Syllable */
508  	};
509  	uint32_t version = 0;
510  	int version_style = 1;
511  
512  	src = data->d_buf;
513  
514  	while ((uintptr_t)src < ((uintptr_t)data->d_buf + data->d_size)) {
515  		memcpy(&note, src, sizeof(Elf_Note));
516  		src += sizeof(Elf_Note);
517  		if ((strncmp ((const char *) src, "FreeBSD", note.n_namesz) == 0) ||
518  		    (strncmp ((const char *) src, "DragonFly", note.n_namesz) == 0) ||
519  		    (strncmp ((const char *) src, "NetBSD", note.n_namesz) == 0) ||
520  		    (note.n_namesz == 0)) {
521  			if (note.n_type == NT_VERSION) {
522  				version_style = 1;
523  				break;
524  			}
525  		}
526  		if (strncmp ((const char *) src, "GNU", note.n_namesz) == 0) {
527  			if (note.n_type == NT_GNU_ABI_TAG) {
528  				version_style = 2;
529  				break;
530  			}
531  		}
532  		src += roundup2(note.n_namesz + note.n_descsz, 4);
533  	}
534  	if ((uintptr_t)src >= ((uintptr_t)data->d_buf + data->d_size)) {
535  		return (false);
536  	}
537  	if (version_style == 2) {
538  		/*
539  		 * NT_GNU_ABI_TAG
540  		 * Operating system (OS) ABI information.  The
541  		 * desc field contains 4 words:
542  		 * word 0: OS descriptor (ELF_NOTE_OS_LINUX, ELF_NOTE_OS_GNU, etc)
543  		 * word 1: major version of the ABI
544  		 * word 2: minor version of the ABI
545  		 * word 3: subminor version of the ABI
546  		 */
547  		src += roundup2(note.n_namesz, 4);
548  		if (elfhdr->e_ident[EI_DATA] == ELFDATA2MSB) {
549  			for (int wdndx = 0; wdndx < 4; wdndx++) {
550  				gnu_abi_tag[wdndx] = be32dec(src);
551  				src += 4;
552  			}
553  		} else {
554  			for (int wdndx = 0; wdndx < 4; wdndx++) {
555  				gnu_abi_tag[wdndx] = le32dec(src);
556  				src += 4;
557  			}
558  		}
559  		if (gnu_abi_tag[0] < 6) {
560  			abi->os= note_ost[gnu_abi_tag[0]];
561  		} else {
562  			abi->os = PKG_OS_UNKNOWN;
563  		}
564  	} else {
565  		if (note.n_namesz == 0) {
566  			abi->os = PKG_OS_UNKNOWN;
567  		} else {
568  			if (STREQ(src, "FreeBSD"))
569  				abi->os = PKG_OS_FREEBSD;
570  			else if (STREQ(src, "DragonFly"))
571  				abi->os = PKG_OS_DRAGONFLY;
572  			else if (STREQ(src, "NetBSD"))
573  				abi->os = PKG_OS_NETBSD;
574  		}
575  		src += roundup2(note.n_namesz, 4);
576  		if (elfhdr->e_ident[EI_DATA] == ELFDATA2MSB)
577  			version = be32dec(src);
578  		else
579  			version = le32dec(src);
580  	}
581  
582  	if (version_style == 2) {
583  		if (abi->os == PKG_OS_LINUX) {
584  			abi->major = gnu_abi_tag[1];
585  			abi->minor = gnu_abi_tag[2];
586  		} else {
587  			abi->major = gnu_abi_tag[1];
588  			abi->minor = gnu_abi_tag[2];
589  			abi->patch = gnu_abi_tag[3];
590  		}
591  	} else {
592  		switch (abi->os) {
593  		case PKG_OS_UNKNOWN:
594  			break;
595  		case PKG_OS_FREEBSD:
596  			pkg_abi_set_freebsd_osversion(abi, version);
597  			break;
598  		case PKG_OS_DRAGONFLY:
599  			abi->major = version / 100000;
600  			abi->minor = (((version / 100 % 1000)+1)/2)*2;
601  			break;
602  		case PKG_OS_NETBSD:
603  			abi->major = (version + 1000000) / 100000000;
604  			break;
605  		default:
606  			assert(0);
607  		}
608  	}
609  
610  	return (true);
611  }
612  
613  static void
614  elf_parse_abi(Elf *elf, GElf_Ehdr *ehdr, struct pkg_abi *abi)
615  {
616  	*abi = (struct pkg_abi){0};
617  
618  	Elf_Scn *scn = NULL;
619  	while ((scn = elf_nextscn(elf, scn)) != NULL) {
620  		GElf_Shdr shdr;
621  		if (gelf_getshdr(scn, &shdr) != &shdr) {
622  			pkg_emit_error("getshdr() failed: %s.", elf_errmsg(-1));
623  			return;
624  		}
625  
626  		if (shdr.sh_type == SHT_NOTE) {
627  			Elf_Data *data = elf_getdata(scn, NULL);
628  			/*
629  			 * loop over all the note section and override what
630  			 * should be overridden if any
631  			 */
632  			if (data == NULL)
633  				continue;
634  			elf_note_analyse(data, ehdr, abi);
635  		}
636  	}
637  
638  	abi->arch = elf_parse_arch(elf, ehdr);
639  }
640  
641  int
642  pkg_elf_abi_from_fd(int fd, struct pkg_abi *abi)
643  {
644  	Elf *elf = NULL;
645  	GElf_Ehdr elfhdr;
646  	int ret = EPKG_OK;
647  
648  	if (elf_version(EV_CURRENT) == EV_NONE) {
649  		pkg_emit_error("ELF library initialization failed: %s",
650  		    elf_errmsg(-1));
651  		return (EPKG_FATAL);
652  	}
653  
654  	if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
655  		ret = EPKG_FATAL;
656  		pkg_emit_error("elf_begin() failed: %s.", elf_errmsg(-1));
657  		goto cleanup;
658  	}
659  
660  	if (gelf_getehdr(elf, &elfhdr) == NULL) {
661  		ret = EPKG_WARN;
662  		pkg_debug(1, "getehdr() failed: %s.", elf_errmsg(-1));
663  		goto cleanup;
664  	}
665  
666  	elf_parse_abi(elf, &elfhdr, abi);
667  
668  	if (abi->os == PKG_OS_UNKNOWN) {
669  		ret = EPKG_FATAL;
670  		pkg_emit_error("failed to determine the operating system");
671  		goto cleanup;
672  	}
673  
674  	if (abi->arch == PKG_ARCH_UNKNOWN) {
675  		ret = EPKG_FATAL;
676  		pkg_emit_error("failed to determine the architecture");
677  		goto cleanup;
678  	}
679  
680  cleanup:
681  	if (elf != NULL)
682  		elf_end(elf);
683  	return (ret);
684  }
685  
686  int pkg_analyse_init_elf(__unused const char* stage) {
687  	if (elf_version(EV_CURRENT) == EV_NONE)
688  		return (EPKG_FATAL);
689  	return (EPKG_OK);
690  }
691  
692  int pkg_analyse_elf(const bool developer_mode, struct pkg *pkg,
693      const char *fpath, char **provided, enum pkg_shlib_flags *provided_flags)
694  {
695  	assert(*provided == NULL);
696  	assert(*provided_flags == PKG_SHLIB_FLAGS_NONE);
697  
698  	int ret = analyse_elf(pkg, fpath, provided, provided_flags);
699  	if (developer_mode) {
700  		if (ret != EPKG_OK && ret != EPKG_END) {
701  			return EPKG_WARN;
702  		}
703  		analyse_fpath(pkg, fpath);
704  	}
705  	return ret;
706  }
707  
708  int pkg_analyse_close_elf(void) {
709  	return EPKG_OK;
710  }