remove /fadec and /raw_pdb content

This commit is contained in:
Sen66
2026-03-01 18:46:01 +01:00
parent ed8a44917b
commit 7524004b32
106 changed files with 0 additions and 24809 deletions

View File

@@ -1,16 +0,0 @@
image: alpine/edge
sources:
- https://git.sr.ht/~aengelke/fadec
packages:
- meson
tasks:
- build: |
mkdir fadec-build1
meson fadec-build1 fadec
ninja -C fadec-build1
ninja -C fadec-build1 test
# Complete test with encode2 API.
mkdir fadec-build2
meson fadec-build2 fadec -Dwith_encode2=true
ninja -C fadec-build2
ninja -C fadec-build2 test

View File

@@ -1,51 +0,0 @@
name: CI
on: [push]
jobs:
build-linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: sudo apt install -y ninja-build meson
- name: Configure
run: mkdir build; CC=clang CXX=clang++ meson -Dbuildtype=debugoptimized -Dwith_encode2=true build
- name: Build
run: ninja -v -C build
- name: Test
run: meson test -v -C build
build-linux-cmake:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: sudo apt install -y ninja-build cmake
- name: Configure
run: CC=clang CXX=clang++ cmake -B build -G Ninja -DFADEC_ENCODE2=ON
- name: Build
run: cmake --build build -v
- name: Test
run: ctest --test-dir build -V
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: pip install ninja meson
- name: Configure
run: mkdir build; meson setup --vsenv -Dbuildtype=debugoptimized -Dwith_encode2=true build
- name: Build
run: meson compile -v -C build
- name: Test
run: meson test -v -C build
build-windows-cmake:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Configure
run: cmake -B build -DFADEC_ENCODE2=ON
- name: Build
run: cmake --build build -v
- name: Test
run: ctest --test-dir build -V -C Debug

View File

@@ -1 +0,0 @@
/build/

View File

@@ -1,109 +0,0 @@
cmake_minimum_required(VERSION 3.23)
project(fadec LANGUAGES C)
enable_testing()
# TODO: make this actually optional
enable_language(CXX OPTIONAL)
# Options
set(FADEC_ARCHMODE "both" CACHE STRING "Support only 32-bit x86, 64-bit x86 or both")
set_property(CACHE FADEC_ARCHMODE PROPERTY STRINGS both only32 only64)
option(FADEC_UNDOC "Include undocumented instructions" FALSE)
option(FADEC_DECODE "Include support for decoding" TRUE)
option(FADEC_ENCODE "Include support for encoding" TRUE)
option(FADEC_ENCODE2 "Include support for new encoding API" FALSE)
set(CMAKE_C_STANDARD 11)
if (MSVC)
add_compile_options(/W4 -D_CRT_SECURE_NO_WARNINGS /wd4018 /wd4146 /wd4244 /wd4245 /wd4267 /wd4310)
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-Zc:preprocessor>)
else()
add_compile_options(-Wall -Wextra -Wpedantic -Wno-overlength-strings)
endif()
find_package(Python3 3.9 REQUIRED)
add_library(fadec)
add_library(fadec::fadec ALIAS fadec)
set_target_properties(fadec PROPERTIES
LINKER_LANGUAGE C
)
set(GEN_ARGS "")
if (NOT FADEC_ARCHMODE STREQUAL "only64")
list(APPEND GEN_ARGS "--32")
endif ()
if (NOT FADEC_ARCHMODE STREQUAL "only32")
list(APPEND GEN_ARGS "--64")
endif ()
if (FADEC_UNDOC)
list(APPEND GEN_ARGS "--with-undoc")
endif ()
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include")
function(fadec_component)
cmake_parse_arguments(ARG "" "NAME" "HEADERS;SOURCES" ${ARGN})
set(PRIV_INC ${CMAKE_CURRENT_BINARY_DIR}/include/fadec-${ARG_NAME}-private.inc)
set(PUB_INC ${CMAKE_CURRENT_BINARY_DIR}/include/fadec-${ARG_NAME}-public.inc)
add_custom_command(
OUTPUT ${PRIV_INC} ${PUB_INC}
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/parseinstrs.py ${ARG_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/instrs.txt
${PUB_INC} ${PRIV_INC} ${GEN_ARGS}
DEPENDS instrs.txt parseinstrs.py
COMMENT "Building table for ${ARG_NAME}"
)
list(APPEND FADEC_HEADERS ${PUB_INC})
target_sources(fadec PRIVATE
${ARG_SOURCES}
PUBLIC
FILE_SET HEADERS
BASE_DIRS .
FILES
${ARG_HEADERS}
PUBLIC
FILE_SET generated_public TYPE HEADERS
BASE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/include
FILES
${PUB_INC}
PRIVATE
FILE_SET generated_private TYPE HEADERS
BASE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/include
FILES
${PRIV_INC}
)
add_executable(fadec-${ARG_NAME}-test ${ARG_NAME}-test.c)
target_link_libraries(fadec-${ARG_NAME}-test PRIVATE fadec)
add_test(NAME ${ARG_NAME} COMMAND fadec-${ARG_NAME}-test)
if (CMAKE_CXX_COMPILER AND ${ARG_NAME} STREQUAL "encode2")
add_executable(fadec-${ARG_NAME}-test-cpp ${ARG_NAME}-test.cc)
target_link_libraries(fadec-${ARG_NAME}-test-cpp PRIVATE fadec)
add_test(NAME ${ARG_NAME}-cpp COMMAND fadec-${ARG_NAME}-test-cpp)
endif()
endfunction()
if (FADEC_DECODE)
fadec_component(NAME decode SOURCES decode.c format.c HEADERS fadec.h)
endif ()
if (FADEC_ENCODE)
fadec_component(NAME encode SOURCES encode.c HEADERS fadec-enc.h)
endif ()
if (FADEC_ENCODE2)
fadec_component(NAME encode2 SOURCES encode2.c HEADERS fadec-enc2.h)
endif ()
install(TARGETS fadec EXPORT fadec
LIBRARY
ARCHIVE
FILE_SET HEADERS FILE_SET generated_public)

View File

@@ -1,28 +0,0 @@
Copyright (c) 2018, Alexis Engelke
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,184 +0,0 @@
# Fadec — Fast Decoder for x86-32 and x86-64 and Encoder for x86-64
Fadec is a fast and lightweight decoder for x86-32 and x86-64. To meet the goal of speed, lookup tables are used to map the opcode the (internal) description of the instruction encoding. This table currently has a size of roughly 37 kiB (for 32/64-bit combined).
Fadec-Enc (or Faenc) is a small, lightweight and easy-to-use encoder, currently for x86-64 only.
## Key features
> **Q: Why not just use any other decoding/encoding library available out there?**
>
> A: I needed to embed a small and fast decoder in a project for a freestanding environment (i.e., no libc). Further, only very few plain encoding libraries are available for x86-64; and most of them are large or make heavy use of external dependencies.
- **Small size:** the entire library with the x86-64/32 decoder and the x86-64 encoder are only 95 kiB; for specific use cases, the size can be reduced even further (e.g., by dropping AVX-512). The main decode/encode routines are only a few hundreds lines of code.
- **Performance:** Fadec is significantly faster than libopcodes, Capstone, or Zydis due to the absence of high-level abstractions and the small lookup table.
- **Zero dependencies:** the entire library has no dependencies, even on the standard library, making it suitable for freestanding environments without a full libc or `malloc`-style memory allocation.
- **Correctness:** even corner cases should be handled correctly (if not, that's a bug), e.g., the order of prefixes, immediate sizes of jump instructions, the presence of the `lock` prefix, or properly handling VEX.W in 32-bit mode.
All components of this library target the Intel 64 implementations of x86. While AMD64 is _mostly similar_, there are some minor differences (e.g. operand sizes for jump instructions, more instructions, `cr8` can be accessed with `lock` prefix, `f34190` is `xchg`, not `pause`) which are currently not handled.
## Decoder Usage
### Example
```c
uint8_t buffer[] = {0x49, 0x90};
FdInstr instr;
// Decode from buffer into instr in 64-bit mode.
int ret = fd_decode(buffer, sizeof(buffer), 64, 0, &instr);
// ret<0 indicates an error, ret>0 the number of decoded bytes
// Relevant properties of instructions can now be queried using the FD_* macros.
// Or, we can format the instruction to a string buffer:
char fmtbuf[64];
fd_format(&instr, fmtbuf, sizeof(fmtbuf));
// fmtbuf now reads: "xchg r8, rax"
```
### API
The API consists of two functions to decode and format instructions, as well as several accessor macros. A full documentation can be found in [fadec.h](fadec.h). Direct access of any structure fields is not recommended.
- `int fd_decode(const uint8_t* buf, size_t len, int mode, uintptr_t address, FdInstr* out_instr)`
- Decode a single instruction. For internal performance reasons, note that:
- The decoded operand sizes are not always exact. However, the exact size can be reconstructed in all cases.
- An implicit `fwait` in FPU instructions is decoded as a separate instruction (matching the opcode layout in machine code). For example, `finit` is decoded as `FD_FWAIT` + `FD_FINIT`
- Return value: number of bytes used, or a negative value in case of an error.
- `buf`/`len`: buffer containing instruction bytes. At most 15 bytes will be read. If the instruction is longer than `len`, an error value is returned.
- `mode`: architecture mode, either `32` or `64`.
- `address`: set to `0`. (Obsolete use: virtual address of the decoded instruction.)
- `out_instr`: Pointer to the instruction buffer, might get written partially in case of an error.
- `void fd_format(const FdInstr* instr, char* buf, size_t len)`
- Format a single instruction to a human-readable format.
- `instr`: decoded instruction.
- `buf`/`len`: buffer for formatted instruction string
- Various accessor macros: see [fadec.h](fadec.h).
## Encoder Usage
The encoder has two API variants: "v1" has a single entry point (`fe_enc64`) and the instruction is specified as integer parameter. "v2" has one entry point per instruction. v2 is currently about 3x faster than v1, but also has much larger code size (v1: <10 kiB; v2: ~3 MiB) and takes much longer to compile. It is therefore off by default and can be enabled by passing `-Dwith_encode2=true` to Meson. Both variants are supported.
### Example (API v1)
```c
int failed = 0;
uint8_t buf[64];
uint8_t* cur = buf;
// xor eax, eax
failed |= fe_enc64(&cur, FE_XOR32rr, FE_AX, FE_AX);
// movzx ecx, byte ptr [rdi + 1*rax + 0]
failed |= fe_enc64(&cur, FE_MOVZXr32m8, FE_CX, FE_MEM(FE_DI, 1, FE_AX, 0));
// test ecx, ecx
failed |= fe_enc64(&cur, FE_TEST32rr, FE_CX, FE_CX);
// jnz $
// This will be replaced later; FE_JMPL enforces use of longest offset
uint8_t* fwd_jmp = cur;
failed |= fe_enc64(&cur, FE_JNZ|FE_JMPL, (intptr_t) cur);
uint8_t* loop_tgt = cur;
// add rax, rcx
failed |= fe_enc64(&cur, FE_ADD64rr, FE_AX, FE_CX);
// sub ecx, 1
failed |= fe_enc64(&cur, FE_SUB32ri, FE_CX, 1);
// jnz loop_tgt
failed |= fe_enc64(&cur, FE_JNZ, (intptr_t) loop_tgt);
// (alternatively: fe_enc64(&cur, FE_Jcc|FE_CC_NZ, (intptr_t) loop_tgt).)
// Update previous jump to jump here. Note that we _must_ specify FE_JMPL too.
failed |= fe_enc64(&fwd_jmp, FE_JNZ|FE_JMPL, (intptr_t) cur);
// ret
failed |= fe_enc64(&cur, FE_RET);
// cur now points to the end of the buffer, failed indicates any failures.
```
### Example (API v2)
```c
uint8_t buf[64];
uint8_t* cur = buf;
// xor eax, eax
cur += fe64_XOR32rr(cur, 0, FE_AX, FE_AX);
// movzx ecx, byte ptr [rdi + 1*rax + 0]
cur += fe64_MOVZXr32m8(cur, 0, FE_CX, FE_MEM(FE_DI, 1, FE_AX, 0));
// test ecx, ecx
cur += fe64_TEST32rr(cur, 0, FE_CX, FE_CX);
// jnz $
// This will be replaced later; FE_JMPL enforces use of longest offset
uint8_t* fwd_jmp = cur;
cur += fe64_JNZ(cur, FE_JMPL, cur);
uint8_t* loop_tgt = cur;
// add rax, rcx
cur += fe64_ADD64rr(cur, 0, FE_AX, FE_CX);
// sub ecx, 1
cur += fe64_SUB32ri(cur, 0, FE_CX, 1);
// jnz loop_tgt
cur += fe64_JNZ(cur, 0, loop_tgt);
// (alternatively: fe64_Jcc(cur, FE_CC_NZ, loop_tgt).)
// Update previous jump to jump here. Note that we _must_ specify FE_JMPL too.
fe64_JNZ(fwd_jmp, FE_JMPL, cur);
// ret
cur += fe64_RET(cur, 0);
// cur now points to the end of the buffer
// errors are ignored, this example should not cause any :-)
```
### API v1
The API consists of one function to handle encode requests, as well as some macros. More information can be found in [fadec-enc.h](fadec-enc.h). Usage of internals like enum values is not recommended.
- `int fe_enc64(uint8_t** buf, uint64_t mnem, int64_t operands...)`
- Encodes an instruction for x86-64 into `*buf`. EVEX-encoded instructions will transparently encode with the shorter VEX prefix where permitted.
- Return value: `0` on success, a negative value in error cases.
- `buf`: Pointer to the pointer to the instruction buffer. The pointer (`*buf`) will be advanced by the number of bytes written. The instruction buffer must have at least 15 bytes left.
- `mnem`: Instruction mnemonic to encode combined with extra flags:
- `FE_SEG(segreg)`: override segment to specified segment register.
- `FE_ADDR32`: override address size to 32-bit.
- `FE_JMPL`: use longest possible offset encoding, useful when jump target is not known.
- `FE_MASK(maskreg)`: specify non-zero mask register (1--7) for instructions that support masking (suffixed with `_mask` or `_maskz`) or require a mask (AVX-512 gather/scatter).
- `FE_RC_RN/RD/RU/RZ`: set rounding mode for instructions with static rounding control (suffixed `_er`).
- `FE_CC_O/NO/E/NE/...`: set condition code for instructions with unspecified condition code (`Jcc`, `SETcc`, `CMOVcc`, `CMPccXADD`).
- `operands...`: Up to 4 instruction operands. The operand kinds must match the requirements of the mnemonic.
- For register operands (`r`=non-mask register, `k`=mask register), use the register: `FE_AX`, `FE_AH`, `FE_XMM12`.
- For immediate operands (`i`=regular, `a`=absolute address), use the constant: `12`, `-0xbeef`.
- For memory operands (`m`=regular or `b`=broadcast), use: `FE_MEM(basereg,scale,indexreg,offset)`. Use `0` to specify _no register_. For RIP-relative addressing, the size of the instruction is added automatically.
- For offset operands (`o`), specify the target address.
### API v2
The API consists of one function per instruction, as well as some macros. The API provides type safety for different register types as well as for memory operands (regular vs. VSIB). Besides a few details listed here, the usage is very similar to API v1. More information can be found in [fadec-enc2.h](fadec-enc2.h). Usage of internals like enum values is not recommended.
- `int fe64_<mnemonic>(uint8_t* buf, int flags, <operands...>)`
- Encodes the specified instruction for x86-64 into `buf`. EVEX-encoded instructions will transparently encode with the shorter VEX prefix where permitted.
- Return value: `0` on failure, otherwise the instruction length.
- `buf`: Pointer to the instruction buffer. The instruction buffer must have at least 15 bytes left. Bytes beyond the returned instruction length can be overwritten.
- `flags`: combination of extra flags, default to `0`:
- `FE_SEG(segreg)`: override segment to specified segment register.
- `FE_ADDR32`: override address size to 32-bit.
- `FE_JMPL`: use longest possible offset encoding, useful when jump target is not known.
- `FE_RC_RN/RD/RU/RZ`: set rounding mode for instructions with static rounding control (suffixed `_er`).
- `FE_CC_O/NO/E/NE/...`: set condition code for instructions with unspecified condition code (`Jcc`, `SETcc`, `CMOVcc`, `CMPccXADD`).
- `FeRegMASK opmask` (instructions with opmask only): specify non-zero mask register (1--7) for instructions suffixed with `_mask`/`_maskz` and AVX-512 gather/scatter.
- `operands...`: up to four instruction operands.
- Registers have types `FeRegGP`/`FeRegXMM`/`FeRegMASK`/etc.; byte registers accepting high-byte operands also accept `FeRegGPH`.
- Immediate operands have an appropriately sized integer type.
- Memory operands use a `FeMem` (VSIB: `FeMemV`) structure, use the macro `FE_MEM(basereg,scale,indexreg,offset)` (VSIB: `FE_MEMV(...)`). Use `FE_NOREG` to specify _no register_. For RIP-relative addressing, the size of the instruction is added automatically.
- For offset operands (`o`), specify the target address relative to `buf`.
- `int fe64_NOP(uint8_t* buf, unsigned size)`
- Encode a series of `nop`s of `size` bytes, but at least emit one byte. This will use larger the `nop` encodings to reduce the number of instructions and is intended for filling padding.
## Known issues
- Decoder/Encoder: register uniqueness constraints are not enforced. This affects:
- VSIB-encoded instructions: no vector register may be used more than once
- AMX instructions: no tile register may be used more than once
- AVX-512 complex FP16 multiplication: destination must be not be equal to a source register
- Prefixes for indirect jumps and calls are not properly decoded, e.g. `notrack`, `bnd`.
- Low test coverage. (Help needed.)
- No Python API.
Some ISA extensions are not supported, often because they are deprecated or unsupported by recent hardware. These are unlikely to be implemented in the near future:
- (Intel) MPX: Intel lists MPX as deprecated.
- (Intel) HLE prefixes `xacquire`/`xrelease`: Intel lists HLE as deprecated. The formatter for decoded instructions is able to reconstruct these in most cases, though.
- (Intel) Xeon Phi (KNC/KNL/KNM) extensions, including the MVEX prefix: the hardware is discontinued/no longer available.
- (AMD) XOP: unsupported by newer hardware.
- (AMD) FMA4: unsupported by newer hardware.
If you find any other issues, please report a bug. Or, even better, send a patch fixing the issue.

File diff suppressed because it is too large Load Diff

View File

@@ -1,791 +0,0 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <fadec.h>
#ifdef __GNUC__
#define LIKELY(x) __builtin_expect((x), 1)
#define UNLIKELY(x) __builtin_expect((x), 0)
#define ASSUME(x) do { if (!(x)) __builtin_unreachable(); } while (0)
#else
#define LIKELY(x) (x)
#define UNLIKELY(x) (x)
#define ASSUME(x) ((void) 0)
#endif
// Defines FD_TABLE_OFFSET_32 and FD_TABLE_OFFSET_64, if available
#define FD_DECODE_TABLE_DEFINES
#include <fadec-decode-private.inc>
#undef FD_DECODE_TABLE_DEFINES
enum DecodeMode {
DECODE_64 = 0,
DECODE_32 = 1,
};
typedef enum DecodeMode DecodeMode;
#define ENTRY_NONE 0
#define ENTRY_INSTR 1
#define ENTRY_TABLE256 2
#define ENTRY_TABLE16 3
#define ENTRY_TABLE8E 4
#define ENTRY_TABLE_PREFIX 5
#define ENTRY_TABLE_VEX 6
#define ENTRY_TABLE_ROOT 8
#define ENTRY_MASK 7
static uint16_t
table_lookup(unsigned cur_idx, unsigned entry_idx) {
static _Alignas(16) const uint16_t _decode_table[] = {
#define FD_DECODE_TABLE_DATA
#include <fadec-decode-private.inc>
#undef FD_DECODE_TABLE_DATA
};
return _decode_table[cur_idx + entry_idx];
}
static unsigned
table_walk(unsigned table_entry, unsigned entry_idx) {
return table_lookup(table_entry & ~0x3, entry_idx);
}
#define LOAD_LE_1(buf) ((uint64_t) *(const uint8_t*) (buf))
#define LOAD_LE_2(buf) (LOAD_LE_1(buf) | LOAD_LE_1((const uint8_t*) (buf) + 1)<<8)
#define LOAD_LE_3(buf) (LOAD_LE_2(buf) | LOAD_LE_1((const uint8_t*) (buf) + 2)<<16)
#define LOAD_LE_4(buf) (LOAD_LE_2(buf) | LOAD_LE_2((const uint8_t*) (buf) + 2)<<16)
#define LOAD_LE_8(buf) (LOAD_LE_4(buf) | LOAD_LE_4((const uint8_t*) (buf) + 4)<<32)
enum
{
PREFIX_REXB = 0x01,
PREFIX_REXX = 0x02,
PREFIX_REXR = 0x04,
PREFIX_REXW = 0x08,
PREFIX_REX = 0x40,
PREFIX_REXRR = 0x10,
PREFIX_VEX = 0x20,
};
struct InstrDesc
{
uint16_t type;
uint16_t operand_indices;
uint16_t operand_sizes;
uint16_t reg_types;
};
#define DESC_HAS_MODRM(desc) (((desc)->operand_indices & (3 << 0)) != 0)
#define DESC_MODRM_IDX(desc) ((((desc)->operand_indices >> 0) & 3) ^ 3)
#define DESC_HAS_MODREG(desc) (((desc)->operand_indices & (3 << 2)) != 0)
#define DESC_MODREG_IDX(desc) ((((desc)->operand_indices >> 2) & 3) ^ 3)
#define DESC_HAS_VEXREG(desc) (((desc)->operand_indices & (3 << 4)) != 0)
#define DESC_VEXREG_IDX(desc) ((((desc)->operand_indices >> 4) & 3) ^ 3)
#define DESC_IMM_CONTROL(desc) (((desc)->operand_indices >> 12) & 0x7)
#define DESC_IMM_IDX(desc) ((((desc)->operand_indices >> 6) & 3) ^ 3)
#define DESC_EVEX_BCST(desc) (((desc)->operand_indices >> 8) & 1)
#define DESC_EVEX_MASK(desc) (((desc)->operand_indices >> 9) & 1)
#define DESC_ZEROREG_VAL(desc) (((desc)->operand_indices >> 10) & 1)
#define DESC_LOCK(desc) (((desc)->operand_indices >> 11) & 1)
#define DESC_VSIB(desc) (((desc)->operand_indices >> 15) & 1)
#define DESC_OPSIZE(desc) (((desc)->reg_types >> 11) & 7)
#define DESC_MODRM_SIZE(desc) (((desc)->operand_sizes >> 0) & 3)
#define DESC_MODREG_SIZE(desc) (((desc)->operand_sizes >> 2) & 3)
#define DESC_VEXREG_SIZE(desc) (((desc)->operand_sizes >> 4) & 3)
#define DESC_IMM_SIZE(desc) (((desc)->operand_sizes >> 6) & 3)
#define DESC_LEGACY(desc) (((desc)->operand_sizes >> 8) & 1)
#define DESC_SIZE_FIX1(desc) (((desc)->operand_sizes >> 10) & 7)
#define DESC_SIZE_FIX2(desc) (((desc)->operand_sizes >> 13) & 3)
#define DESC_INSTR_WIDTH(desc) (((desc)->operand_sizes >> 15) & 1)
#define DESC_MODRM(desc) (((desc)->reg_types >> 14) & 1)
#define DESC_IGN66(desc) (((desc)->reg_types >> 15) & 1)
#define DESC_EVEX_SAE(desc) (((desc)->reg_types >> 8) & 1)
#define DESC_EVEX_ER(desc) (((desc)->reg_types >> 9) & 1)
#define DESC_EVEX_BCST16(desc) (((desc)->reg_types >> 10) & 1)
#define DESC_REGTY_MODRM(desc) (((desc)->reg_types >> 0) & 7)
#define DESC_REGTY_MODREG(desc) (((desc)->reg_types >> 3) & 7)
#define DESC_REGTY_VEXREG(desc) (((desc)->reg_types >> 6) & 3)
int
fd_decode(const uint8_t* buffer, size_t len_sz, int mode_int, uintptr_t address,
FdInstr* instr)
{
int len = len_sz > 15 ? 15 : len_sz;
// Ensure that we can actually handle the decode request
DecodeMode mode;
unsigned table_root_idx;
switch (mode_int)
{
#if defined(FD_TABLE_OFFSET_32)
case 32: table_root_idx = FD_TABLE_OFFSET_32; mode = DECODE_32; break;
#endif
#if defined(FD_TABLE_OFFSET_64)
case 64: table_root_idx = FD_TABLE_OFFSET_64; mode = DECODE_64; break;
#endif
default: return FD_ERR_INTERNAL;
}
int off = 0;
uint8_t vex_operand = 0;
uint8_t addr_size = mode == DECODE_64 ? 3 : 2;
unsigned prefix_rex = 0;
uint8_t prefix_rep = 0;
unsigned vexl = 0;
unsigned prefix_evex = 0;
instr->segment = FD_REG_NONE;
// Values must match prefixes in parseinstrs.py.
enum {
PF_SEG1 = 0xfff8 - 0xfff8,
PF_SEG2 = 0xfff9 - 0xfff8,
PF_66 = 0xfffa - 0xfff8,
PF_67 = 0xfffb - 0xfff8,
PF_LOCK = 0xfffc - 0xfff8,
PF_REP = 0xfffd - 0xfff8,
PF_REX = 0xfffe - 0xfff8,
};
uint8_t prefixes[8] = {0};
unsigned table_entry = 0;
while (true) {
if (UNLIKELY(off >= len))
return FD_ERR_PARTIAL;
uint8_t prefix = buffer[off];
table_entry = table_lookup(table_root_idx, prefix);
if (LIKELY(table_entry - 0xfff8 >= 8))
break;
prefixes[PF_REX] = 0;
prefixes[table_entry - 0xfff8] = prefix;
off++;
}
if (off) {
if (UNLIKELY(prefixes[PF_SEG2])) {
if (prefixes[PF_SEG2] & 0x02)
instr->segment = prefixes[PF_SEG2] >> 3 & 3;
else
instr->segment = prefixes[PF_SEG2] & 7;
}
if (UNLIKELY(prefixes[PF_67]))
addr_size--;
prefix_rex = prefixes[PF_REX];
prefix_rep = prefixes[PF_REP];
}
// table_entry kinds: INSTR(0), T16(1), ESCAPE_A(2), ESCAPE_B(3)
if (LIKELY(!(table_entry & 2))) {
off++;
// Then, walk through ModR/M-encoded opcode extensions.
if (table_entry & 1) {
if (UNLIKELY(off >= len))
return FD_ERR_PARTIAL;
unsigned isreg = buffer[off] >= 0xc0;
table_entry = table_walk(table_entry, ((buffer[off] >> 2) & 0xe) | isreg);
// table_entry kinds: INSTR(0), T8E(1)
if (table_entry & 1)
table_entry = table_walk(table_entry, buffer[off] & 7);
}
// table_entry kinds: INSTR(0)
goto direct;
}
if (UNLIKELY(off >= len))
return FD_ERR_PARTIAL;
unsigned opcode_escape = 0;
uint8_t mandatory_prefix = 0; // without escape/VEX/EVEX, this is ignored.
if (buffer[off] == 0x0f)
{
if (UNLIKELY(off + 1 >= len))
return FD_ERR_PARTIAL;
if (buffer[off + 1] == 0x38)
opcode_escape = 2;
else if (buffer[off + 1] == 0x3a)
opcode_escape = 3;
else
opcode_escape = 1;
off += opcode_escape >= 2 ? 2 : 1;
// If there is no REP/REPNZ prefix offer 66h as mandatory prefix. If
// there is a REP prefix, then the 66h prefix is ignored here.
mandatory_prefix = prefix_rep ? prefix_rep ^ 0xf1 : !!prefixes[PF_66];
}
else if (UNLIKELY((unsigned) buffer[off] - 0xc4 < 2 || buffer[off] == 0x62))
{
unsigned vex_prefix = buffer[off];
// VEX (C4/C5) or EVEX (62)
if (UNLIKELY(off + 1 >= len))
return FD_ERR_PARTIAL;
if (UNLIKELY(mode == DECODE_32 && buffer[off + 1] < 0xc0)) {
off++;
table_entry = table_walk(table_entry, 0);
// table_entry kinds: INSTR(0)
goto direct;
}
// VEX/EVEX + 66/F3/F2/REX will #UD.
// Note: REX is also here only respected if it immediately precedes the
// opcode, in this case the VEX/EVEX "prefix".
if (prefixes[PF_66] || prefixes[PF_REP] || prefix_rex)
return FD_ERR_UD;
uint8_t byte = buffer[off + 1];
if (vex_prefix == 0xc5) // 2-byte VEX
{
opcode_escape = 1;
prefix_rex = byte & 0x80 ? 0 : PREFIX_REXR;
}
else // 3-byte VEX or EVEX
{
// SDM Vol 2A 2-15 (Dec. 2016): Ignored in 32-bit mode
if (mode == DECODE_64)
prefix_rex = byte >> 5 ^ 0x7;
if (vex_prefix == 0x62) // EVEX
{
if (byte & 0x08) // Bit 3 of opcode_escape must be clear.
return FD_ERR_UD;
_Static_assert(PREFIX_REXRR == 0x10, "wrong REXRR value");
if (mode == DECODE_64)
prefix_rex |= (byte & PREFIX_REXRR) ^ PREFIX_REXRR;
}
else // 3-byte VEX
{
if (byte & 0x18) // Bits 4:3 of opcode_escape must be clear.
return FD_ERR_UD;
}
opcode_escape = (byte & 0x07);
if (UNLIKELY(opcode_escape == 0)) {
int prefix_len = vex_prefix == 0x62 ? 4 : 3;
// Pretend to decode the prefix plus one opcode byte.
return off + prefix_len > len ? FD_ERR_PARTIAL : FD_ERR_UD;
}
// Load third byte of VEX prefix
if (UNLIKELY(off + 2 >= len))
return FD_ERR_PARTIAL;
byte = buffer[off + 2];
prefix_rex |= byte & 0x80 ? PREFIX_REXW : 0;
}
mandatory_prefix = byte & 3;
vex_operand = ((byte & 0x78) >> 3) ^ 0xf;
prefix_rex |= PREFIX_VEX;
if (vex_prefix == 0x62) // EVEX
{
if (!(byte & 0x04)) // Bit 10 must be 1.
return FD_ERR_UD;
if (UNLIKELY(off + 3 >= len))
return FD_ERR_PARTIAL;
byte = buffer[off + 3];
// prefix_evex is z:L'L/RC:b:V':aaa
vexl = (byte >> 5) & 3;
prefix_evex = byte | 0x100; // Ensure that prefix_evex is non-zero.
if (mode == DECODE_64) // V' causes UD in 32-bit mode
vex_operand |= byte & 0x08 ? 0 : 0x10; // V'
else if (!(byte & 0x08))
return FD_ERR_UD;
off += 4;
}
else // VEX
{
vexl = byte & 0x04 ? 1 : 0;
off += 0xc7 - vex_prefix; // 3 for c4, 2 for c5
}
}
table_entry = table_walk(table_entry, opcode_escape);
// table_entry kinds: INSTR(0) [only for invalid], T256(2)
if (UNLIKELY(!table_entry))
return FD_ERR_UD;
if (UNLIKELY(off >= len))
return FD_ERR_PARTIAL;
table_entry = table_walk(table_entry, buffer[off++]);
// table_entry kinds: INSTR(0), T16(1), TVEX(2), TPREFIX(3)
// Handle mandatory prefixes (which behave like an opcode ext.).
if ((table_entry & 3) == 3)
table_entry = table_walk(table_entry, mandatory_prefix);
// table_entry kinds: INSTR(0), T16(1), TVEX(2)
// Then, walk through ModR/M-encoded opcode extensions.
if (table_entry & 1) {
if (UNLIKELY(off >= len))
return FD_ERR_PARTIAL;
unsigned isreg = buffer[off] >= 0xc0;
table_entry = table_walk(table_entry, ((buffer[off] >> 2) & 0xe) | isreg);
// table_entry kinds: INSTR(0), T8E(1), TVEX(2)
if (table_entry & 1)
table_entry = table_walk(table_entry, buffer[off] & 7);
}
// table_entry kinds: INSTR(0), TVEX(2)
// For VEX prefix, we have to distinguish between VEX.W and VEX.L which may
// be part of the opcode.
if (UNLIKELY(table_entry & 2))
{
uint8_t index = 0;
index |= prefix_rex & PREFIX_REXW ? (1 << 0) : 0;
// When EVEX.L'L is the rounding mode, the instruction must not have
// L'L constraints.
index |= vexl << 1;
table_entry = table_walk(table_entry, index);
}
// table_entry kinds: INSTR(0)
direct:
// table_entry kinds: INSTR(0)
if (UNLIKELY(!table_entry))
return FD_ERR_UD;
static _Alignas(16) const struct InstrDesc descs[] = {
#define FD_DECODE_TABLE_DESCS
#include <fadec-decode-private.inc>
#undef FD_DECODE_TABLE_DESCS
};
const struct InstrDesc* desc = &descs[table_entry >> 2];
instr->type = desc->type;
instr->addrsz = addr_size;
instr->flags = ((prefix_rep + 1) & 6) + (mode == DECODE_64 ? FD_FLAG_64 : 0);
instr->address = address;
for (unsigned i = 0; i < sizeof(instr->operands) / sizeof(FdOp); i++)
instr->operands[i] = (FdOp) {0};
if (DESC_MODRM(desc) && UNLIKELY(off++ >= len))
return FD_ERR_PARTIAL;
unsigned op_byte = buffer[off - 1] | (!DESC_MODRM(desc) ? 0xc0 : 0);
if (UNLIKELY(prefix_evex)) {
// VSIB inst (gather/scatter) without mask register or w/EVEX.z is UD
if (DESC_VSIB(desc) && (!(prefix_evex & 0x07) || (prefix_evex & 0x80)))
return FD_ERR_UD;
// Inst doesn't support masking, so EVEX.z or EVEX.aaa is UD
if (!DESC_EVEX_MASK(desc) && (prefix_evex & 0x87))
return FD_ERR_UD;
// EVEX.z without EVEX.aaa is UD. The Intel SDM is rather unprecise
// about this, but real hardware doesn't accept this.
if ((prefix_evex & 0x87) == 0x80)
return FD_ERR_UD;
// Cases for SAE/RC (reg operands only):
// - ER supported -> all ok
// - SAE supported -> assume L'L is RC, but ignored (undocumented)
// - Neither supported -> b == 0
if ((prefix_evex & 0x10) && (op_byte & 0xc0) == 0xc0) { // EVEX.b+reg
if (!DESC_EVEX_SAE(desc))
return FD_ERR_UD;
vexl = 2;
if (DESC_EVEX_ER(desc))
instr->evex = prefix_evex;
else
instr->evex = (prefix_evex & 0x87) | 0x60; // set RC, clear B
} else {
if (UNLIKELY(vexl == 3)) // EVEX.L'L == 11b is UD
return FD_ERR_UD;
instr->evex = prefix_evex & 0x87; // clear RC, clear B
}
if (DESC_VSIB(desc))
vex_operand &= 0xf; // EVEX.V' is used as index extension instead.
} else {
instr->evex = 0;
}
unsigned op_size;
unsigned op_size_alt = 0;
if (!(DESC_OPSIZE(desc) & 4)) {
if (mode == DECODE_64)
op_size = ((prefix_rex & PREFIX_REXW) || DESC_OPSIZE(desc) == 3) ? 4 :
UNLIKELY(prefixes[PF_66] && !DESC_IGN66(desc)) ? 2 :
DESC_OPSIZE(desc) ? 4 :
3;
else
op_size = UNLIKELY(prefixes[PF_66] && !DESC_IGN66(desc)) ? 2 : 3;
} else {
op_size = 5 + vexl;
op_size_alt = op_size - (DESC_OPSIZE(desc) & 3);
}
uint8_t operand_sizes[4] = {
DESC_SIZE_FIX1(desc), DESC_SIZE_FIX2(desc) + 1, op_size, op_size_alt
};
if (UNLIKELY(instr->type == FDI_MOV_CR || instr->type == FDI_MOV_DR)) {
unsigned modreg = (op_byte >> 3) & 0x7;
unsigned modrm = op_byte & 0x7;
FdOp* op_modreg = &instr->operands[DESC_MODREG_IDX(desc)];
op_modreg->type = FD_OT_REG;
op_modreg->size = op_size;
op_modreg->reg = modreg | (prefix_rex & PREFIX_REXR ? 8 : 0);
op_modreg->misc = instr->type == FDI_MOV_CR ? FD_RT_CR : FD_RT_DR;
if (instr->type == FDI_MOV_CR && (~0x011d >> op_modreg->reg) & 1)
return FD_ERR_UD;
else if (instr->type == FDI_MOV_DR && prefix_rex & PREFIX_REXR)
return FD_ERR_UD;
FdOp* op_modrm = &instr->operands[DESC_MODRM_IDX(desc)];
op_modrm->type = FD_OT_REG;
op_modrm->size = op_size;
op_modrm->reg = modrm | (prefix_rex & PREFIX_REXB ? 8 : 0);
op_modrm->misc = FD_RT_GPL;
goto skip_modrm;
}
if (DESC_HAS_MODREG(desc))
{
FdOp* op_modreg = &instr->operands[DESC_MODREG_IDX(desc)];
unsigned reg_idx = (op_byte & 0x38) >> 3;
unsigned reg_ty = DESC_REGTY_MODREG(desc);
op_modreg->misc = reg_ty;
if (LIKELY(reg_ty < 2))
reg_idx += prefix_rex & PREFIX_REXR ? 8 : 0;
else if (reg_ty == 7 && (prefix_rex & PREFIX_REXR || prefix_evex & 0x80))
return FD_ERR_UD; // REXR in 64-bit mode or EVEX.z with mask as dest
if (UNLIKELY(reg_ty == FD_RT_VEC)) // REXRR ignored above in 32-bit mode
reg_idx += prefix_rex & PREFIX_REXRR ? 16 : 0;
else if (UNLIKELY(prefix_rex & PREFIX_REXRR))
return FD_ERR_UD;
op_modreg->type = FD_OT_REG;
op_modreg->size = operand_sizes[DESC_MODREG_SIZE(desc)];
op_modreg->reg = reg_idx;
}
if (DESC_HAS_MODRM(desc))
{
FdOp* op_modrm = &instr->operands[DESC_MODRM_IDX(desc)];
op_modrm->size = operand_sizes[DESC_MODRM_SIZE(desc)];
unsigned rm = op_byte & 0x07;
if (op_byte >= 0xc0)
{
uint8_t reg_idx = rm;
unsigned reg_ty = DESC_REGTY_MODRM(desc);
op_modrm->misc = reg_ty;
if (LIKELY(reg_ty < 2))
reg_idx += prefix_rex & PREFIX_REXB ? 8 : 0;
if (prefix_evex && reg_ty == 0) // vector registers only
reg_idx += prefix_rex & PREFIX_REXX ? 16 : 0;
op_modrm->type = FD_OT_REG;
op_modrm->reg = reg_idx;
}
else
{
unsigned dispscale = 0;
if (UNLIKELY(prefix_evex)) {
// EVEX.z for memory destination operand is UD.
if (UNLIKELY(prefix_evex & 0x80) && DESC_MODRM_IDX(desc) == 0)
return FD_ERR_UD;
// EVEX.b for memory-operand without broadcast support is UD.
if (UNLIKELY(prefix_evex & 0x10)) {
if (UNLIKELY(!DESC_EVEX_BCST(desc)))
return FD_ERR_UD;
if (UNLIKELY(DESC_EVEX_BCST16(desc)))
dispscale = 1;
else
dispscale = prefix_rex & PREFIX_REXW ? 3 : 2;
instr->segment |= dispscale << 6; // Store broadcast size
op_modrm->type = FD_OT_MEMBCST;
} else {
dispscale = op_modrm->size - 1;
op_modrm->type = FD_OT_MEM;
}
} else {
op_modrm->type = FD_OT_MEM;
}
// 16-bit address size implies different ModRM encoding
if (UNLIKELY(addr_size == 1)) {
ASSUME(mode == DECODE_32);
if (UNLIKELY(DESC_VSIB(desc))) // 16-bit addr size + VSIB is UD
return FD_ERR_UD;
if (rm < 6)
op_modrm->misc = rm & 1 ? FD_REG_DI : FD_REG_SI;
else
op_modrm->misc = FD_REG_NONE;
if (rm < 4)
op_modrm->reg = rm & 2 ? FD_REG_BP : FD_REG_BX;
else if (rm < 6 || (op_byte & 0xc7) == 0x06)
op_modrm->reg = FD_REG_NONE;
else
op_modrm->reg = rm == 6 ? FD_REG_BP : FD_REG_BX;
const uint8_t* dispbase = &buffer[off];
if (op_byte & 0x40) {
if (UNLIKELY((off += 1) > len))
return FD_ERR_PARTIAL;
instr->disp = (int8_t) LOAD_LE_1(dispbase) * (1 << dispscale);
} else if (op_byte & 0x80 || (op_byte & 0xc7) == 0x06) {
if (UNLIKELY((off += 2) > len))
return FD_ERR_PARTIAL;
instr->disp = (int16_t) LOAD_LE_2(dispbase);
} else {
instr->disp = 0;
}
goto end_modrm;
}
// SIB byte
uint8_t base = rm;
if (rm == 4) {
if (UNLIKELY(off >= len))
return FD_ERR_PARTIAL;
uint8_t sib = buffer[off++];
unsigned scale = sib & 0xc0;
unsigned idx = (sib & 0x38) >> 3;
idx += prefix_rex & PREFIX_REXX ? 8 : 0;
base = sib & 0x07;
if (idx == 4)
idx = FD_REG_NONE;
op_modrm->misc = scale | idx;
} else {
op_modrm->misc = FD_REG_NONE;
}
if (UNLIKELY(DESC_VSIB(desc))) {
// VSIB must have a memory operand with SIB byte.
if (rm != 4)
return FD_ERR_UD;
_Static_assert(FD_REG_NONE == 0x3f, "unexpected FD_REG_NONE");
// idx 4 is valid for VSIB
if ((op_modrm->misc & 0x3f) == FD_REG_NONE)
op_modrm->misc &= 0xc4;
if (prefix_evex) // EVEX.V':EVEX.X:SIB.idx
op_modrm->misc |= prefix_evex & 0x8 ? 0 : 0x10;
}
// RIP-relative addressing only if SIB-byte is absent
if (op_byte < 0x40 && rm == 5 && mode == DECODE_64)
op_modrm->reg = FD_REG_IP;
else if (op_byte < 0x40 && base == 5)
op_modrm->reg = FD_REG_NONE;
else
op_modrm->reg = base + (prefix_rex & PREFIX_REXB ? 8 : 0);
const uint8_t* dispbase = &buffer[off];
if (op_byte & 0x40) {
if (UNLIKELY((off += 1) > len))
return FD_ERR_PARTIAL;
instr->disp = (int8_t) LOAD_LE_1(dispbase) * (1 << dispscale);
} else if (op_byte & 0x80 || (op_byte < 0x40 && base == 5)) {
if (UNLIKELY((off += 4) > len))
return FD_ERR_PARTIAL;
instr->disp = (int32_t) LOAD_LE_4(dispbase);
} else {
instr->disp = 0;
}
end_modrm:;
}
}
if (UNLIKELY(DESC_HAS_VEXREG(desc)))
{
FdOp* operand = &instr->operands[DESC_VEXREG_IDX(desc)];
if (DESC_ZEROREG_VAL(desc)) {
operand->type = FD_OT_REG;
operand->size = 1;
operand->reg = FD_REG_CL;
operand->misc = FD_RT_GPL;
} else {
operand->type = FD_OT_REG;
// Without VEX prefix, this encodes an implicit register
operand->size = operand_sizes[DESC_VEXREG_SIZE(desc)];
if (mode == DECODE_32)
vex_operand &= 0x7;
// Note: 32-bit will never UD here. EVEX.V' is caught above already.
// Note: UD if > 16 for non-VEC. No EVEX-encoded instruction uses
// EVEX.vvvv to refer to non-vector registers. Verified in parseinstrs.
operand->reg = vex_operand;
unsigned reg_ty = DESC_REGTY_VEXREG(desc); // VEC GPL MSK FPU/TMM
if (prefix_rex & PREFIX_VEX) { // TMM with VEX, FPU otherwise
// In 64-bit mode: UD if FD_RT_MASK and vex_operand&8 != 0
if (reg_ty == 2 && vex_operand >= 8)
return FD_ERR_UD;
if (UNLIKELY(reg_ty == 3)) // TMM
operand->reg &= 0x7; // TODO: verify
operand->misc = (06710 >> (3 * reg_ty)) & 0x7;
} else {
operand->misc = (04710 >> (3 * reg_ty)) & 0x7;
}
}
}
else if (vex_operand != 0)
{
// TODO: bit 3 ignored in 32-bit mode? unverified
return FD_ERR_UD;
}
uint32_t imm_control = UNLIKELY(DESC_IMM_CONTROL(desc));
if (LIKELY(!imm_control)) {
} else if (UNLIKELY(imm_control == 1))
{
// 1 = immediate constant 1, used for shifts
FdOp* operand = &instr->operands[DESC_IMM_IDX(desc)];
operand->type = FD_OT_IMM;
operand->size = 1;
instr->imm = 1;
}
else if (UNLIKELY(imm_control == 2))
{
// 2 = memory, address-sized, used for mov with moffs operand
FdOp* operand = &instr->operands[DESC_IMM_IDX(desc)];
operand->type = FD_OT_MEM;
operand->size = operand_sizes[DESC_IMM_SIZE(desc)];
operand->reg = FD_REG_NONE;
operand->misc = FD_REG_NONE;
int moffsz = 1 << addr_size;
if (UNLIKELY(off + moffsz > len))
return FD_ERR_PARTIAL;
if (moffsz == 2)
instr->disp = LOAD_LE_2(&buffer[off]);
if (moffsz == 4)
instr->disp = LOAD_LE_4(&buffer[off]);
if (LIKELY(moffsz == 8))
instr->disp = LOAD_LE_8(&buffer[off]);
off += moffsz;
}
else if (UNLIKELY(imm_control == 3))
{
// 3 = register in imm8[7:4], used for RVMR encoding with VBLENDVP[SD]
FdOp* operand = &instr->operands[DESC_IMM_IDX(desc)];
operand->type = FD_OT_REG;
operand->size = op_size;
operand->misc = FD_RT_VEC;
if (UNLIKELY(off + 1 > len))
return FD_ERR_PARTIAL;
uint8_t reg = (uint8_t) LOAD_LE_1(&buffer[off]);
off += 1;
if (mode == DECODE_32)
reg &= 0x7f;
operand->reg = reg >> 4;
instr->imm = reg & 0x0f;
}
else if (imm_control != 0)
{
// 4/5 = immediate, operand-sized/8 bit
// 6/7 = offset, operand-sized/8 bit (used for jumps/calls)
int imm_byte = imm_control & 1;
int imm_offset = imm_control & 2;
FdOp* operand = &instr->operands[DESC_IMM_IDX(desc)];
operand->type = FD_OT_IMM;
if (imm_byte) {
if (UNLIKELY(off + 1 > len))
return FD_ERR_PARTIAL;
instr->imm = (int8_t) LOAD_LE_1(&buffer[off++]);
operand->size = DESC_IMM_SIZE(desc) & 1 ? 1 : op_size;
} else {
operand->size = operand_sizes[DESC_IMM_SIZE(desc)];
uint8_t imm_size;
if (UNLIKELY(instr->type == FDI_RET || instr->type == FDI_RETF ||
instr->type == FDI_SSE_EXTRQ ||
instr->type == FDI_SSE_INSERTQ))
imm_size = 2;
else if (UNLIKELY(instr->type == FDI_JMPF || instr->type == FDI_CALLF))
imm_size = (1 << op_size >> 1) + 2;
else if (UNLIKELY(instr->type == FDI_ENTER))
imm_size = 3;
else if (instr->type == FDI_MOVABS)
imm_size = (1 << op_size >> 1);
else
imm_size = op_size == 2 ? 2 : 4;
if (UNLIKELY(off + imm_size > len))
return FD_ERR_PARTIAL;
if (imm_size == 2)
instr->imm = (int16_t) LOAD_LE_2(&buffer[off]);
else if (imm_size == 3)
instr->imm = LOAD_LE_3(&buffer[off]);
else if (imm_size == 4)
instr->imm = (int32_t) LOAD_LE_4(&buffer[off]);
else if (imm_size == 6)
instr->imm = LOAD_LE_4(&buffer[off]) | LOAD_LE_2(&buffer[off+4]) << 32;
else if (imm_size == 8)
instr->imm = (int64_t) LOAD_LE_8(&buffer[off]);
off += imm_size;
}
if (imm_offset)
{
if (instr->address != 0)
instr->imm += instr->address + off;
else
operand->type = FD_OT_OFF;
}
}
skip_modrm:
if (UNLIKELY(prefixes[PF_LOCK])) {
if (!DESC_LOCK(desc) || instr->operands[0].type != FD_OT_MEM)
return FD_ERR_UD;
instr->flags |= FD_FLAG_LOCK;
}
if (UNLIKELY(DESC_LEGACY(desc))) {
// Without REX prefix, convert one-byte GP regs to high-byte regs
// This actually only applies to SZ8/MOVSX/MOVZX; but no VEX-encoded
// instructions have a byte-sized GP register in the first two operands.
if (!(prefix_rex & PREFIX_REX)) {
for (int i = 0; i < 2; i++) {
FdOp* operand = &instr->operands[i];
if (operand->type == FD_OT_NONE)
break;
if (operand->type == FD_OT_REG && operand->misc == FD_RT_GPL &&
operand->size == 1 && operand->reg >= 4)
operand->misc = FD_RT_GPH;
}
}
if (instr->type == FDI_XCHG_NOP) {
// Only 4890, 90, and 6690 are true NOPs.
if (instr->operands[0].reg == 0) {
instr->operands[0].type = FD_OT_NONE;
instr->operands[1].type = FD_OT_NONE;
instr->type = FD_HAS_REP(instr) ? FDI_PAUSE : FDI_NOP;
} else if ((instr->operands[0].reg & 7) == 0 && FD_HAS_REP(instr)) {
// On Intel, REX.B is ignored for F3.90.
instr->operands[0].type = FD_OT_NONE;
instr->operands[1].type = FD_OT_NONE;
instr->type = FDI_PAUSE;
} else {
instr->type = FDI_XCHG;
}
}
if (UNLIKELY(instr->type == FDI_3DNOW)) {
unsigned opc3dn = instr->imm;
if (opc3dn & 0x40)
return FD_ERR_UD;
uint64_t msk = opc3dn & 0x80 ? 0x88d144d144d14400 : 0x30003000;
if (!(msk >> (opc3dn & 0x3f) & 1))
return FD_ERR_UD;
}
instr->operandsz = UNLIKELY(DESC_INSTR_WIDTH(desc)) ? op_size - 1 : 0;
} else {
instr->operandsz = 0;
}
instr->size = off;
return off;
}

View File

@@ -1,62 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <fadec-enc.h>
static
void
print_hex(const uint8_t* buf, size_t len)
{
for (size_t i = 0; i < len; i++)
printf("%02x", buf[i]);
}
static
int
test(uint8_t* buf, const char* name, uint64_t mnem, uint64_t op0, uint64_t op1, uint64_t op2, uint64_t op3, const void* exp, size_t exp_len)
{
memset(buf, 0, 16);
uint8_t* inst = buf;
int res = fe_enc64(&inst, mnem, op0, op1, op2, op3);
if ((res != 0) != (exp_len == 0)) goto fail;
if (inst - buf != (ptrdiff_t) exp_len) goto fail;
if (memcmp(buf, exp, exp_len)) goto fail;
return 0;
fail:
printf("Failed case %s:\n", name);
printf(" Exp (%2zu): ", exp_len);
print_hex(exp, exp_len);
printf("\n Got (%2zd): ", inst - buf);
print_hex(buf, inst - buf);
printf("\n");
return -1;
}
#define TEST2(str, exp, exp_len, mnem, flags, op0, op1, op2, op3, ...) test(buf, str, FE_ ## mnem|flags, op0, op1, op2, op3, exp, exp_len)
#define TEST1(str, exp, ...) TEST2(str, exp, sizeof(exp)-1, __VA_ARGS__, 0, 0, 0, 0, 0)
#define TEST(exp, ...) failed |= TEST1(#__VA_ARGS__, exp, __VA_ARGS__)
int
main(int argc, char** argv)
{
(void) argc; (void) argv;
int failed = 0;
uint8_t buf[16];
// VSIB encoding doesn't differ for this API
#define FE_MEMV FE_MEM
#define FE_PTR(off) ((intptr_t) buf + (off))
#define FLAGMASK(flags, mask) (flags | FE_MASK(mask & 7))
#include "encode-test.inc"
puts(failed ? "Some tests FAILED" : "All tests PASSED");
return failed ? EXIT_FAILURE : EXIT_SUCCESS;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,460 +0,0 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <fadec-enc.h>
#ifdef __GNUC__
#define LIKELY(x) __builtin_expect((x), 1)
#define UNLIKELY(x) __builtin_expect((x), 0)
#else
#define LIKELY(x) (x)
#define UNLIKELY(x) (x)
#endif
#define OPC_66 0x80000
#define OPC_F2 0x100000
#define OPC_F3 0x200000
#define OPC_REXW 0x400000
#define OPC_LOCK 0x800000
#define OPC_VEXL0 0x1000000
#define OPC_VEXL1 0x1800000
#define OPC_EVEXL0 0x2000000
#define OPC_EVEXL1 0x2800000
#define OPC_EVEXL2 0x3000000
#define OPC_EVEXL3 0x3800000
#define OPC_EVEXB 0x4000000
#define OPC_VSIB 0x8000000
#define OPC_67 FE_ADDR32
#define OPC_SEG_MSK 0xe0000000
#define OPC_JMPL FE_JMPL
#define OPC_MASK_MSK 0xe00000000
#define OPC_EVEXZ 0x1000000000
#define OPC_USER_MSK (OPC_67|OPC_SEG_MSK|OPC_MASK_MSK)
#define OPC_FORCE_SIB 0x2000000000
#define OPC_DOWNGRADE_VEX 0x4000000000
#define OPC_DOWNGRADE_VEX_FLIPW 0x40000000000
#define OPC_EVEX_DISP8SCALE 0x38000000000
#define OPC_GPH_OP0 0x200000000000
#define OPC_GPH_OP1 0x400000000000
#define EPFX_REX_MSK 0x43f
#define EPFX_REX 0x20
#define EPFX_EVEX 0x40
#define EPFX_REXR 0x10
#define EPFX_REXX 0x08
#define EPFX_REXB 0x04
#define EPFX_REXR4 0x02
#define EPFX_REXB4 0x01
#define EPFX_REXX4 0x400
#define EPFX_VVVV_IDX 11
static bool op_mem(FeOp op) { return op < 0; }
static bool op_reg(FeOp op) { return op >= 0; }
static bool op_reg_gpl(FeOp op) { return (op & ~0x1f) == 0x100; }
static bool op_reg_gph(FeOp op) { return (op & ~0x3) == 0x204; }
static bool op_reg_xmm(FeOp op) { return (op & ~0x1f) == 0x600; }
static int64_t op_mem_offset(FeOp op) { return (int32_t) op; }
static unsigned op_mem_base(FeOp op) { return (op >> 32) & 0xfff; }
static unsigned op_mem_idx(FeOp op) { return (op >> 44) & 0xfff; }
static unsigned op_mem_scale(FeOp op) { return (op >> 56) & 0xf; }
static unsigned op_reg_idx(FeOp op) { return op & 0xff; }
static bool op_imm_n(FeOp imm, unsigned immsz) {
if (immsz == 0 && !imm) return true;
if (immsz == 1 && (int8_t) imm == imm) return true;
if (immsz == 2 && (int16_t) imm == imm) return true;
if (immsz == 3 && (imm&0xffffff) == imm) return true;
if (immsz == 4 && (int32_t) imm == imm) return true;
if (immsz == 8 && (int64_t) imm == imm) return true;
return false;
}
static
unsigned
opc_size(uint64_t opc, uint64_t epfx)
{
unsigned res = 1;
if (UNLIKELY(opc & OPC_EVEXL0)) {
res += 4;
} else if (UNLIKELY(opc & OPC_VEXL0)) {
if (opc & (OPC_REXW|0x20000) || epfx & (EPFX_REXX|EPFX_REXB))
res += 3;
else
res += 2;
} else {
if (opc & OPC_LOCK) res++;
if (opc & OPC_66) res++;
if (opc & (OPC_F2|OPC_F3)) res++;
if (opc & OPC_REXW || epfx & EPFX_REX_MSK) res++;
if (opc & 0x30000) res++;
if (opc & 0x20000) res++;
}
if (opc & OPC_SEG_MSK) res++;
if (opc & OPC_67) res++;
if (opc & 0x8000) res++;
return res;
}
static
int
enc_opc(uint8_t** restrict buf, uint64_t opc, uint64_t epfx)
{
if (opc & OPC_SEG_MSK)
*(*buf)++ = (0x65643e362e2600 >> (8 * ((opc >> 29) & 7))) & 0xff;
if (opc & OPC_67) *(*buf)++ = 0x67;
if (opc & OPC_EVEXL0) {
*(*buf)++ = 0x62;
unsigned b1 = opc >> 16 & 7;
if (!(epfx & EPFX_REXR)) b1 |= 0x80;
if (!(epfx & EPFX_REXX)) b1 |= 0x40;
if (!(epfx & EPFX_REXB)) b1 |= 0x20;
if (!(epfx & EPFX_REXR4)) b1 |= 0x10;
if ((epfx & EPFX_REXB4)) b1 |= 0x08;
*(*buf)++ = b1;
unsigned b2 = opc >> 20 & 3;
if (!(epfx & EPFX_REXX4)) b2 |= 0x04;
b2 |= (~(epfx >> EPFX_VVVV_IDX) & 0xf) << 3;
if (opc & OPC_REXW) b2 |= 0x80;
*(*buf)++ = b2;
unsigned b3 = opc >> 33 & 7;
b3 |= (~(epfx >> EPFX_VVVV_IDX) & 0x10) >> 1;
if (opc & OPC_EVEXB) b3 |= 0x10;
b3 |= (opc >> 23 & 3) << 5;
if (opc & OPC_EVEXZ) b3 |= 0x80;
*(*buf)++ = b3;
} else if (opc & OPC_VEXL0) {
if (epfx & (EPFX_REXR4|EPFX_REXX4|EPFX_REXB4|(0x10<<EPFX_VVVV_IDX))) return -1;
bool vex3 = opc & (OPC_REXW|0x20000) || epfx & (EPFX_REXX|EPFX_REXB);
unsigned pp = opc >> 20 & 3;
*(*buf)++ = 0xc4 | !vex3;
unsigned b2 = pp | (opc & 0x800000 ? 0x4 : 0);
if (vex3) {
unsigned b1 = opc >> 16 & 7;
if (!(epfx & EPFX_REXR)) b1 |= 0x80;
if (!(epfx & EPFX_REXX)) b1 |= 0x40;
if (!(epfx & EPFX_REXB)) b1 |= 0x20;
*(*buf)++ = b1;
if (opc & OPC_REXW) b2 |= 0x80;
} else {
if (!(epfx & EPFX_REXR)) b2 |= 0x80;
}
b2 |= (~(epfx >> EPFX_VVVV_IDX) & 0xf) << 3;
*(*buf)++ = b2;
} else {
if (opc & OPC_LOCK) *(*buf)++ = 0xF0;
if (opc & OPC_66) *(*buf)++ = 0x66;
if (opc & OPC_F2) *(*buf)++ = 0xF2;
if (opc & OPC_F3) *(*buf)++ = 0xF3;
if (opc & OPC_REXW || epfx & (EPFX_REX_MSK)) {
unsigned rex = 0x40;
if (opc & OPC_REXW) rex |= 8;
if (epfx & EPFX_REXR) rex |= 4;
if (epfx & EPFX_REXX) rex |= 2;
if (epfx & EPFX_REXB) rex |= 1;
*(*buf)++ = rex;
}
if (opc & 0x30000) *(*buf)++ = 0x0F;
if ((opc & 0x30000) == 0x20000) *(*buf)++ = 0x38;
if ((opc & 0x30000) == 0x30000) *(*buf)++ = 0x3A;
}
*(*buf)++ = opc & 0xff;
if (opc & 0x8000) *(*buf)++ = (opc >> 8) & 0xff;
return 0;
}
static
int
enc_imm(uint8_t** restrict buf, uint64_t imm, unsigned immsz)
{
if (!op_imm_n(imm, immsz)) return -1;
for (unsigned i = 0; i < immsz; i++)
*(*buf)++ = imm >> 8 * i;
return 0;
}
static
int
enc_o(uint8_t** restrict buf, uint64_t opc, uint64_t epfx, uint64_t op0)
{
if (op_reg_idx(op0) & 0x8) epfx |= EPFX_REXB;
// NB: this cannot happen. There is only one O-encoded instruction which
// accepts high-byte registers (b0+/MOVABS Rb,Ib), which will never have a
// REx prefix if the operand is a high-byte register.
// bool has_rex = opc & OPC_REXW || epfx & EPFX_REX_MSK;
// if (has_rex && op_reg_gph(op0)) return -1;
if (enc_opc(buf, opc, epfx)) return -1;
*(*buf - 1) = (*(*buf - 1) & 0xf8) | (op_reg_idx(op0) & 0x7);
return 0;
}
static
int
enc_mr(uint8_t** restrict buf, uint64_t opc, uint64_t epfx, uint64_t op0,
uint64_t op1, unsigned immsz)
{
// If !op_reg(op1), it is a constant value for ModRM.reg
if (op_reg(op0) && (op_reg_idx(op0) & 0x8)) epfx |= EPFX_REXB;
if (op_reg(op0) && (op_reg_idx(op0) & 0x10))
epfx |= 0 ? EPFX_REXB4 : EPFX_REXX|EPFX_EVEX;
if (op_mem(op0) && (op_mem_base(op0) & 0x8)) epfx |= EPFX_REXB;
if (op_mem(op0) && (op_mem_base(op0) & 0x10)) epfx |= EPFX_REXB4;
if (op_mem(op0) && (op_mem_idx(op0) & 0x8)) epfx |= EPFX_REXX;
if (op_mem(op0) && (op_mem_idx(op0) & 0x10))
epfx |= opc & OPC_VSIB ? 0x10<<EPFX_VVVV_IDX : EPFX_REXX4;
if (op_reg(op1) && (op_reg_idx(op1) & 0x8)) epfx |= EPFX_REXR;
if (op_reg(op1) && (op_reg_idx(op1) & 0x10)) epfx |= EPFX_REXR4;
bool has_rex = opc & (OPC_REXW|OPC_VEXL0|OPC_EVEXL0) || (epfx & EPFX_REX_MSK);
if (has_rex && (op_reg_gph(op0) || op_reg_gph(op1))) return -1;
if (epfx & (EPFX_EVEX|EPFX_REXB4|EPFX_REXX4|EPFX_REXR4|(0x10<<EPFX_VVVV_IDX))) {
if (!(opc & OPC_EVEXL0)) return -1;
} else if (opc & OPC_DOWNGRADE_VEX) { // downgrade EVEX to VEX
// clear EVEX and disp8scale, set VEX
opc = (opc & ~(uint64_t) (OPC_EVEXL0|OPC_EVEX_DISP8SCALE)) | OPC_VEXL0;
if (opc & OPC_DOWNGRADE_VEX_FLIPW)
opc ^= OPC_REXW;
}
if (LIKELY(op_reg(op0))) {
if (enc_opc(buf, opc, epfx)) return -1;
*(*buf)++ = 0xc0 | ((op_reg_idx(op1) & 7) << 3) | (op_reg_idx(op0) & 7);
return 0;
}
unsigned opcsz = opc_size(opc, epfx);
int mod = 0, reg = op1 & 7, rm;
int scale = 0, idx = 4, base = 0;
int32_t off = op_mem_offset(op0);
bool withsib = opc & OPC_FORCE_SIB;
if (!!op_mem_idx(op0) != !!op_mem_scale(op0)) return -1;
if (!op_mem_idx(op0) && (opc & OPC_VSIB)) return -1;
if (op_mem_idx(op0))
{
if (opc & OPC_VSIB)
{
if (!op_reg_xmm(op_mem_idx(op0))) return -1;
// EVEX VSIB requires non-zero opmask
if ((opc & OPC_EVEXL0) && !(opc & OPC_MASK_MSK)) return -1;
}
else
{
if (!op_reg_gpl(op_mem_idx(op0))) return -1;
if (op_reg_idx(op_mem_idx(op0)) == 4) return -1;
}
idx = op_mem_idx(op0) & 7;
int scalabs = op_mem_scale(op0);
if (scalabs & (scalabs - 1)) return -1;
scale = (scalabs & 0xA ? 1 : 0) | (scalabs & 0xC ? 2 : 0);
withsib = true;
}
unsigned dispsz = 0;
if (!op_mem_base(op0))
{
base = 5;
rm = 4;
dispsz = 4;
}
else if (op_mem_base(op0) == FE_IP)
{
rm = 5;
dispsz = 4;
// Adjust offset, caller doesn't know instruction length.
off -= opcsz + 5 + immsz;
if (withsib) return -1;
}
else
{
if (!op_reg_gpl(op_mem_base(op0))) return -1;
rm = op_reg_idx(op_mem_base(op0)) & 7;
if (withsib || rm == 4) {
base = rm;
rm = 4;
}
if (off) {
unsigned disp8scale = (opc & OPC_EVEX_DISP8SCALE) >> 39;
if (!(off & ((1 << disp8scale) - 1)) && op_imm_n(off >> disp8scale, 1)) {
mod = 0x40;
dispsz = 1;
off >>= disp8scale;
} else {
mod = 0x80;
dispsz = 4;
}
} else if (rm == 5) {
mod = 0x40;
dispsz = 1;
}
}
if (opcsz + 1 + (rm == 4) + dispsz + immsz > 15) return -1;
if (enc_opc(buf, opc, epfx)) return -1;
*(*buf)++ = mod | (reg << 3) | rm;
if (UNLIKELY(rm == 4))
*(*buf)++ = (scale << 6) | (idx << 3) | base;
return enc_imm(buf, off, dispsz);
}
typedef enum {
ENC_NP, ENC_M, ENC_R, ENC_M1, ENC_MC, ENC_MR, ENC_RM, ENC_RMA, ENC_MRC,
ENC_AM, ENC_MA, ENC_I, ENC_O, ENC_OA, ENC_S, ENC_A, ENC_D, ENC_FD, ENC_TD,
ENC_IM,
ENC_RVM, ENC_RVMR, ENC_RMV, ENC_VM, ENC_MVR, ENC_MRV,
ENC_MAX
} Encoding;
struct EncodingInfo {
uint8_t modrm : 2;
uint8_t modreg : 2;
uint8_t vexreg : 2;
uint8_t immidx : 2;
// 0 = normal or jump, 1 = constant 1, 2 = address-size, 3 = RVMR
uint8_t immctl : 3;
uint8_t zregidx : 2;
uint8_t zregval : 1;
};
const struct EncodingInfo encoding_infos[ENC_MAX] = {
[ENC_NP] = { 0 },
[ENC_M] = { .modrm = 0x0^3, .immidx = 1 },
[ENC_R] = { .modreg = 0x0^3 },
[ENC_M1] = { .modrm = 0x0^3, .immctl = 1, .immidx = 1 },
[ENC_MC] = { .modrm = 0x0^3, .zregidx = 0x1^3, .zregval = 1 },
[ENC_MR] = { .modrm = 0x0^3, .modreg = 0x1^3, .immidx = 2 },
[ENC_RM] = { .modrm = 0x1^3, .modreg = 0x0^3, .immidx = 2 },
[ENC_RMA] = { .modrm = 0x1^3, .modreg = 0x0^3, .zregidx = 0x2^3, .zregval = 0 },
[ENC_MRC] = { .modrm = 0x0^3, .modreg = 0x1^3, .zregidx = 0x2^3, .zregval = 1 },
[ENC_AM] = { .modrm = 0x1^3, .zregidx = 0x0^3, .zregval = 0 },
[ENC_MA] = { .modrm = 0x0^3, .zregidx = 0x1^3, .zregval = 0 },
[ENC_I] = { .immidx = 0 },
[ENC_O] = { .modreg = 0x0^3, .immidx = 1 },
[ENC_OA] = { .modreg = 0x0^3, .zregidx = 0x1^3, .zregval = 0 },
[ENC_S] = { 0 },
[ENC_A] = { .zregidx = 0x0^3, .zregval = 0, .immidx = 1 },
[ENC_D] = { .immidx = 0 },
[ENC_FD] = { .zregidx = 0x0^3, .zregval = 0, .immctl = 2, .immidx = 1 },
[ENC_TD] = { .zregidx = 0x1^3, .zregval = 0, .immctl = 2, .immidx = 0 },
[ENC_IM] = { .modrm = 0x1^3, .immidx = 0 },
[ENC_RVM] = { .modrm = 0x2^3, .modreg = 0x0^3, .vexreg = 0x1^3, .immidx = 3 },
[ENC_RVMR] = { .modrm = 0x2^3, .modreg = 0x0^3, .vexreg = 0x1^3, .immctl = 3, .immidx = 3 },
[ENC_RMV] = { .modrm = 0x1^3, .modreg = 0x0^3, .vexreg = 0x2^3 },
[ENC_VM] = { .modrm = 0x1^3, .vexreg = 0x0^3, .immidx = 2 },
[ENC_MVR] = { .modrm = 0x0^3, .modreg = 0x2^3, .vexreg = 0x1^3 },
[ENC_MRV] = { .modrm = 0x0^3, .modreg = 0x1^3, .vexreg = 0x2^3 },
};
static const uint64_t alt_tab[] = {
#include <fadec-encode-private.inc>
};
int
fe_enc64_impl(uint8_t** restrict buf, uint64_t opc, FeOp op0, FeOp op1,
FeOp op2, FeOp op3)
{
uint8_t* buf_start = *buf;
uint64_t ops[4] = {op0, op1, op2, op3};
uint64_t epfx = 0;
// Doesn't change between variants
if ((opc & OPC_GPH_OP0) && op_reg_gpl(op0) && op0 >= FE_SP)
epfx |= EPFX_REX;
else if (!(opc & OPC_GPH_OP0) && op_reg_gph(op0))
goto fail;
if ((opc & OPC_GPH_OP1) && op_reg_gpl(op1) && op1 >= FE_SP)
epfx |= EPFX_REX;
else if (!(opc & OPC_GPH_OP1) && op_reg_gph(op1))
goto fail;
try_encode:;
unsigned enc = (opc >> 51) & 0x1f;
const struct EncodingInfo* ei = &encoding_infos[enc];
int64_t imm = 0xcc;
unsigned immsz = (opc >> 47) & 0xf;
if (UNLIKELY(ei->zregidx && op_reg_idx(ops[ei->zregidx^3]) != ei->zregval))
goto next;
if (UNLIKELY(enc == ENC_S)) {
if ((op_reg_idx(op0) << 3 & 0x20) != (opc & 0x20)) goto next;
opc |= op_reg_idx(op0) << 3;
}
if (immsz) {
imm = ops[ei->immidx];
if (UNLIKELY(ei->immctl)) {
if (ei->immctl == 2) {
immsz = UNLIKELY(opc & OPC_67) ? 4 : 8;
if (immsz == 4) imm = (int32_t) imm; // address are zero-extended
} else if (ei->immctl == 3) {
if (!op_reg_xmm(imm)) goto fail;
imm = op_reg_idx(imm) << 4;
if (!op_imm_n(imm, 1)) goto fail;
} else if (ei->immctl == 1) {
if (imm != 1) goto next;
immsz = 0;
}
} else if (enc == ENC_D) {
imm -= (int64_t) *buf + opc_size(opc, epfx) + immsz;
bool has_alt = opc >> 56 != 0;
bool skip_to_alt = has_alt && UNLIKELY(opc & FE_JMPL);
if (skip_to_alt || !op_imm_n(imm, immsz)) {
if (!has_alt) goto fail;
// JMP/Jcc special case
immsz = 4;
if (opc & 0x80) { // JMP
opc -= 2; // Convert opcode 0xeb to 0xe9
imm -= 3; // 3 extra immediate bytes
} else { // Jcc
opc += 0x10010; // Add 0f escape + 0x10 to opcode
imm -= 4; // 0f escape + 3 extra immediate bytes
}
if (!op_imm_n(imm, immsz)) goto fail;
}
} else {
if (!op_imm_n(imm, immsz)) goto next;
}
}
// NOP has no operands, so this must be the 32-bit OA XCHG
if ((opc & 0xfffffff) == 0x90 && ops[0] == FE_AX) goto next;
if (UNLIKELY(enc == ENC_R)) {
if (enc_mr(buf, opc, epfx, 0, ops[0], immsz)) goto fail;
} else if (ei->modrm) {
FeOp modreg = ei->modreg ? ops[ei->modreg^3] : (opc & 0xff00) >> 8;
if (ei->vexreg)
epfx |= ((uint64_t) op_reg_idx(ops[ei->vexreg^3])) << EPFX_VVVV_IDX;
// Can fail for upgrade to EVEX due to high register numbers
if (enc_mr(buf, opc, epfx, ops[ei->modrm^3], modreg, immsz)) goto next;
} else if (ei->modreg) {
if (enc_o(buf, opc, epfx, ops[ei->modreg^3])) goto fail;
} else {
if (enc_opc(buf, opc, epfx)) goto fail;
}
if (immsz)
if (enc_imm(buf, imm, immsz)) goto fail;
return 0;
next:;
uint64_t alt = opc >> 56;
if (alt) { // try alternative encoding, if available
opc = alt_tab[alt] | (opc & OPC_USER_MSK);
goto try_encode;
}
fail:
// Don't advance buffer on error; though we shouldn't write anything.
*buf = buf_start;
return -1;
}

View File

@@ -1,64 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <fadec-enc2.h>
static
void print_hex(const uint8_t* buf, size_t len) {
for (size_t i = 0; i < len; i++)
printf("%02x", buf[i]);
}
static int
check(const uint8_t* buf, const void* exp, size_t exp_len, unsigned res, const char* name) {
if (res == exp_len && !memcmp(buf, exp, exp_len))
return 0;
printf("Failed case (new) %s:\n", name);
printf(" Exp (%2zu): ", exp_len);
print_hex((const uint8_t*)exp, exp_len);
printf("\n Got (%2u): ", res);
print_hex(buf, res);
printf("\n");
return -1;
}
#define TEST1(str, exp, name, ...) do { \
memset(buf, 0, sizeof buf); \
unsigned res = fe64_ ## name(buf, __VA_ARGS__); \
failed |= check(buf, exp, sizeof(exp) - 1, res, str); \
} while (0)
#define TEST(exp, ...) TEST1(#__VA_ARGS__, exp, __VA_ARGS__)
int
main(void) {
int failed = 0;
uint8_t buf[16];
// This API is type safe and prohibits compilation of reg-type mismatches
#define ENC_TEST_TYPESAFE
// Silence -Warray-bounds with double cast
#define FE_PTR(off) (const void*) ((uintptr_t) buf + (off))
#define FLAGMASK(flags, mask) flags, mask
#include "encode-test.inc"
TEST("\x90", NOP, 0);
TEST("\x90", NOP, 1);
TEST("\x66\x90", NOP, 2);
TEST("\x0f\x1f\x00", NOP, 3);
TEST("\x0f\x1f\x40\x00", NOP, 4);
TEST("\x0f\x1f\x44\x00\x00", NOP, 5);
TEST("\x66\x0f\x1f\x44\x00\x00", NOP, 6);
TEST("\x0f\x1f\x80\x00\x00\x00\x00", NOP, 7);
TEST("\x0f\x1f\x84\x00\x00\x00\x00\x00", NOP, 8);
TEST("\x66\x0f\x1f\x84\x00\x00\x00\x00\x00", NOP, 9);
TEST("\x66\x0f\x1f\x84\x00\x00\x00\x00\x00\x90", NOP, 10);
TEST("\x66\x0f\x1f\x84\x00\x00\x00\x00\x00\x66\x90", NOP, 11);
TEST("\x66\x0f\x1f\x84\x00\x00\x00\x00\x00\x0f\x1f\x00", NOP, 12);
puts(failed ? "Some tests FAILED" : "All tests PASSED");
return failed ? EXIT_FAILURE : EXIT_SUCCESS;
}

View File

@@ -1,64 +0,0 @@
#include <array>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <fadec-enc2.h>
using Buffer = std::array<uint8_t, 16>;
static
void print_hex(const uint8_t* buf, size_t len) {
for (size_t i = 0; i < len; i++)
std::printf("%02x", buf[i]);
}
static int
check(const Buffer& buf, const char* exp, size_t exp_len, unsigned res, const char* name) {
if (res == exp_len && !std::memcmp(buf.data(), exp, exp_len))
return 0;
std::printf("Failed case (new) %s:\n", name);
std::printf(" Exp (%2zu): ", exp_len);
print_hex(reinterpret_cast<const uint8_t*>(exp), exp_len);
std::printf("\n Got (%2u): ", res);
print_hex(buf.data(), res);
std::printf("\n");
return -1;
}
#define TEST1(str, exp, name, ...) do { \
buf.fill(0); \
unsigned res = fe64_ ## name(buf.data(), __VA_ARGS__); \
failed |= check(buf, exp, sizeof(exp) - 1, res, str); \
} while (0)
#define TEST(exp, ...) TEST1(#__VA_ARGS__, exp, __VA_ARGS__)
#define TEST_CPP1(str, exp, expr) do { \
buf.fill(0); \
unsigned res = (expr); \
failed |= check(buf, exp, sizeof(exp) - 1, res, str); \
} while (0)
#define TEST_CPP(exp, ...) TEST_CPP1(#__VA_ARGS__, exp, __VA_ARGS__)
int main() {
int failed = 0;
Buffer buf{};
// This API is type safe and prohibits compilation of reg-type mismatches
#define ENC_TEST_TYPESAFE
// Silence -Warray-bounds with double cast
#define FE_PTR(off) (const void*) ((uintptr_t) buf.data() + (off))
#define FLAGMASK(flags, mask) flags, mask
#include "encode-test.inc"
// Test implicit conversion of parameters also on the actual functions
TEST_CPP("\x0f\x90\xc0", fe64_SETO8r(buf.data(), 0, FE_AX));
TEST_CPP("\x0f\x90\xc0", (fe64_SETO8r)(buf.data(), 0, FE_AX));
TEST_CPP("\x0f\x90\xc4", fe64_SETO8r(buf.data(), 0, FE_AH));
TEST_CPP("\x0f\x90\xc4", (fe64_SETO8r)(buf.data(), 0, FE_AH));
std::puts(failed ? "Some tests FAILED" : "All tests PASSED");
return failed ? EXIT_FAILURE : EXIT_SUCCESS;
}

View File

@@ -1,345 +0,0 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <fadec-enc2.h>
#ifdef __GNUC__
#define LIKELY(x) __builtin_expect(!!(x), 1)
#define UNLIKELY(x) __builtin_expect(!!(x), 0)
#if __has_attribute(cold) && __has_attribute(preserve_most)
#define HINT_COLD __attribute__((cold,preserve_most,noinline))
#elif __has_attribute(cold)
#define HINT_COLD __attribute__((cold,noinline))
#else
#define HINT_COLD
#endif
#else
#define LIKELY(x) (x)
#define UNLIKELY(x) (x)
#define HINT_COLD
#endif
#define op_reg_idx(op) (op).idx
#define op_reg_gph(op) (((op).idx & ~0x3) == 0x24)
#define op_mem_base(mem) op_reg_idx((mem).base)
#define op_mem_idx(mem) op_reg_idx((mem).idx)
static bool
op_imm_n(int64_t imm, unsigned immsz) {
if (immsz == 0 && !imm) return true;
if (immsz == 1 && (int8_t) imm == imm) return true;
if (immsz == 2 && (int16_t) imm == imm) return true;
if (immsz == 3 && (imm&0xffffff) == imm) return true;
if (immsz == 4 && (int32_t) imm == imm) return true;
if (immsz == 8 && (int64_t) imm == imm) return true;
return false;
}
HINT_COLD static unsigned
enc_seg67(uint8_t* buf, unsigned flags) {
unsigned idx = 0;
if (UNLIKELY(flags & FE_SEG_MASK)) {
unsigned seg = (0x65643e362e2600 >> (8 * (flags & FE_SEG_MASK))) & 0xff;
buf[idx++] = seg;
}
if (UNLIKELY(flags & FE_ADDR32)) buf[idx++] = 0x67;
return idx;
}
static unsigned
enc_rex_mem(FeMem op0, uint64_t op1) {
// Essentially just an and+or due to struct layout.
uint32_t val = op1 | op0.flags | (op_mem_base(op0) << 8) |
((uint32_t)op_mem_idx(op0) << 24);
// Combine REX.RXB using multiplication for branch-less code.
uint32_t masked = val & 0x08000808;
return masked ? (uint8_t) (masked * (1|(1<<15)|(1<<25)) >> 26) + 0x40 : 0;
}
static void
enc_imm(uint8_t* buf, uint64_t imm, unsigned immsz) {
#ifdef __GNUC__
// Clang doesn't fold the loop into a single store.
// See: https://github.com/llvm/llvm-project/issues/154696
if (__builtin_constant_p(immsz)) {
__builtin_memcpy(buf, &imm, immsz);
return;
}
#endif
for (unsigned i = 0; i < immsz; i++)
*buf++ = imm >> 8 * i;
}
static int
enc_mem_common(uint8_t* buf, unsigned ripoff, FeMem op0, uint64_t op1,
unsigned disp8scale) {
int mod = 0, reg = op1 & 7, rm;
unsigned sib = 0x20;
bool withsib = false;
unsigned dispsz = 0;
int32_t off = op0.off;
if (op_reg_idx(op0.idx) < 0x80) {
int scalabs = op0.scale;
if (UNLIKELY((unsigned) (op0.scale - 1) >= 8 ||
(op0.scale & (op0.scale - 1))))
return 0;
unsigned scale = (scalabs & 0xA ? 1 : 0) | (scalabs & 0xC ? 2 : 0);
sib = scale << 6 | (op_reg_idx(op0.idx) & 7) << 3;
withsib = true;
} else if (UNLIKELY(op0.scale != 0)) {
return 0;
}
if (UNLIKELY(op0.base.idx >= 0x20)) {
if (UNLIKELY(op0.base.idx >= op_reg_idx(FE_NOREG))) {
*buf++ = (reg << 3) | 4;
*buf++ = sib | 5;
enc_imm(buf, off, 4);
return ripoff + 6;
} else if (LIKELY(op0.base.idx == FE_IP.idx)) {
if (withsib)
return 0;
*buf++ = (reg << 3) | 5;
// Adjust offset, caller doesn't know instruction length.
enc_imm(buf, off - ripoff - 5, 4);
return ripoff + 5;
} else {
return 0;
}
}
rm = op_reg_idx(op0.base) & 7;
if (off) {
if (LIKELY(!disp8scale)) {
mod = (int8_t) off == off ? 0x40 : 0x80;
dispsz = (int8_t) off == off ? 1 : 4;
} else {
if (!(off & ((1 << disp8scale) - 1)) && op_imm_n(off >> disp8scale, 1))
off >>= disp8scale, mod = 0x40, dispsz = 1;
else
mod = 0x80, dispsz = 4;
}
} else if (rm == 5) {
dispsz = 1;
mod = 0x40;
}
// Always write four bytes of displacement. The buffer is always large
// enough, and we truncate by returning a smaller "written bytes" count.
if (withsib || rm == 4) {
*buf++ = mod | (reg << 3) | 4;
*buf++ = sib | rm;
enc_imm(buf, off, 4);
return ripoff + 2 + dispsz;
} else {
*buf++ = mod | (reg << 3) | rm;
enc_imm(buf, off, 4);
return ripoff + 1 + dispsz;
}
}
static int
enc_mem(uint8_t* buf, unsigned ripoff, FeMem op0, uint64_t op1, bool forcesib,
unsigned disp8scale) {
if (UNLIKELY(op_reg_idx(op0.idx) == 4))
return 0;
if (forcesib && op_reg_idx(op0.idx) == op_reg_idx(FE_NOREG)) {
op0.scale = 1;
op0.idx = FE_GP(4);
}
return enc_mem_common(buf, ripoff, op0, op1, disp8scale);
}
static int
enc_mem_vsib(uint8_t* buf, unsigned ripoff, FeMemV op0, uint64_t op1,
bool forcesib, unsigned disp8scale) {
(void) forcesib;
FeMem mem = FE_MEM(op0.base, op0.scale, FE_GP(op_reg_idx(op0.idx)), op0.off);
return enc_mem_common(buf, ripoff, mem, op1, disp8scale);
}
// EVEX/VEX "Opcode" format:
//
// | EVEX byte 4 | P P M M M - - W | Opcode byte | VEX-D VEX-D-FLIPW
// 0 8 16 24
enum {
FE_OPC_VEX_WPP_SHIFT = 8,
FE_OPC_VEX_WPP_MASK = 0x83 << FE_OPC_VEX_WPP_SHIFT,
FE_OPC_VEX_MMM_SHIFT = 10,
FE_OPC_VEX_MMM_MASK = 0x1f << FE_OPC_VEX_MMM_SHIFT,
FE_OPC_VEX_DOWNGRADE_VEX = 1 << 24,
FE_OPC_VEX_DOWNGRADE_VEX_FLIPW = 1 << 25,
};
static int
enc_vex_common(uint8_t* buf, unsigned opcode, unsigned base,
unsigned idx, unsigned reg, unsigned vvvv) {
if ((base | idx | reg | vvvv) & 0x10) return 0;
bool vex3 = ((base | idx) & 0x08) || (opcode & 0xfc00) != 0x0400;
if (vex3) {
*buf++ = 0xc4;
unsigned b1 = (opcode & FE_OPC_VEX_MMM_MASK) >> FE_OPC_VEX_MMM_SHIFT;
if (!(reg & 0x08)) b1 |= 0x80;
if (!(idx & 0x08)) b1 |= 0x40;
if (!(base & 0x08)) b1 |= 0x20;
*buf++ = b1;
unsigned b2 = (opcode & FE_OPC_VEX_WPP_MASK) >> FE_OPC_VEX_WPP_SHIFT;
if (opcode & 0x20) b2 |= 0x04;
b2 |= (vvvv ^ 0xf) << 3;
*buf++ = b2;
} else {
*buf++ = 0xc5;
unsigned b2 = opcode >> FE_OPC_VEX_WPP_SHIFT & 3;
if (opcode & 0x20) b2 |= 0x04;
if (!(reg & 0x08)) b2 |= 0x80;
b2 |= (vvvv ^ 0xf) << 3;
*buf++ = b2;
}
*buf++ = (opcode & 0xff0000) >> 16;
return 3 + vex3;
}
static int
enc_vex_reg(uint8_t* buf, unsigned opcode, uint64_t rm, uint64_t reg,
uint64_t vvvv) {
unsigned off = enc_vex_common(buf, opcode, rm, 0, reg, vvvv);
buf[off] = 0xc0 | (reg << 3 & 0x38) | (rm & 7);
return off ? off + 1 : 0;
}
static int
enc_vex_mem(uint8_t* buf, unsigned opcode, FeMem rm, uint64_t reg,
uint64_t vvvv, unsigned ripoff, bool forcesib, unsigned disp8scale) {
unsigned off = enc_vex_common(buf, opcode, op_reg_idx(rm.base), op_reg_idx(rm.idx), reg, vvvv);
unsigned memoff = enc_mem(buf + off, ripoff + off, rm, reg, forcesib, disp8scale);
return off && memoff ? memoff : 0;
}
static int
enc_vex_vsib(uint8_t* buf, unsigned opcode, FeMemV rm, uint64_t reg,
uint64_t vvvv, unsigned ripoff, bool forcesib, unsigned disp8scale) {
unsigned off = enc_vex_common(buf, opcode, op_reg_idx(rm.base), op_reg_idx(rm.idx), reg, vvvv);
unsigned memoff = enc_mem_vsib(buf + off, ripoff + off, rm, reg, forcesib, disp8scale);
return off && memoff ? memoff : 0;
}
static int
enc_evex_common(uint8_t* buf, unsigned opcode, unsigned base,
unsigned idx, unsigned reg, unsigned vvvv) {
*buf++ = 0x62;
bool evexr3 = reg & 0x08;
bool evexr4 = reg & 0x10;
bool evexb3 = base & 0x08;
bool evexb4 = base & 0x10; // evexb4 is unused in AVX-512 encoding
bool evexx3 = idx & 0x08;
bool evexx4 = idx & 0x10;
bool evexv4 = vvvv & 0x10;
unsigned b1 = (opcode & FE_OPC_VEX_MMM_MASK) >> FE_OPC_VEX_MMM_SHIFT;
if (!evexr3) b1 |= 0x80;
if (!evexx3) b1 |= 0x40;
if (!evexb3) b1 |= 0x20;
if (!evexr4) b1 |= 0x10;
if (evexb4) b1 |= 0x08;
*buf++ = b1;
unsigned b2 = (opcode & FE_OPC_VEX_WPP_MASK) >> FE_OPC_VEX_WPP_SHIFT;
if (!evexx4) b2 |= 0x04;
b2 |= (~vvvv & 0xf) << 3;
*buf++ = b2;
unsigned b3 = opcode & 0xff;
if (!evexv4) b3 |= 0x08;
*buf++ = b3;
*buf++ = (opcode & 0xff0000) >> 16;
return 5;
}
static unsigned
enc_evex_to_vex(unsigned opcode) {
return opcode & FE_OPC_VEX_DOWNGRADE_VEX_FLIPW ? opcode ^ 0x8000 : opcode;
}
// Encode AVX-512 EVEX r/m-reg, non-xmm reg, vvvv, prefer vex
static int
enc_evex_reg(uint8_t* buf, unsigned opcode, unsigned rm,
unsigned reg, unsigned vvvv) {
unsigned off;
if (!((rm | reg | vvvv) & 0x10) && (opcode & FE_OPC_VEX_DOWNGRADE_VEX))
off = enc_vex_common(buf, enc_evex_to_vex(opcode), rm, 0, reg, vvvv);
else
off = enc_evex_common(buf, opcode, rm, 0, reg, vvvv);
buf[off] = 0xc0 | (reg << 3 & 0x38) | (rm & 7);
return off + 1;
}
// Encode AVX-512 EVEX r/m-reg, xmm reg, vvvv, prefer vex
static int
enc_evex_xmm(uint8_t* buf, unsigned opcode, unsigned rm,
unsigned reg, unsigned vvvv) {
unsigned off;
if (!((rm | reg | vvvv) & 0x10) && (opcode & FE_OPC_VEX_DOWNGRADE_VEX))
off = enc_vex_common(buf, enc_evex_to_vex(opcode), rm, 0, reg, vvvv);
else
// AVX-512 XMM reg encoding uses X3 instead of B4.
off = enc_evex_common(buf, opcode, rm & 0x0f, rm >> 1, reg, vvvv);
buf[off] = 0xc0 | (reg << 3 & 0x38) | (rm & 7);
return off + 1;
}
static int
enc_evex_mem(uint8_t* buf, unsigned opcode, FeMem rm, uint64_t reg,
uint64_t vvvv, unsigned ripoff, bool forcesib, unsigned disp8scale) {
unsigned off;
if (!((op_reg_idx(rm.base) | op_reg_idx(rm.idx) | reg | vvvv) & 0x10) &&
(opcode & FE_OPC_VEX_DOWNGRADE_VEX)) {
disp8scale = 0; // Only AVX-512 EVEX compresses displacement
off = enc_vex_common(buf, enc_evex_to_vex(opcode), op_reg_idx(rm.base), op_reg_idx(rm.idx), reg, vvvv);
} else {
off = enc_evex_common(buf, opcode, op_reg_idx(rm.base), op_reg_idx(rm.idx), reg, vvvv);
}
unsigned memoff = enc_mem(buf + off, ripoff + off, rm, reg, forcesib, disp8scale);
return off && memoff ? memoff : 0;
}
static int
enc_evex_vsib(uint8_t* buf, unsigned opcode, FeMemV rm, uint64_t reg,
uint64_t vvvv, unsigned ripoff, bool forcesib, unsigned disp8scale) {
(void) vvvv;
// EVEX VSIB requires non-zero mask operand
if (!(opcode & 0x7)) return 0;
// EVEX.X4 is encoded in EVEX.V4
unsigned idx = op_reg_idx(rm.idx);
unsigned off = enc_evex_common(buf, opcode, op_reg_idx(rm.base), idx & 0x0f, reg, idx & 0x10);
unsigned memoff = enc_mem_vsib(buf + off, ripoff + off, rm, reg, forcesib, disp8scale);
return off && memoff ? memoff : 0;
}
unsigned fe64_NOP(uint8_t* buf, unsigned flags) {
unsigned len = flags ? flags : 1;
// Taken from Intel SDM
static const uint8_t tbl[] = {
0x90,
0x66, 0x90,
0x0f, 0x1f, 0x00,
0x0f, 0x1f, 0x40, 0x00,
0x0f, 0x1f, 0x44, 0x00, 0x00,
0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00,
0x0f, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x00,
0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00,
0x66, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00,
};
unsigned remain = len;
for (; remain > 9; remain -= 9)
for (unsigned i = 0; i < 9; i++)
*(buf++) = tbl[36 + i];
const uint8_t* src = tbl + (remain * (remain - 1)) / 2;
for (unsigned i = 0; i < remain; i++)
*(buf++) = src[i];
return len;
}
#include <fadec-encode2-private.inc>

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -1,113 +0,0 @@
#ifndef FD_FADEC_ENC_H_
#define FD_FADEC_ENC_H_
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
FE_AX = 0x100, FE_CX, FE_DX, FE_BX, FE_SP, FE_BP, FE_SI, FE_DI,
FE_R8, FE_R9, FE_R10, FE_R11, FE_R12, FE_R13, FE_R14, FE_R15,
FE_IP = 0x120,
FE_AH = 0x204, FE_CH, FE_DH, FE_BH,
FE_ES = 0x300, FE_CS, FE_SS, FE_DS, FE_FS, FE_GS,
FE_ST0 = 0x400, FE_ST1, FE_ST2, FE_ST3, FE_ST4, FE_ST5, FE_ST6, FE_ST7,
FE_MM0 = 0x500, FE_MM1, FE_MM2, FE_MM3, FE_MM4, FE_MM5, FE_MM6, FE_MM7,
FE_XMM0 = 0x600, FE_XMM1, FE_XMM2, FE_XMM3, FE_XMM4, FE_XMM5, FE_XMM6, FE_XMM7,
FE_XMM8, FE_XMM9, FE_XMM10, FE_XMM11, FE_XMM12, FE_XMM13, FE_XMM14, FE_XMM15,
FE_XMM16, FE_XMM17, FE_XMM18, FE_XMM19, FE_XMM20, FE_XMM21, FE_XMM22, FE_XMM23,
FE_XMM24, FE_XMM25, FE_XMM26, FE_XMM27, FE_XMM28, FE_XMM29, FE_XMM30, FE_XMM31,
FE_K0 = 0x700, FE_K1, FE_K2, FE_K3, FE_K4, FE_K5, FE_K6, FE_K7,
FE_TMM0 = 0x800, FE_TMM1, FE_TMM2, FE_TMM3, FE_TMM4, FE_TMM5, FE_TMM6, FE_TMM7,
} FeReg;
typedef int64_t FeOp;
/** Construct a memory operand. Unused parts can be set to 0 and will be
* ignored. FE_IP can be used as base register, in which case the offset is
* interpreted as the offset from the /current/ position -- the size of the
* encoded instruction will be subtracted during encoding. scale must be 1, 2,
* 4, or 8; but is ignored if idx == 0. **/
#define FE_MEM(base,sc,idx,off) (INT64_MIN | ((int64_t) ((base) & 0xfff) << 32) | ((int64_t) ((idx) & 0xfff) << 44) | ((int64_t) ((sc) & 0xf) << 56) | ((off) & 0xffffffff))
#define FE_NOREG ((FeReg) 0)
/** Add segment override prefix. This may or may not generate prefixes for the
* ignored prefixes ES/CS/DS/SS in 64-bit mode. **/
#define FE_SEG(seg) ((uint64_t) (((seg) & 0x7) + 1) << 29)
/** Do not use. **/
#define FE_SEG_MASK 0xe0000000
/** Overrides address size. **/
#define FE_ADDR32 0x10000000
/** Used together with a RIP-relative (conditional) jump, this will force the
* use of the encoding with the largest distance. Useful for reserving a jump
* when the target offset is still unknown; if the jump is re-encoded later on,
* FE_JMPL must be specified there, too, so that the encoding lengths match. **/
#define FE_JMPL 0x100000000
#define FE_MASK(kreg) ((uint64_t) ((kreg) & 0x7) << 33)
#define FE_RC_RN 0x0000000
#define FE_RC_RD 0x0800000
#define FE_RC_RU 0x1000000
#define FE_RC_RZ 0x1800000
enum {
FE_CC_O = 0x0,
FE_CC_NO = 0x1,
FE_CC_C = 0x2,
FE_CC_B = FE_CC_C,
FE_CC_NAE = FE_CC_C,
FE_CC_NC = 0x3,
FE_CC_AE = FE_CC_NC,
FE_CC_NB = FE_CC_NC,
FE_CC_Z = 0x4,
FE_CC_E = FE_CC_Z,
FE_CC_NZ = 0x5,
FE_CC_NE = FE_CC_NZ,
FE_CC_BE = 0x6,
FE_CC_NA = FE_CC_BE,
FE_CC_A = 0x7,
FE_CC_NBE = FE_CC_A,
FE_CC_S = 0x8,
FE_CC_NS = 0x9,
FE_CC_P = 0xa,
FE_CC_PE = FE_CC_P,
FE_CC_NP = 0xb,
FE_CC_PO = FE_CC_NP,
FE_CC_L = 0xc,
FE_CC_NGE = FE_CC_L,
FE_CC_GE = 0xd,
FE_CC_NL = FE_CC_GE,
FE_CC_LE = 0xe,
FE_CC_NG = FE_CC_LE,
FE_CC_G = 0xf,
FE_CC_NLE = FE_CC_G,
};
#include <fadec-encode-public.inc>
/** Do not use. **/
#define fe_enc64_1(buf, mnem, op0, op1, op2, op3, ...) fe_enc64_impl(buf, mnem, op0, op1, op2, op3)
/** Encode a single instruction for 64-bit mode.
* \param buf Pointer to the buffer for instruction bytes, must have a size of
* 15 bytes. The pointer is advanced by the number of bytes used for
* encoding the specified instruction.
* \param mnem Mnemonic, optionally or-ed with FE_SEG(), FE_ADDR32, or FE_JMPL.
* \param operands... Instruction operands. Immediate operands are passed as
* plain value; register operands using the FeReg enum; memory operands
* using FE_MEM(); and offset operands for RIP-relative jumps/calls are
* specified as _address in buf_, e.g. (intptr_t) jmptgt, the address of
* buf and the size of the encoded instruction are subtracted internally.
* \return Zero for success or a negative value in case of an error.
**/
#define fe_enc64(buf, ...) fe_enc64_1(buf, __VA_ARGS__, 0, 0, 0, 0, 0)
/** Do not use. **/
int fe_enc64_impl(uint8_t** buf, uint64_t mnem, FeOp op0, FeOp op1, FeOp op2, FeOp op3);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,226 +0,0 @@
#ifndef FD_FADEC_ENC2_H_
#define FD_FADEC_ENC2_H_
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
#define FE_STRUCT(name) name
#else
#define FE_STRUCT(name) (name)
#endif
// Flags
#define FE_JMPL 0x8
#define FE_ADDR32 0x10
#define FE_SEG_MASK 0x7
#define FE_SEG(seg) (((seg).idx + 1) & FE_SEG_MASK)
#define FE_RC_MASK 0x60
#define FE_RC_RN 0x00
#define FE_RC_RD 0x20
#define FE_RC_RU 0x40
#define FE_RC_RZ 0x60
// Condition codes
typedef enum FeCond {
FE_CC_O = 0x00000,
FE_CC_NO = 0x10000,
FE_CC_C = 0x20000,
FE_CC_B = FE_CC_C,
FE_CC_NAE = FE_CC_C,
FE_CC_NC = 0x30000,
FE_CC_AE = FE_CC_NC,
FE_CC_NB = FE_CC_NC,
FE_CC_Z = 0x40000,
FE_CC_E = FE_CC_Z,
FE_CC_NZ = 0x50000,
FE_CC_NE = FE_CC_NZ,
FE_CC_BE = 0x60000,
FE_CC_NA = FE_CC_BE,
FE_CC_A = 0x70000,
FE_CC_NBE = FE_CC_A,
FE_CC_S = 0x80000,
FE_CC_NS = 0x90000,
FE_CC_P = 0xa0000,
FE_CC_PE = FE_CC_P,
FE_CC_NP = 0xb0000,
FE_CC_PO = FE_CC_NP,
FE_CC_L = 0xc0000,
FE_CC_NGE = FE_CC_L,
FE_CC_GE = 0xd0000,
FE_CC_NL = FE_CC_GE,
FE_CC_LE = 0xe0000,
FE_CC_NG = FE_CC_LE,
FE_CC_G = 0xf0000,
FE_CC_NLE = FE_CC_G,
FE_CC_MASK = 0xf0000
} FeCond;
typedef struct FeRegGP { unsigned char idx; } FeRegGP;
#define FE_GP(idx) (FE_STRUCT(FeRegGP) { idx })
#define FE_AX FE_GP(0)
#define FE_CX FE_GP(1)
#define FE_DX FE_GP(2)
#define FE_BX FE_GP(3)
#define FE_SP FE_GP(4)
#define FE_BP FE_GP(5)
#define FE_SI FE_GP(6)
#define FE_DI FE_GP(7)
#define FE_R8 FE_GP(8)
#define FE_R9 FE_GP(9)
#define FE_R10 FE_GP(10)
#define FE_R11 FE_GP(11)
#define FE_R12 FE_GP(12)
#define FE_R13 FE_GP(13)
#define FE_R14 FE_GP(14)
#define FE_R15 FE_GP(15)
#define FE_IP FE_GP(0x20)
#define FE_NOREG FE_GP(0x80)
typedef struct FeRegGPH { unsigned char idx; } FeRegGPH;
#define FE_GPH(idx) (FE_STRUCT(FeRegGPH) { idx })
#define FE_AH FE_GPH(4)
#define FE_CH FE_GPH(5)
#define FE_DH FE_GPH(6)
#define FE_BH FE_GPH(7)
typedef struct FeRegSREG { unsigned char idx; } FeRegSREG;
#define FE_SREG(idx) (FE_STRUCT(FeRegSREG) { idx })
#define FE_ES FE_SREG(0)
#define FE_CS FE_SREG(1)
#define FE_SS FE_SREG(2)
#define FE_DS FE_SREG(3)
#define FE_FS FE_SREG(4)
#define FE_GS FE_SREG(5)
typedef struct FeRegST { unsigned char idx; } FeRegST;
#define FE_ST(idx) (FE_STRUCT(FeRegST) { idx })
#define FE_ST0 FE_ST(0)
#define FE_ST1 FE_ST(1)
#define FE_ST2 FE_ST(2)
#define FE_ST3 FE_ST(3)
#define FE_ST4 FE_ST(4)
#define FE_ST5 FE_ST(5)
#define FE_ST6 FE_ST(6)
#define FE_ST7 FE_ST(7)
typedef struct FeRegMM { unsigned char idx; } FeRegMM;
#define FE_MM(idx) (FE_STRUCT(FeRegMM) { idx })
#define FE_MM0 FE_MM(0)
#define FE_MM1 FE_MM(1)
#define FE_MM2 FE_MM(2)
#define FE_MM3 FE_MM(3)
#define FE_MM4 FE_MM(4)
#define FE_MM5 FE_MM(5)
#define FE_MM6 FE_MM(6)
#define FE_MM7 FE_MM(7)
typedef struct FeRegXMM { unsigned char idx; } FeRegXMM;
#define FE_XMM(idx) (FE_STRUCT(FeRegXMM) { idx })
#define FE_XMM0 FE_XMM(0)
#define FE_XMM1 FE_XMM(1)
#define FE_XMM2 FE_XMM(2)
#define FE_XMM3 FE_XMM(3)
#define FE_XMM4 FE_XMM(4)
#define FE_XMM5 FE_XMM(5)
#define FE_XMM6 FE_XMM(6)
#define FE_XMM7 FE_XMM(7)
#define FE_XMM8 FE_XMM(8)
#define FE_XMM9 FE_XMM(9)
#define FE_XMM10 FE_XMM(10)
#define FE_XMM11 FE_XMM(11)
#define FE_XMM12 FE_XMM(12)
#define FE_XMM13 FE_XMM(13)
#define FE_XMM14 FE_XMM(14)
#define FE_XMM15 FE_XMM(15)
#define FE_XMM16 FE_XMM(16)
#define FE_XMM17 FE_XMM(17)
#define FE_XMM18 FE_XMM(18)
#define FE_XMM19 FE_XMM(19)
#define FE_XMM20 FE_XMM(20)
#define FE_XMM21 FE_XMM(21)
#define FE_XMM22 FE_XMM(22)
#define FE_XMM23 FE_XMM(23)
#define FE_XMM24 FE_XMM(24)
#define FE_XMM25 FE_XMM(25)
#define FE_XMM26 FE_XMM(26)
#define FE_XMM27 FE_XMM(27)
#define FE_XMM28 FE_XMM(28)
#define FE_XMM29 FE_XMM(29)
#define FE_XMM30 FE_XMM(30)
#define FE_XMM31 FE_XMM(31)
typedef struct FeRegMASK { unsigned char idx; } FeRegMASK;
#define FE_K(idx) (FE_STRUCT(FeRegMASK) { idx })
#define FE_K0 FE_K(0)
#define FE_K1 FE_K(1)
#define FE_K2 FE_K(2)
#define FE_K3 FE_K(3)
#define FE_K4 FE_K(4)
#define FE_K5 FE_K(5)
#define FE_K6 FE_K(6)
#define FE_K7 FE_K(7)
typedef struct FeRegTMM { unsigned char idx; } FeRegTMM;
#define FE_TMM(idx) (FE_STRUCT(FeRegTMM) { idx })
#define FE_TMM0 FE_TMM(0)
#define FE_TMM1 FE_TMM(1)
#define FE_TMM2 FE_TMM(2)
#define FE_TMM3 FE_TMM(3)
#define FE_TMM4 FE_TMM(4)
#define FE_TMM5 FE_TMM(5)
#define FE_TMM6 FE_TMM(6)
#define FE_TMM7 FE_TMM(7)
typedef struct FeRegCR { unsigned char idx; } FeRegCR;
#define FE_CR(idx) (FE_STRUCT(FeRegCR) { idx })
typedef struct FeRegDR { unsigned char idx; } FeRegDR;
#define FE_DR(idx) (FE_STRUCT(FeRegDR) { idx })
// Internal only
// Disambiguate GP and GPH -- C++ uses conversion constructors; C uses _Generic.
#ifdef __cplusplus
}
namespace {
struct FeRegGPLH {
unsigned char idx;
FeRegGPLH(FeRegGP gp) : idx(gp.idx) {}
FeRegGPLH(FeRegGPH gp) : idx(gp.idx | 0x20) {}
};
}
extern "C" {
#define FE_MAKE_GPLH(reg) reg
#else
typedef struct FeRegGPLH { unsigned char idx; } FeRegGPLH;
#define FE_GPLH(idx) (FE_STRUCT(FeRegGPLH) { idx })
#define FE_MAKE_GPLH(reg) FE_GPLH(_Generic((reg), FeRegGPH: 0x20, FeRegGP: 0) | (reg).idx)
#endif
typedef struct FeMem {
uint8_t flags;
FeRegGP base;
unsigned char scale;
// union {
FeRegGP idx;
// FeRegXMM idx_xmm;
// };
int32_t off;
} FeMem;
#define FE_MEM(base,sc,idx,off) (FE_STRUCT(FeMem) { 0, base, sc, idx, off })
typedef struct FeMemV {
uint8_t flags;
FeRegGP base;
unsigned char scale;
FeRegXMM idx;
int32_t off;
} FeMemV;
#define FE_MEMV(base,sc,idx,off) (FE_STRUCT(FeMemV) { 0, base, sc, idx, off })
// NOP is special: flags is interpreted as the length in bytes, 0 = 1 byte, too.
unsigned fe64_NOP(uint8_t* buf, unsigned flags);
#include <fadec-encode2-public.inc>
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,286 +0,0 @@
#ifndef FD_FADEC_H_
#define FD_FADEC_H_
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
FD_REG_R0 = 0, FD_REG_R1, FD_REG_R2, FD_REG_R3,
FD_REG_R4, FD_REG_R5, FD_REG_R6, FD_REG_R7,
FD_REG_R8, FD_REG_R9, FD_REG_R10, FD_REG_R11,
FD_REG_R12, FD_REG_R13, FD_REG_R14, FD_REG_R15,
// Alternative names for byte registers
FD_REG_AL = 0, FD_REG_CL, FD_REG_DL, FD_REG_BL,
FD_REG_AH, FD_REG_CH, FD_REG_DH, FD_REG_BH,
// Alternative names for general purpose registers
FD_REG_AX = 0, FD_REG_CX, FD_REG_DX, FD_REG_BX,
FD_REG_SP, FD_REG_BP, FD_REG_SI, FD_REG_DI,
// FD_REG_IP can only be accessed in long mode (64-bit)
FD_REG_IP = 0x10,
// Segment register values
FD_REG_ES = 0, FD_REG_CS, FD_REG_SS, FD_REG_DS, FD_REG_FS, FD_REG_GS,
// No register specified
FD_REG_NONE = 0x3f
} FdReg;
typedef enum {
#define FD_MNEMONIC(name,value) FDI_ ## name = value,
#include <fadec-decode-public.inc>
#undef FD_MNEMONIC
} FdInstrType;
/** Internal use only. **/
enum {
FD_FLAG_LOCK = 1 << 0,
FD_FLAG_REP = 1 << 2,
FD_FLAG_REPNZ = 1 << 1,
FD_FLAG_64 = 1 << 7,
};
/** Operand types. **/
typedef enum {
FD_OT_NONE = 0,
FD_OT_REG = 1,
FD_OT_IMM = 2,
FD_OT_MEM = 3,
FD_OT_OFF = 4,
FD_OT_MEMBCST = 5,
} FdOpType;
typedef enum {
/** Vector (SSE/AVX) register XMMn/YMMn/ZMMn **/
FD_RT_VEC = 0,
/** Low general purpose register **/
FD_RT_GPL = 1,
/** High-byte general purpose register **/
FD_RT_GPH = 2,
/** Segment register **/
FD_RT_SEG = 3,
/** FPU register ST(n) **/
FD_RT_FPU = 4,
/** MMX register MMn **/
FD_RT_MMX = 5,
/** TMM register TMMn **/
FD_RT_TMM = 6,
/** Vector mask (AVX-512) register Kn **/
FD_RT_MASK = 7,
/** Bound register BNDn **/
FD_RT_BND = 8,
/** Control Register CRn **/
FD_RT_CR = 9,
/** Debug Register DRn **/
FD_RT_DR = 10,
/** Must be a memory operand **/
FD_RT_MEM = 15,
} FdRegType;
/** Do not depend on the actual enum values. **/
typedef enum {
/** Round to nearest (even) **/
FD_RC_RN = 1,
/** Round down **/
FD_RC_RD = 3,
/** Round up **/
FD_RC_RU = 5,
/** Round to zero (truncate) **/
FD_RC_RZ = 7,
/** Rounding mode as specified in MXCSR **/
FD_RC_MXCSR = 0,
/** Rounding mode irrelevant, but SAE **/
FD_RC_SAE = 6,
} FdRoundControl;
/** Internal use only. **/
typedef struct {
uint8_t type;
uint8_t size;
uint8_t reg;
uint8_t misc;
} FdOp;
/** Never(!) access struct fields directly. Use the macros defined below. **/
typedef struct {
uint16_t type;
uint8_t flags;
uint8_t segment;
uint8_t addrsz;
uint8_t operandsz;
uint8_t size;
uint8_t evex;
FdOp operands[4];
int64_t disp;
int64_t imm;
uint64_t address;
} FdInstr;
typedef enum {
FD_ERR_UD = -1,
FD_ERR_INTERNAL = -2,
FD_ERR_PARTIAL = -3,
} FdErr;
/** Decode an instruction.
* \param buf Buffer for instruction bytes.
* \param len Length of the buffer (in bytes). An instruction is not longer than
* 15 bytes on all x86 architectures.
* \param mode Decoding mode, either 32 for protected/compatibility mode or 64
* for long mode. 16-bit mode is not supported.
* \param address Virtual address where the decoded instruction. This is used
* for computing jump targets. If "0" is passed, operands which require
* adding EIP/RIP will be stored as FD_OT_OFF operands.
* DEPRECATED: Strongly prefer passing 0 and using FD_OT_OFF operands.
* \param out_instr Pointer to the instruction buffer. Note that this may get
* partially written even if an error is returned.
* \return The number of bytes consumed by the instruction, or a negative number
* indicating an error.
**/
int fd_decode(const uint8_t* buf, size_t len, int mode, uintptr_t address,
FdInstr* out_instr);
/** Format an instruction to a string.
* \param instr The instruction.
* \param buf The buffer to hold the formatted string.
* \param len The length of the buffer.
**/
void fd_format(const FdInstr* instr, char* buf, size_t len);
/** Format an instruction to a string.
* NOTE: API stability is currently not guaranteed for this function; its name
* and/or signature may change in future.
*
* \param instr The instruction.
* \param addr The base address to use for printing FD_OT_OFF operands.
* \param buf The buffer to hold the formatted string.
* \param len The length of the buffer.
**/
void fd_format_abs(const FdInstr* instr, uint64_t addr, char* buf, size_t len);
/** Get the stringified name of an instruction type.
* NOTE: API stability is currently not guaranteed for this function; changes
* to the signature and/or the returned string can be expected. E.g., a future
* version may take an extra parameter for the instruction operand size; or may
* take a complete decoded instruction as first parameter and return the
* mnemonic returned by fd_format.
*
* \param ty An instruction type
* \return The instruction type as string, or "(invalid)".
**/
const char* fdi_name(FdInstrType ty);
/** Gets the type/mnemonic of the instruction.
* ABI STABILITY NOTE: different versions or builds of the library may use
* different values. When linking as shared library, any interpretation of this
* value is meaningless; in such cases use fdi_name.
*
* API STABILITY NOTE: a future version of this library may decode string
* instructions prefixed with REP/REPNZ and instructions prefixed with LOCK as
* separate instruction types. **/
#define FD_TYPE(instr) ((FdInstrType) (instr)->type)
/** DEPRECATED: This functionality is obsolete in favor of FD_OT_OFF.
* Gets the address of the instruction. Invalid if decoded address == 0. **/
#define FD_ADDRESS(instr) ((instr)->address)
/** Gets the size of the instruction in bytes. **/
#define FD_SIZE(instr) ((instr)->size)
/** Gets the specified segment override, or FD_REG_NONE for default segment. **/
#define FD_SEGMENT(instr) ((FdReg) (instr)->segment & 0x3f)
/** Gets the address size attribute of the instruction in bytes. **/
#define FD_ADDRSIZE(instr) (1 << (instr)->addrsz)
/** Get the logarithmic address size; FD_ADDRSIZE == 1 << FD_ADDRSIZELG **/
#define FD_ADDRSIZELG(instr) ((instr)->addrsz)
/** Gets the operation width in bytes of the instruction if this is not encoded
* in the operands, for example for the string instruction (e.g. MOVS). **/
#define FD_OPSIZE(instr) (1 << (instr)->operandsz)
/** Get the logarithmic operand size; FD_OPSIZE == 1 << FD_OPSIZELG iff
* FD_OPSIZE is valid. **/
#define FD_OPSIZELG(instr) ((instr)->operandsz)
/** Indicates whether the instruction was encoded with a REP prefix. Needed for:
* (1) Handling the instructions MOVS, STOS, LODS, INS and OUTS properly.
* (2) Handling the instructions SCAS and CMPS, for which this means REPZ. **/
#define FD_HAS_REP(instr) ((instr)->flags & FD_FLAG_REP)
/** Indicates whether the instruction was encoded with a REPNZ prefix. **/
#define FD_HAS_REPNZ(instr) ((instr)->flags & FD_FLAG_REPNZ)
/** Indicates whether the instruction was encoded with a LOCK prefix. **/
#define FD_HAS_LOCK(instr) ((instr)->flags & FD_FLAG_LOCK)
/** Do not use. **/
#define FD_IS64(instr) ((instr)->flags & FD_FLAG_64)
/** Gets the type of an operand at the given index. **/
#define FD_OP_TYPE(instr,idx) ((FdOpType) (instr)->operands[idx].type)
/** Gets the size in bytes of an operand. However, there are a few exceptions:
* (1) For some register types, e.g., segment registers, or x87 registers, the
* size is zero. (This allows some simplifications internally.)
* (2) On some vector instructions this may be only an approximation of the
* actually needed operand size (that is, an instruction may/must only use
* a smaller part than specified here). The real operand size is always
* fully recoverable in combination with the instruction type. **/
#define FD_OP_SIZE(instr,idx) (1 << (instr)->operands[idx].size >> 1)
/** Get the logarithmic size of an operand; see FD_OP_SIZE for special cases.
* The following equality holds: FD_OP_SIZE == 1 << (FD_OP_SIZELG + 1) >> 1
* Note that typically FD_OP_SIZE == 1 << FD_OP_SIZELG unless a zero-sized
* memory operand, FPU register, or mask register is involved. **/
#define FD_OP_SIZELG(instr,idx) ((instr)->operands[idx].size - 1)
/** Gets the accessed register index of a register operand. Note that /only/ the
* index is returned, no further interpretation of the index (which depends on
* the instruction type) is done. The register type can be fetched using
* FD_OP_REG_TYPE, e.g. for distinguishing high-byte registers.
* Only valid if FD_OP_TYPE == FD_OT_REG **/
#define FD_OP_REG(instr,idx) ((FdReg) (instr)->operands[idx].reg)
/** Gets the type of the accessed register.
* Only valid if FD_OP_TYPE == FD_OT_REG **/
#define FD_OP_REG_TYPE(instr,idx) ((FdRegType) (instr)->operands[idx].misc)
/** DEPRECATED: use FD_OP_REG_TYPE() == FD_RT_GPH instead.
* Returns whether the accessed register is a high-byte register. In that case,
* the register index has to be decreased by 4.
* Only valid if FD_OP_TYPE == FD_OT_REG **/
#define FD_OP_REG_HIGH(instr,idx) (FD_OP_REG_TYPE(instr,idx) == FD_RT_GPH)
/** Gets the index of the base register from a memory operand, or FD_REG_NONE,
* if the memory operand has no base register. This is the only case where the
* 64-bit register RIP can be returned, in which case the operand also has no
* scaled index register.
* Only valid if FD_OP_TYPE == FD_OT_MEM/MEMBCST **/
#define FD_OP_BASE(instr,idx) ((FdReg) (instr)->operands[idx].reg)
/** Gets the index of the index register from a memory operand, or FD_REG_NONE,
* if the memory operand has no scaled index register.
* Only valid if FD_OP_TYPE == FD_OT_MEM/MEMBCST **/
#define FD_OP_INDEX(instr,idx) ((FdReg) (instr)->operands[idx].misc & 0x3f)
/** Gets the scale of the index register from a memory operand when existent.
* This does /not/ return the scale in an absolute value but returns the amount
* of bits the index register is shifted to the left (i.e. the value in in the
* range 0-3). The actual scale can be computed easily using 1<<FD_OP_SCALE.
* Only valid if FD_OP_TYPE == FD_OT_MEM/MEMBCST and FD_OP_INDEX != NONE **/
#define FD_OP_SCALE(instr,idx) ((instr)->operands[idx].misc >> 6)
/** Gets the sign-extended displacement of a memory operand.
* Only valid if FD_OP_TYPE == FD_OT_MEM/MEMBCST **/
#define FD_OP_DISP(instr,idx) ((int64_t) (instr)->disp)
/** Get memory broadcast size in bytes.
* Only valid if FD_OP_TYPE == FD_OT_MEMBCST **/
#define FD_OP_BCSTSZ(instr,idx) (1 << FD_OP_BCSTSZLG(instr,idx))
/** Get logarithmic memory broadcast size (1 = 2-byte; 2=4-byte; 3=8-byte).
* Only valid if FD_OP_TYPE == FD_OT_MEMBCST **/
#define FD_OP_BCSTSZLG(instr,idx) ((instr)->segment >> 6)
/** Gets the (sign-extended) encoded constant for an immediate operand.
* Only valid if FD_OP_TYPE == FD_OT_IMM or FD_OP_TYPE == FD_OT_OFF **/
#define FD_OP_IMM(instr,idx) ((instr)->imm)
/** Get the opmask register for EVEX-encoded instructions; 0 for no mask. **/
#define FD_MASKREG(instr) ((instr)->evex & 0x07)
/** Get whether zero masking shall be used. Only valid if FD_MASKREG != 0. **/
#define FD_MASKZERO(instr) ((instr)->evex & 0x80)
/** Get rounding mode for EVEX-encoded instructions. See FdRoundControl. **/
#define FD_ROUNDCONTROL(instr) ((FdRoundControl) (((instr)->evex & 0x70) >> 4))
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,563 +0,0 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef _MSC_VER
#include <intrin.h>
#endif
#include <fadec.h>
#ifdef __GNUC__
#define LIKELY(x) __builtin_expect(!!(x), 1)
#define UNLIKELY(x) __builtin_expect(!!(x), 0)
#define DECLARE_ARRAY_SIZE(n) static n
#define DECLARE_RESTRICTED_ARRAY_SIZE(n) restrict static n
#else
#define LIKELY(x) (x)
#define UNLIKELY(x) (x)
#define DECLARE_ARRAY_SIZE(n) n
#define DECLARE_RESTRICTED_ARRAY_SIZE(n) n
#endif
#if defined(__has_attribute)
#if __has_attribute(fallthrough)
#define FALLTHROUGH() __attribute__((fallthrough))
#endif
#endif
#if !defined(FALLTHROUGH)
#define FALLTHROUGH() ((void)0)
#endif
struct FdStr {
const char* s;
unsigned sz;
};
#define fd_stre(s) ((struct FdStr) { (s "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"), sizeof (s)-1 })
static char*
fd_strpcat(char* restrict dst, struct FdStr src) {
#ifdef __GNUC__
unsigned lim = __builtin_constant_p(src.sz) && src.sz <= 8 ? 8 : 16;
#else
unsigned lim = 16;
#endif
for (unsigned i = 0; i < lim; i++)
dst[i] = src.s[i];
// __builtin_memcpy(dst, src.s, 16);
return dst + src.sz;
}
static unsigned
fd_clz64(uint64_t v) {
#if defined(__GNUC__)
return __builtin_clzll(v);
#elif defined(_MSC_VER)
unsigned long index;
// 32-bit MSVC doesn't support _BitScanReverse64. This is an attempt to
// identify this case.
#if INTPTR_MAX == INT64_MAX
_BitScanReverse64(&index, v);
#else
if (_BitScanReverse(&index, v >> 32))
return 31 - index;
_BitScanReverse(&index, v & 0xffffffff);
#endif
return 63 - index;
#else
#error Unsupported compiler.
#endif
}
#if defined(__SSE2__)
#include <immintrin.h>
#endif
static char*
fd_strpcatnum(char dst[DECLARE_ARRAY_SIZE(18)], uint64_t val) {
unsigned lz = fd_clz64(val|1);
unsigned numbytes = 16 - (lz / 4);
#if defined(__SSE2__)
__m128i mv = _mm_set_epi64x(0, val << (lz & -4));
__m128i mvp = _mm_unpacklo_epi8(mv, mv);
__m128i mva = _mm_srli_epi16(mvp, 12);
__m128i mvb = _mm_and_si128(mvp, _mm_set1_epi16(0x0f00u));
__m128i ml = _mm_or_si128(mva, mvb);
__m128i mn = _mm_or_si128(ml, _mm_set1_epi8(0x30));
__m128i mgt = _mm_cmpgt_epi8(ml, _mm_set1_epi8(9));
__m128i mgtm = _mm_and_si128(mgt, _mm_set1_epi8(0x61 - 0x3a));
__m128i ma = _mm_add_epi8(mn, mgtm);
__m128i msw = _mm_shufflehi_epi16(_mm_shufflelo_epi16(ma, 0x1b), 0x1b);
__m128i ms = _mm_shuffle_epi32(msw, 0x4e);
_mm_storeu_si128((__m128i_u*) (dst + 2), ms);
#else
unsigned idx = numbytes + 2;
do {
dst[--idx] = "0123456789abcdef"[val % 16];
val /= 16;
} while (val);
#endif
dst[0] = '0';
dst[1] = 'x';
return dst + numbytes + 2;
}
static char*
fd_strpcatreg(char* restrict dst, size_t rt, size_t ri, unsigned size) {
const char* nametab =
"\2al\4bnd0\2cl\4bnd1\2dl\4bnd2\2bl\4bnd3"
"\3spl\0 \3bpl\0 \3sil\0 \3dil\0 "
"\3r8b\0 \3r9b\0 \4r10b\0 \4r11b\0 "
"\4r12b\2ah\4r13b\2ch\4r14b\2dh\4r15b\2bh\0\0 "
"\2ax\4tmm0\2cx\4tmm1\2dx\4tmm2\2bx\4tmm3"
"\2sp\4tmm4\2bp\4tmm5\2si\4tmm6\2di\4tmm7"
"\3r8w \2es\3r9w \2cs\4r10w\2ss\4r11w\2ds"
"\4r12w\2fs\4r13w\2gs\4r14w\0 \4r15w\0 \2ip\0 "
"\3eax\3mm0\3ecx\3mm1\3edx\3mm2\3ebx\3mm3"
"\3esp\3mm4\3ebp\3mm5\3esi\3mm6\3edi\3mm7"
"\3r8d \2k0\3r9d \2k1\4r10d\2k2\4r11d\2k3"
"\4r12d\2k4\4r13d\2k5\4r14d\2k6\4r15d\2k7\3eip\0 "
"\3rax\3cr0\3rcx\0 \3rdx\3cr2\3rbx\3cr3"
"\3rsp\3cr4\3rbp\0 \3rsi\0 \3rdi\0 "
"\2r8 \3cr8\2r9 \3dr0\3r10\3dr1\3r11\3dr2"
"\3r12\3dr3\3r13\3dr4\3r14\3dr5\3r15\3dr6\3rip\3dr7"
"\5st(0)\0 \5st(1)\0 \5st(2)\0 \5st(3)\0 "
"\5st(4)\0 \5st(5)\0 \5st(6)\0 \5st(7)\0 "
"\4xmm0\0 \4xmm1\0 \4xmm2\0 \4xmm3\0 "
"\4xmm4\0 \4xmm5\0 \4xmm6\0 \4xmm7\0 "
"\4xmm8\0 \4xmm9\0 \5xmm10\0 \5xmm11\0 "
"\5xmm12\0 \5xmm13\0 \5xmm14\0 \5xmm15\0 "
"\5xmm16\0 \5xmm17\0 \5xmm18\0 \5xmm19\0 "
"\5xmm20\0 \5xmm21\0 \5xmm22\0 \5xmm23\0 "
"\5xmm24\0 \5xmm25\0 \5xmm26\0 \5xmm27\0 "
"\5xmm28\0 \5xmm29\0 \5xmm30\0 \5xmm31\0 ";
static const uint16_t nametabidx[] = {
[FD_RT_GPL] = 0 * 17*8 + 0 * 8 + 0,
[FD_RT_GPH] = 0 * 17*8 + 8 * 8 + 5,
[FD_RT_SEG] = 1 * 17*8 + 8 * 8 + 5,
[FD_RT_FPU] = 4 * 17*8 + 0 * 8 + 0,
[FD_RT_MMX] = 2 * 17*8 + 0 * 8 + 4,
[FD_RT_VEC] = 4 * 17*8 + 8 * 8 + 0,
[FD_RT_MASK]= 2 * 17*8 + 8 * 8 + 5,
[FD_RT_BND] = 0 * 17*8 + 0 * 8 + 3,
[FD_RT_CR] = 3 * 17*8 + 0 * 8 + 4,
[FD_RT_DR] = 3 * 17*8 + 9 * 8 + 4,
[FD_RT_TMM] = 1 * 17*8 + 0 * 8 + 3,
};
unsigned idx = rt == FD_RT_GPL ? size * 17*8 : nametabidx[rt];
const char* name = nametab + idx + 8*ri;
for (unsigned i = 0; i < 8; i++)
dst[i] = name[i+1];
if (UNLIKELY(rt == FD_RT_VEC && size > 4))
dst[0] += size - 4;
return dst + *name;
}
const char*
fdi_name(FdInstrType ty) {
(void) ty;
return "(invalid)";
}
static char*
fd_mnemonic(char buf[DECLARE_RESTRICTED_ARRAY_SIZE(48)], const FdInstr* instr) {
#define FD_DECODE_TABLE_STRTAB1
static const char* mnemonic_str =
#include <fadec-decode-private.inc>
// 20 NULL Bytes to prevent out-of-bounds reads
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
#undef FD_DECODE_TABLE_STRTAB1
#define FD_DECODE_TABLE_STRTAB2
static const uint16_t mnemonic_offs[] = {
#include <fadec-decode-private.inc>
};
#undef FD_DECODE_TABLE_STRTAB2
#define FD_DECODE_TABLE_STRTAB3
static const uint8_t mnemonic_lens[] = {
#include <fadec-decode-private.inc>
};
#undef FD_DECODE_TABLE_STRTAB3
const char* mnem = &mnemonic_str[mnemonic_offs[FD_TYPE(instr)]];
unsigned mnemlen = mnemonic_lens[FD_TYPE(instr)];
bool prefix_xacq_xrel = false;
bool prefix_segment = false;
char sizesuffix[4] = {0};
unsigned sizesuffixlen = 0;
if (UNLIKELY(FD_OP_TYPE(instr, 0) == FD_OT_OFF && FD_OP_SIZELG(instr, 0) == 1))
sizesuffix[0] = 'w', sizesuffixlen = 1;
switch (FD_TYPE(instr)) {
case FDI_C_SEP:
mnem += FD_OPSIZE(instr) & 0xc;
mnemlen = 3;
break;
case FDI_C_EX:
mnem += FD_OPSIZE(instr) & 0xc;
mnemlen = FD_OPSIZE(instr) < 4 ? 3 : 4;
break;
case FDI_CMPXCHGD:
switch (FD_OPSIZELG(instr)) {
default: break;
case 2: sizesuffix[0] = '8', sizesuffix[1] = 'b', sizesuffixlen = 2; break;
case 3: sizesuffix[0] = '1', sizesuffix[1] = '6', sizesuffix[2] = 'b', sizesuffixlen = 3; break;
}
break;
case FDI_JCXZ:
mnemlen = FD_ADDRSIZELG(instr) == 1 ? 4 : 5;
mnem += 5 * (FD_ADDRSIZELG(instr) - 1);
break;
case FDI_PUSH:
if (FD_OP_SIZELG(instr, 0) == 1 && FD_OP_TYPE(instr, 0) == FD_OT_IMM)
sizesuffix[0] = 'w', sizesuffixlen = 1;
FALLTHROUGH();
case FDI_POP:
if (FD_OP_SIZELG(instr, 0) == 1 && FD_OP_TYPE(instr, 0) == FD_OT_REG &&
FD_OP_REG_TYPE(instr, 0) == FD_RT_SEG)
sizesuffix[0] = 'w', sizesuffixlen = 1;
break;
case FDI_XCHG:
if (FD_OP_TYPE(instr, 0) == FD_OT_MEM)
prefix_xacq_xrel = true;
break;
case FDI_MOV:
// MOV C6h/C7h can have XRELEASE prefix.
if (FD_HAS_REP(instr) && FD_OP_TYPE(instr, 0) == FD_OT_MEM &&
FD_OP_TYPE(instr, 1) == FD_OT_IMM)
prefix_xacq_xrel = true;
break;
case FDI_FXSAVE:
case FDI_FXRSTOR:
case FDI_XSAVE:
case FDI_XSAVEC:
case FDI_XSAVEOPT:
case FDI_XSAVES:
case FDI_XRSTOR:
case FDI_XRSTORS:
if (FD_OPSIZELG(instr) == 3)
sizesuffix[0] = '6', sizesuffix[1] = '4', sizesuffixlen = 2;
break;
case FDI_EVX_MOV_G2X:
case FDI_EVX_MOV_X2G:
case FDI_EVX_PEXTR:
sizesuffix[0] = "bwdq"[FD_OP_SIZELG(instr, 0)];
sizesuffixlen = 1;
break;
case FDI_EVX_PBROADCAST:
sizesuffix[0] = "bwdq"[FD_OP_SIZELG(instr, 1)];
sizesuffixlen = 1;
break;
case FDI_EVX_PINSR:
sizesuffix[0] = "bwdq"[FD_OP_SIZELG(instr, 2)];
sizesuffixlen = 1;
break;
case FDI_RET:
case FDI_ENTER:
case FDI_LEAVE:
if (FD_OPSIZELG(instr) == 1)
sizesuffix[0] = 'w', sizesuffixlen = 1;
break;
case FDI_LODS:
case FDI_MOVS:
case FDI_CMPS:
case FDI_OUTS:
prefix_segment = true;
FALLTHROUGH();
case FDI_STOS:
case FDI_SCAS:
case FDI_INS:
if (FD_HAS_REP(instr))
buf = fd_strpcat(buf, fd_stre("rep "));
if (FD_HAS_REPNZ(instr))
buf = fd_strpcat(buf, fd_stre("repnz "));
if (FD_IS64(instr) && FD_ADDRSIZELG(instr) == 2)
buf = fd_strpcat(buf, fd_stre("addr32 "));
if (!FD_IS64(instr) && FD_ADDRSIZELG(instr) == 1)
buf = fd_strpcat(buf, fd_stre("addr16 "));
FALLTHROUGH();
case FDI_IN:
case FDI_OUT:
if (FD_OP_TYPE(instr, 0) != FD_OT_NONE)
break;
FALLTHROUGH();
case FDI_PUSHA:
case FDI_POPA:
case FDI_PUSHF:
case FDI_POPF:
case FDI_RETF:
case FDI_IRET:
sizesuffix[0] = "bwdq"[FD_OPSIZELG(instr)];
sizesuffixlen = 1;
break;
default: break;
}
if (UNLIKELY(prefix_xacq_xrel || FD_HAS_LOCK(instr))) {
if (FD_HAS_REP(instr))
buf = fd_strpcat(buf, fd_stre("xrelease "));
if (FD_HAS_REPNZ(instr))
buf = fd_strpcat(buf, fd_stre("xacquire "));
}
if (UNLIKELY(FD_HAS_LOCK(instr)))
buf = fd_strpcat(buf, fd_stre("lock "));
if (UNLIKELY(prefix_segment && FD_SEGMENT(instr) != FD_REG_NONE)) {
*buf++ = "ecsdfg\0"[FD_SEGMENT(instr) & 7];
*buf++ = 's';
*buf++ = ' ';
}
for (unsigned i = 0; i < 20; i++)
buf[i] = mnem[i];
buf += mnemlen;
for (unsigned i = 0; i < 4; i++)
buf[i] = sizesuffix[i];
buf += sizesuffixlen;
return buf;
}
static char*
fd_format_impl(char buf[DECLARE_RESTRICTED_ARRAY_SIZE(128)], const FdInstr* instr, uint64_t addr) {
buf = fd_mnemonic(buf, instr);
for (int i = 0; i < 4; i++)
{
FdOpType op_type = FD_OP_TYPE(instr, i);
if (op_type == FD_OT_NONE)
break;
if (i > 0)
*buf++ = ',';
*buf++ = ' ';
int size = FD_OP_SIZELG(instr, i);
if (op_type == FD_OT_REG) {
unsigned type = FD_OP_REG_TYPE(instr, i);
unsigned idx = FD_OP_REG(instr, i);
buf = fd_strpcatreg(buf, type, idx, size);
} else if (op_type == FD_OT_MEM || op_type == FD_OT_MEMBCST) {
unsigned idx_rt = FD_RT_GPL;
unsigned idx_sz = FD_ADDRSIZELG(instr);
switch (FD_TYPE(instr)) {
case FDI_CMPXCHGD: size = FD_OPSIZELG(instr) + 1; break;
case FDI_BOUND: size += 1; break;
case FDI_JMPF:
case FDI_CALLF:
case FDI_LDS:
case FDI_LES:
case FDI_LFS:
case FDI_LGS:
case FDI_LSS:
size += 6;
break;
case FDI_FLD:
case FDI_FSTP:
case FDI_FBLD:
case FDI_FBSTP:
size = size >= 0 ? size : 9;
break;
case FDI_VPGATHERQD:
case FDI_VGATHERQPS:
case FDI_EVX_PGATHERQD:
case FDI_EVX_GATHERQPS:
idx_rt = FD_RT_VEC;
idx_sz = FD_OP_SIZELG(instr, 0) + 1;
break;
case FDI_EVX_PSCATTERQD:
case FDI_EVX_SCATTERQPS:
idx_rt = FD_RT_VEC;
idx_sz = FD_OP_SIZELG(instr, 1) + 1;
break;
case FDI_VPGATHERDQ:
case FDI_VGATHERDPD:
case FDI_EVX_PGATHERDQ:
case FDI_EVX_GATHERDPD:
idx_rt = FD_RT_VEC;
idx_sz = FD_OP_SIZELG(instr, 0) - 1;
break;
case FDI_EVX_PSCATTERDQ:
case FDI_EVX_SCATTERDPD:
idx_rt = FD_RT_VEC;
idx_sz = FD_OP_SIZELG(instr, 1) - 1;
break;
case FDI_VPGATHERDD:
case FDI_VPGATHERQQ:
case FDI_VGATHERDPS:
case FDI_VGATHERQPD:
case FDI_EVX_PGATHERDD:
case FDI_EVX_PGATHERQQ:
case FDI_EVX_GATHERDPS:
case FDI_EVX_GATHERQPD:
idx_rt = FD_RT_VEC;
idx_sz = FD_OP_SIZELG(instr, 0);
break;
case FDI_EVX_PSCATTERDD:
case FDI_EVX_PSCATTERQQ:
case FDI_EVX_SCATTERDPS:
case FDI_EVX_SCATTERQPD:
idx_rt = FD_RT_VEC;
idx_sz = FD_OP_SIZELG(instr, 1);
break;
default: break;
}
if (op_type == FD_OT_MEMBCST)
size = FD_OP_BCSTSZLG(instr, i);
const char* ptrsizes =
"\00 "
"\11byte ptr "
"\11word ptr "
"\12dword ptr "
"\12qword ptr "
"\14xmmword ptr "
"\14ymmword ptr "
"\14zmmword ptr "
"\12dword ptr " // far ptr; word + 2
"\12fword ptr " // far ptr; dword + 2
"\12tbyte ptr "; // far ptr/FPU; qword + 2
const char* ptrsize = ptrsizes + 16 * (size + 1);
buf = fd_strpcat(buf, (struct FdStr) { ptrsize+1, *ptrsize });
unsigned seg = FD_SEGMENT(instr);
if (seg != FD_REG_NONE) {
*buf++ = "ecsdfg\0"[seg & 7];
*buf++ = 's';
*buf++ = ':';
}
*buf++ = '[';
bool has_base = FD_OP_BASE(instr, i) != FD_REG_NONE;
bool has_idx = FD_OP_INDEX(instr, i) != FD_REG_NONE;
if (has_base)
buf = fd_strpcatreg(buf, FD_RT_GPL, FD_OP_BASE(instr, i), FD_ADDRSIZELG(instr));
if (has_idx) {
if (has_base)
*buf++ = '+';
*buf++ = '0' + (1 << FD_OP_SCALE(instr, i));
*buf++ = '*';
buf = fd_strpcatreg(buf, idx_rt, FD_OP_INDEX(instr, i), idx_sz);
}
uint64_t disp = FD_OP_DISP(instr, i);
if (disp && (has_base || has_idx)) {
*buf++ = (int64_t) disp < 0 ? '-' : '+';
if ((int64_t) disp < 0)
disp = -disp;
}
if (FD_ADDRSIZELG(instr) == 1)
disp &= 0xffff;
else if (FD_ADDRSIZELG(instr) == 2)
disp &= 0xffffffff;
if (disp || (!has_base && !has_idx))
buf = fd_strpcatnum(buf, disp);
*buf++ = ']';
if (UNLIKELY(op_type == FD_OT_MEMBCST)) {
// {1toX}, X = FD_OP_SIZE(instr, i) / BCSTSZ (=> 2/4/8/16/32)
unsigned bcstszidx = FD_OP_SIZELG(instr, i) - FD_OP_BCSTSZLG(instr, i) - 1;
const char* bcstsizes = "\6{1to2} \6{1to4} \6{1to8} \7{1to16}\7{1to32} ";
const char* bcstsize = bcstsizes + bcstszidx * 8;
buf = fd_strpcat(buf, (struct FdStr) { bcstsize+1, *bcstsize });
}
} else if (op_type == FD_OT_IMM || op_type == FD_OT_OFF) {
uint64_t immediate = FD_OP_IMM(instr, i);
// Some instructions have actually two immediate operands which are
// decoded as a single operand. Split them here appropriately.
switch (FD_TYPE(instr)) {
default:
goto nosplitimm;
case FDI_SSE_EXTRQ:
case FDI_SSE_INSERTQ:
buf = fd_strpcatnum(buf, immediate & 0xff);
buf = fd_strpcat(buf, fd_stre(", "));
immediate = (immediate >> 8) & 0xff;
break;
case FDI_ENTER:
buf = fd_strpcatnum(buf, immediate & 0xffff);
buf = fd_strpcat(buf, fd_stre(", "));
immediate = (immediate >> 16) & 0xff;
break;
case FDI_JMPF:
case FDI_CALLF:
buf = fd_strpcatnum(buf, (immediate >> (8 << size)) & 0xffff);
*buf++ = ':';
// immediate is masked below.
break;
}
nosplitimm:
if (op_type == FD_OT_OFF)
immediate += addr + FD_SIZE(instr);
if (size == 0)
immediate &= 0xff;
else if (size == 1)
immediate &= 0xffff;
else if (size == 2)
immediate &= 0xffffffff;
buf = fd_strpcatnum(buf, immediate);
}
if (i == 0 && FD_MASKREG(instr)) {
*buf++ = '{';
buf = fd_strpcatreg(buf, FD_RT_MASK, FD_MASKREG(instr), 0);
*buf++ = '}';
if (FD_MASKZERO(instr))
buf = fd_strpcat(buf, fd_stre("{z}"));
}
}
if (UNLIKELY(FD_ROUNDCONTROL(instr) != FD_RC_MXCSR)) {
switch (FD_ROUNDCONTROL(instr)) {
case FD_RC_RN: buf = fd_strpcat(buf, fd_stre(", {rn-sae}")); break;
case FD_RC_RD: buf = fd_strpcat(buf, fd_stre(", {rd-sae}")); break;
case FD_RC_RU: buf = fd_strpcat(buf, fd_stre(", {ru-sae}")); break;
case FD_RC_RZ: buf = fd_strpcat(buf, fd_stre(", {rz-sae}")); break;
case FD_RC_SAE: buf = fd_strpcat(buf, fd_stre(", {sae}")); break;
default: break; // should not happen
}
}
*buf++ = '\0';
return buf;
}
void
fd_format(const FdInstr* instr, char* buffer, size_t len)
{
fd_format_abs(instr, 0, buffer, len);
}
void
fd_format_abs(const FdInstr* instr, uint64_t addr, char* restrict buffer, size_t len) {
char tmp[128];
char* buf = buffer;
if (UNLIKELY(len < 128)) {
if (!len)
return;
buf = tmp;
}
char* end = fd_format_impl(buf, instr, addr);
if (buf != buffer) {
unsigned i;
for (i = 0; i < (end - tmp) && i < len-1; i++)
buffer[i] = tmp[i];
buffer[i] = '\0';
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,126 +0,0 @@
project('fadec', ['c'], default_options: ['warning_level=3', 'c_std=c11'],
meson_version: '>=0.49')
python3 = find_program('python3')
# Check Python version
py_version_res = run_command(python3, ['--version'], check: true)
py_version = py_version_res.stdout().split(' ')[1]
if not py_version.version_compare('>=3.9')
error('Python 3.9 required, got @0@'.format(py_version))
endif
has_cpp = add_languages('cpp', required: false)
cc = meson.get_compiler('c')
if cc.has_argument('-fstrict-aliasing')
add_project_arguments('-fstrict-aliasing', language: 'c')
endif
if get_option('warning_level').to_int() >= 3
extra_warnings = [
'-Wmissing-prototypes', '-Wshadow', '-Wwrite-strings', '-Wswitch-default',
'-Winline', '-Wstrict-prototypes', '-Wundef',
# We have strings longer than 4095 characters
'-Wno-overlength-strings',
# GCC 8 requires an extra option for strict cast alignment checks, Clang
# always warns, even on architectures without alignment requirements.
'-Wcast-align', '-Wcast-align=strict',
]
add_project_arguments(cc.get_supported_arguments(extra_warnings), language: 'c')
endif
if cc.get_argument_syntax() == 'msvc'
# Disable some warnings to align warnings with GCC and Clang:
add_project_arguments('-D_CRT_SECURE_NO_WARNINGS',
'/wd4018', # - Signed/unsigned comparison
'/wd4146', # - Unary minus operator applied to unsigned
# type, result still unsigned
'/wd4244', # - Possible loss of data in conversion
# from integer type to smaller integer type
'/wd4245', # - Signed/unsigned assignment
'/wd4267', # - Possible loss of data in conversion
# from size_t to smaller type
'/wd4310', # - Possible loss of data in conversion
# of constant value to smaller type
language: 'c')
endif
if cc.get_id() == 'msvc' and has_cpp
cxx = meson.get_compiler('cpp')
if cxx.get_id() == 'msvc'
# Enable standard conformant preprocessor
add_project_arguments(cxx.get_supported_arguments(['-Zc:preprocessor']), language: 'cpp')
endif
endif
sources = []
headers = []
components = []
if get_option('with_decode')
components += 'decode'
headers += files('fadec.h')
sources += files('decode.c', 'format.c')
endif
if get_option('with_encode')
components += 'encode'
headers += files('fadec-enc.h')
sources += files('encode.c')
endif
if get_option('with_encode2')
components += 'encode2'
headers += files('fadec-enc2.h')
sources += files('encode2.c')
endif
generate_args = []
if get_option('archmode') != 'only64'
generate_args += ['--32']
endif
if get_option('archmode') != 'only32'
generate_args += ['--64']
endif
if get_option('with_undoc')
generate_args += ['--with-undoc']
endif
if not meson.is_subproject()
generate_args += ['--stats']
endif
tables = []
foreach component : components
tables += custom_target('@0@_table'.format(component),
command: [python3, '@INPUT0@', component,
'@INPUT1@', '@OUTPUT@'] + generate_args,
input: files('parseinstrs.py', 'instrs.txt'),
output: ['fadec-@0@-public.inc'.format(component),
'fadec-@0@-private.inc'.format(component)],
install: true,
install_dir: [get_option('includedir'), false])
endforeach
libfadec = static_library('fadec', sources, tables, install: true)
fadec = declare_dependency(link_with: libfadec,
include_directories: include_directories('.'),
sources: tables)
install_headers(headers)
foreach component : components
test(component, executable('@0@-test'.format(component),
'@0@-test.c'.format(component),
dependencies: fadec))
if component == 'encode2' and has_cpp
test(component + '-cpp', executable('@0@-test-cpp'.format(component),
'@0@-test.cc'.format(component),
dependencies: fadec))
endif
endforeach
if meson.version().version_compare('>=0.54.0')
meson.override_dependency('fadec', fadec)
endif
pkg = import('pkgconfig')
pkg.generate(libraries: libfadec,
version: '0.1',
name: 'fadec',
filebase: 'fadec',
description: 'Fast Decoder for x86-32 and x86-64')

View File

@@ -1,6 +0,0 @@
option('archmode', type: 'combo', choices: ['both', 'only32', 'only64'])
option('with_undoc', type: 'boolean', value: false)
option('with_decode', type: 'boolean', value: true)
option('with_encode', type: 'boolean', value: true)
# encode2 is off-by-default to reduce size and compile-time
option('with_encode2', type: 'boolean', value: false)

File diff suppressed because it is too large Load Diff

View File

@@ -1,431 +0,0 @@
# CLion
.idea/
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
*.env
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
[Dd]ebug/x64/
[Dd]ebugPublic/x64/
[Rr]elease/x64/
[Rr]eleases/x64/
bin/x64/
obj/x64/
[Dd]ebug/x86/
[Dd]ebugPublic/x86/
[Rr]elease/x86/
[Rr]eleases/x86/
bin/x86/
obj/x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
[Aa][Rr][Mm]64[Ee][Cc]/
bld/
[Oo]bj/
[Oo]ut/
[Ll]og/
[Ll]ogs/
# Build results on 'Bin' directories
**/[Bb]in/*
# Uncomment if you have tasks that rely on *.refresh files to move binaries
# (https://github.com/github/gitignore/pull/3736)
#!**/[Bb]in/*.refresh
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
*.trx
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Approval Tests result files
*.received.*
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.idb
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
# but not Directory.Build.rsp, as it configures directory-level build defaults
!Directory.Build.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
**/.paket/paket.exe
paket-files/
# FAKE - F# Make
**/.fake/
# CodeRush personal settings
**/.cr/personal
# Python Tools for Visual Studio (PTVS)
**/__pycache__/
*.pyc
# Cake - Uncomment if you are using it
#tools/**
#!tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
MSBuild_Logs/
# AWS SAM Build and Temporary Artifacts folder
.aws-sam
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
**/.mfractor/
# Local History for Visual Studio
**/.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
**/.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp

View File

@@ -1,9 +0,0 @@
cmake_minimum_required(VERSION 3.16)
project(raw_pdb)
set(CMAKE_CXX_STANDARD 11)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
add_subdirectory(src)

View File

@@ -1,25 +0,0 @@
BSD 2-Clause License
Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,190 +0,0 @@
# RawPDB
**RawPDB** is a C++11 library that directly reads Microsoft Program DataBase PDB files. The code is extracted almost directly from <a href="https://liveplusplus.tech/">Live++ 2</a>, a battle-tested hot-reload tool for C++.
## Design
**RawPDB** gives you direct access to the stream data contained in a PDB file. It does not attempt to offer abstractions for iterating symbols, translation units, contributions, etc.
Building a high-level abstraction over the provided low-level data is an ill-fated attempt that can never really be performant for everybody, because different tools like debuggers, hot-reload tools (e.g. <a href="https://liveplusplus.tech/">Live++</a>), profilers (e.g. <a href="https://superluminal.eu/">Superluminal</a>), need to perform different queries against the stored data.
We therefore believe the best solution is to offer direct access to the underlying data, with applications bringing that data into their own structures.
## Goal
Eventually, we want **RawPDB** to become the de-facto replacement of <a href="https://docs.microsoft.com/en-us/visualstudio/debugger/debug-interface-access/debug-interface-access-sdk">Microsoft's DIA SDK</a> that most C++ developers (have to) use.
## Features
* Fast - **RawPDB** works directly with memory-mapped data, so only the data from the streams you touch affect performance. It is orders of magnitudes faster than the DIA SDK, and faster than comparable LLVM code
* Scalable - **RawPDB's** API gives you access to individual streams that can all be read concurrently in a trivial fashion, since all returned data structures are immutable. There are no locks or waits anywhere inside the library
* Lightweight - **RawPDB** is small and compiles in roughly 1 second
* Allocation-friendly - **RawPDB** performs only a few allocations, and those can be overridden easily by changing the underlying macro
* No STL - **RawPDB** does not need any STL containers or algorithms
* No exceptions - **RawPDB** does not use exceptions
* No RTTI - **RawPDB** does not need RTTI or use class hierarchies
* High-quality code - **RawPDB** compiles clean under -Wall
## Building
The code compiles clean under Visual Studio 2015, 2017, 2019, or 2022. A solution for Visual Studio 2019 is included.
## Performance
Running the **Symbols** and **Contributions** examples on a 1GiB PDB yields the following output:
<pre>
Opening PDB file C:\Development\llvm-project\build\tools\clang\unittests\Tooling\RelWithDebInfo\ToolingTests.pdb
Running example "Symbols"
| Reading image section stream
| ---> done in 0.066ms
| Reading module info stream
| ---> done in 0.562ms
| Reading symbol record stream
| ---> done in 25.185ms
| Reading public symbol stream
| ---> done in 1.133ms
| Storing public symbols
| ---> done in 46.171ms (212023 elements)
| Reading global symbol stream
| ---> done in 1.381ms
| Storing global symbols
| ---> done in 12.769ms (448957 elements)
| Storing symbols from modules
| ---> done in 145.849ms (2243 elements)
---> done in 233.694ms (539611 elements)
</pre>
<pre>
Opening PDB file C:\Development\llvm-project\build\tools\clang\unittests\Tooling\RelWithDebInfo\ToolingTests.pdb
Running example "Contributions"
| Reading image section stream
| ---> done in 0.066ms
| Reading module info stream
| ---> done in 0.594ms
| Reading section contribution stream
| ---> done in 9.839ms
| Storing contributions
| ---> done in 67.346ms (630924 elements)
| std::sort contributions
| ---> done in 19.218ms
---> done in 97.283ms
20 largest contributions:
1: 1896496 bytes from LLVMAMDGPUCodeGen.dir\RelWithDebInfo\AMDGPUInstructionSelector.obj
2: 1700720 bytes from LLVMHexagonCodeGen.dir\RelWithDebInfo\HexagonInstrInfo.obj
3: 1536470 bytes from LLVMRISCVCodeGen.dir\RelWithDebInfo\RISCVISelDAGToDAG.obj
4: 1441408 bytes from LLVMAArch64CodeGen.dir\RelWithDebInfo\AArch64InstructionSelector.obj
5: 1187048 bytes from LLVMRISCVCodeGen.dir\RelWithDebInfo\RISCVInstructionSelector.obj
6: 1026504 bytes from LLVMARMCodeGen.dir\RelWithDebInfo\ARMInstructionSelector.obj
7: 952080 bytes from LLVMAMDGPUDesc.dir\RelWithDebInfo\AMDGPUMCTargetDesc.obj
8: 849888 bytes from LLVMX86Desc.dir\RelWithDebInfo\X86MCTargetDesc.obj
9: 712176 bytes from LLVMHexagonCodeGen.dir\RelWithDebInfo\HexagonInstrInfo.obj
10: 679035 bytes from LLVMX86CodeGen.dir\RelWithDebInfo\X86ISelDAGToDAG.obj
11: 525174 bytes from LLVMAMDGPUDesc.dir\RelWithDebInfo\AMDGPUMCTargetDesc.obj
12: 523035 bytes from * Linker *
13: 519312 bytes from LLVMRISCVDesc.dir\RelWithDebInfo\RISCVMCTargetDesc.obj
14: 512496 bytes from LLVMVEDesc.dir\RelWithDebInfo\VEMCTargetDesc.obj
15: 498768 bytes from LLVMX86CodeGen.dir\RelWithDebInfo\X86InstructionSelector.obj
16: 483528 bytes from LLVMMipsCodeGen.dir\RelWithDebInfo\MipsInstructionSelector.obj
17: 449472 bytes from LLVMAMDGPUCodeGen.dir\RelWithDebInfo\AMDGPUISelDAGToDAG.obj
18: 444246 bytes from C:\Development\llvm-project\build\tools\clang\lib\Basic\obj.clangBasic.dir\RelWithDebInfo\DiagnosticIDs.obj
19: 371584 bytes from LLVMAArch64CodeGen.dir\RelWithDebInfo\AArch64ISelDAGToDAG.obj
20: 370272 bytes from LLVMNVPTXDesc.dir\RelWithDebInfo\NVPTXMCTargetDesc.obj
</pre>
This is at least an order of magnitude faster than DIA, even though the example code is completely serial and uses std::vector, std::string, and std::sort, which are used for illustration purposes only.
When reading streams in a concurrent fashion, you will most likely be limited by the speed at which the OS can bring the data into your process.
Running the **Lines** example on a 1.37 GiB PDB yields the following output:
<pre>
Opening PDB file C:\pdb-test-files\clang-debug.pdb
Version 20000404, signature 1658696914, age 1, GUID 563dd8f1-f32b-459b-8c2beae0e70bc19b
Running example "Lines"
| Reading image section stream
| ---> done in 0.313ms
| Reading module info stream
| ---> done in 0.403ms
| Reading names stream
| ---> done in 0.126ms
| Storing lines from modules
| ---> done in 306.720ms (1847 elements)
| std::sort sections
| ---> done in 103.090ms (4023680 elements)
</pre>
## Supported streams
**RawPDB** gives you access to the following PDB stream data:
* DBI stream data
* Public symbols
* Global symbols
* Modules
* Module symbols
* Module lines (C13 line information)
* Image sections
* Info stream
* "/names" stream
* Section contributions
* Source files
* IPI stream data
* TPI stream data
Furthermore, PDBs linked using /DEBUG:FASTLINK are not supported. These PDBs do not contain much information, since private symbol information is distributed among object files and library files.
## Documentation
If you are unfamiliar with the basic structure of a PDB file, the <a href="https://llvm.org/docs/PDB/index.html">LLVM documentation</a> serves as a good introduction.
Consult the example code to see how to read and parse the PDB streams.
## Directory structure
* bin: contains final binary output files (.exe and .pdb)
* build: contains Visual Studio 2019 solution and project files
* lib: contains the RawPDB library output files (.lib and .pdb)
* src: contains the RawPDB source code, as well as example code
* temp: contains intermediate build artefacts
## Examples
### Symbols (<a href="https://github.com/MolecularMatters/raw_pdb/blob/main/src/Examples/ExampleSymbols.cpp">ExampleSymbols.cpp</a>)
A basic example that shows how to load symbols from public, global, and module streams.
### Contributions (<a href="https://github.com/MolecularMatters/raw_pdb/blob/main/src/Examples/ExampleContributions.cpp">ExampleContributions.cpp</a>)
A basic example that shows how to load contributions, sort them by size, and output the 20 largest ones along with the object file they originated from.
### Function symbols (<a href="https://github.com/MolecularMatters/raw_pdb/blob/main/src/Examples/ExampleFunctionSymbols.cpp">ExampleFunctionSymbols.cpp</a>)
An example intended for profiler developers that shows how to enumerate all function symbols and retrieve or compute their code size.
### Function variables (<a href="https://github.com/MolecularMatters/raw_pdb/blob/main/src/Examples/ExampleFunctionVariables.cpp">ExampleFunctionVariables.cpp</a>)
An example intended for debugger developers that shows how to enumerate all function records needed for displaying function variables.
### Lines (<a href="https://github.com/MolecularMatters/raw_pdb/blob/main/src/Examples/ExampleLines.cpp">ExampleLines.cpp</a>)
An example that shows to how to load line information for all modules.
### Types (<a href="https://github.com/MolecularMatters/raw_pdb/blob/main/src/Examples/ExampleTypes.cpp">ExampleTypes.cpp</a>)
An example that prints all type records.
### PDBSize (<a href="https://github.com/MolecularMatters/raw_pdb/blob/main/src/Examples/ExamplePDBSize.cpp">ExamplePDBSize.cpp</a>)
An example that could serve as a starting point for people wanting to investigate and optimize the size of their PDBs.
## Sponsoring or supporting RawPDB
We have chosen a very liberal license to let **RawPDB** be used in as many scenarios as possible, including commercial applications. If you would like to support its development, consider licensing <a href="https://liveplusplus.tech/">Live++</a> instead. Not only do you give something back, but get a great productivity enhancement on top!

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="PDB::ArrayView&lt;*&gt;">
<DisplayString>{{ size={m_length} }}</DisplayString>
<Expand>
<ArrayItems>
<Size>m_length</Size>
<ValuePointer>m_data</ValuePointer>
</ArrayItems>
</Expand>
</Type>
</AutoVisualizer>

View File

@@ -1,112 +0,0 @@
set(SOURCES
Foundation/PDB_ArrayView.h
Foundation/PDB_Assert.h
Foundation/PDB_BitOperators.h
Foundation/PDB_BitUtil.h
Foundation/PDB_CRT.h
Foundation/PDB_Forward.h
Foundation/PDB_Log.h
Foundation/PDB_Macros.h
Foundation/PDB_Memory.h
Foundation/PDB_Move.h
Foundation/PDB_Platform.h
Foundation/PDB_PointerUtil.h
Foundation/PDB_TypeTraits.h
Foundation/PDB_Warnings.h
PDB.cpp
PDB.h
PDB_CoalescedMSFStream.cpp
PDB_CoalescedMSFStream.h
PDB_DBIStream.cpp
PDB_DBIStream.h
PDB_DBITypes.cpp
PDB_DBITypes.h
PDB_DirectMSFStream.cpp
PDB_DirectMSFStream.h
PDB_ErrorCodes.h
PDB_GlobalSymbolStream.cpp
PDB_GlobalSymbolStream.h
PDB_ImageSectionStream.cpp
PDB_ImageSectionStream.h
PDB_InfoStream.cpp
PDB_InfoStream.h
PDB_IPIStream.cpp
PDB_IPIStream.h
PDB_IPITypes.h
PDB_ModuleInfoStream.cpp
PDB_ModuleInfoStream.h
PDB_ModuleLineStream.cpp
PDB_ModuleLineStream.h
PDB_ModuleSymbolStream.cpp
PDB_ModuleSymbolStream.h
PDB_NamesStream.cpp
PDB_NamesStream.h
PDB_PCH.cpp
PDB_PCH.h
PDB_PublicSymbolStream.cpp
PDB_PublicSymbolStream.h
PDB_RawFile.cpp
PDB_RawFile.h
PDB_SectionContributionStream.cpp
PDB_SectionContributionStream.h
PDB_SourceFileStream.cpp
PDB_SourceFileStream.h
PDB_TPIStream.cpp
PDB_TPIStream.h
PDB_TPITypes.h
PDB_Types.cpp
PDB_Types.h
PDB_Util.h
)
source_group(src FILES
${SOURCES}
)
add_library(raw_pdb
${SOURCES}
)
target_include_directories(raw_pdb
PUBLIC
.
)
target_precompile_headers(raw_pdb
PRIVATE
PDB_PCH.h
)
option(RAWPDB_BUILD_EXAMPLES "Build Examples" ON)
if (RAWPDB_BUILD_EXAMPLES)
add_subdirectory(Examples)
endif()
if (UNIX)
include(GNUInstallDirs)
install(
TARGETS raw_pdb
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
)
file(GLOB_RECURSE HEADER_FILES
"${CMAKE_CURRENT_SOURCE_DIR}/*.h"
)
file(GLOB_RECURSE HEADER_FILES_FOUNDATION
"${CMAKE_CURRENT_SOURCE_DIR}/Foundation/*.h"
)
install(
FILES ${HEADER_FILES}
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/raw_pdb/"
)
install(
FILES ${HEADER_FILES_FOUNDATION}
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/raw_pdb/Foundation"
)
endif (UNIX)

View File

@@ -1,39 +0,0 @@
project(Examples)
set(SOURCES
ExampleContributions.cpp
ExampleFunctionSymbols.cpp
ExampleFunctionVariables.cpp
ExampleIPI.cpp
ExampleLines.cpp
ExampleMain.cpp
ExampleMemoryMappedFile.cpp
ExampleMemoryMappedFile.h
ExamplePDBSize.cpp
Examples_PCH.cpp
Examples_PCH.h
ExampleSymbols.cpp
ExampleTimedScope.cpp
ExampleTimedScope.h
ExampleTypes.cpp
ExampleTypeTable.cpp
ExampleTypeTable.h
)
source_group(src FILES
${SOURCES}
)
add_executable(Examples
${SOURCES}
)
target_link_libraries(Examples
PUBLIC
raw_pdb
)
target_precompile_headers(Examples
PUBLIC
Examples_PCH.h
)

View File

@@ -1,96 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "Examples_PCH.h"
#include "ExampleTimedScope.h"
#include "PDB_RawFile.h"
#include "PDB_DBIStream.h"
namespace
{
// we don't have to store std::string in the contributions, since all the data is memory-mapped anyway.
// we do it in this example to ensure that we don't "cheat" when reading the PDB file. memory-mapped data will only
// be faulted into the process once it's touched, so actually copying the string data makes us touch the needed data,
// giving us a real performance measurement.
struct Contribution
{
std::string objectFile;
uint32_t rva;
uint32_t size;
};
}
void ExampleContributions(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream);
void ExampleContributions(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream)
{
TimedScope total("\nRunning example \"Contributions\"");
// in order to keep the example easy to understand, we load the PDB data serially.
// note that this can be improved a lot by reading streams concurrently.
// prepare the image section stream first. it is needed for converting section + offset into an RVA
TimedScope sectionScope("Reading image section stream");
const PDB::ImageSectionStream imageSectionStream = dbiStream.CreateImageSectionStream(rawPdbFile);
sectionScope.Done();
// prepare the module info stream for matching contributions against files
TimedScope moduleScope("Reading module info stream");
const PDB::ModuleInfoStream moduleInfoStream = dbiStream.CreateModuleInfoStream(rawPdbFile);
moduleScope.Done();
// read contribution stream
TimedScope contributionScope("Reading section contribution stream");
const PDB::SectionContributionStream sectionContributionStream = dbiStream.CreateSectionContributionStream(rawPdbFile);
contributionScope.Done();
std::vector<Contribution> contributions;
{
TimedScope scope("Storing contributions");
const PDB::ArrayView<PDB::DBI::SectionContribution> sectionContributions = sectionContributionStream.GetContributions();
const size_t count = sectionContributions.GetLength();
contributions.reserve(count);
for (const PDB::DBI::SectionContribution& contribution : sectionContributions)
{
const uint32_t rva = imageSectionStream.ConvertSectionOffsetToRVA(contribution.section, contribution.offset);
if (rva == 0u)
{
printf("Contribution has invalid RVA\n");
continue;
}
const PDB::ModuleInfoStream::Module& module = moduleInfoStream.GetModule(contribution.moduleIndex);
contributions.push_back(Contribution { module.GetName().Decay(), rva, contribution.size });
}
scope.Done(count);
}
TimedScope sortScope("std::sort contributions");
std::sort(contributions.begin(), contributions.end(), [](const Contribution& lhs, const Contribution& rhs)
{
return lhs.size > rhs.size;
});
sortScope.Done();
total.Done();
// log the 20 largest contributions
{
printf("20 largest contributions:\n");
const size_t countToShow = std::min<size_t>(20ul, contributions.size());
for (size_t i = 0u; i < countToShow; ++i)
{
const Contribution& contribution = contributions[i];
printf("%zu: %u bytes from %s\n", i + 1u, contribution.size, contribution.objectFile.c_str());
}
}
}

View File

@@ -1,262 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "Examples_PCH.h"
#include "ExampleTimedScope.h"
#include "PDB_RawFile.h"
#include "PDB_DBIStream.h"
namespace
{
// in this example, we are only interested in function symbols: function name, RVA, and size.
// this is what most profilers need, they aren't interested in any other data.
struct FunctionSymbol
{
std::string name;
uint32_t rva;
uint32_t size;
const PDB::CodeView::DBI::Record* frameProc;
};
}
void ExampleFunctionSymbols(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream);
void ExampleFunctionSymbols(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream)
{
TimedScope total("\nRunning example \"Function symbols\"");
// in order to keep the example easy to understand, we load the PDB data serially.
// note that this can be improved a lot by reading streams concurrently.
// prepare the image section stream first. it is needed for converting section + offset into an RVA
TimedScope sectionScope("Reading image section stream");
const PDB::ImageSectionStream imageSectionStream = dbiStream.CreateImageSectionStream(rawPdbFile);
sectionScope.Done();
// prepare the module info stream for grabbing function symbols from modules
TimedScope moduleScope("Reading module info stream");
const PDB::ModuleInfoStream moduleInfoStream = dbiStream.CreateModuleInfoStream(rawPdbFile);
moduleScope.Done();
// prepare symbol record stream needed by the public stream
TimedScope symbolStreamScope("Reading symbol record stream");
const PDB::CoalescedMSFStream symbolRecordStream = dbiStream.CreateSymbolRecordStream(rawPdbFile);
symbolStreamScope.Done();
// note that we only use unordered_set in order to keep the example code easy to understand.
// using other hash set implementations like e.g. abseil's Swiss Tables (https://abseil.io/about/design/swisstables) is *much* faster.
std::vector<FunctionSymbol> functionSymbols;
std::unordered_set<uint32_t> seenFunctionRVAs;
// start by reading the module stream, grabbing every function symbol we can find.
// in most cases, this gives us ~90% of all function symbols already, along with their size.
{
TimedScope scope("Storing function symbols from modules");
const PDB::ArrayView<PDB::ModuleInfoStream::Module> modules = moduleInfoStream.GetModules();
for (const PDB::ModuleInfoStream::Module& module : modules)
{
if (!module.HasSymbolStream())
{
continue;
}
const PDB::ModuleSymbolStream moduleSymbolStream = module.CreateSymbolStream(rawPdbFile);
moduleSymbolStream.ForEachSymbol([&functionSymbols, &seenFunctionRVAs, &imageSectionStream](const PDB::CodeView::DBI::Record* record)
{
// only grab function symbols from the module streams
const char* name = nullptr;
uint32_t rva = 0u;
uint32_t size = 0u;
if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_FRAMEPROC)
{
functionSymbols[functionSymbols.size() - 1].frameProc = record;
return;
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_THUNK32)
{
if (record->data.S_THUNK32.thunk == PDB::CodeView::DBI::ThunkOrdinal::TrampolineIncremental)
{
// we have never seen incremental linking thunks stored inside a S_THUNK32 symbol, but better safe than sorry
name = "ILT";
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_THUNK32.section, record->data.S_THUNK32.offset);
size = 5u;
}
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_TRAMPOLINE)
{
// incremental linking thunks are stored in the linker module
name = "ILT";
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_TRAMPOLINE.thunkSection, record->data.S_TRAMPOLINE.thunkOffset);
size = 5u;
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LPROC32)
{
name = record->data.S_LPROC32.name;
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_LPROC32.section, record->data.S_LPROC32.offset);
size = record->data.S_LPROC32.codeSize;
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_GPROC32)
{
name = record->data.S_GPROC32.name;
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_GPROC32.section, record->data.S_GPROC32.offset);
size = record->data.S_GPROC32.codeSize;
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LPROC32_ID)
{
name = record->data.S_LPROC32_ID.name;
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_LPROC32_ID.section, record->data.S_LPROC32_ID.offset);
size = record->data.S_LPROC32_ID.codeSize;
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_GPROC32_ID)
{
name = record->data.S_GPROC32_ID.name;
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_GPROC32_ID.section, record->data.S_GPROC32_ID.offset);
size = record->data.S_GPROC32_ID.codeSize;
}
if (rva == 0u)
{
return;
}
functionSymbols.push_back(FunctionSymbol { name, rva, size, nullptr });
seenFunctionRVAs.emplace(rva);
});
}
scope.Done(modules.GetLength());
}
// we don't need to touch global symbols in this case.
// most of the data we need can be obtained from the module symbol streams, and the global symbol stream only offers data symbols on top of that, which we are not interested in.
// however, there can still be public function symbols we haven't seen yet in any of the modules, especially for PDBs that don't provide module-specific information.
// read public symbols
TimedScope publicScope("Reading public symbol stream");
const PDB::PublicSymbolStream publicSymbolStream = dbiStream.CreatePublicSymbolStream(rawPdbFile);
publicScope.Done();
{
TimedScope scope("Storing public function symbols");
const PDB::ArrayView<PDB::HashRecord> hashRecords = publicSymbolStream.GetRecords();
const size_t count = hashRecords.GetLength();
for (const PDB::HashRecord& hashRecord : hashRecords)
{
const PDB::CodeView::DBI::Record* record = publicSymbolStream.GetRecord(symbolRecordStream, hashRecord);
if (record->header.kind != PDB::CodeView::DBI::SymbolRecordKind::S_PUB32)
{
// normally, a PDB only contains S_PUB32 symbols in the public symbol stream, but we have seen PDBs that also store S_CONSTANT as public symbols.
// ignore these.
continue;
}
if ((PDB_AS_UNDERLYING(record->data.S_PUB32.flags) & PDB_AS_UNDERLYING(PDB::CodeView::DBI::PublicSymbolFlags::Function)) == 0u)
{
// ignore everything that is not a function
continue;
}
const uint32_t rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_PUB32.section, record->data.S_PUB32.offset);
if (rva == 0u)
{
// certain symbols (e.g. control-flow guard symbols) don't have a valid RVA, ignore those
continue;
}
// check whether we already know this symbol from one of the module streams
const auto it = seenFunctionRVAs.find(rva);
if (it != seenFunctionRVAs.end())
{
// we know this symbol already, ignore it
continue;
}
// this is a new function symbol, so store it.
// note that we don't know its size yet.
functionSymbols.push_back(FunctionSymbol { record->data.S_PUB32.name, rva, 0u, nullptr });
}
scope.Done(count);
}
// we still need to find the size of the public function symbols.
// this can be deduced by sorting the symbols by their RVA, and then computing the distance between the current and the next symbol.
// this works since functions are always mapped to executable pages, so they aren't interleaved by any data symbols.
TimedScope sortScope("std::sort function symbols");
std::sort(functionSymbols.begin(), functionSymbols.end(), [](const FunctionSymbol& lhs, const FunctionSymbol& rhs)
{
return lhs.rva < rhs.rva;
});
sortScope.Done();
const size_t symbolCount = functionSymbols.size();
if (symbolCount != 0u)
{
TimedScope computeScope("Computing function symbol sizes");
size_t foundCount = 0u;
// we have at least 1 symbol.
// compute missing symbol sizes by computing the distance from this symbol to the next.
// note that this includes "int 3" padding after the end of a function. if you don't want that, but the actual number of bytes of
// the function's code, your best bet is to use a disassembler instead.
for (size_t i = 0u; i < symbolCount - 1u; ++i)
{
FunctionSymbol& currentSymbol = functionSymbols[i];
if (currentSymbol.size != 0u)
{
// the symbol's size is already known
continue;
}
const FunctionSymbol& nextSymbol = functionSymbols[i + 1u];
const size_t size = nextSymbol.rva - currentSymbol.rva;
(void)size; // unused
++foundCount;
}
// we know have the sizes of all symbols, except the last.
// this can be found by going through the contributions, if needed.
FunctionSymbol& lastSymbol = functionSymbols[symbolCount - 1u];
if (lastSymbol.size == 0u)
{
// bad luck, we can't deduce the last symbol's size, so have to consult the contributions instead.
// we do a linear search in this case to keep the code simple.
const PDB::SectionContributionStream sectionContributionStream = dbiStream.CreateSectionContributionStream(rawPdbFile);
const PDB::ArrayView<PDB::DBI::SectionContribution> sectionContributions = sectionContributionStream.GetContributions();
for (const PDB::DBI::SectionContribution& contribution : sectionContributions)
{
const uint32_t rva = imageSectionStream.ConvertSectionOffsetToRVA(contribution.section, contribution.offset);
if (rva == 0u)
{
printf("Contribution has invalid RVA\n");
continue;
}
if (rva == lastSymbol.rva)
{
lastSymbol.size = contribution.size;
break;
}
if (rva > lastSymbol.rva)
{
// should have found the contribution by now
printf("Unknown contribution for symbol %s at RVA 0x%X", lastSymbol.name.c_str(), lastSymbol.rva);
break;
}
}
}
computeScope.Done(foundCount);
}
total.Done(functionSymbols.size());
}

View File

@@ -1,382 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "Examples_PCH.h"
#include "ExampleTimedScope.h"
#include "ExampleTypeTable.h"
#include "PDB_RawFile.h"
#include "PDB_DBIStream.h"
#include "PDB_TPIStream.h"
using SymbolRecordKind = PDB::CodeView::DBI::SymbolRecordKind;
static std::string GetVariableTypeName(const TypeTable& typeTable, uint32_t typeIndex)
{
// Defined in ExampleTypes.cpp
extern std::string GetTypeName(const TypeTable & typeTable, uint32_t typeIndex);
std::string typeName = GetTypeName(typeTable, typeIndex);
// Remove any '%s' substring used to insert a variable/field name.
const uint64_t markerPos = typeName.find("%s");
if (markerPos != typeName.npos)
{
typeName.erase(markerPos, 2);
}
return typeName;
}
static void Printf(uint32_t indent, const char* format, ...)
{
va_list args;
va_start(args, format);
printf("%*s", indent * 4, "");
vprintf(format, args);
va_end(args);
}
void ExampleFunctionVariables(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream, const PDB::TPIStream& tpiStream);
void ExampleFunctionVariables(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream, const PDB::TPIStream& tpiStream)
{
TimedScope total("\nRunning example \"Function variables\"");
TimedScope typeTableScope("Create TypeTable");
TypeTable typeTable(tpiStream);
typeTableScope.Done();
// in order to keep the example easy to understand, we load the PDB data serially.
// note that this can be improved a lot by reading streams concurrently.
// prepare the image section stream first. it is needed for converting section + offset into an RVA
TimedScope sectionScope("Reading image section stream");
const PDB::ImageSectionStream imageSectionStream = dbiStream.CreateImageSectionStream(rawPdbFile);
sectionScope.Done();
// prepare the module info stream for grabbing function symbols from modules
TimedScope moduleScope("Reading module info stream");
const PDB::ModuleInfoStream moduleInfoStream = dbiStream.CreateModuleInfoStream(rawPdbFile);
moduleScope.Done();
// prepare symbol record stream needed by the public stream
TimedScope symbolStreamScope("Reading symbol record stream");
const PDB::CoalescedMSFStream symbolRecordStream = dbiStream.CreateSymbolRecordStream(rawPdbFile);
symbolStreamScope.Done();
{
TimedScope scope("Printing function variable records from modules\n");
const PDB::ArrayView<PDB::ModuleInfoStream::Module> modules = moduleInfoStream.GetModules();
uint32_t blockLevel = 0;
uint32_t recordCount = 0;
for (const PDB::ModuleInfoStream::Module& module : modules)
{
if (!module.HasSymbolStream())
{
continue;
}
const PDB::ModuleSymbolStream moduleSymbolStream = module.CreateSymbolStream(rawPdbFile);
moduleSymbolStream.ForEachSymbol([&typeTable, &imageSectionStream, &blockLevel, &recordCount](const PDB::CodeView::DBI::Record* record)
{
const SymbolRecordKind kind = record->header.kind;
const PDB::CodeView::DBI::Record::Data& data = record->data;
if (kind == SymbolRecordKind::S_END)
{
PDB_ASSERT(blockLevel > 0, "Block level for S_END is 0");
blockLevel--;
Printf(blockLevel, "S_END\n");
if (blockLevel == 0)
{
Printf(0, "\n");
}
}
else if(kind == SymbolRecordKind::S_SKIP)
{
Printf(blockLevel, "S_SKIP\n");
}
else if (kind == SymbolRecordKind::S_BLOCK32)
{
const uint32_t offset = imageSectionStream.ConvertSectionOffsetToRVA(data.S_BLOCK32.section, data.S_BLOCK32.offset);
Printf(blockLevel, "S_BLOCK32: '%s' | Code Offset 0x%X\n", data.S_BLOCK32.name, offset);
blockLevel++;
}
else if (kind == SymbolRecordKind::S_LABEL32)
{
Printf(blockLevel, "S_LABEL32: '%s' | Offset 0x%X\n", data.S_LABEL32.name, data.S_LABEL32.offset);
}
else if(kind == SymbolRecordKind::S_CONSTANT)
{
const std::string typeName = GetVariableTypeName(typeTable, data.S_CONSTANT.typeIndex);
Printf(blockLevel, "S_CONSTANT: '%s' -> '%s' | Value 0x%X\n", typeName.c_str(), data.S_CONSTANT.name, data.S_CONSTANT.value);
}
else if(kind == SymbolRecordKind::S_LOCAL)
{
const std::string typeName = GetVariableTypeName(typeTable, data.S_LOCAL.typeIndex);
Printf(blockLevel, "S_LOCAL: '%s' -> '%s' | Param: %s | Optimized Out: %s\n", typeName.c_str(), data.S_LOCAL.name, data.S_LOCAL.flags.fIsParam ? "True" : "False", data.S_LOCAL.flags.fIsOptimizedOut ? "True" : "False");
}
else if (kind == SymbolRecordKind::S_DEFRANGE_REGISTER)
{
Printf(blockLevel, "S_DEFRANGE_REGISTER: Register 0x%X\n", data.S_DEFRANGE_REGISTER.reg);
}
else if(kind == SymbolRecordKind::S_DEFRANGE_FRAMEPOINTER_REL)
{
Printf(blockLevel, "S_DEFRANGE_FRAMEPOINTER_REL: Frame Pointer Offset 0x%X | Range Start 0x%X | Range Section Start 0x%X | Range Length %u\n",
data.S_DEFRANGE_FRAMEPOINTER_REL.offsetFramePointer,
data.S_DEFRANGE_FRAMEPOINTER_REL.range.offsetStart,
data.S_DEFRANGE_FRAMEPOINTER_REL.range.isectionStart,
data.S_DEFRANGE_FRAMEPOINTER_REL.range.length);
}
else if(kind == SymbolRecordKind::S_DEFRANGE_SUBFIELD_REGISTER)
{
Printf(blockLevel, "S_DEFRANGE_SUBFIELD_REGISTER: Register %u | Parent offset 0x%X | Range Start 0x%X | Range Section Start 0x%X | Range Length %u\n",
data.S_DEFRANGE_SUBFIELD_REGISTER.reg,
data.S_DEFRANGE_SUBFIELD_REGISTER.offsetParent,
data.S_DEFRANGE_SUBFIELD_REGISTER.range.offsetStart,
data.S_DEFRANGE_SUBFIELD_REGISTER.range.isectionStart,
data.S_DEFRANGE_SUBFIELD_REGISTER.range.length);
}
else if (kind == SymbolRecordKind::S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE)
{
Printf(blockLevel, "S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE: Offset 0x%X\n", data.S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE.offsetFramePointer);
}
else if (kind == SymbolRecordKind::S_DEFRANGE_REGISTER_REL)
{
Printf(blockLevel, "S_DEFRANGE_REGISTER_REL: Base Register %u | Parent offset 0x%X | Base Register Offset 0x%X | Range Start 0x%X | Range Section Start 0x%X | Range Length %u\n",
data.S_DEFRANGE_REGISTER_REL.baseRegister,
data.S_DEFRANGE_REGISTER_REL.offsetParent,
data.S_DEFRANGE_REGISTER_REL.offsetBasePointer,
data.S_DEFRANGE_REGISTER_REL.offsetParent,
data.S_DEFRANGE_REGISTER_REL.range.offsetStart,
data.S_DEFRANGE_REGISTER_REL.range.isectionStart,
data.S_DEFRANGE_REGISTER_REL.range.length);
}
else if(kind == SymbolRecordKind::S_FILESTATIC)
{
Printf(blockLevel, "S_FILESTATIC: '%s'\n", data.S_FILESTATIC.name);
}
else if (kind == SymbolRecordKind::S_INLINESITE)
{
Printf(blockLevel, "S_INLINESITE: Parent 0x%X\n", data.S_INLINESITE.parent);
blockLevel++;
}
else if (kind == SymbolRecordKind::S_INLINESITE_END)
{
PDB_ASSERT(blockLevel > 0, "Block level for S_INLINESITE_END is 0");
blockLevel--;
Printf(blockLevel, "S_INLINESITE_END:\n");
}
else if (kind == SymbolRecordKind::S_CALLEES)
{
Printf(blockLevel, "S_CALLEES: Count %u\n", data.S_CALLEES.count);
}
else if (kind == SymbolRecordKind::S_CALLERS)
{
Printf(blockLevel, "S_CALLERS: Count %u\n", data.S_CALLERS.count);
}
else if (kind == SymbolRecordKind::S_INLINEES)
{
Printf(blockLevel, "S_INLINEES: Count %u\n", data.S_INLINEES.count);
}
else if (kind == SymbolRecordKind::S_LDATA32)
{
if (blockLevel > 0)
{
// Not sure why some type index 0 (T_NO_TYPE) are included in some PDBs.
if (data.S_LDATA32.typeIndex != 0) // PDB::CodeView::TPI::TypeIndexKind::T_NOTYPE)
{
const std::string typeName = GetVariableTypeName(typeTable, data.S_LDATA32.typeIndex);
Printf(blockLevel, "S_LDATA32: '%s' -> '%s'\n", data.S_LDATA32.name, typeName.c_str());
}
}
}
else if (kind == SymbolRecordKind::S_LTHREAD32)
{
if (blockLevel > 0)
{
const std::string typeName = GetVariableTypeName(typeTable, data.S_LTHREAD32.typeIndex);
Printf(blockLevel, "S_LTHREAD32: '%s' -> '%s'\n", data.S_LTHREAD32.name, typeName.c_str());
}
}
else if (kind == SymbolRecordKind::S_UDT)
{
const std::string typeName = GetVariableTypeName(typeTable, data.S_UDT.typeIndex);
Printf(blockLevel, "S_UDT: '%s' -> '%s'\n", data.S_UDT.name, typeName.c_str());
}
else if (kind == PDB::CodeView::DBI::SymbolRecordKind::S_REGISTER)
{
const std::string typeName = GetVariableTypeName(typeTable, data.S_REGSYM.typeIndex);
Printf(blockLevel, "S_REGSYM: '%s' -> '%s' | Register %i\n",
data.S_REGSYM.name, typeName.c_str(),
data.S_REGSYM.reg);
}
else if (kind == PDB::CodeView::DBI::SymbolRecordKind::S_BPREL32)
{
const std::string typeName = GetVariableTypeName(typeTable, data.S_BPRELSYM32.typeIndex);
Printf(blockLevel, "S_BPRELSYM32: '%s' -> '%s' | BP register Offset 0x%X\n",
data.S_BPRELSYM32.name, typeName.c_str(),
data.S_BPRELSYM32.offset);
}
else if (kind == PDB::CodeView::DBI::SymbolRecordKind::S_REGREL32)
{
const std::string typeName = GetVariableTypeName(typeTable, data.S_REGREL32.typeIndex);
Printf(blockLevel, "S_REGREL32: '%s' -> '%s' | Register %i | Register Offset 0x%X\n",
data.S_REGREL32.name, typeName.c_str(),
data.S_REGREL32.reg,
data.S_REGREL32.offset);
}
else if(kind == SymbolRecordKind::S_FRAMECOOKIE)
{
Printf(blockLevel, "S_FRAMECOOKIE: Offset 0x%X | Register %u | Type %u\n",
data.S_FRAMECOOKIE.offset,
data.S_FRAMECOOKIE.reg,
data.S_FRAMECOOKIE.cookietype);
}
else if(kind == SymbolRecordKind::S_CALLSITEINFO)
{
const std::string typeName = GetVariableTypeName(typeTable, data.S_CALLSITEINFO.typeIndex);
Printf(blockLevel, "S_CALLSITEINFO: '%s' | Offset 0x%X | Section %u\n", typeName.c_str(), data.S_CALLSITEINFO.offset, data.S_CALLSITEINFO.section);
}
else if(kind == SymbolRecordKind::S_HEAPALLOCSITE)
{
const std::string typeName = GetVariableTypeName(typeTable, data.S_HEAPALLOCSITE.typeIndex);
Printf(blockLevel, "S_HEAPALLOCSITE: '%s' | Offset 0x%X | Section %u | Instruction Length %u\n", typeName.c_str(),
data.S_HEAPALLOCSITE.offset,
data.S_HEAPALLOCSITE.section,
data.S_HEAPALLOCSITE.instructionLength);
}
else if (kind == SymbolRecordKind::S_FRAMEPROC)
{
Printf(blockLevel, "S_FRAMEPROC: Size %u | Padding %u | Padding Offset 0x%X | Callee Registers Size %u\n",
data.S_FRAMEPROC.cbFrame,
data.S_FRAMEPROC.cbPad,
data.S_FRAMEPROC.offPad,
data.S_FRAMEPROC.cbSaveRegs);
}
else if (kind == SymbolRecordKind::S_ANNOTATION)
{
Printf(blockLevel, "S_ANNOTATION: Offset 0x%X | Count %u\n", data.S_ANNOTATIONSYM.offset, data.S_ANNOTATIONSYM.annotationsCount);
// print N null-terminated annotation strings, skipping their null-terminators to get to the next string
const char* annotation = data.S_ANNOTATIONSYM.annotations;
for (int i = 0; i < data.S_ANNOTATIONSYM.annotationsCount; ++i, annotation += strlen(annotation) + 1)
Printf(blockLevel + 1, "S_ANNOTATION.%u: %s\n", i, annotation);
PDB_ASSERT(annotation <= (const char*)record + record->header.size + sizeof(record->header.size),
"Annotation strings end beyond the record size %X; annotaions count: %u", record->header.size, data.S_ANNOTATIONSYM.annotationsCount);
}
else if (kind == SymbolRecordKind::S_THUNK32)
{
PDB_ASSERT(blockLevel == 0, "BlockLevel %u != 0", blockLevel);
if (data.S_THUNK32.thunk == PDB::CodeView::DBI::ThunkOrdinal::TrampolineIncremental)
{
// we have never seen incremental linking thunks stored inside a S_THUNK32 symbol, but better safe than sorry
const uint32_t rva = imageSectionStream.ConvertSectionOffsetToRVA(data.S_THUNK32.section, data.S_THUNK32.offset);
Printf(blockLevel, "Function: 'ILT/Thunk' | RVA 0x%X\n", rva);
}
else
{
const uint32_t rva = imageSectionStream.ConvertSectionOffsetToRVA(data.S_THUNK32.section, data.S_THUNK32.offset);
Printf(blockLevel, "S_THUNK32 Function '%s' | RVA 0x%X\n", data.S_THUNK32.name, rva);
blockLevel++;
}
}
else if (kind == SymbolRecordKind::S_TRAMPOLINE)
{
PDB_ASSERT(blockLevel == 0, "BlockLevel %u != 0", blockLevel);
// incremental linking thunks are stored in the linker module
const uint32_t rva = imageSectionStream.ConvertSectionOffsetToRVA(data.S_TRAMPOLINE.thunkSection, data.S_TRAMPOLINE.thunkOffset);
Printf(blockLevel, "Function 'ILT/Trampoline' | RVA 0x%X\n", rva);
}
else if (kind == SymbolRecordKind::S_LPROC32)
{
PDB_ASSERT(blockLevel == 0, "BlockLevel %u != 0", blockLevel);
const uint32_t rva = imageSectionStream.ConvertSectionOffsetToRVA(data.S_LPROC32.section, data.S_LPROC32.offset);
Printf(blockLevel, "S_LPROC32 Function '%s' | RVA 0x%X\n", data.S_LPROC32.name, rva);
blockLevel++;
}
else if (kind == SymbolRecordKind::S_GPROC32)
{
PDB_ASSERT(blockLevel == 0, "BlockLevel %u != 0", blockLevel);
const uint32_t rva = imageSectionStream.ConvertSectionOffsetToRVA(data.S_GPROC32.section, data.S_GPROC32.offset);
Printf(blockLevel, "S_GPROC32 Function '%s' | RVA 0x%X\n", data.S_GPROC32.name, rva);
blockLevel++;
}
else if (kind == SymbolRecordKind::S_LPROC32_ID)
{
PDB_ASSERT(blockLevel == 0, "BlockLevel %u != 0", blockLevel);
const uint32_t rva = imageSectionStream.ConvertSectionOffsetToRVA(data.S_LPROC32_ID.section, data.S_LPROC32_ID.offset);
Printf(blockLevel, "S_LPROC32_ID Function '%s' | RVA 0x%X\n", data.S_LPROC32_ID.name, rva);
blockLevel++;
}
else if (kind == SymbolRecordKind::S_GPROC32_ID)
{
PDB_ASSERT(blockLevel == 0, "BlockLevel %u != 0", blockLevel);
const uint32_t rva = imageSectionStream.ConvertSectionOffsetToRVA(data.S_GPROC32_ID.section, data.S_GPROC32_ID.offset);
Printf(blockLevel, "S_GPROC32_ID Function '%s' | RVA 0x%X\n", data.S_GPROC32_ID.name, rva);
blockLevel++;
}
else if (kind == SymbolRecordKind::S_REGREL32_INDIR)
{
const std::string typeName = GetVariableTypeName(typeTable, data.S_REGREL32_INDIR.typeIndex);
Printf(blockLevel, "S_REGREL32_INDIR: '%s' -> '%s' | Register %i | Unknown1 0x%X | Unknown2 0x%X\n",
data.S_REGREL32_INDIR.name, typeName.c_str(),
data.S_REGREL32_INDIR.unknown1,
data.S_REGREL32_INDIR.unknown1);
}
else if (kind == SymbolRecordKind::S_REGREL32_ENCTMP)
{
const std::string typeName = GetVariableTypeName(typeTable, data.S_REGREL32.typeIndex);
Printf(blockLevel, "S_REGREL32_ENCTMP: '%s' -> '%s' | Register %i | Register Offset 0x%X\n",
data.S_REGREL32.name, typeName.c_str(),
data.S_REGREL32.reg,
data.S_REGREL32.offset);
}
else if (kind == SymbolRecordKind::S_UNAMESPACE)
{
Printf(blockLevel, "S_UNAMESPACE: '%s'\n", data.S_UNAMESPACE.name);
}
else if (kind == SymbolRecordKind::S_ARMSWITCHTABLE)
{
Printf(blockLevel, "S_ARMSWITCHTABLE: "
"Switch Type: %u | Num Entries: %u | Base Section: %u | Base Offset: 0x%X | "
"Branch Section: %u | Branch Offset: 0x%X | Table Section: %u | Table Offset: 0x%X\n",
data.S_ARMSWITCHTABLE.switchType,
data.S_ARMSWITCHTABLE.numEntries,
data.S_ARMSWITCHTABLE.sectionBase,
data.S_ARMSWITCHTABLE.offsetBase,
data.S_ARMSWITCHTABLE.sectionBranch,
data.S_ARMSWITCHTABLE.offsetBranch,
data.S_ARMSWITCHTABLE.sectionTable,
data.S_ARMSWITCHTABLE.offsetTable);
}
else
{
// We only care about records inside functions.
if (blockLevel > 0)
{
PDB_ASSERT(false, "Unhandled record kind 0x%X with block level %u\n", static_cast<uint16_t>(kind), blockLevel);
}
}
recordCount++;
});
}
scope.Done(recordCount);
}
}

View File

@@ -1,198 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "Examples_PCH.h"
#include "ExampleTimedScope.h"
#include "ExampleTypeTable.h"
#include "PDB_RawFile.h"
#include "PDB_InfoStream.h"
#include "PDB_IPIStream.h"
#include "PDB_TPIStream.h"
static std::string GetTypeNameIPI(const TypeTable& typeTable, uint32_t typeIndex)
{
// Defined in ExampleTypes.cpp
extern std::string GetTypeName(const TypeTable & typeTable, uint32_t typeIndex);
std::string typeName = GetTypeName(typeTable, typeIndex);
// Remove any '%s' substring used to insert a variable/field name.
const uint64_t markerPos = typeName.find("%s");
if (markerPos != typeName.npos)
{
typeName.erase(markerPos, 2);
}
return typeName;
}
void ExampleIPI(const PDB::RawFile& rawPdbFile, const PDB::InfoStream& infoStream, const PDB::TPIStream& tpiStream, const PDB::IPIStream& ipiStream);
void ExampleIPI(const PDB::RawFile& rawPdbFile, const PDB::InfoStream& infoStream, const PDB::TPIStream& tpiStream, const PDB::IPIStream& ipiStream)
{
if (!infoStream.HasIPIStream())
{
return;
}
TimedScope total("\nRunning example \"IPI\"");
TimedScope typeTableScope("Create TypeTable");
TypeTable typeTable(tpiStream);
typeTableScope.Done();
// prepare names stream for grabbing file paths from lines
TimedScope namesScope("Reading names stream");
const PDB::NamesStream namesStream = infoStream.CreateNamesStream(rawPdbFile);
namesScope.Done();
const uint32_t firstTypeIndex = ipiStream.GetFirstTypeIndex();
PDB::ArrayView<const PDB::CodeView::IPI::Record*> records = ipiStream.GetTypeRecords();
std::vector<const char*> strings;
strings.resize(records.GetLength(), nullptr);
size_t index = 0;
for (const PDB::CodeView::IPI::Record* record : records)
{
const PDB::CodeView::IPI::RecordHeader& header = record->header;
if (header.kind == PDB::CodeView::IPI::TypeRecordKind::LF_STRING_ID)
{
strings[index] = record->data.LF_STRING_ID.name;
}
index++;
}
uint32_t identifier = firstTypeIndex;
std::string typeName, parentTypeName;
printf("\n --- IPI Records ---\n\n");
for(const PDB::CodeView::IPI::Record* record : records)
{
const PDB::CodeView::IPI::RecordHeader& header = record->header;
if (header.kind == PDB::CodeView::IPI::TypeRecordKind::LF_FUNC_ID)
{
typeName = GetTypeNameIPI(typeTable, record->data.LF_FUNC_ID.typeIndex);
printf("Kind: 'LF_FUNC_ID' Size: %i ID: %u\n", header.size, identifier);
printf(" Scope ID: %u\n Type: '%s'\n Name: '%s'\n\n",
record->data.LF_FUNC_ID.scopeId,
typeName.c_str(),
record->data.LF_FUNC_ID.name);
}
else if (header.kind == PDB::CodeView::IPI::TypeRecordKind::LF_MFUNC_ID)
{
typeName = GetTypeNameIPI(typeTable, record->data.LF_MFUNC_ID.typeIndex);
parentTypeName = GetTypeNameIPI(typeTable, record->data.LF_MFUNC_ID.parentTypeIndex);
printf("Kind: 'LF_MFUNC_ID' Size: %i ID: %u\n", header.size, identifier);
printf(" Parent Type: '%s'\n Type: '%s'\n Name: '%s'\n\n",
parentTypeName.c_str(),
typeName.c_str(),
record->data.LF_MFUNC_ID.name);
}
else if (header.kind == PDB::CodeView::IPI::TypeRecordKind::LF_BUILDINFO)
{
printf("Kind: 'LF_BUILDINFO' Size: %u ID: %u\n", header.size, identifier);
if (record->data.LF_BUILDINFO.count == 0)
{
continue;
}
printf("Strings: '%s'", strings[record->data.LF_BUILDINFO.typeIndices[0] - firstTypeIndex]);
for (uint32_t i = 1, size = record->data.LF_BUILDINFO.count; i < size; ++i)
{
const uint32_t stringIndex = record->data.LF_BUILDINFO.typeIndices[i];
if (stringIndex == 0)
{
printf(", ''");
}
else
{
printf(", '%s'", strings[stringIndex - firstTypeIndex]);
}
}
printf("\n\n");
}
else if (header.kind == PDB::CodeView::IPI::TypeRecordKind::LF_SUBSTR_LIST)
{
printf("Kind: 'LF_SUBSTR_LIST' Size: %u ID: %u\n", header.size, identifier);
if (record->data.LF_SUBSTR_LIST.count == 0)
{
continue;
}
printf(" Strings: '%s'", strings[record->data.LF_SUBSTR_LIST.typeIndices[0] - firstTypeIndex]);
for (uint32_t i = 1, size = record->data.LF_SUBSTR_LIST.count; i < size; ++i)
{
const uint32_t stringIndex = record->data.LF_SUBSTR_LIST.typeIndices[i];
if (stringIndex == 0)
{
printf(", ''");
}
else
{
printf(", '%s'", strings[stringIndex - firstTypeIndex]);
}
}
printf("\n\n");
}
else if (header.kind == PDB::CodeView::IPI::TypeRecordKind::LF_STRING_ID)
{
printf("Kind: 'LF_STRING_ID' Size: %u ID: %u\n", header.size, identifier);
printf(" Substring ID: %u\n Name: '%s'\n\n", record->data.LF_STRING_ID.id, record->data.LF_STRING_ID.name);
}
else if (header.kind == PDB::CodeView::IPI::TypeRecordKind::LF_UDT_SRC_LINE)
{
typeName = GetTypeNameIPI(typeTable, record->data.LF_UDT_SRC_LINE.typeIndex);
const uint32_t stringIndex = record->data.LF_UDT_SRC_LINE.stringIndex;
printf("Kind: 'LF_UDT_SRC_LINE' Size: %u ID: %u\n", header.size, identifier);
printf(" Type: '%s'\n Source Path: %s\n Line: %u\n\n",
typeName.c_str(),
strings[stringIndex - firstTypeIndex],
record->data.LF_UDT_SRC_LINE.line);
}
else if (header.kind == PDB::CodeView::IPI::TypeRecordKind::LF_UDT_MOD_SRC_LINE)
{
typeName = GetTypeNameIPI(typeTable, record->data.LF_UDT_MOD_SRC_LINE.typeIndex);
const char* string = namesStream.GetFilename(record->data.LF_UDT_MOD_SRC_LINE.stringIndex);
printf("Kind: 'LF_UDT_SRC_LINE' Size: %u ID: %u\n", header.size, identifier);
printf(" Type: '%s'\n Source Path: %s\n Line: %u\n Module Index: %u\n\n",
typeName.c_str(),
string,
record->data.LF_UDT_MOD_SRC_LINE.line,
record->data.LF_UDT_MOD_SRC_LINE.moduleIndex);
}
else
{
printf("Kind: 0x%X Size: %u ID: %u\n\n", static_cast<uint32_t>(header.kind), header.size, identifier);
}
identifier++;
}
}

View File

@@ -1,268 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "Examples_PCH.h"
#include "ExampleTimedScope.h"
#include "Foundation/PDB_PointerUtil.h"
#include "PDB_RawFile.h"
#include "PDB_DBIStream.h"
#include "PDB_InfoStream.h"
#include <cstring>
namespace
{
struct Section
{
uint16_t index;
uint32_t offset;
size_t lineIndex;
};
struct Filename
{
uint32_t fileChecksumOffset;
uint32_t namesFilenameOffset;
PDB::CodeView::DBI::ChecksumKind checksumKind;
uint8_t checksumSize;
uint8_t checksum[32];
};
struct Line
{
uint32_t lineNumber;
uint32_t codeSize;
size_t filenameIndex;
};
}
void ExampleLines(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream, const PDB::InfoStream& infoStream);
void ExampleLines(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream, const PDB::InfoStream& infoStream)
{
if (!infoStream.HasNamesStream())
{
printf("PDB has no '/names' stream for looking up filenames for lines, skipping \"Lines\" example.");
return;
}
TimedScope total("\nRunning example \"Lines\"");
// prepare the image section stream first. it is needed for converting section + offset into an RVA
TimedScope sectionScope("Reading image section stream");
const PDB::ImageSectionStream imageSectionStream = dbiStream.CreateImageSectionStream(rawPdbFile);
sectionScope.Done();
// prepare the module info stream for grabbing function symbols from modules
TimedScope moduleScope("Reading module info stream");
const PDB::ModuleInfoStream moduleInfoStream = dbiStream.CreateModuleInfoStream(rawPdbFile);
moduleScope.Done();
// prepare names stream for grabbing file paths from lines
TimedScope namesScope("Reading names stream");
const PDB::NamesStream namesStream = infoStream.CreateNamesStream(rawPdbFile);
namesScope.Done();
// keeping sections and lines separate, as sorting the smaller Section struct is 2x faster in release builds
// than having all the fields in one big Line struct and sorting those.
std::vector<Section> sections;
std::vector<Filename> filenames;
std::vector<Line> lines;
{
TimedScope scope("Storing lines from modules");
const PDB::ArrayView<PDB::ModuleInfoStream::Module> modules = moduleInfoStream.GetModules();
for (const PDB::ModuleInfoStream::Module& module : modules)
{
if (!module.HasLineStream())
{
continue;
}
const PDB::ModuleLineStream moduleLineStream = module.CreateLineStream(rawPdbFile);
const size_t moduleFilenamesStartIndex = filenames.size();
const PDB::CodeView::DBI::FileChecksumHeader* moduleFileChecksumHeader = nullptr;
moduleLineStream.ForEachSection([&moduleLineStream, &namesStream, &moduleFileChecksumHeader, &sections, &filenames, &lines](const PDB::CodeView::DBI::LineSection* lineSection)
{
if (lineSection->header.kind == PDB::CodeView::DBI::DebugSubsectionKind::S_LINES)
{
moduleLineStream.ForEachLinesBlock(lineSection,
[&lineSection, &sections, &filenames, &lines](const PDB::CodeView::DBI::LinesFileBlockHeader* linesBlockHeader, const PDB::CodeView::DBI::Line* blocklines, const PDB::CodeView::DBI::Column* blockColumns)
{
if (linesBlockHeader->numLines == 0)
{
return;
}
const PDB::CodeView::DBI::Line& firstLine = blocklines[0];
const uint16_t sectionIndex = lineSection->linesHeader.sectionIndex;
const uint32_t sectionOffset = lineSection->linesHeader.sectionOffset;
const uint32_t fileChecksumOffset = linesBlockHeader->fileChecksumOffset;
const size_t filenameIndex = filenames.size();
// there will be duplicate filenames for any real world pdb.
// ideally the filenames would be stored in a map with the filename or checksum as the key.
// but that would complicate the logic in this example and therefore just use a vector to make it easier to understand.
filenames.push_back({ fileChecksumOffset, 0, PDB::CodeView::DBI::ChecksumKind::None, 0, {0} });
sections.push_back({ sectionIndex, sectionOffset, lines.size() });
// initially set code size of first line to 0, will be updated in loop below.
lines.push_back({ firstLine.linenumStart, 0, filenameIndex });
for(uint32_t i = 1, size = linesBlockHeader->numLines; i < size; ++i)
{
const PDB::CodeView::DBI::Line& line = blocklines[i];
// calculate code size of previous line by using the current line offset.
lines.back().codeSize = line.offset - blocklines[i-1].offset;
sections.push_back({ sectionIndex, sectionOffset + line.offset, lines.size() });
lines.push_back({ line.linenumStart, 0, filenameIndex });
}
// calc code size of last line
lines.back().codeSize = lineSection->linesHeader.codeSize - blocklines[linesBlockHeader->numLines-1].offset;
// columns are optional
if (blockColumns == nullptr)
{
return;
}
for (uint32_t i = 0, size = linesBlockHeader->numLines; i < size; ++i)
{
const PDB::CodeView::DBI::Column& column = blockColumns[i];
(void)column;
}
});
}
else if (lineSection->header.kind == PDB::CodeView::DBI::DebugSubsectionKind::S_FILECHECKSUMS)
{
// how to read checksums and their filenames from the Names Stream
moduleLineStream.ForEachFileChecksum(lineSection, [&namesStream](const PDB::CodeView::DBI::FileChecksumHeader* fileChecksumHeader)
{
const char* filename = namesStream.GetFilename(fileChecksumHeader->filenameOffset);
(void)filename;
});
// store the checksum header for the module, as there might be more lines after the checksums.
// so lines will get their checksum header values assigned after processing all line sections in the module.
PDB_ASSERT(moduleFileChecksumHeader == nullptr, "Module File Checksum Header already set");
moduleFileChecksumHeader = &lineSection->checksumHeader;
}
else if (lineSection->header.kind == PDB::CodeView::DBI::DebugSubsectionKind::S_INLINEELINES)
{
if (lineSection->inlineeHeader.kind == PDB::CodeView::DBI::InlineeSourceLineKind::Signature)
{
moduleLineStream.ForEachInlineeSourceLine(lineSection, [](const PDB::CodeView::DBI::InlineeSourceLine* inlineeSourceLine)
{
(void)inlineeSourceLine;
});
}
else
{
moduleLineStream.ForEachInlineeSourceLineEx(lineSection, [](const PDB::CodeView::DBI::InlineeSourceLineEx* inlineeSourceLineEx)
{
for (uint32_t i = 0; i < inlineeSourceLineEx->extraLines; ++i)
{
const uint32_t checksumOffset = inlineeSourceLineEx->extrafileChecksumOffsets[i];
(void)checksumOffset;
}
});
}
}
else
{
PDB_ASSERT(false, "Line Section kind 0x%X not handled", static_cast<uint32_t>(lineSection->header.kind));
}
});
// assign checksum values for each filename added in this module
for (size_t i = moduleFilenamesStartIndex, size = filenames.size(); i < size; ++i)
{
Filename& filename = filenames[i];
// look up the filename's checksum header in the module's checksums section
const PDB::CodeView::DBI::FileChecksumHeader* checksumHeader = PDB::Pointer::Offset<const PDB::CodeView::DBI::FileChecksumHeader*>(moduleFileChecksumHeader, filename.fileChecksumOffset);
PDB_ASSERT(checksumHeader->checksumKind >= PDB::CodeView::DBI::ChecksumKind::None &&
checksumHeader->checksumKind <= PDB::CodeView::DBI::ChecksumKind::SHA256,
"Invalid checksum kind %u", static_cast<uint16_t>(checksumHeader->checksumKind));
// store checksum values in filname struct
filename.namesFilenameOffset = checksumHeader->filenameOffset;
filename.checksumKind = checksumHeader->checksumKind;
filename.checksumSize = checksumHeader->checksumSize;
std::memcpy(filename.checksum, checksumHeader->checksum, checksumHeader->checksumSize);
}
}
scope.Done(modules.GetLength());
TimedScope sortScope("std::sort sections");
// sort sections, so we can iterate over lines by address order.
std::sort(sections.begin(), sections.end(), [](const Section& lhs, const Section& rhs)
{
if (lhs.index == rhs.index)
{
return lhs.offset < rhs.offset;
}
return lhs.index < rhs.index;
});
sortScope.Done(sections.size());
// Disabled by default, as it will print a lot of lines for large PDBs :-)
#if 0
// DIA2Dump style lines output
static const char hexChars[17] = "0123456789ABCDEF";
char checksumString[128];
printf("*** LINES RAW PDB\n");
const char* prevFilename = nullptr;
for (const Section& section : sections)
{
const Line& line = lines[section.lineIndex];
const Filename& lineFilename = filenames[line.filenameIndex];
const char* filename = namesStream.GetFilename(lineFilename.namesFilenameOffset);
const uint32_t rva = imageSectionStream.ConvertSectionOffsetToRVA(section.index, section.offset);
// only print filename for a line if it is different from the previous one.
if (filename != prevFilename)
{
for (size_t i = 0, j = 0; i < lineFilename.checksumSize; i++, j+=2)
{
checksumString[j] = hexChars[lineFilename.checksum[i] >> 4];
checksumString[j+1] = hexChars[lineFilename.checksum[i] & 0xF];
}
checksumString[lineFilename.checksumSize * 2] = '\0';
printf(" line %u at [0x%08X][0x%04X:0x%08X], len = 0x%X %s (0x%02X: %s)\n",
line.lineNumber, rva, section.index, section.offset, line.codeSize,
filename, static_cast<uint32_t>(lineFilename.checksumKind), checksumString);
prevFilename = filename;
}
else
{
printf(" line %u at [0x%08X][0x%04X:0x%08X], len = 0x%X\n",
line.lineNumber, rva, section.index, section.offset, line.codeSize);
}
}
#endif
}
}

View File

@@ -1,200 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "Examples_PCH.h"
#include "ExampleMemoryMappedFile.h"
#include "PDB.h"
#include "PDB_RawFile.h"
#include "PDB_InfoStream.h"
#include "PDB_DBIStream.h"
#include "PDB_TPIStream.h"
#include "PDB_IPIStream.h"
#include "PDB_NamesStream.h"
namespace
{
PDB_NO_DISCARD static bool IsError(PDB::ErrorCode errorCode)
{
switch (errorCode)
{
case PDB::ErrorCode::Success:
return false;
case PDB::ErrorCode::InvalidSuperBlock:
printf("Invalid Superblock\n");
return true;
case PDB::ErrorCode::InvalidFreeBlockMap:
printf("Invalid free block map\n");
return true;
case PDB::ErrorCode::InvalidStream:
printf("Invalid stream\n");
return true;
case PDB::ErrorCode::InvalidSignature:
printf("Invalid stream signature\n");
return true;
case PDB::ErrorCode::InvalidStreamIndex:
printf("Invalid stream index\n");
return true;
case PDB::ErrorCode::InvalidDataSize:
printf("Invalid data size\n");
return true;
case PDB::ErrorCode::UnknownVersion:
printf("Unknown version\n");
return true;
}
// only ErrorCode::Success means there wasn't an error, so all other paths have to assume there was an error
return true;
}
PDB_NO_DISCARD static bool HasValidDBIStreams(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream)
{
// check whether the DBI stream offers all sub-streams we need
if (IsError(dbiStream.HasValidSymbolRecordStream(rawPdbFile)))
{
return false;
}
if (IsError(dbiStream.HasValidPublicSymbolStream(rawPdbFile)))
{
return false;
}
if (IsError(dbiStream.HasValidGlobalSymbolStream(rawPdbFile)))
{
return false;
}
if (IsError(dbiStream.HasValidSectionContributionStream(rawPdbFile)))
{
return false;
}
if (IsError(dbiStream.HasValidImageSectionStream(rawPdbFile)))
{
return false;
}
return true;
}
}
// declare all examples
extern void ExamplePDBSize(const PDB::RawFile&, const PDB::DBIStream&);
extern void ExampleTPISize(const PDB::TPIStream& tpiStream, const char* outPath);
extern void ExampleContributions(const PDB::RawFile&, const PDB::DBIStream&);
extern void ExampleSymbols(const PDB::RawFile&, const PDB::DBIStream&);
extern void ExampleFunctionSymbols(const PDB::RawFile&, const PDB::DBIStream&);
extern void ExampleFunctionVariables(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream, const PDB::TPIStream&);
extern void ExampleLines(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream, const PDB::InfoStream& infoStream);
extern void ExampleTypes(const PDB::TPIStream&);
extern void ExampleIPI(const PDB::RawFile& rawPdbFile, const PDB::InfoStream& infoStream, const PDB::TPIStream& tpiStream, const PDB::IPIStream& ipiStream);
int main(int argc, char** argv)
{
if (argc != 2)
{
printf("Usage: Examples <PDB path>\nError: Incorrect usage\n");
return 1;
}
printf("Opening PDB file %s\n", argv[1]);
// try to open the PDB file and check whether all the data we need is available
MemoryMappedFile::Handle pdbFile = MemoryMappedFile::Open(argv[1]);
if (!pdbFile.baseAddress)
{
printf("Cannot memory-map file %s\n", argv[1]);
return 1;
}
if (IsError(PDB::ValidateFile(pdbFile.baseAddress, pdbFile.len)))
{
MemoryMappedFile::Close(pdbFile);
return 2;
}
const PDB::RawFile rawPdbFile = PDB::CreateRawFile(pdbFile.baseAddress);
if (IsError(PDB::HasValidDBIStream(rawPdbFile)))
{
MemoryMappedFile::Close(pdbFile);
return 3;
}
const PDB::InfoStream infoStream(rawPdbFile);
if (infoStream.UsesDebugFastLink())
{
printf("PDB was linked using unsupported option /DEBUG:FASTLINK\n");
MemoryMappedFile::Close(pdbFile);
return 4;
}
const auto h = infoStream.GetHeader();
printf("Version %u, signature %u, age %u, GUID %08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x\n",
static_cast<uint32_t>(h->version), h->signature, h->age,
h->guid.Data1, h->guid.Data2, h->guid.Data3,
h->guid.Data4[0], h->guid.Data4[1], h->guid.Data4[2], h->guid.Data4[3], h->guid.Data4[4], h->guid.Data4[5], h->guid.Data4[6], h->guid.Data4[7]);
const PDB::DBIStream dbiStream = PDB::CreateDBIStream(rawPdbFile);
if (!HasValidDBIStreams(rawPdbFile, dbiStream))
{
MemoryMappedFile::Close(pdbFile);
return 5;
}
if (IsError(PDB::HasValidTPIStream(rawPdbFile)))
{
MemoryMappedFile::Close(pdbFile);
return 5;
}
const PDB::TPIStream tpiStream = PDB::CreateTPIStream(rawPdbFile);
PDB::IPIStream ipiStream;
// It's perfectly possible that an old PDB does not have an IPI stream.
if(infoStream.HasIPIStream())
{
PDB::ErrorCode error = PDB::HasValidIPIStream(rawPdbFile);
if (error != PDB::ErrorCode::InvalidStream && IsError(error))
{
MemoryMappedFile::Close(pdbFile);
return 5;
}
ipiStream = PDB::CreateIPIStream(rawPdbFile);
}
// run all examples
ExamplePDBSize(rawPdbFile, dbiStream);
ExampleContributions(rawPdbFile, dbiStream);
ExampleSymbols(rawPdbFile, dbiStream);
ExampleFunctionSymbols(rawPdbFile, dbiStream);
ExampleFunctionVariables(rawPdbFile, dbiStream, tpiStream);
ExampleLines(rawPdbFile, dbiStream, infoStream);
ExampleTypes(tpiStream);
ExampleIPI(rawPdbFile, infoStream, tpiStream, ipiStream);
// uncomment to dump type sizes to a CSV
// ExampleTPISize(tpiStream, "output.csv");
MemoryMappedFile::Close(pdbFile);
return 0;
}

View File

@@ -1,100 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "Examples_PCH.h"
#include "ExampleMemoryMappedFile.h"
MemoryMappedFile::Handle MemoryMappedFile::Open(const char* path)
{
#ifdef _WIN32
void* file = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, nullptr);
if (file == INVALID_HANDLE_VALUE)
{
return Handle { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, nullptr, 0 };
}
void* fileMapping = CreateFileMappingW(file, nullptr, PAGE_READONLY, 0, 0, nullptr);
if (fileMapping == nullptr)
{
CloseHandle(file);
return Handle { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, nullptr, 0 };
}
void* baseAddress = MapViewOfFile(fileMapping, FILE_MAP_READ, 0, 0, 0);
if (baseAddress == nullptr)
{
CloseHandle(fileMapping);
CloseHandle(file);
return Handle { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, nullptr, 0 };
}
BY_HANDLE_FILE_INFORMATION fileInformation;
const bool getInformationResult = GetFileInformationByHandle(file, &fileInformation);
if (!getInformationResult)
{
UnmapViewOfFile(baseAddress);
CloseHandle(fileMapping);
CloseHandle(file);
return Handle { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, nullptr, 0 };
}
const size_t fileSizeHighBytes = static_cast<size_t>(fileInformation.nFileSizeHigh) << 32;
const size_t fileSizeLowBytes = fileInformation.nFileSizeLow;
const size_t fileSize = fileSizeHighBytes | fileSizeLowBytes;
return Handle { file, fileMapping, baseAddress, fileSize };
#else
struct stat fileSb;
int file = open(path, O_RDONLY);
if (file == INVALID_HANDLE_VALUE)
{
return Handle { INVALID_HANDLE_VALUE, nullptr, 0 };
}
if (fstat(file, &fileSb) == -1)
{
close(file);
return Handle { INVALID_HANDLE_VALUE, nullptr, 0 };
}
void* baseAddress = mmap(nullptr, fileSb.st_size, PROT_READ, MAP_PRIVATE, file, 0);
if (baseAddress == MAP_FAILED)
{
close(file);
return Handle { INVALID_HANDLE_VALUE, nullptr, 0 };
}
return Handle { file, baseAddress, static_cast<size_t>(fileSb.st_size) };
#endif
}
void MemoryMappedFile::Close(Handle& handle)
{
#ifdef _WIN32
UnmapViewOfFile(handle.baseAddress);
CloseHandle(handle.fileMapping);
CloseHandle(handle.file);
handle.file = nullptr;
handle.fileMapping = nullptr;
#else
munmap(handle.baseAddress, handle.len);
close(handle.file);
handle.file = 0;
#endif
handle.baseAddress = nullptr;
}

View File

@@ -1,29 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#ifndef _WIN32
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define INVALID_HANDLE_VALUE ((long)-1)
#endif
namespace MemoryMappedFile
{
struct Handle
{
#ifdef _WIN32
void* file;
void* fileMapping;
#else
int file;
#endif
void* baseAddress;
size_t len;
};
Handle Open(const char* path);
void Close(Handle& handle);
}

View File

@@ -1,124 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "Examples_PCH.h"
#include "ExampleTimedScope.h"
#include "PDB_RawFile.h"
#include "PDB_DBIStream.h"
namespace
{
struct Stream
{
std::string name;
uint32_t size;
};
}
void ExamplePDBSize(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream);
void ExamplePDBSize(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream)
{
TimedScope total("\nRunning example \"PDBSize\"");
std::vector<Stream> streams;
// print show general statistics
printf("General\n");
printf("-------\n");
{
const PDB::SuperBlock* superBlock = rawPdbFile.GetSuperBlock();
printf("PDB page size (block size): %u\n", superBlock->blockSize);
printf("PDB block count: %u\n", superBlock->blockCount);
const size_t rawSize = static_cast<size_t>(superBlock->blockSize) * static_cast<size_t>(superBlock->blockCount);
printf("PDB raw size: %zu MiB (%zu GiB)\n", rawSize >> 20u, rawSize >> 30u);
}
// print the sizes of all known streams
printf("\n");
printf("Sizes of known streams\n");
printf("----------------------\n");
{
const uint32_t streamCount = rawPdbFile.GetStreamCount();
const uint32_t tpiStreamSize = (streamCount > 2u) ? rawPdbFile.GetStreamSize(2u) : 0u;
const uint32_t dbiStreamSize = (streamCount > 3u) ? rawPdbFile.GetStreamSize(3u) : 0u;
const uint32_t ipiStreamSize = (streamCount > 4u) ? rawPdbFile.GetStreamSize(4u) : 0u;
printf("TPI stream size: %u KiB (%u MiB)\n", tpiStreamSize >> 10u, tpiStreamSize >> 20u);
printf("DBI stream size: %u KiB (%u MiB)\n", dbiStreamSize >> 10u, dbiStreamSize >> 20u);
printf("IPI stream size: %u KiB (%u MiB)\n", ipiStreamSize >> 10u, ipiStreamSize >> 20u);
streams.push_back(Stream { "TPI", tpiStreamSize });
streams.push_back(Stream { "DBI", dbiStreamSize });
streams.push_back(Stream { "IPI", ipiStreamSize });
const uint32_t globalSymbolStreamSize = rawPdbFile.GetStreamSize(dbiStream.GetHeader().globalStreamIndex);
const uint32_t publicSymbolStreamSize = rawPdbFile.GetStreamSize(dbiStream.GetHeader().publicStreamIndex);
const uint32_t symbolRecordStreamSize = rawPdbFile.GetStreamSize(dbiStream.GetHeader().symbolRecordStreamIndex);
printf("Global symbol stream size: %u KiB (%u MiB)\n", globalSymbolStreamSize >> 10u, globalSymbolStreamSize >> 20u);
printf("Public symbol stream size: %u KiB (%u MiB)\n", publicSymbolStreamSize >> 10u, publicSymbolStreamSize >> 20u);
printf("Symbol record stream size: %u KiB (%u MiB)\n", symbolRecordStreamSize >> 10u, symbolRecordStreamSize >> 20u);
streams.emplace_back(Stream { "Global", globalSymbolStreamSize });
streams.emplace_back(Stream { "Public", publicSymbolStreamSize });
streams.emplace_back(Stream { "Symbol", symbolRecordStreamSize });
}
// print the sizes of all module streams
printf("\n");
printf("Sizes of module streams\n");
printf("-----------------------\n");
{
const PDB::ModuleInfoStream moduleInfoStream = dbiStream.CreateModuleInfoStream(rawPdbFile);
const PDB::ArrayView<PDB::ModuleInfoStream::Module> modules = moduleInfoStream.GetModules();
for (const PDB::ModuleInfoStream::Module& module : modules)
{
const PDB::DBI::ModuleInfo* moduleInfo = module.GetInfo();
const char* name = module.GetName().Decay();
const char* objectName = module.GetObjectName().Decay();
const uint16_t streamIndex = module.HasSymbolStream() ? moduleInfo->moduleSymbolStreamIndex : 0u;
const uint32_t moduleStreamSize = (streamIndex != 0u) ? rawPdbFile.GetStreamSize(streamIndex) : 0u;
printf("Module %s (%s) stream size: %u KiB (%u MiB)\n", name, objectName, moduleStreamSize >> 10u, moduleStreamSize >> 20u);
streams.push_back(Stream { name, moduleStreamSize });
}
}
// sort the streams by their size
std::sort(streams.begin(), streams.end(), [](const Stream& lhs, const Stream& rhs)
{
return lhs.size > rhs.size;
});
// log the 20 largest stream
{
printf("\n");
printf("Sizes of 20 largest streams:\n");
const size_t countToShow = std::min<size_t>(20ul, streams.size());
for (size_t i = 0u; i < countToShow; ++i)
{
const Stream& stream = streams[i];
printf("%zu: %u KiB (%u MiB) from stream %s\n", i + 1u, stream.size >> 10u, stream.size >> 20u, stream.name.c_str());
}
}
// print the raw stream sizes
printf("\n");
printf("Raw sizes of all streams\n");
printf("------------------------\n");
{
const uint32_t streamCount = rawPdbFile.GetStreamCount();
for (uint32_t i = 0u; i < streamCount; ++i)
{
const uint32_t streamSize = rawPdbFile.GetStreamSize(i);
printf("Stream %u size: %u KiB (%u MiB)\n", i, streamSize >> 10u, streamSize >> 20u);
}
}
}

View File

@@ -1,238 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "Examples_PCH.h"
#include "ExampleTimedScope.h"
#include "PDB_RawFile.h"
#include "PDB_DBIStream.h"
namespace
{
// we don't have to store std::string in the symbols, since all the data is memory-mapped anyway.
// we do it in this example to ensure that we don't "cheat" when reading the PDB file. memory-mapped data will only
// be faulted into the process once it's touched, so actually copying the string data makes us touch the needed data,
// giving us a real performance measurement.
struct Symbol
{
std::string name;
uint32_t rva;
};
}
void ExampleSymbols(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream);
void ExampleSymbols(const PDB::RawFile& rawPdbFile, const PDB::DBIStream& dbiStream)
{
TimedScope total("\nRunning example \"Symbols\"");
// in order to keep the example easy to understand, we load the PDB data serially.
// note that this can be improved a lot by reading streams concurrently.
// prepare the image section stream first. it is needed for converting section + offset into an RVA
TimedScope sectionScope("Reading image section stream");
const PDB::ImageSectionStream imageSectionStream = dbiStream.CreateImageSectionStream(rawPdbFile);
sectionScope.Done();
// prepare the module info stream for matching contributions against files
TimedScope moduleScope("Reading module info stream");
const PDB::ModuleInfoStream moduleInfoStream = dbiStream.CreateModuleInfoStream(rawPdbFile);
moduleScope.Done();
// prepare symbol record stream needed by both public and global streams
TimedScope symbolStreamScope("Reading symbol record stream");
const PDB::CoalescedMSFStream symbolRecordStream = dbiStream.CreateSymbolRecordStream(rawPdbFile);
symbolStreamScope.Done();
std::vector<Symbol> symbols;
// read public symbols
TimedScope publicScope("Reading public symbol stream");
const PDB::PublicSymbolStream publicSymbolStream = dbiStream.CreatePublicSymbolStream(rawPdbFile);
publicScope.Done();
{
TimedScope scope("Storing public symbols");
const PDB::ArrayView<PDB::HashRecord> hashRecords = publicSymbolStream.GetRecords();
const size_t count = hashRecords.GetLength();
symbols.reserve(count);
for (const PDB::HashRecord& hashRecord : hashRecords)
{
const PDB::CodeView::DBI::Record* record = publicSymbolStream.GetRecord(symbolRecordStream, hashRecord);
if (record->header.kind != PDB::CodeView::DBI::SymbolRecordKind::S_PUB32)
{
// normally, a PDB only contains S_PUB32 symbols in the public symbol stream, but we have seen PDBs that also store S_CONSTANT as public symbols.
// ignore these.
continue;
}
const uint32_t rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_PUB32.section, record->data.S_PUB32.offset);
if (rva == 0u)
{
// certain symbols (e.g. control-flow guard symbols) don't have a valid RVA, ignore those
continue;
}
symbols.push_back(Symbol { record->data.S_PUB32.name, rva });
}
scope.Done(count);
}
// read global symbols
TimedScope globalScope("Reading global symbol stream");
const PDB::GlobalSymbolStream globalSymbolStream = dbiStream.CreateGlobalSymbolStream(rawPdbFile);
globalScope.Done();
{
TimedScope scope("Storing global symbols");
const PDB::ArrayView<PDB::HashRecord> hashRecords = globalSymbolStream.GetRecords();
const size_t count = hashRecords.GetLength();
symbols.reserve(symbols.size() + count);
for (const PDB::HashRecord& hashRecord : hashRecords)
{
const PDB::CodeView::DBI::Record* record = globalSymbolStream.GetRecord(symbolRecordStream, hashRecord);
const char* name = nullptr;
uint32_t rva = 0u;
if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_GDATA32)
{
name = record->data.S_GDATA32.name;
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_GDATA32.section, record->data.S_GDATA32.offset);
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_GTHREAD32)
{
name = record->data.S_GTHREAD32.name;
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_GTHREAD32.section, record->data.S_GTHREAD32.offset);
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LDATA32)
{
name = record->data.S_LDATA32.name;
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_LDATA32.section, record->data.S_LDATA32.offset);
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LTHREAD32)
{
name = record->data.S_LTHREAD32.name;
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_LTHREAD32.section, record->data.S_LTHREAD32.offset);
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_UDT)
{
name = record->data.S_UDT.name;
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_UDT_ST)
{
name = record->data.S_UDT_ST.name;
}
if (rva == 0u)
{
// certain symbols (e.g. control-flow guard symbols) don't have a valid RVA, ignore those
continue;
}
symbols.push_back(Symbol { name, rva });
}
scope.Done(count);
}
// read module symbols
{
TimedScope scope("Storing symbols from modules");
const PDB::ArrayView<PDB::ModuleInfoStream::Module> modules = moduleInfoStream.GetModules();
for (const PDB::ModuleInfoStream::Module& module : modules)
{
if (!module.HasSymbolStream())
{
continue;
}
const PDB::ModuleSymbolStream moduleSymbolStream = module.CreateSymbolStream(rawPdbFile);
moduleSymbolStream.ForEachSymbol([&symbols, &imageSectionStream](const PDB::CodeView::DBI::Record* record)
{
const char* name = nullptr;
uint32_t rva = 0u;
if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_THUNK32)
{
if (record->data.S_THUNK32.thunk == PDB::CodeView::DBI::ThunkOrdinal::TrampolineIncremental)
{
// we have never seen incremental linking thunks stored inside a S_THUNK32 symbol, but better be safe than sorry
name = "ILT";
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_THUNK32.section, record->data.S_THUNK32.offset);
}
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_TRAMPOLINE)
{
// incremental linking thunks are stored in the linker module
name = "ILT";
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_TRAMPOLINE.thunkSection, record->data.S_TRAMPOLINE.thunkOffset);
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_BLOCK32)
{
// blocks never store a name and are only stored for indicating whether other symbols are children of this block
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LABEL32)
{
// labels don't have a name
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LPROC32)
{
name = record->data.S_LPROC32.name;
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_LPROC32.section, record->data.S_LPROC32.offset);
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_GPROC32)
{
name = record->data.S_GPROC32.name;
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_GPROC32.section, record->data.S_GPROC32.offset);
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LPROC32_ID)
{
name = record->data.S_LPROC32_ID.name;
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_LPROC32_ID.section, record->data.S_LPROC32_ID.offset);
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_GPROC32_ID)
{
name = record->data.S_GPROC32_ID.name;
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_GPROC32_ID.section, record->data.S_GPROC32_ID.offset);
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_REGREL32)
{
name = record->data.S_REGREL32.name;
// You can only get the address while running the program by checking the register value and adding the offset
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LDATA32)
{
name = record->data.S_LDATA32.name;
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_LDATA32.section, record->data.S_LDATA32.offset);
}
else if (record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LTHREAD32)
{
name = record->data.S_LTHREAD32.name;
rva = imageSectionStream.ConvertSectionOffsetToRVA(record->data.S_LTHREAD32.section, record->data.S_LTHREAD32.offset);
}
if (rva == 0u)
{
// certain symbols (e.g. control-flow guard symbols) don't have a valid RVA, ignore those
return;
}
symbols.push_back(Symbol { name, rva });
});
}
scope.Done(modules.GetLength());
}
total.Done(symbols.size());
}

View File

@@ -1,54 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "Examples_PCH.h"
#include "ExampleTimedScope.h"
namespace
{
static unsigned int g_indent = 0u;
static void PrintIndent(void)
{
printf("%.*s", g_indent * 2u, "| | | | | | | | ");
}
}
TimedScope::TimedScope(const char* message)
: m_begin(std::chrono::high_resolution_clock::now())
{
PrintIndent();
++g_indent;
printf("%s\n", message);
}
void TimedScope::Done(void) const
{
--g_indent;
PrintIndent();
const double milliSeconds = ReadMilliseconds();
printf("---> done in %.3fms\n", milliSeconds);
}
void TimedScope::Done(size_t count) const
{
--g_indent;
PrintIndent();
const double milliSeconds = ReadMilliseconds();
printf("---> done in %.3fms (%zu elements)\n", milliSeconds, count);
}
double TimedScope::ReadMilliseconds(void) const
{
const std::chrono::high_resolution_clock::time_point now = std::chrono::high_resolution_clock::now();
const std::chrono::duration<double> seconds = now - m_begin;
return seconds.count() * 1000.0;
}

View File

@@ -1,22 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "Foundation/PDB_Macros.h"
#include <chrono>
class TimedScope
{
public:
explicit TimedScope(const char* message);
void Done(void) const;
void Done(size_t count) const;
private:
double ReadMilliseconds(void) const;
const std::chrono::high_resolution_clock::time_point m_begin;
PDB_DISABLE_COPY_MOVE(TimedScope);
};

View File

@@ -1,41 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "Examples_PCH.h"
#include "ExampleTypeTable.h"
#include "Foundation/PDB_Memory.h"
TypeTable::TypeTable(const PDB::TPIStream& tpiStream) PDB_NO_EXCEPT
: typeIndexBegin(tpiStream.GetFirstTypeIndex()), typeIndexEnd(tpiStream.GetLastTypeIndex()),
m_recordCount(tpiStream.GetTypeRecordCount())
{
// Create coalesced stream from TPI stream, so the records can be referenced directly using pointers.
const PDB::DirectMSFStream& directStream = tpiStream.GetDirectMSFStream();
m_stream = PDB::CoalescedMSFStream(directStream, directStream.GetSize(), 0);
// types in the TPI stream are accessed by their index from other streams.
// however, the index is not stored with types in the TPI stream directly, but has to be built while walking the stream.
// similarly, because types are variable-length records, there are no direct offsets to access individual types.
// we therefore walk the TPI stream once, and store pointers to the records for trivial O(1) array lookup by index later.
m_records = PDB_NEW_ARRAY(const PDB::CodeView::TPI::Record*, m_recordCount);
// parse the CodeView records
uint32_t typeIndex = 0u;
tpiStream.ForEachTypeRecordHeaderAndOffset([this, &typeIndex](const PDB::CodeView::TPI::RecordHeader& header, size_t offset)
{
// The header includes the record kind and size, which can be stored along with offset
// to allow for lazy loading of the types on-demand directly from the TPIStream::GetDirectMSFStream()
// using DirectMSFStream::ReadAtOffset(...). Thus not needing a CoalescedMSFStream to look up the types.
(void)header;
const PDB::CodeView::TPI::Record* record = m_stream.GetDataAtOffset<const PDB::CodeView::TPI::Record>(offset);
m_records[typeIndex] = record;
++typeIndex;
});
}
TypeTable::~TypeTable() PDB_NO_EXCEPT
{
PDB_DELETE_ARRAY(m_records);
}

View File

@@ -1,49 +0,0 @@
#pragma once
#include "PDB_TPIStream.h"
#include "PDB_CoalescedMSFStream.h"
class TypeTable
{
public:
explicit TypeTable(const PDB::TPIStream& tpiStream) PDB_NO_EXCEPT;
~TypeTable() PDB_NO_EXCEPT;
// Returns the index of the first type, which is not necessarily zero.
PDB_NO_DISCARD inline uint32_t GetFirstTypeIndex(void) const PDB_NO_EXCEPT
{
return typeIndexBegin;
}
// Returns the index of the last type.
PDB_NO_DISCARD inline uint32_t GetLastTypeIndex(void) const PDB_NO_EXCEPT
{
return typeIndexEnd;
}
PDB_NO_DISCARD inline const PDB::CodeView::TPI::Record* GetTypeRecord(uint32_t typeIndex) const PDB_NO_EXCEPT
{
if (typeIndex < typeIndexBegin || typeIndex > typeIndexEnd)
return nullptr;
return m_records[typeIndex - typeIndexBegin];
}
// Returns a view of all type records.
// Records identified by a type index can be accessed via "allRecords[typeIndex - firstTypeIndex]".
PDB_NO_DISCARD inline PDB::ArrayView<const PDB::CodeView::TPI::Record*> GetTypeRecords(void) const PDB_NO_EXCEPT
{
return PDB::ArrayView<const PDB::CodeView::TPI::Record*>(m_records, m_recordCount);
}
private:
uint32_t typeIndexBegin;
uint32_t typeIndexEnd;
size_t m_recordCount;
const PDB::CodeView::TPI::Record **m_records;
PDB::CoalescedMSFStream m_stream;
PDB_DISABLE_COPY(TypeTable);
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "Examples_PCH.h"

View File

@@ -1,53 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "Foundation/PDB_Warnings.h"
// The following clang warnings must be disabled for the examples to build with 0 warnings
#if PDB_COMPILER_CLANG
# pragma clang diagnostic ignored "-Wformat-nonliteral" // format string is not a string literal
# pragma clang diagnostic ignored "-Wswitch-default" // switch' missing 'default' label
# pragma clang diagnostic ignored "-Wcast-align" // increases required alignment from X to Y
# pragma clang diagnostic ignored "-Wold-style-cast" // use of old-style cast
#endif
#if PDB_COMPILER_MSVC
# pragma warning(push, 0)
#elif PDB_COMPILER_CLANG
# pragma clang diagnostic push
#endif
#if PDB_COMPILER_MSVC
// we compile without exceptions
# define _ALLOW_RTCc_IN_STL
// triggered by Windows.h
# pragma warning (disable : 4668)
// triggered by xlocale in VS 2017
# pragma warning (disable : 4625) // copy constructor was implicitly defined as deleted
# pragma warning (disable : 4626) // assignment operator was implicitly defined as deleted
# pragma warning (disable : 5026) // move constructor was implicitly defined as deleted
# pragma warning (disable : 5027) // move assignment operator was implicitly defined as deleted
# pragma warning (disable : 4774) // format string expected in argument 1 is not a string literal
#endif
#ifdef _WIN32
# define NOMINMAX
# include <Windows.h>
# undef cdecl
#endif
# include <vector>
# include <unordered_set>
# include <chrono>
# include <string>
# include <algorithm>
# include <cstdarg>
#if PDB_COMPILER_MSVC
# pragma warning(pop)
#elif PDB_COMPILER_CLANG
# pragma clang diagnostic pop
#endif

View File

@@ -1,68 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "PDB_Macros.h"
#include "PDB_Assert.h"
namespace PDB
{
// A read-only view into arrays of any type and length.
template <typename T>
class PDB_NO_DISCARD ArrayView
{
public:
// Constructs an array view from a C array with explicit length.
inline constexpr explicit ArrayView(const T* const array, size_t length) PDB_NO_EXCEPT
: m_data(array)
, m_length(length)
{
}
PDB_DEFAULT_COPY_CONSTRUCTOR(ArrayView);
PDB_DEFAULT_MOVE_CONSTRUCTOR(ArrayView);
// Provides read-only access to the underlying array.
PDB_NO_DISCARD inline constexpr const T* Decay(void) const PDB_NO_EXCEPT
{
return m_data;
}
// Returns the length of the view.
PDB_NO_DISCARD inline constexpr size_t GetLength(void) const PDB_NO_EXCEPT
{
return m_length;
}
// Returns the i-th element.
PDB_NO_DISCARD inline const T& operator[](size_t i) const PDB_NO_EXCEPT
{
PDB_ASSERT(i < GetLength(), "Index %zu out of bounds [0, %zu).", i, GetLength());
return m_data[i];
}
// ------------------------------------------------------------------------------------------------
// Range-based for-loop support
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD inline const T* begin(void) const PDB_NO_EXCEPT
{
return m_data;
}
PDB_NO_DISCARD inline const T* end(void) const PDB_NO_EXCEPT
{
return m_data + m_length;
}
private:
const T* const m_data;
const size_t m_length;
PDB_DISABLE_MOVE_ASSIGNMENT(ArrayView);
PDB_DISABLE_COPY_ASSIGNMENT(ArrayView);
};
}

View File

@@ -1,31 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "PDB_Macros.h"
#include "PDB_Log.h"
PDB_PUSH_WARNING_CLANG
PDB_DISABLE_WARNING_CLANG("-Wgnu-zero-variadic-macro-arguments")
PDB_DISABLE_WARNING_CLANG("-Wreserved-identifier")
#if PDB_COMPILER_MSVC
extern "C" void __cdecl __debugbreak(void);
# pragma intrinsic(__debugbreak)
#elif defined(__has_builtin) && __has_builtin(__builtin_debugtrap)
# define __debugbreak() __builtin_debugtrap()
#else
# include <signal.h>
# define __debugbreak() raise(SIGTRAP)
#endif
#ifdef _DEBUG
# define PDB_ASSERT(_condition, _msg, ...) (_condition) ? (void)true : (PDB_LOG_ERROR(_msg, ##__VA_ARGS__), __debugbreak())
#else
# define PDB_ASSERT(_condition, _msg, ...) PDB_NOOP(_condition, _msg, ##__VA_ARGS__)
#endif
PDB_POP_WARNING_CLANG

View File

@@ -1,23 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "PDB_Macros.h"
#define PDB_DEFINE_BIT_OPERATORS(_type) \
PDB_NO_DISCARD inline constexpr _type operator|(_type lhs, _type rhs) PDB_NO_EXCEPT \
{ \
return static_cast<_type>(PDB_AS_UNDERLYING(lhs) | PDB_AS_UNDERLYING(rhs)); \
} \
\
PDB_NO_DISCARD inline constexpr _type operator&(_type lhs, _type rhs) PDB_NO_EXCEPT \
{ \
return static_cast<_type>(PDB_AS_UNDERLYING(lhs) & PDB_AS_UNDERLYING(rhs)); \
} \
\
PDB_NO_DISCARD inline constexpr _type operator~(_type value) PDB_NO_EXCEPT \
{ \
return static_cast<_type>(~PDB_AS_UNDERLYING(value)); \
}

View File

@@ -1,73 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "PDB_Assert.h"
#ifdef _WIN32
PDB_PUSH_WARNING_CLANG
PDB_DISABLE_WARNING_CLANG("-Wreserved-identifier")
extern "C" unsigned char _BitScanForward(unsigned long* _Index, unsigned long _Mask);
PDB_POP_WARNING_CLANG
# if PDB_COMPILER_MSVC
# pragma intrinsic(_BitScanForward)
# endif
#endif
namespace PDB
{
namespace BitUtil
{
// Returns whether the given unsigned value is a power of two.
template <typename T>
PDB_NO_DISCARD inline constexpr bool IsPowerOfTwo(T value) PDB_NO_EXCEPT
{
PDB_ASSERT(value != 0u, "Invalid value.");
return (value & (value - 1u)) == 0u;
}
// Rounds the given unsigned value up to the next multiple.
template <typename T>
PDB_NO_DISCARD inline constexpr T RoundUpToMultiple(T numToRound, T multipleOf) PDB_NO_EXCEPT
{
PDB_ASSERT(IsPowerOfTwo(multipleOf), "Multiple must be a power-of-two.");
return (numToRound + (multipleOf - 1u)) & ~(multipleOf - 1u);
}
// Finds the position of the first set bit in the given value starting from the LSB, e.g. FindFirstSetBit(0b00000010) == 1.
// This operation is also known as CTZ (Count Trailing Zeros).
template <typename T>
PDB_NO_DISCARD inline uint32_t FindFirstSetBit(T value) PDB_NO_EXCEPT;
template <>
PDB_NO_DISCARD inline uint32_t FindFirstSetBit(uint32_t value) PDB_NO_EXCEPT
{
PDB_ASSERT(value != 0u, "Invalid value.");
#ifdef _WIN32
unsigned long result = 0ul;
_BitScanForward(&result, value);
#else
unsigned int result = 0u;
result = static_cast<unsigned int>(__builtin_ffs(static_cast<int>(value)));
if (result)
{
--result;
}
#endif
return result;
}
}
}

View File

@@ -1,10 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
// Original raw_pdb forward-declares CRT functions to avoid pulling in headers,
// but this conflicts with MinGW's headers when compiled alongside Qt.
// Include the real headers instead.
#include <cstdio>
#include <cstring>

View File

@@ -1,9 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
// See Jonathan Mueller's blog for replacing std::move and std::forward:
// https://foonathan.net/2021/09/move-forward/
#define PDB_FORWARD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__)

View File

@@ -1,15 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "PDB_Macros.h"
#include "PDB_CRT.h"
PDB_PUSH_WARNING_CLANG
PDB_DISABLE_WARNING_CLANG("-Wgnu-zero-variadic-macro-arguments")
#define PDB_LOG_ERROR(_format, ...) printf(_format, ##__VA_ARGS__)
PDB_POP_WARNING_CLANG

View File

@@ -1,126 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "PDB_Platform.h"
#include "PDB_TypeTraits.h"
// ------------------------------------------------------------------------------------------------
// ATTRIBUTES
// ------------------------------------------------------------------------------------------------
// Indicates to the compiler that the return value of a function or class should not be ignored.
#if PDB_CPP_17
# define PDB_NO_DISCARD [[nodiscard]]
#else
# define PDB_NO_DISCARD
#endif
// Indicates to the compiler that a function does not throw an exception.
#define PDB_NO_EXCEPT noexcept
// ------------------------------------------------------------------------------------------------
// SPECIAL MEMBER FUNCTIONS
// ------------------------------------------------------------------------------------------------
// Default special member functions.
#define PDB_DEFAULT_COPY_CONSTRUCTOR(_name) _name(const _name&) PDB_NO_EXCEPT = default
#define PDB_DEFAULT_COPY_ASSIGNMENT(_name) _name& operator=(const _name&) PDB_NO_EXCEPT = default
#define PDB_DEFAULT_MOVE_CONSTRUCTOR(_name) _name(_name&&) PDB_NO_EXCEPT = default
#define PDB_DEFAULT_MOVE_ASSIGNMENT(_name) _name& operator=(_name&&) PDB_NO_EXCEPT = default
// Default copy member functions.
#define PDB_DEFAULT_COPY(_name) PDB_DEFAULT_COPY_CONSTRUCTOR(_name); PDB_DEFAULT_COPY_ASSIGNMENT(_name)
// Default move member functions.
#define PDB_DEFAULT_MOVE(_name) PDB_DEFAULT_MOVE_CONSTRUCTOR(_name); PDB_DEFAULT_MOVE_ASSIGNMENT(_name)
// Single macro to default all copy and move member functions.
#define PDB_DEFAULT_COPY_MOVE(_name) PDB_DEFAULT_COPY(_name); PDB_DEFAULT_MOVE(_name)
// Disable special member functions.
#define PDB_DISABLE_COPY_CONSTRUCTOR(_name) _name(const _name&) PDB_NO_EXCEPT = delete
#define PDB_DISABLE_COPY_ASSIGNMENT(_name) _name& operator=(const _name&) PDB_NO_EXCEPT = delete
#define PDB_DISABLE_MOVE_CONSTRUCTOR(_name) _name(_name&&) PDB_NO_EXCEPT = delete
#define PDB_DISABLE_MOVE_ASSIGNMENT(_name) _name& operator=(_name&&) PDB_NO_EXCEPT = delete
// Disable copy member functions.
#define PDB_DISABLE_COPY(_name) PDB_DISABLE_COPY_CONSTRUCTOR(_name); PDB_DISABLE_COPY_ASSIGNMENT(_name)
// Disable move member functions.
#define PDB_DISABLE_MOVE(_name) PDB_DISABLE_MOVE_CONSTRUCTOR(_name); PDB_DISABLE_MOVE_ASSIGNMENT(_name)
// Single macro to disable all copy and move member functions.
#define PDB_DISABLE_COPY_MOVE(_name) PDB_DISABLE_COPY(_name); PDB_DISABLE_MOVE(_name)
// ------------------------------------------------------------------------------------------------
// COMPILER WARNINGS
// ------------------------------------------------------------------------------------------------
#if PDB_COMPILER_MSVC
# define PDB_PRAGMA(_x) __pragma(_x)
# define PDB_PUSH_WARNING_MSVC PDB_PRAGMA(warning(push))
# define PDB_SUPPRESS_WARNING_MSVC(_number) PDB_PRAGMA(warning(suppress : _number))
# define PDB_DISABLE_WARNING_MSVC(_number) PDB_PRAGMA(warning(disable : _number))
# define PDB_POP_WARNING_MSVC PDB_PRAGMA(warning(pop))
# define PDB_PUSH_WARNING_CLANG
# define PDB_DISABLE_WARNING_CLANG(_diagnostic)
# define PDB_POP_WARNING_CLANG
#elif PDB_COMPILER_CLANG
# define PDB_PRAGMA(_x) _Pragma(#_x)
# define PDB_PUSH_WARNING_MSVC
# define PDB_SUPPRESS_WARNING_MSVC(_number)
# define PDB_DISABLE_WARNING_MSVC(_number)
# define PDB_POP_WARNING_MSVC
# define PDB_PUSH_WARNING_CLANG PDB_PRAGMA(clang diagnostic push)
# define PDB_DISABLE_WARNING_CLANG(_diagnostic) PDB_PRAGMA(clang diagnostic ignored _diagnostic)
# define PDB_POP_WARNING_CLANG PDB_PRAGMA(clang diagnostic pop)
#elif PDB_COMPILER_GCC
# define PDB_PRAGMA(_x) _Pragma(#_x)
# define PDB_PUSH_WARNING_MSVC
# define PDB_SUPPRESS_WARNING_MSVC(_number)
# define PDB_DISABLE_WARNING_MSVC(_number)
# define PDB_POP_WARNING_MSVC
# define PDB_PUSH_WARNING_CLANG
# define PDB_DISABLE_WARNING_CLANG(_diagnostic)
# define PDB_POP_WARNING_CLANG
#endif
// ------------------------------------------------------------------------------------------------
// MISCELLANEOUS
// ------------------------------------------------------------------------------------------------
// Trick to make other macros require a semicolon at the end.
#define PDB_REQUIRE_SEMICOLON static_assert(true, "")
// Defines a C-like flexible array member.
#define PDB_FLEXIBLE_ARRAY_MEMBER(_type, _name) \
PDB_PUSH_WARNING_MSVC \
PDB_PUSH_WARNING_CLANG \
PDB_DISABLE_WARNING_MSVC(4200) \
PDB_DISABLE_WARNING_CLANG("-Wzero-length-array") \
_type _name[0]; \
PDB_POP_WARNING_MSVC \
PDB_POP_WARNING_CLANG \
PDB_REQUIRE_SEMICOLON
// Casts any value to the value of the underlying type.
#define PDB_AS_UNDERLYING(_value) static_cast<typename PDB::underlying_type<decltype(_value)>::type>(_value)
// Signals to the compiler that a function should be ignored, but have its argument list parsed (and "used", so as to not generate "unused variable" warnings).
#if PDB_COMPILER_MSVC
# define PDB_NOOP __noop
#else
# define PDB_NOOP(...) (void)sizeof(__VA_ARGS__)
#endif

View File

@@ -1,11 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#define PDB_NEW(_type) new _type
#define PDB_NEW_ARRAY(_type, _length) new _type[_length]
#define PDB_DELETE(_ptr) delete _ptr
#define PDB_DELETE_ARRAY(_ptr) delete[] _ptr

View File

@@ -1,11 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "PDB_TypeTraits.h"
// See Jonathan Mueller's blog for replacing std::move and std::forward:
// https://foonathan.net/2020/09/move-forward/
#define PDB_MOVE(...) static_cast<PDB::remove_reference<decltype(__VA_ARGS__)>::type&&>(__VA_ARGS__)

View File

@@ -1,45 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
// determine the compiler/toolchain used
#if defined(__clang__)
# define PDB_COMPILER_MSVC 0
# define PDB_COMPILER_CLANG 1
# define PDB_COMPILER_GCC 0
#elif defined(_MSC_VER)
# define PDB_COMPILER_MSVC 1
# define PDB_COMPILER_CLANG 0
# define PDB_COMPILER_GCC 0
#elif defined(__GNUC__)
# define PDB_COMPILER_MSVC 0
# define PDB_COMPILER_CLANG 0
# define PDB_COMPILER_GCC 1
#else
# error("Unknown compiler.");
#endif
// check whether C++17 is available
#if __cplusplus >= 201703L
# define PDB_CPP_17 1
#else
# define PDB_CPP_17 0
#endif
// define used standard types
typedef decltype(sizeof(0)) size_t;
static_assert(sizeof(sizeof(0)) == sizeof(size_t), "Wrong size.");
typedef int int32_t;
static_assert(sizeof(int32_t) == 4u, "Wrong size.");
typedef unsigned char uint8_t;
static_assert(sizeof(uint8_t) == 1u, "Wrong size.");
typedef unsigned short uint16_t;
static_assert(sizeof(uint16_t) == 2u, "Wrong size.");
typedef unsigned int uint32_t;
static_assert(sizeof(uint32_t) == 4u, "Wrong size.");

View File

@@ -1,33 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "PDB_Macros.h"
#include "PDB_TypeTraits.h"
namespace PDB
{
namespace Pointer
{
// Offsets any pointer by a given number of bytes.
template <typename T, typename U, typename V>
PDB_NO_DISCARD inline T Offset(U* anyPointer, V howManyBytes) PDB_NO_EXCEPT
{
static_assert(PDB::is_pointer<T>::value == true, "Type T must be a pointer type.");
union
{
T as_T;
U* as_U_ptr;
char* as_char_ptr;
};
as_U_ptr = anyPointer;
as_char_ptr += howManyBytes;
return as_T;
}
}
}

View File

@@ -1,65 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
// provide our own type traits to avoid pulling in unnecessary includes
namespace PDB
{
template <class T>
struct is_pointer
{
static constexpr bool value = false;
};
template <class T>
struct is_pointer<T*>
{
static constexpr bool value = true;
};
template <class T>
struct is_pointer<T* const>
{
static constexpr bool value = true;
};
template <class T>
struct is_pointer<T* volatile>
{
static constexpr bool value = true;
};
template <class T>
struct is_pointer<T* const volatile>
{
static constexpr bool value = true;
};
template <class T>
struct remove_reference
{
using type = T;
};
template <class T>
struct remove_reference<T&>
{
using type = T;
};
template <class T>
struct remove_reference<T&&>
{
using type = T;
};
template <class T>
struct underlying_type
{
using type = __underlying_type(T);
};
}

View File

@@ -1,45 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "PDB_Platform.h"
#if PDB_COMPILER_MSVC
// some warnings were introduced with different versions of Visual Studio, so we disable this warning instead of using a bunch of #if/#endif
# pragma warning (disable : 4619) // there is no warning number N
// we compile with exceptions disabled
# pragma warning (disable : 4530) // C++ exception handler used, but unwind semantics are not enabled.Specify / EHsc
# pragma warning (disable : 4577) // 'noexcept' used with no exception handling mode specified; termination on exception is not guaranteed. Specify /EHsc
// ignore purely informational warnings
# pragma warning (disable : 4514) // unreferenced inline function has been removed
# pragma warning (disable : 4710) // function not inlined
# pragma warning (disable : 4711) // function selected for automatic inline expansion
# pragma warning (disable : 4820) // 'N' bytes padding added after data member 'm_member'
# pragma warning (disable : 5045) // Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified
#elif PDB_COMPILER_CLANG
// turn on absolutely all available Clang warnings
# pragma clang diagnostic warning "-Wall"
# pragma clang diagnostic warning "-Wextra"
# pragma clang diagnostic warning "-Weverything"
# pragma clang diagnostic warning "-Wpedantic"
// these warnings contradict -Weverything
# pragma clang diagnostic ignored "-Wc++98-compat"
# pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
// this warning is triggered for templates which are explicitly instantiated.
// forgetting to instantiate the template would trigger a linker error anyway, so we disable this warning.
# pragma clang diagnostic ignored "-Wundefined-func-template"
// we don't strive for C++20 compatibility
# pragma clang diagnostic ignored "-Wc++20-compat"
// some structures will have to be padded
# pragma clang diagnostic ignored "-Wpadded"
// it's impossible to write C++ code using raw pointers without triggering this warning
# pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
#endif

View File

@@ -1,55 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "PDB_PCH.h"
#include "PDB.h"
#include "PDB_Types.h"
#include "PDB_Util.h"
#include "PDB_RawFile.h"
#include "Foundation/PDB_PointerUtil.h"
#include "Foundation/PDB_CRT.h"
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD PDB::ErrorCode PDB::ValidateFile(const void* data, size_t size) PDB_NO_EXCEPT
{
// validate whether there is enough size for the super block
if (size < sizeof(SuperBlock))
{
return ErrorCode::InvalidDataSize;
}
// validate the super block
const SuperBlock* superBlock = Pointer::Offset<const SuperBlock*>(data, 0u);
{
// validate header magic
if (memcmp(superBlock->fileMagic, SuperBlock::MAGIC, sizeof(SuperBlock::MAGIC)) != 0)
{
return ErrorCode::InvalidSuperBlock;
}
// validate whether enough size is provided for the PDB file
// blockCount * blockSize is the size of the PDB file on disk
if (size < superBlock->blockCount * superBlock->blockSize)
{
return ErrorCode::InvalidDataSize;
}
// validate free block map.
// the free block map should always reside at either index 1 or 2.
if (superBlock->freeBlockMapIndex != 1u && superBlock->freeBlockMapIndex != 2u)
{
return ErrorCode::InvalidFreeBlockMap;
}
}
return ErrorCode::Success;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD PDB::RawFile PDB::CreateRawFile(const void* data) PDB_NO_EXCEPT
{
return RawFile(data);
}

View File

@@ -1,21 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "Foundation/PDB_Macros.h"
#include "PDB_ErrorCodes.h"
// https://llvm.org/docs/PDB/index.html
namespace PDB
{
class RawFile;
// Validates whether a PDB file is valid.
PDB_NO_DISCARD ErrorCode ValidateFile(const void* data, size_t size) PDB_NO_EXCEPT;
// Creates a raw PDB file that must have been validated.
PDB_NO_DISCARD RawFile CreateRawFile(const void* data) PDB_NO_EXCEPT;
}

View File

@@ -1,169 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "PDB_PCH.h"
#include "PDB_CoalescedMSFStream.h"
#include "PDB_Util.h"
#include "PDB_DirectMSFStream.h"
#include "Foundation/PDB_PointerUtil.h"
#include "Foundation/PDB_Memory.h"
#include "Foundation/PDB_CRT.h"
namespace
{
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD static bool AreBlockIndicesContiguous(const uint32_t* blockIndices, uint32_t blockSize, uint32_t streamSize) PDB_NO_EXCEPT
{
const uint32_t blockCount = PDB::ConvertSizeToBlockCount(streamSize, blockSize);
// start with the first index, checking if all following indices are contiguous (N, N+1, N+2, ...)
uint32_t expectedIndex = blockIndices[0];
for (uint32_t i = 1u; i < blockCount; ++i)
{
++expectedIndex;
if (blockIndices[i] != expectedIndex)
{
return false;
}
}
return true;
}
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::CoalescedMSFStream::CoalescedMSFStream(void) PDB_NO_EXCEPT
: m_ownedData(nullptr)
, m_data(nullptr)
, m_size(0u)
{
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::CoalescedMSFStream::CoalescedMSFStream(CoalescedMSFStream&& other) PDB_NO_EXCEPT
: m_ownedData(PDB_MOVE(other.m_ownedData))
, m_data(PDB_MOVE(other.m_data))
, m_size(PDB_MOVE(other.m_size))
{
other.m_ownedData = nullptr;
other.m_data = nullptr;
other.m_size = 0u;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::CoalescedMSFStream& PDB::CoalescedMSFStream::operator=(CoalescedMSFStream&& other) PDB_NO_EXCEPT
{
if (this != &other)
{
PDB_DELETE_ARRAY(m_ownedData);
m_ownedData = PDB_MOVE(other.m_ownedData);
m_data = PDB_MOVE(other.m_data);
m_size = PDB_MOVE(other.m_size);
other.m_ownedData = nullptr;
other.m_data = nullptr;
other.m_size = 0u;
}
return *this;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::CoalescedMSFStream::CoalescedMSFStream(const void* data, uint32_t blockSize, const uint32_t* blockIndices, uint32_t streamSize) PDB_NO_EXCEPT
: m_ownedData(nullptr)
, m_data(nullptr)
, m_size(streamSize)
{
if (AreBlockIndicesContiguous(blockIndices, blockSize, streamSize))
{
// fast path, all block indices are contiguous, so we don't have to copy any data at all.
// instead, we directly point into the memory-mapped file at the correct offset.
const uint32_t index = blockIndices[0];
const size_t fileOffset = PDB::ConvertBlockIndexToFileOffset(index, blockSize);
m_data = Pointer::Offset<const Byte*>(data, fileOffset);
}
else
{
// slower path, we need to copy disjunct blocks into our own data array, block by block
m_ownedData = PDB_NEW_ARRAY(Byte, streamSize);
m_data = m_ownedData;
Byte* destination = m_ownedData;
// copy full blocks first
const uint32_t fullBlockCount = streamSize / blockSize;
for (uint32_t i = 0u; i < fullBlockCount; ++i)
{
const uint32_t index = blockIndices[i];
// read one single block at the correct offset in the stream
const size_t fileOffset = PDB::ConvertBlockIndexToFileOffset(index, blockSize);
const void* sourceData = Pointer::Offset<const void*>(data, fileOffset);
memcpy(destination, sourceData, blockSize);
destination += blockSize;
}
// account for non-full blocks
const uint32_t remainingBytes = streamSize - (fullBlockCount * blockSize);
if (remainingBytes != 0u)
{
const uint32_t index = blockIndices[fullBlockCount];
// read remaining bytes at correct offset in the stream
const size_t fileOffset = PDB::ConvertBlockIndexToFileOffset(index, blockSize);
const void* sourceData = Pointer::Offset<const void*>(data, fileOffset);
memcpy(destination, sourceData, remainingBytes);
}
}
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::CoalescedMSFStream::CoalescedMSFStream(const DirectMSFStream& directStream, uint32_t size, uint32_t offset) PDB_NO_EXCEPT
: m_ownedData(nullptr)
, m_data(nullptr)
, m_size(size)
{
const DirectMSFStream::IndexAndOffset indexAndOffset = directStream.GetBlockIndexForOffset(offset);
// Note: we need to add the offset within the block to the size of the stream to determine if the block
// indices are contiguous. This is needed to deal with the case where reading the requested number of bytes
// from the specified offset would cross a block boundary. For example, if the offset within the block is
// 64 and we want to read 4096 bytes with a block size of 4096, we need to consider *two* block indices,
// not *one*, even though 4096 / 4096 = 1.
if (AreBlockIndicesContiguous(directStream.GetBlockIndices() + indexAndOffset.index, directStream.GetBlockSize(), indexAndOffset.offsetWithinBlock + size))
{
// fast path, all block indices inside the direct stream from (data + offset) to (data + offset + size) are contiguous
const size_t offsetWithinData = directStream.GetDataOffsetForIndexAndOffset(indexAndOffset);
m_data = Pointer::Offset<const Byte*>(directStream.GetData(), offsetWithinData);
}
else
{
// slower path, we need to copy from disjunct blocks, which is performed by the direct stream
m_ownedData = PDB_NEW_ARRAY(Byte, size);
m_data = m_ownedData;
directStream.ReadAtOffset(m_ownedData, size, offset);
}
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::CoalescedMSFStream::~CoalescedMSFStream(void) PDB_NO_EXCEPT
{
PDB_DELETE_ARRAY(m_ownedData);
}

View File

@@ -1,71 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "Foundation/PDB_Assert.h"
#include "Foundation/PDB_Macros.h"
#include "PDB_Types.h"
// https://llvm.org/docs/PDB/index.html#the-msf-container
// https://llvm.org/docs/PDB/MsfFile.html
namespace PDB
{
class PDB_NO_DISCARD DirectMSFStream;
// provides access to a coalesced version of an MSF stream.
// inherently thread-safe, the stream doesn't carry any internal offset or similar.
// coalesces all blocks into a contiguous stream of data upon construction.
// very fast individual reads, useful when almost all data of a stream is needed anyway.
class PDB_NO_DISCARD CoalescedMSFStream
{
public:
CoalescedMSFStream(void) PDB_NO_EXCEPT;
CoalescedMSFStream(CoalescedMSFStream&& other) PDB_NO_EXCEPT;
CoalescedMSFStream& operator=(CoalescedMSFStream&& other) PDB_NO_EXCEPT;
explicit CoalescedMSFStream(const void* data, uint32_t blockSize, const uint32_t* blockIndices, uint32_t streamSize) PDB_NO_EXCEPT;
// Creates a coalesced stream from a direct stream at any offset.
explicit CoalescedMSFStream(const DirectMSFStream& directStream, uint32_t size, uint32_t offset) PDB_NO_EXCEPT;
~CoalescedMSFStream(void) PDB_NO_EXCEPT;
// Returns the size of the stream.
PDB_NO_DISCARD inline size_t GetSize(void) const PDB_NO_EXCEPT
{
return m_size;
}
// Provides read-only access to the data.
template <typename T>
PDB_NO_DISCARD inline const T* GetDataAtOffset(size_t offset) const PDB_NO_EXCEPT
{
return reinterpret_cast<const T*>(m_data + offset);
}
template <typename T>
PDB_NO_DISCARD inline size_t GetPointerOffset(const T* pointer) const PDB_NO_EXCEPT
{
const Byte* bytePointer = reinterpret_cast<const Byte*>(pointer);
const Byte* dataEnd = m_data + m_size;
PDB_ASSERT(bytePointer >= m_data && bytePointer <= dataEnd, "Pointer 0x%p not within stream range [0x%p:0x%p]",
static_cast<const void*>(bytePointer), static_cast<const void*>(m_data), static_cast<const void*>(dataEnd));
return static_cast<size_t>(bytePointer - m_data);
}
private:
// contiguous, coalesced data, can be null
Byte* m_ownedData;
// either points to the owned data that has been copied from disjunct blocks, or points to the
// memory-mapped data directly in case all stream blocks are contiguous.
const Byte* m_data;
size_t m_size;
PDB_DISABLE_COPY(CoalescedMSFStream);
};
}

View File

@@ -1,335 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "PDB_PCH.h"
#include "PDB_DBIStream.h"
#include "PDB_RawFile.h"
namespace
{
// the DBI stream always resides at index 3
static constexpr const uint32_t DBIStreamIndex = 3u;
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD static inline uint32_t GetModuleInfoSubstreamOffset(const PDB::DBI::StreamHeader& /* dbiHeader */) PDB_NO_EXCEPT
{
return sizeof(PDB::DBI::StreamHeader);
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD static inline uint32_t GetSectionContributionSubstreamOffset(const PDB::DBI::StreamHeader& dbiHeader) PDB_NO_EXCEPT
{
return GetModuleInfoSubstreamOffset(dbiHeader) + dbiHeader.moduleInfoSize;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD static inline uint32_t GetSectionMapSubstreamOffset(const PDB::DBI::StreamHeader& dbiHeader) PDB_NO_EXCEPT
{
return GetSectionContributionSubstreamOffset(dbiHeader) + dbiHeader.sectionContributionSize;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD static inline uint32_t GetSourceInfoSubstreamOffset(const PDB::DBI::StreamHeader& dbiHeader) PDB_NO_EXCEPT
{
return GetSectionMapSubstreamOffset(dbiHeader) + dbiHeader.sectionMapSize;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD static inline uint32_t GetTypeServerMapSubstreamOffset(const PDB::DBI::StreamHeader& dbiHeader) PDB_NO_EXCEPT
{
return GetSourceInfoSubstreamOffset(dbiHeader) + dbiHeader.sourceInfoSize;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD static inline uint32_t GetECSubstreamOffset(const PDB::DBI::StreamHeader& dbiHeader) PDB_NO_EXCEPT
{
return GetTypeServerMapSubstreamOffset(dbiHeader) + dbiHeader.typeServerMapSize;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD static inline uint32_t GetDebugHeaderSubstreamOffset(const PDB::DBI::StreamHeader& dbiHeader) PDB_NO_EXCEPT
{
return GetECSubstreamOffset(dbiHeader) + dbiHeader.ecSize;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD static inline bool HasDebugHeaderSubstream(const PDB::DBI::StreamHeader& dbiHeader) PDB_NO_EXCEPT
{
return dbiHeader.optionalDebugHeaderSize != 0u;
}
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::DBIStream::DBIStream(void) PDB_NO_EXCEPT
: m_header()
, m_stream()
{
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::DBIStream::DBIStream(const RawFile& file, const DBI::StreamHeader& header) PDB_NO_EXCEPT
: m_header(header)
, m_stream(file.CreateMSFStream<DirectMSFStream>(DBIStreamIndex))
{
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD PDB::ErrorCode PDB::HasValidDBIStream(const RawFile& file) PDB_NO_EXCEPT
{
DirectMSFStream stream = file.CreateMSFStream<DirectMSFStream>(DBIStreamIndex);
if (stream.GetSize() < sizeof(DBI::StreamHeader))
{
return ErrorCode::InvalidStream;
}
const DBI::StreamHeader header = stream.ReadAtOffset<DBI::StreamHeader>(0u);
if (header.signature != DBI::StreamHeader::Signature)
{
return ErrorCode::InvalidSignature;
}
else if (header.version != DBI::StreamHeader::Version::V70)
{
return ErrorCode::UnknownVersion;
}
return ErrorCode::Success;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD PDB::DBIStream PDB::CreateDBIStream(const RawFile& file) PDB_NO_EXCEPT
{
DirectMSFStream stream = file.CreateMSFStream<DirectMSFStream>(DBIStreamIndex);
const DBI::StreamHeader header = stream.ReadAtOffset<DBI::StreamHeader>(0u);
return DBIStream { file, header };
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD PDB::ErrorCode PDB::DBIStream::HasValidSymbolRecordStream(const RawFile& /* file */) const PDB_NO_EXCEPT
{
return (m_header.symbolRecordStreamIndex != PDB::NilStreamIndex) ? ErrorCode::Success : ErrorCode::InvalidStreamIndex;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD PDB::ErrorCode PDB::DBIStream::HasValidImageSectionStream(const RawFile& /* file */) const PDB_NO_EXCEPT
{
// the debug header stream is optional. if it's not there, we can't get the image section stream either.
if (!HasDebugHeaderSubstream(m_header))
{
return ErrorCode::InvalidStreamIndex;
}
// find the debug header sub-stream
const uint32_t debugHeaderOffset = GetDebugHeaderSubstreamOffset(m_header);
// validate that we have enough data to read the debug header
// (the header field optionalDebugHeaderSize might claim there's a debug header,
// but the stream might not have enough data - this happens with some .ni.pdb files)
if (debugHeaderOffset + sizeof(DBI::DebugHeader) > m_stream.GetSize())
{
return ErrorCode::InvalidStream;
}
const DBI::DebugHeader& debugHeader = m_stream.ReadAtOffset<DBI::DebugHeader>(debugHeaderOffset);
if (debugHeader.sectionHeaderStreamIndex == DBI::DebugHeader::InvalidStreamIndex)
{
return ErrorCode::InvalidStreamIndex;
}
return ErrorCode::Success;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD PDB::ErrorCode PDB::DBIStream::HasValidPublicSymbolStream(const RawFile& file) const PDB_NO_EXCEPT
{
if (m_header.publicStreamIndex == PDB::NilStreamIndex)
{
return ErrorCode::InvalidStreamIndex;
}
DirectMSFStream publicStream = file.CreateMSFStream<DirectMSFStream>(m_header.publicStreamIndex);
// the public symbol stream always begins with a header, we are not interested in that.
// following the public symbol stream header is a hash table header.
const HashTableHeader hashHeader = publicStream.ReadAtOffset<HashTableHeader>(sizeof(PublicStreamHeader));
if (hashHeader.signature != HashTableHeader::Signature)
{
return ErrorCode::InvalidSignature;
}
else if (hashHeader.version != HashTableHeader::Version)
{
return ErrorCode::UnknownVersion;
}
return ErrorCode::Success;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD PDB::ErrorCode PDB::DBIStream::HasValidGlobalSymbolStream(const RawFile& file) const PDB_NO_EXCEPT
{
if (m_header.globalStreamIndex == PDB::NilStreamIndex)
{
return ErrorCode::InvalidStreamIndex;
}
DirectMSFStream globalStream = file.CreateMSFStream<DirectMSFStream>(m_header.globalStreamIndex);
// the global symbol stream starts with a hash table header
const HashTableHeader hashHeader = globalStream.ReadAtOffset<HashTableHeader>(0u);
if (hashHeader.signature != HashTableHeader::Signature)
{
return ErrorCode::InvalidSignature;
}
else if (hashHeader.version != HashTableHeader::Version)
{
return ErrorCode::UnknownVersion;
}
return ErrorCode::Success;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD PDB::ErrorCode PDB::DBIStream::HasValidSectionContributionStream(const RawFile& /* file */) const PDB_NO_EXCEPT
{
if (m_header.sectionContributionSize < sizeof(DBI::SectionContribution::Version))
{
return ErrorCode::InvalidStream;
}
// find the section contribution sub-stream
// https://llvm.org/docs/PDB/DbiStream.html#section-contribution-substream
const uint32_t streamOffset = GetSectionContributionSubstreamOffset(m_header);
const DBI::SectionContribution::Version version = m_stream.ReadAtOffset<DBI::SectionContribution::Version>(streamOffset);
if (version != DBI::SectionContribution::Version::Ver60)
{
return ErrorCode::UnknownVersion;
}
return ErrorCode::Success;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD PDB::CoalescedMSFStream PDB::DBIStream::CreateSymbolRecordStream(const RawFile& file) const PDB_NO_EXCEPT
{
// the symbol record stream holds the actual CodeView data of the symbols
return file.CreateMSFStream<CoalescedMSFStream>(m_header.symbolRecordStreamIndex);
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD PDB::ImageSectionStream PDB::DBIStream::CreateImageSectionStream(const RawFile& file) const PDB_NO_EXCEPT
{
// find the debug header sub-stream
const uint32_t debugHeaderOffset = GetDebugHeaderSubstreamOffset(m_header);
const DBI::DebugHeader& debugHeader = m_stream.ReadAtOffset<DBI::DebugHeader>(debugHeaderOffset);
// from there, grab the section header stream
return ImageSectionStream(file, debugHeader.sectionHeaderStreamIndex);
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD PDB::PublicSymbolStream PDB::DBIStream::CreatePublicSymbolStream(const RawFile& file) const PDB_NO_EXCEPT
{
DirectMSFStream publicStream = file.CreateMSFStream<DirectMSFStream>(m_header.publicStreamIndex);
// the public symbol stream always begins with a header, we are not interested in that.
// following the public symbol stream header is a hash table header.
// we use this to work out how many symbol records are referenced by the public symbol stream.
const HashTableHeader hashHeader = publicStream.ReadAtOffset<HashTableHeader>(sizeof(PublicStreamHeader));
const uint32_t recordCount = hashHeader.size / sizeof(HashRecord);
return PublicSymbolStream(file, m_header.publicStreamIndex, recordCount);
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD PDB::GlobalSymbolStream PDB::DBIStream::CreateGlobalSymbolStream(const RawFile& file) const PDB_NO_EXCEPT
{
DirectMSFStream globalStream = file.CreateMSFStream<DirectMSFStream>(m_header.globalStreamIndex);
// the global symbol stream starts with a hash table header.
// we use this to work out how many symbol records are referenced by the global symbol stream.
const HashTableHeader hashHeader = globalStream.ReadAtOffset<HashTableHeader>(0u);
const uint32_t recordCount = hashHeader.size / sizeof(HashRecord);
return GlobalSymbolStream(file, m_header.globalStreamIndex, recordCount);
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD PDB::SourceFileStream PDB::DBIStream::CreateSourceFileStream(const RawFile& /* file */) const PDB_NO_EXCEPT
{
// find the source info sub-stream
// https://llvm.org/docs/PDB/DbiStream.html#file-info-substream
const uint32_t streamOffset = GetSourceInfoSubstreamOffset(m_header);
return SourceFileStream(m_stream, m_header.sourceInfoSize, streamOffset);
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD PDB::SectionContributionStream PDB::DBIStream::CreateSectionContributionStream(const RawFile& /* file */) const PDB_NO_EXCEPT
{
// find the section contribution sub-stream
// https://llvm.org/docs/PDB/DbiStream.html#section-contribution-substream
const uint32_t streamOffset = GetSectionContributionSubstreamOffset(m_header);
return SectionContributionStream(m_stream, m_header.sectionContributionSize - sizeof(DBI::SectionContribution::Version), streamOffset + sizeof(DBI::SectionContribution::Version));
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD PDB::ModuleInfoStream PDB::DBIStream::CreateModuleInfoStream(const RawFile& /* file */) const PDB_NO_EXCEPT
{
// find the module info sub-stream
// https://llvm.org/docs/PDB/DbiStream.html#module-info-substream
const uint32_t streamOffset = GetModuleInfoSubstreamOffset(m_header);
return ModuleInfoStream(m_stream, m_header.moduleInfoSize, streamOffset);
}

View File

@@ -1,65 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "Foundation/PDB_Macros.h"
#include "PDB_ErrorCodes.h"
#include "PDB_DBITypes.h"
#include "PDB_CoalescedMSFStream.h"
#include "PDB_DirectMSFStream.h"
#include "PDB_ImageSectionStream.h"
#include "PDB_PublicSymbolStream.h"
#include "PDB_GlobalSymbolStream.h"
#include "PDB_SourceFileStream.h"
#include "PDB_SectionContributionStream.h"
#include "PDB_ModuleInfoStream.h"
// PDB DBI Stream
// https://llvm.org/docs/PDB/DbiStream.html
namespace PDB
{
class RawFile;
class PDB_NO_DISCARD DBIStream
{
public:
DBIStream(void) PDB_NO_EXCEPT;
explicit DBIStream(const RawFile& file, const DBI::StreamHeader& header) PDB_NO_EXCEPT;
PDB_DEFAULT_MOVE(DBIStream);
PDB_NO_DISCARD ErrorCode HasValidSymbolRecordStream(const RawFile& file) const PDB_NO_EXCEPT;
PDB_NO_DISCARD ErrorCode HasValidImageSectionStream(const RawFile& file) const PDB_NO_EXCEPT;
PDB_NO_DISCARD ErrorCode HasValidPublicSymbolStream(const RawFile& file) const PDB_NO_EXCEPT;
PDB_NO_DISCARD ErrorCode HasValidGlobalSymbolStream(const RawFile& file) const PDB_NO_EXCEPT;
PDB_NO_DISCARD ErrorCode HasValidSectionContributionStream(const RawFile& file) const PDB_NO_EXCEPT;
PDB_NO_DISCARD CoalescedMSFStream CreateSymbolRecordStream(const RawFile& file) const PDB_NO_EXCEPT;
PDB_NO_DISCARD ImageSectionStream CreateImageSectionStream(const RawFile& file) const PDB_NO_EXCEPT;
PDB_NO_DISCARD PublicSymbolStream CreatePublicSymbolStream(const RawFile& file) const PDB_NO_EXCEPT;
PDB_NO_DISCARD GlobalSymbolStream CreateGlobalSymbolStream(const RawFile& file) const PDB_NO_EXCEPT;
PDB_NO_DISCARD SourceFileStream CreateSourceFileStream(const RawFile& file) const PDB_NO_EXCEPT;
PDB_NO_DISCARD SectionContributionStream CreateSectionContributionStream(const RawFile& file) const PDB_NO_EXCEPT;
PDB_NO_DISCARD ModuleInfoStream CreateModuleInfoStream(const RawFile& file) const PDB_NO_EXCEPT;
PDB_NO_DISCARD const DBI::StreamHeader& GetHeader(void) const PDB_NO_EXCEPT
{
return m_header;
}
private:
DBI::StreamHeader m_header;
DirectMSFStream m_stream;
PDB_DISABLE_COPY(DBIStream);
};
// Returns whether the given raw file provides a valid DBI stream.
PDB_NO_DISCARD ErrorCode HasValidDBIStream(const RawFile& file) PDB_NO_EXCEPT;
// Creates the DBI stream from a raw file.
PDB_NO_DISCARD DBIStream CreateDBIStream(const RawFile& file) PDB_NO_EXCEPT;
}

View File

@@ -1,9 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "PDB_PCH.h"
#include "PDB_DBITypes.h"
const uint32_t PDB::DBI::StreamHeader::Signature = 0xffffffffu;
const uint16_t PDB::DBI::DebugHeader::InvalidStreamIndex = 0xFFFFu;

View File

@@ -1,928 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "Foundation/PDB_Macros.h"
#include "Foundation/PDB_BitOperators.h"
namespace PDB
{
namespace DBI
{
// https://llvm.org/docs/PDB/DbiStream.html#stream-header
// https://github.com/microsoft/microsoft-pdb/blob/master/PDB/dbi/dbi.h#L124
struct StreamHeader
{
static const uint32_t Signature;
enum class PDB_NO_DISCARD Version : uint32_t
{
VC41 = 930803u,
V50 = 19960307u,
V60 = 19970606u,
V70 = 19990903u,
V110 = 20091201u
};
uint32_t signature;
Version version;
uint32_t age;
uint16_t globalStreamIndex; // index of the global symbol stream
uint16_t toolchain;
uint16_t publicStreamIndex; // index of the public symbol stream
uint16_t pdbDllVersion;
uint16_t symbolRecordStreamIndex; // index of the symbol record stream
uint16_t pdbDllRbld;
uint32_t moduleInfoSize;
uint32_t sectionContributionSize;
uint32_t sectionMapSize;
uint32_t sourceInfoSize;
uint32_t typeServerMapSize;
uint32_t mfcTypeServerIndex;
uint32_t optionalDebugHeaderSize;
uint32_t ecSize;
uint16_t flags;
uint16_t machine;
uint32_t padding;
};
// https://llvm.org/docs/PDB/DbiStream.html#optional-debug-header-stream
struct DebugHeader
{
static const uint16_t InvalidStreamIndex;
uint16_t fpoDataStreamIndex; // IMAGE_DEBUG_TYPE_FPO
uint16_t exceptionDataStreamIndex; // IMAGE_DEBUG_TYPE_EXCEPTION
uint16_t fixupDataStreamIndex; // IMAGE_DEBUG_TYPE_FIXUP
uint16_t omapToSrcDataStreamIndex; // IMAGE_DEBUG_TYPE_OMAP_TO_SRC
uint16_t omapFromSrcDataStreamIndex; // IMAGE_DEBUG_TYPE_OMAP_FROM_SRC
uint16_t sectionHeaderStreamIndex; // a dump of all section headers (IMAGE_SECTION_HEADER) from the original executable
uint16_t tokenDataStreamIndex;
uint16_t xdataStreamIndex;
uint16_t pdataStreamIndex;
uint16_t newFpoDataStreamIndex;
uint16_t originalSectionHeaderDataStreamIndex;
};
// https://llvm.org/docs/PDB/DbiStream.html#section-contribution-substream
struct SectionContribution
{
enum class PDB_NO_DISCARD Version : uint32_t
{
Ver60 = 0xeffe0000u + 19970605u,
V2 = 0xeffe0000u + 20140516u
};
uint16_t section;
uint16_t padding;
uint32_t offset;
uint32_t size;
uint32_t characteristics;
uint16_t moduleIndex;
uint16_t padding2;
uint32_t dataCrc;
uint32_t relocationCrc;
};
// https://llvm.org/docs/PDB/DbiStream.html#module-info-substream
struct ModuleInfo
{
uint32_t unused;
SectionContribution sectionContribution;
uint16_t flags;
uint16_t moduleSymbolStreamIndex;
uint32_t symbolSize;
uint32_t c11Size;
uint32_t c13Size;
uint16_t sourceFileCount;
uint16_t padding;
uint32_t unused2;
uint32_t sourceFileNameIndex;
uint32_t pdbFilePathNameIndex;
};
}
namespace CodeView
{
namespace DBI
{
// code view type records that can appear in a DBI stream.
// this list is not exhaustive, but only contains what we need so far.
// https://llvm.org/docs/PDB/CodeViewSymbols.html
// https://llvm.org/docs/PDB/TpiStream.html#tpi-vs-ipi-stream
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L2735
enum class PDB_NO_DISCARD SymbolRecordKind : uint16_t
{
S_END = 0x0006u, // block, procedure, "with" or thunk end
S_SKIP = 0x0007u, // Reserve symbol space in $$Symbols table
S_FRAMEPROC = 0x1012u, // extra frame and proc information
S_ANNOTATION = 0x1019u, // annotation string literals ("__annotation" intrinsic, e.g. via NT_ASSERT)
S_OBJNAME = 0x1101u, // full path to the original compiled .obj. can point to remote locations and temporary files, not necessarily the file that was linked into the executable
S_THUNK32 = 0x1102u, // thunk start
S_BLOCK32 = 0x1103u, // block start
S_LABEL32 = 0x1105u, // code label
S_REGISTER = 0x1106u, // register variable
S_CONSTANT = 0x1107u, // constant symbol
S_BPREL32 = 0x110Bu, // BP-relative address (almost like S_REGREL32)
S_LDATA32 = 0x110Cu, // (static) local data
S_GDATA32 = 0x110Du, // global data
S_PUB32 = 0x110Eu, // public symbol
S_LPROC32 = 0x110Fu, // local procedure start
S_GPROC32 = 0x1110u, // global procedure start
S_REGREL32 = 0x1111u, // register relative address
S_LTHREAD32 = 0x1112u, // (static) thread-local data
S_GTHREAD32 = 0x1113u, // global thread-local data
S_UNAMESPACE = 0x1124u, // using namespace
S_PROCREF = 0x1125u, // reference to function in any compiland
S_LPROCREF = 0x1127u, // local reference to function in any compiland
S_TRAMPOLINE = 0x112Cu, // incremental linking trampoline
S_SEPCODE = 0x1132u, // separated code (from the compiler)
S_SECTION = 0x1136u, // a COFF section in an executable
S_COFFGROUP = 0x1137u, // original COFF group before it was merged into executable sections by the linker, e.g. .CRT$XCU, .rdata, .bss, .lpp_prepatch_hooks
S_CALLSITEINFO = 0x1139u, // Indirect call site information
S_FRAMECOOKIE = 0x113Au, // Security cookie information
S_COMPILE3 = 0x113Cu, // replacement for S_COMPILE2, more info
S_ENVBLOCK = 0x113Du, // environment block split off from S_COMPILE2
S_LOCAL = 0x113Eu, // defines a local symbol in optimized code
S_DEFRANGE_REGISTER = 0x1141u, // ranges for en-registered symbol
S_DEFRANGE_FRAMEPOINTER_REL = 0x1142u, // range for stack symbol.
S_DEFRANGE_SUBFIELD_REGISTER = 0x1143u, // ranges for en-registered field of symbol
S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE = 0x1144u, // range for stack symbol span valid full scope of function body, gap might apply.
S_DEFRANGE_REGISTER_REL = 0x1145u, // range for symbol address as register + offset.
S_LPROC32_ID = 0x1146u, // S_PROC symbol that references ID instead of type
S_GPROC32_ID = 0x1147u, // S_PROC symbol that references ID instead of type
S_BUILDINFO = 0x114Cu, // build info/environment details of a compiland/translation unit
S_INLINESITE = 0x114Du, // inlined function callsite
S_INLINESITE_END = 0x114Eu,
S_PROC_ID_END = 0x114Fu,
S_FILESTATIC = 0x1153u,
S_LPROC32_DPC = 0x1155u,
S_LPROC32_DPC_ID = 0x1156u,
S_ARMSWITCHTABLE = 0x1159u,
S_CALLEES = 0x115Au,
S_CALLERS = 0x115Bu,
S_INLINESITE2 = 0x115Du, // extended inline site information
S_HEAPALLOCSITE = 0x115Eu, // heap allocation site
S_INLINEES = 0x1168u, // https://llvm.org/docs/PDB/CodeViewSymbols.html#s-inlinees-0x1168
S_REGREL32_INDIR = 0x1171u,
S_REGREL32_ENCTMP = 0x1179u,
S_UDT = 0x1108u, // user-defined type
S_UDT_ST = 0x1003u, // user-defined structured types
};
// https://docs.microsoft.com/en-us/visualstudio/debugger/debug-interface-access/thunk-ordinal
enum class PDB_NO_DISCARD ThunkOrdinal : uint8_t
{
NoType,
ThisAdjustor,
VirtualCall,
PCode,
DelayLoad,
TrampolineIncremental,
TrampolineBranchIsland
};
enum class PDB_NO_DISCARD TrampolineType : uint16_t
{
Incremental,
BranchIsland
};
enum class PDB_NO_DISCARD CookieType : uint8_t
{
COPY = 0,
XOR_SP,
XOR_BP,
XOR_R13,
};
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvconst.h#L392
enum class PDB_NO_DISCARD Register : uint16_t
{
EAX = 17,
ECX = 18,
EDX = 19,
EBX = 20,
ESP = 21,
EBP = 22,
ESI = 23,
EDI = 24,
RAX = 328,
RBX = 329,
RCX = 330,
RDX = 331,
RSI = 332,
RDI = 333,
RBP = 334,
RSP = 335,
R8 = 336,
R9 = 337,
R10 = 338,
R11 = 339,
R12 = 340,
R13 = 341,
R14 = 342,
R15 = 343,
RIP = 33, // also EIP for x32
EFLAGS = 34, // same for x64 and x32
};
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L3038
enum class PDB_NO_DISCARD ProcedureFlags : uint8_t
{
None = 0u,
NoFPO = 1u << 0u,
InterruptReturn = 1u << 1u,
FarReturn = 1u << 2u,
NoReturn = 1u << 3u,
Unreachable = 1u << 4u,
CustomCallingConvention = 1u << 5u,
NoInline = 1u << 6u,
OptimizedDebugInformation = 1u << 7u
};
PDB_DEFINE_BIT_OPERATORS(ProcedureFlags);
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L3676
enum class PDB_NO_DISCARD PublicSymbolFlags : uint32_t
{
None = 0u,
Code = 1u << 0u, // set if public symbol refers to a code address
Function = 1u << 1u, // set if public symbol is a function
ManagedCode = 1u << 2u, // set if managed code (native or IL)
ManagedILCode = 1u << 3u // set if managed IL code
};
PDB_DEFINE_BIT_OPERATORS(PublicSymbolFlags);
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L3341
enum class PDB_NO_DISCARD CompileSymbolFlags : uint32_t
{
None = 0u,
SourceLanguageMask = 0xFFu,
EC = 1u << 8u,
NoDebugInfo = 1u << 9u,
LTCG = 1u << 10u,
NoDataAlign = 1u << 11u,
ManagedCodeOrDataPresent = 1u << 12u,
SecurityChecks = 1u << 13u,
HotPatch = 1u << 14u,
CVTCIL = 1u << 15u,
MSILModule = 1u << 16u,
SDL = 1u << 17u,
PGO = 1u << 18u,
Exp = 1u << 19u
};
PDB_DEFINE_BIT_OPERATORS(CompileSymbolFlags);
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvconst.h#L324
enum class PDB_NO_DISCARD CPUType : uint16_t
{
Intel8080 = 0x0,
Intel8086 = 0x1,
Intel80286 = 0x2,
Intel80386 = 0x3,
Intel80486 = 0x4,
Pentium = 0x5,
PentiumII = 0x6,
PentiumPro = PentiumII,
PentiumIII = 0x7,
MIPS = 0x10,
MIPSR4000 = MIPS,
MIPS16 = 0x11,
MIPS32 = 0x12,
MIPS64 = 0x13,
MIPSI = 0x14,
MIPSII = 0x15,
MIPSIII = 0x16,
MIPSIV = 0x17,
MIPSV = 0x18,
M68000 = 0x20,
M68010 = 0x21,
M68020 = 0x22,
M68030 = 0x23,
M68040 = 0x24,
Alpha = 0x30,
Alpha21164 = 0x31,
Alpha21164A = 0x32,
Alpha21264 = 0x33,
Alpha21364 = 0x34,
PPC601 = 0x40,
PPC603 = 0x41,
PPC604 = 0x42,
PPC620 = 0x43,
PPCFP = 0x44,
PPCBE = 0x45,
SH3 = 0x50,
SH3E = 0x51,
SH3DSP = 0x52,
SH4 = 0x53,
SHMedia = 0x54,
ARM3 = 0x60,
ARM4 = 0x61,
ARM4T = 0x62,
ARM5 = 0x63,
ARM5T = 0x64,
ARM6 = 0x65,
ARM_XMAC = 0x66,
ARM_WMMX = 0x67,
ARM7 = 0x68,
Omni = 0x70,
IA64 = 0x80,
IA64_1 = 0x80,
IA64_2 = 0x81,
CEE = 0x90,
AM33 = 0xA0,
M32R = 0xB0,
TriCore = 0xC0,
X64 = 0xD0,
AMD64 = X64,
EBC = 0xE0,
Thumb = 0xF0,
ARMNT = 0xF4,
ARM64 = 0xF6,
HybridX86ARM64 = 0xF7,
ARM64EC = 0xF8,
ARM64X = 0xF9,
D3D11_Shader = 0x100
};
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L3100
// represents an address range, used for optimized code debug info
struct LocalVariableAddressRange // defines a range of addresses
{
uint32_t offsetStart;
uint16_t isectionStart;
uint16_t length;
};
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L3108
// Represents the holes in overall address range, all address is pre-bbt.
// it is for compress and reduce the amount of relocations need.
struct LocalVariableAddressGap
{
uint16_t offset; // relative offset from the beginning of the live range.
uint16_t length; // length of this gap.
};
// https://github.com/microsoft/microsoft-pdb/blob/0fe89a942f9a0f8e061213313e438884f4c9b876/include/cvinfo.h#L4366
// https://github.com/microsoft/microsoft-pdb/blob/0fe89a942f9a0f8e061213313e438884f4c9b876/cvdump/dumpsym7.cpp#L5518
enum class ARMSwitchType : uint16_t
{
INT1 = 0, // signed byte
UINT1 = 1, // unsigned byte
INT2 = 2, // signed two byte
UINT2 = 3, // unsigned two byte
INT4 = 4, // signed four byte
UINT4 = 5, // unsigned four byte
POINTER = 6,
UINT1SHL1 = 7, // unsigned byte scaled by two
UINT2SHL1 = 8, // unsigned two byte scaled by two
INT1SHL1 = 9, // signed byte scaled by two
INT2SHL1 = 10, // signed two byte scaled by two
TBB = UINT1SHL1,
TBH = UINT2SHL1,
};
// https://llvm.org/docs/PDB/CodeViewTypes.html#leaf-types
struct RecordHeader
{
uint16_t size; // record length, not including this 2-byte field
SymbolRecordKind kind; // record kind
};
// all CodeView records are stored as a header, followed by variable-length data.
// internal Record structs such as S_PUB32, S_GDATA32, etc. correspond to the data layout of a CodeView record of that kind.
struct Record
{
RecordHeader header;
union Data
{
#pragma pack(push, 1)
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L4069
struct
{
uint32_t cbFrame; // count of bytes of total frame of procedure
uint32_t cbPad; // count of bytes of padding in the frame
uint32_t offPad; // offset (relative to frame poniter) to where
// padding starts
uint32_t cbSaveRegs; // count of bytes of callee save registers
uint32_t offExHdlr; // offset of exception handler
uint16_t sectExHdlr; // section id of exception handler
struct {
uint32_t fHasAlloca : 1; // function uses _alloca()
uint32_t fHasSetJmp : 1; // function uses setjmp()
uint32_t fHasLongJmp : 1; // function uses longjmp()
uint32_t fHasInlAsm : 1; // function uses inline asm
uint32_t fHasEH : 1; // function has EH states
uint32_t fInlSpec : 1; // function was speced as inline
uint32_t fHasSEH : 1; // function has SEH
uint32_t fNaked : 1; // function is __declspec(naked)
uint32_t fSecurityChecks : 1; // function has buffer security check introduced by /GS.
uint32_t fAsyncEH : 1; // function compiled with /EHa
uint32_t fGSNoStackOrdering : 1; // function has /GS buffer checks, but stack ordering couldn't be done
uint32_t fWasInlined : 1; // function was inlined within another function
uint32_t fGSCheck : 1; // function is __declspec(strict_gs_check)
uint32_t fSafeBuffers : 1; // function is __declspec(safebuffers)
uint32_t encodedLocalBasePointer : 2; // record function's local pointer explicitly.
uint32_t encodedParamBasePointer : 2; // record function's parameter pointer explicitly.
uint32_t fPogoOn : 1; // function was compiled with PGO/PGU
uint32_t fValidCounts : 1; // Do we have valid Pogo counts?
uint32_t fOptSpeed : 1; // Did we optimize for speed?
uint32_t fGuardCF : 1; // function contains CFG checks (and no write checks)
uint32_t fGuardCFW : 1; // function contains CFW checks and/or instrumentation
uint32_t pad : 9; // must be zero
} flags;
} S_FRAMEPROC;
struct
{
uint32_t offset;
uint16_t section;
uint16_t annotationsCount; // number of zero-terminated annotation strings
PDB_FLEXIBLE_ARRAY_MEMBER(char, annotations); // sequence of zero-terminated annotation strings
} S_ANNOTATIONSYM;
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L3696
struct
{
PublicSymbolFlags flags;
uint32_t offset;
uint16_t section;
PDB_FLEXIBLE_ARRAY_MEMBER(char, name);
} S_PUB32;
struct
{
uint32_t typeIndex;
uint32_t offset;
uint16_t section;
PDB_FLEXIBLE_ARRAY_MEMBER(char, name);
} S_GDATA32, S_GTHREAD32, S_LDATA32, S_LTHREAD32;
struct
{
PDB_FLEXIBLE_ARRAY_MEMBER(char, name);
} S_UNAMESPACE;
struct
{
uint32_t signature;
PDB_FLEXIBLE_ARRAY_MEMBER(char, name);
} S_OBJNAME;
struct
{
TrampolineType type;
uint16_t size;
uint32_t thunkOffset;
uint32_t targetOffset;
uint16_t thunkSection;
uint16_t targetSection;
} S_TRAMPOLINE;
struct
{
uint16_t sectionNumber;
uint8_t alignment;
uint32_t rva;
uint32_t length;
uint32_t characteristics;
PDB_FLEXIBLE_ARRAY_MEMBER(char, name);
} S_SECTION;
struct
{
uint32_t size;
uint32_t characteristics;
uint32_t offset;
uint16_t section;
PDB_FLEXIBLE_ARRAY_MEMBER(char, name);
} S_COFFGROUP;
struct
{
uint32_t offset ; // offset of call site
uint16_t section; // section index of call site
uint16_t padding; // alignment padding field, must be zero
uint32_t typeIndex; // type index describing function signature
} S_CALLSITEINFO;
struct
{
uint32_t offset; // Frame relative offset
uint16_t reg; // Register index
CookieType cookietype; // Type of the cookie
uint8_t flags; // Flags describing this cookie
} S_FRAMECOOKIE;
struct
{
uint32_t parent;
uint32_t end;
uint32_t next;
uint32_t offset;
uint16_t section;
uint16_t length;
ThunkOrdinal thunk;
PDB_FLEXIBLE_ARRAY_MEMBER(char, name);
} S_THUNK32;
struct
{
uint32_t parent;
uint32_t end;
uint32_t next;
uint32_t codeSize;
uint32_t debugStart;
uint32_t debugEnd;
uint32_t typeIndex;
uint32_t offset;
uint16_t section;
ProcedureFlags flags;
PDB_FLEXIBLE_ARRAY_MEMBER(char, name);
} S_LPROC32, S_GPROC32, S_LPROC32_ID, S_GPROC32_ID, S_LPROC32_DPC, S_LPROC32_DPC_ID;
struct
{
uint32_t offset;
uint32_t typeIndex;
PDB_FLEXIBLE_ARRAY_MEMBER(char, name);
} S_BPRELSYM32;
struct
{
uint32_t offset;
uint32_t typeIndex;
Register reg;
PDB_FLEXIBLE_ARRAY_MEMBER(char, name);
} S_REGREL32, S_REGREL32_ENCTMP;
struct
{
uint32_t typeIndex;
Register reg;
PDB_FLEXIBLE_ARRAY_MEMBER(char, name);
} S_REGSYM;
struct
{
uint32_t parent;
uint32_t end;
uint32_t codeSize;
uint32_t offset;
uint16_t section;
PDB_FLEXIBLE_ARRAY_MEMBER(char, name);
} S_BLOCK32;
struct
{
uint32_t offset;
uint16_t section;
ProcedureFlags flags;
PDB_FLEXIBLE_ARRAY_MEMBER(char, name);
} S_LABEL32;
struct
{
uint32_t typeIndex;
uint16_t value;
PDB_FLEXIBLE_ARRAY_MEMBER(char, name);
} S_CONSTANT;
struct
{
uint32_t typeIndex; // refers to a type index in the IPI stream
} S_BUILDINFO;
struct
{
uint32_t parent; // pointer to the inliner
uint32_t end; // pointer to this block's end
uint32_t inlinee; // CV_ItemId of inlinee
PDB_FLEXIBLE_ARRAY_MEMBER(uint8_t, binaryAnnotations);
} S_INLINESITE;
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L4199
struct
{
uint32_t typeIndex; // type index
uint32_t moduleFilenameOffset; // index of mod filename in stringtable
struct
{
uint16_t fIsParam : 1; // variable is a parameter
uint16_t fAddrTaken : 1; // address is taken
uint16_t fCompGenx : 1; // variable is compiler generated
uint16_t fIsAggregate : 1; // the symbol is splitted in temporaries,
// which are treated by compiler as
// independent entities
uint16_t fIsAggregated : 1; // Counterpart of fIsAggregate - tells
// that it is a part of a fIsAggregate symbol
uint16_t fIsAliased : 1; // variable has multiple simultaneous lifetimes
uint16_t fIsAlias : 1; // represents one of the multiple simultaneous lifetimes
uint16_t fIsRetValue : 1; // represents a function return value
uint16_t fIsOptimizedOut : 1; // variable has no lifetimes
uint16_t fIsEnregGlob : 1; // variable is an enregistered global
uint16_t fIsEnregStat : 1; // variable is an enregistered static
uint16_t unused : 5; // must be zero
} flags;
PDB_FLEXIBLE_ARRAY_MEMBER(char, name);
} S_FILESTATIC;
struct
{
CompileSymbolFlags flags;
CPUType machine;
uint16_t versionFrontendMajor;
uint16_t versionFrontendMinor;
uint16_t versionFrontendBuild;
uint16_t versionFrontendQFE;
uint16_t versionBackendMajor;
uint16_t versionBackendMinor;
uint16_t versionBackendBuild;
uint16_t versionBackendQFE;
PDB_FLEXIBLE_ARRAY_MEMBER(char, version);
} S_COMPILE3;
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L3372
struct
{
uint8_t flags;
PDB_FLEXIBLE_ARRAY_MEMBER(char, strings);
} S_ENVBLOCK;
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L4190
struct
{
uint32_t typeIndex;
struct
{
uint16_t fIsParam : 1; // variable is a parameter
uint16_t fAddrTaken : 1; // address is taken
uint16_t fCompGenx : 1; // variable is compiler generated
uint16_t fIsAggregate : 1; // the symbol is splitted in temporaries,
// which are treated by compiler as
// independent entities
uint16_t fIsAggregated : 1; // Counterpart of fIsAggregate - tells
// that it is a part of a fIsAggregate symbol
uint16_t fIsAliased : 1; // variable has multiple simultaneous lifetimes
uint16_t fIsAlias : 1; // represents one of the multiple simultaneous lifetimes
uint16_t fIsRetValue : 1; // represents a function return value
uint16_t fIsOptimizedOut : 1; // variable has no lifetimes
uint16_t fIsEnregGlob : 1; // variable is an enregistered global
uint16_t fIsEnregStat : 1; // variable is an enregistered static
uint16_t unused : 5; // must be zero
} flags;
PDB_FLEXIBLE_ARRAY_MEMBER(char, name);
} S_LOCAL;
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L4236
struct
{
uint16_t reg; // Register to hold the value of the symbol
struct
{
uint16_t maybe : 1; // May have no user name on one of control flow path.
uint16_t padding : 15; // Padding for future use.
} attribute; // Attribute of the register range.
LocalVariableAddressRange range; // Range of addresses where this program is valid
PDB_FLEXIBLE_ARRAY_MEMBER(LocalVariableAddressGap, gaps); // The value is not available in following gaps.
} S_DEFRANGE_REGISTER;
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L4245
struct
{
uint32_t offsetFramePointer;
LocalVariableAddressRange range; // Range of addresses where this program is valid
PDB_FLEXIBLE_ARRAY_MEMBER(LocalVariableAddressGap, gaps); // The value is not available in following gaps.
} S_DEFRANGE_FRAMEPOINTER_REL;
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L4265
struct
{
uint16_t reg; // Register to hold the value of the symbol
struct
{
uint16_t maybe : 1; // May have no user name on one of control flow path.
uint16_t padding : 15; // Padding for future use.
} attribute; // Attribute of the register range.
uint32_t offsetParent : 12; // Offset in parent variable.
uint32_t padding : 20; // Padding for future use.
LocalVariableAddressRange range; // Range of addresses where this program is valid
PDB_FLEXIBLE_ARRAY_MEMBER(LocalVariableAddressGap, gaps); // The value is not available in following gaps.
} S_DEFRANGE_SUBFIELD_REGISTER;
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L4255
struct
{
uint32_t offsetFramePointer; // offset to frame pointer
} S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE;
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L4279
struct
{
uint16_t baseRegister; // Register to hold the base pointer of the symbol
uint16_t spilledUDTMember : 1; // Spilled member for s.i.
uint16_t padding : 3; // Padding for future use.
uint16_t offsetParent : 12; // Offset in parent variable.
uint32_t offsetBasePointer; // offset to register
LocalVariableAddressRange range; // Range of addresses where this program is valid
PDB_FLEXIBLE_ARRAY_MEMBER(LocalVariableAddressGap, gaps); // The value is not available in following gaps.
} S_DEFRANGE_REGISTER_REL;
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L4500
struct
{
uint32_t offset; // offset of call site
uint16_t section; // section index of call site
uint16_t instructionLength; // length of heap allocation call instruction
uint32_t typeIndex; // type index describing function signature
} S_HEAPALLOCSITE;
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L4402
struct
{
uint32_t offsetBase; // Section-relative offset to the base for switch offsets
uint16_t sectionBase; // Section index of the base for switch offsets
ARMSwitchType switchType; // type of each entry
uint32_t offsetBranch; // Section-relative offset to the table branch instruction
uint32_t offsetTable; // Section-relative offset to the start of the table
uint16_t sectionBranch; // Section index of the table branch instruction
uint16_t sectionTable; // Section index of the table
uint32_t numEntries; // number of switch table entries
} S_ARMSWITCHTABLE;
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L4382
struct
{
uint32_t count; // Number of functions
PDB_FLEXIBLE_ARRAY_MEMBER(uint32_t, funcs); // List of functions, dim == count
// uint32_t invocations[CV_ZEROLEN]; Followed by a parallel array of
// invocation counts. Counts > reclen are assumed to be zero
} S_CALLERS, S_CALLEES, S_INLINEES;
struct
{
uint32_t typeIndex;
PDB_FLEXIBLE_ARRAY_MEMBER(char, name);
} S_UDT, S_UDT_ST;
struct
{
uint32_t unknown1;
uint32_t typeIndex;
uint32_t unknown2;
Register reg;
PDB_FLEXIBLE_ARRAY_MEMBER(char, name);
} S_REGREL32_INDIR;
#pragma pack(pop)
} data;
};
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L4576
enum class PDB_NO_DISCARD DebugSubsectionKind : uint32_t
{
S_IGNORE = 0x80000000, // if this bit is set in a subsection type then ignore the subsection contents
S_SYMBOLS = 0xF1,
S_LINES = 0xF2,
S_STRINGTABLE = 0xF3,
S_FILECHECKSUMS = 0xF4,
S_FRAMEDATA = 0xF5,
S_INLINEELINES = 0xF6,
S_CROSSSCOPEIMPORTS = 0xF7,
S_CROSSSCOPEEXPORTS = 0xF8,
S_IL_LINES = 0xF9,
S_FUNC_MDTOKEN_MAP = 0xFA,
S_TYPE_MDTOKEN_MAP = 0xFB,
S_MERGED_ASSEMBLYINPUT = 0xFC,
S_COFF_SYMBOL_RVA = 0xFD,
};
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L4596
struct DebugSubsectionHeader
{
DebugSubsectionKind kind;
uint32_t size;
};
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L4617
struct Line
{
uint32_t offset; // Offset to start of code bytes for line number
uint32_t linenumStart : 24; // line where statement/expression starts
uint32_t deltaLineEnd : 7; // delta to line where statement ends (optional)
uint32_t fStatement : 1; // true if a statement linenumber, else an expression line num
};
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L4630
struct Column
{
uint16_t start;
uint16_t end;
};
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L4601
struct LinesHeader
{
uint32_t sectionOffset;
uint16_t sectionIndex;
struct
{
uint16_t fHasColumns : 1;
uint16_t pad : 15;
} flags;
uint32_t codeSize;
};
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L4608
struct LinesFileBlockHeader
{
uint32_t fileChecksumOffset;
uint32_t numLines;
uint32_t size;
// Line lines[numLines];
// Column columns[numLines]; Might not be present
};
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvconst.h#L88
enum class PDB_NO_DISCARD ChecksumKind : uint8_t
{
None = 0,
MD5 = 1,
SHA1 = 2,
SHA256 = 3,
};
// https://github.com/microsoft/microsoft-pdb/blob/master/cvdump/dumpsym7.cpp#L1097
struct FileChecksumHeader
{
uint32_t filenameOffset;
uint8_t checksumSize;
ChecksumKind checksumKind;
PDB_FLEXIBLE_ARRAY_MEMBER(uint8_t, checksum);
};
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L4822
enum class InlineeSourceLineKind : uint32_t
{
Signature = 0,
SignatureEx = 1,
};
struct InlineeSourceLineHeader
{
InlineeSourceLineKind kind;
};
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L4825
struct InlineeSourceLine
{
uint32_t inlinee;
uint32_t fileChecksumOffset;
uint32_t lineNumber;
};
struct InlineeSourceLineEx
{
uint32_t inlinee;
uint32_t fileChecksumOffset;
uint32_t lineNumber;
uint32_t extraLines;
PDB_FLEXIBLE_ARRAY_MEMBER(uint32_t, extrafileChecksumOffsets);
};
// Combine DebugSubsectionHeader and first subsection header into one struct.
struct LineSection
{
DebugSubsectionHeader header;
union
{
LinesHeader linesHeader;
FileChecksumHeader checksumHeader;
InlineeSourceLineHeader inlineeHeader;
};
};
}
}
}

View File

@@ -1,115 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "PDB_PCH.h"
#include "PDB_DirectMSFStream.h"
#include "Foundation/PDB_PointerUtil.h"
#include "Foundation/PDB_BitUtil.h"
#include "Foundation/PDB_Assert.h"
#include "Foundation/PDB_CRT.h"
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::DirectMSFStream::DirectMSFStream(void) PDB_NO_EXCEPT
: m_data(nullptr)
, m_blockIndices(nullptr)
, m_blockSize(0u)
, m_size(0u)
, m_blockSizeLog2(0u)
{
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::DirectMSFStream::DirectMSFStream(const void* data, uint32_t blockSize, const uint32_t* blockIndices, uint32_t streamSize) PDB_NO_EXCEPT
: m_data(data)
, m_blockIndices(blockIndices)
, m_blockSize(blockSize)
, m_size(streamSize)
, m_blockSizeLog2(BitUtil::FindFirstSetBit(blockSize))
{
PDB_ASSERT(BitUtil::IsPowerOfTwo(blockSize), "MSF block size must be a power of two.");
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void PDB::DirectMSFStream::ReadAtOffset(void* destination, size_t size, size_t offset) const PDB_NO_EXCEPT
{
PDB_ASSERT(destination != nullptr, "Destination buffer not set");
PDB_ASSERT(offset + size <= m_size, "Not enough data left to read.");
// work out which block and offset within the block the read offset corresponds to
size_t blockIndex = offset >> m_blockSizeLog2;
const size_t offsetWithinBlock = offset & (m_blockSize - 1u);
// work out the offset within the data based on the block indices
size_t offsetWithinData = (static_cast<size_t>(m_blockIndices[blockIndex]) << m_blockSizeLog2) + offsetWithinBlock;
const size_t bytesLeftInBlock = m_blockSize - offsetWithinBlock;
if (bytesLeftInBlock >= size)
{
// fast path, all the data can be read in one go
const void* const sourceData = Pointer::Offset<const void*>(m_data, offsetWithinData);
memcpy(destination, sourceData, size);
}
else
{
// slower path, data is scattered across several blocks.
// read remaining bytes in current block first.
{
const void* const sourceData = Pointer::Offset<const void*>(m_data, offsetWithinData);
memcpy(destination, sourceData, bytesLeftInBlock);
}
// read remaining bytes from blocks
size_t bytesLeftToRead = size - bytesLeftInBlock;
while (bytesLeftToRead != 0u)
{
// advance to the next block
++blockIndex;
offsetWithinData = static_cast<size_t>(m_blockIndices[blockIndex]) << m_blockSizeLog2;
void* const destinationData = Pointer::Offset<void*>(destination, size - bytesLeftToRead);
const void* const sourceData = Pointer::Offset<const void*>(m_data, offsetWithinData);
if (bytesLeftToRead > m_blockSize)
{
// copy a whole block at once
memcpy(destinationData, sourceData, m_blockSize);
bytesLeftToRead -= m_blockSize;
}
else
{
// copy remaining bytes
memcpy(destinationData, sourceData, bytesLeftToRead);
bytesLeftToRead -= bytesLeftToRead;
}
}
}
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD PDB::DirectMSFStream::IndexAndOffset PDB::DirectMSFStream::GetBlockIndexForOffset(uint32_t offset) const PDB_NO_EXCEPT
{
// work out which block and offset within the block the offset corresponds to
const uint32_t blockIndex = offset >> m_blockSizeLog2;
const uint32_t offsetWithinBlock = offset & (m_blockSize - 1u);
return IndexAndOffset { blockIndex, offsetWithinBlock };
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD size_t PDB::DirectMSFStream::GetDataOffsetForIndexAndOffset(const IndexAndOffset& indexAndOffset) const PDB_NO_EXCEPT
{
// work out the offset within the data based on the block indices
const size_t offsetWithinData = (static_cast<size_t>(m_blockIndices[indexAndOffset.index]) << m_blockSizeLog2) + indexAndOffset.offsetWithinBlock;
return offsetWithinData;
}

View File

@@ -1,84 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "Foundation/PDB_Macros.h"
// https://llvm.org/docs/PDB/index.html#the-msf-container
// https://llvm.org/docs/PDB/MsfFile.html
namespace PDB
{
// provides direct access to the data of an MSF stream.
// inherently thread-safe, the stream doesn't carry any internal offset or similar.
// trivial to construct.
// slower individual reads, but pays off when not all data of a stream is needed.
class PDB_NO_DISCARD DirectMSFStream
{
public:
DirectMSFStream(void) PDB_NO_EXCEPT;
explicit DirectMSFStream(const void* data, uint32_t blockSize, const uint32_t* blockIndices, uint32_t streamSize) PDB_NO_EXCEPT;
PDB_DEFAULT_MOVE(DirectMSFStream);
// Reads a number of bytes from the stream.
void ReadAtOffset(void* destination, size_t size, size_t offset) const PDB_NO_EXCEPT;
// Reads from the stream.
template <typename T>
PDB_NO_DISCARD inline T ReadAtOffset(size_t offset) const PDB_NO_EXCEPT
{
T data;
ReadAtOffset(&data, sizeof(T), offset);
return data;
}
// Returns the block size of the stream.
PDB_NO_DISCARD inline uint32_t GetBlockSize(void) const PDB_NO_EXCEPT
{
return m_blockSize;
}
// Returns the size of the stream.
PDB_NO_DISCARD inline uint32_t GetSize(void) const PDB_NO_EXCEPT
{
return m_size;
}
private:
friend class CoalescedMSFStream;
struct IndexAndOffset
{
uint32_t index;
uint32_t offsetWithinBlock;
};
// Returns the block index and offset within the block that correspond to the given offset.
PDB_NO_DISCARD IndexAndOffset GetBlockIndexForOffset(uint32_t offset) const PDB_NO_EXCEPT;
// Returns the offset into the data that corresponds to the given indices and offset within a block.
PDB_NO_DISCARD size_t GetDataOffsetForIndexAndOffset(const IndexAndOffset& indexAndOffset) const PDB_NO_EXCEPT;
// Provides read-only access to the memory-mapped data.
PDB_NO_DISCARD inline const void* GetData(void) const PDB_NO_EXCEPT
{
return m_data;
}
// Provides read-only access to the block indices.
PDB_NO_DISCARD inline const uint32_t* GetBlockIndices(void) const PDB_NO_EXCEPT
{
return m_blockIndices;
}
const void* m_data;
const uint32_t* m_blockIndices;
uint32_t m_blockSize;
uint32_t m_size;
uint32_t m_blockSizeLog2;
PDB_DISABLE_COPY(DirectMSFStream);
};
}

View File

@@ -1,26 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "Foundation/PDB_Macros.h"
namespace PDB
{
enum class PDB_NO_DISCARD ErrorCode : unsigned int
{
Success = 0u,
// main PDB validation
InvalidDataSize,
InvalidSuperBlock,
InvalidFreeBlockMap,
// stream validation
InvalidStream,
InvalidSignature,
InvalidStreamIndex,
UnknownVersion
};
}

View File

@@ -1,43 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "PDB_PCH.h"
#include "PDB_GlobalSymbolStream.h"
#include "PDB_RawFile.h"
#include "PDB_Types.h"
#include "PDB_DBITypes.h"
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::GlobalSymbolStream::GlobalSymbolStream(void) PDB_NO_EXCEPT
: m_stream()
, m_hashRecords(nullptr)
, m_count(0u)
{
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::GlobalSymbolStream::GlobalSymbolStream(const RawFile& file, uint16_t streamIndex, uint32_t count) PDB_NO_EXCEPT
: m_stream(file.CreateMSFStream<CoalescedMSFStream>(streamIndex))
, m_hashRecords(m_stream.GetDataAtOffset<HashRecord>(sizeof(HashTableHeader)))
, m_count(count)
{
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD const PDB::CodeView::DBI::Record* PDB::GlobalSymbolStream::GetRecord(const CoalescedMSFStream& symbolRecordStream, const HashRecord& hashRecord) const PDB_NO_EXCEPT
{
// hash record offsets start at 1, not at 0
const uint32_t headerOffset = hashRecord.offset - 1u;
// the offset doesn't point to the global symbol directly, but to the CodeView record:
// https://llvm.org/docs/PDB/CodeViewSymbols.html
const CodeView::DBI::Record* record = symbolRecordStream.GetDataAtOffset<const CodeView::DBI::Record>(headerOffset);
return record;
}

View File

@@ -1,49 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "Foundation/PDB_Macros.h"
#include "Foundation/PDB_ArrayView.h"
#include "PDB_CoalescedMSFStream.h"
namespace PDB
{
class RawFile;
struct HashRecord;
namespace CodeView
{
namespace DBI
{
struct Record;
}
}
class PDB_NO_DISCARD GlobalSymbolStream
{
public:
GlobalSymbolStream(void) PDB_NO_EXCEPT;
explicit GlobalSymbolStream(const RawFile& file, uint16_t streamIndex, uint32_t count) PDB_NO_EXCEPT;
PDB_DEFAULT_MOVE(GlobalSymbolStream);
// Turns a given hash record into a DBI record using the given symbol stream.
PDB_NO_DISCARD const CodeView::DBI::Record* GetRecord(const CoalescedMSFStream& symbolRecordStream, const HashRecord& hashRecord) const PDB_NO_EXCEPT;
// Returns a view of all the records in the stream.
PDB_NO_DISCARD inline ArrayView<HashRecord> GetRecords(void) const PDB_NO_EXCEPT
{
return ArrayView<HashRecord>(m_hashRecords, m_count);
}
private:
CoalescedMSFStream m_stream;
const HashRecord* m_hashRecords;
uint32_t m_count;
PDB_DISABLE_COPY(GlobalSymbolStream);
};
}

View File

@@ -1,140 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "PDB_PCH.h"
#include "PDB_IPIStream.h"
#include "PDB_RawFile.h"
#include "PDB_Util.h"
#include "PDB_DirectMSFStream.h"
#include "PDB_InfoStream.h"
#include "Foundation/PDB_Memory.h"
namespace
{
// the IPI stream always resides at index 4
static constexpr const uint32_t IPIStreamIndex = 4u;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::IPIStream::IPIStream(void) PDB_NO_EXCEPT
: m_header()
, m_stream()
, m_records(nullptr)
, m_recordCount(0u)
{
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::IPIStream::IPIStream(IPIStream&& other) PDB_NO_EXCEPT
: m_header(PDB_MOVE(other.m_header))
, m_stream(PDB_MOVE(other.m_stream))
, m_records(PDB_MOVE(other.m_records))
, m_recordCount(PDB_MOVE(other.m_recordCount))
{
other.m_records = nullptr;
other.m_recordCount = 0u;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::IPIStream& PDB::IPIStream::operator=(IPIStream&& other) PDB_NO_EXCEPT
{
if (this != &other)
{
PDB_DELETE_ARRAY(m_records);
m_header = PDB_MOVE(other.m_header);
m_stream = PDB_MOVE(other.m_stream);
m_records = PDB_MOVE(other.m_records);
m_recordCount = PDB_MOVE(other.m_recordCount);
other.m_records = nullptr;
other.m_recordCount = 0u;
}
return *this;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::IPIStream::IPIStream(const RawFile& file, const IPI::StreamHeader& header) PDB_NO_EXCEPT
: m_header(header)
, m_stream(file.CreateMSFStream<CoalescedMSFStream>(IPIStreamIndex))
, m_records(nullptr)
, m_recordCount(GetLastTypeIndex() - GetFirstTypeIndex())
{
// types in the IPI stream are accessed by their index from other streams.
// however, the index is not stored with types in the IPI stream directly, but has to be built while walking the stream.
// similarly, because types are variable-length records, there are no direct offsets to access individual types.
// we therefore walk the IPI stream once, and store pointers to the records for trivial O(N) array lookup by index later.
m_records = PDB_NEW_ARRAY(const CodeView::IPI::Record*, m_recordCount);
// ignore the stream's header
size_t offset = sizeof(IPI::StreamHeader);
// parse the CodeView records
uint32_t typeIndex = 0u;
while (offset < m_stream.GetSize())
{
// https://llvm.org/docs/PDB/CodeViewTypes.html
const CodeView::IPI::Record* record = m_stream.GetDataAtOffset<const CodeView::IPI::Record>(offset);
const uint32_t recordSize = GetCodeViewRecordSize(record);
m_records[typeIndex] = record;
// position the stream offset at the next record
offset += sizeof(CodeView::IPI::RecordHeader) + recordSize;
++typeIndex;
}
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::IPIStream::~IPIStream(void) PDB_NO_EXCEPT
{
PDB_DELETE_ARRAY(m_records);
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD PDB::ErrorCode PDB::HasValidIPIStream(const RawFile& file) PDB_NO_EXCEPT
{
const PDB::InfoStream infoStream(file);
if (!infoStream.HasIPIStream())
{
return ErrorCode::InvalidStream;
}
DirectMSFStream stream = file.CreateMSFStream<DirectMSFStream>(IPIStreamIndex);
if (stream.GetSize() < sizeof(IPI::StreamHeader))
{
return ErrorCode::InvalidStream;
}
const IPI::StreamHeader header = stream.ReadAtOffset<IPI::StreamHeader>(0u);
if (header.version != IPI::StreamHeader::Version::V80)
{
return ErrorCode::UnknownVersion;
}
return ErrorCode::Success;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD PDB::IPIStream PDB::CreateIPIStream(const RawFile& file) PDB_NO_EXCEPT
{
DirectMSFStream stream = file.CreateMSFStream<DirectMSFStream>(IPIStreamIndex);
const IPI::StreamHeader header = stream.ReadAtOffset<IPI::StreamHeader>(0u);
return IPIStream { file, header };
}

View File

@@ -1,66 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "Foundation/PDB_Macros.h"
#include "Foundation/PDB_ArrayView.h"
#include "PDB_ErrorCodes.h"
#include "PDB_IPITypes.h"
#include "PDB_CoalescedMSFStream.h"
// PDB IPI stream
// https://llvm.org/docs/PDB/TpiStream.html
namespace PDB
{
class RawFile;
class PDB_NO_DISCARD IPIStream
{
public:
IPIStream(void) PDB_NO_EXCEPT;
IPIStream(IPIStream&& other) PDB_NO_EXCEPT;
IPIStream& operator=(IPIStream&& other) PDB_NO_EXCEPT;
explicit IPIStream(const RawFile& file, const IPI::StreamHeader& header) PDB_NO_EXCEPT;
~IPIStream(void) PDB_NO_EXCEPT;
// Returns the index of the first type, which is not necessarily zero.
PDB_NO_DISCARD inline uint32_t GetFirstTypeIndex(void) const PDB_NO_EXCEPT
{
return m_header.typeIndexBegin;
}
// Returns the index of the last type.
PDB_NO_DISCARD inline uint32_t GetLastTypeIndex(void) const PDB_NO_EXCEPT
{
return m_header.typeIndexEnd;
}
// Returns a view of all type records.
// Records identified by a type index can be accessed via "allRecords[typeIndex - firstTypeIndex]".
PDB_NO_DISCARD inline ArrayView<const CodeView::IPI::Record*> GetTypeRecords(void) const PDB_NO_EXCEPT
{
return ArrayView<const CodeView::IPI::Record*>(m_records, m_recordCount);
}
private:
IPI::StreamHeader m_header;
CoalescedMSFStream m_stream;
const CodeView::IPI::Record** m_records;
size_t m_recordCount;
PDB_DISABLE_COPY(IPIStream);
};
// ------------------------------------------------------------------------------------------------
// General
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD ErrorCode HasValidIPIStream(const RawFile& file) PDB_NO_EXCEPT;
PDB_NO_DISCARD IPIStream CreateIPIStream(const RawFile& file) PDB_NO_EXCEPT;
}

View File

@@ -1,144 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "Foundation/PDB_Macros.h"
namespace PDB
{
namespace IPI
{
// https://llvm.org/docs/PDB/TpiStream.html#tpi-header
struct StreamHeader
{
enum class PDB_NO_DISCARD Version : uint32_t
{
V40 = 19950410u,
V41 = 19951122u,
V50 = 19961031u,
V70 = 19990903u,
V80 = 20040203u
};
Version version;
uint32_t headerSize;
uint32_t typeIndexBegin;
uint32_t typeIndexEnd;
uint32_t typeRecordBytes;
uint16_t hashStreamIndex;
uint16_t hashAuxStreamIndex;
uint32_t hashKeySize;
uint32_t hashBucketCount;
uint32_t hashValueBufferOffset;
uint32_t hashValueBufferLength;
uint32_t indexOffsetBufferOffset;
uint32_t indexOffsetBufferLength;
uint32_t hashAdjBufferOffset;
uint32_t hashAdjBufferLength;
};
}
namespace CodeView
{
namespace IPI
{
// code view type records that can appear in an IPI stream
// https://llvm.org/docs/PDB/CodeViewTypes.html
// https://llvm.org/docs/PDB/TpiStream.html#tpi-vs-ipi-stream
enum class PDB_NO_DISCARD TypeRecordKind : uint16_t
{
LF_FUNC_ID = 0x1601u, // global function ID
LF_MFUNC_ID = 0x1602u, // member function ID
LF_BUILDINFO = 0x1603u, // build information
LF_SUBSTR_LIST = 0x1604u, // similar to LF_ARGLIST for a list of substrings
LF_STRING_ID = 0x1605u, // string ID
LF_UDT_SRC_LINE = 0x1606u, // source and line on where an UDT (User Defined Type) is defined, generated by the compiler
LF_UDT_MOD_SRC_LINE = 0x1607u // module, source and line on where an UDT is defined, generated by the linker
};
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L1715
enum class PDB_NO_DISCARD BuildInfoType : uint8_t
{
CurrentDirectory, // compiler working directory
BuildTool, // tool path
SourceFile, // path to source file, relative or absolute
TypeServerPDB, // path to PDB file
CommandLine // command-line used to build the source file
};
struct RecordHeader
{
uint16_t size; // record length, not including this 2-byte field
TypeRecordKind kind; // record kind
};
// all CodeView records are stored as a header, followed by variable-length data.
// internal Record structs such as S_PUB32, S_GDATA32, etc. correspond to the data layout of a CodeView record of that kind.
struct Record
{
RecordHeader header;
union Data
{
#pragma pack(push, 1)
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L1680
struct
{
uint32_t scopeId; // parent scope of the ID, 0 if global
uint32_t typeIndex; // function type
PDB_FLEXIBLE_ARRAY_MEMBER(char, name);
} LF_FUNC_ID;
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L1687
struct
{
uint32_t parentTypeIndex; // parent scope of the ID, 0 if global
uint32_t typeIndex; // function type
PDB_FLEXIBLE_ARRAY_MEMBER(char, name);
} LF_MFUNC_ID;
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L1694
struct
{
uint32_t id; // ID to list of sub-string IDs
PDB_FLEXIBLE_ARRAY_MEMBER(char, name);
} LF_STRING_ID;
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L1700
struct
{
uint32_t typeIndex; // UDT's type index
uint32_t stringIndex; // index to LF_STRING_ID record where source file name is saved
uint32_t line; // line number
} LF_UDT_SRC_LINE;
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L1707
struct
{
uint32_t typeIndex; // UDT's type index
uint32_t stringIndex; // index into '/names' string table where source file name is saved
uint32_t line; // line number
uint16_t moduleIndex; // module that contributes this UDT definition
} LF_UDT_MOD_SRC_LINE;
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L2043
struct
{
uint32_t count;
PDB_FLEXIBLE_ARRAY_MEMBER(uint32_t, typeIndices);
} LF_SUBSTR_LIST;
// https://github.com/microsoft/microsoft-pdb/blob/master/include/cvinfo.h#L1726
struct
{
uint16_t count;
PDB_FLEXIBLE_ARRAY_MEMBER(uint32_t, typeIndices);
} LF_BUILDINFO;
#pragma pack(pop)
} data;
};
}
}
}

View File

@@ -1,47 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "PDB_PCH.h"
#include "PDB_ImageSectionStream.h"
#include "PDB_RawFile.h"
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::ImageSectionStream::ImageSectionStream(void) PDB_NO_EXCEPT
: m_stream()
, m_headers(nullptr)
, m_count(0u)
{
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::ImageSectionStream::ImageSectionStream(const RawFile& file, uint16_t streamIndex) PDB_NO_EXCEPT
: m_stream(file.CreateMSFStream<CoalescedMSFStream>(streamIndex))
, m_headers(m_stream.GetDataAtOffset<IMAGE_SECTION_HEADER>(0u))
, m_count(m_stream.GetSize() / sizeof(IMAGE_SECTION_HEADER))
{
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD uint32_t PDB::ImageSectionStream::ConvertSectionOffsetToRVA(uint16_t oneBasedSectionIndex, uint32_t offsetInSection) const PDB_NO_EXCEPT
{
if (oneBasedSectionIndex == 0u)
{
// should never happen, but prevent underflow
return 0u;
}
else if (oneBasedSectionIndex > m_count)
{
// this symbol is "contained" in a section that is neither part of the PDB, nor the EXE.
// it is a special compiler-generated or linker-generated symbol such as CFG symbols (e.g. __guard_fids_count, __guard_flags).
// we can safely ignore those symbols.
return 0u;
}
return m_headers[oneBasedSectionIndex - 1u].VirtualAddress + offsetInSection;
}

View File

@@ -1,42 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "Foundation/PDB_Macros.h"
#include "Foundation/PDB_ArrayView.h"
#include "PDB_Types.h"
#include "PDB_CoalescedMSFStream.h"
namespace PDB
{
class RawFile;
struct IMAGE_SECTION_HEADER;
class PDB_NO_DISCARD ImageSectionStream
{
public:
ImageSectionStream(void) PDB_NO_EXCEPT;
explicit ImageSectionStream(const RawFile& file, uint16_t streamIndex) PDB_NO_EXCEPT;
PDB_DEFAULT_MOVE(ImageSectionStream);
// Converts a one-based section offset into an RVA.
PDB_NO_DISCARD uint32_t ConvertSectionOffsetToRVA(uint16_t oneBasedSectionIndex, uint32_t offsetInSection) const PDB_NO_EXCEPT;
// Returns a view of all the sections in the stream.
PDB_NO_DISCARD inline ArrayView<IMAGE_SECTION_HEADER> GetImageSections(void) const PDB_NO_EXCEPT
{
return ArrayView<IMAGE_SECTION_HEADER>(m_headers, m_count);
}
private:
CoalescedMSFStream m_stream;
const IMAGE_SECTION_HEADER* m_headers;
size_t m_count;
PDB_DISABLE_COPY(ImageSectionStream);
};
}

View File

@@ -1,102 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "PDB_PCH.h"
#include "PDB_InfoStream.h"
#include "PDB_RawFile.h"
#include "Foundation/PDB_CRT.h"
namespace
{
// the PDB info stream always resides at index 1
static constexpr const uint32_t InfoStreamIndex = 1u;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::InfoStream::InfoStream(void) PDB_NO_EXCEPT
: m_stream()
, m_header(nullptr)
, m_namesStreamIndex(0)
, m_usesDebugFastlink(false)
, m_hasIPIStream(false)
{
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::InfoStream::InfoStream(const RawFile& file) PDB_NO_EXCEPT
: m_stream(file.CreateMSFStream<CoalescedMSFStream>(InfoStreamIndex))
, m_header(m_stream.GetDataAtOffset<const Header>(0u))
, m_namesStreamIndex(0)
, m_usesDebugFastlink(false)
, m_hasIPIStream(false)
{
// the info stream starts with the header, followed by the named stream map, followed by the feature codes
// https://llvm.org/docs/PDB/PdbStream.html#named-stream-map
size_t streamOffset = sizeof(Header);
const NamedStreamMap* namedStreamMap = m_stream.GetDataAtOffset<const NamedStreamMap>(streamOffset);
streamOffset += sizeof(NamedStreamMap) + namedStreamMap->length;
const SerializedHashTable::Header* hashTableHeader = m_stream.GetDataAtOffset<const SerializedHashTable::Header>(streamOffset);
streamOffset += sizeof(SerializedHashTable::Header);
const SerializedHashTable::BitVector* presentBitVector = m_stream.GetDataAtOffset<const SerializedHashTable::BitVector>(streamOffset);
streamOffset += sizeof(SerializedHashTable::BitVector) + sizeof(uint32_t) * presentBitVector->wordCount;
const SerializedHashTable::BitVector* deletedBitVector = m_stream.GetDataAtOffset<const SerializedHashTable::BitVector>(streamOffset);
streamOffset += sizeof(SerializedHashTable::BitVector) + sizeof(uint32_t) * deletedBitVector->wordCount;
// the hash table entries can be used to identify the indices of certain common streams like:
// "/UDTSRCLINEUNDONE"
// "/src/headerblock"
// "/LinkInfo"
// "/TMCache"
// "/names"
const NamedStreamMap::HashTableEntry* namedStreamMapHashEntries = m_stream.GetDataAtOffset<const NamedStreamMap::HashTableEntry>(streamOffset);
// Find "/names" stream, used to look up filenames for lines.
for (uint32_t i = 0, size = hashTableHeader->size; i < size; ++i)
{
const NamedStreamMap::HashTableEntry& entry = namedStreamMapHashEntries[i];
const char* streamName = &namedStreamMap->stringTable[entry.stringTableOffset];
if (strcmp("/names", streamName) == 0)
{
m_namesStreamIndex = entry.streamIndex;
}
}
streamOffset += sizeof(NamedStreamMap::HashTableEntry) * hashTableHeader->size;
// read feature codes by consuming remaining bytes
// https://llvm.org/docs/PDB/PdbStream.html#pdb-feature-codes
const FeatureCode* featureCodes = m_stream.GetDataAtOffset<const FeatureCode>(streamOffset);
const size_t remainingBytes = m_stream.GetSize() - streamOffset;
const size_t count = remainingBytes / sizeof(FeatureCode);
for (size_t i=0u; i < count; ++i)
{
FeatureCode code = featureCodes[i];
if (code == PDB::FeatureCode::MinimalDebugInfo)
{
m_usesDebugFastlink = true;
}
else if (code == PDB::FeatureCode::VC110 || code == PDB::FeatureCode::VC140)
{
m_hasIPIStream = true;
}
}
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::NamesStream PDB::InfoStream::CreateNamesStream(const RawFile& file) const PDB_NO_EXCEPT
{
return NamesStream(file, m_namesStreamIndex);
}

View File

@@ -1,62 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "Foundation/PDB_Macros.h"
#include "PDB_Types.h"
#include "PDB_CoalescedMSFStream.h"
#include "PDB_NamesStream.h"
namespace PDB
{
class RawFile;
// PDB Info Stream
// https://llvm.org/docs/PDB/PdbStream.html
class PDB_NO_DISCARD InfoStream
{
public:
InfoStream(void) PDB_NO_EXCEPT;
explicit InfoStream(const RawFile& file) PDB_NO_EXCEPT;
PDB_DEFAULT_MOVE(InfoStream);
// Returns the header of the stream.
PDB_NO_DISCARD inline const Header* GetHeader(void) const PDB_NO_EXCEPT
{
return m_header;
}
// Returns whether the module has a names stream.
PDB_NO_DISCARD inline bool HasNamesStream(void) const PDB_NO_EXCEPT
{
return (m_namesStreamIndex != 0u);
}
// Returns whether the PDB file was linked using /DEBUG:FASTLINK.
PDB_NO_DISCARD inline bool UsesDebugFastLink(void) const PDB_NO_EXCEPT
{
return m_usesDebugFastlink;
}
// Returns whether the PDB file has an IPI stream.
PDB_NO_DISCARD inline bool HasIPIStream(void) const PDB_NO_EXCEPT
{
return m_hasIPIStream;
}
// Create names stream
PDB_NO_DISCARD NamesStream CreateNamesStream(const RawFile& file) const PDB_NO_EXCEPT;
private:
CoalescedMSFStream m_stream;
const Header* m_header;
uint32_t m_namesStreamIndex;
bool m_usesDebugFastlink;
bool m_hasIPIStream;
PDB_DISABLE_COPY(InfoStream);
};
}

View File

@@ -1,184 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "PDB_PCH.h"
#include "PDB_ModuleInfoStream.h"
#include "Foundation/PDB_Memory.h"
#include "Foundation/PDB_CRT.h"
namespace
{
static constexpr const char* LinkerSymbolName("* Linker *");
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD static inline size_t EstimateModuleCount(size_t streamSize) PDB_NO_EXCEPT
{
// work out how many modules are stored in the stream at most.
// the module info is stored in variable-length records, so we can't determine the exact number without walking the stream.
return streamSize / sizeof(PDB::DBI::ModuleInfo);
}
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::ModuleInfoStream::Module::Module(void) PDB_NO_EXCEPT
: m_info(nullptr)
, m_name(nullptr)
, m_nameLength(0u)
, m_objectName(nullptr)
, m_objectNameLength(0u)
{
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::ModuleInfoStream::Module::Module(const DBI::ModuleInfo* info, const char* name, size_t nameLength, const char* objectName, size_t objectNameLength) PDB_NO_EXCEPT
: m_info(info)
, m_name(name)
, m_nameLength(nameLength)
, m_objectName(objectName)
, m_objectNameLength(objectNameLength)
{
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD bool PDB::ModuleInfoStream::Module::HasSymbolStream(void) const PDB_NO_EXCEPT
{
const uint16_t streamIndex = m_info->moduleSymbolStreamIndex;
// some modules don't have a symbol stream, i.e. no additional debug information is present.
// this usually happens when private symbols are stripped from a PDB.
return (streamIndex != 0xFFFFu);
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD bool PDB::ModuleInfoStream::Module::HasLineStream(void) const PDB_NO_EXCEPT
{
return (m_info->c13Size > 0);
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD PDB::ModuleSymbolStream PDB::ModuleInfoStream::Module::CreateSymbolStream(const RawFile& file) const PDB_NO_EXCEPT
{
PDB_ASSERT(HasSymbolStream(), "Module symbol stream index is invalid.");
return ModuleSymbolStream(file, m_info->moduleSymbolStreamIndex, m_info->symbolSize);
}
PDB_NO_DISCARD PDB::ModuleLineStream PDB::ModuleInfoStream::Module::CreateLineStream(const RawFile& file) const PDB_NO_EXCEPT
{
PDB_ASSERT(HasLineStream(), "Module line stream is not present.");
return ModuleLineStream(file, m_info->moduleSymbolStreamIndex, m_info->symbolSize + m_info->c11Size + m_info->c13Size, m_info->symbolSize + m_info->c11Size);
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::ModuleInfoStream::ModuleInfoStream(void) PDB_NO_EXCEPT
: m_stream()
, m_modules(nullptr)
, m_moduleCount(0u)
{
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::ModuleInfoStream::ModuleInfoStream(ModuleInfoStream&& other) PDB_NO_EXCEPT
: m_stream(PDB_MOVE(other.m_stream))
, m_modules(PDB_MOVE(other.m_modules))
, m_moduleCount(PDB_MOVE(other.m_moduleCount))
{
other.m_modules = nullptr;
other.m_moduleCount = 0u;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::ModuleInfoStream& PDB::ModuleInfoStream::operator=(ModuleInfoStream&& other) PDB_NO_EXCEPT
{
if (this != &other)
{
PDB_DELETE_ARRAY(m_modules);
m_stream = PDB_MOVE(other.m_stream);
m_modules = PDB_MOVE(other.m_modules);
m_moduleCount = PDB_MOVE(other.m_moduleCount);
other.m_modules = nullptr;
other.m_moduleCount = 0u;
}
return *this;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::ModuleInfoStream::ModuleInfoStream(const DirectMSFStream& directStream, uint32_t size, uint32_t offset) PDB_NO_EXCEPT
: m_stream(directStream, size, offset)
, m_modules(nullptr)
, m_moduleCount(0u)
{
m_modules = PDB_NEW_ARRAY(Module, EstimateModuleCount(size));
size_t streamOffset = 0u;
while (streamOffset < size)
{
const DBI::ModuleInfo* moduleInfo = m_stream.GetDataAtOffset<const DBI::ModuleInfo>(streamOffset);
streamOffset += sizeof(DBI::ModuleInfo);
const char* name = m_stream.GetDataAtOffset<const char>(streamOffset);
const size_t nameLength = strlen(name);
streamOffset += nameLength + 1u;
const char* objectName = m_stream.GetDataAtOffset<const char>(streamOffset);
const size_t objectNameLength = strlen(objectName);
streamOffset += objectNameLength + 1u;
// the stream is aligned to 4 bytes
streamOffset = BitUtil::RoundUpToMultiple<size_t>(streamOffset, 4ul);
m_modules[m_moduleCount] = Module(moduleInfo, name, nameLength, objectName, objectNameLength);
++m_moduleCount;
}
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::ModuleInfoStream::~ModuleInfoStream(void) PDB_NO_EXCEPT
{
PDB_DELETE_ARRAY(m_modules);
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD const PDB::ModuleInfoStream::Module* PDB::ModuleInfoStream::FindLinkerModule(void) const PDB_NO_EXCEPT
{
const size_t count = m_moduleCount;
for (size_t i = 0u; i < count; ++i)
{
// with both MSVC cl.exe and Clang, the linker symbol is the last one to be stored, so start searching from the end
const Module& module = m_modules[count - i - 1u];
// check if this is the linker symbol
if (strcmp(module.GetName().Decay(), LinkerSymbolName) == 0)
{
return &module;
}
}
return nullptr;
}

View File

@@ -1,104 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "Foundation/PDB_Macros.h"
#include "Foundation/PDB_ArrayView.h"
#include "PDB_CoalescedMSFStream.h"
#include "PDB_ModuleSymbolStream.h"
#include "PDB_ModuleLineStream.h"
namespace PDB
{
class PDB_NO_DISCARD DirectMSFStream;
class PDB_NO_DISCARD ModuleInfoStream
{
public:
class PDB_NO_DISCARD Module
{
public:
Module(void) PDB_NO_EXCEPT;
explicit Module(const DBI::ModuleInfo* info, const char* name, size_t nameLength, const char* objectName, size_t objectNameLength) PDB_NO_EXCEPT;
PDB_DEFAULT_MOVE(Module);
// Returns whether the module has a symbol stream.
PDB_NO_DISCARD bool HasSymbolStream(void) const PDB_NO_EXCEPT;
// Returns whether the module has a line stream.
PDB_NO_DISCARD bool HasLineStream(void) const PDB_NO_EXCEPT;
// Creates a symbol stream for the module.
PDB_NO_DISCARD ModuleSymbolStream CreateSymbolStream(const RawFile& file) const PDB_NO_EXCEPT;
// Create a line stream for the module
PDB_NO_DISCARD ModuleLineStream CreateLineStream(const RawFile& file) const PDB_NO_EXCEPT;
// Returns the PDB module info.
PDB_NO_DISCARD inline const DBI::ModuleInfo* GetInfo(void) const PDB_NO_EXCEPT
{
return m_info;
}
// Returns the name of the module.
PDB_NO_DISCARD inline ArrayView<char> GetName(void) const PDB_NO_EXCEPT
{
return ArrayView<char>(m_name, m_nameLength);
}
// Returns the name of the object file of the module.
PDB_NO_DISCARD inline ArrayView<char> GetObjectName(void) const PDB_NO_EXCEPT
{
return ArrayView<char>(m_objectName, m_objectNameLength);
}
private:
// the module info is stored in variable-length arrays inside the stream, so rather than store an array directly,
// we need to store pointers to the individual data items inside the stream.
const DBI::ModuleInfo* m_info;
// the module name, e.g. the path to an object file or import library such as "Import:kernel32.dll"
const char* m_name;
size_t m_nameLength;
// the name of the object file. either the same as the module name, or the path to the archive that contained the module
const char* m_objectName;
size_t m_objectNameLength;
PDB_DISABLE_COPY(Module);
};
ModuleInfoStream(void) PDB_NO_EXCEPT;
ModuleInfoStream(ModuleInfoStream&& other) PDB_NO_EXCEPT;
ModuleInfoStream& operator=(ModuleInfoStream&& other) PDB_NO_EXCEPT;
explicit ModuleInfoStream(const DirectMSFStream& directStream, uint32_t size, uint32_t offset) PDB_NO_EXCEPT;
~ModuleInfoStream(void) PDB_NO_EXCEPT;
// Tries to find the linker module corresponding to the linker, i.e. the module named "* Linker *".
PDB_NO_DISCARD const Module* FindLinkerModule(void) const PDB_NO_EXCEPT;
// Returns the module with the given index.
PDB_NO_DISCARD inline const Module& GetModule(uint32_t index) const PDB_NO_EXCEPT
{
return m_modules[index];
}
// Returns a view of all modules in the info stream.
PDB_NO_DISCARD inline ArrayView<Module> GetModules(void) const PDB_NO_EXCEPT
{
return ArrayView<Module>(m_modules, m_moduleCount);
}
private:
CoalescedMSFStream m_stream;
Module* m_modules;
size_t m_moduleCount;
PDB_DISABLE_COPY(ModuleInfoStream);
};
}

View File

@@ -1,31 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "PDB_PCH.h"
#include "PDB_ModuleLineStream.h"
#include "PDB_RawFile.h"
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::ModuleLineStream::ModuleLineStream(void) PDB_NO_EXCEPT
: m_stream(), m_c13LineInfoOffset(0)
{
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::ModuleLineStream::ModuleLineStream(const RawFile& file, uint16_t streamIndex, uint32_t streamSize, size_t c13LineInfoOffset) PDB_NO_EXCEPT
: m_stream(file.CreateMSFStream<CoalescedMSFStream>(streamIndex, streamSize)), m_c13LineInfoOffset(c13LineInfoOffset)
{
// https://llvm.org/docs/PDB/ModiStream.html
// struct ModiStream {
// uint32_t Signature;
// uint8_t Symbols[SymbolSize - 4];
// uint8_t C11LineInfo[C11Size];
// uint8_t C13LineInfo[C13Size];
// uint32_t GlobalRefsSize;
// uint8_t GlobalRefs[GlobalRefsSize];
// };
}

View File

@@ -1,151 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "Foundation/PDB_Macros.h"
#include "Foundation/PDB_BitUtil.h"
#include "PDB_DBITypes.h"
#include "PDB_Util.h"
#include "PDB_CoalescedMSFStream.h"
namespace PDB
{
class RawFile;
class PDB_NO_DISCARD ModuleLineStream
{
public:
ModuleLineStream(void) PDB_NO_EXCEPT;
explicit ModuleLineStream(const RawFile& file, uint16_t streamIndex, uint32_t streamSize, size_t c13LineInfoOffset) PDB_NO_EXCEPT;
PDB_DEFAULT_MOVE(ModuleLineStream);
template <typename F>
void ForEachSection(F&& functor) const PDB_NO_EXCEPT
{
size_t offset = m_c13LineInfoOffset;
// read the line stream sections
while (offset < m_stream.GetSize())
{
const CodeView::DBI::LineSection* section = m_stream.GetDataAtOffset<const CodeView::DBI::LineSection>(offset);
functor(section);
offset = BitUtil::RoundUpToMultiple<size_t>(offset + sizeof(CodeView::DBI::DebugSubsectionHeader) + section->header.size, 4u);
}
}
template <typename F>
void ForEachLinesBlock(const CodeView::DBI::LineSection* section, F&& functor) const PDB_NO_EXCEPT
{
PDB_ASSERT(section->header.kind == CodeView::DBI::DebugSubsectionKind::S_LINES,
"DebugSubsectionHeader::Kind %X != S_LINES (%X)",
static_cast<uint32_t>(section->header.kind), static_cast<uint32_t>(CodeView::DBI::DebugSubsectionKind::S_LINES));
size_t offset = m_stream.GetPointerOffset(section);
const size_t headerEnd = BitUtil::RoundUpToMultiple<size_t>(offset + sizeof(CodeView::DBI::DebugSubsectionHeader) + section->header.size, 4u);
offset = BitUtil::RoundUpToMultiple<size_t>(offset + sizeof(CodeView::DBI::DebugSubsectionHeader) + sizeof(CodeView::DBI::LinesHeader), 4u);
// read all blocks of lines
while (offset < headerEnd)
{
const CodeView::DBI::LinesFileBlockHeader* linesBlockHeader = m_stream.GetDataAtOffset<const CodeView::DBI::LinesFileBlockHeader>(offset);
const CodeView::DBI::Line* blockLines = m_stream.GetDataAtOffset<const CodeView::DBI::Line>(offset + sizeof(CodeView::DBI::LinesFileBlockHeader));
const size_t blockColumnsOffset = sizeof(CodeView::DBI::LinesFileBlockHeader) + (linesBlockHeader->numLines * (sizeof(CodeView::DBI::Line)));
const CodeView::DBI::Column* blockColumns = blockColumnsOffset < linesBlockHeader->size ? m_stream.GetDataAtOffset<const CodeView::DBI::Column>(offset) : nullptr;
functor(linesBlockHeader, blockLines, blockColumns);
offset = BitUtil::RoundUpToMultiple<size_t>(offset + linesBlockHeader->size, 4u);
}
PDB_ASSERT(offset == headerEnd, "Mismatch between offset %zu and header end %zu when reading lines blocks", offset, headerEnd);
}
template <typename F>
void ForEachFileChecksum(const CodeView::DBI::LineSection* section, F&& functor) const PDB_NO_EXCEPT
{
PDB_ASSERT(section->header.kind == CodeView::DBI::DebugSubsectionKind::S_FILECHECKSUMS,
"DebugSubsectionHeader::Kind %X != S_FILECHECKSUMS (%X)",
static_cast<uint32_t>(section->header.kind), static_cast<uint32_t>(CodeView::DBI::DebugSubsectionKind::S_FILECHECKSUMS));
size_t offset = m_stream.GetPointerOffset(section);
const size_t headerEnd = BitUtil::RoundUpToMultiple<size_t>(offset + sizeof(CodeView::DBI::DebugSubsectionHeader) + section->header.size, 4u);
offset = BitUtil::RoundUpToMultiple<size_t>(offset + sizeof(CodeView::DBI::DebugSubsectionHeader), 4u);
// read all file checksums
while (offset < headerEnd)
{
const CodeView::DBI::FileChecksumHeader* fileChecksumHeader = m_stream.GetDataAtOffset<const CodeView::DBI::FileChecksumHeader>(offset);
functor(fileChecksumHeader);
offset = BitUtil::RoundUpToMultiple<size_t>(offset + sizeof(CodeView::DBI::FileChecksumHeader) + fileChecksumHeader->checksumSize, 4u);
}
PDB_ASSERT(offset == headerEnd, "Mismatch between offset %zu and header end %zu when reading file checksums", offset, headerEnd);
}
template <typename F>
void ForEachInlineeSourceLine(const CodeView::DBI::LineSection* section, F&& functor) const PDB_NO_EXCEPT
{
PDB_ASSERT(section->header.kind == CodeView::DBI::DebugSubsectionKind::S_INLINEELINES,
"DebugSubsectionHeader::Kind %X != S_INLINEELINES (%X)",
static_cast<uint32_t>(section->header.kind), static_cast<uint32_t>(CodeView::DBI::DebugSubsectionKind::S_INLINEELINES));
PDB_ASSERT(section->inlineeHeader.kind == CodeView::DBI::InlineeSourceLineKind::Signature,
"InlineeSourceLineKind %X != :InlineeSourceLineKind::Signature (%X)", static_cast<uint32_t>(section->header.kind), static_cast<uint32_t>(CodeView::DBI::InlineeSourceLineKind::Signature));
size_t offset = m_stream.GetPointerOffset(section);
const size_t headerEnd = BitUtil::RoundUpToMultiple<size_t>(offset + sizeof(CodeView::DBI::DebugSubsectionHeader) + section->header.size, 4u);
offset = BitUtil::RoundUpToMultiple<size_t>(offset + sizeof(CodeView::DBI::DebugSubsectionHeader) + sizeof(CodeView::DBI::InlineeSourceLineHeader), 4u);
// read all file checksums
while (offset < headerEnd)
{
const CodeView::DBI::InlineeSourceLine* inlineeSourceLine = m_stream.GetDataAtOffset<const CodeView::DBI::InlineeSourceLine>(offset);
functor(inlineeSourceLine);
offset = BitUtil::RoundUpToMultiple<size_t>(offset + sizeof(CodeView::DBI::InlineeSourceLine), 4u);
}
}
template <typename F>
void ForEachInlineeSourceLineEx(const CodeView::DBI::LineSection* section, F&& functor) const PDB_NO_EXCEPT
{
PDB_ASSERT(section->header.kind == CodeView::DBI::DebugSubsectionKind::S_INLINEELINES,
"DebugSubsectionHeader::Kind %X != S_INLINEELINES (%X)", static_cast<uint32_t>(section->header.kind), static_cast<uint32_t>(CodeView::DBI::DebugSubsectionKind::S_INLINEELINES));
PDB_ASSERT(section->inlineeHeader.kind == CodeView::DBI::InlineeSourceLineKind::SignatureEx,
"InlineeSourceLineKind %X != :InlineeSourceLineKind::SignatureEx (%X)", static_cast<uint32_t>(section->header.kind), static_cast<uint32_t>(CodeView::DBI::InlineeSourceLineKind::SignatureEx));
size_t offset = m_stream.GetPointerOffset(section);
const size_t headerEnd = BitUtil::RoundUpToMultiple<size_t>(offset + sizeof(CodeView::DBI::DebugSubsectionHeader) + section->header.size, 4u);
offset = BitUtil::RoundUpToMultiple<size_t>(offset + sizeof(CodeView::DBI::DebugSubsectionHeader) + sizeof(CodeView::DBI::InlineeSourceLineHeader), 4u);
// read all file checksums
while (offset < headerEnd)
{
const CodeView::DBI::InlineeSourceLineEx* inlineeSourceLineEx = m_stream.GetDataAtOffset<const CodeView::DBI::InlineeSourceLineEx>(offset);
functor(inlineeSourceLineEx);
offset = BitUtil::RoundUpToMultiple<size_t>(offset + sizeof(CodeView::DBI::InlineeSourceLineEx) + (inlineeSourceLineEx->extraLines * sizeof(uint32_t)), 4u);
}
}
private:
CoalescedMSFStream m_stream;
size_t m_c13LineInfoOffset;
PDB_DISABLE_COPY(ModuleLineStream);
};
}

View File

@@ -1,61 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "PDB_PCH.h"
#include "PDB_ModuleSymbolStream.h"
#include "PDB_RawFile.h"
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::ModuleSymbolStream::ModuleSymbolStream(void) PDB_NO_EXCEPT
: m_stream()
{
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::ModuleSymbolStream::ModuleSymbolStream(const RawFile& file, uint16_t streamIndex, uint32_t symbolStreamSize) PDB_NO_EXCEPT
: m_stream(file.CreateMSFStream<CoalescedMSFStream>(streamIndex, symbolStreamSize))
{
// https://llvm.org/docs/PDB/ModiStream.html
// struct ModiStream {
// uint32_t Signature;
// uint8_t Symbols[SymbolSize - 4];
// uint8_t C11LineInfo[C11Size];
// uint8_t C13LineInfo[C13Size];
// uint32_t GlobalRefsSize;
// uint8_t GlobalRefs[GlobalRefsSize];
// };
// we are only interested in the symbols, but not the line information or global refs.
// the coalesced stream is therefore only built for the symbols, not all the data in the stream.
// this potentially saves a lot of memory and performance on large PDBs.
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD const PDB::CodeView::DBI::Record* PDB::ModuleSymbolStream::FindRecord(CodeView::DBI::SymbolRecordKind kind) const PDB_NO_EXCEPT
{
// ignore the stream's 4-byte signature
size_t offset = sizeof(uint32_t);
// parse the CodeView records
while (offset < m_stream.GetSize())
{
// https://llvm.org/docs/PDB/CodeViewTypes.html
const CodeView::DBI::Record* record = m_stream.GetDataAtOffset<const CodeView::DBI::Record>(offset);
if (record->header.kind == kind)
{
return record;
}
const uint32_t recordSize = GetCodeViewRecordSize(record);
// position the module stream offset at the next record
offset = BitUtil::RoundUpToMultiple<size_t>(offset + sizeof(CodeView::DBI::RecordHeader) + recordSize, 4u);
}
return nullptr;
}

View File

@@ -1,70 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "Foundation/PDB_Macros.h"
#include "Foundation/PDB_BitUtil.h"
#include "PDB_DBITypes.h"
#include "PDB_Util.h"
#include "PDB_CoalescedMSFStream.h"
namespace PDB
{
class RawFile;
class PDB_NO_DISCARD ModuleSymbolStream
{
public:
ModuleSymbolStream(void) PDB_NO_EXCEPT;
explicit ModuleSymbolStream(const RawFile& file, uint16_t streamIndex, uint32_t symbolStreamSize) PDB_NO_EXCEPT;
PDB_DEFAULT_MOVE(ModuleSymbolStream);
// Returns a record's parent record.
template <typename T>
PDB_NO_DISCARD inline const CodeView::DBI::Record* GetParentRecord(const T& record) const PDB_NO_EXCEPT
{
return m_stream.GetDataAtOffset<const CodeView::DBI::Record>(record.parent);
}
// Returns a record's end record.
template <typename T>
PDB_NO_DISCARD inline const CodeView::DBI::Record* GetEndRecord(const T& record) const PDB_NO_EXCEPT
{
return m_stream.GetDataAtOffset<const CodeView::DBI::Record>(record.end);
}
// Finds a record of a certain kind.
PDB_NO_DISCARD const CodeView::DBI::Record* FindRecord(CodeView::DBI::SymbolRecordKind Kind) const PDB_NO_EXCEPT;
// Iterates all records in the stream.
template <typename F>
void ForEachSymbol(F&& functor) const PDB_NO_EXCEPT
{
// ignore the stream's 4-byte signature
size_t offset = sizeof(uint32_t);
// parse the CodeView records
while (offset < m_stream.GetSize())
{
// https://llvm.org/docs/PDB/CodeViewTypes.html
const CodeView::DBI::Record* record = m_stream.GetDataAtOffset<const CodeView::DBI::Record>(offset);
const uint32_t recordSize = GetCodeViewRecordSize(record);
functor(record);
// position the module stream offset at the next record
offset = BitUtil::RoundUpToMultiple<size_t>(offset + sizeof(CodeView::DBI::RecordHeader) + recordSize, 4u);
}
}
private:
CoalescedMSFStream m_stream;
PDB_DISABLE_COPY(ModuleSymbolStream);
};
}

View File

@@ -1,28 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "PDB_PCH.h"
#include "PDB_NamesStream.h"
#include "PDB_RawFile.h"
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::NamesStream::NamesStream(void) PDB_NO_EXCEPT
: m_stream()
, m_header(nullptr)
, m_stringTable(nullptr)
{
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::NamesStream::NamesStream(const RawFile& file, uint32_t streamIndex) PDB_NO_EXCEPT
: m_stream(file.CreateMSFStream<CoalescedMSFStream>(streamIndex))
, m_header(m_stream.GetDataAtOffset<const NamesHeader>(0u))
, m_stringTable(nullptr)
{
// grab a pointer into the string table
m_stringTable = m_stream.GetDataAtOffset<char>(sizeof(NamesHeader));
}

View File

@@ -1,48 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "Foundation/PDB_Macros.h"
#include "PDB_Types.h"
#include "PDB_CoalescedMSFStream.h"
namespace PDB
{
class RawFile;
struct NamesHeader
{
uint32_t magic;
uint32_t hashVersion;
uint32_t size;
};
class PDB_NO_DISCARD NamesStream
{
public:
NamesStream(void) PDB_NO_EXCEPT;
explicit NamesStream(const RawFile& file, uint32_t streamIndex) PDB_NO_EXCEPT;
PDB_DEFAULT_MOVE(NamesStream);
// Returns the header of the stream.
PDB_NO_DISCARD inline const NamesHeader* GetHeader(void) const PDB_NO_EXCEPT
{
return m_header;
}
PDB_NO_DISCARD inline const char* GetFilename(uint32_t filenameOffset) const PDB_NO_EXCEPT
{
return m_stringTable + filenameOffset;
}
private:
CoalescedMSFStream m_stream;
const NamesHeader* m_header;
const char* m_stringTable;
PDB_DISABLE_COPY(NamesStream);
};
}

View File

@@ -1,4 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "PDB_PCH.h"

View File

@@ -1,20 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
// this needs to be the first include, since it determines the platform/toolchain we're compiling for
#include "Foundation/PDB_Platform.h"
#include "Foundation/PDB_Macros.h"
#include "Foundation/PDB_Warnings.h"
// library includes
#include "Foundation/PDB_Log.h"
#include "Foundation/PDB_Assert.h"
#include "Foundation/PDB_Move.h"
#include "Foundation/PDB_Forward.h"
#include "Foundation/PDB_Memory.h"
#include "Foundation/PDB_ArrayView.h"
#include "Foundation/PDB_BitUtil.h"
#include "Foundation/PDB_BitOperators.h"
#include "Foundation/PDB_PointerUtil.h"

View File

@@ -1,43 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "PDB_PCH.h"
#include "PDB_PublicSymbolStream.h"
#include "PDB_RawFile.h"
#include "PDB_Types.h"
#include "PDB_DBITypes.h"
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::PublicSymbolStream::PublicSymbolStream(void) PDB_NO_EXCEPT
: m_stream()
, m_hashRecords(nullptr)
, m_count(0u)
{
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::PublicSymbolStream::PublicSymbolStream(const RawFile& file, uint16_t streamIndex, uint32_t count) PDB_NO_EXCEPT
: m_stream(file.CreateMSFStream<CoalescedMSFStream>(streamIndex))
, m_hashRecords(m_stream.GetDataAtOffset<HashRecord>(sizeof(PublicStreamHeader) + sizeof(HashTableHeader)))
, m_count(count)
{
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB_NO_DISCARD const PDB::CodeView::DBI::Record* PDB::PublicSymbolStream::GetRecord(const CoalescedMSFStream& symbolRecordStream, const HashRecord& hashRecord) const PDB_NO_EXCEPT
{
// hash record offsets start at 1, not at 0
const uint32_t headerOffset = hashRecord.offset - 1u;
// the offset doesn't point to the public symbol directly, but to the CodeView record:
// https://llvm.org/docs/PDB/CodeViewSymbols.html
const CodeView::DBI::Record* record = symbolRecordStream.GetDataAtOffset<const CodeView::DBI::Record>(headerOffset);
return record;
}

View File

@@ -1,49 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "Foundation/PDB_Macros.h"
#include "Foundation/PDB_ArrayView.h"
#include "PDB_CoalescedMSFStream.h"
namespace PDB
{
class RawFile;
struct HashRecord;
namespace CodeView
{
namespace DBI
{
struct Record;
}
}
class PDB_NO_DISCARD PublicSymbolStream
{
public:
PublicSymbolStream(void) PDB_NO_EXCEPT;
explicit PublicSymbolStream(const RawFile& file, uint16_t streamIndex, uint32_t count) PDB_NO_EXCEPT;
PDB_DEFAULT_MOVE(PublicSymbolStream);
// Turns a given hash record into a DBI record using the given symbol stream.
PDB_NO_DISCARD const CodeView::DBI::Record* GetRecord(const CoalescedMSFStream& symbolRecordStream, const HashRecord& hashRecord) const PDB_NO_EXCEPT;
// Returns a view of all the records in the stream.
PDB_NO_DISCARD inline ArrayView<HashRecord> GetRecords(void) const PDB_NO_EXCEPT
{
return ArrayView<HashRecord>(m_hashRecords, m_count);
}
private:
CoalescedMSFStream m_stream;
const HashRecord* m_hashRecords;
uint32_t m_count;
PDB_DISABLE_COPY(PublicSymbolStream);
};
}

View File

@@ -1,147 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "PDB_PCH.h"
#include "PDB_RawFile.h"
#include "PDB_Types.h"
#include "PDB_Util.h"
#include "PDB_DirectMSFStream.h"
#include "Foundation/PDB_PointerUtil.h"
#include "Foundation/PDB_Memory.h"
#include "Foundation/PDB_Assert.h"
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::RawFile::RawFile(RawFile&& other) PDB_NO_EXCEPT
: m_data(PDB_MOVE(other.m_data))
, m_superBlock(PDB_MOVE(other.m_superBlock))
, m_directoryStream(PDB_MOVE(other.m_directoryStream))
, m_streamCount(PDB_MOVE(other.m_streamCount))
, m_streamSizes(PDB_MOVE(other.m_streamSizes))
, m_streamBlocks(PDB_MOVE(other.m_streamBlocks))
{
other.m_data = nullptr;
other.m_superBlock = nullptr;
other.m_streamCount = 0u;
other.m_streamSizes = nullptr;
other.m_streamBlocks = nullptr;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::RawFile& PDB::RawFile::operator=(RawFile&& other) PDB_NO_EXCEPT
{
if (this != &other)
{
PDB_DELETE_ARRAY(m_streamBlocks);
m_data = PDB_MOVE(other.m_data);
m_superBlock = PDB_MOVE(other.m_superBlock);
m_directoryStream = PDB_MOVE(other.m_directoryStream);
m_streamCount = PDB_MOVE(other.m_streamCount);
m_streamSizes = PDB_MOVE(other.m_streamSizes);
m_streamBlocks = PDB_MOVE(other.m_streamBlocks);
other.m_data = nullptr;
other.m_superBlock = nullptr;
other.m_streamCount = 0u;
other.m_streamSizes = nullptr;
other.m_streamBlocks = nullptr;
}
return *this;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::RawFile::RawFile(const void* data) PDB_NO_EXCEPT
: m_data(data)
, m_superBlock(Pointer::Offset<const SuperBlock*>(data, 0u))
, m_directoryStream()
, m_streamCount(0u)
, m_streamSizes(nullptr)
, m_streamBlocks(nullptr)
{
// the SuperBlock stores an array of indices of blocks that make up the indices of directory blocks, which need to be stitched together to form the directory.
// the blocks holding the indices of directory blocks are not necessarily contiguous, so they need to be coalesced first.
const uint32_t directoryBlockCount = PDB::ConvertSizeToBlockCount(m_superBlock->directorySize, m_superBlock->blockSize);
// the directory is made up of directoryBlockCount blocks, so we need that many indices to be read from the blocks that make up the indices
CoalescedMSFStream directoryIndicesStream(data, m_superBlock->blockSize, m_superBlock->directoryBlockIndices, directoryBlockCount * sizeof(uint32_t));
// these are the indices of blocks making up the directory stream, now guaranteed to be contiguous
const uint32_t* directoryIndices = directoryIndicesStream.GetDataAtOffset<uint32_t>(0u);
m_directoryStream = CoalescedMSFStream(data, m_superBlock->blockSize, directoryIndices, m_superBlock->directorySize);
// https://llvm.org/docs/PDB/MsfFile.html#the-stream-directory
// parse the directory from its contiguous version. the directory matches the following struct:
// struct StreamDirectory
// {
// uint32_t streamCount;
// uint32_t streamSizes[streamCount];
// uint32_t streamBlocks[streamCount][];
// };
m_streamCount = *m_directoryStream.GetDataAtOffset<uint32_t>(0u);
// we can assign pointers into the stream directly, since the RawFile keeps ownership of the directory stream
m_streamSizes = m_directoryStream.GetDataAtOffset<uint32_t>(sizeof(uint32_t));
const uint32_t* directoryStreamBlocks = m_directoryStream.GetDataAtOffset<uint32_t>(sizeof(uint32_t) + sizeof(uint32_t) * m_streamCount);
// prepare indices for directly accessing individual streams
m_streamBlocks = PDB_NEW_ARRAY(const uint32_t*, m_streamCount);
const uint32_t* indicesForCurrentBlock = directoryStreamBlocks;
for (uint32_t i = 0u; i < m_streamCount; ++i)
{
const uint32_t sizeInBytes = GetStreamSize(i);
const uint32_t blockCount = ConvertSizeToBlockCount(sizeInBytes, m_superBlock->blockSize);
m_streamBlocks[i] = indicesForCurrentBlock;
indicesForCurrentBlock += blockCount;
}
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::RawFile::~RawFile(void) PDB_NO_EXCEPT
{
PDB_DELETE_ARRAY(m_streamBlocks);
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
template <typename T>
PDB_NO_DISCARD T PDB::RawFile::CreateMSFStream(uint32_t streamIndex) const PDB_NO_EXCEPT
{
PDB_ASSERT(streamIndex != PDB::NilStreamIndex, "Invalid stream index.");
PDB_ASSERT(streamIndex < m_streamCount, "Invalid stream index.");
return T(m_data, m_superBlock->blockSize, m_streamBlocks[streamIndex], GetStreamSize(streamIndex));
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
template <typename T>
PDB_NO_DISCARD T PDB::RawFile::CreateMSFStream(uint32_t streamIndex, uint32_t streamSize) const PDB_NO_EXCEPT
{
PDB_ASSERT(streamIndex != PDB::NilStreamIndex, "Invalid stream index.");
PDB_ASSERT(streamIndex < m_streamCount, "Invalid stream index.");
PDB_ASSERT(streamSize <= GetStreamSize(streamIndex), "Invalid stream size.");
return T(m_data, m_superBlock->blockSize, m_streamBlocks[streamIndex], streamSize);
}
// explicit template instantiation
template PDB::CoalescedMSFStream PDB::RawFile::CreateMSFStream<PDB::CoalescedMSFStream>(uint32_t streamIndex) const PDB_NO_EXCEPT;
template PDB::DirectMSFStream PDB::RawFile::CreateMSFStream<PDB::DirectMSFStream>(uint32_t streamIndex) const PDB_NO_EXCEPT;
template PDB::CoalescedMSFStream PDB::RawFile::CreateMSFStream<PDB::CoalescedMSFStream>(uint32_t streamIndex, uint32_t streamSize) const PDB_NO_EXCEPT;
template PDB::DirectMSFStream PDB::RawFile::CreateMSFStream<PDB::DirectMSFStream>(uint32_t streamIndex, uint32_t streamSize) const PDB_NO_EXCEPT;

View File

@@ -1,66 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "Foundation/PDB_Macros.h"
#include "PDB_CoalescedMSFStream.h"
// https://llvm.org/docs/PDB/index.html
namespace PDB
{
struct SuperBlock;
class PDB_NO_DISCARD RawFile
{
public:
RawFile(RawFile&& other) PDB_NO_EXCEPT;
RawFile& operator=(RawFile&& other) PDB_NO_EXCEPT;
explicit RawFile(const void* data) PDB_NO_EXCEPT;
~RawFile(void) PDB_NO_EXCEPT;
// Creates any type of MSF stream.
template <typename T>
PDB_NO_DISCARD T CreateMSFStream(uint32_t streamIndex) const PDB_NO_EXCEPT;
// Creates any type of MSF stream with the given size.
template <typename T>
PDB_NO_DISCARD T CreateMSFStream(uint32_t streamIndex, uint32_t streamSize) const PDB_NO_EXCEPT;
// Returns the SuperBlock.
PDB_NO_DISCARD inline const SuperBlock* GetSuperBlock(void) const PDB_NO_EXCEPT
{
return m_superBlock;
}
// Returns the number of streams in the PDB file.
PDB_NO_DISCARD inline uint32_t GetStreamCount(void) const PDB_NO_EXCEPT
{
return m_streamCount;
}
// Returns the size of the stream with the given index, taking into account nil page sizes.
PDB_NO_DISCARD inline uint32_t GetStreamSize(uint32_t streamIndex) const PDB_NO_EXCEPT
{
const uint32_t streamSize = m_streamSizes[streamIndex];
return (streamSize == NilPageSize) ? 0u : streamSize;
}
private:
const void* m_data;
const SuperBlock* m_superBlock;
CoalescedMSFStream m_directoryStream;
// stream directory
uint32_t m_streamCount;
const uint32_t* m_streamSizes;
const uint32_t** m_streamBlocks;
PDB_DISABLE_COPY(RawFile);
};
}

View File

@@ -1,25 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "PDB_PCH.h"
#include "PDB_SectionContributionStream.h"
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::SectionContributionStream::SectionContributionStream(void) PDB_NO_EXCEPT
: m_stream()
, m_contributions(nullptr)
, m_count(0u)
{
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::SectionContributionStream::SectionContributionStream(const DirectMSFStream& directStream, uint32_t size, uint32_t offset) PDB_NO_EXCEPT
: m_stream(directStream, size, offset)
, m_contributions(m_stream.GetDataAtOffset<DBI::SectionContribution>(0u))
, m_count(size / sizeof(DBI::SectionContribution))
{
}

View File

@@ -1,38 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "Foundation/PDB_Macros.h"
#include "Foundation/PDB_ArrayView.h"
#include "PDB_DBITypes.h"
#include "PDB_CoalescedMSFStream.h"
namespace PDB
{
class PDB_NO_DISCARD DirectMSFStream;
class PDB_NO_DISCARD SectionContributionStream
{
public:
SectionContributionStream(void) PDB_NO_EXCEPT;
explicit SectionContributionStream(const DirectMSFStream& directStream, uint32_t size, uint32_t offset) PDB_NO_EXCEPT;
PDB_DEFAULT_MOVE(SectionContributionStream);
// Returns a view of all section contributions in the stream.
PDB_NO_DISCARD inline ArrayView<DBI::SectionContribution> GetContributions(void) const PDB_NO_EXCEPT
{
return ArrayView<DBI::SectionContribution>(m_contributions, m_count);
}
private:
CoalescedMSFStream m_stream;
const DBI::SectionContribution* m_contributions;
size_t m_count;
PDB_DISABLE_COPY(SectionContributionStream);
};
}

View File

@@ -1,68 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#include "PDB_PCH.h"
#include "PDB_SourceFileStream.h"
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::SourceFileStream::SourceFileStream(void) PDB_NO_EXCEPT
: m_stream()
, m_moduleCount(0u)
, m_moduleIndices(nullptr)
, m_moduleFileCounts(nullptr)
, m_fileNameOffsets(nullptr)
, m_stringTable(nullptr)
{
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PDB::SourceFileStream::SourceFileStream(const DirectMSFStream& directStream, uint32_t size, uint32_t offset) PDB_NO_EXCEPT
: m_stream(directStream, size, offset)
, m_moduleCount(0u)
, m_moduleIndices(nullptr)
, m_moduleFileCounts(nullptr)
, m_fileNameOffsets(nullptr)
, m_stringTable(nullptr)
{
// we are going to consume the whole source info sub-stream, so create a coalesced stream for faster read operations and direct access.
// the sub-stream has the following layout:
// struct SourceInfoSubstream
// {
// uint16_t moduleCount;
// uint16_t sourceFileCount;
// uint16_t moduleIndices[moduleCount];
// uint16_t moduleFileCounts[moduleCount];
// uint32_t fileNameOffsets[realSourceFileCount];
// char stringTable[][realSourceFileCount];
// };
m_moduleCount = *m_stream.GetDataAtOffset<uint16_t>(0u);
size_t readOffset = sizeof(uint16_t);
// skip number of source files. this would only support 64k unique files and is no longer used.
// the number of source files is computed dynamically instead.
readOffset += sizeof(uint16_t);
// grab direct pointers into the stream data
m_moduleIndices = m_stream.GetDataAtOffset<uint16_t>(readOffset);
readOffset += sizeof(uint16_t) * m_moduleCount;
m_moduleFileCounts = m_stream.GetDataAtOffset<uint16_t>(readOffset);
readOffset += sizeof(uint16_t) * m_moduleCount;
// count the actual number of source files
size_t sourceFileCount = 0u;
for (unsigned int i = 0u; i < m_moduleCount; ++i)
{
sourceFileCount += m_moduleFileCounts[i];
}
m_fileNameOffsets = m_stream.GetDataAtOffset<uint32_t>(readOffset);
readOffset += sizeof(uint32_t) * sourceFileCount;
// grab a pointer into the string table
m_stringTable = m_stream.GetDataAtOffset<char>(readOffset);
}

View File

@@ -1,65 +0,0 @@
// Copyright 2011-2022, Molecular Matters GmbH <office@molecular-matters.com>
// See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause)
#pragma once
#include "Foundation/PDB_Macros.h"
#include "Foundation/PDB_ArrayView.h"
#include "PDB_CoalescedMSFStream.h"
namespace PDB
{
class PDB_NO_DISCARD DirectMSFStream;
class PDB_NO_DISCARD SourceFileStream
{
public:
SourceFileStream(void) PDB_NO_EXCEPT;
explicit SourceFileStream(const DirectMSFStream& directStream, uint32_t size, uint32_t offset) PDB_NO_EXCEPT;
PDB_DEFAULT_MOVE(SourceFileStream);
// Returns the number of modules.
PDB_NO_DISCARD inline uint32_t GetModuleCount(void) const PDB_NO_EXCEPT
{
return m_moduleCount;
}
// Returns a view of all the filename offsets for the module with the given index.
PDB_NO_DISCARD inline ArrayView<uint32_t> GetModuleFilenameOffsets(size_t moduleIndex) const PDB_NO_EXCEPT
{
const uint16_t moduleStartIndex = m_moduleIndices[moduleIndex];
const uint16_t moduleFileCount = m_moduleFileCounts[moduleIndex];
return ArrayView<uint32_t>(m_fileNameOffsets + moduleStartIndex, moduleFileCount);
}
// Returns a filename for the given filename offset.
PDB_NO_DISCARD inline const char* GetFilename(uint32_t filenameOffset) const PDB_NO_EXCEPT
{
return m_stringTable + filenameOffset;
}
private:
CoalescedMSFStream m_stream;
// the number of modules
uint32_t m_moduleCount;
// the indices into the file name offsets, for each module
const uint16_t* m_moduleIndices;
// the number of files, for each module
const uint16_t* m_moduleFileCounts;
// the filename offsets into the string table, for all modules
const uint32_t* m_fileNameOffsets;
// the string table storing all filenames
const char* m_stringTable;
PDB_DISABLE_COPY(SourceFileStream);
};
}

Some files were not shown because too many files have changed in this diff Show More