From b281480b26f1f27dc83ad3271fb2eaf9736dc3e0 Mon Sep 17 00:00:00 2001 From: Mark Harmstone Date: Sun, 1 Sep 2024 17:42:30 +0100 Subject: [PATCH] bfd/pdb: fix bitmap generation in pdb_write_bitmap 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. --- 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. */