/[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.185 Revision 1.209
1/* 1/*
2 * Copyright 2003-2007 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.185 2007/08/18 04:59:32 vapier Exp $ 4 * $Header: /var/cvsroot/gentoo-projects/pax-utils/Attic/scanelf.c,v 1.209 2009/03/15 08:53:29 vapier Exp $
5 * 5 *
6 * Copyright 2003-2007 Ned Ludd - <solar@gentoo.org> 6 * Copyright 2003-2007 Ned Ludd - <solar@gentoo.org>
7 * Copyright 2004-2007 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.209 2009/03/15 08:53:29 vapier 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.185 2007/08/18 04:59:32 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);
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);
36static int rematch(const char *regex, const char *match, int cflags); 30static int rematch(const char *regex, const char *match, int cflags);
37 31
38/* variables to control behavior */ 32/* variables to control behavior */
39static char match_etypes[126] = ""; 33static char match_etypes[126] = "";
40static char *ldpaths[256]; 34static char *ldpaths[256];
44static char scan_archives = 0; 38static char scan_archives = 0;
45static char dir_recurse = 0; 39static char dir_recurse = 0;
46static char dir_crossmount = 1; 40static char dir_crossmount = 1;
47static char show_pax = 0; 41static char show_pax = 0;
48static char show_perms = 0; 42static char show_perms = 0;
43static char show_size = 0;
49static char show_phdr = 0; 44static char show_phdr = 0;
50static char show_textrel = 0; 45static char show_textrel = 0;
51static char show_rpath = 0; 46static char show_rpath = 0;
52static char show_needed = 0; 47static char show_needed = 0;
53static char show_interp = 0; 48static char show_interp = 0;
54static char show_bind = 0; 49static char show_bind = 0;
55static char show_soname = 0; 50static char show_soname = 0;
56static char show_textrels = 0; 51static char show_textrels = 0;
57static char show_banner = 1; 52static char show_banner = 1;
58static char show_endian = 0; 53static char show_endian = 0;
54static char show_osabi = 0;
55static char show_eabi = 0;
59static char be_quiet = 0; 56static char be_quiet = 0;
60static char be_verbose = 0; 57static char be_verbose = 0;
61static char be_wewy_wewy_quiet = 0; 58static char be_wewy_wewy_quiet = 0;
62static char be_semi_verbose = 0; 59static char be_semi_verbose = 0;
63static char *find_sym = NULL, *versioned_symname = NULL; 60static char *find_sym = NULL;
64static char *find_lib = NULL; 61static char *find_lib = NULL;
65static char *find_section = NULL; 62static char *find_section = NULL;
66static char *out_format = NULL; 63static char *out_format = NULL;
67static char *search_path = NULL; 64static char *search_path = NULL;
68static char fix_elf = 0; 65static char fix_elf = 0;
71 68
72static char **qa_textrels = NULL; 69static char **qa_textrels = NULL;
73static char **qa_execstack = NULL; 70static char **qa_execstack = NULL;
74static char **qa_wx_load = NULL; 71static char **qa_wx_load = NULL;
75 72
76int match_bits = 0; 73static int match_bits = 0;
77unsigned int match_perms = 0; 74static unsigned int match_perms = 0;
78caddr_t ldcache = 0; 75static caddr_t ldcache = 0;
79size_t ldcache_size = 0; 76static size_t ldcache_size = 0;
80unsigned long setpax = 0UL; 77static unsigned long setpax = 0UL;
81 78
82int has_objdump = 0; 79static int has_objdump = 0;
83
84static char *getstr_perms(const char *fname);
85static char *getstr_perms(const char *fname) {
86 struct stat st;
87 static char buf[8];
88
89 if ((stat(fname, &st)) == (-1))
90 return (char *) "";
91
92 snprintf(buf, sizeof(buf), "%o", st.st_mode);
93
94 return (char *) buf + 2;
95}
96 80
97/* find the path to a file by name */ 81/* find the path to a file by name */
98static char *which(const char *fname) 82static char *which(const char *fname)
99{ 83{
100 static char fullpath[BUFSIZ]; 84 static char fullpath[__PAX_UTILS_PATH_MAX];
101 char *path, *p; 85 char *path, *p;
102 86
103 path = getenv("PATH"); 87 path = getenv("PATH");
104 if (!path) 88 if (!path)
105 return NULL; 89 return NULL;
106 90
107 path = xstrdup(path); 91 path = xstrdup(path);
108 while ((p = strrchr(path, ':')) != NULL) { 92 while ((p = strrchr(path, ':')) != NULL) {
109 snprintf(fullpath, sizeof(fullpath), "%s/%s", p + 1, fname); 93 snprintf(fullpath, sizeof(fullpath), "%s/%s", p + 1, fname);
110 *p = 0; 94 *p = 0;
111 if (access(fullpath, R_OK) != (-1)) { 95 if (access(fullpath, R_OK) != -1) {
112 free(path); 96 free(path);
113 return (char *) fullpath; 97 return fullpath;
114 } 98 }
115 } 99 }
116 free(path); 100 free(path);
117 return NULL; 101 return NULL;
118} 102}
123 regex_t preg; 107 regex_t preg;
124 int ret; 108 int ret;
125 109
126 if ((match == NULL) || (regex == NULL)) 110 if ((match == NULL) || (regex == NULL))
127 return EXIT_FAILURE; 111 return EXIT_FAILURE;
128
129 112
130 if ((ret = regcomp(&preg, regex, cflags))) { 113 if ((ret = regcomp(&preg, regex, cflags))) {
131 char err[256]; 114 char err[256];
132 115
133 if (regerror(ret, &preg, err, sizeof(err))) 116 if (regerror(ret, &preg, err, sizeof(err)))
202 } \ 185 } \
203 } 186 }
204 SHOW_PAX(32) 187 SHOW_PAX(32)
205 SHOW_PAX(64) 188 SHOW_PAX(64)
206 } 189 }
207
208 190
209 if (fix_elf && setpax) { 191 if (fix_elf && setpax) {
210 /* set the chpax settings */ 192 /* set the chpax settings */
211 if (elf->elf_class == ELFCLASS32) { 193 if (elf->elf_class == ELFCLASS32) {
212 if (EHDR32(elf->ehdr)->e_type == ET_DYN || EHDR32(elf->ehdr)->e_type == ET_EXEC) 194 if (EHDR32(elf->ehdr)->e_type == ET_DYN || EHDR32(elf->ehdr)->e_type == ET_EXEC)
271 warnf("%s: multiple PT_GNU_RELRO's !?", elf->filename); \ 253 warnf("%s: multiple PT_GNU_RELRO's !?", elf->filename); \
272 found = found_relro; \ 254 found = found_relro; \
273 offset = 4; \ 255 offset = 4; \
274 check_flags = PF_X; \ 256 check_flags = PF_X; \
275 } else if (EGET(phdr[i].p_type) == PT_LOAD) { \ 257 } else if (EGET(phdr[i].p_type) == PT_LOAD) { \
276 if (ehdr->e_type == ET_DYN || ehdr->e_type == ET_EXEC) \ 258 if (EGET(ehdr->e_type) == ET_DYN || EGET(ehdr->e_type) == ET_EXEC) \
277 if (multi_load++ > max_pt_load) \ 259 if (multi_load++ > max_pt_load) \
278 warnf("%s: more than %i PT_LOAD's !?", elf->filename, max_pt_load); \ 260 warnf("%s: more than %i PT_LOAD's !?", elf->filename, max_pt_load); \
279 if (file_matches_list(elf->filename, qa_wx_load)) \ 261 if (file_matches_list(elf->filename, qa_wx_load)) \
280 continue; \ 262 continue; \
281 found = found_load; \ 263 found = found_load; \
339 return NULL; 321 return NULL;
340 else 322 else
341 return ret; 323 return ret;
342} 324}
343 325
326/*
327 * See if this ELF contains a DT_TEXTREL tag in any of its
328 * PT_DYNAMIC sections.
329 */
344static const char *scanelf_file_textrel(elfobj *elf, char *found_textrel) 330static const char *scanelf_file_textrel(elfobj *elf, char *found_textrel)
345{ 331{
346 static const char *ret = "TEXTREL"; 332 static const char *ret = "TEXTREL";
347 unsigned long i; 333 unsigned long i;
348 334
379 return NULL; 365 return NULL;
380 else 366 else
381 return " - "; 367 return " - ";
382} 368}
383 369
370/*
371 * Scan the .text section to see if there are any relocations in it.
372 * Should rewrite this to check PT_LOAD sections that are marked
373 * Executable rather than the section named '.text'.
374 */
384static char *scanelf_file_textrels(elfobj *elf, char *found_textrels, char *found_textrel) 375static char *scanelf_file_textrels(elfobj *elf, char *found_textrels, char *found_textrel)
385{ 376{
386 unsigned long s, r, rmax; 377 unsigned long s, r, rmax;
387 void *symtab_void, *strtab_void, *text_void; 378 void *symtab_void, *strtab_void, *text_void;
388 379
479 printf("%s", func_name); \ 470 printf("%s", func_name); \
480 } else \ 471 } else \
481 printf("(optimized out)"); \ 472 printf("(optimized out)"); \
482 printf(" [0x%lX]\n", (unsigned long)offset_tmp); \ 473 printf(" [0x%lX]\n", (unsigned long)offset_tmp); \
483 if (be_verbose && has_objdump) { \ 474 if (be_verbose && has_objdump) { \
475 Elf ## B ## _Addr end_addr = offset_tmp + EGET(func->st_size); \
484 char *sysbuf; \ 476 char *sysbuf; \
485 size_t syslen; \ 477 size_t syslen; \
478 int sysret; \
486 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"; \ 479 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"; \
487 syslen = sizeof(sysfmt) + strlen(elf->filename) + 3 * sizeof(unsigned long) + 1; \ 480 syslen = sizeof(sysfmt) + strlen(elf->filename) + 3 * sizeof(unsigned long) + 1; \
488 sysbuf = xmalloc(syslen); \ 481 sysbuf = xmalloc(syslen); \
489 if (sysbuf) { \
490 Elf ## B ## _Addr end_addr = offset_tmp + EGET(func->st_size); \
491 if (end_addr < r_offset) \ 482 if (end_addr < r_offset) \
492 /* not uncommon when things are optimized out */ \ 483 /* not uncommon when things are optimized out */ \
493 end_addr = r_offset + 0x100; \ 484 end_addr = r_offset + 0x100; \
494 snprintf(sysbuf, syslen, sysfmt, \ 485 snprintf(sysbuf, syslen, sysfmt, \
495 (unsigned long)offset_tmp, \ 486 (unsigned long)offset_tmp, \
496 (unsigned long)end_addr, \ 487 (unsigned long)end_addr, \
497 elf->filename, \ 488 elf->filename, \
498 (unsigned long)r_offset); \ 489 (unsigned long)r_offset); \
499 fflush(stdout); \ 490 fflush(stdout); \
500 system(sysbuf); \ 491 sysret = system(sysbuf); \
501 fflush(stdout); \ 492 fflush(stdout); \
502 free(sysbuf); \ 493 free(sysbuf); \
503 } \
504 } \ 494 } \
505 } \ 495 } \
506 } } 496 } }
507 SHOW_TEXTRELS(32) 497 SHOW_TEXTRELS(32)
508 SHOW_TEXTRELS(64) 498 SHOW_TEXTRELS(64)
735 ldcache_size = st.st_size; 725 ldcache_size = st.st_size;
736 ldcache = mmap(0, ldcache_size, PROT_READ, MAP_SHARED, fd, 0); 726 ldcache = mmap(0, ldcache_size, PROT_READ, MAP_SHARED, fd, 0);
737 727
738 close(fd); 728 close(fd);
739 729
740 if (ldcache == (caddr_t)-1) { 730 if (ldcache == MAP_FAILED) {
741 ldcache = 0; 731 ldcache = 0;
742 return NULL; 732 return NULL;
743 } 733 }
744 734
745 if (memcmp(((header_t *) ldcache)->magic, LDSO_CACHE_MAGIC, LDSO_CACHE_MAGIC_LEN)) 735 if (memcmp(((header_t *) ldcache)->magic, LDSO_CACHE_MAGIC, LDSO_CACHE_MAGIC_LEN))
791 781
792 /* not found in any path */ 782 /* not found in any path */
793 return NULL; 783 return NULL;
794} 784}
795#else 785#else
786#ifdef __ELF__
796#warning Cache support not implemented for your target 787#warning Cache support not implemented for your target
788#endif
797static char *lookup_cache_lib(elfobj *elf, char *fname) 789static char *lookup_cache_lib(elfobj *elf, char *fname)
798{ 790{
799 return NULL; 791 return NULL;
800} 792}
801#endif 793#endif
942 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ 934 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
943 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ 935 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
944 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \ 936 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
945 Elf ## B ## _Off offset; \ 937 Elf ## B ## _Off offset; \
946 /* only look for soname in shared objects */ \ 938 /* only look for soname in shared objects */ \
947 if (ehdr->e_type != ET_DYN) \ 939 if (EGET(ehdr->e_type) != ET_DYN) \
948 return NULL; \ 940 return NULL; \
949 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ 941 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
950 if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) continue; \ 942 if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) continue; \
951 offset = EGET(phdr[i].p_offset); \ 943 offset = EGET(phdr[i].p_offset); \
952 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \ 944 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
970 } 962 }
971 963
972 return NULL; 964 return NULL;
973} 965}
974 966
967static int scanelf_match_symname(const char *symname, const char *tomatch) {
968 /* We do things differently when checking with regexp */
969 if (g_match) {
970 return rematch(symname, tomatch, REG_EXTENDED) == 0;
971 } else {
972 const size_t symname_len = strlen(symname);
973 return (strncmp(symname, tomatch, symname_len) == 0 &&
974 /* Accept unversioned symbol names */
975 (tomatch[symname_len] == '\0' || tomatch[symname_len] == '@'));
976 }
977}
978
975static char *scanelf_file_sym(elfobj *elf, char *found_sym) 979static char *scanelf_file_sym(elfobj *elf, char *found_sym)
976{ 980{
977 unsigned long i; 981 unsigned long i;
978 char *ret; 982 char *ret;
979 void *symtab_void, *strtab_void; 983 void *symtab_void, *strtab_void;
992 unsigned long cnt = EGET(symtab->sh_entsize); \ 996 unsigned long cnt = EGET(symtab->sh_entsize); \
993 char *symname; \ 997 char *symname; \
994 if (cnt) \ 998 if (cnt) \
995 cnt = EGET(symtab->sh_size) / cnt; \ 999 cnt = EGET(symtab->sh_size) / cnt; \
996 for (i = 0; i < cnt; ++i) { \ 1000 for (i = 0; i < cnt; ++i) { \
1001 if ((void*)sym > (void*)elf->data_end) { \
1002 warnf("%s: corrupt ELF symbols - aborting", elf->filename); \
1003 goto break_out; \
1004 } \
997 if (sym->st_name) { \ 1005 if (sym->st_name) { \
998 /* make sure the symbol name is in acceptable memory range */ \ 1006 /* make sure the symbol name is in acceptable memory range */ \
999 symname = (char *)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name)); \ 1007 symname = (char *)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name)); \
1000 if ((void*)symname > (void*)elf->data_end) { \ 1008 if ((void*)symname > (void*)elf->data_end) { \
1001 warnf("%s: corrupt ELF symbols", elf->filename); \ 1009 warnf("%s: corrupt ELF symbols", elf->filename); \
1002 ++sym; \ 1010 ++sym; \
1003 continue; \ 1011 continue; \
1004 } \ 1012 } \
1005 /* debug display ... show all symbols and some extra info */ \ 1013 /* debug display ... show all symbols and some extra info */ \
1006 if (g_match ? rematch(ret, symname, REG_EXTENDED) == 0 : *ret == '*') { \ 1014 if (0 && g_match ? rematch(ret, symname, REG_EXTENDED) == 0 : *ret == '*') { \
1007 printf("%s(%s) %5lX %15s %s %s\n", \ 1015 printf("%s(%s) %5lX %15s %s %s\n", \
1008 ((*found_sym == 0) ? "\n\t" : "\t"), \ 1016 ((*found_sym == 0) ? "\n\t" : "\t"), \
1009 elf->base_filename, \ 1017 elf->base_filename, \
1010 (unsigned long)sym->st_size, \ 1018 (unsigned long)sym->st_size, \
1011 get_elfstttype(sym->st_info), \ 1019 get_elfstttype(sym->st_info), \
1012 sym->st_shndx == SHN_UNDEF ? "U" : "D", symname); \ 1020 sym->st_shndx == SHN_UNDEF ? "U" : "D", symname); \
1013 *found_sym = 1; \ 1021 *found_sym = 1; \
1014 } else { \ 1022 } else { \
1015 /* allow the user to specify a comma delimited list of symbols to search for */ \ 1023 /* allow the user to specify a comma delimited list of symbols to search for */ \
1016 char *this_sym, *this_sym_ver, *next_sym; \ 1024 char *this_sym, *next_sym; \
1017 this_sym = ret; \ 1025 next_sym = ret; \
1018 this_sym_ver = versioned_symname; \ 1026 while (next_sym) { \
1019 do { \ 1027 this_sym = next_sym; \
1020 next_sym = strchr(this_sym, ','); \ 1028 if ((next_sym = strchr(this_sym, ','))) \
1021 if (next_sym == NULL) \ 1029 next_sym += 1; /* Skip the comma */ \
1022 next_sym = this_sym + strlen(this_sym); \
1023 /* do we want a defined symbol ? */ \ 1030 /* do we want a defined symbol ? */ \
1024 if (*this_sym == '+') { \ 1031 if (*this_sym == '+') { \
1025 if (sym->st_shndx == SHN_UNDEF) \ 1032 if (sym->st_shndx == SHN_UNDEF) \
1026 goto skip_this_sym##B; \ 1033 continue; \
1027 ++this_sym; \ 1034 ++this_sym; \
1028 ++this_sym_ver; \
1029 /* do we want an undefined symbol ? */ \ 1035 /* do we want an undefined symbol ? */ \
1030 } else if (*this_sym == '-') { \ 1036 } else if (*this_sym == '-') { \
1031 if (sym->st_shndx != SHN_UNDEF) \ 1037 if (sym->st_shndx != SHN_UNDEF) \
1032 goto skip_this_sym##B; \ 1038 continue; \
1033 ++this_sym; \ 1039 ++this_sym; \
1034 ++this_sym_ver; \
1035 } \ 1040 } \
1041 if (next_sym) /* Copy it so that we don't have to worry about the final , */ \
1042 this_sym = xstrndup(this_sym, next_sym-this_sym); \
1036 /* ok, lets compare the name now */ \ 1043 /* ok, lets compare the name now */ \
1037 if ((strncmp(this_sym, symname, (next_sym-this_sym)) == 0 && symname[next_sym-this_sym] == '\0') || \ 1044 if (scanelf_match_symname(this_sym, symname)) { \
1038 (strncmp(this_sym_ver, symname, strlen(this_sym_ver)) == 0)) { \
1039 if (be_semi_verbose) { \ 1045 if (be_semi_verbose) { \
1040 char buf[126]; \ 1046 char buf[126]; \
1041 snprintf(buf, sizeof(buf), "%lX %s %s", \ 1047 snprintf(buf, sizeof(buf), "%lX %s %s", \
1042 (unsigned long)sym->st_size, get_elfstttype(sym->st_info), this_sym); \ 1048 (unsigned long)sym->st_size, get_elfstttype(sym->st_info), this_sym); \
1043 ret = buf; \ 1049 ret = buf; \
1044 } else \ 1050 } else \
1045 ret = this_sym; \ 1051 ret = symname; \
1046 (*found_sym)++; \ 1052 (*found_sym)++; \
1047 goto break_out; \ 1053 goto break_out; \
1048 } \ 1054 } \
1049 skip_this_sym##B: this_sym = next_sym + 1; \ 1055 if (next_sym) free(this_sym); \
1050 } while (*next_sym != '\0'); \ 1056 } \
1051 } \ 1057 } \
1052 } \ 1058 } \
1053 ++sym; \ 1059 ++sym; \
1054 } } 1060 } }
1055 FIND_SYM(32) 1061 FIND_SYM(32)
1064 if (be_quiet) 1070 if (be_quiet)
1065 return NULL; 1071 return NULL;
1066 else 1072 else
1067 return (char *)" - "; 1073 return (char *)" - ";
1068} 1074}
1069
1070 1075
1071static char *scanelf_file_sections(elfobj *elf, char *found_section) 1076static char *scanelf_file_sections(elfobj *elf, char *found_section)
1072{ 1077{
1073 if (!find_section) 1078 if (!find_section)
1074 return NULL; 1079 return NULL;
1120 printf("%s: scanning file\n", elf->filename); 1125 printf("%s: scanning file\n", elf->filename);
1121 1126
1122 /* init output buffer */ 1127 /* init output buffer */
1123 if (!out_buffer) { 1128 if (!out_buffer) {
1124 out_len = sizeof(char) * 80; 1129 out_len = sizeof(char) * 80;
1125 out_buffer = (char*)xmalloc(out_len); 1130 out_buffer = xmalloc(out_len);
1126 } 1131 }
1127 *out_buffer = '\0'; 1132 *out_buffer = '\0';
1128 1133
1129 /* show the header */ 1134 /* show the header */
1130 if (!be_quiet && show_banner) { 1135 if (!be_quiet && show_banner) {
1145 case 'r': prints("RPATH "); break; 1150 case 'r': prints("RPATH "); break;
1146 case 'M': prints("CLASS "); break; 1151 case 'M': prints("CLASS "); break;
1147 case 'n': prints("NEEDED "); break; 1152 case 'n': prints("NEEDED "); break;
1148 case 'i': prints("INTERP "); break; 1153 case 'i': prints("INTERP "); break;
1149 case 'b': prints("BIND "); break; 1154 case 'b': prints("BIND "); break;
1155 case 'Z': prints("SIZE "); break;
1150 case 'S': prints("SONAME "); break; 1156 case 'S': prints("SONAME "); break;
1151 case 's': prints("SYM "); break; 1157 case 's': prints("SYM "); break;
1152 case 'N': prints("LIB "); break; 1158 case 'N': prints("LIB "); break;
1153 case 'T': prints("TEXTRELS "); break; 1159 case 'T': prints("TEXTRELS "); break;
1154 case 'k': prints("SECTION "); break; 1160 case 'k': prints("SECTION "); break;
1155 case 'a': prints("ARCH "); break; 1161 case 'a': prints("ARCH "); break;
1162 case 'I': prints("OSABI "); break;
1163 case 'Y': prints("EABI "); break;
1156 case 'O': prints("PERM "); break; 1164 case 'O': prints("PERM "); break;
1157 case 'D': prints("ENDIAN "); break; 1165 case 'D': prints("ENDIAN "); break;
1158 default: warnf("'%c' has no title ?", out_format[i]); 1166 default: warnf("'%c' has no title ?", out_format[i]);
1159 } 1167 }
1160 } 1168 }
1166 1174
1167 /* dump all the good stuff */ 1175 /* dump all the good stuff */
1168 for (i = 0; out_format[i]; ++i) { 1176 for (i = 0; out_format[i]; ++i) {
1169 const char *out; 1177 const char *out;
1170 const char *tmp; 1178 const char *tmp;
1171 1179 static char ubuf[sizeof(unsigned long)*2];
1172 if (!IS_MODIFIER(out_format[i])) { 1180 if (!IS_MODIFIER(out_format[i])) {
1173 xchrcat(&out_buffer, out_format[i], &out_len); 1181 xchrcat(&out_buffer, out_format[i], &out_len);
1174 continue; 1182 continue;
1175 } 1183 }
1176 1184
1214 case 't': out = scanelf_file_textrel(elf, &found_textrel); break; 1222 case 't': out = scanelf_file_textrel(elf, &found_textrel); break;
1215 case 'T': out = scanelf_file_textrels(elf, &found_textrels, &found_textrel); break; 1223 case 'T': out = scanelf_file_textrels(elf, &found_textrels, &found_textrel); break;
1216 case 'r': scanelf_file_rpath(elf, &found_rpath, &out_buffer, &out_len); break; 1224 case 'r': scanelf_file_rpath(elf, &found_rpath, &out_buffer, &out_len); break;
1217 case 'M': out = get_elfeitype(EI_CLASS, elf->data[EI_CLASS]); break; 1225 case 'M': out = get_elfeitype(EI_CLASS, elf->data[EI_CLASS]); break;
1218 case 'D': out = get_endian(elf); break; 1226 case 'D': out = get_endian(elf); break;
1219 case 'O': out = getstr_perms(elf->filename); break; 1227 case 'O': out = strfileperms(elf->filename); break;
1220 case 'n': 1228 case 'n':
1221 case 'N': out = scanelf_file_needed_lib(elf, &found_needed, &found_lib, (out_format[i]=='N'), &out_buffer, &out_len); break; 1229 case 'N': out = scanelf_file_needed_lib(elf, &found_needed, &found_lib, (out_format[i]=='N'), &out_buffer, &out_len); break;
1222 case 'i': out = scanelf_file_interp(elf, &found_interp); break; 1230 case 'i': out = scanelf_file_interp(elf, &found_interp); break;
1223 case 'b': out = scanelf_file_bind(elf, &found_bind); break; 1231 case 'b': out = scanelf_file_bind(elf, &found_bind); break;
1224 case 'S': out = scanelf_file_soname(elf, &found_soname); break; 1232 case 'S': out = scanelf_file_soname(elf, &found_soname); break;
1225 case 's': out = scanelf_file_sym(elf, &found_sym); break; 1233 case 's': out = scanelf_file_sym(elf, &found_sym); break;
1226 case 'k': out = scanelf_file_sections(elf, &found_section); break; 1234 case 'k': out = scanelf_file_sections(elf, &found_section); break;
1227 case 'a': out = get_elfemtype(elf); break; 1235 case 'a': out = get_elfemtype(elf); break;
1236 case 'I': out = get_elfosabi(elf); break;
1237 case 'Y': out = get_elf_eabi(elf); break;
1238 case 'Z': snprintf(ubuf, sizeof(ubuf), "%lu", (unsigned long)elf->len); out = ubuf; break;;
1228 default: warnf("'%c' has no scan code?", out_format[i]); 1239 default: warnf("'%c' has no scan code?", out_format[i]);
1229 } 1240 }
1230 if (out) { 1241 if (out) {
1231 /* hack for comma delimited output like `scanelf -s sym1,sym2,sym3` */ 1242 /* hack for comma delimited output like `scanelf -s sym1,sym2,sym3` */
1232 if (out_format[i] == 's' && (tmp=strchr(out,',')) != NULL) 1243 if (out_format[i] == 's' && (tmp=strchr(out,',')) != NULL)
1278 if (strlen(match_etypes)) { 1289 if (strlen(match_etypes)) {
1279 char sbuf[126]; 1290 char sbuf[126];
1280 strncpy(sbuf, match_etypes, sizeof(sbuf)); 1291 strncpy(sbuf, match_etypes, sizeof(sbuf));
1281 if (strchr(match_etypes, ',') != NULL) { 1292 if (strchr(match_etypes, ',') != NULL) {
1282 char *p; 1293 char *p;
1283 while((p = strrchr(sbuf, ',')) != NULL) { 1294 while ((p = strrchr(sbuf, ',')) != NULL) {
1284 *p = 0; 1295 *p = 0;
1285 if (etype_lookup(p+1) == get_etype(elf)) 1296 if (etype_lookup(p+1) == get_etype(elf))
1286 goto label_ret; 1297 goto label_ret;
1287 } 1298 }
1288 } 1299 }
1308 1319
1309 ar = ar_open_fd(filename, fd); 1320 ar = ar_open_fd(filename, fd);
1310 if (ar == NULL) 1321 if (ar == NULL)
1311 return 1; 1322 return 1;
1312 1323
1313 ar_buffer = (char*)mmap(0, len, PROT_READ | (fix_elf ? PROT_WRITE : 0), (fix_elf ? MAP_SHARED : MAP_PRIVATE), fd, 0); 1324 ar_buffer = mmap(0, len, PROT_READ | (fix_elf ? PROT_WRITE : 0), (fix_elf ? MAP_SHARED : MAP_PRIVATE), fd, 0);
1314 while ((m=ar_next(ar)) != NULL) { 1325 while ((m=ar_next(ar)) != NULL) {
1315 elf = readelf_buffer(m->name, ar_buffer+lseek(fd,0,SEEK_CUR), m->size); 1326 elf = readelf_buffer(m->name, ar_buffer+lseek(fd,0,SEEK_CUR), m->size);
1316 if (elf) { 1327 if (elf) {
1317 scanelf_elfobj(elf); 1328 scanelf_elfobj(elf);
1318 unreadelf(elf); 1329 unreadelf(elf);
1449 if ((p = strrchr(path, '\r')) != NULL) 1460 if ((p = strrchr(path, '\r')) != NULL)
1450 *p = 0; 1461 *p = 0;
1451 if ((p = strchr(path, '\n')) != NULL) 1462 if ((p = strchr(path, '\n')) != NULL)
1452 *p = 0; 1463 *p = 0;
1453#ifdef __linux__ 1464#ifdef __linux__
1454 // recursive includes of the same file will make this segfault. 1465 /* recursive includes of the same file will make this segfault. */
1455 if ((memcmp(path, "include", 7) == 0) && isblank(path[7])) { 1466 if ((memcmp(path, "include", 7) == 0) && isblank(path[7])) {
1456 glob64_t gl; 1467 glob64_t gl;
1457 size_t x; 1468 size_t x;
1458 char gpath[__PAX_UTILS_PATH_MAX]; 1469 char gpath[__PAX_UTILS_PATH_MAX];
1459 1470
1514 { 1525 {
1515 fclose(fp); 1526 fclose(fp);
1516 return i; 1527 return i;
1517 } 1528 }
1518 1529
1519 b = (char*)malloc(hdr.dirlistlen+1); 1530 b = xmalloc(hdr.dirlistlen + 1);
1520 if (fread(b, 1, hdr.dirlistlen+1, fp) != hdr.dirlistlen+1) { 1531 if (fread(b, 1, hdr.dirlistlen+1, fp) != hdr.dirlistlen+1) {
1521 fclose(fp); 1532 fclose(fp);
1522 free(b); 1533 free(b);
1523 return i; 1534 return i;
1524 } 1535 }
1536 fclose(fp); 1547 fclose(fp);
1537 return i; 1548 return i;
1538} 1549}
1539 1550
1540#else 1551#else
1541 1552#ifdef __ELF__
1542#warning Cache config support not implemented for your target 1553#warning Cache config support not implemented for your target
1554#endif
1543static int load_ld_cache_config(int i, const char *fname) 1555static int load_ld_cache_config(int i, const char *fname)
1544{ 1556{
1545 memset(ldpaths, 0x00, sizeof(ldpaths)); 1557 memset(ldpaths, 0x00, sizeof(ldpaths));
1558 return 0;
1546} 1559}
1547
1548#endif 1560#endif
1549 1561
1550/* scan /etc/ld.so.conf for paths */ 1562/* scan /etc/ld.so.conf for paths */
1551static void scanelf_ldpath(void) 1563static void scanelf_ldpath(void)
1552{ 1564{
1587 } 1599 }
1588 1600
1589 free(path); 1601 free(path);
1590} 1602}
1591 1603
1592
1593/* usage / invocation handling functions */ /* Free Flags: c d j u w C G H I J K P Q U W Y Z */ 1604/* usage / invocation handling functions */ /* Free Flags: c d j u w C G H J K P Q U W */
1594#define PARSE_FLAGS "plRmyAXz:xetrnLibSs:k:gN:TaqvF:f:o:E:M:DO:BhV" 1605#define PARSE_FLAGS "plRmyAXz:xetrnLibSs:k:gN:TaqvF:f:o:E:M:DIYO:ZBhV"
1595#define a_argument required_argument 1606#define a_argument required_argument
1596static struct option const long_opts[] = { 1607static struct option const long_opts[] = {
1597 {"path", no_argument, NULL, 'p'}, 1608 {"path", no_argument, NULL, 'p'},
1598 {"ldpath", no_argument, NULL, 'l'}, 1609 {"ldpath", no_argument, NULL, 'l'},
1599 {"recursive", no_argument, NULL, 'R'}, 1610 {"recursive", no_argument, NULL, 'R'},
1617 {"gmatch", no_argument, NULL, 'g'}, 1628 {"gmatch", no_argument, NULL, 'g'},
1618 {"textrels", no_argument, NULL, 'T'}, 1629 {"textrels", no_argument, NULL, 'T'},
1619 {"etype", a_argument, NULL, 'E'}, 1630 {"etype", a_argument, NULL, 'E'},
1620 {"bits", a_argument, NULL, 'M'}, 1631 {"bits", a_argument, NULL, 'M'},
1621 {"endian", no_argument, NULL, 'D'}, 1632 {"endian", no_argument, NULL, 'D'},
1633 {"osabi", no_argument, NULL, 'I'},
1634 {"eabi", no_argument, NULL, 'Y'},
1622 {"perms", a_argument, NULL, 'O'}, 1635 {"perms", a_argument, NULL, 'O'},
1636 {"size", no_argument, NULL, 'Z'},
1623 {"all", no_argument, NULL, 'a'}, 1637 {"all", no_argument, NULL, 'a'},
1624 {"quiet", no_argument, NULL, 'q'}, 1638 {"quiet", no_argument, NULL, 'q'},
1625 {"verbose", no_argument, NULL, 'v'}, 1639 {"verbose", no_argument, NULL, 'v'},
1626 {"format", a_argument, NULL, 'F'}, 1640 {"format", a_argument, NULL, 'F'},
1627 {"from", a_argument, NULL, 'f'}, 1641 {"from", a_argument, NULL, 'f'},
1656 "Use strncmp to match libraries. (use with -N)", 1670 "Use strncmp to match libraries. (use with -N)",
1657 "Locate cause of TEXTREL", 1671 "Locate cause of TEXTREL",
1658 "Print only ELF files matching etype ET_DYN,ET_EXEC ...", 1672 "Print only ELF files matching etype ET_DYN,ET_EXEC ...",
1659 "Print only ELF files matching numeric bits", 1673 "Print only ELF files matching numeric bits",
1660 "Print Endianness", 1674 "Print Endianness",
1675 "Print OSABI",
1676 "Print EABI (EM_ARM Only)",
1661 "Print only ELF files matching octal permissions", 1677 "Print only ELF files matching octal permissions",
1678 "Print ELF file size",
1662 "Print all scanned info (-x -e -t -r -b)\n", 1679 "Print all scanned info (-x -e -t -r -b)\n",
1663 "Only output 'bad' things", 1680 "Only output 'bad' things",
1664 "Be verbose (can be specified more than once)", 1681 "Be verbose (can be specified more than once)",
1665 "Use specified format for output", 1682 "Use specified format for output",
1666 "Read input stream from a filename", 1683 "Read input stream from a filename",
1731 if (strcmp(optarg, "ELFCLASS64") == 0) 1748 if (strcmp(optarg, "ELFCLASS64") == 0)
1732 match_bits = 64; 1749 match_bits = 64;
1733 } 1750 }
1734 break; 1751 break;
1735 case 'O': 1752 case 'O':
1736 if (sscanf(optarg, "%o", &match_perms) == (-1)) 1753 if (sscanf(optarg, "%o", &match_perms) == -1)
1737 match_bits = 0; 1754 match_bits = 0;
1738 break; 1755 break;
1739 case 'o': { 1756 case 'o': {
1740 if (freopen(optarg, "w", stdout) == NULL) 1757 if (freopen(optarg, "w", stdout) == NULL)
1741 err("Could not open output stream '%s': %s", optarg, strerror(errno)); 1758 err("Could not open output stream '%s': %s", optarg, strerror(errno));
1746 find_section = optarg; 1763 find_section = optarg;
1747 break; 1764 break;
1748 case 's': { 1765 case 's': {
1749 if (find_sym) warn("You prob don't want to specify -s twice"); 1766 if (find_sym) warn("You prob don't want to specify -s twice");
1750 find_sym = optarg; 1767 find_sym = optarg;
1751 versioned_symname = (char*)xmalloc(sizeof(char) * (strlen(find_sym)+1+1));
1752 sprintf(versioned_symname, "%s@", find_sym);
1753 break; 1768 break;
1754 } 1769 }
1755 case 'N': { 1770 case 'N': {
1756 if (find_lib) warn("You prob don't want to specify -N twice"); 1771 if (find_lib) warn("You prob don't want to specify -N twice");
1757 find_lib = optarg; 1772 find_lib = optarg;
1765 } 1780 }
1766 case 'z': { 1781 case 'z': {
1767 unsigned long flags = (PF_NOEMUTRAMP | PF_NORANDEXEC); 1782 unsigned long flags = (PF_NOEMUTRAMP | PF_NORANDEXEC);
1768 size_t x; 1783 size_t x;
1769 1784
1770 for (x = 0 ; x < strlen(optarg); x++) { 1785 for (x = 0; x < strlen(optarg); x++) {
1771 switch(optarg[x]) { 1786 switch (optarg[x]) {
1772 case 'p': 1787 case 'p':
1773 case 'P': 1788 case 'P':
1774 do_pax_state(optarg[x], PAGEEXEC); 1789 do_pax_state(optarg[x], PAGEEXEC);
1775 break; 1790 break;
1776 case 's': 1791 case 's':
1804 ((flags & PF_EMUTRAMP) && (flags & PF_NOEMUTRAMP)) || 1819 ((flags & PF_EMUTRAMP) && (flags & PF_NOEMUTRAMP)) ||
1805 ((flags & PF_RANDMMAP) && (flags & PF_NORANDMMAP)))) 1820 ((flags & PF_RANDMMAP) && (flags & PF_NORANDMMAP))))
1806 setpax = flags; 1821 setpax = flags;
1807 break; 1822 break;
1808 } 1823 }
1824 case 'Z': show_size = 1; break;
1809 case 'g': g_match = 1; break; 1825 case 'g': g_match = 1; break;
1810 case 'L': use_ldcache = 1; break; 1826 case 'L': use_ldcache = 1; break;
1811 case 'y': scan_symlink = 0; break; 1827 case 'y': scan_symlink = 0; break;
1812 case 'A': scan_archives = 1; break; 1828 case 'A': scan_archives = 1; break;
1813 case 'B': show_banner = 0; break; 1829 case 'B': show_banner = 0; break;
1827 case 'T': show_textrels = 1; break; 1843 case 'T': show_textrels = 1; break;
1828 case 'q': be_quiet = 1; break; 1844 case 'q': be_quiet = 1; break;
1829 case 'v': be_verbose = (be_verbose % 20) + 1; break; 1845 case 'v': be_verbose = (be_verbose % 20) + 1; break;
1830 case 'a': show_perms = show_pax = show_phdr = show_textrel = show_rpath = show_bind = show_endian = 1; break; 1846 case 'a': show_perms = show_pax = show_phdr = show_textrel = show_rpath = show_bind = show_endian = 1; break;
1831 case 'D': show_endian = 1; break; 1847 case 'D': show_endian = 1; break;
1848 case 'I': show_osabi = 1; break;
1849 case 'Y': show_eabi = 1; break;
1832 case ':': 1850 case ':':
1833 err("Option '%c' is missing parameter", optopt); 1851 err("Option '%c' is missing parameter", optopt);
1834 case '?': 1852 case '?':
1835 err("Unknown option '%c' or argument missing", optopt); 1853 err("Unknown option '%c' or argument missing", optopt);
1836 default: 1854 default:
1843 } 1861 }
1844 /* let the format option override all other options */ 1862 /* let the format option override all other options */
1845 if (out_format) { 1863 if (out_format) {
1846 show_pax = show_phdr = show_textrel = show_rpath = \ 1864 show_pax = show_phdr = show_textrel = show_rpath = \
1847 show_needed = show_interp = show_bind = show_soname = \ 1865 show_needed = show_interp = show_bind = show_soname = \
1848 show_textrels = show_perms = show_endian = 0; 1866 show_textrels = show_perms = show_endian = show_size = \
1867 show_osabi = show_eabi = 0;
1849 for (i = 0; out_format[i]; ++i) { 1868 for (i = 0; out_format[i]; ++i) {
1850 if (!IS_MODIFIER(out_format[i])) continue; 1869 if (!IS_MODIFIER(out_format[i])) continue;
1851 1870
1852 switch (out_format[++i]) { 1871 switch (out_format[++i]) {
1853 case '+': break; 1872 case '+': break;
1860 case 's': break; 1879 case 's': break;
1861 case 'N': break; 1880 case 'N': break;
1862 case 'o': break; 1881 case 'o': break;
1863 case 'a': break; 1882 case 'a': break;
1864 case 'M': break; 1883 case 'M': break;
1884 case 'Z': show_size = 1; break;
1865 case 'D': show_endian = 1; break; 1885 case 'D': show_endian = 1; break;
1886 case 'I': show_osabi = 1; break;
1887 case 'Y': show_eabi = 1; break;
1866 case 'O': show_perms = 1; break; 1888 case 'O': show_perms = 1; break;
1867 case 'x': show_pax = 1; break; 1889 case 'x': show_pax = 1; break;
1868 case 'e': show_phdr = 1; break; 1890 case 'e': show_phdr = 1; break;
1869 case 't': show_textrel = 1; break; 1891 case 't': show_textrel = 1; break;
1870 case 'r': show_rpath = 1; break; 1892 case 'r': show_rpath = 1; break;
1880 } 1902 }
1881 1903
1882 /* construct our default format */ 1904 /* construct our default format */
1883 } else { 1905 } else {
1884 size_t fmt_len = 30; 1906 size_t fmt_len = 30;
1885 out_format = (char*)xmalloc(sizeof(char) * fmt_len); 1907 out_format = xmalloc(sizeof(char) * fmt_len);
1908 *out_format = '\0';
1886 if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len); 1909 if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len);
1887 if (show_pax) xstrcat(&out_format, "%x ", &fmt_len); 1910 if (show_pax) xstrcat(&out_format, "%x ", &fmt_len);
1888 if (show_perms) xstrcat(&out_format, "%O ", &fmt_len); 1911 if (show_perms) xstrcat(&out_format, "%O ", &fmt_len);
1912 if (show_size) xstrcat(&out_format, "%Z ", &fmt_len);
1889 if (show_endian) xstrcat(&out_format, "%D ", &fmt_len); 1913 if (show_endian) xstrcat(&out_format, "%D ", &fmt_len);
1914 if (show_osabi) xstrcat(&out_format, "%I ", &fmt_len);
1915 if (show_eabi) xstrcat(&out_format, "%Y ", &fmt_len);
1890 if (show_phdr) xstrcat(&out_format, "%e ", &fmt_len); 1916 if (show_phdr) xstrcat(&out_format, "%e ", &fmt_len);
1891 if (show_textrel) xstrcat(&out_format, "%t ", &fmt_len); 1917 if (show_textrel) xstrcat(&out_format, "%t ", &fmt_len);
1892 if (show_rpath) xstrcat(&out_format, "%r ", &fmt_len); 1918 if (show_rpath) xstrcat(&out_format, "%r ", &fmt_len);
1893 if (show_needed) xstrcat(&out_format, "%n ", &fmt_len); 1919 if (show_needed) xstrcat(&out_format, "%n ", &fmt_len);
1894 if (show_interp) xstrcat(&out_format, "%i ", &fmt_len); 1920 if (show_interp) xstrcat(&out_format, "%i ", &fmt_len);
1919 search_path = argv[optind++]; 1945 search_path = argv[optind++];
1920 ret = scanelf_dir(search_path); 1946 ret = scanelf_dir(search_path);
1921 } 1947 }
1922 1948
1923 /* clean up */ 1949 /* clean up */
1924 free(versioned_symname);
1925 for (i = 0; ldpaths[i]; ++i) 1950 for (i = 0; ldpaths[i]; ++i)
1926 free(ldpaths[i]); 1951 free(ldpaths[i]);
1927 1952
1928 if (ldcache != 0) 1953 if (ldcache != 0)
1929 munmap(ldcache, ldcache_size); 1954 munmap(ldcache, ldcache_size);
1978 free(qa_textrels); 2003 free(qa_textrels);
1979 free(qa_execstack); 2004 free(qa_execstack);
1980 free(qa_wx_load); 2005 free(qa_wx_load);
1981} 2006}
1982#endif 2007#endif
1983
1984 2008
1985int main(int argc, char *argv[]) 2009int main(int argc, char *argv[])
1986{ 2010{
1987 int ret; 2011 int ret;
1988 if (argc < 2) 2012 if (argc < 2)
1997 "\t- 1 per QA_TEXTRELS/QA_EXECSTACK/QA_WX_LOAD"); 2021 "\t- 1 per QA_TEXTRELS/QA_EXECSTACK/QA_WX_LOAD");
1998#endif 2022#endif
1999 return ret; 2023 return ret;
2000} 2024}
2001 2025
2002
2003
2004/* utility funcs */
2005static char *xstrdup(const char *s)
2006{
2007 char *ret = strdup(s);
2008 if (!ret) err("Could not strdup(): %s", strerror(errno));
2009 return ret;
2010}
2011static void *xmalloc(size_t size)
2012{
2013 void *ret = malloc(size);
2014 if (!ret) err("Could not malloc() %li bytes", (unsigned long)size);
2015 return ret;
2016}
2017static void *xrealloc(void *ptr, size_t size)
2018{
2019 void *ret = realloc(ptr, size);
2020 if (!ret) err("Could not realloc() %li bytes", (unsigned long)size);
2021 return ret;
2022}
2023static void xstrncat(char **dst, const char *src, size_t *curr_len, size_t n)
2024{
2025 size_t new_len;
2026
2027 new_len = strlen(*dst) + strlen(src);
2028 if (*curr_len <= new_len) {
2029 *curr_len = new_len + (*curr_len / 2);
2030 *dst = realloc(*dst, *curr_len);
2031 if (!*dst)
2032 err("could not realloc() %li bytes", (unsigned long)*curr_len);
2033 }
2034
2035 if (n)
2036 strncat(*dst, src, n);
2037 else
2038 strcat(*dst, src);
2039}
2040static inline void xchrcat(char **dst, const char append, size_t *curr_len)
2041{
2042 static char my_app[2];
2043 my_app[0] = append;
2044 my_app[1] = '\0';
2045 xstrcat(dst, my_app, curr_len);
2046}
2047
2048/* Match filename against entries in matchlist, return TRUE 2026/* Match filename against entries in matchlist, return TRUE
2049 * if the file is listed */ 2027 * if the file is listed */
2050static int file_matches_list(const char *filename, char **matchlist) 2028static int file_matches_list(const char *filename, char **matchlist)
2051{ 2029{
2052 char **file; 2030 char **file;

Legend:
Removed from v.1.185  
changed lines
  Added in v.1.209

  ViewVC Help
Powered by ViewVC 1.1.20