From 50135fcf554b6363ce325b297fa953e8fbe16f49 Mon Sep 17 00:00:00 2001 From: jkratoch <> Date: Fri, 29 Dec 2006 19:14:15 +0000 Subject: [PATCH] Initial import. --- .cvsignore | 9 + .vimrc | 1 + Makefile | 60 +++++ README | 91 ++++++++ TODO | 25 ++ build.c | 226 +++++++++++++++++++ build.h | 4 + common.h | 6 + demo | 76 +++++++ gdb-6.3.patch | 690 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ hook-arch-asm.h | 24 ++ hook-arch-c.h | 14 ++ hook-i386.S | 30 +++ hook-x86_64.S | 29 +++ hook.c | 228 +++++++++++++++++++ hook.h | 18 ++ libobjid.spec | 51 +++++ maps.c | 129 +++++++++++ maps.h | 9 + objid.h | 32 +++ signal-core.h | 16 ++ tst-core.c | 8 + tst-raise.c | 63 ++++++ tst-resethand.c | 84 +++++++ 24 files changed, 1923 insertions(+) create mode 100644 .cvsignore create mode 100644 .vimrc create mode 100644 Makefile create mode 100644 README create mode 100644 TODO create mode 100644 build.c create mode 100644 build.h create mode 100644 common.h create mode 100755 demo create mode 100644 gdb-6.3.patch create mode 100644 hook-arch-asm.h create mode 100644 hook-arch-c.h create mode 100644 hook-i386.S create mode 100644 hook-x86_64.S create mode 100644 hook.c create mode 100644 hook.h create mode 100644 libobjid.spec create mode 100644 maps.c create mode 100644 maps.h create mode 100644 objid.h create mode 100644 signal-core.h create mode 100644 tst-core.c create mode 100644 tst-raise.c create mode 100644 tst-resethand.c diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..c660f69 --- /dev/null +++ b/.cvsignore @@ -0,0 +1,9 @@ +build.o +hook-i386.o +hook.o +libobjid.so +maps.o +tst-core +tst-core-pie +tst-raise +tst-resethand diff --git a/.vimrc b/.vimrc new file mode 100644 index 0000000..ac9076b --- /dev/null +++ b/.vimrc @@ -0,0 +1 @@ +set sw=2 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..67616b2 --- /dev/null +++ b/Makefile @@ -0,0 +1,60 @@ +# $Id$ + +CFLAGS = -ggdb2 -Wall -ansi -D_GNU_SOURCE +TESTS = tst-raise tst-resethand tst-core tst-core-pie +ARCH = $(shell uname -i) +VERSION = 0.1 + +all: libobjid.so $(TESTS) + +SO_OBJS = hook.o hook-$(ARCH).o build.o maps.o +libobjid.so: LDFLAGS += -shared -ldl +libobjid.so $(SO_OBJS): CFLAGS += -fPIC +libobjid.so: $(SO_OBJS) + $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) $^ $(OUTPUT_OPTION) + +tst-core-pie: LDFLAGS += -Wl,-z,relro -Wl,-z,now -fPIE -pie +tst-core-pie: tst-core.c + $(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@ + +build.o: build.c build.h objid.h common.h maps.h hook.h hook-arch-c.h +hook.o: hook.h hook-arch-c.h build.h maps.h signal-core.h common.h +maps.o: maps.c maps.h common.h hook.h +hook-$(ARCH).o: hook-$(ARCH).S hook-arch-asm.h + +libobjid.o: libobjid.c signal_core.h + +.PHONY: check +check: $(TESTS) libobjid.so + export LD_PRELOAD=; \ + set -x; \ + for test in $(TESTS);do \ + ./$$test; \ + done; \ + true + export LD_PRELOAD=./libobjid.so; \ + set -x; \ + for test in $(TESTS);do \ + ./$$test; \ + done; \ + true + +.PHONY: dist +dist: *.c *.h *.S libobjid.spec Makefile gdb-6.3.patch TODO README demo + rm -rf libobjid-$(VERSION) + mkdir libobjid-$(VERSION) + cp -p $^ libobjid-$(VERSION) + tar czf libobjid-$(VERSION).tar.gz libobjid-$(VERSION) + rm -rf libobjid-$(VERSION) + +.PHONY: install +install: libobjid.so objid.h + mkdir -p $(DESTDIR)$(libdir) + install libobjid.so $(DESTDIR)$(libdir)/libobjid.so + mkdir -p $(DESTDIR)$(includedir) + install objid.h $(DESTDIR)$(includedir)/objid.h + +.PHONY: clean +clean: + $(RM) -r libobjid-$(VERSION) demo.d + $(RM) libobjid.so $(TESTS) *.o libobjid-$(VERSION).tar.gz diff --git a/README b/README new file mode 100644 index 0000000..1eec1be --- /dev/null +++ b/README @@ -0,0 +1,91 @@ +Usage +----- + +Provide `/usr/lib/libobjid.so' to the list of libraries in `LD_PRELOAD': + LD_PRELOAD=/usr/lib/libobjid.so programname programarguments + +It will extend the stored core file info in the case the program segfaults. +In this case you can check it got activated by its STDERR message: + libobjid: core + +You need patched GDB and its --objectdirectory option to use the location info. +Copy all the shared libraries used by the program to a common storage directory +and invoke gdb as: + gdb --objectdirectory=storage/directory --core=path/to/core.xyz + +You should keep the original filenames and store them only to different +directories, according to their version. Be sure to also store the debuginfo +files along: + /path/to/libc.so.6 + /path/to/libc.so.6.debug + +More debug info during segfault can be seen by setting the `LIBOBJID' level: + + LIBOBJID=9 LD_PRELOAD=/usr/lib/libobjid.so programname programarguments + + +Limitations +----------- + +The code currently does not undo any relocations. Any (ineffective) non-PIC +shared libraries will not be located by libobjid. + +Non-PIE executable files are loaded at their compiled location - OK. +PIC shared libraries do not use any relocations in their readonly sections - OK. +x86_64 fails to load non-PIC shared libraries during tests - N/A. +i386 permits running non-PIC shared libraries - FAIL but these are ineffective. + + +Implementation Principle +------------------------ + +During SIGSEGV handling dl_iterate_phdr() is used to iterate program headers +and the readonly sections get checksummed and stored to a newly mapped +PAGE_SIZE-based memory being identified by its magic header. + +Stored information structure can be found in "objid.h". + +Unfortunately it cannot be linked to existing ELF structures as locating them +requires the pages of the original executable binary which we try identify and +find by them. +The original executable's shared libraries list can be found from its +program header DYNAMIC / section .dynamic - DT_NEEDED tag. +While this section is located in writable memory stored in the core file the +program headers / section headers table is in the executable read only pages +not present in the core file. + + +Implementation Invocation +------------------------- + +Currently the code binary-patches (after mprotect (PROT_WRITE)) glibc itself as +it does not provide PLT to replace its `__sigaction' function being called by +`__signal' and many others. +It is currently the only architecture-dependent part of libobjid now. + +Later with available `__sigaction' glibc PLT entry one would still need to +`LD_PRELOAD=/usr/lib/libobjid.so'. With integrating this library to Red Hat +glibc it would apply to all the binaries except the ones statically built on +non-libobjid glibc systems. But statically built binaries do not need any +libobjid functionality so it makes no problem. + + +Proper Reimplementation +----------------------- + +Linux kernel should core dump the page(s) containing original executable's +program headers, the page(s) containing its `DYNAMIC' segment and according to +its `DT_GNU_LIBLIST' and `DT_GNU_LIBLISTSZ' also the page(s) containing its +`.gnu.liblist' section. + +prelink(8) should provide DYNAMIC's `DT_CHECKSUM' field even for executables. + +GDB would be afterwards able locate `AUXVEC->AT_PHDR->DYNAMIC->DT_CHECKSUM' for +the main executable checksum and `AUXVEC->AT_PHDR->DYNAMIC->DT_GNU_LIBLIST' for +the list of the needed libraries identified by its `l_checksum' field. + +Only the executables/libraries properly prelink(8)ed together would be +identifiable by GDB. + + +$Id$ diff --git a/TODO b/TODO new file mode 100644 index 0000000..c3433f4 --- /dev/null +++ b/TODO @@ -0,0 +1,25 @@ +general: +* Unify checksumming source code between libobjid and gdb. +* Better checksumming, checksum larger than 32-bit? Uses CRC - section 17.2 of: + http://www.redhat.com/docs/manuals/enterprise/RHEL-4-Manual/pdf/rhel-gdb-en.pdf + +libobjid: +* Hooking to sigaction(3) should be removed, standard ELF PLT - patched glibc. +* Provide also `rpm -q' n-v-r for both the executable and its .so libraries. +* All the arrays should be dynamic, mmap(2)/mremap(2) based. +* Move it to kernel? Static binaries do not need it - it does not have a reason. +* Move it to glibc to no longer need any `LD_PRELOAD'. +* Better libobjid-debugging flags. + +gdb: +* `PAGE_SIZE' should be retrieved from the inferior. +* Object files indexing should be both in-memory and on-disk persistent. +* Address based libraries remapping instead of the names based one? +* Support loading i?86 binaries on x86_64 host, support non-matching endianess. +* Provide `set debug' flag with the debugging info. +* Make `corelow.c' a pluggable module again. +* Autodetect `' availability. +* Update `Makefile.in' dependencies. + + +$Id$ diff --git a/build.c b/build.c new file mode 100644 index 0000000..21831bc --- /dev/null +++ b/build.c @@ -0,0 +1,226 @@ +/* $Id$ */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "build.h" +#include "objid.h" +#include "common.h" +#include "maps.h" +#include "hook.h" +#include "hook-arch-c.h" + + +/* Make the checksumming faster but core file specific by mincore(2)? + For some important swapped out libraries the checksum would get void. */ + + +static ElfW(LibobjidHdr) *header; +static size_t pages_size; +static unsigned obj_allocated; +static ElfW(LibobjidObj) *obj; + + +static void header_update (void) +{ + obj_allocated = (pages_size - offsetof (ElfW(LibobjidHdr), obj)) / sizeof (header->obj[0]); + assert (obj_allocated >= header->obj_count); +} + +static int header_init (void) +{ + pages_size = PAGE_SIZE; + header = mmap (NULL, pages_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (header == MAP_FAILED) + { + if (debug >= 1) + fprintf (stderr, "libobjid: Failed to map %lu bytes: %m\n", + (unsigned long) pages_size); + return -1; + } + header->obj_count = 0; + header_update (); + + return 0; +} + +static int header_resize (size_t pages_size_new) +{ + ElfW(LibobjidHdr) *header_new; + + pages_size_new = (pages_size_new + PAGE_SIZE - 1) & PAGE_MASK; + + header_new = mremap (header, pages_size, pages_size_new, MREMAP_MAYMOVE); + if (header_new == MAP_FAILED) + { + if (debug >= 1) + fprintf (stderr, "libobjid: Failed to remap %lu to %lu bytes: %m\n", + (unsigned long) pages_size, (unsigned long) pages_size_new); + return -1; + } + + header = header_new; + pages_size = pages_size_new; + header_update (); + + return 0; +} + +static void header_finish (void) +{ + size_t strings_size = 0; + unsigned obji; + char *string_ptr; + + for (obji = 0; obji < header->obj_count; obji++) + strings_size += strlen ((const char *) header->obj[obji].filename) + 1; + + header->size = sizeof (*header) + header->obj_count * sizeof (*header->obj) + + strings_size; + /* Possibly even shrink the allocated pages. */ + header_resize (header->size); + + /* Do not reuse any address above, header_resize() could move it. */ + string_ptr = (char *) header + sizeof (*header) + + header->obj_count * sizeof (*header->obj); + + for (obji = 0; obji < header->obj_count; obji++) + { + const char *filename_orig = (const char *) header->obj[obji].filename; + + strcpy (string_ptr, filename_orig); + header->obj[obji].filename = (ElfW (Addr)) string_ptr; + string_ptr += strlen (filename_orig) + 1; + } + assert (string_ptr == (char *) header + header->size); + + header->magic = ElfW(Magic); + header->self = (ElfW(Addr)) header; + header->self_not = ~(ElfW(Addr)) header; +} + +/* Returns non-zero on error. */ +static int checksum_open (const char *filename) +{ + assert (header != NULL); + + if (header->obj_count == obj_allocated) + { + assert (pages_size >= 1); + if (header_resize (pages_size * 2)) + return -1; + assert (header->obj_count < obj_allocated); + } + + obj = &header->obj[header->obj_count]; + + /* It should not happen but avoid the array termination. */ + if (filename == NULL) + filename = ""; + + obj->filename = (ElfW(Addr)) filename; + obj->checksum = 0; + if (debug >= 1) + fprintf (stderr, "%s\n", filename); + + return 0; +} + +static void checksum_data (ElfW(Word) *sum, void *start, void *end) +{ + unsigned char *ptr; + + for (ptr = start; (void *) ptr < end; ptr++) + *sum = *sum * 31 + *ptr; + + if (debug >= 1) + fprintf (stderr, "\t%p - %p (0x%lx)\n", start, end, (unsigned long) *sum); +} + +static void checksum_close (void) +{ + header->obj_count++; + + assert (header->obj_count <= obj_allocated); +} + +/* Range is provided only for debug purposes. */ +static void checksum_cancel (void *start, void *end) +{ + if (debug >= 1) + fprintf (stderr, "\tCANCEL: %p - %p\n", start, end); +} + +static int phdr_callback (struct dl_phdr_info *info, size_t size, void *data) +{ + int j; + int started = 0; + + for (j = 0; j < info->dlpi_phnum; j++) + { + void *start, *end; + + if (info->dlpi_phdr[j].p_type != PT_LOAD) + continue; + if (!(info->dlpi_phdr[j].p_flags & PF_R)) + continue; + /* Present? We probably could not checksum such one. */ + if (info->dlpi_phdr[j].p_flags & PF_W) + continue; + if (!started) + { + /* No longer storage space? No other object may succeed. */ + if (checksum_open (info->dlpi_name) != 0) + return 1; + started = 1; + } + start = (void *) (info->dlpi_addr + info->dlpi_phdr[j].p_vaddr); + end = start + info->dlpi_phdr[j].p_memsz; + /* Use maps_verify() after checksum_open() for more debugging info. */ + if (maps_verify (start, end) != 0) + { + /* This object's checksum is invalid but other objects may be + checksummable. */ + if (started) + checksum_cancel (start, end); + return 0; + } + if ((void *) sigaction_orig_libc_cont < start + || (void *) ((char *) sigaction_orig_libc_cont - trampoline_size) + >= end) + checksum_data (&obj->checksum, start, end); + else + { + checksum_data (&obj->checksum, start, + (char *) sigaction_orig_libc_cont - trampoline_size); + checksum_data (&obj->checksum, sigaction_trampoline, + (char *) sigaction_trampoline + trampoline_size); + checksum_data (&obj->checksum, sigaction_orig_libc_cont, end); + } + } + + if (started) + checksum_close (); + + return 0; +} + +INTERNAL void core_dump (void) +{ + const char *msg = "libobjid: core\n"; + write (STDERR_FILENO, msg, strlen (msg)); + + if (header_init ()) + return; + maps_read (); + dl_iterate_phdr (phdr_callback, NULL); + header_finish (); +} diff --git a/build.h b/build.h new file mode 100644 index 0000000..50f2270 --- /dev/null +++ b/build.h @@ -0,0 +1,4 @@ +/* $Id$ */ + + +extern void core_dump (void); diff --git a/common.h b/common.h new file mode 100644 index 0000000..940cca1 --- /dev/null +++ b/common.h @@ -0,0 +1,6 @@ +/* $Id$ */ + + +#define N_ELEMENTS(arr) (sizeof (arr) / sizeof ((arr)[0])) + +#define INTERNAL __attribute__ ((__visibility__ ("internal"))) diff --git a/demo b/demo new file mode 100755 index 0000000..117d8ce --- /dev/null +++ b/demo @@ -0,0 +1,76 @@ +#! /bin/sh +# $Id$ + + +GDB=$PWD/gdb.`uname -i` + +DIR=$PWD +TEST=tst-core +STORAGE=$PWD/demo.d +LIB=libobjid.so.`uname -i` + +function check +{ + if ! $* + then + echo >&2 "Failed: $*" + exit 1 + fi +} + +check test -f ./demo +check test ! -e $STORAGE + +if test -f $LIB +then + rm -f ./libobjid.so + cp -p $LIB ./libobjid.so +fi +if ! test -f $GDB +then + GDB=gdb +fi + +check ulimit -c unlimited +[ -f Makefile ] && check make libobjid.so $TEST + +check mkdir $STORAGE + +# Move to an empty directory to better test by losing any absolute references. +EMPTY=$STORAGE/empty +check mkdir $EMPTY +cd $EMPTY + +STORAGE2=$STORAGE/`hostname -s` +check mkdir $STORAGE2 +libs="$(ldd $DIR/$TEST $DIR/libobjid.so | sed -n 's/^\t\(.* => \)\?\([^ ]*\).*$/\2/p' | sort -u)" +for obj in $DIR/$TEST $DIR/libobjid.so $libs +do + if test -L $obj + then + link=$(readlink $obj) + ln -s $link $STORAGE2/`basename $obj` + obj=`dirname $obj`/$link + fi + check cp -p $obj $STORAGE2/`basename $obj` + # It may not be present. + cp -p /usr/lib/debug/$obj.debug $STORAGE2/`basename $obj`.debug +done + +# Provide relative pathname to lose the reference to the original. +check ln -s $DIR/libobjid.so +check ln -s $DIR/$TEST +LD_PRELOAD=./libobjid.so ./$TEST & +pid=$! +wait +rm -f ./libobjid.so +rm -f ./$TEST +CORE=$PWD/core.$pid +ls -l $CORE + +strace -s200 -o x -q $GDB --objectdirectory=$STORAGE2 --core=$CORE + +check rm -f $CORE +check rm -rf $STORAGE + +echo $0 done diff --git a/gdb-6.3.patch b/gdb-6.3.patch new file mode 100644 index 0000000..02a5522 --- /dev/null +++ b/gdb-6.3.patch @@ -0,0 +1,690 @@ +https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=207644 + + +diff -u -rup -x testsuite gdb-6.3-orig/gdb/corelow.c gdb-6.3/gdb/corelow.c +--- gdb-6.3-orig/gdb/corelow.c 2004-10-08 22:29:46.000000000 +0200 ++++ gdb-6.3/gdb/corelow.c 2006-12-29 18:51:19.000000000 +0100 +@@ -30,6 +30,8 @@ + #ifdef HAVE_SYS_FILE_H + #include /* needed for F_OK and friends */ + #endif ++#include ++#include /* FIXME: `PAGE_SIZE'! */ + #include "frame.h" /* required by inferior.h */ + #include "inferior.h" + #include "symtab.h" +@@ -45,6 +47,13 @@ + #include "readline/readline.h" + #include "observer.h" + #include "gdb_assert.h" ++#include "gdbcmd.h" ++#include "completer.h" ++#include "elf-bfd.h" ++#include "libbfd.h" ++#include "solist.h" ++ ++#include + + #ifndef O_BINARY + #define O_BINARY 0 +@@ -272,6 +281,369 @@ add_to_thread_list (bfd *abfd, asection + inferior_ptid = pid_to_ptid (thread_id); /* Yes, make it current */ + } + ++static char *object_path; ++ ++static void ++show_objectdirectories (char *ignore, int from_tty) ++{ ++ puts_filtered ("Object directories searched: "); ++ puts_filtered (object_path); ++ puts_filtered ("\n"); ++} ++ ++static void ++init_object_path (void) ++{ ++ object_path = xstrdup (""); ++} ++ ++/* Add zero or more directories to the front of the object path. */ ++ ++void ++objectdirectory_command (char *dirname, int from_tty) ++{ ++ dont_repeat (); ++ /* FIXME, this goes to "delete dir"... */ ++ if (dirname == 0) ++ { ++ if (from_tty && query ("Reinitialize object path to empty? ")) ++ { ++ xfree (object_path); ++ init_object_path (); ++ } ++ } ++ else ++ { ++ mod_path (dirname, &object_path); ++ } ++ if (from_tty) ++ show_objectdirectories ((char *) 0, from_tty); ++} ++ ++struct objid_cache ++ { ++ struct objid_cache *next; ++ char *filename; ++ ElfW(Word) sum; ++ }; ++static struct objid_cache *objid_cache; ++ ++static const char * ++objid_cache_find (ElfW(Word) sum) ++{ ++ struct objid_cache *item; ++ ++ for (item = objid_cache; item != NULL; item = item->next) ++ if (item->sum == sum) ++ return item->filename; ++ ++ return NULL; ++} ++ ++static void ++objid_cache_add (const char *filename, ElfW(Word) sum) ++{ ++ const char *filename_old; ++ struct objid_cache *objid_cache_new; ++ ++ filename_old = objid_cache_find (sum); ++ if (filename_old != NULL && strcmp (filename, filename_old) != 0) ++ warning ("Duplicite checksum of `%s' vs. `%s'", filename_old, filename); ++ if (filename_old != NULL) ++ return; ++ ++ objid_cache_new = xmalloc (sizeof (*objid_cache_new)); ++ objid_cache_new->next = objid_cache; ++ objid_cache_new->filename = xstrdup (filename); ++ objid_cache_new->sum = sum; ++ objid_cache = objid_cache_new; ++} ++ ++static void ++objid_cache_clear (void) ++{ ++ while (objid_cache) ++ { ++ struct objid_cache *freeing; ++ ++ freeing = objid_cache; ++ objid_cache = freeing->next; ++ xfree (freeing->filename); ++ xfree (freeing); ++ } ++} ++ ++static void ++objid_locate_file_bfd (bfd *abfd) ++{ ++ unsigned phi; ++ FILE *f; ++ Elf_Internal_Ehdr *ehdr; ++ ElfW(Word) sum = 0; ++ ++ if (bfd_check_format (abfd, bfd_object) == FALSE) ++ { ++ if (bfd_get_error () != bfd_error_file_not_recognized) ++ warning ("Error checking binary `%s' (%s)", bfd_get_filename (abfd), ++ bfd_errmsg (bfd_get_error ())); ++ return; ++ } ++ /* Non-executable files? Such as the `.o' files. */ ++ if ((bfd_get_file_flags (abfd) & (EXEC_P | DYNAMIC)) == 0) ++ return; ++ ++ /* `bfd_target_elf_flavour' is required for elf_tdata(). */ ++ if (bfd_get_flavour (abfd) != bfd_target_elf_flavour) ++ return; ++ ++ ehdr = elf_tdata (abfd)->elf_header; ++ ++ for (phi = 0; phi < ehdr->e_phnum; phi++) ++ { ++ Elf_Internal_Phdr *phdr = &elf_tdata (abfd)->phdr[phi]; ++ bfd_size_type remains; ++ ++ /* Match the same conditions as in libobjid `phdr_callback'! */ ++ /* FIXME: Unify the code. */ ++ if (phdr->p_type != PT_LOAD) ++ continue; ++ if (!(phdr->p_flags & PF_R)) ++ continue; ++ /* Present? We probably could not checksum such one. */ ++ if (phdr->p_flags & PF_W) ++ continue; ++ ++ if (bfd_seek (abfd, phdr->p_offset, SEEK_SET) != 0) ++ { ++ warning ("Error seeking binary `%s' to program header #%u (%s)", ++ bfd_get_filename (abfd), phi, bfd_errmsg (bfd_get_error ())); ++ return; ++ } ++ remains = phdr->p_filesz; ++ while (remains > 0) ++ { ++ unsigned char buf[0x10000]; ++ bfd_size_type count; ++ unsigned char *ptr; ++ ++ count = min (remains, sizeof (buf)); ++ if (bfd_bread (buf, count, abfd) != count) ++ { ++ warning ("Error reading binary `%s' of program header #%u (%s)", ++ bfd_get_filename (abfd), phi, ++ bfd_errmsg (bfd_get_error ())); ++ return; ++ } ++ ++ for (ptr = buf; ptr < buf + count; ptr++) ++ sum = sum * 31 + *ptr; ++ ++ remains -= count; ++ } ++ } ++ ++ objid_cache_add (bfd_get_filename (abfd), sum); ++} ++ ++static void ++objid_locate_file (const char *filename) ++{ ++ bfd *abfd; ++ ++ /* FIXME: Persistent storage - `core_bfd'! */ ++ abfd = bfd_openr (filename, bfd_get_target (core_bfd)); ++ if (abfd == NULL) ++ { ++ warning ("Error opening binary `%s' (%s)", filename, ++ bfd_errmsg (bfd_get_error ())); ++ return; ++ } ++ ++ objid_locate_file_bfd (abfd); ++ ++ if (!bfd_close (abfd)) ++ { ++ warning ("Error closing binary `%s' (%s)", filename, ++ bfd_errmsg (bfd_get_error ())); ++ return; ++ } ++} ++ ++static void ++objid_locate_dir (const char *dirname) ++{ ++ DIR *dir; ++ struct dirent *dirent; ++ ++ dir = opendir (dirname); ++ if (dir == NULL) ++ { ++ warning ("Error scanning object directory `%s' (%s)", dirname, ++ safe_strerror (errno)); ++ return; ++ } ++ while (errno = 0, dirent = readdir (dir)) ++ { ++ char *targetname; ++ ++ if (strcmp (dirent->d_name, ".") == 0) ++ continue; ++ if (strcmp (dirent->d_name, "..") == 0) ++ continue; ++ targetname = xstrprintf ("%s/%s", dirname, dirent->d_name); ++ if (dirent->d_type == DT_DIR) ++ objid_locate_dir (targetname); ++ if (dirent->d_type == DT_REG) ++ objid_locate_file (targetname); ++ xfree (targetname); ++ } ++ if (errno != 0) ++ warning ("Error reading object directory `%s' (%s)", dirname, ++ safe_strerror (errno)); ++ if (closedir (dir)) ++ { ++ warning ("Error closing object directory `%s' (%s)", dirname, ++ safe_strerror (errno)); ++ return; ++ } ++} ++ ++static void ++objid_cache_reload (void) ++{ ++ char *p, *p1; ++ ++ objid_cache_clear (); ++ ++ if (strcmp (object_path, "") == 0) ++ return; ++ ++ for (p = object_path; p; p = p1 ? p1 + 1 : 0) ++ { ++ char *dirname; ++ DIR *dir; ++ ++ p1 = strchr (p, DIRNAME_SEPARATOR); ++ if (p1 == NULL) ++ dirname = xstrdup (p); ++ else ++ { ++ dirname = xmalloc (p1 + 1 - p); ++ memcpy (dirname, p, p1 - p); ++ dirname[p1 - p] = 0; ++ } ++ objid_locate_dir (dirname); ++ xfree (dirname); ++ } ++} ++ ++/* HEADER_POINTER is at the place of user data OBJ. */ ++/* Return TRUE of allocated header has been read to `*header_pointer'. */ ++static bfd_boolean ++objid_locate_scan (bfd *abfd, asection *sect, ++ ElfW(LibobjidHdr) **header_pointer) ++{ ++ file_ptr offset; ++ bfd_size_type size = bfd_section_size (abfd, sect); ++ ElfW(LibobjidHdr) header_local, *header; ++ ++ if (size < sizeof (header_local)) ++ return FALSE; ++ ++ /* FIXME: `PAGE_SIZE' should be from `auxv'. */ ++ for (offset = 0; offset <= size - sizeof (header_local); offset += PAGE_SIZE) ++ { ++ if (bfd_get_section_contents (abfd, sect, &header_local, offset, ++ sizeof (header_local)) == FALSE) ++ continue; ++ if (header_local.magic != ElfW(Magic)) ++ continue; ++ if (header_local.self != bfd_section_vma (abfd, sect) + offset) ++ continue; ++ if (header_local.self_not != ~header_local.self) ++ continue; ++ /* Sanity check to avoid loading xmalloc() crashes. */ ++ if (header_local.size > 0x10000) ++ continue; ++ if (header_local.size < sizeof (*header) ++ + header_local.obj_count * sizeof (*header->obj)) ++ continue; ++ ++ header = xmalloc (header_local.size); ++ if (bfd_get_section_contents (abfd, sect, header, offset, ++ header_local.size) == TRUE) ++ { ++ *header_pointer = header; ++ return TRUE; ++ } ++ xfree (header); ++ } ++ return FALSE; ++} ++ ++static void ++objid_exec_load (char *exec_file, int from_tty) ++{ ++ if (exec_bfd == NULL ++ || query (_("Load the matching executable/symbols \"%s\"? "), ++ exec_file)) ++ { ++ if (catch_command_errors (exec_file_attach, ++ exec_file, from_tty, RETURN_MASK_ALL)) ++ catch_command_errors (symbol_file_add_main_userloaded, ++ exec_file, from_tty, RETURN_MASK_ALL); ++ } ++} ++ ++static void ++objid_locate (bfd *core_bfd, int from_tty) ++{ ++ asection *sect; ++ ElfW(LibobjidHdr) *header; ++ unsigned obji; ++ ++ if (bfd_sections_find_if (core_bfd, ++ (bfd_boolean (*) (bfd *abfd, asection *sect, void *obj)) ++ objid_locate_scan, &header) == NULL) ++ return; ++ ++ /* FIXME: Keep the cache more persistent. */ ++ objid_cache_reload (); ++ ++ for (obji = 0; obji < header->obj_count; obji++) ++ { ++ ElfW(LibobjidObj) *obj = &header->obj[obji]; ++ CORE_ADDR filename_addr = obj->filename; ++ char *filename; ++ int filename_errno; ++ ++ /* Return value 0 is OK - empty strings. */ ++ if (target_read_string (filename_addr, &filename, FILENAME_MAX, ++ &filename_errno) <= 0 || filename_errno != 0) ++ warning ("Error reading objid object #%u filename (%s)", obji, ++ safe_strerror (filename_errno)); ++ else ++ { ++ const char *filename_found; ++ ++ filename_found = objid_cache_find (obj->checksum); ++ if (filename_found != NULL) ++ { ++ /* Main executable? It is the first entry with empty name. */ ++ if (obji == 0 && strcmp (filename, "") == 0) ++ objid_exec_load ((char *) filename_found, from_tty); ++ else if (strcmp (filename, "") != 0) ++ solib_open_map (filename, filename_found); ++ else ++ warning ("Ignoring invalid objid mapping `%s' -> `%s'", ++ filename, filename_found); ++ } ++ } ++ xfree (filename); ++ } ++ xfree (header); ++} ++ + /* This routine opens and sets up the core file bfd. */ + + static void +@@ -393,6 +765,9 @@ core_open (char *filename, int from_tty) + /* Fetch all registers from core file. */ + target_fetch_registers (-1); + ++ /* Locate objid section, if present. */ ++ objid_locate (core_bfd, from_tty); ++ + /* Add symbols and section mappings for any shared libraries. */ + #ifdef SOLIB_ADD + catch_errors (solib_add_stub, &from_tty, (char *) 0, +@@ -679,8 +1054,26 @@ int coreops_suppress_target; + void + _initialize_corelow (void) + { ++ struct cmd_list_element *c; ++ + init_core_ops (); + + if (!coreops_suppress_target) + add_target (&core_ops); ++ ++ init_object_path (); ++ ++ c = add_cmd ("objectdirectory", class_files, objectdirectory_command, ++ "Add directory DIR to beginning of libobjid based search path\n\ ++for binary files.\n\ ++DIR can also be $cwd for the current working directory.\n\ ++With no argument, reset the search path to be empty, the default.", ++ &cmdlist); ++ ++ set_cmd_completer (c, filename_completer); ++ ++ add_cmd ("objectdirectories", no_class, show_objectdirectories, ++ "Current search path for libobjid based finding binary files.\n\ ++$cwd in the path means the current working directory.", ++ &showlist); + } +diff -u -rup -x testsuite gdb-6.3-orig/gdb/defs.h gdb-6.3/gdb/defs.h +--- gdb-6.3-orig/gdb/defs.h 2006-12-28 17:29:07.000000000 +0100 ++++ gdb-6.3/gdb/defs.h 2006-12-28 18:23:59.000000000 +0100 +@@ -614,6 +614,11 @@ extern void init_source_path (void); + + extern void init_last_source_visited (void); + ++/* From corelow.c */ ++/* FIXME: init.c based. */ ++ ++extern void objectdirectory_command (char *dirname, int from_tty); ++ + /* From exec.c */ + + extern void exec_set_section_offsets (bfd_signed_vma text_off, +diff -u -rup -x testsuite gdb-6.3-orig/gdb/main.c gdb-6.3/gdb/main.c +--- gdb-6.3-orig/gdb/main.c 2006-12-28 17:29:10.000000000 +0100 ++++ gdb-6.3/gdb/main.c 2006-12-28 20:23:46.000000000 +0100 +@@ -134,6 +134,13 @@ captured_main (void *data) + /* Number of elements of cmdarg used. */ + int ncmd; + ++ /* Indices of all arguments of --objectdirectory option. */ ++ char **objectdirarg; ++ /* Allocated size. */ ++ int objectdirsize; ++ /* Number of elements used. */ ++ int nobjectdir; ++ + /* Indices of all arguments of --directory option. */ + char **dirarg; + /* Allocated size. */ +@@ -173,6 +180,9 @@ captured_main (void *data) + dirsize = 1; + dirarg = (char **) xmalloc (dirsize * sizeof (*dirarg)); + ndir = 0; ++ objectdirsize = 1; ++ objectdirarg = (char **) xmalloc (objectdirsize * sizeof (*objectdirarg)); ++ nobjectdir = 0; + + quit_flag = 0; + line = (char *) xmalloc (linesize); +@@ -240,7 +250,8 @@ captured_main (void *data) + OPT_STATISTICS, + OPT_TUI, + OPT_NOWINDOWS, +- OPT_WINDOWS ++ OPT_WINDOWS, ++ OPT_OBJECTDIRECTORY + }; + static struct option long_options[] = + { +@@ -289,6 +300,7 @@ captured_main (void *data) + {"interpreter", required_argument, 0, 'i'}, + {"i", required_argument, 0, 'i'}, + {"directory", required_argument, 0, 'd'}, ++ {"objectdirectory", required_argument, 0, OPT_OBJECTDIRECTORY}, + {"d", required_argument, 0, 'd'}, + {"cd", required_argument, 0, OPT_CD}, + {"tty", required_argument, 0, 't'}, +@@ -457,6 +469,16 @@ extern int gdbtk_test (char *); + remote_timeout = i; + } + break; ++ case OPT_OBJECTDIRECTORY: ++ objectdirarg[nobjectdir++] = optarg; ++ if (nobjectdir >= objectdirsize) ++ { ++ objectdirsize *= 2; ++ objectdirarg = (char **) xrealloc ((char *) objectdirarg, ++ objectdirsize ++ * sizeof (*objectdirarg)); ++ } ++ break; + + case '?': + fprintf_unfiltered (gdb_stderr, +@@ -636,6 +658,11 @@ extern int gdbtk_test (char *); + catch_command_errors (directory_command, dirarg[i], 0, RETURN_MASK_ALL); + xfree (dirarg); + ++ for (i = 0; i < nobjectdir; i++) ++ catch_command_errors (objectdirectory_command, objectdirarg[i], 0, ++ RETURN_MASK_ALL); ++ xfree (objectdirarg); ++ + if (execarg != NULL + && symarg != NULL + && strcmp (execarg, symarg) == 0) +@@ -845,6 +872,7 @@ Options:\n\n\ + fputs_unfiltered (_("\ + --dbx DBX compatibility mode.\n\ + --directory=DIR Search for source files in DIR.\n\ ++ --objectdirectory=DIR Search for binary files by libobjid in DIR.\n\ + --epoch Output information used by epoch emacs-GDB interface.\n\ + --exec=EXECFILE Use EXECFILE as the executable.\n\ + --fullname Output information used by emacs-GDB interface.\n\ +diff -u -rup -x testsuite gdb-6.3-orig/gdb/objid.h gdb-6.3/gdb/objid.h +--- gdb-6.3-orig/gdb/objid.h 2006-12-29 11:34:47.000000000 +0100 ++++ gdb-6.3/gdb/objid.h 2006-12-28 21:27:51.000000000 +0100 +@@ -0,0 +1,29 @@ ++#ifndef OBJID_H ++#define OBJID_H 1 ++ ++ ++#include ++ ++ ++#define Elf32_Magic 0x0B11D032 ++#define Elf64_Magic 0x0B11D064 ++ ++typedef struct ++ { ++ ElfW(Addr) filename; ++ ElfW(Word) checksum; ++ } ElfW(LibobjidObj); ++ ++typedef struct ++ { ++ ElfW(Word) magic; ++ ElfW(Addr) self; ++ ElfW(Addr) self_not; /* == ~self */ ++ /* Total size of all OBJs including this header. */ ++ ElfW(Word) size; ++ ElfW(Word) obj_count; ++ ElfW(LibobjidObj) obj[0]; ++ } ElfW(LibobjidHdr); ++ ++ ++#endif /* !OBJID_H */ +diff -u -rup -x testsuite gdb-6.3-orig/gdb/solib.c gdb-6.3/gdb/solib.c +--- gdb-6.3-orig/gdb/solib.c 2006-12-28 17:29:11.000000000 +0100 ++++ gdb-6.3/gdb/solib.c 2006-12-29 01:37:08.000000000 +0100 +@@ -73,6 +73,60 @@ static char *solib_search_path = NULL; + + void add_to_target_sections (int, struct target_ops *, struct so_list *); + ++struct solib_open_map ++ { ++ struct solib_open_map *next; ++ char *from; ++ char *to; ++ }; ++static struct solib_open_map *solib_open_map_list; ++ ++static const char * ++solib_open_map_find (const char *from) ++{ ++ struct solib_open_map *item; ++ ++ for (item = solib_open_map_list; item != NULL; item = item->next) ++ if (strcmp (item->from, from) == 0) ++ return item->to; ++ ++ return NULL; ++} ++ ++void ++solib_open_map (const char *from, const char *to) ++{ ++ const char *to_old; ++ struct solib_open_map *item_new; ++ ++ to_old = solib_open_map_find (from); ++ if (to_old != NULL && strcmp (to_old, to) != 0) ++ warning ("Duplicite mapping of `%s' -> `%s' vs. `%s'", from, to_old, to); ++ if (to_old != NULL) ++ return; ++ ++ item_new = xmalloc (sizeof (*item_new)); ++ item_new->from = xstrdup (from); ++ item_new->to = xstrdup (to); ++ item_new->next = solib_open_map_list; ++ solib_open_map_list = item_new; ++} ++ ++static void ++solib_open_map_reset (void) ++{ ++ while (solib_open_map_list != NULL) ++ { ++ struct solib_open_map *freeing; ++ ++ freeing = solib_open_map_list; ++ xfree (freeing->from); ++ xfree (freeing->to); ++ solib_open_map_list = freeing->next; ++ xfree (freeing); ++ } ++} ++ + /* + + GLOBAL FUNCTION +@@ -117,6 +171,11 @@ solib_open (char *in_pathname, char **fo + char *temp_pathname = NULL; + char *p = in_pathname; + int solib_absolute_prefix_is_empty; ++ const char *in_pathname_mapped; ++ ++ in_pathname_mapped = solib_open_map_find (in_pathname); ++ if (in_pathname_mapped != NULL) ++ in_pathname = (char *) in_pathname_mapped; + + solib_absolute_prefix_is_empty = (solib_absolute_prefix == NULL + || *solib_absolute_prefix == 0); +@@ -821,6 +880,8 @@ clear_solib (void) + } + + TARGET_SO_CLEAR_SOLIB (); ++ ++ solib_open_map_reset (); + } + + static void +diff -u -rup -x testsuite gdb-6.3-orig/gdb/solist.h gdb-6.3/gdb/solist.h +--- gdb-6.3-orig/gdb/solist.h 2006-12-28 17:29:07.000000000 +0100 ++++ gdb-6.3/gdb/solist.h 2006-12-29 00:10:49.000000000 +0100 +@@ -121,6 +121,8 @@ struct so_list *master_so_list (void); + /* Find solib binary file and open it. */ + extern int solib_open (char *in_pathname, char **found_pathname); + ++extern void solib_open_map (const char *from, const char *to); ++ + /* Add the list of sections in so_list to the target to_sections. */ + extern void add_to_target_sections (int, struct target_ops *, struct so_list *); + +diff -u -rup -x testsuite gdb-6.3-orig/gdb/symfile.c gdb-6.3/gdb/symfile.c +--- gdb-6.3-orig/gdb/symfile.c 2006-12-28 17:29:10.000000000 +0100 ++++ gdb-6.3/gdb/symfile.c 2006-12-28 21:59:10.000000000 +0100 +@@ -973,6 +973,12 @@ symbol_file_add_main (char *args, int fr + symbol_file_add_main_1 (args, from_tty, 0); + } + ++void ++symbol_file_add_main_userloaded (char *args, int from_tty) ++{ ++ symbol_file_add_main_1 (args, from_tty, OBJF_USERLOADED); ++} ++ + static void + symbol_file_add_main_1 (char *args, int from_tty, int flags) + { +diff -u -rup -x testsuite gdb-6.3-orig/gdb/symfile.h gdb-6.3/gdb/symfile.h +--- gdb-6.3-orig/gdb/symfile.h 2004-09-08 23:58:19.000000000 +0200 ++++ gdb-6.3/gdb/symfile.h 2006-12-28 21:59:11.000000000 +0100 +@@ -310,6 +310,7 @@ extern CORE_ADDR symbol_overlayed_addres + + /* Load symbols from a file. */ + extern void symbol_file_add_main (char *args, int from_tty); ++extern void symbol_file_add_main_userloaded (char *args, int from_tty); + + /* Clear GDB symbol tables. */ + extern void symbol_file_clear (int from_tty); diff --git a/hook-arch-asm.h b/hook-arch-asm.h new file mode 100644 index 0000000..3538cb8 --- /dev/null +++ b/hook-arch-asm.h @@ -0,0 +1,24 @@ +/* $Id$ */ + + + .text + .globl sigaction_trampoline + .internal sigaction_trampoline + .globl sigaction_trampoline_jmpdir + .internal sigaction_trampoline_jmpdir + .globl sigaction_trampoline_jmpdir_vec + .internal sigaction_trampoline_jmpdir_vec + .globl sigaction_trampoline_jmpdir_base + .internal sigaction_trampoline_jmpdir_base + + .data + .globl trampoline_size + .internal trampoline_size +trampoline_size: + .long TRAMPOLINE_SIZE + +#ifdef __linux__ + .section .note.GNU-stack, "", @progbits +#endif + + .text diff --git a/hook-arch-c.h b/hook-arch-c.h new file mode 100644 index 0000000..302300c --- /dev/null +++ b/hook-arch-c.h @@ -0,0 +1,14 @@ +/* $Id$ */ + + +#include +#include + + +extern int sigaction_trampoline (int signum, const struct sigaction *act, + struct sigaction *oldact); +extern void sigaction_trampoline_jmpdir (void); +extern void sigaction_trampoline_jmpdir_vec (void); +extern void sigaction_trampoline_jmpdir_base (void); + +extern size_t trampoline_size; diff --git a/hook-i386.S b/hook-i386.S new file mode 100644 index 0000000..b901bb7 --- /dev/null +++ b/hook-i386.S @@ -0,0 +1,30 @@ +/* $Id$ */ + + +#define TRAMPOLINE_SIZE 5 + +#include "hook-arch-asm.h" + +sigaction_trampoline: +/* +0: */ push %ebp +/* +1: */ mov %esp,%ebp +/* +3: */ push %edi +/* +4: */ push %esi +/* +5: */ + .ifne . - sigaction_trampoline - TRAMPOLINE_SIZE + .err + .endif + call pic_base +pic_base: pop %eax + jmp *(sigaction_orig_libc_cont-pic_base)(%eax) + +sigaction_trampoline_jmpdir: + /* long absolute direct jump */ +/* +0: */ .byte 0xe9 + .long 0x0 + .set sigaction_trampoline_jmpdir_vec, . - 4 + .set sigaction_trampoline_jmpdir_base, . +/* +5: */ + .ifne . - sigaction_trampoline_jmpdir - TRAMPOLINE_SIZE + .err + .endif diff --git a/hook-x86_64.S b/hook-x86_64.S new file mode 100644 index 0000000..baec2db --- /dev/null +++ b/hook-x86_64.S @@ -0,0 +1,29 @@ +/* $Id$ */ + + +#define TRAMPOLINE_SIZE 13 + +#include "hook-arch-asm.h" + +sigaction_trampoline: +/* +0: */ lea -0x20(%rdi), %eax +/* +3: */ sub $0xd0, %rsp +/* +10: */ mov %rsi, %rcx +/* +13: */ + .ifne . - sigaction_trampoline - TRAMPOLINE_SIZE + .err + .endif + jmp *sigaction_orig_libc_cont(%rip) + +sigaction_trampoline_jmpdir: +/* +0: */ mov $0x0123456789abcdef,%rax + .set sigaction_trampoline_jmpdir_vec, . - 8 + /* Set it to `sigaction_trampoline' to define it as NULL, + simple value `0' would be relative and thus not NULL. */ + .set sigaction_trampoline_jmpdir_base, sigaction_trampoline +/* +10: */ jmpq *%rax +/* +12: */ nop +/* +13: */ + .ifne . - sigaction_trampoline_jmpdir - TRAMPOLINE_SIZE + .err + .endif diff --git a/hook.c b/hook.c new file mode 100644 index 0000000..9ae7887 --- /dev/null +++ b/hook.c @@ -0,0 +1,228 @@ +/* $Id$ */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hook.h" +#include "hook-arch-c.h" +#include "build.h" +#include "maps.h" +#include "common.h" + + +static sigaction_t sigaction_orig; +/* == sigaction_orig_libc + trampoline_size */ +INTERNAL sigaction_t sigaction_orig_libc_cont; + + +INTERNAL int debug; + +typedef void (*sa_sigaction_t) (int, siginfo_t *, void *); + +static const sa_sigaction_t *const_sigaction_pointer_get (const struct sigaction *act) +{ + if (act->sa_flags & SA_SIGINFO) + return (sa_sigaction_t *) &act->sa_sigaction; + else + return (sa_sigaction_t *) &act->sa_handler; +} + +static sa_sigaction_t *sigaction_pointer_get (struct sigaction *act) +{ + return (sa_sigaction_t *) const_sigaction_pointer_get + ((const struct sigaction *) act); +} + +/* `SA_RESETHAND' will reset `*sigaction_pointer_get ()' to `SIG_DFL' + even for `sigaction' parameter `oldact'. + `SA_RESETHAND' will stay set there. */ + +static struct sigaction act_app[NSIG]; + +static void handler (int signum, siginfo_t *siginfo, void *ucontext_t_pointer) +{ + struct sigaction *act = &act_app[signum]; + sa_sigaction_t *act_sigaction_pointer; + + act_sigaction_pointer = sigaction_pointer_get (act); + if (*act_sigaction_pointer != (sa_sigaction_t) SIG_DFL) + (*act_sigaction_pointer) (signum, siginfo, ucontext_t_pointer); + else + { + struct sigaction sig_dfl; + + /* Reset the handler first to get caught SEGV in core_dump(). */ + sig_dfl.sa_handler = SIG_DFL; + sigemptyset (&sig_dfl.sa_mask); + sig_dfl.sa_flags = 0; + (*sigaction_orig) (signum, act, NULL); + + core_dump (); + } + if (act->sa_flags & SA_RESETHAND) + *act_sigaction_pointer = (sa_sigaction_t) SIG_DFL; +} + +static int sigaction_subst (int signum, const struct sigaction *act, + struct sigaction *oldact) +{ + struct sigaction act_new, act_app_saved; + int retval; + + switch (signum) + { + /* Core dumping signals: */ +#define SIGNAL_CORE(name) case name: +#include "signal-core.h" +#undef SIGNAL_CORE + break; + + default: + return (*sigaction_orig) (signum, act, oldact); + } + if (oldact != NULL) + act_app_saved = act_app[signum]; + if (act != NULL) + { + const sa_sigaction_t *const_act_sigaction_pointer; + + const_act_sigaction_pointer = const_sigaction_pointer_get (act); + if (*const_act_sigaction_pointer == (sa_sigaction_t) SIG_DFL + || act->sa_flags & SA_RESETHAND) + { + sa_sigaction_t *act_sigaction_pointer; + + act_app[signum] = *act; + act_new = *act; + act = &act_new; + act_sigaction_pointer = sigaction_pointer_get (&act_new); + *act_sigaction_pointer = handler; + act_new.sa_flags &= ~SA_RESETHAND; + } + } + retval = (*sigaction_orig) (signum, act, oldact); + if (oldact != NULL) + { + sa_sigaction_t *oldact_sigaction_pointer; + + assert (memcmp (&act_app_saved.sa_mask, &oldact->sa_mask, + sizeof (&act_app_saved.sa_mask)) == 0); + assert ((act_app_saved.sa_flags & ~SA_RESETHAND) + == (oldact->sa_flags & ~SA_RESETHAND)); + oldact_sigaction_pointer = sigaction_pointer_get (oldact); + if (*oldact_sigaction_pointer == handler) + *oldact_sigaction_pointer = *(sigaction_pointer_get (&act_app[signum])); + oldact->sa_flags = act_app_saved.sa_flags; + } + return retval; +} + +static void sigaction_orig_patch (void) +{ + unsigned long page_start, page_end; + sigaction_t sigaction_orig_libc; + + sigaction_orig_libc = sigaction_orig; + + page_start = (unsigned long) sigaction_orig_libc & PAGE_MASK; + page_end = ((unsigned long) sigaction_orig_libc + trampoline_size + + PAGE_SIZE - 1) & PAGE_MASK; + maps_read (); + if (maps_verify ((void *) page_start, (void *) page_end)) + { + fprintf (stderr, "libobjid: Invalid \"%s\" (0x%lx, %u)\n", + MAPS_FILENAME, page_start, (unsigned) (page_end - page_start)); + return; + } + maps_static_setup ((void *) page_start, (void *) page_end); + if (mprotect ((void *) page_start, page_end - page_start, + PROT_READ | PROT_WRITE | PROT_EXEC)) + { + fprintf (stderr, "libobjid: mprotect (0x%lx, %u): %m\n", page_start, + (unsigned) (page_end - page_start)); + return; + } + if (memcmp (sigaction_orig_libc, sigaction_trampoline, trampoline_size) != 0) + { + fprintf (stderr, "libobjid: libc prologue (%p) is not matching\n", + sigaction_orig_libc); + return; + } + /* Target jump from our trampoline. */ + sigaction_orig_libc_cont = (sigaction_t) ((char *) sigaction_orig_libc + + trampoline_size); + /* Jump instruction to our `sigaction_subst'. */ + memcpy (sigaction_orig_libc, sigaction_trampoline_jmpdir, + trampoline_size); + /* Long direct jump operand is relative on i386 but absolute on x86_64. */ +#define REBASE(ptr) ((char *) (ptr) - (char *) sigaction_trampoline_jmpdir \ + + (char *) sigaction_orig_libc) + *(void **) REBASE (sigaction_trampoline_jmpdir_vec) = (void *) + ((char *) sigaction_subst - ((void *) sigaction_trampoline_jmpdir_base + == (void *) sigaction_trampoline ? NULL + : REBASE (sigaction_trampoline_jmpdir_base))); +#undef REBASE + + /* Returning through our trampoline code back to the original function. */ + sigaction_orig = sigaction_trampoline; +} + +static void signal_init (int signum) +{ + sighandler_t handler_orig; + + handler_orig = signal (signum, SIG_DFL); + if (handler_orig != SIG_DFL) + fprintf (stderr, "libobjid: signal_init: %d: %p\n", signum, handler_orig); +} + +static void *libc_handle; + +static void libobjid_init (void) __attribute__((__constructor__)); +static void libobjid_init (void) +{ + char *debug_str; + + debug_str = getenv ("LIBOBJID"); + if (debug_str != NULL) + debug = atoi (debug_str); + + if (libc_handle == NULL) + { + libc_handle = dlopen (NULL, RTLD_LAZY); /* RTLD_LAZY is mandatory */ + if (libc_handle == NULL) + fprintf (stderr, "libobjid: dlopen (NULL): %s\n", dlerror ()); + } + if (libc_handle == NULL) + return; + +#define SIGACTION_NAME "sigaction" + sigaction_orig = (sigaction_t) dlsym (libc_handle, SIGACTION_NAME); + if (sigaction_orig == NULL) + fprintf (stderr, "libobjid: dlsym (\"%s\"): %s\n", SIGACTION_NAME, + dlerror ()); +#undef SIGACTION_NAME + if (sigaction_orig == NULL) + return; + + sigaction_orig_patch (); + +#define SIGNAL_CORE(name) signal_init (name); +#include "signal-core.h" +#undef SIGNAL_CORE +} + +static void libobjid_fini (void) __attribute__((__constructor__)); +static void libobjid_fini (void) +{ + if (libc_handle != NULL && dlclose (libc_handle)) + fprintf (stderr, "libobjid: dlclose (): %s\n", dlerror ()); + libc_handle = NULL; +} diff --git a/hook.h b/hook.h new file mode 100644 index 0000000..611f762 --- /dev/null +++ b/hook.h @@ -0,0 +1,18 @@ +/* $Id$ */ + + +#ifndef HOOK_H +#define HOOK_H 1 + + +#include + + +typedef int (*sigaction_t) (int signum, const struct sigaction *act, + struct sigaction *oldact); + +extern sigaction_t sigaction_orig_libc_cont; + +extern int debug; + +#endif /* !HOOK_H */ diff --git a/libobjid.spec b/libobjid.spec new file mode 100644 index 0000000..cc26eb3 --- /dev/null +++ b/libobjid.spec @@ -0,0 +1,51 @@ +# $Id$ + + +Summary: Binary location info for core files +Name: libobjid +Version: 0.1 +Release: 1 +License: GPL +Group: System Environment/Base +Source: http://www.jankratochvil.net/priv/%{name}-%{version}.tar.gz +ExclusiveArch: %{ix86} x86_64 +Buildroot: %{_tmppath}/libobjid-root + +%description +LD_PRELOAD=/usr/lib/libobjid.so programname +will extend the core file in the case the program segfaults. +You need patched GDB and its --objectdirectory option to use the location info. +Debug stderr output for core file extension confirmation: +libobjid: core + +%package devel +Summary: Interface files for libobjid +Group: System Environment/Base + +%description devel +The libobjid-devel package contains interface header file for applications +retrieving the information stored to the core files uniquely identifying the +original binaries. + +%prep +%setup + +%build +make +make check + +%install +make DESTDIR=$RPM_BUILD_ROOT libdir=%{_libdir} includedir=%{_includedir} install + +%files +%defattr(-,root,root) +%{_libdir}/libobjid.so +%doc README TODO + +%files devel +%defattr(-,root,root) +%{_includedir}/objid.h + +%changelog +* Fri Dec 29 2006 Jan Kratochvil 0.1 +- Initial release. diff --git a/maps.c b/maps.c new file mode 100644 index 0000000..8550515 --- /dev/null +++ b/maps.c @@ -0,0 +1,129 @@ +/* $Id$ */ + + +#include +#include +#include +#include +#include +#include +#include + +#include "maps.h" +#include "common.h" +#include "hook.h" + + +struct range + { + void *start; + void *end; + }; +/* FIXME: Dynamic allocation. */ +static struct range ranges[PAGE_SIZE * 8 / sizeof (struct range)]; +static struct range *ranges_static_end = ranges; +static struct range *ranges_end = ranges; /* = ranges_static_end */ + + +/* Returns 0 on readable start..end-1. */ +/* TODO: Sort + binary search. */ +INTERNAL int maps_verify (void *start, void *end) +{ + int changed; + + do + { + struct range *range; + + changed = 0; + for (range = ranges; start < end && range < ranges_end; range++) + if (start >= range->start && start < range->end) + { + start = range->end; + changed = 1; + } + } + while (start < end && changed); + + if (start < end) + return -1; + /* Success. */ + return 0; +} + +INTERNAL void maps_static_setup (void *start, void *end) +{ + if (ranges_static_end >= ranges + N_ELEMENTS (ranges)) + return; + + ranges_static_end->start = start; + ranges_static_end->end = end; + ranges_static_end++; + + if (debug >= 1) + fprintf (stderr,"static: %p - %p\n", start, end); +} + +INTERNAL void maps_read (void) +{ + int fd; + ssize_t bytes_read; + char *s; + + /* Reset. */ + ranges_end = ranges_static_end; + + /* Do not use level 3 I/O as it may be too SIGSEGV intrusive. */ + fd = open (MAPS_FILENAME, O_RDONLY); + if (fd == -1) + return; + bytes_read = read (fd, ranges_end, (char *) ranges + sizeof (ranges) + - (char *) ranges_end - 1); + if (close (fd)) + return; + if (bytes_read <= 0) + return; + s = (char *) ranges_end; + s[bytes_read] = 0; + + while (*s) + { + unsigned long start, end; + char prot_read, prot_write, prot_exec, prot_mayshare; + int offset, i; + const char *vdso_name = " [vdso]"; + const size_t vdso_len = strlen (vdso_name); + + i = sscanf (s, "%lx-%lx %c%c%c%c %n", &start, &end, &prot_read, + &prot_write, &prot_exec, &prot_mayshare, &offset); + if (i != 6) + break; + s += offset; + /* Reuse the trailing absolute pathname? + Currently we use the relative one from dl_iterate_phdr(3). */ + while (*s && *s != '\n') + s++; + /* Skip [vdso] entry to discard its dl_iterate_phdr(3) which has the + same name as the main executable - "". */ + if (s >= (char *) ranges + vdso_len + && memcmp (s - vdso_len, vdso_name, vdso_len) == 0) + continue; + if (prot_read != 'r') + continue; + if (prot_write != '-') + continue; + if (prot_exec != 'x') + continue; + if (prot_mayshare != 'p') + continue; + ranges_end->start = (void *) start; + ranges_end->end = (void *) end; + ranges_end++; + + if (debug >= 1) + fprintf (stderr,"range: %p - %p\n", (void *) start, (void *) end); + + if (ranges_end >= ranges + N_ELEMENTS (ranges)) + return; + } +} diff --git a/maps.h b/maps.h new file mode 100644 index 0000000..52a28ac --- /dev/null +++ b/maps.h @@ -0,0 +1,9 @@ +/* $Id$ */ + + +#define MAPS_FILENAME "/proc/self/maps" + +/* Returns 0 on readable start..end-1. */ +extern int maps_verify (void *start, void *end); +extern void maps_read (void); +extern void maps_static_setup (void *start, void *end); diff --git a/objid.h b/objid.h new file mode 100644 index 0000000..1511b01 --- /dev/null +++ b/objid.h @@ -0,0 +1,32 @@ +/* $Id$ */ + + +#ifndef _OBJID_H +#define _OBJID_H 1 + + +#include + + +#define Elf32_Magic 0x0B11D032 +#define Elf64_Magic 0x0B11D064 + +typedef struct + { + ElfW(Addr) filename; + ElfW(Word) checksum; + } ElfW(LibobjidObj); + +typedef struct + { + ElfW(Word) magic; + ElfW(Addr) self; + ElfW(Addr) self_not; /* == ~self */ + /* Total size of all OBJs including this header. */ + ElfW(Word) size; + ElfW(Word) obj_count; + ElfW(LibobjidObj) obj[0]; + } ElfW(LibobjidHdr); + + +#endif /* !_OBJID_H */ diff --git a/signal-core.h b/signal-core.h new file mode 100644 index 0000000..52417ee --- /dev/null +++ b/signal-core.h @@ -0,0 +1,16 @@ +/* $Id$ */ + + +SIGNAL_CORE (SIGQUIT) +SIGNAL_CORE (SIGILL) +SIGNAL_CORE (SIGABRT) +SIGNAL_CORE (SIGFPE) +SIGNAL_CORE (SIGSEGV) +SIGNAL_CORE (SIGBUS) +SIGNAL_CORE (SIGSYS) +SIGNAL_CORE (SIGTRAP) +SIGNAL_CORE (SIGXCPU) +SIGNAL_CORE (SIGXFSZ) +#if SIGIOT != SIGABRT +SIGNAL_CORE (SIGIOT) +#endif diff --git a/tst-core.c b/tst-core.c new file mode 100644 index 0000000..b7b0d7e --- /dev/null +++ b/tst-core.c @@ -0,0 +1,8 @@ +/* $Id$ */ + + +int main (void) +{ + *(int *) 0 = 0; + return 0; +} diff --git a/tst-raise.c b/tst-raise.c new file mode 100644 index 0000000..34fbd1a --- /dev/null +++ b/tst-raise.c @@ -0,0 +1,63 @@ +/* $Id$ */ +/* Copyright (C) 2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Jakub Jelinek , 2003. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include +#include + +volatile int count; + +void +sh (int sig) +{ + ++count; +} + +int +main (void) +{ + struct sigaction sa; + sa.sa_handler = sh; + sigemptyset (&sa.sa_mask); + sa.sa_flags = 0; + if (sigaction (SIGUSR1, &sa, NULL) < 0) + { + printf ("sigaction failed: %m\n"); + exit (1); + } + if (raise (SIGUSR1) < 0) + { + printf ("first raise failed: %m\n"); + exit (1); + } + if (raise (SIGUSR1) < 0) + { + printf ("second raise failed: %m\n"); + exit (1); + } + if (count != 2) + { + printf ("signal handler not called 2 times\n"); + exit (1); + } + exit (0); +} diff --git a/tst-resethand.c b/tst-resethand.c new file mode 100644 index 0000000..266ac03 --- /dev/null +++ b/tst-resethand.c @@ -0,0 +1,84 @@ +/* $Id$ */ +/* Copyright (C) 2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Jakub Jelinek , 2003. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include +#include + +volatile int count; + +void +sh (int sig) +{ + ++count; +} + +int +main (void) +{ + struct sigaction sa; + sa.sa_handler = sh; + sigemptyset (&sa.sa_mask); + sa.sa_flags = SA_RESETHAND; + sigaction (SIGCHLD, NULL, NULL); + if (sigaction (SIGCHLD, &sa, NULL) < 0) + { + printf ("sigaction set failed: %m\n"); + exit (1); + } + if (raise (SIGCHLD) < 0) + { + printf ("first raise failed: %m\n"); + exit (1); + } + if (raise (SIGCHLD) < 0) + { + printf ("second raise failed: %m\n"); + exit (1); + } + if (count != 1) + { + printf ("signal handler not called 1 times\n"); + exit (1); + } + if (sigaction (SIGCHLD, NULL, &sa) < 0) + { + printf ("sigaction get failed: %m\n"); + exit (1); + } + if (sa.sa_handler != SIG_DFL) + { + printf ("sigaction retrieved sa_handler: %p\n", sa.sa_handler); + exit (1); + } + /* `SA_RESTORER' gets set on `glibc-2.3.4-2.25.i686' + but not on `glibc-2.5-7.i686'. */ +#ifndef SA_RESTORER +#define SA_RESTORER 0x04000000 +#endif + if ((sa.sa_flags & ~SA_RESTORER) != SA_RESETHAND) + { + printf ("sigaction retrieved sa_flags: 0x%x\n", sa.sa_flags); + exit (1); + } + exit (0); +} -- 1.8.3.1