qemu/contrib/elf2dmp/pdb.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2018 Virtuozzo International GmbH
   3 *
   4 * Based on source of Wine project
   5 *
   6 * This library is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU Lesser General Public
   8 * License as published by the Free Software Foundation; either
   9 * version 2.1 of the License, or (at your option) any later version.
  10 *
  11 * This library is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14 * Lesser General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU Lesser General Public
  17 * License along with this library; if not, write to the Free Software
  18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  19 */
  20
  21#include <inttypes.h>
  22
  23#include "qemu/osdep.h"
  24#include "pdb.h"
  25#include "err.h"
  26
  27static uint32_t pdb_get_file_size(const struct pdb_reader *r, unsigned idx)
  28{
  29    return r->ds.toc->file_size[idx];
  30}
  31
  32static pdb_seg *get_seg_by_num(struct pdb_reader *r, size_t n)
  33{
  34    size_t i = 0;
  35    char *ptr;
  36
  37    for (ptr = r->segs; (ptr < r->segs + r->segs_size); ) {
  38        i++;
  39        ptr += 8;
  40        if (i == n) {
  41            break;
  42        }
  43        ptr += sizeof(pdb_seg);
  44    }
  45
  46    return (pdb_seg *)ptr;
  47}
  48
  49uint64_t pdb_find_public_v3_symbol(struct pdb_reader *r, const char *name)
  50{
  51    size_t size = pdb_get_file_size(r, r->symbols->gsym_file);
  52    int length;
  53    const union codeview_symbol *sym;
  54    const uint8_t *root = r->modimage;
  55    size_t i;
  56
  57    for (i = 0; i < size; i += length) {
  58        sym = (const void *)(root + i);
  59        length = sym->generic.len + 2;
  60
  61        if (!sym->generic.id || length < 4) {
  62            break;
  63        }
  64
  65        if (sym->generic.id == S_PUB_V3 &&
  66                !strcmp(name, sym->public_v3.name)) {
  67            pdb_seg *segment = get_seg_by_num(r, sym->public_v3.segment);
  68            uint32_t sect_rva = segment->dword[1];
  69            uint64_t rva = sect_rva + sym->public_v3.offset;
  70
  71            printf("%s: 0x%016x(%d:\'%.8s\') + 0x%08x = 0x%09"PRIx64"\n", name,
  72                    sect_rva, sym->public_v3.segment,
  73                    ((char *)segment - 8), sym->public_v3.offset, rva);
  74            return rva;
  75        }
  76    }
  77
  78    return 0;
  79}
  80
  81uint64_t pdb_resolve(uint64_t img_base, struct pdb_reader *r, const char *name)
  82{
  83    uint64_t rva = pdb_find_public_v3_symbol(r, name);
  84
  85    if (!rva) {
  86        return 0;
  87    }
  88
  89    return img_base + rva;
  90}
  91
  92static void pdb_reader_ds_exit(struct pdb_reader *r)
  93{
  94    free(r->ds.toc);
  95}
  96
  97static void pdb_exit_symbols(struct pdb_reader *r)
  98{
  99    free(r->modimage);
 100    free(r->symbols);
 101}
 102
 103static void pdb_exit_segments(struct pdb_reader *r)
 104{
 105    free(r->segs);
 106}
 107
 108static void *pdb_ds_read(const PDB_DS_HEADER *header,
 109        const uint32_t *block_list, int size)
 110{
 111    int i, nBlocks;
 112    uint8_t *buffer;
 113
 114    if (!size) {
 115        return NULL;
 116    }
 117
 118    nBlocks = (size + header->block_size - 1) / header->block_size;
 119
 120    buffer = malloc(nBlocks * header->block_size);
 121    if (!buffer) {
 122        return NULL;
 123    }
 124
 125    for (i = 0; i < nBlocks; i++) {
 126        memcpy(buffer + i * header->block_size, (const char *)header +
 127                block_list[i] * header->block_size, header->block_size);
 128    }
 129
 130    return buffer;
 131}
 132
 133static void *pdb_ds_read_file(struct pdb_reader* r, uint32_t file_number)
 134{
 135    const uint32_t *block_list;
 136    uint32_t block_size;
 137    const uint32_t *file_size;
 138    size_t i;
 139
 140    if (!r->ds.toc || file_number >= r->ds.toc->num_files) {
 141        return NULL;
 142    }
 143
 144    file_size = r->ds.toc->file_size;
 145    r->file_used[file_number / 32] |= 1 << (file_number % 32);
 146
 147    if (file_size[file_number] == 0 || file_size[file_number] == 0xFFFFFFFF) {
 148        return NULL;
 149    }
 150
 151    block_list = file_size + r->ds.toc->num_files;
 152    block_size = r->ds.header->block_size;
 153
 154    for (i = 0; i < file_number; i++) {
 155        block_list += (file_size[i] + block_size - 1) / block_size;
 156    }
 157
 158    return pdb_ds_read(r->ds.header, block_list, file_size[file_number]);
 159}
 160
 161static int pdb_init_segments(struct pdb_reader *r)
 162{
 163    char *segs;
 164    unsigned stream_idx = r->sidx.segments;
 165
 166    segs = pdb_ds_read_file(r, stream_idx);
 167    if (!segs) {
 168        return 1;
 169    }
 170
 171    r->segs = segs;
 172    r->segs_size = pdb_get_file_size(r, stream_idx);
 173
 174    return 0;
 175}
 176
 177static int pdb_init_symbols(struct pdb_reader *r)
 178{
 179    int err = 0;
 180    PDB_SYMBOLS *symbols;
 181    PDB_STREAM_INDEXES *sidx = &r->sidx;
 182
 183    memset(sidx, -1, sizeof(*sidx));
 184
 185    symbols = pdb_ds_read_file(r, 3);
 186    if (!symbols) {
 187        return 1;
 188    }
 189
 190    r->symbols = symbols;
 191
 192    if (symbols->stream_index_size != sizeof(PDB_STREAM_INDEXES)) {
 193        err = 1;
 194        goto out_symbols;
 195    }
 196
 197    memcpy(sidx, (const char *)symbols + sizeof(PDB_SYMBOLS) +
 198            symbols->module_size + symbols->offset_size +
 199            symbols->hash_size + symbols->srcmodule_size +
 200            symbols->pdbimport_size + symbols->unknown2_size, sizeof(*sidx));
 201
 202    /* Read global symbol table */
 203    r->modimage = pdb_ds_read_file(r, symbols->gsym_file);
 204    if (!r->modimage) {
 205        err = 1;
 206        goto out_symbols;
 207    }
 208
 209    return 0;
 210
 211out_symbols:
 212    free(symbols);
 213
 214    return err;
 215}
 216
 217static int pdb_reader_ds_init(struct pdb_reader *r, PDB_DS_HEADER *hdr)
 218{
 219    memset(r->file_used, 0, sizeof(r->file_used));
 220    r->ds.header = hdr;
 221    r->ds.toc = pdb_ds_read(hdr, (uint32_t *)((uint8_t *)hdr +
 222                hdr->toc_page * hdr->block_size), hdr->toc_size);
 223
 224    if (!r->ds.toc) {
 225        return 1;
 226    }
 227
 228    return 0;
 229}
 230
 231static int pdb_reader_init(struct pdb_reader *r, void *data)
 232{
 233    int err = 0;
 234    const char pdb7[] = "Microsoft C/C++ MSF 7.00";
 235
 236    if (memcmp(data, pdb7, sizeof(pdb7) - 1)) {
 237        return 1;
 238    }
 239
 240    if (pdb_reader_ds_init(r, data)) {
 241        return 1;
 242    }
 243
 244    r->ds.root = pdb_ds_read_file(r, 1);
 245    if (!r->ds.root) {
 246        err = 1;
 247        goto out_ds;
 248    }
 249
 250    if (pdb_init_symbols(r)) {
 251        err = 1;
 252        goto out_root;
 253    }
 254
 255    if (pdb_init_segments(r)) {
 256        err = 1;
 257        goto out_sym;
 258    }
 259
 260    return 0;
 261
 262out_sym:
 263    pdb_exit_symbols(r);
 264out_root:
 265    free(r->ds.root);
 266out_ds:
 267    pdb_reader_ds_exit(r);
 268
 269    return err;
 270}
 271
 272static void pdb_reader_exit(struct pdb_reader *r)
 273{
 274    pdb_exit_segments(r);
 275    pdb_exit_symbols(r);
 276    free(r->ds.root);
 277    pdb_reader_ds_exit(r);
 278}
 279
 280int pdb_init_from_file(const char *name, struct pdb_reader *reader)
 281{
 282    GError *gerr = NULL;
 283    int err = 0;
 284    void *map;
 285
 286    reader->gmf = g_mapped_file_new(name, TRUE, &gerr);
 287    if (gerr) {
 288        eprintf("Failed to map PDB file \'%s\'\n", name);
 289        return 1;
 290    }
 291
 292    reader->file_size = g_mapped_file_get_length(reader->gmf);
 293    map = g_mapped_file_get_contents(reader->gmf);
 294    if (pdb_reader_init(reader, map)) {
 295        err = 1;
 296        goto out_unmap;
 297    }
 298
 299    return 0;
 300
 301out_unmap:
 302    g_mapped_file_unref(reader->gmf);
 303
 304    return err;
 305}
 306
 307void pdb_exit(struct pdb_reader *reader)
 308{
 309    g_mapped_file_unref(reader->gmf);
 310    pdb_reader_exit(reader);
 311}
 312