Move cache_sections' reloc_info initialization code into new function
cache_sections_locked, which cache_sections calls while holding
dwfl->load_lock.

Atomics are used to acquire intitialized reloc_info without needing to
obtain the load_lock.

Signed-off-by: Aaron Merey <[email protected]>
---
 libdwfl/derelocate.c | 53 ++++++++++++++++++++++++++++++++------------
 1 file changed, 39 insertions(+), 14 deletions(-)

diff --git a/libdwfl/derelocate.c b/libdwfl/derelocate.c
index 2f80b20f..f9dbbe55 100644
--- a/libdwfl/derelocate.c
+++ b/libdwfl/derelocate.c
@@ -77,13 +77,11 @@ compare_secrefs (const void *a, const void *b)
 }
 
 static int
-cache_sections (Dwfl_Module *mod)
+cache_sections_locked (Dwfl_Module *mod, struct dwfl_relocation **result)
 {
-  if (likely (mod->reloc_info != NULL))
-    return mod->reloc_info->count;
-
   struct secref *refs = NULL;
   size_t nrefs = 0;
+  struct dwfl_relocation *reloc_info = NULL;
 
   size_t shstrndx;
   if (unlikely (elf_getshdrstrndx (mod->main.elf, &shstrndx) < 0))
@@ -165,8 +163,8 @@ cache_sections (Dwfl_Module *mod)
        }
     }
 
-  mod->reloc_info = malloc (offsetof (struct dwfl_relocation, refs[nrefs]));
-  if (unlikely (mod->reloc_info == NULL))
+  reloc_info = malloc (offsetof (struct dwfl_relocation, refs[nrefs]));
+  if (unlikely (reloc_info == NULL))
     goto nomem;
 
   struct secref **sortrefs = malloc (nrefs * sizeof sortrefs[0]);
@@ -179,14 +177,14 @@ cache_sections (Dwfl_Module *mod)
 
   qsort (sortrefs, nrefs, sizeof sortrefs[0], &compare_secrefs);
 
-  mod->reloc_info->count = nrefs;
+  reloc_info->count = nrefs;
   for (size_t i = 0; i < nrefs; ++i)
     {
-      mod->reloc_info->refs[i].name = sortrefs[i]->name;
-      mod->reloc_info->refs[i].scn = sortrefs[i]->scn;
-      mod->reloc_info->refs[i].relocs = sortrefs[i]->relocs;
-      mod->reloc_info->refs[i].start = sortrefs[i]->start;
-      mod->reloc_info->refs[i].end = sortrefs[i]->end;
+      reloc_info->refs[i].name = sortrefs[i]->name;
+      reloc_info->refs[i].scn = sortrefs[i]->scn;
+      reloc_info->refs[i].relocs = sortrefs[i]->relocs;
+      reloc_info->refs[i].start = sortrefs[i]->start;
+      reloc_info->refs[i].end = sortrefs[i]->end;
       free (sortrefs[i]);
     }
 
@@ -212,16 +210,20 @@ cache_sections (Dwfl_Module *mod)
              Elf_Scn *tscn = elf_getscn (mod->main.elf, shdr->sh_info);
              if (likely (tscn != NULL))
                for (size_t i = 0; i < nrefs; ++i)
-                 if (mod->reloc_info->refs[i].scn == tscn)
+                 if (reloc_info->refs[i].scn == tscn)
                    {
-                     mod->reloc_info->refs[i].relocs = scn;
+                     reloc_info->refs[i].relocs = scn;
                      break;
                    }
            }
        }
     }
 
+  *result = reloc_info;
+  reloc_info = NULL;
+
 free_refs:
+  free (reloc_info);
   while (refs != NULL)
     {
       struct secref *ref = refs;
@@ -232,6 +234,29 @@ free_refs:
   return nrefs;
 }
 
+static int
+cache_sections (Dwfl_Module *mod)
+{
+  if (likely (atomic_load_acquire (&mod->reloc_info) != NULL))
+    return mod->reloc_info->count;
+
+  int count;
+  mutex_lock (mod->dwfl->load_lock);
+
+  if (mod->reloc_info == NULL)
+    {
+      struct dwfl_relocation *reloc_info = NULL;
+      count = cache_sections_locked (mod, &reloc_info);
+      if (count >= 0)
+       atomic_store_release (&mod->reloc_info, reloc_info);
+    }
+  else
+    count = mod->reloc_info->count;
+
+  mutex_unlock (mod->dwfl->load_lock);
+  return count;
+}
+
 
 int
 dwfl_module_relocations (Dwfl_Module *mod)
-- 
2.56.0

Reply via email to