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 "qemu/osdep.h"
  22
  23#include "pdb.h"
  24#include "err.h"
  25
  26static uint32_t pdb_get_file_size(const struct pdb_reader *r, unsigned idx)
  27{
  28    return r->ds.toc->file_size[idx];
  29}
  30
  31static pdb_seg *get_seg_by_num(struct pdb_reader *r, size_t n)
  32{
  33    size_t i = 0;
  34    char *ptr;
  35
  36    for (ptr = r->segs; (ptr < r->segs + r->segs_size); ) {
  37        i++;
  38        ptr += 8;
  39        if (i == n) {
  40            break;
  41        }
  42        ptr += sizeof(pdb_seg);
  43    }
  44
  45    return (pdb_seg *)ptr;
  46}
  47
  48uint64_t pdb_find_public_v3_symbol(struct pdb_reader *r, const char *name)
  49{
  50    size_t size = pdb_get_file_size(r, r->symbols->gsym_file);
  51    int length;
  52    const union codeview_symbol *sym;
  53    const uint8_t *root = r->modimage;
  54    size_t i;
  55
  56    for (i = 0; i < size; i += length) {
  57        sym = (const void *)(root + i);
  58        length = sym->generic.len + 2;
  59
  60        if (!sym->generic.id || length < 4) {
  61            break;
  62        }
  63
  64        if (sym->generic.id == S_PUB_V3 &&
  65                !strcmp(name, sym->public_v3.name)) {
  66            pdb_seg *segment = get_seg_by_num(r, sym->public_v3.segment);
  67            uint32_t sect_rva = segment->dword[1];
  68            uint64_t rva = sect_rva + sym->public_v3.offset;
  69
  70            printf("%s: 0x%016x(%d:\'%.8s\') + 0x%08x = 0x%09"PRIx64"\n", name,
  71                    sect_rva, sym->public_v3.segment,
  72                    ((char *)segment - 8), sym->public_v3.offset, rva);
  73            return rva;
  74        }
  75    }
  76
  77    return 0;
  78}
  79
  80uint64_t pdb_resolve(uint64_t img_base, struct pdb_reader *r, const char *name)
  81{
  82    uint64_t rva = pdb_find_public_v3_symbol(r, name);
  83
  84    if (!rva) {
  85        return 0;
  86    }
  87
  88    return img_base + rva;
  89}
  90
  91static void pdb_reader_ds_exit(struct pdb_reader *r)
  92{
  93    free(r->ds.toc);
  94}
  95
  96static void pdb_exit_symbols(struct pdb_reader *r)
  97{
  98    free(r->modimage);
  99    free(r->symbols);
 100}
 101
 102static void pdb_exit_segments(struct pdb_reader *r)
 103{
 104    free(r->segs);
 105}
 106
 107static void *pdb_ds_read(const PDB_DS_HEADER *header,
 108        const uint32_t *block_list, int size)
 109{
 110    int i, nBlocks;
 111    uint8_t *buffer;
 112
 113    if (!size) {
 114        return NULL;
 115    }
 116
 117    nBlocks = (size + header->block_size - 1) / header->block_size;
 118
 119    buffer = malloc(nBlocks * header->block_size);
 120    if (!buffer) {
 121        return NULL;
 122    }
 123
 124    for (i = 0; i < nBlocks; i++) {
 125        memcpy(buffer + i * header->block_size, (const char *)header +
 126                block_list[i] * header->block_size, header->block_size);
 127    }
 128
 129    return buffer;
 130}
 131
 132static void *pdb_ds_read_file(struct pdb_reader* r, uint32_t file_number)
 133{
 134    const uint32_t *block_list;
 135    uint32_t block_size;
 136    const uint32_t *file_size;
 137    size_t i;
 138
 139    if (!r->ds.toc || file_number >= r->ds.toc->num_files) {
 140        return NULL;
 141    }
 142
 143    file_size = r->ds.toc->file_size;
 144    r->file_used[file_number / 32] |= 1 << (file_number % 32);
 145
 146    if (file_size[file_number] == 0 || file_size[file_number] == 0xFFFFFFFF) {
 147        return NULL;
 148    }
 149
 150    block_list = file_size + r->ds.toc->num_files;
 151    block_size = r->ds.header->block_size;
 152
 153    for (i = 0; i < file_number; i++) {
 154        block_list += (file_size[i] + block_size - 1) / block_size;
 155    }
 156
 157    return pdb_ds_read(r->ds.header, block_list, file_size[file_number]);
 158}
 159
 160static int pdb_init_segments(struct pdb_reader *r)
 161{
 162    char *segs;
 163    unsigned stream_idx = r->sidx.segments;
 164
 165    segs = pdb_ds_read_file(r, stream_idx);
 166    if (!segs) {
 167        return 1;
 168    }
 169
 170    r->segs = segs;
 171    r->segs_size = pdb_get_file_size(r, stream_idx);
 172
 173    return 0;
 174}
 175
 176static int pdb_init_symbols(struct pdb_reader *r)
 177{
 178    int err = 0;
 179    PDB_SYMBOLS *symbols;
 180    PDB_STREAM_INDEXES *sidx = &r->sidx;
 181
 182    memset(sidx, -1, sizeof(*sidx));
 183
 184    symbols = pdb_ds_read_file(r, 3);
 185    if (!symbols) {
 186        return 1;
 187    }
 188
 189    r->symbols = symbols;
 190
 191    if (symbols->stream_index_size != sizeof(PDB_STREAM_INDEXES)) {
 192        err = 1;
 193        goto out_symbols;
 194    }
 195
 196    memcpy(sidx, (const char *)symbols + sizeof(PDB_SYMBOLS) +
 197            symbols->module_size + symbols->offset_size +
 198            symbols->hash_size + symbols->srcmodule_size +
 199            symbols->pdbimport_size + symbols->unknown2_size, sizeof(*sidx));
 200
 201    /* Read global symbol table */
 202    r->modimage = pdb_ds_read_file(r, symbols->gsym_file);
 203    if (!r->modimage) {
 204        err = 1;
 205        goto out_symbols;
 206    }
 207
 208    return 0;
 209
 210out_symbols:
 211    free(symbols);
 212
 213    return err;
 214}
 215
 216static int pdb_reader_ds_init(struct pdb_reader *r, PDB_DS_HEADER *hdr)
 217{
 218    if (hdr->block_size == 0) {
 219        return 1;
 220    }
 221
 222    memset(r->file_used, 0, sizeof(r->file_used));
 223    r->ds.header = hdr;
 224    r->ds.toc = pdb_ds_read(hdr, (uint32_t *)((uint8_t *)hdr +
 225                hdr->toc_page * hdr->block_size), hdr->toc_size);
 226
 227    if (!r->ds.toc) {
 228        return 1;
 229    }
 230
 231    return 0;
 232}
 233
 234static int pdb_reader_init(struct pdb_reader *r, void *data)
 235{
 236    int err = 0;
 237    const char pdb7[] = "Microsoft C/C++ MSF 7.00";
 238
 239    if (memcmp(data, pdb7, sizeof(pdb7) - 1)) {
 240        return 1;
 241    }
 242
 243    if (pdb_reader_ds_init(r, data)) {
 244        return 1;
 245    }
 246
 247    r->ds.root = pdb_ds_read_file(r, 1);
 248    if (!r->ds.root) {
 249        err = 1;
 250        goto out_ds;
 251    }
 252
 253    if (pdb_init_symbols(r)) {
 254        err = 1;
 255        goto out_root;
 256    }
 257
 258    if (pdb_init_segments(r)) {
 259        err = 1;
 260        goto out_sym;
 261    }
 262
 263    return 0;
 264
 265out_sym:
 266    pdb_exit_symbols(r);
 267out_root:
 268    free(r->ds.root);
 269out_ds:
 270    pdb_reader_ds_exit(r);
 271
 272    return err;
 273}
 274
 275static void pdb_reader_exit(struct pdb_reader *r)
 276{
 277    pdb_exit_segments(r);
 278    pdb_exit_symbols(r);
 279    free(r->ds.root);
 280    pdb_reader_ds_exit(r);
 281}
 282
 283int pdb_init_from_file(const char *name, struct pdb_reader *reader)
 284{
 285    GError *gerr = NULL;
 286    int err = 0;
 287    void *map;
 288
 289    reader->gmf = g_mapped_file_new(name, TRUE, &gerr);
 290    if (gerr) {
 291        eprintf("Failed to map PDB file \'%s\'\n", name);
 292        g_error_free(gerr);
 293        return 1;
 294    }
 295
 296    reader->file_size = g_mapped_file_get_length(reader->gmf);
 297    map = g_mapped_file_get_contents(reader->gmf);
 298    if (pdb_reader_init(reader, map)) {
 299        err = 1;
 300        goto out_unmap;
 301    }
 302
 303    return 0;
 304
 305out_unmap:
 306    g_mapped_file_unref(reader->gmf);
 307
 308    return err;
 309}
 310
 311void pdb_exit(struct pdb_reader *reader)
 312{
 313    g_mapped_file_unref(reader->gmf);
 314    pdb_reader_exit(reader);
 315}
 316