sstrip.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. /* http://www.muppetlabs.com/~breadbox/software/elfkickers.html */
  2. /* sstrip: Copyright (C) 1999-2001 by Brian Raiter, under the GNU
  3. * General Public License. No warranty. See COPYING for details.
  4. *
  5. * Aug 23, 2004 Hacked by Manuel Novoa III <mjn3@codepoet.org> to
  6. * handle targets of different endianness and/or elf class, making
  7. * it more useful in a cross-devel environment.
  8. */
  9. /* ============== original README ===================
  10. *
  11. * sstrip is a small utility that removes the contents at the end of an
  12. * ELF file that are not part of the program's memory image.
  13. *
  14. * Most ELF executables are built with both a program header table and a
  15. * section header table. However, only the former is required in order
  16. * for the OS to load, link and execute a program. sstrip attempts to
  17. * extract the ELF header, the program header table, and its contents,
  18. * leaving everything else in the bit bucket. It can only remove parts of
  19. * the file that occur at the end, after the parts to be saved. However,
  20. * this almost always includes the section header table, and occasionally
  21. * a few random sections that are not used when running a program.
  22. *
  23. * It should be noted that the GNU bfd library is (understandably)
  24. * dependent on the section header table as an index to the file's
  25. * contents. Thus, an executable file that has no section header table
  26. * cannot be used with gdb, objdump, or any other program based upon the
  27. * bfd library, at all. In fact, the program will not even recognize the
  28. * file as a valid executable. (This limitation is noted in the source
  29. * code comments for bfd, and is marked "FIXME", so this may change at
  30. * some future date. However, I would imagine that it is a pretty
  31. * low-priority item, as executables without a section header table are
  32. * rare in the extreme.) This probably also explains why strip doesn't
  33. * offer the option to do this.
  34. *
  35. * Shared library files may also have their section header table removed.
  36. * Such a library will still function; however, it will no longer be
  37. * possible for a compiler to link a new program against it.
  38. *
  39. * As an added bonus, sstrip also tries to removes trailing zero bytes
  40. * from the end of the file. (This normally cannot be done with an
  41. * executable that has a section header table.)
  42. *
  43. * sstrip is a very simplistic program. It depends upon the common
  44. * practice of putting the parts of the file that contribute to the
  45. * memory image at the front, and the remaining material at the end. This
  46. * permits it to discard the latter material without affecting file
  47. * offsets and memory addresses in what remains. Of course, the ELF
  48. * standard permits files to be organized in almost any order, so if a
  49. * pathological linker decided to put its section headers at the top,
  50. * sstrip would be useless on such executables.
  51. */
  52. #include <stdio.h>
  53. #include <stdlib.h>
  54. #include <string.h>
  55. #include <errno.h>
  56. #include <unistd.h>
  57. #include <fcntl.h>
  58. #include <elf.h>
  59. #ifndef TRUE
  60. #define TRUE 1
  61. #define FALSE 0
  62. #endif
  63. /* The name of the program.
  64. */
  65. static char const *progname;
  66. /* The name of the current file.
  67. */
  68. static char const *filename;
  69. /* A simple error-handling function. FALSE is always returned for the
  70. * convenience of the caller.
  71. */
  72. static int err(char const *errmsg)
  73. {
  74. fprintf(stderr, "%s: %s: %s\n", progname, filename, errmsg);
  75. return FALSE;
  76. }
  77. /* A flag to signal the need for endian reversal.
  78. */
  79. static int do_reverse_endian;
  80. /* Get a value from the elf header, compensating for endianness.
  81. */
  82. #define EGET(X) \
  83. (__extension__ ({ \
  84. uint64_t __res; \
  85. if (!do_reverse_endian) { \
  86. __res = (X); \
  87. } else if (sizeof(X) == 1) { \
  88. __res = (X); \
  89. } else if (sizeof(X) == 2) { \
  90. __res = bswap_16((X)); \
  91. } else if (sizeof(X) == 4) { \
  92. __res = bswap_32((X)); \
  93. } else if (sizeof(X) == 8) { \
  94. __res = bswap_64((X)); \
  95. } else { \
  96. fprintf(stderr, "%s: %s: EGET failed for size %d\n", \
  97. progname, filename, sizeof(X)); \
  98. exit(EXIT_FAILURE); \
  99. } \
  100. __res; \
  101. }))
  102. /* Set a value 'Y' in the elf header to 'X', compensating for endianness.
  103. */
  104. #define ESET(Y,X) \
  105. do if (!do_reverse_endian) { \
  106. Y = (X); \
  107. } else if (sizeof(Y) == 1) { \
  108. Y = (X); \
  109. } else if (sizeof(Y) == 2) { \
  110. Y = bswap_16((uint16_t)(X)); \
  111. } else if (sizeof(Y) == 4) { \
  112. Y = bswap_32((uint32_t)(X)); \
  113. } else if (sizeof(Y) == 8) { \
  114. Y = bswap_64((uint64_t)(X)); \
  115. } else { \
  116. fprintf(stderr, "%s: %s: ESET failed for size %d\n", \
  117. progname, filename, sizeof(Y)); \
  118. exit(EXIT_FAILURE); \
  119. } while (0)
  120. /* A macro for I/O errors: The given error message is used only when
  121. * errno is not set.
  122. */
  123. #define ferr(msg) (err(errno ? strerror(errno) : (msg)))
  124. #define HEADER_FUNCTIONS(CLASS) \
  125. \
  126. /* readelfheader() reads the ELF header into our global variable, and \
  127. * checks to make sure that this is in fact a file that we should be \
  128. * munging. \
  129. */ \
  130. static int readelfheader ## CLASS (int fd, Elf ## CLASS ## _Ehdr *ehdr) \
  131. { \
  132. if (read(fd, ((char *)ehdr)+EI_NIDENT, sizeof(*ehdr) - EI_NIDENT) \
  133. != sizeof(*ehdr) - EI_NIDENT) \
  134. return ferr("missing or incomplete ELF header."); \
  135. \
  136. /* Verify the sizes of the ELF header and the program segment \
  137. * header table entries. \
  138. */ \
  139. if (EGET(ehdr->e_ehsize) != sizeof(Elf ## CLASS ## _Ehdr)) \
  140. return err("unrecognized ELF header size."); \
  141. if (EGET(ehdr->e_phentsize) != sizeof(Elf ## CLASS ## _Phdr)) \
  142. return err("unrecognized program segment header size."); \
  143. \
  144. /* Finally, check the file type. \
  145. */ \
  146. if (EGET(ehdr->e_type) != ET_EXEC && EGET(ehdr->e_type) != ET_DYN) \
  147. return err("not an executable or shared-object library."); \
  148. \
  149. return TRUE; \
  150. } \
  151. \
  152. /* readphdrtable() loads the program segment header table into memory. \
  153. */ \
  154. static int readphdrtable ## CLASS (int fd, Elf ## CLASS ## _Ehdr const *ehdr, \
  155. Elf ## CLASS ## _Phdr **phdrs) \
  156. { \
  157. size_t size; \
  158. \
  159. if (!EGET(ehdr->e_phoff) || !EGET(ehdr->e_phnum) \
  160. ) return err("ELF file has no program header table."); \
  161. \
  162. size = EGET(ehdr->e_phnum) * sizeof **phdrs; \
  163. if (!(*phdrs = malloc(size))) \
  164. return err("Out of memory!"); \
  165. \
  166. errno = 0; \
  167. if (read(fd, *phdrs, size) != (ssize_t)size) \
  168. return ferr("missing or incomplete program segment header table."); \
  169. \
  170. return TRUE; \
  171. } \
  172. \
  173. /* getmemorysize() determines the offset of the last byte of the file \
  174. * that is referenced by an entry in the program segment header table. \
  175. * (Anything in the file after that point is not used when the program \
  176. * is executing, and thus can be safely discarded.) \
  177. */ \
  178. static int getmemorysize ## CLASS (Elf ## CLASS ## _Ehdr const *ehdr, \
  179. Elf ## CLASS ## _Phdr const *phdrs, \
  180. unsigned long *newsize) \
  181. { \
  182. Elf ## CLASS ## _Phdr const *phdr; \
  183. unsigned long size, n; \
  184. int i; \
  185. \
  186. /* Start by setting the size to include the ELF header and the \
  187. * complete program segment header table. \
  188. */ \
  189. size = EGET(ehdr->e_phoff) + EGET(ehdr->e_phnum) * sizeof *phdrs; \
  190. if (size < sizeof *ehdr) \
  191. size = sizeof *ehdr; \
  192. \
  193. /* Then keep extending the size to include whatever data the \
  194. * program segment header table references. \
  195. */ \
  196. for (i = 0, phdr = phdrs ; i < EGET(ehdr->e_phnum) ; ++i, ++phdr) { \
  197. if (EGET(phdr->p_type) != PT_NULL) { \
  198. n = EGET(phdr->p_offset) + EGET(phdr->p_filesz); \
  199. if (n > size) \
  200. size = n; \
  201. } \
  202. } \
  203. \
  204. *newsize = size; \
  205. return TRUE; \
  206. } \
  207. \
  208. /* modifyheaders() removes references to the section header table if \
  209. * it was stripped, and reduces program header table entries that \
  210. * included truncated bytes at the end of the file. \
  211. */ \
  212. static int modifyheaders ## CLASS (Elf ## CLASS ## _Ehdr *ehdr, \
  213. Elf ## CLASS ## _Phdr *phdrs, \
  214. unsigned long newsize) \
  215. { \
  216. Elf ## CLASS ## _Phdr *phdr; \
  217. int i; \
  218. \
  219. /* If the section header table is gone, then remove all references \
  220. * to it in the ELF header. \
  221. */ \
  222. if (EGET(ehdr->e_shoff) >= newsize) { \
  223. ESET(ehdr->e_shoff,0); \
  224. ESET(ehdr->e_shnum,0); \
  225. ESET(ehdr->e_shentsize,0); \
  226. ESET(ehdr->e_shstrndx,0); \
  227. } \
  228. \
  229. /* The program adjusts the file size of any segment that was \
  230. * truncated. The case of a segment being completely stripped out \
  231. * is handled separately. \
  232. */ \
  233. for (i = 0, phdr = phdrs ; i < EGET(ehdr->e_phnum) ; ++i, ++phdr) { \
  234. if (EGET(phdr->p_offset) >= newsize) { \
  235. ESET(phdr->p_offset,newsize); \
  236. ESET(phdr->p_filesz,0); \
  237. } else if (EGET(phdr->p_offset) + EGET(phdr->p_filesz) > newsize) { \
  238. ESET(phdr->p_filesz, newsize - EGET(phdr->p_offset)); \
  239. } \
  240. } \
  241. \
  242. return TRUE; \
  243. } \
  244. \
  245. /* commitchanges() writes the new headers back to the original file \
  246. * and sets the file to its new size. \
  247. */ \
  248. static int commitchanges ## CLASS (int fd, Elf ## CLASS ## _Ehdr const *ehdr, \
  249. Elf ## CLASS ## _Phdr *phdrs, \
  250. unsigned long newsize) \
  251. { \
  252. size_t n; \
  253. \
  254. /* Save the changes to the ELF header, if any. \
  255. */ \
  256. if (lseek(fd, 0, SEEK_SET)) \
  257. return ferr("could not rewind file"); \
  258. errno = 0; \
  259. if (write(fd, ehdr, sizeof *ehdr) != sizeof *ehdr) \
  260. return err("could not modify file"); \
  261. \
  262. /* Save the changes to the program segment header table, if any. \
  263. */ \
  264. if (lseek(fd, EGET(ehdr->e_phoff), SEEK_SET) == (off_t)-1) { \
  265. err("could not seek in file."); \
  266. goto warning; \
  267. } \
  268. n = EGET(ehdr->e_phnum) * sizeof *phdrs; \
  269. if (write(fd, phdrs, n) != (ssize_t)n) { \
  270. err("could not write to file"); \
  271. goto warning; \
  272. } \
  273. \
  274. /* Eleventh-hour sanity check: don't truncate before the end of \
  275. * the program segment header table. \
  276. */ \
  277. if (newsize < EGET(ehdr->e_phoff) + n) \
  278. newsize = EGET(ehdr->e_phoff) + n; \
  279. \
  280. /* Chop off the end of the file. \
  281. */ \
  282. if (ftruncate(fd, newsize)) { \
  283. err("could not resize file"); \
  284. goto warning; \
  285. } \
  286. \
  287. return TRUE; \
  288. \
  289. warning: \
  290. return err("ELF file may have been corrupted!"); \
  291. }
  292. /* First elements of Elf32_Ehdr and Elf64_Ehdr are common.
  293. */
  294. static int readelfheaderident(int fd, Elf32_Ehdr *ehdr)
  295. {
  296. errno = 0;
  297. if (read(fd, ehdr, EI_NIDENT) != EI_NIDENT)
  298. return ferr("missing or incomplete ELF header.");
  299. /* Check the ELF signature.
  300. */
  301. if (!(ehdr->e_ident[EI_MAG0] == ELFMAG0 &&
  302. ehdr->e_ident[EI_MAG1] == ELFMAG1 &&
  303. ehdr->e_ident[EI_MAG2] == ELFMAG2 &&
  304. ehdr->e_ident[EI_MAG3] == ELFMAG3))
  305. {
  306. err("missing ELF signature.");
  307. return -1;
  308. }
  309. /* Compare the file's class and endianness with the program's.
  310. */
  311. #if __BYTE_ORDER == __LITTLE_ENDIAN
  312. if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB) {
  313. do_reverse_endian = 0;
  314. } else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB) {
  315. /* fprintf(stderr, "ELF file has different endianness.\n"); */
  316. do_reverse_endian = 1;
  317. }
  318. #elif __BYTE_ORDER == __BIG_ENDIAN
  319. if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB) {
  320. /* fprintf(stderr, "ELF file has different endianness.\n"); */
  321. do_reverse_endian = 1;
  322. } else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB) {
  323. do_reverse_endian = 0;
  324. }
  325. #else
  326. #error unkown endianness
  327. #endif
  328. else {
  329. err("Unsupported endianness");
  330. return -1;
  331. }
  332. /* Check the target architecture.
  333. */
  334. /* if (EGET(ehdr->e_machine) != ELF_ARCH) { */
  335. /* /\* return err("ELF file created for different architecture."); *\/ */
  336. /* fprintf(stderr, "ELF file created for different architecture.\n"); */
  337. /* } */
  338. return ehdr->e_ident[EI_CLASS];
  339. }
  340. HEADER_FUNCTIONS(32)
  341. HEADER_FUNCTIONS(64)
  342. /* truncatezeros() examines the bytes at the end of the file's
  343. * size-to-be, and reduces the size to exclude any trailing zero
  344. * bytes.
  345. */
  346. static int truncatezeros(int fd, unsigned long *newsize)
  347. {
  348. unsigned char contents[1024];
  349. unsigned long size, n;
  350. size = *newsize;
  351. do {
  352. n = sizeof contents;
  353. if (n > size)
  354. n = size;
  355. if (lseek(fd, size - n, SEEK_SET) == (off_t)-1)
  356. return ferr("cannot seek in file.");
  357. if (read(fd, contents, n) != (ssize_t)n)
  358. return ferr("cannot read file contents");
  359. while (n && !contents[--n])
  360. --size;
  361. } while (size && !n);
  362. /* Sanity check.
  363. */
  364. if (!size)
  365. return err("ELF file is completely blank!");
  366. *newsize = size;
  367. return TRUE;
  368. }
  369. /* main() loops over the cmdline arguments, leaving all the real work
  370. * to the other functions.
  371. */
  372. int main(int argc, char *argv[])
  373. {
  374. int fd;
  375. union {
  376. Elf32_Ehdr ehdr32;
  377. Elf64_Ehdr ehdr64;
  378. } e;
  379. union {
  380. Elf32_Phdr *phdrs32;
  381. Elf64_Phdr *phdrs64;
  382. } p;
  383. unsigned long newsize;
  384. char **arg;
  385. int failures = 0;
  386. if (argc < 2 || argv[1][0] == '-') {
  387. printf("Usage: sstrip FILE...\n"
  388. "sstrip discards all nonessential bytes from an executable.\n\n"
  389. "Version 2.0-X Copyright (C) 2000,2001 Brian Raiter.\n"
  390. "Cross-devel hacks Copyright (C) 2004 Manuel Novoa III.\n"
  391. "This program is free software, licensed under the GNU\n"
  392. "General Public License. There is absolutely no warranty.\n");
  393. return EXIT_SUCCESS;
  394. }
  395. progname = argv[0];
  396. for (arg = argv + 1 ; *arg != NULL ; ++arg) {
  397. filename = *arg;
  398. fd = open(*arg, O_RDWR);
  399. if (fd < 0) {
  400. ferr("can't open");
  401. ++failures;
  402. continue;
  403. }
  404. switch (readelfheaderident(fd, &e.ehdr32)) {
  405. case ELFCLASS32:
  406. if (!(readelfheader32(fd, &e.ehdr32) &&
  407. readphdrtable32(fd, &e.ehdr32, &p.phdrs32) &&
  408. getmemorysize32(&e.ehdr32, p.phdrs32, &newsize) &&
  409. truncatezeros(fd, &newsize) &&
  410. modifyheaders32(&e.ehdr32, p.phdrs32, newsize) &&
  411. commitchanges32(fd, &e.ehdr32, p.phdrs32, newsize)))
  412. ++failures;
  413. break;
  414. case ELFCLASS64:
  415. if (!(readelfheader64(fd, &e.ehdr64) &&
  416. readphdrtable64(fd, &e.ehdr64, &p.phdrs64) &&
  417. getmemorysize64(&e.ehdr64, p.phdrs64, &newsize) &&
  418. truncatezeros(fd, &newsize) &&
  419. modifyheaders64(&e.ehdr64, p.phdrs64, newsize) &&
  420. commitchanges64(fd, &e.ehdr64, p.phdrs64, newsize)))
  421. ++failures;
  422. break;
  423. default:
  424. ++failures;
  425. break;
  426. }
  427. close(fd);
  428. }
  429. return failures ? EXIT_FAILURE : EXIT_SUCCESS;
  430. }