Questions regarding editing an elf executable

Mark Wielaard
Thu Jun 25 23:18:10 GMT 2020

Hi Anastasios,

On Thu, 2020-06-25 at 02:46 +0100, Anastasios Andronidis via Elfutils-
devel wrote:
> My end goal is to add a DT_NEEDED entry into an arbitrary elf file,
> but before this I should just print the DT_NEEDED entries like this:
> ```C
> // Some code that copies a source_elf_file to a new target_elf_file
> so
> // we don't destroy the original binary.
> int fd = open(target_elf_file, O_RDWR, 0);
> Elf *e = elf_begin(fd, ELF_C_RDWR_MMAP, NULL);

In general I would recommend against using ELF_C_RDWR_MMAP and use
ELF_C_RDWR instead. Unless you know all your changes are "in place".
ELF_C_RDWR_MAP sometimes has problems extending the mmap. It really is
an optimization only useful for in-place replacing of data.

> Elf_Scn *scn = NULL;
> while ((scn = elf_nextscn(e, scn)) != NULL) {
>   GElf_Shdr shdr;
>   gelf_getshdr(scn, &shdr);
>   if (shdr.sh_type == SHT_DYNAMIC) {
>     Elf_Data *data = elf_getdata(scn, data);
>     size_t sh_entsize = gelf_fsize(e, ELF_T_DYN, 1, EV_CURRENT);
>     for (size_t i = 0; i < shdr.sh_size / sh_entsize; i++) {
>       GElf_Dyn dynmem;
>       GElf_Dyn *dyn = gelf_getdyn(data, i, &dynmem);
>       if (dyn->d_tag == DT_NEEDED) {
>         printf("Lib: %s\n", elf_strptr(e, shdr.sh_link, dyn-
> >d_un.d_val));
>       }
>    }
> }
> elf_update(e, ELF_C_WRITE);
> elf_end(e);
> close(fd);
> ```
> 1) Notice that I run `elf_update(e, ELF_C_WRITE);` in the end as I
> had the impression that this command should actually do nothing
> because I modified nothing. Unfortunately this is not what happens.
> The resulting elf executable is corrupted. I expected libelf would
> leave the elf file as it was, or at least produce a valid executable.

libelf is too "smart". When it sees the elf_update() it believe it can
help "optimize" the way the sections are placed together in the ELF
file. Normally that is fine. But you are operating on an ELF file with
both a section header and a program header. libelf knows very little
about the program header. It also doesn't know how (and if) the program
header segments map to the sections.

Wikipedia has a nice graphic showing ELF segments and sections:

By updating the section data offsets it messes up the program header
segment offsets.

If you don't want libelf to be "helpful" then call:
elf_flagelf (e, ELF_C_SET, ELF_F_LAYOUT);

Then elf_update will still write out any changed (dirty) data, but you
are responsible for making sure the section header offsets and sizes
are correct. If you add some extra data and don't update the size and
offset [of the next section], elf_update will happily write over the
data in the next section instead of moving it.

> 2b) I created a new data and copied the old to new. Didn't work.
> ```C
> ...
> if (shdr.sh_type == SHT_DYNAMIC) {
>   ...
>   // Create a new data container for SHT_DYNAMIC and copy all old
> values.
>   Elf_Data *new_data = elf_newdata(scn);
>   *new_data = *data;
>   // We will add 1 more entry.
>   size_t new_data_size = data->d_size + sh_entsize;
>   // Allocate and set the new data buffer.
>   void *new_data_buf = malloc(new_data_size);
>   new_data->d_buf = new_data_buf;
>   new_data->d_size = new_data_size;
>   // Copy old data to the new buffer.
>   memcpy(new_data->d_buf, data->d_buf, data->d_size);
>   // Add our new entry.
>   GElf_Dyn *new_data_dyn = new_data->d_buf;
>   new_data_dyn[new_data_size / sh_entsize-2].d_tag = DT_NEEDED;
>   new_data_dyn[new_data_size / sh_entsize-2].d_un.d_val = 1;
> }
> ```

Your real problem is not calling ELF_F_LAYOUT as described above. But
the above will add the new_data to the section, in addition to the
already existing data. If you aren't changing the size then you can
just replace the data in place through data->d_buf. Then call
elf_flagdata (data, ELF_C_SET, ELF_F_DIRTY) to tell libelf to write it
out. If you want to totally replace the data, you can just replace the
data->d_buf and data->d_size fields (and set ELF_F_DIRTY before calling

Do note that if you do use ELF_F_LAYOUT you are responsible for
updating the sh_size yourself. And possibly move any section data after
it by updating the next sections sh_offset. Also if any of the data
moved is referenced through the program headers you'll need to update
those too.

Note that there are helpers for dealing with the dynamic section
entries gelf_getdyn and gelf_update_dyn.

But updating something like the dynamic section is really tricky if you
want to change the size. It is an allocated section and if there is a
program header there is a corresponding PT_DYNAMIC segment that has to
be kept in sync. Normally allocated (SHF_ALLOC) sections are packed
together when the file has a program header. So moving them around is a
little tricky (you might have to move them all and/or update the
segment headers too). Also there might be other entries, like the
_DYNAMIC symbol, pointing at them.

libelf doesn't provide any help here, sorry. It only keeps track of the
section headers, but is unaware how to update the program headers (or
how the section data offset/sizes corresponds to the segments).



More information about the Elfutils-devel mailing list