diff mbox series

[08/14] finalize elf segments on a relocation step

Message ID 20230518082854.3903342-9-stsp2@yandex.ru
State New
Headers show
Series implement RTLD_NORELOCATE api [BZ #30007] | expand

Commit Message

stsp May 18, 2023, 8:28 a.m. UTC
This is needed so that the segment finalization can be delayed,
together with the relocation process.

The test-suite was run on x86_64/64 and showed no regressions.

Signed-off-by: Stas Sergeev <stsp2@yandex.ru>
---
 elf/dl-close.c        |  1 +
 elf/dl-load.c         | 33 ++++++++++++++++++++++-----------
 elf/dl-load.h         |  2 +-
 elf/dl-map-segments.h |  7 ++++++-
 elf/dl-open.c         | 10 +++++++++-
 include/link.h        |  5 ++++-
 6 files changed, 43 insertions(+), 15 deletions(-)
diff mbox series

Patch

diff --git a/elf/dl-close.c b/elf/dl-close.c
index 48ac3663ec..0640fc0bec 100644
--- a/elf/dl-close.c
+++ b/elf/dl-close.c
@@ -691,6 +691,7 @@  _dl_close_worker (struct link_map *map, bool force)
 	    GL(dl_initfirst) = NULL;
 
 	  free (imap->l_dlopen_args);
+	  free (imap->l_loadcmds);
 
 	  free (imap);
 	}
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 28e582fbdf..31514fca84 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -950,6 +950,7 @@  _dl_map_object_from_fd (const char *name, const char *origname, int fd,
   int type;
   /* Initialize to keep the compiler happy.  */
   const char *errstring = NULL;
+  struct loadcmd *loadcmds = NULL;
   int errval = 0;
 
   /* Get file information.  To match the kernel behavior, do not fill
@@ -969,6 +970,8 @@  _dl_map_object_from_fd (const char *name, const char *origname, int fd,
 	lose_errno:
 	  errval = errno;
 	lose:
+	  if (loadcmds)
+	    free (loadcmds);
 	  /* The file might already be closed.  */
 	  if (fd != -1)
 	    __close_nocancel (fd);
@@ -1080,18 +1083,23 @@  _dl_map_object_from_fd (const char *name, const char *origname, int fd,
 	}
     }
 
-   /* On most platforms presume that PT_GNU_STACK is absent and the stack is
-    * executable.  Other platforms default to a nonexecutable stack and don't
-    * need PT_GNU_STACK to do so.  */
-   unsigned int stack_flags = DEFAULT_STACK_PERMS;
+  /* On most platforms presume that PT_GNU_STACK is absent and the stack is
+   * executable.  Other platforms default to a nonexecutable stack and don't
+   * need PT_GNU_STACK to do so.  */
+  unsigned int stack_flags = DEFAULT_STACK_PERMS;
 
   {
     /* Scan the program header table, collecting its load commands.  */
-    struct loadcmd loadcmds[l->l_phnum];
-    size_t nloadcmds = 0;
+    #define nloadcmds l->l_nloadcmds
     bool empty_dynamic = false;
     ElfW(Addr) p_align_max = 0;
 
+    loadcmds = (struct loadcmd *) malloc (sizeof (struct loadcmd) * l->l_phnum);
+    if (loadcmds == NULL)
+      goto lose;
+    l->l_loadcmds = loadcmds;
+    nloadcmds = 0;
+
     /* The struct is initialized to zero so this is not necessary:
     l->l_ld = 0;
     l->l_phdr = 0;
@@ -1225,8 +1233,7 @@  _dl_map_object_from_fd (const char *name, const char *origname, int fd,
       }
 
     /* Align all PT_LOAD segments to the maximum p_align.  */
-    for (size_t i = 0; i < nloadcmds; i++)
-      loadcmds[i].mapalign = p_align_max;
+    l->l_map_align = p_align_max;
 
     /* dlopen of an executable is not valid because it is not possible
        to perform proper relocations, handle static TLS, or run the
@@ -1263,9 +1270,13 @@  _dl_map_object_from_fd (const char *name, const char *origname, int fd,
 	l->l_map_start = l->l_map_end = 0;
 	goto lose;
       }
-    errstring = _dl_finalize_segments (l, type, loadcmds, nloadcmds);
-    if (__glibc_unlikely (errstring != NULL))
-      goto lose;
+    /* dlopen()ed solibs are finalized on a relocation step. */
+    if (!(mode & __RTLD_DLOPEN))
+      {
+        errstring = _dl_finalize_segments (l, type, loadcmds, nloadcmds);
+        if (__glibc_unlikely (errstring != NULL))
+	  goto lose;
+      }
   }
 
   if (l->l_ld != 0)
