Blame tools/sepdebugcrcfix.c

2ff057
/* Copyright (C) 2013 Free Software Foundation, Inc.
2ff057
2ff057
   This program is free software; you can redistribute it and/or modify
2ff057
   it under the terms of the GNU General Public License as published by
2ff057
   the Free Software Foundation; either version 3 of the License, or
2ff057
   (at your option) any later version.
2ff057
2ff057
   This program is distributed in the hope that it will be useful,
2ff057
   but WITHOUT ANY WARRANTY; without even the implied warranty of
2ff057
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
2ff057
   GNU General Public License for more details.
2ff057
2ff057
   You should have received a copy of the GNU General Public License
2ff057
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
2ff057
2ff057
/* Version 2013-06-24.  */
2ff057
2ff057
#define _GNU_SOURCE
2ff057
2ff057
#include "system.h"
2ff057
2ff057
#include <string.h>
2ff057
#include <fcntl.h>
2ff057
#include <errno.h>
2ff057
#include <sys/types.h>
2ff057
#include <sys/stat.h>
2ff057
#include <unistd.h>
2ff057
#include <sys/mman.h>
2ff057
#include <endian.h>
2ff057
#include <stdio.h>
2ff057
#include <stdlib.h>
2ff057
#include <error.h>
2ff057
#include <libelf.h>
2ff057
#include <gelf.h>
2ff057
2ff057
#ifndef _
2ff057
#define _(x) x
2ff057
#endif
2ff057
#define static_assert(expr) \
2ff057
  extern int never_defined_just_used_for_checking[(expr) ? 1 : -1]
2ff057
#ifndef min
2ff057
# define min(a, b) ((a) < (b) ? (a) : (b))
2ff057
#endif
2ff057
2ff057
static_assert (sizeof (unsigned long) >= sizeof (uint32_t));
2ff057
2ff057
typedef int bool;
2ff057
static const bool false = 0, true = 1;
2ff057
2ff057
/* This is bfd_calc_gnu_debuglink_crc32 from bfd/opncls.c.  */
2ff057
static unsigned long
2ff057
    calc_gnu_debuglink_crc32 (unsigned long crc,
2ff057
			      const unsigned char *buf,
2ff057
			      size_t len)
