binutils-gdb/gdb/minidebug.c
Tom Tromey 192b62ce0b Use class to manage BFD reference counts
This introduces a new specialization of gdb::ref_ptr that can be used
to manage BFD reference counts.  Then it changes most places in gdb to
use this new class, rather than explicit reference-counting or
cleanups.  This patch removes make_cleanup_bfd_unref.

If you look you will see a couple of spots using "release" where a use
of gdb_bfd_ref_ptr would be cleaner.  These will be fixed in the next
patch.

I think this patch fixes some latent bugs.  For example, it seems to
me that previously objfpy_add_separate_debug_file leaked a BFD.

I'm not 100% certain that the macho_symfile_read_all_oso change is
correct.  The existing code here is hard for me to follow.  One goal
of this sort of automated reference counting, though, is to make it
more difficult to make logic errors; so hopefully the code is clear
now.

2017-01-10  Tom Tromey  <tom@tromey.com>

	* windows-tdep.c (windows_xfer_shared_library): Update.
	* windows-nat.c (windows_make_so): Update.
	* utils.h (make_cleanup_bfd_unref): Remove.
	* utils.c (do_bfd_close_cleanup, make_cleanup_bfd_unref): Remove.
	* symfile.h (symfile_bfd_open)
	(find_separate_debug_file_in_section): Return gdb_bfd_ref_ptr.
	* symfile.c (read_symbols, symbol_file_add)
	(separate_debug_file_exists): Update.
	(symfile_bfd_open): Return gdb_bfd_ref_ptr.
	(generic_load, reread_symbols): Update.
	* symfile-mem.c (symbol_file_add_from_memory): Update.
	* spu-linux-nat.c (spu_bfd_open): Return gdb_bfd_ref_ptr.
	(spu_symbol_file_add_from_memory): Update.
	* solist.h (struct target_so_ops) <bfd_open>: Return
	gdb_bfd_ref_ptr.
	(solib_bfd_fopen, solib_bfd_open): Return gdb_bfd_ref_ptr.
	* solib.c (solib_bfd_fopen, solib_bfd_open): Return
	gdb_bfd_ref_ptr.
	(solib_map_sections, reload_shared_libraries_1): Update.
	* solib-svr4.c (enable_break): Update.
	* solib-spu.c (spu_bfd_fopen): Return gdb_bfd_ref_ptr.
	* solib-frv.c (enable_break2): Update.
	* solib-dsbt.c (enable_break): Update.
	* solib-darwin.c (gdb_bfd_mach_o_fat_extract): Return
	gdb_bfd_ref_ptr.
	(darwin_solib_get_all_image_info_addr_at_init): Update.
	(darwin_bfd_open): Return gdb_bfd_ref_ptr.
	* solib-aix.c (solib_aix_bfd_open): Return gdb_bfd_ref_ptr.
	* record-full.c (record_full_save): Update.
	* python/py-objfile.c (objfpy_add_separate_debug_file): Update.
	* procfs.c (insert_dbx_link_bpt_in_file): Update.
	* minidebug.c (find_separate_debug_file_in_section): Return
	gdb_bfd_ref_ptr.
	* machoread.c (macho_add_oso_symfile): Change abfd to
	gdb_bfd_ref_ptr.
	(macho_symfile_read_all_oso): Update.
	(macho_check_dsym): Return gdb_bfd_ref_ptr.
	(macho_symfile_read): Update.
	* jit.c (bfd_open_from_target_memory): Return gdb_bfd_ref_ptr.
	(jit_bfd_try_read_symtab): Update.
	* gdb_bfd.h (gdb_bfd_open, gdb_bfd_fopen, gdb_bfd_openr)
	(gdb_bfd_openw, gdb_bfd_openr_iovec)
	(gdb_bfd_openr_next_archived_file, gdb_bfd_fdopenr): Return
	gdb_bfd_ref_ptr.
	(gdb_bfd_ref_policy): New struct.
	(gdb_bfd_ref_ptr): New typedef.
	* gdb_bfd.c (gdb_bfd_open, gdb_bfd_fopen, gdb_bfd_openr)
	(gdb_bfd_openw, gdb_bfd_openr_iovec)
	(gdb_bfd_openr_next_archived_file, gdb_bfd_fdopenr): Return
	gdb_bfd_ref_ptr.
	* gcore.h (create_gcore_bfd): Return gdb_bfd_ref_ptr.
	* gcore.c (create_gcore_bfd): Return gdb_bfd_ref_ptr.
	(gcore_command): Update.
	* exec.c (exec_file_attach): Update.
	* elfread.c (elf_symfile_read): Update.
	* dwarf2read.c (dwarf2_get_dwz_file): Update.
	(try_open_dwop_file, open_dwo_file): Return gdb_bfd_ref_ptr.
	(open_and_init_dwo_file): Update.
	(open_dwp_file): Return gdb_bfd_ref_ptr.
	(open_and_init_dwp_file): Update.
	* corelow.c (core_open): Update.
	* compile/compile-object-load.c (compile_object_load): Update.
	* common/gdb_ref_ptr.h (ref_ptr::operator->): New operator.
	* coffread.c (coff_symfile_read): Update.
	* cli/cli-dump.c (bfd_openr_or_error, bfd_openw_or_error): Return
	gdb_bfd_ref_ptr.  Rename.
	(dump_bfd_file, restore_command): Update.
	* build-id.h (build_id_to_debug_bfd): Return gdb_bfd_ref_ptr.
	* build-id.c (build_id_to_debug_bfd): Return gdb_bfd_ref_ptr.
	(find_separate_debug_file_by_buildid): Update.
2017-01-10 19:14:10 -07:00

290 lines
8.1 KiB
C

/* Read MiniDebugInfo data from an objfile.
Copyright (C) 2012-2017 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;
int ret;
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
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.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;
}