view chrpath.c @ 1:bbbfb3f97919

implement 32 and 64 bit support
author Peter Meerwald <p.meerwald@bct-electronic.com>
date Fri, 20 Jul 2012 11:26:24 +0200
parents b8f7423e385c
children
line wrap: on
line source

/*
<URL:http://gcc.gnu.org/ml/gcc/1999-04n/msg01105.html>

Re: changing embedded RPATH in existing executables.

  To: geoffk@ozemail.com.au
  Subject: Re: changing embedded RPATH in existing executables.
  From: <peeter_joot@VNET.IBM.COM> (peeter joot)
  Date: Fri, 30 Apr 1999 16:14:44 -0400 (EDT)
  Cc: peeterj@ca.ibm.com, egcs@cygnus.com, libc-hacker@cygnus.com, linux-gcc@vger.rutgers.edu
  Reply-To: <peeter_joot@VNET.IBM.COM>

> _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
--
*/

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <elf.h>
#if defined(HAVE_LINK_H)
#  include <link.h>
#endif /* HAVE_LINK_H */
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include "protos.h"

/**
 * Reads an ELF file, and reads or alters the RPATH setting.
 *
 * TODO:
 *  modify to add RPATH setting if none exists.
 */


static int
chrpath32(int fd, const char *filename, const char *newpath, Elf32_Ehdr *ehdr, int convert) {
  Elf32_Phdr phdr;
  Elf32_Shdr shdr;
  Elf32_Dyn *dyns;
  int i;
  int rpathoff;
  char * strtab;
  char * rpath;
  unsigned int rpathlen;
  int rpath_dyns_index;

   if (0 != elf32_find_dynamic_section(fd, ehdr, &phdr))
   {
     perror("found no dynamic section");
     return 1;
   }

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

  rpathoff = -1;
  for ( rpath_dyns_index = 0; dyns[rpath_dyns_index].d_tag != DT_NULL;
        ++rpath_dyns_index )
    {
      if ( elf_dynpath_tag(dyns[rpath_dyns_index].d_tag) )
      {
         rpathoff = dyns[rpath_dyns_index].d_un.d_ptr;
         break;
      }
    }
  if (rpathoff == -1)
    {
      printf("%s: no rpath or runpath tag found.\n", filename);
      free(dyns);
      return 2;
    }

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

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

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

  if ((int)shdr.sh_size < rpathoff)
  {
    fprintf(stderr, "%s string offset not contained in string table",
            elf_tagname(dyns[rpath_dyns_index].d_tag));
    free(strtab);
    free(dyns);
    return 5;
  }
  rpath = strtab+rpathoff;

#if defined(DT_RUNPATH)
  if (convert && dyns[rpath_dyns_index].d_tag == DT_RPATH)
  {
    dyns[rpath_dyns_index].d_tag = DT_RUNPATH;
    if (lseek(fd, phdr.p_offset, SEEK_SET) == -1
        || write(fd, dyns, phdr.p_filesz) != (int)phdr.p_filesz)
    {
      perror ("converting RPATH to RUNPATH");
      return 1;
    }
    printf("%s: RPATH converted to RUNPATH\n", filename);
  }
#endif /* DT_RUNPATH */

  printf("%s: %s=%s\n", filename, elf_tagname(dyns[rpath_dyns_index].d_tag),
         rpath);

  if (NULL == newpath)
  {
    free(dyns);
    free(strtab);
    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 < (int)shdr.sh_size
                                   && strtab[i] == '\0') ; i++ )
    ;
  i--;

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

  if (strlen(newpath) > rpathlen)
  {
    fprintf(stderr, "new rpath '%s' too large; maximum length %i\n",
            newpath, rpathlen);
    free(dyns);
    free(strtab);
    return 7;
  }

  memset(rpath, 0, rpathlen);
  strcpy(rpath, newpath);

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

  elf_close(fd);

  free(dyns);
  dyns = NULL;

  free(strtab);

  return 0;
}

static int 
chrpath64(int fd, const char *filename, const char *newpath, Elf64_Ehdr *ehdr, int convert) {
  Elf64_Phdr phdr;
  Elf64_Shdr shdr;
  Elf64_Dyn *dyns;
  int i;
  int rpathoff;
  char * strtab;
  char * rpath;
  unsigned int rpathlen;
  int rpath_dyns_index;

   if (0 != elf64_find_dynamic_section(fd, ehdr, &phdr))
   {
     perror("found no dynamic section");
     return 1;
   }

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

  rpathoff = -1;
  for ( rpath_dyns_index = 0; dyns[rpath_dyns_index].d_tag != DT_NULL;
        ++rpath_dyns_index )
    {
      if ( elf_dynpath_tag(dyns[rpath_dyns_index].d_tag) )
      {
         rpathoff = dyns[rpath_dyns_index].d_un.d_ptr;
         break;
      }
    }
  if (rpathoff == -1)
    {
      printf("%s: no rpath or runpath tag found.\n", filename);
      free(dyns);
      return 2;
    }

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

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

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

  if ((int)shdr.sh_size < rpathoff)
  {
    fprintf(stderr, "%s string offset not contained in string table",
            elf_tagname(dyns[rpath_dyns_index].d_tag));
    free(strtab);
    free(dyns);
    return 5;
  }
  rpath = strtab+rpathoff;

#if defined(DT_RUNPATH)
  if (convert && dyns[rpath_dyns_index].d_tag == DT_RPATH)
  {
    dyns[rpath_dyns_index].d_tag = DT_RUNPATH;
    if (lseek(fd, phdr.p_offset, SEEK_SET) == -1
        || write(fd, dyns, phdr.p_filesz) != (int)phdr.p_filesz)
    {
      perror ("converting RPATH to RUNPATH");
      return 1;
    }
    printf("%s: RPATH converted to RUNPATH\n", filename);
  }
#endif /* DT_RUNPATH */

  printf("%s: %s=%s\n", filename, elf_tagname(dyns[rpath_dyns_index].d_tag),
         rpath);

  if (NULL == newpath)
  {
    free(dyns);
    free(strtab);
    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 < (int)shdr.sh_size
                                   && strtab[i] == '\0') ; i++ )
    ;
  i--;

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

  if (strlen(newpath) > rpathlen)
  {
    fprintf(stderr, "new rpath '%s' too large; maximum length %i\n",
            newpath, rpathlen);
    free(dyns);
    free(strtab);
    return 7;
  }

  memset(rpath, 0, rpathlen);
  strcpy(rpath, newpath);

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

  elf_close(fd);

  free(dyns);
  dyns = NULL;

  free(strtab);

  return 0;
}

int
chrpath(const char *filename, const char *newpath, int convert)
{
  int fd;
  Elf32_Ehdr ehdr32;
  Elf64_Ehdr ehdr64;
  int oflags;

  if (NULL == newpath && 0 == convert)
     oflags = O_RDONLY;
  else
     oflags = O_RDWR;

  fd = elf32_open(filename, oflags, &ehdr32);
  if (fd >= 0)
     return chrpath32(fd, filename, newpath, &ehdr32, convert);

  fd = elf64_open(filename, oflags, &ehdr64);
  if (fd >= 0)
     return chrpath64(fd, filename, newpath, &ehdr64, convert);

  perror ("elf_open");
  return 1;
}

Repositories maintained by Peter Meerwald, pmeerw@pmeerw.net.