This is the mail archive of the libc-hacker@cygnus.com mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: changing embedded RPATH in existing executables.


> _Changing_ is a little tricky, but the attached program strips rpaths
> from executables (I find it essential for debugging the binutils).
> It's endian-dependent, if you want this for x86 you can just change
> the occurrences of 'MSB' to 'LSB' and compile (I should really fix
> that).

Hi Geoff,

With your program as a guide (and some peeks into libbfd, elf.h, a bit
of the glibc dynamic loader code, objdump, and a hex-editor) I was able to
figure out enough to find and change the rpath string.  That was fun!

This program assumes (unlike your original program) that there is only
one DT_RPATH tag in the dynamic section as even with multiple '-Wl,-rpath,'
commands in the link this seems to occur (they all get concatonated into
a : separated path).

Thanks for your help.  If you want to use this on non-x86 you have to change
the occurances of LSB back to MSB:)

Peeter
--
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <elf.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>

/* Reads an ELF file, and reads or alters the RPATH setting. */

int
main(int argc, char **argv)
{
  int fd;
  Elf32_Ehdr ehdr;
  int i;
  Elf32_Phdr phdr;
  Elf32_Shdr shdr;
  Elf32_Dyn *dyns;
  int rpathoff;
  char * strtab;
  char * rpath;
  int rpathlen;
  int oflags;

  if (argc != 2 && argc != 3)
  {
    printf ("Usage: %s objectfile [newrpath]\n", argv[0]);
    return 1;
  }
  if (argc == 2)
     oflags = O_RDONLY;
  else
     oflags = O_RDWR;

  fd = open(argv[1], oflags);
  if (fd == -1)
  {
    perror ("open");
    return 1;
  }

  if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr))
  {
    perror ("reading header");
    return 1;
  }

  if (*(unsigned *)ehdr.e_ident != *(const unsigned *)ELFMAG ||
      ehdr.e_ident[EI_CLASS] != ELFCLASS32 ||
      ehdr.e_ident[EI_DATA] != ELFDATA2LSB ||
      ehdr.e_ident[EI_VERSION] != EV_CURRENT)
  {
    fprintf(stderr, "`%s' probably isn't a 32-bit LSB-first ELF file.\n",
            argv[1]);
    return 1;
  }

  if (ehdr.e_phentsize != sizeof(Elf32_Phdr))
  {
    fprintf(stderr, "section size was read as %d, not %d!\n",
            ehdr.e_phentsize, sizeof(Elf32_Phdr));
    return 1;
  }

  if (lseek(fd, ehdr.e_phoff, SEEK_SET) == -1)
  {
    perror ("positioning for sections");
    return 1;
  }

  for (i = 0; i < ehdr.e_phnum; i++)
  {
    if (read(fd, &phdr, sizeof(phdr)) != sizeof(phdr))
    {
      perror ("reading section header");
      return 1;
    }
    if (phdr.p_type == PT_DYNAMIC)
      break;
  }
  if (i == ehdr.e_phnum)
    {
      fprintf (stderr, "No dynamic section found.\n");
      return 2;
    }

  dyns = malloc(phdr.p_memsz);
  if (dyns == NULL)
    {
      perror ("allocating memory for dynamic section");
      return 1;
    }
  memset(dyns, 0, phdr.p_memsz);
  if (lseek(fd, phdr.p_offset, SEEK_SET) == -1
      || read(fd, dyns, phdr.p_filesz) != phdr.p_filesz)
    {
      perror ("reading dynamic section");
      return 1;
    }

  rpathoff = -1;
  for (i = 0; dyns[i].d_tag != DT_NULL; i++)
    {
      if (dyns[i].d_tag == DT_RPATH)
      {
         rpathoff = dyns[i].d_un.d_ptr;
         break;
      }
    }
  if (rpathoff == -1)
    {
      fprintf (stderr, "No rpath tag found.\n");
      return 2;
    }

  if (lseek(fd, ehdr.e_shoff, SEEK_SET) == -1)
  {
    perror ("positioning for sections");
    return 1;
  }

  for (i = 0; i < ehdr.e_shnum; i++)
  {
    if (read(fd, &shdr, sizeof(shdr)) != sizeof(shdr))
    {
      perror ("reading section header");
      return 1;
    }
    if (shdr.sh_type == SHT_STRTAB)
      break;
  }
  if (i == ehdr.e_shnum)
    {
      fprintf (stderr, "No string table found.\n");
      return 2;
    }
  strtab = (char *)malloc(shdr.sh_size);
  if (strtab == NULL)
    {
      perror ("allocating memory for string table");
      return 1;
    }
  memset(strtab, 0, shdr.sh_size);

  if (lseek(fd, shdr.sh_offset, SEEK_SET) == -1)
  {
    perror ("positioning for string table");
    return 1;
  }
  if (read(fd, strtab, shdr.sh_size) != shdr.sh_size)
  {
    perror ("reading string table");
    return 1;
  }

  if (shdr.sh_size < rpathoff)
  {
     fprintf(stderr, "RPATH string offset not contained in string table");
     return 5;
  }
  rpath = strtab+rpathoff;
  printf("%s:existing RPATH: %s\n", argv[1], rpath);

  if (argc == 2)
     return 0;

  rpathlen = strlen(rpath);

  /*
   * Calculate the maximum rpath length (will be equal to rpathlen unless
   * we have previously truncated it).
   */
  for ( i = rpathoff + rpathlen ; i < shdr.sh_size && strtab[i] == '\0' ; i++ );
  i--;

  if (i> rpathoff + rpathlen)
     rpathlen = i - rpathoff;

  if (strlen(argv[2]) > rpathlen)
  {
     fprintf(stderr, "new rpath '%s' too large; maximum length %i\n",
             argv[2], rpathlen);
     return 7;
  }

  memset(rpath, 0, rpathlen);
  strcpy(rpath, argv[2]);

  if (lseek(fd, shdr.sh_offset+rpathoff, SEEK_SET) == -1)
  {
    perror ("positioning for RPATH");
    return 1;
  }
  if (write(fd, rpath, rpathlen) != rpathlen)
  {
    perror ("writing RPATH");
    return 1;
  }
  printf("%s:new RPATH: %s\n", argv[1], rpath);

  return 0;
}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]