]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
libelf: Replace list of elf_getdata_rawchunk results with a tree
authorMark Wielaard <mark@klomp.org>
Wed, 21 Jun 2023 16:05:12 +0000 (18:05 +0200)
committerMark Wielaard <mark@klomp.org>
Tue, 27 Jun 2023 14:05:28 +0000 (16:05 +0200)
elf_getdata_rawchunks did a linear search to see if a chunk was
already fetched. Replace this list with a binary search tree to make
lookup faster when a lot of Elf_Data_Chunk were created.

       * libelf/libelfP.h (Elf_Data_Chunk): Remove next field.
       (struct Elf): Change the rawchunks type from Elf_Data_Chunk *
       to void *.
       * elf_getdata_rawchunk.c (chunk_compare): New static function.
       (elf_getdata_rawchunk): Use tsearch instead of a manual linked
       list.
       * elf_end.c (free_chunk): New static function.
       (elf_end): Call tdestroy instead of walking linked list.

Signed-off-by: Mark Wielaard <mark@klomp.org>
libelf/elf_end.c
libelf/elf_getdata_rawchunk.c
libelf/libelfP.h

index 5c451f36daddd6ac33a43fd69911e1af6a6b4e92..3e5d4c86734650be9433c7cbbb63fcc775849ec2 100644 (file)
@@ -1,5 +1,6 @@
 /* Free resources associated with Elf descriptor.
    Copyright (C) 1998,1999,2000,2001,2002,2004,2005,2007,2015,2016 Red Hat, Inc.
+   Copyright (C) 2023 Mark J. Wielaard <mark@klomp.org>
    This file is part of elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 1998.
 
 #endif
 
 #include <assert.h>
+#include <search.h>
 #include <stddef.h>
 #include <stdlib.h>
 
 #include "libelfP.h"
 
 
+static void
+free_chunk (void *n)
+{
+  Elf_Data_Chunk *rawchunk = (Elf_Data_Chunk *)n;
+  if (rawchunk->dummy_scn.flags & ELF_F_MALLOCED)
+    free (rawchunk->data.d.d_buf);
+  free (rawchunk);
+}
+
 int
 elf_end (Elf *elf)
 {
@@ -112,20 +123,13 @@ elf_end (Elf *elf)
 
     case ELF_K_ELF:
       {
-       Elf_Data_Chunk *rawchunks
+       void *rawchunks
          = (elf->class == ELFCLASS32
             || (offsetof (struct Elf, state.elf32.rawchunks)
                 == offsetof (struct Elf, state.elf64.rawchunks))
             ? elf->state.elf32.rawchunks
             : elf->state.elf64.rawchunks);
-       while (rawchunks != NULL)
-         {
-           Elf_Data_Chunk *next = rawchunks->next;
-           if (rawchunks->dummy_scn.flags & ELF_F_MALLOCED)
-             free (rawchunks->data.d.d_buf);
-           free (rawchunks);
-           rawchunks = next;
-         }
+       tdestroy (rawchunks, free_chunk);
 
        Elf_ScnList *list = (elf->class == ELFCLASS32
                             || (offsetof (struct Elf, state.elf32.scns)
index 5a35ccdc6b2bf8cc80075448ad8b515be97ddb67..cfd4039639c9fc9410f4c6c1d3d2322feb1ccb9d 100644 (file)
@@ -1,6 +1,6 @@
 /* Return converted data from raw chunk of ELF file.
    Copyright (C) 2007, 2014, 2015 Red Hat, Inc.
-   Copyright (C) 2022 Mark J. Wielaard <mark@klomp.org>
+   Copyright (C) 2022, 2023 Mark J. Wielaard <mark@klomp.org>
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
 
 #include <assert.h>
 #include <errno.h>
+#include <search.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "libelfP.h"
 #include "common.h"
 
+static int
+chunk_compare (const void *a, const void *b)
+{
+  Elf_Data_Chunk *da = (Elf_Data_Chunk *)a;
+  Elf_Data_Chunk *db = (Elf_Data_Chunk *)b;
+
+  if (da->offset != db->offset)
+    return da->offset - db->offset;
+
+  if (da->data.d.d_size != db->data.d.d_size)
+    return da->data.d.d_size - db->data.d.d_size;
+
+  return da->data.d.d_type - db->data.d.d_type;
+}
+
 Elf_Data *
 elf_getdata_rawchunk (Elf *elf, int64_t offset, size_t size, Elf_Type type)
 {
@@ -75,19 +91,25 @@ elf_getdata_rawchunk (Elf *elf, int64_t offset, size_t size, Elf_Type type)
   rwlock_rdlock (elf->lock);
 
   /* Maybe we already got this chunk?  */
