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