Example #1
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;
}
Example #2
0
size_t xelf_getphdrnum(Elf *e)
{
  size_t size;
  if (elf_getphdrnum(e, &size) < 0)
	{
	  eprintf("elf_getphdrnum() failed: %s", elf_errmsg(-1));
	  exit(EXIT_FAILURE);
	}
  return size;
}
Example #3
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;
}
Example #4
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);
}
Example #5
0
GElf_Phdr *
gelf_getphdr(Elf *e, int index, GElf_Phdr *d)
{
	int ec;
	Elf32_Ehdr *eh32;
	Elf64_Ehdr *eh64;
	Elf32_Phdr *ep32;
	Elf64_Phdr *ep64;
	size_t phnum;

	if (d == NULL || e == NULL ||
	    ((ec = e->e_class) != ELFCLASS32 && ec != ELFCLASS64) ||
	    (e->e_kind != ELF_K_ELF) || index < 0 ||
	    elf_getphdrnum(e, &phnum) < 0) {
		LIBELF_SET_ERROR(ARGUMENT, 0);
		return (NULL);
	}

	if ((size_t)index >= phnum) {
		LIBELF_SET_ERROR(ARGUMENT, 0);
		return (NULL);
	}

	if (ec == ELFCLASS32) {
		if ((eh32 = _libelf_ehdr(e, ELFCLASS32, 0)) == NULL ||
		    ((ep32 = _libelf_getphdr(e, ELFCLASS32)) == NULL))
			return (NULL);

		ep32 += index;

		d->p_type   = ep32->p_type;
		d->p_offset = ep32->p_offset;
		d->p_vaddr  = (Elf64_Addr) ep32->p_vaddr;
		d->p_paddr  = (Elf64_Addr) ep32->p_paddr;
		d->p_filesz = (Elf64_Xword) ep32->p_filesz;
		d->p_memsz  = (Elf64_Xword) ep32->p_memsz;
		d->p_flags  = ep32->p_flags;
		d->p_align  = (Elf64_Xword) ep32->p_align;

	} else {
		if ((eh64 = _libelf_ehdr(e, ELFCLASS64, 0)) == NULL ||
		    (ep64 = _libelf_getphdr(e, ELFCLASS64)) == NULL)
			return (NULL);

		ep64 += index;

		*d = *ep64;
	}

	return (d);
}
Example #6
0
int
gelf_update_phdr(Elf *e, int ndx, GElf_Phdr *s)
{
	int ec;
	size_t phnum;
	void *ehdr;
	Elf32_Phdr *ph32;
	Elf64_Phdr *ph64;

	if (s == NULL || e == NULL || e->e_kind != ELF_K_ELF ||
	    ((ec = e->e_class) != ELFCLASS32 && ec != ELFCLASS64) ||
	    elf_getphdrnum(e, &phnum) < 0) {
		LIBELF_SET_ERROR(ARGUMENT, 0);
		return (0);
	}

	if (e->e_cmd == ELF_C_READ) {
		LIBELF_SET_ERROR(MODE, 0);
		return (0);
	}

	if ((ehdr = _libelf_ehdr(e, ec, 0)) == NULL)
		return (0);

	if (ndx < 0 || (size_t)ndx > phnum) {
		LIBELF_SET_ERROR(ARGUMENT, 0);
		return (0);
	}

	(void) elf_flagphdr(e, ELF_C_SET, ELF_F_DIRTY);

	if (ec == ELFCLASS64) {
		ph64 = e->e_u.e_elf.e_phdr.e_phdr64 + ndx;
		*ph64 = *s;
		return (1);
	}

	ph32 = e->e_u.e_elf.e_phdr.e_phdr32 + ndx;

	ph32->p_type     =  s->p_type;
	ph32->p_flags    =  s->p_flags;
	LIBELF_COPY_U32(ph32, s, p_offset);
	LIBELF_COPY_U32(ph32, s, p_vaddr);
	LIBELF_COPY_U32(ph32, s, p_paddr);
	LIBELF_COPY_U32(ph32, s, p_filesz);
	LIBELF_COPY_U32(ph32, s, p_memsz);
	LIBELF_COPY_U32(ph32, s, p_align);

	return (1);
}
Example #7
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;
}
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;
}
Example #9
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>();
}
Example #10
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;
}
Example #11
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;
}
Example #12
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;
}
Example #13
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;
}
Example #14
0
int init(char* fname)
{
	char *id, bytes[5];

	if (elf_version (EV_CURRENT) == EV_NONE) {
		errx (EXIT_FAILURE, " ELF  library   initialization  "
		      " failed : %s", elf_errmsg (-1));
		return -1;
	}

	int fd;
	if ((fd = open (fname, O_RDONLY, 0)) < 0) {
		err (EXIT_FAILURE, " open  \"%s\"  failed ", fname);
		return -1;
	}
	g.fd = fd;

	Elf *e;
	if ((e = elf_begin (fd, ELF_C_READ, NULL)) == NULL) {
		errx (EXIT_FAILURE, " elf_begin ()  failed : %s.",
		      elf_errmsg (-1));
		return -1;
	}

	if (elf_kind (e) != ELF_K_ELF) {
		errx (EXIT_FAILURE, " \"%s\" is  not an  ELF  object .",
		      fname);
		return -1;
	}
	g.e = e;

	GElf_Ehdr ehdr;
	if (gelf_getehdr (e, &ehdr) == NULL) {
		errx (EXIT_FAILURE, " getehdr ()  failed : %s.",
		      elf_errmsg (-1));
		return -1;
	}
	g.ehdr = ehdr;

	int i;
	if ((i = gelf_getclass (e)) == ELFCLASSNONE) {
		errx (EXIT_FAILURE, " getclass ()  failed : %s.",
		      elf_errmsg (-1));
		return -1;
	}

	if ((id = elf_getident (e, NULL)) == NULL) {
		errx (EXIT_FAILURE, " getident ()  failed : %s.",
		      elf_errmsg (-1));
		return -1;
	}

	size_t n;
	if (elf_getshdrnum (e, &n) != 0) {
		errx (EXIT_FAILURE, " getshdrnum ()  failed : %s.",
		      elf_errmsg (-1));
		return -1;
	}
	g.shdrnum = n;

	if (elf_getshdrstrndx (e, &n) != 0) {
		errx (EXIT_FAILURE, " getshdrstrndx ()  failed : %s.",
		      elf_errmsg (-1));
		return -1;
	}
	g.shstrndx = n;

	if (elf_getphdrnum (e, &n) != 0) {
		errx (EXIT_FAILURE, " getphdrnum ()  failed : %s.",
		      elf_errmsg (-1));
		return -1;
	}
	g.phdrnum = n;

	init_scns();
	init_phdrs();

	return 0;
}
Example #15
0
File: elves.c Project: abrt/satyr
struct sr_elf_fde *
sr_elf_get_eh_frame(const char *filename,
                    char **error_message)
{
#ifdef WITH_ELFUTILS
    /* Open the input file. */
    int fd = open(filename, O_RDONLY);
    if (fd < 0)
    {
        *error_message = sr_asprintf("Failed to open file %s: %s",
                                     filename,
                                     strerror(errno));
        return NULL;
    }

    /* Initialize libelf on the opened file. */
    Elf *elf = elf_begin(fd, ELF_C_READ, NULL);
    if (!elf)
    {
        *error_message = sr_asprintf("Failed to run elf_begin on file %s: %s",
                                     filename,
                                     elf_errmsg(-1));
        close(fd);
        return NULL;
    }

    unsigned char *e_ident = (unsigned char *)elf_getident(elf, NULL);
    if (!e_ident)
    {
        *error_message = sr_asprintf("elf_getident failed for %s: %s",
                                     filename,
                                     elf_errmsg(-1));
        elf_end(elf);
        close(fd);
        return NULL;
    }

    /* Look up the .eh_frame section */
    GElf_Shdr shdr;
    Elf_Data *section_data;
    char *find_section_error_message;
    if (!find_elf_section_by_name(elf,
                                  ".eh_frame",
                                  &section_data,
                                  &shdr,
                                  &find_section_error_message))
    {
        *error_message = sr_asprintf("Failed to find .eh_frame section for %s: %s",
                                     filename,
                                     find_section_error_message);

        free(find_section_error_message);
        elf_end(elf);
        close(fd);
        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.
     */
    size_t phnum;
    if (elf_getphdrnum(elf, &phnum) != 0)
    {
        *error_message = sr_asprintf("elf_getphdrnum failed for %s: %s",
                                     filename,
                                     elf_errmsg(-1));
        elf_end(elf);
        close(fd);
        return NULL;
    }

    uint64_t exec_base = -1;
    int i;
    for (i = 0; i < phnum; i++)
    {
        GElf_Phdr phdr;
        if (gelf_getphdr(elf, i, &phdr) != &phdr)
        {
            *error_message = sr_asprintf("gelf_getphdr failed for %s: %s",
                                         filename,
                                         elf_errmsg(-1));
            elf_end(elf);
            close(fd);
            return NULL;
        }

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

    if (-1 == exec_base)
    {
        *error_message = sr_asprintf("Can't determine executable base for %s",
                                     filename);

        elf_end(elf);
        close(fd);
        return NULL;
    }

    /* 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.
     *
     * @todo If this linear scan is too slow, we can do binary search
     * on .eh_frame_hdr -- see http://www.airs.com/blog/archives/462
     */

    struct sr_elf_fde *result = NULL, *last = NULL;
    struct cie *cie_list = NULL, *cie_list_last = NULL;

    Dwarf_Off cfi_offset_next = 0;
    while (true)
    {
        Dwarf_CFI_Entry cfi;
        Dwarf_Off cfi_offset = cfi_offset_next;
        int ret = dwarf_next_cfi(e_ident,
                                 section_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;

            *error_message = sr_asprintf("dwarf_next_cfi failed for %s: %s",
                                         filename,
                                         dwarf_errmsg(-1));

            cie_free(cie_list);
            sr_elf_eh_frame_free(result);
            elf_end(elf);
            close(fd);
            return NULL;
        }

        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.
             */
            char *cie_error_message;
            struct cie *cie = read_cie(&cfi,
                                       cfi_offset,
                                       e_ident,
                                       &cie_error_message);
            if (!cie)
            {
                *error_message = sr_asprintf("CIE reading failed for %s: %s",
                                             filename,
                                             cie_error_message);

                free(cie_error_message);
                cie_free(cie_list);
                sr_elf_eh_frame_free(result);
                elf_end(elf);
                close(fd);
                return NULL;
            }

            /* Append the newly parsed CIE to our list of CIEs. */
            if (cie_list)
            {
                cie_list_last->next = cie;
                cie_list_last = cie;
            }
            else
                cie_list = cie_list_last = cie;
        }
        else
        {
            /* Current CFI is an FDE.
             */
            struct cie *cie = cie_list;

            /* Find the CIE data that we should have saved earlier. */
            while (cie)
            {
                /* In .eh_frame, CIE_pointer is relative, but libdw converts it
                 * to absolute offset. */
                if (cfi.fde.CIE_pointer == cie->cie_offset)
                    break; /* Found. */

                cie = cie->next;
            }

            if (!cie)
            {
                *error_message = sr_asprintf("CIE not found for FDE %jx in %s",
                                             (uintmax_t)cfi_offset,
                                             filename);

                cie_free(cie_list);
                sr_elf_eh_frame_free(result);
                elf_end(elf);
                close(fd);
                return NULL;
            }

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

            uint64_t initial_location = fde_read_address(cfi.fde.start,
                                                         cie->ptr_len);

            uint64_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. */
                uint64_t length = fde_read_address(section_data->d_buf + cfi_offset, 4);
                uint64_t skip = (length == 0xffffffffUL ? 12 : 4);
                uint64_t mask = (cie->ptr_len == 4 ? 0xffffffffUL : 0xffffffffffffffffUL);
                initial_location += shdr.sh_offset + 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;
            }

            struct sr_elf_fde *fde = sr_malloc(sizeof(struct sr_elf_fde));
            fde->exec_base = exec_base;
            fde->start_address = initial_location;
            fde->length = address_range;
            fde->next = NULL;

            /* Append the newly parsed Frame Description Entry to our
             * list of FDEs.
             */
            if (result)
            {
                last->next = fde;
                last = fde;
            }
            else
                result = last = fde;
        }
    }

    cie_free(cie_list);
    elf_end(elf);
    close(fd);
    return result;
#else /* WITH_ELFUTILS */
    *error_message = sr_asprintf("satyr compiled without elfutils");
    return NULL;
#endif /* WITH_ELFUTILS */
}
Example #16
0
static const uint8_t *
parse_eh_frame_hdr (const uint8_t *hdr, size_t hdr_size, GElf_Addr hdr_vaddr,
		    const GElf_Ehdr *ehdr, GElf_Addr *eh_frame_vaddr,
		    size_t *table_entries, uint8_t *table_encoding)
{
  const uint8_t *h = hdr;

  if (*h++ != 1)		/* version */
    return (void *) -1l;

  uint8_t eh_frame_ptr_encoding = *h++;
  uint8_t fde_count_encoding = *h++;
  uint8_t fde_table_encoding = *h++;

  if (eh_frame_ptr_encoding == DW_EH_PE_omit)
    return (void *) -1l;

  /* Dummy used by read_encoded_value.  */
  Elf_Data_Scn dummy_cfi_hdr_data =
    {
      .d = { .d_buf = (void *) hdr, .d_size = hdr_size }
    };
  Dwarf_CFI dummy_cfi =
    {
      .e_ident = ehdr->e_ident,
      .datarel = hdr_vaddr,
      .frame_vaddr = hdr_vaddr,
      .data = &dummy_cfi_hdr_data,
    };

  if (unlikely (read_encoded_value (&dummy_cfi, eh_frame_ptr_encoding, &h,
				    eh_frame_vaddr)))
    return (void *) -1l;

  if (fde_count_encoding != DW_EH_PE_omit)
    {
      Dwarf_Word fde_count;
      if (unlikely (read_encoded_value (&dummy_cfi, fde_count_encoding, &h,
					&fde_count)))
	return (void *) -1l;
      if (fde_count != 0 && (size_t) fde_count == fde_count
	  && fde_table_encoding != DW_EH_PE_omit
	  && (fde_table_encoding &~ DW_EH_PE_signed) != DW_EH_PE_uleb128)
	{
	  *table_entries = fde_count;
	  *table_encoding = fde_table_encoding;
	  return h;
	}
    }

  return NULL;
}

