| 1 | /* Read MiniDebugInfo data from an objfile. |
| 2 | |
| 3 | Copyright (C) 2012-2018 Free Software Foundation, Inc. |
| 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" |
| 22 | #include "symfile.h" |
| 23 | #include "objfiles.h" |
| 24 | #include "gdbcore.h" |
| 25 | #include <algorithm> |
| 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 | |
| 56 | struct gdb_lzma_stream |
| 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 | |
| 74 | Return 'struct gdb_lzma_stream *' must be freed by caller by xfree, |
| 75 | together with its INDEX lzma data. */ |
| 76 | |
| 77 | static void * |
| 78 | lzma_open (struct bfd *nbfd, void *open_closure) |
| 79 | { |
| 80 | asection *section = (asection *) open_closure; |
| 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 | uint64_t memlimit = UINT64_MAX; |
| 87 | struct gdb_lzma_stream *lstream; |
| 88 | size_t pos; |
| 89 | |
| 90 | size = bfd_get_section_size (section); |
| 91 | offset = section->filepos + size - LZMA_STREAM_HEADER_SIZE; |
| 92 | if (size < LZMA_STREAM_HEADER_SIZE |
| 93 | || bfd_seek (section->owner, offset, SEEK_SET) != 0 |
| 94 | || bfd_bread (footer, LZMA_STREAM_HEADER_SIZE, section->owner) |
| 95 | != LZMA_STREAM_HEADER_SIZE |
| 96 | || lzma_stream_footer_decode (&options, footer) != LZMA_OK |
| 97 | || offset < options.backward_size) |
| 98 | { |
| 99 | bfd_set_error (bfd_error_wrong_format); |
| 100 | return NULL; |
| 101 | } |
| 102 | |
| 103 | offset -= options.backward_size; |
| 104 | indexdata = (gdb_byte *) xmalloc (options.backward_size); |
| 105 | index = NULL; |
| 106 | pos = 0; |
| 107 | if (bfd_seek (section->owner, offset, SEEK_SET) != 0 |
| 108 | || bfd_bread (indexdata, options.backward_size, section->owner) |
| 109 | != options.backward_size |
| 110 | || lzma_index_buffer_decode (&index, &memlimit, &gdb_lzma_allocator, |
| 111 | indexdata, &pos, options.backward_size) |
| 112 | != LZMA_OK |
| 113 | || lzma_index_size (index) != options.backward_size) |
| 114 | { |
| 115 | xfree (indexdata); |
| 116 | bfd_set_error (bfd_error_wrong_format); |
| 117 | return NULL; |
| 118 | } |
| 119 | xfree (indexdata); |
| 120 | |
| 121 | lstream = XCNEW (struct gdb_lzma_stream); |
| 122 | lstream->section = section; |
| 123 | lstream->index = index; |
| 124 | |
| 125 | return lstream; |
| 126 | } |
| 127 | |
| 128 | /* bfd_openr_iovec PREAD_P implementation for |
| 129 | find_separate_debug_file_in_section. Passed STREAM |
| 130 | is 'struct gdb_lzma_stream *'. */ |
| 131 | |
| 132 | static file_ptr |
| 133 | lzma_pread (struct bfd *nbfd, void *stream, void *buf, file_ptr nbytes, |
| 134 | file_ptr offset) |
| 135 | { |
| 136 | struct gdb_lzma_stream *lstream = (struct gdb_lzma_stream *) stream; |
| 137 | bfd_size_type chunk_size; |
| 138 | lzma_index_iter iter; |
| 139 | gdb_byte *compressed, *uncompressed; |
| 140 | file_ptr block_offset; |
| 141 | lzma_filter filters[LZMA_FILTERS_MAX + 1]; |
| 142 | lzma_block block; |
| 143 | size_t compressed_pos, uncompressed_pos; |
| 144 | file_ptr res; |
| 145 | |
| 146 | res = 0; |
| 147 | while (nbytes > 0) |
| 148 | { |
| 149 | if (lstream->data == NULL |
| 150 | || lstream->data_start > offset || offset >= lstream->data_end) |
| 151 | { |
| 152 | asection *section = lstream->section; |
| 153 | |
| 154 | lzma_index_iter_init (&iter, lstream->index); |
| 155 | if (lzma_index_iter_locate (&iter, offset)) |
| 156 | break; |
| 157 | |
| 158 | compressed = (gdb_byte *) xmalloc (iter.block.total_size); |
| 159 | block_offset = section->filepos + iter.block.compressed_file_offset; |
| 160 | if (bfd_seek (section->owner, block_offset, SEEK_SET) != 0 |
| 161 | || bfd_bread (compressed, iter.block.total_size, section->owner) |
| 162 | != iter.block.total_size) |
| 163 | { |
| 164 | xfree (compressed); |
| 165 | break; |
| 166 | } |
| 167 | |
| 168 | uncompressed = (gdb_byte *) xmalloc (iter.block.uncompressed_size); |
| 169 | |
| 170 | memset (&block, 0, sizeof (block)); |
| 171 | block.filters = filters; |
| 172 | block.header_size = lzma_block_header_size_decode (compressed[0]); |
| 173 | if (lzma_block_header_decode (&block, &gdb_lzma_allocator, compressed) |
| 174 | != LZMA_OK) |
| 175 | { |
| 176 | xfree (compressed); |
| 177 | xfree (uncompressed); |
| 178 | break; |
| 179 | } |
| 180 | |
| 181 | compressed_pos = block.header_size; |
| 182 | uncompressed_pos = 0; |
| 183 | if (lzma_block_buffer_decode (&block, &gdb_lzma_allocator, |
| 184 | compressed, &compressed_pos, |
| 185 | iter.block.total_size, |
| 186 | uncompressed, &uncompressed_pos, |
| 187 | iter.block.uncompressed_size) |
| 188 | != LZMA_OK) |
| 189 | { |
| 190 | xfree (compressed); |
| 191 | xfree (uncompressed); |
| 192 | break; |
| 193 | } |
| 194 | |
| 195 | xfree (compressed); |
| 196 | |
| 197 | xfree (lstream->data); |
| 198 | lstream->data = uncompressed; |
| 199 | lstream->data_start = iter.block.uncompressed_file_offset; |
| 200 | lstream->data_end = (iter.block.uncompressed_file_offset |
| 201 | + iter.block.uncompressed_size); |
| 202 | } |
| 203 | |
| 204 | chunk_size = std::min (nbytes, (file_ptr) lstream->data_end - offset); |
| 205 | memcpy (buf, lstream->data + offset - lstream->data_start, chunk_size); |
| 206 | buf = (gdb_byte *) buf + chunk_size; |
| 207 | offset += chunk_size; |
| 208 | nbytes -= chunk_size; |
| 209 | res += chunk_size; |
| 210 | } |
| 211 | |
| 212 | return res; |
| 213 | } |
| 214 | |
| 215 | /* bfd_openr_iovec CLOSE_P implementation for |
| 216 | find_separate_debug_file_in_section. Passed STREAM |
| 217 | is 'struct gdb_lzma_stream *'. */ |
| 218 | |
| 219 | static int |
| 220 | lzma_close (struct bfd *nbfd, |
| 221 | void *stream) |
| 222 | { |
| 223 | struct gdb_lzma_stream *lstream = (struct gdb_lzma_stream *) stream; |
| 224 | |
| 225 | lzma_index_end (lstream->index, &gdb_lzma_allocator); |
| 226 | xfree (lstream->data); |
| 227 | xfree (lstream); |
| 228 | |
| 229 | /* Zero means success. */ |
| 230 | return 0; |
| 231 | } |
| 232 | |
| 233 | /* bfd_openr_iovec STAT_P implementation for |
| 234 | find_separate_debug_file_in_section. Passed STREAM |
| 235 | is 'struct gdb_lzma_stream *'. */ |
| 236 | |
| 237 | static int |
| 238 | lzma_stat (struct bfd *abfd, |
| 239 | void *stream, |
| 240 | struct stat *sb) |
| 241 | { |
| 242 | struct gdb_lzma_stream *lstream = (struct gdb_lzma_stream *) stream; |
| 243 | |
| 244 | memset (sb, 0, sizeof (struct stat)); |
| 245 | sb->st_size = lzma_index_uncompressed_size (lstream->index); |
| 246 | return 0; |
| 247 | } |
| 248 | |
| 249 | #endif /* HAVE_LIBLZMA */ |
| 250 | |
| 251 | /* This looks for a xz compressed separate debug info object file embedded |
| 252 | in a section called .gnu_debugdata. See |
| 253 | http://fedoraproject.org/wiki/Features/MiniDebugInfo |
| 254 | or the "Separate Debug Sections" of the manual for details. |
| 255 | If we find one we create a iovec based bfd that decompresses the |
| 256 | object data on demand. If we don't find one, return NULL. */ |
| 257 | |
| 258 | gdb_bfd_ref_ptr |
| 259 | find_separate_debug_file_in_section (struct objfile *objfile) |
| 260 | { |
| 261 | asection *section; |
| 262 | gdb_bfd_ref_ptr abfd; |
| 263 | |
| 264 | if (objfile->obfd == NULL) |
| 265 | return NULL; |
| 266 | |
| 267 | section = bfd_get_section_by_name (objfile->obfd, ".gnu_debugdata"); |
| 268 | if (section == NULL) |
| 269 | return NULL; |
| 270 | |
| 271 | #ifdef HAVE_LIBLZMA |
| 272 | abfd = gdb_bfd_openr_iovec (objfile_name (objfile), gnutarget, lzma_open, |
| 273 | section, lzma_pread, lzma_close, lzma_stat); |
| 274 | if (abfd == NULL) |
| 275 | return NULL; |
| 276 | |
| 277 | if (!bfd_check_format (abfd.get (), bfd_object)) |
| 278 | { |
| 279 | warning (_("Cannot parse .gnu_debugdata section; not a BFD object")); |
| 280 | return NULL; |
| 281 | } |
| 282 | #else |
| 283 | warning (_("Cannot parse .gnu_debugdata section; LZMA support was " |
| 284 | "disabled at compile time")); |
| 285 | #endif /* !HAVE_LIBLZMA */ |
| 286 | |
| 287 | return abfd; |
| 288 | } |