-  Elf_Data_Chunk *rawchunks = elf->state.elf.rawchunks;
-  while (rawchunks != NULL)
+  Elf_Data_Chunk key;
+  key.offset = offset;
+  key.data.d.d_size = size;
+  key.data.d.d_type = type;
+  Elf_Data_Chunk **found = tsearch (&key, &elf->state.elf.rawchunks,
+                                   &chunk_compare);
+  if (found == NULL)
+    goto nomem;
+
+  /* Existing entry.  */
+  if (*found != &key && *found != NULL)
     {
-      if ((rawchunks->offset == offset || size == 0)
-         && rawchunks->data.d.d_size == size
-         && rawchunks->data.d.d_type == type)
-       {
-         result = &rawchunks->data.d;
-         goto out;
-       }
-      rawchunks = rawchunks->next;
+      result = &(*found)->data.d;
+      goto out;
     }
 
+  /* New entry.  */
+  *found = NULL;
+
   size_t align = __libelf_type_align (elf->class, type);
   if (elf->map_address != NULL)
     {
@@ -189,8 +211,7 @@ elf_getdata_rawchunk (Elf *elf, int64_t offset, size_t size, Elf_Type type)
   rwlock_unlock (elf->lock);
   rwlock_wrlock (elf->lock);
 
-  chunk->next = elf->state.elf.rawchunks;
-  elf->state.elf.rawchunks = chunk;
+  *found = chunk;
   result = &chunk->data.d;
 
  out:
index 6624f38a25460f7ee4aa6175935ea5be1f042b1a..d3c241e5509b64309c715b654611a5c6134321e4 100644 (file)
@@ -1,5 +1,6 @@
 /* Internal interfaces for libelf.
    Copyright (C) 1998-2010, 2015, 2016 Red Hat, Inc.
+   Copyright (C) 2023 Mark J. Wielaard <mark@klomp.org>
    This file is part of elfutils.
    Contributed by Ulrich Drepper <drepper@redhat.com>, 1998.
 
@@ -262,11 +263,7 @@ typedef struct Elf_ScnList
 typedef struct Elf_Data_Chunk
 {
   Elf_Data_Scn data;
-  union
-  {
-    Elf_Scn dummy_scn;
-    struct Elf_Data_Chunk *next;
-  };
+  Elf_Scn dummy_scn;
   int64_t offset;              /* The original raw offset in the Elf image.  */
 } Elf_Data_Chunk;
 
@@ -324,7 +321,7 @@ struct Elf
       Elf_ScnList *scns_last;  /* Last element in the section list.
                                   If NULL the data has not yet been
                                   read from the file.  */
-      Elf_Data_Chunk *rawchunks; /* List of elf_getdata_rawchunk results.  */
+      void *rawchunks;         /* Tree of elf_getdata_rawchunk results.  */
       unsigned int scnincr;    /* Number of sections allocate the last
                                   time.  */
       int ehdr_flags;          /* Flags (dirty) for ELF header.  */
@@ -343,7 +340,7 @@ struct Elf
       Elf_ScnList *scns_last;  /* Last element in the section list.
                                   If NULL the data has not yet been
                                   read from the file.  */
-      Elf_Data_Chunk *rawchunks; /* List of elf_getdata_rawchunk results.  */
+      void *rawchunks;         /* Tree of elf_getdata_rawchunk results.  */
       unsigned int scnincr;    /* Number of sections allocate the last
                                   time.  */
       int ehdr_flags;          /* Flags (dirty) for ELF header.  */
@@ -368,7 +365,7 @@ struct Elf
       Elf_ScnList *scns_last;  /* Last element in the section list.
                                   If NULL the data has not yet been
                                   read from the file.  */
-      Elf_Data_Chunk *rawchunks; /* List of elf_getdata_rawchunk results.  */
+      void *rawchunks;         /* Tree of elf_getdata_rawchunk results.  */
       unsigned int scnincr;    /* Number of sections allocate the last
                                   time.  */
       int ehdr_flags;          /* Flags (dirty) for ELF header.  */