Hello everyone,

please forgive my rather noob-ish question but I'm very confused on how libelf 
works. 

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);

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.

In addition, when I'm running `diff <(readelf -a orig_bin) <(readelf -a 
new_bin)` I see many differences. e.g  I see the following:

```
<       [Requesting program interpreter: ]
---
>       [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
```

Or with `readelf -l`:

```
...
  INTERP         0x0000000000005310 0x0000000000005310 0x0000000000005310
                 0x000000000000001c 0x000000000000001c  R      0x1
readelf: Error: Unable to read program interpreter name
      [Requesting program interpreter: ]
...
```

I using the latest libelf version 180 (manually compiled and installed, the 
same behavior exists in the default Ubuntu libelf) and running on Ubuntu 
18.04.4 LTS.

2) In any case, as I said in the beginning, I want to add an extra entry to 
`.dynamic` and ideally an extra string to `.dynstr`.

So I tried the following:

2a) As a simple prototype I tried to update and existing GElf_Dyn entry. It 
didn't work, the elf file is again corrupted.

```C
...
if (shdr.sh_type == SHT_DYNAMIC) {
  ...

  GElf_Dyn dyn;
  assert(gelf_getdyn (data, 0, &dyn) == &dyn);

  dyn.d_un.d_val = 1; // This is a valid index

  gelf_update_dyn(data, 0, &dyn);
}
```

When I run `readelf -l` I see:
```
...
  DYNAMIC        0x000000000001fa38 0x000000000021fa38 0x000000000021fa38
                 0x0000000000000200 0x0000000000000200  RW     0x8
readelf: Warning: the .dynamic section is not contained within the dynamic 
segment
...
```

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;

}
```

I'm completely lost as I'm not sure if issue (1) blocks everything, while (2) 
is ok. Could someone point me to the right direction please?

Kindly,
Anastasios

Reply via email to