From: jkratoch <> Date: Fri, 29 Dec 2006 19:14:15 +0000 (+0000) Subject: Initial import. X-Git-Tag: libobjid-0_1~1 X-Git-Url: http://git.jankratochvil.net/?p=libobjid.git;a=commitdiff_plain;h=50135fcf554b6363ce325b297fa953e8fbe16f49;ds=sidebyside Initial import. --- 50135fcf554b6363ce325b297fa953e8fbe16f49 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); +}