1 /* Read MiniDebugInfo data from an objfile. 2 3 Copyright (C) 2012-2024 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 "gdb_bfd.h" 21 #include "symfile.h" 22 #include "objfiles.h" 23 #include "gdbcore.h" 24 #include <algorithm> 25 26 #ifdef HAVE_LIBLZMA 27 28 /* We stash a reference to the .gnu_debugdata BFD on the enclosing 29 BFD. */ 30 static const registry<bfd>::key<gdb_bfd_ref_ptr> gnu_debug_key; 31 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 59 struct gdb_lzma_stream : public gdb_bfd_iovec_base 60 { 61 /* Section of input BFD from which we are decoding data. */ 62 asection *section = nullptr; 63 64 /* lzma library decompression state. */ 65 lzma_index *index = nullptr; 66 67 /* Currently decoded block. */ 68 bfd_size_type data_start = 0; 69 bfd_size_type data_end = 0; 70 gdb::byte_vector data; 71 72 73 ~gdb_lzma_stream () 74 { 75 lzma_index_end (index, &gdb_lzma_allocator); 76 } 77 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) 89 { 90 bfd_size_type size, offset; 91 lzma_stream_flags options; 92 gdb_byte footer[LZMA_STREAM_HEADER_SIZE]; 93 lzma_index *index; 94 uint64_t memlimit = UINT64_MAX; 95 struct gdb_lzma_stream *lstream; 96 size_t pos; 97 98 size = bfd_section_size (section); 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 102 || bfd_read (footer, LZMA_STREAM_HEADER_SIZE, section->owner) 103 != LZMA_STREAM_HEADER_SIZE 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; 112 gdb::byte_vector indexdata (options.backward_size); 113 index = NULL; 114 pos = 0; 115 if (bfd_seek (section->owner, offset, SEEK_SET) != 0 116 || bfd_read (indexdata.data (), options.backward_size, section->owner) 117 != options.backward_size 118 || lzma_index_buffer_decode (&index, &memlimit, &gdb_lzma_allocator, 119 indexdata.data (), &pos, 120 options.backward_size) 121 != LZMA_OK 122 || lzma_index_size (index) != options.backward_size) 123 { 124 bfd_set_error (bfd_error_wrong_format); 125 return NULL; 126 } 127 128 lstream = new struct gdb_lzma_stream; 129 lstream->section = section; 130 lstream->index = index; 131 132 return lstream; 133 } 134 135 /* bfd_openr_iovec read implementation for 136 find_separate_debug_file_in_section. */ 137 138 file_ptr 139 gdb_lzma_stream::read (struct bfd *nbfd, void *buf, file_ptr nbytes, 140 file_ptr offset) 141 { 142 bfd_size_type chunk_size; 143 lzma_index_iter iter; 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 (data.empty () || data_start > offset || offset >= data_end) 154 { 155 lzma_index_iter_init (&iter, index); 156 if (lzma_index_iter_locate (&iter, offset)) 157 break; 158 159 gdb::byte_vector compressed (iter.block.total_size); 160 block_offset = section->filepos + iter.block.compressed_file_offset; 161 if (bfd_seek (section->owner, block_offset, SEEK_SET) != 0 162 || bfd_read (compressed.data (), iter.block.total_size, 163 section->owner) != iter.block.total_size) 164 break; 165 166 gdb::byte_vector uncompressed (iter.block.uncompressed_size); 167 168 memset (&block, 0, sizeof (block)); 169 block.filters = filters; 170 block.header_size = lzma_block_header_size_decode (compressed[0]); 171 if (lzma_block_header_decode (&block, &gdb_lzma_allocator, 172 compressed.data ()) 173 != LZMA_OK) 174 break; 175 176 compressed_pos = block.header_size; 177 uncompressed_pos = 0; 178 if (lzma_block_buffer_decode (&block, &gdb_lzma_allocator, 179 compressed.data (), &compressed_pos, 180 iter.block.total_size, 181 uncompressed.data (), 182 &uncompressed_pos, 183 iter.block.uncompressed_size) 184 != LZMA_OK) 185 break; 186 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); 191 } 192 193 chunk_size = std::min (nbytes, (file_ptr) data_end - offset); 194 memcpy (buf, data.data () + offset - data_start, chunk_size); 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 204 /* bfd_openr_iovec stat implementation for 205 find_separate_debug_file_in_section. */ 206 207 int 208 gdb_lzma_stream::stat (struct bfd *abfd, struct stat *sb) 209 { 210 memset (sb, 0, sizeof (struct stat)); 211 sb->st_size = lzma_index_uncompressed_size (index); 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 224 gdb_bfd_ref_ptr 225 find_separate_debug_file_in_section (struct objfile *objfile) 226 { 227 asection *section; 228 gdb_bfd_ref_ptr abfd; 229 230 if (objfile->obfd == NULL) 231 return NULL; 232 233 section = bfd_get_section_by_name (objfile->obfd.get (), ".gnu_debugdata"); 234 if (section == NULL) 235 return NULL; 236 237 #ifdef HAVE_LIBLZMA 238 gdb_bfd_ref_ptr *shared = gnu_debug_key.get (objfile->obfd.get ()); 239 if (shared != nullptr) 240 return *shared; 241 242 std::string filename = string_printf (_(".gnu_debugdata for %s"), 243 objfile_name (objfile)); 244 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); 251 if (abfd == NULL) 252 return NULL; 253 254 if (!bfd_check_format (abfd.get (), bfd_object)) 255 { 256 warning (_("Cannot parse .gnu_debugdata section; not a BFD object")); 257 return NULL; 258 } 259 260 gnu_debug_key.emplace (objfile->obfd.get (), abfd); 261 262 #else 263 warning (_("Cannot parse .gnu_debugdata section; LZMA support was " 264 "disabled at compile time")); 265 #endif /* !HAVE_LIBLZMA */ 266 267 return abfd; 268 } 269