]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
elf: Batch program-header reads in _dl_map_segments (oversight fix)
authorAdhemerval Zanella <adhemerval.zanella@linaro.org>
Wed, 13 May 2026 19:18:19 +0000 (16:18 -0300)
committerAdhemerval Zanella <adhemerval.zanella@linaro.org>
Fri, 15 May 2026 13:58:46 +0000 (10:58 -0300)
The fix for BZ 26577 ("Fix stack overflow in _dl_map_object_from_fd
with large e_phnum") removed the alloca for the program-header table
and introduced a streaming iterator (dl_pt_load_iterator) so segments
could be walked without staging the entire table on the stack.

That patch batched reads correctly in _dl_map_object_scan_phdrs (the
first walk, which collects PT_DYNAMIC/PT_TLS/PT_GNU_* metadata), but
overlooked the second walk in _dl_map_segments:
_dl_pt_load_iterator_next issued one pread64 per program header to
find the next PT_LOAD entry.  For an object with N program headers
this added N redundant per-phdr syscalls on every dlopen / loader
startup -- regardless of whether the table had already been read by
open_verify into struct filebuf.

Unify both walks behind a single batched helper,
_dl_pt_load_iterator_phdr_at:

  - When the program header table fits in the bytes already read by
    open_verify into fbp->buf (the common case for nearly all shared
    objects), all phdr accesses are served from that buffer with no
    syscall at all.

  - Otherwise, up to FILEBUF_SIZE / sizeof(ElfW(Phdr)) program headers
    are read into fbp->buf with a single pread64; subsequent indices
    in the same window hit the buffer.

Both _dl_map_object_scan_phdrs and _dl_pt_load_iterator_next now go
through this helper, eliminating the separate batching logic in
_dl_map_object_scan_phdrs.  struct filebuf moves from dl-load.c to
dl-load.h so the inline iterator in dl-map-segments.h can reach
fbp->buf.

The filebuf size is also bumped to ensure the cached fast path
triggers for all observed binaries.  A survey of an Ubuntu 24.04
installation (scanning /usr) shows:

    Candidate files       : 465834
    ELF files inspected   : 11624
    glibc-linked binaries : 10164
    Minimum e_phnum       : 5
    Maximum e_phnum       : 14
    Average e_phnum       : 11.37
    Median  e_phnum       : 11.0

shows e_phnum capped at 14 (for instance gcc's cc1, lto1, perl,
and gdb).  The previous FILEBUF_SIZE of 832 on 64-bit fit only 13
program headers after the ELF header (64 + 13*56 = 792), so 64-bit
binaries with 14 phdrs missed the cached path.  FILEBUF_SIZE is
bumped from 512/832 to 640/1024 (32-bit / 64-bit) -- enough for at
least 16 program headers on either ABI, leaving headroom over the
observed maximum.

For a typical shared library where open_verify's initial read covers
the program header table, this reduces _dl_map_segments from N
preads to 0.  For a worst-case e_phnum that does not fit in fbp->buf,
reads drop from N to ceil(N / phdrs_per_buf) -- the same cost
_dl_map_object_scan_phdrs already pays.

No functional change.  Tested on x86_64-linux-gnu, aaarch64-linux-gnu,
and i686-linux-gnu.
Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
elf/dl-load.c
elf/dl-load.h

index f6e391a468978c83301f9c99b2aa2a5bc8e4a97c..d20e49d526a1ccf78752ff618a95e7923cee4120 100644 (file)
 #include <gnu/lib-names.h>
 #include <dl-tunables.h>
 
-/* Type for the buffer we put the ELF header and hopefully the program
-   header.  This buffer does not really have to be too large.  In most
-   cases the program header follows the ELF header directly.  If this
-   is not the case all bets are off and we can make the header
-   arbitrarily large and still won't get it read.  This means the only
-   question is how large are the ELF and program header combined.  The
-   ELF header 32-bit files is 52 bytes long and in 64-bit files is 64
-   bytes long.  Each program header entry is again 32 and 56 bytes
-   long respectively.  I.e., even with a file which has 10 program
-   header entries we only have to read 372B/624B respectively.  Add to
-   this a bit of margin for program notes and reading 512B and 832B
-   for 32-bit and 64-bit files respectively is enough.  If this
-   heuristic should really fail for some file the code in
-   `_dl_map_object_from_fd' knows how to recover.  */
-struct filebuf
-{
-  ssize_t len;
-#if __WORDSIZE == 32
-# define FILEBUF_SIZE 512
-#else
-# define FILEBUF_SIZE 832
-#endif
-  char buf[FILEBUF_SIZE] __attribute__ ((aligned (__alignof (ElfW(Ehdr)))));
-};
-
 #include "dynamic-link.h"
 #include "get-dynamic-info.h"
 #include <abi-tag.h>
@@ -936,13 +911,18 @@ _dl_notify_new_object (int mode, Lmid_t nsid, struct link_map *l)
 }
 
 /* Initialize the PT_LOAD iterator IT for reading program headers from FD
-   at file offset PHOFF with PHNUM entries.  Zeros all precomputed fields
-   so the caller's scan loop can fill them in.  */
+   at file offset PHOFF with PHNUM entries.  FBP is used as scratch space
+   for batched program-header reads; if open_verify's initial read into
+   FBP->buf already covers the whole phdr table, the iterator runs
+   entirely from that buffer without any further pread.  Zeros all
+   precomputed fields so the caller's scan loop can fill them in.  */
 static void
