linuxelflibc

Linux userspace api: how to get size of address spaces of running executable linked as PIE without parsing /proc/self/maps?


With help of dl_iterate_phdr I can load start address of main executable and all binaries via dlpi_addr and it is correlates to function pointers address I can print via printf, however other fields , such as p_offset p_vaddr p_paddr p_memsz and even p_filesz show me very small numbers for my case (my executable is 1.7G alone as well as tons of dependencies).

For reference this is what kind of output I do get when I print with following string for 64bit PIE executable for dlpi_addr dlpi_name dlpi_phdr.p_offset dlpi_phdr.p_vaddr dlpi_phdr.p_paddr dlpi_phdr.p_memsz dlpi_phdr.p_filesz

"sec 0x%lx %s %ld %ld %ld %ld %ld \n\0"

sec 0x55bf2722f000   64 64 64 784 784 
sec 0x7fff8a3ea000  linux-vdso.so.1 0 0 0 4079 4079 
sec 0x7f03ce15d000  /lib/x86_64-linux-gnu/libgstplayer-1.0.so.0 0 0 0 21128 21128 
sec 0x7f03ce08b000  /lib/x86_64-linux-gnu/libgstvideo-1.0.so.0 0 0 0 108632 108632 
sec 0x7f03ce004000  /lib/x86_64-linux-gnu/libgstbase-1.0.so.0 0 0 0 57520 57520 
sec 0x7f03cdeae000  /lib/x86_64-linux-gnu/libgstreamer-1.0.so.0 0 0 0 192512 192512 
sec 0x7f03cde4d000  /lib/x86_64-linux-gnu/libgobject-2.0.so.0 0 0 0 59216 59216 
sec 0x7f03cdd05000  /lib/x86_64-linux-gnu/libglib-2.0.so.0 0 0 0 119968 119968 
sec 0x7f03cdcf2000  /lib/x86_64-linux-gnu/libgstwebrtc-1.0.so.0 0 0 0 16776 16776 
sec 0x7f03cdcd7000  /lib/x86_64-linux-gnu/libgstsdp-1.0.so.0 0 0 0 20248 20248 
sec 0x7f03cdc54000  /lib/x86_64-linux-gnu/libgstaudio-1.0.so.0 0 0 0 64008 64008 
sec 0x7f03cdc3e000  /lib/x86_64-linux-gnu/libgstapp-1.0.so.0 0 0 0 15208 15208 
sec 0x7f03cdbb6000  /lib/x86_64-linux-gnu/libgstgl-1.0.so.0 0 0 0 92984 92984 
sec 0x7f03cd9c7000  /lib/x86_64-linux-gnu/libgio-2.0.so.0 0 0 0 236080 236080 
sec 0x7f03cd600000  /lib/x86_64-linux-gnu/libstdc++.so.6 0 0 0 637488 637488 
sec 0x7f03cd9a8000  /lib/x86_64-linux-gnu/libz.so.1 0 0 0 8832 8832
...and more

