|  | /* BFD back-end for PDB Multi-Stream Format archives. | 
|  | Copyright (C) 2022-2024 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_read (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; | 
|  | } | 
|  |  | 
|  | void *tdata = bfd_zalloc (abfd, sizeof (struct artdata)); | 
|  | if (tdata == NULL) | 
|  | return NULL; | 
|  | bfd_ardata (abfd) = tdata; | 
|  |  | 
|  | 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_read (int_buf, sizeof (uint32_t), abfd) != sizeof (uint32_t)) | 
|  | { | 
|  | bfd_set_error (bfd_error_malformed_archive); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | block_size = bfd_getl32 (int_buf); | 
|  | if ((block_size & -block_size) != block_size | 
|  | || block_size < 512 | 
|  | || block_size > 4096) | 
|  | { | 
|  | bfd_set_error (bfd_error_malformed_archive); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* Get block_map_addr.  */ | 
|  |  | 
|  | if (bfd_seek (abfd, 4 * sizeof (uint32_t), SEEK_CUR)) | 
|  | return NULL; | 
|  |  | 
|  | if (bfd_read (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_read (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_read (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_read (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_read (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_zmalloc (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_read (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_read (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_read (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_read (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_read (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_read (buf, to_read, abfd) != to_read) | 
|  | { | 
|  | bfd_set_error (bfd_error_malformed_archive); | 
|  | goto fail2; | 
|  | } | 
|  |  | 
|  | if (bfd_write (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_write (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_write (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_write (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_write (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_write (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_write (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_read (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_write (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_write (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_write (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_write (pdb_magic, sizeof (pdb_magic), abfd) != sizeof (pdb_magic)) | 
|  | return false; | 
|  |  | 
|  | bfd_putl32 (block_size, tmp); | 
|  |  | 
|  | if (bfd_write (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_write (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_write (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_write (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_write (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_generic_close_and_cleanup | 
|  |  | 
|  | #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 | 
|  | }; |