aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/include/pdb/symbol.hpp
blob: 98379d8b3f40c706a6e45547b3e66b105f3ff95f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// Copyright (c) 2017 Egor Tensin <Egor.Tensin@gmail.com>
// This file is part of the "PDB repository" project.
// For details, see https://github.com/egor-tensin/pdb-repo.
// Distributed under the MIT License.

#pragma once

#include "address.hpp"
#include "module.hpp"

#include <SafeInt.hpp>

#include <DbgHelp.h>
#include <Windows.h>

#include <climits>
#include <cstddef>
#include <cstring>
#include <stdexcept>
#include <string>

namespace pdb {
namespace symbol {

// MinGW-w64 (as of version 7.0) doesn't have SymTagEnum
typedef ULONG Tag;

constexpr Tag SYM_TAG_FUNCTION = 5;

#ifdef _MSC_VER
static_assert(static_cast<Tag>(SymTagFunction) == SYM_TAG_FUNCTION,
              "unexpected SymTagFunction value");
#endif

} // namespace symbol

class SymbolInfo {
public:
    typedef SYMBOL_INFO Raw;

    SymbolInfo() : raw{*reinterpret_cast<Raw*>(buffer)} {
        raw.SizeOfStruct = sizeof(Raw);
        raw.MaxNameLen = MAX_SYM_NAME;
    }

    explicit SymbolInfo(const Raw& raw) : SymbolInfo{} {
        if (raw.SizeOfStruct != sizeof(raw))
            throw std::runtime_error{"invalid SYMBOL_INFO.SizeOfStruct"};
        const auto raw_size = calc_size(raw);
        if (raw_size > sizeof(buffer))
            throw std::runtime_error{"SYMBOL_INFO is too large"};
        std::memcpy(buffer, &raw, raw_size);
    }

    explicit operator Raw&() { return raw; }

    explicit operator const Raw&() const { return raw; }

    Address get_displacement() const { return displacement; }

    void set_displacement(Address new_value) { displacement = new_value; }

    std::string get_name() const { return {raw.Name, raw.NameLen}; }

    Address get_offline_base() const { return raw.ModBase; }

    Address get_offline_address() const { return raw.Address; }

    symbol::Tag get_tag() const { return raw.Tag; }

    enum class Type : symbol::Tag {
        Function = symbol::SYM_TAG_FUNCTION,
        RESERVED = ULONG_MAX,
    };

    Type get_type() const { return static_cast<Type>(get_tag()); }

    bool is_function() const { return get_type() == Type::Function; }

private:
    static constexpr std::size_t max_buffer_size = sizeof(Raw) + MAX_SYM_NAME - 1;

    static std::size_t calc_size(const Raw& raw) {
        try {
            return SafeInt<std::size_t>{raw.SizeOfStruct} + raw.NameLen - 1;
        } catch (const SafeIntException&) {
            throw std::runtime_error{"invalid SYMBOL_INFO size"};
        }
    }

    unsigned char buffer[max_buffer_size] = {0};
    Address displacement = 0;

protected:
    Raw& raw;
};

class Symbol : public SymbolInfo {
public:
    Symbol(Address online_address, const SymbolInfo& info)
        : SymbolInfo{info}, online_address{online_address} {}

    Address get_online_address() const { return online_address; }

private:
    const Address online_address;
};

class LineInfo {
public:
    typedef IMAGEHLP_LINE64 Raw;

    explicit LineInfo(const Raw& raw)
        : file_path{raw.FileName}, line_number{cast_line_number(raw.LineNumber)} {}

    const std::string file_path;
    const unsigned long line_number;

private:
    static unsigned long cast_line_number(DWORD raw) {
        unsigned long dest = 0;

        if (!SafeCast(raw, dest))
            throw std::runtime_error{"invalid line number"};

        return dest;
    }
};

} // namespace pdb