Commit | Line | Data |
---|---|---|
b181bbd0 TT |
1 | /* Read MiniDebugInfo data from an objfile. |
2 | ||
618f726f | 3 | Copyright (C) 2012-2016 Free Software Foundation, Inc. |
b181bbd0 TT |
4 | |
5 | This file is part of GDB. | |
6 | ||
7 | This program is free software; you can redistribute it and/or modify | |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation; either version 3 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | This program is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
19 | ||
20 | #include "defs.h" | |
21 | #include "gdb_bfd.h" | |
b181bbd0 TT |
22 | #include "symfile.h" |
23 | #include "objfiles.h" | |
24 | #include "gdbcore.h" | |
325fac50 | 25 | #include <algorithm> |
b181bbd0 TT |
26 | |
27 | #ifdef HAVE_LIBLZMA | |
28 | ||
29 | #include <lzma.h> | |
30 | ||
31 | /* Allocator function for LZMA. */ | |
32 | ||
33 | static void * | |
34 | alloc_lzma (void *opaque, size_t nmemb, size_t size) | |
35 | { | |
36 | return xmalloc (nmemb * size); | |
37 | } | |
38 | ||
39 | /* Free function for LZMA. */ | |
40 | ||
41 | static void | |
42 | free_lzma (void *opaque, void *ptr) | |
43 | { | |
44 | xfree (ptr); | |
45 | } | |
46 | ||
47 | /* The allocator object for LZMA. Note that 'gdb_lzma_allocator' | |
48 | cannot be const due to the lzma library function prototypes. */ | |
49 | ||
50 | static lzma_allocator gdb_lzma_allocator = { alloc_lzma, free_lzma, NULL }; | |
51 | ||
52 | /* Custom bfd_openr_iovec implementation to read compressed data from | |
53 | a section. This keeps only the last decompressed block in memory | |
54 | to allow larger data without using to much memory. */ | |
55 | ||
1424c16e | 56 | struct gdb_lzma_stream |
b181bbd0 TT |
57 | { |
58 | /* Section of input BFD from which we are decoding data. */ | |
59 | asection *section; | |
60 | ||
61 | /* lzma library decompression state. */ | |
62 | lzma_index *index; | |
63 | ||
64 | /* Currently decoded block. */ | |
65 | bfd_size_type data_start; | |
66 | bfd_size_type data_end; | |
67 | gdb_byte *data; | |
68 | }; | |
69 | ||
70 | /* bfd_openr_iovec OPEN_P implementation for | |
71 | find_separate_debug_file_in_section. OPEN_CLOSURE is 'asection *' | |
72 | of the section to decompress. | |
73 | ||
1424c16e TT |
74 | Return 'struct gdb_lzma_stream *' must be freed by caller by xfree, |
75 | together with its INDEX lzma data. */ | |
b181bbd0 TT |
76 | |
77 | static void * | |
78 | lzma_open (struct bfd *nbfd, void *open_closure) | |
79 | { | |
19ba03f4 | 80 | asection *section = (asection *) open_closure; |
b181bbd0 TT |
81 | bfd_size_type size, offset; |
82 | lzma_stream_flags options; | |
83 | gdb_byte footer[LZMA_STREAM_HEADER_SIZE]; | |
84 | gdb_byte *indexdata; | |
85 | lzma_index *index; | |
86 | int ret; | |
87 | uint64_t memlimit = UINT64_MAX; | |
1424c16e | 88 | struct gdb_lzma_stream *lstream; |
b181bbd0 TT |
89 | size_t pos; |
90 | ||
91 | size = bfd_get_section_size (section); | |
92 | offset = section->filepos + size - LZMA_STREAM_HEADER_SIZE; | |
93 | if (size < LZMA_STREAM_HEADER_SIZE | |
94 | || bfd_seek (section->owner, offset, SEEK_SET) != 0 | |
95 | || bfd_bread (footer, LZMA_STREAM_HEADER_SIZE, section->owner) | |
96 | != LZMA_STREAM_HEADER_SIZE | |
97 | || lzma_stream_footer_decode (&options, footer) != LZMA_OK | |
98 | || offset < options.backward_size) | |
99 | { | |
100 | bfd_set_error (bfd_error_wrong_format); | |
101 | return NULL; | |
102 | } | |
103 | ||
104 | offset -= options.backward_size; | |
224c3ddb | 105 | indexdata = (gdb_byte *) xmalloc (options.backward_size); |
b181bbd0 TT |
106 | index = NULL; |
107 | pos = 0; | |
108 | if (bfd_seek (section->owner, offset, SEEK_SET) != 0 | |
109 | || bfd_bread (indexdata, options.backward_size, section->owner) | |
110 | != options.backward_size | |
111 | || lzma_index_buffer_decode (&index, &memlimit, &gdb_lzma_allocator, | |
112 | indexdata, &pos, options.backward_size) | |
113 | != LZMA_OK | |
114 | || lzma_index_size (index) != options.backward_size) | |
115 | { | |
116 | xfree (indexdata); | |
117 | bfd_set_error (bfd_error_wrong_format); | |
118 | return NULL; | |
119 | } | |
120 | xfree (indexdata); | |
121 | ||
8d749320 | 122 | lstream = XCNEW (struct gdb_lzma_stream); |
b181bbd0 TT |
123 | lstream->section = section; |
124 | lstream->index = index; | |
125 | ||
126 | return lstream; | |
127 | } | |
128 | ||
129 | /* bfd_openr_iovec PREAD_P implementation for | |
130 | find_separate_debug_file_in_section. Passed STREAM | |
1424c16e | 131 | is 'struct gdb_lzma_stream *'. */ |
b181bbd0 TT |
132 | |
133 | static file_ptr | |
134 | lzma_pread (struct bfd *nbfd, void *stream, void *buf, file_ptr nbytes, | |
135 | file_ptr offset) | |
136 | { | |
19ba03f4 | 137 | struct gdb_lzma_stream *lstream = (struct gdb_lzma_stream *) stream; |
b181bbd0 TT |
138 | bfd_size_type chunk_size; |
139 | lzma_index_iter iter; | |
140 | gdb_byte *compressed, *uncompressed; | |
141 | file_ptr block_offset; | |
142 | lzma_filter filters[LZMA_FILTERS_MAX + 1]; | |
143 | lzma_block block; | |
144 | size_t compressed_pos, uncompressed_pos; | |
145 | file_ptr res; | |
146 | ||
147 | res = 0; | |
148 | while (nbytes > 0) | |
149 | { | |
150 | if (lstream->data == NULL | |
151 | || lstream->data_start > offset || offset >= lstream->data_end) | |
152 | { | |
153 | asection *section = lstream->section; | |
154 | ||
155 | lzma_index_iter_init (&iter, lstream->index); | |
156 | if (lzma_index_iter_locate (&iter, offset)) | |
157 | break; | |
158 | ||
224c3ddb | 159 | compressed = (gdb_byte *) xmalloc (iter.block.total_size); |
b181bbd0 TT |
160 | block_offset = section->filepos + iter.block.compressed_file_offset; |
161 | if (bfd_seek (section->owner, block_offset, SEEK_SET) != 0 | |
162 | || bfd_bread (compressed, iter.block.total_size, section->owner) | |
163 | != iter.block.total_size) | |
164 | { | |
165 | xfree (compressed); | |
166 | break; | |
167 | } | |
168 | ||
224c3ddb | 169 | uncompressed = (gdb_byte *) xmalloc (iter.block.uncompressed_size); |
b181bbd0 TT |
170 | |
171 | memset (&block, 0, sizeof (block)); | |
172 | block.filters = filters; | |
173 | block.header_size = lzma_block_header_size_decode (compressed[0]); | |
174 | if (lzma_block_header_decode (&block, &gdb_lzma_allocator, compressed) | |
175 | != LZMA_OK) | |
176 | { | |
177 | xfree (compressed); | |
178 | xfree (uncompressed); | |
179 | break; | |
180 | } | |
181 | ||
182 | compressed_pos = block.header_size; | |
183 | uncompressed_pos = 0; | |
184 | if (lzma_block_buffer_decode (&block, &gdb_lzma_allocator, | |
185 | compressed, &compressed_pos, | |
186 | iter.block.total_size, | |
187 | uncompressed, &uncompressed_pos, | |
188 | iter.block.uncompressed_size) | |
189 | != LZMA_OK) | |
190 | { | |
191 | xfree (compressed); | |
192 | xfree (uncompressed); | |
193 | break; | |
194 | } | |
195 | ||
196 | xfree (compressed); | |
197 | ||
198 | xfree (lstream->data); | |
199 | lstream->data = uncompressed; | |
200 | lstream->data_start = iter.block.uncompressed_file_offset; | |
201 | lstream->data_end = (iter.block.uncompressed_file_offset | |
202 | + iter.block.uncompressed_size); | |
203 | } | |
204 | ||
325fac50 | 205 | chunk_size = std::min (nbytes, (file_ptr) lstream->data_end - offset); |
b181bbd0 TT |
206 | memcpy (buf, lstream->data + offset - lstream->data_start, chunk_size); |
207 | buf = (gdb_byte *) buf + chunk_size; | |
208 | offset += chunk_size; | |
209 | nbytes -= chunk_size; | |
210 | res += chunk_size; | |
211 | } | |
212 | ||
213 | return res; | |
214 | } | |
215 | ||
216 | /* bfd_openr_iovec CLOSE_P implementation for | |
217 | find_separate_debug_file_in_section. Passed STREAM | |
1424c16e | 218 | is 'struct gdb_lzma_stream *'. */ |
b181bbd0 TT |
219 | |
220 | static int | |
221 | lzma_close (struct bfd *nbfd, | |
222 | void *stream) | |
223 | { | |
19ba03f4 | 224 | struct gdb_lzma_stream *lstream = (struct gdb_lzma_stream *) stream; |
b181bbd0 TT |
225 | |
226 | lzma_index_end (lstream->index, &gdb_lzma_allocator); | |
227 | xfree (lstream->data); | |
228 | xfree (lstream); | |
39ed5604 JK |
229 | |
230 | /* Zero means success. */ | |
b181bbd0 TT |
231 | return 0; |
232 | } | |
233 | ||
234 | /* bfd_openr_iovec STAT_P implementation for | |
235 | find_separate_debug_file_in_section. Passed STREAM | |
1424c16e | 236 | is 'struct gdb_lzma_stream *'. */ |
b181bbd0 TT |
237 | |
238 | static int | |
239 | lzma_stat (struct bfd *abfd, | |
240 | void *stream, | |
241 | struct stat *sb) | |
242 | { | |
19ba03f4 | 243 | struct gdb_lzma_stream *lstream = (struct gdb_lzma_stream *) stream; |
b181bbd0 | 244 | |
326a5c7e | 245 | memset (sb, 0, sizeof (struct stat)); |
b181bbd0 TT |
246 | sb->st_size = lzma_index_uncompressed_size (lstream->index); |
247 | return 0; | |
248 | } | |
249 | ||
250 | #endif /* HAVE_LIBLZMA */ | |
251 | ||
252 | /* This looks for a xz compressed separate debug info object file embedded | |
253 | in a section called .gnu_debugdata. See | |
254 | http://fedoraproject.org/wiki/Features/MiniDebugInfo | |
255 | or the "Separate Debug Sections" of the manual for details. | |
256 | If we find one we create a iovec based bfd that decompresses the | |
257 | object data on demand. If we don't find one, return NULL. */ | |
258 | ||
259 | bfd * | |
260 | find_separate_debug_file_in_section (struct objfile *objfile) | |
261 | { | |
262 | asection *section; | |
263 | bfd *abfd; | |
264 | ||
265 | if (objfile->obfd == NULL) | |
266 | return NULL; | |
267 | ||
268 | section = bfd_get_section_by_name (objfile->obfd, ".gnu_debugdata"); | |
269 | if (section == NULL) | |
270 | return NULL; | |
271 | ||
272 | #ifdef HAVE_LIBLZMA | |
4262abfb JK |
273 | abfd = gdb_bfd_openr_iovec (objfile_name (objfile), gnutarget, lzma_open, |
274 | section, lzma_pread, lzma_close, lzma_stat); | |
b181bbd0 TT |
275 | if (abfd == NULL) |
276 | return NULL; | |
277 | ||
278 | if (!bfd_check_format (abfd, bfd_object)) | |
279 | { | |
280 | warning (_("Cannot parse .gnu_debugdata section; not a BFD object")); | |
281 | gdb_bfd_unref (abfd); | |
282 | return NULL; | |
283 | } | |
284 | #else | |
285 | warning (_("Cannot parse .gnu_debugdata section; LZMA support was " | |
286 | "disabled at compile time")); | |
287 | abfd = NULL; | |
288 | #endif /* !HAVE_LIBLZMA */ | |
289 | ||
290 | return abfd; | |
291 | } |