Add pdb archive format
authorMark Harmstone <mark@harmstone.com>
Tue, 13 Sep 2022 09:31:05 +0000 (10:31 +0100)
committerNick Clifton <nickc@redhat.com>
Tue, 13 Sep 2022 09:31:05 +0000 (10:31 +0100)
Resubmitted with changes in
https://sourceware.org/pipermail/binutils/2022-September/122791.html
made.

bfd/Makefile.am
bfd/Makefile.in
bfd/config.bfd
bfd/configure
bfd/configure.ac
bfd/pdb.c [new file with mode: 0644]
bfd/targets.c

index 2fa8df45135b0daffdf50502c885bf63a884314d..04fb335d47dd3e15db8bdd3bf7bea618fa3ef58a 100644 (file)
@@ -367,6 +367,7 @@ BFD32_BACKENDS = \
        mach-o-arm.lo \
        ns32knetbsd.lo \
        pc532-mach.lo \
+       pdb.lo \
        pdp11.lo \
        pe-arm-wince.lo \
        pe-arm.lo \
@@ -499,6 +500,7 @@ BFD32_BACKENDS_CFILES = \
        mach-o-arm.c \
        ns32knetbsd.c \
        pc532-mach.c \
+       pdb.c \
        pdp11.c \
        pe-arm-wince.c \
        pe-arm.c \
index 35475569e112ce6ac762edc4525237dc8043a7b2..e05b0c9d156583e6ffb8322472df9b111c8ae313 100644 (file)
@@ -829,6 +829,7 @@ BFD32_BACKENDS = \
        mach-o-arm.lo \
        ns32knetbsd.lo \
        pc532-mach.lo \
+       pdb.lo \
        pdp11.lo \
        pe-arm-wince.lo \
        pe-arm.lo \
@@ -961,6 +962,7 @@ BFD32_BACKENDS_CFILES = \
        mach-o-arm.c \
        ns32knetbsd.c \
        pc532-mach.c \
+       pdb.c \
        pdp11.c \
        pe-arm-wince.c \
        pe-arm.c \
@@ -1688,6 +1690,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/opncls.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/osf-core.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pc532-mach.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdb.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdp11.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pe-aarch64igen.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pe-arm-wince.Plo@am__quote@
index a79a0e2bf5b2bb6a13b7122c3d5d71864612b53d..5eb82d7e613ee8660a686ceb3a32a079acb44ed8 100644 (file)
@@ -711,7 +711,7 @@ case "${targ}" in
     ;;
   x86_64-*-mingw* | x86_64-*-pe | x86_64-*-pep | x86_64-*-cygwin)
     targ_defvec=x86_64_pe_vec
-    targ_selvecs="x86_64_pe_vec x86_64_pei_vec x86_64_pe_big_vec x86_64_elf64_vec i386_pe_vec i386_pei_vec i386_elf32_vec iamcu_elf32_vec"
+    targ_selvecs="x86_64_pe_vec x86_64_pei_vec x86_64_pe_big_vec x86_64_elf64_vec i386_pe_vec i386_pei_vec i386_elf32_vec iamcu_elf32_vec pdb_vec"
     want64=true
     targ_underscore=no
     ;;
@@ -765,7 +765,7 @@ case "${targ}" in
     ;;
   i[3-7]86-*-mingw32* | i[3-7]86-*-cygwin* | i[3-7]86-*-winnt | i[3-7]86-*-pe)
     targ_defvec=i386_pe_vec
-    targ_selvecs="i386_pe_vec i386_pe_big_vec i386_pei_vec i386_elf32_vec iamcu_elf32_vec"
+    targ_selvecs="i386_pe_vec i386_pe_big_vec i386_pei_vec i386_elf32_vec iamcu_elf32_vec pdb_vec"
     targ_underscore=yes
     ;;
   i[3-7]86-*-vxworks*)
