mirror of https://gitlab.com/qemu-project/qemu
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
304 lines
6.9 KiB
C
304 lines
6.9 KiB
C
/*
|
|
* 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 "qemu/bswap.h"
|
|
|
|
#include "pdb.h"
|
|
#include "err.h"
|
|
|
|
static uint32_t pdb_get_file_size(const struct pdb_reader *r, unsigned idx)
|
|
{
|
|
if (idx >= r->ds.toc->num_files) {
|
|
return 0;
|
|
}
|
|
|
|
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%09"PRIx64"\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)
|
|
{
|
|
g_free(r->ds.toc);
|
|
}
|
|
|
|
static void pdb_exit_symbols(struct pdb_reader *r)
|
|
{
|
|
g_free(r->modimage);
|
|
g_free(r->symbols);
|
|
}
|
|
|
|
static void pdb_exit_segments(struct pdb_reader *r)
|
|
{
|
|
g_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 = g_malloc(nBlocks * header->block_size);
|
|
|
|
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 bool pdb_init_segments(struct pdb_reader *r)
|
|
{
|
|
unsigned stream_idx = r->segments;
|
|
|
|
r->segs = pdb_ds_read_file(r, stream_idx);
|
|
if (!r->segs) {
|
|
return false;
|
|
}
|
|
|
|
r->segs_size = pdb_get_file_size(r, stream_idx);
|
|
if (!r->segs_size) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool pdb_init_symbols(struct pdb_reader *r)
|
|
{
|
|
PDB_SYMBOLS *symbols;
|
|
|
|
symbols = pdb_ds_read_file(r, 3);
|
|
if (!symbols) {
|
|
return false;
|
|
}
|
|
|
|
r->symbols = symbols;
|
|
|
|
r->segments = lduw_le_p((const char *)symbols + sizeof(PDB_SYMBOLS) +
|
|
symbols->module_size + symbols->offset_size +
|
|
symbols->hash_size + symbols->srcmodule_size +
|
|
symbols->pdbimport_size + symbols->unknown2_size +
|
|
offsetof(PDB_STREAM_INDEXES, segments));
|
|
|
|
/* Read global symbol table */
|
|
r->modimage = pdb_ds_read_file(r, symbols->gsym_file);
|
|
if (!r->modimage) {
|
|
goto out_symbols;
|
|
}
|
|
|
|
return true;
|
|
|
|
out_symbols:
|
|
g_free(symbols);
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool pdb_reader_ds_init(struct pdb_reader *r, PDB_DS_HEADER *hdr)
|
|
{
|
|
if (hdr->block_size == 0) {
|
|
return false;
|
|
}
|
|
|
|
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 false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool pdb_reader_init(struct pdb_reader *r, void *data)
|
|
{
|
|
const char pdb7[] = "Microsoft C/C++ MSF 7.00";
|
|
|
|
if (memcmp(data, pdb7, sizeof(pdb7) - 1)) {
|
|
return false;
|
|
}
|
|
|
|
if (!pdb_reader_ds_init(r, data)) {
|
|
return false;
|
|
}
|
|
|
|
r->ds.root = pdb_ds_read_file(r, 1);
|
|
if (!r->ds.root) {
|
|
goto out_ds;
|
|
}
|
|
|
|
if (!pdb_init_symbols(r)) {
|
|
goto out_root;
|
|
}
|
|
|
|
if (!pdb_init_segments(r)) {
|
|
goto out_sym;
|
|
}
|
|
|
|
return true;
|
|
|
|
out_sym:
|
|
pdb_exit_symbols(r);
|
|
out_root:
|
|
g_free(r->ds.root);
|
|
out_ds:
|
|
pdb_reader_ds_exit(r);
|
|
|
|
return false;
|
|
}
|
|
|
|
static void pdb_reader_exit(struct pdb_reader *r)
|
|
{
|
|
pdb_exit_segments(r);
|
|
pdb_exit_symbols(r);
|
|
g_free(r->ds.root);
|
|
pdb_reader_ds_exit(r);
|
|
}
|
|
|
|
bool pdb_init_from_file(const char *name, struct pdb_reader *reader)
|
|
{
|
|
GError *gerr = NULL;
|
|
void *map;
|
|
|
|
reader->gmf = g_mapped_file_new(name, TRUE, &gerr);
|
|
if (gerr) {
|
|
eprintf("Failed to map PDB file \'%s\'\n", name);
|
|
g_error_free(gerr);
|
|
return false;
|
|
}
|
|
|
|
reader->file_size = g_mapped_file_get_length(reader->gmf);
|
|
map = g_mapped_file_get_contents(reader->gmf);
|
|
if (!pdb_reader_init(reader, map)) {
|
|
goto out_unmap;
|
|
}
|
|
|
|
return true;
|
|
|
|
out_unmap:
|
|
g_mapped_file_unref(reader->gmf);
|
|
|
|
return false;
|
|
}
|
|
|
|
void pdb_exit(struct pdb_reader *reader)
|
|
{
|
|
g_mapped_file_unref(reader->gmf);
|
|
pdb_reader_exit(reader);
|
|
}
|