Bump to chrpath 0.16
[platform/upstream/chrpath.git] / chrpath.c
1 /*
2 <URL:http://gcc.gnu.org/ml/gcc/1999-04n/msg01105.html>
3
4 Re: changing embedded RPATH in existing executables.
5
6   To: geoffk@ozemail.com.au
7   Subject: Re: changing embedded RPATH in existing executables.
8   From: <peeter_joot@VNET.IBM.COM> (peeter joot)
9   Date: Fri, 30 Apr 1999 16:14:44 -0400 (EDT)
10   Cc: peeterj@ca.ibm.com, egcs@cygnus.com, libc-hacker@cygnus.com, linux-gcc@vger.rutgers.edu
11   Reply-To: <peeter_joot@VNET.IBM.COM>
12
13 > _Changing_ is a little tricky, but the attached program strips rpaths
14 > from executables (I find it essential for debugging the binutils).
15 > It's endian-dependent, if you want this for x86 you can just change
16 > the occurrences of 'MSB' to 'LSB' and compile (I should really fix
17 > that).
18
19 Hi Geoff,
20
21 With your program as a guide (and some peeks into libbfd, elf.h, a bit
22 of the glibc dynamic loader code, objdump, and a hex-editor) I was able to
23 figure out enough to find and change the rpath string.  That was fun!
24
25 This program assumes (unlike your original program) that there is only
26 one DT_RPATH tag in the dynamic section as even with multiple '-Wl,-rpath,'
27 commands in the link this seems to occur (they all get concatonated into
28 a : separated path).
29
30 Thanks for your help.  If you want to use this on non-x86 you have to change
31 the occurances of LSB back to MSB:)
32
33 Peeter
34 --
35 */
36
37 #ifdef HAVE_CONFIG_H
38 #  include "config.h"
39 #endif
40
41 #include <stdio.h>
42 #include <unistd.h>
43 #include <fcntl.h>
44 #include <elf.h>
45 #if defined(HAVE_LINK_H)
46 #  include <link.h>
47 #endif /* HAVE_LINK_H */
48 #include <stdlib.h>
49 #include <string.h>
50 #include <sys/stat.h>
51 #include "protos.h"
52
53 /**
54  * Reads an ELF file, and reads or alters the RPATH setting.
55  *
56  * TODO:
57  *  modify to add RPATH setting if none exists.
58  */
59
60
61 int
62 chrpath(const char *filename, const char *newpath, int convert)
63 {
64   int fd;
65   Elf_Ehdr ehdr;
66   int i;
67   Elf_Phdr phdr;
68   Elf_Shdr shdr;
69   void *dyns;
70   int rpathoff;
71   char * strtab;
72   char * rpath;
73   unsigned int rpathlen;
74   int oflags;
75   int rpath_dyns_index;
76
77   if (NULL == newpath && 0 == convert)
78      oflags = O_RDONLY;
79   else
80      oflags = O_RDWR;
81
82   fd = elf_open(filename, oflags, &ehdr);
83   if (fd == -1)
84   {
85     perror ("elf_open");
86     return 1;
87   }
88
89    if (0 != elf_find_dynamic_section(fd, &ehdr, &phdr))
90    {
91      perror("found no dynamic section");
92      elf_close(fd);
93      return 1;
94    }
95
96   dyns = malloc(PHDR(p_filesz));
97   if (dyns == NULL)
98     {
99       perror ("allocating memory for dynamic section");
100       elf_close(fd);
101       return 1;
102     }
103   memset(dyns, 0, PHDR(p_filesz));
104   if (lseek(fd, PHDR(p_offset), SEEK_SET) == -1
105       || read(fd, dyns, PHDR(p_filesz)) != (ssize_t)PHDR(p_filesz))
106     {
107       perror ("reading dynamic section");
108       free(dyns);
109       elf_close(fd);
110       return 1;
111     }
112
113   rpathoff = -1;
114   for ( rpath_dyns_index = 0; DYNSS(rpath_dyns_index, d_tag) != DT_NULL;
115         ++rpath_dyns_index )
116     {
117       if ( elf_dynpath_tag(DYNSS(rpath_dyns_index, d_tag)) )
118       {
119          rpathoff = DYNSU(rpath_dyns_index, d_un.d_ptr);
120          break;
121       }
122     }
123   if (rpathoff == -1)
124     {
125       printf("%s: no rpath or runpath tag found.\n", filename);
126       free(dyns);
127       elf_close(fd);
128       return 2;
129     }
130
131   if (lseek(fd, EHDRWU(e_shoff), SEEK_SET) == -1)
132   {
133     perror ("positioning for sections");
134     free(dyns);
135     elf_close(fd);
136     return 1;
137   }
138
139   for (i = 0; i < EHDRHU(e_shnum); i++)
140   {
141     const size_t sz_shdr = is_e32() ? sizeof(Elf32_Shdr) : sizeof(Elf64_Shdr);
142     if (read(fd, &shdr, sz_shdr) != (ssize_t)sz_shdr)
143     {
144       perror ("reading section header");
145       free(dyns);
146       elf_close(fd);
147       return 1;
148     }
149     if (SHDR_W(sh_type) == SHT_STRTAB)
150       break;
151   }
152   if (i == EHDRHU(e_shnum))
153     {
154       fprintf (stderr, "No string table found.\n");
155       free(dyns);
156       elf_close(fd);
157       return 2;
158     }
159   /* +1 for forced trailing null */
160   strtab = (char *)calloc(1, SHDR_O(sh_size)+1);
161   if (strtab == NULL)
162     {
163       perror ("allocating memory for string table");
164       free(dyns);
165       elf_close(fd);
166       return 1;
167     }
168
169   if (lseek(fd, SHDR_O(sh_offset), SEEK_SET) == -1)
170   {
171     perror ("positioning for string table");
172     free(strtab);
173     free(dyns);
174     elf_close(fd);
175     return 1;
176   }
177   if (read(fd, strtab, SHDR_O(sh_size)) != (ssize_t)SHDR_O(sh_size))
178   {
179     perror ("reading string table");
180     free(strtab);
181     free(dyns);
182     elf_close(fd);
183     return 1;
184   }
185   strtab[SHDR_O(sh_size)] = 0; /* make sure printed string is null terminated */
186
187   if ((int)SHDR_O(sh_size) < rpathoff)
188   {
189     fprintf(stderr, "%s string offset not contained in string table",
190             elf_tagname(DYNSS(rpath_dyns_index, d_tag)));
191     free(strtab);
192     free(dyns);
193     elf_close(fd);
194     return 5;
195   }
196   rpath = strtab+rpathoff;
197
198 #if defined(DT_RUNPATH)
199   if (convert && DYNSS(rpath_dyns_index, d_tag) == DT_RPATH)
200   {
201     if (is_e32())
202       ((Elf32_Dyn *)dyns)[rpath_dyns_index].d_tag = swap_bytes() ?
203         bswap_32(DT_RUNPATH) : DT_RUNPATH;
204     else
205       ((Elf64_Dyn *)dyns)[rpath_dyns_index].d_tag = swap_bytes() ?
206         bswap_64(DT_RUNPATH) : DT_RUNPATH;
207     if (lseek(fd, PHDR(p_offset), SEEK_SET) == -1
208         || write(fd, dyns, PHDR(p_filesz)) != (int)PHDR(p_filesz))
209     {
210       perror ("converting RPATH to RUNPATH");
211       free(strtab);
212       free(dyns);
213       elf_close(fd);
214       return 1;
215     }
216     printf("%s: RPATH converted to RUNPATH\n", filename);
217   }
218 #endif /* DT_RUNPATH */
219
220   printf("%s: %s=%s\n", filename, elf_tagname(DYNSS(rpath_dyns_index, d_tag)),
221          rpath);
222
223   if (NULL == newpath)
224   {
225     free(dyns);
226     free(strtab);
227     elf_close(fd);
228     return 0;
229   }
230
231   rpathlen = strlen(rpath);
232
233   /*
234    * Calculate the maximum rpath length (will be equal to rpathlen unless
235    * we have previously truncated it).
236    */
237   for ( i = rpathoff + rpathlen ; (i < (int)SHDR_O(sh_size)
238                                    && strtab[i] == '\0') ; i++ )
239     ;
240   i--;
241
242   if (i > (int)(rpathoff + rpathlen))
243      rpathlen = i - rpathoff;
244
245   if (strlen(newpath) > rpathlen)
246   {
247     fprintf(stderr, "new rpath '%s' too large; maximum length %i\n",
248             newpath, rpathlen);
249     free(dyns);
250     free(strtab);
251     elf_close(fd);
252     return 7;
253   }
254
255   memset(rpath, 0, rpathlen);
256   strcpy(rpath, newpath);
257
258   if (lseek(fd, SHDR_O(sh_offset)+rpathoff, SEEK_SET) == -1)
259   {
260     perror ("positioning for RPATH");
261     free(dyns);
262     free(strtab);
263     elf_close(fd);
264     return 1;
265   }
266   if (write(fd, rpath, rpathlen) != (int)rpathlen)
267   {
268     perror ("writing RPATH");
269     free(dyns);
270     free(strtab);
271     elf_close(fd);
272     return 1;
273   }
274   printf("%s: new %s: %s\n", filename,
275          elf_tagname(DYNSS(rpath_dyns_index, d_tag)), rpath);
276
277   elf_close(fd);
278
279   free(dyns);
280   dyns = NULL;
281
282   free(strtab);
283
284   return 0;
285 }