Esempio n. 1
0
void elfcreator_end(ElfCreator *ctor)
{
	GElf_Phdr phdr_mem, *phdr;
	int m,n;

	for (m = 0; (phdr = gelf_getphdr(ctor->oldelf, m, &phdr_mem)) != NULL; m++)
		/* XXX this should check if an entry is needed */;

	gelf_newphdr(ctor->elf, m);
	elf_update(ctor->elf, ELF_C_NULL);
	update_dyn_cache(ctor);

	for (n = 0; n < m; n++) {
		/* XXX this should check if an entry is needed */
		phdr = gelf_getphdr(ctor->oldelf, n, &phdr_mem);
		if (ctor->dynshdr && phdr->p_type == PT_DYNAMIC)
			phdr->p_offset = ctor->dynshdr->sh_offset;

		gelf_update_phdr(ctor->elf, n, phdr);
	}

	fixup_dynamic(ctor);

	clear(ctor, 0);
	free(ctor);
}
Esempio n. 2
0
/*
 * Search through PHT saving the beginning and ending segment offsets
 */
static int
build_segment_table(Elf * elf, GElf_Ehdr * ehdr)
{
	unsigned int i;

	if ((b_e_seg_table = (Seg_Table *)
		calloc(ehdr->e_phnum, sizeof (Seg_Table))) == NULL) {
		error_message(MALLOC_ERROR,
		PLAIN_ERROR, (char *)0,
		prog);
		mcs_exit(FAILURE);
	}

	for (i = 0; i < ehdr->e_phnum; i++) {
		GElf_Phdr ph;

		(void) gelf_getphdr(elf, i, &ph);

		/*
		 * remember the note SEGMENTS index so that we can
		 * re-set it's p_offset later if needed.
		 */
		if (ph.p_type == PT_NOTE)
			notesegndx = i;

		b_e_seg_table[i].p_offset = ph.p_offset;
		b_e_seg_table[i].p_memsz  = ph.p_offset + ph.p_memsz;
		b_e_seg_table[i].p_filesz = ph.p_offset + ph.p_filesz;
	}
	return (SUCCESS);
}
Esempio n. 3
0
static GElf_Addr
get_txtorigin(Elf *elf, char *filename)
{
	GElf_Ehdr	ehdr;
	GElf_Phdr	phdr;
	GElf_Half	ndx;
	GElf_Addr	txt_origin = 0;
	bool		first_load_seg = TRUE;

	if (gelf_getehdr(elf, &ehdr) == NULL) {
		(void) fprintf(stderr, "%s: can't read ELF header of %s\n",
						    cmdname, filename);
		exit(ERR_ELF);
	}

	for (ndx = 0; ndx < ehdr.e_phnum; ndx++) {
		if (gelf_getphdr(elf, ndx, &phdr) == NULL)
			continue;

		if ((phdr.p_type == PT_LOAD) && !(phdr.p_flags & PF_W)) {
			if (first_load_seg || phdr.p_vaddr < txt_origin)
				txt_origin = phdr.p_vaddr;

			if (first_load_seg)
				first_load_seg = FALSE;
		}
	}

	return (txt_origin);
}
/* Open libelf FILE->fd and compute the load base of ELF as loaded in MOD.
   When we return success, FILE->elf and FILE->bias are set up.  */
static inline Dwfl_Error
open_elf (Dwfl_Module *mod, struct dwfl_file *file)
{
  if (file->elf == NULL)
    {
      /* If there was a pre-primed file name left that the callback left
	 behind, try to open that file name.  */
      if (file->fd < 0 && file->name != NULL)
	file->fd = TEMP_FAILURE_RETRY (open64 (file->name, O_RDONLY));

      if (file->fd < 0)
	return CBFAIL;

      file->elf = elf_begin (file->fd, ELF_C_READ_MMAP_PRIVATE, NULL);
    }

  if (unlikely (elf_kind (file->elf) != ELF_K_ELF))
    {
      close (file->fd);
      file->fd = -1;
      return DWFL_E_BADELF;
    }

  GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (file->elf, &ehdr_mem);
  if (ehdr == NULL)
    {
    elf_error:
      close (file->fd);
      file->fd = -1;
      return DWFL_E (LIBELF, elf_errno ());
    }

  /* The addresses in an ET_EXEC file are absolute.  The lowest p_vaddr of
     the main file can differ from that of the debug file due to prelink.
     But that doesn't not change addresses that symbols, debuginfo, or
     sh_addr of any program sections refer to.  */
  file->bias = 0;
  if (mod->e_type != ET_EXEC)
    for (uint_fast16_t i = 0; i < ehdr->e_phnum; ++i)
      {
	GElf_Phdr ph_mem;
	GElf_Phdr *ph = gelf_getphdr (file->elf, i, &ph_mem);
	if (ph == NULL)
	  goto elf_error;
	if (ph->p_type == PT_LOAD)
	  {
	    file->bias = ((mod->low_addr & -ph->p_align)
			  - (ph->p_vaddr & -ph->p_align));
	    break;
	  }
      }

  mod->e_type = ehdr->e_type;

  /* Relocatable Linux kernels are ET_EXEC but act like ET_DYN.  */
  if (mod->e_type == ET_EXEC && file->bias != 0)
    mod->e_type = ET_DYN;

  return DWFL_E_NOERROR;
}
Esempio n. 5
0
int
internal_function
__libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf)
{
  size_t shstrndx = SHN_UNDEF;
  int result = 0;

  Elf_Scn *scn = elf_nextscn (elf, NULL);

  if (scn == NULL)
    {
      /* No sections, have to look for phdrs.  */
      GElf_Ehdr ehdr_mem;
      GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
      size_t phnum;
      if (unlikely (ehdr == NULL)
	  || unlikely (elf_getphdrnum (elf, &phnum) != 0))
	{
	  __libdwfl_seterrno (DWFL_E_LIBELF);
	  return -1;
	}
      for (size_t i = 0; result == 0 && i < phnum; ++i)
	{
	  GElf_Phdr phdr_mem;
	  GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
	  if (likely (phdr != NULL) && phdr->p_type == PT_NOTE)
	    result = check_notes (mod, set,
				  elf_getdata_rawchunk (elf,
							phdr->p_offset,
							phdr->p_filesz,
							ELF_T_NHDR),
				  phdr->p_vaddr + mod->main.bias);
	}
    }
  else
    do
      {
	GElf_Shdr shdr_mem;
	GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
	if (likely (shdr != NULL) && shdr->sh_type == SHT_NOTE)
	  {
	    /* Determine the right sh_addr in this module.  */
	    GElf_Addr vaddr = 0;
	    if (!(shdr->sh_flags & SHF_ALLOC))
	      vaddr = NO_VADDR;
	    else if (mod->e_type != ET_REL)
	      vaddr = shdr->sh_addr + mod->main.bias;
	    else if (__libdwfl_relocate_value (mod, elf, &shstrndx,
					       elf_ndxscn (scn), &vaddr))
	      vaddr = NO_VADDR;
	    result = check_notes (mod, set, elf_getdata (scn, NULL), vaddr);
	  }
      }
    while (result == 0 && (scn = elf_nextscn (elf, scn)) != NULL);

  return result;
}
Esempio n. 6
0
/**
 * parse initial ELF file and collect essential program header information
 */
