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