Index: gdb/minidebug.c |
diff --git a/gdb/minidebug.c b/gdb/minidebug.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..29612340d868962ca3ab36607256c14581d75349 |
--- /dev/null |
+++ b/gdb/minidebug.c |
@@ -0,0 +1,290 @@ |
+/* Read MiniDebugInfo data from an objfile. |
+ |
+ Copyright (C) 2012-2013 Free Software Foundation, Inc. |
+ |
+ This file is part of GDB. |
+ |
+ This program is free software; you can redistribute it and/or modify |
+ it under the terms of the GNU General Public License as published by |
+ the Free Software Foundation; either version 3 of the License, or |
+ (at your option) any later version. |
+ |
+ This program is distributed in the hope that it will be useful, |
+ but WITHOUT ANY WARRANTY; without even the implied warranty of |
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
+ GNU General Public License for more details. |
+ |
+ You should have received a copy of the GNU General Public License |
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
+ |
+#include "defs.h" |
+#include "gdb_bfd.h" |
+#include <string.h> |
+#include "symfile.h" |
+#include "objfiles.h" |
+#include "gdbcore.h" |
+ |
+#ifdef HAVE_LIBLZMA |
+ |
+#include <lzma.h> |
+ |
+/* Allocator function for LZMA. */ |
+ |
+static void * |
+alloc_lzma (void *opaque, size_t nmemb, size_t size) |
+{ |
+ return xmalloc (nmemb * size); |
+} |
+ |
+/* Free function for LZMA. */ |
+ |
+static void |
+free_lzma (void *opaque, void *ptr) |
+{ |
+ xfree (ptr); |
+} |
+ |
+/* The allocator object for LZMA. Note that 'gdb_lzma_allocator' |
+ cannot be const due to the lzma library function prototypes. */ |
+ |
+static lzma_allocator gdb_lzma_allocator = { alloc_lzma, free_lzma, NULL }; |
+ |
+/* Custom bfd_openr_iovec implementation to read compressed data from |
+ a section. This keeps only the last decompressed block in memory |
+ to allow larger data without using to much memory. */ |
+ |
+struct lzma_stream |
+{ |
+ /* Section of input BFD from which we are decoding data. */ |
+ asection *section; |
+ |
+ /* lzma library decompression state. */ |
+ lzma_index *index; |
+ |
+ /* Currently decoded block. */ |
+ bfd_size_type data_start; |
+ bfd_size_type data_end; |
+ gdb_byte *data; |
+}; |
+ |
+/* bfd_openr_iovec OPEN_P implementation for |
+ find_separate_debug_file_in_section. OPEN_CLOSURE is 'asection *' |
+ of the section to decompress. |
+ |
+ Return 'struct lzma_stream *' must be freed by caller by xfree, together |
+ with its INDEX lzma data. */ |
+ |
+static void * |
+lzma_open (struct bfd *nbfd, void *open_closure) |
+{ |
+ asection *section = open_closure; |
+ bfd_size_type size, offset; |
+ lzma_stream_flags options; |
+ gdb_byte footer[LZMA_STREAM_HEADER_SIZE]; |
+ gdb_byte *indexdata; |
+ lzma_index *index; |
+ int ret; |
+ uint64_t memlimit = UINT64_MAX; |
+ struct lzma_stream *lstream; |
+ size_t pos; |
+ |
+ size = bfd_get_section_size (section); |
+ offset = section->filepos + size - LZMA_STREAM_HEADER_SIZE; |
+ if (size < LZMA_STREAM_HEADER_SIZE |
+ || bfd_seek (section->owner, offset, SEEK_SET) != 0 |
+ || bfd_bread (footer, LZMA_STREAM_HEADER_SIZE, section->owner) |
+ != LZMA_STREAM_HEADER_SIZE |
+ || lzma_stream_footer_decode (&options, footer) != LZMA_OK |
+ || offset < options.backward_size) |
+ { |
+ bfd_set_error (bfd_error_wrong_format); |
+ return NULL; |
+ } |
+ |
+ offset -= options.backward_size; |
+ indexdata = xmalloc (options.backward_size); |
+ index = NULL; |
+ pos = 0; |
+ if (bfd_seek (section->owner, offset, SEEK_SET) != 0 |
+ || bfd_bread (indexdata, options.backward_size, section->owner) |
+ != options.backward_size |
+ || lzma_index_buffer_decode (&index, &memlimit, &gdb_lzma_allocator, |
+ indexdata, &pos, options.backward_size) |
+ != LZMA_OK |
+ || lzma_index_size (index) != options.backward_size) |
+ { |
+ xfree (indexdata); |
+ bfd_set_error (bfd_error_wrong_format); |
+ return NULL; |
+ } |
+ xfree (indexdata); |
+ |
+ lstream = xzalloc (sizeof (struct lzma_stream)); |
+ lstream->section = section; |
+ lstream->index = index; |
+ |
+ return lstream; |
+} |
+ |
+/* bfd_openr_iovec PREAD_P implementation for |
+ find_separate_debug_file_in_section. Passed STREAM |
+ is 'struct lzma_stream *'. */ |
+ |
+static file_ptr |
+lzma_pread (struct bfd *nbfd, void *stream, void *buf, file_ptr nbytes, |
+ file_ptr offset) |
+{ |
+ struct lzma_stream *lstream = stream; |
+ bfd_size_type chunk_size; |
+ lzma_index_iter iter; |
+ gdb_byte *compressed, *uncompressed; |
+ file_ptr block_offset; |
+ lzma_filter filters[LZMA_FILTERS_MAX + 1]; |
+ lzma_block block; |
+ size_t compressed_pos, uncompressed_pos; |
+ file_ptr res; |
+ |
+ res = 0; |
+ while (nbytes > 0) |
+ { |
+ if (lstream->data == NULL |
+ || lstream->data_start > offset || offset >= lstream->data_end) |
+ { |
+ asection *section = lstream->section; |
+ |
+ lzma_index_iter_init (&iter, lstream->index); |
+ if (lzma_index_iter_locate (&iter, offset)) |
+ break; |
+ |
+ compressed = xmalloc (iter.block.total_size); |
+ block_offset = section->filepos + iter.block.compressed_file_offset; |
+ if (bfd_seek (section->owner, block_offset, SEEK_SET) != 0 |
+ || bfd_bread (compressed, iter.block.total_size, section->owner) |
+ != iter.block.total_size) |
+ { |
+ xfree (compressed); |
+ break; |
+ } |
+ |
+ uncompressed = xmalloc (iter.block.uncompressed_size); |
+ |
+ memset (&block, 0, sizeof (block)); |
+ block.filters = filters; |
+ block.header_size = lzma_block_header_size_decode (compressed[0]); |
+ if (lzma_block_header_decode (&block, &gdb_lzma_allocator, compressed) |
+ != LZMA_OK) |
+ { |
+ xfree (compressed); |
+ xfree (uncompressed); |
+ break; |
+ } |
+ |
+ compressed_pos = block.header_size; |
+ uncompressed_pos = 0; |
+ if (lzma_block_buffer_decode (&block, &gdb_lzma_allocator, |
+ compressed, &compressed_pos, |
+ iter.block.total_size, |
+ uncompressed, &uncompressed_pos, |
+ iter.block.uncompressed_size) |
+ != LZMA_OK) |
+ { |
+ xfree (compressed); |
+ xfree (uncompressed); |
+ break; |
+ } |
+ |
+ xfree (compressed); |
+ |
+ xfree (lstream->data); |
+ lstream->data = uncompressed; |
+ lstream->data_start = iter.block.uncompressed_file_offset; |
+ lstream->data_end = (iter.block.uncompressed_file_offset |
+ + iter.block.uncompressed_size); |
+ } |
+ |
+ chunk_size = min (nbytes, lstream->data_end - offset); |
+ memcpy (buf, lstream->data + offset - lstream->data_start, chunk_size); |
+ buf = (gdb_byte *) buf + chunk_size; |
+ offset += chunk_size; |
+ nbytes -= chunk_size; |
+ res += chunk_size; |
+ } |
+ |
+ return res; |
+} |
+ |
+/* bfd_openr_iovec CLOSE_P implementation for |
+ find_separate_debug_file_in_section. Passed STREAM |
+ is 'struct lzma_stream *'. */ |
+ |
+static int |
+lzma_close (struct bfd *nbfd, |
+ void *stream) |
+{ |
+ struct lzma_stream *lstream = stream; |
+ |
+ lzma_index_end (lstream->index, &gdb_lzma_allocator); |
+ xfree (lstream->data); |
+ xfree (lstream); |
+ |
+ /* Zero means success. */ |
+ return 0; |
+} |
+ |
+/* bfd_openr_iovec STAT_P implementation for |
+ find_separate_debug_file_in_section. Passed STREAM |
+ is 'struct lzma_stream *'. */ |
+ |
+static int |
+lzma_stat (struct bfd *abfd, |
+ void *stream, |
+ struct stat *sb) |
+{ |
+ struct lzma_stream *lstream = stream; |
+ |
+ sb->st_size = lzma_index_uncompressed_size (lstream->index); |
+ return 0; |
+} |
+ |
+#endif /* HAVE_LIBLZMA */ |
+ |
+/* This looks for a xz compressed separate debug info object file embedded |
+ in a section called .gnu_debugdata. See |
+ http://fedoraproject.org/wiki/Features/MiniDebugInfo |
+ or the "Separate Debug Sections" of the manual for details. |
+ If we find one we create a iovec based bfd that decompresses the |
+ object data on demand. If we don't find one, return NULL. */ |
+ |
+bfd * |
+find_separate_debug_file_in_section (struct objfile *objfile) |
+{ |
+ asection *section; |
+ bfd *abfd; |
+ |
+ if (objfile->obfd == NULL) |
+ return NULL; |
+ |
+ section = bfd_get_section_by_name (objfile->obfd, ".gnu_debugdata"); |
+ if (section == NULL) |
+ return NULL; |
+ |
+#ifdef HAVE_LIBLZMA |
+ abfd = gdb_bfd_openr_iovec (objfile_name (objfile), gnutarget, lzma_open, |
+ section, lzma_pread, lzma_close, lzma_stat); |
+ if (abfd == NULL) |
+ return NULL; |
+ |
+ if (!bfd_check_format (abfd, bfd_object)) |
+ { |
+ warning (_("Cannot parse .gnu_debugdata section; not a BFD object")); |
+ gdb_bfd_unref (abfd); |
+ return NULL; |
+ } |
+#else |
+ warning (_("Cannot parse .gnu_debugdata section; LZMA support was " |
+ "disabled at compile time")); |
+ abfd = NULL; |
+#endif /* !HAVE_LIBLZMA */ |
+ |
+ return abfd; |
+} |