Atomically check whether the target shdr has already been loaded.
This reduces scn->elf->lock overhead.  If the shdr is not loaded,
then use scn->elf->lock only for the one-time lazy loading of the
shdr.

Signed-off-by: Aaron Merey <[email protected]>
---
 libelf/elf32_getshdr.c | 15 ++++++++++-----
 libelf/gelf_getshdr.c  | 33 ++++++++++++++++++++-------------
 2 files changed, 30 insertions(+), 18 deletions(-)

diff --git a/libelf/elf32_getshdr.c b/libelf/elf32_getshdr.c
index e4bebe18..c3d46671 100644
--- a/libelf/elf32_getshdr.c
+++ b/libelf/elf32_getshdr.c
@@ -198,8 +198,9 @@ load_shdr_wrlock (Elf_Scn *scn)
 
   /* Set the pointers in the `scn's.  */
   for (size_t cnt = 0; cnt < shnum; ++cnt)
-    elf->state.ELFW(elf,LIBELFBITS).scns.data[cnt].shdr.ELFW(e,LIBELFBITS)
-      = &elf->state.ELFW(elf,LIBELFBITS).shdr[cnt];
+    atomic_store_release
+      (&elf->state.ELFW(elf,LIBELFBITS).scns.data[cnt].shdr.ELFW(e,LIBELFBITS),
+       &elf->state.ELFW(elf,LIBELFBITS).shdr[cnt]);
 
   result = scn->shdr.ELFW(e,LIBELFBITS);
   assert (result != NULL);
@@ -275,9 +276,13 @@ elfw2(LIBELFBITS,getshdr) (Elf_Scn *scn)
   if (!scn_valid (scn))
     return NULL;
 
-  rwlock_rdlock (scn->elf->lock);
-  result = __elfw2(LIBELFBITS,getshdr_rdlock) (scn);
-  rwlock_unlock (scn->elf->lock);
+  result = atomic_load_acquire (&scn->shdr.ELFW(e,LIBELFBITS));
+  if (result == NULL)
+    {
+      rwlock_wrlock (scn->elf->lock);
+      result = __elfw2(LIBELFBITS,getshdr_wrlock) (scn);
+      rwlock_unlock (scn->elf->lock);
+    }
 
   return result;
 }
diff --git a/libelf/gelf_getshdr.c b/libelf/gelf_getshdr.c
index 3858c8e1..2b16e502 100644
--- a/libelf/gelf_getshdr.c
+++ b/libelf/gelf_getshdr.c
@@ -51,18 +51,22 @@ gelf_getshdr (Elf_Scn *scn, GElf_Shdr *dst)
       return NULL;
     }
 
-  rwlock_rdlock (scn->elf->lock);
-
   if (scn->elf->class == ELFCLASS32)
     {
       /* Copy the elements one-by-one.  */
-      Elf32_Shdr *shdr
-       = scn->shdr.e32 ?: __elf32_getshdr_rdlock (scn);
+      Elf32_Shdr *shdr = atomic_load_acquire (&scn->shdr.e32);
 
       if (shdr == NULL)
        {
-         __libelf_seterrno (ELF_E_INVALID_OPERAND);
-         goto out;
+         rwlock_wrlock (scn->elf->lock);
+         shdr = __elf32_getshdr_wrlock (scn);
+         rwlock_unlock (scn->elf->lock);
+
+         if (shdr == NULL)
+           {
+             __libelf_seterrno (ELF_E_INVALID_OPERAND);
+             return NULL;
+           }
        }
 
 #define COPY(name) \
@@ -82,22 +86,25 @@ gelf_getshdr (Elf_Scn *scn, GElf_Shdr *dst)
     }
   else
     {
-      Elf64_Shdr *shdr
-       = scn->shdr.e64 ?: __elf64_getshdr_rdlock (scn);
+      Elf64_Shdr *shdr = atomic_load_acquire (&scn->shdr.e64);
 
       if (shdr == NULL)
        {
-         __libelf_seterrno (ELF_E_INVALID_OPERAND);
-         goto out;
+         rwlock_wrlock (scn->elf->lock);
+         shdr = __elf64_getshdr_wrlock (scn);
+         rwlock_unlock (scn->elf->lock);
+
+         if (shdr == NULL)
+           {
+             __libelf_seterrno (ELF_E_INVALID_OPERAND);
+             return NULL;
+           }
        }
 
       /* We only have to copy the data.  */
       result = memcpy (dst, shdr, sizeof (GElf_Shdr));
     }
 
- out:
-  rwlock_unlock (scn->elf->lock);
-
   return result;
 }
 INTDEF(gelf_getshdr)
-- 
2.55.0

Reply via email to