index b5e44e6af796270fa70e3e14f69137fd970eb4e7..6afdce94fe88c9696ea22f1f87877edbbe606fe5 100755 (executable)
@@ -13481,6 +13481,7 @@ do
     ns32k_aout_pc532mach_vec)   tb="$tb pc532-mach.lo aout-ns32k.lo" ;;
     ns32k_aout_pc532nbsd_vec)   tb="$tb ns32knetbsd.lo aout-ns32k.lo" ;;
     or1k_elf32_vec)             tb="$tb elf32-or1k.lo elf32.lo $elf" ;;
+    pdb_vec)                    tb="$tb pdb.lo" ;;
     pdp11_aout_vec)             tb="$tb pdp11.lo" ;;
     pef_vec)                    tb="$tb pef.lo" ;;
     pef_xlib_vec)               tb="$tb pef.lo" ;;
index fb8795bdffc1cd103683c145679ca8a9aac46116..28f3d1afce6965b6bf17573433092d9181151ea4 100644 (file)
@@ -560,6 +560,7 @@ do
     ns32k_aout_pc532mach_vec)   tb="$tb pc532-mach.lo aout-ns32k.lo" ;;
     ns32k_aout_pc532nbsd_vec)   tb="$tb ns32knetbsd.lo aout-ns32k.lo" ;;
     or1k_elf32_vec)             tb="$tb elf32-or1k.lo elf32.lo $elf" ;;
+    pdb_vec)                    tb="$tb pdb.lo" ;;
     pdp11_aout_vec)             tb="$tb pdp11.lo" ;;
     pef_vec)                    tb="$tb pef.lo" ;;
     pef_xlib_vec)               tb="$tb pef.lo" ;;
