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