diff -u pax-utils-0.0.9/Makefile pax-utils-0.1.0/Makefile --- pax-utils-0.0.9/Makefile 2005-05-28 02:31:18.000000000 -0400 +++ pax-utils-0.1.0/Makefile 2005-06-08 11:34:05.000000000 -0400 @@ -1,6 +1,6 @@ # Copyright 2003 Ned Ludd # Distributed under the terms of the GNU General Public License v2 -# $Header: /var/cvsroot/gentoo-projects/pax-utils/Makefile,v 1.22 2005/05/27 02:53:14 vapier Exp $ +# $Header: /var/cvsroot/gentoo-projects/pax-utils/Makefile,v 1.29 2005/06/08 03:02:49 vapier Exp $ #################################################################### # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as @@ -19,19 +19,29 @@ #################################################################### #################################################### -CFLAGS := -Wall -O2 -#CFLAGS += -DEBUG -g -#LDFLAGS :=-pie -DESTDIR = -PREFIX := $(DESTDIR)/usr -STRIP := strip -MKDIR := mkdir -p -CP := cp +WFLAGS := -Wall -Wextra -Wunused -Wimplicit -Wshadow -Wformat=2 \ + -Wmissing-declarations -Wmissing-prototypes -Wwrite-strings \ + -Wbad-function-cast -Wnested-externs -Wcomment -Wsequence-point \ + -Wdeclaration-after-statement -Wchar-subscripts -Wcast-align \ + -Winline +CFLAGS := -O2 -pipe +#CFLAGS += -DEBUG -g +#LDFLAGS := -pie +DESTDIR = +PREFIX := $(DESTDIR)/usr +STRIP := strip +MKDIR := mkdir -p +CP := cp + +# Build with -Werror while emerging +ifneq ($(S),) +WFLAGS += -Werror +endif ##################################################### -TARGETS = scanelf pspax dumpelf -OBJS = ${TARGETS:%=%.o} paxelf.o -MPAGES = ${TARGETS:%=man/%.1} -SOURCES = ${OBJS:%.o=%.c} +TARGETS = scanelf pspax dumpelf +OBJS = ${TARGETS:%=%.o} paxelf.o +MPAGES = ${TARGETS:%=man/%.1} +SOURCES = ${OBJS:%.o=%.c} all: $(OBJS) $(TARGETS) @: @@ -39,9 +49,10 @@ debug: all @-/sbin/chpax -permsx $(TARGETS) @-/sbin/paxctl -permsx $(TARGETS) - + %.o: %.c - $(CC) $(CFLAGS) -c $< + @echo $(CC) $(CFLAGS) -c $< + @$(CC) $(CFLAGS) $(WFLAGS) -c $< %: %.o paxelf.o $(CC) $(CFLAGS) paxelf.o -o $@ $< $(LDFLAGS) @@ -57,11 +68,11 @@ distclean: clean -rm -f *~ core - + install: all -$(STRIP) $(TARGETS) -$(MKDIR) $(PREFIX)/bin/ $(PREFIX)/share/man/man1/ - -$(CP) $(TARGETS) $(PREFIX)/bin/ + $(CP) $(TARGETS) $(PREFIX)/bin/ for mpage in $(MPAGES) ; do \ [ -e $$mpage ] \ && cp $$mpage $(PREFIX)/share/man/man1/ || : ;\ diff -u pax-utils-0.0.9/README pax-utils-0.1.0/README --- pax-utils-0.0.9/README 2005-05-28 02:31:18.000000000 -0400 +++ pax-utils-0.1.0/README 2005-06-08 11:34:05.000000000 -0400 @@ -7,7 +7,7 @@ Usage: scanelf [options] [dir2 dirN fileN ...] -Options: -[plRmyxetrnibs:aqvF:f:o:BhV] +Options: -[plRmyxetrnibs:N:TaqvF:f:o:BhV] -p, --path * Scan all directories in PATH environment -l, --ldpath * Scan all directories in /etc/ld.so.conf -R, --recursive * Scan directories recursively @@ -15,13 +15,15 @@ -y, --symlink * Don't scan symlinks -x, --pax * Print PaX markings - -e, --header * Print GNU_STACK markings + -e, --header * Print GNU_STACK/PT_LOAD markings -t, --textrel * Print TEXTREL information -r, --rpath * Print RPATH information -n, --needed * Print NEEDED information -i, --interp * Print INTERP information -b, --bind * Print BIND information -s, --symbol * Find a specified symbol + -N, --lib * Find a specified library + -T, --textrels * Locate cause of TEXTREL -a, --all * Print all scanned info (-x -e -t -r -n -i -b) -q, --quiet * Only output 'bad' things @@ -34,26 +36,45 @@ -V, --version * Print version and exit The format modifiers for the -F option are: - %F Filename %x PaX Flags %e STACK/RELRO - %t TEXTREL %r RPATH %n NEEDED - %i INTERP %b BIND %s symbol + F Filename x PaX Flags e STACK/RELRO + t TEXTREL r RPATH n NEEDED + i INTERP b BIND s symbol + N library o Type T TEXTRELs + p filename (with search path removed) + f base filename + +Prefix each modifier with '%' (verbose) or '#' (silent) == pspax == Usage: pspax [options] Options: - -a, --all × Show all processes + -a, --all * Show all processes - -B, --nobanner × Don't display the header - -h, --help × Print this help and exit - -V, --version × Print version and exit + -B, --nobanner * Don't display the header + -h, --help * Print this help and exit + -V, --version * Print version and exit If pspax was compiled with CFLAGS += -DWANT_SYSCAP and LDFLAGS +=- lcap it will also list runtime capabilities If extended attribute support is enabled it will list those values as well. +== dumpelf == +* Dump internal ELF structure + +Usage: dumpelf [file2 fileN ...] + +Options: + -v, --verbose * Be verbose (can be specified more than once) + -h, --help * Print this help and exit + -V, --version * Print version and exit + + == INSTALL == make install -PaX Homepage: -http://pax.grsecurity.net/ +You don't need PaX to use the pax-utils. Infact the only thing they +really have in common is that pax-utils was initially written to aid in +deploying PaX systems so it includes support for PT_PAX_FLAGS and the +deprecated but still in use EI_PAX flags. For more information about PaX +see the homepage at http://pax.grsecurity.net/ diff -u pax-utils-0.0.9/dumpelf.c pax-utils-0.1.0/dumpelf.c --- pax-utils-0.0.9/dumpelf.c 2005-05-28 02:31:18.000000000 -0400 +++ pax-utils-0.1.0/dumpelf.c 2005-06-08 11:34:05.000000000 -0400 @@ -1,7 +1,7 @@ /* - * Copyright 1999-2005 Gentoo Foundation + * Copyright 2005 Gentoo Foundation * Distributed under the terms of the GNU General Public License v2 - * $Header: /var/cvsroot/gentoo-projects/pax-utils/dumpelf.c,v 1.6 2005/05/10 22:53:31 vapier Exp $ + * $Header: /var/cvsroot/gentoo-projects/pax-utils/dumpelf.c,v 1.10 2005/06/03 23:18:27 vapier Exp $ */ #include @@ -15,17 +15,18 @@ #include #include #include +#include #include "paxelf.h" -static const char *rcsid = "$Id: dumpelf.c,v 1.6 2005/05/10 22:53:31 vapier Exp $"; +static const char *rcsid = "$Id: dumpelf.c,v 1.10 2005/06/03 23:18:27 vapier Exp $"; #define argv0 "dumpelf" /* prototypes */ static void dumpelf(const char *filename, long file_cnt); static void dump_ehdr(elfobj *elf, void *ehdr); static void dump_phdr(elfobj *elf, void *phdr, long phdr_cnt); -static void dump_shdr(elfobj *elf, void *shdr, long shdr_cnt); +static void dump_shdr(elfobj *elf, void *shdr, long shdr_cnt, char *name); #if 0 static void dump_dyn(elfobj *elf, void *dyn); static void dump_sym(elfobj *elf, void *sym); @@ -38,7 +39,7 @@ /* variables to control behavior */ - /* none yet ! */ +static char be_verbose = 0; @@ -104,10 +105,12 @@ if (elf->elf_class == ELFCLASS ## B) { \ Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ Elf ## B ## _Shdr *shdr = SHDR ## B (elf->shdr); \ + uint16_t shstrndx = EGET(ehdr->e_shstrndx); \ + Elf ## B ## _Off offset = EGET(shdr[shstrndx].sh_offset); \ uint16_t shnum = EGET(ehdr->e_shnum); \ for (i = 0; i < shnum; ++i) { \ if (i) printf(",\n"); \ - dump_shdr(elf, shdr, i); \ + dump_shdr(elf, shdr, i, (char*)(elf->data + offset + EGET(shdr->sh_name))); \ ++shdr; \ } } DUMP_SHDRS(32) @@ -138,10 +141,10 @@ /* "\t\t/ [%i] EI_BRAND: / 0x%02X\n" */ \ "\t},\n", \ EI_MAG0, (unsigned int)ehdr->e_ident[EI_MAG0], ehdr->e_ident[EI_MAG1], ehdr->e_ident[EI_MAG2], ehdr->e_ident[EI_MAG3], \ - EI_CLASS, (int)ehdr->e_ident[EI_CLASS], get_elfeitype(elf, EI_CLASS, ehdr->e_ident[EI_CLASS]), \ - EI_DATA, (int)ehdr->e_ident[EI_DATA], get_elfeitype(elf, EI_DATA, ehdr->e_ident[EI_DATA]), \ - EI_VERSION, (int)ehdr->e_ident[EI_VERSION], get_elfeitype(elf, EI_VERSION, ehdr->e_ident[EI_VERSION]), \ - EI_OSABI, (int)ehdr->e_ident[EI_OSABI], get_elfeitype(elf, EI_OSABI, ehdr->e_ident[EI_OSABI]), \ + EI_CLASS, (int)ehdr->e_ident[EI_CLASS], get_elfeitype(EI_CLASS, ehdr->e_ident[EI_CLASS]), \ + EI_DATA, (int)ehdr->e_ident[EI_DATA], get_elfeitype(EI_DATA, ehdr->e_ident[EI_DATA]), \ + EI_VERSION, (int)ehdr->e_ident[EI_VERSION], get_elfeitype(EI_VERSION, ehdr->e_ident[EI_VERSION]), \ + EI_OSABI, (int)ehdr->e_ident[EI_OSABI], get_elfeitype(EI_OSABI, ehdr->e_ident[EI_OSABI]), \ EI_ABIVERSION, (int)ehdr->e_ident[EI_ABIVERSION], \ EI_PAD, (unsigned int)ehdr->e_ident[EI_PAD], EI_NIDENT - EI_PAD \ /* EI_BRAND, ehdr->e_ident[EI_BRAND] */ \ @@ -183,14 +186,18 @@ DUMP_PHDR(32) DUMP_PHDR(64) } -static void dump_shdr(elfobj *elf, void *shdr_void, long shdr_cnt) +static void dump_shdr(elfobj *elf, void *shdr_void, long shdr_cnt, char *name) { + unsigned long i; #define DUMP_SHDR(B) \ if (elf->elf_class == ELFCLASS ## B) { \ Elf ## B ## _Shdr *shdr = SHDR ## B (shdr_void); \ - printf("/* Section Header #%li 0x%lX */\n{\n", shdr_cnt, (unsigned long)shdr_void - (unsigned long)elf->data); \ + uint32_t type = EGET(shdr->sh_type); \ + uint ## B ## _t size = EGET(shdr->sh_size); \ + printf("/* Section Header #%li '%s' 0x%lX */\n{\n", \ + shdr_cnt, name, (unsigned long)shdr_void - (unsigned long)elf->data); \ printf("\t.sh_name = %-10i ,\n", (int)EGET(shdr->sh_name)); \ - printf("\t.sh_type = %-10i ,\n", (int)EGET(shdr->sh_type)); \ + printf("\t.sh_type = %-10i , /* [%s] */\n", (int)EGET(shdr->sh_type), get_elfshttype(type)); \ printf("\t.sh_flags = %-10li ,\n", (long)EGET(shdr->sh_flags)); \ printf("\t.sh_addr = 0x%-8lX ,\n", (unsigned long)EGET(shdr->sh_addr)); \ printf("\t.sh_offset = %-10i , /* (bytes) */\n", (int)EGET(shdr->sh_offset)); \ @@ -199,6 +206,36 @@ printf("\t.sh_info = %-10i ,\n", (int)EGET(shdr->sh_info)); \ printf("\t.sh_addralign = %-10li ,\n", (long)EGET(shdr->sh_addralign)); \ printf("\t.sh_entsize = %-10li\n", (long)EGET(shdr->sh_entsize)); \ + if (size && \ + ((be_verbose && type == SHT_STRTAB) || \ + (be_verbose > 1))) { \ + unsigned char *data = (unsigned char*)(elf->data + EGET(shdr->sh_offset)); \ + char bool; \ + printf("\n\t/* section dump:\n"); \ + if (type == SHT_STRTAB) { \ + bool = 1; \ + for (i = 0; i < size; ++i) { \ + ++data; \ + if (*data) { \ + if (bool) printf("\t * "); \ + printf("%c", *data); \ + bool = 0; \ + } else if (!bool) { \ + printf("\n"); \ + bool = 1; \ + } \ + } \ + } else { \ + for (i = 0; i < size; ++i) { \ + ++data; \ + if (i % 20 == 0) printf("\t * "); \ + printf("%.2X ", *data); \ + if (i % 20 == 19) printf("\n"); \ + } \ + if (i % 20) printf("\n"); \ + } \ + printf("\t */\n"); \ + } \ printf("}"); \ } DUMP_SHDR(32) @@ -208,14 +245,16 @@ /* usage / invocation handling functions */ -#define PARSE_FLAGS "hV" +#define PARSE_FLAGS "vhV" #define a_argument required_argument static struct option const long_opts[] = { + {"verbose", no_argument, NULL, 'v'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, {NULL, no_argument, NULL, 0x0} }; -static char *opts_help[] = { +static const char *opts_help[] = { + "Be verbose (can be specified more than once)", "Print this help and exit", "Print version and exit", NULL @@ -225,15 +264,15 @@ static void usage(int status) { int i; - printf("¤ Dump internal ELF structure\n\n" + printf("* Dump internal ELF structure\n\n" "Usage: %s [file2 fileN ...]\n\n", argv0); printf("Options:\n"); for (i = 0; long_opts[i].name; ++i) if (long_opts[i].has_arg == no_argument) - printf(" -%c, --%-13s× %s\n", long_opts[i].val, + printf(" -%c, --%-13s* %s\n", long_opts[i].val, long_opts[i].name, opts_help[i]); else - printf(" -%c, --%-6s × %s\n", long_opts[i].val, + printf(" -%c, --%-6s * %s\n", long_opts[i].val, long_opts[i].name, opts_help[i]); exit(status); } @@ -255,17 +294,14 @@ break; case 'h': usage(EXIT_SUCCESS); break; + case 'v': be_verbose = (be_verbose % 20) + 1; break; + case ':': - warn("Option missing parameter"); - usage(EXIT_FAILURE); - break; + err("Option missing parameter"); case '?': - warn("Unknown option"); - usage(EXIT_FAILURE); - break; + err("Unknown option"); default: err("Unhandled option '%c'", flag); - break; } } Common subdirectories: pax-utils-0.0.9/man and pax-utils-0.1.0/man diff -u pax-utils-0.0.9/paxelf.c pax-utils-0.1.0/paxelf.c --- pax-utils-0.0.9/paxelf.c 2005-05-28 02:31:18.000000000 -0400 +++ pax-utils-0.1.0/paxelf.c 2005-06-08 11:34:05.000000000 -0400 @@ -1,8 +1,7 @@ /* - * Copyright 2003 Ned Ludd - * Copyright 1999-2005 Gentoo Foundation + * Copyright 2003-2005 Gentoo Foundation * Distributed under the terms of the GNU General Public License v2 - * $Header: /var/cvsroot/gentoo-projects/pax-utils/paxelf.c,v 1.21 2005/05/25 21:58:03 vapier Exp $ + * $Header: /var/cvsroot/gentoo-projects/pax-utils/paxelf.c,v 1.26 2005/06/08 04:16:35 vapier Exp $ * ******************************************************************** * This program is free software; you can redistribute it and/or @@ -20,12 +19,6 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. ******************************************************************** - * - * This program was written for the hcc suite by (solar|pappy)@g.o. - * visit http://www.gentoo.org/proj/en/hardened/etdyn-ssp.xml for more - * information on the Gentoo Hardened gcc suite - * Also of interest is the pax site http://pax.grsecurity.net/ - * but you should know about that already. */ #include @@ -34,7 +27,6 @@ #include #include #include - #include "paxelf.h" #define argv0 "paxelf" @@ -97,7 +89,7 @@ QUERY(ELFOSABI_STANDALONE), { 0, 0 } }; -const char *get_elfeitype(elfobj *elf, int ei_type, int type) +const char *get_elfeitype(int ei_type, int type) { switch (ei_type) { case EI_CLASS: return find_pairtype(elf_ei_class, type); @@ -282,6 +274,49 @@ return find_pairtype(elf_dtypes, type); } +/* translate elf SHT_ defines */ +static pairtype elf_shttypes[] = { + QUERY(SHT_NULL), + QUERY(SHT_PROGBITS), + QUERY(SHT_SYMTAB), + QUERY(SHT_STRTAB), + QUERY(SHT_RELA), + QUERY(SHT_HASH), + QUERY(SHT_DYNAMIC), + QUERY(SHT_NOTE), + QUERY(SHT_NOBITS), + QUERY(SHT_REL), + QUERY(SHT_SHLIB), + QUERY(SHT_DYNSYM), + QUERY(SHT_INIT_ARRAY), + QUERY(SHT_FINI_ARRAY), + QUERY(SHT_PREINIT_ARRAY), + QUERY(SHT_GROUP), + QUERY(SHT_SYMTAB_SHNDX), + QUERY(SHT_NUM), + QUERY(SHT_LOOS), + QUERY(SHT_GNU_LIBLIST), + QUERY(SHT_CHECKSUM), + QUERY(SHT_LOSUNW), + QUERY(SHT_SUNW_move), + QUERY(SHT_SUNW_COMDAT), + QUERY(SHT_SUNW_syminfo), + QUERY(SHT_GNU_verdef), + QUERY(SHT_GNU_verneed), + QUERY(SHT_GNU_versym), + QUERY(SHT_HISUNW), + QUERY(SHT_HIOS), + QUERY(SHT_LOPROC), + QUERY(SHT_HIPROC), + QUERY(SHT_LOUSER), + QUERY(SHT_HIUSER), + { 0, 0 } +}; +const char *get_elfshttype(int type) +{ + return find_pairtype(elf_shttypes, type); +} + /* translate elf STT_ defines */ static pairtype elf_stttypes[] = { QUERY(STT_NOTYPE), @@ -347,13 +382,19 @@ if (!DO_WE_LIKE_ELF(elf->data)) { /* check class and stuff */ warn("we no likey %s: {%s,%s,%s,%s}", filename, - get_elfeitype(elf, EI_CLASS, elf->data[EI_CLASS]), - get_elfeitype(elf, EI_DATA, elf->data[EI_DATA]), - get_elfeitype(elf, EI_VERSION, elf->data[EI_VERSION]), - get_elfeitype(elf, EI_OSABI, elf->data[EI_OSABI])); + get_elfeitype(EI_CLASS, elf->data[EI_CLASS]), + get_elfeitype(EI_DATA, elf->data[EI_DATA]), + get_elfeitype(EI_VERSION, elf->data[EI_VERSION]), + get_elfeitype(EI_OSABI, elf->data[EI_OSABI])); goto unmap_data_and_return; } + elf->filename = filename; + elf->base_filename = strrchr(filename, '/'); + if (elf->base_filename == NULL) + elf->base_filename = elf->filename; + else + elf->base_filename = elf->base_filename + 1; elf->elf_class = elf->data[EI_CLASS]; do_reverse_endian = (ELF_DATA != elf->data[EI_DATA]); elf->ehdr = (void*)elf->data; @@ -385,6 +426,7 @@ } READELF_HEADER(32) READELF_HEADER(64) + /* { char *p; strncpy(elf->basename, (p = strrchr(filename, '/')) == NULL ? "?" : p+1 , sizeof(elf->basename)); } */ return elf; @@ -405,10 +447,6 @@ free(elf); } -/* the display logic is: - * lower case: explicitly disabled - * upper case: explicitly enabled - * - : default */ char *pax_short_hf_flags(unsigned long flags) { static char buffer[7]; @@ -423,23 +461,41 @@ return buffer; } + +/* the display logic is: + * lower case: explicitly disabled + * upper case: explicitly enabled + * - : default */ char *pax_short_pf_flags(unsigned long flags) { - static char buffer[13]; + static char buffer[7]; + /* PT_PAX_FLAGS are tristate */ buffer[0] = (flags & PF_PAGEEXEC ? 'P' : '-'); - buffer[1] = (flags & PF_NOPAGEEXEC ? 'p' : '-'); - buffer[2] = (flags & PF_SEGMEXEC ? 'S' : '-'); - buffer[3] = (flags & PF_NOSEGMEXEC ? 's' : '-'); - buffer[4] = (flags & PF_MPROTECT ? 'M' : '-'); - buffer[5] = (flags & PF_NOMPROTECT ? 'm' : '-'); - buffer[6] = (flags & PF_RANDEXEC ? 'X' : '-'); - buffer[7] = (flags & PF_NORANDEXEC ? 'x' : '-'); - buffer[8] = (flags & PF_EMUTRAMP ? 'E' : '-'); - buffer[9] = (flags & PF_NOEMUTRAMP ? 'e' : '-'); - buffer[10] = (flags & PF_RANDMMAP ? 'R' : '-'); - buffer[11] = (flags & PF_NORANDMMAP ? 'r' : '-'); - buffer[12] = 0; + buffer[0] = (flags & PF_NOPAGEEXEC ? 'p' : buffer[0]); + + buffer[1] = (flags & PF_SEGMEXEC ? 'S' : '-'); + buffer[1] = (flags & PF_NOSEGMEXEC ? 's' : buffer[1]); + + buffer[2] = (flags & PF_MPROTECT ? 'M' : '-'); + buffer[2] = (flags & PF_NOMPROTECT ? 'm' : buffer[2]); + + buffer[3] = (flags & PF_RANDEXEC ? 'X' : '-'); + buffer[3] = (flags & PF_NORANDEXEC ? 'x' : buffer[3]); + + buffer[4] = (flags & PF_EMUTRAMP ? 'E' : '-'); + buffer[4] = (flags & PF_NOEMUTRAMP ? 'e' : buffer[4]); + + buffer[5] = (flags & PF_RANDMMAP ? 'R' : '-'); + buffer[5] = (flags & PF_NORANDMMAP ? 'r' : buffer[5]); + + buffer[6] = 0; + + + if (((flags & PF_PAGEEXEC) && (flags & PF_NOPAGEEXEC)) || ((flags & PF_SEGMEXEC) && (flags & PF_NOSEGMEXEC)) + || ((flags & PF_RANDMMAP) && (flags & PF_NORANDMMAP)) || ((flags & PF_RANDEXEC) && (flags & PF_NORANDEXEC)) + || ((flags & PF_EMUTRAMP) && (flags & PF_NOEMUTRAMP)) || ((flags & PF_RANDMMAP) && (flags & PF_NORANDMMAP))) + warn("inconsistent state detected. flags=%lu\n", flags); return buffer; } @@ -458,7 +514,7 @@ void *elf_findsecbyname(elfobj *elf, const char *name) { - int i; + unsigned int i; char *shdr_name; if (elf->shdr == NULL) return NULL; @@ -476,7 +532,7 @@ for (i = 0; i < shnum; ++i) { \ if (EGET(shdr[i].sh_offset) >= elf->len - EGET(ehdr->e_shentsize)) continue; \ offset = EGET(strtbl->sh_offset) + EGET(shdr[i].sh_name); \ - if (offset >= elf->len) continue; \ + if (offset >= (Elf ## B ## _Off)elf->len) continue; \ shdr_name = (char*)(elf->data + offset); \ if (!strcmp(shdr_name, name)) \ return &(shdr[i]); \ diff -u pax-utils-0.0.9/paxelf.h pax-utils-0.1.0/paxelf.h --- pax-utils-0.0.9/paxelf.h 2005-05-28 02:31:18.000000000 -0400 +++ pax-utils-0.1.0/paxelf.h 2005-06-08 11:34:05.000000000 -0400 @@ -1,5 +1,5 @@ /* - * $Header: /var/cvsroot/gentoo-projects/pax-utils/paxelf.h,v 1.25 2005/05/27 02:58:11 vapier Exp $ + * $Header: /var/cvsroot/gentoo-projects/pax-utils/paxelf.h,v 1.30 2005/06/08 04:16:35 vapier Exp $ * Make sure all of the common elf stuff is setup as we expect */ @@ -24,17 +24,6 @@ } \ __res; \ })) -/* Set a value 'Y' in the elf header to 'X', compensating for endianness. */ -#define ESET(Y,X) \ - do if (!do_reverse_endian) { Y = (X); \ - } else if (sizeof(Y) == 1) { Y = (X); \ - } else if (sizeof(Y) == 2) { Y = bswap_16((uint16_t)(X)); \ - } else if (sizeof(Y) == 4) { Y = bswap_32((uint32_t)(X)); \ - } else if (sizeof(Y) == 8) { Y = bswap_64((uint64_t)(X)); \ - } else { \ - fprintf(stderr, "ESET failed ;(\n")); \ - exit(EXIT_FAILURE); \ - } while (0) typedef struct { void *ehdr; @@ -44,6 +33,8 @@ char elf_class; off_t len; int fd; + const char *filename; + const char *base_filename; } elfobj; #define EHDR32(ptr) ((Elf32_Ehdr *)(ptr)) #define EHDR64(ptr) ((Elf64_Ehdr *)(ptr)) @@ -51,6 +42,10 @@ #define PHDR64(ptr) ((Elf64_Phdr *)(ptr)) #define SHDR32(ptr) ((Elf32_Shdr *)(ptr)) #define SHDR64(ptr) ((Elf64_Shdr *)(ptr)) +#define RELA32(ptr) ((Elf32_Rela *)(ptr)) +#define RELA64(ptr) ((Elf64_Rela *)(ptr)) +#define REL32(ptr) ((Elf32_Rel *)(ptr)) +#define REL64(ptr) ((Elf64_Rel *)(ptr)) #define DYN32(ptr) ((Elf32_Dyn *)(ptr)) #define DYN64(ptr) ((Elf64_Dyn *)(ptr)) #define SYM32(ptr) ((Elf32_Sym *)(ptr)) @@ -62,11 +57,12 @@ extern char *gnu_short_stack_flags(unsigned long flags); extern elfobj *readelf(const char *filename); extern void unreadelf(elfobj *elf); -extern const char *get_elfeitype(elfobj *elf, int ei_type, int type); +extern const char *get_elfeitype(int ei_type, int type); extern const char *get_elfetype(elfobj *elf); extern const char *get_elfemtype(int type); extern const char *get_elfptype(int type); extern const char *get_elfdtype(int type); +extern const char *get_elfshttype(int type); extern const char *get_elfstttype(int type); extern void *elf_findsecbyname(elfobj *elf, const char *name); @@ -81,23 +77,24 @@ } while (0) /* PaX flags (to be read in elfhdr.e_flags) */ -#define HF_PAX_PAGEEXEC 1 /* 0: Paging based non-exec pages */ -#define HF_PAX_EMUTRAMP 2 /* 0: Emulate trampolines */ -#define HF_PAX_MPROTECT 4 /* 0: Restrict mprotect() */ -#define HF_PAX_RANDMMAP 8 /* 0: Randomize mmap() base */ -#define HF_PAX_RANDEXEC 16 /* 1: Randomize ET_EXEC base */ -#define HF_PAX_SEGMEXEC 32 /* 0: Segmentation based non-exec pages */ - -#define EI_PAX 14 /* Index in e_ident[] where to read flags */ -#define __PAX_FLAGS(B, elf) \ - ((EHDR ## B (elf->ehdr)->e_ident[EI_PAX + 1] << 8) + EHDR ## B (elf->ehdr)->e_ident[EI_PAX]) -#define PAX_FLAGS(elf) \ +#define HF_PAX_PAGEEXEC 1 /* 0: Paging based non-exec pages */ +#define HF_PAX_EMUTRAMP 2 /* 0: Emulate trampolines */ +#define HF_PAX_MPROTECT 4 /* 0: Restrict mprotect() */ +#define HF_PAX_RANDMMAP 8 /* 0: Randomize mmap() base */ +#define HF_PAX_RANDEXEC 16 /* 1: Randomize ET_EXEC base */ +#define HF_PAX_SEGMEXEC 32 /* 0: Segmentation based non-exec pages */ + +#define EI_PAX 14 /* Index in e_ident[] where to read flags */ +#define __EI_PAX_FLAGS(B, elf) \ + ((EHDR ## B (elf->ehdr)->e_ident[EI_PAX + 1] << 8) + \ + EHDR ## B (elf->ehdr)->e_ident[EI_PAX]) +#define EI_PAX_FLAGS(elf) \ (__extension__ ({ \ unsigned long __res; \ if (elf->elf_class == ELFCLASS32) \ - __res = __PAX_FLAGS(32, elf); \ + __res = __EI_PAX_FLAGS(32, elf); \ else \ - __res = __PAX_FLAGS(64, elf); \ + __res = __EI_PAX_FLAGS(64, elf); \ __res; \ })) diff -u pax-utils-0.0.9/porting.h pax-utils-0.1.0/porting.h --- pax-utils-0.0.9/porting.h 2005-05-28 02:31:18.000000000 -0400 +++ pax-utils-0.1.0/porting.h 2005-06-08 11:34:05.000000000 -0400 @@ -1,5 +1,5 @@ /* - * $Header: /var/cvsroot/gentoo-projects/pax-utils/porting.h,v 1.2 2005/05/27 22:15:16 vapier Exp $ + * $Header: /var/cvsroot/gentoo-projects/pax-utils/porting.h,v 1.3 2005/05/30 03:23:07 vapier Exp $ * Make sure all of the common elf stuff is setup as we expect */ @@ -15,10 +15,48 @@ # include #endif -#if defined(bswap16) && !defined(bswap_16) -# define bswap_16 bswap16 -# define bswap_32 bswap32 -# define bswap_64 bswap64 +#if defined(__APPLE__) +# define SET_STDOUT(fp) err("Darwin has stupid stdout handling") +#else +# define SET_STDOUT(fp) stdout = fp +#endif + +#if !defined(bswap_16) +# if defined(bswap16) +# define bswap_16 bswap16 +# define bswap_32 bswap32 +# define bswap_64 bswap64 +# else +# define bswap_16(x) \ + ((((x) & 0xff00) >> 8) | \ + (((x) & 0x00ff) << 8)) +# define bswap_32(x) \ + ((((x) & 0xff000000) >> 24) | \ + (((x) & 0x00ff0000) >> 8) | \ + (((x) & 0x0000ff00) << 8) | \ + (((x) & 0x000000ff) << 24)) +# if defined(__GNUC__) +# define bswap_64(x) \ + ((((x) & 0xff00000000000000ull) >> 56) | \ + (((x) & 0x00ff000000000000ull) >> 40) | \ + (((x) & 0x0000ff0000000000ull) >> 24) | \ + (((x) & 0x000000ff00000000ull) >> 8) | \ + (((x) & 0x00000000ff000000ull) << 8) | \ + (((x) & 0x0000000000ff0000ull) << 24) | \ + (((x) & 0x000000000000ff00ull) << 40) | \ + (((x) & 0x00000000000000ffull) << 56)) +# else +# define bswap_64(x) \ + ((((x) & 0xff00000000000000) >> 56) | \ + (((x) & 0x00ff000000000000) >> 40) | \ + (((x) & 0x0000ff0000000000) >> 24) | \ + (((x) & 0x000000ff00000000) >> 8) | \ + (((x) & 0x00000000ff000000) << 8) | \ + (((x) & 0x0000000000ff0000) << 24) | \ + (((x) & 0x000000000000ff00) << 40) | \ + (((x) & 0x00000000000000ff) << 56)) +# endif +# endif #endif #if !defined(ELF_DATA) diff -u pax-utils-0.0.9/pspax.c pax-utils-0.1.0/pspax.c --- pax-utils-0.0.9/pspax.c 2005-05-28 02:31:18.000000000 -0400 +++ pax-utils-0.1.0/pspax.c 2005-06-08 11:34:05.000000000 -0400 @@ -41,7 +41,7 @@ #define PROC_DIR "/proc" -static const char *rcsid = "$Id: pspax.c,v 1.17 2005/05/27 22:15:03 vapier Exp $"; +static const char *rcsid = "$Id: pspax.c,v 1.20 2005/06/08 04:34:59 solar Exp $"; #define argv0 "pspax" @@ -73,7 +73,7 @@ return (str+1); } -int get_proc_maps(pid_t pid) { +static int get_proc_maps(pid_t pid) { static char str[_POSIX_PATH_MAX]; FILE *fp; @@ -131,7 +131,7 @@ return NULL; } -static char *get_proc_status(pid_t pid, char *name) +static char *get_proc_status(pid_t pid, const char *name) { FILE *fp; size_t len; @@ -219,7 +219,7 @@ if (show_banner) printf("%-8s %-6s %-6s %-4s %-10s %-16s %-4s %-4s\n", - "USER", "PID", "PAX", "MAPS", "ELF_TYPE", "NAME", "CAPS", "ATTR"); + "USER", "PID", "PAX", "MAPS", "ETYPE", "NAME", "CAPS", "ATTR"); while ((de = readdir(dir))) { errno = 0; @@ -235,9 +235,9 @@ caps = cap_to_text(cap_d, &length); #endif - uid = get_proc_uid(pid); - pax = get_proc_status(pid, "PAX"); - wx = get_proc_maps(pid); + uid = get_proc_uid(pid); + pax = get_proc_status(pid, "PAX"); + wx = get_proc_maps(pid); type = get_proc_type(pid); name = get_proc_name(pid); attr = (have_attr ? get_pid_attr(pid) : NULL); @@ -272,7 +272,7 @@ {"version", no_argument, NULL, 'V'}, {NULL, no_argument, NULL, 0x0} }; -static char *opts_help[] = { +static const char *opts_help[] = { "Show all processes\n", "Don't display the header", "Print this help and exit", @@ -284,7 +284,7 @@ static void usage(int status) { int i; - printf("¤ List ELF/PaX information about running processes\n\n" + printf("* List ELF/PaX information about running processes\n\n" "Usage: %s [options]\n\n", argv0); fputs("Options:\n", stdout); for (i = 0; long_opts[i].name; ++i) diff -u pax-utils-0.0.9/scanelf.c pax-utils-0.1.0/scanelf.c --- pax-utils-0.0.9/scanelf.c 2005-05-28 02:31:18.000000000 -0400 +++ pax-utils-0.1.0/scanelf.c 2005-06-08 11:34:05.000000000 -0400 @@ -1,8 +1,7 @@ /* - * Copyright 2003 Ned Ludd - * Copyright 1999-2005 Gentoo Foundation + * Copyright 2003-2005 Gentoo Foundation * Distributed under the terms of the GNU General Public License v2 - * $Header: /var/cvsroot/gentoo-projects/pax-utils/scanelf.c,v 1.60 2005/05/27 02:58:39 vapier Exp $ + * $Header: /var/cvsroot/gentoo-projects/pax-utils/scanelf.c,v 1.77 2005/06/08 05:43:01 vapier Exp $ * ******************************************************************** * This program is free software; you can redistribute it and/or @@ -34,12 +33,13 @@ #include #include #include - #include "paxelf.h" -static const char *rcsid = "$Id: scanelf.c,v 1.60 2005/05/27 02:58:39 vapier Exp $"; +static const char *rcsid = "$Id: scanelf.c,v 1.77 2005/06/08 05:43:01 vapier Exp $"; #define argv0 "scanelf" +#define IS_MODIFIER(c) (c == '%' || c == '#') + /* prototypes */ @@ -62,68 +62,147 @@ static char dir_recurse = 0; static char dir_crossmount = 1; static char show_pax = 0; -static char show_stack = 0; +static char show_phdr = 0; static char show_textrel = 0; static char show_rpath = 0; static char show_needed = 0; static char show_interp = 0; static char show_bind = 0; +static char show_textrels = 0; static char show_banner = 1; static char be_quiet = 0; static char be_verbose = 0; +static char be_wewy_wewy_quiet = 0; static char *find_sym = NULL, *versioned_symname = NULL; +static char *find_lib = NULL; static char *out_format = NULL; +static char *search_path = NULL; /* sub-funcs for scanelf_file() */ +static void scanelf_file_get_symtabs(elfobj *elf, void **sym, void **tab) +{ + /* find the best SHT_DYNSYM and SHT_STRTAB sections */ +#define GET_SYMTABS(B) \ + if (elf->elf_class == ELFCLASS ## B) { \ + Elf ## B ## _Shdr *symtab, *strtab, *dynsym, *dynstr; \ + /* debug sections */ \ + symtab = SHDR ## B (elf_findsecbyname(elf, ".symtab")); \ + strtab = SHDR ## B (elf_findsecbyname(elf, ".strtab")); \ + /* runtime sections */ \ + dynsym = SHDR ## B (elf_findsecbyname(elf, ".dynsym")); \ + dynstr = SHDR ## B (elf_findsecbyname(elf, ".dynstr")); \ + if (symtab && dynsym) { \ + *sym = (void*)((EGET(symtab->sh_size) > EGET(dynsym->sh_size)) ? symtab : dynsym); \ + } else { \ + *sym = (void*)(symtab ? symtab : dynsym); \ + } \ + if (strtab && dynstr) { \ + *tab = (void*)((EGET(strtab->sh_size) > EGET(dynstr->sh_size)) ? strtab : dynstr); \ + } else { \ + *tab = (void*)(strtab ? strtab : dynstr); \ + } \ + } + GET_SYMTABS(32) + GET_SYMTABS(64) +} static char *scanelf_file_pax(elfobj *elf, char *found_pax) { static char *paxflags; + static char ret[7]; + unsigned long i, shown; + if (!show_pax) return NULL; - paxflags = pax_short_hf_flags(PAX_FLAGS(elf)); - if (!be_quiet || (be_quiet && strncmp(paxflags, "PeMRxS", 6))) { - *found_pax = 1; - return paxflags; + shown = 0; + memset(&ret, 0, sizeof(ret)); + + if (elf->phdr) { +#define SHOW_PAX(B) \ + if (elf->elf_class == ELFCLASS ## B) { \ + Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ + Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ + for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ + if (EGET(phdr[i].p_type) != PT_PAX_FLAGS) \ + continue; \ + if (be_quiet && (EGET(phdr[i].p_flags) == 10240)) \ + continue; \ + memcpy(ret, pax_short_pf_flags(EGET(phdr[i].p_flags)), 6); \ + *found_pax = 1; \ + ++shown; \ + break; \ + } \ + } + SHOW_PAX(32) + SHOW_PAX(64) } - return NULL; + /* fall back to EI_PAX if no PT_PAX was found */ + if (!*ret) { + paxflags = pax_short_hf_flags(EI_PAX_FLAGS(elf)); + if (!be_quiet || (be_quiet && EI_PAX_FLAGS(elf))) { + *found_pax = 1; + return paxflags; + } + strncpy(ret, paxflags, sizeof(ret)); + // ++shown; + } + + if (be_quiet && !shown) + return NULL; + return ret; + } -static char *scanelf_file_stack(elfobj *elf, char *found_stack, char *found_relro) +static char *scanelf_file_phdr(elfobj *elf, char *found_phdr, char *found_relro, char *found_load) { - static char ret[8] = "--- ---"; + static char ret[12]; char *found; - unsigned long i, off, shown; + unsigned long i, off, shown, check_flags; + unsigned char multi_stack, multi_relro, multi_load; - if (!show_stack) return NULL; + if (!show_phdr) return NULL; + + memcpy(ret, "--- --- ---\0", 12); shown = 0; + multi_stack = multi_relro = multi_load = 0; if (elf->phdr) { -#define SHOW_STACK(B) \ +#define SHOW_PHDR(B) \ if (elf->elf_class == ELFCLASS ## B) { \ Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ + uint32_t flags; \ for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ if (EGET(phdr[i].p_type) == PT_GNU_STACK) { \ - found = found_stack; \ + if (multi_stack++) warnf("%s: multiple PT_GNU_STACK's !?", elf->filename); \ + found = found_phdr; \ off = 0; \ + check_flags = PF_X; \ } else if (EGET(phdr[i].p_type) == PT_GNU_RELRO) { \ + if (multi_relro++) warnf("%s: multiple PT_GNU_RELRO's !?", elf->filename); \ found = found_relro; \ off = 4; \ + check_flags = PF_X; \ + } else if (EGET(phdr[i].p_type) == PT_LOAD) { \ + if (multi_load++ > 2) warnf("%s: more than 2 PT_LOAD's !?", elf->filename); \ + found = found_load; \ + off = 8; \ + check_flags = PF_W|PF_X; \ } else \ continue; \ - if (be_quiet && !(EGET(phdr[i].p_flags) & PF_X)) \ + flags = EGET(phdr[i].p_flags); \ + if (be_quiet && ((flags & check_flags) != check_flags)) \ continue; \ - memcpy(ret+off, gnu_short_stack_flags(EGET(phdr[i].p_flags)), 3); \ + memcpy(ret+off, gnu_short_stack_flags(flags), 3); \ *found = 1; \ ++shown; \ } \ } - SHOW_STACK(32) - SHOW_STACK(64) + SHOW_PHDR(32) + SHOW_PHDR(64) } if (be_quiet && !shown) @@ -133,7 +212,7 @@ } static char *scanelf_file_textrel(elfobj *elf, char *found_textrel) { - static char *ret = "TEXTREL"; + static char ret[] = "TEXTREL"; unsigned long i; if (!show_textrel) return NULL; @@ -154,7 +233,7 @@ if (EGET(dyn->d_tag) == DT_TEXTREL) { /*dyn->d_tag != DT_FLAGS)*/ \ *found_textrel = 1; \ /*if (dyn->d_un.d_val & DF_TEXTREL)*/ \ - return ret; \ + return (be_wewy_wewy_quiet ? NULL : ret); \ } \ ++dyn; \ } \ @@ -163,14 +242,112 @@ SHOW_TEXTREL(64) } - if (be_quiet) + if (be_quiet || be_wewy_wewy_quiet) return NULL; else - return " - "; + return (char *)" - "; +} +static char *scanelf_file_textrels(elfobj *elf, char *found_textrels) +{ + unsigned long p, s, r, rmax; + void *symtab_void, *strtab_void; + + if (!show_textrels) return NULL; + + scanelf_file_get_symtabs(elf, &symtab_void, &strtab_void); + + if (symtab_void && strtab_void && elf->phdr && elf->shdr) { +#define SHOW_TEXTRELS(B) \ + if (elf->elf_class == ELFCLASS ## B) { \ + Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ + Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ + Elf ## B ## _Shdr *shdr = SHDR ## B (elf->shdr); \ + Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \ + Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \ + Elf ## B ## _Rel *rel; \ + Elf ## B ## _Rela *rela; \ + /* search the section headers for relocations */ \ + for (s = 0; s < EGET(ehdr->e_shnum); ++s) { \ + uint32_t sh_type = EGET(shdr[s].sh_type); \ + if (sh_type == SHT_REL) { \ + rel = REL ## B (elf->data + EGET(shdr[s].sh_offset)); \ + rela = NULL; \ + rmax = EGET(shdr[s].sh_size) / sizeof(*rel); \ + } else if (sh_type == SHT_RELA) { \ + rel = NULL; \ + rela = RELA ## B (elf->data + EGET(shdr[s].sh_offset)); \ + rmax = EGET(shdr[s].sh_size) / sizeof(*rela); \ + } else \ + continue; \ + /* search the program headers for PT_LOAD headers */ \ + for (p = 0; p < EGET(ehdr->e_phnum); ++p) { \ + Elf ## B ## _Addr vaddr; \ + uint ## B ## _t memsz; \ + if (EGET(phdr[p].p_type) != PT_LOAD) continue; \ + if (EGET(phdr[p].p_flags) & PF_W) continue; \ + vaddr = EGET(phdr[p].p_vaddr); \ + memsz = EGET(phdr[p].p_memsz); \ + *found_textrels = 1; \ + /* now see if any of the relocs are in the PT_LOAD */ \ + for (r = 0; r < rmax; ++r) { \ + unsigned long sym_max; \ + Elf ## B ## _Addr offset_tmp; \ + Elf ## B ## _Sym *func; \ + Elf ## B ## _Sym *sym; \ + Elf ## B ## _Addr r_offset; \ + uint ## B ## _t r_info; \ + if (sh_type == SHT_REL) { \ + r_offset = EGET(rel[r].r_offset); \ + r_info = EGET(rel[r].r_info); \ + } else { \ + r_offset = EGET(rela[r].r_offset); \ + r_info = EGET(rela[r].r_info); \ + } \ + /* make sure this relocation is inside of the .text */ \ + if (r_offset < vaddr || r_offset >= vaddr + memsz) continue; \ + /* locate this relocation symbol name */ \ + sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \ + sym_max = ELF ## B ## _R_SYM(r_info); \ + if (sym_max * EGET(symtab->sh_entsize) < symtab->sh_size) \ + sym += sym_max; \ + else \ + sym = NULL; \ + sym_max = EGET(symtab->sh_size) / EGET(symtab->sh_entsize); \ + /* show the raw details about this reloc */ \ + printf("\tTEXTREL %s: ", elf->base_filename); \ + if (sym && sym->st_name) \ + printf("%s", (char*)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name))); \ + else \ + printf("(NULL: fake?)"); \ + printf(" [0x%lX]", (unsigned long)r_offset); \ + /* now try to find the closest symbol that this rel is probably in */ \ + sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \ + func = NULL; \ + offset_tmp = 0; \ + while (sym_max--) { \ + if (EGET(sym->st_value) < r_offset && EGET(sym->st_value) > offset_tmp) { \ + func = sym; \ + offset_tmp = EGET(sym->st_value); \ + } \ + ++sym; \ + } \ + printf(" in "); \ + if (func && func->st_name) \ + printf("%s", (char*)(elf->data + EGET(strtab->sh_offset) + EGET(func->st_name))); \ + else \ + printf("(NULL: fake?)"); \ + printf(" [0x%lX]\n", (unsigned long)offset_tmp); \ + } \ + } \ + } } + SHOW_TEXTRELS(32) + SHOW_TEXTRELS(64) + } + + return NULL; } static void scanelf_file_rpath(elfobj *elf, char *found_rpath, char **ret, size_t *ret_len) { - /* TODO: when checking RPATH entries, check each subpath (between :) in ld.so.conf */ unsigned long i, s; char *rpath, *runpath, **r; void *strtbl_void; @@ -208,16 +385,32 @@ } \ /* Verify the memory is somewhat sane */ \ offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \ - if (offset < elf->len) { \ + if (offset < (Elf ## B ## _Off)elf->len) { \ if (*r) warn("ELF has multiple %s's !?", get_elfdtype(word)); \ *r = (char*)(elf->data + offset); \ /* If quiet, don't output paths in ld.so.conf */ \ - if (be_quiet) \ - for (s = 0; ldpaths[s]; ++s) \ - if (!strcmp(ldpaths[s], *r)) { \ - *r = NULL; \ - break; \ + if (be_quiet) { \ + size_t len; \ + char *start, *end; \ + /* note that we only 'chop' off leading known paths. */ \ + /* since *r is read-only memory, we can only move the ptr forward. */ \ + start = *r; \ + /* scan each path in : delimited list */ \ + while (start) { \ + end = strchr(start, ':'); \ + len = (end ? abs(end - start) : strlen(start)); \ + for (s = 0; ldpaths[s]; ++s) { \ + if (!strncmp(ldpaths[s], start, len) && !ldpaths[s][len]) { \ + *r = (end ? end + 1 : NULL); \ + break; \ + } \ } \ + if (!*r || !ldpaths[s] || !end) \ + start = NULL; \ + else \ + start = start + len + 1; \ + } \ + } \ if (*r) *found_rpath = 1; \ } \ ++dyn; \ @@ -227,6 +420,8 @@ SHOW_RPATH(64) } + if (be_wewy_wewy_quiet) return; + if (rpath && runpath) { if (!strcmp(rpath, runpath)) { xstrcat(ret, runpath, ret_len); @@ -243,13 +438,13 @@ else if (!be_quiet) xstrcat(ret, " - ", ret_len); } -static void scanelf_file_needed(elfobj *elf, char *found_needed, char **ret, size_t *ret_len) +static char *scanelf_file_needed_lib(elfobj *elf, char *found_needed, char *found_lib, int op, char **ret, size_t *ret_len) { unsigned long i; char *needed; void *strtbl_void; - if (!show_needed) return; + if ((op==0 && !show_needed) || (op==1 && !find_lib)) return NULL; strtbl_void = elf_findsecbyname(elf, ".dynstr"); @@ -269,14 +464,22 @@ while (EGET(dyn->d_tag) != DT_NULL) { \ if (EGET(dyn->d_tag) == DT_NEEDED) { \ offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \ - if (offset >= elf->len) { \ + if (offset >= (Elf ## B ## _Off)elf->len) { \ ++dyn; \ continue; \ } \ needed = (char*)(elf->data + offset); \ - if (*found_needed) xchrcat(ret, ',', ret_len); \ - xstrcat(ret, needed, ret_len); \ - *found_needed = 1; \ + if (op == 0) { \ + if (!be_wewy_wewy_quiet) { \ + if (*found_needed) xchrcat(ret, ',', ret_len); \ + xstrcat(ret, needed, ret_len); \ + } \ + *found_needed = 1; \ + } else { \ + if (strcmp(find_lib, needed)) return NULL; \ + *found_lib = 1; \ + return (be_wewy_wewy_quiet ? NULL : find_lib); \ + } \ } \ ++dyn; \ } \ @@ -284,6 +487,8 @@ SHOW_NEEDED(32) SHOW_NEEDED(64) } + + return NULL; } static char *scanelf_file_interp(elfobj *elf, char *found_interp) { @@ -298,7 +503,7 @@ if (elf->elf_class == ELFCLASS ## B) { \ Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \ *found_interp = 1; \ - return elf->data + EGET(strtbl->sh_offset); \ + return (be_wewy_wewy_quiet ? NULL : elf->data + EGET(strtbl->sh_offset)); \ } SHOW_INTERP(32) SHOW_INTERP(64) @@ -330,7 +535,7 @@ { \ if (be_quiet) return NULL; \ *found_bind = 1; \ - return "NOW"; \ + return (char *)(be_wewy_wewy_quiet ? NULL : "NOW"); \ } \ ++dyn; \ } \ @@ -339,27 +544,25 @@ SHOW_BIND(32) SHOW_BIND(64) + if (be_wewy_wewy_quiet) return NULL; + if (be_quiet && !fstat(elf->fd, &s) && !(s.st_mode & S_ISUID || s.st_mode & S_ISGID)) { return NULL; } else { *found_bind = 1; - return "LAZY"; + return (char *) "LAZY"; } } -static char *scanelf_file_sym(elfobj *elf, char *found_sym, const char *filename) +static char *scanelf_file_sym(elfobj *elf, char *found_sym) { unsigned long i; void *symtab_void, *strtab_void; if (!find_sym) return NULL; - symtab_void = elf_findsecbyname(elf, ".symtab"); - strtab_void = elf_findsecbyname(elf, ".strtab"); + scanelf_file_get_symtabs(elf, &symtab_void, &strtab_void); if (symtab_void && strtab_void) { - char *base, *basemem; - basemem = xstrdup(filename); - base = basename(basemem); #define FIND_SYM(B) \ if (elf->elf_class == ELFCLASS ## B) { \ Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \ @@ -373,7 +576,7 @@ if (*find_sym == '*') { \ printf("%s(%s) %5lX %15s %s\n", \ ((*found_sym == 0) ? "\n\t" : "\t"), \ - base, \ + elf->base_filename, \ (long)sym->st_size, \ (char *)get_elfstttype(sym->st_info), \ symname); \ @@ -386,24 +589,25 @@ } } FIND_SYM(32) FIND_SYM(64) - free(basemem); } + + if (be_wewy_wewy_quiet) return NULL; + if (*find_sym != '*' && *found_sym) return find_sym; if (be_quiet) return NULL; else - return " - "; + return (char *)" - "; } /* scan an elf file and show all the fun stuff */ -// #define prints(str) fputs(str, stdout) #define prints(str) write(fileno(stdout), str, strlen(str)) static void scanelf_file(const char *filename) { unsigned long i; - char found_pax, found_stack, found_relro, found_textrel, + char found_pax, found_phdr, found_relro, found_load, found_textrel, found_rpath, found_needed, found_interp, found_bind, - found_sym, found_file; + found_sym, found_lib, found_file, found_textrels; elfobj *elf; struct stat st; static char *out_buffer = NULL; @@ -424,9 +628,9 @@ return; } - found_pax = found_stack = found_relro = found_textrel = \ + found_pax = found_phdr = found_relro = found_load = found_textrel = \ found_rpath = found_needed = found_interp = found_bind = \ - found_sym = found_file = 0; + found_sym = found_lib = found_file = found_textrels = 0; /* verify this is real ELF */ if ((elf = readelf(filename)) == NULL) { @@ -436,8 +640,8 @@ if (be_verbose > 1) printf("%s: scanning file {%s,%s}\n", filename, - get_elfeitype(elf, EI_CLASS, elf->elf_class), - get_elfeitype(elf, EI_DATA, elf->data[EI_DATA])); + get_elfeitype(EI_CLASS, elf->elf_class), + get_elfeitype(EI_DATA, elf->data[EI_DATA])); else if (be_verbose) printf("%s: scanning file\n", filename); @@ -451,20 +655,26 @@ /* show the header */ if (!be_quiet && show_banner) { for (i = 0; out_format[i]; ++i) { - if (out_format[i] != '%') continue; + if (!IS_MODIFIER(out_format[i])) continue; switch (out_format[++i]) { case '%': break; - case 'F': prints("FILE "); found_file = 1; break; + case '#': break; + case 'F': + case 'p': + case 'f': prints("FILE "); found_file = 1; break; case 'o': prints(" TYPE "); break; case 'x': prints(" PAX "); break; - case 'e': prints("STK/REL "); break; + case 'e': prints("STK/REL/PTL "); break; case 't': prints("TEXTREL "); break; case 'r': prints("RPATH "); break; case 'n': prints("NEEDED "); break; case 'i': prints("INTERP "); break; case 'b': prints("BIND "); break; case 's': prints("SYM "); break; + case 'N': prints("LIB "); break; + case 'T': prints("TEXTRELS "); break; + default: warnf("'%c' has no title ?", out_format[i]); } } if (!found_file) prints("FILE "); @@ -476,36 +686,69 @@ /* dump all the good stuff */ for (i = 0; out_format[i]; ++i) { const char *out; + const char *tmp; /* make sure we trim leading spaces in quiet mode */ if (be_quiet && *out_buffer == ' ' && !out_buffer[1]) *out_buffer = '\0'; - if (out_format[i] != '%') { + if (!IS_MODIFIER(out_format[i])) { xchrcat(&out_buffer, out_format[i], &out_len); continue; } out = NULL; + be_wewy_wewy_quiet = (out_format[i] == '#'); switch (out_format[++i]) { - case '%': xchrcat(&out_buffer, '%', &out_len); break; - case 'F': found_file = 1; xstrcat(&out_buffer, filename, &out_len); break; + case '%': + case '#': + xchrcat(&out_buffer, out_format[i], &out_len); break; + case 'F': + found_file = 1; + if (be_wewy_wewy_quiet) break; + xstrcat(&out_buffer, filename, &out_len); + break; + case 'p': + found_file = 1; + if (be_wewy_wewy_quiet) break; + tmp = filename; + if (search_path) { + ssize_t len_search = strlen(search_path); + ssize_t len_file = strlen(filename); + if (!strncmp(filename, search_path, len_search) && \ + len_file > len_search) + tmp += len_search; + if (*tmp == '/' && search_path[len_search-1] == '/') tmp++; + } + xstrcat(&out_buffer, tmp, &out_len); + break; + case 'f': + found_file = 1; + if (be_wewy_wewy_quiet) break; + tmp = strrchr(filename, '/'); + tmp = (tmp == NULL ? filename : tmp+1); + xstrcat(&out_buffer, tmp, &out_len); + break; case 'o': out = get_elfetype(elf); break; case 'x': out = scanelf_file_pax(elf, &found_pax); break; - case 'e': out = scanelf_file_stack(elf, &found_stack, &found_relro); break; + case 'e': out = scanelf_file_phdr(elf, &found_phdr, &found_relro, &found_load); break; case 't': out = scanelf_file_textrel(elf, &found_textrel); break; + case 'T': out = scanelf_file_textrels(elf, &found_textrels); break; case 'r': scanelf_file_rpath(elf, &found_rpath, &out_buffer, &out_len); break; - case 'n': scanelf_file_needed(elf, &found_needed, &out_buffer, &out_len); break; + case 'n': + case 'N': out = scanelf_file_needed_lib(elf, &found_needed, &found_lib, (out_format[i]=='N'), &out_buffer, &out_len); break; case 'i': out = scanelf_file_interp(elf, &found_interp); break; case 'b': out = scanelf_file_bind(elf, &found_bind); break; - case 's': out = scanelf_file_sym(elf, &found_sym, filename); break; + case 's': out = scanelf_file_sym(elf, &found_sym); break; + default: warnf("'%c' has no scan code?", out_format[i]); } if (out) xstrcat(&out_buffer, out, &out_len); } #define FOUND_SOMETHING() \ - (found_pax || found_stack || found_textrel || found_rpath || \ - found_needed || found_interp || found_bind || found_sym) + (found_pax || found_phdr || found_relro || found_load || found_textrel || \ + found_rpath || found_needed || found_interp || found_bind || \ + found_sym || found_lib || found_textrels) if (!found_file && (!be_quiet || (be_quiet && FOUND_SOMETHING()))) { xchrcat(&out_buffer, ' ', &out_len); @@ -551,7 +794,8 @@ continue; len = (pathlen + 1 + strlen(dentry->d_name) + 1); if (len >= sizeof(buf)) { - warnf("Skipping '%s': len > sizeof(buf); %d > %d\n", path, (int)len, (int)sizeof(buf)); + warnf("Skipping '%s': len > sizeof(buf); %lu > %lu\n", path, + (unsigned long)len, (unsigned long)sizeof(buf)); continue; } sprintf(buf, "%s/%s", path, dentry->d_name); @@ -581,6 +825,7 @@ while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL) { if ((p = strchr(path, '\n')) != NULL) *p = 0; + search_path = path; scanelf_dir(path); } if (fp != stdin) @@ -662,7 +907,7 @@ /* usage / invocation handling functions */ -#define PARSE_FLAGS "plRmyxetrnibs:aqvF:f:o:BhV" +#define PARSE_FLAGS "plRmyxetrnibs:N:TaqvF:f:o:BhV" #define a_argument required_argument static struct option const long_opts[] = { {"path", no_argument, NULL, 'p'}, @@ -677,33 +922,37 @@ {"needed", no_argument, NULL, 'n'}, {"interp", no_argument, NULL, 'i'}, {"bind", no_argument, NULL, 'b'}, - {"symbol", a_argument, NULL, 's'}, + {"symbol", a_argument, NULL, 's'}, + {"lib", a_argument, NULL, 'N'}, + {"textrels", no_argument, NULL, 'T'}, {"all", no_argument, NULL, 'a'}, {"quiet", no_argument, NULL, 'q'}, {"verbose", no_argument, NULL, 'v'}, - {"format", a_argument, NULL, 'F'}, - {"from", a_argument, NULL, 'f'}, - {"file", a_argument, NULL, 'o'}, + {"format", a_argument, NULL, 'F'}, + {"from", a_argument, NULL, 'f'}, + {"file", a_argument, NULL, 'o'}, {"nobanner", no_argument, NULL, 'B'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, {NULL, no_argument, NULL, 0x0} }; -static char *opts_help[] = { +static const char *opts_help[] = { "Scan all directories in PATH environment", "Scan all directories in /etc/ld.so.conf", "Scan directories recursively", "Don't recursively cross mount points", "Don't scan symlinks\n", "Print PaX markings", - "Print GNU_STACK markings", + "Print GNU_STACK/PT_LOAD markings", "Print TEXTREL information", "Print RPATH information", "Print NEEDED information", "Print INTERP information", "Print BIND information", "Find a specified symbol", + "Find a specified library", + "Locate cause of TEXTREL", "Print all scanned info (-x -e -t -r -n -i -b)\n", "Only output 'bad' things", "Be verbose (can be specified more than once)", @@ -735,9 +984,13 @@ exit(status); puts("\nThe format modifiers for the -F option are:"); - puts(" %F Filename \t%x PaX Flags \t%e STACK/RELRO"); - puts(" %t TEXTREL \t%r RPATH \t%n NEEDED"); - puts(" %i INTERP \t%b BIND \t%s symbol"); + puts(" F Filename \tx PaX Flags \te STACK/RELRO"); + puts(" t TEXTREL \tr RPATH \tn NEEDED"); + puts(" i INTERP \tb BIND \ts symbol"); + puts(" N library \to Type \tT TEXTRELs"); + puts(" p filename (with search path removed)"); + puts(" f base filename"); + puts("Prefix each modifier with '%' (verbose) or '#' (silent)"); exit(status); } @@ -760,28 +1013,34 @@ break; case 'h': usage(EXIT_SUCCESS); break; case 'f': - if (from_file == NULL) - from_file = xstrdup(optarg); + if (from_file) err("Don't specify -f twice"); + from_file = xstrdup(optarg); break; case 'o': { FILE *fp = NULL; - fp = freopen(optarg, "w", stdout); - if (fp == NULL) + if ((fp = freopen(optarg, "w", stdout)) == NULL) err("Could not open output stream '%s': %s", optarg, strerror(errno)); - stdout = fp; + SET_STDOUT(fp); break; } case 's': { size_t len; + if (find_sym) err("Don't specify -s twice"); find_sym = xstrdup(optarg); len = strlen(find_sym) + 1; versioned_symname = (char*)xmalloc(sizeof(char) * (len+1)); sprintf(versioned_symname, "%s@", find_sym); break; } + case 'N': { + if (find_lib) err("Don't specify -N twice"); + find_lib = xstrdup(optarg); + break; + } case 'F': { + if (out_format) err("Don't specify -F twice"); out_format = xstrdup(optarg); break; } @@ -793,15 +1052,16 @@ case 'R': dir_recurse = 1; break; case 'm': dir_crossmount = 0; break; case 'x': show_pax = 1; break; - case 'e': show_stack = 1; break; + case 'e': show_phdr = 1; break; case 't': show_textrel = 1; break; case 'r': show_rpath = 1; break; case 'n': show_needed = 1; break; case 'i': show_interp = 1; break; case 'b': show_bind = 1; break; + case 'T': show_textrels = 1; break; case 'q': be_quiet = 1; break; case 'v': be_verbose = (be_verbose % 20) + 1; break; - case 'a': show_pax = show_stack = show_textrel = show_rpath = \ + case 'a': show_pax = show_phdr = show_textrel = show_rpath = \ show_needed = show_interp = show_bind = 1; break; case ':': @@ -815,23 +1075,28 @@ /* let the format option override all other options */ if (out_format) { - show_pax = show_stack = show_textrel = show_rpath = \ - show_needed = show_interp = show_bind = 0; + show_pax = show_phdr = show_textrel = show_rpath = \ + show_needed = show_interp = show_bind = show_textrels = 0; for (i = 0; out_format[i]; ++i) { - if (out_format[i] != '%') continue; + if (!IS_MODIFIER(out_format[i])) continue; switch (out_format[++i]) { case '%': break; + case '#': break; case 'F': break; + case 'p': break; + case 'f': break; case 's': break; + case 'N': break; case 'o': break; case 'x': show_pax = 1; break; - case 'e': show_stack = 1; break; + case 'e': show_phdr = 1; break; case 't': show_textrel = 1; break; case 'r': show_rpath = 1; break; case 'n': show_needed = 1; break; case 'i': show_interp = 1; break; case 'b': show_bind = 1; break; + case 'T': show_textrels = 1; break; default: err("Invalid format specifier '%c' (byte %i)", out_format[i], i+1); @@ -842,16 +1107,18 @@ } else { size_t fmt_len = 30; out_format = (char*)xmalloc(sizeof(char) * fmt_len); - if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len); - if (show_pax) xstrcat(&out_format, "%x ", &fmt_len); - if (show_stack) xstrcat(&out_format, "%e ", &fmt_len); - if (show_textrel) xstrcat(&out_format, "%t ", &fmt_len); - if (show_rpath) xstrcat(&out_format, "%r ", &fmt_len); - if (show_needed) xstrcat(&out_format, "%n ", &fmt_len); - if (show_interp) xstrcat(&out_format, "%i ", &fmt_len); - if (show_bind) xstrcat(&out_format, "%b ", &fmt_len); - if (find_sym) xstrcat(&out_format, "%s ", &fmt_len); - if (!be_quiet) xstrcat(&out_format, "%F ", &fmt_len); + if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len); + if (show_pax) xstrcat(&out_format, "%x ", &fmt_len); + if (show_phdr) xstrcat(&out_format, "%e ", &fmt_len); + if (show_textrel) xstrcat(&out_format, "%t ", &fmt_len); + if (show_rpath) xstrcat(&out_format, "%r ", &fmt_len); + if (show_needed) xstrcat(&out_format, "%n ", &fmt_len); + if (show_interp) xstrcat(&out_format, "%i ", &fmt_len); + if (show_bind) xstrcat(&out_format, "%b ", &fmt_len); + if (show_textrels) xstrcat(&out_format, "%T ", &fmt_len); + if (find_sym) xstrcat(&out_format, "%s ", &fmt_len); + if (find_lib) xstrcat(&out_format, "%N ", &fmt_len); + if (!be_quiet) xstrcat(&out_format, "%F ", &fmt_len); } if (be_verbose > 2) printf("Format: %s\n", out_format); @@ -867,14 +1134,17 @@ } if (optind == argc && !scan_ldpath && !scan_envpath && !from_file) err("Nothing to scan !?"); - while (optind < argc) - scanelf_dir(argv[optind++]); + while (optind < argc) { + search_path = argv[optind++]; + scanelf_dir(search_path); + } /* clean up */ if (find_sym) { free(find_sym); free(versioned_symname); } + if (find_lib) free(find_lib); if (out_format) free(out_format); for (i = 0; ldpaths[i]; ++i) free(ldpaths[i]); @@ -899,7 +1169,7 @@ static void xstrcat(char **dst, const char *src, size_t *curr_len) { - long new_len; + size_t new_len; new_len = strlen(*dst) + strlen(src); if (*curr_len <= new_len) { @@ -921,11 +1191,15 @@ } + int main(int argc, char *argv[]) { if (argc < 2) usage(EXIT_FAILURE); parseargs(argc, argv); fclose(stdout); +#ifdef __BOUNDS_CHECKING_ON + warn("The calls to add/delete heap should be off by 1 due to the out_buffer not being freed in scanelf_file()"); +#endif return EXIT_SUCCESS; }