0

I was wondering if anyone can think of a reason how a read from a pointer could cause a segmentation when the pointer is: 1. non NULL pointer.

The thing I'm trying to do is walk the shared library and access their symbol table. The segmentation happens when I try to access the ELF hash table to get the amount of symbols within the symbol table.

This is not noticed in x86 platform. It happens only when executing on MIPS64 platform.

The code are based on from this link: How to interpret the dynamic symbol table in an ELF executable?

static void
btrace_dl_symtab_walk(struct dl_phdr_info *info,
                  btrace_dl_lib_t *ctx) {
ElfW(Dyn*) dyn;
ElfW(Sym*) sym = NULL;
ElfW(Word*) hash;

ElfW(Word) sym_cnt = 0;
char* strtab = NULL;
char* sym_name = NULL;
unsigned int i;  
int j;

/*
 * Make indicator to show all of them acomplished before going forward
 */

for (j = 0; j < info->dlpi_phnum; j++) {
    if (info->dlpi_phdr[j].p_type == PT_DYNAMIC) {
        dyn = (ElfW(Dyn)*)(info->dlpi_addr +  info->dlpi_phdr[j].p_vaddr);
        while(dyn->d_tag != DT_NULL) {
            if (dyn->d_tag == DT_HASH) {
                hash = (ElfW(Word*))dyn->d_un.d_ptr;
                if (!hash) {
                    return;
                }

                /*
                 * SEGFAULT happens here
                 */
                printf("Before Seg Fault\n");
                sym_cnt = *(hash + 1);      //<=============== This line causes seg fault
                printf("Never reached here\n");
            } else if(dyn->d_tag == DT_GNU_HASH) { 
                /*
                 * Since there is no simply way to find entry count
                 * in GNU hash table, we have no choice but to
                 * count by hand
                 */
                uint32_t *buckets;
                uint32_t *hashval;
                hash = (ElfW(Word*))dyn->d_un.d_ptr;
                buckets = hash + 4 + (hash[2]*sizeof(size_t)/4);
                for (i = sym_cnt = 0; i < hash[0]; i++) {
                    if (buckets[i] > sym_cnt) {
                        sym_cnt = buckets[i];
                    }
                }
                if (sym_cnt) {
                    sym_cnt -= hash[1];
                    hashval = buckets + hash[0] + sym_cnt;
                    do {
                        sym_cnt++;
                    } while (!(*hashval++ & 1));
                }
                sym_cnt += hash[1];
            }else if (dyn->d_tag == DT_STRTAB) {
                strtab = (char*)dyn->d_un.d_ptr;
            } else if (dyn->d_tag == DT_SYMTAB) {
                sym = (ElfW(Sym*))dyn->d_un.d_ptr;
                break;
            }
            dyn++;
        }
        break;
    }
}

// Other acitivities
}

Any guidance are welcome. Thank you

Community
  • 1
  • 1
user3761728
  • 97
  • 1
  • 2
  • 8
  • 2
    "Any pointers are welcome." This is the part you seem to misunderstand. You are not welcome to dereference just any pointer value you want. – e0k Feb 19 '16 at 21:29
  • No..by that I mean any guidance are welcome. Any idea/ comment regarding this issue are welcome..etc – user3761728 Feb 19 '16 at 21:34
  • "how a read from a pointer could cause a segmentation" These faults happen on memory *access* .. – Daniel Jour Feb 19 '16 at 21:49

1 Answers1

1

I was wondering if anyone can think of a reason how a read from a pointer could cause a segmentation when the pointer is: 1. non NULL pointer.

The fact that pointer is not NULL does not imply that you can read from it. It could be invalid for any number of reasons, e.g.

char *p = mmap(...);
munmap(p, ...);

char c = p[0];  // p points into unmapped memory, SIGSEGV likely.

This is not noticed in x86 platform. It happens only when executing on MIPS64 platform.

Are you using the same version of GLIBC on both platforms?

If I recall correctly, older versions of GLIBC did not relocate DT_HASH, but newer versions do. This may also be architecture-specific.

You'll want to print the value of hash, and compare it to the value of dyn. If hash is small, you'll need to relocate it by info->dlpi_addr.

Employed Russian
  • 199,314
  • 34
  • 295
  • 362