diff --git a/bfd/pdb.c b/bfd/pdb.c
new file mode 100644 (file)
index 0000000..9a431c2
--- /dev/null
+++ b/bfd/pdb.c
@@ -0,0 +1,814 @@
+/* BFD back-end for PDB Multi-Stream Format archives.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+
+   This file is part of BFD, the Binary File Descriptor library.
+
+   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, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
+
+/* This describes the MSF file archive format, which is used for the
+   PDB debug info generated by MSVC. See https://llvm.org/docs/PDB/MsfFile.html
+   for a full description of the format.  */
+
+#include "sysdep.h"
+#include "bfd.h"
+#include "libbfd.h"
+
+/* "Microsoft C/C++ MSF 7.00\r\n\x1a\x44\x53\0\0\0" */
+static const uint8_t pdb_magic[] =
+{ 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66,
+  0x74, 0x20, 0x43, 0x2f, 0x43, 0x2b, 0x2b, 0x20,
+  0x4d, 0x53, 0x46, 0x20, 0x37, 0x2e, 0x30, 0x30,
+  0x0d, 0x0a, 0x1a, 0x44, 0x53, 0x00, 0x00, 0x00 };
+
+#define arch_eltdata(bfd) ((struct areltdata *) ((bfd)->arelt_data))
+
+static bfd_cleanup
+pdb_archive_p (bfd *abfd)
+{
+  int ret;
+  char magic[sizeof (pdb_magic)];
+
+  ret = bfd_bread (magic, sizeof (magic), abfd);
+  if (ret != sizeof (magic))
+    {
+      bfd_set_error (bfd_error_wrong_format);
+      return NULL;
+    }
+
+  if (memcmp (magic, pdb_magic, sizeof (magic)))
+    {
+      bfd_set_error (bfd_error_wrong_format);
+      return NULL;
+    }
+
+  return _bfd_no_cleanup;
+}
+
+static bfd *
+pdb_get_elt_at_index (bfd *abfd, symindex sym_index)
+{
+  char int_buf[sizeof (uint32_t)];
+  uint32_t block_size, block_map_addr, block, num_files;
+  uint32_t first_dir_block, dir_offset, file_size, block_off, left;
+  char name[10];
+  bfd *file;
+  char *buf;
+
+  /* Get block_size.  */
+
+  if (bfd_seek (abfd, sizeof (pdb_magic), SEEK_SET))
+    return NULL;
+
+  if (bfd_bread (int_buf, sizeof (uint32_t), abfd) != sizeof (uint32_t))
+    {
+      bfd_set_error (bfd_error_malformed_archive);
+      return NULL;
+    }
+
+  block_size = bfd_getl32 (int_buf);
+
+  /* Get block_map_addr.  */
+
+  if (bfd_seek (abfd, 4 * sizeof (uint32_t), SEEK_CUR))
+    return NULL;
+
+  if (bfd_bread (int_buf, sizeof (uint32_t), abfd) != sizeof (uint32_t))
+    {
+      bfd_set_error (bfd_error_malformed_archive);
+      return NULL;
+    }
+
+  block_map_addr = bfd_getl32 (int_buf);
+
+  /* Get num_files.  */
+
+  if (bfd_seek (abfd, block_map_addr * block_size, SEEK_SET))
+    return NULL;
+
+  if (bfd_bread (int_buf, sizeof (uint32_t), abfd) != sizeof (uint32_t))
+    {
+      bfd_set_error (bfd_error_malformed_archive);
+      return NULL;
+    }
+
+  first_dir_block = bfd_getl32 (int_buf);
+
+  if (bfd_seek (abfd, first_dir_block * block_size, SEEK_SET))
+    return NULL;
+
+  if (bfd_bread (int_buf, sizeof (uint32_t), abfd) != sizeof (uint32_t))
+    {
+      bfd_set_error (bfd_error_malformed_archive);
+      return NULL;
+    }
+
+  num_files = bfd_getl32 (int_buf);
+
+  if (sym_index >= num_files)
+    {
+      bfd_set_error (bfd_error_no_more_archived_files);
+      return NULL;
+    }
+
+  /* Read file size.  */
+
+  dir_offset = sizeof (uint32_t) * (sym_index + 1);
+
+  if (dir_offset >= block_size)
+    {
+      uint32_t block_map_addr_off;
+
+      block_map_addr_off = ((dir_offset / block_size) * sizeof (uint32_t));
+
+      if (bfd_seek (abfd, (block_map_addr * block_size) + block_map_addr_off,
+                   SEEK_SET))
+       return NULL;
+
+      if (bfd_bread (int_buf, sizeof (uint32_t), abfd) != sizeof (uint32_t))
+       {
+         bfd_set_error (bfd_error_malformed_archive);
+         return NULL;
+       }
+
+      block = bfd_getl32 (int_buf);
+    }
+  else
+    {
+      block = first_dir_block;
+    }
+
+  if (bfd_seek (abfd, (block * block_size) + (dir_offset % block_size),
+               SEEK_SET))
+    return NULL;
+
+  if (bfd_bread (int_buf, sizeof (uint32_t), abfd) != sizeof (uint32_t))
+    {
+      bfd_set_error (bfd_error_malformed_archive);
+      return NULL;
+    }
+
+  file_size = bfd_getl32 (int_buf);
+
+  /* Undocumented? Seen on PDBs created by MSVC 2022.  */
+  if (file_size == 0xffffffff)
+    file_size = 0;
+
+  /* Create BFD. */
+
+  /* Four hex digits is enough - even though MSF allows for 32 bits, the
+     PDB format itself only uses 16 bits for stream numbers.  */
+  sprintf (name, "%04lx", sym_index);
+
+  file = bfd_create (name, abfd);
+
+  if (!file)
+    return NULL;
+
+  if (!bfd_make_writable (file))
+    goto fail;
+
+  file->arelt_data =
+    (struct areltdata *) bfd_malloc (sizeof (struct areltdata));
+
+  if (!file->arelt_data)
+    goto fail;
+
+  arch_eltdata (file)->parsed_size = file_size;
+  arch_eltdata (file)->key = sym_index;
+
+  if (file_size == 0)
+    return file;
+
+  block_off = 0;
+
+  /* Sum number of blocks in previous files.  */
+
+  if (sym_index != 0)
+    {
+      dir_offset = sizeof (uint32_t);
+
+      if (bfd_seek (abfd, (first_dir_block * block_size) + sizeof (uint32_t),
+                   SEEK_SET))
+       goto fail;
+
+      for (symindex i = 0; i < sym_index; i++)
+       {
+         uint32_t size, num_blocks;
+
+         if ((dir_offset % block_size) == 0)
+           {
+             uint32_t block_map_addr_off;
+
+             block_map_addr_off =
+               ((dir_offset / block_size) * sizeof (uint32_t));
+
+             if (bfd_seek
+                 (abfd, (block_map_addr * block_size) + block_map_addr_off,
+                  SEEK_SET))
+               goto fail;
+
+             if (bfd_bread (int_buf, sizeof (uint32_t), abfd) !=
+                 sizeof (uint32_t))
+               {
+                 bfd_set_error (bfd_error_malformed_archive);
+                 goto fail;
+               }
+
+             block = bfd_getl32 (int_buf);
+
+             if (bfd_seek (abfd, block * block_size, SEEK_SET))
+               goto fail;
+           }
+
+         if (bfd_bread (int_buf, sizeof (uint32_t), abfd) !=
+             sizeof (uint32_t))
+           {
+             bfd_set_error (bfd_error_malformed_archive);
+             goto fail;
+           }
+
+         size = bfd_getl32 (int_buf);
+
+         if (size == 0xffffffff)
+           size = 0;
+
+         num_blocks = (size + block_size - 1) / block_size;
+         block_off += num_blocks;
+
+         dir_offset += sizeof (uint32_t);
+       }
+    }
+
+  /* Read blocks, and write into new BFD.  */
+
+  dir_offset = sizeof (uint32_t) * (num_files + block_off + 1);
+
+  if (dir_offset >= block_size)
+    {
+      uint32_t block_map_addr_off;
+
+      block_map_addr_off = ((dir_offset / block_size) * sizeof (uint32_t));
+
+      if (bfd_seek (abfd, (block_map_addr * block_size) + block_map_addr_off,
+                   SEEK_SET))
+       goto fail;
+
+      if (bfd_bread (int_buf, sizeof (uint32_t), abfd) != sizeof (uint32_t))
+       {
+         bfd_set_error (bfd_error_malformed_archive);
+         goto fail;
+       }
+
+      block = bfd_getl32 (int_buf);
+    }
+  else
+    {
+      block = first_dir_block;
+    }
+
+  buf = bfd_malloc (block_size);
+  if (!buf)
+    goto fail;
+
+  left = file_size;
+  do
+    {
+      uint32_t file_block, to_read;
+
+      if ((dir_offset % block_size) == 0 && left != file_size)
+       {
+         uint32_t block_map_addr_off;
+
+         block_map_addr_off =
+           ((dir_offset / block_size) * sizeof (uint32_t));
+
+         if (bfd_seek
+             (abfd, (block_map_addr * block_size) + block_map_addr_off,
+              SEEK_SET))
+           goto fail2;
+
+         if (bfd_bread (int_buf, sizeof (uint32_t), abfd) !=
+             sizeof (uint32_t))
+           {
+             bfd_set_error (bfd_error_malformed_archive);
+             goto fail2;
+           }
+
+         block = bfd_getl32 (int_buf);
+       }
+
+      if (bfd_seek (abfd, (block * block_size) + (dir_offset % block_size),
+                   SEEK_SET))
+       goto fail2;
+
+      if (bfd_bread (int_buf, sizeof (uint32_t), abfd) != sizeof (uint32_t))
+       {
+         bfd_set_error (bfd_error_malformed_archive);
+         goto fail2;
+       }
+
+      file_block = bfd_getl32 (int_buf);
+
+      if (bfd_seek (abfd, file_block * block_size, SEEK_SET))
+       goto fail2;
+
+      to_read = left > block_size ? block_size : left;
+
+      if (bfd_bread (buf, to_read, abfd) != to_read)
+       {
+         bfd_set_error (bfd_error_malformed_archive);
+         goto fail2;
+       }
+
+      if (bfd_bwrite (buf, to_read, file) != to_read)
+       goto fail2;
+
+      if (left > block_size)
+       left -= block_size;
+      else
+       break;
+
+      dir_offset += sizeof (uint32_t);
+    }
+  while (left > 0);
+
+  free (buf);
+
+  return file;
+
+fail2:
+  free (buf);
+
+fail:
+  bfd_close (file);
+  return NULL;
+}
+
+static bfd *
+pdb_openr_next_archived_file (bfd *archive, bfd *last_file)
+{
+  if (!last_file)
+    return pdb_get_elt_at_index (archive, 0);
+  else
+    return pdb_get_elt_at_index (archive, arch_eltdata (last_file)->key + 1);
+}
+
+static int
+pdb_generic_stat_arch_elt (bfd *abfd, struct stat *buf)
+{
+  buf->st_mtime = 0;
+  buf->st_uid = 0;
+  buf->st_gid = 0;
+  buf->st_mode = 0644;
+  buf->st_size = arch_eltdata (abfd)->parsed_size;
+
+  return 0;
+}
+
+static uint32_t
+pdb_allocate_block (uint32_t *num_blocks, uint32_t block_size)
+{
+  uint32_t block;
+
+  block = *num_blocks;
+
+  (*num_blocks)++;
+
+  /* If new interval, skip two blocks for free space map.  */
+
+  if ((block % block_size) == 1)
+    {
+      block += 2;
+      (*num_blocks) += 2;
+    }
+
+  return block;
+}
+
+static bool
+pdb_write_directory (bfd *abfd, uint32_t block_size, uint32_t num_files,
+                    uint32_t block_map_addr, uint32_t * num_blocks)
+{
+  char tmp[sizeof (uint32_t)];
+  uint32_t block, left, block_map_off;
+  bfd *arelt;
+  char *buf;
+
+  /* Allocate first block for directory.  */
+
+  block = pdb_allocate_block (num_blocks, block_size);
+  left = block_size;
+
+  /* Write allocated block no. at beginning of block map.  */
+
+  if (bfd_seek (abfd, block_map_addr * block_size, SEEK_SET))
+    return false;
+
+  bfd_putl32 (block, tmp);
+
+  if (bfd_bwrite (tmp, sizeof (uint32_t), abfd) != sizeof (uint32_t))
+    return false;
+
+  block_map_off = sizeof (uint32_t);
+
+  /* Write num_files at beginning of directory.  */
+
+  if (bfd_seek (abfd, block * block_size, SEEK_SET))
+    return false;
+
+  bfd_putl32 (num_files, tmp);
+
+  if (bfd_bwrite (tmp, sizeof (uint32_t), abfd) != sizeof (uint32_t))
+    return false;
+
+  left -= sizeof (uint32_t);
+
+  /* Write file sizes.  */
+
+  arelt = abfd->archive_head;
+  while (arelt)
+    {
+      if (left == 0)
+       {
+         if (block_map_off == block_size) /* Too many blocks.  */
+           {
+             bfd_set_error (bfd_error_invalid_operation);
+             return false;
+           }
+
+         block = pdb_allocate_block (num_blocks, block_size);
+         left = block_size;
+
+         if (bfd_seek
+             (abfd, (block_map_addr * block_size) + block_map_off, SEEK_SET))
+           return false;
+
+         bfd_putl32 (block, tmp);
+
+         if (bfd_bwrite (tmp, sizeof (uint32_t), abfd) != sizeof (uint32_t))
+           return false;
+
+         block_map_off += sizeof (uint32_t);
+
+         if (bfd_seek (abfd, block * block_size, SEEK_SET))
+           return false;
+       }
+
+      bfd_putl32 (bfd_get_size (arelt), tmp);
+
+      if (bfd_bwrite (tmp, sizeof (uint32_t), abfd) != sizeof (uint32_t))
+       return false;
+
+      left -= sizeof (uint32_t);
+
+      arelt = arelt->archive_next;
+    }
+
+  /* Write blocks.  */
+
+  buf = bfd_malloc (block_size);
+  if (!buf)
+    return false;
+
+  arelt = abfd->archive_head;
+  while (arelt)
+    {
+      ufile_ptr size = bfd_get_size (arelt);
+      uint32_t req_blocks = (size + block_size - 1) / block_size;
+
+      if (bfd_seek (arelt, 0, SEEK_SET))
+       {
+         free (buf);
+         return false;
+       }
+
+      for (uint32_t i = 0; i < req_blocks; i++)
+       {
+         uint32_t file_block, to_read;
+
+         if (left == 0)
+           {
+             if (block_map_off == block_size) /* Too many blocks.  */
+               {
+                 bfd_set_error (bfd_error_invalid_operation);
+                 free (buf);
+                 return false;
+               }
+
+             block = pdb_allocate_block (num_blocks, block_size);
+             left = block_size;
+
+             if (bfd_seek
+                 (abfd, (block_map_addr * block_size) + block_map_off,
+                  SEEK_SET))
+               {
+                 free (buf);
+                 return false;
+               }
+
+             bfd_putl32 (block, tmp);
+
+             if (bfd_bwrite (tmp, sizeof (uint32_t), abfd) !=
+                 sizeof (uint32_t))
+               {
+                 free (buf);
+                 return false;
+               }
+
+             block_map_off += sizeof (uint32_t);
+
+             if (bfd_seek (abfd, block * block_size, SEEK_SET))
+               {
+                 free (buf);
+                 return false;
+               }
+           }
+
+         /* Allocate block and write number into directory.  */
+
+         file_block = pdb_allocate_block (num_blocks, block_size);
+
+         bfd_putl32 (file_block, tmp);
+
+         if (bfd_bwrite (tmp, sizeof (uint32_t), abfd) != sizeof (uint32_t))
+           {
+             free (buf);
+             return false;
+           }
+
+         left -= sizeof (uint32_t);
+
+         /* Read file contents into buffer.  */
+
+         to_read = size > block_size ? block_size : size;
+
+         if (bfd_bread (buf, to_read, arelt) != to_read)
+           {
+             free (buf);
+             return false;
+           }
+
+         size -= to_read;
+
+         if (to_read < block_size)
+           memset (buf + to_read, 0, block_size - to_read);
+
+         if (bfd_seek (abfd, file_block * block_size, SEEK_SET))
+           {
+             free (buf);
+             return false;
+           }
+
+         /* Write file contents into allocated block.  */
+
+         if (bfd_bwrite (buf, block_size, abfd) != block_size)
+           {
+             free (buf);
+             return false;
+           }
+
+         if (bfd_seek
+             (abfd, (block * block_size) + block_size - left, SEEK_SET))
+           {
+             free (buf);
+             return false;
+           }
+       }
+
+      arelt = arelt->archive_next;
+    }
+
+  memset (buf, 0, left);
+
+  if (bfd_bwrite (buf, left, abfd) != left)
+    {
+      free (buf);
+      return false;
+    }
+
+  free (buf);
+
+  return true;
+}
+
+static bool
+pdb_write_bitmap (bfd *abfd, uint32_t block_size, uint32_t num_blocks)
+{
+  char *buf;
+  uint32_t num_intervals = (num_blocks + block_size - 1) / block_size;
+
+  buf = bfd_malloc (block_size);
+  if (!buf)
+    return false;
+
+  num_blocks--;                        /* Superblock not included.  */
+
+  for (uint32_t i = 0; i < num_intervals; i++)
+    {
+      if (bfd_seek (abfd, ((i * block_size) + 1) * block_size, SEEK_SET))
+       {
+         free (buf);
+         return false;
+       }
+
+      /* All of our blocks are contiguous, making our free block map simple.
+         0 = used, 1 = free.  */
+
+      if (num_blocks >= 8)
+       memset (buf, 0,
+               (num_blocks / 8) >
+               block_size ? block_size : (num_blocks / 8));
+
+      if (num_blocks < block_size * 8)
+       {
+         unsigned int off = num_blocks / 8;
+
+         if (num_blocks % 8)
+           {
+             buf[off] = (1 << (8 - (num_blocks % 8))) - 1;
+             off++;
+           }
+
+         if (off < block_size)
+           memset (buf + off, 0xff, block_size - off);
+       }
+
+      if (num_blocks < block_size * 8)
+       num_blocks = 0;
+      else
+       num_blocks -= block_size * 8;
+
+      if (bfd_bwrite (buf, block_size, abfd) != block_size)
+       return false;
+    }
+
+  free (buf);
+
+  return true;
+}
+
+static bool
+pdb_write_contents (bfd *abfd)
+{
+  char tmp[sizeof (uint32_t)];
+  const uint32_t block_size = 0x400;
+  uint32_t block_map_addr;
+  uint32_t num_blocks;
+  uint32_t num_files = 0;
+  uint32_t num_directory_bytes = sizeof (uint32_t);
+  bfd *arelt;
+
+  if (bfd_bwrite (pdb_magic, sizeof (pdb_magic), abfd) != sizeof (pdb_magic))
+    return false;
+
+  bfd_putl32 (block_size, tmp);
+
+  if (bfd_bwrite (tmp, sizeof (uint32_t), abfd) != sizeof (uint32_t))
+    return false;
+
+  bfd_putl32 (1, tmp); /* Free block map block (always either 1 or 2).  */
+
+  if (bfd_bwrite (tmp, sizeof (uint32_t), abfd) != sizeof (uint32_t))
+    return false;
+
+  arelt = abfd->archive_head;
+
+  while (arelt)
+    {
+      uint32_t blocks_required =
+       (bfd_get_size (arelt) + block_size - 1) / block_size;
+
+      num_directory_bytes += sizeof (uint32_t); /* Size.  */
+      num_directory_bytes += blocks_required * sizeof (uint32_t); /* Blocks.  */
+
+      num_files++;
+
+      arelt = arelt->archive_next;
+    }
+
+  /* Superblock plus two bitmap blocks.  */
+  num_blocks = 3;
+
+  /* Skip num_blocks for now.  */
+  if (bfd_seek (abfd, sizeof (uint32_t), SEEK_CUR))
+    return false;
+
+  bfd_putl32 (num_directory_bytes, tmp);
+
+  if (bfd_bwrite (tmp, sizeof (uint32_t), abfd) != sizeof (uint32_t))
+    return false;
+
+  /* Skip unknown uint32_t (always 0?).  */
+  if (bfd_seek (abfd, sizeof (uint32_t), SEEK_CUR))
+    return false;
+
+  block_map_addr = pdb_allocate_block (&num_blocks, block_size);
+
+  bfd_putl32 (block_map_addr, tmp);
+
+  if (bfd_bwrite (tmp, sizeof (uint32_t), abfd) != sizeof (uint32_t))
+    return false;
+
+  if (!pdb_write_directory
+      (abfd, block_size, num_files, block_map_addr, &num_blocks))
+    return false;
+
+  if (!pdb_write_bitmap (abfd, block_size, num_blocks))
+    return false;
+
+  /* Write num_blocks now we know it.  */
+
+  if (bfd_seek
+      (abfd, sizeof (pdb_magic) + sizeof (uint32_t) + sizeof (uint32_t),
+       SEEK_SET))
+    return false;
+
+  bfd_putl32 (num_blocks, tmp);
+
+  if (bfd_bwrite (tmp, sizeof (uint32_t), abfd) != sizeof (uint32_t))
+    return false;
+
+  return true;
+}
+
+#define pdb_bfd_free_cached_info _bfd_generic_bfd_free_cached_info
+#define pdb_new_section_hook _bfd_generic_new_section_hook
+#define pdb_get_section_contents _bfd_generic_get_section_contents
+#define pdb_get_section_contents_in_window _bfd_generic_get_section_contents_in_window
+#define pdb_close_and_cleanup _bfd_bool_bfd_true
+
+#define pdb_slurp_armap _bfd_noarchive_slurp_armap
+#define pdb_slurp_extended_name_table _bfd_noarchive_slurp_extended_name_table
+#define pdb_construct_extended_name_table _bfd_noarchive_construct_extended_name_table
+#define pdb_truncate_arname _bfd_noarchive_truncate_arname
+#define pdb_write_armap _bfd_noarchive_write_armap
+#define pdb_read_ar_hdr _bfd_noarchive_read_ar_hdr
+#define pdb_write_ar_hdr _bfd_noarchive_write_ar_hdr
+#define pdb_update_armap_timestamp _bfd_noarchive_update_armap_timestamp
+
+const bfd_target pdb_vec =
+{
+  "pdb",
+  bfd_target_unknown_flavour,
+  BFD_ENDIAN_LITTLE,           /* target byte order */
+  BFD_ENDIAN_LITTLE,           /* target headers byte order */
+  0,                           /* object flags */
+  0,                           /* section flags */
+  0,                           /* leading underscore */
+  ' ',                         /* ar_pad_char */
+  16,                          /* ar_max_namelen */
+  0,                           /* match priority.  */
+  TARGET_KEEP_UNUSED_SECTION_SYMBOLS, /* keep unused section symbols.  */
+  bfd_getl64, bfd_getl_signed_64, bfd_putl64,
+  bfd_getl32, bfd_getl_signed_32, bfd_putl32,
+  bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* Data.  */
+  bfd_getl64, bfd_getl_signed_64, bfd_putl64,
+  bfd_getl32, bfd_getl_signed_32, bfd_putl32,
+  bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* Hdrs.  */
+
+  {                            /* bfd_check_format */
+    _bfd_dummy_target,
+    _bfd_dummy_target,
+    pdb_archive_p,
+    _bfd_dummy_target
+  },
+  {                            /* bfd_set_format */
+    _bfd_bool_bfd_false_error,
+    _bfd_bool_bfd_false_error,
+    _bfd_bool_bfd_true,
+    _bfd_bool_bfd_false_error
+  },
+  {                            /* bfd_write_contents */
+    _bfd_bool_bfd_true,
+    _bfd_bool_bfd_false_error,
+    pdb_write_contents,
+    _bfd_bool_bfd_false_error
+  },
+
+  BFD_JUMP_TABLE_GENERIC (pdb),
+  BFD_JUMP_TABLE_COPY (_bfd_generic),
+  BFD_JUMP_TABLE_CORE (_bfd_nocore),
+  BFD_JUMP_TABLE_ARCHIVE (pdb),
+  BFD_JUMP_TABLE_SYMBOLS (_bfd_nosymbols),
+  BFD_JUMP_TABLE_RELOCS (_bfd_norelocs),
+  BFD_JUMP_TABLE_WRITE (_bfd_generic),
+  BFD_JUMP_TABLE_LINK (_bfd_nolink),
+  BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
+
+  NULL,
+
+  NULL
+};
index 1ec8acdb03ff53d0797131cdba6c49b16856f77a..dc331230affe1cbe072f101ba7efd995350dd501 100644 (file)
@@ -840,6 +840,7 @@ extern const bfd_target nios2_elf32_le_vec;
 extern const bfd_target ns32k_aout_pc532mach_vec;
 extern const bfd_target ns32k_aout_pc532nbsd_vec;
 extern const bfd_target or1k_elf32_vec;
+extern const bfd_target pdb_vec;
 extern const bfd_target pdp11_aout_vec;
 extern const bfd_target pef_vec;
 extern const bfd_target pef_xlib_vec;
@@ -1221,6 +1222,8 @@ static const bfd_target * const _bfd_target_vector[] =
 
        &or1k_elf32_vec,
 
+       &pdb_vec,
+
        &pdp11_aout_vec,
 
        &pef_vec,