/* Re: changing embedded RPATH in existing executables. To: geoffk@ozemail.com.au Subject: Re: changing embedded RPATH in existing executables. From: (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: > _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 #include #include #include #if defined(HAVE_LINK_H) # include #endif /* HAVE_LINK_H */ #include #include #include #include "protos.h" /** * Reads an ELF file, and reads or alters the RPATH setting. * * TODO: * modify to add RPATH setting if none exists. */ int chrpath(const char *filename, const char *newpath, int convert) { int fd; Elf_Ehdr ehdr; int i; Elf_Phdr phdr; Elf_Shdr shdr; void *dyns; int rpathoff; char * strtab; char * rpath; unsigned int rpathlen; int oflags; int rpath_dyns_index; if (NULL == newpath && 0 == convert) oflags = O_RDONLY; else oflags = O_RDWR; fd = elf_open(filename, oflags, &ehdr); if (fd == -1) { perror ("elf_open"); return 1; } if (0 != elf_find_dynamic_section(fd, &ehdr, &phdr)) { perror("found no dynamic section"); elf_close(fd); return 1; } dyns = malloc(PHDR(p_filesz)); if (dyns == NULL) { perror ("allocating memory for dynamic section"); elf_close(fd); return 1; } memset(dyns, 0, PHDR(p_filesz)); if (lseek(fd, PHDR(p_offset), SEEK_SET) == -1 || read(fd, dyns, PHDR(p_filesz)) != (ssize_t)PHDR(p_filesz)) { perror ("reading dynamic section"); free(dyns); elf_close(fd); return 1; } rpathoff = -1; for ( rpath_dyns_index = 0; DYNSS(rpath_dyns_index, d_tag) != DT_NULL; ++rpath_dyns_index ) { if ( elf_dynpath_tag(DYNSS(rpath_dyns_index, d_tag)) ) { rpathoff = DYNSU(rpath_dyns_index, d_un.d_ptr); break; } } if (rpathoff == -1) { printf("%s: no rpath or runpath tag found.\n", filename); free(dyns); elf_close(fd); return 2; } if (lseek(fd, EHDRWU(e_shoff), SEEK_SET) == -1) { perror ("positioning for sections"); free(dyns); elf_close(fd); return 1; } for (i = 0; i < EHDRHU(e_shnum); i++) { const size_t sz_shdr = is_e32() ? sizeof(Elf32_Shdr) : sizeof(Elf64_Shdr); if (read(fd, &shdr, sz_shdr) != (ssize_t)sz_shdr) { perror ("reading section header"); free(dyns); elf_close(fd); return 1; } if (SHDR_W(sh_type) == SHT_STRTAB) break; } if (i == EHDRHU(e_shnum)) { fprintf (stderr, "No string table found.\n"); free(dyns); elf_close(fd); return 2; } /* +1 for forced trailing null */ strtab = (char *)calloc(1, SHDR_O(sh_size)+1); if (strtab == NULL) { perror ("allocating memory for string table"); free(dyns); elf_close(fd); return 1; } if (lseek(fd, SHDR_O(sh_offset), SEEK_SET) == -1) { perror ("positioning for string table"); free(strtab); free(dyns); elf_close(fd); return 1; } if (read(fd, strtab, SHDR_O(sh_size)) != (ssize_t)SHDR_O(sh_size)) { perror ("reading string table"); free(strtab); free(dyns); elf_close(fd); return 1; } strtab[SHDR_O(sh_size)] = 0; /* make sure printed string is null terminated */ if ((int)SHDR_O(sh_size) < rpathoff) { fprintf(stderr, "%s string offset not contained in string table", elf_tagname(DYNSS(rpath_dyns_index, d_tag))); free(strtab); free(dyns); elf_close(fd); return 5; } rpath = strtab+rpathoff; #if defined(DT_RUNPATH) if (convert && DYNSS(rpath_dyns_index, d_tag) == DT_RPATH) { if (is_e32()) ((Elf32_Dyn *)dyns)[rpath_dyns_index].d_tag = swap_bytes() ? bswap_32(DT_RUNPATH) : DT_RUNPATH; else ((Elf64_Dyn *)dyns)[rpath_dyns_index].d_tag = swap_bytes() ? bswap_64(DT_RUNPATH) : 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"); free(strtab); free(dyns); elf_close(fd); return 1; } printf("%s: RPATH converted to RUNPATH\n", filename); } #endif /* DT_RUNPATH */ printf("%s: %s=%s\n", filename, elf_tagname(DYNSS(rpath_dyns_index, d_tag)), rpath); if (NULL == newpath) { free(dyns); free(strtab); elf_close(fd); 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_O(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); elf_close(fd); return 7; } memset(rpath, 0, rpathlen); strcpy(rpath, newpath); if (lseek(fd, SHDR_O(sh_offset)+rpathoff, SEEK_SET) == -1) { perror ("positioning for RPATH"); free(dyns); free(strtab); elf_close(fd); return 1; } if (write(fd, rpath, rpathlen) != (int)rpathlen) { perror ("writing RPATH"); free(dyns); free(strtab); elf_close(fd); return 1; } printf("%s: new %s: %s\n", filename, elf_tagname(DYNSS(rpath_dyns_index, d_tag)), rpath); elf_close(fd); free(dyns); dyns = NULL; free(strtab); return 0; }