and here is elf executable objdump section list

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .interp       0000001c  0000000000000350  0000000000000350  00000350  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .note.gnu.property 00000020  0000000000000370  0000000000000370  00000370  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .note.gnu.build-id 00000024  0000000000000390  0000000000000390  00000390  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .note.ABI-tag 00000020  00000000000003b4  00000000000003b4  000003b4  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .gnu.hash     00000624  00000000000003d8  00000000000003d8  000003d8  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  5 .dynsym       000059a0  0000000000000a00  0000000000000a00  00000a00  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  6 .dynstr       00004e8a  00000000000063a0  00000000000063a0  000063a0  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  7 .gnu.version  00000778  000000000000b22a  000000000000b22a  0000b22a  2**1
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  8 .gnu.version_r 000002c0  000000000000b9a8  000000000000b9a8  0000b9a8  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  9 .rela.dyn     00552480  000000000000bc68  000000000000bc68  0000bc68  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 10 .rela.plt     00001290  000000000055e0e8  000000000055e0e8  0055e0e8  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 11 .init         00000017  0000000000560000  0000000000560000  00560000  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 12 .plt          00000c70  0000000000560020  0000000000560020  00560020  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 13 .plt.got      00000288  0000000000560c90  0000000000560c90  00560c90  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 14 .text         03693fac  0000000000560f40  0000000000560f40  00560f40  2**6
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 15 .fini         00000009  0000000003bf4eec  0000000003bf4eec  03bf4eec  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 16 .rodata       01f5cd20  0000000003bf5000  0000000003bf5000  03bf5000  2**12
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 17 .debug_gdb_scripts 00000022  0000000005b51d20  0000000005b51d20  05b51d20  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 18 .eh_frame_hdr 0012f7a4  0000000005b51d44  0000000005b51d44  05b51d44  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 19 .eh_frame     00755710  0000000005c814e8  0000000005c814e8  05c814e8  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 20 .gcc_except_table 002634a8  00000000063d6bf8  00000000063d6bf8  063d6bf8  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 21 .tdata        00005398  000000000663b740  000000000663b740  0663a740  2**3
                  CONTENTS, ALLOC, LOAD, DATA, THREAD_LOCAL
 22 .tbss         00000a00  0000000006640ad8  0000000006640ad8  0663fad8  2**3
                  ALLOC, THREAD_LOCAL
 23 .init_array   00000090  0000000006640ad8  0000000006640ad8  0663fad8  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 24 .fini_array   00000008  0000000006640b68  0000000006640b68  0663fb68  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 25 .data.rel.ro  00367708  0000000006640b70  0000000006640b70  0663fb70  2**4
                  CONTENTS, ALLOC, LOAD, DATA
 26 .dynamic      00000360  00000000069a8278  00000000069a8278  069a7278  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 27 .got          0001ba20  00000000069a85d8  00000000069a85d8  069a75d8  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 28 .data         00037c58  00000000069c4000  00000000069c4000  069c3000  2**4
                  CONTENTS, ALLOC, LOAD, DATA
 29 .bss          026a5348  00000000069fbc80  00000000069fbc80  069fac58  2**7
                  ALLOC
 30 .comment      0000006b  0000000000000000  0000000000000000  069fac58  2**0
                  CONTENTS, READONLY
 31 .debug_aranges 00420190  0000000000000000  0000000000000000  069facc3  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
 32 .debug_pubnames 08936c54  0000000000000000  0000000000000000  06e1ae53  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
 33 .debug_info   1df05433  0000000000000000  0000000000000000  0f751aa7  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
 34 .debug_abbrev 00442a52  0000000000000000  0000000000000000  2d656eda  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
 35 .debug_line   0423eb3e  0000000000000000  0000000000000000  2da9992c  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
 36 .debug_str    0b447666  0000000000000000  0000000000000000  31cd846a  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
 37 .debug_loc    123b4882  0000000000000000  0000000000000000  3d11fad0  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
 38 .debug_pubtypes 0cce2c03  0000000000000000  0000000000000000  4f4d4352  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
 39 .debug_ranges 07b1f340  0000000000000000  0000000000000000  5c1b6f55  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
 40 .debug_addr   0000d840  0000000000000000  0000000000000000  63cd6295  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
 41 .debug_line_str 00001f90  0000000000000000  0000000000000000  63ce3ad5  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
 42 .debug_loclists 00039d5e  0000000000000000  0000000000000000  63ce5a65  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
 43 .debug_rnglists 00009afd  0000000000000000  0000000000000000  63d1f7c3  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
 44 .debug_str_offsets 0001c6c0  0000000000000000  0000000000000000  63d292c0  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS

So, whats up with all these tiny numbers and how do I get proper with linux api?

For reference the existing answer How do you detect the size of a dynamic library(shared object) in the current process? suggest to parse /proc/self/maps which is kind of silly and this one Getting the ELF header of the main executable only shows start address which I already have

I tried dladdr as well dladdr1 to load some info, i was loaded file sections with of /proc/self/exe in various ways, but i dont see how output of dl_iterate_phdr can let me correlate it info to third party libs

Update: Here is output of readelf utility

Elf file type is DYN (Position-Independent Executable file)
Entry point 0x6cfe20
There are 14 program headers, starting at offset 64

Program Headers:
  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
  PHDR           0x000040 0x0000000000000040 0x0000000000000040 0x000310 0x000310 R   0x8
  INTERP         0x000350 0x0000000000000350 0x0000000000000350 0x00001c 0x00001c R   0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x000000 0x0000000000000000 0x0000000000000000 0x55f378 0x55f378 R   0x1000
  LOAD           0x560000 0x0000000000560000 0x0000000000560000 0x3694ef5 0x3694ef5 R E 0x1000
  LOAD           0x3bf5000 0x0000000003bf5000 0x0000000003bf5000 0x2a450a0 0x2a450a0 R   0x1000
  LOAD           0x663a740 0x000000000663b740 0x000000000663b740 0x3c0518 0x2a65888 RW  0x1000
  DYNAMIC        0x69a7278 0x00000000069a8278 0x00000000069a8278 0x000360 0x000360 RW  0x8
  NOTE           0x000370 0x0000000000000370 0x0000000000000370 0x000020 0x000020 R   0x8
  NOTE           0x000390 0x0000000000000390 0x0000000000000390 0x000044 0x000044 R   0x4
  TLS            0x663a740 0x000000000663b740 0x000000000663b740 0x005398 0x005d98 R   0x8
  GNU_PROPERTY   0x000370 0x0000000000000370 0x0000000000000370 0x000020 0x000020 R   0x8
  GNU_EH_FRAME   0x5b51d44 0x0000000005b51d44 0x0000000005b51d44 0x12f7a4 0x12f7a4 R   0x4
  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW  0x10
  GNU_RELRO      0x663a740 0x000000000663b740 0x000000000663b740 0x3888c0 0x3888c0 R   0x1

 Section to Segment mapping:
  Segment Sections...
   00     
   01     .interp 
   02     .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt 
   03     .init .plt .plt.got .text .fini 
   04     .rodata .debug_gdb_scripts .eh_frame_hdr .eh_frame .gcc_except_table 
   05     .tdata .init_array .fini_array .data.rel.ro .dynamic .got .data .bss 
   06     .dynamic 
   07     .note.gnu.property 
   08     .note.gnu.build-id .note.ABI-tag 
   09     .tdata .tbss 
   10     .note.gnu.property 
   11     .eh_frame_hdr 
   12     
   13     .tdata .init_array .fini_array .data.rel.ro .dynamic .got 