2ff057
{
2ff057
  static const unsigned long crc32_table[256] =
2ff057
    {
2ff057
      0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
2ff057
      0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
2ff057
      0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
2ff057
      0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
2ff057
      0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
2ff057
      0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
2ff057
      0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
2ff057
      0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
2ff057
      0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
2ff057
      0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
2ff057
      0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
2ff057
      0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
2ff057
      0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
2ff057
      0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
2ff057
      0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
2ff057
      0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
2ff057
      0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
2ff057
      0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
2ff057
      0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
2ff057
      0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
2ff057
      0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
2ff057
      0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
2ff057
      0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
2ff057
      0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
2ff057
      0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
2ff057
      0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
2ff057
      0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
2ff057
      0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
2ff057
      0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
2ff057
      0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
2ff057
      0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
2ff057
      0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
2ff057
      0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
2ff057
      0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
2ff057
      0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
2ff057
      0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
2ff057
      0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
2ff057
      0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
2ff057
      0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
2ff057
      0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
2ff057
      0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
2ff057
      0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
2ff057
      0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
2ff057
      0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
2ff057
      0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
2ff057
      0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
2ff057
      0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
2ff057
      0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
2ff057
      0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
2ff057
      0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
2ff057
      0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
2ff057
      0x2d02ef8d
2ff057
    };
2ff057
  const unsigned char *end;
2ff057
2ff057
  crc = ~crc & 0xffffffff;
2ff057
  for (end = buf + len; buf < end; ++ buf)
2ff057
    crc = crc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
2ff057
  return ~crc & 0xffffffff;
2ff057
}
2ff057
2ff057
static size_t updated_count, matched_count, failed_count;
2ff057
2ff057
static const char *usr_lib_debug;
2ff057
2ff057
static bool
2ff057
crc32 (const char *fname, const char *base_fname, uint32_t *crcp)
2ff057
{
2ff057
  char *reldir = strdup (base_fname);
2ff057
  if (reldir == NULL)
2ff057
    error (1, 0, _("out of memory"));
2ff057
  char *s = reldir + strlen (reldir);
2ff057
  while (s > reldir && s[-1] != '/')
2ff057
    *--s = '\0';
2ff057
  char *debugname;
2ff057
  if (asprintf (&debugname, "%s/%s/%s", usr_lib_debug, reldir, fname) <= 0)
2ff057
    error (1, 0, _("out of memory"));
2ff057
  free (reldir);
2ff057
  int fd = open (debugname, O_RDONLY);
2ff057
  if (fd == -1)
2ff057
    {
2ff057
      error (0, errno, _("cannot open \"%s\""), debugname);
2ff057
      return false;
2ff057
    }
2ff057
  off64_t size = lseek64 (fd, 0, SEEK_END);
2ff057
  if (size == -1)
2ff057
    {
2ff057
      error (0, errno, _("cannot get size of \"%s\""), debugname);
2ff057
      return false;
2ff057
    }
2ff057
  off_t offset = 0;
2ff057
  uint32_t crc = 0;
2ff057
  void *buf = NULL;
2ff057
  while (offset < size)
2ff057
    {
2ff057
      const size_t maplen = min (0x10000, size - offset);
2ff057
      void *map = NULL;
2ff057
      if (buf == NULL)
2ff057
	{
2ff057
	  map = mmap (NULL, maplen, PROT_READ, MAP_PRIVATE | MAP_POPULATE,
2ff057
		      fd, offset);
2ff057
	  if (map == MAP_FAILED)
2ff057
	    {
2ff057
	      error (0, errno, _("cannot map 0x%llx bytes at offset 0x%llx "
2ff057
				 "of file \"%s\""),
2ff057
		     (unsigned long long) maplen, (unsigned long long) offset,
2ff057
		     debugname);
2ff057
	      map = NULL;
2ff057
	    }
2ff057
	}
2ff057
      if (map == NULL)
2ff057
	{
2ff057
	  if (buf == NULL)
2ff057
	    {
2ff057
	      buf = malloc (maplen);
2ff057
	      if (buf == NULL)
2ff057
		error (1, 0, _("out of memory"));
2ff057
	    }
2ff057
	  ssize_t got = pread (fd, buf, maplen, offset);
2ff057
	  if (got != maplen)
2ff057
	    {
2ff057
	      error (0, errno, _("cannot read 0x%llx bytes at offset 0x%llx "
2ff057
				 "of file \"%s\""),
2ff057
		     (unsigned long long) maplen, (unsigned long long) offset,
2ff057
		     debugname);
2ff057
	      free (buf);
2ff057
	      free (debugname);
2ff057
	      return false;
2ff057
	    }
2ff057
	}
2ff057
      crc = calc_gnu_debuglink_crc32 (crc, map ?: buf, maplen);
2ff057
      if (map && munmap (map, maplen) != 0)
2ff057
	error (1, errno, _("cannot unmap 0x%llx bytes at offset 0x%llx "
2ff057
			   "of file \"%s\""),
2ff057
	       (unsigned long long) maplen, (unsigned long long) offset,
2ff057
	       debugname);
2ff057
      offset += maplen;
2ff057
    }
2ff057
  free (buf);
2ff057
  if (close (fd) != 0)
2ff057
    {
2ff057
      error (0, errno, _("cannot close \"%s\""), debugname);
2ff057
      free (debugname);
2ff057
      return false;
2ff057
    }
2ff057
  free (debugname);
2ff057
  *crcp = crc;
2ff057
  return true;
2ff057
}
2ff057
2ff057
static bool
2ff057
process (Elf *elf, int fd, const char *fname)
2ff057
{
2ff057
  GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
2ff057
  if (ehdr == NULL)
2ff057
    {
2ff057
      error (0, 0, _("cannot get ELF header of \"%s\""), fname);
2ff057
      return false;
2ff057
    }
2ff057
  if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB
2ff057
      && ehdr->e_ident[EI_DATA] != ELFDATA2MSB)
2ff057
    {
2ff057
      error (0, 0, _("invalid ELF endianity of \"%s\""), fname);
2ff057
      return false;
2ff057
    }
2ff057
  Elf_Scn *scn = NULL;
2ff057
  const char scnname[] = ".gnu_debuglink";
2ff057
  while ((scn = elf_nextscn (elf, scn)) != NULL)
