/[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.93 Revision 1.127
1/* 1/*
2 * Copyright 2003-2005 Gentoo Foundation 2 * Copyright 2003-2006 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.93 2005/12/10 04:07:55 vapier Exp $ 4 * $Header: /var/cvsroot/gentoo-projects/pax-utils/Attic/scanelf.c,v 1.127 2006/02/17 07:13:54 solar Exp $
5 * 5 *
6 * Copyright 2003-2005 Ned Ludd - <solar@gentoo.org> 6 * Copyright 2003-2006 Ned Ludd - <solar@gentoo.org>
7 * Copyright 2004-2005 Mike Frysinger - <vapier@gentoo.org> 7 * Copyright 2004-2006 Mike Frysinger - <vapier@gentoo.org>
8 */ 8 */
9 9
10#include <stdio.h>
11#include <stdlib.h>
12#include <sys/types.h>
13#include <libgen.h>
14#include <limits.h>
15#include <string.h>
16#include <errno.h>
17#include <unistd.h>
18#include <sys/stat.h>
19#include <dirent.h>
20#include <getopt.h>
21#include <assert.h>
22#include "paxinc.h" 10#include "paxinc.h"
23 11
24static const char *rcsid = "$Id: scanelf.c,v 1.93 2005/12/10 04:07:55 vapier Exp $"; 12static const char *rcsid = "$Id: scanelf.c,v 1.127 2006/02/17 07:13:54 solar Exp $";
25#define argv0 "scanelf" 13#define argv0 "scanelf"
26 14
27#define IS_MODIFIER(c) (c == '%' || c == '#') 15#define IS_MODIFIER(c) (c == '%' || c == '#')
28 16
29 17
30 18
31/* prototypes */ 19/* prototypes */
20static int scanelf_elfobj(elfobj *elf);
21static int scanelf_elf(const char *filename, int fd, size_t len);
22static int scanelf_archive(const char *filename, int fd, size_t len);
32static void scanelf_file(const char *filename); 23static void scanelf_file(const char *filename);
33static void scanelf_dir(const char *path); 24static void scanelf_dir(const char *path);
34static void scanelf_ldpath(); 25static void scanelf_ldpath(void);
35static void scanelf_envpath(); 26static void scanelf_envpath(void);
36static void usage(int status); 27static void usage(int status);
37static void parseargs(int argc, char *argv[]); 28static void parseargs(int argc, char *argv[]);
38static char *xstrdup(const char *s); 29static char *xstrdup(const char *s);
39static void *xmalloc(size_t size); 30static void *xmalloc(size_t size);
40static void xstrcat(char **dst, const char *src, size_t *curr_len); 31static void xstrncat(char **dst, const char *src, size_t *curr_len, size_t n);
32#define xstrcat(dst,src,curr_len) xstrncat(dst,src,curr_len,0)
41static inline void xchrcat(char **dst, const char append, size_t *curr_len); 33static inline void xchrcat(char **dst, const char append, size_t *curr_len);
42 34
43/* variables to control behavior */ 35/* variables to control behavior */
36static char match_etypes[126] = "";
44static char *ldpaths[256]; 37static char *ldpaths[256];
45static char scan_ldpath = 0; 38static char scan_ldpath = 0;
46static char scan_envpath = 0; 39static char scan_envpath = 0;
47static char scan_symlink = 1; 40static char scan_symlink = 1;
41static char scan_archives = 0;
48static char dir_recurse = 0; 42static char dir_recurse = 0;
49static char dir_crossmount = 1; 43static char dir_crossmount = 1;
50static char show_pax = 0; 44static char show_pax = 0;
51static char show_phdr = 0; 45static char show_phdr = 0;
52static char show_textrel = 0; 46static char show_textrel = 0;
60static char be_quiet = 0; 54static char be_quiet = 0;
61static char be_verbose = 0; 55static char be_verbose = 0;
62static char be_wewy_wewy_quiet = 0; 56static char be_wewy_wewy_quiet = 0;
63static char *find_sym = NULL, *versioned_symname = NULL; 57static char *find_sym = NULL, *versioned_symname = NULL;
64static char *find_lib = NULL; 58static char *find_lib = NULL;
59static char *find_section = NULL;
65static char *out_format = NULL; 60static char *out_format = NULL;
66static char *search_path = NULL; 61static char *search_path = NULL;
62static char fix_elf = 0;
67static char gmatch = 0; 63static char gmatch = 0;
64static char use_ldcache = 0;
68 65
66int match_bits = 0;
67caddr_t ldcache = 0;
68size_t ldcache_size = 0;
69unsigned long setpax = 0UL;
69 70
70/* sub-funcs for scanelf_file() */ 71/* sub-funcs for scanelf_file() */
71static void scanelf_file_get_symtabs(elfobj *elf, void **sym, void **tab) 72static void scanelf_file_get_symtabs(elfobj *elf, void **sym, void **tab)
72{ 73{
73 /* find the best SHT_DYNSYM and SHT_STRTAB sections */ 74 /* find the best SHT_DYNSYM and SHT_STRTAB sections */
92 } \ 93 } \
93 } 94 }
94 GET_SYMTABS(32) 95 GET_SYMTABS(32)
95 GET_SYMTABS(64) 96 GET_SYMTABS(64)
96} 97}
98
97static char *scanelf_file_pax(elfobj *elf, char *found_pax) 99static char *scanelf_file_pax(elfobj *elf, char *found_pax)
98{ 100{
99 static char ret[7]; 101 static char ret[7];
100 unsigned long i, shown; 102 unsigned long i, shown;
101 103
110 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ 112 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
111 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ 113 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
112 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ 114 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
113 if (EGET(phdr[i].p_type) != PT_PAX_FLAGS) \ 115 if (EGET(phdr[i].p_type) != PT_PAX_FLAGS) \
114 continue; \ 116 continue; \
117 if (fix_elf && setpax) { \
118 /* set the paxctl flags */ \
119 ESET(phdr[i].p_flags, setpax); \
120 } \
115 if (be_quiet && (EGET(phdr[i].p_flags) == 10240)) \ 121 if (be_quiet && (EGET(phdr[i].p_flags) == 10240)) \
116 continue; \ 122 continue; \
117 memcpy(ret, pax_short_pf_flags(EGET(phdr[i].p_flags)), 6); \ 123 memcpy(ret, pax_short_pf_flags(EGET(phdr[i].p_flags)), 6); \
118 *found_pax = 1; \ 124 *found_pax = 1; \
119 ++shown; \ 125 ++shown; \
125 } 131 }
126 132
127 /* fall back to EI_PAX if no PT_PAX was found */ 133 /* fall back to EI_PAX if no PT_PAX was found */
128 if (!*ret) { 134 if (!*ret) {
129 static char *paxflags; 135 static char *paxflags;
136
137 if (fix_elf && setpax) {
138 /* set the chpax settings */
139 // ESET(EHDR ## B (elf->ehdr)->e_ident[EI_PAX]), setpax);
140 }
141
130 paxflags = pax_short_hf_flags(EI_PAX_FLAGS(elf)); 142 paxflags = pax_short_hf_flags(EI_PAX_FLAGS(elf));
131 if (!be_quiet || (be_quiet && EI_PAX_FLAGS(elf))) { 143 if (!be_quiet || (be_quiet && EI_PAX_FLAGS(elf))) {
132 *found_pax = 1; 144 *found_pax = 1;
133 return (be_wewy_wewy_quiet ? NULL : paxflags); 145 return (be_wewy_wewy_quiet ? NULL : paxflags);
134 } 146 }
143 155
144static char *scanelf_file_phdr(elfobj *elf, char *found_phdr, char *found_relro, char *found_load) 156static char *scanelf_file_phdr(elfobj *elf, char *found_phdr, char *found_relro, char *found_load)
145{ 157{
146 static char ret[12]; 158 static char ret[12];
147 char *found; 159 char *found;
148 unsigned long i, shown;
149 unsigned char multi_stack, multi_relro, multi_load; 160 unsigned long i, shown, multi_stack, multi_relro, multi_load;
161 int max_pt_load;
150 162
151 if (!show_phdr) return NULL; 163 if (!show_phdr) return NULL;
152 164
153 memcpy(ret, "--- --- ---\0", 12); 165 memcpy(ret, "--- --- ---\0", 12);
154 166
155 shown = 0; 167 shown = 0;
156 multi_stack = multi_relro = multi_load = 0; 168 multi_stack = multi_relro = multi_load = 0;
169 max_pt_load = elf_max_pt_load(elf);
157 170
171#define NOTE_GNU_STACK ".note.GNU-stack"
158#define SHOW_PHDR(B) \ 172#define SHOW_PHDR(B) \
159 if (elf->elf_class == ELFCLASS ## B) { \ 173 if (elf->elf_class == ELFCLASS ## B) { \
160 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ 174 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
161 Elf ## B ## _Off offset; \ 175 Elf ## B ## _Off offset; \
162 uint32_t flags, check_flags; \ 176 uint32_t flags, check_flags; \
172 if (multi_relro++) warnf("%s: multiple PT_GNU_RELRO's !?", elf->filename); \ 186 if (multi_relro++) warnf("%s: multiple PT_GNU_RELRO's !?", elf->filename); \
173 found = found_relro; \ 187 found = found_relro; \
174 offset = 4; \ 188 offset = 4; \
175 check_flags = PF_X; \ 189 check_flags = PF_X; \
176 } else if (EGET(phdr[i].p_type) == PT_LOAD) { \ 190 } else if (EGET(phdr[i].p_type) == PT_LOAD) { \
191 if (ehdr->e_type == ET_DYN || ehdr->e_type == ET_EXEC) \
177 if (multi_load++ > 2) warnf("%s: more than 2 PT_LOAD's !?", elf->filename); \ 192 if (multi_load++ > max_pt_load) warnf("%s: more than %i PT_LOAD's !?", elf->filename, max_pt_load); \
178 found = found_load; \ 193 found = found_load; \
179 offset = 8; \ 194 offset = 8; \
180 check_flags = PF_W|PF_X; \ 195 check_flags = PF_W|PF_X; \
181 } else \ 196 } else \
182 continue; \ 197 continue; \
183 flags = EGET(phdr[i].p_flags); \ 198 flags = EGET(phdr[i].p_flags); \
184 if (be_quiet && ((flags & check_flags) != check_flags)) \ 199 if (be_quiet && ((flags & check_flags) != check_flags)) \
185 continue; \ 200 continue; \
201 if (fix_elf && ((flags & PF_X) != flags)) { \
202 ESET(phdr[i].p_flags, flags & (PF_X ^ (size_t)-1)); \
203 ret[3] = ret[7] = '!'; \
204 flags = EGET(phdr[i].p_flags); \
205 } \
186 memcpy(ret+offset, gnu_short_stack_flags(flags), 3); \ 206 memcpy(ret+offset, gnu_short_stack_flags(flags), 3); \
187 *found = 1; \ 207 *found = 1; \
188 ++shown; \ 208 ++shown; \
189 } \ 209 } \
190 } else if (elf->shdr != NULL) { \ 210 } else if (elf->shdr != NULL) { \
191 /* no program headers which means this is prob an object file */ \ 211 /* no program headers which means this is prob an object file */ \
192 Elf ## B ## _Shdr *shdr = SHDR ## B (elf->shdr); \ 212 Elf ## B ## _Shdr *shdr = SHDR ## B (elf->shdr); \
193 Elf ## B ## _Shdr *strtbl = shdr + EGET(ehdr->e_shstrndx); \ 213 Elf ## B ## _Shdr *strtbl = shdr + EGET(ehdr->e_shstrndx); \
214 char *str; \
215 if ((void*)strtbl > (void*)elf->data_end) \
216 goto skip_this_shdr##B; \
194 check_flags = SHF_WRITE|SHF_EXECINSTR; \ 217 check_flags = SHF_WRITE|SHF_EXECINSTR; \
195 for (i = 0; i < EGET(ehdr->e_shnum); ++i) { \ 218 for (i = 0; i < EGET(ehdr->e_shnum); ++i) { \
196 if (EGET(shdr[i].sh_type) != SHT_PROGBITS) continue; \ 219 if (EGET(shdr[i].sh_type) != SHT_PROGBITS) continue; \
197 offset = EGET(strtbl->sh_offset) + EGET(shdr[i].sh_name); \ 220 offset = EGET(strtbl->sh_offset) + EGET(shdr[i].sh_name); \
198 if (!strcmp((char*)(elf->data + offset), ".note.GNU-stack")) { \ 221 str = elf->data + offset; \
222 if (str > elf->data + offset + sizeof(NOTE_GNU_STACK)) continue; \
223 if (!strcmp(str, NOTE_GNU_STACK)) { \
199 if (multi_stack++) warnf("%s: multiple .note.GNU-stack's !?", elf->filename); \ 224 if (multi_stack++) warnf("%s: multiple .note.GNU-stack's !?", elf->filename); \
200 flags = EGET(shdr[i].sh_flags); \ 225 flags = EGET(shdr[i].sh_flags); \
201 if (be_quiet && ((flags & check_flags) != check_flags)) \ 226 if (be_quiet && ((flags & check_flags) != check_flags)) \
202 continue; \ 227 continue; \
203 ++*found_phdr; \ 228 ++*found_phdr; \
207 if (flags & SHF_EXECINSTR) ret[2] = 'X'; \ 232 if (flags & SHF_EXECINSTR) ret[2] = 'X'; \
208 if (flags & 0xFFFFFFF8) warn("Invalid section flags for GNU-stack"); \ 233 if (flags & 0xFFFFFFF8) warn("Invalid section flags for GNU-stack"); \
209 break; \ 234 break; \
210 } \ 235 } \
211 } \ 236 } \
237 skip_this_shdr##B: \
212 if (!multi_stack) { \ 238 if (!multi_stack) { \
213 *found_phdr = 1; \ 239 *found_phdr = 1; \
214 shown = 1; \ 240 shown = 1; \
215 memcpy(ret, "!WX", 3); \ 241 memcpy(ret, "!WX", 3); \
216 } \ 242 } \
320 if (be_verbose <= 2) continue; \ 346 if (be_verbose <= 2) continue; \
321 } else \ 347 } else \
322 *found_textrels = 1; \ 348 *found_textrels = 1; \
323 /* locate this relocation symbol name */ \ 349 /* locate this relocation symbol name */ \
324 sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \ 350 sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
351 if ((void*)sym > (void*)elf->data_end) { \
352 warn("%s: corrupt ELF symbol", elf->filename); \
353 continue; \
354 } \
325 sym_max = ELF ## B ## _R_SYM(r_info); \ 355 sym_max = ELF ## B ## _R_SYM(r_info); \
326 if (sym_max * EGET(symtab->sh_entsize) < symtab->sh_size) \ 356 if (sym_max * EGET(symtab->sh_entsize) < symtab->sh_size) \
327 sym += sym_max; \ 357 sym += sym_max; \
328 else \ 358 else \
329 sym = NULL; \ 359 sym = NULL; \
361 warnf("ELF %s has TEXTREL markings but doesnt appear to have any real TEXTREL's !?", elf->filename); 391 warnf("ELF %s has TEXTREL markings but doesnt appear to have any real TEXTREL's !?", elf->filename);
362 392
363 return NULL; 393 return NULL;
364} 394}
365 395
366static void rpath_security_checks(elfobj *, char *); 396static void rpath_security_checks(elfobj *, char *, const char *);
367static void rpath_security_checks(elfobj *elf, char *item) { 397static void rpath_security_checks(elfobj *elf, char *item, const char *dt_type)
398{
368 struct stat st; 399 struct stat st;
369 switch (*item) { 400 switch (*item) {
370 case '/': break; 401 case '/': break;
371 case '.': 402 case '.':
372 warnf("Security problem with relative RPATH '%s' in %s", item, elf->filename); 403 warnf("Security problem with relative %s '%s' in %s", dt_type, item, elf->filename);
373 break; 404 break;
405 case ':':
374 case '\0': 406 case '\0':
375 warnf("Security problem NULL RPATH in %s", elf->filename); 407 warnf("Security problem NULL %s in %s", dt_type, elf->filename);
376 break; 408 break;
377 case '$': 409 case '$':
378 if (fstat(elf->fd, &st) != -1) 410 if (fstat(elf->fd, &st) != -1)
379 if ((st.st_mode & S_ISUID) || (st.st_mode & S_ISGID)) 411 if ((st.st_mode & S_ISUID) || (st.st_mode & S_ISGID))
380 warnf("Security problem with RPATH='%s' in %s with mode set of %o", 412 warnf("Security problem with %s='%s' in %s with mode set of %o",
381 item, elf->filename, st.st_mode & 07777); 413 dt_type, item, elf->filename, st.st_mode & 07777);
382 break; 414 break;
383 default: 415 default:
384 warnf("Maybe? sec problem with RPATH='%s' in %s", item, elf->filename); 416 warnf("Maybe? sec problem with %s='%s' in %s", dt_type, item, elf->filename);
385 break; 417 break;
386 } 418 }
387} 419}
388static void scanelf_file_rpath(elfobj *elf, char *found_rpath, char **ret, size_t *ret_len) 420static void scanelf_file_rpath(elfobj *elf, char *found_rpath, char **ret, size_t *ret_len)
389{ 421{
425 /* Verify the memory is somewhat sane */ \ 457 /* Verify the memory is somewhat sane */ \
426 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \ 458 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
427 if (offset < (Elf ## B ## _Off)elf->len) { \ 459 if (offset < (Elf ## B ## _Off)elf->len) { \
428 if (*r) warn("ELF has multiple %s's !?", get_elfdtype(word)); \ 460 if (*r) warn("ELF has multiple %s's !?", get_elfdtype(word)); \
429 *r = (char*)(elf->data + offset); \ 461 *r = (char*)(elf->data + offset); \
462 /* cache the length in case we need to nuke this section later on */ \
463 if (fix_elf) \
464 offset = strlen(*r); \
430 /* If quiet, don't output paths in ld.so.conf */ \ 465 /* If quiet, don't output paths in ld.so.conf */ \
431 if (be_quiet) { \ 466 if (be_quiet) { \
432 size_t len; \ 467 size_t len; \
433 char *start, *end; \ 468 char *start, *end; \
434 /* note that we only 'chop' off leading known paths. */ \ 469 /* note that we only 'chop' off leading known paths. */ \
435 /* since *r is read-only memory, we can only move the ptr forward. */ \ 470 /* since *r is read-only memory, we can only move the ptr forward. */ \
436 start = *r; \ 471 start = *r; \
437 /* scan each path in : delimited list */ \ 472 /* scan each path in : delimited list */ \
438 while (start) { \ 473 while (start) { \
439 rpath_security_checks(elf, start); \ 474 rpath_security_checks(elf, start, get_elfdtype(word)); \
440 end = strchr(start, ':'); \ 475 end = strchr(start, ':'); \
441 len = (end ? abs(end - start) : strlen(start)); \ 476 len = (end ? abs(end - start) : strlen(start)); \
477 if (use_ldcache) \
442 for (s = 0; ldpaths[s]; ++s) { \ 478 for (s = 0; ldpaths[s]; ++s) \
443 if (!strncmp(ldpaths[s], start, len) && !ldpaths[s][len]) { \ 479 if (!strncmp(ldpaths[s], start, len) && !ldpaths[s][len]) { \
444 *r = (end ? end + 1 : NULL); \ 480 *r = end; \
481 /* corner case ... if RPATH reads "/usr/lib:", we want \
482 * to show ':' rather than '' */ \
483 if (end && end[1] != '\0') \
484 (*r)++; \
445 break; \ 485 break; \
446 } \ 486 } \
447 } \
448 if (!*r || !ldpaths[s] || !end) \ 487 if (!*r || !end) \
449 start = NULL; \ 488 break; \
450 else \ 489 else \
451 start = start + len + 1; \ 490 start = start + len + 1; \
452 } \ 491 } \
453 } \ 492 } \
493 if (*r) { \
494 if (fix_elf > 2 || (fix_elf && **r == '\0')) { \
495 /* just nuke it */ \
496 nuke_it##B: \
497 memset(*r, 0x00, offset); \
498 *r = NULL; \
499 ESET(dyn->d_tag, DT_DEBUG); \
500 ESET(dyn->d_un.d_ptr, 0); \
501 } else if (fix_elf) { \
502 /* try to clean "bad" paths */ \
503 size_t len, tmpdir_len; \
504 char *start, *end; \
505 const char *tmpdir; \
506 start = *r; \
507 tmpdir = (getenv("TMPDIR") ? : "."); \
508 tmpdir_len = strlen(tmpdir); \
509 while (1) { \
510 end = strchr(start, ':'); \
511 if (start == end) { \
512 eat_this_path##B: \
513 len = strlen(end); \
514 memmove(start, end+1, len); \
515 start[len-1] = '\0'; \
516 end = start - 1; \
517 } else if (tmpdir && !strncmp(start, tmpdir, tmpdir_len)) { \
518 if (!end) { \
519 if (start == *r) \
520 goto nuke_it##B; \
521 *--start = '\0'; \
522 } else \
523 goto eat_this_path##B; \
524 } \
525 if (!end) \
526 break; \
527 start = end + 1; \
528 } \
529 if (**r == '\0') \
530 goto nuke_it##B; \
531 } \
532 if (*r) \
454 if (*r) *found_rpath = 1; \ 533 *found_rpath = 1; \
534 } \
455 } \ 535 } \
456 ++dyn; \ 536 ++dyn; \
457 } \ 537 } \
458 } } 538 } }
459 SHOW_RPATH(32) 539 SHOW_RPATH(32)
476 } else if (rpath || runpath) 556 } else if (rpath || runpath)
477 xstrcat(ret, (runpath ? runpath : rpath), ret_len); 557 xstrcat(ret, (runpath ? runpath : rpath), ret_len);
478 else if (!be_quiet) 558 else if (!be_quiet)
479 xstrcat(ret, " - ", ret_len); 559 xstrcat(ret, " - ", ret_len);
480} 560}
561
562#define LDSO_CACHE_MAGIC "ld.so-"
563#define LDSO_CACHE_MAGIC_LEN (sizeof LDSO_CACHE_MAGIC -1)
564#define LDSO_CACHE_VER "1.7.0"
565#define LDSO_CACHE_VER_LEN (sizeof LDSO_CACHE_VER -1)
566#define FLAG_ANY -1
567#define FLAG_TYPE_MASK 0x00ff
568#define FLAG_LIBC4 0x0000
569#define FLAG_ELF 0x0001
570#define FLAG_ELF_LIBC5 0x0002
571#define FLAG_ELF_LIBC6 0x0003
572#define FLAG_REQUIRED_MASK 0xff00
573#define FLAG_SPARC_LIB64 0x0100
574#define FLAG_IA64_LIB64 0x0200
575#define FLAG_X8664_LIB64 0x0300
576#define FLAG_S390_LIB64 0x0400
577#define FLAG_POWERPC_LIB64 0x0500
578#define FLAG_MIPS64_LIBN32 0x0600
579#define FLAG_MIPS64_LIBN64 0x0700
580
581static char *lookup_cache_lib(elfobj *, char *);
582static char *lookup_cache_lib(elfobj *elf, char *fname)
583{
584 int fd = 0;
585 char *strs;
586 static char buf[__PAX_UTILS_PATH_MAX] = "";
587 const char *cachefile = "/etc/ld.so.cache";
588 struct stat st;
589
590 typedef struct {
591 char magic[LDSO_CACHE_MAGIC_LEN];
592 char version[LDSO_CACHE_VER_LEN];
593 int nlibs;
594 } header_t;
595 header_t *header;
596
597 typedef struct {
598 int flags;
599 int sooffset;
600 int liboffset;
601 } libentry_t;
602 libentry_t *libent;
603
604 if (fname == NULL)
605 return NULL;
606
607 if (ldcache == 0) {
608 if (stat(cachefile, &st) || (fd = open(cachefile, O_RDONLY)) == -1)
609 return NULL;
610
611 /* cache these values so we only map/unmap the cache file once */
612 ldcache_size = st.st_size;
613 ldcache = mmap(0, ldcache_size, PROT_READ, MAP_SHARED, fd, 0);
614
615 close(fd);
616
617 if (ldcache == (caddr_t)-1) {
618 ldcache = 0;
619 return NULL;
620 }
621
622 if (memcmp(((header_t *) ldcache)->magic, LDSO_CACHE_MAGIC, LDSO_CACHE_MAGIC_LEN))
623 return NULL;
624 if (memcmp (((header_t *) ldcache)->version, LDSO_CACHE_VER, LDSO_CACHE_VER_LEN))
625 return NULL;
626 }
627
628 header = (header_t *) ldcache;
629 libent = (libentry_t *) (ldcache + sizeof(header_t));
630 strs = (char *) &libent[header->nlibs];
631
632 for (fd = 0; fd < header->nlibs; fd++) {
633 /* this should be more fine grained, but for now we assume that
634 * diff arches will not be cached together. and we ignore the
635 * the different multilib mips cases. */
636 if (elf->elf_class == ELFCLASS64 && !(libent[fd].flags & FLAG_REQUIRED_MASK))
637 continue;
638 if (elf->elf_class == ELFCLASS32 && (libent[fd].flags & FLAG_REQUIRED_MASK))
639 continue;
640
641 if (strcmp(fname, strs + libent[fd].sooffset) != 0)
642 continue;
643 strncpy(buf, strs + libent[fd].liboffset, sizeof(buf));
644 }
645 return buf;
646}
647
648
481static const char *scanelf_file_needed_lib(elfobj *elf, char *found_needed, char *found_lib, int op, char **ret, size_t *ret_len) 649static const char *scanelf_file_needed_lib(elfobj *elf, char *found_needed, char *found_lib, int op, char **ret, size_t *ret_len)
482{ 650{
483 unsigned long i; 651 unsigned long i;
484 char *needed; 652 char *needed;
485 void *strtbl_void; 653 void *strtbl_void;
654 char *p;
486 655
487 if ((op==0 && !show_needed) || (op==1 && !find_lib)) return NULL; 656 if ((op==0 && !show_needed) || (op==1 && !find_lib)) return NULL;
488 657
489 strtbl_void = elf_findsecbyname(elf, ".dynstr"); 658 strtbl_void = elf_findsecbyname(elf, ".dynstr");
490 659
510 } \ 679 } \
511 needed = (char*)(elf->data + offset); \ 680 needed = (char*)(elf->data + offset); \
512 if (op == 0) { \ 681 if (op == 0) { \
513 if (!be_wewy_wewy_quiet) { \ 682 if (!be_wewy_wewy_quiet) { \
514 if (*found_needed) xchrcat(ret, ',', ret_len); \ 683 if (*found_needed) xchrcat(ret, ',', ret_len); \
684 if (use_ldcache) \
685 if ((p = lookup_cache_lib(elf, needed)) != NULL) \
686 needed = p; \
515 xstrcat(ret, needed, ret_len); \ 687 xstrcat(ret, needed, ret_len); \
516 } \ 688 } \
517 *found_needed = 1; \ 689 *found_needed = 1; \
518 } else { \ 690 } else { \
519 if (!strncmp(find_lib, needed, strlen( !gmatch ? needed : find_lib))) { \ 691 if (!strncmp(find_lib, needed, strlen( !gmatch ? needed : find_lib))) { \
643 return NULL; 815 return NULL;
644} 816}
645static char *scanelf_file_sym(elfobj *elf, char *found_sym) 817static char *scanelf_file_sym(elfobj *elf, char *found_sym)
646{ 818{
647 unsigned long i; 819 unsigned long i;
820 char *ret;
648 void *symtab_void, *strtab_void; 821 void *symtab_void, *strtab_void;
649 822
650 if (!find_sym) return NULL; 823 if (!find_sym) return NULL;
824 ret = find_sym;
651 825
652 scanelf_file_get_symtabs(elf, &symtab_void, &strtab_void); 826 scanelf_file_get_symtabs(elf, &symtab_void, &strtab_void);
653 827
654 if (symtab_void && strtab_void) { 828 if (symtab_void && strtab_void) {
655#define FIND_SYM(B) \ 829#define FIND_SYM(B) \
656 if (elf->elf_class == ELFCLASS ## B) { \ 830 if (elf->elf_class == ELFCLASS ## B) { \
657 Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \ 831 Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
658 Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \ 832 Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
659 Elf ## B ## _Sym *sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \ 833 Elf ## B ## _Sym *sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
660 unsigned long cnt = EGET(symtab->sh_size) / EGET(symtab->sh_entsize); \ 834 unsigned long cnt = EGET(symtab->sh_entsize); \
661 char *symname; \ 835 char *symname; \
836 if (cnt) \
837 cnt = EGET(symtab->sh_size) / cnt; \
662 for (i = 0; i < cnt; ++i) { \ 838 for (i = 0; i < cnt; ++i) { \
663 if (sym->st_name) { \ 839 if (sym->st_name) { \
664 symname = (char *)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name)); \ 840 symname = (char *)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name)); \
841 if ((void*)symname > (void*)elf->data_end) { \
842 warnf("%s: corrupt ELF symbols", elf->filename); \
843 continue; \
844 } \
665 if (*find_sym == '*') { \ 845 if (*find_sym == '*') { \
666 printf("%s(%s) %5lX %15s %s\n", \ 846 printf("%s(%s) %5lX %15s %s\n", \
667 ((*found_sym == 0) ? "\n\t" : "\t"), \ 847 ((*found_sym == 0) ? "\n\t" : "\t"), \
668 elf->base_filename, \ 848 elf->base_filename, \
669 (long)sym->st_size, \ 849 (unsigned long)sym->st_size, \
670 (char *)get_elfstttype(sym->st_info), \ 850 get_elfstttype(sym->st_info), \
671 symname); \ 851 symname); \
672 *found_sym = 1; \ 852 *found_sym = 1; \
673 } else if ((strcmp(find_sym, symname) == 0) || \ 853 } else { \
854 char *this_sym, *next_sym; \
855 this_sym = find_sym; \
856 do { \
857 next_sym = strchr(this_sym, ','); \
858 if (next_sym == NULL) \
859 next_sym = this_sym + strlen(this_sym); \
860 if ((strncmp(this_sym, symname, (next_sym-this_sym)) == 0 && symname[next_sym-this_sym] == '\0') || \
674 (strcmp(symname, versioned_symname) == 0)) \ 861 (strcmp(symname, versioned_symname) == 0)) { \
862 ret = this_sym; \
675 (*found_sym)++; \ 863 (*found_sym)++; \
864 goto break_out; \
865 } \
866 this_sym = next_sym + 1; \
867 } while (*next_sym != '\0'); \
868 } \
676 } \ 869 } \
677 ++sym; \ 870 ++sym; \
678 } } 871 } }
679 FIND_SYM(32) 872 FIND_SYM(32)
680 FIND_SYM(64) 873 FIND_SYM(64)
681 } 874 }
682 875
876break_out:
683 if (be_wewy_wewy_quiet) return NULL; 877 if (be_wewy_wewy_quiet) return NULL;
684 878
685 if (*find_sym != '*' && *found_sym) 879 if (*find_sym != '*' && *found_sym)
686 return find_sym; 880 return ret;
687 if (be_quiet) 881 if (be_quiet)
688 return NULL; 882 return NULL;
689 else 883 else
690 return (char *)" - "; 884 return (char *)" - ";
691} 885}
886
887
888static char *scanelf_file_sections(elfobj *elf, char *found_section)
889{
890 if (!find_section)
891 return NULL;
892
893#define FIND_SECTION(B) \
894 if (elf->elf_class == ELFCLASS ## B) { \
895 Elf ## B ## _Shdr *section; \
896 section = SHDR ## B (elf_findsecbyname(elf, find_section)); \
897 if (section != NULL) \
898 *found_section = 1; \
899 }
900 FIND_SECTION(32)
901 FIND_SECTION(64)
902
903
904 if (be_wewy_wewy_quiet) return NULL;
905
906 if (*found_section)
907 return find_section;
908
909 if (be_quiet)
910 return NULL;
911 else
912 return (char *)" - ";
913}
914
692/* scan an elf file and show all the fun stuff */ 915/* scan an elf file and show all the fun stuff */
693#define prints(str) write(fileno(stdout), str, strlen(str)) 916#define prints(str) write(fileno(stdout), str, strlen(str))
694static void scanelf_file(const char *filename) 917static int scanelf_elfobj(elfobj *elf)
695{ 918{
696 unsigned long i; 919 unsigned long i;
697 char found_pax, found_phdr, found_relro, found_load, found_textrel, 920 char found_pax, found_phdr, found_relro, found_load, found_textrel,
698 found_rpath, found_needed, found_interp, found_bind, found_soname, 921 found_rpath, found_needed, found_interp, found_bind, found_soname,
699 found_sym, found_lib, found_file, found_textrels; 922 found_sym, found_lib, found_file, found_textrels, found_section;
700 elfobj *elf;
701 struct stat st;
702 static char *out_buffer = NULL; 923 static char *out_buffer = NULL;
703 static size_t out_len; 924 static size_t out_len;
704 925
705 /* make sure 'filename' exists */
706 if (lstat(filename, &st) == -1) {
707 if (be_verbose > 2) printf("%s: does not exist\n", filename);
708 return;
709 }
710 /* always handle regular files and handle symlinked files if no -y */
711 if (S_ISLNK(st.st_mode)) {
712 if (!scan_symlink) return;
713 stat(filename, &st);
714 }
715 if (!S_ISREG(st.st_mode)) {
716 if (be_verbose > 2) printf("%s: skipping non-file\n", filename);
717 return;
718 }
719
720 found_pax = found_phdr = found_relro = found_load = found_textrel = \ 926 found_pax = found_phdr = found_relro = found_load = found_textrel = \
721 found_rpath = found_needed = found_interp = found_bind = found_soname = \ 927 found_rpath = found_needed = found_interp = found_bind = found_soname = \
722 found_sym = found_lib = found_file = found_textrels = 0; 928 found_sym = found_lib = found_file = found_textrels = found_section = 0;
723 929
724 /* verify this is real ELF */
725 if ((elf = readelf(filename)) == NULL) {
726 if (be_verbose > 2) printf("%s: not an ELF\n", filename);
727 return;
728 }
729
730 if (be_verbose > 1) 930 if (be_verbose > 2)
731 printf("%s: scanning file {%s,%s}\n", filename, 931 printf("%s: scanning file {%s,%s}\n", elf->filename,
732 get_elfeitype(EI_CLASS, elf->elf_class), 932 get_elfeitype(EI_CLASS, elf->elf_class),
733 get_elfeitype(EI_DATA, elf->data[EI_DATA])); 933 get_elfeitype(EI_DATA, elf->data[EI_DATA]));
734 else if (be_verbose) 934 else if (be_verbose > 1)
735 printf("%s: scanning file\n", filename); 935 printf("%s: scanning file\n", elf->filename);
736 936
737 /* init output buffer */ 937 /* init output buffer */
738 if (!out_buffer) { 938 if (!out_buffer) {
739 out_len = sizeof(char) * 80; 939 out_len = sizeof(char) * 80;
740 out_buffer = (char*)xmalloc(out_len); 940 out_buffer = (char*)xmalloc(out_len);
762 case 'b': prints("BIND "); break; 962 case 'b': prints("BIND "); break;
763 case 'S': prints("SONAME "); break; 963 case 'S': prints("SONAME "); break;
764 case 's': prints("SYM "); break; 964 case 's': prints("SYM "); break;
765 case 'N': prints("LIB "); break; 965 case 'N': prints("LIB "); break;
766 case 'T': prints("TEXTRELS "); break; 966 case 'T': prints("TEXTRELS "); break;
967 case 'k': prints("SECTION "); break;
767 default: warnf("'%c' has no title ?", out_format[i]); 968 default: warnf("'%c' has no title ?", out_format[i]);
768 } 969 }
769 } 970 }
770 if (!found_file) prints("FILE "); 971 if (!found_file) prints("FILE ");
771 prints("\n"); 972 prints("\n");
775 976
776 /* dump all the good stuff */ 977 /* dump all the good stuff */
777 for (i = 0; out_format[i]; ++i) { 978 for (i = 0; out_format[i]; ++i) {
778 const char *out; 979 const char *out;
779 const char *tmp; 980 const char *tmp;
780
781 /* make sure we trim leading spaces in quiet mode */
782 if (be_quiet && *out_buffer == ' ' && !out_buffer[1])
783 *out_buffer = '\0';
784 981
785 if (!IS_MODIFIER(out_format[i])) { 982 if (!IS_MODIFIER(out_format[i])) {
786 xchrcat(&out_buffer, out_format[i], &out_len); 983 xchrcat(&out_buffer, out_format[i], &out_len);
787 continue; 984 continue;
788 } 985 }
794 case '#': 991 case '#':
795 xchrcat(&out_buffer, out_format[i], &out_len); break; 992 xchrcat(&out_buffer, out_format[i], &out_len); break;
796 case 'F': 993 case 'F':
797 found_file = 1; 994 found_file = 1;
798 if (be_wewy_wewy_quiet) break; 995 if (be_wewy_wewy_quiet) break;
799 xstrcat(&out_buffer, filename, &out_len); 996 xstrcat(&out_buffer, elf->filename, &out_len);
800 break; 997 break;
801 case 'p': 998 case 'p':
802 found_file = 1; 999 found_file = 1;
803 if (be_wewy_wewy_quiet) break; 1000 if (be_wewy_wewy_quiet) break;
804 tmp = filename; 1001 tmp = elf->filename;
805 if (search_path) { 1002 if (search_path) {
806 ssize_t len_search = strlen(search_path); 1003 ssize_t len_search = strlen(search_path);
807 ssize_t len_file = strlen(filename); 1004 ssize_t len_file = strlen(elf->filename);
808 if (!strncmp(filename, search_path, len_search) && \ 1005 if (!strncmp(elf->filename, search_path, len_search) && \
809 len_file > len_search) 1006 len_file > len_search)
810 tmp += len_search; 1007 tmp += len_search;
811 if (*tmp == '/' && search_path[len_search-1] == '/') tmp++; 1008 if (*tmp == '/' && search_path[len_search-1] == '/') tmp++;
812 } 1009 }
813 xstrcat(&out_buffer, tmp, &out_len); 1010 xstrcat(&out_buffer, tmp, &out_len);
814 break; 1011 break;
815 case 'f': 1012 case 'f':
816 found_file = 1; 1013 found_file = 1;
817 if (be_wewy_wewy_quiet) break; 1014 if (be_wewy_wewy_quiet) break;
818 tmp = strrchr(filename, '/'); 1015 tmp = strrchr(elf->filename, '/');
819 tmp = (tmp == NULL ? filename : tmp+1); 1016 tmp = (tmp == NULL ? elf->filename : tmp+1);
820 xstrcat(&out_buffer, tmp, &out_len); 1017 xstrcat(&out_buffer, tmp, &out_len);
821 break; 1018 break;
822 case 'o': out = get_elfetype(elf); break; 1019 case 'o': out = get_elfetype(elf); break;
823 case 'x': out = scanelf_file_pax(elf, &found_pax); break; 1020 case 'x': out = scanelf_file_pax(elf, &found_pax); break;
824 case 'e': out = scanelf_file_phdr(elf, &found_phdr, &found_relro, &found_load); break; 1021 case 'e': out = scanelf_file_phdr(elf, &found_phdr, &found_relro, &found_load); break;
829 case 'N': out = scanelf_file_needed_lib(elf, &found_needed, &found_lib, (out_format[i]=='N'), &out_buffer, &out_len); break; 1026 case 'N': out = scanelf_file_needed_lib(elf, &found_needed, &found_lib, (out_format[i]=='N'), &out_buffer, &out_len); break;
830 case 'i': out = scanelf_file_interp(elf, &found_interp); break; 1027 case 'i': out = scanelf_file_interp(elf, &found_interp); break;
831 case 'b': out = scanelf_file_bind(elf, &found_bind); break; 1028 case 'b': out = scanelf_file_bind(elf, &found_bind); break;
832 case 'S': out = scanelf_file_soname(elf, &found_soname); break; 1029 case 'S': out = scanelf_file_soname(elf, &found_soname); break;
833 case 's': out = scanelf_file_sym(elf, &found_sym); break; 1030 case 's': out = scanelf_file_sym(elf, &found_sym); break;
1031 case 'k': out = scanelf_file_sections(elf, &found_section); break;
834 default: warnf("'%c' has no scan code?", out_format[i]); 1032 default: warnf("'%c' has no scan code?", out_format[i]);
835 } 1033 }
1034 if (out) {
1035 /* hack for comma delimited output like `scanelf -s sym1,sym2,sym3` */
1036 if (out_format[i] == 's' && (tmp=strchr(out,',')) != NULL)
1037 xstrncat(&out_buffer, out, &out_len, (tmp-out));
1038 else
836 if (out) xstrcat(&out_buffer, out, &out_len); 1039 xstrcat(&out_buffer, out, &out_len);
1040 }
837 } 1041 }
838 1042
839#define FOUND_SOMETHING() \ 1043#define FOUND_SOMETHING() \
840 (found_pax || found_phdr || found_relro || found_load || found_textrel || \ 1044 (found_pax || found_phdr || found_relro || found_load || found_textrel || \
841 found_rpath || found_needed || found_interp || found_bind || \ 1045 found_rpath || found_needed || found_interp || found_bind || \
842 found_soname || found_sym || found_lib || found_textrels) 1046 found_soname || found_sym || found_lib || found_textrels || found_section )
843 1047
844 if (!found_file && (!be_quiet || (be_quiet && FOUND_SOMETHING()))) { 1048 if (!found_file && (!be_quiet || (be_quiet && FOUND_SOMETHING()))) {
845 xchrcat(&out_buffer, ' ', &out_len); 1049 xchrcat(&out_buffer, ' ', &out_len);
846 xstrcat(&out_buffer, filename, &out_len); 1050 xstrcat(&out_buffer, elf->filename, &out_len);
847 } 1051 }
848 if (!be_quiet || (be_quiet && FOUND_SOMETHING())) { 1052 if (!be_quiet || (be_quiet && FOUND_SOMETHING())) {
849 puts(out_buffer); 1053 puts(out_buffer);
850 fflush(stdout); 1054 fflush(stdout);
851 } 1055 }
852 1056
1057 return 0;
1058}
1059
1060/* scan a single elf */
1061static int scanelf_elf(const char *filename, int fd, size_t len)
1062{
1063 int ret = 1;
1064 elfobj *elf;
1065
1066 /* verify this is real ELF */
1067 if ((elf = _readelf_fd(filename, fd, len, !fix_elf)) == NULL) {
1068 if (be_verbose > 2) printf("%s: not an ELF\n", filename);
1069 return ret;
1070 }
1071 switch (match_bits) {
1072 case 32:
1073 if (elf->elf_class != ELFCLASS32)
1074 goto label_done;
1075 break;
1076 case 64:
1077 if (elf->elf_class != ELFCLASS64)
1078 goto label_done;
1079 break;
1080 default: break;
1081 }
1082 if (strlen(match_etypes)) {
1083 char sbuf[126];
1084 strncpy(sbuf, match_etypes, sizeof(sbuf));
1085 if (strchr(match_etypes, ',') != NULL) {
1086 char *p;
1087 while((p = strrchr(sbuf, ',')) != NULL) {
1088 *p = 0;
1089 if (atoi(p+1) == get_etype(elf))
1090 goto label_ret;
1091 }
1092 }
1093 if (atoi(sbuf) != get_etype(elf))
1094 goto label_done;
1095 }
1096
1097label_ret:
1098 ret = scanelf_elfobj(elf);
1099
1100label_done:
853 unreadelf(elf); 1101 unreadelf(elf);
1102 return ret;
1103}
1104
1105/* scan an archive of elfs */
1106static int scanelf_archive(const char *filename, int fd, size_t len)
1107{
1108 archive_handle *ar;
1109 archive_member *m;
1110 char *ar_buffer;
1111 elfobj *elf;
1112
1113 ar = ar_open_fd(filename, fd);
1114 if (ar == NULL)
1115 return 1;
1116
1117 ar_buffer = (char*)mmap(0, len, PROT_READ | (fix_elf ? PROT_WRITE : 0), (fix_elf ? MAP_SHARED : MAP_PRIVATE), fd, 0);
1118 while ((m=ar_next(ar)) != NULL) {
1119 elf = readelf_buffer(m->name, ar_buffer+lseek(fd,0,SEEK_CUR), m->size);
1120 if (elf) {
1121 scanelf_elfobj(elf);
1122 unreadelf(elf);
1123 }
1124 }
1125 munmap(ar_buffer, len);
1126
1127 return 0;
1128}
1129/* scan a file which may be an elf or an archive or some other magical beast */
1130static void scanelf_file(const char *filename)
1131{
1132 struct stat st;
1133 int fd;
1134
1135 /* make sure 'filename' exists */
1136 if (lstat(filename, &st) == -1) {
1137 if (be_verbose > 2) printf("%s: does not exist\n", filename);
1138 return;
1139 }
1140
1141 /* always handle regular files and handle symlinked files if no -y */
1142 if (S_ISLNK(st.st_mode)) {
1143 if (!scan_symlink) return;
1144 stat(filename, &st);
1145 }
1146 if (!S_ISREG(st.st_mode)) {
1147 if (be_verbose > 2) printf("%s: skipping non-file\n", filename);
1148 return;
1149 }
1150
1151 if ((fd=open(filename, (fix_elf ? O_RDWR : O_RDONLY))) == -1)
1152 return;
1153
1154 if (scanelf_elf(filename, fd, st.st_size) == 1 && scan_archives)
1155 /* if it isn't an ELF, maybe it's an .a archive */
1156 scanelf_archive(filename, fd, st.st_size);
1157
1158 close(fd);
854} 1159}
855 1160
856/* scan a directory for ET_EXEC files and print when we find one */ 1161/* scan a directory for ET_EXEC files and print when we find one */
857static void scanelf_dir(const char *path) 1162static void scanelf_dir(const char *path)
858{ 1163{
859 register DIR *dir; 1164 register DIR *dir;
860 register struct dirent *dentry; 1165 register struct dirent *dentry;
861 struct stat st_top, st; 1166 struct stat st_top, st;
862 char buf[_POSIX_PATH_MAX]; 1167 char buf[__PAX_UTILS_PATH_MAX];
863 size_t pathlen = 0, len = 0; 1168 size_t pathlen = 0, len = 0;
864 1169
865 /* make sure path exists */ 1170 /* make sure path exists */
866 if (lstat(path, &st_top) == -1) { 1171 if (lstat(path, &st_top) == -1) {
867 if (be_verbose > 2) printf("%s: does not exist\n", path); 1172 if (be_verbose > 2) printf("%s: does not exist\n", path);
877 /* now scan the dir looking for fun stuff */ 1182 /* now scan the dir looking for fun stuff */
878 if ((dir = opendir(path)) == NULL) { 1183 if ((dir = opendir(path)) == NULL) {
879 warnf("could not opendir %s: %s", path, strerror(errno)); 1184 warnf("could not opendir %s: %s", path, strerror(errno));
880 return; 1185 return;
881 } 1186 }
882 if (be_verbose) printf("%s: scanning dir\n", path); 1187 if (be_verbose > 1) printf("%s: scanning dir\n", path);
883 1188
884 pathlen = strlen(path); 1189 pathlen = strlen(path);
885 while ((dentry = readdir(dir))) { 1190 while ((dentry = readdir(dir))) {
886 if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, "..")) 1191 if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
887 continue; 1192 continue;
906 1211
907static int scanelf_from_file(char *filename) 1212static int scanelf_from_file(char *filename)
908{ 1213{
909 FILE *fp = NULL; 1214 FILE *fp = NULL;
910 char *p; 1215 char *p;
911 char path[_POSIX_PATH_MAX]; 1216 char path[__PAX_UTILS_PATH_MAX];
912 1217
913 if (((strcmp(filename, "-")) == 0) && (ttyname(0) == NULL)) 1218 if (((strcmp(filename, "-")) == 0) && (ttyname(0) == NULL))
914 fp = stdin; 1219 fp = stdin;
915 else if ((fp = fopen(filename, "r")) == NULL) 1220 else if ((fp = fopen(filename, "r")) == NULL)
916 return 1; 1221 return 1;
917 1222
918 while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL) { 1223 while ((fgets(path, __PAX_UTILS_PATH_MAX, fp)) != NULL) {
919 if ((p = strchr(path, '\n')) != NULL) 1224 if ((p = strchr(path, '\n')) != NULL)
920 *p = 0; 1225 *p = 0;
921 search_path = path; 1226 search_path = path;
922 scanelf_dir(path); 1227 scanelf_dir(path);
923 } 1228 }
924 if (fp != stdin) 1229 if (fp != stdin)
925 fclose(fp); 1230 fclose(fp);
926 return 0; 1231 return 0;
927} 1232}
928 1233
929static void load_ld_so_conf() 1234static int load_ld_so_conf(int i, const char *fname)
930{ 1235{
931 FILE *fp = NULL; 1236 FILE *fp = NULL;
932 char *p; 1237 char *p;
933 char path[_POSIX_PATH_MAX]; 1238 char path[__PAX_UTILS_PATH_MAX];
934 int i = 0;
935 1239
936 if ((fp = fopen("/etc/ld.so.conf", "r")) == NULL) 1240 if (i + 1 == sizeof(ldpaths) / sizeof(*ldpaths))
937 return; 1241 return i;
938 1242
1243 if ((fp = fopen(fname, "r")) == NULL)
1244 return i;
1245
939 while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL) { 1246 while ((fgets(path, __PAX_UTILS_PATH_MAX, fp)) != NULL) {
940 if (*path != '/')
941 continue;
942
943 if ((p = strrchr(path, '\r')) != NULL) 1247 if ((p = strrchr(path, '\r')) != NULL)
944 *p = 0; 1248 *p = 0;
945 if ((p = strchr(path, '\n')) != NULL) 1249 if ((p = strchr(path, '\n')) != NULL)
946 *p = 0; 1250 *p = 0;
1251#ifdef HAVE_GLOB
1252 // recursive includes of the same file will make this segfault.
1253 if ((*path == 'i') && (strncmp(path, "include", 7) == 0) && isblank(path[7])) {
1254 glob64_t gl;
1255 size_t x;
1256 char gpath[__PAX_UTILS_PATH_MAX];
1257
1258 gpath[sizeof(gpath)] = 0;
1259
1260 if (path[8] != '/')
1261 snprintf(gpath, sizeof(gpath)-1, "/etc/%s", &path[8]);
1262 else
1263 strncpy(gpath, &path[8], sizeof(gpath)-1);
1264
1265 if ((glob64(gpath, 0, NULL, &gl)) == 0) {
1266 for (x = 0; x < gl.gl_pathc; ++x) {
1267 /* try to avoid direct loops */
1268 if (strcmp(gl.gl_pathv[x], fname) == 0)
1269 continue;
1270 i = load_ld_so_conf(i, gl.gl_pathv[x]);
1271 if (i + 1 >= sizeof(ldpaths) / sizeof(*ldpaths)) {
1272 globfree64(&gl);
1273 return i;
1274 }
1275 }
1276 globfree64 (&gl);
1277 continue;
1278 } else
1279 abort();
1280 }
1281#endif
1282 if (*path != '/')
1283 continue;
947 1284
948 ldpaths[i++] = xstrdup(path); 1285 ldpaths[i++] = xstrdup(path);
949 1286
950 if (i + 1 == sizeof(ldpaths) / sizeof(*ldpaths)) 1287 if (i + 1 == sizeof(ldpaths) / sizeof(*ldpaths))
951 break; 1288 break;
952 } 1289 }
953 ldpaths[i] = NULL; 1290 ldpaths[i] = NULL;
954 1291
955 fclose(fp); 1292 fclose(fp);
1293 return i;
956} 1294}
957 1295
958/* scan /etc/ld.so.conf for paths */ 1296/* scan /etc/ld.so.conf for paths */
959static void scanelf_ldpath() 1297static void scanelf_ldpath()
960{ 1298{
995 } 1333 }
996 1334
997 free(path); 1335 free(path);
998} 1336}
999 1337
1000
1001
1002/* usage / invocation handling functions */ 1338/* usage / invocation handling functions */
1003#define PARSE_FLAGS "plRmyxetrnibSs:gN:TaqvF:f:o:BhV" 1339#define PARSE_FLAGS "plRmyAXz:xetrnLibSs:k:gN:TaqvF:f:o:E:M:BhV"
1004#define a_argument required_argument 1340#define a_argument required_argument
1005static struct option const long_opts[] = { 1341static struct option const long_opts[] = {
1006 {"path", no_argument, NULL, 'p'}, 1342 {"path", no_argument, NULL, 'p'},
1007 {"ldpath", no_argument, NULL, 'l'}, 1343 {"ldpath", no_argument, NULL, 'l'},
1008 {"recursive", no_argument, NULL, 'R'}, 1344 {"recursive", no_argument, NULL, 'R'},
1009 {"mount", no_argument, NULL, 'm'}, 1345 {"mount", no_argument, NULL, 'm'},
1010 {"symlink", no_argument, NULL, 'y'}, 1346 {"symlink", no_argument, NULL, 'y'},
1347 {"archives", no_argument, NULL, 'A'},
1348 {"ldcache", no_argument, NULL, 'L'},
1349 {"fix", no_argument, NULL, 'X'},
1350 {"setpax", a_argument, NULL, 'z'},
1011 {"pax", no_argument, NULL, 'x'}, 1351 {"pax", no_argument, NULL, 'x'},
1012 {"header", no_argument, NULL, 'e'}, 1352 {"header", no_argument, NULL, 'e'},
1013 {"textrel", no_argument, NULL, 't'}, 1353 {"textrel", no_argument, NULL, 't'},
1014 {"rpath", no_argument, NULL, 'r'}, 1354 {"rpath", no_argument, NULL, 'r'},
1015 {"needed", no_argument, NULL, 'n'}, 1355 {"needed", no_argument, NULL, 'n'},
1016 {"interp", no_argument, NULL, 'i'}, 1356 {"interp", no_argument, NULL, 'i'},
1017 {"bind", no_argument, NULL, 'b'}, 1357 {"bind", no_argument, NULL, 'b'},
1018 {"soname", no_argument, NULL, 'S'}, 1358 {"soname", no_argument, NULL, 'S'},
1019 {"symbol", a_argument, NULL, 's'}, 1359 {"symbol", a_argument, NULL, 's'},
1360 {"section", a_argument, NULL, 'k'},
1020 {"lib", a_argument, NULL, 'N'}, 1361 {"lib", a_argument, NULL, 'N'},
1021 {"gmatch", no_argument, NULL, 'g'}, 1362 {"gmatch", no_argument, NULL, 'g'},
1022 {"textrels", no_argument, NULL, 'T'}, 1363 {"textrels", no_argument, NULL, 'T'},
1364 {"etype", a_argument, NULL, 'E'},
1365 {"bits", a_argument, NULL, 'M'},
1023 {"all", no_argument, NULL, 'a'}, 1366 {"all", no_argument, NULL, 'a'},
1024 {"quiet", no_argument, NULL, 'q'}, 1367 {"quiet", no_argument, NULL, 'q'},
1025 {"verbose", no_argument, NULL, 'v'}, 1368 {"verbose", no_argument, NULL, 'v'},
1026 {"format", a_argument, NULL, 'F'}, 1369 {"format", a_argument, NULL, 'F'},
1027 {"from", a_argument, NULL, 'f'}, 1370 {"from", a_argument, NULL, 'f'},
1035static const char *opts_help[] = { 1378static const char *opts_help[] = {
1036 "Scan all directories in PATH environment", 1379 "Scan all directories in PATH environment",
1037 "Scan all directories in /etc/ld.so.conf", 1380 "Scan all directories in /etc/ld.so.conf",
1038 "Scan directories recursively", 1381 "Scan directories recursively",
1039 "Don't recursively cross mount points", 1382 "Don't recursively cross mount points",
1040 "Don't scan symlinks\n", 1383 "Don't scan symlinks",
1384 "Scan archives (.a files)",
1385 "Utilize ld.so.cache information (use with -r/-n)",
1386 "Try and 'fix' bad things (use with -r/-e)",
1387 "Sets EI_PAX/PT_PAX_FLAGS to <arg> (use with -Xx)\n",
1041 "Print PaX markings", 1388 "Print PaX markings",
1042 "Print GNU_STACK/PT_LOAD markings", 1389 "Print GNU_STACK/PT_LOAD markings",
1043 "Print TEXTREL information", 1390 "Print TEXTREL information",
1044 "Print RPATH information", 1391 "Print RPATH information",
1045 "Print NEEDED information", 1392 "Print NEEDED information",
1046 "Print INTERP information", 1393 "Print INTERP information",
1047 "Print BIND information", 1394 "Print BIND information",
1048 "Print SONAME information", 1395 "Print SONAME information",
1049 "Find a specified symbol", 1396 "Find a specified symbol",
1397 "Find a specified section",
1050 "Find a specified library", 1398 "Find a specified library",
1051 "Use strncmp to match libraries. (use with -N)", 1399 "Use strncmp to match libraries. (use with -N)",
1052 "Locate cause of TEXTREL", 1400 "Locate cause of TEXTREL",
1401 "Print only ELF files matching numeric constant type",
1402 "Print only ELF files matching numeric bits",
1053 "Print all scanned info (-x -e -t -r -b)\n", 1403 "Print all scanned info (-x -e -t -r -b)\n",
1054 "Only output 'bad' things", 1404 "Only output 'bad' things",
1055 "Be verbose (can be specified more than once)", 1405 "Be verbose (can be specified more than once)",
1056 "Use specified format for output", 1406 "Use specified format for output",
1057 "Read input stream from a filename", 1407 "Read input stream from a filename",
1065/* display usage and exit */ 1415/* display usage and exit */
1066static void usage(int status) 1416static void usage(int status)
1067{ 1417{
1068 unsigned long i; 1418 unsigned long i;
1069 printf("* Scan ELF binaries for stuff\n\n" 1419 printf("* Scan ELF binaries for stuff\n\n"
1070 "Usage: %s [options] <dir1/file1> [dir2 dirN fileN ...]\n\n", argv0); 1420 "Usage: %s [options] <dir1/file1> [dir2 dirN file2 fileN ...]\n\n", argv0);
1071 printf("Options: -[%s]\n", PARSE_FLAGS); 1421 printf("Options: -[%s]\n", PARSE_FLAGS);
1072 for (i = 0; long_opts[i].name; ++i) 1422 for (i = 0; long_opts[i].name; ++i)
1073 if (long_opts[i].has_arg == no_argument) 1423 if (long_opts[i].has_arg == no_argument)
1074 printf(" -%c, --%-13s* %s\n", long_opts[i].val, 1424 printf(" -%c, --%-14s* %s\n", long_opts[i].val,
1075 long_opts[i].name, opts_help[i]); 1425 long_opts[i].name, opts_help[i]);
1076 else 1426 else
1077 printf(" -%c, --%-6s <arg> * %s\n", long_opts[i].val, 1427 printf(" -%c, --%-7s <arg> * %s\n", long_opts[i].val,
1078 long_opts[i].name, opts_help[i]); 1428 long_opts[i].name, opts_help[i]);
1079 1429
1080 if (status != EXIT_SUCCESS) 1430 if (status != EXIT_SUCCESS)
1081 exit(status); 1431 exit(status);
1082 1432
1083 puts("\nThe format modifiers for the -F option are:"); 1433 puts("\nThe format modifiers for the -F option are:");
1084 puts(" F Filename \tx PaX Flags \te STACK/RELRO"); 1434 puts(" F Filename \tx PaX Flags \te STACK/RELRO");
1085 puts(" t TEXTREL \tr RPATH \tn NEEDED"); 1435 puts(" t TEXTREL \tr RPATH \tn NEEDED");
1086 puts(" i INTERP \tb BIND \ts symbol"); 1436 puts(" i INTERP \tb BIND \ts symbol");
1087 puts(" N library \to Type \tT TEXTRELs"); 1437 puts(" N library \to Type \tT TEXTRELs");
1088 puts(" S SONAME"); 1438 puts(" S SONAME \tk section");
1089 puts(" p filename (with search path removed)"); 1439 puts(" p filename (with search path removed)");
1090 puts(" f filename (short name/basename)"); 1440 puts(" f filename (short name/basename)");
1091 puts("Prefix each modifier with '%' (verbose) or '#' (silent)"); 1441 puts("Prefix each modifier with '%' (verbose) or '#' (silent)");
1442
1443 puts("\nELF Etypes:");
1444 print_etypes(stdout);
1092 1445
1093 exit(status); 1446 exit(status);
1094} 1447}
1095 1448
1096/* parse command line arguments and preform needed actions */ 1449/* parse command line arguments and preform needed actions */
1109 VERSION, __FILE__, __DATE__, rcsid, argv0); 1462 VERSION, __FILE__, __DATE__, rcsid, argv0);
1110 exit(EXIT_SUCCESS); 1463 exit(EXIT_SUCCESS);
1111 break; 1464 break;
1112 case 'h': usage(EXIT_SUCCESS); break; 1465 case 'h': usage(EXIT_SUCCESS); break;
1113 case 'f': 1466 case 'f':
1114 if (from_file) err("Don't specify -f twice"); 1467 if (from_file) warn("You prob don't want to specify -f twice");
1115 from_file = xstrdup(optarg); 1468 from_file = optarg;
1469 break;
1470 case 'E':
1471 strncpy(match_etypes, optarg, sizeof(match_etypes));
1472 break;
1473 case 'M':
1474 match_bits = atoi(optarg);
1116 break; 1475 break;
1117 case 'o': { 1476 case 'o': {
1118 FILE *fp = NULL; 1477 FILE *fp = NULL;
1119 if ((fp = freopen(optarg, "w", stdout)) == NULL) 1478 if ((fp = freopen(optarg, "w", stdout)) == NULL)
1120 err("Could not open output stream '%s': %s", optarg, strerror(errno)); 1479 err("Could not open output stream '%s': %s", optarg, strerror(errno));
1121 SET_STDOUT(fp); 1480 SET_STDOUT(fp);
1122 break; 1481 break;
1123 } 1482 }
1124 1483 case 'k':
1484 if (find_section) warn("You prob don't want to specify -k twice");
1485 find_section = optarg;
1486 break;
1125 case 's': { 1487 case 's': {
1126 if (find_sym) warn("You prob don't want to specify -s twice"); 1488 if (find_sym) warn("You prob don't want to specify -s twice");
1127 find_sym = optarg; 1489 find_sym = optarg;
1128 versioned_symname = (char*)xmalloc(sizeof(char) * (strlen(find_sym)+1+1)); 1490 versioned_symname = (char*)xmalloc(sizeof(char) * (strlen(find_sym)+1+1));
1129 sprintf(versioned_symname, "%s@", find_sym); 1491 sprintf(versioned_symname, "%s@", find_sym);
1138 case 'F': { 1500 case 'F': {
1139 if (out_format) warn("You prob don't want to specify -F twice"); 1501 if (out_format) warn("You prob don't want to specify -F twice");
1140 out_format = optarg; 1502 out_format = optarg;
1141 break; 1503 break;
1142 } 1504 }
1505 case 'z': {
1506 unsigned long flags = 10240;
1507 size_t x;
1143 1508
1509#define do_state(option, flag) \
1510 if (islower(option)) { \
1511 flags &= ~PF_##flag; \
1512 flags |= PF_NO##flag; \
1513 } else { \
1514 flags &= ~PF_NO##flag; \
1515 flags |= PF_##flag; \
1516 }
1517
1518 for (x = 0 ; x < strlen(optarg); x++) {
1519 switch(optarg[x]) {
1520 case 'p':
1521 case 'P':
1522 do_state(optarg[x], PAGEEXEC);
1523 break;
1524 case 's':
1525 case 'S':
1526 do_state(optarg[x], SEGMEXEC);
1527 break;
1528 case 'm':
1529 case 'M':
1530 do_state(optarg[x], MPROTECT);
1531 break;
1532 case 'e':
1533 case 'E':
1534 do_state(optarg[x], EMUTRAMP);
1535 break;
1536 case 'r':
1537 case 'R':
1538 do_state(optarg[x], RANDMMAP);
1539 break;
1540 case 'x':
1541 case 'X':
1542 do_state(optarg[x], RANDEXEC);
1543 break;
1544 default:
1545 break;
1546 }
1547 }
1548 if (!(((flags & PF_PAGEEXEC) && (flags & PF_NOPAGEEXEC)) ||
1549 ((flags & PF_SEGMEXEC) && (flags & PF_NOSEGMEXEC)) ||
1550 ((flags & PF_RANDMMAP) && (flags & PF_NORANDMMAP)) ||
1551 ((flags & PF_RANDEXEC) && (flags & PF_NORANDEXEC)) ||
1552 ((flags & PF_EMUTRAMP) && (flags & PF_NOEMUTRAMP)) ||
1553 ((flags & PF_RANDMMAP) && (flags & PF_NORANDMMAP))))
1554 setpax = flags;
1555 break;
1556 }
1144 case 'g': gmatch = 1; 1557 case 'g': gmatch = 1; break;
1558 case 'L': use_ldcache = 1; break;
1145 case 'y': scan_symlink = 0; break; 1559 case 'y': scan_symlink = 0; break;
1560 case 'A': scan_archives = 1; break;
1146 case 'B': show_banner = 0; break; 1561 case 'B': show_banner = 0; break;
1147 case 'l': scan_ldpath = 1; break; 1562 case 'l': scan_ldpath = 1; break;
1148 case 'p': scan_envpath = 1; break; 1563 case 'p': scan_envpath = 1; break;
1149 case 'R': dir_recurse = 1; break; 1564 case 'R': dir_recurse = 1; break;
1150 case 'm': dir_crossmount = 0; break; 1565 case 'm': dir_crossmount = 0; break;
1566 case 'X': ++fix_elf; break;
1151 case 'x': show_pax = 1; break; 1567 case 'x': show_pax = 1; break;
1152 case 'e': show_phdr = 1; break; 1568 case 'e': show_phdr = 1; break;
1153 case 't': show_textrel = 1; break; 1569 case 't': show_textrel = 1; break;
1154 case 'r': show_rpath = 1; break; 1570 case 'r': show_rpath = 1; break;
1155 case 'n': show_needed = 1; break; 1571 case 'n': show_needed = 1; break;
1160 case 'q': be_quiet = 1; break; 1576 case 'q': be_quiet = 1; break;
1161 case 'v': be_verbose = (be_verbose % 20) + 1; break; 1577 case 'v': be_verbose = (be_verbose % 20) + 1; break;
1162 case 'a': show_pax = show_phdr = show_textrel = show_rpath = show_bind = 1; break; 1578 case 'a': show_pax = show_phdr = show_textrel = show_rpath = show_bind = 1; break;
1163 1579
1164 case ':': 1580 case ':':
1165 err("Option missing parameter\n"); 1581 err("Option '%c' is missing parameter", optopt);
1166 case '?': 1582 case '?':
1167 err("Unknown option\n"); 1583 err("Unknown option '%c' or argument missing", optopt);
1168 default: 1584 default:
1169 err("Unhandled option '%c'", i); 1585 err("Unhandled option '%c'; please report this", i);
1170 } 1586 }
1171 } 1587 }
1172 1588
1173 /* let the format option override all other options */ 1589 /* let the format option override all other options */
1174 if (out_format) { 1590 if (out_format) {
1182 case '%': break; 1598 case '%': break;
1183 case '#': break; 1599 case '#': break;
1184 case 'F': break; 1600 case 'F': break;
1185 case 'p': break; 1601 case 'p': break;
1186 case 'f': break; 1602 case 'f': break;
1603 case 'k': break;
1187 case 's': break; 1604 case 's': break;
1188 case 'N': break; 1605 case 'N': break;
1189 case 'o': break; 1606 case 'o': break;
1190 case 'x': show_pax = 1; break; 1607 case 'x': show_pax = 1; break;
1191 case 'e': show_phdr = 1; break; 1608 case 'e': show_phdr = 1; break;
1215 if (show_interp) xstrcat(&out_format, "%i ", &fmt_len); 1632 if (show_interp) xstrcat(&out_format, "%i ", &fmt_len);
1216 if (show_bind) xstrcat(&out_format, "%b ", &fmt_len); 1633 if (show_bind) xstrcat(&out_format, "%b ", &fmt_len);
1217 if (show_soname) xstrcat(&out_format, "%S ", &fmt_len); 1634 if (show_soname) xstrcat(&out_format, "%S ", &fmt_len);
1218 if (show_textrels) xstrcat(&out_format, "%T ", &fmt_len); 1635 if (show_textrels) xstrcat(&out_format, "%T ", &fmt_len);
1219 if (find_sym) xstrcat(&out_format, "%s ", &fmt_len); 1636 if (find_sym) xstrcat(&out_format, "%s ", &fmt_len);
1637 if (find_section) xstrcat(&out_format, "%k ", &fmt_len);
1220 if (find_lib) xstrcat(&out_format, "%N ", &fmt_len); 1638 if (find_lib) xstrcat(&out_format, "%N ", &fmt_len);
1221 if (!be_quiet) xstrcat(&out_format, "%F ", &fmt_len); 1639 if (!be_quiet) xstrcat(&out_format, "%F ", &fmt_len);
1222 } 1640 }
1223 if (be_verbose > 2) printf("Format: %s\n", out_format); 1641 if (be_verbose > 2) printf("Format: %s\n", out_format);
1224 1642
1225 /* now lets actually do the scanning */ 1643 /* now lets actually do the scanning */
1226 if (scan_ldpath || (show_rpath && be_quiet)) 1644 if (scan_ldpath || use_ldcache)
1227 load_ld_so_conf(); 1645 load_ld_so_conf(0, "/etc/ld.so.conf");
1228 if (scan_ldpath) scanelf_ldpath(); 1646 if (scan_ldpath) scanelf_ldpath();
1229 if (scan_envpath) scanelf_envpath(); 1647 if (scan_envpath) scanelf_envpath();
1230 if (from_file) { 1648 if (from_file) {
1231 scanelf_from_file(from_file); 1649 scanelf_from_file(from_file);
1232 free(from_file);
1233 from_file = *argv; 1650 from_file = *argv;
1234 } 1651 }
1235 if (optind == argc && !scan_ldpath && !scan_envpath && !from_file) 1652 if (optind == argc && !scan_ldpath && !scan_envpath && !from_file)
1236 err("Nothing to scan !?"); 1653 err("Nothing to scan !?");
1237 while (optind < argc) { 1654 while (optind < argc) {
1241 1658
1242 /* clean up */ 1659 /* clean up */
1243 if (versioned_symname) free(versioned_symname); 1660 if (versioned_symname) free(versioned_symname);
1244 for (i = 0; ldpaths[i]; ++i) 1661 for (i = 0; ldpaths[i]; ++i)
1245 free(ldpaths[i]); 1662 free(ldpaths[i]);
1663
1664 if (ldcache != 0)
1665 munmap(ldcache, ldcache_size);
1246} 1666}
1247 1667
1248 1668
1249 1669
1250/* utility funcs */ 1670/* utility funcs */
1252{ 1672{
1253 char *ret = strdup(s); 1673 char *ret = strdup(s);
1254 if (!ret) err("Could not strdup(): %s", strerror(errno)); 1674 if (!ret) err("Could not strdup(): %s", strerror(errno));
1255 return ret; 1675 return ret;
1256} 1676}
1257
1258static void *xmalloc(size_t size) 1677static void *xmalloc(size_t size)
1259{ 1678{
1260 void *ret = malloc(size); 1679 void *ret = malloc(size);
1261 if (!ret) err("Could not malloc() %li bytes", (unsigned long)size); 1680 if (!ret) err("Could not malloc() %li bytes", (unsigned long)size);
1262 return ret; 1681 return ret;
1263} 1682}
1264
1265static void xstrcat(char **dst, const char *src, size_t *curr_len) 1683static void xstrncat(char **dst, const char *src, size_t *curr_len, size_t n)
1266{ 1684{
1267 size_t new_len; 1685 size_t new_len;
1268 1686
1269 new_len = strlen(*dst) + strlen(src); 1687 new_len = strlen(*dst) + strlen(src);
1270 if (*curr_len <= new_len) { 1688 if (*curr_len <= new_len) {
1271 *curr_len = new_len + (*curr_len / 2); 1689 *curr_len = new_len + (*curr_len / 2);
1272 *dst = realloc(*dst, *curr_len); 1690 *dst = realloc(*dst, *curr_len);
1273 if (!*dst) 1691 if (!*dst)
1274 err("could not realloc %li bytes", (unsigned long)*curr_len); 1692 err("could not realloc() %li bytes", (unsigned long)*curr_len);
1275 } 1693 }
1276 1694
1695 if (n)
1696 strncat(*dst, src, n);
1697 else
1277 strcat(*dst, src); 1698 strcat(*dst, src);
1278} 1699}
1279
1280static inline void xchrcat(char **dst, const char append, size_t *curr_len) 1700static inline void xchrcat(char **dst, const char append, size_t *curr_len)
1281{ 1701{
1282 static char my_app[2]; 1702 static char my_app[2];
1283 my_app[0] = append; 1703 my_app[0] = append;
1284 my_app[1] = '\0'; 1704 my_app[1] = '\0';

Legend:
Removed from v.1.93  
changed lines
  Added in v.1.127

  ViewVC Help
Powered by ViewVC 1.1.20