Getting the range of addresses where global variables are stored

90 Views Asked by At

I am trying to find where global variables are stored in a binary exec (ELF).

In my final context, my exec is not executed directly, but called from another program that dlopens it, so I cannot use __data_start and _end.

I looked at this post here: How to determine the address range of global variables in a shared library at runtime? Which is indeed very interesting, but some of my shared variables are not in the range returned by Employed Russian's approach.

If put everything in a single reproducer:

#define _GNU_SOURCE 
#include <link.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>

#define N 16

static int tab[N];
uintmax_t data_start;
uintmax_t data_end;

static int smpi_global_callback( struct dl_phdr_info *info, size_t size, void *data ){
    int p_type, p_flags;
    for (size_t j = 0; j < info->dlpi_phnum; j++) {
        p_type = info->dlpi_phdr[j].p_type;
        p_flags = info->dlpi_phdr[j].p_flags;
        if( p_type == PT_LOAD && p_flags == PF_R|PF_W ){
            data_start = info->dlpi_addr + info->dlpi_phdr->p_vaddr;
            data_end = info->dlpi_addr + info->dlpi_phdr->p_vaddr + info->dlpi_phdr->p_memsz;
            printf( "begin = %2zu ; end = %2zu\n", data_start, data_end );
            return 0; /* always the same values so return after the first one */
        }        
    }
    return 0;
}

int main(){    
    int i = getpid();

    dl_iterate_phdr(smpi_global_callback, NULL);

    tab[0] = i;
    printf( "ptr: %p %2zu\n", &tab, (uintmax_t)&tab );
    printf( "ptr: %p %2zu\n", tab, (uintmax_t)tab );
    printf( "ptr: %p %2zu\n", &data_start, (uintmax_t)data_start );
    printf( "ptr: %p %2zu\n", &data_end, (uintmax_t)data_end );

    return EXIT_SUCCESS;
}

And I compile it with gcc -o myexec myexec.c -fPIC, I get something like:

$ ./myexec 
begin = 93878406574144 ; end = 93878406574872
begin = 140732748611584 ; end = 140732748615005
begin = 140256805367872 ; end = 140256805368656
begin = 140256807493632 ; end = 140256807497096
tab ptr: 0x5561c513b080 93878406590592
&tab ptr: 0x5561c513b080 93878406590592
data_start ptr: 0x5561c513b060 140256807493632
data_end ptr: 0x5561c513b068 140256807497096

A few observations:

  • the array is beyond the first range
  • data_start is at the beginning of the fourth range
  • data_end is just after the end of the fourth range

Is it because of some relocation that was not disabled by -fPIC? Can I disable it, or find the true location?

1

There are 1 best solutions below

3
Employed Russian On
  1. Your code would greatly benefit by printing all values in hex and aligning the addresses so it's easier to compare.
  2. You are returning from the callback after printing the first RW LOAD segment, but there is no guarantee that there is only one such segment (although usually there is only one).
  3. This expression: && p_flags == PF_R|PF_W doesn't do what you think it does.
  4. tab and &tab are exactly the same.
  5. You are printing begin/end from DSOs, but you are only interested in the main executable.
  6. You are printing &data_start but probably you meant to print data_start instead.
  7. You are using 0th dlpi_phdr when you should be using jth one.

Correcting these mistakes, I get all the addresses in the expected range:

gcc -fPIE -pie tt.c && ./a.out

begin = 0x562ef3318dc8; end = 0x562ef33190a0
PHDR:   [0x562ef3318dc8, 0x562ef33190a0)
tab:    [0x562ef3319060, 0x562ef33190a0)

gcc -no-pie tt.c && ./a.out

begin = 0x403df8; end = 0x4040a0
PHDR:   [0x403df8, 0x4040a0)
tab:    [0x404060, 0x4040a0)

The code:

#define _GNU_SOURCE
#include <link.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>

#define N 16

static int tab[N];
intptr_t data_start;
intptr_t data_end;

static int smpi_global_callback(struct dl_phdr_info *info, size_t size, void *data)
{
    int p_type, p_flags;
    for (size_t j = 0; j < info->dlpi_phnum; j++) {
        p_type = info->dlpi_phdr[j].p_type;
        p_flags = info->dlpi_phdr[j].p_flags;
        if (p_type == PT_LOAD && p_flags == (PF_R|PF_W)) {
            data_start = info->dlpi_addr + info->dlpi_phdr[j].p_vaddr;
            data_end = info->dlpi_addr + info->dlpi_phdr[j].p_vaddr + info->dlpi_phdr[j].p_memsz;
            printf("begin = %p; end = %p\n", (void*)data_start, (void*)data_end);
        }
    }
    return 1;
}

int main()
{
    dl_iterate_phdr(smpi_global_callback, NULL);

    printf("PHDR: \t[%p, %p)\n", (void*)data_start, (void*)data_end);
    printf("tab: \t[%p, %p)\n", &tab[0], &tab[N]);

    return EXIT_SUCCESS;
}