--- /dev/null
+build.o
+hook-i386.o
+hook.o
+libobjid.so
+maps.o
+tst-core
+tst-core-pie
+tst-raise
+tst-resethand
--- /dev/null
+# $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
--- /dev/null
+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$
--- /dev/null
+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$
--- /dev/null
+/* $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 ();
+}
--- /dev/null
+/* $Id$ */
+
+
+extern void core_dump (void);
--- /dev/null
+/* $Id$ */
+
+
+#define N_ELEMENTS(arr) (sizeof (arr) / sizeof ((arr)[0]))
+
+#define INTERNAL __attribute__ ((__visibility__ ("internal")))
--- /dev/null
+#! /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
--- /dev/null
+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);
--- /dev/null
+/* $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
--- /dev/null
+/* $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;
--- /dev/null
+/* $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
--- /dev/null
+/* $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
--- /dev/null
+/* $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;
+}
--- /dev/null
+/* $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 */
--- /dev/null
+# $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.
--- /dev/null
+/* $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;
+ }
+}
--- /dev/null
+/* $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);
--- /dev/null
+/* $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 */
--- /dev/null
+/* $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
--- /dev/null
+/* $Id$ */
+
+
+int main (void)
+{
+ *(int *) 0 = 0;
+ return 0;
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}