[PATCH] bfd function for chasing .gnu_debuglink

graydon hoare graydon@redhat.com
Wed Jan 29 00:47:00 GMT 2003


Hi,

A change recently entered GDB to support "separate debug info". This
permits shipment and installation of smaller, stripped binaries, with
secondary packages (on secondary media) to support users who care about
debugging symbols. This trick is accomplished by embedding a filename
and checksum in a special ".gnu_debuglink" section of an object, which
specifies another object containing *only* the debug symbols for the
first. GDB then merges these symbols, as though they were present in the
initial object. 

It has become clear recently that GDB is not the only program which
would benefit from these separate packages; notably objdump and the
oprofile post-processing tools require access to them. Rather than
duplicate the same section-extracting, path-searching and
checksum-matching code in these tools, it was suggested that we
centralize the feature in binutils itself. 

Attached it a patch to BFD which adds such a function. The comments give
a pretty clear overview of its use. Configury also needs to be rebuilt,
of course.

Any BFD maintainers interested in pulling this in?

-graydon

-------------- next part --------------
--- binutils-2.13.90.0.4/bfd/Makefile.am.debuglink	2003-01-28 14:22:49.000000000 -0500
+++ binutils-2.13.90.0.4/bfd/Makefile.am	2003-01-28 14:27:37.000000000 -0500
@@ -865,7 +865,8 @@
 format.lo: format.c $(INCDIR)/filenames.h
 init.lo: init.c $(INCDIR)/filenames.h
 libbfd.lo: libbfd.c $(INCDIR)/filenames.h
-opncls.lo: opncls.c $(INCDIR)/filenames.h $(INCDIR)/objalloc.h
+opncls.lo: opncls.c $(INCDIR)/filenames.h $(INCDIR)/libiberty.h \
+  $(INCDIR)/objalloc.h
 reloc.lo: reloc.c $(INCDIR)/filenames.h $(INCDIR)/bfdlink.h
 section.lo: section.c $(INCDIR)/filenames.h $(INCDIR)/bfdlink.h
 syms.lo: syms.c $(INCDIR)/filenames.h $(INCDIR)/safe-ctype.h \