int
ElfInject::collectPHInfo() {
	size_t phCount;
	GElf_Phdr phdr;

	info("collect program header information");

	if (elf_getphdrnum(bin.elf, &phCount) != 0) {
		error("cannot extract program header information");
		return -1;
	}

	for (size_t i=0; i<phCount; i++) {
		if (gelf_getphdr(bin.elf, i, &phdr) != &phdr) {
			error("cannot extract program header nr %d", i);
			return -1;
		}

		switch (phdr.p_type) {
		case PT_PHDR:
			bin.phPhdrIndex = i;
			debug("° program header PHDR: %d", i);
			break;
		case PT_DYNAMIC:
			bin.phDynamicIndex = i;
			debug("° program header DYNAMIC: %d", i);
			break;
		case PT_LOAD:
			if (!phdr.p_offset) {
				/* text segment */
				bin.phLoadTextIndex = i;
				debug("° program header LOAD (.text): %d", i);
			} else if (!(phdr.p_flags & PF_X)) {
				/* data segment */
				bin.phLoadDataIndex = i;
				debug("° program header LOAD (.data): %d", i);
			}
			break;
		case PT_GNU_STACK:
			bin.phGnuStackIndex = i;
			debug("° program header GNU_STACK: %d", i);
			break;
		case PT_GNU_RELRO:
			bin.phGnuRelroIndex = i;
			debug("° program header GNU_RELRO: %d", i);
			break;
		default:
			/* do nothing */
			break;
		}
	}

	return 0;
}
Esempio n. 7
0
void
set_pt_flags(int fd, uint16_t pt_flags)
{
	Elf *elf;
	GElf_Phdr phdr;
	size_t i, phnum;

	if(elf_version(EV_CURRENT) == EV_NONE)
	{
		PyErr_SetString(PaxError, "set_pt_flags: library out of date");
		return;
	}

	if((elf = elf_begin(fd, ELF_C_RDWR_MMAP, NULL)) == NULL)
	{
		PyErr_SetString(PaxError, "set_pt_flags: elf_begin() failed");
		return;
	}

	if(elf_kind(elf) != ELF_K_ELF)
	{
		elf_end(elf);
		PyErr_SetString(PaxError, "set_pt_flags: elf_kind() failed: this is not an elf file.");
		return;
	}

	elf_getphdrnum(elf, &phnum);

	for(i=0; i<phnum; i++)
	{
		if(gelf_getphdr(elf, i, &phdr) != &phdr)
		{
			elf_end(elf);
			PyErr_SetString(PaxError, "set_pt_flags: gelf_getphdr() failed");
			return;
		}

		if(phdr.p_type == PT_PAX_FLAGS)
		{
			phdr.p_flags = pt_flags;

			if(!gelf_update_phdr(elf, i, &phdr))
			{
				elf_end(elf);
				PyErr_SetString(PaxError, "set_pt_flags: gelf_update_phdr() failed");
				return;
			}
		}
	}

	elf_end(elf);
}
Esempio n. 8
0
int elf_utils_shift_contents(Elf *e, int start_offset, int shift_amount)
{
	GElf_Ehdr ehdr;
	Elf_Scn *scn;
	GElf_Shdr shdr;
	size_t segment_count = 0, segndx;
	GElf_Phdr phdr;
	int bottom_section_offset = 0;

	ELF_ASSERT(gelf_getehdr(e, &ehdr));
	if (ehdr.e_shoff >= start_offset) {
		ehdr.e_shoff += shift_amount;
		ELF_ASSERT(gelf_update_ehdr(e, &ehdr));
	}

	scn = NULL;
	while ((scn = elf_nextscn(e, scn)) != NULL) {
		ELF_ASSERT(gelf_getshdr(scn, &shdr));
		if (shdr.sh_offset >= start_offset) {
			shdr.sh_offset += shift_amount;
			ELF_ASSERT(gelf_update_shdr(scn, &shdr));
		}
		if (shdr.sh_offset + shdr.sh_size > bottom_section_offset) {
			bottom_section_offset = shdr.sh_offset + shdr.sh_size;
		}
	}

	if (bottom_section_offset > ehdr.e_shoff) {
		ELF_ASSERT(gelf_getehdr(e, &ehdr));
		ehdr.e_shoff = bottom_section_offset;
		ELF_ASSERT(gelf_update_ehdr(e, &ehdr));
	}

	/* A bug in libelf means that getphdrnum will report failure in a new file.
	 * However, it will still set segment_count, so we'll use it. */
	ELF_ASSERT((elf_getphdrnum(e, &segment_count), segment_count > 0));

	for (segndx = 0; segndx < segment_count; segndx++) {
		ELF_ASSERT(gelf_getphdr(e, segndx, &phdr));
		if (phdr.p_offset >= start_offset) {
			phdr.p_offset += shift_amount;
			ELF_ASSERT(gelf_update_phdr(e, segndx, &phdr));
		}
	}
		
	return 1;
failure:
	return 0;
}
Esempio n. 9
0
//get next loadable program header
GElf_Phdr* ELF_next_loadable_phdr(){
    GElf_Phdr *phdr = malloc(sizeof(GElf_Phdr));
    int phdr_num = ehdr.e_phnum;
    while(phdr_index < phdr_num){
        if(gelf_getphdr(e, phdr_index, phdr) != phdr){
            phdr_index = 0;
            fprintf(stderr, "getphdr() failed: %s.", elf_errmsg(-1));
            exit(1);
        }
        phdr_index ++;
        if(phdr->p_type == PT_LOAD)
            return phdr;
    }
    phdr_index = 0;
    return NULL;
}
Esempio n. 10
0
//get loadable segment num
int ELF_loadable_seg_num(){
    GElf_Phdr phdr;
    int result = 0;
    int phdr_num = ehdr.e_phnum;
    while(phdr_index < phdr_num){
        if(gelf_getphdr(e, phdr_index, &phdr) != &phdr){
            fprintf(stderr, "getphdr() failed: %s.", elf_errmsg(-1));
            phdr_index = 0;
            exit(1);
        }
        phdr_index ++;
        if(phdr.p_type == PT_LOAD)
            result++;
    }
    phdr_index = 0;
    return result;
}
Esempio n. 11
0
static void processProgHeaders(elfInfo *ei, GElf_Ehdr *ehdr)
{
    for (size_t i = 0; i < ehdr->e_phnum; i++) {
	GElf_Phdr mem;
	GElf_Phdr *phdr = gelf_getphdr(ei->elf, i, &mem);

	if (phdr && phdr->p_type == PT_INTERP) {
	    size_t maxsize;
	    char * filedata = elf_rawfile(ei->elf, &maxsize);

	    if (filedata && phdr->p_offset < maxsize) {
		ei->interp = rstrdup(filedata + phdr->p_offset);
		break;
	    }
	}
    }
}
Esempio n. 12
0
uint8_t *dump_program_data(Elf *elf_object, int *size)
{
	uint8_t *buffer = NULL;
	Elf_Data *data = NULL;
	size_t phdr_num;
	size_t max_paddr = 0;
	GElf_Phdr phdr;

	*size = 0;

	int ret = elf_getphdrnum(elf_object, &phdr_num);
	if (ret) {
		printf("Problem during ELF parsing\n");
		return NULL;
	}

	if (phdr_num == 0)
		return NULL;

	for (int i = 0; i < phdr_num; i++) {
		if (gelf_getphdr(elf_object, i, &phdr) != &phdr) {
			printf("Problem during ELF parsing\n");
			return NULL;
		}

		printf("Program header %d: addr 0x%08X,", i, (unsigned int)phdr.p_paddr);
		printf(" size 0x%08X\n", (unsigned int)phdr.p_filesz);

		if (phdr.p_paddr + phdr.p_filesz >= max_paddr) {
			max_paddr = phdr.p_paddr + phdr.p_filesz;
			buffer = realloc(buffer, max_paddr);
		}

		data = elf_getdata_rawchunk(elf_object, phdr.p_offset, phdr.p_filesz, ELF_T_BYTE);
		if (data != NULL)
			memcpy(buffer + phdr.p_paddr, data->d_buf, data->d_size);
		else {
			printf("Couldn't load program data chunk\n");
			return NULL;
		}
	}

	*size = max_paddr;
	return buffer;
}
Esempio n. 13
0
/** Get the build-id (NT_GNU_BUILD_ID) from the ELF file
 *
 *  This build-id may is used to locate an external debug (DWARF) file
 *  for this ELF file.
 *
 *  @param  elf libelf handle for an ELF file
 *  @return build-id for this ELF file (or an empty vector if none is found)
 */
static
std::vector<char> get_build_id(Elf* elf)
{
#ifdef __linux
  // Summary: the GNU build ID is stored in a ("GNU, NT_GNU_BUILD_ID) note
  // found in a PT_NOTE entry in the program header table.

  size_t phnum;
  if (elf_getphdrnum (elf, &phnum) != 0)
    xbt_die("Could not read program headers");

  // Iterate over the program headers and find the PT_NOTE ones:
  for (size_t i = 0; i < phnum; ++i) {
    GElf_Phdr phdr_temp;
    GElf_Phdr *phdr = gelf_getphdr(elf, i, &phdr_temp);
    if (phdr->p_type != PT_NOTE)
      continue;

    Elf_Data* data = elf_getdata_rawchunk(elf, phdr->p_offset, phdr->p_filesz, ELF_T_NHDR);

    // Iterate over the notes and find the NT_GNU_BUILD_ID one:
    size_t pos = 0;
    while (pos < data->d_size) {
      GElf_Nhdr nhdr;
      // Location of the name within Elf_Data:
      size_t name_pos;
      size_t desc_pos;
      pos = gelf_getnote(data, pos, &nhdr, &name_pos, &desc_pos);
      // A build ID note is identified by the pair ("GNU", NT_GNU_BUILD_ID)
      // (a namespace and a type within this namespace):
      if (nhdr.n_type == NT_GNU_BUILD_ID
          && nhdr.n_namesz == sizeof("GNU")
          && memcmp((char*) data->d_buf + name_pos, "GNU", sizeof("GNU")) == 0) {
        XBT_DEBUG("Found GNU/NT_GNU_BUILD_ID note");
        char* start = (char*) data->d_buf + desc_pos;
        char* end = (char*) start + nhdr.n_descsz;
        return std::vector<char>(start, end);
      }
    }

  }
#endif
  return std::vector<char>();
}
Esempio n. 14
0
File: elfp.c Progetto: nwnk/elflets
static int
has_dt_debug(Elf *elf, GElf_Ehdr *ehdr)
{
    int i;

    for (i = 0; i < ehdr->e_phnum; i++) {
	GElf_Phdr phdr;
	GElf_Shdr shdr;
	Elf_Scn *scn;
	Elf_Data *data;
	unsigned int j;

	if (gelf_getphdr(elf, i, &phdr) == NULL)
	    continue;

	if (phdr.p_type != PT_DYNAMIC)
	    continue;

	scn = gelf_offscn(elf, phdr.p_offset);

	if (gelf_getshdr(scn, &shdr) == NULL)
	    continue;

	if (shdr.sh_type != SHT_DYNAMIC)
	    continue;

	if ((data = elf_getdata(scn, NULL)) == NULL)
	    continue;

	for (j = 0;
	     j < shdr.sh_size / gelf_fsize(elf, ELF_T_DYN, 1, EV_CURRENT);
	     j++) {
	    GElf_Dyn dyn;
	    if (gelf_getdyn(data, j, &dyn)) {
		if (dyn.d_tag == DT_DEBUG)
		    return 1;
	    }
	}
    }

    return 0;
}
Esempio n. 15
0
static int read_phdr(struct elf32_info *info, FILE *in)
{
	int i;

	if (info->file_ehdr.e_phnum > MAX_PHDRS) {
		printc_err("elf32: too many program headers: %d\n",
			info->file_ehdr.e_phnum);
		return -1;
	}

	for (i = 0; i < info->file_ehdr.e_phnum; i++) {
		GElf_Phdr *phdr = &info->file_phdrs[i];
		if (gelf_getphdr(info->elf, i, phdr) != phdr) {
			printc_err("elf32: can't read phdr %d: %s\n",
				i, elf_errmsg(elf_errno()));
		}
	}

	return 0;
}
Esempio n. 16
0
/* Translate addresses into file offsets.
   OFFS[*] start out zero and remain zero if unresolved.  */
