1/* Read MiniDebugInfo data from an objfile. 2 3 Copyright (C) 2012-2020 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/* We stash a reference to the .gnu_debugdata BFD on the enclosing 30 BFD. */ 31static const bfd_key<gdb_bfd_ref_ptr> gnu_debug_key; 32 33#include <lzma.h> 34 35/* Allocator function for LZMA. */ 36 37static void * 38alloc_lzma (void *opaque, size_t nmemb, size_t size) 39{ 40 return xmalloc (nmemb * size); 41} 42 43/* Free function for LZMA. */ 44 45static void 46free_lzma (void *opaque, void *ptr) 47{ 48 xfree (ptr); 49} 50 51/* The allocator object for LZMA. Note that 'gdb_lzma_allocator' 52 cannot be const due to the lzma library function prototypes. */ 53 54static lzma_allocator gdb_lzma_allocator = { alloc_lzma, free_lzma, NULL }; 55 56/* Custom bfd_openr_iovec implementation to read compressed data from 57 a section. This keeps only the last decompressed block in memory 58 to allow larger data without using to much memory. */ 59 60struct gdb_lzma_stream 61{ 62 /* Section of input BFD from which we are decoding data. */ 63 asection *section; 64 65 /* lzma library decompression state. */ 66 lzma_index *index; 67 68 /* Currently decoded block. */ 69 bfd_size_type data_start; 70 bfd_size_type data_end; 71 gdb_byte *data; 72}; 73 74/* bfd_openr_iovec OPEN_P implementation for 75 find_separate_debug_file_in_section. OPEN_CLOSURE is 'asection *' 76 of the section to decompress. 77 78 Return 'struct gdb_lzma_stream *' must be freed by caller by xfree, 79 together with its INDEX lzma data. */ 80 81static void * 82lzma_open (struct bfd *nbfd, void *open_closure) 83{ 84 asection *section = (asection *) open_closure; 85 bfd_size_type size, offset; 86 lzma_stream_flags options; 87 gdb_byte footer[LZMA_STREAM_HEADER_SIZE]; 88 gdb_byte *indexdata; 89 lzma_index *index; 90 uint64_t memlimit = UINT64_MAX; 91 struct gdb_lzma_stream *lstream; 92 size_t pos; 93 94 size = bfd_section_size (section); 95 offset = section->filepos + size - LZMA_STREAM_HEADER_SIZE; 96 if (size < LZMA_STREAM_HEADER_SIZE 97 || bfd_seek (section->owner, offset, SEEK_SET) != 0 98 || bfd_bread (footer, LZMA_STREAM_HEADER_SIZE, section->owner) 99 != LZMA_STREAM_HEADER_SIZE 100 || lzma_stream_footer_decode (&options, footer) != LZMA_OK 101 || offset < options.backward_size) 102 { 103 bfd_set_error (bfd_error_wrong_format); 104 return NULL; 105 } 106 107 offset -= options.backward_size; 108 indexdata = (gdb_byte *) xmalloc (options.backward_size); 109 index = NULL; 110 pos = 0; 111 if (bfd_seek (section->owner, offset, SEEK_SET) != 0 112 || bfd_bread (indexdata, options.backward_size, section->owner) 113 != options.backward_size 114 || lzma_index_buffer_decode (&index, &memlimit, &gdb_lzma_allocator, 115 indexdata, &pos, options.backward_size) 116 != LZMA_OK 117 || lzma_index_size (index) != options.backward_size) 118 { 119 xfree (indexdata); 120 bfd_set_error (bfd_error_wrong_format); 121 return NULL; 122 } 123 xfree (indexdata); 124 125 lstream = XCNEW (struct gdb_lzma_stream); 126 lstream->section = section; 127 lstream->index = index; 128 129 return lstream; 130} 131 132/* bfd_openr_iovec PREAD_P implementation for 133 find_separate_debug_file_in_section. Passed STREAM 134 is 'struct gdb_lzma_stream *'. */ 135 136static file_ptr 137lzma_pread (struct bfd *nbfd, void *stream, void *buf, file_ptr nbytes, 138 file_ptr offset) 139{ 140 struct gdb_lzma_stream *lstream = (struct gdb_lzma_stream *) stream; 141 bfd_size_type chunk_size; 142 lzma_index_iter iter; 143 gdb_byte *compressed, *uncompressed; 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 { 153 if (lstream->data == NULL 154 || lstream->data_start > offset || offset >= lstream->data_end) 155 { 156 asection *section = lstream->section; 157 158 lzma_index_iter_init (&iter, lstream->index); 159 if (lzma_index_iter_locate (&iter, offset)) 160 break; 161 162 compressed = (gdb_byte *) xmalloc (iter.block.total_size); 163 block_offset = section->filepos + iter.block.compressed_file_offset; 164 if (bfd_seek (section->owner, block_offset, SEEK_SET) != 0 165 || bfd_bread (compressed, iter.block.total_size, section->owner) 166 != iter.block.total_size) 167 { 168 xfree (compressed); 169 break; 170 } 171 172 uncompressed = (gdb_byte *) xmalloc (iter.block.uncompressed_size); 173 174 memset (&block, 0, sizeof (block)); 175 block.filters = filters; 176 block.header_size = lzma_block_header_size_decode (compressed[0]); 177 if (lzma_block_header_decode (&block, &gdb_lzma_allocator, compressed) 178 != LZMA_OK) 179 { 180 xfree (compressed); 181 xfree (uncompressed); 182 break; 183 } 184 185 compressed_pos = block.header_size; 186 uncompressed_pos = 0; 187 if (lzma_block_buffer_decode (&block, &gdb_lzma_allocator, 188 compressed, &compressed_pos, 189 iter.block.total_size, 190 uncompressed, &uncompressed_pos, 191 iter.block.uncompressed_size) 192 != LZMA_OK) 193 { 194 xfree (compressed); 195 xfree (uncompressed); 196 break; 197 } 198 199 xfree (compressed); 200 201 xfree (lstream->data); 202 lstream->data = uncompressed; 203 lstream->data_start = iter.block.uncompressed_file_offset; 204 lstream->data_end = (iter.block.uncompressed_file_offset 205 + iter.block.uncompressed_size); 206 } 207 208 chunk_size = std::min (nbytes, (file_ptr) lstream->data_end - offset); 209 memcpy (buf, lstream->data + offset - lstream->data_start, chunk_size); 210 buf = (gdb_byte *) buf + chunk_size; 211 offset += chunk_size; 212 nbytes -= chunk_size; 213 res += chunk_size; 214 } 215 216 return res; 217} 218 219/* bfd_openr_iovec CLOSE_P implementation for 220 find_separate_debug_file_in_section. Passed STREAM 221 is 'struct gdb_lzma_stream *'. */ 222 223static int 224lzma_close (struct bfd *nbfd, 225 void *stream) 226{ 227 struct gdb_lzma_stream *lstream = (struct gdb_lzma_stream *) stream; 228 229 lzma_index_end (lstream->index, &gdb_lzma_allocator); 230 xfree (lstream->data); 231 xfree (lstream); 232 233 /* Zero means success. */ 234 return 0; 235} 236 237/* bfd_openr_iovec STAT_P implementation for 238 find_separate_debug_file_in_section. Passed STREAM 239 is 'struct gdb_lzma_stream *'. */ 240 241static int 242lzma_stat (struct bfd *abfd, 243 void *stream, 244 struct stat *sb) 245{ 246 struct gdb_lzma_stream *lstream = (struct gdb_lzma_stream *) stream; 247 248 memset (sb, 0, sizeof (struct stat)); 249 sb->st_size = lzma_index_uncompressed_size (lstream->index); 250 return 0; 251} 252 253#endif /* HAVE_LIBLZMA */ 254 255/* This looks for a xz compressed separate debug info object file embedded 256 in a section called .gnu_debugdata. See 257 http://fedoraproject.org/wiki/Features/MiniDebugInfo 258 or the "Separate Debug Sections" of the manual for details. 259 If we find one we create a iovec based bfd that decompresses the 260 object data on demand. If we don't find one, return NULL. */ 261 262gdb_bfd_ref_ptr 263find_separate_debug_file_in_section (struct objfile *objfile) 264{ 265 asection *section; 266 gdb_bfd_ref_ptr abfd; 267 268 if (objfile->obfd == NULL) 269 return NULL; 270 271 section = bfd_get_section_by_name (objfile->obfd, ".gnu_debugdata"); 272 if (section == NULL) 273 return NULL; 274 275#ifdef HAVE_LIBLZMA 276 gdb_bfd_ref_ptr *shared = gnu_debug_key.get (objfile->obfd); 277 if (shared != nullptr) 278 return *shared; 279 280 std::string filename = string_printf (_(".gnu_debugdata for %s"), 281 objfile_name (objfile)); 282 283 abfd = gdb_bfd_openr_iovec (filename.c_str (), gnutarget, lzma_open, 284 section, lzma_pread, lzma_close, lzma_stat); 285 if (abfd == NULL) 286 return NULL; 287 288 if (!bfd_check_format (abfd.get (), bfd_object)) 289 { 290 warning (_("Cannot parse .gnu_debugdata section; not a BFD object")); 291 return NULL; 292 } 293 294 gnu_debug_key.emplace (objfile->obfd, abfd); 295 296#else 297 warning (_("Cannot parse .gnu_debugdata section; LZMA support was " 298 "disabled at compile time")); 299#endif /* !HAVE_LIBLZMA */ 300 301 return abfd; 302} 303