static Dwarf_CFI *
getcfi_gnu_eh_frame (Elf *elf, const GElf_Ehdr *ehdr, const GElf_Phdr *phdr)
{
  if (unlikely (phdr->p_filesz < 4))
    goto invalid;

  Elf_Data *data = elf_getdata_rawchunk (elf, phdr->p_offset, phdr->p_filesz,
					 ELF_T_BYTE);
  if (data == NULL)
    {
    invalid_hdr:
    invalid:
      /* XXX might be read error or corrupt phdr */
      __libdw_seterrno (DWARF_E_INVALID_CFI);
      return NULL;
    }

  Dwarf_Addr eh_frame_ptr;
  size_t search_table_entries;
  uint8_t search_table_encoding;
  const uint8_t *search_table = parse_eh_frame_hdr (data->d_buf, phdr->p_filesz,
						    phdr->p_vaddr, ehdr,
						    &eh_frame_ptr,
						    &search_table_entries,
						    &search_table_encoding);
  if (search_table == (void *) -1l)
    goto invalid_hdr;

  Dwarf_Off eh_frame_offset = eh_frame_ptr - phdr->p_vaddr + phdr->p_offset;
  Dwarf_Word eh_frame_size = 0;

  /* XXX we have no way without section headers to know the size
     of the .eh_frame data.  Calculate the largest it might possibly be.
     This won't be wasteful if the file is already mmap'd, but if it isn't
     it might be quite excessive.  */
  size_t filesize;
  if (elf_rawfile (elf, &filesize) != NULL)
    eh_frame_size = filesize - eh_frame_offset;

  data = elf_getdata_rawchunk (elf, eh_frame_offset, eh_frame_size, ELF_T_BYTE);
  if (data == NULL)
    {
      __libdw_seterrno (DWARF_E_INVALID_ELF); /* XXX might be read error */
      return NULL;
    }
  Dwarf_CFI *cfi = allocate_cfi (elf, eh_frame_ptr);
  if (cfi != NULL)
    {
      cfi->data = (Elf_Data_Scn *) data;

      if (search_table != NULL)
	{
	  cfi->search_table = search_table;
	  cfi->search_table_vaddr = phdr->p_vaddr;
	  cfi->search_table_encoding = search_table_encoding;
	  cfi->search_table_entries = search_table_entries;
	}
    }
  return cfi;
}

