]>
Commit | Line | Data |
---|---|---|
b181bbd0 TT |
1 | /* Read MiniDebugInfo data from an objfile. |
2 | ||
d01e8234 | 3 | Copyright (C) 2012-2025 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 | ||
b181bbd0 | 20 | #include "gdb_bfd.h" |
b181bbd0 TT |
21 | #include "symfile.h" |
22 | #include "objfiles.h" | |
23 | #include "gdbcore.h" | |
325fac50 | 24 | #include <algorithm> |
b181bbd0 TT |
25 | |
26 | #ifdef HAVE_LIBLZMA | |
27 | ||
a4a38eb4 TT |
28 | /* We stash a reference to the .gnu_debugdata BFD on the enclosing |
29 | BFD. */ | |
08b8a139 | 30 | static const registry<bfd>::key<gdb_bfd_ref_ptr> gnu_debug_key; |
a4a38eb4 | 31 | |
b181bbd0 TT |
32 | #include <lzma.h> |
33 | ||
34 | /* Allocator function for LZMA. */ | |
35 | ||
36 | static void * | |
37 | alloc_lzma (void *opaque, size_t nmemb, size_t size) | |
38 | { | |
39 | return xmalloc (nmemb * size); | |
40 | } | |
41 | ||
42 | /* Free function for LZMA. */ | |
43 | ||
44 | static void | |
45 | free_lzma (void *opaque, void *ptr) | |
46 | { | |
47 | xfree (ptr); | |
48 | } | |
49 | ||
50 | /* The allocator object for LZMA. Note that 'gdb_lzma_allocator' | |
51 | cannot be const due to the lzma library function prototypes. */ | |
52 | ||
53 | static lzma_allocator gdb_lzma_allocator = { alloc_lzma, free_lzma, NULL }; | |
54 | ||
55 | /* Custom bfd_openr_iovec implementation to read compressed data from | |
56 | a section. This keeps only the last decompressed block in memory | |
57 | to allow larger data without using to much memory. */ | |
58 | ||
d1f50898 | 59 | struct gdb_lzma_stream : public gdb_bfd_iovec_base |
b181bbd0 TT |
60 | { |
61 | /* Section of input BFD from which we are decoding data. */ | |
e76d241a | 62 | asection *section = nullptr; |
b181bbd0 TT |
63 | |
64 | /* lzma library decompression state. */ | |
e76d241a | 65 | lzma_index *index = nullptr; |
b181bbd0 TT |
66 | |
67 | /* Currently decoded block. */ | |
e76d241a TT |
68 | bfd_size_type data_start = 0; |
69 | bfd_size_type data_end = 0; | |
70 | gdb::byte_vector data; | |
b181bbd0 | 71 | |
b181bbd0 | 72 | |
d1f50898 TT |
73 | ~gdb_lzma_stream () |
74 | { | |
75 | lzma_index_end (index, &gdb_lzma_allocator); | |
76 | } | |
b181bbd0 | 77 | |
d1f50898 TT |
78 | file_ptr read (bfd *abfd, void *buffer, file_ptr nbytes, |
79 | file_ptr offset) override; | |
80 | ||
81 | int stat (struct bfd *abfd, struct stat *sb) override; | |
82 | }; | |
83 | ||
84 | /* bfd_openr_iovec implementation helper for | |
85 | find_separate_debug_file_in_section. */ | |
86 | ||
87 | static gdb_lzma_stream * | |
88 | lzma_open (struct bfd *nbfd, asection *section) | |
b181bbd0 | 89 | { |
b181bbd0 TT |
90 | bfd_size_type size, offset; |
91 | lzma_stream_flags options; | |
92 | gdb_byte footer[LZMA_STREAM_HEADER_SIZE]; | |
b181bbd0 | 93 | lzma_index *index; |
b181bbd0 | 94 | uint64_t memlimit = UINT64_MAX; |
1424c16e | 95 | struct gdb_lzma_stream *lstream; |
b181bbd0 TT |
96 | size_t pos; |
97 | ||
fd361982 | 98 | size = bfd_section_size (section); |
b181bbd0 TT |
99 | offset = section->filepos + size - LZMA_STREAM_HEADER_SIZE; |
100 | if (size < LZMA_STREAM_HEADER_SIZE | |
101 | || bfd_seek (section->owner, offset, SEEK_SET) != 0 | |
226f9f4f | 102 | || bfd_read (footer, LZMA_STREAM_HEADER_SIZE, section->owner) |
dda83cd7 | 103 | != LZMA_STREAM_HEADER_SIZE |
b181bbd0 TT |
104 | || lzma_stream_footer_decode (&options, footer) != LZMA_OK |
105 | || offset < options.backward_size) | |
106 | { | |
107 | bfd_set_error (bfd_error_wrong_format); | |
108 | return NULL; | |
109 | } | |
110 | ||
111 | offset -= options.backward_size; | |
e76d241a | 112 | gdb::byte_vector indexdata (options.backward_size); |
b181bbd0 TT |
113 | index = NULL; |
114 | pos = 0; | |
115 | if (bfd_seek (section->owner, offset, SEEK_SET) != 0 | |
e76d241a | 116 | || bfd_read (indexdata.data (), options.backward_size, section->owner) |
dda83cd7 | 117 | != options.backward_size |
b181bbd0 | 118 | || lzma_index_buffer_decode (&index, &memlimit, &gdb_lzma_allocator, |
e76d241a TT |
119 | indexdata.data (), &pos, |
120 | options.backward_size) | |
dda83cd7 | 121 | != LZMA_OK |
b181bbd0 TT |
122 | || lzma_index_size (index) != options.backward_size) |
123 | { | |
b181bbd0 TT |
124 | bfd_set_error (bfd_error_wrong_format); |
125 | return NULL; | |
126 | } | |
b181bbd0 | 127 | |
e76d241a | 128 | lstream = new struct gdb_lzma_stream; |
b181bbd0 TT |
129 | lstream->section = section; |
130 | lstream->index = index; | |
131 | ||
132 | return lstream; | |
133 | } | |
134 | ||
d1f50898 TT |
135 | /* bfd_openr_iovec read implementation for |
136 | find_separate_debug_file_in_section. */ | |
b181bbd0 | 137 | |
d1f50898 TT |
138 | file_ptr |
139 | gdb_lzma_stream::read (struct bfd *nbfd, void *buf, file_ptr nbytes, | |
140 | file_ptr offset) | |
b181bbd0 | 141 | { |
b181bbd0 TT |
142 | bfd_size_type chunk_size; |
143 | lzma_index_iter iter; | |
b181bbd0 TT |
144 | file_ptr block_offset; |
145 | lzma_filter filters[LZMA_FILTERS_MAX + 1]; | |
146 | lzma_block block; | |
147 | size_t compressed_pos, uncompressed_pos; | |
148 | file_ptr res; | |
149 | ||
150 | res = 0; | |
151 | while (nbytes > 0) | |
152 | { | |
d1f50898 | 153 | if (data.empty () || data_start > offset || offset >= data_end) |
b181bbd0 | 154 | { |
d1f50898 | 155 | lzma_index_iter_init (&iter, index); |
b181bbd0 TT |
156 | if (lzma_index_iter_locate (&iter, offset)) |
157 | break; | |
158 | ||
e76d241a | 159 | gdb::byte_vector compressed (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 | |
e76d241a TT |
162 | || bfd_read (compressed.data (), iter.block.total_size, |
163 | section->owner) != iter.block.total_size) | |
164 | break; | |
b181bbd0 | 165 | |
e76d241a | 166 | gdb::byte_vector uncompressed (iter.block.uncompressed_size); |
b181bbd0 TT |
167 | |
168 | memset (&block, 0, sizeof (block)); | |
169 | block.filters = filters; | |
170 | block.header_size = lzma_block_header_size_decode (compressed[0]); | |
e76d241a TT |
171 | if (lzma_block_header_decode (&block, &gdb_lzma_allocator, |
172 | compressed.data ()) | |
b181bbd0 | 173 | != LZMA_OK) |
e76d241a | 174 | break; |
b181bbd0 TT |
175 | |
176 | compressed_pos = block.header_size; | |
177 | uncompressed_pos = 0; | |
178 | if (lzma_block_buffer_decode (&block, &gdb_lzma_allocator, | |
e76d241a | 179 | compressed.data (), &compressed_pos, |
b181bbd0 | 180 | iter.block.total_size, |
e76d241a TT |
181 | uncompressed.data (), |
182 | &uncompressed_pos, | |
b181bbd0 TT |
183 | iter.block.uncompressed_size) |
184 | != LZMA_OK) | |
e76d241a | 185 | break; |
b181bbd0 | 186 | |
d1f50898 TT |
187 | data = std::move (uncompressed); |
188 | data_start = iter.block.uncompressed_file_offset; | |
189 | data_end = (iter.block.uncompressed_file_offset | |
190 | + iter.block.uncompressed_size); | |
b181bbd0 TT |
191 | } |
192 | ||
d1f50898 TT |
193 | chunk_size = std::min (nbytes, (file_ptr) data_end - offset); |
194 | memcpy (buf, data.data () + offset - data_start, chunk_size); | |
b181bbd0 TT |
195 | buf = (gdb_byte *) buf + chunk_size; |
196 | offset += chunk_size; | |
197 | nbytes -= chunk_size; | |
198 | res += chunk_size; | |
199 | } | |
200 | ||
201 | return res; | |
202 | } | |
203 | ||
d1f50898 TT |
204 | /* bfd_openr_iovec stat implementation for |
205 | find_separate_debug_file_in_section. */ | |
b181bbd0 | 206 | |
d1f50898 TT |
207 | int |
208 | gdb_lzma_stream::stat (struct bfd *abfd, struct stat *sb) | |
b181bbd0 | 209 | { |
326a5c7e | 210 | memset (sb, 0, sizeof (struct stat)); |
d1f50898 | 211 | sb->st_size = lzma_index_uncompressed_size (index); |
b181bbd0 TT |
212 | return 0; |
213 | } | |
214 | ||
215 | #endif /* HAVE_LIBLZMA */ | |
216 | ||
217 | /* This looks for a xz compressed separate debug info object file embedded | |
218 | in a section called .gnu_debugdata. See | |
219 | http://fedoraproject.org/wiki/Features/MiniDebugInfo | |
220 | or the "Separate Debug Sections" of the manual for details. | |
221 | If we find one we create a iovec based bfd that decompresses the | |
222 | object data on demand. If we don't find one, return NULL. */ | |
223 | ||
192b62ce | 224 | gdb_bfd_ref_ptr |
b181bbd0 TT |
225 | find_separate_debug_file_in_section (struct objfile *objfile) |
226 | { | |
227 | asection *section; | |
192b62ce | 228 | gdb_bfd_ref_ptr abfd; |
b181bbd0 TT |
229 | |
230 | if (objfile->obfd == NULL) | |
231 | return NULL; | |
232 | ||
98badbfd | 233 | section = bfd_get_section_by_name (objfile->obfd.get (), ".gnu_debugdata"); |
b181bbd0 TT |
234 | if (section == NULL) |
235 | return NULL; | |
236 | ||
237 | #ifdef HAVE_LIBLZMA | |
98badbfd | 238 | gdb_bfd_ref_ptr *shared = gnu_debug_key.get (objfile->obfd.get ()); |
a4a38eb4 TT |
239 | if (shared != nullptr) |
240 | return *shared; | |
241 | ||
921222e2 TT |
242 | std::string filename = string_printf (_(".gnu_debugdata for %s"), |
243 | objfile_name (objfile)); | |
244 | ||
d1f50898 TT |
245 | auto open = [&] (bfd *nbfd) -> gdb_lzma_stream * |
246 | { | |
247 | return lzma_open (nbfd, section); | |
248 | }; | |
249 | ||
250 | abfd = gdb_bfd_openr_iovec (filename.c_str (), gnutarget, open); | |
b181bbd0 TT |
251 | if (abfd == NULL) |
252 | return NULL; | |
253 | ||
192b62ce | 254 | if (!bfd_check_format (abfd.get (), bfd_object)) |
b181bbd0 TT |
255 | { |
256 | warning (_("Cannot parse .gnu_debugdata section; not a BFD object")); | |
b181bbd0 TT |
257 | return NULL; |
258 | } | |
a4a38eb4 | 259 | |
98badbfd | 260 | gnu_debug_key.emplace (objfile->obfd.get (), abfd); |
a4a38eb4 | 261 | |
b181bbd0 TT |
262 | #else |
263 | warning (_("Cannot parse .gnu_debugdata section; LZMA support was " | |
264 | "disabled at compile time")); | |
b181bbd0 TT |
265 | #endif /* !HAVE_LIBLZMA */ |
266 | ||
267 | return abfd; | |
268 | } |