Elfutils fiddles fo fix striptofile.
[platform/upstream/rpm.git] / tools / unstripfile.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
10 #include "sections.h"
11 #include "utils.h"
12
13 #include "debug.h"
14
15 static DebugLink *
16 read_debuglink (Elf *elf)
17 {
18   GElf_Ehdr ehdr;
19   Elf_Scn *section;
20   GElf_Shdr section_header;
21   Elf_Data *data;
22   unsigned char *section_strtab;
23
24   if (gelf_getehdr (elf, &ehdr) == NULL)
25     {
26       printf ("Not an elf binary, exiting\n");
27       exit (1);
28     }
29
30   /* Locate section header strtab */
31   section = elf_getscn (elf, ehdr.e_shstrndx);
32   data = elf_getdata (section, NULL);
33   section_strtab = data->d_buf;
34
35   section = NULL;
36   while ((section = elf_nextscn (elf, section)) != NULL)
37     {
38       char *section_name;
39
40       gelf_getshdr (section, &section_header);
41
42       section_name = section_strtab + section_header.sh_name;
43
44       if (strcmp (section_name, DEBUGLINKNAME) == 0)
45         {
46           data = elf_rawdata (section, NULL);
47           return debug_link_from_data (elf, data);
48         }
49     }
50   return NULL;
51 }
52
53 static Elf_Scn *
54 find_section (Elf *elf, const unsigned char *name, const unsigned char *strtab)
55 {
56   Elf_Scn *section;
57   GElf_Shdr section_header;
58   const unsigned char *section_name;
59   
60   section = NULL;
61   while ((section = elf_nextscn (elf, section)) != NULL)
62     {
63
64       gelf_getshdr (section, &section_header);
65       
66       section_name = strtab + section_header.sh_name;
67
68       if (strcmp (section_name, name) == 0)
69         break;
70     }
71   
72   return section;
73 }
74
75 static size_t
76 find_in_strtab (char *name, char *strtab, size_t strtab_len)
77 {
78   int name_len, i;
79
80   name_len = strlen (name);
81
82   for (i = 0; i < strtab_len - (name_len + 1); i++)
83     if (strcmp (strtab+i, name) == 0)
84       return i;
85   return 0;
86 }
87
88 static void
89 unstrip_file (Elf *elf, Elf *debug_elf, Elf *out_elf)
90 {
91   UnstripInfo *info;
92   GElf_Ehdr ehdr, debug_ehdr;
93   GElf_Phdr phdr;
94   GElf_Shdr section_header;
95   Elf_Scn *section, *out_section;
96   Elf_Data *data, *out_data;
97   unsigned char *section_strtab;
98   size_t section_strtab_len;
99   unsigned char *debug_section_strtab;
100   size_t debug_section_strtab_len;
101   size_t *debug_section_map;
102   size_t *stripped_section_map;
103   char *section_name;
104   int i;
105   size_t new_strtab_index;
106
107   elf_flagelf (out_elf, ELF_C_SET, ELF_F_LAYOUT);
108   
109   gelf_getehdr (elf, &ehdr);
110   if (gelf_getehdr (debug_elf, &debug_ehdr) == NULL)
111     {
112       printf ("debug file not an elf binary, exiting\n");
113       exit (1);
114     }
115
116   /* copy elf header: */
117   gelf_newehdr (out_elf, ehdr.e_ident[EI_CLASS]);
118   gelf_update_ehdr (out_elf, &ehdr);
119
120   /* Copy program headers: */
121   gelf_newphdr (out_elf, ehdr.e_phnum);
122
123   for (i = 0; i < ehdr.e_phnum; i++)
124     {
125       gelf_getphdr (elf, i, &phdr);
126       gelf_update_phdr(out_elf, i, &phdr);
127     }
128
129   /* Locate section header strtabs */
130   section = elf_getscn (elf, ehdr.e_shstrndx);
131   data = elf_getdata (section, NULL);
132   section_strtab = data->d_buf;
133   section_strtab_len = data->d_size;
134
135   section = elf_getscn (debug_elf, debug_ehdr.e_shstrndx);
136   data = elf_getdata (section, NULL);
137   debug_section_strtab = data->d_buf;
138   debug_section_strtab_len = data->d_size;
139
140   /* Read unlinkinfo */
141   info = NULL;
142   section = find_section (debug_elf, UNSTRIPINFONAME, debug_section_strtab);
143   if (section)
144     {
145       data = elf_rawdata (section, NULL);
146       info = unstrip_info_from_data (elf, data);
147     }
148   
149   if (info == NULL)
150     {
151       printf ("Can't find unstrip info in debug file\n");
152       exit (1);
153     }
154
155   /* Construct backward section index maps */
156   debug_section_map = calloc (info->n_sections, sizeof (size_t));
157   for (i = 0; i < info->n_sections; i++)
158     {
159       if (info->sections[i].debug_section > 0)
160         debug_section_map[info->sections[i].debug_section] = i;
161     }
162
163   stripped_section_map = calloc (ehdr.e_shnum, sizeof (size_t));
164   section = NULL;
165   while ((section = elf_nextscn(elf, section)) != NULL)
166     {
167       gelf_getshdr (section, &section_header);
168       section_name = section_strtab + section_header.sh_name;
169       
170       for (i = 0; i < info->n_sections; i++)
171         {
172           char *debug_section_name;
173           
174           debug_section_name = debug_section_strtab + info->sections[i].name;
175           
176           /* If section name is same as an original section, and the original
177            * section is not in debugfile, use this section */
178           if (info->sections[i].debug_section == 0 &&
179               strcmp (debug_section_name, section_name) == 0)
180             {
181               stripped_section_map[elf_ndxscn(section)] = i;
182               break;
183             }
184         }
185     }
186   
187   /* combine sections */
188   new_strtab_index = 0;
189   for (i = 0; i < info->n_sections; i++)
190     {
191       if (info->sections[i].debug_section != 0)
192         {
193           section = elf_getscn(debug_elf, info->sections[i].debug_section);
194
195           out_section = elf_newscn(out_elf);
196
197           /* Copy section header */
198           gelf_getshdr (section, &section_header);
199           section_header.sh_offset = info->sections[i].orig_offset;
200           section_header.sh_link = debug_section_map[section_header.sh_link];
201           if (section_header.sh_type == SHT_REL ||
202               section_header.sh_type == SHT_RELA)
203             section_header.sh_info = stripped_section_map[section_header.sh_info];
204
205           /* Copy data */
206           data = NULL;
207           while ((data = elf_rawdata (section, data)))
208             {
209               out_data = elf_newdata (out_section);
210               
211               /* TODO: remove .unstripinfo for shstrtab */
212               
213               out_data->d_buf = data->d_buf;
214               out_data->d_type = data->d_type;
215               out_data->d_size = data->d_size;
216               if (debug_ehdr.e_shstrndx == info->sections[i].debug_section)
217                 {
218                   new_strtab_index = i;
219                   if (strcmp (data->d_buf + data->d_size - (strlen(UNSTRIPINFONAME) + 1), UNSTRIPINFONAME) == 0)
220                     {
221                       out_data->d_size -= strlen(UNSTRIPINFONAME) + 1;
222                       section_header.sh_size = out_data->d_size;
223                     }
224                 }
225               out_data->d_off = data->d_off;
226               out_data->d_align = section_header.sh_addralign;
227               out_data->d_version = data->d_version;
228             }
229           gelf_update_shdr(out_section, &section_header);
230         }
231       else
232         {
233           section_name = debug_section_strtab + info->sections[i].name;
234           section = find_section (elf, section_name, section_strtab);
235
236           if (section)
237             {
238               out_section = elf_newscn(out_elf);
239
240               /* Copy section header */
241               gelf_getshdr (section, &section_header);
242               section_header.sh_offset = info->sections[i].orig_offset;
243               section_header.sh_link = stripped_section_map[section_header.sh_link];
244               if (section_header.sh_type == SHT_REL ||
245                   section_header.sh_type == SHT_RELA)
246                 section_header.sh_info = stripped_section_map[section_header.sh_info];
247               section_header.sh_name = find_in_strtab (section_name, debug_section_strtab, debug_section_strtab_len);
248               gelf_update_shdr (out_section, &section_header);
249
250               /* Copy data */
251               data = NULL;
252               while ((data = elf_rawdata (section, data)))
253                 {
254                   out_data = elf_newdata (out_section);
255                   
256                   out_data->d_buf = data->d_buf;
257                   out_data->d_type = data->d_type;
258                   out_data->d_size = data->d_size;
259                   out_data->d_off = data->d_off;
260                   out_data->d_align = section_header.sh_addralign;
261                   out_data->d_version = data->d_version;
262                 }
263             }
264         }
265     }
266
267   gelf_getehdr (out_elf, &ehdr);
268   ehdr.e_shstrndx = new_strtab_index;
269   ehdr.e_shoff = info->orig_e_shoff;
270   gelf_update_ehdr (out_elf, &ehdr);
271 }
272
273
274 int
275 main (int argc, char *argv[])
276 {
277   Elf *elf, *debug_elf, *out_elf;
278   int fd, debug_fd, out;
279   char *origname, *unstrippedname;
280   DebugLink *debuglink;
281   
282   if (elf_version(EV_CURRENT) == EV_NONE)
283     {
284       printf ("library out of date\n");
285       exit (1);
286     }
287   
288   if (argc != 2)
289     {
290       printf ("usage: unstriptofile filename\n");
291       exit (1);
292     }
293   
294   origname = argv[1];
295
296   fd = open (origname, O_RDONLY);
297   if (fd < 0)
298     {
299       printf ("Failed to open input file\n");
300       exit (1);
301     }
302
303   elf = elf_begin (fd, ELF_C_READ, NULL);
304   if (elf == NULL)
305     {
306       printf ("Failed to elf_begin input file\n");
307       exit (1);
308     }
309
310   debuglink = read_debuglink (elf);
311   if (debuglink == NULL)
312     {
313       printf ("Cannot find .debuglink section in input file\n");
314       exit (1);
315     }
316
317   if (debuglink->checksum != crc32_file (debuglink->filename))
318     {
319       printf ("Invalid checksum for debug file. File has been modified.\n");
320       exit (1);
321     }
322   
323   debug_fd = open (debuglink->filename, O_RDONLY);
324   if (debug_fd < 0)
325     {
326       printf ("Failed to open debug file\n");
327       exit (1);
328     }
329  
330   debug_elf = elf_begin (debug_fd, ELF_C_READ, NULL);
331   if (debug_elf == NULL)
332     {
333       printf ("Failed to elf_begin debug file\n");
334       exit (1);
335     }
336
337   unstrippedname = malloc (strlen (origname) + strlen (".unstripped") + 1);
338   strcpy (unstrippedname, origname);
339   strcat (unstrippedname, ".unstripped");
340   
341   out = open (unstrippedname, O_RDWR | O_TRUNC | O_CREAT, 0644);
342   if (out < 0)
343     {
344       printf ("Failed to open output file\n");
345       exit (1);
346     }
347
348   out_elf = elf_begin (out, ELF_C_WRITE, NULL);
349   if (out_elf == NULL)
350     {
351       printf ("Failed to elf_begin output file\n");
352       exit (1);
353     }
354
355   unstrip_file (elf, debug_elf, out_elf);
356
357   elf_update (out_elf, ELF_C_WRITE);
358   elf_end (out_elf);
359   close (out);
360   
361   elf_end (debug_elf);
362   close (debug_fd);
363   
364   elf_end (elf);
365   close (fd);
366   
367   return 0;
368 }
369