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(§_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(¬e, 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 }