static void
find_offsets (Elf *elf, GElf_Addr main_bias, size_t phnum, size_t n,
	      GElf_Addr addrs[n], GElf_Off offs[n])
{
  size_t unsolved = n;
  for (size_t i = 0; i < phnum; ++i)
    {
      GElf_Phdr phdr_mem;
      GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
      if (phdr != NULL && phdr->p_type == PT_LOAD && phdr->p_memsz > 0)
	for (size_t j = 0; j < n; ++j)
	  if (offs[j] == 0
	      && addrs[j] >= phdr->p_vaddr + main_bias
	      && addrs[j] - (phdr->p_vaddr + main_bias) < phdr->p_filesz)
	    {
	      offs[j] = addrs[j] - (phdr->p_vaddr + main_bias) + phdr->p_offset;
	      if (--unsolved == 0)
		break;
	    }
    }
}
Esempio n. 17
0
long ElfData::getElfMem() {
	long totalMem = 0;

	int i;
	size_t n;
	/* Causes problems on intel C++ */
	//assert(elf_getphnum(e, &n) == 1);
	GElf_Ehdr elfhdr;
	assert(gelf_getehdr(e, &elfhdr) != 0);
	n = (size_t) elfhdr.e_phnum;

	for (i = 0; i < n; i++) {
		if (gelf_getphdr(e, i, &phdr) != &phdr)
			return totalMem;

		totalMem += (long) phdr.p_memsz;

	}
	return totalMem;

}
Esempio n. 18
0
static const value_t *
elf_get_phdrs(const value_t *this_fn, parser_state_t *state,
              const char *filename, int binfd,
              Elf *e, GElf_Ehdr *ehdr, void *arg)
{
   const value_t *resval = NULL;
   size_t n;

   if (elf_getphdrnum(e, &n) != 0)
      parser_report(state, "ELF file has unknown number of segments - %s\n",
                    elf_errmsg(-1));
   else {
      size_t i;
      dir_t *partdir = dir_vec_new();

      for (i = 0; i < n; i++) {
         GElf_Phdr phdr;
         dir_t *pdir = dir_id_new();
         if (gelf_getphdr(e, i, &phdr) != &phdr)
            parser_report(state,
                          "ELF segment %d unretrievable - %s\n",
                          i, elf_errmsg(-1));
         else {
            dir_string_set(pdir,"type"   , value_int_new(phdr.p_type));
            dir_string_set(pdir,"offset" , value_int_new(phdr.p_offset));
            dir_string_set(pdir,"vaddr"  , value_int_new(phdr.p_vaddr));
            dir_string_set(pdir,"paddr"  , value_int_new(phdr.p_paddr));
            dir_string_set(pdir,"filesz" , value_int_new(phdr.p_filesz));
            dir_string_set(pdir,"memsz"  , value_int_new(phdr.p_memsz));
            dir_string_set(pdir,"flags"  , value_int_new(phdr.p_flags));
            dir_string_set(pdir,"align"  , value_int_new(phdr.p_align));

            dir_int_set(partdir, i, dir_value(pdir));
         }
      }
      resval = dir_value(partdir);
   }

   return resval;
}
Esempio n. 19
0
int elf_utils_copy(Elf *dest, Elf *source)
{
	GElf_Ehdr ehdr;
	Elf_Scn *dst_scn, *src_scn;
	GElf_Shdr shdr;
	Elf_Data *dst_data, *src_data;
	size_t segment_count, segndx;
	GElf_Phdr phdr;

	ELF_ASSERT(elf_flagelf(dest, ELF_C_SET, ELF_F_LAYOUT));

	ELF_ASSERT(gelf_getehdr(source, &ehdr));
	ELF_ASSERT(gelf_newehdr(dest, gelf_getclass(source)));
	ELF_ASSERT(gelf_update_ehdr(dest, &ehdr));

	src_scn = NULL;
	while ((src_scn = elf_nextscn(source, src_scn)) != NULL) {
		ELF_ASSERT(gelf_getshdr(src_scn, &shdr));
		ELF_ASSERT(dst_scn = elf_newscn(dest));
		ELF_ASSERT(gelf_update_shdr(dst_scn, &shdr));

		src_data = NULL;
		while ((src_data = elf_getdata(src_scn, src_data)) != NULL) {
			ELF_ASSERT(dst_data = elf_newdata(dst_scn));
			memcpy(dst_data, src_data, sizeof(Elf_Data));
		}
	}

	ELF_ASSERT(elf_getphdrnum(source, &segment_count) == 0);
	ELF_ASSERT(gelf_newphdr(dest, segment_count));

	for (segndx = 0; segndx < segment_count; segndx++) {
		ELF_ASSERT(gelf_getphdr(source, segndx, &phdr));
		ELF_ASSERT(gelf_update_phdr(dest, segndx, &phdr));
	}
		
	return 1;
failure:
	return 0;
}
Esempio n. 20
0
int
find_rewritable_segment(elf_data_t *elf, inject_data_t *inject, char **err)
{
  int ret;
  size_t i, n;

  /* Get number of program headers */
  ret = elf_getphdrnum(elf->e, &n);
  if(ret != 0) {
    (*err) = "cannot find any program headers";
    return -1;
  }

  /* Look for a rewritable program header */
  for(i = 0; i < n; i++) {
    if(!gelf_getphdr(elf->e, i, &inject->phdr)) {
      (*err) = "failed to get program header";
      return -1;
    }

    switch(inject->phdr.p_type) {
    case PT_NOTE:
      inject->pidx = i;
      return 0;
    case PT_NULL:
    case PT_LOAD:
    case PT_DYNAMIC:
    case PT_INTERP:
    case PT_SHLIB:
    case PT_PHDR:
    case PT_TLS:
    default:
      break;
    }
  }

  (*err) = "cannot find segment to rewrite";
  return -1;
}
Esempio n. 21
0
static int init_phdrs()
{
	Elf* e = g.e;
	size_t phdr_num = g.phdrnum;
	GElf_Phdr* buf = (GElf_Phdr*) malloc(phdr_num * sizeof(GElf_Phdr));
	if (buf == NULL) {
		errx (EXIT_FAILURE , "malloc failed with GElf_Phdr*");
		return -1;
	}

	int i;
	for (i=0; i<phdr_num; i++) {
		//GElf_Phdr* phdr = (GElf_Phdr*) malloc(sizeof(GElf_Phdr));
		GElf_Phdr* phdr = buf + i;
		if (gelf_getphdr (e, i, phdr) != phdr) {
			errx(EXIT_FAILURE, " gelf_getphdr() failed : %s.",
			       elf_errmsg(-1));
		}
	}

	g.phdrs = buf;
	return 0;
}
Esempio n. 22
0
int elfu_eCheck(Elf *e)
{
  int elfclass;
  GElf_Ehdr ehdr;
  GElf_Phdr *phdrs = NULL;
  GElf_Shdr *shdrs = NULL;
  size_t i, j, numPhdr, numShdr;
  int retval = 0;

  assert(e);

  elfclass = gelf_getclass(e);
  if (elfclass == ELFCLASSNONE) {
    ELFU_WARNELF("getclass");
    goto ERROR;
  }

  if (!gelf_getehdr(e, &ehdr)) {
    ELFU_WARNELF("gelf_getehdr");
    goto ERROR;
  }

  if (ehdr.e_machine != EM_386 && ehdr.e_machine != EM_X86_64) {
    ELFU_WARN("Sorry, only x86-32 and x86-64 ELF files are supported at the moment.\n");
    goto ERROR;
  }

  if (elf_getphdrnum(e, &numPhdr)) {
    ELFU_WARNELF("elf_getphdrnum");
    goto ERROR;
  }

  if (elf_getshdrnum(e, &numShdr)) {
    ELFU_WARNELF("elf_getshdrnum");
    goto ERROR;
  }


  if (numPhdr > 0) {
    phdrs = malloc(numPhdr * sizeof(GElf_Phdr));
    if (!phdrs) {
      ELFU_WARN("elfu_eCheck: malloc() failed for phdrs.\n");
      goto ERROR;
    }

    /* Attempt to load all PHDRs at once to catch any errors early */
    for (i = 0; i < numPhdr; i++) {
      GElf_Phdr phdr;
      if (gelf_getphdr(e, i, &phdr) != &phdr) {
        ELFU_WARN("gelf_getphdr() failed for #%d: %s\n", i, elf_errmsg(-1));
        goto ERROR;
      }

      phdrs[i] = phdr;
    }

    /* Check that LOAD PHDR memory ranges do not overlap, and that others
     * are either fully contained in a LOAD range, or not at all. */
    for (i = 0; i < numPhdr; i++) {
      if (phdrs[i].p_type != PT_LOAD) {
        continue;
      }

      for (j = 0; j < numPhdr; j++) {
        if (j == i || phdrs[j].p_type != PT_LOAD) {
          continue;
        }

        if (OVERLAPPING(phdrs[i].p_vaddr, phdrs[i].p_memsz,
                        phdrs[j].p_vaddr, phdrs[j].p_memsz)) {
          if (phdrs[j].p_type == PT_LOAD) {
            ELFU_WARN("elfu_eCheck: Found LOAD PHDRs that overlap in memory.\n");
            goto ERROR;
          } else if (!FULLY_OVERLAPPING(phdrs[i].p_vaddr, phdrs[i].p_memsz,
                                        phdrs[j].p_vaddr, phdrs[j].p_memsz)) {
            ELFU_WARN("elfu_eCheck: PHDRs %d and %d partially overlap in memory.\n", i, j);
            goto ERROR;
          }
        }
      }
    }
  }


  if (numShdr > 1) {
    /* SHDRs should not overlap with PHDRs. */
    if (OVERLAPPING(ehdr.e_shoff, numShdr * ehdr.e_shentsize,
                    ehdr.e_phoff, numPhdr * ehdr.e_phentsize)) {
      ELFU_WARN("elfu_eCheck: SHDRs overlap with PHDRs.\n");
      goto ERROR;
    }

    shdrs = malloc(numShdr * sizeof(GElf_Shdr));
    if (!shdrs) {
      ELFU_WARN("elfu_eCheck: malloc() failed for shdrs.\n");
      goto ERROR;
    }

    /* Attempt to load all SHDRs at once to catch any errors early */
    for (i = 1; i < numShdr; i++) {
      Elf_Scn *scn;
      GElf_Shdr shdr;

      scn = elf_getscn(e, i);
      if (!scn) {
        ELFU_WARN("elf_getscn() failed for #%d: %s\n", i, elf_errmsg(-1));
      }

      if (gelf_getshdr(scn, &shdr) != &shdr) {
        ELFU_WARNELF("gelf_getshdr");
        goto ERROR;
      }

      shdrs[i] = shdr;
    }


    /* Check that Section memory ranges do not overlap.
     * NB: Section 0 is reserved and thus ignored. */
    for (i = 1; i < numShdr; i++) {
      /* Section should not overlap with EHDR. */
      if (shdrs[i].sh_offset == 0) {
        ELFU_WARN("elfu_eCheck: Section %d overlaps with EHDR.\n", i);
        goto ERROR;
      }

      /* Section should not overlap with PHDRs. */
      if (OVERLAPPING(shdrs[i].sh_offset, SCNFILESIZE(&shdrs[i]),
                      ehdr.e_phoff, numPhdr * ehdr.e_phentsize)) {
        ELFU_WARN("elfu_eCheck: Section %d overlaps with PHDR.\n", i);
        goto ERROR;
      }

      /* Section should not overlap with SHDRs. */
      if (OVERLAPPING(shdrs[i].sh_offset, SCNFILESIZE(&shdrs[i]),
                      ehdr.e_shoff, numShdr * ehdr.e_shentsize)) {
        ELFU_WARN("elfu_eCheck: Section %d overlaps with SHDRs.\n", i);
        goto ERROR;
      }

      for (j = 1; j < numShdr; j++) {
        if (j == i) {
          continue;
        }

        /* Sections must not overlap in memory. */
        if (shdrs[i].sh_addr != 0
            && shdrs[j].sh_addr != 0
            && OVERLAPPING(shdrs[i].sh_addr, shdrs[i].sh_size,
                           shdrs[j].sh_addr, shdrs[j].sh_size)) {
          ELFU_WARN("elfu_eCheck: Sections %d and %d overlap in memory.\n", i, j);
          goto ERROR;
        }

        /* Sections must not overlap in file. */
        if (OVERLAPPING(shdrs[i].sh_offset, SCNFILESIZE(&shdrs[i]),
                        shdrs[j].sh_offset, SCNFILESIZE(&shdrs[j]))) {
          ELFU_WARN("elfu_eCheck: Sections %d and %d overlap in file.\n", i, j);
          goto ERROR;
        }

        /* We may not have more than one symbol table */
        if (shdrs[i].sh_type == SHT_SYMTAB && shdrs[j].sh_type == SHT_SYMTAB) {
          ELFU_WARN("elfu_eCheck: Found more than one SYMTAB section.\n");
          goto ERROR;
        }

        /* We may not have more than one dynamic symbol table */
        if (shdrs[i].sh_type == SHT_DYNSYM && shdrs[j].sh_type == SHT_DYNSYM) {
          ELFU_WARN("elfu_eCheck: Found more than one DYNSYM section.\n");
          goto ERROR;
        }
      }

      /* Section addr/offset should match parent PHDR.
       * Find parent PHDR: */
      for (j = 0; j < numPhdr; j++) {
        if (PHDR_CONTAINS_SCN_IN_MEMORY(&phdrs[j], &shdrs[i])) {
          GElf_Off shoff = phdrs[j].p_offset + (shdrs[i].sh_addr - phdrs[j].p_vaddr);

          if ((shdrs[i].sh_offset != shoff && shdrs[i].sh_type != SHT_NOBITS)
              || !PHDR_CONTAINS_SCN_IN_FILE(&phdrs[j], &shdrs[i])) {
            ELFU_WARN("elfu_eCheck: SHDR %d and PHDR %d report conflicting file/memory regions.\n", i, j);
            goto ERROR;
          }
        }
      }

      /* sh_link members should not point to sections out of range. */
      if (shdrs[i].sh_link >= numShdr) {
        ELFU_WARN("elfu_eCheck: Bogus sh_link in SHDR %d.\n", i);
      }
    }
  }


  DONE:
  if (phdrs) {
    free(phdrs);
  }
  if (shdrs) {
    free(shdrs);
  }
  return retval;

  ERROR:
  ELFU_WARN("elfu_eCheck: Errors found.\n");
  retval = -1;
  goto DONE;
}
Esempio n. 23
0
short
get_signal_number(Elf *e, const char *elf_file)
{
    const char NOTE_CORE[] = "CORE";

    size_t nphdr;
    if (elf_getphdrnum(e, &nphdr) != 0)
    {
        warn_elf("elf_getphdrnum");
        return 0;
    }

    /* Go through phdrs, look for prstatus note */
    int i;
    for (i = 0; i < nphdr; i++)
    {
        GElf_Phdr phdr;
        if (gelf_getphdr(e, i, &phdr) != &phdr)
        {
            warn_elf("gelf_getphdr");
            continue;
        }

        if (phdr.p_type != PT_NOTE)
        {
            continue;
        }

        Elf_Data *data, *name_data, *desc_data;
        GElf_Nhdr nhdr;
        size_t note_offset = 0;
        size_t name_offset, desc_offset;
        /* Elf_Data buffers are freed when elf_end is called. */
        data = elf_getdata_rawchunk(e, phdr.p_offset, phdr.p_filesz,
                                    ELF_T_NHDR);
        if (!data)
        {
            warn_elf("elf_getdata_rawchunk");
            continue;
        }

        while ((note_offset = gelf_getnote(data, note_offset, &nhdr,
                                           &name_offset, &desc_offset)) != 0)
        {
            /*
            printf("Note: type:%x name:%x+%d desc:%x+%d\n", nhdr.n_type,
                   name_offset, nhdr.n_namesz, desc_offset, nhdr.n_descsz);
            */

            if (nhdr.n_type != NT_PRSTATUS
                || nhdr.n_namesz < sizeof(NOTE_CORE))
                continue;

            name_data = elf_getdata_rawchunk(e, phdr.p_offset + name_offset,
                                             nhdr.n_namesz, ELF_T_BYTE);
            desc_data = elf_getdata_rawchunk(e, phdr.p_offset + desc_offset,
                                             nhdr.n_descsz, ELF_T_BYTE);
            if (!(name_data && desc_data))
                continue;

            if (name_data->d_size < sizeof(NOTE_CORE))
                continue;

            if (strcmp(NOTE_CORE, name_data->d_buf))
                continue;

            if (desc_data->d_size != sizeof(struct elf_prstatus))
            {
                warn("PRSTATUS core note of size %zu found, expected size: %zu",
                    desc_data->d_size, sizeof(struct elf_prstatus));
                continue;
            }

            struct elf_prstatus *prstatus = (struct elf_prstatus*)desc_data->d_buf;
            short signal = prstatus->pr_cursig;
            if (signal)
                return signal;
        }
    }

    return 0;
}
/* Search an ELF file for a ".gnu_debuglink" section.  */
static const char *
find_debuglink (Elf *elf, GElf_Word *crc)
{
  size_t shstrndx;
  if (elf_getshstrndx (elf, &shstrndx) < 0)
    return NULL;

  Elf_Scn *scn = NULL;
  while ((scn = elf_nextscn (elf, scn)) != NULL)
    {
      GElf_Shdr shdr_mem;
      GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
      if (shdr == NULL)
	return NULL;

      const char *name = elf_strptr (elf, shstrndx, shdr->sh_name);
      if (name == NULL)
	return NULL;

      if (!strcmp (name, ".gnu_debuglink"))
	break;
    }

  if (scn == NULL)
    return NULL;

  /* Found the .gnu_debuglink section.  Extract its contents.  */
  Elf_Data *rawdata = elf_rawdata (scn, NULL);
  if (rawdata == NULL)
    return NULL;

  Elf_Data crcdata =
    {
      .d_type = ELF_T_WORD,
      .d_buf = crc,
      .d_size = sizeof *crc,
      .d_version = EV_CURRENT,
    };
  Elf_Data conv =
    {
      .d_type = ELF_T_WORD,
      .d_buf = rawdata->d_buf + rawdata->d_size - sizeof *crc,
      .d_size = sizeof *crc,
      .d_version = EV_CURRENT,
    };

  GElf_Ehdr ehdr_mem;
  GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
  if (ehdr == NULL)
    return NULL;

  Elf_Data *d = gelf_xlatetom (elf, &crcdata, &conv, ehdr->e_ident[EI_DATA]);
  if (d == NULL)
    return NULL;
  assert (d == &crcdata);

  return rawdata->d_buf;
}


