Initial import.
authorjkratoch <>
Fri, 29 Dec 2006 19:14:15 +0000 (19:14 +0000)
committerjkratoch <>
Fri, 29 Dec 2006 19:14:15 +0000 (19:14 +0000)
24 files changed:
.cvsignore [new file with mode: 0644]
.vimrc [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
build.c [new file with mode: 0644]
build.h [new file with mode: 0644]
common.h [new file with mode: 0644]
demo [new file with mode: 0755]
gdb-6.3.patch [new file with mode: 0644]
hook-arch-asm.h [new file with mode: 0644]
hook-arch-c.h [new file with mode: 0644]
hook-i386.S [new file with mode: 0644]
hook-x86_64.S [new file with mode: 0644]
hook.c [new file with mode: 0644]
hook.h [new file with mode: 0644]
libobjid.spec [new file with mode: 0644]
maps.c [new file with mode: 0644]
maps.h [new file with mode: 0644]
objid.h [new file with mode: 0644]
signal-core.h [new file with mode: 0644]
tst-core.c [new file with mode: 0644]
tst-raise.c [new file with mode: 0644]
tst-resethand.c [new file with mode: 0644]

diff --git a/.cvsignore b/.cvsignore
new file mode 100644 (file)
index 0000000..c660f69
--- /dev/null
@@ -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 (file)
index 0000000..ac9076b
--- /dev/null
+++ b/.vimrc
@@ -0,0 +1 @@
+set sw=2
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
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 (file)
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 (file)
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 `<objid.h>' availability.
+* Update `Makefile.in' dependencies.
+
+
+$Id$
diff --git a/build.c b/build.c
new file mode 100644 (file)
index 0000000..21831bc
--- /dev/null
+++ b/build.c
@@ -0,0 +1,226 @@
+/* $Id$ */
+
+
+#include <stdio.h>
+#include <link.h>
+#include <assert.h>
+#include <asm/page.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <unistd.h>
+#include <stddef.h>
+
+#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 (file)
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 (file)
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 (executable)
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 (file)
index 0000000..02a5522
--- /dev/null
@@ -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 <sys/file.h>         /* needed for F_OK and friends */
+ #endif
++#include <dirent.h>
++#include <asm/page.h>         /* 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 <objid.h>
+ #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 <link.h>
++
++
++#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 (file)
index 0000000..3538cb8
--- /dev/null
@@ -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 (file)
index 0000000..302300c
--- /dev/null
@@ -0,0 +1,14 @@
+/* $Id$ */
+
+
+#include <signal.h>
+#include <stddef.h>
+
+
+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 (file)
index 0000000..b901bb7
--- /dev/null
@@ -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 (file)
index 0000000..baec2db
--- /dev/null
@@ -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 (file)
index 0000000..9ae7887
--- /dev/null
+++ b/hook.c
@@ -0,0 +1,228 @@
+/* $Id$ */
+
+
+#include <signal.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <asm/page.h>
+#include <sys/mman.h>
+#include <stdlib.h>
+
+#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 (file)
index 0000000..611f762
--- /dev/null
+++ b/hook.h
@@ -0,0 +1,18 @@
+/* $Id$ */
+
+
+#ifndef HOOK_H
+#define HOOK_H 1
+
+
+#include <signal.h>
+
+
+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 (file)
index 0000000..cc26eb3
--- /dev/null
@@ -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 <jan.kratochvil@redhat.com> 0.1
+- Initial release.
diff --git a/maps.c b/maps.c
new file mode 100644 (file)
index 0000000..8550515
--- /dev/null
+++ b/maps.c
@@ -0,0 +1,129 @@
+/* $Id$ */
+
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <asm/page.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+
+#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 (file)
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 (file)
index 0000000..1511b01
--- /dev/null
+++ b/objid.h
@@ -0,0 +1,32 @@
+/* $Id$ */
+
+
+#ifndef _OBJID_H
+#define _OBJID_H 1
+
+
+#include <link.h>
+
+
+#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 (file)
index 0000000..52417ee
--- /dev/null
@@ -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 (file)
index 0000000..b7b0d7e
--- /dev/null
@@ -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 (file)
index 0000000..34fbd1a
--- /dev/null
@@ -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 <jakub@redhat.com>, 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 <errno.h>
+#include <error.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+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 (file)
index 0000000..266ac03
--- /dev/null
@@ -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 <jakub@redhat.com>, 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 <errno.h>
+#include <error.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+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);
+}