/[gentoo-projects]/pax-utils/scanelf.c
Gentoo

Diff of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

Revision 1.171 Revision 1.188
1/* 1/*
2 * Copyright 2003-2006 Gentoo Foundation 2 * Copyright 2003-2007 Gentoo Foundation
3 * Distributed under the terms of the GNU General Public License v2 3 * Distributed under the terms of the GNU General Public License v2
4 * $Header: /var/cvsroot/gentoo-projects/pax-utils/Attic/scanelf.c,v 1.171 2007/01/09 23:01:09 vapier Exp $ 4 * $Header: /var/cvsroot/gentoo-projects/pax-utils/Attic/scanelf.c,v 1.188 2007/08/31 17:45:24 solar Exp $
5 * 5 *
6 * Copyright 2003-2006 Ned Ludd - <solar@gentoo.org> 6 * Copyright 2003-2007 Ned Ludd - <solar@gentoo.org>
7 * Copyright 2004-2006 Mike Frysinger - <vapier@gentoo.org> 7 * Copyright 2004-2007 Mike Frysinger - <vapier@gentoo.org>
8 */ 8 */
9 9
10static const char *rcsid = "$Id: scanelf.c,v 1.188 2007/08/31 17:45:24 solar Exp $";
11const char * const argv0 = "scanelf";
12
10#include "paxinc.h" 13#include "paxinc.h"
11
12static const char *rcsid = "$Id: scanelf.c,v 1.171 2007/01/09 23:01:09 vapier Exp $";
13#define argv0 "scanelf"
14 14
15#define IS_MODIFIER(c) (c == '%' || c == '#' || c == '+') 15#define IS_MODIFIER(c) (c == '%' || c == '#' || c == '+')
16 16
17/* prototypes */ 17/* prototypes */
18static int file_matches_list(const char *filename, char **matchlist); 18static int file_matches_list(const char *filename, char **matchlist);
25static void scanelf_envpath(void); 25static void scanelf_envpath(void);
26static void usage(int status); 26static void usage(int status);
27static char **get_split_env(const char *envvar); 27static char **get_split_env(const char *envvar);
28static void parseenv(void); 28static void parseenv(void);
29static int parseargs(int argc, char *argv[]); 29static int parseargs(int argc, char *argv[]);
30static char *xstrdup(const char *s); 30static int rematch(const char *regex, const char *match, int cflags);
31static void *xmalloc(size_t size);
32static void *xrealloc(void *ptr, size_t size);
33static void xstrncat(char **dst, const char *src, size_t *curr_len, size_t n);
34#define xstrcat(dst,src,curr_len) xstrncat(dst,src,curr_len,0)
35static inline void xchrcat(char **dst, const char append, size_t *curr_len);
36 31
37/* variables to control behavior */ 32/* variables to control behavior */
38static char match_etypes[126] = ""; 33static char match_etypes[126] = "";
39static char *ldpaths[256]; 34static char *ldpaths[256];
40static char scan_ldpath = 0; 35static char scan_ldpath = 0;
42static char scan_symlink = 1; 37static char scan_symlink = 1;
43static char scan_archives = 0; 38static char scan_archives = 0;
44static char dir_recurse = 0; 39static char dir_recurse = 0;
45static char dir_crossmount = 1; 40static char dir_crossmount = 1;
46static char show_pax = 0; 41static char show_pax = 0;
42static char show_perms = 0;
47static char show_phdr = 0; 43static char show_phdr = 0;
48static char show_textrel = 0; 44static char show_textrel = 0;
49static char show_rpath = 0; 45static char show_rpath = 0;
50static char show_needed = 0; 46static char show_needed = 0;
51static char show_interp = 0; 47static char show_interp = 0;
52static char show_bind = 0; 48static char show_bind = 0;
53static char show_soname = 0; 49static char show_soname = 0;
54static char show_textrels = 0; 50static char show_textrels = 0;
55static char show_banner = 1; 51static char show_banner = 1;
52static char show_endian = 0;
56static char be_quiet = 0; 53static char be_quiet = 0;
57static char be_verbose = 0; 54static char be_verbose = 0;
58static char be_wewy_wewy_quiet = 0; 55static char be_wewy_wewy_quiet = 0;
59static char be_semi_verbose = 0; 56static char be_semi_verbose = 0;
60static char *find_sym = NULL, *versioned_symname = NULL; 57static char *find_sym = NULL, *versioned_symname = NULL;
61static char *find_lib = NULL; 58static char *find_lib = NULL;
62static char *find_section = NULL; 59static char *find_section = NULL;
63static char *out_format = NULL; 60static char *out_format = NULL;
64static char *search_path = NULL; 61static char *search_path = NULL;
65static char fix_elf = 0; 62static char fix_elf = 0;
66static char gmatch = 0; 63static char g_match = 0;
67static char use_ldcache = 0; 64static char use_ldcache = 0;
68 65
69static char **qa_textrels = NULL; 66static char **qa_textrels = NULL;
70static char **qa_execstack = NULL; 67static char **qa_execstack = NULL;
71static char **qa_wx_load = NULL; 68static char **qa_wx_load = NULL;
72 69
73int match_bits = 0; 70int match_bits = 0;
71unsigned int match_perms = 0;
74caddr_t ldcache = 0; 72caddr_t ldcache = 0;
75size_t ldcache_size = 0; 73size_t ldcache_size = 0;
76unsigned long setpax = 0UL; 74unsigned long setpax = 0UL;
77 75
78int has_objdump = 0; 76int has_objdump = 0;
79 77
78static char *getstr_perms(const char *fname);
79static char *getstr_perms(const char *fname)
80{
81 struct stat st;
82 static char buf[8];
83
84 if ((stat(fname, &st)) == (-1))
85 return (char *) "";
86
87 snprintf(buf, sizeof(buf), "%o", st.st_mode);
88
89 return (char *) buf + 2;
90}
91
92/* find the path to a file by name */
80static char *which(const char *fname) 93static char *which(const char *fname)
81{ 94{
82 static char fullpath[BUFSIZ]; 95 static char fullpath[BUFSIZ];
83 char *path, *p; 96 char *path, *p;
84 97
85 memset(fullpath, 0x0, sizeof(fullpath));
86
87 path = getenv("PATH"); 98 path = getenv("PATH");
88 99 if (!path)
89 if (!path)
90 return NULL; 100 return NULL;
91 101
92 path = xstrdup(path); 102 path = xstrdup(path);
93 while ((p = strrchr(path, ':')) != NULL) { 103 while ((p = strrchr(path, ':')) != NULL) {
94 snprintf(fullpath, sizeof(fullpath), "%s/%s", p + 1, fname); 104 snprintf(fullpath, sizeof(fullpath), "%s/%s", p + 1, fname);
95 *p = 0; 105 *p = 0;
96 if (access(fullpath, R_OK) != (-1)) { 106 if (access(fullpath, R_OK) != (-1)) {
97 free(path); 107 free(path);
98 return (char *) fullpath; 108 return (char *) fullpath;
99 } 109 }
100 } 110 }
101 free(path); 111 free(path);
102 return NULL; 112 return NULL;
113}
114
115/* 1 on failure. 0 otherwise */
116static int rematch(const char *regex, const char *match, int cflags)
117{
118 regex_t preg;
119 int ret;
120
121 if ((match == NULL) || (regex == NULL))
122 return EXIT_FAILURE;
123
124
125 if ((ret = regcomp(&preg, regex, cflags))) {
126 char err[256];
127
128 if (regerror(ret, &preg, err, sizeof(err)))
129 fprintf(stderr, "regcomp failed: %s", err);
130 else
131 fprintf(stderr, "regcomp failed");
132
133 return EXIT_FAILURE;
134 }
135 ret = regexec(&preg, match, 0, NULL, 0);
136 regfree(&preg);
137
138 return ret;
103} 139}
104 140
105/* sub-funcs for scanelf_file() */ 141/* sub-funcs for scanelf_file() */
106static void scanelf_file_get_symtabs(elfobj *elf, void **sym, void **tab) 142static void scanelf_file_get_symtabs(elfobj *elf, void **sym, void **tab)
107{ 143{
230 warnf("%s: multiple PT_GNU_RELRO's !?", elf->filename); \ 266 warnf("%s: multiple PT_GNU_RELRO's !?", elf->filename); \
231 found = found_relro; \ 267 found = found_relro; \
232 offset = 4; \ 268 offset = 4; \
233 check_flags = PF_X; \ 269 check_flags = PF_X; \
234 } else if (EGET(phdr[i].p_type) == PT_LOAD) { \ 270 } else if (EGET(phdr[i].p_type) == PT_LOAD) { \
235 if (ehdr->e_type == ET_DYN || ehdr->e_type == ET_EXEC) \ 271 if (EGET(ehdr->e_type) == ET_DYN || EGET(ehdr->e_type) == ET_EXEC) \
236 if (multi_load++ > max_pt_load) \ 272 if (multi_load++ > max_pt_load) \
237 warnf("%s: more than %i PT_LOAD's !?", elf->filename, max_pt_load); \ 273 warnf("%s: more than %i PT_LOAD's !?", elf->filename, max_pt_load); \
238 if (file_matches_list(elf->filename, qa_wx_load)) \ 274 if (file_matches_list(elf->filename, qa_wx_load)) \
239 continue; \ 275 continue; \
240 found = found_load; \ 276 found = found_load; \
298 return NULL; 334 return NULL;
299 else 335 else
300 return ret; 336 return ret;
301} 337}
302 338
339/*
340 * See if this ELF contains a DT_TEXTREL tag in any of its
341 * PT_DYNAMIC sections.
342 */
303static const char *scanelf_file_textrel(elfobj *elf, char *found_textrel) 343static const char *scanelf_file_textrel(elfobj *elf, char *found_textrel)
304{ 344{
305 static const char *ret = "TEXTREL"; 345 static const char *ret = "TEXTREL";
306 unsigned long i; 346 unsigned long i;
307 347
315 Elf ## B ## _Dyn *dyn; \ 355 Elf ## B ## _Dyn *dyn; \
316 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ 356 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
317 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ 357 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
318 Elf ## B ## _Off offset; \ 358 Elf ## B ## _Off offset; \
319 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ 359 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
320 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \ 360 if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) continue; \
321 offset = EGET(phdr[i].p_offset); \ 361 offset = EGET(phdr[i].p_offset); \
322 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \ 362 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
323 dyn = DYN ## B (elf->data + offset); \ 363 dyn = DYN ## B (elf->data + offset); \
324 while (EGET(dyn->d_tag) != DT_NULL) { \ 364 while (EGET(dyn->d_tag) != DT_NULL) { \
325 if (EGET(dyn->d_tag) == DT_TEXTREL) { /*dyn->d_tag != DT_FLAGS)*/ \ 365 if (EGET(dyn->d_tag) == DT_TEXTREL) { /*dyn->d_tag != DT_FLAGS)*/ \
337 if (be_quiet || be_wewy_wewy_quiet) 377 if (be_quiet || be_wewy_wewy_quiet)
338 return NULL; 378 return NULL;
339 else 379 else
340 return " - "; 380 return " - ";
341} 381}
382
383/*
384 * Scan the .text section to see if there are any relocations in it.
385 * Should rewrite this to check PT_LOAD sections that are marked
386 * Executable rather than the section named '.text'.
387 */
342static char *scanelf_file_textrels(elfobj *elf, char *found_textrels, char *found_textrel) 388static char *scanelf_file_textrels(elfobj *elf, char *found_textrels, char *found_textrel)
343{ 389{
344 unsigned long s, r, rmax; 390 unsigned long s, r, rmax;
345 void *symtab_void, *strtab_void, *text_void; 391 void *symtab_void, *strtab_void, *text_void;
346 392
437 printf("%s", func_name); \ 483 printf("%s", func_name); \
438 } else \ 484 } else \
439 printf("(optimized out)"); \ 485 printf("(optimized out)"); \
440 printf(" [0x%lX]\n", (unsigned long)offset_tmp); \ 486 printf(" [0x%lX]\n", (unsigned long)offset_tmp); \
441 if (be_verbose && has_objdump) { \ 487 if (be_verbose && has_objdump) { \
488 Elf ## B ## _Addr end_addr = offset_tmp + EGET(func->st_size); \
442 char *sysbuf; \ 489 char *sysbuf; \
443 size_t syslen; \ 490 size_t syslen; \
444 const char sysfmt[] = "objdump -r -R -d -w -l --start-address=0x%lX --stop-address=0x%lX %s | grep --color -i -C 3 '.*[[:space:]]%lX:[[:space:]]*R_.*'\n"; \ 491 const char sysfmt[] = "objdump -r -R -d -w -l --start-address=0x%lX --stop-address=0x%lX %s | grep --color -i -C 3 '.*[[:space:]]%lX:[[:space:]]*R_.*'\n"; \
445 syslen = sizeof(sysfmt) + strlen(elf->filename) + 3 * sizeof(unsigned long) + 1; \ 492 syslen = sizeof(sysfmt) + strlen(elf->filename) + 3 * sizeof(unsigned long) + 1; \
446 sysbuf = xmalloc(syslen); \ 493 sysbuf = xmalloc(syslen); \
447 if (sysbuf) { \
448 Elf ## B ## _Addr end_addr = offset_tmp + EGET(func->st_size); \
449 if (end_addr < r_offset) \ 494 if (end_addr < r_offset) \
450 /* not uncommon when things are optimized out */ \ 495 /* not uncommon when things are optimized out */ \
451 end_addr = r_offset + 0x100; \ 496 end_addr = r_offset + 0x100; \
452 snprintf(sysbuf, syslen, sysfmt, \ 497 snprintf(sysbuf, syslen, sysfmt, \
453 (unsigned long)offset_tmp, \ 498 (unsigned long)offset_tmp, \
454 (unsigned long)end_addr, \ 499 (unsigned long)end_addr, \
455 elf->filename, \ 500 elf->filename, \
456 (unsigned long)r_offset); \ 501 (unsigned long)r_offset); \
457 fflush(stdout); \ 502 fflush(stdout); \
458 system(sysbuf); \ 503 system(sysbuf); \
459 fflush(stdout); \ 504 fflush(stdout); \
460 free(sysbuf); \ 505 free(sysbuf); \
461 } \
462 } \ 506 } \
463 } \ 507 } \
464 } } 508 } }
465 SHOW_TEXTRELS(32) 509 SHOW_TEXTRELS(32)
466 SHOW_TEXTRELS(64) 510 SHOW_TEXTRELS(64)
486 break; 530 break;
487 case '$': 531 case '$':
488 if (fstat(elf->fd, &st) != -1) 532 if (fstat(elf->fd, &st) != -1)
489 if ((st.st_mode & S_ISUID) || (st.st_mode & S_ISGID)) 533 if ((st.st_mode & S_ISUID) || (st.st_mode & S_ISGID))
490 warnf("Security problem with %s='%s' in %s with mode set of %o", 534 warnf("Security problem with %s='%s' in %s with mode set of %o",
491 dt_type, item, elf->filename, st.st_mode & 07777); 535 dt_type, item, elf->filename, (unsigned int) st.st_mode & 07777);
492 break; 536 break;
493 default: 537 default:
494 warnf("Maybe? sec problem with %s='%s' in %s", dt_type, item, elf->filename); 538 warnf("Maybe? sec problem with %s='%s' in %s", dt_type, item, elf->filename);
495 break; 539 break;
496 } 540 }
516 Elf ## B ## _Off offset; \ 560 Elf ## B ## _Off offset; \
517 Elf ## B ## _Xword word; \ 561 Elf ## B ## _Xword word; \
518 /* Scan all the program headers */ \ 562 /* Scan all the program headers */ \
519 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ 563 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
520 /* Just scan dynamic headers */ \ 564 /* Just scan dynamic headers */ \
521 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \ 565 if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) continue; \
522 offset = EGET(phdr[i].p_offset); \ 566 offset = EGET(phdr[i].p_offset); \
523 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \ 567 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
524 /* Just scan dynamic RPATH/RUNPATH headers */ \ 568 /* Just scan dynamic RPATH/RUNPATH headers */ \
525 dyn = DYN ## B (elf->data + offset); \ 569 dyn = DYN ## B (elf->data + offset); \
526 while ((word=EGET(dyn->d_tag)) != DT_NULL) { \ 570 while ((word=EGET(dyn->d_tag)) != DT_NULL) { \
776 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ 820 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
777 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ 821 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
778 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \ 822 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
779 Elf ## B ## _Off offset; \ 823 Elf ## B ## _Off offset; \
780 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ 824 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
781 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \ 825 if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) continue; \
782 offset = EGET(phdr[i].p_offset); \ 826 offset = EGET(phdr[i].p_offset); \
783 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \ 827 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
784 dyn = DYN ## B (elf->data + offset); \ 828 dyn = DYN ## B (elf->data + offset); \
785 while (EGET(dyn->d_tag) != DT_NULL) { \ 829 while (EGET(dyn->d_tag) != DT_NULL) { \
786 if (EGET(dyn->d_tag) == DT_NEEDED) { \ 830 if (EGET(dyn->d_tag) == DT_NEEDED) { \
798 needed = p; \ 842 needed = p; \
799 xstrcat(ret, needed, ret_len); \ 843 xstrcat(ret, needed, ret_len); \
800 } \ 844 } \
801 *found_needed = 1; \ 845 *found_needed = 1; \
802 } else { \ 846 } else { \
803 if (!strncmp(find_lib, needed, strlen( !gmatch ? needed : find_lib))) { \ 847 if (!strncmp(find_lib, needed, strlen( !g_match ? needed : find_lib))) { \
804 *found_lib = 1; \ 848 *found_lib = 1; \
805 return (be_wewy_wewy_quiet ? NULL : needed); \ 849 return (be_wewy_wewy_quiet ? NULL : needed); \
806 } \ 850 } \
807 } \ 851 } \
808 } \ 852 } \
851 Elf ## B ## _Dyn *dyn; \ 895 Elf ## B ## _Dyn *dyn; \
852 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ 896 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
853 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ 897 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
854 Elf ## B ## _Off offset; \ 898 Elf ## B ## _Off offset; \
855 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ 899 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
856 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \ 900 if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) continue; \
857 dynamic = 1; \ 901 dynamic = 1; \
858 offset = EGET(phdr[i].p_offset); \ 902 offset = EGET(phdr[i].p_offset); \
859 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \ 903 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
860 dyn = DYN ## B (elf->data + offset); \ 904 dyn = DYN ## B (elf->data + offset); \
861 while (EGET(dyn->d_tag) != DT_NULL) { \ 905 while (EGET(dyn->d_tag) != DT_NULL) { \
900 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ 944 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
901 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ 945 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
902 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \ 946 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
903 Elf ## B ## _Off offset; \ 947 Elf ## B ## _Off offset; \
904 /* only look for soname in shared objects */ \ 948 /* only look for soname in shared objects */ \
905 if (ehdr->e_type != ET_DYN) \ 949 if (EGET(ehdr->e_type) != ET_DYN) \
906 return NULL; \ 950 return NULL; \
907 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ 951 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
908 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \ 952 if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) continue; \
909 offset = EGET(phdr[i].p_offset); \ 953 offset = EGET(phdr[i].p_offset); \
910 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \ 954 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
911 dyn = DYN ## B (elf->data + offset); \ 955 dyn = DYN ## B (elf->data + offset); \
912 while (EGET(dyn->d_tag) != DT_NULL) { \ 956 while (EGET(dyn->d_tag) != DT_NULL) { \
913 if (EGET(dyn->d_tag) == DT_SONAME) { \ 957 if (EGET(dyn->d_tag) == DT_SONAME) { \
927 SHOW_SONAME(64) 971 SHOW_SONAME(64)
928 } 972 }
929 973
930 return NULL; 974 return NULL;
931} 975}
976
932static char *scanelf_file_sym(elfobj *elf, char *found_sym) 977static char *scanelf_file_sym(elfobj *elf, char *found_sym)
933{ 978{
934 unsigned long i; 979 unsigned long i;
935 char *ret; 980 char *ret;
936 void *symtab_void, *strtab_void; 981 void *symtab_void, *strtab_void;
958 warnf("%s: corrupt ELF symbols", elf->filename); \ 1003 warnf("%s: corrupt ELF symbols", elf->filename); \
959 ++sym; \ 1004 ++sym; \
960 continue; \ 1005 continue; \
961 } \ 1006 } \
962 /* debug display ... show all symbols and some extra info */ \ 1007 /* debug display ... show all symbols and some extra info */ \
963 if (*ret == '*') { \ 1008 if (g_match ? rematch(ret, symname, REG_EXTENDED) == 0 : *ret == '*') { \
964 printf("%s(%s) %5lX %15s %s\n", \ 1009 printf("%s(%s) %5lX %15s %s %s\n", \
965 ((*found_sym == 0) ? "\n\t" : "\t"), \ 1010 ((*found_sym == 0) ? "\n\t" : "\t"), \
966 elf->base_filename, \ 1011 elf->base_filename, \
967 (unsigned long)sym->st_size, \ 1012 (unsigned long)sym->st_size, \
968 get_elfstttype(sym->st_info), \ 1013 get_elfstttype(sym->st_info), \
969 symname); \ 1014 sym->st_shndx == SHN_UNDEF ? "U" : "D", symname); \
970 *found_sym = 1; \ 1015 *found_sym = 1; \
971 } else { \ 1016 } else { \
972 /* allow the user to specify a comma delimited list of symbols to search for */ \ 1017 /* allow the user to specify a comma delimited list of symbols to search for */ \
973 char *this_sym, *this_sym_ver, *next_sym; \ 1018 char *this_sym, *this_sym_ver, *next_sym; \
974 this_sym = ret; \ 1019 this_sym = ret; \
1077 printf("%s: scanning file\n", elf->filename); 1122 printf("%s: scanning file\n", elf->filename);
1078 1123
1079 /* init output buffer */ 1124 /* init output buffer */
1080 if (!out_buffer) { 1125 if (!out_buffer) {
1081 out_len = sizeof(char) * 80; 1126 out_len = sizeof(char) * 80;
1082 out_buffer = (char*)xmalloc(out_len); 1127 out_buffer = xmalloc(out_len);
1083 } 1128 }
1084 *out_buffer = '\0'; 1129 *out_buffer = '\0';
1085 1130
1086 /* show the header */ 1131 /* show the header */
1087 if (!be_quiet && show_banner) { 1132 if (!be_quiet && show_banner) {
1108 case 's': prints("SYM "); break; 1153 case 's': prints("SYM "); break;
1109 case 'N': prints("LIB "); break; 1154 case 'N': prints("LIB "); break;
1110 case 'T': prints("TEXTRELS "); break; 1155 case 'T': prints("TEXTRELS "); break;
1111 case 'k': prints("SECTION "); break; 1156 case 'k': prints("SECTION "); break;
1112 case 'a': prints("ARCH "); break; 1157 case 'a': prints("ARCH "); break;
1158 case 'O': prints("PERM "); break;
1159 case 'D': prints("ENDIAN "); break;
1113 default: warnf("'%c' has no title ?", out_format[i]); 1160 default: warnf("'%c' has no title ?", out_format[i]);
1114 } 1161 }
1115 } 1162 }
1116 if (!found_file) prints("FILE "); 1163 if (!found_file) prints("FILE ");
1117 prints("\n"); 1164 prints("\n");
1168 case 'e': out = scanelf_file_phdr(elf, &found_phdr, &found_relro, &found_load); break; 1215 case 'e': out = scanelf_file_phdr(elf, &found_phdr, &found_relro, &found_load); break;
1169 case 't': out = scanelf_file_textrel(elf, &found_textrel); break; 1216 case 't': out = scanelf_file_textrel(elf, &found_textrel); break;
1170 case 'T': out = scanelf_file_textrels(elf, &found_textrels, &found_textrel); break; 1217 case 'T': out = scanelf_file_textrels(elf, &found_textrels, &found_textrel); break;
1171 case 'r': scanelf_file_rpath(elf, &found_rpath, &out_buffer, &out_len); break; 1218 case 'r': scanelf_file_rpath(elf, &found_rpath, &out_buffer, &out_len); break;
1172 case 'M': out = get_elfeitype(EI_CLASS, elf->data[EI_CLASS]); break; 1219 case 'M': out = get_elfeitype(EI_CLASS, elf->data[EI_CLASS]); break;
1220 case 'D': out = get_endian(elf); break;
1221 case 'O': out = getstr_perms(elf->filename); break;
1173 case 'n': 1222 case 'n':
1174 case 'N': out = scanelf_file_needed_lib(elf, &found_needed, &found_lib, (out_format[i]=='N'), &out_buffer, &out_len); break; 1223 case 'N': out = scanelf_file_needed_lib(elf, &found_needed, &found_lib, (out_format[i]=='N'), &out_buffer, &out_len); break;
1175 case 'i': out = scanelf_file_interp(elf, &found_interp); break; 1224 case 'i': out = scanelf_file_interp(elf, &found_interp); break;
1176 case 'b': out = scanelf_file_bind(elf, &found_bind); break; 1225 case 'b': out = scanelf_file_bind(elf, &found_bind); break;
1177 case 'S': out = scanelf_file_soname(elf, &found_soname); break; 1226 case 'S': out = scanelf_file_soname(elf, &found_soname); break;
1231 if (strlen(match_etypes)) { 1280 if (strlen(match_etypes)) {
1232 char sbuf[126]; 1281 char sbuf[126];
1233 strncpy(sbuf, match_etypes, sizeof(sbuf)); 1282 strncpy(sbuf, match_etypes, sizeof(sbuf));
1234 if (strchr(match_etypes, ',') != NULL) { 1283 if (strchr(match_etypes, ',') != NULL) {
1235 char *p; 1284 char *p;
1236 while((p = strrchr(sbuf, ',')) != NULL) { 1285 while ((p = strrchr(sbuf, ',')) != NULL) {
1237 *p = 0; 1286 *p = 0;
1238 if (etype_lookup(p+1) == get_etype(elf)) 1287 if (etype_lookup(p+1) == get_etype(elf))
1239 goto label_ret; 1288 goto label_ret;
1240 } 1289 }
1241 } 1290 }
1292 if (!S_ISREG(st->st_mode)) { 1341 if (!S_ISREG(st->st_mode)) {
1293 if (be_verbose > 2) printf("%s: skipping non-file\n", filename); 1342 if (be_verbose > 2) printf("%s: skipping non-file\n", filename);
1294 return 1; 1343 return 1;
1295 } 1344 }
1296 1345
1346 if (match_perms) {
1347 if ((st->st_mode | match_perms) != st->st_mode)
1348 return 1;
1349 }
1297 if ((fd=open(filename, (fix_elf ? O_RDWR : O_RDONLY))) == -1) 1350 if ((fd=open(filename, (fix_elf ? O_RDWR : O_RDONLY))) == -1)
1298 return 1; 1351 return 1;
1299 1352
1300 if (scanelf_elf(filename, fd, st->st_size) == 1 && scan_archives) 1353 if (scanelf_elf(filename, fd, st->st_size) == 1 && scan_archives)
1301 /* if it isn't an ELF, maybe it's an .a archive */ 1354 /* if it isn't an ELF, maybe it's an .a archive */
1398 if ((p = strrchr(path, '\r')) != NULL) 1451 if ((p = strrchr(path, '\r')) != NULL)
1399 *p = 0; 1452 *p = 0;
1400 if ((p = strchr(path, '\n')) != NULL) 1453 if ((p = strchr(path, '\n')) != NULL)
1401 *p = 0; 1454 *p = 0;
1402#ifdef __linux__ 1455#ifdef __linux__
1403 // recursive includes of the same file will make this segfault. 1456 /* recursive includes of the same file will make this segfault. */
1404 if ((memcmp(path, "include", 7) == 0) && isblank(path[7])) { 1457 if ((memcmp(path, "include", 7) == 0) && isblank(path[7])) {
1405 glob64_t gl; 1458 glob64_t gl;
1406 size_t x; 1459 size_t x;
1407 char gpath[__PAX_UTILS_PATH_MAX]; 1460 char gpath[__PAX_UTILS_PATH_MAX];
1408 1461
1463 { 1516 {
1464 fclose(fp); 1517 fclose(fp);
1465 return i; 1518 return i;
1466 } 1519 }
1467 1520
1468 b = (char*)malloc(hdr.dirlistlen+1); 1521 b = xmalloc(hdr.dirlistlen + 1);
1469 if (fread(b, 1, hdr.dirlistlen+1, fp) != hdr.dirlistlen+1) { 1522 if (fread(b, 1, hdr.dirlistlen+1, fp) != hdr.dirlistlen+1) {
1470 fclose(fp); 1523 fclose(fp);
1471 free(b); 1524 free(b);
1472 return i; 1525 return i;
1473 } 1526 }
1495} 1548}
1496 1549
1497#endif 1550#endif
1498 1551
1499/* scan /etc/ld.so.conf for paths */ 1552/* scan /etc/ld.so.conf for paths */
1500static void scanelf_ldpath() 1553static void scanelf_ldpath(void)
1501{ 1554{
1502 char scan_l, scan_ul, scan_ull; 1555 char scan_l, scan_ul, scan_ull;
1503 int i = 0; 1556 int i = 0;
1504 1557
1505 if (!ldpaths[0]) 1558 if (!ldpaths[0])
1519 if (!scan_ul) scanelf_dir("/usr/lib"); 1572 if (!scan_ul) scanelf_dir("/usr/lib");
1520 if (!scan_ull) scanelf_dir("/usr/local/lib"); 1573 if (!scan_ull) scanelf_dir("/usr/local/lib");
1521} 1574}
1522 1575
1523/* scan env PATH for paths */ 1576/* scan env PATH for paths */
1524static void scanelf_envpath() 1577static void scanelf_envpath(void)
1525{ 1578{
1526 char *path, *p; 1579 char *path, *p;
1527 1580
1528 path = getenv("PATH"); 1581 path = getenv("PATH");
1529 if (!path) 1582 if (!path)
1536 } 1589 }
1537 1590
1538 free(path); 1591 free(path);
1539} 1592}
1540 1593
1541/* usage / invocation handling functions */ 1594
1595/* usage / invocation handling functions */ /* Free Flags: c d j u w C G H I J K P Q U W Y Z */
1542#define PARSE_FLAGS "plRmyAXz:xetrnLibSs:k:gN:TaqvF:f:o:E:M:BhV" 1596#define PARSE_FLAGS "plRmyAXz:xetrnLibSs:k:gN:TaqvF:f:o:E:M:DO:BhV"
1543#define a_argument required_argument 1597#define a_argument required_argument
1544static struct option const long_opts[] = { 1598static struct option const long_opts[] = {
1545 {"path", no_argument, NULL, 'p'}, 1599 {"path", no_argument, NULL, 'p'},
1546 {"ldpath", no_argument, NULL, 'l'}, 1600 {"ldpath", no_argument, NULL, 'l'},
1547 {"recursive", no_argument, NULL, 'R'}, 1601 {"recursive", no_argument, NULL, 'R'},
1564 {"lib", a_argument, NULL, 'N'}, 1618 {"lib", a_argument, NULL, 'N'},
1565 {"gmatch", no_argument, NULL, 'g'}, 1619 {"gmatch", no_argument, NULL, 'g'},
1566 {"textrels", no_argument, NULL, 'T'}, 1620 {"textrels", no_argument, NULL, 'T'},
1567 {"etype", a_argument, NULL, 'E'}, 1621 {"etype", a_argument, NULL, 'E'},
1568 {"bits", a_argument, NULL, 'M'}, 1622 {"bits", a_argument, NULL, 'M'},
1623 {"endian", no_argument, NULL, 'D'},
1624 {"perms", a_argument, NULL, 'O'},
1569 {"all", no_argument, NULL, 'a'}, 1625 {"all", no_argument, NULL, 'a'},
1570 {"quiet", no_argument, NULL, 'q'}, 1626 {"quiet", no_argument, NULL, 'q'},
1571 {"verbose", no_argument, NULL, 'v'}, 1627 {"verbose", no_argument, NULL, 'v'},
1572 {"format", a_argument, NULL, 'F'}, 1628 {"format", a_argument, NULL, 'F'},
1573 {"from", a_argument, NULL, 'f'}, 1629 {"from", a_argument, NULL, 'f'},
1601 "Find a specified library", 1657 "Find a specified library",
1602 "Use strncmp to match libraries. (use with -N)", 1658 "Use strncmp to match libraries. (use with -N)",
1603 "Locate cause of TEXTREL", 1659 "Locate cause of TEXTREL",
1604 "Print only ELF files matching etype ET_DYN,ET_EXEC ...", 1660 "Print only ELF files matching etype ET_DYN,ET_EXEC ...",
1605 "Print only ELF files matching numeric bits", 1661 "Print only ELF files matching numeric bits",
1662 "Print Endianness",
1663 "Print only ELF files matching octal permissions",
1606 "Print all scanned info (-x -e -t -r -b)\n", 1664 "Print all scanned info (-x -e -t -r -b)\n",
1607 "Only output 'bad' things", 1665 "Only output 'bad' things",
1608 "Be verbose (can be specified more than once)", 1666 "Be verbose (can be specified more than once)",
1609 "Use specified format for output", 1667 "Use specified format for output",
1610 "Read input stream from a filename", 1668 "Read input stream from a filename",
1628 long_opts[i].name, opts_help[i]); 1686 long_opts[i].name, opts_help[i]);
1629 else 1687 else
1630 printf(" -%c, --%-7s <arg> * %s\n", long_opts[i].val, 1688 printf(" -%c, --%-7s <arg> * %s\n", long_opts[i].val,
1631 long_opts[i].name, opts_help[i]); 1689 long_opts[i].name, opts_help[i]);
1632 1690
1633 if (status != EXIT_SUCCESS) 1691 puts("\nFor more information, see the scanelf(1) manpage");
1634 exit(status);
1635
1636 puts("\nThe format modifiers for the -F option are:");
1637 puts(" F Filename \tx PaX Flags \te STACK/RELRO");
1638 puts(" t TEXTREL \tr RPATH \tn NEEDED");
1639 puts(" i INTERP \tb BIND \ts symbol");
1640 puts(" N library \to Type \tT TEXTRELs");
1641 puts(" S SONAME \tk section \ta arch");
1642 puts(" p filename (with search path removed)");
1643 puts(" f filename (short name/basename)");
1644 puts("Prefix each modifier with '%' (verbose) or '#' (silent)");
1645
1646 puts("\nELF Etypes:");
1647 print_etypes(stdout);
1648
1649 exit(status); 1692 exit(status);
1650} 1693}
1651 1694
1652/* parse command line arguments and preform needed actions */ 1695/* parse command line arguments and preform needed actions */
1653#define do_pax_state(option, flag) \ 1696#define do_pax_state(option, flag) \
1682 case 'E': 1725 case 'E':
1683 strncpy(match_etypes, optarg, sizeof(match_etypes)); 1726 strncpy(match_etypes, optarg, sizeof(match_etypes));
1684 break; 1727 break;
1685 case 'M': 1728 case 'M':
1686 match_bits = atoi(optarg); 1729 match_bits = atoi(optarg);
1730 if (match_bits == 0) {
1731 if (strcmp(optarg, "ELFCLASS32") == 0)
1732 match_bits = 32;
1733 if (strcmp(optarg, "ELFCLASS64") == 0)
1734 match_bits = 64;
1735 }
1736 break;
1737 case 'O':
1738 if (sscanf(optarg, "%o", &match_perms) == (-1))
1739 match_bits = 0;
1687 break; 1740 break;
1688 case 'o': { 1741 case 'o': {
1689 if (freopen(optarg, "w", stdout) == NULL) 1742 if (freopen(optarg, "w", stdout) == NULL)
1690 err("Could not open output stream '%s': %s", optarg, strerror(errno)); 1743 err("Could not open output stream '%s': %s", optarg, strerror(errno));
1691 break; 1744 break;
1695 find_section = optarg; 1748 find_section = optarg;
1696 break; 1749 break;
1697 case 's': { 1750 case 's': {
1698 if (find_sym) warn("You prob don't want to specify -s twice"); 1751 if (find_sym) warn("You prob don't want to specify -s twice");
1699 find_sym = optarg; 1752 find_sym = optarg;
1700 versioned_symname = (char*)xmalloc(sizeof(char) * (strlen(find_sym)+1+1)); 1753 versioned_symname = xmalloc(sizeof(char) * (strlen(find_sym)+1+1));
1701 sprintf(versioned_symname, "%s@", find_sym); 1754 sprintf(versioned_symname, "%s@", find_sym);
1702 break; 1755 break;
1703 } 1756 }
1704 case 'N': { 1757 case 'N': {
1705 if (find_lib) warn("You prob don't want to specify -N twice"); 1758 if (find_lib) warn("You prob don't want to specify -N twice");
1714 } 1767 }
1715 case 'z': { 1768 case 'z': {
1716 unsigned long flags = (PF_NOEMUTRAMP | PF_NORANDEXEC); 1769 unsigned long flags = (PF_NOEMUTRAMP | PF_NORANDEXEC);
1717 size_t x; 1770 size_t x;
1718 1771
1719 for (x = 0 ; x < strlen(optarg); x++) { 1772 for (x = 0; x < strlen(optarg); x++) {
1720 switch(optarg[x]) { 1773 switch (optarg[x]) {
1721 case 'p': 1774 case 'p':
1722 case 'P': 1775 case 'P':
1723 do_pax_state(optarg[x], PAGEEXEC); 1776 do_pax_state(optarg[x], PAGEEXEC);
1724 break; 1777 break;
1725 case 's': 1778 case 's':
1753 ((flags & PF_EMUTRAMP) && (flags & PF_NOEMUTRAMP)) || 1806 ((flags & PF_EMUTRAMP) && (flags & PF_NOEMUTRAMP)) ||
1754 ((flags & PF_RANDMMAP) && (flags & PF_NORANDMMAP)))) 1807 ((flags & PF_RANDMMAP) && (flags & PF_NORANDMMAP))))
1755 setpax = flags; 1808 setpax = flags;
1756 break; 1809 break;
1757 } 1810 }
1758 case 'g': gmatch = 1; break; 1811 case 'g': g_match = 1; break;
1759 case 'L': use_ldcache = 1; break; 1812 case 'L': use_ldcache = 1; break;
1760 case 'y': scan_symlink = 0; break; 1813 case 'y': scan_symlink = 0; break;
1761 case 'A': scan_archives = 1; break; 1814 case 'A': scan_archives = 1; break;
1762 case 'B': show_banner = 0; break; 1815 case 'B': show_banner = 0; break;
1763 case 'l': scan_ldpath = 1; break; 1816 case 'l': scan_ldpath = 1; break;
1774 case 'b': show_bind = 1; break; 1827 case 'b': show_bind = 1; break;
1775 case 'S': show_soname = 1; break; 1828 case 'S': show_soname = 1; break;
1776 case 'T': show_textrels = 1; break; 1829 case 'T': show_textrels = 1; break;
1777 case 'q': be_quiet = 1; break; 1830 case 'q': be_quiet = 1; break;
1778 case 'v': be_verbose = (be_verbose % 20) + 1; break; 1831 case 'v': be_verbose = (be_verbose % 20) + 1; break;
1779 case 'a': show_pax = show_phdr = show_textrel = show_rpath = show_bind = 1; break; 1832 case 'a': show_perms = show_pax = show_phdr = show_textrel = show_rpath = show_bind = show_endian = 1; break;
1780 1833 case 'D': show_endian = 1; break;
1781 case ':': 1834 case ':':
1782 err("Option '%c' is missing parameter", optopt); 1835 err("Option '%c' is missing parameter", optopt);
1783 case '?': 1836 case '?':
1784 err("Unknown option '%c' or argument missing", optopt); 1837 err("Unknown option '%c' or argument missing", optopt);
1785 default: 1838 default:
1792 } 1845 }
1793 /* let the format option override all other options */ 1846 /* let the format option override all other options */
1794 if (out_format) { 1847 if (out_format) {
1795 show_pax = show_phdr = show_textrel = show_rpath = \ 1848 show_pax = show_phdr = show_textrel = show_rpath = \
1796 show_needed = show_interp = show_bind = show_soname = \ 1849 show_needed = show_interp = show_bind = show_soname = \
1797 show_textrels = 0; 1850 show_textrels = show_perms = show_endian = 0;
1798 for (i = 0; out_format[i]; ++i) { 1851 for (i = 0; out_format[i]; ++i) {
1799 if (!IS_MODIFIER(out_format[i])) continue; 1852 if (!IS_MODIFIER(out_format[i])) continue;
1800 1853
1801 switch (out_format[++i]) { 1854 switch (out_format[++i]) {
1802 case '+': break; 1855 case '+': break;
1809 case 's': break; 1862 case 's': break;
1810 case 'N': break; 1863 case 'N': break;
1811 case 'o': break; 1864 case 'o': break;
1812 case 'a': break; 1865 case 'a': break;
1813 case 'M': break; 1866 case 'M': break;
1867 case 'D': show_endian = 1; break;
1868 case 'O': show_perms = 1; break;
1814 case 'x': show_pax = 1; break; 1869 case 'x': show_pax = 1; break;
1815 case 'e': show_phdr = 1; break; 1870 case 'e': show_phdr = 1; break;
1816 case 't': show_textrel = 1; break; 1871 case 't': show_textrel = 1; break;
1817 case 'r': show_rpath = 1; break; 1872 case 'r': show_rpath = 1; break;
1818 case 'n': show_needed = 1; break; 1873 case 'n': show_needed = 1; break;
1827 } 1882 }
1828 1883
1829 /* construct our default format */ 1884 /* construct our default format */
1830 } else { 1885 } else {
1831 size_t fmt_len = 30; 1886 size_t fmt_len = 30;
1832 out_format = (char*)xmalloc(sizeof(char) * fmt_len); 1887 out_format = xmalloc(sizeof(char) * fmt_len);
1833 if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len); 1888 if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len);
1834 if (show_pax) xstrcat(&out_format, "%x ", &fmt_len); 1889 if (show_pax) xstrcat(&out_format, "%x ", &fmt_len);
1890 if (show_perms) xstrcat(&out_format, "%O ", &fmt_len);
1891 if (show_endian) xstrcat(&out_format, "%D ", &fmt_len);
1835 if (show_phdr) xstrcat(&out_format, "%e ", &fmt_len); 1892 if (show_phdr) xstrcat(&out_format, "%e ", &fmt_len);
1836 if (show_textrel) xstrcat(&out_format, "%t ", &fmt_len); 1893 if (show_textrel) xstrcat(&out_format, "%t ", &fmt_len);
1837 if (show_rpath) xstrcat(&out_format, "%r ", &fmt_len); 1894 if (show_rpath) xstrcat(&out_format, "%r ", &fmt_len);
1838 if (show_needed) xstrcat(&out_format, "%n ", &fmt_len); 1895 if (show_needed) xstrcat(&out_format, "%n ", &fmt_len);
1839 if (show_interp) xstrcat(&out_format, "%i ", &fmt_len); 1896 if (show_interp) xstrcat(&out_format, "%i ", &fmt_len);
1906 1963
1907 /* don't want to free(env) as it contains the memory that backs 1964 /* don't want to free(env) as it contains the memory that backs
1908 * the envvals array of strings */ 1965 * the envvals array of strings */
1909 return envvals; 1966 return envvals;
1910} 1967}
1968
1911static void parseenv() 1969static void parseenv(void)
1912{ 1970{
1913 qa_textrels = get_split_env("QA_TEXTRELS"); 1971 qa_textrels = get_split_env("QA_TEXTRELS");
1914 qa_execstack = get_split_env("QA_EXECSTACK"); 1972 qa_execstack = get_split_env("QA_EXECSTACK");
1915 qa_wx_load = get_split_env("QA_WX_LOAD"); 1973 qa_wx_load = get_split_env("QA_WX_LOAD");
1916} 1974}
1917 1975
1918#ifdef __PAX_UTILS_CLEANUP 1976#ifdef __PAX_UTILS_CLEANUP
1919static void cleanup() 1977static void cleanup(void)
1920{ 1978{
1921 free(out_format); 1979 free(out_format);
1922 free(qa_textrels); 1980 free(qa_textrels);
1923 free(qa_execstack); 1981 free(qa_execstack);
1924 free(qa_wx_load); 1982 free(qa_wx_load);
1942#endif 2000#endif
1943 return ret; 2001 return ret;
1944} 2002}
1945 2003
1946 2004
1947
1948/* utility funcs */
1949static char *xstrdup(const char *s)
1950{
1951 char *ret = strdup(s);
1952 if (!ret) err("Could not strdup(): %s", strerror(errno));
1953 return ret;
1954}
1955static void *xmalloc(size_t size)
1956{
1957 void *ret = malloc(size);
1958 if (!ret) err("Could not malloc() %li bytes", (unsigned long)size);
1959 return ret;
1960}
1961static void *xrealloc(void *ptr, size_t size)
1962{
1963 void *ret = realloc(ptr, size);
1964 if (!ret) err("Could not realloc() %li bytes", (unsigned long)size);
1965 return ret;
1966}
1967static void xstrncat(char **dst, const char *src, size_t *curr_len, size_t n)
1968{
1969 size_t new_len;
1970
1971 new_len = strlen(*dst) + strlen(src);
1972 if (*curr_len <= new_len) {
1973 *curr_len = new_len + (*curr_len / 2);
1974 *dst = realloc(*dst, *curr_len);
1975 if (!*dst)
1976 err("could not realloc() %li bytes", (unsigned long)*curr_len);
1977 }
1978
1979 if (n)
1980 strncat(*dst, src, n);
1981 else
1982 strcat(*dst, src);
1983}
1984static inline void xchrcat(char **dst, const char append, size_t *curr_len)
1985{
1986 static char my_app[2];
1987 my_app[0] = append;
1988 my_app[1] = '\0';
1989 xstrcat(dst, my_app, curr_len);
1990}
1991
1992/* Match filename against entries in matchlist, return TRUE 2005/* Match filename against entries in matchlist, return TRUE
1993 * if the file is listed */ 2006 * if the file is listed */
1994static int file_matches_list(const char *filename, char **matchlist) 2007static int file_matches_list(const char *filename, char **matchlist)
1995{ 2008{
1996 char **file; 2009 char **file;

Legend:
Removed from v.1.171  
changed lines
  Added in v.1.188

  ViewVC Help
Powered by ViewVC 1.1.20