/* Find the separate debuginfo file for this module and open libelf on it.
   When we return success, MOD->debug is set up.  */
static Dwfl_Error
find_debuginfo (Dwfl_Module *mod)
{
  if (mod->debug.elf != NULL)
    return DWFL_E_NOERROR;

  GElf_Word debuglink_crc = 0;
  const char *debuglink_file = find_debuglink (mod->main.elf, &debuglink_crc);

  mod->debug.fd = (*mod->dwfl->callbacks->find_debuginfo) (MODCB_ARGS (mod),
							   mod->main.name,
							   debuglink_file,
							   debuglink_crc,
							   &mod->debug.name);
  return open_elf (mod, &mod->debug);
}


/* Try to find a symbol table in FILE.
   Returns DWFL_E_NOERROR if a proper one is found.
   Returns DWFL_E_NO_SYMTAB if not, but still sets results for SHT_DYNSYM.  */
static Dwfl_Error
load_symtab (struct dwfl_file *file, struct dwfl_file **symfile,
	     Elf_Scn **symscn, Elf_Scn **xndxscn,
	     size_t *syments, GElf_Word *strshndx)
{
  bool symtab = false;
  Elf_Scn *scn = NULL;
  while ((scn = elf_nextscn (file->elf, scn)) != NULL)
    {
      GElf_Shdr shdr_mem, *shdr = gelf_getshdr (scn, &shdr_mem);
      if (shdr != NULL)
	switch (shdr->sh_type)
	  {
	  case SHT_SYMTAB:
	    symtab = true;
	    *symscn = scn;
	    *symfile = file;
	    *strshndx = shdr->sh_link;
	    *syments = shdr->sh_size / shdr->sh_entsize;
	    if (*xndxscn != NULL)
	      return DWFL_E_NOERROR;
	    break;

	  case SHT_DYNSYM:
	    if (symtab)
	      break;
	    /* Use this if need be, but keep looking for SHT_SYMTAB.  */
	    *symscn = scn;
	    *symfile = file;
	    *strshndx = shdr->sh_link;
	    *syments = shdr->sh_size / shdr->sh_entsize;
	    break;

	  case SHT_SYMTAB_SHNDX:
	    *xndxscn = scn;
	    if (symtab)
	      return DWFL_E_NOERROR;
	    break;

	  default:
	    break;
	  }
    }

  if (symtab)
    /* We found one, though no SHT_SYMTAB_SHNDX to go with it.  */
    return DWFL_E_NOERROR;

  /* We found no SHT_SYMTAB, so any SHT_SYMTAB_SHNDX was bogus.
     We might have found an SHT_DYNSYM and set *SYMSCN et al though.  */
  *xndxscn = NULL;
  return DWFL_E_NO_SYMTAB;
}