--- binutils-2.13.90.0.4/bfd/configure.in.debuglink~	2002-08-14 13:35:11.000000000 -0400
+++ binutils-2.13.90.0.4/bfd/configure.in	2003-01-24 19:31:23.000000000 -0500
@@ -869,6 +869,33 @@
   true+yes )  AC_DEFINE(USE_MMAP, 1, [Use mmap if it's available?]) ;;
 esac
 
+AH_TEMPLATE([DEBUGDIR],
+            [Define a default directory for separate 
+	    debugging information files])
+
+dnl written by Guido Draheim <guidod@gmx.de>, original by Alexandre Oliva 
+dnl Version 1.3 (2001/03/02)
+dnl source http://www.gnu.org/software/ac-archive/Miscellaneous/ac_define_dir.html
+
+AC_DEFUN([AC_DEFINE_DIR], [
+  test "x$prefix" = xNONE && prefix="$ac_default_prefix"
+  test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+  ac_define_dir=`eval echo [$]$2`
+  ac_define_dir=`eval echo [$]ac_define_dir`
+  ifelse($3, ,
+    AC_DEFINE_UNQUOTED($1, "$ac_define_dir"),
+    AC_DEFINE_UNQUOTED($1, "$ac_define_dir", $3))
+])
+
+debugdir=${libdir}/debug
+	 
+AC_ARG_WITH(separate-debug-dir,
+[  --with-separate-debug-dir=path   Look for global separate debug info in this path [LIBDIR/debug]],
+[debugdir="${withval}"])
+	
+AC_DEFINE_DIR(DEBUGDIR, debugdir)
+
+
 rm -f doc/config.status
 AC_OUTPUT(../binutils.spec Makefile doc/Makefile bfd-in3.h:bfd-in2.h po/Makefile.in:po/Make-in,
 [sed -e '/SRC-POTFILES =/r po/SRC-POTFILES' -e '/BLD-POTFILES =/r po/BLD-POTFILES' po/Makefile.in > po/Makefile])
--- binutils-2.13.90.0.4/bfd/opncls.c.debuglink~	2002-06-19 00:41:58.000000000 -0400
+++ binutils-2.13.90.0.4/bfd/opncls.c	2003-01-28 15:39:40.000000000 -0500
@@ -25,6 +25,7 @@
 #include "sysdep.h"
 #include "objalloc.h"
 #include "libbfd.h"
+#include "libiberty.h"
 
 #ifndef S_IXUSR
 #define S_IXUSR 0100	/* Execute by owner.  */
@@ -691,3 +692,337 @@
 {
   objalloc_free_block ((struct objalloc *) abfd->memory, block);
 }
+
+
+/* 
+   GNU Extension: separate debug-info files 
+   
+   The idea here is that a special section called .gnu_debuglink might be
+   embedded in a binary file, which indicates that some *other* file
+   contains the real debugging information. This special section contains a
+   filename and CRC32 checksum, which we read and resolve to another file,
+   if it exists.
+
+   This facilitates "optional" provision of debugging information, without
+   having to provide two complete copies of every binary object (with and
+   without debug symbols).
+*/
+
+static unsigned long calc_crc32 PARAMS ((unsigned long, const unsigned char *, size_t));
+static char *get_debug_link_info PARAMS ((bfd *, unsigned long *));
+static boolean separate_debug_file_exists PARAMS ((const char *, const unsigned long));
+static char *find_separate_debug_file PARAMS ((bfd *, const char *));
+
+/*
+INTERNAL_FUNCTION
+	calc_crc32
+
+SYNOPSIS
+	unsigned long calc_crc32 (unsigned long crc, const unsigned char *buf, size_t len);
+
+DESCRIPTION
+	Advance the CRC32 given by @var{crc} through @var{len}
+	bytes of @var{buf}. Return the updated CRC32 value.
+*/     
+
+static unsigned long
+calc_crc32 (crc, buf, len)
+     unsigned long crc;
+     const unsigned char *buf;
+     size_t len;
+{
+  static const unsigned long crc32_table[256] =
+    {
+      0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
+      0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
+      0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
+      0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+      0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
+      0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+      0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
+      0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+      0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
+      0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
+      0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
+      0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+      0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
+      0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
+      0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
+      0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+      0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
+      0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+      0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
+      0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+      0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
+      0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
+      0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
+      0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+      0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
+      0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
+      0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
+      0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+      0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
+      0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+      0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
+      0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+      0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
+      0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
+      0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
+      0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+      0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
+      0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
+      0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
+      0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+      0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
+      0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+      0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
+      0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+      0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
+      0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
+      0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
+      0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+      0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
+      0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
+      0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
+      0x2d02ef8d
+    };
+  const unsigned char *end;
+
+  crc = ~crc & 0xffffffff;
+  for (end = buf + len; buf < end; ++buf)
+    crc = crc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
+  return ~crc & 0xffffffff;;
+}
+
+
+/*
+INTERNAL_FUNCTION
+	get_debug_link_info
+
+SYNOPSIS
+	char *get_debug_link_info (bfd *abfd, unsigned long *crc32_out)
+
+DESCRIPTION
+	fetch the filename and CRC32 value for any separate debuginfo
+	associated with @var{abfd}. Return NULL if no such info found,
+	otherwise return filename and update @var{crc32_out}.
+*/
+
+static char *
+get_debug_link_info (abfd, crc32_out)
+     bfd *abfd;
+     unsigned long *crc32_out;
+{
+  asection *sect;
+  bfd_size_type debuglink_size;
+  unsigned long crc32;
+  char *contents;
+  int crc_offset;
+  boolean ret;
+
+  BFD_ASSERT (abfd);
+  BFD_ASSERT (crc32_out);
+
+  sect = bfd_get_section_by_name (abfd, ".gnu_debuglink");
+  
+  if (sect == NULL)
+    return NULL;
+  
+  debuglink_size = bfd_section_size (abfd, sect);  
+
+  contents = xmalloc (debuglink_size);
+  ret = bfd_get_section_contents (abfd, sect, contents,
+				  (file_ptr)0, debuglink_size);
+  if (! ret)
+    {
+      free (contents);
+      return NULL;
+    }
+  
+  /* Crc value is stored after the filename, aligned up to 4 bytes. */
+  crc_offset = strlen (contents) + 1;
+  crc_offset = (crc_offset + 3) & ~3;
+  
+  crc32 = bfd_get_32 (abfd, (bfd_byte *) (contents + crc_offset));
+  
+  *crc32_out = crc32;
+  return contents;
+}
+
+/*
+INTERNAL_FUNCTION
+	separate_debug_file_exists
+
+SYNOPSIS
+	boolean separate_debug_file_exists (char * name, unsigned long crc32)
+
+DESCRIPTION
+	Checks to see if @var{name} is a file and if its contents
+	match @var{crc32}.
+*/
+
+static boolean
+separate_debug_file_exists (name, crc)
+     const char *name;
+     const unsigned long crc;
+{
+  unsigned long file_crc = 0;
+  int fd;
+  char buffer[8*1024];
+  int count;
+
+  BFD_ASSERT (name);
+
+  fd = open (name, O_RDONLY);
+  if (fd < 0)
+    return 0;
+
+  while ((count = read (fd, buffer, sizeof (buffer))) > 0)
+    file_crc = calc_crc32 (file_crc, buffer, count);
+
+  close (fd);
+
+  return crc == file_crc;
+}
+
+
+/*
+INTERNAL_FUNCTION
+	find_separate_debug_file
+
+SYNOPSIS
+	boolean find_separate_debug_file (bfd *abfd)
+
+DESCRIPTION
+	Searches @var{abfd} for a reference to separate debugging
+	information, scans various locations in the filesystem, including
+	the file tree rooted at @var{debug_file_directory}, and returns a
+	filename of such debugging information if the file is found and has
+	matching CRC32. Returns NULL if no reference to debugging file
+	exists, or file cannot be found.
+*/
+
+static char *
+find_separate_debug_file (abfd, debug_file_directory)
+     bfd *abfd;
+     const char *debug_file_directory;
+{
+  char *basename;
+  char *dir;
+  char *debugfile;
+  unsigned long crc32;
+  int i;
+
+  BFD_ASSERT (abfd);
+  BFD_ASSERT (debug_file_directory);
+
+  /* BFD may have been opened from a stream */
+  if (! abfd->filename)
+    return NULL;
+  
+  basename = get_debug_link_info (abfd, &crc32);
+
+  if (basename == NULL || strlen (basename) < 1)
+    return NULL;
+
+  dir = xstrdup (abfd->filename);
+  BFD_ASSERT (strlen (dir) != 0);
+  
+  /* Strip off filename part */
+  for (i = strlen (dir) - 1; i >= 0; i--)
+    {
+      if (IS_DIR_SEPARATOR (dir[i]))
+	break;
+    }
+  
+  dir[i+1] = '\0';
+  BFD_ASSERT (dir[i] == '/' || dir[0] == '\0')
+  
+  debugfile = xmalloc (strlen (debug_file_directory) + 1
+		       + strlen (dir)
+		       + strlen (".debug/")
+		       + strlen (basename) 
+		       + 1);
+
+  /* First try in the same directory as the original file: */
+  strcpy (debugfile, dir);
+  strcat (debugfile, basename);
+
+  if (separate_debug_file_exists (debugfile, crc32))
+    {
+      free (basename);
+      free (dir);
+      return debugfile;
+    }
+  
+  /* Then try in a subdirectory called .debug */
+  strcpy (debugfile, dir);
+  strcat (debugfile, ".debug/");
+  strcat (debugfile, basename);
+
+  if (separate_debug_file_exists (debugfile, crc32))
+    {
+      free (basename);
+      free (dir);
+      return debugfile;
+    }
+  
+  /* Then try in the global debugfile directory */
+
+  strcpy (debugfile, debug_file_directory);
+  i = strlen (debug_file_directory) - 1;
+  if (i > 0
+      && debug_file_directory[i] != '/'
+      && dir[0] != '/')
+    strcat (debugfile, "/");
+  strcat (debugfile, dir);
+  strcat (debugfile, basename);
+
+  if (separate_debug_file_exists (debugfile, crc32))
+    {
+      free (basename);
+      free (dir);
+      return debugfile;
+    }
+  
+  free (debugfile);
+  free (basename);
+  free (dir);
+  return NULL;
+}
+
+
+/*
+FUNCTION
+	bfd_follow_gnu_debuglink
+
+SYNOPSIS
+	char *bfd_follow_gnu_debuglink(bfd *abfd, const char *dir);
+
+DESCRIPTION
+
+	Takes a BFD and searches it for a .gnu_debuglink section.  If this
+	section is found, examines the section for the name and checksum of
+	a '.debug' file containing auxiliary debugging
+	information. Searches filesystem for .debug file in some standard
+	locations, including the directory tree rooted at @var{dir}, and if
+	found returns the full filename. If @var{dir} is NULL, will search
+	default path configured into libbfd at build time.
+
+RETURNS
+	<<NULL>> on any errors or failure to locate the .debug file,
+	otherwise a pointer to a heap-allocated string containing the
+	filename. The caller is responsible for freeing this string.
+*/
+
+char *
+bfd_follow_gnu_debuglink (abfd, dir)
+     bfd *abfd;
+     const char * dir;
+{
+  if (dir == NULL)
+    dir = DEBUGDIR;
+  
+  return find_separate_debug_file (abfd, dir);
+}
+


More information about the Binutils mailing list