Im using rust bindings, but answer in c/c++/whatever-calling-libc is appreciated

libc::dl_iterate_phdr(Some(collect_objs), std::ptr::null_mut());

    
unsafe extern "C" fn collect_objs(
    info: *mut dl_phdr_info,
    _sz: usize,
    data: *mut c_void,
) -> c_int {
    libc::printf("sec 0x%lx  %s %ld %ld %ld %ld %ld \n\0".as_bytes().as_ptr() as *const i8, 
        (*info).dlpi_addr, 
        (*info).dlpi_name, 
        (*(*info).dlpi_phdr).p_offset, 
        (*(*info).dlpi_phdr).p_vaddr, 
        (*(*info).dlpi_phdr).p_paddr, 
        (*(*info).dlpi_phdr).p_memsz, 
        (*(*info).dlpi_phdr).p_filesz);
    0
}

Solution

  • It turns out name of function describes what function doing internally and not describing what it returns and in reality instead of dl_iterate_phdr should be called dl_iterate_objects, because in callback as stated by documentation it returns you (surprise surprise) shared object, which are executable/lib and not section headers themselve. The name of variables it returns p_memsz is not actually a pointer/pointer offset to a memory object somewhere as you might think from its name but a size of a section, but an actual size, regarless if other field of structure is actuall pointers as mentioned in manual, and dlpi_phnum is not a number of current section but length of array of sections.

    The documentation is contradictionary between names of things and what the things are actually going which leaded me to load first section of object and (which was header section) and get surprised why its so small relative to executable size, and i should looked at example at the bottom of manual to understand what it actually returning instead of piecing together a puzzle of what this api actually is

    Here is the code output from example program from manual

    #define _GNU_SOURCE
    #include <link.h>
    #include <stdlib.h>
    #include <stdio.h>
    
    static int
    callback(struct dl_phdr_info *info, size_t size, void *data)
    {
       char *type;
       int p_type, j;
    
       printf("Name: \"%s\" (%d segments)\n", info->dlpi_name,
                  info->dlpi_phnum);
    
       for (j = 0; j < info->dlpi_phnum; j++) {
           p_type = info->dlpi_phdr[j].p_type;
           type =  (p_type == PT_LOAD) ? "PT_LOAD" :
                   (p_type == PT_DYNAMIC) ? "PT_DYNAMIC" :
                   (p_type == PT_INTERP) ? "PT_INTERP" :
                   (p_type == PT_NOTE) ? "PT_NOTE" :
                   (p_type == PT_INTERP) ? "PT_INTERP" :
                   (p_type == PT_PHDR) ? "PT_PHDR" :
                   (p_type == PT_TLS) ? "PT_TLS" :
                   (p_type == PT_GNU_EH_FRAME) ? "PT_GNU_EH_FRAME" :
                   (p_type == PT_GNU_STACK) ? "PT_GNU_STACK" :
                   (p_type == PT_GNU_RELRO) ? "PT_GNU_RELRO" : NULL;
    
           printf("    %2d: [%14p; memsz:%7lx] flags: 0x%x; ", j,
                   (void *) (info->dlpi_addr + info->dlpi_phdr[j].p_vaddr),
                   info->dlpi_phdr[j].p_memsz,
                   info->dlpi_phdr[j].p_flags);
           if (type != NULL)
               printf("%s\n", type);
           else
               printf("[other (0x%x)]\n", p_type);
       }
    
       return 0;
    }
    
    int
    main(int argc, char *argv[])
    {
       dl_iterate_phdr(callback, NULL);
    
       exit(EXIT_SUCCESS);
    }
    

    and output for it for huge executable im running

    Name: "" (14 segments)
         0: [0x563b1048e040; memsz:    310] flags: 0x4; PT_PHDR
         1: [0x563b1048e350; memsz:     1c] flags: 0x4; PT_INTERP
         2: [0x563b1048e000; memsz: 55f180] flags: 0x4; PT_LOAD
         3: [0x563b109ee000; memsz:36936b5] flags: 0x5; PT_LOAD
         4: [0x563b14082000; memsz:2a44490] flags: 0x4; PT_LOAD
         5: [0x563b16ac78e0; memsz:2a656e8] flags: 0x6; PT_LOAD
         6: [0x563b16e342a8; memsz:    360] flags: 0x6; PT_DYNAMIC
         7: [0x563b1048e370; memsz:     20] flags: 0x4; PT_NOTE
         8: [0x563b1048e390; memsz:     44] flags: 0x4; PT_NOTE
         9: [0x563b16ac78e0; memsz:   5d98] flags: 0x4; PT_TLS
        10: [0x563b1048e370; memsz:     20] flags: 0x4; [other (0x6474e553)]
        11: [0x563b15fded44; memsz: 12f6c4] flags: 0x4; PT_GNU_EH_FRAME
        12: [0x563b1048e000; memsz:      0] flags: 0x6; PT_GNU_STACK
        13: [0x563b16ac78e0; memsz: 388720] flags: 0x4; PT_GNU_RELRO
    Name: "linux-vdso.so.1" (4 segments)
         0: [0x7ffe23dbc000; memsz:    fef] flags: 0x5; PT_LOAD
         1: [0x7ffe23dbc3e0; memsz:    120] flags: 0x4; PT_DYNAMIC
         2: [0x7ffe23dbc500; memsz:     60] flags: 0x4; PT_NOTE
         3: [0x7ffe23dbc560; memsz:     4c] flags: 0x4; PT_GNU_EH_FRAME
    Name: "/lib/x86_64-linux-gnu/libgstplayer-1.0.so.0" (9 segments)
         0: [0x7ff5f2572000; memsz:   5288] flags: 0x4; PT_LOAD
         1: [0x7ff5f2578000; memsz:   6261] flags: 0x5; PT_LOAD
         2: [0x7ff5f257f000; memsz:   443c] flags: 0x4; PT_LOAD
         3: [0x7ff5f2584750; memsz:    b60] flags: 0x6; PT_LOAD
         4: [0x7ff5f25848e0; memsz:    250] flags: 0x6; PT_DYNAMIC
         5: [0x7ff5f2572238; memsz:     24] flags: 0x4; PT_NOTE
         6: [0x7ff5f2580fc0; memsz:    64c] flags: 0x4; PT_GNU_EH_FRAME
         7: [0x7ff5f2572000; memsz:      0] flags: 0x6; PT_GNU_STACK
         8: [0x7ff5f2584750; memsz:    8b0] flags: 0x4; PT_GNU_RELRO
    Name: "/lib/x86_64-linux-gnu/libgstvideo-1.0.so.0" (9 segments)
         0: [0x7ff5f24a0000; memsz:  1a858] flags: 0x4; PT_LOAD
         1: [0x7ff5f24bb000; memsz:  73271] flags: 0x5; PT_LOAD
         2: [0x7ff5f252f000; memsz:  2f4f4] flags: 0x4; PT_LOAD
         3: [0x7ff5f255f870; memsz:  118e8] flags: 0x6; PT_LOAD
         4: [0x7ff5f256ee20; memsz:    250] flags: 0x6; PT_DYNAMIC
         5: [0x7ff5f24a0238; memsz:     24] flags: 0x4; PT_NOTE
         6: [0x7ff5f2545d08; memsz:   2e14] flags: 0x4; PT_GNU_EH_FRAME
         7: [0x7ff5f24a0000; memsz:      0] flags: 0x6; PT_GNU_STACK
         8: [0x7ff5f255f870; memsz:  10790] flags: 0x4; PT_GNU_RELRO
    Name: "/lib/x86_64-linux-gnu/libgstbase-1.0.so.0" (9 segments)
         0: [0x7ff5f2419000; memsz:   e0b0] flags: 0x4; PT_LOAD
         1: [0x7ff5f2428000; memsz:  54c5d] flags: 0x5; PT_LOAD
         2: [0x7ff5f247d000; memsz:  20650] flags: 0x4; PT_LOAD
         3: [0x7ff5f249e9f0; memsz:   1450] flags: 0x6; PT_LOAD
         4: [0x7ff5f249ece8; memsz:    220] flags: 0x6; PT_DYNAMIC
         5: [0x7ff5f2419238; memsz:     24] flags: 0x4; PT_NOTE
         6: [0x7ff5f248d9c4; memsz:   153c] flags: 0x4; PT_GNU_EH_FRAME
         7: [0x7ff5f2419000; memsz:      0] flags: 0x6; PT_GNU_STACK
         8: [0x7ff5f249e9f0; memsz:    610] flags: 0x4; PT_GNU_RELRO
    ...and more