2ff057
    {
2ff057
      GElf_Shdr shdr_mem, *shdr = gelf_getshdr (scn, &shdr_mem);
2ff057
      if (shdr == NULL)
2ff057
	{
2ff057
	  error (0, 0, _("cannot get section # %zu in \"%s\""),
2ff057
		 elf_ndxscn (scn), fname);
2ff057
	  continue;
2ff057
	}
2ff057
      const char *sname = elf_strptr (elf, ehdr->e_shstrndx, shdr->sh_name);
2ff057
      if (sname == NULL)
2ff057
	{
2ff057
	  error (0, 0, _("cannot get name of section # %zu in \"%s\""),
2ff057
		 elf_ndxscn (scn), fname);
2ff057
	  continue;
2ff057
	}
2ff057
      if (strcmp (sname, scnname) != 0)
2ff057
	continue;
2ff057
      Elf_Data *data = elf_getdata (scn, NULL);
2ff057
      if (data == NULL)
2ff057
	{
2ff057
	  error (0, 0, _("cannot get data of section \"%s\" # %zu in \"%s\""),
2ff057
		 scnname, elf_ndxscn (scn), fname);
2ff057
	  continue;
2ff057
	}
2ff057
      if ((data->d_size & 3) != 0)
2ff057
	{
2ff057
	  error (0, 0, _("invalid size of section \"%s\" # %zu in \"%s\""),
2ff057
		 scnname, elf_ndxscn (scn), fname);
2ff057
	  continue;
2ff057
	}
2ff057
      const uint8_t *zerop = memchr (data->d_buf, '\0', data->d_size);
2ff057
      const uint8_t *crcp = (zerop == NULL
2ff057
			     ? NULL
2ff057
			     : (const uint8_t *) ((uintptr_t) (zerop + 1 + 3)
2ff057
						  & -4));
2ff057
      if (crcp + 4 != (uint8_t *) data->d_buf + data->d_size)
2ff057
	{
2ff057
	  error (0, 0, _("invalid format of section \"%s\" # %zu in \"%s\""),
2ff057
		 scnname, elf_ndxscn (scn), fname);
2ff057
	  continue;
2ff057
	}
2ff057
      uint32_t had_crc_targetendian = *(const uint32_t *) crcp;
2ff057
      uint32_t had_crc = (ehdr->e_ident[EI_DATA] == ELFDATA2LSB
2ff057
			  ? le32toh (had_crc_targetendian)
2ff057
			  : be32toh (had_crc_targetendian));
2ff057
      uint32_t crc;
2ff057
      if (! crc32 (data->d_buf, fname, &crc))
2ff057
	return false;
2ff057
      if (crc == had_crc)
2ff057
	{
2ff057
	  matched_count++;
2ff057
	  return true;
2ff057
	}
2ff057
      updated_count++;
2ff057
      off64_t seekto = (shdr->sh_offset + data->d_off
2ff057
			+ (crcp - (const uint8_t *) data->d_buf));
2ff057
      uint32_t crc_targetendian = (ehdr->e_ident[EI_DATA] == ELFDATA2LSB
2ff057
				   ? htole32 (crc) : htobe32 (crc));
2ff057
      ssize_t wrote = pwrite (fd, &crc_targetendian, sizeof (crc_targetendian),
2ff057
			      seekto);
2ff057
      if (wrote != sizeof (crc_targetendian))
2ff057
	{
2ff057
	  error (0, 0, _("cannot write new CRC to 0x%llx "
2ff057
			 "inside section \"%s\" # %zu in \"%s\""),
2ff057
		 (unsigned long long) seekto, scnname, elf_ndxscn (scn), fname);
2ff057
	  return false;
2ff057
	}
2ff057
      return true;
2ff057
    }
2ff057
  error (0, 0, _("cannot find section \"%s\" in \"%s\""), scnname, fname);
2ff057
  return false;
2ff057
}
2ff057
2ff057
int
2ff057
main (int argc, char **argv)
2ff057
{
2ff057
  if (argc < 2)
2ff057
    error (1, 0, _("usr/lib/debug [<relative filenames>...]"));
2ff057
  usr_lib_debug = argv[1];
2ff057
  if (elf_version (EV_CURRENT) == EV_NONE)
2ff057
    error (1, 0, _("error initializing libelf: %s"), elf_errmsg (-1));
2ff057
  for (int argi = 2; argi < argc; argi++)
2ff057
    {
2ff057
      const char *fname = argv[argi];
2ff057
      struct stat stat_buf;
2ff057
      if (stat(fname, &stat_buf) < 0)
2ff057
	{
2ff057
	  error (0, errno, _("cannot stat input \"%s\""), fname);
2ff057
	  failed_count++;
2ff057
	  continue;
2ff057
	}
2ff057
2ff057
      /* Make sure we can read and write */
2ff057
      chmod (fname, stat_buf.st_mode | S_IRUSR | S_IWUSR);
2ff057
2ff057
      bool failed = false;
2ff057
      int fd = open64 (fname, O_RDWR);
2ff057
      if (fd == -1)
2ff057
	{
2ff057
	  error (0, errno, _("cannot open \"%s\""), fname);
2ff057
	  failed = true;
2ff057
	}
2ff057
      else
2ff057
	{
2ff057
	  Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
2ff057
	  if (elf == NULL)
2ff057
	    {
2ff057
	      error (0, 0, _("cannot open \"%s\" as ELF: %s"), fname,
2ff057
		     elf_errmsg (-1));
2ff057
	      failed = true;
2ff057
	    }
2ff057
	  else
2ff057
	    {
2ff057
	      if (! process (elf, fd, fname))
2ff057
		failed = true;
2ff057
	      if (elf_end (elf) != 0)
2ff057
		{
2ff057
		  error (0, 0, _("cannot close \"%s\" as ELF: %s"), fname,
2ff057
			 elf_errmsg (-1));
2ff057
		  failed = true;
2ff057
		}
2ff057
	    }
2ff057
	  if (close (fd) != 0)
2ff057
	    {
2ff057
	      error (0, errno, _("cannot close \"%s\""), fname);
2ff057
	      failed = true;
2ff057
	    }
2ff057
	}
2ff057
2ff057
      /* Restore old access rights. Including any suid bits reset. */
2ff057
      chmod (fname, stat_buf.st_mode);
2ff057
2ff057
      if (failed)
2ff057
	failed_count++;
2ff057
    }
2ff057
  printf ("%s: Updated %zu CRC32s, %zu CRC32s did match.\n", argv[0],
2ff057
	  updated_count, matched_count);
2ff057
  if (failed_count)
2ff057
    printf ("%s: Failed for %zu files.\n", argv[0], failed_count);
2ff057
  return failed_count == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
2ff057
}