[v2,10/19] libctf: ELF file opening via BFD

Message ID 20190517221002.408822-11-nick.alcock@oracle.com
State New
Headers show
Series
  • libctf, and CTF support for objdump and readelf
Related show

Commit Message

Nick Alcock May 17, 2019, 10:09 p.m.
These functions let you open an ELF file with a customarily-named CTF
section in it, automatically opening the CTF file and associating the
symbol and string tables in the ELF file with the CTF container, so that
you can look up the types of symbols in the ELF file via
ctf_lookup_by_symbol(), and so that strings can be shared between the
ELF file and CTF container, to save space.

It uses BFD machinery to do so.  I have no idea if I'm using this
machinery right, and it hasn't been thoroughly tested yet, but it is
better than the manual thrashing that was there before.

We use a forward declaration for the struct bfd in ctf-api.h, so that
ctf-api.h users are not required to pull in <bfd.h>.  (This is mostly
for the sake of readelf.)

These functions have not been tested since their CTF rewrite: this will
happen soon, once we have enough linker machinery together to build ELF files
with all the necessary sections in at once.

Changes from v1:
 - Correct erroneous license (GPLv2+ -> v3+) and reset copyright years.
 - Move out of ctf_lib.c; functions now based on BFD, and located in
   ctf-open-bfd.c.
 - New ctf_bfdopen() to do the low-level opening given a bfd.
 - New ctf_bfdopen_ctfsect() to do the low-level opening given a bfd and
   a separately-specified CTF section (will later be used to open
   archives using a bfd).

libctf/
	* ctf-open-bfd.c: New file.
	* ctf-impl.h: Include bfd.h.
	(ctf_file): New members ctf_abfd, ctf_data_alloced,
	ctf_data_mmapped, ctf_data_mmapped_len, ctf_symtab_alloced,
	ctf_strtab_alloced, ctf_bfd_close.
	(ctf_bfdopen_ctfsect): New declaration.
	(_CTF_SECTION): likewise.

include/
	* ctf-api.h (struct bfd): New forward.
	(ctf_fdopen): New.
	(ctf_bfdopen): Likewise.
	(ctf_open): Likewise.
---
 include/ctf-api.h     |   8 ++
 libctf/ctf-impl.h     |  11 ++
 libctf/ctf-open-bfd.c | 264 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 283 insertions(+)
 create mode 100644 libctf/ctf-open-bfd.c

-- 
2.21.0.237.gd0cfaa883d

Patch

diff --git a/include/ctf-api.h b/include/ctf-api.h
index c5eb9c676b..4c532f2867 100644
--- a/include/ctf-api.h
+++ b/include/ctf-api.h
@@ -45,6 +45,11 @@  typedef struct ctf_file ctf_file_t;
 typedef struct ctf_archive ctf_archive_t;
 typedef long ctf_id_t;
 
+/* This opaque definition allows libctf to accept BFD data structures without
+   importing all the BFD noise into users' namespaces.  */
+
+struct bfd;
+
 /* If the debugger needs to provide the CTF library with a set of raw buffers
    for use as the CTF data, symbol table, and string table, it can do so by
    filling in ctf_sect_t structures and passing them to ctf_bufopen().
@@ -200,8 +205,11 @@  enum
 
 extern ctf_file_t *ctf_simple_open (const char *, size_t, const char *, size_t,
 				   size_t, const char *, size_t, int *);
+extern ctf_file_t *ctf_bfdopen (struct bfd *, int *);
 extern ctf_file_t *ctf_bufopen (const ctf_sect_t *, const ctf_sect_t *,
 				const ctf_sect_t *, int *);
+extern ctf_file_t *ctf_fdopen (int, const char *, int *);
+extern ctf_file_t *ctf_open (const char *, int *);
 extern ctf_file_t *ctf_create (int *);
 extern void ctf_close (ctf_file_t *);
 extern ctf_sect_t ctf_getdatasect (const ctf_file_t *);
diff --git a/libctf/ctf-impl.h b/libctf/ctf-impl.h
index 53d062745a..3944cfeaee 100644
--- a/libctf/ctf-impl.h
+++ b/libctf/ctf-impl.h
@@ -31,6 +31,7 @@ 
 #include <limits.h>
 #include <ctype.h>
 #include <elf.h>
+#include <bfd.h>
 
 #ifdef	__cplusplus
 extern "C"
@@ -185,9 +186,15 @@  typedef struct ctf_bundle
 struct ctf_file
 {
   const ctf_fileops_t *ctf_fileops; /* Version-specific file operations.  */