/* Translate addresses into file offsets.
   OFFS[*] start out zero and remain zero if unresolved.  */
static void
find_offsets (Elf *elf, const GElf_Ehdr *ehdr, size_t n,
	      GElf_Addr addrs[n], GElf_Off offs[n])
{
  size_t unsolved = n;
  for (uint_fast16_t i = 0; i < ehdr->e_phnum; ++i)
    {
      GElf_Phdr phdr_mem;
      GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
      if (phdr != NULL && phdr->p_type == PT_LOAD && phdr->p_memsz > 0)
	for (size_t j = 0; j < n; ++j)
	  if (offs[j] == 0
	      && addrs[j] >= phdr->p_vaddr
	      && addrs[j] - phdr->p_vaddr < phdr->p_filesz)
	    {
	      offs[j] = addrs[j] - phdr->p_vaddr + phdr->p_offset;
	      if (--unsolved == 0)
		break;
	    }
    }
}
/* Try to find a dynamic symbol table via phdrs.  */
static void
find_dynsym (Dwfl_Module *mod)
{
  GElf_Ehdr ehdr_mem;
  GElf_Ehdr *ehdr = gelf_getehdr (mod->main.elf, &ehdr_mem);

  for (uint_fast16_t i = 0; i < ehdr->e_phnum; ++i)
    {
      GElf_Phdr phdr_mem;
      GElf_Phdr *phdr = gelf_getphdr (mod->main.elf, i, &phdr_mem);
      if (phdr == NULL)
	break;

      if (phdr->p_type == PT_DYNAMIC)
	{
	  /* Examine the dynamic section for the pointers we need.  */

	  Elf_Data *data = elf_getdata_rawchunk (mod->main.elf,
						 phdr->p_offset, phdr->p_filesz,
						 ELF_T_DYN);
	  if (data == NULL)
	    continue;

	  enum
	    {
	      i_symtab,
	      i_strtab,
	      i_hash,
	      i_gnu_hash,
	      i_max
	    };
	  GElf_Addr addrs[i_max] = { 0, };
	  GElf_Xword strsz = 0;
	  size_t n = data->d_size / gelf_fsize (mod->main.elf,
						ELF_T_DYN, 1, EV_CURRENT);
	  for (size_t j = 0; j < n; ++j)
	    {
	      GElf_Dyn dyn_mem;
	      GElf_Dyn *dyn = gelf_getdyn (data, j, &dyn_mem);
	      if (dyn != NULL)
		switch (dyn->d_tag)
		  {
		  case DT_SYMTAB:
		    addrs[i_symtab] = dyn->d_un.d_ptr;
		    continue;

		  case DT_HASH:
		    addrs[i_hash] = dyn->d_un.d_ptr;
		    continue;

		  case DT_GNU_HASH:
		    addrs[i_gnu_hash] = dyn->d_un.d_ptr;
		    continue;

		  case DT_STRTAB:
		    addrs[i_strtab] = dyn->d_un.d_ptr;
		    continue;

		  case DT_STRSZ:
		    strsz = dyn->d_un.d_val;
		    continue;

		  default:
		    continue;

		  case DT_NULL:
		    break;
		  }
	      break;
	    }

	  /* Translate pointers into file offsets.  */
	  GElf_Off offs[i_max] = { 0, };
	  find_offsets (mod->main.elf, ehdr, i_max, addrs, offs);

	  /* Figure out the size of the symbol table.  */
	  if (offs[i_hash] != 0)
	    {
	      /* In the original format, .hash says the size of .dynsym.  */

	      size_t entsz = SH_ENTSIZE_HASH (ehdr);
	      data = elf_getdata_rawchunk (mod->main.elf,
					   offs[i_hash] + entsz, entsz,
					   entsz == 4 ? ELF_T_WORD
					   : ELF_T_XWORD);
	      if (data != NULL)
		mod->syments = (entsz == 4
				? *(const GElf_Word *) data->d_buf
				: *(const GElf_Xword *) data->d_buf);
	    }
	  if (offs[i_gnu_hash] != 0 && mod->syments == 0)
	    {
	      /* In the new format, we can derive it with some work.  */

	      const struct
	      {
		Elf32_Word nbuckets;
		Elf32_Word symndx;
		Elf32_Word maskwords;
		Elf32_Word shift2;
	      } *header;

	      data = elf_getdata_rawchunk (mod->main.elf, offs[i_gnu_hash],
					   sizeof *header, ELF_T_WORD);
	      if (data != NULL)
		{
		  header = data->d_buf;
		  Elf32_Word nbuckets = header->nbuckets;
		  Elf32_Word symndx = header->symndx;
		  GElf_Off buckets_at = (offs[i_gnu_hash] + sizeof *header
					 + (gelf_getclass (mod->main.elf)
					    * sizeof (Elf32_Word)
					    * header->maskwords));

		  data = elf_getdata_rawchunk (mod->main.elf, buckets_at,
					       nbuckets * sizeof (Elf32_Word),
					       ELF_T_WORD);
		  if (data != NULL && symndx < nbuckets)
		    {
		      const Elf32_Word *const buckets = data->d_buf;
		      Elf32_Word maxndx = symndx;
		      for (Elf32_Word bucket = 0; bucket < nbuckets; ++bucket)
			if (buckets[bucket] > maxndx)
			  maxndx = buckets[bucket];

		      GElf_Off hasharr_at = (buckets_at
					     + nbuckets * sizeof (Elf32_Word));
		      hasharr_at += (maxndx - symndx) * sizeof (Elf32_Word);
		      do
			{
			  data = elf_getdata_rawchunk (mod->main.elf,
						       hasharr_at,
						       sizeof (Elf32_Word),
						       ELF_T_WORD);
			  if (data != NULL
			      && (*(const Elf32_Word *) data->d_buf & 1u))
			    {
			      mod->syments = maxndx + 1;
			      break;
			    }
			  ++maxndx;
			  hasharr_at += sizeof (Elf32_Word);
			} while (data != NULL);
		    }
		}
	    }
	  if (offs[i_strtab] > offs[i_symtab] && mod->syments == 0)
	    mod->syments = ((offs[i_strtab] - offs[i_symtab])
			    / gelf_fsize (mod->main.elf,
					  ELF_T_SYM, 1, EV_CURRENT));

	  if (mod->syments > 0)
	    {
	      mod->symdata = elf_getdata_rawchunk (mod->main.elf,
						   offs[i_symtab],
						   gelf_fsize (mod->main.elf,
							       ELF_T_SYM,
							       mod->syments,
							       EV_CURRENT),
						   ELF_T_SYM);
	      if (mod->symdata != NULL)
		{
		  mod->symstrdata = elf_getdata_rawchunk (mod->main.elf,
							  offs[i_strtab],
							  strsz,
							  ELF_T_BYTE);
		  if (mod->symstrdata == NULL)
		    mod->symdata = NULL;
		}
	      if (mod->symdata == NULL)
		mod->symerr = DWFL_E (LIBELF, elf_errno ());
	      else
		{
		  mod->symfile = &mod->main;
		  mod->symerr = DWFL_E_NOERROR;
		}
	      return;
	    }
	}
    }
}
Esempio n. 26
0
/*
 * Given a core dump file, this function maps program headers to segments.
 */