/* Search the phdrs for PT_GNU_EH_FRAME.  */
static Dwarf_CFI *
getcfi_phdr (Elf *elf, const GElf_Ehdr *ehdr)
{
  size_t phnum;
  if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
    return NULL;

  for (size_t i = 0; i < phnum; ++i)
    {
      GElf_Phdr phdr_mem;
      GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
      if (unlikely (phdr == NULL))
	return NULL;
      if (phdr->p_type == PT_GNU_EH_FRAME)
	return getcfi_gnu_eh_frame (elf, ehdr, phdr);
    }

  __libdw_seterrno (DWARF_E_NO_DWARF);
  return NULL;
}
Example #17
0
int main(int argc, const char *argv[])
{
	int i, fd;
	Elf *pelf = NULL;
	char *id, bytes[5];
	size_t n = 0;
	Elf32_Phdr *phdr32 = NULL; 
	GElf_Phdr gphdr = { 0 };	

	if (argc != 2)	
		errx(EXIT_FAILURE, "usage: %s file-name", argv[0]);

	if (elf_version(EV_CURRENT) == EV_NONE)
		errx(EXIT_FAILURE, "ELF library initialization "
				"failed: %s", elf_errmsg(-1));

	if ((fd = open(argv[1], O_RDONLY, 0)) < 0)
		errx(EXIT_FAILURE, "open \"%s\" failed", argv[1]);

	if ((pelf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
		errx(EXIT_FAILURE, "elf_begin() failed: %s",
				elf_errmsg(-1));

	if (elf_kind(pelf) != ELF_K_ELF)
		errx(EXIT_FAILURE, "\"%s\" is not an ELF object.",
				argv[1]);

	// get the num of program header table
	if (elf_getphdrnum(pelf, &n) != 0)
		errx(EXIT_FAILURE, "elf_getphdrnum() failed: %s.",
				elf_errmsg(-1));
	
#if 0
	// 32bit
	if ((phdr32 = elf32_getphdr(pelf)) == NULL)
		errx(EXIT_FAILURE, "elf32_getphdr() failed: %s.",
				elf_errmsg(-1));
	for (i = 0; i < n; i++) {
		Elf32_Phdr phdr = phdr32[i];
		printf("PHDR %d:\n", i);
#define PRINT_FMT	"    %-20s 0x%jx"
#define PRINT_FIELD(N)	do { printf(PRINT_FMT, #N, (uintmax_t)phdr.N); } while (0);
#define NL() do { printf("\n"); } while (0);

		PRINT_FIELD(p_type);
		print_ptype(phdr.p_type); 				NL();
		PRINT_FIELD(p_offset);					NL();
		PRINT_FIELD(p_vaddr);					NL();
		PRINT_FIELD(p_paddr); 					NL();
		PRINT_FIELD(p_filesz);					NL();
		PRINT_FIELD(p_memsz);					NL();
		PRINT_FIELD(p_flags);					
		printf(" [");
		if (phdr.p_flags & PF_X)
			printf(" execute");
		if (phdr.p_flags & PF_R)
			printf(" read");
		if (phdr.p_flags & PF_W)
			printf(" write");
		printf(" ]");			NL();
		PRINT_FIELD(p_align);	NL();
	}
#endif

#if 1
	for (i = 0; i < n; i++) {
		// get every entry fist 
		if (gelf_getphdr(pelf, i, &gphdr) != &gphdr)	
			errx(EXIT_FAILURE, "gelf_getphdr() failed: %s.",
													elf_errmsg(-1));
		// now print every entry
		printf("PHDR %d:\n", i);
#define PRINT_FMT	"    %-20s 0x%jx"
#define PRINT_FIELD(N)	do { printf(PRINT_FMT, #N, (uintmax_t)gphdr.N); } while (0);
#define NL() do { printf("\n"); } while (0);

		PRINT_FIELD(p_type);
		print_ptype(gphdr.p_type); 				NL();
		PRINT_FIELD(p_offset);					NL();
		PRINT_FIELD(p_vaddr);					NL();
		PRINT_FIELD(p_paddr); 					NL();
		PRINT_FIELD(p_filesz);					NL();
		PRINT_FIELD(p_memsz);					NL();
		PRINT_FIELD(p_flags);					;
		printf(" [");
		if (gphdr.p_flags & PF_X)
			printf(" execute");
		if (gphdr.p_flags & PF_R)
			printf(" read");
		if (gphdr.p_flags & PF_W)
			printf(" write");
		printf(" ]");			NL();
		PRINT_FIELD(p_align);	NL();
	}
#endif

	elf_end(pelf);
	close(fd);

	exit(EXIT_SUCCESS);
}
/* If the main file might have been prelinked, then we need to
   discover the correct synchronization address between the main and
   debug files.  Because of prelink's section juggling, we cannot rely
   on the address_sync computed from PT_LOAD segments (see open_elf).

   We will attempt to discover a synchronization address based on the
   section headers instead.  But finding a section address that is
   safe to use requires identifying which sections are SHT_PROGBITS.
   We can do that in the main file, but in the debug file all the
   allocated sections have been transformed into SHT_NOBITS so we have
   lost the means to match them up correctly.

   The only method left to us is to decode the .gnu.prelink_undo
   section in the prelinked main file.  This shows what the sections
   looked like before prelink juggled them--when they still had a
   direct correspondence to the debug file.  */
static Dwfl_Error
find_prelink_address_sync (Dwfl_Module *mod, struct dwfl_file *file)
{
  /* The magic section is only identified by name.  */
  size_t shstrndx;
  if (elf_getshdrstrndx (mod->main.elf, &shstrndx) < 0)
    return DWFL_E_LIBELF;

  Elf_Scn *scn = NULL;
  while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
    {
      GElf_Shdr shdr_mem;
      GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
      if (unlikely (shdr == NULL))
	return DWFL_E_LIBELF;
      if (shdr->sh_type == SHT_PROGBITS
	  && !(shdr->sh_flags & SHF_ALLOC)
	  && shdr->sh_name != 0)
	{
	  const char *secname = elf_strptr (mod->main.elf, shstrndx,
					    shdr->sh_name);
	  if (unlikely (secname == NULL))
	    return DWFL_E_LIBELF;
	  if (!strcmp (secname, ".gnu.prelink_undo"))
	    break;
	}
    }

  if (scn == NULL)
    /* There was no .gnu.prelink_undo section.  */
    return DWFL_E_NOERROR;

  Elf_Data *undodata = elf_rawdata (scn, NULL);
  if (unlikely (undodata == NULL))
    return DWFL_E_LIBELF;

  /* Decode the section.  It consists of the original ehdr, phdrs,
     and shdrs (but omits section 0).  */

  union
  {
    Elf32_Ehdr e32;
    Elf64_Ehdr e64;
  } ehdr;
  Elf_Data dst =
    {
      .d_buf = &ehdr,
      .d_size = sizeof ehdr,
      .d_type = ELF_T_EHDR,
      .d_version = EV_CURRENT
    };
  Elf_Data src = *undodata;
  src.d_size = gelf_fsize (mod->main.elf, ELF_T_EHDR, 1, EV_CURRENT);
  src.d_type = ELF_T_EHDR;
  if (unlikely (gelf_xlatetom (mod->main.elf, &dst, &src,
			       elf_getident (mod->main.elf, NULL)[EI_DATA])
		== NULL))
    return DWFL_E_LIBELF;

  size_t shentsize = gelf_fsize (mod->main.elf, ELF_T_SHDR, 1, EV_CURRENT);
  size_t phentsize = gelf_fsize (mod->main.elf, ELF_T_PHDR, 1, EV_CURRENT);

  uint_fast16_t phnum;
  uint_fast16_t shnum;
  if (ehdr.e32.e_ident[EI_CLASS] == ELFCLASS32)
    {
      if (ehdr.e32.e_shentsize != shentsize
	  || ehdr.e32.e_phentsize != phentsize)
	return DWFL_E_BAD_PRELINK;
      phnum = ehdr.e32.e_phnum;
      shnum = ehdr.e32.e_shnum;
    }
  else
    {
      if (ehdr.e64.e_shentsize != shentsize
	  || ehdr.e64.e_phentsize != phentsize)
	return DWFL_E_BAD_PRELINK;
      phnum = ehdr.e64.e_phnum;
      shnum = ehdr.e64.e_shnum;
    }

  /* Since prelink does not store the zeroth section header in the undo
     section, it cannot support SHN_XINDEX encoding.  */
  if (unlikely (shnum >= SHN_LORESERVE)
      || unlikely (undodata->d_size != (src.d_size
					+ phnum * phentsize
					+ (shnum - 1) * shentsize)))
    return DWFL_E_BAD_PRELINK;

  /* We look at the allocated SHT_PROGBITS (or SHT_NOBITS) sections.  (Most
     every file will have some SHT_PROGBITS sections, but it's possible to
     have one with nothing but .bss, i.e. SHT_NOBITS.)  The special sections
     that can be moved around have different sh_type values--except for
     .interp, the section that became the PT_INTERP segment.  So we exclude
     the SHT_PROGBITS section whose address matches the PT_INTERP p_vaddr.
     For this reason, we must examine the phdrs first to find PT_INTERP.  */

  GElf_Addr main_interp = 0;
  {
    size_t main_phnum;
    if (unlikely (elf_getphdrnum (mod->main.elf, &main_phnum)))
      return DWFL_E_LIBELF;
    for (size_t i = 0; i < main_phnum; ++i)
      {
	GElf_Phdr phdr;
	if (unlikely (gelf_getphdr (mod->main.elf, i, &phdr) == NULL))
	  return DWFL_E_LIBELF;
	if (phdr.p_type == PT_INTERP)
	  {
	    main_interp = phdr.p_vaddr;
	    break;
	  }
      }
  }

  src.d_buf += src.d_size;
  src.d_type = ELF_T_PHDR;
  src.d_size = phnum * phentsize;

  GElf_Addr undo_interp = 0;
  bool class32 = ehdr.e32.e_ident[EI_CLASS] == ELFCLASS32;
  {
    size_t phdr_size = class32 ? sizeof (Elf32_Phdr) : sizeof (Elf64_Phdr);
    if (unlikely (phnum > SIZE_MAX / phdr_size))
      return DWFL_E_NOMEM;
    const size_t phdrs_bytes = phnum * phdr_size;
    void *phdrs = malloc (phdrs_bytes);
    if (unlikely (phdrs == NULL))
      return DWFL_E_NOMEM;
    dst.d_buf = phdrs;
    dst.d_size = phdrs_bytes;
    if (unlikely (gelf_xlatetom (mod->main.elf, &dst, &src,
				 ehdr.e32.e_ident[EI_DATA]) == NULL))
      {
	free (phdrs);
	return DWFL_E_LIBELF;
      }
    if (class32)
      {
	Elf32_Phdr (*p32)[phnum] = phdrs;
	for (uint_fast16_t i = 0; i < phnum; ++i)
	  if ((*p32)[i].p_type == PT_INTERP)
	    {
	      undo_interp = (*p32)[i].p_vaddr;
	      break;
	    }
      }
    else
      {
	Elf64_Phdr (*p64)[phnum] = phdrs;
	for (uint_fast16_t i = 0; i < phnum; ++i)
	  if ((*p64)[i].p_type == PT_INTERP)
	    {
	      undo_interp = (*p64)[i].p_vaddr;
	      break;
	    }
      }
    free (phdrs);
  }

  if (unlikely ((main_interp == 0) != (undo_interp == 0)))
    return DWFL_E_BAD_PRELINK;

  src.d_buf += src.d_size;
  src.d_type = ELF_T_SHDR;
  src.d_size = gelf_fsize (mod->main.elf, ELF_T_SHDR, shnum - 1, EV_CURRENT);

  size_t shdr_size = class32 ? sizeof (Elf32_Shdr) : sizeof (Elf64_Shdr);
  if (unlikely (shnum - 1  > SIZE_MAX / shdr_size))
    return DWFL_E_NOMEM;
  const size_t shdrs_bytes = (shnum - 1) * shdr_size;
  void *shdrs = malloc (shdrs_bytes);
  if (unlikely (shdrs == NULL))
    return DWFL_E_NOMEM;
  dst.d_buf = shdrs;
  dst.d_size = shdrs_bytes;
  if (unlikely (gelf_xlatetom (mod->main.elf, &dst, &src,
			       ehdr.e32.e_ident[EI_DATA]) == NULL))
    {
      free (shdrs);
      return DWFL_E_LIBELF;
    }

  /* Now we can look at the original section headers of the main file
     before it was prelinked.  First we'll apply our method to the main
     file sections as they are after prelinking, to calculate the
     synchronization address of the main file.  Then we'll apply that
     same method to the saved section headers, to calculate the matching
     synchronization address of the debug file.

     The method is to consider SHF_ALLOC sections that are either
     SHT_PROGBITS or SHT_NOBITS, excluding the section whose sh_addr
     matches the PT_INTERP p_vaddr.  The special sections that can be
     moved by prelink have other types, except for .interp (which
     becomes PT_INTERP).  The "real" sections cannot move as such, but
     .bss can be split into .dynbss and .bss, with the total memory
     image remaining the same but being spread across the two sections.
     So we consider the highest section end, which still matches up.  */

  GElf_Addr highest;

  inline void consider_shdr (GElf_Addr interp,
			     GElf_Word sh_type,
			     GElf_Xword sh_flags,
			     GElf_Addr sh_addr,
			     GElf_Xword sh_size)
  {
    if ((sh_flags & SHF_ALLOC)
	&& ((sh_type == SHT_PROGBITS && sh_addr != interp)
	    || sh_type == SHT_NOBITS))
      {
	const GElf_Addr sh_end = sh_addr + sh_size;
	if (sh_end > highest)
	  highest = sh_end;
      }
  }

  highest = 0;
  scn = NULL;
  while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
    {
      GElf_Shdr sh_mem;
      GElf_Shdr *sh = gelf_getshdr (scn, &sh_mem);
      if (unlikely (sh == NULL))
	{
	  free (shdrs);
	  return DWFL_E_LIBELF;
	}
      consider_shdr (main_interp, sh->sh_type, sh->sh_flags,
		     sh->sh_addr, sh->sh_size);
    }
  if (highest > mod->main.vaddr)
    {
      mod->main.address_sync = highest;

      highest = 0;
      if (class32)
	{
	  Elf32_Shdr (*s32)[shnum - 1] = shdrs;
	  for (size_t i = 0; i < shnum - 1; ++i)
	    consider_shdr (undo_interp, (*s32)[i].sh_type,
			   (*s32)[i].sh_flags, (*s32)[i].sh_addr,
			   (*s32)[i].sh_size);
	}
      else
	{
	  Elf64_Shdr (*s64)[shnum - 1] = shdrs;
	  for (size_t i = 0; i < shnum - 1; ++i)
	    consider_shdr (undo_interp, (*s64)[i].sh_type,
			   (*s64)[i].sh_flags, (*s64)[i].sh_addr,
			   (*s64)[i].sh_size);
	}

      if (highest > file->vaddr)
	file->address_sync = highest;
      else
	{
	  free (shdrs);
	  return DWFL_E_BAD_PRELINK;
	}
    }

  free (shdrs);

  return DWFL_E_NOERROR;
}

/* 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;
  debuglink_file = INTUSE(dwelf_elf_gnu_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);
  Dwfl_Error result = open_elf (mod, &mod->debug);
  if (result == DWFL_E_NOERROR && mod->debug.address_sync != 0)
    result = find_prelink_address_sync (mod, &mod->debug);
  return result;
}

/* Try to find the alternative debug link for the given DWARF and set
   it if found.  Only called when mod->dw is already setup but still
   might need an alternative (dwz multi) debug file.  filename is either
   the main or debug name from which the Dwarf was created. */
static void
find_debug_altlink (Dwfl_Module *mod, const char *filename)
{
  assert (mod->dw != NULL);

  const char *altname;
  const void *build_id;
  ssize_t build_id_len = INTUSE(dwelf_dwarf_gnu_debugaltlink) (mod->dw,
							       &altname,
							       &build_id);

  if (build_id_len > 0)
    {
      /* We could store altfile in the module, but don't really need it.  */
      char *altfile = NULL;
      mod->alt_fd = (*mod->dwfl->callbacks->find_debuginfo) (MODCB_ARGS (mod),
							     filename,
							     altname,
							     0,
							     &altfile);

      /* The (internal) callbacks might just set mod->alt_elf directly
	 because they open the Elf anyway for sanity checking.
	 Otherwise open either the given file name or use the fd
	 returned.  */
      Dwfl_Error error = open_elf_file (&mod->alt_elf, &mod->alt_fd,
					&altfile);
      if (error == DWFL_E_NOERROR)
	{
	  mod->alt = INTUSE(dwarf_begin_elf) (mod->alt_elf,
					      DWARF_C_READ, NULL);
	  if (mod->alt == NULL)
	    {
	      elf_end (mod->alt_elf);
	      mod->alt_elf = NULL;
	      close (mod->alt_fd);
	      mod->alt_fd = -1;
	    }
	  else
	    dwarf_setalt (mod->dw, mod->alt);
	}

      free (altfile); /* See above, we don't really need it.  */
    }
}
int main(void)
{
	size_t dst;

	return elf_getphdrnum(0, &dst);
}
Example #20
0
static const value_t *
elf_get_ehdr(const value_t *this_fn, parser_state_t *state,
             const char *filename, int binfd,
             Elf *e, GElf_Ehdr *ehdr, void *arg)
{
   int eclass = gelf_getclass(e);
   char *id = elf_getident(e, NULL);
   dir_t *einfo = dir_id_new();
   dir_t *edir = dir_id_new();
   size_t n;

   if (eclass == ELFCLASS32)
      dir_string_set(einfo, "class", value_int_new(32));
   else if (eclass == ELFCLASS64)
      dir_string_set(einfo, "class", value_int_new(64));
   else {
      dir_string_set(einfo, "class", &value_null);
      if (eclass != ELFCLASSNONE)
         parser_report(state, "ELF file has unknown class %d - %s\n",
                       eclass, elf_errmsg(-1));
   }

   if (id == NULL)
      parser_report(state, "ELF file has unknown identity - %s\n",
                    elf_errmsg(-1));
   else
      dir_string_set(einfo, "id",
                     value_string_new(id, EI_NIDENT));

   dir_string_set(einfo, "ehdr", dir_value(edir));

   dir_string_set(edir,"type"   , value_int_new(ehdr->e_type));
   dir_string_set(edir,"machine", value_int_new(ehdr->e_machine));
   dir_string_set(edir,"version", value_int_new(ehdr->e_version));
   dir_string_set(edir,"entry"  , value_int_new(ehdr->e_entry));
   dir_string_set(edir,"phoff"  , value_int_new(ehdr->e_phoff));
   dir_string_set(edir,"shoff"  , value_int_new(ehdr->e_shoff));
   dir_string_set(edir,"flags"  , value_int_new(ehdr->e_flags));
   dir_string_set(edir,"ehsize" , value_int_new(ehdr->e_ehsize));
   dir_string_set(edir,"phentsize",
                  value_int_new(ehdr->e_phentsize));
   dir_string_set(edir,"shentsize",
                  value_int_new(ehdr->e_shentsize));

   if (elf_getphdrnum(e, &n) != 0)
      parser_report(state,"ELF file has unknown number of segments - %s\n",
                    elf_errmsg(-1));
   else
      dir_string_set(einfo, "progsects", value_int_new(n));

   if (elf_getshdrnum(e, &n) != 0)
      parser_report(state, "%s: ELF file has unknown number of sections - %s\n",
                    elf_errmsg(-1));
   else
      dir_string_set(einfo, "sections", value_int_new(n));

   if (elf_getshdrstrndx(e, &n) != 0)
      parser_report(state, "%s: ELF file has no section names - %s\n",
                    elf_errmsg(-1));
   else
      dir_string_set(einfo, "shdrstrndx", value_int_new(n));

   return dir_value(einfo);
}
Example #21
0
int main(int argc, char **argv)
{
    if (argc != 2) {
        print_usage();
        return 1;
    }

    if (strcmp(argv[1], "demo") == 0) {
        test();
        return 0;
    }


    /* Code below adapted from 'libelf by Example' */
    Elf *e;
    GElf_Phdr phdr;
    int fd;
    size_t n, i;

    if (elf_version(EV_CURRENT) == EV_NONE) {
        printf("Error with elf library [%s]\n", elf_errmsg(-1));
        exit(1);
    }

    if ((fd = open(argv[1], O_RDONLY, 0)) < 0) {
        printf("Error reading file.\n");
        exit(1);
    }

    if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
        printf("elf_begin() failed [%s]\n", elf_errmsg(-1));
        exit(1);
    }

    if (elf_kind(e) != ELF_K_ELF) {
        printf("File is not an ELF object.\n");
        exit(1);
    }

    if (elf_getphdrnum(e, &n) != 0) {
        printf("elf_getphdrnum() failed [%s]\n", elf_errmsg(-1));
        exit(1);
    }

    /* Initialize libdis */
    x86_init(opt_none, NULL, NULL);

    gadget_t gadgets;
    gadget_init(&gadgets);

    /* Loop over each program header (segment) */
    for (i = 0; i < n; i++) {
        if (gelf_getphdr(e, i, &phdr) != &phdr) {
            printf("gelf_getphdr() failed [%s]\n", elf_errmsg(-1));
            exit(1);
        }

        /* If the segment is loaded into memory AND is executable */
        if (phdr.p_type == PT_LOAD && phdr.p_flags & PF_X) {
            /* Load the segment from the ELF file */
            unsigned char *buffer = malloc(phdr.p_filesz);
            lseek(fd, phdr.p_offset, SEEK_SET);
            ssize_t count = read(fd, buffer, phdr.p_filesz);
            if (count != phdr.p_filesz) {
                printf("read sucks.\n"); // FIXME: EINTR etc.
                exit(1);
            }
            /* Add all gadgets in the segment to our gadget tree */
            gadget_add(&gadgets, buffer, phdr.p_filesz, phdr.p_vaddr);
            free(buffer);
        }
    }

    elf_end(e);
    close(fd);

    gadget_print(&gadgets);
    gadget_destroy(&gadgets);

    return 0;
}
Example #22
0
struct ps_prochandle *
Pgrab_file(const char *fname, int *perr)
{
	struct ps_prochandle *P = NULL;
	char buf[PATH_MAX];
	GElf_Ehdr ehdr;
	Elf *elf = NULL;
	size_t phnum;
	file_info_t *fp = NULL;
	int fd;
	int i;

	if ((fd = open64(fname, O_RDONLY)) < 0) {
		dprintf("couldn't open file");
		*perr = (errno == ENOENT) ? G_NOEXEC : G_STRANGE;
		return (NULL);
	}

	if (elf_version(EV_CURRENT) == EV_NONE) {
		dprintf("libproc ELF version is more recent than libelf");
		*perr = G_ELF;
		goto err;
	}

	if ((P = calloc(1, sizeof (struct ps_prochandle))) == NULL) {
		*perr = G_STRANGE;
		goto err;
	}

	(void) mutex_init(&P->proc_lock, USYNC_THREAD, NULL);
	P->state = PS_IDLE;
	P->pid = (pid_t)-1;
	P->asfd = fd;
	P->ctlfd = -1;
	P->statfd = -1;
	P->agentctlfd = -1;
	P->agentstatfd = -1;
	P->info_valid = -1;
	P->ops = &P_idle_ops;
	Pinitsym(P);

	if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
		*perr = G_ELF;
		return (NULL);
	}

	/*
	 * Construct a file_info_t that corresponds to this file.
	 */
	if ((fp = calloc(1, sizeof (file_info_t))) == NULL) {
		*perr = G_STRANGE;
		goto err;
	}

	if ((fp->file_lo = calloc(1, sizeof (rd_loadobj_t))) == NULL) {
		*perr = G_STRANGE;
		goto err;
	}

	if (*fname == '/') {
		(void) strncpy(fp->file_pname, fname, sizeof (fp->file_pname));
	} else {
		size_t sz;

		if (getcwd(fp->file_pname, sizeof (fp->file_pname) - 1) ==
		    NULL) {
			*perr = G_STRANGE;
			goto err;
		}

		sz = strlen(fp->file_pname);
		(void) snprintf(&fp->file_pname[sz],
		    sizeof (fp->file_pname) - sz, "/%s", fname);
	}

	fp->file_fd = fd;
	fp->file_lo->rl_lmident = LM_ID_BASE;
	if ((fp->file_lname = strdup(fp->file_pname)) == NULL) {
		*perr = G_STRANGE;
		goto err;
	}
	fp->file_lbase = basename(fp->file_lname);

	if ((P->execname = strdup(fp->file_pname)) == NULL) {
		*perr = G_STRANGE;
		goto err;
	}

	P->num_files++;
	list_link(fp, &P->file_head);

	if (gelf_getehdr(elf, &ehdr) == NULL) {
		*perr = G_STRANGE;
		goto err;
	}

	if (elf_getphdrnum(elf, &phnum) == -1) {
		*perr = G_STRANGE;
		goto err;
	}

	dprintf("Pgrab_file: program header count = %lu\n", (ulong_t)phnum);

	/*
	 * Sift through the program headers making the relevant maps.
	 */
	for (i = 0; i < phnum; i++) {
		GElf_Phdr phdr, *php;

		if ((php = gelf_getphdr(elf, i, &phdr)) == NULL) {
			*perr = G_STRANGE;
			goto err;
		}

		if (php->p_type != PT_LOAD)
			continue;

		if (idle_add_mapping(P, php, fp) != 0) {
			*perr = G_STRANGE;
			goto err;
		}
	}
	Psort_mappings(P);

	(void) elf_end(elf);

	P->map_exec = fp->file_map;

	P->status.pr_flags = PR_STOPPED;
	P->status.pr_nlwp = 0;
	P->status.pr_pid = (pid_t)-1;
	P->status.pr_ppid = (pid_t)-1;
	P->status.pr_pgid = (pid_t)-1;
	P->status.pr_sid = (pid_t)-1;
	P->status.pr_taskid = (taskid_t)-1;
	P->status.pr_projid = (projid_t)-1;
	P->status.pr_zoneid = (zoneid_t)-1;
	switch (ehdr.e_ident[EI_CLASS]) {
	case ELFCLASS32:
		P->status.pr_dmodel = PR_MODEL_ILP32;
		break;
	case ELFCLASS64:
		P->status.pr_dmodel = PR_MODEL_LP64;
		break;
	default:
		*perr = G_FORMAT;
		goto err;
	}

	/*
	 * Pfindobj() checks what zone a process is associated with, so
	 * we call it after initializing pr_zoneid to -1.  This ensures
	 * we don't get associated with any zone on the system.
	 */
	if (Pfindobj(P, fp->file_lname, buf, sizeof (buf)) != NULL) {
		free(P->execname);
		P->execname = strdup(buf);
		if ((fp->file_rname = strdup(buf)) != NULL)
			fp->file_rbase = basename(fp->file_rname);
	}

	/*
	 * The file and map lists are complete, and will never need to be
	 * adjusted.
	 */
	P->info_valid = 1;

	return (P);
err:
	(void) close(fd);
	if (P != NULL)
		Pfree(P);
	if (elf != NULL)
		(void) elf_end(elf);
	return (NULL);
}
/* Search an ELF file for a ".gnu_debuglink" section.  */
static const char *
find_debuglink (Elf *elf, GElf_Word *crc)
{
  size_t shstrndx;
  if (elf_getshdrstrndx (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;
}

/* If the main file might have been prelinked, then we need to
   discover the correct synchronization address between the main and
   debug files.  Because of prelink's section juggling, we cannot rely
   on the address_sync computed from PT_LOAD segments (see open_elf).

   We will attempt to discover a synchronization address based on the
   section headers instead.  But finding a section address that is
   safe to use requires identifying which sections are SHT_PROGBITS.
   We can do that in the main file, but in the debug file all the
   allocated sections have been transformed into SHT_NOBITS so we have
   lost the means to match them up correctly.

   The only method left to us is to decode the .gnu.prelink_undo
   section in the prelinked main file.  This shows what the sections
   looked like before prelink juggled them--when they still had a
   direct correspondence to the debug file.  */
static Dwfl_Error
find_prelink_address_sync (Dwfl_Module *mod, struct dwfl_file *file)
{
  /* The magic section is only identified by name.  */
  size_t shstrndx;
  if (elf_getshdrstrndx (mod->main.elf, &shstrndx) < 0)
    return DWFL_E_LIBELF;

  Elf_Scn *scn = NULL;
  while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
    {
      GElf_Shdr shdr_mem;
      GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
      if (unlikely (shdr == NULL))
	return DWFL_E_LIBELF;
      if (shdr->sh_type == SHT_PROGBITS
	  && !(shdr->sh_flags & SHF_ALLOC)
	  && shdr->sh_name != 0)
	{
	  const char *secname = elf_strptr (mod->main.elf, shstrndx,
					    shdr->sh_name);
	  if (unlikely (secname == NULL))
	    return DWFL_E_LIBELF;
	  if (!strcmp (secname, ".gnu.prelink_undo"))
	    break;
	}
    }

  if (scn == NULL)
    /* There was no .gnu.prelink_undo section.  */
    return DWFL_E_NOERROR;

  Elf_Data *undodata = elf_rawdata (scn, NULL);
  if (unlikely (undodata == NULL))
    return DWFL_E_LIBELF;

  /* Decode the section.  It consists of the original ehdr, phdrs,
     and shdrs (but omits section 0).  */

  union
  {
    Elf32_Ehdr e32;
    Elf64_Ehdr e64;
  } ehdr;
  Elf_Data dst =
    {
      .d_buf = &ehdr,
      .d_size = sizeof ehdr,
      .d_type = ELF_T_EHDR,
      .d_version = EV_CURRENT
    };
  Elf_Data src = *undodata;
  src.d_size = gelf_fsize (mod->main.elf, ELF_T_EHDR, 1, EV_CURRENT);
  src.d_type = ELF_T_EHDR;
  if (unlikely (gelf_xlatetom (mod->main.elf, &dst, &src,
			       elf_getident (mod->main.elf, NULL)[EI_DATA])
		== NULL))
    return DWFL_E_LIBELF;

  size_t shentsize = gelf_fsize (mod->main.elf, ELF_T_SHDR, 1, EV_CURRENT);
  size_t phentsize = gelf_fsize (mod->main.elf, ELF_T_PHDR, 1, EV_CURRENT);

  uint_fast16_t phnum;
  uint_fast16_t shnum;
  if (ehdr.e32.e_ident[EI_CLASS] == ELFCLASS32)
    {
      if (ehdr.e32.e_shentsize != shentsize
	  || ehdr.e32.e_phentsize != phentsize)
	return DWFL_E_BAD_PRELINK;
      phnum = ehdr.e32.e_phnum;
      shnum = ehdr.e32.e_shnum;
    }
  else
    {
      if (ehdr.e64.e_shentsize != shentsize
	  || ehdr.e64.e_phentsize != phentsize)
	return DWFL_E_BAD_PRELINK;
      phnum = ehdr.e64.e_phnum;
      shnum = ehdr.e64.e_shnum;
    }

  /* Since prelink does not store the zeroth section header in the undo
     section, it cannot support SHN_XINDEX encoding.  */
  if (unlikely (shnum >= SHN_LORESERVE)
      || unlikely (undodata->d_size != (src.d_size
					+ phnum * phentsize
					+ (shnum - 1) * shentsize)))
    return DWFL_E_BAD_PRELINK;

  /* We look at the allocated SHT_PROGBITS (or SHT_NOBITS) sections.  (Most
     every file will have some SHT_PROGBITS sections, but it's possible to
     have one with nothing but .bss, i.e. SHT_NOBITS.)  The special sections
     that can be moved around have different sh_type values--except for
     .interp, the section that became the PT_INTERP segment.  So we exclude
     the SHT_PROGBITS section whose address matches the PT_INTERP p_vaddr.
     For this reason, we must examine the phdrs first to find PT_INTERP.  */

  GElf_Addr main_interp = 0;
  {
    size_t main_phnum;
    if (unlikely (elf_getphdrnum (mod->main.elf, &main_phnum)))
      return DWFL_E_LIBELF;
    for (size_t i = 0; i < main_phnum; ++i)
      {
	GElf_Phdr phdr;
	if (unlikely (gelf_getphdr (mod->main.elf, i, &phdr) == NULL))
	  return DWFL_E_LIBELF;
	if (phdr.p_type == PT_INTERP)
	  {
	    main_interp = phdr.p_vaddr;
	    break;
	  }
      }
  }

  src.d_buf += src.d_size;
  src.d_type = ELF_T_PHDR;
  src.d_size = phnum * phentsize;

  GElf_Addr undo_interp = 0;
  {
    union
    {
      Elf32_Phdr p32[phnum];
      Elf64_Phdr p64[phnum];
    } phdr;
    dst.d_buf = &phdr;
    dst.d_size = sizeof phdr;
    if (unlikely (gelf_xlatetom (mod->main.elf, &dst, &src,
				 ehdr.e32.e_ident[EI_DATA]) == NULL))
      return DWFL_E_LIBELF;
    if (ehdr.e32.e_ident[EI_CLASS] == ELFCLASS32)
      {
	for (uint_fast16_t i = 0; i < phnum; ++i)
	  if (phdr.p32[i].p_type == PT_INTERP)
	    {
	      undo_interp = phdr.p32[i].p_vaddr;
	      break;
	    }
      }
    else
      {
	for (uint_fast16_t i = 0; i < phnum; ++i)
	  if (phdr.p64[i].p_type == PT_INTERP)
	    {
	      undo_interp = phdr.p64[i].p_vaddr;
	      break;
	    }
      }
  }

  if (unlikely ((main_interp == 0) != (undo_interp == 0)))
    return DWFL_E_BAD_PRELINK;

  src.d_buf += src.d_size;
  src.d_type = ELF_T_SHDR;
  src.d_size = gelf_fsize (mod->main.elf, ELF_T_SHDR, shnum - 1, EV_CURRENT);

  union
  {
    Elf32_Shdr s32[shnum - 1];
    Elf64_Shdr s64[shnum - 1];
  } shdr;
  dst.d_buf = &shdr;
  dst.d_size = sizeof shdr;
  if (unlikely (gelf_xlatetom (mod->main.elf, &dst, &src,
			       ehdr.e32.e_ident[EI_DATA]) == NULL))
    return DWFL_E_LIBELF;

  /* Now we can look at the original section headers of the main file
     before it was prelinked.  First we'll apply our method to the main
     file sections as they are after prelinking, to calculate the
     synchronization address of the main file.  Then we'll apply that
     same method to the saved section headers, to calculate the matching
     synchronization address of the debug file.

     The method is to consider SHF_ALLOC sections that are either
     SHT_PROGBITS or SHT_NOBITS, excluding the section whose sh_addr
     matches the PT_INTERP p_vaddr.  The special sections that can be
     moved by prelink have other types, except for .interp (which
     becomes PT_INTERP).  The "real" sections cannot move as such, but
     .bss can be split into .dynbss and .bss, with the total memory
     image remaining the same but being spread across the two sections.
     So we consider the highest section end, which still matches up.  */

  GElf_Addr highest;

  inline void consider_shdr (GElf_Addr interp,
			     GElf_Word sh_type,
			     GElf_Xword sh_flags,
			     GElf_Addr sh_addr,
			     GElf_Xword sh_size)
  {
    if ((sh_flags & SHF_ALLOC)
	&& ((sh_type == SHT_PROGBITS && sh_addr != interp)
	    || sh_type == SHT_NOBITS))
      {
	const GElf_Addr sh_end = sh_addr + sh_size;
	if (sh_end > highest)
	  highest = sh_end;
      }
  }

  highest = 0;
  scn = NULL;
  while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
    {
      GElf_Shdr sh_mem;
      GElf_Shdr *sh = gelf_getshdr (scn, &sh_mem);
      if (unlikely (sh == NULL))
	return DWFL_E_LIBELF;
      consider_shdr (main_interp, sh->sh_type, sh->sh_flags,
		     sh->sh_addr, sh->sh_size);
    }
  if (highest > mod->main.vaddr)
    {
      mod->main.address_sync = highest;

      highest = 0;
      if (ehdr.e32.e_ident[EI_CLASS] == ELFCLASS32)
	for (size_t i = 0; i < shnum - 1; ++i)
	  consider_shdr (undo_interp, shdr.s32[i].sh_type, shdr.s32[i].sh_flags,
			 shdr.s32[i].sh_addr, shdr.s32[i].sh_size);
      else
	for (size_t i = 0; i < shnum - 1; ++i)
	  consider_shdr (undo_interp, shdr.s64[i].sh_type, shdr.s64[i].sh_flags,
			 shdr.s64[i].sh_addr, shdr.s64[i].sh_size);

      if (highest > file->vaddr)
	file->address_sync = highest;
      else
	return DWFL_E_BAD_PRELINK;
    }

  return DWFL_E_NOERROR;
}

/* 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);
  Dwfl_Error result = open_elf (mod, &mod->debug);
  if (result == DWFL_E_NOERROR && mod->debug.address_sync != 0)
    result = find_prelink_address_sync (mod, &mod->debug);
  return result;
}


/* 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, int *first_global, 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;
	    *first_global = shdr->sh_info;
	    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;
	    *first_global = shdr->sh_info;
	    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, 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
	      && 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);

  size_t phnum;
  if (unlikely (elf_getphdrnum (mod->main.elf, &phnum) != 0))
    return;

  for (size_t i = 0; i < 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.  ADJUST is either zero
	     in case the dynamic segment wasn't adjusted or mod->main_bias.  */
	  void translate_offs (GElf_Addr adjust)
	  {
	    GElf_Off offs[i_max] = { 0, };
	    find_offsets (mod->main.elf, adjust, phnum, 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));

		    // elf_getdata_rawchunk takes a size_t, make sure it
		    // doesn't overflow.
#if SIZE_MAX <= UINT32_MAX
		    if (nbuckets > SIZE_MAX / sizeof (Elf32_Word))
		      data = NULL;
		    else
#endif
		      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;
		  }
	      }
	  }

	  /* First try unadjusted, like ELF files from disk, vdso.
	     Then try for already adjusted dynamic section, like ELF
	     from remote memory.  */
	  translate_offs (0);
	  if (mod->symfile == NULL)
	    translate_offs (mod->main_bias);

	  return;
	}