+  bfd *ctf_abfd;		    /* Optional source of section data.  */
   ctf_sect_t ctf_data;		    /* CTF data from object file.  */
   ctf_sect_t ctf_symtab;	    /* Symbol table from object file.  */
   ctf_sect_t ctf_strtab;	    /* String table from object file.  */
+  void *ctf_data_alloced;	    /* CTF data we allocated, to free later.  */
+  void *ctf_data_mmapped;	    /* CTF data we mmapped, to free later.  */
+  size_t ctf_data_mmapped_len;	    /* Length of CTF data we mmapped.  */
+  void *ctf_symtab_alloced;	    /* symtab data we allocated.  */
+  void *ctf_strtab_alloced;	    /* symtab data we allocated.  */
   ctf_hash_t *ctf_structs;	    /* Hash table of struct types.  */
   ctf_hash_t *ctf_unions;	    /* Hash table of union types.  */
   ctf_hash_t *ctf_enums;	    /* Hash table of enum types.  */
@@ -226,6 +233,7 @@  struct ctf_file
   unsigned long ctf_snapshot_lu;  /* ctf_snapshot() call count at last update.  */
   char *ctf_tmp_typeslice;	  /* Storage for slicing up type names.  */
   size_t ctf_tmp_typeslicelen;	  /* Size of the typeslice.  */
+  void (*ctf_bfd_close) (struct ctf_file *); /* Frees BFD bits on close.  */
   void *ctf_specific;		  /* Data for ctf_get/setspecific().  */
 };
 
@@ -311,6 +319,8 @@  extern const char *ctf_strptr (ctf_file_t *, uint32_t);
 
 extern ctf_file_t *ctf_set_open_errno (int *, int);
 extern long ctf_set_errno (ctf_file_t *, int);
+extern ctf_file_t *ctf_bfdopen_ctfsect (struct bfd *, const ctf_sect_t *,
+					int *);
 
 _libctf_malloc_
 extern void *ctf_data_alloc (size_t);
@@ -342,6 +352,7 @@  extern Elf64_Sym *ctf_sym_to_elf64 (const Elf32_Sym *src, Elf64_Sym *dst);
 
 /* Variables, all underscore-prepended. */
 
+extern const char _CTF_SECTION[];	/* name of CTF ELF section */
 extern const char _CTF_NULLSTR[];	/* empty string */
 
 extern int _libctf_debug;	/* debugging messages enabled */