static int
handle_core(char const *name, Elf *elf, GElf_Ehdr *elfhdr)
{
	GElf_Phdr phdr;
	uint32_t i;
	char *core_cmdline;
	const char *seg_name;

	if (name == NULL || elf == NULL || elfhdr == NULL)
		return (RETURN_DATAERR);
	if  (elfhdr->e_shnum != 0 || elfhdr->e_type != ET_CORE)
		return (RETURN_DATAERR);

	seg_name = core_cmdline = NULL;
	if (style == STYLE_SYSV)
		sysv_header(name, NULL);
	else
		berkeley_header();

	for (i = 0; i < elfhdr->e_phnum; i++) {
		if (gelf_getphdr(elf, i, &phdr) != NULL) {
			if (phdr.p_type == PT_NOTE) {
				handle_phdr(elf, elfhdr, &phdr, i, "note");
				handle_core_note(elf, elfhdr, &phdr,
				    &core_cmdline);
			} else {
				switch(phdr.p_type) {
				case PT_NULL:
					seg_name = "null";
					break;
				case PT_LOAD:
					seg_name = "load";
					break;
				case PT_DYNAMIC:
					seg_name = "dynamic";
					break;
				case PT_INTERP:
					seg_name = "interp";
					break;
				case PT_SHLIB:
					seg_name = "shlib";
					break;
				case PT_PHDR:
					seg_name = "phdr";
					break;
				case PT_GNU_EH_FRAME:
					seg_name = "eh_frame_hdr";
					break;
				case PT_GNU_STACK:
					seg_name = "stack";
					break;
				default:
					seg_name = "segment";
				}
				handle_phdr(elf, elfhdr, &phdr, i, seg_name);
			}
		}
	}

	if (style == STYLE_BERKELEY) {
		if (core_cmdline != NULL) {
			berkeley_footer(core_cmdline, name,
			    "core file invoked as");
		} else {
			berkeley_footer(core_cmdline, name, "core file");
		}
	} else {
		sysv_footer();
		if (core_cmdline != NULL) {
			(void) printf(" (core file invoked as %s)\n\n",
			    core_cmdline);
		} else {
			(void) printf(" (core file)\n\n");
		}
	}
	free(core_cmdline);
	return (RETURN_OK);
}
Esempio n. 27
0
int
main (int argc, char *argv[])
{
  Elf *elf;
  int fd;
  GElf_Ehdr ehdr;
  int cnt;

  fd = open (argv[1], O_RDONLY);
  if (fd == -1)
    {
      printf ("cannot open \"%s\": %s\n", argv[1], strerror (errno));
      exit (1);
    }

  elf_version (EV_CURRENT);

  elf = elf_begin (fd, ELF_C_READ, NULL);
  if (elf == NULL)
    {
      printf ("cannot open ELF file: %s\n", elf_errmsg (-1));
      exit (1);
    }

  if (elf_kind (elf) != ELF_K_ELF)
    {
      printf ("\"%s\" is not an ELF file\n", argv[1]);
      exit (1);
    }

  if (gelf_getehdr (elf, &ehdr) == NULL)
    {
      printf ("cannot get the ELF header: %s\n", elf_errmsg (-1));
      exit (1);
    }

  printf ("idx type    %*s %*s %*s %*s %*s  align flags\n",
	  gelf_getclass (elf) == ELFCLASS32 ? 9 : 17, "offset",
	  gelf_getclass (elf) == ELFCLASS32 ? 10 : 18, "vaddr",
	  gelf_getclass (elf) == ELFCLASS32 ? 10 : 18, "paddr",
	  gelf_getclass (elf) == ELFCLASS32 ? 9 : 12, "filesz",
	  gelf_getclass (elf) == ELFCLASS32 ? 9 : 12, "memsz");

  for (cnt = 0; cnt < ehdr.e_phnum; ++cnt)
    {
      static const char *typenames[] =
      {
	[PT_NULL] = "NULL",
	[PT_LOAD] = "LOAD",
	[PT_DYNAMIC] = "DYNAMIC",
	[PT_INTERP] = "INTERP",
	[PT_NOTE] = "NOTE",
	[PT_SHLIB] = "SHLIB",
	[PT_PHDR] = "PHDR"
      };
      GElf_Phdr mem;
      GElf_Phdr *phdr = gelf_getphdr (elf, cnt, &mem);
      char buf[19];
      const char *p_type = typenames[phdr->p_type];

      /* If we don't know the name of the type we use the number value.  */
      if (phdr->p_type >= PT_NUM)
	{
	  snprintf (buf, sizeof (buf), "%x", phdr->p_type);
	  p_type = buf;
	}

      printf ("%3d %-7s %#0*llx %#0*llx %#0*llx %#0*llx %#0*llx %#6llx ",
	      cnt, p_type,
	      gelf_getclass (elf) == ELFCLASS32 ? 9 : 17,
	      (unsigned long long int) phdr->p_offset,
	      gelf_getclass (elf) == ELFCLASS32 ? 10 : 18,
	      (unsigned long long int) phdr->p_vaddr,
	      gelf_getclass (elf) == ELFCLASS32 ? 10 : 18,
	      (unsigned long long int) phdr->p_paddr,
	      gelf_getclass (elf) == ELFCLASS32 ? 9 : 12,
	      (unsigned long long int) phdr->p_filesz,
	      gelf_getclass (elf) == ELFCLASS32 ? 9 : 12,
	      (unsigned long long int) phdr->p_memsz,
	      (unsigned long long int) phdr->p_align);

      putc_unlocked ((phdr->p_flags & PF_X) ? 'X' : ' ', stdout);
      putc_unlocked ((phdr->p_flags & PF_W) ? 'W' : ' ', stdout);
      putc_unlocked ((phdr->p_flags & PF_R) ? 'R' : ' ', stdout);

      putc_unlocked ('\n', stdout);

      if (phdr->p_type == PT_INTERP)
	{
	  /* We can show the user the name of the interpreter.  */
	  size_t maxsize;
	  char *filedata = elf_rawfile (elf, &maxsize);

	  if (filedata != NULL && phdr->p_offset < maxsize)
	    printf ("\t[Requesting program interpreter: %s]\n",
		    filedata + phdr->p_offset);
	}
    }

  if (elf_end (elf) != 0)
    {
      printf ("error while freeing ELF descriptor: %s\n", elf_errmsg (-1));
      exit (1);
    }

  return 0;
}
Esempio n. 28
0
dt_module_update(dtrace_hdl_t *dtp, struct kld_file_stat *k_stat)
#endif
{
	char fname[MAXPATHLEN];
	struct stat64 st;
	int fd, err, bits;

	dt_module_t *dmp;
	const char *s;
	size_t shstrs;
	GElf_Shdr sh;
	Elf_Data *dp;
	Elf_Scn *sp;

#if defined(sun)
	(void) snprintf(fname, sizeof (fname),
	    "%s/%s/object", OBJFS_ROOT, name);
#else
	GElf_Ehdr ehdr;
	GElf_Phdr ph;
	char name[MAXPATHLEN];
	uintptr_t mapbase, alignmask;
	int i = 0;
	int is_elf_obj;

	(void) strlcpy(name, k_stat->name, sizeof(name));
	(void) strlcpy(fname, k_stat->pathname, sizeof(fname));
#endif

	if ((fd = open(fname, O_RDONLY)) == -1 || fstat64(fd, &st) == -1 ||
	    (dmp = dt_module_create(dtp, name)) == NULL) {
		dt_dprintf("failed to open %s: %s\n", fname, strerror(errno));
		(void) close(fd);
		return;
	}

	/*
	 * Since the module can unload out from under us (and /system/object
	 * will return ENOENT), tell libelf to cook the entire file now and
	 * then close the underlying file descriptor immediately.  If this
	 * succeeds, we know that we can continue safely using dmp->dm_elf.
	 */
	dmp->dm_elf = elf_begin(fd, ELF_C_READ, NULL);
	err = elf_cntl(dmp->dm_elf, ELF_C_FDREAD);
	(void) close(fd);

	if (dmp->dm_elf == NULL || err == -1 ||
	    elf_getshdrstrndx(dmp->dm_elf, &shstrs) == -1) {
		dt_dprintf("failed to load %s: %s\n",
		    fname, elf_errmsg(elf_errno()));
		dt_module_destroy(dtp, dmp);
		return;
	}

	switch (gelf_getclass(dmp->dm_elf)) {
	case ELFCLASS32:
		dmp->dm_ops = &dt_modops_32;
		bits = 32;
		break;
	case ELFCLASS64:
		dmp->dm_ops = &dt_modops_64;
		bits = 64;
		break;
	default:
		dt_dprintf("failed to load %s: unknown ELF class\n", fname);
		dt_module_destroy(dtp, dmp);
		return;
	}
#if defined(__FreeBSD__)
	mapbase = (uintptr_t)k_stat->address;
	gelf_getehdr(dmp->dm_elf, &ehdr);
	is_elf_obj = (ehdr.e_type == ET_REL);
	if (is_elf_obj) {
		dmp->dm_sec_offsets =
		    malloc(ehdr.e_shnum * sizeof(*dmp->dm_sec_offsets));
		if (dmp->dm_sec_offsets == NULL) {
			dt_dprintf("failed to allocate memory\n");
			dt_module_destroy(dtp, dmp);
			return;
		}
	}
#endif
	/*
	 * Iterate over the section headers locating various sections of
	 * interest and use their attributes to flesh out the dt_module_t.
	 */
	for (sp = NULL; (sp = elf_nextscn(dmp->dm_elf, sp)) != NULL; ) {
		if (gelf_getshdr(sp, &sh) == NULL || sh.sh_type == SHT_NULL ||
		    (s = elf_strptr(dmp->dm_elf, shstrs, sh.sh_name)) == NULL)
			continue; /* skip any malformed sections */
#if defined(__FreeBSD__)
		if (sh.sh_size == 0)
			continue;
		if (sh.sh_type == SHT_PROGBITS || sh.sh_type == SHT_NOBITS) {
			alignmask = sh.sh_addralign - 1;
			mapbase += alignmask;
			mapbase &= ~alignmask;
			sh.sh_addr = mapbase;
			if (is_elf_obj)
				dmp->dm_sec_offsets[elf_ndxscn(sp)] = sh.sh_addr;
			mapbase += sh.sh_size;
		}
#endif
		if (strcmp(s, ".text") == 0) {
			dmp->dm_text_size = sh.sh_size;
			dmp->dm_text_va = sh.sh_addr;
		} else if (strcmp(s, ".data") == 0) {
			dmp->dm_data_size = sh.sh_size;
			dmp->dm_data_va = sh.sh_addr;
		} else if (strcmp(s, ".bss") == 0) {
			dmp->dm_bss_size = sh.sh_size;
			dmp->dm_bss_va = sh.sh_addr;
		} else if (strcmp(s, ".info") == 0 &&
		    (dp = elf_getdata(sp, NULL)) != NULL) {
			bcopy(dp->d_buf, &dmp->dm_info,
			    MIN(sh.sh_size, sizeof (dmp->dm_info)));
		} else if (strcmp(s, ".filename") == 0 &&
		    (dp = elf_getdata(sp, NULL)) != NULL) {
			(void) strlcpy(dmp->dm_file,
			    dp->d_buf, sizeof (dmp->dm_file));
		}
	}

	dmp->dm_flags |= DT_DM_KERNEL;
#if defined(sun)
	dmp->dm_modid = (int)OBJFS_MODID(st.st_ino);
#else
	/*
	 * Include .rodata and special sections into .text.
	 * This depends on default section layout produced by GNU ld
	 * for ELF objects and libraries:
	 * [Text][R/O data][R/W data][Dynamic][BSS][Non loadable]
	 */
	dmp->dm_text_size = dmp->dm_data_va - dmp->dm_text_va;
#if defined(__i386__)
	/*
	 * Find the first load section and figure out the relocation
	 * offset for the symbols. The kernel module will not need
	 * relocation, but the kernel linker modules will.
	 */
	for (i = 0; gelf_getphdr(dmp->dm_elf, i, &ph) != NULL; i++) {
		if (ph.p_type == PT_LOAD) {
			dmp->dm_reloc_offset = k_stat->address - ph.p_vaddr;
			break;
		}
	}
#endif
#endif

	if (dmp->dm_info.objfs_info_primary)
		dmp->dm_flags |= DT_DM_PRIMARY;

	dt_dprintf("opened %d-bit module %s (%s) [%d]\n",
	    bits, dmp->dm_name, dmp->dm_file, dmp->dm_modid);
}
Esempio n. 29
0
DSO *
fdopen_dso (int fd, const char *name)
{
  Elf *elf = NULL;
  GElf_Ehdr ehdr;
  GElf_Addr last_off;
  int i, j, k, last, *sections, *invsections;
  DSO *dso = NULL;
  struct PLArch *plarch;
  extern struct PLArch __start_pl_arch[], __stop_pl_arch[];

  elf = elf_begin (fd, ELF_C_READ, NULL);
  if (elf == NULL)
    {
      error (0, 0, "cannot open ELF file: %s", elf_errmsg (-1));
      goto error_out;
    }

  if (elf_kind (elf) != ELF_K_ELF)
    {
      error (0, 0, "\"%s\" is not an ELF file", name);
      goto error_out;
    }

  if (gelf_getehdr (elf, &ehdr) == NULL)
    {
      error (0, 0, "cannot get the ELF header: %s",
	     elf_errmsg (-1));
      goto error_out;
    }

  if (ehdr.e_type != ET_DYN && ehdr.e_type != ET_EXEC)
    {
      error (0, 0, "\"%s\" is not a shared library", name);
      goto error_out;
    }

  if (ehdr.e_shnum == 0)
    {
      GElf_Phdr phdr;

      /* Check for UPX compressed executables.  */
      if (ehdr.e_type == ET_EXEC
	  && ehdr.e_phnum > 0
	  && (gelf_getphdr (elf, 0, &phdr), phdr.p_type == PT_LOAD)
	  && phdr.p_filesz >= 256
	  && phdr.p_filesz <= 4096
	  && phdr.p_offset == 0
	  && ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize < phdr.p_filesz)
	{
	  char *buf = alloca (phdr.p_filesz);
	  size_t start = ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize;

	  if (pread (fd, buf, phdr.p_filesz, 0) == phdr.p_filesz
	      && memmem (buf + start, phdr.p_filesz - start,
			 "UPX!", 4) != NULL)
	    {
	      error (0, 0, "\"%s\" is UPX compressed executable", name);
	      goto error_out;
	    }
	}
      error (0, 0, "\"%s\" has no section headers", name);
      goto error_out;
    }

  /* Allocate DSO structure. Leave place for additional 20 new section
     headers.  */
  dso = (DSO *)
	malloc (sizeof(DSO) + (ehdr.e_shnum + 20) * sizeof(GElf_Shdr)
		+ (ehdr.e_phnum + 1) * sizeof(GElf_Phdr)
		+ (ehdr.e_shnum + 20) * sizeof(Elf_Scn *));
  if (!dso)
    {
      error (0, ENOMEM, "Could not open DSO");
      goto error_out;
    }

  elf_flagelf (elf, ELF_C_SET, ELF_F_LAYOUT | ELF_F_PERMISSIVE);

  memset (dso, 0, sizeof(DSO));
  dso->elf = elf;
  dso->ehdr = ehdr;
  dso->phdr = (GElf_Phdr *) &dso->shdr[ehdr.e_shnum + 20];
  dso->scn = (Elf_Scn **) &dso->phdr[ehdr.e_phnum + 1];
  switch (ehdr.e_ident[EI_CLASS])
    {
    case ELFCLASS32:
      dso->mask = 0xffffffff; break;
    case ELFCLASS64:
      dso->mask = 0xffffffffffffffffULL; break;
    }
  for (i = 0; i < ehdr.e_phnum; ++i)
    gelf_getphdr (elf, i, dso->phdr + i);
  dso->fd = fd;

  for (i = 0, j = 0; i < ehdr.e_shnum; ++i)
    {
      dso->scn[i] = elf_getscn (elf, i);
      gelfx_getshdr (elf, dso->scn[i], dso->shdr + i);
      if ((dso->shdr[i].sh_flags & SHF_ALLOC) && dso->shdr[i].sh_type != SHT_NOBITS)
	j = 1;
    }
  if (j == 0)
    {
      /* If all ALLOC sections are SHT_NOBITS, then this is a
	 stripped-to-file debuginfo.  Skip it silently.  */
      goto error_out;
    }

  sections = (int *) alloca (dso->ehdr.e_shnum * sizeof (int) * 2);
  sections[0] = 0;
  for (i = 1, j = 1, k = dso->ehdr.e_shnum, last = -1;
       i < dso->ehdr.e_shnum; ++i)
    if (RELOCATE_SCN (dso->shdr[i].sh_flags))
      {
	last = i;
	sections[j++] = i;
      }
    else
      sections[--k] = i;
  assert (j == k);

  section_cmp_dso = dso;
  qsort (sections + k, dso->ehdr.e_shnum - k, sizeof (*sections), section_cmp);
  invsections = sections + dso->ehdr.e_shnum;
  invsections[0] = 0;
  for (i = 1, j = 0; i < ehdr.e_shnum; ++i)
    {
      if (i != sections[i])
	{
	  j = 1;
	  dso->scn[i] = elf_getscn (elf, sections[i]);
	  gelfx_getshdr (elf, dso->scn[i], dso->shdr + i);
	}
      invsections[sections[i]] = i;
    }

  if (j)
    {
      dso->move = init_section_move (dso);
      if (dso->move == NULL)
	goto error_out;
      memcpy (dso->move->old_to_new, invsections, dso->ehdr.e_shnum * sizeof (int));
      memcpy (dso->move->new_to_old, sections, dso->ehdr.e_shnum * sizeof (int));
    }

  last_off = 0;
  for (i = 1; i < ehdr.e_shnum; ++i)
    {
      if (dso->shdr[i].sh_link >= ehdr.e_shnum)
	{
	  error (0, 0, "%s: bogus sh_link value %d", name,
		 dso->shdr[i].sh_link);
	  goto error_out;
	}
      dso->shdr[i].sh_link = invsections[dso->shdr[i].sh_link];
      if (dso->shdr[i].sh_type == SHT_REL
	  || dso->shdr[i].sh_type == SHT_RELA
	  || (dso->shdr[i].sh_flags & SHF_INFO_LINK))
	{
	  if (dso->shdr[i].sh_info >= ehdr.e_shnum)
	    {
	      error (0, 0, "%s: bogus sh_info value %d", name,
		     dso->shdr[i].sh_info);
	      goto error_out;
	    }
	  dso->shdr[i].sh_info = invsections[dso->shdr[i].sh_info];
	}

      /* Some linkers mess up sh_offset fields for empty or nobits
	 sections.  */
      if (RELOCATE_SCN (dso->shdr[i].sh_flags)
	  && (dso->shdr[i].sh_size == 0
	      || dso->shdr[i].sh_type == SHT_NOBITS))
	{
	  for (j = i + 1; j < ehdr.e_shnum; ++j)
	    if (! RELOCATE_SCN (dso->shdr[j].sh_flags))
	      break;
	    else if (dso->shdr[j].sh_size != 0
		     && dso->shdr[j].sh_type != SHT_NOBITS)
	      break;
	  dso->shdr[i].sh_offset = (last_off + dso->shdr[i].sh_addralign - 1)
				   & ~(dso->shdr[i].sh_addralign - 1);
	  if (j < ehdr.e_shnum
	      && dso->shdr[i].sh_offset > dso->shdr[j].sh_offset)
	    {
	      GElf_Addr k;

	      for (k = dso->shdr[i].sh_addralign - 1; k; )
		{
		  k >>= 1;
		  dso->shdr[i].sh_offset = (last_off + k) & ~k;
		  if (dso->shdr[i].sh_offset <= dso->shdr[j].sh_offset)
		    break;
		}
	    }
	  last_off = dso->shdr[i].sh_offset;
	}
      else
Esempio n. 30
0
/* Load ELF 'filename', parse the .eh_frame contents, and for each entry in the
 * second argument check whether its address is contained in the range of some
 * Frame Description Entry. If it does, fill in the function range of the
 * entry. In other words, try to assign start address and length of function
 * corresponding to each backtrace entry. We'll need that for the disassembly.
 *
 * Fails quietly - we should still be able to use the build ids.
 *
 * I wonder if this is really better than parsing eu-readelf text output.
 */
static GHashTable *elf_iterate_fdes(const char *filename, GList *entries, Elf *e)
{
    const unsigned char *e_ident;
    Elf_Data *scn_data;
    GElf_Shdr shdr;
    GElf_Phdr phdr;
    size_t phnum;
    GHashTable *retval = NULL; /* NULL = error */

    e_ident = (unsigned char *)elf_getident(e, NULL);
    if (e_ident == NULL)
    {
        VERB1 log_elf_error("elf_getident", filename);
        return NULL;
    }

    /* Look up the .eh_frame section */
    if (!xelf_section_by_name(e, ".eh_frame", filename, &scn_data, &shdr))
    {
        VERB1 log("Section .eh_frame not found in %s", filename);
        return NULL;
    }

    /* Get the address at which the executable segment is loaded. If the
     * .eh_frame addresses are absolute, this is used to convert them to
     * relative to the beginning of executable segment. We are looking for the
     * first LOAD segment that is executable, I hope this is sufficient.
     */
    if (elf_getphdrnum(e, &phnum) != 0)
    {
        VERB1 log_elf_error("elf_getphdrnum", filename);
        return NULL;
    }

    uintptr_t exec_base;
    int i;
    for (i = 0; i < phnum; i++)
    {
        if (gelf_getphdr(e, i, &phdr) != &phdr)
        {
            VERB1 log_elf_error("gelf_getphdr", filename);
            return NULL;
        }

        if (phdr.p_type == PT_LOAD && phdr.p_flags & PF_X)
        {
            exec_base = (uintptr_t)phdr.p_vaddr;
            goto base_found;
        }
    }

    VERB1 log("Can't determine executable base for '%s'", filename);
    return NULL;

base_found:
    VERB2 log("Executable base: %jx", (uintmax_t)exec_base);

    /* We now have a handle to .eh_frame data. We'll use dwarf_next_cfi to
     * iterate through all FDEs looking for those matching the addresses we
     * have.
     * Some info on .eh_frame can be found at http://www.airs.com/blog/archives/460
     * and in DWARF documentation for .debug_frame. The initial_location and
     * address_range decoding is 'inspired' by elfutils source.
     * XXX: If this linear scan is too slow, we can do binary search on
     * .eh_frame_hdr -- see http://www.airs.com/blog/archives/462
     */
    int ret;
    Dwarf_Off cfi_offset;
    Dwarf_Off cfi_offset_next = 0;
    Dwarf_CFI_Entry cfi;

    struct cie_encoding {
        Dwarf_Off cie_offset;
        int ptr_len;
        bool pcrel;
    } *cie;
    GList *cie_list = NULL;

    /* Init hash table
     * keys are pointers to integers which we allocate with malloc
     * values stored directly */
    GHashTable *hash = g_hash_table_new_full(g_int64_hash, g_int64_equal, free, NULL);

    while(1)
    {
        cfi_offset = cfi_offset_next;
        ret = dwarf_next_cfi(e_ident, scn_data, 1, cfi_offset, &cfi_offset_next, &cfi);

        if (ret > 0)
        {
            /* We're at the end. */
            break;
        }

        if (ret < 0)
        {
            /* Error. If cfi_offset_next was updated, we may skip the
             * erroneous cfi. */
            if (cfi_offset_next > cfi_offset)
            {
                continue;
            }
            VERB1 log("dwarf_next_cfi failed for %s: %s", filename, dwarf_errmsg(-1));
            goto ret_free;
        }

        if (dwarf_cfi_cie_p(&cfi))
        {
            /* Current CFI is a CIE. We store its offset and FDE encoding
             * attributes to be used when reading FDEs.
             */

            /* Default FDE encoding (i.e. no R in augmentation string) is
             * DW_EH_PE_absptr.
             */
            cie = btp_mallocz(sizeof(*cie));
            cie->cie_offset = cfi_offset;
            cie->ptr_len = encoded_size(DW_EH_PE_absptr, e_ident);

            /* Search the augmentation data for FDE pointer encoding.
             * Unfortunately, 'P' can come before 'R' (which we are looking
             * for), so we may have to parse the whole thing. See the
             * abovementioned blog post for details.
             */
            const char *aug = cfi.cie.augmentation;
            const uint8_t *augdata = cfi.cie.augmentation_data;
            bool skip_cie = 0;
            if (*aug == 'z')
            {
                aug++;
            }
            while (*aug != '\0')
            {
                if(*aug == 'R')
                {
                    cie->ptr_len = encoded_size(*augdata, e_ident);

                    if (cie->ptr_len != 4 && cie->ptr_len != 8)
                    {
                        VERB1 log("Unknown FDE encoding (CIE %jx) in %s",
                                (uintmax_t)cfi_offset, filename);
                        skip_cie = 1;
                    }
                    if ((*augdata & 0x70) == DW_EH_PE_pcrel)
                    {
                        cie->pcrel = 1;
                    }
                    break;
                }
                else if (*aug == 'L')
                {
                    augdata++;
                }
                else if (*aug == 'P')
                {
                    unsigned size = encoded_size(*augdata, e_ident);
                    if (size == 0)
                    {
                        VERB1 log("Unknown size for personality encoding in %s",
                                filename);
                        skip_cie = 1;
                        break;
                    }
                    augdata += (size + 1);
                }
                else
                {
                    VERB1 log("Unknown augmentation char in %s", filename);
                    skip_cie = 1;
                    break;
                }
                aug++;
            }
            if (skip_cie)
            {
                free(cie);
                continue;
            }

            cie_list = g_list_append(cie_list, cie);
        }
        else
        {
            /* Current CFI is an FDE.
             */
            GList *it = cie_list;
            cie = NULL;

            /* Find the CIE data that we should have saved earlier. XXX: We can
             * use hash table/tree to speed up the search, the number of CIEs
             * should usally be very low though. */
            while (it != NULL)
            {
                cie = it->data;

                /* In .eh_frame, CIE_pointer is relative, but libdw converts it
                 * to absolute offset. */
                if(cfi.fde.CIE_pointer == cie->cie_offset)
                {
                    break; /* Found. */
                }

                it = g_list_next(it);
            }

            if (it == NULL)
            {
                VERB1 log("CIE not found for FDE %jx in %s",
                        (uintmax_t)cfi_offset, filename);
                continue;
            }

            /* Read the two numbers we need and if they are PC-relative,
             * compute the offset from VMA base
             */

            uintptr_t initial_location = fde_read_address(cfi.fde.start, cie->ptr_len);
            uintptr_t address_range = fde_read_address(cfi.fde.start+cie->ptr_len, cie->ptr_len);

            if (cie->pcrel)
            {
                /* We need to determine how long is the 'length' (and
                 * consequently CIE id) field of this FDE -- it can be either 4
                 * or 12 bytes long. */
                uintptr_t length = fde_read_address(scn_data->d_buf + cfi_offset, 4);
                uintptr_t skip = (length == 0xffffffffUL ? 12 : 4);

                uintptr_t mask = (cie->ptr_len == 4 ? 0xffffffffUL : 0xffffffffffffffffUL);
                initial_location += (uintptr_t)shdr.sh_offset + (uintptr_t)cfi_offset + 2*skip;
                initial_location &= mask;
            }
            else
            {
                /* Assuming that not pcrel means absolute address (what if the file is a library?).
                 * Convert to text-section-start-relative.
                 */
                initial_location -= exec_base;
            }

            /* Insert the pair into hash */
            uintptr_t *key = addr_alloc(initial_location + exec_base);
            g_hash_table_insert(hash, key, (gpointer)address_range);
            VERB3 log("FDE start: 0x%jx length: %u", (uintmax_t)*key, (unsigned)address_range);

            /* Iterate through the backtrace entries and check each address
             * member whether it belongs into the range given by current FDE.
             */
            for (it = entries; it != NULL; it = g_list_next(it))
            {
                struct backtrace_entry *entry = it->data;
                if (initial_location <= entry->build_id_offset
                        && entry->build_id_offset < initial_location + address_range)
                {
                    /* Convert to before-relocation absolute addresses, disassembler uses those. */
                    entry->function_initial_loc = exec_base + initial_location;
                    entry->function_length = address_range;
                    /*TODO: remove the entry from the list to save a bit of time in next iteration?*/
                }
            }
        }
    }

    retval = hash; /* success */

ret_free:
    list_free_with_free(cie_list);
    if (retval == NULL)
        g_hash_table_destroy(hash);
    return retval;
}