Hi Mark,

On Wed, Sep 9, 2026 at 3:57 PM Mark Wielaard <[email protected]> wrote:
>
> Hi Aaron,
>
> On Mon, Aug 31, 2026 at 06:46:47PM -0400, Aaron Merey wrote:
> > 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.
>
> OK, so this and the previous patch work together.
>
> > 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]);
>
> So you need to store
> elf->state.ELFW(elf,LIBELFBITS).scns.data[0].shdr... atomicly, but do
> you need to set the others also atomic?

Yes because one thread may try to load particular shdr from the
gelf_getshdr hot path while another thread is performing this store.

>
> Would it make sense to set scns.data[0] last? So another check doesn't see
> it set before all other shdrs have been set?

That would be better, I'll add it to v2.

>
> Where in the code/call path is scns.cnt set?
> Asking this question might show I don't fully grok what is going on.

scns.cnt is set during elf_begin and can be modified by elf_newscn,
elfNN_newphdr and elf_getscn. elf_begin runs before the relevant elf
descriptor can be used by multiple threads. The THREAD-SAFETY doc
lists elf_newscn and elfNN_newphdr as caller-serialized functions so
they must not run concurrently with elfNN/gelf_getshdr. elf_getscn
uses atomics to avoid races between stores and loads (added in the
previous patch in this series). There is one non-atomic load of cnt in
elf_getscn with elf->lock wrlock held but there is no store to cnt
that can occur concurrently (either the store happens in a
caller-serialized function or when elf->lock wrlock is held or in
elf_begin when the descriptor hasn't yet been shared across threads).

Aaron

>
> >    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;
> >  }
>
> OK, the same result check is then done in getshdr_wrlock to make sure
> some other thread didn't race past us.
>
> > 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);
>
> OK, like above, 32bit case.
>
> > +       if (shdr == NULL)
> > +         {
> > +           __libelf_seterrno (ELF_E_INVALID_OPERAND);
> > +           return NULL;
> > +         }
> >       }
>
> OK, as done above (but without goto out, lock already dropped).
>
> >  #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);
>
> OK, like above, 32bit case.
>
> > +       if (shdr == NULL)
> > +         {
> > +           __libelf_seterrno (ELF_E_INVALID_OPERAND);
> > +           return NULL;
> > +         }
> >       }
>
> OK, as done above (but without goto out, lock already dropped).
>
> >
> >        /* 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)
>
> Ack, lock/unlock pairs are much smaller now.
>
> Cheers,
>
> Mark
>

Reply via email to