[PATCH] bfd/pdb: fix bitmap generation in pdb_write_bitmap

Mark Harmstone mark@harmstone.com
Sun Sep 1 16:47:15 GMT 2024


MSVC 2022 is more pedantic than MSVC 2019 when it comes to loading PDB
files, and was rejecting PDB files generated by binutils because of
their invalid free-space bitmaps. This patch fixes pdb_write_bitmap so
we generate files that MSVC will accept.

Specifically there were three things we were doing wrong:

- We weren't including the superblock (block 0)

- We were setting bits in bytes backwards (MSB to LSB, rather than LSB to MSB)

- We should have been marking the contents of stream 0 as free. This is
  because, as the comment says, it's intended to be used for the
  directory for the previous write, to allow atomic updates.
---
 bfd/pdb.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 48 insertions(+), 9 deletions(-)

diff --git a/bfd/pdb.c b/bfd/pdb.c
index b0455b06c45..c4c528711ed 100644
--- a/bfd/pdb.c
+++ b/bfd/pdb.c
@@ -412,7 +412,8 @@ pdb_allocate_block (uint32_t *num_blocks, uint32_t block_size)
 
 static bool
 pdb_write_directory (bfd *abfd, uint32_t block_size, uint32_t num_files,
-		     uint32_t block_map_addr, uint32_t * num_blocks)
+		     uint32_t block_map_addr, uint32_t * num_blocks,
+		     uint32_t *stream0_start)
 {
   char tmp[sizeof (uint32_t)];
   uint32_t block, left, block_map_off;
@@ -561,6 +562,9 @@ pdb_write_directory (bfd *abfd, uint32_t block_size, uint32_t num_files,
 	      return false;
 	    }
 
+	  if (arelt == abfd->archive_head && i == 0)
+	    *stream0_start = file_block;
+
 	  left -= sizeof (uint32_t);
 
 	  /* Read file contents into buffer.  */
@@ -617,7 +621,8 @@ pdb_write_directory (bfd *abfd, uint32_t block_size, uint32_t num_files,
 }
 
 static bool
-pdb_write_bitmap (bfd *abfd, uint32_t block_size, uint32_t num_blocks)
+pdb_write_bitmap (bfd *abfd, uint32_t block_size, uint32_t num_blocks,
+		  uint32_t stream0_start)
 {
   char *buf;
   uint32_t num_intervals = (num_blocks + block_size - 1) / block_size;
@@ -626,8 +631,6 @@ pdb_write_bitmap (bfd *abfd, uint32_t block_size, uint32_t num_blocks)
   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))
@@ -636,8 +639,8 @@ pdb_write_bitmap (bfd *abfd, uint32_t block_size, uint32_t num_blocks)
 	  return false;
 	}
 
-      /* All of our blocks are contiguous, making our free block map simple.
-         0 = used, 1 = free.  */
+      /* All of our blocks are contiguous, making our free block map
+	 relatively simple.  0 = used, 1 = free.  */
 
       if (num_blocks >= 8)
 	memset (buf, 0,
@@ -650,7 +653,7 @@ pdb_write_bitmap (bfd *abfd, uint32_t block_size, uint32_t num_blocks)
 
 	  if (num_blocks % 8)
 	    {
-	      buf[off] = (1 << (8 - (num_blocks % 8))) - 1;
+	      buf[off] = 256 - (1 << (num_blocks % 8));
 	      off++;
 	    }
 
@@ -658,6 +661,40 @@ pdb_write_bitmap (bfd *abfd, uint32_t block_size, uint32_t num_blocks)
 	    memset (buf + off, 0xff, block_size - off);
 	}
 
+      /* Mark the blocks allocated to stream 0 as free.  This is because stream
+	 0 is intended to be used for the previous MSF directory, to allow
+	 atomic updates.  This doesn't apply to us, as we rewrite the whole
+	 file whenever any change is made.  */
+
+      if (i == 0 && abfd->archive_head)
+	{
+	  bfd *arelt = abfd->archive_head;
+	  uint32_t stream0_blocks =
+	    (bfd_get_size (arelt) + block_size - 1) / block_size;
+
+	  if (stream0_start % 8)
+	    {
+	      unsigned int high_bit;
+
+	      high_bit = (stream0_start % 8) + stream0_blocks;
+	      if (high_bit > 8)
+		high_bit = 8;
+
+	      buf[stream0_start / 8] |=
+		(1 << high_bit) - (1 << (stream0_start % 8));
+
+	      stream0_blocks -= high_bit - (stream0_start % 8);
+	      stream0_start += high_bit - (stream0_start % 8);
+	    }
+
+	  memset (buf + (stream0_start / 8), 0xff, stream0_blocks / 8);
+	  stream0_start += stream0_blocks / 8;
+	  stream0_blocks %= 8;
+
+	  if (stream0_blocks > 0)
+	    buf[stream0_start / 8] |= (1 << stream0_blocks) - 1;
+	}
+
       if (num_blocks < block_size * 8)
 	num_blocks = 0;
       else
@@ -681,6 +718,7 @@ pdb_write_contents (bfd *abfd)
   uint32_t num_blocks;
   uint32_t num_files = 0;
   uint32_t num_directory_bytes = sizeof (uint32_t);
+  uint32_t stream0_start;
   bfd *arelt;
 
   if (bfd_write (pdb_magic, sizeof (pdb_magic), abfd) != sizeof (pdb_magic))
@@ -735,10 +773,11 @@ pdb_write_contents (bfd *abfd)
     return false;
 
   if (!pdb_write_directory
-      (abfd, block_size, num_files, block_map_addr, &num_blocks))
+      (abfd, block_size, num_files, block_map_addr, &num_blocks,
+       &stream0_start))
     return false;
 
-  if (!pdb_write_bitmap (abfd, block_size, num_blocks))
+  if (!pdb_write_bitmap (abfd, block_size, num_blocks, stream0_start))
     return false;
 
   /* Write num_blocks now we know it.  */
-- 
2.44.2



More information about the Binutils mailing list