/* 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;
}
/* Open libelf FILE->fd and compute the load base of ELF as loaded in MOD.
   When we return success, FILE->elf and FILE->vaddr are set up.  */
static inline Dwfl_Error
open_elf (Dwfl_Module *mod, struct dwfl_file *file)
{
  Dwfl_Error error = open_elf_file (&file->elf, &file->fd, &file->name);
  if (error != DWFL_E_NOERROR)
    return error;

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

  if (ehdr->e_type != ET_REL)
    {
      /* In any non-ET_REL file, we compute the "synchronization address".

	 We start with the address at the end of the first PT_LOAD
	 segment.  When prelink converts REL to RELA in an ET_DYN
	 file, it expands the space between the beginning of the
	 segment and the actual code/data addresses.  Since that
	 change wasn't made in the debug file, the distance from
	 p_vaddr to an address of interest (in an st_value or DWARF
	 data) now differs between the main and debug files.  The
	 distance from address_sync to an address of interest remains
	 consistent.

	 If there are no section headers at all (full stripping), then
	 the end of the first segment is a valid synchronization address.
	 This cannot happen in a prelinked file, since prelink itself
	 relies on section headers for prelinking and for undoing it.
	 (If you do full stripping on a prelinked file, then you get what
	 you deserve--you can neither undo the prelinking, nor expect to
	 line it up with a debug file separated before prelinking.)

	 However, when prelink processes an ET_EXEC file, it can do
	 something different.  There it juggles the "special" sections
	 (SHT_DYNSYM et al) to make space for the additional prelink
	 special sections.  Sometimes it will do this by moving a special
	 section like .dynstr after the real program sections in the first
	 PT_LOAD segment--i.e. to the end.  That changes the end address of
	 the segment, so it no longer lines up correctly and is not a valid
	 synchronization address to use.  Because of this, we need to apply
	 a different prelink-savvy means to discover the synchronization
	 address when there is a separate debug file and a prelinked main
	 file.  That is done in find_debuginfo, below.  */

      size_t phnum;
      if (unlikely (elf_getphdrnum (file->elf, &phnum) != 0))
	goto elf_error;

      file->vaddr = file->address_sync = 0;
      for (size_t i = 0; i < phnum; ++i)
	{
	  GElf_Phdr ph_mem;
	  GElf_Phdr *ph = gelf_getphdr (file->elf, i, &ph_mem);
	  if (unlikely (ph == NULL))
	    goto elf_error;
	  if (ph->p_type == PT_LOAD)
	    {
	      file->vaddr = ph->p_vaddr & -ph->p_align;
	      file->address_sync = ph->p_vaddr + ph->p_memsz;
	      break;
	    }
	}
    }

  /* We only want to set the module e_type explictly once, derived from
     the main ELF file.  (It might be changed for the kernel, because
     that is special - see below.)  open_elf is always called first for
     the main ELF file, because both find_dw and find_symtab call
     __libdwfl_getelf first to open the main file.  So don't let debug
     or aux files override the module e_type.  The kernel heuristic
     below could otherwise trigger for non-kernel/non-main files, since
     their phdrs might not match the actual load addresses.  */
  if (file == &mod->main)
    {
      mod->e_type = ehdr->e_type;

      /* Relocatable Linux kernels are ET_EXEC but act like ET_DYN.  */
      if (mod->e_type == ET_EXEC && file->vaddr != mod->low_addr)
	mod->e_type = ET_DYN;
    }
  else
    assert (mod->main.elf != NULL);

  return DWFL_E_NOERROR;
}
Example #27
0
/* load_kernel loads an ELF file as a kernel. */
uintptr_t
load_kernel(char *filename)
{
	Elf64_Ehdr *ehdr;
	Elf *elf;
	size_t phnum = 0;
	Elf64_Phdr *hdrs;
	int fd;

	elf_version(EV_CURRENT);
	fd = open(filename, O_RDONLY);
	if (fd < 0) {
		fprintf(stderr, "Can't open %s: %r\n", filename);
		return 0;
	}

	elf = elf_begin(fd, ELF_C_READ, NULL);
	if (elf == NULL) {
		fprintf(stderr, "%s: cannot read %s ELF file.\n", __func__, filename);
		close(fd);
		return 0;
	}

	ehdr = elf64_getehdr(elf);
	if (ehdr == NULL) {
		fprintf(stderr, "%s: cannot get exec header of %s.\n",
		        __func__, filename);
		goto fail;
	}
	fprintf(stderr, "%s ELF entry point is %p\n", filename,
		(void *)ehdr->e_entry);

	if (elf_getphdrnum(elf, &phnum) < 0) {
		fprintf(stderr, "%s: cannot get program header num of %s.\n",
		        __func__, filename);
		goto fail;
	}
	fprintf(stderr, "%s has %p program headers\n", filename, phnum);

	hdrs = elf64_getphdr(elf);
	if (hdrs == NULL) {
		fprintf(stderr, "%s: cannot get program headers of %s.\n",
		        __func__, filename);
		goto fail;
	}

	for (int i = 0; i < phnum; i++) {
		size_t tot;
		Elf64_Phdr *h = &hdrs[i];
		uintptr_t pa;

		fprintf(stderr,
		        "%d: type 0x%lx flags 0x%lx  offset 0x%lx vaddr 0x%lx paddr 0x%lx size 0x%lx  memsz 0x%lx align 0x%lx\n",
		        i,
		        h->p_type,		/* Segment type */
		        h->p_flags,		/* Segment flags */
		        h->p_offset,		/* Segment file offset */
		        h->p_vaddr,		/* Segment virtual address */
		        h->p_paddr,		/* Segment physical address */
		        h->p_filesz,		/* Segment size in file */
		        h->p_memsz,		/* Segment size in memory */
		        h->p_align		/* Segment alignment */);
		if (h->p_type != PT_LOAD)
			continue;
		if ((h->p_flags & (PF_R | PF_W | PF_X)) == 0)
			continue;

		pa = h->p_paddr;
		fprintf(stderr,
		        "Read header %d @offset %p to %p (elf PA is %p) %d bytes:",
		        i, h->p_offset, pa, h->p_paddr, h->p_filesz);
		tot = 0;
		while (tot < h->p_filesz) {
			int amt = pread(fd, (void *)(pa + tot), h->p_filesz - tot,
			                h->p_offset + tot);
			if (amt < 1)
				break;
			tot += amt;
		}
		fprintf(stderr, "read a total of %d bytes\n", tot);
		if (tot < h->p_filesz) {
			fprintf(stderr, "%s: got %d bytes, wanted %d bytes\n",
			        filename, tot, h->p_filesz);
			goto fail;
		}
	}

	close(fd);
	elf_end(elf);
	return ehdr->e_entry;
 fail:
	close(fd);
	elf_end(elf);
	return 0;
}
Example #28
0
/* Invoke the argument FTL function with <addr> <buf> arguments for each of
   the ELF file's program segments */
