d407487488b5c5bbea6bcd46359cdcca5a977534
[platform/upstream/rpm.git] / tools / striptofile.c
1 #include "system.h"
2
3 #include <elf.h>
4 #include <libelf.h>
5 #include <gelf.h>
6 #include <popt.h>
7
8 #include "sections.h"
9 #include "utils.h"
10
11 #include "debug.h"
12
13 char *output_dir = NULL;
14 int keep_strtab = 0;
15 int keep_all_section_headers = 1;
16 int add_unstrip_info = 0;
17
18 #if defined(NhUNUSED)
19 static void
20 copy_to_file(Elf *elf, Elf *out_elf)
21 {
22   GElf_Ehdr ehdr;
23   GElf_Phdr phdr;
24   Elf_Scn *section, *out_section;
25   GElf_Shdr section_header;
26   Elf_Data *data, *out_data;
27   int i;
28
29   elf_flagelf (out_elf, ELF_C_SET, ELF_F_LAYOUT);
30   
31   gelf_getehdr (elf, &ehdr);
32
33   /* copy elf header: */
34   gelf_newehdr(out_elf, ehdr.e_ident[EI_CLASS]);
35   gelf_update_ehdr(out_elf, &ehdr);
36
37   /* Copy program headers: */
38   gelf_newphdr(out_elf, ehdr.e_phnum);
39
40   for (i = 0; i < ehdr.e_phnum; i++)
41     {
42       gelf_getphdr (elf, i, &phdr);
43       gelf_update_phdr(out_elf, i, &phdr);
44     }
45
46   section = NULL;
47   while ((section = elf_nextscn(elf, section)) != NULL)
48     {
49       out_section = elf_newscn(out_elf);
50
51       /* Copy section header */
52       gelf_getshdr (section, &section_header);
53       gelf_update_shdr(out_section, &section_header);
54
55       /* Copy data */
56
57       data = NULL;
58       while ((data = elf_rawdata (section, data)))
59         {
60           out_data = elf_newdata (out_section);
61
62           out_data->d_buf = data->d_buf;
63           out_data->d_type = data->d_type;
64           out_data->d_size = data->d_size;
65           out_data->d_off = data->d_off;
66           out_data->d_align = section_header.sh_addralign;
67           out_data->d_version = data->d_version;
68         }
69     }
70 }
71 #endif
72
73 static void
74 strip_to_file(Elf *elf, Elf *out_elf, DebugLink *debuglink)
75 {
76   GElf_Ehdr ehdr;
77   GElf_Ehdr out_ehdr;
78   GElf_Phdr phdr;
79   Elf_Scn *section, *out_section;
80   GElf_Shdr section_header;
81   Elf_Data *data, *out_data;
82   unsigned char *section_strtab;
83   size_t *section_map;
84   int keep_section;
85   int changed_offsets;
86   GElf_Off last_offset;
87   int i;
88   int debuglink_name = 0;
89
90   elf_flagelf (out_elf, ELF_C_SET, ELF_F_LAYOUT);
91   
92   gelf_getehdr (elf, &ehdr);
93
94   /* copy elf header: */
95   gelf_newehdr(out_elf, ehdr.e_ident[EI_CLASS]);
96   gelf_update_ehdr(out_elf, &ehdr);
97
98   section_map = calloc(ehdr.e_shnum, sizeof (size_t));
99   
100   /* Locate section header strtab */
101   section = elf_getscn(elf, ehdr.e_shstrndx);
102   data = elf_getdata(section, NULL);
103   section_strtab = data->d_buf;
104
105   /* Copy program headers: */
106   gelf_newphdr(out_elf, ehdr.e_phnum);
107
108   for (i = 0; i < ehdr.e_phnum; i++)
109     {
110       gelf_getphdr (elf, i, &phdr);
111       gelf_update_phdr(out_elf, i, &phdr);
112     }
113
114   /* Copy section headers */
115   changed_offsets = 0;
116   last_offset = 0;
117   section = NULL;
118   while ((section = elf_nextscn(elf, section)) != NULL)
119     {
120       char *section_name;
121
122       gelf_getshdr (section, &section_header);
123
124       section_name = section_strtab + section_header.sh_name;
125       
126       keep_section =
127         !string_has_prefix (section_name, ".stab") &&
128         !string_has_prefix (section_name, ".debug") &&
129         (keep_strtab || 
130          (!keep_strtab &&
131           !string_has_prefix (section_name, ".symtab") &&
132           !string_has_prefix (section_name, ".strtab")));
133
134       if (keep_section)
135         {
136           out_section = elf_newscn(out_elf);
137       
138           section_map[elf_ndxscn(section)] = elf_ndxscn(out_section);
139
140           /* Update offset if necessary */
141           if (changed_offsets)
142             section_header.sh_offset = align_up (last_offset, section_header.sh_addralign);
143
144           /* Copy data */
145           data = NULL;
146           out_data = NULL;
147           while ((data = elf_rawdata (section, data)))
148             {
149               out_data = elf_newdata(out_section);
150
151               /* Add ".debuglink" to section header strtab */
152               if (ehdr.e_shstrndx == elf_ndxscn(section))
153                 {
154                   out_data->d_size = data->d_size + strlen (DEBUGLINKNAME) + 1;
155                   out_data->d_buf = malloc (out_data->d_size);
156                   memcpy (out_data->d_buf, data->d_buf, data->d_size);
157                   strcpy (out_data->d_buf + data->d_size, DEBUGLINKNAME);
158
159                   section_header.sh_size += out_data->d_size;
160                   changed_offsets = 1;
161                   debuglink_name = data->d_size;
162                 }
163               else
164                 {
165                   out_data->d_buf = data->d_buf;
166                   out_data->d_size = data->d_size;
167                 }
168               out_data->d_off = data->d_off;
169               out_data->d_type = data->d_type;
170               out_data->d_align = section_header.sh_addralign;
171               out_data->d_version = data->d_version;
172             }
173
174           last_offset = section_header.sh_offset + section_header.sh_size;
175           /* Write section header */
176           gelf_update_shdr(out_section, &section_header);
177         }
178       else
179         changed_offsets = 1;
180       
181     }
182
183   /* Add debuglink section header */
184   out_section = elf_newscn(out_elf);
185   section_header.sh_name = debuglink_name;
186   section_header.sh_type = SHT_PROGBITS;
187   section_header.sh_flags = 0;
188   section_header.sh_addr = 0;
189   section_header.sh_addralign = 4;
190   section_header.sh_offset = align_up (last_offset, section_header.sh_addralign);
191   section_header.sh_size = 0;
192   section_header.sh_link = 0;
193   section_header.sh_info = 0;
194   section_header.sh_entsize = 0;
195
196   out_data = elf_newdata(out_section);
197   debug_link_to_data (debuglink, elf, out_data);
198   
199   section_header.sh_size = out_data->d_size;
200   
201   last_offset = section_header.sh_offset + section_header.sh_size;
202   gelf_update_shdr(out_section, &section_header);
203
204   /* Update section header stringtab ref */
205   gelf_getehdr (out_elf, &out_ehdr);
206   out_ehdr.e_shstrndx = section_map[out_ehdr.e_shstrndx];
207   out_ehdr.e_shoff = align_up (last_offset, 8);
208   gelf_update_ehdr(out_elf, &out_ehdr);
209
210   /* Update section header links */
211   out_section = NULL;
212   while ((out_section = elf_nextscn(out_elf, out_section)) != NULL)
213     {
214       gelf_getshdr (out_section, &section_header);
215       
216       section_header.sh_link = section_map[section_header.sh_link];
217
218       if (section_header.sh_type == SHT_REL ||
219           section_header.sh_type == SHT_RELA)
220         section_header.sh_info = section_map[section_header.sh_info];
221       
222       gelf_update_shdr(out_section, &section_header);
223     }
224 }
225
226 static void
227 copy_debuginfo_to_file(Elf *elf, Elf *out_elf)
228 {
229   GElf_Ehdr ehdr;
230   Elf_Scn *section, *out_section;
231   GElf_Shdr section_header;
232   GElf_Shdr out_section_header;
233   Elf_Data *data, *out_data;
234   unsigned char *section_strtab;
235   int keep_section;
236   UnstripInfo *info;
237   int unstripinfo_name = 0;
238
239   info = malloc (sizeof (UnstripInfo));
240   
241   if (gelf_getehdr (elf, &ehdr) == NULL)
242     {
243       fprintf (stderr, "Not an elf binary, exiting\n");
244       exit (1);
245     }
246
247   gelf_newehdr(out_elf, ehdr.e_ident[EI_CLASS]);
248
249   /* copy elf header: */
250   gelf_update_ehdr(out_elf, &ehdr);
251
252   info->orig_e_shoff = ehdr.e_shoff;
253   info->n_sections = ehdr.e_shnum;
254   info->sections = calloc (info->n_sections, sizeof (UnstripInfoSection));
255   
256   /* Locate section header strtab */
257   section = elf_getscn(elf, ehdr.e_shstrndx);
258   data = elf_getdata(section, NULL);
259   section_strtab = data->d_buf;
260   
261   /* Copy section headers */
262   section = NULL;
263   while ((section = elf_nextscn(elf, section)) != NULL)
264     {
265       char *section_name;
266       size_t section_index;
267       GElf_Off last_offset;
268       gelf_getshdr (section, &section_header);
269
270       section_index = elf_ndxscn(section);
271       info->sections[section_index].name = section_header.sh_name;
272       info->sections[section_index].orig_offset = section_header.sh_offset;
273       info->sections[section_index].debug_section = 0;
274       
275       section_name = section_strtab + section_header.sh_name;
276       
277       keep_section =
278         string_has_prefix (section_name, ".stab") ||
279         string_has_prefix (section_name, ".debug") ||
280         string_has_prefix (section_name, ".symtab") ||
281         string_has_prefix (section_name, ".strtab") ||
282         section_index == ehdr.e_shstrndx;
283
284       if (keep_section)
285         {
286           out_section = elf_newscn(out_elf);
287
288           info->sections[section_index].debug_section = elf_ndxscn(out_section);
289           
290           memset (&out_section_header, 0, sizeof(out_section_header));
291           out_section_header.sh_name = section_header.sh_name;
292           out_section_header.sh_type = section_header.sh_type;
293           out_section_header.sh_flags = section_header.sh_flags;
294           out_section_header.sh_addr = section_header.sh_addr;
295           out_section_header.sh_offset = section_header.sh_offset;
296           out_section_header.sh_size = section_header.sh_size;
297           out_section_header.sh_link = section_header.sh_link;
298           out_section_header.sh_info = section_header.sh_info;
299           out_section_header.sh_addralign = section_header.sh_addralign;
300           out_section_header.sh_entsize = section_header.sh_entsize;
301           gelf_update_shdr(out_section, &out_section_header);
302           
303           /* Copy data */
304           
305           data = NULL;
306           last_offset = 0;
307           while ((data = elf_rawdata (section, data)))
308             {
309               out_data = elf_newdata(out_section);
310               
311               out_data->d_buf = data->d_buf;
312               out_data->d_type = data->d_type;
313               out_data->d_size = data->d_size;
314               out_data->d_off = data->d_off;
315               out_data->d_align = section_header.sh_addralign;
316               out_data->d_version = data->d_version;
317               last_offset = out_data->d_off + out_data->d_size;
318             }
319           /* Add ".debuglink" to section header strtab */
320           if (ehdr.e_shstrndx == elf_ndxscn(section))
321             {
322               out_data = elf_newdata(out_section);
323               
324               out_data->d_size = strlen (UNSTRIPINFONAME) + 1;
325               out_data->d_buf = UNSTRIPINFONAME;
326               out_data->d_off = last_offset;
327               out_data->d_align = 0;
328               
329               unstripinfo_name = out_data->d_off;
330             }
331         }
332       else if (keep_all_section_headers)
333         {
334           out_section = elf_newscn(out_elf);
335           
336           info->sections[section_index].debug_section = 0;
337
338           section_header.sh_type = SHT_NOBITS;
339           gelf_update_shdr(out_section, &section_header);
340           
341           if ((data = elf_rawdata (section, data)))
342             {
343               out_data = elf_newdata(out_section);
344
345               out_data->d_buf = NULL;
346               out_data->d_size = data->d_size;
347               out_data->d_off = data->d_off;
348               out_data->d_type = data->d_type;
349               out_data->d_align = section_header.sh_addralign;
350               out_data->d_version = data->d_version;
351             }
352         }
353
354     }
355
356   /* Add unlinkinfo section header */
357   if (add_unstrip_info)
358     {
359       out_section = elf_newscn(out_elf);
360       section_header.sh_name = unstripinfo_name;
361       section_header.sh_type = SHT_PROGBITS;
362       section_header.sh_flags = 0;
363       section_header.sh_addr = 0;
364       section_header.sh_addralign = 4;
365       section_header.sh_link = 0;
366       section_header.sh_info = 0;
367       section_header.sh_entsize = 0;
368
369       out_data = elf_newdata(out_section);
370       unstrip_info_to_data (info, elf, out_data);
371   
372       gelf_update_shdr(out_section, &section_header);
373     }
374   
375   /* Update section header stringtab ref */
376   gelf_getehdr (out_elf, &ehdr);
377   ehdr.e_shstrndx = info->sections[ehdr.e_shstrndx].debug_section;
378   gelf_update_ehdr(out_elf, &ehdr);
379   
380   /* Update section header links */
381   out_section = NULL;
382   while ((out_section = elf_nextscn(out_elf, out_section)) != NULL)
383     {
384       gelf_getshdr (out_section, &out_section_header);
385       out_section_header.sh_link = info->sections[out_section_header.sh_link].debug_section;
386       gelf_update_shdr(out_section, &out_section_header);
387     }
388
389 }
390
391 static struct poptOption optionsTable[] = {
392     { "output-dir",  'o', POPT_ARG_STRING, &output_dir, 0,
393       "directory to store result", "/usr/lib/debug" },
394     { "strip-debug", 'g', POPT_ARG_NONE, &keep_strtab, 0,
395       "Remove debugging symbols only, keep symbols", 0 },
396     { "unstrip-info", 'u', POPT_ARG_NONE, &add_unstrip_info, 0,
397       "Add unstripping information to the debug file", 0 },
398       POPT_AUTOHELP
399     { NULL, 0, 0, NULL, 0 }
400 };
401
402 int
403 main (int argc, char *argv[])
404 {
405   Elf *elf, *out_elf;
406   int fd, out;
407   const char *origname;
408   char *origname_base;
409   char *debugname, *strippedname;
410   DebugLink *debuglink;
411   poptContext optCon;   /* context for parsing command-line options */
412   int nextopt;
413   const char **args;
414   struct stat stat_buf;
415   
416   optCon = poptGetContext("striptofile", argc, (const char **)argv,
417                           optionsTable, 0);
418   
419   while ((nextopt = poptGetNextOpt (optCon)) > 0 || nextopt == POPT_ERROR_BADOPT)
420     /* do nothing */ ;
421
422   if (nextopt != -1)
423     {
424       fprintf (stderr, "Error on option %s: %s.\nRun '%s --help' to see a full list of available command line options.\n",
425               poptBadOption (optCon, 0),
426               poptStrerror (nextopt),
427               argv[0]);
428       exit (1);
429     }
430   
431   args = poptGetArgs (optCon);
432   if (args == NULL || args[0] == NULL || args[1] != NULL)
433     {
434       poptPrintHelp(optCon, stdout, 0);
435       exit (1);
436     }
437   
438   origname = args[0];
439
440   if (output_dir)
441     {
442       origname_base = path_basename (origname);
443       debugname = strconcat (output_dir, "/", origname_base, ".debug", NULL);
444       free (origname_base);
445     }
446   else
447     debugname = strconcat (origname, ".debug", NULL);
448   
449   strippedname = strconcat (origname, ".XXXXXX", NULL);
450   
451   if (elf_version(EV_CURRENT) == EV_NONE)
452     {
453       fprintf (stderr, "library out of date\n");
454       exit (1);
455     }
456
457   fd = open (origname, O_RDONLY);
458   if (fd < 0)
459     {
460       fprintf (stderr, "Failed to open input file: %s\n", origname);
461       exit (1);
462     }
463   
464   elf = elf_begin (fd, ELF_C_READ, NULL);
465   if (elf == NULL)
466     {
467       fprintf (stderr, "Failed to elf_begin input file: %s\n", origname);
468       exit (1);
469     }
470
471   /* Create debug file: */
472   out = open (debugname, O_RDWR | O_TRUNC | O_CREAT, 0644);
473   if (out < 0)
474     {
475       fprintf (stderr, "Failed to open output file: %s\n", debugname);
476       exit (1);
477     }
478
479   out_elf = elf_begin (out, ELF_C_WRITE, NULL);
480   if (out_elf == NULL)
481     {
482       fprintf (stderr, "Failed to elf_begin output file: %s\n", debugname);
483       exit (1);
484     }
485
486   copy_debuginfo_to_file (elf, out_elf);
487
488   elf_update (out_elf, ELF_C_WRITE);
489   elf_end (out_elf);
490   close (out);
491   
492   debuglink = malloc (sizeof (DebugLink));
493   debuglink->filename = path_basename (debugname);
494   debuglink->checksum = crc32_file (debugname);
495
496   /* Create stripped file: */
497   out = mkstemp (strippedname);
498   if (out < 0)
499     {
500       fprintf (stderr, "Failed to open output file: %s\n", strippedname);
501       exit (1);
502     }
503
504   /* Copy access rights */
505   if (fstat(fd, &stat_buf) == 0)
506     fchmod(out, stat_buf.st_mode);
507
508   out_elf = elf_begin (out, ELF_C_WRITE, NULL);
509   if (out_elf == NULL)
510     {
511       fprintf (stderr, "Failed to elf_begin output file: %s\n", strippedname);
512       exit (1);
513     }
514
515   strip_to_file (elf, out_elf, debuglink);
516
517   elf_update (out_elf, ELF_C_WRITE);
518   elf_end (out_elf);
519   close (out);        
520   
521   elf_end (elf);
522   close (fd);
523
524   
525   if (rename (strippedname, origname) != 0)
526     fprintf(stderr, "unable to write to %s\n", origname);
527
528   unlink (strippedname);
529   
530   poptFreeContext (optCon);
531
532   return 0;
533 }
534