-_dl_pt_load_iterator_init (struct dl_pt_load_iterator *it,
-                          int fd, ElfW(Off) phoff, uint16_t phnum)
+_dl_pt_load_iterator_init (struct dl_pt_load_iterator *it, int fd,
+                          struct filebuf *fbp, ElfW(Off) phoff,
+                          uint16_t phnum)
 {
   it->fd = fd;
+  it->fbp = fbp;
   it->phoff = phoff;
   it->phnum = phnum;
   it->idx = 0;
@@ -952,161 +932,134 @@ _dl_pt_load_iterator_init (struct dl_pt_load_iterator *it,
   it->first_mapstart = 0;
   it->last_mapstart = 0;
   it->last_allocend = 0;
+  it->cached = (phoff + (ElfW(Off)) phnum * sizeof (ElfW(Phdr))
+               <= (ElfW(Off)) fbp->len);
+  it->buf_base = 0;
+  it->buf_count = it->cached ? phnum : 0;
 }
 
-/* Scan all program headers from IT->fd in chunks, using FBP->buf as a
-   scratch buffer.  Fills in IT's precomputed PT_LOAD metadata and collects
-   segment attributes into L.  Returns NULL on success, or an error message
-   string on failure; sets *ERRVALP to errno for I/O errors, 0 otherwise.  */
+/* Scan all program headers from IT->fd, using the iterator's filebuf as a
+   scratch buffer for batched reads (skipped entirely if open_verify
+   already read the whole table).  Fills in IT's precomputed PT_LOAD
+   metadata and collects segment attributes into L.  Returns NULL on
+   success, or an error message string on failure; sets *ERRVALP to errno
+   for I/O errors, 0 otherwise.  */
 static const char *
 _dl_map_object_scan_phdrs (struct dl_pt_load_iterator *it,
-                          struct filebuf *fbp, struct link_map *l, int mode,
+                          struct link_map *l, int mode,
                           unsigned int *stack_flagsp, bool *has_holesp,
                           bool *empty_dynamicp, int *errvalp)
 {
   ElfW(Addr) prev_mapend = 0;
-  const ElfW(Half) phdrs_per_buf = sizeof (fbp->buf) / sizeof (ElfW(Phdr));
-  ElfW(Phdr) *chunk = (ElfW(Phdr) *) fbp->buf;
   struct dl_machine_phdr_info minfo;
   elf_machine_phdr_info_init (&minfo);
 
-  /* Fast path: if all program headers fit within the bytes already read
-     into fbp->buf by open_verify, iterate them directly without any
-     additional pread syscalls.  The slow path falls through to pread
-     in chunks (which overwrites fbp->buf, but the caller has already
-     saved the ELF header to a local copy).  */
-  const bool cached
-    = (it->phoff + (ElfW(Off)) it->phnum * sizeof (ElfW(Phdr))
-       <= (ElfW(Off)) fbp->len);
-
-  for (ElfW(Half) base = 0; base < it->phnum; )
+  for (ElfW(Half) i = 0; i < it->phnum; i++)
     {
-      ElfW(Half) batch;
-      const ElfW(Phdr) *batch_ptr;
-
-      if (__glibc_likely (cached))
+      const ElfW(Phdr) *ph = _dl_pt_load_iterator_phdr_at (it, i);
+      if (__glibc_unlikely (ph == NULL))
        {
-         batch = it->phnum;
-         batch_ptr = (const ElfW(Phdr) *) (fbp->buf + it->phoff);
+         *errvalp = errno;
+         return N_("cannot read file data");
        }
-      else
+      elf_machine_phdr_collect (&minfo, ph);
+      switch (ph->p_type)
        {
-         batch = it->phnum - base;
-         if (batch > phdrs_per_buf)
-           batch = phdrs_per_buf;
-         size_t bytes = (size_t) batch * sizeof (ElfW(Phdr));
-         ElfW(Off) off = it->phoff + (ElfW(Off)) base * sizeof (ElfW(Phdr));
-         if (__pread64_nocancel (it->fd, chunk, bytes, off) != bytes)
-           {
-             *errvalp = errno;
-             return N_("cannot read file data");
-           }
-         batch_ptr = chunk;
-       }
-
-      for (ElfW(Half) i = 0; i < batch; i++)
-       {
-         const ElfW(Phdr) *ph = &batch_ptr[i];
-         elf_machine_phdr_collect (&minfo, ph);
-         switch (ph->p_type)
-           {
-           case PT_LOAD:
+       case PT_LOAD:
+         {
+           if (__glibc_unlikely (((ph->p_vaddr - ph->p_offset)
+                                  & (it->pagesize - 1)) != 0))
              {
-               if (__glibc_unlikely (((ph->p_vaddr - ph->p_offset)
-                                      & (it->pagesize - 1)) != 0))
-                 {
-                   *errvalp = 0;
-                   return N_("ELF load command address/offset not page-aligned");
-                 }
-               ElfW(Addr) mapstart = ALIGN_DOWN (ph->p_vaddr, it->pagesize);
-               ElfW(Addr) mapend = ALIGN_UP (ph->p_vaddr + ph->p_filesz,
-                                             it->pagesize);
-               ElfW(Off)  mapoff = ALIGN_DOWN (ph->p_offset, it->pagesize);
-               int prot = pf_to_prot (ph->p_flags);
-               if (powerof2 (ph->p_align) && ph->p_align > it->p_align_max)
-                 it->p_align_max = ph->p_align;
-               it->p_align_max = _dl_map_segment_align (&(struct loadcmd) {
-                                                          .mapstart = mapstart,
-                                                          .mapend   = mapend,
-                                                          .mapoff   = mapoff,
-                                                          .prot     = prot },
-                                                        it->p_align_max);
-               if (it->nloadcmds > 0 && prev_mapend != mapstart)
-                 *has_holesp = true;
-               prev_mapend = mapend;
-               if (it->nloadcmds == 0)
-                 it->first_mapstart = mapstart;
-               it->last_mapstart = mapstart;
-               it->last_allocend = ph->p_vaddr + ph->p_memsz;
-               it->nloadcmds++;
+               *errvalp = 0;
+               return N_("ELF load command address/offset not page-aligned");
              }
-             break;
+           ElfW(Addr) mapstart = ALIGN_DOWN (ph->p_vaddr, it->pagesize);
+           ElfW(Addr) mapend = ALIGN_UP (ph->p_vaddr + ph->p_filesz,
+                                         it->pagesize);
+           ElfW(Off)  mapoff = ALIGN_DOWN (ph->p_offset, it->pagesize);
+           int prot = pf_to_prot (ph->p_flags);
+           if (powerof2 (ph->p_align) && ph->p_align > it->p_align_max)
+             it->p_align_max = ph->p_align;
+           it->p_align_max = _dl_map_segment_align (&(struct loadcmd) {
+                                                      .mapstart = mapstart,
+                                                      .mapend   = mapend,
+                                                      .mapoff   = mapoff,
+                                                      .prot     = prot },
+                                                    it->p_align_max);
+           if (it->nloadcmds > 0 && prev_mapend != mapstart)
+             *has_holesp = true;
+           prev_mapend = mapend;
+           if (it->nloadcmds == 0)
+             it->first_mapstart = mapstart;
+           it->last_mapstart = mapstart;
+           it->last_allocend = ph->p_vaddr + ph->p_memsz;
+           it->nloadcmds++;
+         }
+         break;
 
-           /* These entries tell us where to find things once the file's
-              segments are mapped in.  We record the addresses it says
-              verbatim, and later correct for the run-time load address.  */
-           case PT_DYNAMIC:
-             if (ph->p_filesz == 0)
-               *empty_dynamicp = true; /* Usually separate debuginfo.  */
-             else
-               {
-                 /* Debuginfo only files from "objcopy --only-keep-debug"
-                    contain a PT_DYNAMIC segment with p_filesz == 0.  Skip
-                    such a segment to avoid a crash later.  */
-                 l->l_ld = (void *) ph->p_vaddr;
-                 l->l_ldnum = ph->p_memsz / sizeof (ElfW(Dyn));
-                 l->l_ld_readonly = (ph->p_flags & PF_W) == 0;
-               }
-             break;
+       /* These entries tell us where to find things once the file's
+          segments are mapped in.  We record the addresses it says
+          verbatim, and later correct for the run-time load address.  */
+       case PT_DYNAMIC:
+         if (ph->p_filesz == 0)
+           *empty_dynamicp = true; /* Usually separate debuginfo.  */
+         else
+           {
+             /* Debuginfo only files from "objcopy --only-keep-debug"
+                contain a PT_DYNAMIC segment with p_filesz == 0.  Skip
+                such a segment to avoid a crash later.  */
+             l->l_ld = (void *) ph->p_vaddr;
+             l->l_ldnum = ph->p_memsz / sizeof (ElfW(Dyn));
+             l->l_ld_readonly = (ph->p_flags & PF_W) == 0;
+           }
+         break;
 
-           case PT_PHDR:
-             l->l_phdr = (void *) ph->p_vaddr;
-             break;
+       case PT_PHDR:
+         l->l_phdr = (void *) ph->p_vaddr;
+         break;
 
-           case PT_TLS:
-             if (ph->p_memsz == 0)
-               /* Nothing to do for an empty segment.  */
-               break;
+       case PT_TLS:
+         if (ph->p_memsz == 0)
+           /* Nothing to do for an empty segment.  */
+           break;
 
-             l->l_tls_blocksize = ph->p_memsz;
-             l->l_tls_align = ph->p_align;
-             if (ph->p_align == 0)
-               l->l_tls_firstbyte_offset = 0;
-             else
-               l->l_tls_firstbyte_offset = ph->p_vaddr & (ph->p_align - 1);
-             l->l_tls_initimage_size = ph->p_filesz;
-             /* Since we don't know the load address yet only store the
-                offset.  We will adjust it later.  */
-             l->l_tls_initimage = (void *) ph->p_vaddr;
+         l->l_tls_blocksize = ph->p_memsz;
+         l->l_tls_align = ph->p_align;
+         if (ph->p_align == 0)
+           l->l_tls_firstbyte_offset = 0;
+         else
+           l->l_tls_firstbyte_offset = ph->p_vaddr & (ph->p_align - 1);
+         l->l_tls_initimage_size = ph->p_filesz;
+         /* Since we don't know the load address yet only store the
+            offset.  We will adjust it later.  */
+         l->l_tls_initimage = (void *) ph->p_vaddr;
 
-             /* l->l_tls_modid is assigned below, once there is no
-                possibility for failure.  */
+         /* l->l_tls_modid is assigned below, once there is no
+            possibility for failure.  */
 
-             if (l->l_type != lt_library
-                 && GL(dl_tls_dtv_slotinfo_list) == NULL)
-               {
+         if (l->l_type != lt_library
+             && GL(dl_tls_dtv_slotinfo_list) == NULL)
+           {
 #ifdef SHARED
-                 /* We are loading the executable itself when the dynamic
-                    linker was executed directly.  The setup will happen
-                    later.  */
-                 assert (l->l_prev == NULL || (mode & __RTLD_AUDIT) != 0);
+             /* We are loading the executable itself when the dynamic
+                linker was executed directly.  The setup will happen
+                later.  */
+             assert (l->l_prev == NULL || (mode & __RTLD_AUDIT) != 0);
 #else
-                 assert (false && "TLS not initialized in static application");
+             assert (false && "TLS not initialized in static application");
 #endif
-               }
-             break;
+           }
+         break;
 
-           case PT_GNU_STACK:
-             *stack_flagsp = pf_to_prot (ph->p_flags);
-             break;
+       case PT_GNU_STACK:
+         *stack_flagsp = pf_to_prot (ph->p_flags);
+         break;
 
-           case PT_GNU_RELRO:
-             l->l_relro_addr = ph->p_vaddr;
-             l->l_relro_size = ph->p_memsz;
-             break;
-           }
+       case PT_GNU_RELRO:
+         l->l_relro_addr = ph->p_vaddr;
+         l->l_relro_size = ph->p_memsz;
+         break;
        }
-      base += batch;
     }
 
   if (__glibc_unlikely (elf_machine_reject_phdr_p (&minfo, l, it->fd)))
@@ -1275,10 +1228,10 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
     bool has_holes;
     bool empty_dynamic = false;
 
-    _dl_pt_load_iterator_init (&it, fd, header.e_phoff, l->l_phnum);
+    _dl_pt_load_iterator_init (&it, fd, fbp, header.e_phoff, l->l_phnum);
     has_holes = false;
 
-    errstring = _dl_map_object_scan_phdrs (&it, fbp, l, mode, &stack_flags,
+    errstring = _dl_map_object_scan_phdrs (&it, l, mode, &stack_flags,
                                           &has_holes, &empty_dynamic, &errval);
     if (__glibc_unlikely (errstring != NULL))
       goto lose;
index e58028038c9382adb6b282597b788a3f70c40eb1..80ae5db4b3d2ff0c705ae246b16349f1fd1b1618 100644 (file)
 #define _DL_LOAD_H     1
 
 #include <link.h>
+#include <stddef.h>
 #include <sys/mman.h>
+#include <sys/types.h>
 #include <libc-pointer-arith.h>
 #include <stackinfo.h>
 #include <not-cancel.h>
 
 
+/* Type for the buffer we put the ELF header and hopefully the program
+   header.  This buffer does not really have to be too large.  In most
+   cases the program header follows the ELF header directly.  If this
+   is not the case all bets are off and we can make the header
+   arbitrarily large and still won't get it read.  This means the only
+   question is how large are the ELF and program header combined.  The
+   ELF header for 32-bit files is 52 bytes long and for 64-bit files
+   64 bytes long.  Each program header entry is 32 and 56 bytes long
+   respectively.
+
+   Size for at least 16 entries (with a little margin for program notes)
+   needs 52 + 16*32 = 564 bytes on 32-bit and 64 + 16*56 = 960 bytes on
+   64-bit; round up to 640 and  1024 respectively.  If this heuristic
+   should still fail for some file the code in
+   `_dl_map_object_from_fd' knows how to recover.  */
+struct filebuf
+{
+  ssize_t len;
+#if __WORDSIZE == 32
+# define FILEBUF_SIZE 640
+#else
+# define FILEBUF_SIZE 1024
+#endif
+  char buf[FILEBUF_SIZE] __attribute__ ((aligned (__alignof (ElfW(Ehdr)))));
+};
+
+
 /* On some systems, no flag bits are given to specify file mapping.  */
 #ifndef MAP_FILE
 # define MAP_FILE       0
@@ -84,28 +113,65 @@ struct loadcmd
 };
 
 
-/* Iterator for PT_LOAD program header segments.  It should be initialized
-   by _dl_pt_load_iterator_init once, then _dl_pt_load_iterator_next
-   repeatedly to walk each PT_LOAD segment without storing them all.
-   Segments are re-read one at a time via pread so that no large stack
-   buffer is needed for the program header table.  */
+/* Iterator for program header segments.  Initialize with
+   _dl_pt_load_iterator_init, then either walk PT_LOAD segments via
+   _dl_pt_load_iterator_next or do random access via
+   _dl_pt_load_iterator_phdr_at.  A scratch buffer (fbp->buf) is used to
+   batch-read program headers; if the entire program header table was
+   already loaded by open_verify's initial read no pread is issued.  */
 struct dl_pt_load_iterator
 {
   int fd;                       /* File descriptor for pread.  */
+  struct filebuf *fbp;          /* Scratch buffer for batched phdr reads.  */
   ElfW(Off) phoff;              /* Program header table file offset.  */
   ElfW(Half) phnum;             /* Total number of program headers.  */
   ElfW(Half) idx;               /* Index of next header to read.  */
+  ElfW(Half) buf_base;          /* Index of phdr at start of fbp->buf
+                                   (chunked mode only).  */
+  ElfW(Half) buf_count;         /* Number of phdrs currently in fbp->buf.  */
+  bool cached;                  /* True iff entire phdr table is already
+                                   resident in fbp->buf from open_verify.  */
   ElfW(Addr) p_align_max;       /* Maximum p_align over all PT_LOAD segments.  */
   ElfW(Addr) pagesize;          /* System page size (GLRO(dl_pagesize)).  */
 
-  /* Fields below are precomputed by _dl_pt_load_iterator_init and
-     are intended for use by _dl_map_segments.  */
+  /* Fields below are precomputed by _dl_map_object_scan_phdrs and are
+     intended for use by _dl_map_segments.  */
   ElfW(Addr) first_mapstart;    /* mapstart of the first PT_LOAD segment.  */
   ElfW(Addr) last_mapstart;     /* mapstart of the last PT_LOAD segment.  */
   ElfW(Addr) last_allocend;     /* allocend of the last PT_LOAD segment.  */
   size_t nloadcmds;             /* Number of PT_LOAD segments found.  */
 };
 
+/* Return a pointer to the program header at INDEX.  If the entire phdr
+   table is already cached in fbp->buf (from open_verify), it is served
+   directly with no syscall; otherwise a batch of up to FILEBUF_SIZE /
+   sizeof(ElfW(Phdr)) entries is read into fbp->buf via a single pread.
+   Subsequent calls within the same batch hit the buffer.  Returns NULL on
+   read failure (errno set by pread).  */
+static __always_inline const ElfW(Phdr) *
+_dl_pt_load_iterator_phdr_at (struct dl_pt_load_iterator *it, ElfW(Half) idx)
+{
+  if (__glibc_likely (it->cached))
+    return (const ElfW(Phdr) *) (it->fbp->buf + it->phoff) + idx;
+
+  if (idx < it->buf_base || idx >= it->buf_base + it->buf_count)
+    {
+      const ElfW(Half) phdrs_per_buf
+       = sizeof (it->fbp->buf) / sizeof (ElfW(Phdr));
+      ElfW(Half) batch = it->phnum - idx;
+      if (batch > phdrs_per_buf)
+       batch = phdrs_per_buf;
+      size_t bytes = (size_t) batch * sizeof (ElfW(Phdr));
+      ElfW(Off) off = it->phoff + (ElfW(Off)) idx * sizeof (ElfW(Phdr));
+      if (__pread64_nocancel (it->fd, it->fbp->buf, bytes, off)
+         != (ssize_t) bytes)
+       return NULL;
+      it->buf_base = idx;
+      it->buf_count = batch;
+    }
+  return (const ElfW(Phdr) *) it->fbp->buf + (idx - it->buf_base);
+}
+
 /* Advance iterator IT to the next PT_LOAD segment and fill C with its
    decoded load command.  Returns true when a segment was found, false
    when the end of the program header table has been reached or a read
@@ -115,21 +181,19 @@ _dl_pt_load_iterator_next (struct dl_pt_load_iterator *it, struct loadcmd *c)
 {
   while (it->idx < it->phnum)
     {
-      ElfW(Phdr) ph;
-      ElfW(Off) off = it->phoff + (ElfW(Off)) it->idx * sizeof ph;
+      const ElfW(Phdr) *ph = _dl_pt_load_iterator_phdr_at (it, it->idx);
       it->idx++;
-      if (__pread64_nocancel (it->fd, &ph, sizeof ph, off)
-         != (ssize_t) sizeof ph)
+      if (__glibc_unlikely (ph == NULL))
         return false;
-      if (ph.p_type != PT_LOAD)
+      if (ph->p_type != PT_LOAD)
         continue;
 
-      c->mapstart = ALIGN_DOWN (ph.p_vaddr, it->pagesize);
-      c->mapend   = ALIGN_UP (ph.p_vaddr + ph.p_filesz, it->pagesize);
-      c->dataend  = ph.p_vaddr + ph.p_filesz;
-      c->allocend = ph.p_vaddr + ph.p_memsz;
-      c->mapoff   = ALIGN_DOWN (ph.p_offset, it->pagesize);
-      c->prot     = pf_to_prot (ph.p_flags);
+      c->mapstart = ALIGN_DOWN (ph->p_vaddr, it->pagesize);
+      c->mapend   = ALIGN_UP (ph->p_vaddr + ph->p_filesz, it->pagesize);
+      c->dataend  = ph->p_vaddr + ph->p_filesz;
+      c->allocend = ph->p_vaddr + ph->p_memsz;
+      c->mapoff   = ALIGN_DOWN (ph->p_offset, it->pagesize);
+      c->prot     = pf_to_prot (ph->p_flags);
       c->mapalign = it->p_align_max;
       return true;
     }