diff --git a/elf/dl-load.h b/elf/dl-load.h
index 61f5c4fadf..555eed176d 100644
--- a/elf/dl-load.h
+++ b/elf/dl-load.h
@@ -75,7 +75,7 @@  ELF_PREFERRED_ADDRESS_DATA;
    Its details have been expanded out and converted.  */
 struct loadcmd
 {
-  ElfW(Addr) mapstart, mapend, dataend, allocend, mapalign, maphole;
+  ElfW(Addr) mapstart, mapend, dataend, allocend, maphole;
   ElfW(Off) mapoff;
   int prot;                             /* PROT_* bits.  */
 };
diff --git a/elf/dl-map-segments.h b/elf/dl-map-segments.h
index da6b762bca..a0078a77c8 100644
--- a/elf/dl-map-segments.h
+++ b/elf/dl-map-segments.h
@@ -100,7 +100,7 @@  _dl_map_segments (struct link_map *l, int fd,
            - MAP_BASE_ADDR (l));
 
       /* Remember which part of the address space this object uses.  */
-      l->l_map_start = _dl_map_segment (mappref, maplength, c->mapalign);
+      l->l_map_start = _dl_map_segment (mappref, maplength, l->l_map_align);
       if (__glibc_unlikely ((void *) l->l_map_start == MAP_FAILED))
         return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT;
 
@@ -150,6 +150,9 @@  _dl_finalize_segments (struct link_map *l, int type,
 {
   const struct loadcmd *c = loadcmds;
 
+  if (l->l_map_completed)
+    return NULL;
+
   while (c < &loadcmds[nloadcmds])
     {
       ElfW(Addr) hole_start, hole_size;
@@ -234,5 +237,7 @@  _dl_finalize_segments (struct link_map *l, int type,
       ++c;
     }
 
+  l->l_map_completed = 1;
+
   return NULL;
 }
diff --git a/elf/dl-open.c b/elf/dl-open.c
index e553403e8b..f1f2e8d3a4 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -40,7 +40,8 @@ 
 
 #include <dl-dst.h>
 #include <dl-prop.h>
-
+#include "dl-load.h"
+#include "dl-map-segments.h"
 
 /* We must be careful not to leave us in an inconsistent state.  Thus we
    catch any error and re-raise it after cleaning up.  */
@@ -543,6 +544,8 @@  do_reloc_1 (struct link_map *new, int mode, Lmid_t nsid, bool call_ctors)
 
   for (unsigned int i = last; i-- > first; )
     {
+      const char *errstring;
+
       l = new->l_initfini[i];
 
       if (l->l_real->l_relocated)
@@ -555,6 +558,11 @@  do_reloc_1 (struct link_map *new, int mode, Lmid_t nsid, bool call_ctors)
 	  relocation_in_progress = 1;
 	}
 
+      errstring = _dl_finalize_segments (l, ET_DYN, l->l_loadcmds,
+                                         l->l_nloadcmds);
+      if (__glibc_unlikely (errstring != NULL))
+        _dl_signal_error (EINVAL, l->l_libname->name, NULL, errstring);
+
 #ifdef SHARED
       if (__glibc_unlikely (GLRO(dl_profile) != NULL))
 	{
diff --git a/include/link.h b/include/link.h
index 03b9f82715..fa16dfa337 100644
--- a/include/link.h
+++ b/include/link.h
@@ -179,6 +179,7 @@  struct link_map
       } l_type:2;
     unsigned int l_dt_relr_ref:1; /* Nonzero if GLIBC_ABI_DT_RELR is
 				     referenced.  */
+    unsigned int l_map_completed:1; /* Nonzero if object fully mapped.  */
     unsigned int l_relocated:1;	/* Nonzero if object's relocations done.  */
     unsigned int l_init_called:1; /* Nonzero if DT_INIT function called.  */
     unsigned int l_global:1;	/* Nonzero if object in _dl_global_scope.  */
@@ -252,7 +253,7 @@  struct link_map
 
     /* Start and finish of memory map for this object.  l_map_start
        need not be the same as l_addr.  */
-    ElfW(Addr) l_map_start, l_map_end;
+    ElfW(Addr) l_map_start, l_map_end, l_map_align;
     /* End of the executable part of the mapping.  */
     ElfW(Addr) l_text_end;
 
@@ -349,6 +350,8 @@  struct link_map
     unsigned long long int l_serial;
 
     void *l_dlopen_args;
+    void *l_loadcmds;
+    size_t l_nloadcmds;
   };
 
 #include <dl-relocate-ld.h>