Skip to content

Commit

Permalink
bfd/pdb: fix bitmap generation in pdb_write_bitmap
Browse files Browse the repository at this point in the history
MSVC 2022 is more pedantic than MSVC 2019 when it comes to loading PDB
files in readonly mode, and was rejecting PDB files generated by binutils
because of their invalid free-space bitmaps. It's unknown what would
have happened if you tried to use MS tools to modify a PDB created by
binutils, but it probably would have led to a corrupted file.

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.
  • Loading branch information
maharmstone committed Sep 4, 2024
1 parent 6225a4d commit b281480
Showing 1 changed file with 48 additions and 9 deletions.
57 changes: 48 additions & 9 deletions bfd/pdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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. */
Expand Down Expand Up @@ -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;
Expand All @@ -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))
Expand All @@ -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,
Expand All @@ -650,14 +653,48 @@ 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++;
}

if (off < block_size)
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
Expand All @@ -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))
Expand Down Expand Up @@ -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. */
Expand Down

0 comments on commit b281480

Please sign in to comment.