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