diff --git a/libctf/ctf-open-bfd.c b/libctf/ctf-open-bfd.c
new file mode 100644
index 0000000000..8dee8a35fa
--- /dev/null
+++ b/libctf/ctf-open-bfd.c
@@ -0,0 +1,264 @@ 
+/* Opening CTF files with BFD.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+
+   This file is part of libctf.
+
+   libctf 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, 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; see the file COPYING.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <ctf-impl.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <elf.h>
+#include <bfd.h>
+
+#include "elf-bfd.h"
+
+/* Free the BFD bits of a CTF file on ctf_close().  */
+static void
+ctf_bfdclose (ctf_file_t *fp)
+{
+  if (fp->ctf_abfd != NULL)
+    if (!bfd_close_all_done (fp->ctf_abfd))
+      ctf_dprintf ("Cannot close BFD: %s\n", bfd_errmsg (bfd_get_error()));
+}
+
+/* Open a CTF file given the specified BFD.  */
+
+ctf_file_t *
+ctf_bfdopen (struct bfd *abfd, int *errp)
+{
+  ctf_file_t *fp;
+  asection *ctf_asect;
+  bfd_byte *contents;
+  ctf_sect_t ctfsect;
+
+  libctf_init_debug();
+
+  if ((ctf_asect = bfd_get_section_by_name (abfd, _CTF_SECTION)) == NULL)
+    {
+      return (ctf_set_open_errno (errp, ECTF_NOCTFDATA));
+    }
+
+  if (!bfd_malloc_and_get_section (abfd, ctf_asect, &contents))
+    {
+      ctf_dprintf ("ctf_bfdopen(): cannot malloc CTF section: %s\n",
+		   bfd_errmsg (bfd_get_error()));
+      return (ctf_set_open_errno (errp, ECTF_FMT));
+    }
+
+  ctfsect.cts_name = _CTF_SECTION;
+  ctfsect.cts_type = SHT_PROGBITS;
+  ctfsect.cts_flags = 0;
+  ctfsect.cts_entsize = 1;
+  ctfsect.cts_offset = 0;
+  ctfsect.cts_size = bfd_section_size (abfd, ctf_asect);
+  ctfsect.cts_data = contents;
+
+  if ((fp = ctf_bfdopen_ctfsect (abfd, &ctfsect, errp)) != NULL)
+    {
+      fp->ctf_data_alloced = (void *) ctfsect.cts_data;
+      return fp;
+    }
+
+  free (contents);
+  return NULL;					/* errno is set for us.  */
+}
+
+/* Open a CTF file given the specified BFD and CTF section.  */
+
+ctf_file_t *
+ctf_bfdopen_ctfsect (struct bfd *abfd, const ctf_sect_t *ctfsect, int *errp)
+{
+  ctf_file_t *fp;
+  ctf_sect_t *symsectp = NULL;
+  ctf_sect_t *strsectp = NULL;
+  const char *bfderrstr = NULL;
+
+  asection *sym_asect;
+  ctf_sect_t symsect, strsect;
+  /* TODO: handle SYMTAB_SHNDX.  */
+
+  if ((sym_asect = bfd_section_from_elf_index (abfd,
+					       elf_onesymtab (abfd))) != NULL)
+    {
+      Elf_Internal_Shdr *symhdr = &elf_symtab_hdr (abfd);
+      asection *str_asect = NULL;
+      bfd_byte *contents;
+
+      if (symhdr->sh_link != SHN_UNDEF &&
+	  symhdr->sh_link <= elf_numsections (abfd))
+	str_asect = bfd_section_from_elf_index (abfd, symhdr->sh_link);
+
+      Elf_Internal_Shdr *strhdr = elf_elfsections (abfd)[symhdr->sh_link];
+
+      if (sym_asect && str_asect)
+	{
+	  if (!bfd_malloc_and_get_section (abfd, str_asect, &contents))
+	    {
+	      bfderrstr = "Cannot malloc string table";
+	      free (contents);
+	      goto err;
+	    }
+	  strsect.cts_data = contents;
+	  strsect.cts_name = (char *) strsect.cts_data + strhdr->sh_name;
+	  strsect.cts_type = strhdr->sh_type;
+	  strsect.cts_flags = strhdr->sh_flags;
+	  strsect.cts_entsize = strhdr->sh_size;
+	  strsect.cts_offset = strhdr->sh_offset;
+	  strsectp = &strsect;
+
+	  if (!bfd_malloc_and_get_section (abfd, sym_asect, &contents))
+	    {
+	      bfderrstr = "Cannot malloc symbol table";
+	      free (contents);
+	      goto err_free_str;
+	    }
+
+	  symsect.cts_name = (char *) strsect.cts_data + symhdr->sh_name;
+	  symsect.cts_type = symhdr->sh_type;
+	  symsect.cts_flags = symhdr->sh_flags;
+	  symsect.cts_entsize = symhdr->sh_size;
+	  symsect.cts_data = contents;
+	  symsect.cts_offset = symhdr->sh_offset;
+	  symsectp = &symsect;
+	}
+    }
+
+  if ((fp = ctf_bufopen (ctfsect, symsectp, strsectp, errp)) != NULL)
+    {
+      if (symsectp)
+	fp->ctf_symtab_alloced = (void *) symsectp->cts_data;
+      if (strsectp)
+	fp->ctf_strtab_alloced = (void *) strsectp->cts_data;
+      fp->ctf_bfd_close = ctf_bfdclose;
+      return fp;
+    }
+  ctf_dprintf ("ctf_internal_open(): cannot open CTF: %s\n",
+	       ctf_errmsg (*errp));
+
+err_free_str:
+  free ((void *) strsect.cts_data);
+err: _libctf_unused_;
+  if (bfderrstr)
+    {
+      ctf_dprintf ("ctf_bfdopen(): %s: %s\n", bfderrstr,
+		   bfd_errmsg (bfd_get_error()));
+      ctf_set_open_errno (errp, ECTF_FMT);
+    }
+  return NULL;
+}
+
+
+/* Open the specified file descriptor and return a pointer to a CTF container.
+   The file can be either an ELF file or raw CTF file.  The caller is
+   responsible for closing the file descriptor when it is no longer needed.
+
+   TODO: handle CTF archives too.  */
+
+ctf_file_t *
+ctf_fdopen (int fd, const char *filename, int *errp)
+{
+  ctf_file_t *fp = NULL;
+  bfd *abfd;
+  int nfd;
+
+  struct stat st;
+  ssize_t nbytes;
+
+  ctf_preamble_t ctfhdr;
+
+  memset (&ctfhdr, 0, sizeof (ctfhdr));
+
+  libctf_init_debug();
+
+  if (fstat (fd, &st) == -1)
+    return (ctf_set_open_errno (errp, errno));
+
+  if ((nbytes = ctf_pread (fd, &ctfhdr, sizeof (ctfhdr), 0)) <= 0)
+    return (ctf_set_open_errno (errp, nbytes < 0 ? errno : ECTF_FMT));
+
+  /* If we have read enough bytes to form a CTF header and the magic
+     string matches, attempt to interpret the file as raw CTF.  */
+
+  if ((size_t) nbytes >= sizeof (ctf_preamble_t) &&
+      ctfhdr.ctp_magic == CTF_MAGIC)
+    {
+      void *data;
+
+      if (ctfhdr.ctp_version > CTF_VERSION)
+	return (ctf_set_open_errno (errp, ECTF_CTFVERS));
+
+      if ((data = ctf_mmap (st.st_size, 0, fd)) == NULL)
+	return (ctf_set_open_errno (errp, errno));
+
+      if ((fp = ctf_simple_open (data, (size_t) st.st_size, NULL, 0, 0,
+				 NULL, 0, errp)) == NULL)
+	ctf_munmap (data, (size_t) st.st_size);
+      fp->ctf_data_mmapped = data;
+      fp->ctf_data_mmapped_len = (size_t) st.st_size;
+
+      return fp;
+    }
+
+  /* Attempt to open the file with BFD.  We must dup the fd first, since bfd
+     takes ownership of the passed fd.  */
+
+  if ((nfd = dup (fd)) < 0)
+      return (ctf_set_open_errno (errp, errno));
+
+  if ((abfd = bfd_fdopenr (filename, NULL, nfd)) == NULL)
+    {
+      ctf_dprintf ("Cannot open BFD from %s: %s\n",
+		   filename ? filename : "(unknown file)",
+		   bfd_errmsg (bfd_get_error()));
+      return (ctf_set_open_errno (errp, ECTF_FMT));
+    }
+
+  if ((fp = ctf_bfdopen (abfd, errp)) == NULL)
+    {
+      if (!bfd_close_all_done (abfd))
+	ctf_dprintf ("Cannot close BFD: %s\n", bfd_errmsg (bfd_get_error()));
+      return NULL;			/* errno is set for us.  */
+    }
+  fp->ctf_abfd = abfd;
+
+  return fp;
+}
+
+/* Open the specified file and return a pointer to a CTF container.  The file
+   can be either an ELF file or raw CTF file.  This is just a convenient
+   wrapper around ctf_fdopen() for callers.  */
+
+ctf_file_t *
+ctf_open (const char *filename, int *errp)
+{
+  ctf_file_t *fp;
+  int fd;
+
+  if ((fd = open (filename, O_RDONLY)) == -1)
+    {
+      if (errp != NULL)
+	*errp = errno;
+      return NULL;
+    }
+
+  fp = ctf_fdopen (fd, filename, errp);
+  (void) close (fd);
+  return fp;
+}