1 /***************************************************************************
2 * *
3 * This is a pre-configured single file version of chrpath for linux. *
4 * It is used in the binary installer for OpenSCAD on Linux. *
5 * *
6 * This file has been created by running *
7 * cat *.h *.c | sed 's, *# *include *",//&,' > ../chrpath_linux.c *
8 * in the configured chrpath-0.13 source directory (as found in the debian *
9 * package repository as the original download site seams to be down). *
10 * *
11 * chrpath is licensed under the terms of GPLv2: *
12 * *
13 * This program is free software; you can redistribute it and/or modify *
14 * it under the terms of the GNU General Public License as published by *
15 * the Free Software Foundation; either version 2 of the License, or *
16 * (at your option) any later version. *
17 * *
18 * This program is distributed in the hope that it will be useful, *
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
21 * GNU General Public License for more details. *
22 * *
23 * The Authors are: *
24 * *
25 * Geoffrey Keating <geoffk@ozemail.com.au> *
26 * Implemented first version of chrpath.c based on first version of *
27 * killrpath. *
28 * Peeter Joot <peeterj@ca.ibm.com> *
29 * Implemented first version of killrpath.c. *
30 * Petter Reinholdtsen <pere@hungry.com> *
31 * Collected both implementations and made userfriendly wrapper. *
32 * *
33 * With patches from: *
34 * *
35 * Darren Salt <linux@youmustbejoking.demon.co.uk> *
36 * David Hull <hull@paracel.com> *
37 * Bradford W. Johnson <bradford@math.umn.edu> *
38 * Thomas Anders <anders@hmi.de> *
39 * Tollef Fog Heen <tollef@add.no> *
40 * *
41 ***************************************************************************/
42
43
44 /* config.h. Generated by configure. */
45 /* config.h.in. Generated from configure.ac by autoheader. */
46
47 /* Define to 1 if you have the <elf.h> header file. */
48 #define HAVE_ELF_H 1
49
50 /* Define to 1 if you have the <fcntl.h> header file. */
51 #define HAVE_FCNTL_H 1
52
53 /* Define to 1 if you have the <getopt.h> header file. */
54 #define HAVE_GETOPT_H 1
55
56 /* Define to 1 if you have the `getopt_long' function. */
57 #define HAVE_GETOPT_LONG 1
58
59 /* Define to 1 if you have the <inttypes.h> header file. */
60 #define HAVE_INTTYPES_H 1
61
62 /* Define to 1 if you have the <link.h> header file. */
63 #define HAVE_LINK_H 1
64
65 /* Define to 1 if you have the <memory.h> header file. */
66 #define HAVE_MEMORY_H 1
67
68 /* Define to 1 if you have the <stdint.h> header file. */
69 #define HAVE_STDINT_H 1
70
71 /* Define to 1 if you have the <stdlib.h> header file. */
72 #define HAVE_STDLIB_H 1
73
74 /* Define to 1 if you have the <strings.h> header file. */
75 #define HAVE_STRINGS_H 1
76
77 /* Define to 1 if you have the <string.h> header file. */
78 #define HAVE_STRING_H 1
79
80 /* Define to 1 if you have the <sys/link.h> header file. */
81 /* #undef HAVE_SYS_LINK_H */
82
83 /* Define to 1 if you have the <sys/stat.h> header file. */
84 #define HAVE_SYS_STAT_H 1
85
86 /* Define to 1 if you have the <sys/types.h> header file. */
87 #define HAVE_SYS_TYPES_H 1
88
89 /* Define to 1 if you have the <unistd.h> header file. */
90 #define HAVE_UNISTD_H 1
91
92 /* Name of package */
93 #define PACKAGE "chrpath"
94
95 /* Define to the address where bug reports for this package should be sent. */
96 #define PACKAGE_BUGREPORT "pere@hungry.com"
97
98 /* Define to the full name of this package. */
99 #define PACKAGE_NAME ""chrpath""
100
101 /* Define to the full name and version of this package. */
102 #define PACKAGE_STRING ""chrpath" "0.13""
103
104 /* Define to the one symbol short name of this package. */
105 #define PACKAGE_TARNAME "-chrpath-"
106
107 /* Define to the version of this package. */
108 #define PACKAGE_VERSION ""0.13""
109
110 /* Define to 1 if you have the ANSI C header files. */
111 #define STDC_HEADERS 1
112
113 /* Version number of package */
114 #define VERSION "0.13"
115
116 /* Define to 1 if your processor stores words with the most significant byte
117 first (like Motorola and SPARC, unlike Intel and VAX). */
118 /* #undef WORDS_BIGENDIAN */
119
120 /* Define to empty if `const' does not conform to ANSI C. */
121 /* #undef const */
122 #ifndef PROTOS_H
123 #define PROTOS_H
124
125 #include <elf.h>
126 //#include "config.h"
127
128 #ifdef WORDS_BIGENDIAN
129 #define ELFDATA2 ELFDATA2MSB
130 #else
131 #define ELFDATA2 ELFDATA2LSB
132 #endif
133 #if SIZEOF_VOID_P == 8
134 #define Elf_Ehdr Elf64_Ehdr
135 #define ELFCLASS ELFCLASS64
136 #define Elf_Phdr Elf64_Phdr
137 #define Elf_Shdr Elf64_Shdr
138 #define Elf_Dyn Elf64_Dyn
139 #elif SIZEOF_VOID_P == 4
140 #define Elf_Ehdr Elf32_Ehdr
141 #define ELFCLASS ELFCLASS32
142 #define Elf_Phdr Elf32_Phdr
143 #define Elf_Shdr Elf32_Shdr
144 #define Elf_Dyn Elf32_Dyn
145 #else
146 #error "Unknown word size (SIZEOF_VOID_P)!"
147 #endif
148
149 int killrpath(const char *filename);
150 int chrpath(const char *filename, const char *newpath, int convert);
151
152 int elf_open(const char *filename, int flags, Elf_Ehdr *ehdr);
153 void elf_close(int fd);
154 int elf_find_dynamic_section(int fd, Elf_Ehdr *ehdr, Elf_Phdr *phdr);
155 const char *elf_tagname(int tag);
156 int elf_dynpath_tag(int tag);
157
158 #endif /* PROTOS_H */
159 /*
160 <URL:http://gcc.gnu.org/ml/gcc/1999-04n/msg01105.html>
161
162 Re: changing embedded RPATH in existing executables.
163
164 To: geoffk@ozemail.com.au
165 Subject: Re: changing embedded RPATH in existing executables.
166 From: <peeter_joot@VNET.IBM.COM> (peeter joot)
167 Date: Fri, 30 Apr 1999 16:14:44 -0400 (EDT)
168 Cc: peeterj@ca.ibm.com, egcs@cygnus.com, libc-hacker@cygnus.com, linux-gcc@vger.rutgers.edu
169 Reply-To: <peeter_joot@VNET.IBM.COM>
170
171 > _Changing_ is a little tricky, but the attached program strips rpaths
172 > from executables (I find it essential for debugging the binutils).
173 > It's endian-dependent, if you want this for x86 you can just change
174 > the occurrences of 'MSB' to 'LSB' and compile (I should really fix
175 > that).
176
177 Hi Geoff,
178
179 With your program as a guide (and some peeks into libbfd, elf.h, a bit
180 of the glibc dynamic loader code, objdump, and a hex-editor) I was able to
181 figure out enough to find and change the rpath string. That was fun!
182
183 This program assumes (unlike your original program) that there is only
184 one DT_RPATH tag in the dynamic section as even with multiple '-Wl,-rpath,'
185 commands in the link this seems to occur (they all get concatenated into
186 a : separated path).
187
188 Thanks for your help. If you want to use this on non-x86 you have to change
189 the occurrences of LSB back to MSB:)
190
191 Peeter
192 --
193 */
194
195 #ifdef HAVE_CONFIG_H
196 //# include "config.h"
197 #endif
198
199 #include <stdio.h>
200 #include <unistd.h>
201 #include <fcntl.h>
202 #include <elf.h>
203 #if defined(HAVE_LINK_H)
204 # include <link.h>
205 #endif /* HAVE_LINK_H */
206 #include <stdlib.h>
207 #include <string.h>
208 #include <sys/stat.h>
209 //#include "protos.h"
210
211 /**
212 * Reads an ELF file, and reads or alters the RPATH setting.
213 *
214 * TODO:
215 * modify to add RPATH setting if none exists.
216 */
217
218
219 int
chrpath(const char * filename,const char * newpath,int convert)220 chrpath(const char *filename, const char *newpath, int convert)
221 {
222 int fd;
223 Elf_Ehdr ehdr;
224 int i;
225 Elf_Phdr phdr;
226 Elf_Shdr shdr;
227 Elf_Dyn *dyns;
228 int rpathoff;
229 char * strtab;
230 char * rpath;
231 unsigned int rpathlen;
232 int oflags;
233 int rpath_dyns_index;
234
235 if (NULL == newpath && 0 == convert)
236 oflags = O_RDONLY;
237 else
238 oflags = O_RDWR;
239
240 fd = elf_open(filename, oflags, &ehdr);
241 if (fd == -1)
242 {
243 perror ("elf_open");
244 return 1;
245 }
246
247 if (0 != elf_find_dynamic_section(fd, &ehdr, &phdr))
248 {
249 perror("found no dynamic section");
250 return 1;
251 }
252
253 dyns = malloc(phdr.p_filesz);
254 if (dyns == NULL)
255 {
256 perror ("allocating memory for dynamic section");
257 return 1;
258 }
259 memset(dyns, 0, phdr.p_filesz);
260 if (lseek(fd, phdr.p_offset, SEEK_SET) == -1
261 || read(fd, dyns, phdr.p_filesz) != (int)phdr.p_filesz)
262 {
263 perror ("reading dynamic section");
264 free(dyns);
265 return 1;
266 }
267
268 rpathoff = -1;
269 for ( rpath_dyns_index = 0; dyns[rpath_dyns_index].d_tag != DT_NULL;
270 ++rpath_dyns_index )
271 {
272 if ( elf_dynpath_tag(dyns[rpath_dyns_index].d_tag) )
273 {
274 rpathoff = dyns[rpath_dyns_index].d_un.d_ptr;
275 break;
276 }
277 }
278 if (rpathoff == -1)
279 {
280 printf("%s: no rpath or runpath tag found.\n", filename);
281 free(dyns);
282 return 2;
283 }
284
285 if (lseek(fd, ehdr.e_shoff, SEEK_SET) == -1)
286 {
287 perror ("positioning for sections");
288 free(dyns);
289 return 1;
290 }
291
292 for (i = 0; i < ehdr.e_shnum; ++i)
293 {
294 if (read(fd, &shdr, sizeof(shdr)) != sizeof(shdr))
295 {
296 perror ("reading section header");
297 free(dyns);
298 return 1;
299 }
300 if (shdr.sh_type == SHT_STRTAB)
301 break;
302 }
303 if (i == ehdr.e_shnum)
304 {
305 fprintf (stderr, "No string table found.\n");
306 free(dyns);
307 return 2;
308 }
309 strtab = (char *)malloc(shdr.sh_size);
310 if (strtab == NULL)
311 {
312 perror ("allocating memory for string table");
313 free(dyns);
314 return 1;
315 }
316 memset(strtab, 0, shdr.sh_size);
317
318 if (lseek(fd, shdr.sh_offset, SEEK_SET) == -1)
319 {
320 perror ("positioning for string table");
321 free(strtab);
322 free(dyns);
323 return 1;
324 }
325 if (read(fd, strtab, shdr.sh_size) != (int)shdr.sh_size)
326 {
327 perror ("reading string table");
328 free(strtab);
329 free(dyns);
330 return 1;
331 }
332
333 if ((int)shdr.sh_size < rpathoff)
334 {
335 fprintf(stderr, "%s string offset not contained in string table",
336 elf_tagname(dyns[rpath_dyns_index].d_tag));
337 free(strtab);
338 free(dyns);
339 return 5;
340 }
341 rpath = strtab+rpathoff;
342
343 #if defined(DT_RUNPATH)
344 if (convert && dyns[rpath_dyns_index].d_tag == DT_RPATH)
345 {
346 dyns[rpath_dyns_index].d_tag = DT_RUNPATH;
347 if (lseek(fd, phdr.p_offset, SEEK_SET) == -1
348 || write(fd, dyns, phdr.p_filesz) != (int)phdr.p_filesz)
349 {
350 perror ("converting RPATH to RUNPATH");
351 return 1;
352 }
353 printf("%s: RPATH converted to RUNPATH\n", filename);
354 }
355 #endif /* DT_RUNPATH */
356
357 printf("%s: %s=%s\n", filename, elf_tagname(dyns[rpath_dyns_index].d_tag),
358 rpath);
359
360 if (NULL == newpath)
361 {
362 free(dyns);
363 free(strtab);
364 return 0;
365 }
366
367 rpathlen = strlen(rpath);
368
369 /*
370 * Calculate the maximum rpath length (will be equal to rpathlen unless
371 * we have previously truncated it).
372 */
373 for ( i = rpathoff + rpathlen ; (i < (int)shdr.sh_size
374 && strtab[i] == '\0') ; i++ )
375 ;
376 i--;
377
378 if (i > (int)(rpathoff + rpathlen))
379 rpathlen = i - rpathoff;
380
381 if (strlen(newpath) > rpathlen)
382 {
383 fprintf(stderr, "new rpath '%s' too large; maximum length %i\n",
384 newpath, rpathlen);
385 free(dyns);
386 free(strtab);
387 return 7;
388 }
389
390 memset(rpath, 0, rpathlen);
391 strcpy(rpath, newpath);
392
393 if (lseek(fd, shdr.sh_offset+rpathoff, SEEK_SET) == -1)
394 {
395 perror ("positioning for RPATH");
396 free(dyns);
397 free(strtab);
398 return 1;
399 }
400 if (write(fd, rpath, rpathlen) != (int)rpathlen)
401 {
402 perror ("writing RPATH");
403 free(dyns);
404 free(strtab);
405 return 1;
406 }
407 printf("%s: new %s: %s\n", filename,
408 elf_tagname(dyns[rpath_dyns_index].d_tag), rpath);
409
410 elf_close(fd);
411
412 free(dyns);
413 dyns = NULL;
414
415 free(strtab);
416
417 return 0;
418 }
419
420 #ifdef HAVE_CONFIG_H
421 //# include "config.h"
422 #endif
423
424 #include <elf.h>
425 #if defined(HAVE_SYS_LINK_H)
426 # include <sys/link.h> /* Find DT_RPATH on Solaris 2.6 */
427 #endif /* HAVE_SYS_LINK_H */
428 #include <stdio.h>
429 #include <string.h>
430 #include <unistd.h>
431 #include <sys/types.h>
432 #include <sys/stat.h>
433 #include <errno.h>
434 #include <fcntl.h>
435 //#include "protos.h"
436
437 int
elf_open(const char * filename,int flags,Elf_Ehdr * ehdr)438 elf_open(const char *filename, int flags, Elf_Ehdr *ehdr)
439 {
440 int fd;
441
442 fd = open(filename, flags);
443 if (fd == -1)
444 {
445 perror ("open");
446 return -1;
447 }
448
449 if (read(fd, ehdr, sizeof(*ehdr)) != sizeof(*ehdr))
450 {
451 perror ("reading header");
452 close(fd);
453 return -1;
454 }
455
456 if (0 != memcmp(ehdr->e_ident, ELFMAG, SELFMAG) ||
457 ehdr->e_ident[EI_CLASS] != ELFCLASS ||
458 ehdr->e_ident[EI_DATA] != ELFDATA2 ||
459 ehdr->e_ident[EI_VERSION] != EV_CURRENT)
460 {
461 fprintf(stderr,
462 #ifdef WORDS_BIGENDIAN
463 "`%s' probably isn't a %d-bit MSB-first ELF file.\n",
464 #else /* not WORD_BIGENDIAN */
465 "`%s' probably isn't a %d-bit LSB-first ELF file.\n",
466 #endif /* not WORD_BIGENDIAN */
467 filename, SIZEOF_VOID_P * 8);
468 close(fd);
469 errno = ENOEXEC; /* Hm, is this the best errno code to use? */
470 return -1;
471 }
472
473 if (ehdr->e_phentsize != sizeof(Elf_Phdr))
474 {
475 fprintf(stderr, "section size was read as %d, not %d!\n",
476 ehdr->e_phentsize, (int)sizeof(Elf_Phdr));
477 close(fd);
478 return -1;
479 }
480 return fd;
481 }
482
483 int
elf_find_dynamic_section(int fd,Elf_Ehdr * ehdr,Elf_Phdr * phdr)484 elf_find_dynamic_section(int fd, Elf_Ehdr *ehdr, Elf_Phdr *phdr)
485 {
486 int i;
487 if (lseek(fd, ehdr->e_phoff, SEEK_SET) == -1)
488 {
489 perror ("positioning for sections");
490 return 1;
491 }
492
493 for (i = 0; i < ehdr->e_phnum; ++i)
494 {
495 if (read(fd, phdr, sizeof(*phdr)) != sizeof(*phdr))
496 {
497 perror ("reading section header");
498 return 1;
499 }
500 if (phdr->p_type == PT_DYNAMIC)
501 break;
502 }
503 if (i == ehdr->e_phnum)
504 {
505 fprintf (stderr, "No dynamic section found.\n");
506 return 2;
507 }
508
509 if (0 == phdr->p_filesz)
510 {
511 fprintf (stderr, "Length of dynamic section is zero.\n");
512 return 3;
513 }
514
515 return 0;
516 }
517
518 void
elf_close(int fd)519 elf_close(int fd)
520 {
521 close(fd);
522 }
523
524 const char *
elf_tagname(int tag)525 elf_tagname(int tag)
526 {
527 switch (tag) {
528 case DT_RPATH:
529 return "RPATH";
530 break;
531 #if defined(DT_RUNPATH)
532 case DT_RUNPATH:
533 return "RUNPATH";
534 break;
535 #endif /* DT_RUNPATH */
536 }
537 return "UNKNOWN";
538 }
539
540 int
elf_dynpath_tag(int tag)541 elf_dynpath_tag(int tag)
542 {
543 return ( tag == DT_RPATH
544 #if defined(DT_RUNPATH)
545 || tag == DT_RUNPATH
546 #endif /* DT_RUNPATH */
547 );
548 }
549 /*
550 Taken from another list:
551
552 _Changing_ is a little tricky, but the attached program strips rpaths
553 from executables (I find it essential for debugging the binutils).
554 It's endian-dependent, if you want this for x86 you can just change
555 the occurrences of 'MSB' to 'LSB' and compile (I should really fix
556 that).
557
558 --
559 Geoffrey Keating <geoffk@ozemail.com.au>
560 */
561
562 #ifdef HAVE_CONFIG_H
563 //# include "config.h"
564 #endif
565
566 #include <stdio.h>
567 #include <unistd.h>
568 #include <fcntl.h>
569 #include <elf.h>
570 #if defined(HAVE_LINK_H)
571 # include <link.h>
572 #endif /* HAVE_LINK_H */
573 #include <stdlib.h>
574 //#include "protos.h"
575 #include <string.h>
576
577 /* Reads an ELF file, nukes all the RPATH entries. */
578
579 int
killrpath(const char * filename)580 killrpath(const char *filename)
581 {
582 int fd;
583 Elf_Ehdr ehdr;
584 int i;
585 Elf_Phdr phdr;
586 Elf_Dyn *dyns;
587 int dynpos;
588
589 fd = elf_open(filename, O_RDWR, &ehdr);
590
591 if (fd == -1)
592 {
593 perror ("elf_open");
594 return 1;
595 }
596
597 if (0 != elf_find_dynamic_section(fd, &ehdr, &phdr))
598 {
599 perror("found no dynamic section");
600 return 1;
601 }
602
603 dyns = malloc(phdr.p_memsz);
604 if (dyns == NULL)
605 {
606 perror ("allocating memory for dynamic section");
607 return 1;
608 }
609 memset(dyns, 0, phdr.p_memsz);
610 if (lseek(fd, phdr.p_offset, SEEK_SET) == -1
611 || read(fd, dyns, phdr.p_filesz) != (int)phdr.p_filesz)
612 {
613 perror ("reading dynamic section");
614 return 1;
615 }
616
617 dynpos = 0;
618 for (i = 0; dyns[i].d_tag != DT_NULL; ++i)
619 {
620 dyns[dynpos] = dyns[i];
621 if ( ! elf_dynpath_tag(dyns[i].d_tag) )
622 dynpos++;
623 }
624 for (; dynpos < i; ++dynpos)
625 dyns[dynpos].d_tag = DT_NULL;
626
627 if (lseek(fd, phdr.p_offset, SEEK_SET) == -1
628 || write(fd, dyns, phdr.p_filesz) != (int)phdr.p_filesz)
629 {
630 perror ("writing dynamic section");
631 return 1;
632 }
633
634 elf_close(fd);
635
636 return 0;
637 }
638 /*
639 * Author: Petter Reinholdtsen <pere@hungry.com>
640 * date: 2001-01-20
641 *
642 * Alter ELF rpath information (insert, modify, remove).
643 *
644 * Based on source from Peeter Joot <peeterj@ca.ibm.com> and Geoffrey
645 * Keating <geoffk@ozemail.com.au>.
646 */
647
648 #ifdef HAVE_CONFIG_H
649 //# include "config.h"
650 #endif
651
652 #include <stdio.h>
653 #include <stdlib.h>
654 #include <unistd.h>
655 #ifdef HAVE_GETOPT_H
656 #include <getopt.h>
657 #endif
658 //#include "protos.h"
659
660 #ifdef HAVE_GETOPT_LONG
661 # define GETOPT_LONG getopt_long
662
663 static struct option long_options[] =
664 {
665 {"convert", 0, 0, 'c'},
666 {"delete", 0, 0, 'd'},
667 {"help", 0, 0, 'h'},
668 {"keepgoing", 0, 0, 'k'},
669 {"list", 0, 0, 'l'},
670 {"replace", 1, 0, 'r'},
671 {"version", 0, 0, 'v'}
672 };
673
674 #else /* not HAVE_GETOPT_LONG */
675 # define GETOPT_LONG(argc,argv,optstr,lopts,lidx) getopt(argc,argv,optstr)
676 #endif /* not HAVE_GETOPT_LONG */
677
678 static void
usage(char * progname)679 usage(char *progname)
680 {
681 printf("Usage: %s [-v|-d|-c|-r <path>] <program> [<program> ...]\n\n",
682 progname);
683 printf(" -v|--version Display program version number\n");
684 printf(" -d|--delete Delete current rpath/runpath setting\n");
685 #if defined(DT_RUNPATH)
686 printf(" -c|--convert Convert rpath to runpath\n");
687 #endif /* DT_RUNPATH */
688 printf(" -r <path>|--replace <path> Replace current rpath/runpath setting\n");
689 printf(" with the path given\n");
690 printf(" -l|--list List the current rpath/runpath (default)\n");
691 printf(" -h|--help Show this usage information.\n");
692 #ifndef HAVE_GETOPT_LONG
693 printf("\n *** The long options are not available on this platform");
694 #endif /* not HAVE_GETOPT_LONG */
695 #if !defined(DT_RUNPATH)
696 printf("\n *** There is no support for runpath on this platform");
697 #endif /* DT_RUNPATH */
698 printf("\n");
699 }
700
701 int
main(int argc,char * const argv[])702 main(int argc, char * const argv[])
703 {
704 int retval = 0;
705 int convert = 0; /* convert to given type */
706 int remove = 0; /* remove or not */
707 int keep_going = 0; /* Break on first error, or keep going? */
708 char *newpath = NULL; /* insert this path */
709 int opt;
710 #ifdef HAVE_GETOPT_LONG
711 int option_index = 0;
712 #endif /* HAVE_GETOPT_LONG */
713
714 if (argc < 2)
715 {
716 usage(argv[0]);
717 return 1;
718 }
719
720 do {
721 opt = GETOPT_LONG(argc, argv, "cdhklr:v", long_options, &option_index);
722 switch (opt)
723 {
724 #if defined(DT_RUNPATH)
725 case 'c':
726 convert = 1;
727 break;
728 #endif /* DT_RUNPATH */
729 case 'd':
730 remove = 1;
731 break;
732 case 'k':
733 keep_going = 1;
734 break;
735 case 'r':
736 newpath = optarg;
737 break;
738 case 'v':
739 printf("%s version %s\n", PACKAGE, VERSION);
740 exit(0);
741 break;
742 case 'l': /* This is the default action */
743 newpath = NULL;
744 break;
745 case -1:
746 break;
747 default:
748 printf("Invalid argument '%c'\n", opt);
749 case 'h':
750 usage(argv[0]);
751 exit(0);
752 break;
753 }
754 } while (-1 != opt);
755
756 while (optind < argc && (!retval || keep_going))
757 {
758 if (remove)
759 retval |= killrpath(argv[optind++]);
760 else
761 /* list by default, replace if path is set */
762 retval |= chrpath(argv[optind++], newpath, convert);
763 }
764
765 return retval;
766 }
767