921222e2e8
One part of PR cli/19551 is that the mini debug info objfile reuses the name of the main objfile from which it comes. This can be seen because gdb claims to be reading symbols from the same file two times, like: Reading symbols from /bin/gdb...Reading symbols from /bin/gdb...(no debugging symbols found)...done. I think this would be less confusing if the minidebug objfile were given a different name. That is what this patch implements. It also arranges for the minidebug objfile to be marked OBJF_NOT_FILENAME. After this patch the output looks like: Reading symbols from /bin/gdb...Reading symbols from .gnu_debugdata for /usr/libexec/gdb...(no debugging symbols found)...done. Tested by the buildbot. gdb/ChangeLog 2018-05-16 Tom Tromey <tom@tromey.com> PR cli/19551: * symfile-add-flags.h (enum symfile_add_flags) <SYMFILE_NOT_FILENAME>: New constant. * symfile.c (read_symbols): Use SYMFILE_NOT_FILENAME. Get objfile name from BFD. (symbol_file_add_with_addrs): Check SYMFILE_NOT_FILENAME. * minidebug.c (find_separate_debug_file_in_section): Put ".gnu_debugdata" into BFD's file name.
292 lines
8.1 KiB
C
292 lines
8.1 KiB
C
/* Read MiniDebugInfo data from an objfile.
|
|
|
|
Copyright (C) 2012-2018 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 "symfile.h"
|
|
#include "objfiles.h"
|
|
#include "gdbcore.h"
|
|
#include <algorithm>
|
|
|
|
#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 gdb_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 gdb_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 = (asection *) open_closure;
|
|
bfd_size_type size, offset;
|
|
lzma_stream_flags options;
|
|
gdb_byte footer[LZMA_STREAM_HEADER_SIZE];
|
|
gdb_byte *indexdata;
|
|
lzma_index *index;
|
|
uint64_t memlimit = UINT64_MAX;
|
|
struct gdb_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 = (gdb_byte *) 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 = XCNEW (struct gdb_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 gdb_lzma_stream *'. */
|
|
|
|
static file_ptr
|
|
lzma_pread (struct bfd *nbfd, void *stream, void *buf, file_ptr nbytes,
|
|
file_ptr offset)
|
|
{
|
|
struct gdb_lzma_stream *lstream = (struct gdb_lzma_stream *) 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 = (gdb_byte *) 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 = (gdb_byte *) 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 = std::min (nbytes, (file_ptr) 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 gdb_lzma_stream *'. */
|
|
|
|
static int
|
|
lzma_close (struct bfd *nbfd,
|
|
void *stream)
|
|
{
|
|
struct gdb_lzma_stream *lstream = (struct gdb_lzma_stream *) 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 gdb_lzma_stream *'. */
|
|
|
|
static int
|
|
lzma_stat (struct bfd *abfd,
|
|
void *stream,
|
|
struct stat *sb)
|
|
{
|
|
struct gdb_lzma_stream *lstream = (struct gdb_lzma_stream *) stream;
|
|
|
|
memset (sb, 0, sizeof (struct stat));
|
|
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. */
|
|
|
|
gdb_bfd_ref_ptr
|
|
find_separate_debug_file_in_section (struct objfile *objfile)
|
|
{
|
|
asection *section;
|
|
gdb_bfd_ref_ptr 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
|
|
std::string filename = string_printf (_(".gnu_debugdata for %s"),
|
|
objfile_name (objfile));
|
|
|
|
abfd = gdb_bfd_openr_iovec (filename.c_str (), gnutarget, lzma_open,
|
|
section, lzma_pread, lzma_close, lzma_stat);
|
|
if (abfd == NULL)
|
|
return NULL;
|
|
|
|
if (!bfd_check_format (abfd.get (), bfd_object))
|
|
{
|
|
warning (_("Cannot parse .gnu_debugdata section; not a BFD object"));
|
|
return NULL;
|
|
}
|
|
#else
|
|
warning (_("Cannot parse .gnu_debugdata section; LZMA support was "
|
|
"disabled at compile time"));
|
|
#endif /* !HAVE_LIBLZMA */
|
|
|
|
return abfd;
|
|
}
|