Imported Upstream version 0.155
[platform/upstream/elfutils.git] / libdwfl / open.c
1 /* Decompression support for libdwfl: zlib (gzip) and/or bzlib (bzip2).
2    Copyright (C) 2009 Red Hat, Inc.
3    This file is part of elfutils.
4
5    This file is free software; you can redistribute it and/or modify
6    it under the terms of either
7
8      * the GNU Lesser General Public License as published by the Free
9        Software Foundation; either version 3 of the License, or (at
10        your option) any later version
11
12    or
13
14      * the GNU General Public License as published by the Free
15        Software Foundation; either version 2 of the License, or (at
16        your option) any later version
17
18    or both in parallel, as here.
19
20    elfutils is distributed in the hope that it will be useful, but
21    WITHOUT ANY WARRANTY; without even the implied warranty of
22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23    General Public License for more details.
24
25    You should have received copies of the GNU General Public License and
26    the GNU Lesser General Public License along with this program.  If
27    not, see <http://www.gnu.org/licenses/>.  */
28
29 #include "../libelf/libelfP.h"
30 #undef  _
31 #include "libdwflP.h"
32
33 #include <unistd.h>
34
35 #if !USE_ZLIB
36 # define __libdw_gunzip(...)    false
37 #endif
38
39 #if !USE_BZLIB
40 # define __libdw_bunzip2(...)   false
41 #endif
42
43 #if !USE_LZMA
44 # define __libdw_unlzma(...)    false
45 #endif
46
47 /* Consumes and replaces *ELF only on success.  */
48 static Dwfl_Error
49 decompress (int fd __attribute__ ((unused)), Elf **elf)
50 {
51   Dwfl_Error error = DWFL_E_BADELF;
52   void *buffer = NULL;
53   size_t size = 0;
54
55 #if USE_ZLIB || USE_BZLIB || USE_LZMA
56   const off64_t offset = (*elf)->start_offset;
57   void *const mapped = ((*elf)->map_address == NULL ? NULL
58                         : (*elf)->map_address + offset);
59   const size_t mapped_size = (*elf)->maximum_size;
60   if (mapped_size == 0)
61     return error;
62
63   error = __libdw_gunzip (fd, offset, mapped, mapped_size, &buffer, &size);
64   if (error == DWFL_E_BADELF)
65     error = __libdw_bunzip2 (fd, offset, mapped, mapped_size, &buffer, &size);
66   if (error == DWFL_E_BADELF)
67     error = __libdw_unlzma (fd, offset, mapped, mapped_size, &buffer, &size);
68 #endif
69
70   if (error == DWFL_E_NOERROR)
71     {
72       if (unlikely (size == 0))
73         {
74           error = DWFL_E_BADELF;
75           free (buffer);
76         }
77       else
78         {
79           Elf *memelf = elf_memory (buffer, size);
80           if (memelf == NULL)
81             {
82               error = DWFL_E_LIBELF;
83               free (buffer);
84             }
85           else
86             {
87               memelf->flags |= ELF_F_MALLOCED;
88               elf_end (*elf);
89               *elf = memelf;
90             }
91         }
92     }
93   else
94     free (buffer);
95
96   return error;
97 }
98
99 static Dwfl_Error
100 what_kind (int fd, Elf **elfp, Elf_Kind *kind, bool *close_fd)
101 {
102   Dwfl_Error error = DWFL_E_NOERROR;
103   *kind = elf_kind (*elfp);
104   if (unlikely (*kind == ELF_K_NONE))
105     {
106       if (unlikely (*elfp == NULL))
107         error = DWFL_E_LIBELF;
108       else
109         {
110           error = decompress (fd, elfp);
111           if (error == DWFL_E_NOERROR)
112             {
113               *close_fd = true;
114               *kind = elf_kind (*elfp);
115             }
116         }
117     }
118   return error;
119 }
120
121 Dwfl_Error internal_function
122 __libdw_open_file (int *fdp, Elf **elfp, bool close_on_fail, bool archive_ok)
123 {
124   bool close_fd = false;
125
126   Elf *elf = elf_begin (*fdp, ELF_C_READ_MMAP_PRIVATE, NULL);
127
128   Elf_Kind kind;
129   Dwfl_Error error = what_kind (*fdp, &elf, &kind, &close_fd);
130   if (error == DWFL_E_BADELF)
131     {
132       /* It's not an ELF file or a compressed file.
133          See if it's an image with a header preceding the real file.  */
134
135       off64_t offset = elf->start_offset;
136       error = __libdw_image_header (*fdp, &offset,
137                                     (elf->map_address == NULL ? NULL
138                                      : elf->map_address + offset),
139                                     elf->maximum_size);
140       if (error == DWFL_E_NOERROR)
141         {
142           /* Pure evil.  libelf needs some better interfaces.  */
143           elf->kind = ELF_K_AR;
144           elf->state.ar.elf_ar_hdr.ar_name = "libdwfl is faking you out";
145           elf->state.ar.elf_ar_hdr.ar_size = elf->maximum_size - offset;
146           elf->state.ar.offset = offset - sizeof (struct ar_hdr);
147           Elf *subelf = elf_begin (-1, ELF_C_READ_MMAP_PRIVATE, elf);
148           elf->kind = ELF_K_NONE;
149           if (unlikely (subelf == NULL))
150             error = DWFL_E_LIBELF;
151           else
152             {
153               subelf->parent = NULL;
154               subelf->flags |= elf->flags & (ELF_F_MMAPPED | ELF_F_MALLOCED);
155               elf->flags &= ~(ELF_F_MMAPPED | ELF_F_MALLOCED);
156               elf_end (elf);
157               elf = subelf;
158               error = what_kind (*fdp, &elf, &kind, &close_fd);
159             }
160         }
161     }
162
163   if (error == DWFL_E_NOERROR
164       && kind != ELF_K_ELF
165       && !(archive_ok && kind == ELF_K_AR))
166     error = DWFL_E_BADELF;
167
168   if (error != DWFL_E_NOERROR)
169     {
170       elf_end (elf);
171       elf = NULL;
172     }
173
174   if (error == DWFL_E_NOERROR ? close_fd : close_on_fail)
175     {
176       close (*fdp);
177       *fdp = -1;
178     }
179
180   *elfp = elf;
181   return error;
182 }