Patchwork libbacktrace: walk the elf_syminfo_data chain in elf_syminfo

login
register
mail settings
Submitter Alexander Monakov
Date July 22, 2013, 6:20 p.m.
Message ID <alpine.LNX.2.00.1307222213520.1061@monopod.intra.ispras.ru>
Download mbox | patch
Permalink /patch/260786/
State New
Headers show

Comments

Alexander Monakov - July 22, 2013, 6:20 p.m.
On Mon, 22 Jul 2013, Ian Lance Taylor wrote:
> Thanks for noticing the problem.  This patch isn't enough by itself.
> The code has to protect itself against the list changing in
> mid-stream.  See dwarf_fileline in dwarf.c.

Thank you.  Here's the updated patch, however I have to admit it's not
entirely clear to me what __sync_bool_compare_and_swap should achieve.  Is it
only to ensure that we do not use a partially updated pointer (which shouldn't
happen with a naturally aligned pointer on hardware that updates cache lines
atomically)?

2013-07-22  Alexander Monakov  <amonakov@ispras.ru>

	* elf.c (elf_syminfo): Loop over the elf_syminfo_data chain.
Ian Taylor - July 22, 2013, 8:23 p.m.
On Mon, Jul 22, 2013 at 11:20 AM, Alexander Monakov <amonakov@ispras.ru> wrote:
> On Mon, 22 Jul 2013, Ian Lance Taylor wrote:
>> Thanks for noticing the problem.  This patch isn't enough by itself.
>> The code has to protect itself against the list changing in
>> mid-stream.  See dwarf_fileline in dwarf.c.
>
> Thank you.  Here's the updated patch, however I have to admit it's not
> entirely clear to me what __sync_bool_compare_and_swap should achieve.  Is it
> only to ensure that we do not use a partially updated pointer (which shouldn't
> happen with a naturally aligned pointer on hardware that updates cache lines
> atomically)?

We not only need to get a valid pointer, we need to ensure when thread
T1 creates the pointer and thread T2 loads the pointer, thread T2 sees
all the global stores that thread T1 made before T1 set the pointer.
That is, we need an acquire-load.  The hope here is that any sane
implementation of __sync_bool_compare_and_swap would ensure sequential
consistency between T1 and T2, implying an acquire-load.  On x86 every
load is an acquire-load, but that is not true on other processors.

If we added some configure tests, or didn't worry about letting people
use this library with older versions of GCC, we could use
__atomic_load_n (pp, __ATOMIC_ACQUIRE) here, and use something like
__atomic_compare_exchange_n (pp, NULL, edata, true, __ATOMIC_RELEASE,
__ATOMIC_RELAXED) in elf_add_syminfo_data.  I don't think it would
make any difference on x86, though.

> 2013-07-22  Alexander Monakov  <amonakov@ispras.ru>
>
>         * elf.c (elf_syminfo): Loop over the elf_syminfo_data chain.


> +      for (edata = (struct elf_syminfo_data *) state->syminfo_data;
> +          edata;

Please explicitly write edata != NULL.

This is OK with that change.

Thanks.

Ian

Patch

diff --git a/libbacktrace/elf.c b/libbacktrace/elf.c
index ef9bcdf..1d64a1f 100644
--- a/libbacktrace/elf.c
+++ b/libbacktrace/elf.c
@@ -454,12 +454,46 @@  elf_syminfo (struct backtrace_state *state, uintptr_t pc,
             void *data)
 {
   struct elf_syminfo_data *edata;
-  struct elf_symbol *sym;
+  struct elf_symbol *sym = NULL;
+
+  if (!state->threaded)
+    {
+      for (edata = (struct elf_syminfo_data *) state->syminfo_data;
+          edata;
+          edata = edata->next)
+       {
+         sym = ((struct elf_symbol *)
+                bsearch (&pc, edata->symbols, edata->count,
+                         sizeof (struct elf_symbol), elf_symbol_search));
+         if (sym != NULL)
+           break;
+       }
+    }
+  else
+    {
+      struct elf_syminfo_data **pp;
+
+      pp = (struct elf_syminfo_data **) (void *) &state->syminfo_data;
+      while (1)
+       {
+         edata = *pp;
+         /* Atomic load.  */
+         while (!__sync_bool_compare_and_swap (pp, edata, edata))
+           edata = *pp;
+
+         if (edata == NULL)
+           break;
+
+         sym = ((struct elf_symbol *)
+                bsearch (&pc, edata->symbols, edata->count,
+                         sizeof (struct elf_symbol), elf_symbol_search));
+         if (sym != NULL)
+           break;
+
+         pp = &edata->next;
+       }
+    }

-  edata = (struct elf_syminfo_data *) state->syminfo_data;
-  sym = ((struct elf_symbol *)
-        bsearch (&pc, edata->symbols, edata->count,
-                 sizeof (struct elf_symbol), elf_symbol_search));
   if (sym == NULL)
     callback (data, pc, NULL, 0);
   else