diff options
Diffstat (limited to 'contrib/elf2dmp/pdb.c')
-rw-r--r-- | contrib/elf2dmp/pdb.c | 322 |
1 files changed, 322 insertions, 0 deletions
diff --git a/contrib/elf2dmp/pdb.c b/contrib/elf2dmp/pdb.c new file mode 100644 index 0000000..bcb01b4 --- /dev/null +++ b/contrib/elf2dmp/pdb.c @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2018 Virtuozzo International GmbH + * + * Based on source of Wine project + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "qemu/osdep.h" +#include "pdb.h" +#include "err.h" + +static uint32_t pdb_get_file_size(const struct pdb_reader *r, unsigned idx) +{ + return r->ds.toc->file_size[idx]; +} + +static pdb_seg *get_seg_by_num(struct pdb_reader *r, size_t n) +{ + size_t i = 0; + char *ptr; + + for (ptr = r->segs; (ptr < r->segs + r->segs_size); ) { + i++; + ptr += 8; + if (i == n) { + break; + } + ptr += sizeof(pdb_seg); + } + + return (pdb_seg *)ptr; +} + +uint64_t pdb_find_public_v3_symbol(struct pdb_reader *r, const char *name) +{ + size_t size = pdb_get_file_size(r, r->symbols->gsym_file); + int length; + const union codeview_symbol *sym; + const uint8_t *root = r->modimage; + size_t i; + + for (i = 0; i < size; i += length) { + sym = (const void *)(root + i); + length = sym->generic.len + 2; + + if (!sym->generic.id || length < 4) { + break; + } + + if (sym->generic.id == S_PUB_V3 && + !strcmp(name, sym->public_v3.name)) { + pdb_seg *segment = get_seg_by_num(r, sym->public_v3.segment); + uint32_t sect_rva = segment->dword[1]; + uint64_t rva = sect_rva + sym->public_v3.offset; + + printf("%s: 0x%016x(%d:\'%.8s\') + 0x%08x = 0x%09lx\n", name, + sect_rva, sym->public_v3.segment, + ((char *)segment - 8), sym->public_v3.offset, rva); + return rva; + } + } + + return 0; +} + +uint64_t pdb_resolve(uint64_t img_base, struct pdb_reader *r, const char *name) +{ + uint64_t rva = pdb_find_public_v3_symbol(r, name); + + if (!rva) { + return 0; + } + + return img_base + rva; +} + +static void pdb_reader_ds_exit(struct pdb_reader *r) +{ + free(r->ds.toc); +} + +static void pdb_exit_symbols(struct pdb_reader *r) +{ + free(r->modimage); + free(r->symbols); +} + +static void pdb_exit_segments(struct pdb_reader *r) +{ + free(r->segs); +} + +static void *pdb_ds_read(const PDB_DS_HEADER *header, + const uint32_t *block_list, int size) +{ + int i, nBlocks; + uint8_t *buffer; + + if (!size) { + return NULL; + } + + nBlocks = (size + header->block_size - 1) / header->block_size; + + buffer = malloc(nBlocks * header->block_size); + if (!buffer) { + return NULL; + } + + for (i = 0; i < nBlocks; i++) { + memcpy(buffer + i * header->block_size, (const char *)header + + block_list[i] * header->block_size, header->block_size); + } + + return buffer; +} + +static void *pdb_ds_read_file(struct pdb_reader* r, uint32_t file_number) +{ + const uint32_t *block_list; + uint32_t block_size; + const uint32_t *file_size; + size_t i; + + if (!r->ds.toc || file_number >= r->ds.toc->num_files) { + return NULL; + } + + file_size = r->ds.toc->file_size; + r->file_used[file_number / 32] |= 1 << (file_number % 32); + + if (file_size[file_number] == 0 || file_size[file_number] == 0xFFFFFFFF) { + return NULL; + } + + block_list = file_size + r->ds.toc->num_files; + block_size = r->ds.header->block_size; + + for (i = 0; i < file_number; i++) { + block_list += (file_size[i] + block_size - 1) / block_size; + } + + return pdb_ds_read(r->ds.header, block_list, file_size[file_number]); +} + +static int pdb_init_segments(struct pdb_reader *r) +{ + char *segs; + unsigned stream_idx = r->sidx.segments; + + segs = pdb_ds_read_file(r, stream_idx); + if (!segs) { + return 1; + } + + r->segs = segs; + r->segs_size = pdb_get_file_size(r, stream_idx); + + return 0; +} + +static int pdb_init_symbols(struct pdb_reader *r) +{ + int err = 0; + PDB_SYMBOLS *symbols; + PDB_STREAM_INDEXES *sidx = &r->sidx; + + memset(sidx, -1, sizeof(*sidx)); + + symbols = pdb_ds_read_file(r, 3); + if (!symbols) { + return 1; + } + + r->symbols = symbols; + + if (symbols->stream_index_size != sizeof(PDB_STREAM_INDEXES)) { + err = 1; + goto out_symbols; + } + + memcpy(sidx, (const char *)symbols + sizeof(PDB_SYMBOLS) + + symbols->module_size + symbols->offset_size + + symbols->hash_size + symbols->srcmodule_size + + symbols->pdbimport_size + symbols->unknown2_size, sizeof(*sidx)); + + /* Read global symbol table */ + r->modimage = pdb_ds_read_file(r, symbols->gsym_file); + if (!r->modimage) { + err = 1; + goto out_symbols; + } + + return 0; + +out_symbols: + free(symbols); + + return err; +} + +static int pdb_reader_ds_init(struct pdb_reader *r, PDB_DS_HEADER *hdr) +{ + memset(r->file_used, 0, sizeof(r->file_used)); + r->ds.header = hdr; + r->ds.toc = pdb_ds_read(hdr, (uint32_t *)((uint8_t *)hdr + + hdr->toc_page * hdr->block_size), hdr->toc_size); + + if (!r->ds.toc) { + return 1; + } + + return 0; +} + +static int pdb_reader_init(struct pdb_reader *r, void *data) +{ + int err = 0; + const char pdb7[] = "Microsoft C/C++ MSF 7.00"; + + if (memcmp(data, pdb7, sizeof(pdb7) - 1)) { + return 1; + } + + if (pdb_reader_ds_init(r, data)) { + return 1; + } + + r->ds.root = pdb_ds_read_file(r, 1); + if (!r->ds.root) { + err = 1; + goto out_ds; + } + + if (pdb_init_symbols(r)) { + err = 1; + goto out_root; + } + + if (pdb_init_segments(r)) { + err = 1; + goto out_sym; + } + + return 0; + +out_sym: + pdb_exit_symbols(r); +out_root: + free(r->ds.root); +out_ds: + pdb_reader_ds_exit(r); + + return err; +} + +static void pdb_reader_exit(struct pdb_reader *r) +{ + pdb_exit_segments(r); + pdb_exit_symbols(r); + free(r->ds.root); + pdb_reader_ds_exit(r); +} + +int pdb_init_from_file(const char *name, struct pdb_reader *reader) +{ + int err = 0; + int fd; + void *map; + struct stat st; + + fd = open(name, O_RDONLY, 0); + if (fd == -1) { + eprintf("Failed to open PDB file \'%s\'\n", name); + return 1; + } + reader->fd = fd; + + fstat(fd, &st); + reader->file_size = st.st_size; + + map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (map == MAP_FAILED) { + eprintf("Failed to map PDB file\n"); + err = 1; + goto out_fd; + } + + if (pdb_reader_init(reader, map)) { + err = 1; + goto out_unmap; + } + + return 0; + +out_unmap: + munmap(map, st.st_size); +out_fd: + close(fd); + + return err; +} + +void pdb_exit(struct pdb_reader *reader) +{ + munmap(reader->ds.header, reader->file_size); + close(reader->fd); + pdb_reader_exit(reader); +} |