static const value_t *
elf_load_with_fn(const value_t *this_fn, parser_state_t *state,
                 const char *filename, int binfd,
                 Elf *e, GElf_Ehdr *ehdr, void *arg)
{
   elf_loadfn_args_t *fnarg = (elf_loadfn_args_t *)arg;
   const value_t *hdrcheckfn = fnarg->hdrcheckfn;
   const value_t *memwrfn = fnarg->memwrfn;
   const value_t *resval = value_false;
   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;
      bool ok = FALSE;

      if (hdrcheckfn != NULL) {
         const value_t *ehdrval = elf_get_ehdr(this_fn, state, filename, binfd,
                                               e, ehdr, /*arg*/NULL);
         if (ehdrval != NULL) {
            const value_t *code = value_closure_bind(hdrcheckfn, ehdrval);

            if (code == NULL) {
               parser_error(state, "couldn't apply "
                            "header argument to check function\n");
            } else {
               const value_t *fnres = invoke(code, state);
               /* we expect this to return a TRUE/FALSE value */
               ok = (fnres == value_true);
               /*if (!ok) printf("%s: check fn returns non-TRUE\n", codeid());*/
            }
         }
      } else
         ok = TRUE;

      resval = ok? value_true: value_false;

      if (ok && memwrfn != NULL) {
         for (i = 0; ok && i < n; i++) {
            GElf_Phdr phdr;

            if (gelf_getphdr(e, i, &phdr) != &phdr)
               parser_report(state, "ELF segment %d unretrievable - %s\n",
                             i, elf_errmsg(-1));
            else {
               long offset = (long)phdr.p_offset;
               if (phdr.p_offset != (Elf64_Off)offset)
                  parser_report(state, "ELF segment %d - "
                                 "offset does not fit in 32-bits\n", i);
               else
               if (-1 == fe_lseek(binfd, offset))
                  parser_report(state, "ELF segment %d - "
                                 "offset 0X%lX is outside file\n",
                                 i, offset);
               else
               if (phdr.p_memsz < phdr.p_filesz)
                  parser_report(state,
                                "ELF segment %d - smaller size in memory (%d)"
                                "than on file (%d) - corrupt header?\n",
                                i, phdr.p_memsz, phdr.p_filesz);
               else {
                  number_t addr = phdr.p_vaddr; /* where to load segment */
                  char *buf = NULL;
                  size_t buflen = (size_t)phdr.p_memsz;
                  value_t *addrval = value_int_new(addr);
                  value_t *dataval = value_string_alloc_new(buflen, &buf);

                  if (phdr.p_memsz != (Elf64_Xword)buflen)
                     parser_report(state, "ELF segment %d - "
                                    "size does not fit in 32-bits\n", i);
                  else
                  if (dataval == NULL)
                     parser_report(state,
                                   "ELF segment %d - no memory for "
                                   "next %d bytes\n",
                                   i, buflen);
                  else {
                     ssize_t readbytes = fe_read(binfd, buf, buflen);

                     if (readbytes < buflen)
                        parser_report(state,
                                      "ELF segment %d - "
                                      "only %d bytes of %d read\n",
                                      i, readbytes);
                     else {
                        const value_t *code;
                        ok = TRUE;

                        /* zero any uninitialized area */
                        if (phdr.p_memsz > phdr.p_filesz) {
                           size_t extra_bytes = (size_t)
                                                (phdr.p_memsz - phdr.p_filesz);
                           if ((Elf64_Xword)extra_bytes !=
                               phdr.p_memsz-phdr.p_filesz) {
                              ok = FALSE;
                              parser_report(state, "ELF segment %d - "
                                            "BSS area larger than 32-bits\n",
                                            i);
                           } else
                              memset(&buf[phdr.p_filesz], '\0', extra_bytes);
                        }

                        code = value_closure_bind_2(state, memwrfn,
                                                    "address", addrval,
                                                    "data", dataval);

                        if (NULL != code) {
                           const value_t *fnres = invoke(code, state);
                           /* we expect this to return a TRUE/FALSE value */
                           if (fnres != value_true) {
                              /*printf("%s: seg fn returns non-TRUE\n",
                                       codeid());*/
                              ok = FALSE;
                           }
                           /* we rely on garbage collection to collect the data
                            * buffer */
                        } else
                           value_delete(&dataval);
                     }
                  }
               }
            }
         }

         if (!ok)
            resval = value_false;
      }
   }
   return resval;
}
Example #29
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;
}
Example #30
0
static int
ctf_write_elf(ctf_file_t *fp, Elf *src, Elf *dst, int flags)
{
	GElf_Ehdr sehdr, dehdr;
	Elf_Scn *sscn, *dscn;
	Elf_Data *sdata, *ddata;
	GElf_Shdr shdr;
	int symtab_idx = -1;
	off_t new_offset = 0;
	off_t ctfnameoff = 0;
	int compress = (flags & CTF_ELFWRITE_F_COMPRESS);
	int *secxlate = NULL;
	int srcidx, dstidx, pad, i;
	int curnmoff = 0;
	int changing = 0;
	int ret;
	size_t nshdr, nphdr, strndx;
	void *strdatabuf = NULL, *symdatabuf = NULL;
	size_t strdatasz = 0, symdatasz = 0;

	void *cdata = NULL;
	size_t elfsize, asize;

	if ((flags & ~(CTF_ELFWRITE_F_COMPRESS)) != 0) {
		ret = ctf_set_errno(fp, EINVAL);
		goto out;
	}

	if (gelf_newehdr(dst, gelf_getclass(src)) == 0) {
		ret = ctf_set_errno(fp, ECTF_ELF);
		goto out;
	}
	if (gelf_getehdr(src, &sehdr) == NULL) {
		ret = ctf_set_errno(fp, ECTF_ELF);
		goto out;
	}
	(void) memcpy(&dehdr, &sehdr, sizeof (GElf_Ehdr));
	if (gelf_update_ehdr(dst, &dehdr) == 0) {
		ret = ctf_set_errno(fp, ECTF_ELF);
		goto out;
	}

	/*
	 * Use libelf to get the number of sections and the string section to
	 * deal with ELF files that may have a large number of sections. We just
	 * always use this to make our live easier.
	 */
	if (elf_getphdrnum(src, &nphdr) != 0) {
		ret = ctf_set_errno(fp, ECTF_ELF);
		goto out;
	}
	if (elf_getshdrnum(src, &nshdr) != 0) {
		ret = ctf_set_errno(fp, ECTF_ELF);
		goto out;
	}
	if (elf_getshdrstrndx(src, &strndx) != 0) {
		ret = ctf_set_errno(fp, ECTF_ELF);
		goto out;
	}

	/*
	 * Neither the existing debug sections nor the SUNW_ctf sections (new or
	 * existing) are SHF_ALLOC'd, so they won't be in areas referenced by
	 * program headers.  As such, we can just blindly copy the program
	 * headers from the existing file to the new file.
	 */
	if (nphdr != 0) {
		(void) elf_flagelf(dst, ELF_C_SET, ELF_F_LAYOUT);
		if (gelf_newphdr(dst, nphdr) == 0) {
			ret = ctf_set_errno(fp, ECTF_ELF);
			goto out;
		}

		for (i = 0; i < nphdr; i++) {
			GElf_Phdr phdr;

			if (gelf_getphdr(src, i, &phdr) == NULL) {
				ret = ctf_set_errno(fp, ECTF_ELF);
				goto out;
			}
			if (gelf_update_phdr(dst, i, &phdr) == 0) {
				ret = ctf_set_errno(fp, ECTF_ELF);
				goto out;
			}
		}
	}

	secxlate = ctf_alloc(sizeof (int) * nshdr);
	for (srcidx = dstidx = 0; srcidx < nshdr; srcidx++) {
		Elf_Scn *scn = elf_getscn(src, srcidx);
		GElf_Shdr shdr;
		char *sname;

		if (gelf_getshdr(scn, &shdr) == NULL) {
			ret = ctf_set_errno(fp, ECTF_ELF);
			goto out;
		}
		sname = elf_strptr(src, strndx, shdr.sh_name);
		if (sname == NULL) {
			ret = ctf_set_errno(fp, ECTF_ELF);
			goto out;
		}

		if (strcmp(sname, CTF_ELF_SCN_NAME) == 0) {
			secxlate[srcidx] = -1;
		} else {
			secxlate[srcidx] = dstidx++;
			curnmoff += strlen(sname) + 1;
		}

		new_offset = (off_t)dehdr.e_phoff;
	}

	for (srcidx = 1; srcidx < nshdr; srcidx++) {
		char *sname;

		sscn = elf_getscn(src, srcidx);
		if (gelf_getshdr(sscn, &shdr) == NULL) {
			ret = ctf_set_errno(fp, ECTF_ELF);
			goto out;
		}

		if (secxlate[srcidx] == -1) {
			changing = 1;
			continue;
		}

		dscn = elf_newscn(dst);
		if (dscn == NULL) {
			ret = ctf_set_errno(fp, ECTF_ELF);
			goto out;
		}

		/*
		 * If this file has program headers, we need to explicitly lay
		 * out sections.  If none of the sections prior to this one have
		 * been removed, then we can just use the existing location.  If
		 * one or more sections have been changed, then we need to
		 * adjust this one to avoid holes.
		 */
		if (changing && nphdr != 0) {
			pad = new_offset % shdr.sh_addralign;

			if (pad != 0)
				new_offset += shdr.sh_addralign - pad;
			shdr.sh_offset = new_offset;
		}

		shdr.sh_link = secxlate[shdr.sh_link];

		if (shdr.sh_type == SHT_REL || shdr.sh_type == SHT_RELA)
			shdr.sh_info = secxlate[shdr.sh_info];

		sname = elf_strptr(src, strndx, shdr.sh_name);
		if (sname == NULL) {
			ret = ctf_set_errno(fp, ECTF_ELF);
			goto out;
		}
		if ((sdata = elf_getdata(sscn, NULL)) == NULL) {
			ret = ctf_set_errno(fp, ECTF_ELF);
			goto out;
		}
		if ((ddata = elf_newdata(dscn)) == NULL) {
			ret = ctf_set_errno(fp, ECTF_ELF);
			goto out;
		}
		bcopy(sdata, ddata, sizeof (Elf_Data));

		if (srcidx == strndx) {
			char seclen = strlen(CTF_ELF_SCN_NAME);

			strdatasz = ddata->d_size + shdr.sh_size +
			    seclen + 1;
			ddata->d_buf = strdatabuf = ctf_alloc(strdatasz);
			if (ddata->d_buf == NULL) {
				ret = ctf_set_errno(fp, ECTF_ELF);
				goto out;
			}
			bcopy(sdata->d_buf, ddata->d_buf, shdr.sh_size);
			(void) strcpy((caddr_t)ddata->d_buf + shdr.sh_size,
			    CTF_ELF_SCN_NAME);
			ctfnameoff = (off_t)shdr.sh_size;
			shdr.sh_size += seclen + 1;
			ddata->d_size += seclen + 1;

			if (nphdr != 0)
				changing = 1;
		}

		if (shdr.sh_type == SHT_SYMTAB && shdr.sh_entsize != 0) {
			int nsym = shdr.sh_size / shdr.sh_entsize;

			symtab_idx = secxlate[srcidx];

			symdatasz = shdr.sh_size;
			ddata->d_buf = symdatabuf = ctf_alloc(symdatasz);
			if (ddata->d_buf == NULL) {
				ret = ctf_set_errno(fp, ECTF_ELF);
				goto out;
			}
			(void) bcopy(sdata->d_buf, ddata->d_buf, shdr.sh_size);

			for (i = 0; i < nsym; i++) {
				GElf_Sym sym;
				short newscn;

				(void) gelf_getsym(ddata, i, &sym);

				if (sym.st_shndx >= SHN_LORESERVE)
					continue;

				if ((newscn = secxlate[sym.st_shndx]) !=
				    sym.st_shndx) {
					sym.st_shndx =
					    (newscn == -1 ? 1 : newscn);

					if (gelf_update_sym(ddata, i, &sym) ==
					    0) {
						ret = ctf_set_errno(fp,
						    ECTF_ELF);
						goto out;
					}
				}
			}
		}

		if (gelf_update_shdr(dscn, &shdr) == 0) {
			ret = ctf_set_errno(fp, ECTF_ELF);
			goto out;
		}

		new_offset = (off_t)shdr.sh_offset;
		if (shdr.sh_type != SHT_NOBITS)
			new_offset += shdr.sh_size;
	}

	if (symtab_idx == -1) {
		ret = ctf_set_errno(fp, ECTF_ELF);
		goto out;
	}

	/* Add the ctf section */
	if ((dscn = elf_newscn(dst)) == NULL) {
		ret = ctf_set_errno(fp, ECTF_ELF);
		goto out;
	}
	if (gelf_getshdr(dscn, &shdr) == NULL) {
		ret = ctf_set_errno(fp, ECTF_ELF);
		goto out;
	}
	shdr.sh_name = ctfnameoff;
	shdr.sh_type = SHT_PROGBITS;
	shdr.sh_size = fp->ctf_size;
	shdr.sh_link = symtab_idx;
	shdr.sh_addralign = 4;
	if (changing && nphdr != 0) {
		pad = new_offset % shdr.sh_addralign;

		if (pad)
			new_offset += shdr.sh_addralign - pad;

		shdr.sh_offset = new_offset;
		new_offset += shdr.sh_size;
	}

	if ((ddata = elf_newdata(dscn)) == NULL) {
		ret = ctf_set_errno(fp, ECTF_ELF);
		goto out;
	}

	if (compress != 0) {
		int err;

		if (ctf_zopen(&err) == NULL) {
			ret = ctf_set_errno(fp, err);
			goto out;
		}

		if ((err = ctf_compress(fp, &cdata, &asize, &elfsize)) != 0) {
			ret = ctf_set_errno(fp, err);
			goto out;
		}
		ddata->d_buf = cdata;
		ddata->d_size = elfsize;
	} else {
		ddata->d_buf = (void *)fp->ctf_base;
		ddata->d_size = fp->ctf_size;
	}
	ddata->d_align = shdr.sh_addralign;

	if (gelf_update_shdr(dscn, &shdr) == 0) {
		ret = ctf_set_errno(fp, ECTF_ELF);
		goto out;
	}

	/* update the section header location */
	if (nphdr != 0) {
		size_t align = gelf_fsize(dst, ELF_T_ADDR, 1, EV_CURRENT);
		size_t r = new_offset % align;

		if (r)
			new_offset += align - r;

		dehdr.e_shoff = new_offset;
	}

	/* commit to disk */
	if (sehdr.e_shstrndx == SHN_XINDEX)
		dehdr.e_shstrndx = SHN_XINDEX;
	else
		dehdr.e_shstrndx = secxlate[sehdr.e_shstrndx];
	if (gelf_update_ehdr(dst, &dehdr) == 0) {
		ret = ctf_set_errno(fp, ECTF_ELF);
		goto out;
	}
	if (elf_update(dst, ELF_C_WRITE) < 0) {
		ret = ctf_set_errno(fp, ECTF_ELF);
		goto out;
	}

	ret = 0;

out:
	if (strdatabuf != NULL)
		ctf_free(strdatabuf, strdatasz);
	if (symdatabuf != NULL)
		ctf_free(symdatabuf, symdatasz);
	if (cdata != NULL)
		ctf_data_free(cdata, fp->ctf_size);
	if (secxlate != NULL)
		ctf_free(secxlate, sizeof (int) * nshdr);

	return (ret);
}