[PATCH, take 3] Add static probes to runtime linker
Gary Benson
gbenson@redhat.com
Thu Jul 12 12:51:00 GMT 2012
Hi all,
This patch adds a number of static probes to the runtime linker,
allowing debuggers to more finely control where they stop and
passing more information so as to allow incremental loading of
the list of loaded libraries, and also to allow debugging of
programs that use dlmopen. The GDB side of this patch, along
with a more detailed description of the issues I am attempting
to solve is at [1].
This patch is a resubmission of [2], the differences being:
- The "map_complete" and "reloc_complete" probes have an
extra argument, the link_map of the first newly added
library, to allow the list to be loaded incrementally.
- The "map_complete" probe in dl-load.c has been renamed
as "map_failed" to allow debuggers to differentiate the
two cases.
- The documentation has been updated to reflect these
changes.
How does this look?
Thanks,
Gary
[1] http://www.cygwin.com/ml/gdb-patches/2012-07/msg00155.html
[2] http://sourceware.org/ml/libc-alpha/2012-07/msg00096.html
--
http://gbenson.net/
-------------- next part --------------
2012-07-12 Gary Benson <gbenson@redhat.com>
[BZ #14298]
* elf/rtld.c: Include <stap-probe.h>.
(dl_main): Added static probes "init_start" and "init_complete".
* elf/dl-load.c: Include <stap-probe.h>.
(lose): Take new parameter "nsid".
Added static probe "map_failed".
(_dl_map_object_from_fd): Pass namespace id to lose.
Added static probe "map_start".
(open_verify): Pass namespace id to lose.
* elf/dl-open.c: Include <stap-probe.h>.
(dl_open_worker) Added static probes "map_complete", "reloc_start"
and "reloc_complete".
* elf/dl-close.c: Include <stap-probe.h>.
(_dl_close_worker): Added static probes "unmap_start" and
"unmap_complete".
* elf/rtld-debugger-interface.txt: New file documenting the above.
diff --git a/elf/rtld-debugger-interface.txt b/elf/rtld-debugger-interface.txt
new file mode 100644
index 0000000..61bc99e
--- /dev/null
+++ b/elf/rtld-debugger-interface.txt
@@ -0,0 +1,122 @@
+Standard debugger interface
+===========================
+
+The run-time linker exposes a rendezvous structure to allow debuggers
+to interface with it. This structure, r_debug, is defined in link.h.
+If the executable's dynamic section has a DT_DEBUG element, the
+run-time linker sets that element's value to the address where this
+structure can be found.
+
+The r_debug structure contains (amongst others) the following fields:
+
+ struct link_map *r_map:
+ A linked list of loaded objects.
+
+ enum { RT_CONSISTENT, RT_ADD, RT_DELETE } r_state:
+ The current state of the r_map list. RT_CONSISTENT means that r_map
+ is not currently being modified and may safely be inspected. RT_ADD
+ means that an object is being added to r_map, and that the list is
+ not guaranteed to be consistent. Likewise RT_DELETE means that an
+ object is being removed from the list.
+
+ ElfW(Addr) r_brk:
+ The address of a function internal to the run-time linker which is
+ called whenever r_state is changed. The debugger should set a
+ breakpoint at this address if it wants to notice mapping changes.
+
+This protocol is widely supported, but somewhat limited in that it
+has no provision to provide access to multiple namespaces, and that
+the notifications (via r_brk) only refer to changes to r_map--the
+debugger is notified that a new object has been added, for instance,
+but there is no way for the debugger to discover whether any of the
+objects in the link-map have been relocated or not.
+
+
+Probe-based debugger interface
+==============================
+
+Systemtap is a dynamic tracing/instrumenting tool available on Linux.
+Probes that are not fired at run time have close to zero overhead.
+glibc contains a number of probes that debuggers can set breakpoints
+on in order to notice certain events.
+
+All rtld probes have the following arguments:
+
+ arg1: Lmid_t lmid:
+ The link-map ID of the link-map list that the object was loaded
+ into. This will be LM_ID_BASE for the application's main link-map
+ list, or some other value for different namespaces.
+
+ arg2: struct r_debug *r_debug:
+ A pointer to the r_debug structure containing the link-map list
+ that the object was loaded into. This will be the value stored in
+ DT_DEBUG for the application's main link-map list, or some other
+ value for different namespaces.
+
+map_complete and reloc_complete may have the following additional
+argument:
+
+ arg3: struct link_map *new:
+ A pointer which, if not NULL, points to the entry in the specified
+ r_debug structure's link-map list corresponding to the first new
+ object to have been mapped or relocated, with new->l_next pointing
+ to the link-map of the next new object to have been mapped or
+ relocated, and so on. Note that because `new' is an entry in a
+ larger list, new->l_prev (if not NULL) will point to what was the
+ last link-map in the link-map list prior to the new objects being
+ mapped or relocated.
+
+The following probes are available:
+
+ init_start:
+ This is called once, when the linker is about to fill in the main
+ r_debug structure at application startup. init_start always has
+ lmid set to LM_ID_BASE and r_debug set to the value stored in
+ DT_DEBUG. r_debug is not guaranteed to be consistent until
+ init_complete is fired.
+
+ init_complete:
+ This is called once, when the linker has filled in the main
+ r_debug structure at application startup. init_complete always
+ has lmid set to LM_ID_BASE and r_debug set to the value stored
+ in DT_DEBUG. The r_debug structure is consistent and may be
+ inspected, and all objects in the link-map are guaranteed to
+ have been relocated.
+
+ map_start:
+ The linker is about to map new objects into the specified
+ namespace. The namespace's r_debug structure is not guaranteed
+ to be consistent until a corresponding map_complete is fired.
+
+ map_complete:
+ The linker has finished mapping new objects into the specified
+ namespace. The namespace's r_debug structure is consistent and
+ may be inspected, although objects in the namespace's link-map
+ are not guaranteed to have been relocated.
+
+ map_failed:
+ The linker failed while attempting to map new objects into
+ the specified namespace. The namespace's r_debug structure
+ is consistent and may be inspected.
+
+ reloc_start:
+ The linker is about to relocate all unrelocated objects in the
+ specified namespace. The namespace's r_debug structure is not
+ guaranteed to be consistent until a corresponding reloc_complete
+ is fired.
+
+ reloc_complete:
+ The linker has relocated all objects in the specified namespace.
+ The namespace's r_debug structure is consistent and may be
+ inspected, and all objects in the namespace's link-map are
+ guaranteed to have been relocated.
+
+ unmap_start:
+ The linker is about to remove objects from the specified
+ namespace. The namespace's r_debug structure is not guaranteed to
+ be consistent until a corresponding unmap_complete is fired.
+
+ unmap_complete:
+ The linker has finished removing objects into the specified
+ namespace. The namespace's r_debug structure is consistent and
+ may be inspected.
diff --git a/elf/rtld.c b/elf/rtld.c
index 6bcf224..06c4220 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -39,6 +39,7 @@
#include <dl-osinfo.h>
#include <dl-procinfo.h>
#include <tls.h>
+#include <stap-probe.h>
#include <stackinfo.h>
#include <assert.h>
@@ -1683,6 +1684,7 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
/* We start adding objects. */
r->r_state = RT_ADD;
_dl_debug_state ();
+ LIBC_PROBE (init_start, 2, LM_ID_BASE, r);
/* Auditing checkpoint: we are ready to signal that the initial map
is being constructed. */
@@ -2402,6 +2404,7 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
r = _dl_debug_initialize (0, LM_ID_BASE);
r->r_state = RT_CONSISTENT;
_dl_debug_state ();
+ LIBC_PROBE (init_complete, 2, LM_ID_BASE, r);
#ifndef MAP_COPY
/* We must munmap() the cache file. */
diff --git a/elf/dl-load.c b/elf/dl-load.c
index fe83f87..43e1269 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -35,6 +35,7 @@
#include <stackinfo.h>
#include <caller.h>
#include <sysdep.h>
+#include <stap-probe.h>
#include <dl-dst.h>
@@ -882,7 +883,7 @@ _dl_init_paths (const char *llp)
static void
__attribute__ ((noreturn, noinline))
lose (int code, int fd, const char *name, char *realname, struct link_map *l,
- const char *msg, struct r_debug *r)
+ const char *msg, struct r_debug *r, Lmid_t nsid)
{
/* The file might already be closed. */
if (fd != -1)
@@ -896,6 +897,7 @@ lose (int code, int fd, const char *name, char *realname, struct link_map *l,
{
r->r_state = RT_CONSISTENT;
_dl_debug_state ();
+ LIBC_PROBE (map_failed, 2, nsid, r);
}
_dl_signal_error (code, name, NULL, msg);
@@ -934,7 +936,7 @@ _dl_map_object_from_fd (const char *name, int fd, struct filebuf *fbp,
errval = errno;
call_lose:
lose (errval, fd, name, realname, l, errstring,
- make_consistent ? r : NULL);
+ make_consistent ? r : NULL, nsid);
}
/* Look again to see if the real name matched another already loaded. */
@@ -1041,6 +1043,7 @@ _dl_map_object_from_fd (const char *name, int fd, struct filebuf *fbp,
linking has not been used before. */
r->r_state = RT_ADD;
_dl_debug_state ();
+ LIBC_PROBE (map_start, 2, nsid, r);
make_consistent = true;
}
else
@@ -1736,7 +1739,7 @@ open_verify (const char *name, struct filebuf *fbp, struct link_map *loader,
name = strdupa (realname);
free (realname);
}
- lose (errval, fd, name, NULL, NULL, errstring, NULL);
+ lose (errval, fd, name, NULL, NULL, errstring, NULL, 0);
}
/* See whether the ELF header is what we expect. */
diff --git a/elf/dl-open.c b/elf/dl-open.c
index 9fe0a7f..9c788c2 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -32,6 +32,7 @@
#include <caller.h>
#include <sysdep-cancel.h>
#include <tls.h>
+#include <stap-probe.h>
#include <dl-dst.h>
@@ -291,6 +292,7 @@ dl_open_worker (void *a)
struct r_debug *r = _dl_debug_initialize (0, args->nsid);
r->r_state = RT_CONSISTENT;
_dl_debug_state ();
+ LIBC_PROBE (map_complete, 3, args->nsid, r, new);
/* Print scope information. */
if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_SCOPES, 0))
@@ -376,10 +378,19 @@ dl_open_worker (void *a)
}
}
+ int relocation_in_progress = 0;
+
for (size_t i = nmaps; i-- > 0; )
{
l = maps[i];
+ if (! relocation_in_progress)
+ {
+ /* Notify the debugger that relocations are about to happen. */
+ LIBC_PROBE (reloc_start, 2, args->nsid, r);
+ relocation_in_progress = 1;
+ }
+
#ifdef SHARED
if (__builtin_expect (GLRO(dl_profile) != NULL, 0))
{
@@ -544,6 +555,10 @@ cannot load any more object with static TLS"));
}
}
+ /* Notify the debugger all new objects have been relocated. */
+ if (relocation_in_progress)
+ LIBC_PROBE (reloc_complete, 3, args->nsid, r, new);
+
/* Run the initializer functions of new objects. */
_dl_init (new, args->argc, args->argv, args->env);
diff --git a/elf/dl-close.c b/elf/dl-close.c
index a250ea5..45b2187 100644
--- a/elf/dl-close.c
+++ b/elf/dl-close.c
@@ -31,6 +31,7 @@
#include <sys/mman.h>
#include <sysdep-cancel.h>
#include <tls.h>
+#include <stap-probe.h>
/* Type of the constructor functions. */
@@ -468,6 +469,7 @@ _dl_close_worker (struct link_map *map)
struct r_debug *r = _dl_debug_initialize (0, nsid);
r->r_state = RT_DELETE;
_dl_debug_state ();
+ LIBC_PROBE (unmap_start, 2, nsid, r);
if (unload_global)
{
@@ -737,6 +739,7 @@ _dl_close_worker (struct link_map *map)
/* Notify the debugger those objects are finalized and gone. */
r->r_state = RT_CONSISTENT;
_dl_debug_state ();
+ LIBC_PROBE (unmap_complete, 2, nsid, r);
/* Recheck if we need to retry, release the lock. */
out:
More information about the Libc-alpha
mailing list