edb-debugger/0000755000175000017500000000000013765535721012726 5ustar eteraneteranedb-debugger/libPE/0000755000175000017500000000000013765535463013724 5ustar eteraneteranedb-debugger/libPE/CMakeLists.txt0000644000175000017500000000035013765535463016462 0ustar eteraneterancmake_minimum_required(VERSION 3.1) add_library(PE INTERFACE ) target_include_directories(PE INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include ) target_sources(PE INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include/libPE/pe_binary.h ) edb-debugger/libPE/include/0000755000175000017500000000000013765535463015347 5ustar eteraneteranedb-debugger/libPE/include/libPE/0000755000175000017500000000000013765535463016342 5ustar eteraneteranedb-debugger/libPE/include/libPE/pe_binary.h0000644000175000017500000001004713765535463020465 0ustar eteraneteran/* Copyright (C) 2012 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PE_BINARY_H_20121007_ #define PE_BINARY_H_20121007_ #include #ifdef WIN32 #include #else using BYTE = uint8_t; using BOOLEAN = uint8_t; using SHORT = int16_t; using WORD = uint16_t; using USHORT = uint16_t; using INT = int32_t; using LONG = int32_t; using DWORD = uint32_t; using ULONG = uint32_t; using UINT = uint32_t; using ULONGLONG = uint64_t; using LONGLONG = int64_t; constexpr int IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16; #endif namespace libPE { struct IMAGE_DOS_HEADER { WORD e_magic; // "MZ" WORD e_cblp; WORD e_cp; WORD e_crlc; WORD e_cparhdr; WORD e_minalloc; WORD e_maxalloc; WORD e_ss; WORD e_sp; WORD e_csum; WORD e_ip; WORD e_cs; WORD e_lfarlc; WORD e_ovno; WORD e_res[4]; WORD e_oemid; WORD e_oeminfo; WORD e_res2[10]; LONG e_lfanew; }; struct IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; DWORD Size; }; struct IMAGE_OPTIONAL_HEADER64 { WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; ULONGLONG ImageBase; DWORD SectionAlignment; DWORD FileAlignment; WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Win32VersionValue; DWORD SizeOfImage; DWORD SizeOfHeaders; DWORD CheckSum; WORD Subsystem; WORD DllCharacteristics; ULONGLONG SizeOfStackReserve; ULONGLONG SizeOfStackCommit; ULONGLONG SizeOfHeapReserve; ULONGLONG SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; }; struct IMAGE_OPTIONAL_HEADER32 { WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; DWORD BaseOfData; DWORD ImageBase; DWORD SectionAlignment; DWORD FileAlignment; WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Win32VersionValue; DWORD SizeOfImage; DWORD SizeOfHeaders; DWORD CheckSum; WORD Subsystem; WORD DllCharacteristics; DWORD SizeOfStackReserve; DWORD SizeOfStackCommit; DWORD SizeOfHeapReserve; DWORD SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; }; struct IMAGE_FILE_HEADER { WORD Machine; WORD NumberOfSections; DWORD TimeDateStamp; DWORD PointerToSymbolTable; DWORD NumberOfSymbols; WORD SizeOfOptionalHeader; WORD Characteristics; }; struct IMAGE_NT_HEADERS32 { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER32 OptionalHeader; }; struct IMAGE_NT_HEADERS64 { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER64 OptionalHeader; }; } #endif edb-debugger/edb.10000644000175000017500000000347413765535463013555 0ustar eteraneteran.\"Text automatically generated by txt2man .TH EDB 1 "December 2011" "" "" .SH NAME \fBedb \fP- graphical debugger and disassembler for executables .SH SYNOPSIS .nf .fam C \fBedb\fP [OPTION]\.\.\. [\fITARGET\fP] .fam T .fi .fam T .fi .SH DESCRIPTION \fBedb\fP (Evan's Debugger) is a modular and modern disassembler and debugger for binary ELF files based on ptrace API and the capstone disassembly library. .TP .B \fB--help\fP Show usage and exit. .TP .B \fB--symbols\fP generate symbols map for file .TP .B \fB--attach\fP attach the process of PID to debugger .TP .B \fB--run\fP [args\.\.\.] open in debugger with optional [args\.\.\.] .TP .B \fB--version\fP show version string and exit. .TP .B \fB--dump-version\fP show version and exit. .SH EXAMPLE \fBedb\fP \fB--symbols\fP /lib/libc.so.6 > libc.so.6.map .PP .nf .fam C Will generate symbols for libc and save it in a text file. It's useful if you store this map files in the symbols directory configured in edb's preferences. .fam T .fi for i in $(ls /lib); do \fBedb\fP \fB--symbols\fP $i > $(basename $i).map; done .PP .nf .fam C Useful to generate maps for all libs you have in /lib. .fam T .fi \fBedb\fP \fB--run\fP /bin/ls .PP .nf .fam C Will open the ls program binary in debugger. .fam T .fi \fBedb\fP \fB--attach\fP 1720 .PP .nf .fam C Attach the process of PID 1720 to debugger. .fam T .fi .SH AUTHOR Written by Evan Teran .SH REPORTING BUGS Report any bugs or requests for features via BTS on https://github.com/eteran/edb-debugger/issues .SH COPYRIGHT Copyright © 2008 CodeF00. Licensed GPLv2: GNU GPL version 2 . This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. edb-debugger/appveyor.yml0000644000175000017500000000336713765535463015332 0ustar eteraneteranversion: "{build}" environment: CAPSTONE_SDK: C:\capstone\sdk matrix: - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 BOOST_INCLUDEDIR: C:\Libraries\boost_1_64_0 CAPSTONE_ARCHIVE: capstone-3.0.5-rc2-win64 CMAKE_GENERATOR: Visual Studio 15 2017 Win64 QT_BASEDIR: C:\Qt\5.9\msvc2017_64 configuration: - Debug - Release install: - ps: new-item -itemtype directory -path C:\capstone\sdk | out-null - ps: new-item -itemtype directory -path C:\capstone\sdk\include\capstone | out-null - ps: new-item -itemtype directory -path C:\capstone\sdk\lib | out-null - ps: "[Environment]::CurrentDirectory = 'C:\\capstone'" - ps: (new-object net.webclient).DownloadFile("https://github.com/aquynh/capstone/releases/download/3.0.5-rc2/${env:CAPSTONE_ARCHIVE}.zip", 'capstone.zip') - ps: expand-archive C:\capstone\capstone.zip -destinationpath C:\capstone - ps: copy-item C:\capstone\${env:CAPSTONE_ARCHIVE}\include\*.h C:\capstone\sdk\include\capstone - ps: copy-item C:\capstone\${env:CAPSTONE_ARCHIVE}\capstone.lib C:\capstone\sdk\lib\capstone_dll.lib before_build: - cmd: git submodule update --init - cmd: cd C:\projects - cmd: md build - cmd: cd build - cmd: cmake -Wno-dev -G "%CMAKE_GENERATOR%" -DCMAKE_BUILD_TYPE=%configuration% -DCMAKE_INSTALL_PREFIX=C:\projects\install -DBOOST_INCLUDEDIR="%BOOST_INCLUDEDIR%" -DCAPSTONE_SDK="%CAPSTONE_SDK%" -DQt5Core_DIR="%QT_BASEDIR%\lib\cmake\Qt5Core" -DQt5_DIR="%QT_BASEDIR%\lib\cmake\Qt5" ..\edb-debugger build_script: - cmd: msbuild C:\projects\build\edb.sln /t:edb /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" - cmd: msbuild C:\projects\build\edb.sln /t:DebuggerCore /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" edb-debugger/BUGS0000644000175000017500000000014513765535463013414 0ustar eteraneteranThis file has been superseded by the issue tracker at: https://github.com/eteran/edb-debugger/issues edb-debugger/libELF/0000755000175000017500000000000013765535463014026 5ustar eteraneteranedb-debugger/libELF/CMakeLists.txt0000644000175000017500000000224213765535463016566 0ustar eteraneterancmake_minimum_required(VERSION 3.1) add_library(ELF INTERFACE ) target_include_directories(ELF INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include ) target_sources(ELF INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include/libELF/elf_model.h ${CMAKE_CURRENT_SOURCE_DIR}/include/libELF/elf_auxv.h ${CMAKE_CURRENT_SOURCE_DIR}/include/libELF/elf_binary.h ${CMAKE_CURRENT_SOURCE_DIR}/include/libELF/elf_dyn.h ${CMAKE_CURRENT_SOURCE_DIR}/include/libELF/elf_header.h ${CMAKE_CURRENT_SOURCE_DIR}/include/libELF/elf_move.h ${CMAKE_CURRENT_SOURCE_DIR}/include/libELF/elf_nhdr.h ${CMAKE_CURRENT_SOURCE_DIR}/include/libELF/elf_phdr.h ${CMAKE_CURRENT_SOURCE_DIR}/include/libELF/elf_rela.h ${CMAKE_CURRENT_SOURCE_DIR}/include/libELF/elf_rel.h ${CMAKE_CURRENT_SOURCE_DIR}/include/libELF/elf_shdr.h ${CMAKE_CURRENT_SOURCE_DIR}/include/libELF/elf_sym.h ${CMAKE_CURRENT_SOURCE_DIR}/include/libELF/elf_syminfo.h ${CMAKE_CURRENT_SOURCE_DIR}/include/libELF/elf_types.h ${CMAKE_CURRENT_SOURCE_DIR}/include/libELF/elf_verdaux.h ${CMAKE_CURRENT_SOURCE_DIR}/include/libELF/elf_verdef.h ${CMAKE_CURRENT_SOURCE_DIR}/include/libELF/elf_vernaux.h ${CMAKE_CURRENT_SOURCE_DIR}/include/libELF/elf_verneed.h ) edb-debugger/libELF/include/0000755000175000017500000000000013765535463015451 5ustar eteraneteranedb-debugger/libELF/include/libELF/0000755000175000017500000000000013765535463016546 5ustar eteraneteranedb-debugger/libELF/include/libELF/elf_auxv.h0000644000175000017500000000764713765535463020546 0ustar eteraneteran/* Copyright (C) 2012 - 2015 Evan Teran evan.teran@gmail.com Copyright (C) 1995-2003,2004,2005,2006,2007,2008,2009,2010,2011 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ELF_AUXV_H_20121007_ #define ELF_AUXV_H_20121007_ #include "elf_types.h" /* Auxiliary vector. */ /* This vector is normally only used by the program interpreter. The usual definition in an ABI supplement uses the name auxv_t. The vector is not usually defined in a standard file, but it can't hurt. We rename it to avoid conflicts. The sizes of these types are an arrangement between the exec server and the program interpreter, so we don't fully specify them here. */ struct elf32_auxv_t { uint32_t a_type; /* Entry type */ union { uint32_t a_val; /* Integer value */ /* We use to have pointer elements added here. We cannot do that, though, since it does not work when using 32-bit definitions on 64-bit platforms and vice versa. */ } a_un; }; struct elf64_auxv_t { uint64_t a_type; /* Entry type */ union { uint64_t a_val; /* Integer value */ /* We use to have pointer elements added here. We cannot do that, though, since it does not work when using 32-bit definitions on 64-bit platforms and vice versa. */ } a_un; }; // Legal values for a_type (entry type). enum { AT_NULL = 0, // End of vector AT_IGNORE = 1, // Entry should be ignored AT_EXECFD = 2, // File descriptor of program AT_PHDR = 3, // Program headers for program AT_PHENT = 4, // Size of program header entry AT_PHNUM = 5, // Number of program headers AT_PAGESZ = 6, // System page size AT_BASE = 7, // Base address of interpreter AT_FLAGS = 8, // Flags AT_ENTRY = 9, // Entry point of program AT_NOTELF = 10, // Program is not ELF AT_UID = 11, // Real uid AT_EUID = 12, // Effective uid AT_GID = 13, // Real gid AT_EGID = 14, // Effective gid AT_CLKTCK = 17, // Frequency of times() // Some more special a_type values describing the hardware. AT_PLATFORM = 15, // String identifying platform. AT_HWCAP = 16, // Machine dependent hints about processor capabilities. // This entry gives some information about the FPU initialization // performed by the kernel. AT_FPUCW = 18, // Used FPU control word. // Cache block sizes. AT_DCACHEBSIZE = 19, // Data cache block size. AT_ICACHEBSIZE = 20, // Instruction cache block size. AT_UCACHEBSIZE = 21, // Unified cache block size. // A special ignored value for PPC, used by the kernel to control the // interpretation of the AUXV. Must be > 16. AT_IGNOREPPC = 22, // Entry should be ignored. AT_SECURE = 23, // Boolean, was exec setuid-like? AT_BASE_PLATFORM = 24, // String identifying real platforms. AT_RANDOM = 25, // Address of 16 random bytes. AT_EXECFN = 31, // Filename of executable. // Pointer to the global system page used for system calls and other // nice things. AT_SYSINFO = 32, AT_SYSINFO_EHDR = 33, // Shapes of the caches. Bits 0-3 contains associativity; bits 4-7 contains // log2 of line size; mask those to get cache size. AT_L1I_CACHESHAPE = 34, AT_L1D_CACHESHAPE = 35, AT_L2_CACHESHAPE = 36, AT_L3_CACHESHAPE = 37 }; #endif edb-debugger/libELF/include/libELF/elf_header.h0000644000175000017500000002305013765535463020775 0ustar eteraneteran/* Copyright (C) 2012 - 2015 Evan Teran evan.teran@gmail.com Copyright (C) 1995-2003,2004,2005,2006,2007,2008,2009,2010,2011 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ELF_HEADER_H_20121007_ #define ELF_HEADER_H_20121007_ #include "elf_types.h" // The ELF file header. This appears at the start of every ELF file. enum { EI_NIDENT = 16 }; /* Fields in the e_ident array. The EI_* macros are indices into the array. The macros under each EI_* macro are the values the byte may have. */ enum { EI_MAG0 = 0, /* File identification byte 0 index */ EI_MAG1 = 1, /* File identification byte 1 index */ EI_MAG2 = 2, /* File identification byte 2 index */ EI_MAG3 = 3, /* File identification byte 3 index */ }; #define ELFMAG0 0x7f /* Magic number byte 0 */ #define ELFMAG1 'E' /* Magic number byte 1 */ #define ELFMAG2 'L' /* Magic number byte 2 */ #define ELFMAG3 'F' /* Magic number byte 3 */ /* Conglomeration of the identification bytes, for easy testing as a word. */ #define ELFMAG "\177ELF" #define SELFMAG 4 enum { EI_CLASS = 4, /* File class byte index */ ELFCLASSNONE = 0, /* Invalid class */ ELFCLASS32 = 1, /* 32-bit objects */ ELFCLASS64 = 2, /* 64-bit objects */ ELFCLASSNUM = 3, EI_DATA = 5, /* Data encoding byte index */ ELFDATANONE = 0, /* Invalid data encoding */ ELFDATA2LSB = 1, /* 2's complement, little endian */ ELFDATA2MSB = 2, /* 2's complement, big endian */ ELFDATANUM = 3, EI_VERSION = 6, /* File version byte index */ /* Value must be EV_CURRENT */ }; /* Legal values for e_version (version). */ enum { EV_NONE = 0, /* Invalid ELF version */ EV_CURRENT = 1, /* Current version */ EV_NUM = 2, }; enum { EI_OSABI = 7, /* OS ABI identification */ ELFOSABI_NONE = 0, /* UNIX System V ABI */ ELFOSABI_SYSV = 0, /* Alias. */ ELFOSABI_HPUX = 1, /* HP-UX */ ELFOSABI_NETBSD = 2, /* NetBSD. */ ELFOSABI_GNU = 3, /* Object uses GNU ELF extensions. */ ELFOSABI_LINUX = ELFOSABI_GNU, /* Compatibility alias. */ ELFOSABI_SOLARIS = 6, /* Sun Solaris. */ ELFOSABI_AIX = 7, /* IBM AIX. */ ELFOSABI_IRIX = 8, /* SGI Irix. */ ELFOSABI_FREEBSD = 9, /* FreeBSD. */ ELFOSABI_TRU64 = 10, /* Compaq TRU64 UNIX. */ ELFOSABI_MODESTO = 11, /* Novell Modesto. */ ELFOSABI_OPENBSD = 12, /* OpenBSD. */ ELFOSABI_ARM_AEABI = 64, /* ARM EABI */ ELFOSABI_ARM = 97, /* ARM */ ELFOSABI_STANDALONE = 255, /* Standalone (embedded) application */ EI_ABIVERSION = 8, /* ABI version */ EI_PAD = 9, /* Byte index of padding bytes */ }; /* Legal values for e_type (object file type). */ enum { ET_NONE = 0, /* No file type */ ET_REL = 1, /* Relocatable file */ ET_EXEC = 2, /* Executable file */ ET_DYN = 3, /* Shared object file */ ET_CORE = 4, /* Core file */ ET_NUM = 5, /* Number of defined types */ ET_LOOS = 0xfe00, /* OS-specific range start */ ET_HIOS = 0xfeff, /* OS-specific range end */ ET_LOPROC = 0xff00, /* Processor-specific range start */ ET_HIPROC = 0xffff, /* Processor-specific range end */ }; /* Legal values for e_machine (architecture). */ enum { EM_NONE = 0, /* No machine */ EM_M32 = 1, /* AT&T WE 32100 */ EM_SPARC = 2, /* SUN SPARC */ EM_386 = 3, /* Intel 80386 */ EM_68K = 4, /* Motorola m68k family */ EM_88K = 5, /* Motorola m88k family */ EM_860 = 7, /* Intel 80860 */ EM_MIPS = 8, /* MIPS R3000 big-endian */ EM_S370 = 9, /* IBM System/370 */ EM_MIPS_RS3_LE = 10, /* MIPS R3000 little-endian */ EM_PARISC = 15, /* HPPA */ EM_VPP500 = 17, /* Fujitsu VPP500 */ EM_SPARC32PLUS = 18, /* Sun's "v8plus" */ EM_960 = 19, /* Intel 80960 */ EM_PPC = 20, /* PowerPC */ EM_PPC64 = 21, /* PowerPC 64-bit */ EM_S390 = 22, /* IBM S390 */ EM_V800 = 36, /* NEC V800 series */ EM_FR20 = 37, /* Fujitsu FR20 */ EM_RH32 = 38, /* TRW RH-32 */ EM_RCE = 39, /* Motorola RCE */ EM_ARM = 40, /* ARM */ EM_FAKE_ALPHA = 41, /* Digital Alpha */ EM_SH = 42, /* Hitachi SH */ EM_SPARCV9 = 43, /* SPARC v9 64-bit */ EM_TRICORE = 44, /* Siemens Tricore */ EM_ARC = 45, /* Argonaut RISC Core */ EM_H8_300 = 46, /* Hitachi H8/300 */ EM_H8_300H = 47, /* Hitachi H8/300H */ EM_H8S = 48, /* Hitachi H8S */ EM_H8_500 = 49, /* Hitachi H8/500 */ EM_IA_64 = 50, /* Intel Merced */ EM_MIPS_X = 51, /* Stanford MIPS-X */ EM_COLDFIRE = 52, /* Motorola Coldfire */ EM_68HC12 = 53, /* Motorola M68HC12 */ EM_MMA = 54, /* Fujitsu MMA Multimedia Accelerator*/ EM_PCP = 55, /* Siemens PCP */ EM_NCPU = 56, /* Sony nCPU embeeded RISC */ EM_NDR1 = 57, /* Denso NDR1 microprocessor */ EM_STARCORE = 58, /* Motorola Start*Core processor */ EM_ME16 = 59, /* Toyota ME16 processor */ EM_ST100 = 60, /* STMicroelectronic ST100 processor */ EM_TINYJ = 61, /* Advanced Logic Corp. Tinyj emb.fam*/ EM_X86_64 = 62, /* AMD x86-64 architecture */ EM_PDSP = 63, /* Sony DSP Processor */ EM_FX66 = 66, /* Siemens FX66 microcontroller */ EM_ST9PLUS = 67, /* STMicroelectronics ST9+ 8/16 mc */ EM_ST7 = 68, /* STmicroelectronics ST7 8 bit mc */ EM_68HC16 = 69, /* Motorola MC68HC16 microcontroller */ EM_68HC11 = 70, /* Motorola MC68HC11 microcontroller */ EM_68HC08 = 71, /* Motorola MC68HC08 microcontroller */ EM_68HC05 = 72, /* Motorola MC68HC05 microcontroller */ EM_SVX = 73, /* Silicon Graphics SVx */ EM_ST19 = 74, /* STMicroelectronics ST19 8 bit mc */ EM_VAX = 75, /* Digital VAX */ EM_CRIS = 76, /* Axis Communications 32-bit embedded processor */ EM_JAVELIN = 77, /* Infineon Technologies 32-bit embedded processor */ EM_FIREPATH = 78, /* Element 14 64-bit DSP Processor */ EM_ZSP = 79, /* LSI Logic 16-bit DSP Processor */ EM_MMIX = 80, /* Donald Knuth's educational 64-bit processor */ EM_HUANY = 81, /* Harvard University machine-independent object files */ EM_PRISM = 82, /* SiTera Prism */ EM_AVR = 83, /* Atmel AVR 8-bit microcontroller */ EM_FR30 = 84, /* Fujitsu FR30 */ EM_D10V = 85, /* Mitsubishi D10V */ EM_D30V = 86, /* Mitsubishi D30V */ EM_V850 = 87, /* NEC v850 */ EM_M32R = 88, /* Mitsubishi M32R */ EM_MN10300 = 89, /* Matsushita MN10300 */ EM_MN10200 = 90, /* Matsushita MN10200 */ EM_PJ = 91, /* picoJava */ EM_OPENRISC = 92, /* OpenRISC 32-bit embedded processor */ EM_ARC_A5 = 93, /* ARC Cores Tangent-A5 */ EM_XTENSA = 94, /* Tensilica Xtensa Architecture */ EM_NUM = 95, /* If it is necessary to assign new unofficial EM_* values, please pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the chances of collision with official or non-GNU unofficial values. */ EM_ALPHA = 0x9026, }; struct elf32_phdr; struct elf32_header { using elf_phdr = elf32_phdr; enum { ELFCLASS = ELFCLASS32 }; uint8_t e_ident[EI_NIDENT]; /* Magic number and other info */ elf32_half e_type; /* Object file type */ elf32_half e_machine; /* Architecture */ elf32_word e_version; /* Object file version */ elf32_addr e_entry; /* Entry point virtual address */ elf32_off e_phoff; /* Program header table file offset */ elf32_off e_shoff; /* Section header table file offset */ elf32_word e_flags; /* Processor-specific flags */ elf32_half e_ehsize; /* ELF header size in bytes */ elf32_half e_phentsize; /* Program header table entry size */ elf32_half e_phnum; /* Program header table entry count */ elf32_half e_shentsize; /* Section header table entry size */ elf32_half e_shnum; /* Section header table entry count */ elf32_half e_shstrndx; /* Section header string table index */ }; struct elf64_phdr; struct elf64_header { using elf_phdr = elf64_phdr; enum { ELFCLASS = ELFCLASS64 }; uint8_t e_ident[EI_NIDENT]; /* Magic number and other info */ elf64_half e_type; /* Object file type */ elf64_half e_machine; /* Architecture */ elf64_word e_version; /* Object file version */ elf64_addr e_entry; /* Entry point virtual address */ elf64_off e_phoff; /* Program header table file offset */ elf64_off e_shoff; /* Section header table file offset */ elf64_word e_flags; /* Processor-specific flags */ elf64_half e_ehsize; /* ELF header size in bytes */ elf64_half e_phentsize; /* Program header table entry size */ elf64_half e_phnum; /* Program header table entry count */ elf64_half e_shentsize; /* Section header table entry size */ elf64_half e_shnum; /* Section header table entry count */ elf64_half e_shstrndx; /* Section header string table index */ }; #endif edb-debugger/libELF/include/libELF/elf_verdaux.h0000644000175000017500000000234413765535463021226 0ustar eteraneteran/* Copyright (C) 2012 - 2015 Evan Teran evan.teran@gmail.com Copyright (C) 1995-2003,2004,2005,2006,2007,2008,2009,2010,2011 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ELF_VERDAUX_H_20121007_ #define ELF_VERDAUX_H_20121007_ #include "elf_types.h" /* Auxialiary version information. */ struct elf32_verdaux { elf32_word vda_name; /* Version or dependency names */ elf32_word vda_next; /* Offset in bytes to next verdaux entry */ }; struct elf64_verdaux { elf64_word vda_name; /* Version or dependency names */ elf64_word vda_next; /* Offset in bytes to next verdaux entry */ }; #endif edb-debugger/libELF/include/libELF/elf_vernaux.h0000644000175000017500000000323013765535463021233 0ustar eteraneteran/* Copyright (C) 2012 - 2015 Evan Teran evan.teran@gmail.com Copyright (C) 1995-2003,2004,2005,2006,2007,2008,2009,2010,2011 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ELF_VERNAUX_H_20121007_ #define ELF_VERNAUX_H_20121007_ #include "elf_types.h" /* Auxiliary needed version information. */ struct elf32_vernaux { elf32_word vna_hash; /* Hash value of dependency name */ elf32_half vna_flags; /* Dependency specific information */ elf32_half vna_other; /* Unused */ elf32_word vna_name; /* Dependency name string offset */ elf32_word vna_next; /* Offset in bytes to next vernaux entry */ }; struct elf64_vernaux { elf64_word vna_hash; /* Hash value of dependency name */ elf64_half vna_flags; /* Dependency specific information */ elf64_half vna_other; /* Unused */ elf64_word vna_name; /* Dependency name string offset */ elf64_word vna_next; /* Offset in bytes to next vernaux entry */ }; /* Legal values for vna_flags. */ #if 0 enum { VER_FLG_WEAK = 0x2 /* Weak version identifier */ }; #endif #endif edb-debugger/libELF/include/libELF/elf_syminfo.h0000644000175000017500000001140613765535463021233 0ustar eteraneteran/* Copyright (C) 2012 - 2015 Evan Teran evan.teran@gmail.com Copyright (C) 1995-2003,2004,2005,2006,2007,2008,2009,2010,2011 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ELF_SYMINFO_H_20121007_ #define ELF_SYMINFO_H_20121007_ #include "elf_types.h" /* The syminfo section if available contains additional information about every dynamic symbol. */ struct elf32_syminfo { elf32_half si_boundto; /* Direct bindings, symbol bound to */ elf32_half si_flags; /* Per symbol flags */ }; struct elf64_syminfo { elf64_half si_boundto; /* Direct bindings, symbol bound to */ elf64_half si_flags; /* Per symbol flags */ }; /* Possible values for si_boundto. */ enum { SYMINFO_BT_SELF = 0xffff, /* Symbol bound to self */ SYMINFO_BT_PARENT = 0xfffe, /* Symbol bound to parent */ SYMINFO_BT_LOWRESERVE = 0xff00, /* Beginning of reserved entries */ }; /* Possible bitmasks for si_flags. */ enum { SYMINFO_FLG_DIRECT = 0x0001, /* Direct bound symbol */ SYMINFO_FLG_PASSTHRU = 0x0002, /* Pass-thru symbol for translator */ SYMINFO_FLG_COPY = 0x0004, /* Symbol is a copy-reloc */ SYMINFO_FLG_LAZYLOAD = 0x0008, /* Symbol bound to object to be lazy loaded */ }; /* Syminfo version values. */ enum { SYMINFO_NONE = 0, SYMINFO_CURRENT = 1, SYMINFO_NUM = 2, }; /* How to extract and insert information held in the st_info field. */ template constexpr auto ELF32_ST_BIND(T val) { return static_cast(val) >> 4; } template constexpr uint8_t ELF32_ST_TYPE(T val) { return val & 0xf; } template constexpr auto ELF32_ST_INFO(T1 bind, T2 type) { return (bind << 4) + (type & 0xf); } /* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ template constexpr uint8_t ELF64_ST_BIND(T val) { return ELF32_ST_BIND(val); } template constexpr uint8_t ELF64_ST_TYPE(T val) { return ELF32_ST_TYPE(val); } template constexpr auto ELF64_ST_INFO(T1 bind, T2 type) { return ELF32_ST_INFO(bind, type); } /* Legal values for ST_BIND subfield of st_info (symbol binding). */ enum { STB_LOCAL = 0, /* Local symbol */ STB_GLOBAL = 1, /* Global symbol */ STB_WEAK = 2, /* Weak symbol */ STB_NUM = 3, /* Number of defined types. */ STB_LOOS = 10, /* Start of OS-specific */ STB_GNU_UNIQUE = 10, /* Unique symbol. */ STB_HIOS = 12, /* End of OS-specific */ STB_LOPROC = 13, /* Start of processor-specific */ STB_HIPROC = 15, /* End of processor-specific */ }; /* Legal values for ST_TYPE subfield of st_info (symbol type). */ enum { STT_NOTYPE = 0, /* Symbol type is unspecified */ STT_OBJECT = 1, /* Symbol is a data object */ STT_FUNC = 2, /* Symbol is a code object */ STT_SECTION = 3, /* Symbol associated with a section */ STT_FILE = 4, /* Symbol's name is file name */ STT_COMMON = 5, /* Symbol is a common data object */ STT_TLS = 6, /* Symbol is thread-local data object*/ STT_NUM = 7, /* Number of defined types. */ STT_LOOS = 10, /* Start of OS-specific */ STT_GNU_IFUNC = 10, /* Symbol is indirect code object */ STT_HIOS = 12, /* End of OS-specific */ STT_LOPROC = 13, /* Start of processor-specific */ STT_HIPROC = 15, /* End of processor-specific */ }; /* Symbol table indices are found in the hash buckets and chain table of a symbol hash table section. This special index value indicates the end of a chain, meaning no further symbols are found in that bucket. */ enum { STN_UNDEF = 0, /* End of a chain. */ }; /* How to extract and insert information held in the st_other field. */ template constexpr T ELF32_ST_VISIBILITY(T o) { return ((o)&0x03); } /* For ELF64 the definitions are the same. */ template constexpr T ELF64_ST_VISIBILITY(T o) { return ELF32_ST_VISIBILITY(o); } /* Symbol visibility specification encoded in the st_other field. */ enum { STV_DEFAULT = 0, /* Default symbol visibility rules */ STV_INTERNAL = 1, /* Processor specific hidden class */ STV_HIDDEN = 2, /* Sym unavailable in other modules */ STV_PROTECTED = 3, /* Not preemptible, not exported */ }; #endif edb-debugger/libELF/include/libELF/elf_shdr.h0000644000175000017500000001414413765535463020511 0ustar eteraneteran/* Copyright (C) 2012 - 2015 Evan Teran evan.teran@gmail.com Copyright (C) 1995-2003,2004,2005,2006,2007,2008,2009,2010,2011 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ELF_SHDR_H_20121007_ #define ELF_SHDR_H_20121007_ #include "elf_types.h" // Section header. struct elf32_shdr { elf32_word sh_name; /* Section name (string tbl index) */ elf32_word sh_type; /* Section type */ elf32_word sh_flags; /* Section flags */ elf32_addr sh_addr; /* Section virtual addr at execution */ elf32_off sh_offset; /* Section file offset */ elf32_word sh_size; /* Section size in bytes */ elf32_word sh_link; /* Link to another section */ elf32_word sh_info; /* Additional section information */ elf32_word sh_addralign; /* Section alignment */ elf32_word sh_entsize; /* Entry size if section holds table */ }; struct elf64_shdr { elf64_word sh_name; /* Section name (string tbl index) */ elf64_word sh_type; /* Section type */ elf64_xword sh_flags; /* Section flags */ elf64_addr sh_addr; /* Section virtual addr at execution */ elf64_off sh_offset; /* Section file offset */ elf64_xword sh_size; /* Section size in bytes */ elf64_word sh_link; /* Link to another section */ elf64_word sh_info; /* Additional section information */ elf64_xword sh_addralign; /* Section alignment */ elf64_xword sh_entsize; /* Entry size if section holds table */ }; /* Special section indices. */ enum { SHN_UNDEF = 0, /* Undefined section */ SHN_LORESERVE = 0xff00, /* Start of reserved indices */ SHN_LOPROC = 0xff00, /* Start of processor-specific */ SHN_BEFORE = 0xff00, /* Order section before all others (Solaris). */ SHN_AFTER = 0xff01, /* Order section after all others (Solaris). */ SHN_HIPROC = 0xff1f, /* End of processor-specific */ SHN_LOOS = 0xff20, /* Start of OS-specific */ SHN_HIOS = 0xff3f, /* End of OS-specific */ SHN_ABS = 0xfff1, /* Associated symbol is absolute */ SHN_COMMON = 0xfff2, /* Associated symbol is common */ SHN_XINDEX = 0xffff, /* Index is in extra table. */ SHN_HIRESERVE = 0xffff, /* End of reserved indices */ }; /* Legal values for sh_type (section type). */ enum { SHT_NULL = 0, /* Section header table entry unused */ SHT_PROGBITS = 1, /* Program data */ SHT_SYMTAB = 2, /* Symbol table */ SHT_STRTAB = 3, /* String table */ SHT_RELA = 4, /* Relocation entries with addends */ SHT_HASH = 5, /* Symbol hash table */ SHT_DYNAMIC = 6, /* Dynamic linking information */ SHT_NOTE = 7, /* Notes */ SHT_NOBITS = 8, /* Program space with no data (bss) */ SHT_REL = 9, /* Relocation entries, no addends */ SHT_SHLIB = 10, /* Reserved */ SHT_DYNSYM = 11, /* Dynamic linker symbol table */ SHT_INIT_ARRAY = 14, /* Array of constructors */ SHT_FINI_ARRAY = 15, /* Array of destructors */ SHT_PREINIT_ARRAY = 16, /* Array of pre-constructors */ SHT_GROUP = 17, /* Section group */ SHT_SYMTAB_SHNDX = 18, /* Extended section indeces */ SHT_NUM = 19, /* Number of defined types. */ SHT_LOOS = 0x60000000, /* Start OS-specific. */ SHT_GNU_ATTRIBUTES = 0x6ffffff5, /* Object attributes. */ SHT_GNU_HASH = 0x6ffffff6, /* GNU-style hash table. */ SHT_GNU_LIBLIST = 0x6ffffff7, /* Prelink library list */ SHT_CHECKSUM = 0x6ffffff8, /* Checksum for DSO content. */ SHT_LOSUNW = 0x6ffffffa, /* Sun-specific low bound. */ SHT_SUNW_move = 0x6ffffffa, SHT_SUNW_COMDAT = 0x6ffffffb, SHT_SUNW_syminfo = 0x6ffffffc, SHT_GNU_verdef = 0x6ffffffd, /* Version definition section. */ SHT_GNU_verneed = 0x6ffffffe, /* Version needs section. */ SHT_GNU_versym = 0x6fffffff, /* Version symbol table. */ SHT_HISUNW = 0x6fffffff, /* Sun-specific high bound. */ SHT_HIOS = 0x6fffffff, /* End OS-specific type */ SHT_LOPROC = 0x70000000, /* Start of processor-specific */ SHT_HIPROC = 0x7fffffff, /* End of processor-specific */ SHT_LOUSER = 0x80000000, /* Start of application-specific */ SHT_HIUSER = 0x8fffffff, /* End of application-specific */ }; /* Legal values for sh_flags (section flags). */ enum { SHF_WRITE = (1U << 0), /* Writable */ SHF_ALLOC = (1U << 1), /* Occupies memory during execution */ SHF_EXECINSTR = (1U << 2), /* Executable */ SHF_MERGE = (1U << 4), /* Might be merged */ SHF_STRINGS = (1U << 5), /* Contains nul-terminated strings */ SHF_INFO_LINK = (1U << 6), /* `sh_info' contains SHT index */ SHF_LINK_ORDER = (1U << 7), /* Preserve order after combining */ SHF_OS_NONCONFORMING = (1U << 8), /* Non-standard OS specific handling required */ SHF_GROUP = (1U << 9), /* Section is member of a group. */ SHF_TLS = (1U << 10), /* Section hold thread-local data. */ SHF_MASKOS = 0x0ff00000, /* OS-specific. */ SHF_MASKPROC = 0xf0000000, /* Processor-specific */ SHF_ORDERED = (1U << 30), /* Special ordering requirement (Solaris). */ SHF_EXCLUDE = (1U << 31), /* Section is excluded unless referenced or allocated (Solaris).*/ }; /* Section group handling. */ enum { GRP_COMDAT = 0x1, /* Mark group as COMDAT. */ }; #endif edb-debugger/libELF/include/libELF/elf_sym.h0000644000175000017500000000300113765535463020347 0ustar eteraneteran/* Copyright (C) 2012 - 2015 Evan Teran evan.teran@gmail.com Copyright (C) 1995-2003,2004,2005,2006,2007,2008,2009,2010,2011 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ELF_SYM_H_20121007_ #define ELF_SYM_H_20121007_ #include "elf_types.h" // Symbol table entry. struct elf32_sym { elf32_word st_name; // Symbol name (string tbl index) elf32_addr st_value; // Symbol value elf32_word st_size; // Symbol size uint8_t st_info; // Symbol type and binding uint8_t st_other; // Symbol visibility elf32_section st_shndx; // Section index }; struct elf64_sym { elf64_word st_name; // Symbol name (string tbl index) uint8_t st_info; // Symbol type and binding uint8_t st_other; // Symbol visibility elf64_section st_shndx; // Section index elf64_addr st_value; // Symbol value elf64_xword st_size; // Symbol size }; #endif edb-debugger/libELF/include/libELF/elf_move.h0000644000175000017500000000372313765535463020520 0ustar eteraneteran/* Copyright (C) 2012 - 2015 Evan Teran evan.teran@gmail.com Copyright (C) 1995-2003,2004,2005,2006,2007,2008,2009,2010,2011 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ELF_MOVE_H_20121007_ #define ELF_MOVE_H_20121007_ #include "elf_types.h" // Move records. struct elf32_move { elf32_xword m_value; // Symbol value. elf32_word m_info; // Size and index. elf32_word m_poffset; // Symbol offset. elf32_half m_repeat; // Repeat count. elf32_half m_stride; // Stride info. }; struct elf64_move { elf64_xword m_value; // Symbol value. elf64_xword m_info; // Size and index. elf64_xword m_poffset; // Symbol offset. elf64_half m_repeat; // Repeat count. elf64_half m_stride; // Stride info. }; // Macro to construct move records. template constexpr T ELF32_M_SYM(T info) { return info >> 8; } template constexpr auto ELF32_M_SIZE(T info) { return static_cast(info); } template constexpr auto ELF32_M_INFO(T1 sym, T2 size) { return (sym << 8) + static_cast(size); } template constexpr T ELF64_M_SYM(T info) { return ELF32_M_SYM(info); } template constexpr auto ELF64_M_SIZE(T info) { return ELF32_M_SIZE(info); } template constexpr auto ELF64_M_INFO(T1 sym, T2 size) { return ELF32_M_INFO(sym, size); } #endif edb-debugger/libELF/include/libELF/elf_rela.h0000644000175000017500000000360113765535463020470 0ustar eteraneteran/* Copyright (C) 2012 - 2015 Evan Teran evan.teran@gmail.com Copyright (C) 1995-2003,2004,2005,2006,2007,2008,2009,2010,2011 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ELF_RELA_H_20121007_ #define ELF_RELA_H_20121007_ #include "elf_types.h" /* Relocation table entry with addend (in section of type SHT_RELA). */ struct elf32_rela { elf32_addr r_offset; /* Address */ elf32_word r_info; /* Relocation type and symbol index */ elf32_sword r_addend; /* Addend */ }; struct elf64_rela { elf64_addr r_offset; /* Address */ elf64_xword r_info; /* Relocation type and symbol index */ elf64_sxword r_addend; /* Addend */ }; /* How to extract and insert information held in the r_info field. */ template constexpr T ELF32_R_SYM(T val) { return val >> 8; } template constexpr T ELF32_R_TYPE(T val) { return val & 0xff; } template constexpr auto ELF32_R_INFO(T1 sym, T2 type) { return (sym << 8) + (type & 0xff); } template constexpr T ELF64_R_SYM(T i) { return i >> 32; } template constexpr T ELF64_R_TYPE(T i) { return i & 0xffffffff; } template constexpr auto ELF64_R_INFO(T1 sym, T2 type) { return (static_cast(sym) << 32) + type; } #endif edb-debugger/libELF/include/libELF/elf_rel.h0000644000175000017500000000266213765535463020335 0ustar eteraneteran/* Copyright (C) 2012 - 2015 Evan Teran evan.teran@gmail.com Copyright (C) 1995-2003,2004,2005,2006,2007,2008,2009,2010,2011 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ELF_REL_H_20121007_ #define ELF_REL_H_20121007_ #include "elf_types.h" // Relocation table entry without addend (in section of type SHT_REL). struct elf32_rel { elf32_addr r_offset; /* Address */ elf32_word r_info; /* Relocation type and symbol index */ }; /* I have seen two different definitions of the Elf64_Rel and Elf64_Rela structures, so we'll leave them out until Novell (or whoever) gets their act together. */ /* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ struct elf64_rel { elf64_addr r_offset; /* Address */ elf64_xword r_info; /* Relocation type and symbol index */ }; #endif edb-debugger/libELF/include/libELF/elf_verneed.h0000644000175000017500000000345113765535463021200 0ustar eteraneteran/* Copyright (C) 2012 - 2015 Evan Teran evan.teran@gmail.com Copyright (C) 1995-2003,2004,2005,2006,2007,2008,2009,2010,2011 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ELF_VERNEED_H_20121007_ #define ELF_VERNEED_H_20121007_ #include "elf_types.h" /* Version dependency section. */ struct elf32_verneed { elf32_half vn_version; /* Version of structure */ elf32_half vn_cnt; /* Number of associated aux entries */ elf32_word vn_file; /* Offset of filename for this dependency */ elf32_word vn_aux; /* Offset in bytes to vernaux array */ elf32_word vn_next; /* Offset in bytes to next verneed entry */ }; struct elf64_verneed { elf64_half vn_version; /* Version of structure */ elf64_half vn_cnt; /* Number of associated aux entries */ elf64_word vn_file; /* Offset of filename for this dependency */ elf64_word vn_aux; /* Offset in bytes to vernaux array */ elf64_word vn_next; /* Offset in bytes to next verneed entry */ }; /* Legal values for vn_version (version revision). */ enum { VER_NEED_NONE = 0, /* No version */ VER_NEED_CURRENT = 1, /* Current version */ VER_NEED_NUM = 2 /* Given version number */ }; #endif edb-debugger/libELF/include/libELF/elf_phdr.h0000644000175000017500000001352113765535463020504 0ustar eteraneteran/* Copyright (C) 2012 - 2015 Evan Teran evan.teran@gmail.com Copyright (C) 1995-2003,2004,2005,2006,2007,2008,2009,2010,2011 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ELF_PHDR_H_20121007_ #define ELF_PHDR_H_20121007_ #include "elf_types.h" /* Program segment header. */ struct elf32_phdr { elf32_word p_type; /* Segment type */ elf32_off p_offset; /* Segment file offset */ elf32_addr p_vaddr; /* Segment virtual address */ elf32_addr p_paddr; /* Segment physical address */ elf32_word p_filesz; /* Segment size in file */ elf32_word p_memsz; /* Segment size in memory */ elf32_word p_flags; /* Segment flags */ elf32_word p_align; /* Segment alignment */ }; struct elf64_phdr { elf64_word p_type; /* Segment type */ elf64_word p_flags; /* Segment flags */ elf64_off p_offset; /* Segment file offset */ elf64_addr p_vaddr; /* Segment virtual address */ elf64_addr p_paddr; /* Segment physical address */ elf64_xword p_filesz; /* Segment size in file */ elf64_xword p_memsz; /* Segment size in memory */ elf64_xword p_align; /* Segment alignment */ }; /* Special value for e_phnum. This indicates that the real number of program headers is too large to fit into e_phnum. Instead the real value is in the field sh_info of section 0. */ enum { PN_XNUM = 0xffff, }; /* Legal values for p_type (segment type). */ enum { PT_NULL = 0, /* Program header table entry unused */ PT_LOAD = 1, /* Loadable program segment */ PT_DYNAMIC = 2, /* Dynamic linking information */ PT_INTERP = 3, /* Program interpreter */ PT_NOTE = 4, /* Auxiliary information */ PT_SHLIB = 5, /* Reserved */ PT_PHDR = 6, /* Entry for header table itself */ PT_TLS = 7, /* Thread-local storage segment */ PT_NUM = 8, /* Number of defined types */ PT_LOOS = 0x60000000, /* Start of OS-specific */ PT_GNU_EH_FRAME = 0x6474e550, /* GCC .eh_frame_hdr segment */ PT_GNU_STACK = 0x6474e551, /* Indicates stack executability */ PT_GNU_RELRO = 0x6474e552, /* Read-only after relocation */ PT_PAX_FLAGS = 0x65041580, /* Indicates PaX flag markings */ PT_LOSUNW = 0x6ffffffa, PT_SUNWBSS = 0x6ffffffa, /* Sun Specific segment */ PT_SUNWSTACK = 0x6ffffffb, /* Stack segment */ PT_HISUNW = 0x6fffffff, PT_HIOS = 0x6fffffff, /* End of OS-specific */ PT_LOPROC = 0x70000000, /* Start of processor-specific */ PT_HIPROC = 0x7fffffff, /* End of processor-specific */ }; /* Legal values for p_flags (segment flags). */ enum { PF_X = (1 << 0), /* Segment is executable */ PF_W = (1 << 1), /* Segment is writable */ PF_R = (1 << 2), /* Segment is readable */ PF_PAGEEXEC = (1 << 4), /* Enable PAGEEXEC */ PF_NOPAGEEXEC = (1 << 5), /* Disable PAGEEXEC */ PF_SEGMEXEC = (1 << 6), /* Enable SEGMEXEC */ PF_NOSEGMEXEC = (1 << 7), /* Disable SEGMEXEC */ PF_MPROTECT = (1 << 8), /* Enable MPROTECT */ PF_NOMPROTECT = (1 << 9), /* Disable MPROTECT */ PF_RANDEXEC = (1 << 10), /* Enable RANDEXEC */ PF_NORANDEXEC = (1 << 11), /* Disable RANDEXEC */ PF_EMUTRAMP = (1 << 12), /* Enable EMUTRAMP */ PF_NOEMUTRAMP = (1 << 13), /* Disable EMUTRAMP */ PF_RANDMMAP = (1 << 14), /* Enable RANDMMAP */ PF_NORANDMMAP = (1 << 15), /* Disable RANDMMAP */ PF_MASKOS = 0x0ff00000, /* OS-specific */ PF_MASKPROC = 0xf0000000, /* Processor-specific */ }; /* Legal values for note segment descriptor types for core files. */ enum { NT_PRSTATUS = 1, /* Contains copy of prstatus struct */ NT_FPREGSET = 2, /* Contains copy of fpregset struct */ NT_PRPSINFO = 3, /* Contains copy of prpsinfo struct */ NT_PRXREG = 4, /* Contains copy of prxregset struct */ NT_TASKSTRUCT = 4, /* Contains copy of task structure */ NT_PLATFORM = 5, /* String from sysinfo(SI_PLATFORM) */ NT_AUXV = 6, /* Contains copy of auxv array */ NT_GWINDOWS = 7, /* Contains copy of gwindows struct */ NT_ASRS = 8, /* Contains copy of asrset struct */ NT_PSTATUS = 10, /* Contains copy of pstatus struct */ NT_PSINFO = 13, /* Contains copy of psinfo struct */ NT_PRCRED = 14, /* Contains copy of prcred struct */ NT_UTSNAME = 15, /* Contains copy of utsname struct */ NT_LWPSTATUS = 16, /* Contains copy of lwpstatus struct */ NT_LWPSINFO = 17, /* Contains copy of lwpinfo struct */ NT_PRFPXREG = 20, /* Contains copy of fprxregset struct */ NT_PRXFPREG = 0x46e62b7f, /* Contains copy of user_fxsr_struct */ NT_PPC_VMX = 0x100, /* PowerPC Altivec/VMX registers */ NT_PPC_SPE = 0x101, /* PowerPC SPE/EVR registers */ NT_PPC_VSX = 0x102, /* PowerPC VSX registers */ NT_386_TLS = 0x200, /* i386 TLS slots (struct user_desc) */ NT_386_IOPERM = 0x201, /* x86 io permission bitmap (1=deny) */ NT_X86_XSTATE = 0x202, /* x86 extended state using xsave */ }; /* Legal values for the note segment descriptor types for object files. */ enum { NT_VERSION = 1, /* Contains a version string. */ }; #endif edb-debugger/libELF/include/libELF/elf_types.h0000644000175000017500000000334413765535463020715 0ustar eteraneteran/* Copyright (C) 2012 - 2015 Evan Teran evan.teran@gmail.com Copyright (C) 1995-2003,2004,2005,2006,2007,2008,2009,2010,2011 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ELF_TYPES_H_20121007_ #define ELF_TYPES_H_20121007_ #include /* Type for a 16-bit quantity. */ using elf32_half = uint16_t; using elf64_half = uint16_t; /* Types for signed and unsigned 32-bit quantities. */ using elf32_word = uint32_t; using elf32_sword = int32_t; using elf64_word = uint32_t; using elf64_sword = int32_t; /* Types for signed and unsigned 64-bit quantities. */ using elf32_xword = uint64_t; using elf32_sxword = int64_t; using elf64_xword = uint64_t; using elf64_sxword = int64_t; /* Type of addresses. */ using elf32_addr = uint32_t; using elf64_addr = uint64_t; /* Type of file offsets. */ using elf32_off = uint32_t; using elf64_off = uint64_t; /* Type for section indices, which are 16-bit quantities. */ using elf32_section = uint16_t; using elf64_section = uint16_t; /* Type for version symbol information. */ using elf32_versym = elf32_half; using elf64_versym = elf64_half; #endif edb-debugger/libELF/include/libELF/elf_verdef.h0000644000175000017500000000461513765535463021026 0ustar eteraneteran/* Copyright (C) 2012 - 2015 Evan Teran evan.teran@gmail.com Copyright (C) 1995-2003,2004,2005,2006,2007,2008,2009,2010,2011 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ELF_VERDEF_H_20121007_ #define ELF_VERDEF_H_20121007_ #include "elf_types.h" /* Version definition sections. */ struct elf32_verdef { elf32_half vd_version; /* Version revision */ elf32_half vd_flags; /* Version information */ elf32_half vd_ndx; /* Version Index */ elf32_half vd_cnt; /* Number of associated aux entries */ elf32_word vd_hash; /* Version name hash value */ elf32_word vd_aux; /* Offset in bytes to verdaux array */ elf32_word vd_next; /* Offset in bytes to next verdef entry */ }; struct elf64_verdef { elf64_half vd_version; /* Version revision */ elf64_half vd_flags; /* Version information */ elf64_half vd_ndx; /* Version Index */ elf64_half vd_cnt; /* Number of associated aux entries */ elf64_word vd_hash; /* Version name hash value */ elf64_word vd_aux; /* Offset in bytes to verdaux array */ elf64_word vd_next; /* Offset in bytes to next verdef entry */ }; /* Legal values for vd_version (version revision). */ enum { VER_DEF_NONE = 0, /* No version */ VER_DEF_CURRENT = 1, /* Current version */ VER_DEF_NUM = 2 /* Given version number */ }; /* Legal values for vd_flags (version information flags). */ enum { VER_FLG_BASE = 0x1, /* Version definition of file itself */ VER_FLG_WEAK = 0x2 /* Weak version identifier */ }; /* Versym symbol index values. */ enum { VER_NDX_LOCAL = 0, /* Symbol is local. */ VER_NDX_GLOBAL = 1, /* Symbol is global. */ VER_NDX_LORESERVE = 0xff00, /* Beginning of reserved entries. */ VER_NDX_ELIMINATE = 0xff01 /* Symbol is to be eliminated. */ }; #endif edb-debugger/libELF/include/libELF/elf_model.h0000644000175000017500000000570713765535463020656 0ustar eteraneteran/* Copyright (C) 2018 - 2018 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ELF_MODEL_H_20181206_ #define ELF_MODEL_H_20181206_ #include "elf_auxv.h" #include "elf_dyn.h" #include "elf_header.h" #include "elf_move.h" #include "elf_nhdr.h" #include "elf_phdr.h" #include "elf_rel.h" #include "elf_rela.h" #include "elf_shdr.h" #include "elf_sym.h" #include "elf_syminfo.h" #include "elf_types.h" #include "elf_verdaux.h" #include "elf_verdef.h" #include "elf_vernaux.h" #include "elf_verneed.h" template struct elf_model; template <> struct elf_model<64> { // types using elf_half = elf64_half; using elf_word = elf64_word; using elf_sword = elf64_sword; using elf_xword = elf64_xword; using elf_sxword = elf64_sxword; using elf_addr = elf64_addr; using elf_off = elf64_off; using elf_section = elf64_section; using elf_versym = elf64_versym; // structures using elf_auxv_t = elf64_auxv_t; using elf_dyn = elf64_dyn; using elf_header = elf64_header; #if 0 using elf_lib = elf64_lib; #endif using elf_move = elf64_move; using elf_nhdr = elf64_nhdr; using elf_phdr = elf64_phdr; using elf_rel = elf64_rel; using elf_rela = elf64_rela; using elf_shdr = elf64_shdr; using elf_sym = elf64_sym; using elf_syminfo = elf64_syminfo; using elf_verdaux = elf64_verdaux; using elf_verdef = elf64_verdef; using elf_vernaux = elf64_vernaux; using elf_verneed = elf64_verneed; }; template <> struct elf_model<32> { // types using elf_half = elf32_half; using elf_word = elf32_word; using elf_sword = elf32_sword; using elf_xword = elf32_xword; using elf_sxword = elf32_sxword; using elf_addr = elf32_addr; using elf_off = elf32_off; using elf_section = elf32_section; using elf_versym = elf32_versym; // structures using elf_auxv_t = elf32_auxv_t; using elf_dyn = elf32_dyn; using elf_header = elf32_header; #if 0 using elf_lib = elf32_lib; #endif using elf_move = elf32_move; using elf_nhdr = elf32_nhdr; using elf_phdr = elf32_phdr; using elf_rel = elf32_rel; using elf_rela = elf32_rela; using elf_shdr = elf32_shdr; using elf_sym = elf32_sym; using elf_syminfo = elf32_syminfo; using elf_verdaux = elf32_verdaux; using elf_verdef = elf32_verdef; using elf_vernaux = elf32_vernaux; using elf_verneed = elf32_verneed; }; #endif edb-debugger/libELF/include/libELF/elf_nhdr.h0000644000175000017500000000550413765535463020504 0ustar eteraneteran/* Copyright (C) 2012 - 2015 Evan Teran evan.teran@gmail.com Copyright (C) 1995-2003,2004,2005,2006,2007,2008,2009,2010,2011 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ELF_NHDR_H_20121007_ #define ELF_NHDR_H_20121007_ #include "elf_types.h" /* Note section contents. Each entry in the note section begins with a header of a fixed form. */ struct elf32_nhdr { elf32_word n_namesz; /* Length of the note's name. */ elf32_word n_descsz; /* Length of the note's descriptor. */ elf32_word n_type; /* Type of the note. */ }; struct elf64_nhdr { elf64_word n_namesz; /* Length of the note's name. */ elf64_word n_descsz; /* Length of the note's descriptor. */ elf64_word n_type; /* Type of the note. */ }; /* Known names of notes. */ /* Solaris entries in the note section have this name. */ #define ELF_NOTE_SOLARIS "SUNW Solaris" /* Note entries for GNU systems have this name. */ #define ELF_NOTE_GNU "GNU" /* Defined types of notes for Solaris. */ /* Value of descriptor (one word) is desired pagesize for the binary. */ enum { ELF_NOTE_PAGESIZE_HINT = 1, }; /* Defined note types for GNU systems. */ /* ABI information. The descriptor consists of words: word 0: OS descriptor word 1: major version of the ABI word 2: minor version of the ABI word 3: subminor version of the ABI */ enum { NT_GNU_ABI_TAG = 1, ELF_NOTE_ABI = NT_GNU_ABI_TAG, /* Old name. */ }; /* Known OSes. These values can appear in word 0 of an NT_GNU_ABI_TAG note section entry. */ enum { ELF_NOTE_OS_LINUX = 0, ELF_NOTE_OS_GNU = 1, ELF_NOTE_OS_SOLARIS2 = 2, ELF_NOTE_OS_FREEBSD = 3, }; enum { /* Synthetic hwcap information. The descriptor begins with two words: word 0: number of entries word 1: bitmask of enabled entries Then follow variable-length entries, one byte followed by a '\0'-terminated hwcap name string. The byte gives the bit number to test if enabled, (1U << bit) & bitmask. */ NT_GNU_HWCAP = 2, NT_GNU_BUILD_ID = 3, /* Build ID bits as generated by ld --build-id. The descriptor consists of any nonzero number of bytes. */ NT_GNU_GOLD_VERSION = 4, /* Version note generated by GNU gold containing a version string. */ }; #endif edb-debugger/libELF/include/libELF/elf_dyn.h0000644000175000017500000003070313765535463020342 0ustar eteraneteran/* Copyright (C) 2012 - 2015 Evan Teran evan.teran@gmail.com Copyright (C) 1995-2003,2004,2005,2006,2007,2008,2009,2010,2011 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ELF_DYN_H_20121007_ #define ELF_DYN_H_20121007_ #include "elf_types.h" /* Dynamic section entry. */ struct elf32_dyn { elf32_sword d_tag; /* Dynamic entry type */ union { elf32_word d_val; /* Integer value */ elf32_addr d_ptr; /* Address value */ } d_un; }; struct elf64_dyn { elf64_sxword d_tag; /* Dynamic entry type */ union { elf64_xword d_val; /* Integer value */ elf64_addr d_ptr; /* Address value */ } d_un; }; /* Legal values for d_tag field of Elf32_Dyn. */ enum { DT_MIPS_RLD_VERSION = 0x70000001, /* Runtime linker interface version */ DT_MIPS_TIME_STAMP = 0x70000002, /* Timestamp */ DT_MIPS_ICHECKSUM = 0x70000003, /* Checksum */ DT_MIPS_IVERSION = 0x70000004, /* Version string (string tbl index) */ DT_MIPS_FLAGS = 0x70000005, /* Flags */ DT_MIPS_BASE_ADDRESS = 0x70000006, /* Base address */ DT_MIPS_MSYM = 0x70000007, DT_MIPS_CONFLICT = 0x70000008, /* Address of CONFLICT section */ DT_MIPS_LIBLIST = 0x70000009, /* Address of LIBLIST section */ DT_MIPS_LOCAL_GOTNO = 0x7000000a, /* Number of local GOT entries */ DT_MIPS_CONFLICTNO = 0x7000000b, /* Number of CONFLICT entries */ DT_MIPS_LIBLISTNO = 0x70000010, /* Number of LIBLIST entries */ DT_MIPS_SYMTABNO = 0x70000011, /* Number of DYNSYM entries */ DT_MIPS_UNREFEXTNO = 0x70000012, /* First external DYNSYM */ DT_MIPS_GOTSYM = 0x70000013, /* First GOT entry in DYNSYM */ DT_MIPS_HIPAGENO = 0x70000014, /* Number of GOT page table entries */ DT_MIPS_RLD_MAP = 0x70000016, /* Address of run time loader map. */ DT_MIPS_DELTA_CLASS = 0x70000017, /* Delta C++ class definition. */ DT_MIPS_DELTA_CLASS_NO = 0x70000018, /* Number of entries in DT_MIPS_DELTA_CLASS. */ DT_MIPS_DELTA_INSTANCE = 0x70000019, /* Delta C++ class instances. */ DT_MIPS_DELTA_INSTANCE_NO = 0x7000001a, /* Number of entries in DT_MIPS_DELTA_INSTANCE. */ DT_MIPS_DELTA_RELOC = 0x7000001b, /* Delta relocations. */ DT_MIPS_DELTA_RELOC_NO = 0x7000001c, /* Number of entries in DT_MIPS_DELTA_RELOC. */ DT_MIPS_DELTA_SYM = 0x7000001d, /* Delta symbols that Delta relocations refer to. */ DT_MIPS_DELTA_SYM_NO = 0x7000001e, /* Number of entries in DT_MIPS_DELTA_SYM. */ DT_MIPS_DELTA_CLASSSYM = 0x70000020, /* Delta symbols that hold the class declaration. */ DT_MIPS_DELTA_CLASSSYM_NO = 0x70000021, /* Number of entries in DT_MIPS_DELTA_CLASSSYM. */ DT_MIPS_CXX_FLAGS = 0x70000022, /* Flags indicating for C++ flavor. */ DT_MIPS_PIXIE_INIT = 0x70000023, DT_MIPS_SYMBOL_LIB = 0x70000024, DT_MIPS_LOCALPAGE_GOTIDX = 0x70000025, DT_MIPS_LOCAL_GOTIDX = 0x70000026, DT_MIPS_HIDDEN_GOTIDX = 0x70000027, DT_MIPS_PROTECTED_GOTIDX = 0x70000028, DT_MIPS_OPTIONS = 0x70000029, /* Address of .options. */ DT_MIPS_INTERFACE = 0x7000002a, /* Address of .interface. */ DT_MIPS_DYNSTR_ALIGN = 0x7000002b, DT_MIPS_INTERFACE_SIZE = 0x7000002c, /* Size of the .interface section. */ DT_MIPS_RLD_TEXT_RESOLVE_ADDR = 0x7000002d, /* Address of rld_text_rsolve function stored in GOT. */ DT_MIPS_PERF_SUFFIX = 0x7000002e, /* Default suffix of dso to be added by rld on dlopen() calls. */ DT_MIPS_COMPACT_SIZE = 0x7000002f, /* (O32)Size of compact rel section. */ DT_MIPS_GP_VALUE = 0x70000030, /* GP value for aux GOTs. */ DT_MIPS_AUX_DYNAMIC = 0x70000031, /* Address of aux .dynamic. */ /* The address of .got.plt in an executable using the new non-PIC ABI. */ DT_MIPS_PLTGOT = 0x70000032, /* The base of the PLT in an executable using the new non-PIC ABI if that PLT is writable. For a non-writable PLT, this is omitted or has a zero value. */ DT_MIPS_RWPLT = 0x70000034, DT_MIPS_NUM = 0x35, }; /* Legal values for d_tag (dynamic entry type). */ enum { DT_NULL = 0, /* Marks end of dynamic section */ DT_NEEDED = 1, /* Name of needed library */ DT_PLTRELSZ = 2, /* Size in bytes of PLT relocs */ DT_PLTGOT = 3, /* Processor defined value */ DT_HASH = 4, /* Address of symbol hash table */ DT_STRTAB = 5, /* Address of string table */ DT_SYMTAB = 6, /* Address of symbol table */ DT_RELA = 7, /* Address of Rela relocs */ DT_RELASZ = 8, /* Total size of Rela relocs */ DT_RELAENT = 9, /* Size of one Rela reloc */ DT_STRSZ = 10, /* Size of string table */ DT_SYMENT = 11, /* Size of one symbol table entry */ DT_INIT = 12, /* Address of init function */ DT_FINI = 13, /* Address of termination function */ DT_SONAME = 14, /* Name of shared object */ DT_RPATH = 15, /* Library search path (deprecated) */ DT_SYMBOLIC = 16, /* Start symbol search here */ DT_REL = 17, /* Address of Rel relocs */ DT_RELSZ = 18, /* Total size of Rel relocs */ DT_RELENT = 19, /* Size of one Rel reloc */ DT_PLTREL = 20, /* Type of reloc in PLT */ DT_DEBUG = 21, /* For debugging; unspecified */ DT_TEXTREL = 22, /* Reloc might modify .text */ DT_JMPREL = 23, /* Address of PLT relocs */ DT_BIND_NOW = 24, /* Process relocations of object */ DT_INIT_ARRAY = 25, /* Array with addresses of init fct */ DT_FINI_ARRAY = 26, /* Array with addresses of fini fct */ DT_INIT_ARRAYSZ = 27, /* Size in bytes of DT_INIT_ARRAY */ DT_FINI_ARRAYSZ = 28, /* Size in bytes of DT_FINI_ARRAY */ DT_RUNPATH = 29, /* Library search path */ DT_FLAGS = 30, /* Flags for the object being loaded */ DT_ENCODING = 32, /* Start of encoded range */ DT_PREINIT_ARRAY = 32, /* Array with addresses of preinit fct*/ DT_PREINIT_ARRAYSZ = 33, /* size in bytes of DT_PREINIT_ARRAY */ DT_NUM = 34, /* Number used */ DT_LOOS = 0x6000000d, /* Start of OS-specific */ DT_HIOS = 0x6ffff000, /* End of OS-specific */ DT_LOPROC = 0x70000000, /* Start of processor-specific */ DT_HIPROC = 0x7fffffff, /* End of processor-specific */ DT_PROCNUM = DT_MIPS_NUM /* Most used by any processor */ }; /* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's approach. */ enum { DT_VALRNGLO = 0x6ffffd00, DT_GNU_PRELINKED = 0x6ffffdf5, /* Prelinking timestamp */ DT_GNU_CONFLICTSZ = 0x6ffffdf6, /* Size of conflict section */ DT_GNU_LIBLISTSZ = 0x6ffffdf7, /* Size of library list */ DT_CHECKSUM = 0x6ffffdf8, DT_PLTPADSZ = 0x6ffffdf9, DT_MOVEENT = 0x6ffffdfa, DT_MOVESZ = 0x6ffffdfb, DT_FEATURE_1 = 0x6ffffdfc, /* Feature selection (DTF_*). */ DT_POSFLAG_1 = 0x6ffffdfd, /* Flags for DT_* entries, effecting the following DT_* entry. */ DT_SYMINSZ = 0x6ffffdfe, /* Size of syminfo table (in bytes) */ DT_SYMINENT = 0x6ffffdff, /* Entry size of syminfo */ DT_VALRNGHI = 0x6ffffdff, DT_VALNUM = 12, }; constexpr inline uint32_t DT_VALTAGIDX(uint32_t tag) { return DT_VALRNGHI - tag; /* Reverse order! */ } /* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the Dyn.d_un.d_ptr field of the Elf*_Dyn structure. If any adjustment is made to the ELF object after it has been built these entries will need to be adjusted. */ enum { DT_ADDRRNGLO = 0x6ffffe00, DT_GNU_HASH = 0x6ffffef5, /* GNU-style hash table. */ DT_TLSDESC_PLT = 0x6ffffef6, DT_TLSDESC_GOT = 0x6ffffef7, DT_GNU_CONFLICT = 0x6ffffef8, /* Start of conflict section */ DT_GNU_LIBLIST = 0x6ffffef9, /* Library list */ DT_CONFIG = 0x6ffffefa, /* Configuration information. */ DT_DEPAUDIT = 0x6ffffefb, /* Dependency auditing. */ DT_AUDIT = 0x6ffffefc, /* Object auditing. */ DT_PLTPAD = 0x6ffffefd, /* PLT padding. */ DT_MOVETAB = 0x6ffffefe, /* Move table. */ DT_SYMINFO = 0x6ffffeff, /* Syminfo table. */ DT_ADDRRNGHI = 0x6ffffeff, DT_ADDRNUM = 11, }; constexpr inline uint32_t DT_ADDRTAGIDX(uint32_t tag) { return DT_ADDRRNGHI - tag; /* Reverse order! */ } /* The versioning entry types. The next are defined as part of the GNU extension. */ enum { DT_VERSYM = 0x6ffffff0, }; enum { DT_RELACOUNT = 0x6ffffff9, DT_RELCOUNT = 0x6ffffffa, }; /* These were chosen by Sun. */ enum { DT_FLAGS_1 = 0x6ffffffb, /* State flags, see DF_1_* below. */ DT_VERDEF = 0x6ffffffc, /* Address of version definition table */ DT_VERDEFNUM = 0x6ffffffd, /* Number of version definitions */ DT_VERNEED = 0x6ffffffe, /* Address of table with needed versions */ DT_VERNEEDNUM = 0x6fffffff, /* Number of needed versions */ DT_VERSIONTAGNUM = 16, }; constexpr inline uint32_t DT_VERSIONTAGIDX(uint32_t tag) { return DT_VERNEEDNUM - tag; /* Reverse order! */ } /* Sun added these machine-independent extensions in the "processor-specific" range. Be compatible. */ enum { DT_AUXILIARY = 0x7ffffffd, /* Shared object to load before self */ DT_FILTER = 0x7fffffff, /* Shared object to get values from */ DT_EXTRANUM = 3, }; constexpr inline uint32_t DT_EXTRATAGIDX(uint32_t tag) { return static_cast(-(static_cast(tag) << 1 >> 1) - 1); } /* Values of `d_un.d_val' in the DT_FLAGS entry. */ enum { DF_ORIGIN = 0x00000001, /* Object may use DF_ORIGIN */ DF_SYMBOLIC = 0x00000002, /* Symbol resolutions starts here */ DF_TEXTREL = 0x00000004, /* Object contains text relocations */ DF_BIND_NOW = 0x00000008, /* No lazy binding for this object */ DF_STATIC_TLS = 0x00000010, /* Module uses the static TLS model */ }; /* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 entry in the dynamic section. */ enum { DF_1_NOW = 0x00000001, /* Set RTLD_NOW for this object. */ DF_1_GLOBAL = 0x00000002, /* Set RTLD_GLOBAL for this object. */ DF_1_GROUP = 0x00000004, /* Set RTLD_GROUP for this object. */ DF_1_NODELETE = 0x00000008, /* Set RTLD_NODELETE for this object.*/ DF_1_LOADFLTR = 0x00000010, /* Trigger filtee loading at runtime.*/ DF_1_INITFIRST = 0x00000020, /* Set RTLD_INITFIRST for this object*/ DF_1_NOOPEN = 0x00000040, /* Set RTLD_NOOPEN for this object. */ DF_1_ORIGIN = 0x00000080, /* $ORIGIN must be handled. */ DF_1_DIRECT = 0x00000100, /* Direct binding enabled. */ DF_1_TRANS = 0x00000200, DF_1_INTERPOSE = 0x00000400, /* Object is used to interpose. */ DF_1_NODEFLIB = 0x00000800, /* Ignore default lib search path. */ DF_1_NODUMP = 0x00001000, /* Object can't be dldump'ed. */ DF_1_CONFALT = 0x00002000, /* Configuration alternative created.*/ DF_1_ENDFILTEE = 0x00004000, /* Filtee terminates filters search. */ DF_1_DISPRELDNE = 0x00008000, /* Disp reloc applied at build time. */ DF_1_DISPRELPND = 0x00010000, /* Disp reloc applied at run-time. */ }; /* Flags for the feature selection in DT_FEATURE_1. */ enum { DTF_1_PARINIT = 0x00000001, DTF_1_CONFEXP = 0x00000002, }; /* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ enum { DF_P1_LAZYLOAD = 0x00000001, /* Lazyload following object. */ DF_P1_GROUPPERM = 0x00000002, /* Symbols from next object are not generally available. */ }; #endif edb-debugger/libELF/include/libELF/elf_binary.h0000644000175000017500000021610713765535463021040 0ustar eteraneteran/* Copyright (C) 2012 Evan Teran evan.teran@gmail.com Copyright (C) 1995-2003,2004,2005,2006,2007,2008,2009,2010,2011 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ELF_BINARY_H_20121007_ #define ELF_BINARY_H_20121007_ #include "elf_auxv.h" #include "elf_dyn.h" #include "elf_header.h" #include "elf_move.h" #include "elf_nhdr.h" #include "elf_phdr.h" #include "elf_rel.h" #include "elf_rela.h" #include "elf_shdr.h" #include "elf_sym.h" #include "elf_syminfo.h" #include "elf_types.h" #include "elf_verdaux.h" #include "elf_verdef.h" #include "elf_vernaux.h" #include "elf_verneed.h" /* Motorola 68k specific definitions. */ /* Values for Elf32_Ehdr.e_flags. */ enum { EF_CPU32 = 0x00810000 }; /* m68k relocs. */ enum { R_68K_NONE = 0, /* No reloc */ R_68K_32 = 1, /* Direct 32 bit */ R_68K_16 = 2, /* Direct 16 bit */ R_68K_8 = 3, /* Direct 8 bit */ R_68K_PC32 = 4, /* PC relative 32 bit */ R_68K_PC16 = 5, /* PC relative 16 bit */ R_68K_PC8 = 6, /* PC relative 8 bit */ R_68K_GOT32 = 7, /* 32 bit PC relative GOT entry */ R_68K_GOT16 = 8, /* 16 bit PC relative GOT entry */ R_68K_GOT8 = 9, /* 8 bit PC relative GOT entry */ R_68K_GOT32O = 10, /* 32 bit GOT offset */ R_68K_GOT16O = 11, /* 16 bit GOT offset */ R_68K_GOT8O = 12, /* 8 bit GOT offset */ R_68K_PLT32 = 13, /* 32 bit PC relative PLT address */ R_68K_PLT16 = 14, /* 16 bit PC relative PLT address */ R_68K_PLT8 = 15, /* 8 bit PC relative PLT address */ R_68K_PLT32O = 16, /* 32 bit PLT offset */ R_68K_PLT16O = 17, /* 16 bit PLT offset */ R_68K_PLT8O = 18, /* 8 bit PLT offset */ R_68K_COPY = 19, /* Copy symbol at runtime */ R_68K_GLOB_DAT = 20, /* Create GOT entry */ R_68K_JMP_SLOT = 21, /* Create PLT entry */ R_68K_RELATIVE = 22, /* Adjust by program base */ R_68K_TLS_GD32 = 25, /* 32 bit GOT offset for GD */ R_68K_TLS_GD16 = 26, /* 16 bit GOT offset for GD */ R_68K_TLS_GD8 = 27, /* 8 bit GOT offset for GD */ R_68K_TLS_LDM32 = 28, /* 32 bit GOT offset for LDM */ R_68K_TLS_LDM16 = 29, /* 16 bit GOT offset for LDM */ R_68K_TLS_LDM8 = 30, /* 8 bit GOT offset for LDM */ R_68K_TLS_LDO32 = 31, /* 32 bit module-relative offset */ R_68K_TLS_LDO16 = 32, /* 16 bit module-relative offset */ R_68K_TLS_LDO8 = 33, /* 8 bit module-relative offset */ R_68K_TLS_IE32 = 34, /* 32 bit GOT offset for IE */ R_68K_TLS_IE16 = 35, /* 16 bit GOT offset for IE */ R_68K_TLS_IE8 = 36, /* 8 bit GOT offset for IE */ R_68K_TLS_LE32 = 37, /* 32 bit offset relative to static TLS block */ R_68K_TLS_LE16 = 38, /* 16 bit offset relative to static TLS block */ R_68K_TLS_LE8 = 39, /* 8 bit offset relative to static TLS block */ R_68K_TLS_DTPMOD32 = 40, /* 32 bit module number */ R_68K_TLS_DTPREL32 = 41, /* 32 bit module-relative offset */ R_68K_TLS_TPREL32 = 42, /* 32 bit TP-relative offset */ /* Keep this the last entry. */ R_68K_NUM = 43 }; /* Intel 80386 specific definitions. */ /* i386 relocs. */ enum { R_386_NONE = 0, /* No reloc */ R_386_32 = 1, /* Direct 32 bit */ R_386_PC32 = 2, /* PC relative 32 bit */ R_386_GOT32 = 3, /* 32 bit GOT entry */ R_386_PLT32 = 4, /* 32 bit PLT address */ R_386_COPY = 5, /* Copy symbol at runtime */ R_386_GLOB_DAT = 6, /* Create GOT entry */ R_386_JMP_SLOT = 7, /* Create PLT entry */ R_386_RELATIVE = 8, /* Adjust by program base */ R_386_GOTOFF = 9, /* 32 bit offset to GOT */ R_386_GOTPC = 10, /* 32 bit PC relative offset to GOT */ R_386_32PLT = 11, R_386_TLS_TPOFF = 14, /* Offset in static TLS block */ R_386_TLS_IE = 15, /* Address of GOT entry for static TLS block offset */ R_386_TLS_GOTIE = 16, /* GOT entry for static TLS block offset */ R_386_TLS_LE = 17, /* Offset relative to static TLS block */ R_386_TLS_GD = 18, /* Direct 32 bit for GNU version of general dynamic thread local data */ R_386_TLS_LDM = 19, /* Direct 32 bit for GNU version of local dynamic thread local data in LE code */ R_386_16 = 20, R_386_PC16 = 21, R_386_8 = 22, R_386_PC8 = 23, R_386_TLS_GD_32 = 24, /* Direct 32 bit for general dynamic thread local data */ R_386_TLS_GD_PUSH = 25, /* Tag for pushl in GD TLS code */ R_386_TLS_GD_CALL = 26, /* Relocation for call to __tls_get_addr() */ R_386_TLS_GD_POP = 27, /* Tag for popl in GD TLS code */ R_386_TLS_LDM_32 = 28, /* Direct 32 bit for local dynamic thread local data in LE code */ R_386_TLS_LDM_PUSH = 29, /* Tag for pushl in LDM TLS code */ R_386_TLS_LDM_CALL = 30, /* Relocation for call to __tls_get_addr() in LDM code */ R_386_TLS_LDM_POP = 31, /* Tag for popl in LDM TLS code */ R_386_TLS_LDO_32 = 32, /* Offset relative to TLS block */ R_386_TLS_IE_32 = 33, /* GOT entry for negated static TLS block offset */ R_386_TLS_LE_32 = 34, /* Negated offset relative to static TLS block */ R_386_TLS_DTPMOD32 = 35, /* ID of module containing symbol */ R_386_TLS_DTPOFF32 = 36, /* Offset in TLS block */ R_386_TLS_TPOFF32 = 37, /* Negated offset in static TLS block */ /* 38? */ R_386_TLS_GOTDESC = 39, /* GOT offset for TLS descriptor. */ R_386_TLS_DESC_CALL = 40, /* Marker of call through TLS descriptor for relaxation. */ R_386_TLS_DESC = 41, /* TLS descriptor containing pointer to code and to argument, returning the TLS offset for the symbol. */ R_386_IRELATIVE = 42, /* Adjust indirectly by program base */ /* Keep this the last entry. */ R_386_NUM = 43 }; /* SUN SPARC specific definitions. */ /* Legal values for ST_TYPE subfield of st_info (symbol type). */ enum { STT_SPARC_REGISTER = 13 /* Global register reserved to app. */ }; /* Values for Elf64_Ehdr.e_flags. */ enum { EF_SPARCV9_MM = 3, EF_SPARCV9_TSO = 0, EF_SPARCV9_PSO = 1, EF_SPARCV9_RMO = 2, EF_SPARC_LEDATA = 0x800000, /* little endian data */ EF_SPARC_EXT_MASK = 0xFFFF00, EF_SPARC_32PLUS = 0x000100, /* generic V8+ features */ EF_SPARC_SUN_US1 = 0x000200, /* Sun UltraSPARC1 extensions */ EF_SPARC_HAL_R1 = 0x000400, /* HAL R1 extensions */ EF_SPARC_SUN_US3 = 0x000800 /* Sun UltraSPARCIII extensions */ }; /* SPARC relocs. */ enum { R_SPARC_NONE = 0, /* No reloc */ R_SPARC_8 = 1, /* Direct 8 bit */ R_SPARC_16 = 2, /* Direct 16 bit */ R_SPARC_32 = 3, /* Direct 32 bit */ R_SPARC_DISP8 = 4, /* PC relative 8 bit */ R_SPARC_DISP16 = 5, /* PC relative 16 bit */ R_SPARC_DISP32 = 6, /* PC relative 32 bit */ R_SPARC_WDISP30 = 7, /* PC relative 30 bit shifted */ R_SPARC_WDISP22 = 8, /* PC relative 22 bit shifted */ R_SPARC_HI22 = 9, /* High 22 bit */ R_SPARC_22 = 10, /* Direct 22 bit */ R_SPARC_13 = 11, /* Direct 13 bit */ R_SPARC_LO10 = 12, /* Truncated 10 bit */ R_SPARC_GOT10 = 13, /* Truncated 10 bit GOT entry */ R_SPARC_GOT13 = 14, /* 13 bit GOT entry */ R_SPARC_GOT22 = 15, /* 22 bit GOT entry shifted */ R_SPARC_PC10 = 16, /* PC relative 10 bit truncated */ R_SPARC_PC22 = 17, /* PC relative 22 bit shifted */ R_SPARC_WPLT30 = 18, /* 30 bit PC relative PLT address */ R_SPARC_COPY = 19, /* Copy symbol at runtime */ R_SPARC_GLOB_DAT = 20, /* Create GOT entry */ R_SPARC_JMP_SLOT = 21, /* Create PLT entry */ R_SPARC_RELATIVE = 22, /* Adjust by program base */ R_SPARC_UA32 = 23, /* Direct 32 bit unaligned */ }; /* Additional Sparc64 relocs. */ enum { R_SPARC_PLT32 = 24, /* Direct 32 bit ref to PLT entry */ R_SPARC_HIPLT22 = 25, /* High 22 bit PLT entry */ R_SPARC_LOPLT10 = 26, /* Truncated 10 bit PLT entry */ R_SPARC_PCPLT32 = 27, /* PC rel 32 bit ref to PLT entry */ R_SPARC_PCPLT22 = 28, /* PC rel high 22 bit PLT entry */ R_SPARC_PCPLT10 = 29, /* PC rel trunc 10 bit PLT entry */ R_SPARC_10 = 30, /* Direct 10 bit */ R_SPARC_11 = 31, /* Direct 11 bit */ R_SPARC_64 = 32, /* Direct 64 bit */ R_SPARC_OLO10 = 33, /* 10bit with secondary 13bit addend */ R_SPARC_HH22 = 34, /* Top 22 bits of direct 64 bit */ R_SPARC_HM10 = 35, /* High middle 10 bits of ... */ R_SPARC_LM22 = 36, /* Low middle 22 bits of ... */ R_SPARC_PC_HH22 = 37, /* Top 22 bits of pc rel 64 bit */ R_SPARC_PC_HM10 = 38, /* High middle 10 bit of ... */ R_SPARC_PC_LM22 = 39, /* Low miggle 22 bits of ... */ R_SPARC_WDISP16 = 40, /* PC relative 16 bit shifted */ R_SPARC_WDISP19 = 41, /* PC relative 19 bit shifted */ R_SPARC_GLOB_JMP = 42, /* was part of v9 ABI but was removed */ R_SPARC_7 = 43, /* Direct 7 bit */ R_SPARC_5 = 44, /* Direct 5 bit */ R_SPARC_6 = 45, /* Direct 6 bit */ R_SPARC_DISP64 = 46, /* PC relative 64 bit */ R_SPARC_PLT64 = 47, /* Direct 64 bit ref to PLT entry */ R_SPARC_HIX22 = 48, /* High 22 bit complemented */ R_SPARC_LOX10 = 49, /* Truncated 11 bit complemented */ R_SPARC_H44 = 50, /* Direct high 12 of 44 bit */ R_SPARC_M44 = 51, /* Direct mid 22 of 44 bit */ R_SPARC_L44 = 52, /* Direct low 10 of 44 bit */ R_SPARC_REGISTER = 53, /* Global register usage */ R_SPARC_UA64 = 54, /* Direct 64 bit unaligned */ R_SPARC_UA16 = 55, /* Direct 16 bit unaligned */ R_SPARC_TLS_GD_HI22 = 56, R_SPARC_TLS_GD_LO10 = 57, R_SPARC_TLS_GD_ADD = 58, R_SPARC_TLS_GD_CALL = 59, R_SPARC_TLS_LDM_HI22 = 60, R_SPARC_TLS_LDM_LO10 = 61, R_SPARC_TLS_LDM_ADD = 62, R_SPARC_TLS_LDM_CALL = 63, R_SPARC_TLS_LDO_HIX22 = 64, R_SPARC_TLS_LDO_LOX10 = 65, R_SPARC_TLS_LDO_ADD = 66, R_SPARC_TLS_IE_HI22 = 67, R_SPARC_TLS_IE_LO10 = 68, R_SPARC_TLS_IE_LD = 69, R_SPARC_TLS_IE_LDX = 70, R_SPARC_TLS_IE_ADD = 71, R_SPARC_TLS_LE_HIX22 = 72, R_SPARC_TLS_LE_LOX10 = 73, R_SPARC_TLS_DTPMOD32 = 74, R_SPARC_TLS_DTPMOD64 = 75, R_SPARC_TLS_DTPOFF32 = 76, R_SPARC_TLS_DTPOFF64 = 77, R_SPARC_TLS_TPOFF32 = 78, R_SPARC_TLS_TPOFF64 = 79, R_SPARC_GOTDATA_HIX22 = 80, R_SPARC_GOTDATA_LOX10 = 81, R_SPARC_GOTDATA_OP_HIX22 = 82, R_SPARC_GOTDATA_OP_LOX10 = 83, R_SPARC_GOTDATA_OP = 84, R_SPARC_H34 = 85, R_SPARC_SIZE32 = 86, R_SPARC_SIZE64 = 87, R_SPARC_JMP_IREL = 248, R_SPARC_IRELATIVE = 249, R_SPARC_GNU_VTINHERIT = 250, R_SPARC_GNU_VTENTRY = 251, R_SPARC_REV32 = 252, /* Keep this the last entry. */ R_SPARC_NUM = 253, }; /* For Sparc64, legal values for d_tag of Elf64_Dyn. */ enum { DT_SPARC_REGISTER = 0x70000001, DT_SPARC_NUM = 2, }; /* MIPS R3000 specific definitions. */ /* Legal values for e_flags field of Elf32_Ehdr. */ enum { EF_MIPS_NOREORDER = 1, /* A .noreorder directive was used */ EF_MIPS_PIC = 2, /* Contains PIC code */ EF_MIPS_CPIC = 4, /* Uses PIC calling sequence */ EF_MIPS_XGOT = 8, EF_MIPS_64BIT_WHIRL = 16, EF_MIPS_ABI2 = 32, EF_MIPS_ABI_ON32 = 64, EF_MIPS_ARCH = 0xf0000000 /* MIPS architecture level */ }; /* Legal values for MIPS architecture level. */ enum { EF_MIPS_ARCH_1 = 0x00000000, /* -mips1 code. */ EF_MIPS_ARCH_2 = 0x10000000, /* -mips2 code. */ EF_MIPS_ARCH_3 = 0x20000000, /* -mips3 code. */ EF_MIPS_ARCH_4 = 0x30000000, /* -mips4 code. */ EF_MIPS_ARCH_5 = 0x40000000, /* -mips5 code. */ EF_MIPS_ARCH_32 = 0x60000000, /* MIPS32 code. */ EF_MIPS_ARCH_64 = 0x70000000, /* MIPS64 code. */ }; /* The following are non-official names and should not be used. */ enum { E_MIPS_ARCH_1 = 0x00000000, /* -mips1 code. */ E_MIPS_ARCH_2 = 0x10000000, /* -mips2 code. */ E_MIPS_ARCH_3 = 0x20000000, /* -mips3 code. */ E_MIPS_ARCH_4 = 0x30000000, /* -mips4 code. */ E_MIPS_ARCH_5 = 0x40000000, /* -mips5 code. */ E_MIPS_ARCH_32 = 0x60000000, /* MIPS32 code. */ E_MIPS_ARCH_64 = 0x70000000, /* MIPS64 code. */ }; /* Special section indices. */ enum { SHN_MIPS_ACOMMON = 0xff00, /* Allocated common symbols */ SHN_MIPS_TEXT = 0xff01, /* Allocated test symbols. */ SHN_MIPS_DATA = 0xff02, /* Allocated data symbols. */ SHN_MIPS_SCOMMON = 0xff03, /* Small common symbols */ SHN_MIPS_SUNDEFINED = 0xff04, /* Small undefined symbols */ }; /* Legal values for sh_type field of Elf32_Shdr. */ enum { SHT_MIPS_LIBLIST = 0x70000000, /* Shared objects used in link */ SHT_MIPS_MSYM = 0x70000001, SHT_MIPS_CONFLICT = 0x70000002, /* Conflicting symbols */ SHT_MIPS_GPTAB = 0x70000003, /* Global data area sizes */ SHT_MIPS_UCODE = 0x70000004, /* Reserved for SGI/MIPS compilers */ SHT_MIPS_DEBUG = 0x70000005, /* MIPS ECOFF debugging information*/ SHT_MIPS_REGINFO = 0x70000006, /* Register usage information */ SHT_MIPS_PACKAGE = 0x70000007, SHT_MIPS_PACKSYM = 0x70000008, SHT_MIPS_RELD = 0x70000009, SHT_MIPS_IFACE = 0x7000000b, SHT_MIPS_CONTENT = 0x7000000c, SHT_MIPS_OPTIONS = 0x7000000d, /* Miscellaneous options. */ SHT_MIPS_SHDR = 0x70000010, SHT_MIPS_FDESC = 0x70000011, SHT_MIPS_EXTSYM = 0x70000012, SHT_MIPS_DENSE = 0x70000013, SHT_MIPS_PDESC = 0x70000014, SHT_MIPS_LOCSYM = 0x70000015, SHT_MIPS_AUXSYM = 0x70000016, SHT_MIPS_OPTSYM = 0x70000017, SHT_MIPS_LOCSTR = 0x70000018, SHT_MIPS_LINE = 0x70000019, SHT_MIPS_RFDESC = 0x7000001a, SHT_MIPS_DELTASYM = 0x7000001b, SHT_MIPS_DELTAINST = 0x7000001c, SHT_MIPS_DELTACLASS = 0x7000001d, SHT_MIPS_DWARF = 0x7000001e, /* DWARF debugging information. */ SHT_MIPS_DELTADECL = 0x7000001f, SHT_MIPS_SYMBOL_LIB = 0x70000020, SHT_MIPS_EVENTS = 0x70000021, /* Event section. */ SHT_MIPS_TRANSLATE = 0x70000022, SHT_MIPS_PIXIE = 0x70000023, SHT_MIPS_XLATE = 0x70000024, SHT_MIPS_XLATE_DEBUG = 0x70000025, SHT_MIPS_WHIRL = 0x70000026, SHT_MIPS_EH_REGION = 0x70000027, SHT_MIPS_XLATE_OLD = 0x70000028, SHT_MIPS_PDR_EXCEPTION = 0x70000029, }; /* Legal values for sh_flags field of Elf32_Shdr. */ enum { SHF_MIPS_GPREL = 0x10000000, /* Must be part of global data area */ SHF_MIPS_MERGE = 0x20000000, SHF_MIPS_ADDR = 0x40000000, SHF_MIPS_STRINGS = 0x80000000, SHF_MIPS_NOSTRIP = 0x08000000, SHF_MIPS_LOCAL = 0x04000000, SHF_MIPS_NAMES = 0x02000000, SHF_MIPS_NODUPE = 0x01000000, }; /* Symbol tables. */ /* MIPS specific values for `st_other'. */ enum { STO_MIPS_DEFAULT = 0x0, STO_MIPS_INTERNAL = 0x1, STO_MIPS_HIDDEN = 0x2, STO_MIPS_PROTECTED = 0x3, STO_MIPS_PLT = 0x8, STO_MIPS_SC_ALIGN_UNUSED = 0xff, }; /* MIPS specific values for `st_info'. */ enum { STB_MIPS_SPLIT_COMMON = 13, }; /* Entries found in sections of type SHT_MIPS_GPTAB. */ union elf32_gptab { struct { elf32_word gt_current_g_value; /* -G value used for compilation */ elf32_word gt_unused; /* Not used */ } gt_header; /* First entry in section */ struct { elf32_word gt_g_value; /* If this value were used for -G */ elf32_word gt_bytes; /* This many bytes would be used */ } gt_entry; /* Subsequent entries in section */ }; /* Entry found in sections of type SHT_MIPS_REGINFO. */ struct elf32_reginfo { elf32_word ri_gprmask; /* General registers used */ elf32_word ri_cprmask[4]; /* Coprocessor registers used */ elf32_sword ri_gp_value; /* $gp register value */ }; /* Entries found in sections of type SHT_MIPS_OPTIONS. */ struct elf_options { uint8_t kind; /* Determines interpretation of the variable part of descriptor. */ uint8_t size; /* Size of descriptor, including header. */ elf32_section section; /* Section header index of section affected, 0 for global options. */ elf32_word info; /* Kind-specific information. */ }; /* Values for `kind' field in Elf_Options. */ enum { ODK_NULL = 0, /* Undefined. */ ODK_REGINFO = 1, /* Register usage information. */ ODK_EXCEPTIONS = 2, /* Exception processing options. */ ODK_PAD = 3, /* Section padding options. */ ODK_HWPATCH = 4, /* Hardware workarounds performed */ ODK_FILL = 5, /* record the fill value used by the linker. */ ODK_TAGS = 6, /* reserve space for desktop tools to write. */ ODK_HWAND = 7, /* HW workarounds. 'AND' bits when merging. */ ODK_HWOR = 8, /* HW workarounds. 'OR' bits when merging. */ }; /* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ enum { OEX_FPU_MIN = 0x1f, /* FPE's which MUST be enabled. */ OEX_FPU_MAX = 0x1f00, /* FPE's which MAY be enabled. */ OEX_PAGE0 = 0x10000, /* page zero must be mapped. */ OEX_SMM = 0x20000, /* Force sequential memory mode? */ OEX_FPDBUG = 0x40000, /* Force floating point debug mode? */ OEX_PRECISEFP = OEX_FPDBUG, OEX_DISMISS = 0x80000, /* Dismiss invalid address faults? */ OEX_FPU_INVAL = 0x10, OEX_FPU_DIV0 = 0x08, OEX_FPU_OFLO = 0x04, OEX_FPU_UFLO = 0x02, OEX_FPU_INEX = 0x01, }; /* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ enum { OHW_R4KEOP = 0x1, /* R4000 end-of-page patch. */ OHW_R8KPFETCH = 0x2, /* may need R8000 prefetch patch. */ OHW_R5KEOP = 0x4, /* R5000 end-of-page patch. */ OHW_R5KCVTL = 0x8, /* R5000 cvt.[ds].l bug. clean=1. */ OPAD_PREFIX = 0x1, OPAD_POSTFIX = 0x2, OPAD_SYMBOL = 0x4, }; /* Entry found in `.options' section. */ struct elf_options_hw { elf32_word hwp_flags1; /* Extra flags. */ elf32_word hwp_flags2; /* Extra flags. */ }; /* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ enum { OHWA0_R4KEOP_CHECKED = 0x00000001, OHWA1_R4KEOP_CLEAN = 0x00000002, }; /* MIPS relocs. */ enum { R_MIPS_NONE = 0, /* No reloc */ R_MIPS_16 = 1, /* Direct 16 bit */ R_MIPS_32 = 2, /* Direct 32 bit */ R_MIPS_REL32 = 3, /* PC relative 32 bit */ R_MIPS_26 = 4, /* Direct 26 bit shifted */ R_MIPS_HI16 = 5, /* High 16 bit */ R_MIPS_LO16 = 6, /* Low 16 bit */ R_MIPS_GPREL16 = 7, /* GP relative 16 bit */ R_MIPS_LITERAL = 8, /* 16 bit literal entry */ R_MIPS_GOT16 = 9, /* 16 bit GOT entry */ R_MIPS_PC16 = 10, /* PC relative 16 bit */ R_MIPS_CALL16 = 11, /* 16 bit GOT entry for function */ R_MIPS_GPREL32 = 12, /* GP relative 32 bit */ R_MIPS_SHIFT5 = 16, R_MIPS_SHIFT6 = 17, R_MIPS_64 = 18, R_MIPS_GOT_DISP = 19, R_MIPS_GOT_PAGE = 20, R_MIPS_GOT_OFST = 21, R_MIPS_GOT_HI16 = 22, R_MIPS_GOT_LO16 = 23, R_MIPS_SUB = 24, R_MIPS_INSERT_A = 25, R_MIPS_INSERT_B = 26, R_MIPS_DELETE = 27, R_MIPS_HIGHER = 28, R_MIPS_HIGHEST = 29, R_MIPS_CALL_HI16 = 30, R_MIPS_CALL_LO16 = 31, R_MIPS_SCN_DISP = 32, R_MIPS_REL16 = 33, R_MIPS_ADD_IMMEDIATE = 34, R_MIPS_PJUMP = 35, R_MIPS_RELGOT = 36, R_MIPS_JALR = 37, R_MIPS_TLS_DTPMOD32 = 38, /* Module number 32 bit */ R_MIPS_TLS_DTPREL32 = 39, /* Module-relative offset 32 bit */ R_MIPS_TLS_DTPMOD64 = 40, /* Module number 64 bit */ R_MIPS_TLS_DTPREL64 = 41, /* Module-relative offset 64 bit */ R_MIPS_TLS_GD = 42, /* 16 bit GOT offset for GD */ R_MIPS_TLS_LDM = 43, /* 16 bit GOT offset for LDM */ R_MIPS_TLS_DTPREL_HI16 = 44, /* Module-relative offset, high 16 bits */ R_MIPS_TLS_DTPREL_LO16 = 45, /* Module-relative offset, low 16 bits */ R_MIPS_TLS_GOTTPREL = 46, /* 16 bit GOT offset for IE */ R_MIPS_TLS_TPREL32 = 47, /* TP-relative offset, 32 bit */ R_MIPS_TLS_TPREL64 = 48, /* TP-relative offset, 64 bit */ R_MIPS_TLS_TPREL_HI16 = 49, /* TP-relative offset, high 16 bits */ R_MIPS_TLS_TPREL_LO16 = 50, /* TP-relative offset, low 16 bits */ R_MIPS_GLOB_DAT = 51, R_MIPS_COPY = 126, R_MIPS_JUMP_SLOT = 127, /* Keep this the last entry. */ R_MIPS_NUM = 128, }; /* Legal values for p_type field of Elf32_Phdr. */ enum { PT_MIPS_REGINFO = 0x70000000, /* Register usage information */ PT_MIPS_RTPROC = 0x70000001, /* Runtime procedure table. */ PT_MIPS_OPTIONS = 0x70000002, }; /* Special program header types. */ enum { PF_MIPS_LOCAL = 0x10000000, }; /* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ enum { RHF_NONE = 0, /* No flags */ RHF_QUICKSTART = (1 << 0), /* Use quickstart */ RHF_NOTPOT = (1 << 1), /* Hash size not power of 2 */ RHF_NO_LIBRARY_REPLACEMENT = (1 << 2), /* Ignore LD_LIBRARY_PATH */ RHF_NO_MOVE = (1 << 3), RHF_SGI_ONLY = (1 << 4), RHF_GUARANTEE_INIT = (1 << 5), RHF_DELTA_C_PLUS_PLUS = (1 << 6), RHF_GUARANTEE_START_INIT = (1 << 7), RHF_PIXIE = (1 << 8), RHF_DEFAULT_DELAY_LOAD = (1 << 9), RHF_REQUICKSTART = (1 << 10), RHF_REQUICKSTARTED = (1 << 11), RHF_CORD = (1 << 12), RHF_NO_UNRES_UNDEF = (1 << 13), RHF_RLD_ORDER_SAFE = (1 << 14), }; /* Entries found in sections of type SHT_MIPS_LIBLIST. */ struct elf32_lib { elf32_word l_name; /* Name (string table index) */ elf32_word l_time_stamp; /* Timestamp */ elf32_word l_checksum; /* Checksum */ elf32_word l_version; /* Interface version */ elf32_word l_flags; /* Flags */ }; struct elf64_lib { elf64_word l_name; /* Name (string table index) */ elf64_word l_time_stamp; /* Timestamp */ elf64_word l_checksum; /* Checksum */ elf64_word l_version; /* Interface version */ elf64_word l_flags; /* Flags */ }; /* Legal values for l_flags. */ enum { LL_NONE = 0, LL_EXACT_MATCH = (1 << 0), /* Require exact match */ LL_IGNORE_INT_VER = (1 << 1), /* Ignore interface version */ LL_REQUIRE_MINOR = (1 << 2), LL_EXPORTS = (1 << 3), LL_DELAY_LOAD = (1 << 4), LL_DELTA = (1 << 5), }; /* Entries found in sections of type SHT_MIPS_CONFLICT. */ using elf32_conflict = elf32_addr; /* HPPA specific definitions. */ /* Legal values for e_flags field of Elf32_Ehdr. */ enum { EF_PARISC_TRAPNIL = 0x00010000, /* Trap nil pointer dereference. */ EF_PARISC_EXT = 0x00020000, /* Program uses arch. extensions. */ EF_PARISC_LSB = 0x00040000, /* Program expects little endian. */ EF_PARISC_WIDE = 0x00080000, /* Program expects wide mode. */ EF_PARISC_NO_KABP = 0x00100000, /* No kernel assisted branch prediction. */ EF_PARISC_LAZYSWAP = 0x00400000, /* Allow lazy swapping. */ EF_PARISC_ARCH = 0x0000ffff, /* Architecture version. */ }; /* Defined values for `e_flags & EF_PARISC_ARCH' are: */ enum { EFA_PARISC_1_0 = 0x020b, /* PA-RISC 1.0 big-endian. */ EFA_PARISC_1_1 = 0x0210, /* PA-RISC 1.1 big-endian. */ EFA_PARISC_2_0 = 0x0214, /* PA-RISC 2.0 big-endian. */ }; /* Additional section indeces. */ enum { SHN_PARISC_ANSI_COMMON = 0xff00, /* Section for tenatively declared symbols in ANSI C. */ SHN_PARISC_HUGE_COMMON = 0xff01, /* Common blocks in huge model. */ }; /* Legal values for sh_type field of Elf32_Shdr. */ enum { SHT_PARISC_EXT = 0x70000000, /* Contains product specific ext. */ SHT_PARISC_UNWIND = 0x70000001, /* Unwind information. */ SHT_PARISC_DOC = 0x70000002, /* Debug info for optimized code. */ }; /* Legal values for sh_flags field of Elf32_Shdr. */ enum { SHF_PARISC_SHORT = 0x20000000, /* Section with short addressing. */ SHF_PARISC_HUGE = 0x40000000, /* Section far from gp. */ SHF_PARISC_SBP = 0x80000000, /* Static branch prediction code. */ }; /* Legal values for ST_TYPE subfield of st_info (symbol type). */ enum { STT_PARISC_MILLICODE = 13 /* Millicode function entry point. */ }; enum { STT_HP_OPAQUE = (STT_LOOS + 0x1), STT_HP_STUB = (STT_LOOS + 0x2), }; /* HPPA relocs. */ enum { R_PARISC_NONE = 0, /* No reloc. */ R_PARISC_DIR32 = 1, /* Direct 32-bit reference. */ R_PARISC_DIR21L = 2, /* Left 21 bits of eff. address. */ R_PARISC_DIR17R = 3, /* Right 17 bits of eff. address. */ R_PARISC_DIR17F = 4, /* 17 bits of eff. address. */ R_PARISC_DIR14R = 6, /* Right 14 bits of eff. address. */ R_PARISC_PCREL32 = 9, /* 32-bit rel. address. */ R_PARISC_PCREL21L = 10, /* Left 21 bits of rel. address. */ R_PARISC_PCREL17R = 11, /* Right 17 bits of rel. address. */ R_PARISC_PCREL17F = 12, /* 17 bits of rel. address. */ R_PARISC_PCREL14R = 14, /* Right 14 bits of rel. address. */ R_PARISC_DPREL21L = 18, /* Left 21 bits of rel. address. */ R_PARISC_DPREL14R = 22, /* Right 14 bits of rel. address. */ R_PARISC_GPREL21L = 26, /* GP-relative, left 21 bits. */ R_PARISC_GPREL14R = 30, /* GP-relative, right 14 bits. */ R_PARISC_LTOFF21L = 34, /* LT-relative, left 21 bits. */ R_PARISC_LTOFF14R = 38, /* LT-relative, right 14 bits. */ R_PARISC_SECREL32 = 41, /* 32 bits section rel. address. */ R_PARISC_SEGBASE = 48, /* No relocation, set segment base. */ R_PARISC_SEGREL32 = 49, /* 32 bits segment rel. address. */ R_PARISC_PLTOFF21L = 50, /* PLT rel. address, left 21 bits. */ R_PARISC_PLTOFF14R = 54, /* PLT rel. address, right 14 bits. */ R_PARISC_LTOFF_FPTR32 = 57, /* 32 bits LT-rel. function pointer. */ R_PARISC_LTOFF_FPTR21L = 58, /* LT-rel. fct ptr, left 21 bits. */ R_PARISC_LTOFF_FPTR14R = 62, /* LT-rel. fct ptr, right 14 bits. */ R_PARISC_FPTR64 = 64, /* 64 bits function address. */ R_PARISC_PLABEL32 = 65, /* 32 bits function address. */ R_PARISC_PLABEL21L = 66, /* Left 21 bits of fdesc address. */ R_PARISC_PLABEL14R = 70, /* Right 14 bits of fdesc address. */ R_PARISC_PCREL64 = 72, /* 64 bits PC-rel. address. */ R_PARISC_PCREL22F = 74, /* 22 bits PC-rel. address. */ R_PARISC_PCREL14WR = 75, /* PC-rel. address, right 14 bits. */ R_PARISC_PCREL14DR = 76, /* PC rel. address, right 14 bits. */ R_PARISC_PCREL16F = 77, /* 16 bits PC-rel. address. */ R_PARISC_PCREL16WF = 78, /* 16 bits PC-rel. address. */ R_PARISC_PCREL16DF = 79, /* 16 bits PC-rel. address. */ R_PARISC_DIR64 = 80, /* 64 bits of eff. address. */ R_PARISC_DIR14WR = 83, /* 14 bits of eff. address. */ R_PARISC_DIR14DR = 84, /* 14 bits of eff. address. */ R_PARISC_DIR16F = 85, /* 16 bits of eff. address. */ R_PARISC_DIR16WF = 86, /* 16 bits of eff. address. */ R_PARISC_DIR16DF = 87, /* 16 bits of eff. address. */ R_PARISC_GPREL64 = 88, /* 64 bits of GP-rel. address. */ R_PARISC_GPREL14WR = 91, /* GP-rel. address, right 14 bits. */ R_PARISC_GPREL14DR = 92, /* GP-rel. address, right 14 bits. */ R_PARISC_GPREL16F = 93, /* 16 bits GP-rel. address. */ R_PARISC_GPREL16WF = 94, /* 16 bits GP-rel. address. */ R_PARISC_GPREL16DF = 95, /* 16 bits GP-rel. address. */ R_PARISC_LTOFF64 = 96, /* 64 bits LT-rel. address. */ R_PARISC_LTOFF14WR = 99, /* LT-rel. address, right 14 bits. */ R_PARISC_LTOFF14DR = 100, /* LT-rel. address, right 14 bits. */ R_PARISC_LTOFF16F = 101, /* 16 bits LT-rel. address. */ R_PARISC_LTOFF16WF = 102, /* 16 bits LT-rel. address. */ R_PARISC_LTOFF16DF = 103, /* 16 bits LT-rel. address. */ R_PARISC_SECREL64 = 104, /* 64 bits section rel. address. */ R_PARISC_SEGREL64 = 112, /* 64 bits segment rel. address. */ R_PARISC_PLTOFF14WR = 115, /* PLT-rel. address, right 14 bits. */ R_PARISC_PLTOFF14DR = 116, /* PLT-rel. address, right 14 bits. */ R_PARISC_PLTOFF16F = 117, /* 16 bits LT-rel. address. */ R_PARISC_PLTOFF16WF = 118, /* 16 bits PLT-rel. address. */ R_PARISC_PLTOFF16DF = 119, /* 16 bits PLT-rel. address. */ R_PARISC_LTOFF_FPTR64 = 120, /* 64 bits LT-rel. function ptr. */ R_PARISC_LTOFF_FPTR14WR = 123, /* LT-rel. fct. ptr., right 14 bits. */ R_PARISC_LTOFF_FPTR14DR = 124, /* LT-rel. fct. ptr., right 14 bits. */ R_PARISC_LTOFF_FPTR16F = 125, /* 16 bits LT-rel. function ptr. */ R_PARISC_LTOFF_FPTR16WF = 126, /* 16 bits LT-rel. function ptr. */ R_PARISC_LTOFF_FPTR16DF = 127, /* 16 bits LT-rel. function ptr. */ R_PARISC_LORESERVE = 128, R_PARISC_COPY = 128, /* Copy relocation. */ R_PARISC_IPLT = 129, /* Dynamic reloc, imported PLT */ R_PARISC_EPLT = 130, /* Dynamic reloc, exported PLT */ R_PARISC_TPREL32 = 153, /* 32 bits TP-rel. address. */ R_PARISC_TPREL21L = 154, /* TP-rel. address, left 21 bits. */ R_PARISC_TPREL14R = 158, /* TP-rel. address, right 14 bits. */ R_PARISC_LTOFF_TP21L = 162, /* LT-TP-rel. address, left 21 bits. */ R_PARISC_LTOFF_TP14R = 166, /* LT-TP-rel. address, right 14 bits.*/ R_PARISC_LTOFF_TP14F = 167, /* 14 bits LT-TP-rel. address. */ R_PARISC_TPREL64 = 216, /* 64 bits TP-rel. address. */ R_PARISC_TPREL14WR = 219, /* TP-rel. address, right 14 bits. */ R_PARISC_TPREL14DR = 220, /* TP-rel. address, right 14 bits. */ R_PARISC_TPREL16F = 221, /* 16 bits TP-rel. address. */ R_PARISC_TPREL16WF = 222, /* 16 bits TP-rel. address. */ R_PARISC_TPREL16DF = 223, /* 16 bits TP-rel. address. */ R_PARISC_LTOFF_TP64 = 224, /* 64 bits LT-TP-rel. address. */ R_PARISC_LTOFF_TP14WR = 227, /* LT-TP-rel. address, right 14 bits.*/ R_PARISC_LTOFF_TP14DR = 228, /* LT-TP-rel. address, right 14 bits.*/ R_PARISC_LTOFF_TP16F = 229, /* 16 bits LT-TP-rel. address. */ R_PARISC_LTOFF_TP16WF = 230, /* 16 bits LT-TP-rel. address. */ R_PARISC_LTOFF_TP16DF = 231, /* 16 bits LT-TP-rel. address. */ R_PARISC_GNU_VTENTRY = 232, R_PARISC_GNU_VTINHERIT = 233, R_PARISC_TLS_GD21L = 234, /* GD 21-bit left. */ R_PARISC_TLS_GD14R = 235, /* GD 14-bit right. */ R_PARISC_TLS_GDCALL = 236, /* GD call to __t_g_a. */ R_PARISC_TLS_LDM21L = 237, /* LD module 21-bit left. */ R_PARISC_TLS_LDM14R = 238, /* LD module 14-bit right. */ R_PARISC_TLS_LDMCALL = 239, /* LD module call to __t_g_a. */ R_PARISC_TLS_LDO21L = 240, /* LD offset 21-bit left. */ R_PARISC_TLS_LDO14R = 241, /* LD offset 14-bit right. */ R_PARISC_TLS_DTPMOD32 = 242, /* DTP module 32-bit. */ R_PARISC_TLS_DTPMOD64 = 243, /* DTP module 64-bit. */ R_PARISC_TLS_DTPOFF32 = 244, /* DTP offset 32-bit. */ R_PARISC_TLS_DTPOFF64 = 245, /* DTP offset 32-bit. */ R_PARISC_TLS_LE21L = R_PARISC_TPREL21L, R_PARISC_TLS_LE14R = R_PARISC_TPREL14R, R_PARISC_TLS_IE21L = R_PARISC_LTOFF_TP21L, R_PARISC_TLS_IE14R = R_PARISC_LTOFF_TP14R, R_PARISC_TLS_TPREL32 = R_PARISC_TPREL32, R_PARISC_TLS_TPREL64 = R_PARISC_TPREL64, R_PARISC_HIRESERVE = 255, }; /* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ enum { PT_HP_TLS = (PT_LOOS + 0x0), PT_HP_CORE_NONE = (PT_LOOS + 0x1), PT_HP_CORE_VERSION = (PT_LOOS + 0x2), PT_HP_CORE_KERNEL = (PT_LOOS + 0x3), PT_HP_CORE_COMM = (PT_LOOS + 0x4), PT_HP_CORE_PROC = (PT_LOOS + 0x5), PT_HP_CORE_LOADABLE = (PT_LOOS + 0x6), PT_HP_CORE_STACK = (PT_LOOS + 0x7), PT_HP_CORE_SHM = (PT_LOOS + 0x8), PT_HP_CORE_MMF = (PT_LOOS + 0x9), PT_HP_PARALLEL = (PT_LOOS + 0x10), PT_HP_FASTBIND = (PT_LOOS + 0x11), PT_HP_OPT_ANNOT = (PT_LOOS + 0x12), PT_HP_HSL_ANNOT = (PT_LOOS + 0x13), PT_HP_STACK = (PT_LOOS + 0x14), }; enum { PT_PARISC_ARCHEXT = 0x70000000, PT_PARISC_UNWIND = 0x70000001, }; /* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ enum { PF_PARISC_SBP = 0x08000000, }; enum { PF_HP_PAGE_SIZE = 0x00100000, PF_HP_FAR_SHARED = 0x00200000, PF_HP_NEAR_SHARED = 0x00400000, PF_HP_CODE = 0x01000000, PF_HP_MODIFY = 0x02000000, PF_HP_LAZYSWAP = 0x04000000, PF_HP_SBP = 0x08000000, }; /* Alpha specific definitions. */ /* Legal values for e_flags field of Elf64_Ehdr. */ enum { EF_ALPHA_32BIT = 1, /* All addresses must be < 2GB. */ EF_ALPHA_CANRELAX = 2 /* Relocations for relaxing exist. */ }; /* Legal values for sh_type field of Elf64_Shdr. */ /* These two are primerily concerned with ECOFF debugging info. */ enum { SHT_ALPHA_DEBUG = 0x70000001, SHT_ALPHA_REGINFO = 0x70000002, }; /* Legal values for sh_flags field of Elf64_Shdr. */ enum { SHF_ALPHA_GPREL = 0x10000000, }; /* Legal values for st_other field of Elf64_Sym. */ enum { STO_ALPHA_NOPV = 0x80, /* No PV required. */ STO_ALPHA_STD_GPLOAD = 0x88, /* PV only used for initial ldgp. */ }; /* Alpha relocs. */ enum { R_ALPHA_NONE = 0, /* No reloc */ R_ALPHA_REFLONG = 1, /* Direct 32 bit */ R_ALPHA_REFQUAD = 2, /* Direct 64 bit */ R_ALPHA_GPREL32 = 3, /* GP relative 32 bit */ R_ALPHA_LITERAL = 4, /* GP relative 16 bit w/optimization */ R_ALPHA_LITUSE = 5, /* Optimization hint for LITERAL */ R_ALPHA_GPDISP = 6, /* Add displacement to GP */ R_ALPHA_BRADDR = 7, /* PC+4 relative 23 bit shifted */ R_ALPHA_HINT = 8, /* PC+4 relative 16 bit shifted */ R_ALPHA_SREL16 = 9, /* PC relative 16 bit */ R_ALPHA_SREL32 = 10, /* PC relative 32 bit */ R_ALPHA_SREL64 = 11, /* PC relative 64 bit */ R_ALPHA_GPRELHIGH = 17, /* GP relative 32 bit, high 16 bits */ R_ALPHA_GPRELLOW = 18, /* GP relative 32 bit, low 16 bits */ R_ALPHA_GPREL16 = 19, /* GP relative 16 bit */ R_ALPHA_COPY = 24, /* Copy symbol at runtime */ R_ALPHA_GLOB_DAT = 25, /* Create GOT entry */ R_ALPHA_JMP_SLOT = 26, /* Create PLT entry */ R_ALPHA_RELATIVE = 27, /* Adjust by program base */ R_ALPHA_TLS_GD_HI = 28, R_ALPHA_TLSGD = 29, R_ALPHA_TLS_LDM = 30, R_ALPHA_DTPMOD64 = 31, R_ALPHA_GOTDTPREL = 32, R_ALPHA_DTPREL64 = 33, R_ALPHA_DTPRELHI = 34, R_ALPHA_DTPRELLO = 35, R_ALPHA_DTPREL16 = 36, R_ALPHA_GOTTPREL = 37, R_ALPHA_TPREL64 = 38, R_ALPHA_TPRELHI = 39, R_ALPHA_TPRELLO = 40, R_ALPHA_TPREL16 = 41, /* Keep this the last entry. */ R_ALPHA_NUM = 46, }; /* Magic values of the LITUSE relocation addend. */ enum { LITUSE_ALPHA_ADDR = 0, LITUSE_ALPHA_BASE = 1, LITUSE_ALPHA_BYTOFF = 2, LITUSE_ALPHA_JSR = 3, LITUSE_ALPHA_TLS_GD = 4, LITUSE_ALPHA_TLS_LDM = 5, }; /* Legal values for d_tag of Elf64_Dyn. */ enum { DT_ALPHA_PLTRO = (DT_LOPROC + 0), DT_ALPHA_NUM = 1, }; /* PowerPC specific declarations */ /* Values for Elf32/64_Ehdr.e_flags. */ enum { EF_PPC_EMB = 0x80000000, /* PowerPC embedded flag */ }; /* Cygnus local bits below */ enum { EF_PPC_RELOCATABLE = 0x00010000, /* PowerPC -mrelocatable flag*/ EF_PPC_RELOCATABLE_LIB = 0x00008000, /* PowerPC -mrelocatable-lib flag */ }; /* PowerPC relocations defined by the ABIs */ enum { R_PPC_NONE = 0, R_PPC_ADDR32 = 1, /* 32bit absolute address */ R_PPC_ADDR24 = 2, /* 26bit address, 2 bits ignored. */ R_PPC_ADDR16 = 3, /* 16bit absolute address */ R_PPC_ADDR16_LO = 4, /* lower 16bit of absolute address */ R_PPC_ADDR16_HI = 5, /* high 16bit of absolute address */ R_PPC_ADDR16_HA = 6, /* adjusted high 16bit */ R_PPC_ADDR14 = 7, /* 16bit address, 2 bits ignored */ R_PPC_ADDR14_BRTAKEN = 8, R_PPC_ADDR14_BRNTAKEN = 9, R_PPC_REL24 = 10, /* PC relative 26 bit */ R_PPC_REL14 = 11, /* PC relative 16 bit */ R_PPC_REL14_BRTAKEN = 12, R_PPC_REL14_BRNTAKEN = 13, R_PPC_GOT16 = 14, R_PPC_GOT16_LO = 15, R_PPC_GOT16_HI = 16, R_PPC_GOT16_HA = 17, R_PPC_PLTREL24 = 18, R_PPC_COPY = 19, R_PPC_GLOB_DAT = 20, R_PPC_JMP_SLOT = 21, R_PPC_RELATIVE = 22, R_PPC_LOCAL24PC = 23, R_PPC_UADDR32 = 24, R_PPC_UADDR16 = 25, R_PPC_REL32 = 26, R_PPC_PLT32 = 27, R_PPC_PLTREL32 = 28, R_PPC_PLT16_LO = 29, R_PPC_PLT16_HI = 30, R_PPC_PLT16_HA = 31, R_PPC_SDAREL16 = 32, R_PPC_SECTOFF = 33, R_PPC_SECTOFF_LO = 34, R_PPC_SECTOFF_HI = 35, R_PPC_SECTOFF_HA = 36, }; /* PowerPC relocations defined for the TLS access ABI. */ enum { R_PPC_TLS = 67, /* none (sym+add)@tls */ R_PPC_DTPMOD32 = 68, /* word32 (sym+add)@dtpmod */ R_PPC_TPREL16 = 69, /* half16* (sym+add)@tprel */ R_PPC_TPREL16_LO = 70, /* half16 (sym+add)@tprel@l */ R_PPC_TPREL16_HI = 71, /* half16 (sym+add)@tprel@h */ R_PPC_TPREL16_HA = 72, /* half16 (sym+add)@tprel@ha */ R_PPC_TPREL32 = 73, /* word32 (sym+add)@tprel */ R_PPC_DTPREL16 = 74, /* half16* (sym+add)@dtprel */ R_PPC_DTPREL16_LO = 75, /* half16 (sym+add)@dtprel@l */ R_PPC_DTPREL16_HI = 76, /* half16 (sym+add)@dtprel@h */ R_PPC_DTPREL16_HA = 77, /* half16 (sym+add)@dtprel@ha */ R_PPC_DTPREL32 = 78, /* word32 (sym+add)@dtprel */ R_PPC_GOT_TLSGD16 = 79, /* half16* (sym+add)@got@tlsgd */ R_PPC_GOT_TLSGD16_LO = 80, /* half16 (sym+add)@got@tlsgd@l */ R_PPC_GOT_TLSGD16_HI = 81, /* half16 (sym+add)@got@tlsgd@h */ R_PPC_GOT_TLSGD16_HA = 82, /* half16 (sym+add)@got@tlsgd@ha */ R_PPC_GOT_TLSLD16 = 83, /* half16* (sym+add)@got@tlsld */ R_PPC_GOT_TLSLD16_LO = 84, /* half16 (sym+add)@got@tlsld@l */ R_PPC_GOT_TLSLD16_HI = 85, /* half16 (sym+add)@got@tlsld@h */ R_PPC_GOT_TLSLD16_HA = 86, /* half16 (sym+add)@got@tlsld@ha */ R_PPC_GOT_TPREL16 = 87, /* half16* (sym+add)@got@tprel */ R_PPC_GOT_TPREL16_LO = 88, /* half16 (sym+add)@got@tprel@l */ R_PPC_GOT_TPREL16_HI = 89, /* half16 (sym+add)@got@tprel@h */ R_PPC_GOT_TPREL16_HA = 90, /* half16 (sym+add)@got@tprel@ha */ R_PPC_GOT_DTPREL16 = 91, /* half16* (sym+add)@got@dtprel */ R_PPC_GOT_DTPREL16_LO = 92, /* half16* (sym+add)@got@dtprel@l */ R_PPC_GOT_DTPREL16_HI = 93, /* half16* (sym+add)@got@dtprel@h */ R_PPC_GOT_DTPREL16_HA = 94, /* half16* (sym+add)@got@dtprel@ha */ }; /* The remaining relocs are from the Embedded ELF ABI, and are not in the SVR4 ELF ABI. */ enum { R_PPC_EMB_NADDR32 = 101, R_PPC_EMB_NADDR16 = 102, R_PPC_EMB_NADDR16_LO = 103, R_PPC_EMB_NADDR16_HI = 104, R_PPC_EMB_NADDR16_HA = 105, R_PPC_EMB_SDAI16 = 106, R_PPC_EMB_SDA2I16 = 107, R_PPC_EMB_SDA2REL = 108, R_PPC_EMB_SDA21 = 109, /* 16 bit offset in SDA */ R_PPC_EMB_MRKREF = 110, R_PPC_EMB_RELSEC16 = 111, R_PPC_EMB_RELST_LO = 112, R_PPC_EMB_RELST_HI = 113, R_PPC_EMB_RELST_HA = 114, R_PPC_EMB_BIT_FLD = 115, R_PPC_EMB_RELSDA = 116, /* 16 bit relative offset in SDA */ }; /* Diab tool relocations. */ enum { R_PPC_DIAB_SDA21_LO = 180, /* like EMB_SDA21, but lower 16 bit */ R_PPC_DIAB_SDA21_HI = 181, /* like EMB_SDA21, but high 16 bit */ R_PPC_DIAB_SDA21_HA = 182, /* like EMB_SDA21, adjusted high 16 */ R_PPC_DIAB_RELSDA_LO = 183, /* like EMB_RELSDA, but lower 16 bit */ R_PPC_DIAB_RELSDA_HI = 184, /* like EMB_RELSDA, but high 16 bit */ R_PPC_DIAB_RELSDA_HA = 185, /* like EMB_RELSDA, adjusted high 16 */ }; /* GNU extension to support local ifunc. */ enum { R_PPC_IRELATIVE = 248, }; /* GNU relocs used in PIC code sequences. */ enum { R_PPC_REL16 = 249, /* half16 (sym+add-.) */ R_PPC_REL16_LO = 250, /* half16 (sym+add-.)@l */ R_PPC_REL16_HI = 251, /* half16 (sym+add-.)@h */ R_PPC_REL16_HA = 252, /* half16 (sym+add-.)@ha */ }; /* This is a phony reloc to handle any old fashioned TOC16 references that may still be in object files. */ enum { R_PPC_TOC16 = 255, }; /* PowerPC specific values for the Dyn d_tag field. */ enum { DT_PPC_GOT = (DT_LOPROC + 0), DT_PPC_NUM = 1, }; /* PowerPC64 relocations defined by the ABIs */ enum { R_PPC64_NONE = R_PPC_NONE, R_PPC64_ADDR32 = R_PPC_ADDR32, /* 32bit absolute address */ R_PPC64_ADDR24 = R_PPC_ADDR24, /* 26bit address, word aligned */ R_PPC64_ADDR16 = R_PPC_ADDR16, /* 16bit absolute address */ R_PPC64_ADDR16_LO = R_PPC_ADDR16_LO, /* lower 16bits of address */ R_PPC64_ADDR16_HI = R_PPC_ADDR16_HI, /* high 16bits of address. */ R_PPC64_ADDR16_HA = R_PPC_ADDR16_HA, /* adjusted high 16bits. */ R_PPC64_ADDR14 = R_PPC_ADDR14, /* 16bit address, word aligned */ R_PPC64_ADDR14_BRTAKEN = R_PPC_ADDR14_BRTAKEN, R_PPC64_ADDR14_BRNTAKEN = R_PPC_ADDR14_BRNTAKEN, R_PPC64_REL24 = R_PPC_REL24, /* PC-rel. 26 bit, word aligned */ R_PPC64_REL14 = R_PPC_REL14, /* PC relative 16 bit */ R_PPC64_REL14_BRTAKEN = R_PPC_REL14_BRTAKEN, R_PPC64_REL14_BRNTAKEN = R_PPC_REL14_BRNTAKEN, R_PPC64_GOT16 = R_PPC_GOT16, R_PPC64_GOT16_LO = R_PPC_GOT16_LO, R_PPC64_GOT16_HI = R_PPC_GOT16_HI, R_PPC64_GOT16_HA = R_PPC_GOT16_HA, R_PPC64_COPY = R_PPC_COPY, R_PPC64_GLOB_DAT = R_PPC_GLOB_DAT, R_PPC64_JMP_SLOT = R_PPC_JMP_SLOT, R_PPC64_RELATIVE = R_PPC_RELATIVE, R_PPC64_UADDR32 = R_PPC_UADDR32, R_PPC64_UADDR16 = R_PPC_UADDR16, R_PPC64_REL32 = R_PPC_REL32, R_PPC64_PLT32 = R_PPC_PLT32, R_PPC64_PLTREL32 = R_PPC_PLTREL32, R_PPC64_PLT16_LO = R_PPC_PLT16_LO, R_PPC64_PLT16_HI = R_PPC_PLT16_HI, R_PPC64_PLT16_HA = R_PPC_PLT16_HA, R_PPC64_SECTOFF = R_PPC_SECTOFF, R_PPC64_SECTOFF_LO = R_PPC_SECTOFF_LO, R_PPC64_SECTOFF_HI = R_PPC_SECTOFF_HI, R_PPC64_SECTOFF_HA = R_PPC_SECTOFF_HA, R_PPC64_ADDR30 = 37, /* word30 (S + A - P) >> 2 */ R_PPC64_ADDR64 = 38, /* doubleword64 S + A */ R_PPC64_ADDR16_HIGHER = 39, /* half16 #higher(S + A) */ R_PPC64_ADDR16_HIGHERA = 40, /* half16 #highera(S + A) */ R_PPC64_ADDR16_HIGHEST = 41, /* half16 #highest(S + A) */ R_PPC64_ADDR16_HIGHESTA = 42, /* half16 #highesta(S + A) */ R_PPC64_UADDR64 = 43, /* doubleword64 S + A */ R_PPC64_REL64 = 44, /* doubleword64 S + A - P */ R_PPC64_PLT64 = 45, /* doubleword64 L + A */ R_PPC64_PLTREL64 = 46, /* doubleword64 L + A - P */ R_PPC64_TOC16 = 47, /* half16* S + A - .TOC */ R_PPC64_TOC16_LO = 48, /* half16 #lo(S + A - .TOC.) */ R_PPC64_TOC16_HI = 49, /* half16 #hi(S + A - .TOC.) */ R_PPC64_TOC16_HA = 50, /* half16 #ha(S + A - .TOC.) */ R_PPC64_TOC = 51, /* doubleword64 .TOC */ R_PPC64_PLTGOT16 = 52, /* half16* M + A */ R_PPC64_PLTGOT16_LO = 53, /* half16 #lo(M + A) */ R_PPC64_PLTGOT16_HI = 54, /* half16 #hi(M + A) */ R_PPC64_PLTGOT16_HA = 55, /* half16 #ha(M + A) */ R_PPC64_ADDR16_DS = 56, /* half16ds* (S + A) >> 2 */ R_PPC64_ADDR16_LO_DS = 57, /* half16ds #lo(S + A) >> 2 */ R_PPC64_GOT16_DS = 58, /* half16ds* (G + A) >> 2 */ R_PPC64_GOT16_LO_DS = 59, /* half16ds #lo(G + A) >> 2 */ R_PPC64_PLT16_LO_DS = 60, /* half16ds #lo(L + A) >> 2 */ R_PPC64_SECTOFF_DS = 61, /* half16ds* (R + A) >> 2 */ R_PPC64_SECTOFF_LO_DS = 62, /* half16ds #lo(R + A) >> 2 */ R_PPC64_TOC16_DS = 63, /* half16ds* (S + A - .TOC.) >> 2 */ R_PPC64_TOC16_LO_DS = 64, /* half16ds #lo(S + A - .TOC.) >> 2 */ R_PPC64_PLTGOT16_DS = 65, /* half16ds* (M + A) >> 2 */ R_PPC64_PLTGOT16_LO_DS = 66, /* half16ds #lo(M + A) >> 2 */ }; /* PowerPC64 relocations defined for the TLS access ABI. */ enum { R_PPC64_TLS = 67, /* none (sym+add)@tls */ R_PPC64_DTPMOD64 = 68, /* doubleword64 (sym+add)@dtpmod */ R_PPC64_TPREL16 = 69, /* half16* (sym+add)@tprel */ R_PPC64_TPREL16_LO = 70, /* half16 (sym+add)@tprel@l */ R_PPC64_TPREL16_HI = 71, /* half16 (sym+add)@tprel@h */ R_PPC64_TPREL16_HA = 72, /* half16 (sym+add)@tprel@ha */ R_PPC64_TPREL64 = 73, /* doubleword64 (sym+add)@tprel */ R_PPC64_DTPREL16 = 74, /* half16* (sym+add)@dtprel */ R_PPC64_DTPREL16_LO = 75, /* half16 (sym+add)@dtprel@l */ R_PPC64_DTPREL16_HI = 76, /* half16 (sym+add)@dtprel@h */ R_PPC64_DTPREL16_HA = 77, /* half16 (sym+add)@dtprel@ha */ R_PPC64_DTPREL64 = 78, /* doubleword64 (sym+add)@dtprel */ R_PPC64_GOT_TLSGD16 = 79, /* half16* (sym+add)@got@tlsgd */ R_PPC64_GOT_TLSGD16_LO = 80, /* half16 (sym+add)@got@tlsgd@l */ R_PPC64_GOT_TLSGD16_HI = 81, /* half16 (sym+add)@got@tlsgd@h */ R_PPC64_GOT_TLSGD16_HA = 82, /* half16 (sym+add)@got@tlsgd@ha */ R_PPC64_GOT_TLSLD16 = 83, /* half16* (sym+add)@got@tlsld */ R_PPC64_GOT_TLSLD16_LO = 84, /* half16 (sym+add)@got@tlsld@l */ R_PPC64_GOT_TLSLD16_HI = 85, /* half16 (sym+add)@got@tlsld@h */ R_PPC64_GOT_TLSLD16_HA = 86, /* half16 (sym+add)@got@tlsld@ha */ R_PPC64_GOT_TPREL16_DS = 87, /* half16ds* (sym+add)@got@tprel */ R_PPC64_GOT_TPREL16_LO_DS = 88, /* half16ds (sym+add)@got@tprel@l */ R_PPC64_GOT_TPREL16_HI = 89, /* half16 (sym+add)@got@tprel@h */ R_PPC64_GOT_TPREL16_HA = 90, /* half16 (sym+add)@got@tprel@ha */ R_PPC64_GOT_DTPREL16_DS = 91, /* half16ds* (sym+add)@got@dtprel */ R_PPC64_GOT_DTPREL16_LO_DS = 92, /* half16ds (sym+add)@got@dtprel@l */ R_PPC64_GOT_DTPREL16_HI = 93, /* half16 (sym+add)@got@dtprel@h */ R_PPC64_GOT_DTPREL16_HA = 94, /* half16 (sym+add)@got@dtprel@ha */ R_PPC64_TPREL16_DS = 95, /* half16ds* (sym+add)@tprel */ R_PPC64_TPREL16_LO_DS = 96, /* half16ds (sym+add)@tprel@l */ R_PPC64_TPREL16_HIGHER = 97, /* half16 (sym+add)@tprel@higher */ R_PPC64_TPREL16_HIGHERA = 98, /* half16 (sym+add)@tprel@highera */ R_PPC64_TPREL16_HIGHEST = 99, /* half16 (sym+add)@tprel@highest */ R_PPC64_TPREL16_HIGHESTA = 100, /* half16 (sym+add)@tprel@highesta */ R_PPC64_DTPREL16_DS = 101, /* half16ds* (sym+add)@dtprel */ R_PPC64_DTPREL16_LO_DS = 102, /* half16ds (sym+add)@dtprel@l */ R_PPC64_DTPREL16_HIGHER = 103, /* half16 (sym+add)@dtprel@higher */ R_PPC64_DTPREL16_HIGHERA = 104, /* half16 (sym+add)@dtprel@highera */ R_PPC64_DTPREL16_HIGHEST = 105, /* half16 (sym+add)@dtprel@highest */ R_PPC64_DTPREL16_HIGHESTA = 106, /* half16 (sym+add)@dtprel@highesta */ }; /* GNU extension to support local ifunc. */ enum { R_PPC64_JMP_IREL = 247, R_PPC64_IRELATIVE = 248, R_PPC64_REL16 = 249, /* half16 (sym+add-.) */ R_PPC64_REL16_LO = 250, /* half16 (sym+add-.)@l */ R_PPC64_REL16_HI = 251, /* half16 (sym+add-.)@h */ R_PPC64_REL16_HA = 252, /* half16 (sym+add-.)@ha */ }; /* PowerPC64 specific values for the Dyn d_tag field. */ enum { DT_PPC64_GLINK = (DT_LOPROC + 0), DT_PPC64_OPD = (DT_LOPROC + 1), DT_PPC64_OPDSZ = (DT_LOPROC + 2), DT_PPC64_NUM = 3, }; /* ARM specific declarations */ /* Processor specific flags for the ELF header e_flags field. */ enum { EF_ARM_RELEXEC = 0x01, EF_ARM_HASENTRY = 0x02, EF_ARM_INTERWORK = 0x04, EF_ARM_APCS_26 = 0x08, EF_ARM_APCS_FLOAT = 0x10, EF_ARM_PIC = 0x20, EF_ARM_ALIGN8 = 0x40, /* 8-bit structure alignment is in use */ EF_ARM_NEW_ABI = 0x80, EF_ARM_OLD_ABI = 0x100, EF_ARM_SOFT_FLOAT = 0x200, EF_ARM_VFP_FLOAT = 0x400, EF_ARM_MAVERICK_FLOAT = 0x800, }; /* Other constants defined in the ARM ELF spec. version B-01. */ /* NB. These conflict with values defined above. */ enum { EF_ARM_SYMSARESORTED = 0x04, EF_ARM_DYNSYMSUSESEGIDX = 0x08, EF_ARM_MAPSYMSFIRST = 0x10, EF_ARM_EABIMASK = 0XFF000000 }; /* Constants defined in AAELF. */ enum { EF_ARM_BE8 = 0x00800000, EF_ARM_LE8 = 0x00400000, }; template constexpr T EF_ARM_EABI_VERSION(T flags) { return flags & EF_ARM_EABIMASK; } enum { EF_ARM_EABI_UNKNOWN = 0x00000000, EF_ARM_EABI_VER1 = 0x01000000, EF_ARM_EABI_VER2 = 0x02000000, EF_ARM_EABI_VER3 = 0x03000000, EF_ARM_EABI_VER4 = 0x04000000, EF_ARM_EABI_VER5 = 0x05000000, }; /* Additional symbol types for Thumb. */ enum { STT_ARM_TFUNC = STT_LOPROC, /* A Thumb function. */ STT_ARM_16BIT = STT_HIPROC, /* A Thumb label. */ }; /* ARM-specific values for sh_flags */ enum { SHF_ARM_ENTRYSECT = 0x10000000, /* Section contains an entry point */ SHF_ARM_COMDEF = 0x80000000, /* Section may be multiply defined in the input to a link step. */ }; /* ARM-specific program header flags */ enum { PF_ARM_SB = 0x10000000, /* Segment contains the location addressed by the static base. */ PF_ARM_PI = 0x20000000, /* Position-independent segment. */ PF_ARM_ABS = 0x40000000, /* Absolute segment. */ }; /* Processor specific values for the Phdr p_type field. */ enum { PT_ARM_EXIDX = (PT_LOPROC + 1), /* ARM unwind segment. */ }; /* Processor specific values for the Shdr sh_type field. */ enum { SHT_ARM_EXIDX = (SHT_LOPROC + 1), /* ARM unwind section. */ SHT_ARM_PREEMPTMAP = (SHT_LOPROC + 2), /* Preemption details. */ SHT_ARM_ATTRIBUTES = (SHT_LOPROC + 3), /* ARM attributes section. */ }; /* ARM relocs. */ enum { R_ARM_NONE = 0, /* No reloc */ R_ARM_PC24 = 1, /* PC relative 26 bit branch */ R_ARM_ABS32 = 2, /* Direct 32 bit */ R_ARM_REL32 = 3, /* PC relative 32 bit */ R_ARM_PC13 = 4, R_ARM_ABS16 = 5, /* Direct 16 bit */ R_ARM_ABS12 = 6, /* Direct 12 bit */ R_ARM_THM_ABS5 = 7, R_ARM_ABS8 = 8, /* Direct 8 bit */ R_ARM_SBREL32 = 9, R_ARM_THM_PC22 = 10, R_ARM_THM_PC8 = 11, R_ARM_AMP_VCALL9 = 12, R_ARM_SWI24 = 13, /* Obsolete static relocation. */ R_ARM_TLS_DESC = 13, /* Dynamic relocation. */ R_ARM_THM_SWI8 = 14, R_ARM_XPC25 = 15, R_ARM_THM_XPC22 = 16, R_ARM_TLS_DTPMOD32 = 17, /* ID of module containing symbol */ R_ARM_TLS_DTPOFF32 = 18, /* Offset in TLS block */ R_ARM_TLS_TPOFF32 = 19, /* Offset in static TLS block */ R_ARM_COPY = 20, /* Copy symbol at runtime */ R_ARM_GLOB_DAT = 21, /* Create GOT entry */ R_ARM_JUMP_SLOT = 22, /* Create PLT entry */ R_ARM_RELATIVE = 23, /* Adjust by program base */ R_ARM_GOTOFF = 24, /* 32 bit offset to GOT */ R_ARM_GOTPC = 25, /* 32 bit PC relative offset to GOT */ R_ARM_GOT32 = 26, /* 32 bit GOT entry */ R_ARM_PLT32 = 27, /* 32 bit PLT address */ R_ARM_ALU_PCREL_7_0 = 32, R_ARM_ALU_PCREL_15_8 = 33, R_ARM_ALU_PCREL_23_15 = 34, R_ARM_LDR_SBREL_11_0 = 35, R_ARM_ALU_SBREL_19_12 = 36, R_ARM_ALU_SBREL_27_20 = 37, R_ARM_TLS_GOTDESC = 90, R_ARM_TLS_CALL = 91, R_ARM_TLS_DESCSEQ = 92, R_ARM_THM_TLS_CALL = 93, R_ARM_GNU_VTENTRY = 100, R_ARM_GNU_VTINHERIT = 101, R_ARM_THM_PC11 = 102, /* thumb unconditional branch */ R_ARM_THM_PC9 = 103, /* thumb conditional branch */ R_ARM_TLS_GD32 = 104, /* PC-rel 32 bit for global dynamic thread local data */ R_ARM_TLS_LDM32 = 105, /* PC-rel 32 bit for local dynamic thread local data */ R_ARM_TLS_LDO32 = 106, /* 32 bit offset relative to TLS block */ R_ARM_TLS_IE32 = 107, /* PC-rel 32 bit for GOT entry of static TLS block offset */ R_ARM_TLS_LE32 = 108, /* 32 bit offset relative to static TLS block */ R_ARM_THM_TLS_DESCSEQ = 129, R_ARM_IRELATIVE = 160, R_ARM_RXPC25 = 249, R_ARM_RSBREL32 = 250, R_ARM_THM_RPC22 = 251, R_ARM_RREL32 = 252, R_ARM_RABS22 = 253, R_ARM_RPC24 = 254, R_ARM_RBASE = 255, /* Keep this the last entry. */ R_ARM_NUM = 256, }; /* IA-64 specific declarations. */ /* Processor specific flags for the Ehdr e_flags field. */ enum { EF_IA_64_MASKOS = 0x0000000f, /* os-specific flags */ EF_IA_64_ABI64 = 0x00000010, /* 64-bit ABI */ EF_IA_64_ARCH = 0xff000000, /* arch. version mask */ }; /* Processor specific values for the Phdr p_type field. */ enum { PT_IA_64_ARCHEXT = (PT_LOPROC + 0), /* arch extension bits */ PT_IA_64_UNWIND = (PT_LOPROC + 1), /* ia64 unwind bits */ PT_IA_64_HP_OPT_ANOT = (PT_LOOS + 0x12), PT_IA_64_HP_HSL_ANOT = (PT_LOOS + 0x13), PT_IA_64_HP_STACK = (PT_LOOS + 0x14), }; /* Processor specific flags for the Phdr p_flags field. */ enum { PF_IA_64_NORECOV = 0x80000000, /* spec insns w/o recovery */ }; /* Processor specific values for the Shdr sh_type field. */ enum { SHT_IA_64_EXT = (SHT_LOPROC + 0), /* extension bits */ SHT_IA_64_UNWIND = (SHT_LOPROC + 1), /* unwind bits */ }; /* Processor specific flags for the Shdr sh_flags field. */ enum { SHF_IA_64_SHORT = 0x10000000, /* section near gp */ SHF_IA_64_NORECOV = 0x20000000, /* spec insns w/o recovery */ }; /* Processor specific values for the Dyn d_tag field. */ enum { DT_IA_64_PLT_RESERVE = (DT_LOPROC + 0), DT_IA_64_NUM = 1, }; /* IA-64 relocations. */ enum { R_IA64_NONE = 0x00, /* none */ R_IA64_IMM14 = 0x21, /* symbol + addend, add imm14 */ R_IA64_IMM22 = 0x22, /* symbol + addend, add imm22 */ R_IA64_IMM64 = 0x23, /* symbol + addend, mov imm64 */ R_IA64_DIR32MSB = 0x24, /* symbol + addend, data4 MSB */ R_IA64_DIR32LSB = 0x25, /* symbol + addend, data4 LSB */ R_IA64_DIR64MSB = 0x26, /* symbol + addend, data8 MSB */ R_IA64_DIR64LSB = 0x27, /* symbol + addend, data8 LSB */ R_IA64_GPREL22 = 0x2a, /* @gprel(sym + add), add imm22 */ R_IA64_GPREL64I = 0x2b, /* @gprel(sym + add), mov imm64 */ R_IA64_GPREL32MSB = 0x2c, /* @gprel(sym + add), data4 MSB */ R_IA64_GPREL32LSB = 0x2d, /* @gprel(sym + add), data4 LSB */ R_IA64_GPREL64MSB = 0x2e, /* @gprel(sym + add), data8 MSB */ R_IA64_GPREL64LSB = 0x2f, /* @gprel(sym + add), data8 LSB */ R_IA64_LTOFF22 = 0x32, /* @ltoff(sym + add), add imm22 */ R_IA64_LTOFF64I = 0x33, /* @ltoff(sym + add), mov imm64 */ R_IA64_PLTOFF22 = 0x3a, /* @pltoff(sym + add), add imm22 */ R_IA64_PLTOFF64I = 0x3b, /* @pltoff(sym + add), mov imm64 */ R_IA64_PLTOFF64MSB = 0x3e, /* @pltoff(sym + add), data8 MSB */ R_IA64_PLTOFF64LSB = 0x3f, /* @pltoff(sym + add), data8 LSB */ R_IA64_FPTR64I = 0x43, /* @fptr(sym + add), mov imm64 */ R_IA64_FPTR32MSB = 0x44, /* @fptr(sym + add), data4 MSB */ R_IA64_FPTR32LSB = 0x45, /* @fptr(sym + add), data4 LSB */ R_IA64_FPTR64MSB = 0x46, /* @fptr(sym + add), data8 MSB */ R_IA64_FPTR64LSB = 0x47, /* @fptr(sym + add), data8 LSB */ R_IA64_PCREL60B = 0x48, /* @pcrel(sym + add), brl */ R_IA64_PCREL21B = 0x49, /* @pcrel(sym + add), ptb, call */ R_IA64_PCREL21M = 0x4a, /* @pcrel(sym + add), chk.s */ R_IA64_PCREL21F = 0x4b, /* @pcrel(sym + add), fchkf */ R_IA64_PCREL32MSB = 0x4c, /* @pcrel(sym + add), data4 MSB */ R_IA64_PCREL32LSB = 0x4d, /* @pcrel(sym + add), data4 LSB */ R_IA64_PCREL64MSB = 0x4e, /* @pcrel(sym + add), data8 MSB */ R_IA64_PCREL64LSB = 0x4f, /* @pcrel(sym + add), data8 LSB */ R_IA64_LTOFF_FPTR22 = 0x52, /* @ltoff(@fptr(s+a)), imm22 */ R_IA64_LTOFF_FPTR64I = 0x53, /* @ltoff(@fptr(s+a)), imm64 */ R_IA64_LTOFF_FPTR32MSB = 0x54, /* @ltoff(@fptr(s+a)), data4 MSB */ R_IA64_LTOFF_FPTR32LSB = 0x55, /* @ltoff(@fptr(s+a)), data4 LSB */ R_IA64_LTOFF_FPTR64MSB = 0x56, /* @ltoff(@fptr(s+a)), data8 MSB */ R_IA64_LTOFF_FPTR64LSB = 0x57, /* @ltoff(@fptr(s+a)), data8 LSB */ R_IA64_SEGREL32MSB = 0x5c, /* @segrel(sym + add), data4 MSB */ R_IA64_SEGREL32LSB = 0x5d, /* @segrel(sym + add), data4 LSB */ R_IA64_SEGREL64MSB = 0x5e, /* @segrel(sym + add), data8 MSB */ R_IA64_SEGREL64LSB = 0x5f, /* @segrel(sym + add), data8 LSB */ R_IA64_SECREL32MSB = 0x64, /* @secrel(sym + add), data4 MSB */ R_IA64_SECREL32LSB = 0x65, /* @secrel(sym + add), data4 LSB */ R_IA64_SECREL64MSB = 0x66, /* @secrel(sym + add), data8 MSB */ R_IA64_SECREL64LSB = 0x67, /* @secrel(sym + add), data8 LSB */ R_IA64_REL32MSB = 0x6c, /* data 4 + REL */ R_IA64_REL32LSB = 0x6d, /* data 4 + REL */ R_IA64_REL64MSB = 0x6e, /* data 8 + REL */ R_IA64_REL64LSB = 0x6f, /* data 8 + REL */ R_IA64_LTV32MSB = 0x74, /* symbol + addend, data4 MSB */ R_IA64_LTV32LSB = 0x75, /* symbol + addend, data4 LSB */ R_IA64_LTV64MSB = 0x76, /* symbol + addend, data8 MSB */ R_IA64_LTV64LSB = 0x77, /* symbol + addend, data8 LSB */ R_IA64_PCREL21BI = 0x79, /* @pcrel(sym + add), 21bit inst */ R_IA64_PCREL22 = 0x7a, /* @pcrel(sym + add), 22bit inst */ R_IA64_PCREL64I = 0x7b, /* @pcrel(sym + add), 64bit inst */ R_IA64_IPLTMSB = 0x80, /* dynamic reloc, imported PLT, MSB */ R_IA64_IPLTLSB = 0x81, /* dynamic reloc, imported PLT, LSB */ R_IA64_COPY = 0x84, /* copy relocation */ R_IA64_SUB = 0x85, /* Addend and symbol difference */ R_IA64_LTOFF22X = 0x86, /* LTOFF22, relaxable. */ R_IA64_LDXMOV = 0x87, /* Use of LTOFF22X. */ R_IA64_TPREL14 = 0x91, /* @tprel(sym + add), imm14 */ R_IA64_TPREL22 = 0x92, /* @tprel(sym + add), imm22 */ R_IA64_TPREL64I = 0x93, /* @tprel(sym + add), imm64 */ R_IA64_TPREL64MSB = 0x96, /* @tprel(sym + add), data8 MSB */ R_IA64_TPREL64LSB = 0x97, /* @tprel(sym + add), data8 LSB */ R_IA64_LTOFF_TPREL22 = 0x9a, /* @ltoff(@tprel(s+a)), imm2 */ R_IA64_DTPMOD64MSB = 0xa6, /* @dtpmod(sym + add), data8 MSB */ R_IA64_DTPMOD64LSB = 0xa7, /* @dtpmod(sym + add), data8 LSB */ R_IA64_LTOFF_DTPMOD22 = 0xaa, /* @ltoff(@dtpmod(sym + add)), imm22 */ R_IA64_DTPREL14 = 0xb1, /* @dtprel(sym + add), imm14 */ R_IA64_DTPREL22 = 0xb2, /* @dtprel(sym + add), imm22 */ R_IA64_DTPREL64I = 0xb3, /* @dtprel(sym + add), imm64 */ R_IA64_DTPREL32MSB = 0xb4, /* @dtprel(sym + add), data4 MSB */ R_IA64_DTPREL32LSB = 0xb5, /* @dtprel(sym + add), data4 LSB */ R_IA64_DTPREL64MSB = 0xb6, /* @dtprel(sym + add), data8 MSB */ R_IA64_DTPREL64LSB = 0xb7, /* @dtprel(sym + add), data8 LSB */ R_IA64_LTOFF_DTPREL22 = 0xba, /* @ltoff(@dtprel(s+a)), imm22 */ }; /* SH specific declarations */ /* Processor specific flags for the ELF header e_flags field. */ enum { EF_SH_MACH_MASK = 0x1f, EF_SH_UNKNOWN = 0x0, EF_SH1 = 0x1, EF_SH2 = 0x2, EF_SH3 = 0x3, EF_SH_DSP = 0x4, EF_SH3_DSP = 0x5, EF_SH4AL_DSP = 0x6, EF_SH3E = 0x8, EF_SH4 = 0x9, EF_SH2E = 0xb, EF_SH4A = 0xc, EF_SH2A = 0xd, EF_SH4_NOFPU = 0x10, EF_SH4A_NOFPU = 0x11, EF_SH4_NOMMU_NOFPU = 0x12, EF_SH2A_NOFPU = 0x13, EF_SH3_NOMMU = 0x14, EF_SH2A_SH4_NOFPU = 0x15, EF_SH2A_SH3_NOFPU = 0x16, EF_SH2A_SH4 = 0x17, EF_SH2A_SH3E = 0x18, }; /* SH relocs. */ enum { R_SH_NONE = 0, R_SH_DIR32 = 1, R_SH_REL32 = 2, R_SH_DIR8WPN = 3, R_SH_IND12W = 4, R_SH_DIR8WPL = 5, R_SH_DIR8WPZ = 6, R_SH_DIR8BP = 7, R_SH_DIR8W = 8, R_SH_DIR8L = 9, R_SH_SWITCH16 = 25, R_SH_SWITCH32 = 26, R_SH_USES = 27, R_SH_COUNT = 28, R_SH_ALIGN = 29, R_SH_CODE = 30, R_SH_DATA = 31, R_SH_LABEL = 32, R_SH_SWITCH8 = 33, R_SH_GNU_VTINHERIT = 34, R_SH_GNU_VTENTRY = 35, R_SH_TLS_GD_32 = 144, R_SH_TLS_LD_32 = 145, R_SH_TLS_LDO_32 = 146, R_SH_TLS_IE_32 = 147, R_SH_TLS_LE_32 = 148, R_SH_TLS_DTPMOD32 = 149, R_SH_TLS_DTPOFF32 = 150, R_SH_TLS_TPOFF32 = 151, R_SH_GOT32 = 160, R_SH_PLT32 = 161, R_SH_COPY = 162, R_SH_GLOB_DAT = 163, R_SH_JMP_SLOT = 164, R_SH_RELATIVE = 165, R_SH_GOTOFF = 166, R_SH_GOTPC = 167, /* Keep this the last entry. */ R_SH_NUM = 256, }; /* S/390 specific definitions. */ /* Valid values for the e_flags field. */ enum { EF_S390_HIGH_GPRS = 0x00000001, /* High GPRs kernel facility needed. */ }; /* Additional s390 relocs */ enum { R_390_NONE = 0, /* No reloc. */ R_390_8 = 1, /* Direct 8 bit. */ R_390_12 = 2, /* Direct 12 bit. */ R_390_16 = 3, /* Direct 16 bit. */ R_390_32 = 4, /* Direct 32 bit. */ R_390_PC32 = 5, /* PC relative 32 bit. */ R_390_GOT12 = 6, /* 12 bit GOT offset. */ R_390_GOT32 = 7, /* 32 bit GOT offset. */ R_390_PLT32 = 8, /* 32 bit PC relative PLT address. */ R_390_COPY = 9, /* Copy symbol at runtime. */ R_390_GLOB_DAT = 10, /* Create GOT entry. */ R_390_JMP_SLOT = 11, /* Create PLT entry. */ R_390_RELATIVE = 12, /* Adjust by program base. */ R_390_GOTOFF32 = 13, /* 32 bit offset to GOT. */ R_390_GOTPC = 14, /* 32 bit PC relative offset to GOT. */ R_390_GOT16 = 15, /* 16 bit GOT offset. */ R_390_PC16 = 16, /* PC relative 16 bit. */ R_390_PC16DBL = 17, /* PC relative 16 bit shifted by 1. */ R_390_PLT16DBL = 18, /* 16 bit PC rel. PLT shifted by 1. */ R_390_PC32DBL = 19, /* PC relative 32 bit shifted by 1. */ R_390_PLT32DBL = 20, /* 32 bit PC rel. PLT shifted by 1. */ R_390_GOTPCDBL = 21, /* 32 bit PC rel. GOT shifted by 1. */ R_390_64 = 22, /* Direct 64 bit. */ R_390_PC64 = 23, /* PC relative 64 bit. */ R_390_GOT64 = 24, /* 64 bit GOT offset. */ R_390_PLT64 = 25, /* 64 bit PC relative PLT address. */ R_390_GOTENT = 26, /* 32 bit PC rel. to GOT entry >> 1. */ R_390_GOTOFF16 = 27, /* 16 bit offset to GOT. */ R_390_GOTOFF64 = 28, /* 64 bit offset to GOT. */ R_390_GOTPLT12 = 29, /* 12 bit offset to jump slot. */ R_390_GOTPLT16 = 30, /* 16 bit offset to jump slot. */ R_390_GOTPLT32 = 31, /* 32 bit offset to jump slot. */ R_390_GOTPLT64 = 32, /* 64 bit offset to jump slot. */ R_390_GOTPLTENT = 33, /* 32 bit rel. offset to jump slot. */ R_390_PLTOFF16 = 34, /* 16 bit offset from GOT to PLT. */ R_390_PLTOFF32 = 35, /* 32 bit offset from GOT to PLT. */ R_390_PLTOFF64 = 36, /* 16 bit offset from GOT to PLT. */ R_390_TLS_LOAD = 37, /* Tag for load insn in TLS code. */ R_390_TLS_GDCALL = 38, /* Tag for function call in general dynamic TLS code. */ R_390_TLS_LDCALL = 39, /* Tag for function call in local dynamic TLS code. */ R_390_TLS_GD32 = 40, /* Direct 32 bit for general dynamic thread local data. */ R_390_TLS_GD64 = 41, /* Direct 64 bit for general dynamic thread local data. */ R_390_TLS_GOTIE12 = 42, /* 12 bit GOT offset for static TLS block offset. */ R_390_TLS_GOTIE32 = 43, /* 32 bit GOT offset for static TLS block offset. */ R_390_TLS_GOTIE64 = 44, /* 64 bit GOT offset for static TLS block offset. */ R_390_TLS_LDM32 = 45, /* Direct 32 bit for local dynamic thread local data in LE code. */ R_390_TLS_LDM64 = 46, /* Direct 64 bit for local dynamic thread local data in LE code. */ R_390_TLS_IE32 = 47, /* 32 bit address of GOT entry for negated static TLS block offset. */ R_390_TLS_IE64 = 48, /* 64 bit address of GOT entry for negated static TLS block offset. */ R_390_TLS_IEENT = 49, /* 32 bit rel. offset to GOT entry for negated static TLS block offset. */ R_390_TLS_LE32 = 50, /* 32 bit negated offset relative to static TLS block. */ R_390_TLS_LE64 = 51, /* 64 bit negated offset relative to static TLS block. */ R_390_TLS_LDO32 = 52, /* 32 bit offset relative to TLS block. */ R_390_TLS_LDO64 = 53, /* 64 bit offset relative to TLS block. */ R_390_TLS_DTPMOD = 54, /* ID of module containing symbol. */ R_390_TLS_DTPOFF = 55, /* Offset in TLS block. */ R_390_TLS_TPOFF = 56, /* Negated offset in static TLS block. */ R_390_20 = 57, /* Direct 20 bit. */ R_390_GOT20 = 58, /* 20 bit GOT offset. */ R_390_GOTPLT20 = 59, /* 20 bit offset to jump slot. */ R_390_TLS_GOTIE20 = 60, /* 20 bit GOT offset for static TLS block offset. */ /* Keep this the last entry. */ R_390_NUM = 61, }; /* CRIS relocations. */ enum { R_CRIS_NONE = 0, R_CRIS_8 = 1, R_CRIS_16 = 2, R_CRIS_32 = 3, R_CRIS_8_PCREL = 4, R_CRIS_16_PCREL = 5, R_CRIS_32_PCREL = 6, R_CRIS_GNU_VTINHERIT = 7, R_CRIS_GNU_VTENTRY = 8, R_CRIS_COPY = 9, R_CRIS_GLOB_DAT = 10, R_CRIS_JUMP_SLOT = 11, R_CRIS_RELATIVE = 12, R_CRIS_16_GOT = 13, R_CRIS_32_GOT = 14, R_CRIS_16_GOTPLT = 15, R_CRIS_32_GOTPLT = 16, R_CRIS_32_GOTREL = 17, R_CRIS_32_PLT_GOTREL = 18, R_CRIS_32_PLT_PCREL = 19, R_CRIS_NUM = 20, }; /* AMD x86-64 relocations. */ enum { R_X86_64_NONE = 0, /* No reloc */ R_X86_64_64 = 1, /* Direct 64 bit */ R_X86_64_PC32 = 2, /* PC relative 32 bit signed */ R_X86_64_GOT32 = 3, /* 32 bit GOT entry */ R_X86_64_PLT32 = 4, /* 32 bit PLT address */ R_X86_64_COPY = 5, /* Copy symbol at runtime */ R_X86_64_GLOB_DAT = 6, /* Create GOT entry */ R_X86_64_JUMP_SLOT = 7, /* Create PLT entry */ R_X86_64_RELATIVE = 8, /* Adjust by program base */ R_X86_64_GOTPCREL = 9, /* 32 bit signed PC relative offset to GOT */ R_X86_64_32 = 10, /* Direct 32 bit zero extended */ R_X86_64_32S = 11, /* Direct 32 bit sign extended */ R_X86_64_16 = 12, /* Direct 16 bit zero extended */ R_X86_64_PC16 = 13, /* 16 bit sign extended pc relative */ R_X86_64_8 = 14, /* Direct 8 bit sign extended */ R_X86_64_PC8 = 15, /* 8 bit sign extended pc relative */ R_X86_64_DTPMOD64 = 16, /* ID of module containing symbol */ R_X86_64_DTPOFF64 = 17, /* Offset in module's TLS block */ R_X86_64_TPOFF64 = 18, /* Offset in initial TLS block */ R_X86_64_TLSGD = 19, /* 32 bit signed PC relative offset to two GOT entries for GD symbol */ R_X86_64_TLSLD = 20, /* 32 bit signed PC relative offset to two GOT entries for LD symbol */ R_X86_64_DTPOFF32 = 21, /* Offset in TLS block */ R_X86_64_GOTTPOFF = 22, /* 32 bit signed PC relative offset GOT entry for IE symbol */ R_X86_64_TPOFF32 = 23, /* Offset in initial TLS block */ R_X86_64_PC64 = 24, /* PC relative 64 bit */ R_X86_64_GOTOFF64 = 25, /* 64 bit offset to GOT */ R_X86_64_GOTPC32 = 26, /* 32 bit signed pc relative offset to GOT */ R_X86_64_GOT64 = 27, /* 64-bit GOT entry offset */ R_X86_64_GOTPCREL64 = 28, /* 64-bit PC relative offset to GOT entry */ R_X86_64_GOTPC64 = 29, /* 64-bit PC relative offset to GOT */ R_X86_64_GOTPLT64 = 30, /* like GOT64, says PLT entry needed */ R_X86_64_PLTOFF64 = 31, /* 64-bit GOT relative offset to PLT entry */ R_X86_64_SIZE32 = 32, /* Size of symbol plus 32-bit addend */ R_X86_64_SIZE64 = 33, /* Size of symbol plus 64-bit addend */ R_X86_64_GOTPC32_TLSDESC = 34, /* GOT offset for TLS descriptor. */ R_X86_64_TLSDESC_CALL = 35, /* Marker for call through TLS descriptor. */ R_X86_64_TLSDESC = 36, /* TLS descriptor. */ R_X86_64_IRELATIVE = 37, /* Adjust indirectly by program base */ R_X86_64_NUM = 38, }; /* AM33 relocations. */ enum { R_MN10300_NONE = 0, /* No reloc. */ R_MN10300_32 = 1, /* Direct 32 bit. */ R_MN10300_16 = 2, /* Direct 16 bit. */ R_MN10300_8 = 3, /* Direct 8 bit. */ R_MN10300_PCREL32 = 4, /* PC-relative 32-bit. */ R_MN10300_PCREL16 = 5, /* PC-relative 16-bit signed. */ R_MN10300_PCREL8 = 6, /* PC-relative 8-bit signed. */ R_MN10300_GNU_VTINHERIT = 7, /* Ancient C++ vtable garbage... */ R_MN10300_GNU_VTENTRY = 8, /* ... collection annotation. */ R_MN10300_24 = 9, /* Direct 24 bit. */ R_MN10300_GOTPC32 = 10, /* 32-bit PCrel offset to GOT. */ R_MN10300_GOTPC16 = 11, /* 16-bit PCrel offset to GOT. */ R_MN10300_GOTOFF32 = 12, /* 32-bit offset from GOT. */ R_MN10300_GOTOFF24 = 13, /* 24-bit offset from GOT. */ R_MN10300_GOTOFF16 = 14, /* 16-bit offset from GOT. */ R_MN10300_PLT32 = 15, /* 32-bit PCrel to PLT entry. */ R_MN10300_PLT16 = 16, /* 16-bit PCrel to PLT entry. */ R_MN10300_GOT32 = 17, /* 32-bit offset to GOT entry. */ R_MN10300_GOT24 = 18, /* 24-bit offset to GOT entry. */ R_MN10300_GOT16 = 19, /* 16-bit offset to GOT entry. */ R_MN10300_COPY = 20, /* Copy symbol at runtime. */ R_MN10300_GLOB_DAT = 21, /* Create GOT entry. */ R_MN10300_JMP_SLOT = 22, /* Create PLT entry. */ R_MN10300_RELATIVE = 23, /* Adjust by program base. */ R_MN10300_NUM = 24, }; /* M32R relocs. */ enum { R_M32R_NONE = 0, /* No reloc. */ R_M32R_16 = 1, /* Direct 16 bit. */ R_M32R_32 = 2, /* Direct 32 bit. */ R_M32R_24 = 3, /* Direct 24 bit. */ R_M32R_10_PCREL = 4, /* PC relative 10 bit shifted. */ R_M32R_18_PCREL = 5, /* PC relative 18 bit shifted. */ R_M32R_26_PCREL = 6, /* PC relative 26 bit shifted. */ R_M32R_HI16_ULO = 7, /* High 16 bit with unsigned low. */ R_M32R_HI16_SLO = 8, /* High 16 bit with signed low. */ R_M32R_LO16 = 9, /* Low 16 bit. */ R_M32R_SDA16 = 10, /* 16 bit offset in SDA. */ R_M32R_GNU_VTINHERIT = 11, R_M32R_GNU_VTENTRY = 12, }; /* M32R relocs use SHT_RELA. */ enum { R_M32R_16_RELA = 33, /* Direct 16 bit. */ R_M32R_32_RELA = 34, /* Direct 32 bit. */ R_M32R_24_RELA = 35, /* Direct 24 bit. */ R_M32R_10_PCREL_RELA = 36, /* PC relative 10 bit shifted. */ R_M32R_18_PCREL_RELA = 37, /* PC relative 18 bit shifted. */ R_M32R_26_PCREL_RELA = 38, /* PC relative 26 bit shifted. */ R_M32R_HI16_ULO_RELA = 39, /* High 16 bit with unsigned low */ R_M32R_HI16_SLO_RELA = 40, /* High 16 bit with signed low */ R_M32R_LO16_RELA = 41, /* Low 16 bit */ R_M32R_SDA16_RELA = 42, /* 16 bit offset in SDA */ R_M32R_RELA_GNU_VTINHERIT = 43, R_M32R_RELA_GNU_VTENTRY = 44, R_M32R_REL32 = 45, /* PC relative 32 bit. */ R_M32R_GOT24 = 48, /* 24 bit GOT entry */ R_M32R_26_PLTREL = 49, /* 26 bit PC relative to PLT shifted */ R_M32R_COPY = 50, /* Copy symbol at runtime */ R_M32R_GLOB_DAT = 51, /* Create GOT entry */ R_M32R_JMP_SLOT = 52, /* Create PLT entry */ R_M32R_RELATIVE = 53, /* Adjust by program base */ R_M32R_GOTOFF = 54, /* 24 bit offset to GOT */ R_M32R_GOTPC24 = 55, /* 24 bit PC relative offset to GOT */ R_M32R_GOT16_HI_ULO = 56, /* High 16 bit GOT entry with unsigned low */ R_M32R_GOT16_HI_SLO = 57, /* High 16 bit GOT entry with signed low */ R_M32R_GOT16_LO = 58, /* Low 16 bit GOT entry */ R_M32R_GOTPC_HI_ULO = 59, /* High 16 bit PC relative offset to GOT with unsigned low */ R_M32R_GOTPC_HI_SLO = 60, /* High 16 bit PC relative offset to GOT with signed low */ R_M32R_GOTPC_LO = 61, /* Low 16 bit PC relative offset to GOT */ R_M32R_GOTOFF_HI_ULO = 62, /* High 16 bit offset to GOT with unsigned low */ R_M32R_GOTOFF_HI_SLO = 63, /* High 16 bit offset to GOT with signed low */ R_M32R_GOTOFF_LO = 64, /* Low 16 bit offset to GOT */ R_M32R_NUM = 256, /* Keep this the last entry. */ }; #endif edb-debugger/.editorconfig0000644000175000017500000000057613765535463015416 0ustar eteraneteran# EditorConfig is awesome: http://EditorConfig.org # top-most EditorConfig file root = true # Unix-style newlines with a newline ending every file [*] end_of_line = lf insert_final_newline = true # Matches multiple files with brace expansion notation # Set default charset [*.{c,h,cpp,hpp,tcc}] charset = utf-8 indent_style = tab indent_size = 4 trim_trailing_whitespace = true edb-debugger/CMakeLists.txt0000644000175000017500000001212413765535463015471 0ustar eteraneterancmake_minimum_required (VERSION 3.1) project (edb CXX) enable_testing() if(NOT EXISTS "${PROJECT_SOURCE_DIR}/src/qhexview/.git") message(SEND_ERROR "The git submodules are not available. Please run:\ngit submodule update --init --recursive") endif() set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/Modules/") include("GNUInstallDirs") include("CheckIncludeFileCXX") include("DetectCompiler") include("DetectOS") include("DetectArchitecture") include("EnableSanitizers") include("EnableSTLDebug") include("ProjectDefaults") if(TARGET_COMPILER_GCC AND ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 5.0) message(FATAL_ERROR "Your g++ version is too old. At least 5.0 is required.") endif() find_package(Boost 1.35 REQUIRED) include_directories(${Boost_INCLUDE_DIR}) find_package(Capstone REQUIRED) include_directories(${CAPSTONE_INCLUDE_DIRS}) link_directories(${CAPSTONE_LIBRARY_DIRS}) # eventually use this to generate version.h? set(TARGET_VERSION_MAJOR 1) set(TARGET_VERSION_MINOR 3) set(TARGET_VERSION_PATCH 0) set_property(GLOBAL PROPERTY VERSION ${TARGET_VERSION_MAJOR}.${TARGET_VERSION_MINOR}.${TARGET_VERSION_PATCH}) if (UNIX) find_package(PkgConfig REQUIRED) pkg_check_modules(GRAPHVIZ libgvc>=2.38.0) if(GRAPHVIZ_FOUND) include_directories(${GRAPHVIZ_INCLUDE_DIRS}) link_directories(${GRAPHVIZ_LIBRARY_DIRS}) add_definitions(-DENABLE_GRAPH) endif() endif() message(STATUS "Checking for module 'double-conversion'") # Using check_include_file_cxx instead of find_package etc. to support various versions of double-conversion. Some versions don't have CMake modules, some have a bit incompatible ones... check_include_file_cxx("double-conversion/double-conversion.h" HAVE_DOUBLE_CONVERSION) if(NOT HAVE_DOUBLE_CONVERSION) UNSET(HAVE_DOUBLE_CONVERSION CACHE) message(WARNING "libdouble-conversion header wasn't found. 32- and 64-bit floating-point values will be shown with max_digits10 digits of precision instead of shortest representation.") else() find_library(DOUBLE_CONVERSION_LIBRARIES double-conversion) if(NOT DOUBLE_CONVERSION_LIBRARIES) message(WARNING "libdouble-conversion library wasn't found. 32- and 64-bit floating-point values will be shown with max_digits10 digits of precision instead of shortest representation.") else() add_definitions("-DHAVE_DOUBLE_CONVERSION") endif() endif() find_package(Qt5Core) include_directories("include") if(TARGET_PLATFORM_LINUX) include_directories("include/os/unix") include_directories("include/os/unix/linux") elseif(TARGET_PLATFORM_WINDOWS) include_directories("include/os/win32") endif() if(TARGET_ARCH_X86 OR TARGET_ARCH_ARM32) add_definitions(-DEDB_IS_32_BIT=true -DEDB_IS_64_BIT=false) elseif(TARGET_ARCH_X64 OR TARGET_ARCH_ARM64) add_definitions(-DEDB_IS_32_BIT=false -DEDB_IS_64_BIT=true) else() message(SEND_ERROR "Unexpected bitness: \"sizeof(void*)=${CMAKE_SIZEOF_VOID_P}.\"") endif() if(TARGET_ARCH_X86) add_definitions(-DEDB_X86) include_directories("include/arch/x86-generic") elseif(TARGET_ARCH_X64) add_definitions(-DEDB_X86_64) include_directories("include/arch/x86-generic") elseif(TARGET_ARCH_ARM32) add_definitions(-DEDB_ARM32) include_directories("include/arch/arm-generic") elseif(TARGET_ARCH_ARM64) add_definitions(-DEDB_ARM64) include_directories("include/arch/arm-generic") endif() # FIXME: This is also useful on Windows, so it should be made usable there too. if(UNIX) if(TARGET_ARCH_FAMILY_X86) pkg_check_modules(GDTOA gdtoa-desktop) if(NOT GDTOA_FOUND) message(WARNING "gdtoa-desktop package wasn't found. 80-bit floating-point values will be shown with max_digits10 digits of precision instead of shortest representation.") else() add_definitions("-DHAVE_GDTOA") include_directories(${GDTOA_INCLUDE_DIRS}) link_directories(${GDTOA_LIBRARY_DIRS}) endif() endif() endif() if (TARGET_COMPILER_CLANG OR TARGET_COMPILER_GCC) add_compile_options( -W -Wall #-Wshadow -Wnon-virtual-dtor #-Wold-style-cast -Wcast-align -Wunused -Woverloaded-virtual -pedantic #-Wconversion #-Wsign-conversion #-Wnull-dereference -Wdouble-promotion -Wformat=2 -Wno-unused-macros -Wno-switch-enum -Wno-unknown-pragmas ) if(TARGET_COMPILER_CLANG) add_compile_options( #-Wshorten-64-to-32 -Wconditional-uninitialized #-Wshadow-uncaptured-local -Wmissing-prototypes #-Wexit-time-destructors #-Wglobal-constructors -Wimplicit-fallthrough ) elseif(TARGET_COMPILER_GCC) add_compile_options( #-Wuseless-cast #-Wduplicated-cond #-Wduplicated-branches -Wlogical-op #-Wsuggest-attribute=pure #-Wsuggest-attribute=const #-Wsuggest-attribute=noreturn #-Wsuggest-final-types #-Wsuggest-final-methods -Wsuggest-override ) endif() endif() add_subdirectory(src) add_subdirectory(plugins) add_subdirectory(libELF) add_subdirectory(libPE) install (FILES ${CMAKE_SOURCE_DIR}/edb.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) install (FILES ${CMAKE_SOURCE_DIR}/edb.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications/) install (FILES ${CMAKE_SOURCE_DIR}/src/images/edb.png DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pixmaps/) edb-debugger/edb.desktop0000644000175000017500000000030313765535463015052 0ustar eteraneteran[Desktop Entry] Name=edb GenericName="Evan's Debugger" Comment="edb is a cross platform x86/x86-64 debugger" Exec=edb Icon=edb Terminal=false Type=Application Categories=Development;Qt;Debugger; edb-debugger/cmake/0000755000175000017500000000000013765535463014011 5ustar eteraneteranedb-debugger/cmake/Modules/0000755000175000017500000000000013765535463015421 5ustar eteraneteranedb-debugger/cmake/Modules/DetectArchitecture.cmake0000644000175000017500000000135513765535463022202 0ustar eteraneteran if((CMAKE_SYSTEM_PROCESSOR MATCHES "i[3456]86") OR (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") OR (CMAKE_SYSTEM_PROCESSOR MATCHES "[aA][mM][dD]64")) set(TARGET_ARCH_FAMILY_X86 1) if (CMAKE_SIZEOF_VOID_P EQUAL 8) set(TARGET_ARCH_X64 1) set(TARGET_ARCH_NAME "x86-64") elseif (CMAKE_SIZEOF_VOID_P EQUAL 4) set(TARGET_ARCH_X86 1) set(TARGET_ARCH_NAME "x86") endif() elseif((CMAKE_SYSTEM_PROCESSOR MATCHES "armv[0-9]+")) set(TARGET_ARCH_FAMILY_ARM 1) if (CMAKE_SIZEOF_VOID_P EQUAL 8) set(TARGET_ARCH_ARM64 1) set(TARGET_ARCH_NAME "arm64") elseif (CMAKE_SIZEOF_VOID_P EQUAL 4) set(TARGET_ARCH_ARM32 1) set(TARGET_ARCH_NAME "arm32") endif() else() message(FATAL_ERROR "Unsupported Architecture: ${CMAKE_SYSTEM_PROCESSOR}") endif() edb-debugger/cmake/Modules/EnableSTLDebug.cmake0000644000175000017500000000046013765535463021143 0ustar eteraneteran include("DetectCompiler") if((TARGET_COMPILER_GCC) OR (TARGET_COMPILER_CLANG)) option(ENABLE_STL_DEBUG "Enable STL container debugging") if(ENABLE_STL_DEBUG) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_GLIBCXX_DEBUG") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GLIBCXX_DEBUG") endif() endif() edb-debugger/cmake/Modules/EnableSanitizers.cmake0000644000175000017500000000216113765535463021665 0ustar eteraneteran include("DetectCompiler") function(JOIN VALUES GLUE OUTPUT) string (REPLACE ";" "${GLUE}" _TMP_STR "${VALUES}") set (${OUTPUT} "${_TMP_STR}" PARENT_SCOPE) endfunction() if((TARGET_COMPILER_GCC) OR (TARGET_COMPILER_CLANG)) option(ENABLE_ASAN "Enable address sanitizer") option(ENABLE_MSAN "Enable memory sanitizer") option(ENABLE_USAN "Enable undefined sanitizer") option(ENABLE_TSAN "Enable thread sanitizer") set(SANITIZERS "") if(ENABLE_ASAN) list(APPEND SANITIZERS "address") endif() if(ENABLE_MSAN) list(APPEND SANITIZERS "memory") endif() if(ENABLE_USAN) list(APPEND SANITIZERS "undefined") endif() if(ENABLE_TSAN) list(APPEND SANITIZERS "thread") endif() JOIN("${SANITIZERS}" "," LIST_OF_SANITIZERS) endif() if(LIST_OF_SANITIZERS) if(NOT "${LIST_OF_SANITIZERS}" STREQUAL "") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=${LIST_OF_SANITIZERS}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=${LIST_OF_SANITIZERS}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=${LIST_OF_SANITIZERS}") endif() endif() edb-debugger/cmake/Modules/ProjectDefaults.cmake0000644000175000017500000000073513765535463021526 0ustar eteraneteran include("DetectCompiler") if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Choose the type of build." FORCE) set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "RelWithDebInfo" "MinSizeRel") endif() # Generate compile_commands.json to make it easier to work with clang based tools set(CMAKE_EXPORT_COMPILE_COMMANDS ON) if(TARGET_COMPILER_MSVC) add_definitions(-DUNICODE -D_UNICODE -D_CRT_SECURE_NO_WARNINGS) endif() edb-debugger/cmake/Modules/DetectCompiler.cmake0000644000175000017500000000046413765535463021332 0ustar eteraneteran if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") set(TARGET_COMPILER_MSVC 1) elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(TARGET_COMPILER_CLANG 1) elseif (CMAKE_CXX_COMPILER_ID MATCHES "GNU") set(TARGET_COMPILER_GCC 1) else() message(FATAL_ERROR "Unsupported Compiler: ${CMAKE_CXX_COMPILER_ID}") endif() edb-debugger/cmake/Modules/FindCapstone.cmake0000644000175000017500000000174613765535463021010 0ustar eteraneteranif (UNIX) find_package(PkgConfig REQUIRED) pkg_check_modules(CAPSTONE REQUIRED capstone>=3.0.4) elseif (WIN32) # CAPSTONE_SDK should be the path to a directory containing a subdirectory "include/capstone" # with all the include headers of capstone and another subdirectory "lib" with "capstone_dll.lib" or "libcapstone.dll". if (NOT DEFINED CAPSTONE_SDK) set(CAPSTONE_SDK NOTFOUND CACHE PATH "Path to the Capstone SDK") message(SEND_ERROR "Path to Capstone SDK is missing. Please specify CAPSTONE_SDK.") endif() find_path(CAPSTONE_INCLUDE_DIRS capstone/capstone.h PATHS ${CAPSTONE_SDK}/include ) if (NOT CAPSTONE_INCLUDE_DIRS) message(SEND_ERROR "Include directory for Capstone not found. Please specify CAPSTONE_SDK.") endif() find_library(CAPSTONE_LIBRARIES NAMES capstone_dll libcapstone HINTS "${CAPSTONE_SDK}/lib" ) if (NOT CAPSTONE_LIBRARIES) message(SEND_ERROR "Library directory for Capstone not found. Please specify CAPSTONE_SDK.") endif() endif() edb-debugger/cmake/Modules/DetectOS.cmake0000644000175000017500000000057113765535463020100 0ustar eteraneteran if (CMAKE_SYSTEM_NAME MATCHES "Windows") set(TARGET_PLATFORM_WINDOWS 1) elseif (CMAKE_SYSTEM_NAME MATCHES "Linux") set(TARGET_PLATFORM_LINUX 1) elseif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") set(TARGET_PLATFORM_FREEBSD 1) elseif (CMAKE_SYSTEM_NAME MATCHES "Darwin") set(TARGET_PLATFORM_MACOS 1) else() message(FATAL_ERROR "Unsupported OS: ${CMAKE_SYSTEM_NAME}") endif () edb-debugger/cmake/Modules/DetectGitBranch.cmake0000644000175000017500000000052513765535463021417 0ustar eteraneteran function(git_get_branch RESULT) if(EXISTS "${CMAKE_SOURCE_DIR}/.git") execute_process( COMMAND git rev-parse HEAD WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE OUTPUT OUTPUT_STRIP_TRAILING_WHITESPACE ) set(${RESULT} ${OUTPUT} PARENT_SCOPE) else() set(${RESULT} "Unknown" PARENT_SCOPE) endif() endfunction() edb-debugger/pkg/0000755000175000017500000000000013765535463013512 5ustar eteraneteranedb-debugger/pkg/deb/0000755000175000017500000000000013765535463014244 5ustar eteraneteranedb-debugger/pkg/deb/create-deb-qt5.sh0000755000175000017500000000340013765535463017302 0ustar eteraneteran#!/bin/bash SOURCE_DIR=$PWD/../../ TEMP_DIR=$(mktemp -d) BUILD_DIR=$TEMP_DIR/build DEB_DIR=$BUILD_DIR/deb trap "rm -rf $TEMP_DIR" EXIT # Build edb mkdir -p $BUILD_DIR pushd $BUILD_DIR cmake $SOURCE_DIR if ! make -j8; then echo "Compiling error. Exiting..." exit 1 fi # Install it into a temp directory make DESTDIR=$DEB_DIR install # Getting Arch if [ $(uname -m) == "x86_64" ]; then ARCH="amd64" else ARCH="i386" fi INSTALL_SIZE=$(du -b -s $DEB_DIR | cut -f1) VERSION=$($BUILD_DIR/edb --dump-version 2> /dev/null) #TODO(eteran): figure out the proper deps for Qt5 on Ubuntu/Debian DEPENDS="libqt5core5a (>= 5.0.0), libqt5gui5 (>= 5.0.0), libcapstone3" # Create the meta-data dir mkdir -p $DEB_DIR/DEBIAN # MD5s # TODO(eteran): do we need to get rid of the prefix on the files here? find $DEB_DIR -type f | xargs md5sum > $DEB_DIR/DEBIAN/md5sums # Generating Debian control file echo "Package: edb-debugger-qt5 Version: $VERSION Architecture: $ARCH Maintainer: Evan Teran Homepage: http://www.codef00.com Installed-Size: $INSTALL_SIZE Depends: $DEPENDS Provides: edb Section: devel Priority: extra Description: Graphical debugger and disassembler for ELF binaries EDB (Evan's Debugger) is a modular and modern disassembler and debugger for binary ELF files based on ptrace API and the capstone disassembly library. EDB is very similar to OllyDbg, a famous freeware debugger for PE (Portable Executable) files. The intent of EDB is to debug binaries without source code. It's possible to set conditional and inconditional breakpoints, display memory stack, processor registers state and more. The power of EDB can be increased with many plugins." > $DEB_DIR/DEBIAN/control # Generate package dpkg-deb -b $DEB_DIR $SOURCE_DIR/edb-debugger-qt5_${VERSION}_${ARCH}.deb edb-debugger/plugins/0000755000175000017500000000000013765535463014412 5ustar eteraneteranedb-debugger/plugins/ProcessProperties/0000755000175000017500000000000013765535463020105 5ustar eteraneteranedb-debugger/plugins/ProcessProperties/DialogResults.cpp0000644000175000017500000000261013765535463023371 0ustar eteraneteran #include "DialogResults.h" #include "edb.h" #include namespace ProcessPropertiesPlugin { /** * @brief DialogResults::DialogResults * @param parent * @param f */ DialogResults::DialogResults(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f) { ui.setupUi(this); ui.tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); model_ = new ResultsModel(this); filterModel_ = new QSortFilterProxyModel(this); filterModel_->setFilterKeyColumn(2); filterModel_->setSourceModel(model_); ui.tableView->setModel(filterModel_); connect(ui.textFilter, &QLineEdit::textChanged, filterModel_, &QSortFilterProxyModel::setFilterFixedString); } /** * @brief DialogResults::addResult * @param result */ void DialogResults::addResult(const ResultsModel::Result &result) { model_->addResult(result); } /** * @brief DialogResults::on_tableView_doubleClicked * @param index */ void DialogResults::on_tableView_doubleClicked(const QModelIndex &index) { if (index.isValid()) { const QModelIndex realIndex = filterModel_->mapToSource(index); if (realIndex.isValid()) { if (auto item = static_cast(realIndex.internalPointer())) { edb::v1::dump_data(item->address, false); } } } } /** * @brief DialogResults::resultCount * @return */ int DialogResults::resultCount() const { return model_->rowCount(); } } edb-debugger/plugins/ProcessProperties/DialogProcessProperties.h0000644000175000017500000000354713765535463025102 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DIALOG_PROCESS_PROPERTIES_H_20120817_ #define DIALOG_PROCESS_PROPERTIES_H_20120817_ #include "ThreadsModel.h" #include "ui_DialogProcessProperties.h" #include #include namespace ProcessPropertiesPlugin { class DialogProcessProperties : public QDialog { Q_OBJECT public: explicit DialogProcessProperties(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~DialogProcessProperties() override = default; public Q_SLOTS: void on_btnParent_clicked(); void on_btnImage_clicked(); void on_btnRefreshEnvironment_clicked(); void on_btnRefreshHandles_clicked(); void on_btnStrings_clicked(); void on_btnRefreshThreads_clicked(); void on_btnRefreshMemory_clicked(); void on_txtSearchEnvironment_textChanged(const QString &text); private: void updateGeneralPage(); void updateMemoryPage(); void updateModulePage(); void updateHandles(); void updateThreads(); void updateEnvironmentPage(const QString &filter); private: void showEvent(QShowEvent *event) override; private: Ui::DialogProcessProperties ui; ThreadsModel *threadsModel_ = nullptr; QSortFilterProxyModel *threadsFilter_ = nullptr; }; } #endif edb-debugger/plugins/ProcessProperties/ProcessProperties.cpp0000644000175000017500000000331313765535463024304 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "ProcessProperties.h" #include "DialogProcessProperties.h" #include "edb.h" #include namespace ProcessPropertiesPlugin { /** * @brief ProcessProperties::ProcessProperties * @param parent */ ProcessProperties::ProcessProperties(QObject *parent) : QObject(parent) { dialog_ = new DialogProcessProperties(edb::v1::debugger_ui); } /** * @brief ProcessProperties::~ProcessProperties */ ProcessProperties::~ProcessProperties() { #if 0 delete dialog_; #endif } /** * @brief ProcessProperties::menu * @param parent * @return */ QMenu *ProcessProperties::menu(QWidget *parent) { Q_ASSERT(parent); if (!menu_) { menu_ = new QMenu(tr("Process Properties"), parent); menu_->addAction(tr("&Process Properties"), this, SLOT(showMenu()), QKeySequence(tr("Ctrl+P"))); menu_->addAction(tr("Process &Strings"), dialog_, SLOT(on_btnStrings_clicked()), QKeySequence(tr("Ctrl+S"))); } return menu_; } /** * @brief ProcessProperties::showMenu */ void ProcessProperties::showMenu() { dialog_->show(); } } edb-debugger/plugins/ProcessProperties/ResultsModel.cpp0000644000175000017500000001045513765535463023240 0ustar eteraneteran/* Copyright (C) 2006 - 2019 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "ResultsModel.h" #include "edb.h" #include namespace ProcessPropertiesPlugin { /** * @brief ResultsModel::ResultsModel * @param parent */ ResultsModel::ResultsModel(QObject *parent) : QAbstractItemModel(parent) { } /** * @brief ResultsModel::headerData * @param section * @param orientation * @param role * @return */ QVariant ResultsModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { switch (section) { case 0: return tr("Address"); case 1: return tr("Type"); case 2: return tr("String"); } } return QVariant(); } /** * @brief ResultsModel::data * @param index * @param role * @return */ QVariant ResultsModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } const Result &result = results_[index.row()]; if (role == Qt::DisplayRole) { switch (index.column()) { case 0: return edb::v1::format_pointer(result.address); case 1: switch (result.type) { case Result::Ascii: return tr("ASCII"); case Result::Utf8: return tr("UTF8"); case Result::Utf16: return tr("UTF16"); case Result::Utf32: return tr("UTF32"); } break; case 2: return result.string; default: return QVariant(); } } return QVariant(); } /** * @brief ResultsModel::addResult * @param r */ void ResultsModel::addResult(const Result &r) { beginInsertRows(QModelIndex(), rowCount(), rowCount()); results_.push_back(r); endInsertRows(); } /** * @brief ResultsModel::index * @param row * @param column * @param parent * @return */ QModelIndex ResultsModel::index(int row, int column, const QModelIndex &parent) const { Q_UNUSED(parent) if (row >= results_.size()) { return QModelIndex(); } if (column >= 3) { return QModelIndex(); } if (row >= 0) { return createIndex(row, column, const_cast(&results_[row])); } else { return createIndex(row, column); } } /** * @brief ResultsModel::parent * @param index * @return */ QModelIndex ResultsModel::parent(const QModelIndex &index) const { Q_UNUSED(index) return QModelIndex(); } /** * @brief ResultsModel::rowCount * @param parent * @return */ int ResultsModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) return results_.size(); } /** * @brief ResultsModel::columnCount * @param parent * @return */ int ResultsModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent) return 3; } /** * @brief ResultsModel::sort * @param column * @param order */ void ResultsModel::sort(int column, Qt::SortOrder order) { if (order == Qt::AscendingOrder) { switch (column) { case 0: std::sort(results_.begin(), results_.end(), [](const Result &s1, const Result &s2) { return s1.address < s2.address; }); break; case 1: std::sort(results_.begin(), results_.end(), [](const Result &s1, const Result &s2) { return s1.type < s2.type; }); break; case 2: std::sort(results_.begin(), results_.end(), [](const Result &s1, const Result &s2) { return s1.string < s2.string; }); break; } } else { switch (column) { case 0: std::sort(results_.begin(), results_.end(), [](const Result &s1, const Result &s2) { return s1.address > s2.address; }); break; case 1: std::sort(results_.begin(), results_.end(), [](const Result &s1, const Result &s2) { return s1.type > s2.type; }); break; case 2: std::sort(results_.begin(), results_.end(), [](const Result &s1, const Result &s2) { return s1.string < s2.string; }); break; } } Q_EMIT dataChanged(createIndex(0, 0, nullptr), createIndex(-1, -1, nullptr)); } } edb-debugger/plugins/ProcessProperties/DialogStrings.ui0000644000175000017500000000621313765535463023217 0ustar eteraneteran Evan Teran ProcessPropertiesPlugin::DialogStrings 0 0 702 384 String Search Regions To Search: Filter true Monospace QAbstractItemView::NoEditTriggers true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows true false true Include UTF-16 Results QDialogButtonBox::Close 0 Qt::Horizontal buttonBox accepted() ProcessPropertiesPlugin::DialogStrings accept() 644 326 671 282 buttonBox rejected() ProcessPropertiesPlugin::DialogStrings reject() 532 331 516 283 edb-debugger/plugins/ProcessProperties/ResultsModel.h0000644000175000017500000000356413765535463022710 0ustar eteraneteran/* Copyright (C) 2006 - 2019 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PROCESS_PROPERTIES_RESULTS_MODEL_H_20191119_ #define PROCESS_PROPERTIES_RESULTS_MODEL_H_20191119_ #include "Function.h" #include "Types.h" #include #include namespace ProcessPropertiesPlugin { class ResultsModel : public QAbstractItemModel { Q_OBJECT public: struct Result { edb::address_t address = 0; QString string; enum { Ascii, Utf8, Utf16, Utf32, } type; }; public: explicit ResultsModel(QObject *parent = nullptr); public: QVariant data(const QModelIndex &index, int role) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &index) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; public: void addResult(const Result &r); public: const QVector &results() const { return results_; } private: QVector results_; }; } #endif edb-debugger/plugins/ProcessProperties/CMakeLists.txt0000644000175000017500000000210213765535463022640 0ustar eteraneterancmake_minimum_required (VERSION 3.1) include("GNUInstallDirs") set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(PluginName "ProcessProperties") find_package(Qt5 5.0.0 REQUIRED Widgets Network) add_library(${PluginName} SHARED DialogProcessProperties.cpp DialogProcessProperties.h DialogProcessProperties.ui DialogStrings.cpp DialogStrings.h DialogStrings.ui ProcessProperties.cpp ProcessProperties.h DialogResults.cpp DialogResults.h DialogResults.ui ResultsModel.cpp ResultsModel.h ) target_link_libraries(${PluginName} Qt5::Widgets Qt5::Network edb) install (TARGETS ${PluginName} DESTINATION ${CMAKE_INSTALL_LIBDIR}/edb) set_property(TARGET ${PluginName} PROPERTY CXX_EXTENSIONS OFF) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD 14) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD_REQUIRED ON) set_property(TARGET ${PluginName} PROPERTY LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) set_property(TARGET ${PluginName} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) edb-debugger/plugins/ProcessProperties/DialogProcessProperties.cpp0000644000175000017500000003730013765535463025427 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "DialogProcessProperties.h" #include "Configuration.h" #include "DialogStrings.h" #include "IDebugger.h" #include "IProcess.h" #include "IRegion.h" #include "ISymbolManager.h" #include "MemoryRegions.h" #include "Module.h" #include "QtHelper.h" #include "Symbol.h" #include "edb.h" #include #include #include #include #include #include #include #include #include #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) #include #include #endif namespace ProcessPropertiesPlugin { Q_DECLARE_NAMESPACE_TR(ProcessPropertiesPlugin) namespace { /** * @brief arguments_to_string * @param args * @return */ QString arguments_to_string(const QList &args) { QString ret; for (const QByteArray &arg : args) { ret.append(' '); ret.append(QString::fromUtf8(arg)); } ret.remove(0, 1); return ret; } /** * @brief size_to_string * @param n * @return */ QString size_to_string(size_t n) { static constexpr size_t KiB = 1024; static constexpr size_t MiB = KiB * 1024; static constexpr size_t GiB = MiB * 1024; if (n < KiB) { return QString::number(n); } else if (n < MiB) { return QString::number(n / KiB) + tr(" KiB"); } else if (n < GiB) { return QString::number(n / MiB) + tr(" MiB"); } else { return QString::number(n / GiB) + tr(" GiB"); } } #if defined(Q_OS_LINUX) /** * @brief file_type * @param filename * @return */ QString file_type(const QString &filename) { const QFileInfo info(filename); const QString basename(info.completeBaseName()); if (basename.startsWith("socket:")) { return tr("Socket"); } if (basename.startsWith("pipe:")) { return tr("Pipe"); } return tr("File"); } /** * @brief tcp_socket_prcoessor * @param symlink * @param sock * @param lst * @return */ bool tcp_socket_prcoessor(QString *symlink, int sock, const QStringList &lst) { Q_ASSERT(symlink); if (lst.size() >= 13) { bool ok; const uint32_t local_address = ntohl(lst[1].toUInt(&ok, 16)); if (ok) { const uint16_t local_port = lst[2].toUInt(&ok, 16); if (ok) { const uint32_t remote_address = ntohl(lst[3].toUInt(&ok, 16)); if (ok) { const uint16_t remote_port = lst[4].toUInt(&ok, 16); if (ok) { const uint8_t state = lst[5].toUInt(&ok, 16); Q_UNUSED(state) if (ok) { const int inode = lst[13].toUInt(&ok, 10); if (ok) { if (inode == sock) { *symlink = QString("TCP: %1:%2 -> %3:%4") .arg(QHostAddress(local_address).toString()) .arg(local_port) .arg(QHostAddress(remote_address).toString()) .arg(remote_port); return true; } } } } } } } } return false; } /** * @brief udp_socket_processor * @param symlink * @param sock * @param lst * @return */ bool udp_socket_processor(QString *symlink, int sock, const QStringList &lst) { Q_ASSERT(symlink); if (lst.size() >= 13) { bool ok; const uint32_t local_address = ntohl(lst[1].toUInt(&ok, 16)); if (ok) { const uint16_t local_port = lst[2].toUInt(&ok, 16); if (ok) { const uint32_t remote_address = ntohl(lst[3].toUInt(&ok, 16)); if (ok) { const uint16_t remote_port = lst[4].toUInt(&ok, 16); if (ok) { const uint8_t state = lst[5].toUInt(&ok, 16); Q_UNUSED(state) if (ok) { const int inode = lst[13].toUInt(&ok, 10); if (ok) { if (inode == sock) { *symlink = QString("UDP: %1:%2 -> %3:%4") .arg(QHostAddress(local_address).toString()) .arg(local_port) .arg(QHostAddress(remote_address).toString()) .arg(remote_port); return true; } } } } } } } } return false; } /** * @brief unix_socket_processor * @param symlink * @param sock * @param lst * @return */ bool unix_socket_processor(QString *symlink, int sock, const QStringList &lst) { Q_ASSERT(symlink); if (lst.size() >= 6) { bool ok; // TODO(eteran): should this be toInt(...)? const int inode = lst[6].toUInt(&ok, 10); if (ok) { if (inode == sock) { *symlink = QString("UNIX [%1]").arg(lst[0]); return true; } } } return false; } /** * @brief process_socket_file * @param filename * @param symlink * @param sock * @param func * @return */ template QString process_socket_file(const QString &filename, QString *symlink, int sock, F func) { Q_ASSERT(symlink); QFile net(filename); net.open(QIODevice::ReadOnly | QIODevice::Text); if (net.isOpen()) { QTextStream in(&net); QString line; // ditch first line, it is just table headings in.readLine(); // read in the first line we care about line = in.readLine(); // a null string means end of file (but not an empty string!) while (!line.isNull()) { QString lline(line); const QStringList lst = lline.replace(":", " ").split(" ", QString::SkipEmptyParts); if (func(symlink, sock, lst)) { break; } line = in.readLine(); } } return *symlink; } /** * @brief process_socket_tcp * @param symlink * @return */ QString process_socket_tcp(QString *symlink) { Q_ASSERT(symlink); const QString socket_info(symlink->mid(symlink->indexOf("socket:["))); const int socket_number = socket_info.mid(8).remove("]").toUInt(); return process_socket_file("/proc/net/tcp", symlink, socket_number, tcp_socket_prcoessor); } /** * @brief process_socket_unix * @param symlink * @return */ QString process_socket_unix(QString *symlink) { Q_ASSERT(symlink); const QString socket_info(symlink->mid(symlink->indexOf("socket:["))); const int socket_number = socket_info.mid(8).remove("]").toUInt(); return process_socket_file("/proc/net/unix", symlink, socket_number, unix_socket_processor); } /** * @brief process_socket_udp * @param symlink * @return */ QString process_socket_udp(QString *symlink) { Q_ASSERT(symlink); const QString socket_info(symlink->mid(symlink->indexOf("socket:["))); const int socket_number = socket_info.mid(8).remove("]").toUInt(); return process_socket_file("/proc/net/udp", symlink, socket_number, udp_socket_processor); } #endif } /** * @brief DialogProcessProperties::DialogProcessProperties * @param parent * @param f */ DialogProcessProperties::DialogProcessProperties(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f) { ui.setupUi(this); ui.tableModules->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); ui.tableMemory->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); ui.threadTable->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); threadsModel_ = new ThreadsModel(this); threadsFilter_ = new QSortFilterProxyModel(this); threadsFilter_->setSourceModel(threadsModel_); threadsFilter_->setFilterCaseSensitivity(Qt::CaseInsensitive); ui.threadTable->setModel(threadsFilter_); } /** * @brief DialogProcessProperties::updateGeneralPage */ void DialogProcessProperties::updateGeneralPage() { if (edb::v1::debugger_core) { if (IProcess *process = edb::v1::debugger_core->process()) { const QString exe = process->executable(); const QString cwd = process->currentWorkingDirectory(); std::shared_ptr parent = process->parent(); const edb::pid_t parent_pid = parent ? parent->pid() : 0; const QString parent_exe = parent ? parent->executable() : QString(); const QList args = process->arguments(); ui.editImage->setText(exe); // TODO(eteran): handle arguments with spaces ui.editCommand->setText(arguments_to_string(args)); ui.editCurrentDirectory->setText(cwd); ui.editStarted->setText(process->startTime().toString("yyyy-MM-dd hh:mm:ss.z")); if (parent_pid) { ui.editParent->setText(QString("%1 (%2)").arg(parent_exe).arg(parent_pid)); } else { ui.editParent->setText(QString()); } } else { ui.editImage->setText(QString()); ui.editCommand->setText(QString()); ui.editCurrentDirectory->setText(QString()); ui.editStarted->setText(QString()); ui.editParent->setText(QString()); } } } /** * @brief DialogProcessProperties::updateModulePage */ void DialogProcessProperties::updateModulePage() { ui.tableModules->clearContents(); ui.tableModules->setRowCount(0); if (edb::v1::debugger_core) { if (IProcess *process = edb::v1::debugger_core->process()) { const QList modules = process->loadedModules(); ui.tableModules->setSortingEnabled(false); for (const Module &m : modules) { const int row = ui.tableModules->rowCount(); ui.tableModules->insertRow(row); ui.tableModules->setItem(row, 0, new QTableWidgetItem(edb::v1::format_pointer(m.baseAddress))); ui.tableModules->setItem(row, 1, new QTableWidgetItem(m.name)); } ui.tableModules->setSortingEnabled(true); } } } /** * @brief DialogProcessProperties::updateMemoryPage */ void DialogProcessProperties::updateMemoryPage() { ui.tableMemory->clearContents(); ui.tableMemory->setRowCount(0); if (edb::v1::debugger_core) { edb::v1::memory_regions().sync(); const QList> regions = edb::v1::memory_regions().regions(); ui.tableMemory->setSortingEnabled(false); for (const std::shared_ptr &r : regions) { const int row = ui.tableMemory->rowCount(); ui.tableMemory->insertRow(row); ui.tableMemory->setItem(row, 0, new QTableWidgetItem(edb::v1::format_pointer(r->start()))); // address ui.tableMemory->setItem(row, 1, new QTableWidgetItem(size_to_string(r->size()))); // size ui.tableMemory->setItem(row, 2, new QTableWidgetItem(QString("%1%2%3") // protection .arg(r->readable() ? 'r' : '-') .arg(r->writable() ? 'w' : '-') .arg(r->executable() ? 'x' : '-'))); ui.tableMemory->setItem(row, 3, new QTableWidgetItem(r->name())); // name } ui.tableMemory->setSortingEnabled(true); } } /** * @brief DialogProcessProperties::on_txtSearchEnvironment_textChanged * @param text */ void DialogProcessProperties::on_txtSearchEnvironment_textChanged(const QString &text) { updateEnvironmentPage(text); } /** * @brief DialogProcessProperties::updateEnvironmentPage * @param filter */ void DialogProcessProperties::updateEnvironmentPage(const QString &filter) { // tableEnvironment ui.tableEnvironment->clearContents(); ui.tableEnvironment->setSortingEnabled(false); ui.tableEnvironment->setRowCount(0); const QString lower_filter = filter.toLower(); #ifdef Q_OS_LINUX if (IProcess *process = edb::v1::debugger_core->process()) { QFile proc_environ(QString("/proc/%1/environ").arg(process->pid())); if (proc_environ.open(QIODevice::ReadOnly)) { QByteArray env = proc_environ.readAll(); char *p = env.data(); char *ptr = p; while (ptr != p + env.size()) { const QString env = QString::fromUtf8(ptr); const QString env_name = env.mid(0, env.indexOf("=")); const QString env_value = env.mid(env.indexOf("=") + 1); if (lower_filter.isEmpty() || env_name.contains(lower_filter, Qt::CaseInsensitive)) { const int row = ui.tableEnvironment->rowCount(); ui.tableEnvironment->insertRow(row); ui.tableEnvironment->setItem(row, 0, new QTableWidgetItem(env_name)); ui.tableEnvironment->setItem(row, 1, new QTableWidgetItem(env_value)); } ptr += qstrlen(ptr) + 1; } } } #endif ui.tableEnvironment->setSortingEnabled(true); } /** * @brief DialogProcessProperties::updateHandles */ void DialogProcessProperties::updateHandles() { ui.tableHandles->setSortingEnabled(false); ui.tableHandles->setRowCount(0); #ifdef Q_OS_LINUX if (IProcess *process = edb::v1::debugger_core->process()) { QDir dir(QString("/proc/%1/fd/").arg(process->pid())); const QFileInfoList entries = dir.entryInfoList(QStringList() << "[0-9]*"); for (const QFileInfo &info : entries) { if (info.isSymLink()) { QString symlink(info.symLinkTarget()); const QString type(file_type(symlink)); if (type == tr("Socket")) { symlink = process_socket_tcp(&symlink); symlink = process_socket_udp(&symlink); symlink = process_socket_unix(&symlink); } if (type == tr("Pipe")) { symlink = tr("FIFO"); } const int row = ui.tableHandles->rowCount(); ui.tableHandles->insertRow(row); auto itemFD = new QTableWidgetItem; itemFD->setData(Qt::DisplayRole, info.fileName().toUInt()); ui.tableHandles->setItem(row, 0, new QTableWidgetItem(type)); ui.tableHandles->setItem(row, 1, itemFD); ui.tableHandles->setItem(row, 2, new QTableWidgetItem(symlink)); } } } #endif ui.tableHandles->setSortingEnabled(true); } /** * @brief DialogProcessProperties::showEvent */ void DialogProcessProperties::showEvent(QShowEvent *) { updateGeneralPage(); updateMemoryPage(); updateModulePage(); updateHandles(); updateThreads(); updateEnvironmentPage(ui.txtSearchEnvironment->text()); } /** * @brief DialogProcessProperties::on_btnParent_clicked */ void DialogProcessProperties::on_btnParent_clicked() { if (edb::v1::debugger_core) { if (IProcess *process = edb::v1::debugger_core->process()) { std::shared_ptr parent = process->parent(); const QString parent_exe = parent ? parent->executable() : QString(); QFileInfo info(parent_exe); QDir dir = info.absoluteDir(); QDesktopServices::openUrl(QUrl(tr("file://%1").arg(dir.absolutePath()), QUrl::TolerantMode)); } } } /** * @brief DialogProcessProperties::on_btnImage_clicked */ void DialogProcessProperties::on_btnImage_clicked() { if (edb::v1::debugger_core) { QFileInfo info(ui.editImage->text()); QDir dir = info.absoluteDir(); QDesktopServices::openUrl(QUrl(tr("file://%1").arg(dir.absolutePath()), QUrl::TolerantMode)); } } /** * @brief DialogProcessProperties::on_btnRefreshEnvironment_clicked */ void DialogProcessProperties::on_btnRefreshEnvironment_clicked() { updateEnvironmentPage(ui.txtSearchEnvironment->text()); } /** * @brief DialogProcessProperties::on_btnRefreshHandles_clicked */ void DialogProcessProperties::on_btnRefreshHandles_clicked() { updateHandles(); } /** * @brief DialogProcessProperties::on_btnStrings_clicked */ void DialogProcessProperties::on_btnStrings_clicked() { static auto dialog = new DialogStrings(edb::v1::debugger_ui); dialog->show(); } /** * @brief DialogProcessProperties::on_btnRefreshMemory_clicked */ void DialogProcessProperties::on_btnRefreshMemory_clicked() { updateMemoryPage(); } /** * @brief DialogProcessProperties::on_btnRefreshThreads_clicked */ void DialogProcessProperties::on_btnRefreshThreads_clicked() { updateThreads(); } /** * @brief DialogProcessProperties::updateThreads */ void DialogProcessProperties::updateThreads() { threadsModel_->clear(); if (IProcess *process = edb::v1::debugger_core->process()) { std::shared_ptr current = process->currentThread(); for (std::shared_ptr &thread : process->threads()) { if (thread == current) { threadsModel_->addThread(thread, true); } else { threadsModel_->addThread(thread, false); } } } } } edb-debugger/plugins/ProcessProperties/DialogResults.h0000644000175000017500000000136113765535463023040 0ustar eteraneteran #ifndef PROCESS_PROPERTIES_DIALOG_RESULTS_H_20191119_ #define PROCESS_PROPERTIES_DIALOG_RESULTS_H_20191119_ #include "ResultsModel.h" #include "ui_DialogResults.h" #include #include class QSortFilterProxyModel; namespace ProcessPropertiesPlugin { class DialogResults : public QDialog { Q_OBJECT public: explicit DialogResults(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); public: void addResult(const ResultsModel::Result &result); private Q_SLOTS: void on_tableView_doubleClicked(const QModelIndex &index); public: int resultCount() const; private: Ui::DialogResults ui; ResultsModel *model_ = nullptr; QSortFilterProxyModel *filterModel_ = nullptr; }; } #endif edb-debugger/plugins/ProcessProperties/DialogResults.ui0000644000175000017500000000547313765535463023236 0ustar eteraneteran ProcessPropertiesPlugin::DialogResults 0 0 690 315 Strings Monospace Qt::CustomContextMenu QAbstractItemView::NoEditTriggers true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows true false true false Filter true Qt::Horizontal QDialogButtonBox::Close buttonBox accepted() ProcessPropertiesPlugin::DialogResults accept() 224 326 157 274 buttonBox rejected() ProcessPropertiesPlugin::DialogResults reject() 292 332 286 274 edb-debugger/plugins/ProcessProperties/DialogStrings.cpp0000644000175000017500000001001113765535463023353 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "DialogStrings.h" #include "Configuration.h" #include "DialogResults.h" #include "IRegion.h" #include "MemoryRegions.h" #include "ResultsModel.h" #include "edb.h" #include "util/Math.h" #include #include #include #include namespace ProcessPropertiesPlugin { /** * @brief DialogStrings::DialogStrings * @param parent * @param f */ DialogStrings::DialogStrings(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f) { ui.setupUi(this); ui.tableView->verticalHeader()->hide(); ui.tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); filterModel_ = new QSortFilterProxyModel(this); connect(ui.txtSearch, &QLineEdit::textChanged, filterModel_, &QSortFilterProxyModel::setFilterFixedString); buttonFind_ = new QPushButton(QIcon::fromTheme("edit-find"), tr("Find")); connect(buttonFind_, &QPushButton::clicked, this, [this]() { buttonFind_->setEnabled(false); ui.progressBar->setValue(0); doFind(); ui.progressBar->setValue(100); buttonFind_->setEnabled(true); }); ui.buttonBox->addButton(buttonFind_, QDialogButtonBox::ActionRole); } /** * @brief DialogStrings::showEvent */ void DialogStrings::showEvent(QShowEvent *) { filterModel_->setFilterKeyColumn(3); filterModel_->setSourceModel(&edb::v1::memory_regions()); ui.tableView->setModel(filterModel_); ui.progressBar->setValue(0); } /** * @brief DialogStrings::doFind */ void DialogStrings::doFind() { const int min_string_length = edb::v1::config().min_string_length; const QItemSelectionModel *const selection_model = ui.tableView->selectionModel(); const QModelIndexList sel = selection_model->selectedRows(); QString str; if (sel.size() == 0) { QMessageBox::critical( this, tr("No Region Selected"), tr("You must select a region which is to be scanned for strings.")); return; } auto resultsDialog = new DialogResults(this); for (const QModelIndex &selected_item : sel) { const QModelIndex index = filterModel_->mapToSource(selected_item); if (auto region = *reinterpret_cast *>(index.internalPointer())) { edb::address_t start_address = region->start(); const edb::address_t end_address = region->end(); const edb::address_t orig_start = start_address; // do the search for this region! while (start_address < end_address) { int string_length = 0; bool ok = edb::v1::get_ascii_string_at_address(start_address, str, min_string_length, 256, string_length); if (ok) { resultsDialog->addResult({start_address, str, ResultsModel::Result::Ascii}); } else if (ui.search_unicode->isChecked()) { string_length = 0; ok = edb::v1::get_utf16_string_at_address(start_address, str, min_string_length, 256, string_length); if (ok) { resultsDialog->addResult({start_address, str, ResultsModel::Result::Utf16}); } } ui.progressBar->setValue(util::percentage((start_address - orig_start), region->size())); if (ok) { start_address += string_length; } else { ++start_address; } } } } if (resultsDialog->resultCount() == 0) { QMessageBox::information(this, tr("No Strings Found"), tr("No strings were found in the selected region")); delete resultsDialog; } else { resultsDialog->show(); } } } edb-debugger/plugins/ProcessProperties/ProcessProperties.h0000644000175000017500000000254213765535463023754 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PROCESS_PROPERTIES_H_20100817_ #define PROCESS_PROPERTIES_H_20100817_ #include "IPlugin.h" class QMenu; class QDialog; namespace ProcessPropertiesPlugin { class ProcessProperties : public QObject, public IPlugin { Q_OBJECT Q_INTERFACES(IPlugin) Q_PLUGIN_METADATA(IID "edb.IPlugin/1.0") Q_CLASSINFO("author", "Evan Teran") Q_CLASSINFO("url", "http://www.codef00.com") public: explicit ProcessProperties(QObject *parent = nullptr); ~ProcessProperties() override; public: QMenu *menu(QWidget *parent = nullptr) override; public Q_SLOTS: void showMenu(); private: QMenu *menu_ = nullptr; QPointer dialog_ = nullptr; }; } #endif edb-debugger/plugins/ProcessProperties/DialogStrings.h0000644000175000017500000000250113765535463023025 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DIALOG_STRINGS_H_20061101_ #define DIALOG_STRINGS_H_20061101_ #include "Types.h" #include "ui_DialogStrings.h" #include class QSortFilterProxyModel; class QListWidgetItem; namespace ProcessPropertiesPlugin { class DialogStrings : public QDialog { Q_OBJECT public: explicit DialogStrings(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~DialogStrings() override = default; private: void showEvent(QShowEvent *event) override; private: void doFind(); private: Ui::DialogStrings ui; QSortFilterProxyModel *filterModel_ = nullptr; QPushButton *buttonFind_ = nullptr; }; } #endif edb-debugger/plugins/ProcessProperties/DialogProcessProperties.ui0000644000175000017500000004325113765535463025264 0ustar eteraneteran Evan Teran ProcessPropertiesPlugin::DialogProcessProperties 0 0 714 599 Process Properties 0 General File Image File Name: true ... .. Qt::NoArrow Process Command Line: true Current Directory: true Started: true Parent: true Qt::Vertical 20 236 ... .. Threads Qt::Horizontal 604 20 Refresh .. Monospace 8 QAbstractItemView::NoEditTriggers true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows true false true false Modules Qt::Horizontal 610 20 Refresh .. Monospace 8 QAbstractItemView::NoEditTriggers true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows true false true false Base Address Name Memory Strings Qt::Horizontal 467 20 Refresh .. Monospace 8 QAbstractItemView::NoEditTriggers true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows true false true false Address Size Protection Name Environment Filter Refresh .. Monospace 8 QAbstractItemView::NoEditTriggers true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows true false true false Name Value Handles Qt::Horizontal 40 20 Refresh .. Monospace 8 QAbstractItemView::NoEditTriggers true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows true false true false Type Handle Name QDialogButtonBox::Close buttonBox rejected() ProcessPropertiesPlugin::DialogProcessProperties reject() 650 574 439 557 buttonBox accepted() ProcessPropertiesPlugin::DialogProcessProperties accept() 30 571 3 554 edb-debugger/plugins/FunctionFinder/0000755000175000017500000000000013765535463017327 5ustar eteraneteranedb-debugger/plugins/FunctionFinder/DialogResults.cpp0000644000175000017500000001342213765535463022616 0ustar eteraneteran/* Copyright (C) 2006 - 2019 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "DialogResults.h" #include "IAnalyzer.h" #include "ISymbolManager.h" #include "MemoryRegions.h" #include "ResultsModel.h" #include "edb.h" #ifdef ENABLE_GRAPH #include "GraphEdge.h" #include "GraphNode.h" #include "GraphWidget.h" #endif #include #include #include #include #include #include namespace FunctionFinderPlugin { /** * @brief DialogResults::DialogResults * @param parent * @param f */ DialogResults::DialogResults(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f) { ui.setupUi(this); ui.tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); resultsModel_ = new ResultsModel(this); filterModel_ = new QSortFilterProxyModel(this); filterModel_->setFilterKeyColumn(5); filterModel_->setSourceModel(resultsModel_); connect(ui.textFilter, &QLineEdit::textChanged, filterModel_, &QSortFilterProxyModel::setFilterFixedString); ui.tableView->setModel(filterModel_); buttonGraph_ = new QPushButton(QIcon::fromTheme("distribute-graph"), tr("Graph Selected Function")); #if defined(ENABLE_GRAPH) connect(buttonGraph_, &QPushButton::clicked, this, [this]() { // this code is not very pretty... // but it works! qDebug("[FunctionFinder] Constructing Graph..."); const QItemSelectionModel *const selModel = ui.tableView->selectionModel(); const QModelIndexList sel = selModel->selectedRows(); if (sel.size() == 1) { const QModelIndex index = filterModel_->mapToSource(sel[0]); if (auto item = static_cast(index.internalPointer())) { const edb::address_t addr = item->startAddress; if (IAnalyzer *const analyzer = edb::v1::analyzer()) { const IAnalyzer::FunctionMap &functions = analyzer->functions(); auto it = functions.find(addr); if (it != functions.end()) { Function f = *it; auto graph = new GraphWidget(nullptr); graph->setAttribute(Qt::WA_DeleteOnClose); QMap nodes; // first create all of the nodes for (const auto &pair : f) { const BasicBlock &bb = pair.second; auto node = new GraphNode(graph, bb.toString(), Qt::lightGray); nodes.insert(bb.firstAddress(), node); } // then connect them! for (const auto &pair : f) { const BasicBlock &bb = pair.second; if (!bb.empty()) { auto term = bb.back(); auto &inst = *term; if (is_unconditional_jump(inst)) { Q_ASSERT(inst.operandCount() >= 1); const auto op = inst[0]; // TODO: we need some heuristic for detecting when this is // a call/ret -> jmp optimization if (is_immediate(op)) { const edb::address_t ea = op->imm; auto from = nodes.find(bb.firstAddress()); auto to = nodes.find(ea); if (to != nodes.end() && from != nodes.end()) { new GraphEdge(from.value(), to.value(), Qt::black); } } } else if (is_conditional_jump(inst)) { Q_ASSERT(inst.operandCount() == 1); const auto op = inst[0]; if (is_immediate(op)) { auto from = nodes.find(bb.firstAddress()); auto to_taken = nodes.find(op->imm); if (to_taken != nodes.end() && from != nodes.end()) { new GraphEdge(from.value(), to_taken.value(), Qt::green); } auto to_skipped = nodes.find(inst.rva() + inst.byteSize()); if (to_taken != nodes.end() && from != nodes.end()) { new GraphEdge(from.value(), to_skipped.value(), Qt::red); } } } else if (is_terminator(inst)) { } } } graph->layout(); graph->show(); } } } } }); #endif ui.buttonBox->addButton(buttonGraph_, QDialogButtonBox::ActionRole); #ifdef ENABLE_GRAPH buttonGraph_->setEnabled(true); #else buttonGraph_->setEnabled(false); #endif } /** * @brief DialogResults::on_tableView_doubleClicked * @param index */ void DialogResults::on_tableView_doubleClicked(const QModelIndex &index) { if (index.isValid()) { const QModelIndex realIndex = filterModel_->mapToSource(index); if (auto item = static_cast(realIndex.internalPointer())) { edb::v1::jump_to_address(item->startAddress); } } } /** * @brief DialogResults::addResult * @param function */ void DialogResults::addResult(const Function &function) { ResultsModel::Result result; // entry point result.startAddress = function.entryAddress(); // upper bound of the function result.endAddress = function.endAddress(); result.size = function.endAddress() - function.entryAddress() + 1; // reference count result.score = function.referenceCount(); // type result.type = function.type(); QString symbol_name = edb::v1::symbol_manager().findAddressName(function.entryAddress()); if (!symbol_name.isEmpty()) { result.symbol = symbol_name; } resultsModel_->addResult(result); } /** * @brief DialogResults::resultCount * @return */ int DialogResults::resultCount() const { return resultsModel_->rowCount(); } } edb-debugger/plugins/FunctionFinder/FunctionFinder.h0000644000175000017500000000252113765535463022415 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef FUNCTION_FINDER_H_20060430_ #define FUNCTION_FINDER_H_20060430_ #include "IPlugin.h" class QMenu; class QDialog; namespace FunctionFinderPlugin { class FunctionFinder : public QObject, public IPlugin { Q_OBJECT Q_INTERFACES(IPlugin) Q_PLUGIN_METADATA(IID "edb.IPlugin/1.0") Q_CLASSINFO("author", "Evan Teran") Q_CLASSINFO("url", "http://www.codef00.com") public: explicit FunctionFinder(QObject *parent = nullptr); ~FunctionFinder() override; public: QMenu *menu(QWidget *parent = nullptr) override; public Q_SLOTS: void showMenu(); private: QMenu *menu_ = nullptr; QPointer dialog_ = nullptr; }; } #endif edb-debugger/plugins/FunctionFinder/ResultsModel.cpp0000644000175000017500000001242113765535463022455 0ustar eteraneteran/* Copyright (C) 2006 - 2019 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "ResultsModel.h" #include "edb.h" #include namespace FunctionFinderPlugin { /** * @brief ResultsModel::ResultsModel * @param parent */ ResultsModel::ResultsModel(QObject *parent) : QAbstractItemModel(parent) { } /** * @brief ResultsModel::headerData * @param section * @param orientation * @param role * @return */ QVariant ResultsModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { switch (section) { case 0: return tr("Start Address"); case 1: return tr("End Address"); case 2: return tr("Size"); case 3: return tr("Score"); case 4: return tr("Type"); case 5: return tr("Symbol"); } } return QVariant(); } /** * @brief ResultsModel::data * @param index * @param role * @return */ QVariant ResultsModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } const Result &result = results_[index.row()]; if (role == Qt::DisplayRole) { switch (index.column()) { case 0: return edb::v1::format_pointer(result.startAddress); case 1: return edb::v1::format_pointer(result.endAddress); case 2: return static_cast(result.size); case 3: return result.score; case 4: return result.type == Function::Thunk ? tr("Thunk") : tr("Standard Function"); case 5: return result.symbol; default: return QVariant(); } } return QVariant(); } /** * @brief ResultsModel::addResult * @param r */ void ResultsModel::addResult(const Result &r) { beginInsertRows(QModelIndex(), rowCount(), rowCount()); results_.push_back(r); endInsertRows(); } /** * @brief ResultsModel::index * @param row * @param column * @param parent * @return */ QModelIndex ResultsModel::index(int row, int column, const QModelIndex &parent) const { Q_UNUSED(parent) if (row >= results_.size()) { return QModelIndex(); } if (column >= 6) { return QModelIndex(); } if (row >= 0) { return createIndex(row, column, const_cast(&results_[row])); } else { return createIndex(row, column); } } /** * @brief ResultsModel::parent * @param index * @return */ QModelIndex ResultsModel::parent(const QModelIndex &index) const { Q_UNUSED(index) return QModelIndex(); } /** * @brief ResultsModel::rowCount * @param parent * @return */ int ResultsModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) return results_.size(); } /** * @brief ResultsModel::columnCount * @param parent * @return */ int ResultsModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent) return 6; } /** * @brief ResultsModel::sort * @param column * @param order */ void ResultsModel::sort(int column, Qt::SortOrder order) { if (order == Qt::AscendingOrder) { switch (column) { case 0: std::sort(results_.begin(), results_.end(), [](const Result &s1, const Result &s2) { return s1.startAddress < s2.startAddress; }); break; case 1: std::sort(results_.begin(), results_.end(), [](const Result &s1, const Result &s2) { return s1.endAddress < s2.endAddress; }); break; case 2: std::sort(results_.begin(), results_.end(), [](const Result &s1, const Result &s2) { return s1.size < s2.size; }); break; case 3: std::sort(results_.begin(), results_.end(), [](const Result &s1, const Result &s2) { return s1.score < s2.score; }); break; case 4: std::sort(results_.begin(), results_.end(), [](const Result &s1, const Result &s2) { return s1.type < s2.type; }); break; case 5: std::sort(results_.begin(), results_.end(), [](const Result &s1, const Result &s2) { return s1.symbol < s2.symbol; }); break; } } else { switch (column) { case 0: std::sort(results_.begin(), results_.end(), [](const Result &s1, const Result &s2) { return s1.startAddress > s2.startAddress; }); break; case 1: std::sort(results_.begin(), results_.end(), [](const Result &s1, const Result &s2) { return s1.endAddress > s2.endAddress; }); break; case 2: std::sort(results_.begin(), results_.end(), [](const Result &s1, const Result &s2) { return s1.size > s2.size; }); break; case 3: std::sort(results_.begin(), results_.end(), [](const Result &s1, const Result &s2) { return s1.score > s2.score; }); break; case 4: std::sort(results_.begin(), results_.end(), [](const Result &s1, const Result &s2) { return s1.type > s2.type; }); break; case 5: std::sort(results_.begin(), results_.end(), [](const Result &s1, const Result &s2) { return s1.symbol > s2.symbol; }); break; } } Q_EMIT dataChanged(createIndex(0, 0, nullptr), createIndex(-1, -1, nullptr)); } } edb-debugger/plugins/FunctionFinder/ResultsModel.h0000644000175000017500000000367013765535463022130 0ustar eteraneteran/* Copyright (C) 2006 - 2019 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef RESULTS_MODEL_H_20070419_ #define RESULTS_MODEL_H_20070419_ #include "Function.h" #include "Types.h" #include #include namespace FunctionFinderPlugin { class ResultsModel : public QAbstractItemModel { Q_OBJECT public: struct Result { edb::address_t startAddress = 0; edb::address_t endAddress = 0; size_t size = 0; int score = 0; Function::Type type = Function::Type::Standard; QString symbol; }; public: explicit ResultsModel(QObject *parent = nullptr); public: QVariant data(const QModelIndex &index, int role) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &index) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; public: void addResult(const Result &r); public: const QVector &results() const { return results_; } private: QVector results_; }; } #endif edb-debugger/plugins/FunctionFinder/CMakeLists.txt0000644000175000017500000000165713765535463022100 0ustar eteraneterancmake_minimum_required (VERSION 3.1) include("GNUInstallDirs") set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(PluginName "FunctionFinder") find_package(Qt5 5.0.0 REQUIRED Widgets) add_library(${PluginName} SHARED DialogFunctions.cpp DialogFunctions.h DialogFunctions.ui DialogResults.cpp DialogResults.h DialogResults.ui FunctionFinder.cpp FunctionFinder.h ResultsModel.h ResultsModel.cpp ) target_link_libraries(${PluginName} Qt5::Widgets edb) install (TARGETS ${PluginName} DESTINATION ${CMAKE_INSTALL_LIBDIR}/edb) set_property(TARGET ${PluginName} PROPERTY CXX_EXTENSIONS OFF) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD 14) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD_REQUIRED ON) set_property(TARGET ${PluginName} PROPERTY LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) set_property(TARGET ${PluginName} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) edb-debugger/plugins/FunctionFinder/DialogResults.h0000644000175000017500000000270513765535463022265 0ustar eteraneteran/* Copyright (C) 2006 - 2019 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DIALOG_RESULTS_H_20190403_ #define DIALOG_RESULTS_H_20190403_ #include "Types.h" #include "ui_DialogResults.h" #include class QSortFilterProxyModel; class IAnalyzer; class Function; namespace FunctionFinderPlugin { class ResultsModel; class DialogResults : public QDialog { Q_OBJECT public: explicit DialogResults(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~DialogResults() override = default; public: void addResult(const Function &function); int resultCount() const; public Q_SLOTS: void on_tableView_doubleClicked(const QModelIndex &index); private: Ui::DialogResults ui; QSortFilterProxyModel *filterModel_ = nullptr; ResultsModel *resultsModel_ = nullptr; QPushButton *buttonGraph_ = nullptr; }; } #endif edb-debugger/plugins/FunctionFinder/DialogResults.ui0000644000175000017500000000522413765535463022452 0ustar eteraneteran FunctionFinderPlugin::DialogResults 0 0 850 450 Functions Found Filter true Monospace Qt::CustomContextMenu QAbstractItemView::NoEditTriggers true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows true false true false QDialogButtonBox::Close buttonBox accepted() FunctionFinderPlugin::DialogResults accept() 791 422 788 406 buttonBox rejected() FunctionFinderPlugin::DialogResults reject() 810 423 826 407 edb-debugger/plugins/FunctionFinder/DialogFunctions.h0000644000175000017500000000246513765535463022577 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DIALOG_FUNCTIONS_H_20061101_ #define DIALOG_FUNCTIONS_H_20061101_ #include "Types.h" #include "ui_DialogFunctions.h" #include class QSortFilterProxyModel; namespace FunctionFinderPlugin { class DialogFunctions : public QDialog { Q_OBJECT public: explicit DialogFunctions(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~DialogFunctions() override = default; private: void showEvent(QShowEvent *event) override; private: void doFind(); private: Ui::DialogFunctions ui; QSortFilterProxyModel *filterModel_ = nullptr; QPushButton *buttonFind_ = nullptr; }; } #endif edb-debugger/plugins/FunctionFinder/DialogFunctions.ui0000644000175000017500000000611113765535463022755 0ustar eteraneteran Evan Teran FunctionFinderPlugin::DialogFunctions 0 0 640 400 Function Finder Regions To Search: Filter true Monospace QAbstractItemView::NoEditTriggers true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows true false true false QDialogButtonBox::Close 0 Qt::Horizontal tableView txtSearch buttonBox accepted() FunctionFinderPlugin::DialogFunctions accept() 161 353 113 338 buttonBox rejected() FunctionFinderPlugin::DialogFunctions reject() 346 358 361 340 edb-debugger/plugins/FunctionFinder/FunctionFinder.cpp0000644000175000017500000000305013765535463022746 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "FunctionFinder.h" #include "DialogFunctions.h" #include "edb.h" #include namespace FunctionFinderPlugin { /** * @brief FunctionFinder::FunctionFinder * @param parent */ FunctionFinder::FunctionFinder(QObject *parent) : QObject(parent) { } /** * @brief FunctionFinder::~FunctionFinder */ FunctionFinder::~FunctionFinder() { delete dialog_; } /** * @brief FunctionFinder::menu * @param parent * @return */ QMenu *FunctionFinder::menu(QWidget *parent) { Q_ASSERT(parent); if (!menu_) { menu_ = new QMenu(tr("FunctionFinder"), parent); menu_->addAction(tr("&Function Finder"), this, SLOT(showMenu()), QKeySequence(tr("Ctrl+Shift+F"))); } return menu_; } /** * @brief FunctionFinder::showMenu */ void FunctionFinder::showMenu() { if (!dialog_) { dialog_ = new DialogFunctions(edb::v1::debugger_ui); } dialog_->show(); } } edb-debugger/plugins/FunctionFinder/DialogFunctions.cpp0000644000175000017500000000673013765535463023131 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "DialogFunctions.h" #include "DialogResults.h" #include "IAnalyzer.h" #include "MemoryRegions.h" #include "edb.h" #include #include #include #include #include #include namespace FunctionFinderPlugin { /** * @brief DialogFunctions::DialogFunctions * @param parent * @param f */ DialogFunctions::DialogFunctions(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f) { ui.setupUi(this); ui.tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); filterModel_ = new QSortFilterProxyModel(this); connect(ui.txtSearch, &QLineEdit::textChanged, filterModel_, &QSortFilterProxyModel::setFilterFixedString); buttonFind_ = new QPushButton(QIcon::fromTheme("edit-find"), tr("Find")); connect(buttonFind_, &QPushButton::clicked, this, [this]() { buttonFind_->setEnabled(false); ui.progressBar->setValue(0); doFind(); ui.progressBar->setValue(100); buttonFind_->setEnabled(true); }); ui.buttonBox->addButton(buttonFind_, QDialogButtonBox::ActionRole); } /** * @brief DialogFunctions::showEvent */ void DialogFunctions::showEvent(QShowEvent *) { filterModel_->setFilterKeyColumn(3); filterModel_->setSourceModel(&edb::v1::memory_regions()); ui.tableView->setModel(filterModel_); ui.progressBar->setValue(0); } /** * @brief DialogFunctions::doFind */ void DialogFunctions::doFind() { if (IAnalyzer *const analyzer = edb::v1::analyzer()) { const QItemSelectionModel *const selModel = ui.tableView->selectionModel(); const QModelIndexList sel = selModel->selectedRows(); if (sel.size() == 0) { QMessageBox::critical(this, tr("No Region Selected"), tr("You must select a region which is to be scanned for functions.")); return; } auto analyzer_object = dynamic_cast(analyzer); if (analyzer_object) { connect(analyzer_object, SIGNAL(updateProgress(int)), ui.progressBar, SLOT(setValue(int))); } auto resultsDialog = new DialogResults(this); for (const QModelIndex &selected_item : sel) { const QModelIndex index = filterModel_->mapToSource(selected_item); // do the search for this region! if (auto region = *reinterpret_cast *>(index.internalPointer())) { analyzer->analyze(region); const IAnalyzer::FunctionMap &results = analyzer->functions(region); for (const Function &function : results) { resultsDialog->addResult(function); } } } if (resultsDialog->resultCount() == 0) { QMessageBox::information(this, tr("No Results"), tr("No Functions Found!")); delete resultsDialog; } else { resultsDialog->show(); } if (analyzer_object) { disconnect(analyzer_object, SIGNAL(updateProgress(int)), ui.progressBar, SLOT(setValue(int))); } } } } edb-debugger/plugins/HardwareBreakpoints/0000755000175000017500000000000013765535463020351 5ustar eteraneteranedb-debugger/plugins/HardwareBreakpoints/DialogHwBreakpoints.cpp0000644000175000017500000000713213765535463024760 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "DialogHwBreakpoints.h" #include "IDebugger.h" #include "IProcess.h" #include "IThread.h" #include "State.h" #include "edb.h" #include "libHardwareBreakpoints.h" namespace HardwareBreakpointsPlugin { /** * @brief DialogHwBreakpoints::DialogHwBreakpoints * @param parent * @param f */ DialogHwBreakpoints::DialogHwBreakpoints(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f) { ui.setupUi(this); connect(ui.cmbType1, SIGNAL(currentIndexChanged(int)), this, SLOT(type1IndexChanged(int))); connect(ui.cmbType2, SIGNAL(currentIndexChanged(int)), this, SLOT(type2IndexChanged(int))); connect(ui.cmbType3, SIGNAL(currentIndexChanged(int)), this, SLOT(type3IndexChanged(int))); connect(ui.cmbType4, SIGNAL(currentIndexChanged(int)), this, SLOT(type4IndexChanged(int))); } /** * @brief DialogHwBreakpoints::type1IndexChanged * @param index */ void DialogHwBreakpoints::type1IndexChanged(int index) { ui.cmbSize1->setEnabled(index != 0); } /** * @brief DialogHwBreakpoints::type2IndexChanged * @param index */ void DialogHwBreakpoints::type2IndexChanged(int index) { ui.cmbSize2->setEnabled(index != 0); } /** * @brief DialogHwBreakpoints::type3IndexChanged * @param index */ void DialogHwBreakpoints::type3IndexChanged(int index) { ui.cmbSize3->setEnabled(index != 0); } /** * @brief DialogHwBreakpoints::type4IndexChanged * @param index */ void DialogHwBreakpoints::type4IndexChanged(int index) { ui.cmbSize4->setEnabled(index != 0); } /** * @brief DialogHwBreakpoints::showEvent * @param event */ void DialogHwBreakpoints::showEvent(QShowEvent *event) { Q_UNUSED(event) if (IProcess *process = edb::v1::debugger_core->process()) { State state; process->currentThread()->getState(&state); const BreakpointState bp_state1 = breakpoint_state(&state, Register1); const BreakpointState bp_state2 = breakpoint_state(&state, Register2); const BreakpointState bp_state3 = breakpoint_state(&state, Register3); const BreakpointState bp_state4 = breakpoint_state(&state, Register4); ui.chkBP1->setChecked(bp_state1.enabled); ui.chkBP2->setChecked(bp_state2.enabled); ui.chkBP3->setChecked(bp_state3.enabled); ui.chkBP4->setChecked(bp_state4.enabled); if (bp_state1.enabled) { ui.txtBP1->setText(bp_state1.addr.toPointerString()); ui.cmbSize1->setCurrentIndex(bp_state1.size); ui.cmbType1->setCurrentIndex(bp_state1.type); } if (bp_state2.enabled) { ui.txtBP2->setText(bp_state2.addr.toPointerString()); ui.cmbSize2->setCurrentIndex(bp_state2.size); ui.cmbType2->setCurrentIndex(bp_state2.type); } if (bp_state3.enabled) { ui.txtBP3->setText(bp_state3.addr.toPointerString()); ui.cmbSize3->setCurrentIndex(bp_state3.size); ui.cmbType3->setCurrentIndex(bp_state3.type); } if (bp_state4.enabled) { ui.txtBP4->setText(bp_state4.addr.toPointerString()); ui.cmbSize4->setCurrentIndex(bp_state4.size); ui.cmbType4->setCurrentIndex(bp_state4.type); } } } } edb-debugger/plugins/HardwareBreakpoints/HardwareBreakpoints.cpp0000644000175000017500000004510213765535463025016 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "HardwareBreakpoints.h" #include "DialogHwBreakpoints.h" #include "IDebugEvent.h" #include "IDebugger.h" #include "IProcess.h" #include "IThread.h" #include "State.h" #include "edb.h" #include #include #include #include #include "ui_DialogHwBreakpoints.h" // TODO: at the moment, nearly this entire file is x86/x86-64 specific // we need to figure out a proper way to support (if at all) non // x86 arches #if !(defined(EDB_X86_64) || defined(EDB_X86)) #error "Unsupported Platform" #endif namespace HardwareBreakpointsPlugin { /** * @brief HardwareBreakpoints::HardwareBreakpoints * @param parent */ HardwareBreakpoints::HardwareBreakpoints(QObject *parent) : QObject(parent) { } /** * @brief HardwareBreakpoints::private_init */ void HardwareBreakpoints::privateInit() { auto dialog = new DialogHwBreakpoints(edb::v1::debugger_ui); dialog_ = dialog; // indexed access to members for simplicity later enabled_[Register1] = dialog->ui.chkBP1; enabled_[Register2] = dialog->ui.chkBP2; enabled_[Register3] = dialog->ui.chkBP3; enabled_[Register4] = dialog->ui.chkBP4; types_[Register1] = dialog->ui.cmbType1; types_[Register2] = dialog->ui.cmbType2; types_[Register3] = dialog->ui.cmbType3; types_[Register4] = dialog->ui.cmbType4; sizes_[Register1] = dialog->ui.cmbSize1; sizes_[Register2] = dialog->ui.cmbSize2; sizes_[Register3] = dialog->ui.cmbSize3; sizes_[Register4] = dialog->ui.cmbSize4; addresses_[Register1] = dialog->ui.txtBP1; addresses_[Register2] = dialog->ui.txtBP2; addresses_[Register3] = dialog->ui.txtBP3; addresses_[Register4] = dialog->ui.txtBP4; edb::v1::add_debug_event_handler(this); } /** * @brief HardwareBreakpoints::privateFini */ void HardwareBreakpoints::privateFini() { edb::v1::remove_debug_event_handler(this); } /** * @brief HardwareBreakpoints::menu * @param parent * @return */ QMenu *HardwareBreakpoints::menu(QWidget *parent) { Q_ASSERT(parent); if (!menu_) { menu_ = new QMenu(tr("Hardware BreakpointManager"), parent); menu_->addAction(tr("&Hardware Breakpoints"), this, SLOT(showMenu()), QKeySequence(tr("Ctrl+Shift+H"))); } return menu_; } /** * @brief HardwareBreakpoints::setupBreakpoints */ void HardwareBreakpoints::setupBreakpoints() { if (IProcess *process = edb::v1::debugger_core->process()) { if (!process->isPaused()) { QMessageBox::warning( nullptr, tr("Process Not Paused"), tr("Unable to update hardware breakpoints because the process does not appear to be currently paused. Please suspend the process.")); return; } const bool enabled = enabled_[Register1]->isChecked() || enabled_[Register2]->isChecked() || enabled_[Register3]->isChecked() || enabled_[Register4]->isChecked(); if (enabled) { edb::address_t addr[RegisterCount]; bool ok[RegisterCount]; // evaluate all the expressions for (int i = 0; i < RegisterCount; ++i) { ok[i] = enabled_[i]->isChecked() && edb::v1::eval_expression(addresses_[i]->text(), &addr[i]); } if (!ok[Register1] && !ok[Register2] && !ok[Register3] && !ok[Register4]) { QMessageBox::critical( nullptr, tr("Address Error"), tr("An address expression provided does not appear to be valid")); return; } for (int i = 0; i < RegisterCount; ++i) { if (ok[i]) { const BreakpointStatus status = validate_breakpoint({enabled_[i]->isChecked(), addr[i], types_[i]->currentIndex(), sizes_[i]->currentIndex()}); switch (status) { case AlignmentError: QMessageBox::critical( nullptr, tr("Address Alignment Error"), tr("Hardware read/write breakpoint address must be aligned to breakpoint size.")); return; case SizeError: QMessageBox::critical( nullptr, tr("BP Size Error"), tr("Hardware read/write breakpoints cannot be 8-bytes in a 32-bit debuggee.")); return; case Valid: break; } } } for (std::shared_ptr &thread : process->threads()) { State state; thread->getState(&state); for (int i = 0; i < RegisterCount; ++i) { if (ok[i]) { set_breakpoint_state( &state, i, {enabled_[i]->isChecked(), addr[i], types_[i]->currentIndex(), sizes_[i]->currentIndex()}); } } thread->setState(state); } } else { for (std::shared_ptr &thread : process->threads()) { State state; thread->getState(&state); state.setDebugRegister(7, 0); thread->setState(state); } } } edb::v1::update_ui(); } /** * @brief HardwareBreakpoints::showMenu */ void HardwareBreakpoints::showMenu() { if (dialog_->exec() == QDialog::Accepted) { setupBreakpoints(); } } /** * @brief HardwareBreakpoints::handleEvent * * this hooks the debug event handler so we can make the breakpoints able to be resumed * * @param event * @return */ edb::EventStatus HardwareBreakpoints::handleEvent(const std::shared_ptr &event) { if (event->stopped() && event->isTrap()) { if (IProcess *process = edb::v1::debugger_core->process()) { if (std::shared_ptr thread = process->currentThread()) { // check DR6 to see if it was a HW BP event // if so, set the resume flag State state; thread->getState(&state); if ((state.debugRegister(6) & 0x0f) != 0x00) { state.setFlags(state.flags() | (1 << 16)); thread->setState(state); } } } } // pass the event down the stack return edb::DEBUG_NEXT_HANDLER; } /** * @brief HardwareBreakpoints::stackContextMenu * @return */ QList HardwareBreakpoints::stackContextMenu() { auto menu = new QMenu(tr("Hardware Breakpoints")); auto rw1 = menu->addAction(tr("Hardware, On Read/Write #1"), this, SLOT(setAccess1())); auto rw2 = menu->addAction(tr("Hardware, On Read/Write #2"), this, SLOT(setAccess2())); auto rw3 = menu->addAction(tr("Hardware, On Read/Write #3"), this, SLOT(setAccess3())); auto rw4 = menu->addAction(tr("Hardware, On Read/Write #4"), this, SLOT(setAccess4())); auto wo1 = menu->addAction(tr("Hardware, On Write #1"), this, SLOT(setWrite1())); auto wo2 = menu->addAction(tr("Hardware, On Write #2"), this, SLOT(setWrite2())); auto wo3 = menu->addAction(tr("Hardware, On Write #3"), this, SLOT(setWrite3())); auto wo4 = menu->addAction(tr("Hardware, On Write #4"), this, SLOT(setWrite4())); rw1->setData(1); rw2->setData(1); rw3->setData(1); rw4->setData(1); wo1->setData(1); wo2->setData(1); wo3->setData(1); wo4->setData(1); QList ret; auto action = new QAction(tr("Hardware Breakpoints"), this); action->setMenu(menu); ret << action; return ret; } /** * @brief HardwareBreakpoints::dataContextMenu * @return */ QList HardwareBreakpoints::dataContextMenu() { auto menu = new QMenu(tr("Hardware Breakpoints")); auto rw1 = menu->addAction(tr("Hardware, On Read/Write #1"), this, SLOT(setAccess1())); auto rw2 = menu->addAction(tr("Hardware, On Read/Write #2"), this, SLOT(setAccess2())); auto rw3 = menu->addAction(tr("Hardware, On Read/Write #3"), this, SLOT(setAccess3())); auto rw4 = menu->addAction(tr("Hardware, On Read/Write #4"), this, SLOT(setAccess4())); auto wo1 = menu->addAction(tr("Hardware, On Write #1"), this, SLOT(setWrite1())); auto wo2 = menu->addAction(tr("Hardware, On Write #2"), this, SLOT(setWrite2())); auto wo3 = menu->addAction(tr("Hardware, On Write #3"), this, SLOT(setWrite3())); auto wo4 = menu->addAction(tr("Hardware, On Write #4"), this, SLOT(setWrite4())); rw1->setData(2); rw2->setData(2); rw3->setData(2); rw4->setData(2); wo1->setData(2); wo2->setData(2); wo3->setData(2); wo4->setData(2); QList ret; auto action = new QAction(tr("Hardware Breakpoints"), this); action->setMenu(menu); ret << action; return ret; } /** * @brief HardwareBreakpoints::cpuContextMenu * @return */ QList HardwareBreakpoints::cpuContextMenu() { auto menu = new QMenu(tr("Hardware Breakpoints")); auto ex1 = menu->addAction(tr("Hardware, On Execute #1"), this, SLOT(setExec1())); auto ex2 = menu->addAction(tr("Hardware, On Execute #2"), this, SLOT(setExec2())); auto ex3 = menu->addAction(tr("Hardware, On Execute #3"), this, SLOT(setExec3())); auto ex4 = menu->addAction(tr("Hardware, On Execute #4"), this, SLOT(setExec4())); auto rw1 = menu->addAction(tr("Hardware, On Read/Write #1"), this, SLOT(setAccess1())); auto rw2 = menu->addAction(tr("Hardware, On Read/Write #2"), this, SLOT(setAccess2())); auto rw3 = menu->addAction(tr("Hardware, On Read/Write #3"), this, SLOT(setAccess3())); auto rw4 = menu->addAction(tr("Hardware, On Read/Write #4"), this, SLOT(setAccess4())); auto wo1 = menu->addAction(tr("Hardware, On Write #1"), this, SLOT(setWrite1())); auto wo2 = menu->addAction(tr("Hardware, On Write #2"), this, SLOT(setWrite2())); auto wo3 = menu->addAction(tr("Hardware, On Write #3"), this, SLOT(setWrite3())); auto wo4 = menu->addAction(tr("Hardware, On Write #4"), this, SLOT(setWrite4())); ex1->setData(3); ex2->setData(3); ex3->setData(3); ex4->setData(3); rw1->setData(3); rw2->setData(3); rw3->setData(3); rw4->setData(3); wo1->setData(3); wo2->setData(3); wo3->setData(3); wo4->setData(3); QList ret; auto action = new QAction(tr("Hardware Breakpoints"), this); action->setMenu(menu); ret << action; return ret; } /** * @brief HardwareBreakpoints::setExecuteBP * @param index * @param inUse */ void HardwareBreakpoints::setExecuteBP(int index, bool inUse) { if (IProcess *process = edb::v1::debugger_core->process()) { if (!process->isPaused()) { QMessageBox::warning( nullptr, tr("Process Not Paused"), tr("Unable to update hardware breakpoints because the process does not appear to be currently paused. Please suspend the process.")); return; } if (inUse) { QMessageBox::StandardButton button = QMessageBox::question(nullptr, tr("Breakpoint Already In Use"), tr("This breakpoint is already being used. Do you want to replace it?"), QMessageBox::Yes | QMessageBox::Cancel); if (button != QMessageBox::Yes) { return; } } edb::address_t address = edb::v1::cpu_selected_address(); for (std::shared_ptr &thread : process->threads()) { State state; thread->getState(&state); set_breakpoint_state(&state, index, {true, address, 0, 0}); thread->setState(state); } } edb::v1::update_ui(); } /** * @brief HardwareBreakpoints::setWriteBP * @param index * @param inUse * @param address * @param size */ void HardwareBreakpoints::setWriteBP(int index, bool inUse, edb::address_t address, size_t size) { if (IProcess *process = edb::v1::debugger_core->process()) { if (!process->isPaused()) { QMessageBox::warning( nullptr, tr("Process Not Paused"), tr("Unable to update hardware breakpoints because the process does not appear to be currently paused. Please suspend the process.")); return; } if (inUse) { QMessageBox::StandardButton button = QMessageBox::question(nullptr, tr("Breakpoint Already In Use"), tr("This breakpoint is already being used. Do you want to replace it?"), QMessageBox::Yes | QMessageBox::Cancel); if (button != QMessageBox::Yes) { return; } } for (std::shared_ptr &thread : process->threads()) { State state; thread->getState(&state); switch (size) { case 1: set_breakpoint_state(&state, index, {true, address, 1, 0}); break; case 2: set_breakpoint_state(&state, index, {true, address, 1, 1}); break; case 4: set_breakpoint_state(&state, index, {true, address, 1, 2}); break; case 8: set_breakpoint_state(&state, index, {true, address, 1, 3}); break; default: QMessageBox::critical(nullptr, tr("Invalid Selection Size"), tr("Please select 1, 2, 4, or 8 bytes for this type of hardware breakpoint")); return; } thread->setState(state); } } edb::v1::update_ui(); } /** * @brief HardwareBreakpoints::setReadWriteBP * @param index * @param inUse * @param address * @param size */ void HardwareBreakpoints::setReadWriteBP(int index, bool inUse, edb::address_t address, size_t size) { if (IProcess *process = edb::v1::debugger_core->process()) { if (!process->isPaused()) { QMessageBox::warning( nullptr, tr("Process Not Paused"), tr("Unable to update hardware breakpoints because the process does not appear to be currently paused. Please suspend the process.")); return; } if (inUse) { QMessageBox::StandardButton button = QMessageBox::question( nullptr, tr("Breakpoint Already In Use"), tr("This breakpoint is already being used. Do you want to replace it?"), QMessageBox::Yes | QMessageBox::Cancel); if (button != QMessageBox::Yes) { return; } } for (std::shared_ptr &thread : process->threads()) { State state; thread->getState(&state); switch (size) { case 1: set_breakpoint_state(&state, index, {true, address, 2, 0}); break; case 2: set_breakpoint_state(&state, index, {true, address, 2, 1}); break; case 4: set_breakpoint_state(&state, index, {true, address, 2, 2}); break; case 8: set_breakpoint_state(&state, index, {true, address, 2, 3}); break; default: QMessageBox::critical(nullptr, tr("Invalid Selection Size"), tr("Please select 1, 2, 4, or 8 bytes for this type of hardward breakpoint")); return; } thread->setState(state); } } edb::v1::update_ui(); } /** * @brief HardwareBreakpoints::setExec * @param index */ void HardwareBreakpoints::setExec(int index) { if (auto a = qobject_cast(sender())) { switch (a->data().toLongLong()) { case 3: setExecuteBP(index, enabled_[index]->isChecked()); break; default: Q_ASSERT(0 && "Internal Error"); break; } } } /** * @brief HardwareBreakpoints::setWrite * @param index */ void HardwareBreakpoints::setWrite(int index) { if (auto a = qobject_cast(sender())) { switch (a->data().toLongLong()) { case 1: setStackWriteBP(index, enabled_[index]->isChecked()); break; case 2: setDataWriteBP(index, enabled_[index]->isChecked()); break; case 3: setCPUWriteBP(index, enabled_[index]->isChecked()); break; default: Q_ASSERT(0 && "Internal Error"); break; } } } /** * @brief HardwareBreakpoints::setAccess * @param index */ void HardwareBreakpoints::setAccess(int index) { if (auto a = qobject_cast(sender())) { switch (a->data().toLongLong()) { case 1: setStackReadWriteBP(index, enabled_[index]->isChecked()); break; case 2: setDataReadWriteBP(index, enabled_[index]->isChecked()); break; case 3: setCPUReadWriteBP(index, enabled_[index]->isChecked()); break; default: Q_ASSERT(0 && "Internal Error"); break; } } } /** * @brief HardwareBreakpoints::setDataReadWriteBP * @param index * @param inUse */ void HardwareBreakpoints::setDataReadWriteBP(int index, bool inUse) { const edb::address_t address = edb::v1::selected_data_address(); const size_t size = edb::v1::selected_data_size(); setReadWriteBP(index, inUse, address, size); } /** * @brief HardwareBreakpoints::setDataWriteBP * @param index * @param inUse */ void HardwareBreakpoints::setDataWriteBP(int index, bool inUse) { const edb::address_t address = edb::v1::selected_data_address(); const size_t size = edb::v1::selected_data_size(); setReadWriteBP(index, inUse, address, size); } /** * @brief HardwareBreakpoints::setStackReadWriteBP * @param index * @param inUse */ void HardwareBreakpoints::setStackReadWriteBP(int index, bool inUse) { const edb::address_t address = edb::v1::selected_stack_address(); const size_t size = edb::v1::selected_stack_size(); setReadWriteBP(index, inUse, address, size); } /** * @brief HardwareBreakpoints::setStackWriteBP * @param index * @param inUse */ void HardwareBreakpoints::setStackWriteBP(int index, bool inUse) { const edb::address_t address = edb::v1::selected_stack_address(); const size_t size = edb::v1::selected_stack_size(); setWriteBP(index, inUse, address, size); } /** * @brief HardwareBreakpoints::setCPUReadWriteBP * @param index * @param inUse */ void HardwareBreakpoints::setCPUReadWriteBP(int index, bool inUse) { const edb::address_t address = edb::v1::cpu_selected_address(); constexpr size_t Size = 1; setWriteBP(index, inUse, address, Size); } /** * @brief HardwareBreakpoints::setCPUWriteBP * @param index * @param inUse */ void HardwareBreakpoints::setCPUWriteBP(int index, bool inUse) { const edb::address_t address = edb::v1::cpu_selected_address(); constexpr size_t Size = 1; setWriteBP(index, inUse, address, Size); } /** * @brief HardwareBreakpoints::setExec1 */ void HardwareBreakpoints::setExec1() { setExec(Register1); } /** * @brief HardwareBreakpoints::setExec2 */ void HardwareBreakpoints::setExec2() { setExec(Register2); } /** * @brief HardwareBreakpoints::setExec3 */ void HardwareBreakpoints::setExec3() { setExec(Register3); } /** * @brief HardwareBreakpoints::setExec4 */ void HardwareBreakpoints::setExec4() { setExec(Register4); } /** * @brief HardwareBreakpoints::setAccess1 */ void HardwareBreakpoints::setAccess1() { setAccess(Register1); } /** * @brief HardwareBreakpoints::setAccess2 */ void HardwareBreakpoints::setAccess2() { setAccess(Register2); } /** * @brief HardwareBreakpoints::setAccess3 */ void HardwareBreakpoints::setAccess3() { setAccess(Register3); } /** * @brief HardwareBreakpoints::setAccess4 */ void HardwareBreakpoints::setAccess4() { setAccess(Register4); } /** * @brief HardwareBreakpoints::setWrite1 */ void HardwareBreakpoints::setWrite1() { setWrite(Register1); } /** * @brief HardwareBreakpoints::setWrite2 */ void HardwareBreakpoints::setWrite2() { setWrite(Register2); } /** * @brief HardwareBreakpoints::setWrite3 */ void HardwareBreakpoints::setWrite3() { setWrite(Register3); } /** * @brief HardwareBreakpoints::setWrite4 */ void HardwareBreakpoints::setWrite4() { setWrite(Register4); } } edb-debugger/plugins/HardwareBreakpoints/DialogHwBreakpoints.h0000644000175000017500000000257413765535463024432 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DIALOG_HW_BREAKPOINTS_H_20080228_ #define DIALOG_HW_BREAKPOINTS_H_20080228_ #include "ui_DialogHwBreakpoints.h" #include namespace HardwareBreakpointsPlugin { class DialogHwBreakpoints : public QDialog { Q_OBJECT private: friend class HardwareBreakpoints; public: explicit DialogHwBreakpoints(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~DialogHwBreakpoints() override = default; private: void showEvent(QShowEvent *event) override; private Q_SLOTS: void type1IndexChanged(int index); void type2IndexChanged(int index); void type3IndexChanged(int index); void type4IndexChanged(int index); private: Ui::DialogHwBreakpoints ui; }; } #endif edb-debugger/plugins/HardwareBreakpoints/CMakeLists.txt0000644000175000017500000000165013765535463023113 0ustar eteraneterancmake_minimum_required (VERSION 3.1) include("GNUInstallDirs") set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(PluginName "HardwareBreakpoints") find_package(Qt5 5.0.0 REQUIRED Widgets) add_library(${PluginName} SHARED DialogHwBreakpoints.cpp DialogHwBreakpoints.h DialogHwBreakpoints.ui HardwareBreakpoints.cpp HardwareBreakpoints.h libHardwareBreakpoints.cpp libHardwareBreakpoints.h ) target_link_libraries(${PluginName} Qt5::Widgets edb) install (TARGETS ${PluginName} DESTINATION ${CMAKE_INSTALL_LIBDIR}/edb) set_property(TARGET ${PluginName} PROPERTY CXX_EXTENSIONS OFF) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD 14) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD_REQUIRED ON) set_property(TARGET ${PluginName} PROPERTY LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) set_property(TARGET ${PluginName} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) edb-debugger/plugins/HardwareBreakpoints/HardwareBreakpoints.h0000644000175000017500000000533513765535463024467 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef HARDWARE_BREAKPOINTS_H_20080228_ #define HARDWARE_BREAKPOINTS_H_20080228_ #include "IDebugEventHandler.h" #include "IPlugin.h" #include "libHardwareBreakpoints.h" class QDialog; class QMenu; class State; class QCheckBox; class QComboBox; class QLineEdit; namespace HardwareBreakpointsPlugin { class HardwareBreakpoints : public QObject, public IPlugin, public IDebugEventHandler { Q_OBJECT Q_INTERFACES(IPlugin) Q_PLUGIN_METADATA(IID "edb.IPlugin/1.0") Q_CLASSINFO("author", "Evan Teran") Q_CLASSINFO("url", "http://www.codef00.com") public: HardwareBreakpoints(QObject *parent = nullptr); protected: void privateInit() override; void privateFini() override; public: QMenu *menu(QWidget *parent = nullptr) override; edb::EventStatus handleEvent(const std::shared_ptr &event) override; QList cpuContextMenu() override; QList stackContextMenu() override; QList dataContextMenu() override; public Q_SLOTS: void showMenu(); private: void setupBreakpoints(); void setExecuteBP(int index, bool inUse); void setReadWriteBP(int index, bool inUse, edb::address_t address, size_t size); void setWriteBP(int index, bool inUse, edb::address_t address, size_t size); void setDataReadWriteBP(int index, bool inUse); void setDataWriteBP(int index, bool inUse); void setStackReadWriteBP(int index, bool inUse); void setStackWriteBP(int index, bool inUse); void setCPUReadWriteBP(int index, bool inUse); void setCPUWriteBP(int index, bool inUse); void setExec(int index); void setWrite(int index); void setAccess(int index); private Q_SLOTS: void setWrite1(); void setWrite2(); void setWrite3(); void setWrite4(); void setAccess1(); void setAccess2(); void setAccess3(); void setAccess4(); void setExec1(); void setExec2(); void setExec3(); void setExec4(); private: QMenu *menu_ = nullptr; QPointer dialog_ = nullptr; QLineEdit *addresses_[4] = {}; QCheckBox *enabled_[4] = {}; QComboBox *types_[4] = {}; QComboBox *sizes_[4] = {}; }; } #endif edb-debugger/plugins/HardwareBreakpoints/libHardwareBreakpoints.h0000644000175000017500000000256613765535463025161 0ustar eteraneteran/* Copyright (C) 2015 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef LIB_HARDWARE_BREAKPOINTS_H_20191119_ #define LIB_HARDWARE_BREAKPOINTS_H_20191119_ #include "edb.h" namespace HardwareBreakpointsPlugin { constexpr int Register1 = 0; constexpr int Register2 = 1; constexpr int Register3 = 2; constexpr int Register4 = 3; constexpr int RegisterCount = 4; struct BreakpointState { bool enabled; edb::address_t addr; int type; int size; }; enum BreakpointStatus { Valid, AlignmentError, SizeError }; BreakpointState breakpoint_state(const State *state, int num); void set_breakpoint_state(State *state, int num, const BreakpointState &bp_state); BreakpointStatus validate_breakpoint(const BreakpointState &bp_state); } #endif edb-debugger/plugins/HardwareBreakpoints/libHardwareBreakpoints.cpp0000644000175000017500000001027113765535463025504 0ustar eteraneteran/* Copyright (C) 2015 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "libHardwareBreakpoints.h" #include "State.h" namespace HardwareBreakpointsPlugin { /** * @brief validate_breakpoint * @param bp_state * @return */ BreakpointStatus validate_breakpoint(const BreakpointState &bp_state) { if (bp_state.enabled) { switch (bp_state.type) { case 2: case 1: { const edb::address_t address_mask = (1u << bp_state.size) - 1; if ((bp_state.addr & address_mask) != 0) { return AlignmentError; } } break; default: break; } if (edb::v1::debuggeeIs32Bit()) { if (bp_state.size == 3) { return SizeError; } } } return Valid; } /** * @brief breakpoint_state * @param state * @param num * @return */ BreakpointState breakpoint_state(const State *state, int num) { Q_ASSERT(num < RegisterCount); const int N1 = 16 + (num * 4); const int N2 = 18 + (num * 4); BreakpointState bp_state; // enabled switch (num) { case Register1: bp_state.enabled = (state->debugRegister(7) & 0x00000001) != 0; break; case Register2: bp_state.enabled = (state->debugRegister(7) & 0x00000004) != 0; break; case Register3: bp_state.enabled = (state->debugRegister(7) & 0x00000010) != 0; break; case Register4: bp_state.enabled = (state->debugRegister(7) & 0x00000040) != 0; break; } // address bp_state.addr = state->debugRegister(num); // type switch ((state->debugRegister(7) >> N1) & 0x03) { case 0x00: bp_state.type = 0; break; case 0x01: bp_state.type = 1; break; case 0x03: bp_state.type = 2; break; default: bp_state.type = -1; Q_ASSERT(0 && "Internal Error"); } // size switch ((state->debugRegister(7) >> N2) & 0x03) { case 0x00: bp_state.size = 0; break; case 0x01: bp_state.size = 1; break; case 0x03: bp_state.size = 2; break; case 0x02: bp_state.size = 3; } return bp_state; } /** * @brief set_breakpoint_state * @param state * @param num * @param bp_state */ void set_breakpoint_state(State *state, int num, const BreakpointState &bp_state) { const int N1 = 16 + (num * 4); const int N2 = 18 + (num * 4); // default to disabled state->setDebugRegister(7, (state->debugRegister(7) & ~(0x01ULL << (num * 2)))); if (bp_state.enabled) { // set the address state->setDebugRegister(num, bp_state.addr); // enable this breakpoint state->setDebugRegister(7, state->debugRegister(7) | (0x01ULL << (num * 2))); // setup the type switch (bp_state.type) { case 2: // read/write state->setDebugRegister(7, (state->debugRegister(7) & ~(0x03ULL << N1)) | (0x03ULL << N1)); break; case 1: // write state->setDebugRegister(7, (state->debugRegister(7) & ~(0x03ULL << N1)) | (0x01ULL << N1)); break; case 0: // execute state->setDebugRegister(7, (state->debugRegister(7) & ~(0x03ULL << N1)) | (0x00ULL << N1)); break; } if (bp_state.type != 0) { // setup the size switch (bp_state.size) { case 3: // 8 bytes Q_ASSERT(edb::v1::debuggeeIs64Bit()); state->setDebugRegister(7, (state->debugRegister(7) & ~(0x03ULL << N2)) | (0x02ULL << N2)); break; case 2: // 4 bytes state->setDebugRegister(7, (state->debugRegister(7) & ~(0x03ULL << N2)) | (0x03ULL << N2)); break; case 1: // 2 bytes state->setDebugRegister(7, (state->debugRegister(7) & ~(0x03ULL << N2)) | (0x01ULL << N2)); break; case 0: // 1 byte state->setDebugRegister(7, (state->debugRegister(7) & ~(0x03ULL << N2)) | (0x00ULL << N2)); break; } } else { state->setDebugRegister(7, (state->debugRegister(7) & ~(0x03ULL << N2))); } } } } edb-debugger/plugins/HardwareBreakpoints/DialogHwBreakpoints.ui0000644000175000017500000002066113765535463024615 0ustar eteraneteran Evan Teran HardwareBreakpointsPlugin::DialogHwBreakpoints 0 0 461 222 Hardware Breakpoints Address Type Size BP 1 Monospace Execute Write Read/Write false 1 Byte 2 Bytes 4 Bytes 8 Bytes Enabled BP 2 Monospace Execute Write Read/Write false 1 Byte 2 Bytes 4 Bytes 8 Bytes Enabled BP 3 Monospace Execute Write Read/Write false 1 Byte 2 Bytes 4 Bytes 8 Bytes Enabled BP 4 Monospace Execute Write Read/Write false 1 Byte 2 Bytes 4 Bytes 8 Bytes Enabled Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() HardwareBreakpointsPlugin::DialogHwBreakpoints accept() 262 199 89 191 buttonBox rejected() HardwareBreakpointsPlugin::DialogHwBreakpoints reject() 401 201 18 189 edb-debugger/plugins/CMakeLists.txt0000644000175000017500000000151413765535463017153 0ustar eteraneterancmake_minimum_required (VERSION 3.1) add_definitions(-DQT_PLUGIN) add_subdirectory(DebuggerCore) add_subdirectory(Analyzer) add_subdirectory(Assembler) add_subdirectory(BinaryInfo) add_subdirectory(Bookmarks) add_subdirectory(BreakpointManager) add_subdirectory(CheckVersion) add_subdirectory(OpcodeSearcher) add_subdirectory(ProcessProperties) add_subdirectory(ROPTool) add_subdirectory(References) add_subdirectory(SymbolViewer) add_subdirectory(Backtrace) add_subdirectory(DumpState) add_subdirectory(FunctionFinder) add_subdirectory(DebuggerErrorConsole) add_subdirectory(BinarySearcher) add_subdirectory(InstructionInspector) add_subdirectory(FasLoader) add_subdirectory(ODbgRegisterView) if(TARGET_ARCH_FAMILY_X86) add_subdirectory(HardwareBreakpoints) endif() if(TARGET_PLATFORM_LINUX) add_subdirectory(HeapAnalyzer) endif() edb-debugger/plugins/BinarySearcher/0000755000175000017500000000000013765535463017313 5ustar eteraneteranedb-debugger/plugins/BinarySearcher/DialogResults.cpp0000644000175000017500000000243413765535463022603 0ustar eteraneteran #include "DialogResults.h" #include "edb.h" namespace BinarySearcherPlugin { /** * @brief DialogResults::DialogResults * @param parent * @param f */ DialogResults::DialogResults(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f) { ui.setupUi(this); } /** * follows the found item in the appropriate view * * @brief DialogResults::on_listWidget_itemDoubleClicked * @param item */ void DialogResults::on_listWidget_itemDoubleClicked(QListWidgetItem *item) { const edb::address_t addr = item->data(Qt::UserRole).toULongLong(); switch (static_cast(item->data(Qt::UserRole + 1).toInt())) { case RegionType::Code: edb::v1::jump_to_address(addr); break; case RegionType::Stack: edb::v1::dump_stack(addr, true); break; case RegionType::Data: edb::v1::dump_data(addr); break; } } /** * @brief DialogResults::addResult * @param address */ void DialogResults::addResult(RegionType region, edb::address_t address) { auto item = new QListWidgetItem(edb::v1::format_pointer(address)); item->setData(Qt::UserRole, address.toQVariant()); item->setData(Qt::UserRole + 1, static_cast(region)); ui.listWidget->addItem(item); } /** * @brief DialogResults::resultCount * @return */ int DialogResults::resultCount() const { return ui.listWidget->count(); } } edb-debugger/plugins/BinarySearcher/BinarySearcher.h0000644000175000017500000000256213765535463022372 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef BINARY_SEARCHER_H_20060430_ #define BINARY_SEARCHER_H_20060430_ #include "IPlugin.h" class QMenu; class QDialog; namespace BinarySearcherPlugin { class BinarySearcher : public QObject, public IPlugin { Q_OBJECT Q_INTERFACES(IPlugin) Q_PLUGIN_METADATA(IID "edb.IPlugin/1.0") Q_CLASSINFO("author", "Evan Teran") Q_CLASSINFO("url", "http://www.codef00.com") public: explicit BinarySearcher(QObject *parent = nullptr); ~BinarySearcher() override = default; public: QMenu *menu(QWidget *parent = nullptr) override; QList stackContextMenu() override; public Q_SLOTS: void showMenu(); void mnuStackFindAscii(); private: QMenu *menu_ = nullptr; }; } #endif edb-debugger/plugins/BinarySearcher/DialogAsciiString.ui0000644000175000017500000000450013765535463023210 0ustar eteraneteran BinarySearcherPlugin::DialogAsciiString 0 0 391 142 Find ASCII String ASCII String true false Case Sensitive true QDialogButtonBox::Close txtAscii chkCaseSensitive buttonBox accepted() BinarySearcherPlugin::DialogAsciiString accept() 174 83 285 56 buttonBox rejected() BinarySearcherPlugin::DialogAsciiString reject() 133 88 326 99 edb-debugger/plugins/BinarySearcher/DialogAsciiString.cpp0000644000175000017500000000716013765535463023362 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "DialogAsciiString.h" #include "DialogResults.h" #include "IDebugger.h" #include "IProcess.h" #include "IRegion.h" #include "IThread.h" #include "MemoryRegions.h" #include "State.h" #include "edb.h" #include "util/Math.h" #include #include #include #include #include #include namespace BinarySearcherPlugin { /** * @brief DialogAsciiString::DialogAsciiString * @param parent * @param f */ DialogAsciiString::DialogAsciiString(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f) { ui.setupUi(this); ui.progressBar->setValue(0); buttonFind_ = new QPushButton(QIcon::fromTheme("edit-find"), tr("Find")); connect(buttonFind_, &QPushButton::clicked, this, [this]() { buttonFind_->setEnabled(false); ui.progressBar->setValue(0); doFind(); ui.progressBar->setValue(100); buttonFind_->setEnabled(true); }); ui.buttonBox->addButton(buttonFind_, QDialogButtonBox::ActionRole); } /** * @brief DialogAsciiString::doFind * * find *stack aligned pointers* to exact string matches */ void DialogAsciiString::doFind() { const QByteArray b = ui.txtAscii->text().toLatin1(); auto results = new DialogResults(this); const auto sz = static_cast(b.size()); if (sz != 0) { edb::v1::memory_regions().sync(); if (IProcess *process = edb::v1::debugger_core->process()) { if (std::shared_ptr thread = process->currentThread()) { State state; thread->getState(&state); edb::address_t stack_ptr = state.stackPointer(); if (std::shared_ptr region = edb::v1::memory_regions().findRegion(stack_ptr)) { edb::address_t count = (region->end() - stack_ptr) / edb::v1::pointer_size(); stack_ptr = region->start(); try { std::vector chars(sz); int i = 0; while (stack_ptr < region->end()) { // get the value from the stack edb::address_t stack_address; if (process->readBytes(stack_ptr, &stack_address, edb::v1::pointer_size())) { if (process->readBytes(stack_address, &chars[0], chars.size())) { if (std::memcmp(&chars[0], b.constData(), chars.size()) == 0) { results->addResult(DialogResults::RegionType::Stack, stack_ptr); } } } ui.progressBar->setValue(util::percentage(i++, count)); stack_ptr += edb::v1::pointer_size(); } } catch (const std::bad_alloc &) { QMessageBox::critical( nullptr, tr("Memroy Allocation Error"), tr("Unable to satisfy memory allocation request for search string.")); } } } } } if (results->resultCount() == 0) { QMessageBox::information(nullptr, tr("No Results"), tr("No Results were found!")); delete results; } else { results->show(); } } /** * @brief DialogAsciiString::showEvent * @param event */ void DialogAsciiString::showEvent(QShowEvent *event) { Q_UNUSED(event) ui.txtAscii->setFocus(); } } edb-debugger/plugins/BinarySearcher/CMakeLists.txt0000644000175000017500000000173113765535463022055 0ustar eteraneterancmake_minimum_required (VERSION 3.1) include("GNUInstallDirs") set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(PluginName "BinarySearcher") find_package(Qt5 5.0.0 REQUIRED Widgets) add_library(${PluginName} SHARED BinarySearcher.cpp BinarySearcher.h DialogAsciiString.cpp DialogAsciiString.h DialogAsciiString.ui DialogBinaryString.cpp DialogBinaryString.h DialogBinaryString.ui DialogResults.cpp DialogResults.h DialogResults.ui ) target_link_libraries(${PluginName} Qt5::Widgets edb) install (TARGETS ${PluginName} DESTINATION ${CMAKE_INSTALL_LIBDIR}/edb) set_property(TARGET ${PluginName} PROPERTY CXX_EXTENSIONS OFF) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD 14) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD_REQUIRED ON) set_property(TARGET ${PluginName} PROPERTY LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) set_property(TARGET ${PluginName} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) edb-debugger/plugins/BinarySearcher/DialogAsciiString.h0000644000175000017500000000236313765535463023027 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DIALOG_ASCII_STRING_H_20082201_ #define DIALOG_ASCII_STRING_H_20082201_ #include "ui_DialogAsciiString.h" #include class QListWidgetItem; namespace BinarySearcherPlugin { class DialogAsciiString : public QDialog { Q_OBJECT public: explicit DialogAsciiString(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~DialogAsciiString() override = default; protected: void showEvent(QShowEvent *event) override; private: void doFind(); private: Ui::DialogAsciiString ui; QPushButton *buttonFind_ = nullptr; }; } #endif edb-debugger/plugins/BinarySearcher/DialogResults.h0000644000175000017500000000250413765535463022246 0ustar eteraneteran/* Copyright (C) 2006 - 2019 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DIALOG_RESULTS_H_20190403_ #define DIALOG_RESULTS_H_20190403_ #include "edb.h" #include "ui_DialogResults.h" #include class QListWidgetItem; namespace BinarySearcherPlugin { class DialogResults : public QDialog { Q_OBJECT public: enum class RegionType { Code, Stack, Data }; public: explicit DialogResults(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~DialogResults() override = default; public: void addResult(RegionType region, edb::address_t address); int resultCount() const; public Q_SLOTS: void on_listWidget_itemDoubleClicked(QListWidgetItem *); private: Ui::DialogResults ui; }; } #endif edb-debugger/plugins/BinarySearcher/DialogResults.ui0000644000175000017500000000336513765535463022442 0ustar eteraneteran BinarySearcherPlugin::DialogResults 0 0 581 254 Search Results Monospace true Qt::Horizontal QDialogButtonBox::Close buttonBox accepted() BinarySearcherPlugin::DialogResults accept() 224 317 157 274 buttonBox rejected() BinarySearcherPlugin::DialogResults reject() 292 323 286 274 edb-debugger/plugins/BinarySearcher/BinarySearcher.cpp0000644000175000017500000000372513765535463022727 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "BinarySearcher.h" #include "DialogAsciiString.h" #include "DialogBinaryString.h" #include "edb.h" #include namespace BinarySearcherPlugin { /** * @brief BinarySearcher::BinarySearcher * @param parent */ BinarySearcher::BinarySearcher(QObject *parent) : QObject(parent) { } /** * @brief BinarySearcher::menu * @param parent * @return */ QMenu *BinarySearcher::menu(QWidget *parent) { Q_ASSERT(parent); if (!menu_) { menu_ = new QMenu(tr("BinarySearcher"), parent); menu_->addAction(tr("&Binary String Search"), this, SLOT(showMenu()), QKeySequence(tr("Ctrl+F"))); } return menu_; } /** * @brief BinarySearcher::stackContextMenu * @return */ QList BinarySearcher::stackContextMenu() { QList ret; auto action_find = new QAction(tr("&Find ASCII String"), this); connect(action_find, &QAction::triggered, this, &BinarySearcher::mnuStackFindAscii); ret << action_find; return ret; } /** * @brief BinarySearcher::showMenu */ void BinarySearcher::showMenu() { static auto dialog = new DialogBinaryString(edb::v1::debugger_ui); dialog->show(); } /** * @brief BinarySearcher::mnuStackFindAscii */ void BinarySearcher::mnuStackFindAscii() { static auto dialog = new DialogAsciiString(edb::v1::debugger_ui); dialog->show(); } } edb-debugger/plugins/BinarySearcher/DialogBinaryString.h0000644000175000017500000000230113765535463023213 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DIALOG_BINARY_STRING_H_20061101_ #define DIALOG_BINARY_STRING_H_20061101_ #include "ui_DialogBinaryString.h" #include class QListWidgetItem; namespace BinarySearcherPlugin { class DialogBinaryString : public QDialog { Q_OBJECT public: explicit DialogBinaryString(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~DialogBinaryString() override = default; private: void doFind(); private: Ui::DialogBinaryString ui; QPushButton *buttonFind_ = nullptr; }; } #endif edb-debugger/plugins/BinarySearcher/DialogBinaryString.cpp0000644000175000017500000000670613765535463023563 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "DialogBinaryString.h" #include "DialogResults.h" #include "IDebugger.h" #include "IRegion.h" #include "MemoryRegions.h" #include "edb.h" #include "util/Math.h" #include #include #include #include #include namespace BinarySearcherPlugin { /** * @brief DialogBinaryString::DialogBinaryString * @param parent * @param f */ DialogBinaryString::DialogBinaryString(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f) { ui.setupUi(this); ui.progressBar->setValue(0); // NOTE(eteran): address issue #574 ui.binaryString->setShowKeepSize(false); buttonFind_ = new QPushButton(QIcon::fromTheme("edit-find"), tr("Find")); connect(buttonFind_, &QPushButton::clicked, this, [this]() { buttonFind_->setEnabled(false); ui.progressBar->setValue(0); doFind(); ui.progressBar->setValue(100); buttonFind_->setEnabled(true); }); ui.buttonBox->addButton(buttonFind_, QDialogButtonBox::ActionRole); } /** * @brief DialogBinaryString::doFind */ void DialogBinaryString::doFind() { const QByteArray b = ui.binaryString->value(); auto results = new DialogResults(this); const int sz = b.size(); if (sz != 0) { edb::v1::memory_regions().sync(); const QList> regions = edb::v1::memory_regions().regions(); const size_t page_size = edb::v1::debugger_core->pageSize(); int i = 0; for (const std::shared_ptr ®ion : regions) { const size_t region_size = region->size(); // a short circut for speading things up if (ui.chkSkipNoAccess->isChecked() && !region->accessible()) { ui.progressBar->setValue(util::percentage(++i, regions.size())); continue; } const size_t page_count = region_size / page_size; const QVector pages = edb::v1::read_pages(region->start(), page_count); if (!pages.isEmpty()) { const uint8_t *p = &pages[0]; const uint8_t *const pages_end = &pages[0] + region_size - sz; while (p < pages_end) { // compare values.. if (std::memcmp(p, b.constData(), sz) == 0) { const edb::address_t addr = p - &pages[0] + region->start(); const edb::address_t align = 1 << (ui.cmbAlignment->currentIndex() + 1); if (!ui.chkAlignment->isChecked() || (addr % align) == 0) { results->addResult(DialogResults::RegionType::Data, addr); } } // update progress bar every 64KB if ((uint64_t(p) & 0xFFFF) == 0) { ui.progressBar->setValue(util::percentage(i, regions.size(), p - &pages[0], region_size)); } ++p; } } ++i; } if (results->resultCount() == 0) { QMessageBox::information(nullptr, tr("No Results"), tr("No Results were found!")); delete results; } else { results->show(); } } } } edb-debugger/plugins/BinarySearcher/DialogBinaryString.ui0000644000175000017500000000676313765535463023421 0ustar eteraneteran Evan Teran BinarySearcherPlugin::DialogBinaryString 0 0 480 185 Binary String 0 0 Skip Regions With No Access Rights false Case Sensitive true Show Results With This Address Alignment 1 2 Byte Alignment 4 Byte Alignment 8 Byte Alignment QDialogButtonBox::Close BinaryString QFrame
BinaryString.h
1
chkSkipNoAccess chkCaseSensitive chkAlignment cmbAlignment buttonBox accepted() BinarySearcherPlugin::DialogBinaryString accept() 417 130 458 153 buttonBox rejected() BinarySearcherPlugin::DialogBinaryString reject() 443 126 329 150
edb-debugger/plugins/BreakpointManager/0000755000175000017500000000000013765535463020003 5ustar eteraneteranedb-debugger/plugins/BreakpointManager/DialogBreakpoints.cpp0000644000175000017500000002251213765535463024112 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "DialogBreakpoints.h" #include "Expression.h" #include "IBreakpoint.h" #include "IDebugger.h" #include "MemoryRegions.h" #include "edb.h" #include #include #include #include #include #include #include #include #include namespace BreakpointManagerPlugin { /** * @brief DialogBreakpoints::DialogBreakpoints * @param parent * @param f */ DialogBreakpoints::DialogBreakpoints(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f) { ui.setupUi(this); ui.tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); } /** * @brief DialogBreakpoints::showEvent */ void DialogBreakpoints::showEvent(QShowEvent *) { connect(edb::v1::disassembly_widget(), SIGNAL(signalUpdated()), this, SLOT(updateList())); updateList(); } /** * @brief DialogBreakpoints::hideEvent */ void DialogBreakpoints::hideEvent(QHideEvent *) { disconnect(edb::v1::disassembly_widget(), SIGNAL(signalUpdated()), this, SLOT(updateList())); } /** * @brief DialogBreakpoints::updateList */ void DialogBreakpoints::updateList() { ui.tableWidget->setSortingEnabled(false); ui.tableWidget->setRowCount(0); const IDebugger::BreakpointList breakpoint_state = edb::v1::debugger_core->backupBreakpoints(); for (const std::shared_ptr &bp : breakpoint_state) { //Skip if it's an internal bp; we don't want to insert a row for it. if (bp->internal()) { continue; } const int row = ui.tableWidget->rowCount(); ui.tableWidget->insertRow(row); const edb::address_t address = bp->address(); const QString condition = bp->condition; const bool onetime = bp->oneTime(); const QString symname = edb::v1::find_function_symbol(address, QString(), nullptr); const QString bytes = edb::v1::format_bytes(bp->originalBytes(), bp->size()); auto item = new QTableWidgetItem(edb::v1::format_pointer(address)); item->setData(Qt::UserRole, address.toQVariant()); ui.tableWidget->setItem(row, 0, item); ui.tableWidget->setItem(row, 1, new QTableWidgetItem(condition)); ui.tableWidget->setItem(row, 2, new QTableWidgetItem(bytes)); ui.tableWidget->setItem(row, 3, new QTableWidgetItem(onetime ? tr("One Time") : tr("Standard"))); ui.tableWidget->setItem(row, 4, new QTableWidgetItem(symname)); } ui.tableWidget->setSortingEnabled(true); } /** * @brief DialogBreakpoints::on_btnAdd_clicked */ void DialogBreakpoints::on_btnAdd_clicked() { bool ok; QString text = QInputDialog::getText(this, tr("Add Breakpoint"), tr("Address:"), QLineEdit::Normal, QString(), &ok); if (ok && !text.isEmpty()) { Expression expr(text, edb::v1::get_variable, edb::v1::get_value); const Result address = expr.evaluate(); if (address) { edb::v1::create_breakpoint(*address); updateList(); } else { QMessageBox::critical(this, tr("Error In Address Expression!"), address.error().what()); } } } /** * @brief DialogBreakpoints::on_btnCondition_clicked */ void DialogBreakpoints::on_btnCondition_clicked() { QList sel = ui.tableWidget->selectedItems(); if (!sel.empty()) { QTableWidgetItem *const item = sel[0]; bool ok; const edb::address_t address = item->data(Qt::UserRole).toULongLong(); const QString condition = edb::v1::get_breakpoint_condition(address); const QString text = QInputDialog::getText(this, tr("Set Breakpoint Condition"), tr("Expression:"), QLineEdit::Normal, condition, &ok); if (ok) { edb::v1::set_breakpoint_condition(address, text); updateList(); } } } /** * @brief DialogBreakpoints::on_btnRemove_clicked */ void DialogBreakpoints::on_btnRemove_clicked() { QList sel = ui.tableWidget->selectedItems(); if (!sel.empty()) { QTableWidgetItem *const item = sel[0]; const edb::address_t address = item->data(Qt::UserRole).toULongLong(); edb::v1::remove_breakpoint(address); } updateList(); } /** * @brief DialogBreakpoints::on_tableWidget_cellDoubleClicked * @param row * @param col */ void DialogBreakpoints::on_tableWidget_cellDoubleClicked(int row, int col) { switch (col) { case 0: // address if (QTableWidgetItem *const address_item = ui.tableWidget->item(row, 0)) { const edb::address_t address = address_item->data(Qt::UserRole).toULongLong(); edb::v1::jump_to_address(address); } break; case 1: // condition if (QTableWidgetItem *const address_item = ui.tableWidget->item(row, 0)) { bool ok; const edb::address_t address = address_item->data(Qt::UserRole).toULongLong(); const QString condition = edb::v1::get_breakpoint_condition(address); const QString text = QInputDialog::getText(this, tr("Set Breakpoint Condition"), tr("Expression:"), QLineEdit::Normal, condition, &ok); if (ok) { edb::v1::set_breakpoint_condition(address, text); updateList(); } } break; } } /** * @brief DialogBreakpoints::on_btnImport_clicked * * Opens a file selection window to choose a file with newline-separated, * hex address breakpoints. */ void DialogBreakpoints::on_btnImport_clicked() { // Let the user choose the file; get the file name. QString home_directory = QDir::homePath(); QString file_name = QFileDialog::getOpenFileName(this, tr("Breakpoint Import File"), home_directory, nullptr); if (file_name.isEmpty()) { return; } // Open the file; fail if error or it doesn't exist. QFile file(file_name); if (!file.open(QIODevice::ReadOnly)) { QMessageBox::critical(this, tr("Error Opening File"), tr("Unable to open breakpoint file: %1").arg(file_name)); return; } // Keep a list of any lines in the file that don't make valid breakpoints. QStringList errors; // Iterate through each line; attempt to make a breakpoint for each line. // Addreses should be prefixed with 0x, i.e. a hex number. // Count each breakpoint successfully made. int count = 0; Q_FOREVER { // Get the address QString line = file.readLine().trimmed(); if (line.isEmpty()) { break; } bool ok; int base = 16; edb::address_t address = line.toULong(&ok, base); // Skip if there's an issue. if (!ok) { errors.append(line); continue; } // If there's an issue with the line or address isn't in any region, // add to error list and skip. edb::v1::memory_regions().sync(); std::shared_ptr p = edb::v1::memory_regions().findRegion(address); if (!p) { errors.append(line); continue; } // If the bp already exists, skip. No error. if (edb::v1::debugger_core->findBreakpoint(address)) { continue; } // If the line was converted to an address, try to create the breakpoint. // Access debugger_core directly to avoid many possible error windows by edb::v1::create_breakpoint() if (const std::shared_ptr bp = edb::v1::debugger_core->addBreakpoint(address)) { count++; } else { errors.append(line); } } // Report any errors to the user if (errors.size() > 0) { QMessageBox::warning(this, tr("Invalid Breakpoints"), tr("The following breakpoints were not made:\n%1").arg(errors.join(""))); } // Report breakpoints successfully made QMessageBox::information(this, tr("Breakpoint Import"), tr("Imported %1 breakpoints.").arg(count)); updateList(); } /** * @brief DialogBreakpoints::on_btnExport_clicked * * Opens a file selection window to choose a file to save newline-separated, * hex address breakpoints. */ void DialogBreakpoints::on_btnExport_clicked() { //Get the current list of breakpoints const IDebugger::BreakpointList breakpoint_state = edb::v1::debugger_core->backupBreakpoints(); //Create a list for addresses to be exported at the end QList export_list; //Go through our breakpoints and add for export if not one-time and not internal. for (const std::shared_ptr &bp : breakpoint_state) { if (!bp->oneTime() && !bp->internal()) { export_list.append(bp->address()); } } //If there are no breakpoints, fail if (export_list.isEmpty()) { QMessageBox::critical(this, tr("No Breakpoints"), tr("There are no breakpoints to export.")); return; } //Now ask the user for a file, open it, and write each address to it. QString filename = QFileDialog::getSaveFileName(this, tr("Breakpoint Export File"), QDir::homePath()); if (filename.isEmpty()) { return; } QFile file(filename); if (!file.open(QIODevice::WriteOnly)) { return; } for (edb::address_t address : export_list) { QString string_address = "0x" + QString::number(address, 16) + "\n"; file.write(string_address.toLatin1()); } QMessageBox::information(this, tr("Breakpoint Export"), tr("Exported %1 breakpoints").arg(export_list.size())); } } edb-debugger/plugins/BreakpointManager/CMakeLists.txt0000644000175000017500000000154613765535463022551 0ustar eteraneterancmake_minimum_required (VERSION 3.1) include("GNUInstallDirs") set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(PluginName "BreakpointManager") find_package(Qt5 5.0.0 REQUIRED Widgets) add_library(${PluginName} SHARED BreakpointManager.cpp BreakpointManager.h DialogBreakpoints.cpp DialogBreakpoints.h DialogBreakpoints.ui ) target_link_libraries(${PluginName} Qt5::Widgets edb) install (TARGETS ${PluginName} DESTINATION ${CMAKE_INSTALL_LIBDIR}/edb) set_property(TARGET ${PluginName} PROPERTY CXX_EXTENSIONS OFF) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD 14) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD_REQUIRED ON) set_property(TARGET ${PluginName} PROPERTY LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) set_property(TARGET ${PluginName} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) edb-debugger/plugins/BreakpointManager/DialogBreakpoints.ui0000644000175000017500000001176113765535463023751 0ustar eteraneteran Evan Teran BreakpointManagerPlugin::DialogBreakpoints 0 0 853 252 Breakpoint Manager &Import Breakpoints .. Set Breakpoint &Condition .. Qt::Vertical 20 40 &Close .. true &Remove Breakpoint .. &Add Breakpoint .. QAbstractItemView::NoEditTriggers true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows false true false Address Condition Original Byte Type Function Qt::Vertical 20 40 &Export Breakpoints .. tableWidget btnAdd btnRemove btnCondition okButton okButton clicked() BreakpointManagerPlugin::DialogBreakpoints accept() 702 242 660 212 on_btnImport_clicked() on_btnExport_clicked() edb-debugger/plugins/BreakpointManager/DialogBreakpoints.h0000644000175000017500000000267513765535463023567 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DIALOG_BREAKPOINTS_H_20061101_ #define DIALOG_BREAKPOINTS_H_20061101_ #include "ui_DialogBreakpoints.h" #include namespace BreakpointManagerPlugin { class DialogBreakpoints : public QDialog { Q_OBJECT public: explicit DialogBreakpoints(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~DialogBreakpoints() override = default; public Q_SLOTS: void updateList(); void on_btnAdd_clicked(); void on_btnRemove_clicked(); void on_btnCondition_clicked(); void on_tableWidget_cellDoubleClicked(int row, int col); void on_btnImport_clicked(); void on_btnExport_clicked(); private: void showEvent(QShowEvent *event) override; void hideEvent(QHideEvent *event) override; private: Ui::DialogBreakpoints ui; }; } #endif edb-debugger/plugins/BreakpointManager/BreakpointManager.cpp0000644000175000017500000000314713765535463024105 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "BreakpointManager.h" #include "DialogBreakpoints.h" #include "edb.h" #include #include namespace BreakpointManagerPlugin { /** * @brief BreakpointManager::BreakpointManager * @param parent */ BreakpointManager::BreakpointManager(QObject *parent) : QObject(parent) { } /** * @brief BreakpointManager::~BreakpointManager */ BreakpointManager::~BreakpointManager() { delete dialog_; } /** * @brief BreakpointManager::menu * @param parent * @return */ QMenu *BreakpointManager::menu(QWidget *parent) { Q_ASSERT(parent); if (!menu_) { menu_ = new QMenu(tr("BreakpointManager"), parent); menu_->addAction(tr("&Breakpoints"), this, SLOT(showMenu()), QKeySequence(tr("Ctrl+B"))); } return menu_; } /** * @brief BreakpointManager::showMenu */ void BreakpointManager::showMenu() { if (!dialog_) { dialog_ = new DialogBreakpoints(edb::v1::debugger_ui); } dialog_->show(); } } edb-debugger/plugins/BreakpointManager/BreakpointManager.h0000644000175000017500000000254313765535463023551 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef BREAKPOINT_MANAGER_H_20060430_ #define BREAKPOINT_MANAGER_H_20060430_ #include "IPlugin.h" class QMenu; class QDialog; namespace BreakpointManagerPlugin { class BreakpointManager : public QObject, public IPlugin { Q_OBJECT Q_INTERFACES(IPlugin) Q_PLUGIN_METADATA(IID "edb.IPlugin/1.0") Q_CLASSINFO("author", "Evan Teran") Q_CLASSINFO("url", "http://www.codef00.com") public: explicit BreakpointManager(QObject *parent = nullptr); ~BreakpointManager() override; public: QMenu *menu(QWidget *parent = nullptr) override; public Q_SLOTS: void showMenu(); private: QMenu *menu_ = nullptr; QPointer dialog_ = nullptr; }; } #endif edb-debugger/plugins/CheckVersion/0000755000175000017500000000000013765535463016775 5ustar eteraneteranedb-debugger/plugins/CheckVersion/OptionsPage.h0000644000175000017500000000224513765535463021401 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef OPTIONS_PAGE_H_20090703_ #define OPTIONS_PAGE_H_20090703_ #include "ui_OptionsPage.h" #include namespace CheckVersionPlugin { class OptionsPage : public QWidget { Q_OBJECT public: explicit OptionsPage(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~OptionsPage() override = default; public: void showEvent(QShowEvent *event) override; public Q_SLOTS: void on_checkBox_toggled(bool checked); private: Ui::OptionsPage ui; }; } #endif edb-debugger/plugins/CheckVersion/CheckVersion.h0000644000175000017500000000316213765535463021533 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef CHECK_VERSION_H_20061122_ #define CHECK_VERSION_H_20061122_ #include "IPlugin.h" class QMenu; class QNetworkReply; class QNetworkAccessManager; class QUrl; namespace CheckVersionPlugin { class CheckVersion : public QObject, public IPlugin { Q_OBJECT Q_INTERFACES(IPlugin) Q_PLUGIN_METADATA(IID "edb.IPlugin/1.0") Q_CLASSINFO("author", "Evan Teran") Q_CLASSINFO("url", "http://www.codef00.com") public: explicit CheckVersion(QObject *parent = nullptr); ~CheckVersion() override = default; public: QMenu *menu(QWidget *parent = nullptr) override; QWidget *optionsPage() override; public Q_SLOTS: void showMenu(); private: void requestFinished(QNetworkReply *reply); protected: void privateInit() override; private: void doCheck(); void setProxy(const QUrl &url); private: QMenu *menu_ = nullptr; QNetworkAccessManager *network_ = nullptr; bool initialCheck_ = true; }; } #endif edb-debugger/plugins/CheckVersion/CMakeLists.txt0000644000175000017500000000153213765535463021536 0ustar eteraneterancmake_minimum_required (VERSION 3.1) include("GNUInstallDirs") set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(PluginName "CheckVersion") find_package(Qt5 5.0.0 REQUIRED Widgets Network) add_library(${PluginName} SHARED CheckVersion.cpp CheckVersion.h OptionsPage.cpp OptionsPage.h OptionsPage.ui ) target_link_libraries(${PluginName} Qt5::Widgets Qt5::Network edb) install (TARGETS ${PluginName} DESTINATION ${CMAKE_INSTALL_LIBDIR}/edb) set_property(TARGET ${PluginName} PROPERTY CXX_EXTENSIONS OFF) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD 14) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD_REQUIRED ON) set_property(TARGET ${PluginName} PROPERTY LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) set_property(TARGET ${PluginName} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) edb-debugger/plugins/CheckVersion/OptionsPage.ui0000644000175000017500000000173113765535463021566 0ustar eteraneteran CheckVersionPlugin::OptionsPage 0 0 400 300 Check Version Plugin Check for new version on startup Qt::Vertical 20 262 edb-debugger/plugins/CheckVersion/CheckVersion.cpp0000644000175000017500000001147113765535463022070 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "CheckVersion.h" #include "OptionsPage.h" #include "edb.h" #include "version.h" #include #include #include #include #include #include #include #include #include #include #include namespace CheckVersionPlugin { /** * @brief CheckVersion::CheckVersion * @param parent */ CheckVersion::CheckVersion(QObject *parent) : QObject(parent) { } /** * @brief CheckVersion::privateInit */ void CheckVersion::privateInit() { QSettings settings; if (settings.value("CheckVersion/check_on_start.enabled", true).toBool()) { doCheck(); } } /** * @brief CheckVersion::optionsPage * @return */ QWidget *CheckVersion::optionsPage() { return new OptionsPage; } /** * @brief CheckVersion::menu * @param parent * @return */ QMenu *CheckVersion::menu(QWidget *parent) { Q_ASSERT(parent); if (!menu_) { menu_ = new QMenu(tr("CheckVersion"), parent); menu_->addAction(tr("&Check For Latest Version"), this, SLOT(showMenu())); } return menu_; } /** * @brief CheckVersion::doCheck */ void CheckVersion::doCheck() { if (!network_) { network_ = new QNetworkAccessManager(this); connect(network_, &QNetworkAccessManager::finished, this, &CheckVersion::requestFinished); } QUrl update_url(QString("https://codef00.com/projects/debugger-latest.json?v=%1").arg(edb::version)); QNetworkRequest request(update_url); setProxy(update_url); network_->get(request); } /** * @brief CheckVersion::setProxy * @param url */ void CheckVersion::setProxy(const QUrl &url) { QNetworkProxy proxy; #ifdef Q_OS_LINUX Q_UNUSED(url) auto proxy_str = QString::fromUtf8(qgetenv("HTTP_PROXY")); if (proxy_str.isEmpty()) { proxy_str = QString::fromUtf8(qgetenv("http_proxy")); } if (!proxy_str.isEmpty()) { const QUrl proxy_url = QUrl::fromUserInput(proxy_str); proxy = QNetworkProxy(QNetworkProxy::HttpProxy, proxy_url.host(), proxy_url.port(80), proxy_url.userName(), proxy_url.password()); } #else QList proxies = QNetworkProxyFactory::systemProxyForQuery(QNetworkProxyQuery(url)); if (proxies.size() >= 1) { proxy = proxies.first(); } #endif network_->setProxy(proxy); } /** * @brief CheckVersion::showMenu */ void CheckVersion::showMenu() { initialCheck_ = false; doCheck(); } /** * @brief CheckVersion::requestFinished * @param reply */ void CheckVersion::requestFinished(QNetworkReply *reply) { if (reply->error() != QNetworkReply::NoError) { if (!initialCheck_) { QMessageBox::critical( nullptr, tr("An Error Occured"), reply->errorString()); } } else { QByteArray s = reply->readAll(); QJsonParseError e; QJsonDocument d = QJsonDocument::fromJson(s, &e); if(d.isNull() || e.error != QJsonParseError::NoError) { qDebug("[CheckVersion] Error parsing JSON response: %s", qPrintable(e.errorString())); return; } if(!d.isObject()) { qDebug("[CheckVersion] Unexpected data format in JSON response"); return; } QJsonObject obj = d.object(); QString version = obj["version"].toString(); QString url = obj["url"].toString(); QString md5 = obj["md5"].toString(); QString sha1 = obj["sha1"].toString(); if(version.isEmpty() || url.isEmpty() || md5.isEmpty() || sha1.isEmpty()) { qDebug("[CheckVersion] Unexpected data format in JSON response"); return; } qDebug("comparing versions: [%d] [%d]", edb::v1::int_version(version), edb::v1::edb_version()); if (edb::v1::int_version(version) > edb::v1::edb_version()) { QMessageBox msg; msg.setTextFormat(Qt::RichText); msg.setWindowTitle(tr("New Version Available")); msg.setText(tr("A newer version of edb is available: %1

" "URL: %2

" "MD5: %3
" "SHA1: %4").arg(version, url, md5, sha1)); msg.setStandardButtons(QMessageBox::Ok); msg.exec(); } else { if (!initialCheck_) { QMessageBox::information( nullptr, tr("You are up to date"), tr("You are running the latest version of edb")); } } } reply->deleteLater(); initialCheck_ = false; } } edb-debugger/plugins/CheckVersion/OptionsPage.cpp0000644000175000017500000000266513765535463021742 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "OptionsPage.h" #include namespace CheckVersionPlugin { /** * @brief OptionsPage::OptionsPage * @param parent * @param f */ OptionsPage::OptionsPage(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f) { ui.setupUi(this); } /** * @brief OptionsPage::showEvent * @param event */ void OptionsPage::showEvent(QShowEvent *event) { Q_UNUSED(event) QSettings settings; ui.checkBox->setChecked(settings.value("CheckVersion/check_on_start.enabled", true).toBool()); } /** * @brief OptionsPage::on_checkBox_toggled * @param checked */ void OptionsPage::on_checkBox_toggled(bool checked) { Q_UNUSED(checked) QSettings settings; settings.setValue("CheckVersion/check_on_start.enabled", ui.checkBox->isChecked()); } } edb-debugger/plugins/InstructionInspector/0000755000175000017500000000000013765535463020622 5ustar eteraneteranedb-debugger/plugins/InstructionInspector/CMakeLists.txt0000644000175000017500000000142113765535463023360 0ustar eteraneterancmake_minimum_required (VERSION 3.1) include("GNUInstallDirs") set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(PluginName "InstructionInspector") find_package(Qt5 5.0.0 REQUIRED Widgets) add_library(${PluginName} SHARED Plugin.cpp Plugin.h ) target_link_libraries(${PluginName} Qt5::Widgets edb) install (TARGETS ${PluginName} DESTINATION ${CMAKE_INSTALL_LIBDIR}/edb) set_property(TARGET ${PluginName} PROPERTY CXX_EXTENSIONS OFF) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD 14) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD_REQUIRED ON) set_property(TARGET ${PluginName} PROPERTY LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) set_property(TARGET ${PluginName} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) edb-debugger/plugins/InstructionInspector/Plugin.cpp0000644000175000017500000013142413765535463022571 0ustar eteraneteran/* Copyright (C) 2016 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "Plugin.h" #include "Configuration.h" #include "IDebugger.h" #include "Util.h" #include "edb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_UNIX #include // TODO(eteran): refactor this in terms of "libELF" so we are more portable #endif #include namespace InstructionInspector { namespace { struct NormalizeFailure {}; /** * @brief printBytes * @param ptr * @param size * @param printZeros * @return */ std::string printBytes(const void *ptr, std::size_t size, bool printZeros = true) { std::ostringstream str; str << std::setfill('0') << std::uppercase << std::hex; const auto bytes = reinterpret_cast(ptr); for (std::size_t i = 0; i < size; ++i) { if (!str.str().empty()) { str << ' '; } if (bytes[i] || printZeros) { str << std::setw(2) << static_cast(bytes[i]); } } return str.str(); } /** * @brief toHex * @param x * @param Signed * @return */ std::string toHex(unsigned long long x, bool Signed = false) { bool negative = false; if (Signed && static_cast(x) < 0) { x = ~x + 1; if (~x + 1 != x) { negative = true; } } std::ostringstream str; str << std::hex << (negative ? "-" : "") << "0x" << std::uppercase << x; return str.str(); } #if defined(EDB_ARM32) /** * @brief toFloatString * @param x * @return */ std::string toFloatString(double x) { std::ostringstream str; str.precision(17); str << x; return str.str(); } #endif /** * @brief uppercase * @param text * @return */ std::string uppercase(std::string text) { std::transform(text.begin(), text.end(), text.begin(), [](int ch) { return std::toupper(ch); }); return text; } /** * @brief getGroupNames * @param csh * @param insn_ * @return */ std::vector getGroupNames(csh csh, const cs_insn *insn_) { std::vector groupNames; for (int g = 0; g < insn_->detail->groups_count; ++g) { const auto grp = insn_->detail->groups[g]; if (!grp) { groupNames.emplace_back("INVALID"); continue; } if (const auto groupName = cs_group_name(csh, grp)) { groupNames.emplace_back(uppercase(groupName)); } else { groupNames.emplace_back(toHex(grp)); } } return groupNames; } /** * @brief printReg * @param csh * @param reg * @param canBeZero * @return */ std::string printReg(csh csh, int reg, bool canBeZero = false) { if (!reg) { return canBeZero ? "" : "INVALID"; } if (const auto regName = cs_reg_name(csh, reg)) { return uppercase(regName); } else { return toHex(reg); } } #if 0 /** * @brief printRegs * @param csh * @param regsBuffer * @param size * @return */ std::string printRegs(csh csh, const uint16_t *regsBuffer, std::size_t size) { std::ostringstream str; for (std::size_t r = 0; r < size; ++r) { if (!str.str().empty()) { str << ","; } str << printReg(csh, regsBuffer[r]); } const std::string string = str.str(); if (string.empty()) { return "(none)"; } return string; } #endif #if CS_API_MAJOR >= 4 /** * @brief printXOP_CC * @param cc * @return */ std::string printXOP_CC(x86_xop_cc cc) { static const std::map codes{ {X86_XOP_CC_INVALID, "INVALID"}, {X86_XOP_CC_LT, "LT"}, {X86_XOP_CC_LE, "LE"}, {X86_XOP_CC_GT, "GT"}, {X86_XOP_CC_GE, "GE"}, {X86_XOP_CC_EQ, "EQ"}, {X86_XOP_CC_NEQ, "NEQ"}, {X86_XOP_CC_FALSE, "FALSE"}, {X86_XOP_CC_TRUE, "TRUE"}, }; const auto found = codes.find(cc); if (found == codes.end()) { return toHex(cc); } return found->second; } #endif /** * @brief printSSE_CC * @param cc * @return */ std::string printSSE_CC(x86_sse_cc cc) { static const std::map codes{ {X86_SSE_CC_INVALID, "INVALID"}, {X86_SSE_CC_EQ, "EQ"}, {X86_SSE_CC_LT, "LT"}, {X86_SSE_CC_LE, "LE"}, {X86_SSE_CC_UNORD, "UNORD"}, {X86_SSE_CC_NEQ, "NEQ"}, {X86_SSE_CC_NLT, "NLT"}, {X86_SSE_CC_NLE, "NLE"}, {X86_SSE_CC_ORD, "ORD"}, }; const auto found = codes.find(cc); if (found == codes.end()) { return toHex(cc); } return found->second; } /** * @brief printAVX_CC * @param cc * @return */ std::string printAVX_CC(x86_avx_cc cc) { static const std::map codes{ {X86_AVX_CC_INVALID, "INVALID"}, {X86_AVX_CC_EQ, "EQ"}, {X86_AVX_CC_LT, "LT"}, {X86_AVX_CC_LE, "LE"}, {X86_AVX_CC_UNORD, "UNORD"}, {X86_AVX_CC_NEQ, "NEQ"}, {X86_AVX_CC_NLT, "NLT"}, {X86_AVX_CC_NLE, "NLE"}, {X86_AVX_CC_ORD, "ORD"}, {X86_AVX_CC_EQ_UQ, "EQ_UQ"}, {X86_AVX_CC_NGE, "NGE"}, {X86_AVX_CC_NGT, "NGT"}, {X86_AVX_CC_FALSE, "FALSE"}, {X86_AVX_CC_NEQ_OQ, "NEQ_OQ"}, {X86_AVX_CC_GE, "GE"}, {X86_AVX_CC_GT, "GT"}, {X86_AVX_CC_TRUE, "TRUE"}, {X86_AVX_CC_EQ_OS, "EQ_OS"}, {X86_AVX_CC_LT_OQ, "LT_OQ"}, {X86_AVX_CC_LE_OQ, "LE_OQ"}, {X86_AVX_CC_UNORD_S, "UNORD_S"}, {X86_AVX_CC_NEQ_US, "NEQ_US"}, {X86_AVX_CC_NLT_UQ, "NLT_UQ"}, {X86_AVX_CC_NLE_UQ, "NLE_UQ"}, {X86_AVX_CC_ORD_S, "ORD_S"}, {X86_AVX_CC_EQ_US, "EQ_US"}, {X86_AVX_CC_NGE_UQ, "NGE_UQ"}, {X86_AVX_CC_NGT_UQ, "NGT_UQ"}, {X86_AVX_CC_FALSE_OS, "FALSE_OS"}, {X86_AVX_CC_NEQ_OS, "NEQ_OS"}, {X86_AVX_CC_GE_OQ, "GE_OQ"}, {X86_AVX_CC_GT_OQ, "GT_OQ"}, {X86_AVX_CC_TRUE_US, "TRUE_US"}, }; const auto found = codes.find(cc); if (found == codes.end()) { return toHex(cc); } return found->second; } /** * @brief printAVX_RM * @param cc * @return */ std::string printAVX_RM(x86_avx_rm cc) { static const std::map codes{ {X86_AVX_RM_INVALID, "invalid"}, {X86_AVX_RM_RN, "to nearest"}, {X86_AVX_RM_RD, "down"}, {X86_AVX_RM_RU, "up"}, {X86_AVX_RM_RZ, "toward zero"}, }; const auto found = codes.find(cc); if (found == codes.end()) { return toHex(cc); } return found->second; } #if CS_API_MAJOR >= 4 /** * @brief getChangedEFLAGSNames * @param efl * @return */ std::vector getChangedEFLAGSNames(std::uint64_t efl) { std::vector flags; if (efl & X86_EFLAGS_MODIFY_AF) { flags.emplace_back("MODIFY_AF"); efl &= ~X86_EFLAGS_MODIFY_AF; } if (efl & X86_EFLAGS_MODIFY_CF) { flags.emplace_back("MODIFY_CF"); efl &= ~X86_EFLAGS_MODIFY_CF; } if (efl & X86_EFLAGS_MODIFY_SF) { flags.emplace_back("MODIFY_SF"); efl &= ~X86_EFLAGS_MODIFY_SF; } if (efl & X86_EFLAGS_MODIFY_ZF) { flags.emplace_back("MODIFY_ZF"); efl &= ~X86_EFLAGS_MODIFY_ZF; } if (efl & X86_EFLAGS_MODIFY_PF) { flags.emplace_back("MODIFY_PF"); efl &= ~X86_EFLAGS_MODIFY_PF; } if (efl & X86_EFLAGS_MODIFY_OF) { flags.emplace_back("MODIFY_OF"); efl &= ~X86_EFLAGS_MODIFY_OF; } if (efl & X86_EFLAGS_MODIFY_TF) { flags.emplace_back("MODIFY_TF"); efl &= ~X86_EFLAGS_MODIFY_TF; } if (efl & X86_EFLAGS_MODIFY_IF) { flags.emplace_back("MODIFY_IF"); efl &= ~X86_EFLAGS_MODIFY_IF; } if (efl & X86_EFLAGS_MODIFY_DF) { flags.emplace_back("MODIFY_DF"); efl &= ~X86_EFLAGS_MODIFY_DF; } if (efl & X86_EFLAGS_MODIFY_NT) { flags.emplace_back("MODIFY_NT"); efl &= ~X86_EFLAGS_MODIFY_NT; } if (efl & X86_EFLAGS_MODIFY_RF) { flags.emplace_back("MODIFY_RF"); efl &= ~X86_EFLAGS_MODIFY_RF; } if (efl & X86_EFLAGS_PRIOR_OF) { flags.emplace_back("PRIOR_OF"); efl &= ~X86_EFLAGS_PRIOR_OF; } if (efl & X86_EFLAGS_PRIOR_SF) { flags.emplace_back("PRIOR_SF"); efl &= ~X86_EFLAGS_PRIOR_SF; } if (efl & X86_EFLAGS_PRIOR_ZF) { flags.emplace_back("PRIOR_ZF"); efl &= ~X86_EFLAGS_PRIOR_ZF; } if (efl & X86_EFLAGS_PRIOR_AF) { flags.emplace_back("PRIOR_AF"); efl &= ~X86_EFLAGS_PRIOR_AF; } if (efl & X86_EFLAGS_PRIOR_PF) { flags.emplace_back("PRIOR_PF"); efl &= ~X86_EFLAGS_PRIOR_PF; } if (efl & X86_EFLAGS_PRIOR_CF) { flags.emplace_back("PRIOR_CF"); efl &= ~X86_EFLAGS_PRIOR_CF; } if (efl & X86_EFLAGS_PRIOR_TF) { flags.emplace_back("PRIOR_TF"); efl &= ~X86_EFLAGS_PRIOR_TF; } if (efl & X86_EFLAGS_PRIOR_IF) { flags.emplace_back("PRIOR_IF"); efl &= ~X86_EFLAGS_PRIOR_IF; } if (efl & X86_EFLAGS_PRIOR_DF) { flags.emplace_back("PRIOR_DF"); efl &= ~X86_EFLAGS_PRIOR_DF; } if (efl & X86_EFLAGS_PRIOR_NT) { flags.emplace_back("PRIOR_NT"); efl &= ~X86_EFLAGS_PRIOR_NT; } if (efl & X86_EFLAGS_RESET_OF) { flags.emplace_back("RESET_OF"); efl &= ~X86_EFLAGS_RESET_OF; } if (efl & X86_EFLAGS_RESET_CF) { flags.emplace_back("RESET_CF"); efl &= ~X86_EFLAGS_RESET_CF; } if (efl & X86_EFLAGS_RESET_DF) { flags.emplace_back("RESET_DF"); efl &= ~X86_EFLAGS_RESET_DF; } if (efl & X86_EFLAGS_RESET_IF) { flags.emplace_back("RESET_IF"); efl &= ~X86_EFLAGS_RESET_IF; } if (efl & X86_EFLAGS_RESET_SF) { flags.emplace_back("RESET_SF"); efl &= ~X86_EFLAGS_RESET_SF; } if (efl & X86_EFLAGS_RESET_AF) { flags.emplace_back("RESET_AF"); efl &= ~X86_EFLAGS_RESET_AF; } if (efl & X86_EFLAGS_RESET_TF) { flags.emplace_back("RESET_TF"); efl &= ~X86_EFLAGS_RESET_TF; } if (efl & X86_EFLAGS_RESET_NT) { flags.emplace_back("RESET_NT"); efl &= ~X86_EFLAGS_RESET_NT; } if (efl & X86_EFLAGS_RESET_PF) { flags.emplace_back("RESET_PF"); efl &= ~X86_EFLAGS_RESET_PF; } if (efl & X86_EFLAGS_SET_CF) { flags.emplace_back("SET_CF"); efl &= ~X86_EFLAGS_SET_CF; } if (efl & X86_EFLAGS_SET_DF) { flags.emplace_back("SET_DF"); efl &= ~X86_EFLAGS_SET_DF; } if (efl & X86_EFLAGS_SET_IF) { flags.emplace_back("SET_IF"); efl &= ~X86_EFLAGS_SET_IF; } if (efl & X86_EFLAGS_TEST_OF) { flags.emplace_back("TEST_OF"); efl &= ~X86_EFLAGS_TEST_OF; } if (efl & X86_EFLAGS_TEST_SF) { flags.emplace_back("TEST_SF"); efl &= ~X86_EFLAGS_TEST_SF; } if (efl & X86_EFLAGS_TEST_ZF) { flags.emplace_back("TEST_ZF"); efl &= ~X86_EFLAGS_TEST_ZF; } if (efl & X86_EFLAGS_TEST_PF) { flags.emplace_back("TEST_PF"); efl &= ~X86_EFLAGS_TEST_PF; } if (efl & X86_EFLAGS_TEST_CF) { flags.emplace_back("TEST_CF"); efl &= ~X86_EFLAGS_TEST_CF; } if (efl & X86_EFLAGS_TEST_NT) { flags.emplace_back("TEST_NT"); efl &= ~X86_EFLAGS_TEST_NT; } if (efl & X86_EFLAGS_TEST_DF) { flags.emplace_back("TEST_DF"); efl &= ~X86_EFLAGS_TEST_DF; } if (efl & X86_EFLAGS_UNDEFINED_OF) { flags.emplace_back("UNDEFINED_OF"); efl &= ~X86_EFLAGS_UNDEFINED_OF; } if (efl & X86_EFLAGS_UNDEFINED_SF) { flags.emplace_back("UNDEFINED_SF"); efl &= ~X86_EFLAGS_UNDEFINED_SF; } if (efl & X86_EFLAGS_UNDEFINED_ZF) { flags.emplace_back("UNDEFINED_ZF"); efl &= ~X86_EFLAGS_UNDEFINED_ZF; } if (efl & X86_EFLAGS_UNDEFINED_PF) { flags.emplace_back("UNDEFINED_PF"); efl &= ~X86_EFLAGS_UNDEFINED_PF; } if (efl & X86_EFLAGS_UNDEFINED_AF) { flags.emplace_back("UNDEFINED_AF"); efl &= ~X86_EFLAGS_UNDEFINED_AF; } if (efl & X86_EFLAGS_UNDEFINED_CF) { flags.emplace_back("UNDEFINED_CF"); efl &= ~X86_EFLAGS_UNDEFINED_CF; } if (efl) { flags.emplace_back(toHex(efl)); } return flags; } #endif #if defined(EDB_ARM32) /** * @brief printCond * @param cc * @return */ std::string printCond(arm_cc cc) { static const std::map types{ {ARM_CC_INVALID, "invalid"}, {ARM_CC_EQ, "EQ"}, {ARM_CC_NE, "NE"}, {ARM_CC_HS, "HS"}, {ARM_CC_LO, "LO"}, {ARM_CC_MI, "MI"}, {ARM_CC_PL, "PL"}, {ARM_CC_VS, "VS"}, {ARM_CC_VC, "VC"}, {ARM_CC_HI, "HI"}, {ARM_CC_LS, "LS"}, {ARM_CC_GE, "GE"}, {ARM_CC_LT, "LT"}, {ARM_CC_GT, "GT"}, {ARM_CC_LE, "LE"}, {ARM_CC_AL, "AL"}, }; const auto found = types.find(cc); if (found == types.end()) { return toHex(cc); } return found->second; } #endif /** * @brief printOpType * @param op * @return */ std::string printOpType(const x86_op_type &op) { static const std::map types{ {X86_OP_INVALID, "invalid"}, {X86_OP_REG, "register"}, {X86_OP_IMM, "immediate"}, {X86_OP_MEM, "memory"}, }; const auto found = types.find(op); if (found == types.end()) { return toHex(op); } return found->second; } #if defined(EDB_ARM32) /** * @brief printShiftType * @param op * @return */ std::string printShiftType(const arm_shifter &op) { static const std::map types{ {ARM_SFT_INVALID, "invalid"}, {ARM_SFT_ASR, "ASR"}, {ARM_SFT_LSL, "LSL"}, {ARM_SFT_LSR, "LSR"}, {ARM_SFT_ROR, "ROR"}, {ARM_SFT_RRX, "RRX"}, {ARM_SFT_ASR_REG, "ASR with register"}, {ARM_SFT_LSL_REG, "LSL with register"}, {ARM_SFT_LSR_REG, "LSR with register"}, {ARM_SFT_ROR_REG, "ROR with register"}, {ARM_SFT_RRX_REG, "RRX with register"}, }; const auto found = types.find(op); if (found == types.end()) { return toHex(op); } return found->second; } /** * @brief printOpType * @param op * @return */ std::string printOpType(const arm_setend_type &op) { static const std::map types{ {ARM_SETEND_INVALID, "invalid"}, {ARM_SETEND_BE, "BE"}, {ARM_SETEND_LE, "LE"}, }; const auto found = types.find(op); if (found == types.end()) { return toHex(op); } return found->second; } /** * @brief printOpType * @param op * @return */ std::string printOpType(const arm_op_type &op) { static const std::map types{ {ARM_OP_INVALID, "invalid"}, {ARM_OP_REG, "register"}, {ARM_OP_IMM, "immediate"}, {ARM_OP_MEM, "memory"}, {ARM_OP_FP, "floating-point"}, {ARM_OP_CIMM, "C-Immediate"}, {ARM_OP_PIMM, "P-Immediate"}, {ARM_OP_SETEND, "operand for SETEND"}, {ARM_OP_SYSREG, "MSR/MSR special register"}, }; const auto found = types.find(op); if (found == types.end()) { return toHex(op); } return found->second; } #endif /** * @brief printAVX_Bcast * @param bc * @return */ std::string printAVX_Bcast(x86_avx_bcast bc) { static const std::map types{ {X86_AVX_BCAST_INVALID, "invalid"}, {X86_AVX_BCAST_2, "{1to2}"}, {X86_AVX_BCAST_4, "{1to4}"}, {X86_AVX_BCAST_8, "{1to8}"}, {X86_AVX_BCAST_16, "{1to16}"}, }; const auto found = types.find(bc); if (found == types.end()) { return toHex(bc); } return found->second; } #if CS_API_MAJOR >= 4 /** * @brief printAccessMode * @param mode * @return */ std::string printAccessMode(unsigned mode) { std::ostringstream str; if (mode & CS_AC_READ) { if (!str.str().empty()) { str << "+"; } str << "read"; mode &= ~CS_AC_READ; } if (mode & CS_AC_WRITE) { if (!str.str().empty()) { str << "+"; } str << "write"; mode &= ~CS_AC_WRITE; } if (mode) { if (!str.str().empty()) { str << "+"; } str << toHex(mode); } const std::string string = str.str(); if (string.empty()) { return "none"; } return string; } #endif /** * @brief normalizeOBJDUMP * @param text * @param bits * @return */ QString normalizeOBJDUMP(const QString &text, int bits) { auto parts = text.split('\t'); #if defined(EDB_X86) || defined(EDB_X86_64) if (parts.size() != 3) return text + " ; unexpected format"; #elif defined(EDB_ARM32) if (parts.size() < 3) return text + " ; unexpected format"; #else return text + " ; WARNING: InstructionInspector's normalization is not implemented for this arch"; #endif auto &addr = parts[0]; auto &bytes = parts[1]; auto &disasm = parts[2]; addr = addr.trimmed().toUpper(); // left removes colon addr = addr.left(addr.size() - 1).rightJustified(bits / 4, '0'); bytes = bytes.trimmed().toUpper(); disasm = disasm.trimmed().replace(QRegExp(" +"), " "); #if defined(EDB_ARM32) // ARM objdump prints instruction bytes as a word instead of separate bytes. We won't // change this format, but will align the disassembly. if (bytes.size() > 2) bytes = bytes.leftJustified(bytes.size() * 3 / 2 - 1); // operands and comments are separated by a one or more tabs on ARM for (unsigned i = 3; i < parts.size(); ++i) disasm += " " + parts[i]; #endif return addr + " " + bytes + " " + disasm; } /** * @brief runOBJDUMP * @param bytes * @param address * @return */ std::string runOBJDUMP(const std::vector &bytes, edb::address_t address) { const std::string processName = "objdump"; const auto bits = edb::v1::debuggeeIs32Bit() ? 32 : 64; QTemporaryFile binary(QDir::tempPath() + "/edb_insn_inspector_temp_XXXXXX.bin"); if (!binary.open()) { return "; Failed to create binary file"; } const int size = bytes.size(); if (binary.write(reinterpret_cast(bytes.data()), size) != size) { return "; Failed to write to binary file"; } binary.close(); QProcess process; process.start(processName.c_str(), { "-D", "--target=binary", #if defined(EDB_X86) || defined(EDB_X86_64) "--insn-width=15", "--architecture=i386" + QString(bits == 64 ? ":x86-64" : ""), "-M", "intel," "intel-mnemonic", #elif defined(EDB_ARM32) "--insn-width=4", "-m", "arm", edb::v1::debugger_core->cpuMode() == IDebugger::CpuMode::Thumb ? "-Mforce-thumb" : "-Mno-force-thumb", #else #error "Not implemented" #endif "--adjust-vma=" + address.toPointerString(), binary.fileName() }); if (process.waitForFinished()) { if (process.exitCode() != 0) return ("; got response: \"" + process.readAllStandardError() + "\"").constData(); if (process.exitStatus() != QProcess::NormalExit) return "; process crashed"; const auto output = QString::fromUtf8(process.readAllStandardOutput()).split('\n'); const auto addrStr = address.toHexString().toLower().replace(QRegExp("^0+"), ""); QString result; for (auto &line : output) { if (line.contains(QRegExp("^ *" + addrStr + ":\t[^\t]+\t"))) { result = line; break; } } if (result.isEmpty()) { // Try truncating higher bits of address: some versions of objconv can't --adjust-vma to // 64-bit values even with --architecture=i386:x86-64 (namely, 32-bit binutils 2.26.20160125) if (bits == 64 && address > UINT32_MAX) { return runOBJDUMP(bytes, address & UINT32_MAX) + " ; WARNING: origin had to be truncated for objdump"; } return ("; failed to find disassembly. stdout: \"" + output.join("\n") + "\"").toStdString(); } return normalizeOBJDUMP(result, bits).toStdString(); } else if (process.error() == QProcess::FailedToStart) { return "; Failed to start " + processName; } return "; Unknown error while running " + processName; } #if defined(EDB_X86) || defined(EDB_X86_64) /** * @brief normalizeNDISASM * @param text * @param bits * @return */ QString normalizeNDISASM(const QString &text, int bits) { auto lines = text.split('\n'); Q_ASSERT(!lines.isEmpty()); auto parts = lines.takeFirst().replace(QRegExp(" +"), "\t").split('\t'); if (parts.size() != 3) return text + " ; unexpected format 1"; auto &addr = parts[0]; auto &bytes = parts[1]; auto &disasm = parts[2]; addr = addr.rightJustified(bits / 4, '0'); bytes = bytes.trimmed(); // connect the rest of lines to bytes for (auto &line : lines) { if (!line.contains(QRegExp("^ +-[0-9a-fA-F]+$"))) { return text + " ; unexpected format 2"; } line = line.trimmed(); bytes += line.rightRef(line.size() - 1); // remove leading '-' } bytes.replace(QRegExp("(..)"), "\\1 "); return addr + " " + bytes.trimmed() + " " + disasm.trimmed(); } /** * @brief runNDISASM * @param bytes * @param address * @return */ std::string runNDISASM(const std::vector &bytes, edb::address_t address) { const std::string processName = "ndisasm"; const auto bits = edb::v1::debuggeeIs32Bit() ? 32 : 64; QTemporaryFile binary(QDir::tempPath() + "/edb_insn_inspector_temp_XXXXXX.bin"); if (!binary.open()) { return "; Failed to create binary file"; } const int size = bytes.size(); if (binary.write(reinterpret_cast(bytes.data()), size) != size) { return "; Failed to write to binary file"; } binary.close(); QProcess process; process.start(processName.c_str(), {"-o", address.toPointerString(), "-b", std::to_string(bits).c_str(), binary.fileName()}); if (process.waitForFinished()) { if (process.exitCode() != 0) { return ("; got response: \"" + process.readAllStandardError() + "\"").constData(); } if (process.exitStatus() != QProcess::NormalExit) { return "; process crashed"; } auto output = QString::fromUtf8(process.readAllStandardOutput()).split('\n'); QString result = output.takeFirst(); for (auto &line : output) { if (line.contains(QRegExp("^ +-[0-9a-fA-F]+$"))) { result += "\n" + line; } else { break; } } return normalizeNDISASM(result, bits).toStdString(); } else if (process.error() == QProcess::FailedToStart) return "; Failed to start " + processName; return "; Unknown error while running " + processName; } /** * @brief normalizeOBJCONV * @param text * @param bits * @return */ std::pair normalizeOBJCONV(const QString &text, int bits) { QRegExp expectedFormat("^ +([^;]+); ([0-9a-fA-F]+) _ (.*)"); if (expectedFormat.indexIn(text, 0) == -1) { throw NormalizeFailure{}; } const auto addr = expectedFormat.cap(2).rightJustified(bits / 4, '0'); auto bytes = expectedFormat.cap(3).trimmed(); const auto disasm = expectedFormat.cap(1).trimmed().replace(QRegExp(" +"), " "); const auto result = addr + " " + bytes + " " + disasm; bytes.replace(QRegExp("[^0-9a-fA-F]"), ""); const std::size_t insnLength = bytes.length() / 2; return std::make_pair(result, insnLength); } /** * @brief runOBJCONV * @param bytes * @param address * @return */ std::string runOBJCONV(std::vector bytes, edb::address_t address) { const std::string processName = "objconv"; const auto bits = edb::v1::debuggeeIs32Bit() ? 32 : 64; QString binaryFileName; { QTemporaryFile binary(QDir::tempPath() + "/edb_insn_inspector_temp_XXXXXX.bin"); if (!binary.open()) { return "; Failed to create binary file"; } { #ifdef Q_OS_UNIX if (bits == 32) { struct FileData { Elf32_Ehdr elfHeader; Elf32_Phdr programHeader; Elf32_Shdr zerothSectionHeader = {}; Elf32_Shdr codeSectionHeader; Elf32_Shdr stringTableSectionHeader; // start of zero fill + instruction } fileData; fileData.elfHeader = Elf32_Ehdr{ {(unsigned char)ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3, ELFCLASS32, ELFDATA2LSB, EV_CURRENT}, ET_EXEC, EM_386, EV_CURRENT, Elf32_Addr(address.toUint()), // entry point offsetof(FileData, programHeader), // program header table offset offsetof(FileData, zerothSectionHeader), // section header table offset 0, // flags sizeof(Elf32_Ehdr), // size of ELF header... sizeof(Elf32_Phdr), 1, // number of program headers sizeof(Elf32_Shdr), 3, // number of section headers (one for zeroth, another for ours,third string // table (which is only needed for objconv, not for correct ELF file)) 2 // string table index in section header table (could be SHN_UNDEF, but objconv is picky) }; const edb::value32 insnAddr(address); // aligned on page boundary and one page before // Loading the file one page earlier to make it possible to have // code origin even at the beginning of actual page where ELF // header would otherwise be const auto pageSize = 4096u; const edb::value32 fileAddr = (insnAddr & ~(pageSize - 1)) - pageSize; fileData.programHeader = Elf32_Phdr{ PT_LOAD, 0, // start of file is beginning of segment Elf32_Addr(fileAddr.toUint()), // vaddr of the segment 0, // paddr of the segment, irrelevant 2 * pageSize, // size of file image of the segment 2 * pageSize, // size of memory image of the segment PF_R | PF_X, 0 // no alignment requirements }; fileData.codeSectionHeader = Elf32_Shdr{ 0, // index of name in string table SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, Elf32_Addr(address.toUint()), insnAddr - fileAddr, // section offset in file Elf32_Addr(bytes.size()), SHN_UNDEF, // sh_link 0, // sh_info 0, // alignment constraints: 0 or 1 mean unaligned 0 // no fixed-size entries in this section }; fileData.stringTableSectionHeader = Elf32_Shdr{ 0, // index of name in string table SHT_STRTAB, 0, 0, offsetof(FileData, zerothSectionHeader), // section offset in file: at the zeroth section header, // which starts with zeros 1, // single byte should be enough SHN_UNDEF, // sh_link 0, // sh_info 0, // alignment constraints: 0 or 1 mean unaligned 0 // no fixed-size entries in this section }; binary.write(reinterpret_cast(&fileData), sizeof(fileData)); binary.seek(insnAddr - fileAddr); binary.write(reinterpret_cast(bytes.data()), bytes.size()); } else if (bits == 64) { // FIXME: the file generated here is actually broken: I don't know why, but trying to run it will lead // to "Exec format error" struct FileData { Elf64_Ehdr elfHeader; Elf64_Phdr programHeader; Elf64_Shdr zerothSectionHeader = {}; Elf64_Shdr codeSectionHeader; Elf64_Shdr stringTableSectionHeader; // start of zero fill + instruction } fileData; fileData.elfHeader = Elf64_Ehdr{ {(unsigned char)ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3, ELFCLASS64, ELFDATA2LSB, EV_CURRENT}, ET_EXEC, EM_386, EV_CURRENT, Elf64_Addr(address.toUint()), // entry point offsetof(FileData, programHeader), // program header table offset offsetof(FileData, zerothSectionHeader), // section header table offset 0, // flags sizeof(Elf64_Ehdr), // size of ELF header... sizeof(Elf64_Phdr), 1, // number of program headers sizeof(Elf64_Shdr), 3, // number of section headers (one for zeroth, another for ours,third string // table (which is only needed for objconv, not for correct ELF file)) 2 // string table index in section header table (could be SHN_UNDEF, but objconv is picky) }; const edb::value64 insnAddr(address); // aligned on page boundary and one page before // Loading the file one page earlier to make it possible to have // code origin even at the beginning of actual page where ELF // header would otherwise be const auto pageSize = 4096ull; const edb::value64 fileAddr = (insnAddr & ~(pageSize - 1)) - pageSize; fileData.programHeader = Elf64_Phdr{ PT_LOAD, PF_R | PF_X, // NOTE: This field is placed differently than in Elf32_Phdr!!! 0, // start of file is beginning of segment Elf64_Addr(fileAddr.toUint()), // vaddr of the segment 0, // paddr of the segment, irrelevant 2 * pageSize, // size of file image of the segment 2 * pageSize, // size of memory image of the segment 0 // no alignment requirements }; fileData.codeSectionHeader = Elf64_Shdr{ 0, // index of name in string table SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, Elf64_Addr(address.toUint()), insnAddr - fileAddr, // section offset in file bytes.size(), SHN_UNDEF, // sh_link 0, // sh_info 0, // alignment constraints: 0 or 1 mean unaligned 0 // no fixed-size entries in this section }; fileData.stringTableSectionHeader = Elf64_Shdr{ 0, // index of name in string table SHT_STRTAB, 0, 0, offsetof(FileData, zerothSectionHeader), // section offset in file: at the zeroth section header, // which starts with zeros 1, // single byte should be enough SHN_UNDEF, // sh_link 0, // sh_info 0, // alignment constraints: 0 or 1 mean unaligned 0 // no fixed-size entries in this section }; binary.write(reinterpret_cast(&fileData), sizeof(fileData)); binary.seek(insnAddr - fileAddr); binary.write(reinterpret_cast(bytes.data()), bytes.size()); } else { return "; " + std::to_string(bits) + " bit?.. Not implemented."; } #endif } binary.close(); binary.setPermissions(QFile::ExeOwner | QFile::ReadOwner | QFile::WriteOwner); // We have to destroy the QTemporaryFile object, otherwise for some reason we get // "Text file busy" when trying to run the binary. So will remove it manually. binary.setAutoRemove(false); binaryFileName = binary.fileName(); } QProcess process; process.start(processName.c_str(), {"-fnasm", binaryFileName, "/dev/stdout"}); const bool success = process.waitForFinished(); QFile::remove(binaryFileName); if (success) { const auto output = process.readAllStandardOutput(); const auto err = process.readAllStandardError(); if (process.exitCode() != 0) { return ("; got response: \"" + err + "\"").constData(); } if (process.exitStatus() != QProcess::NormalExit) { return "; process crashed"; } const auto lines = output.split('\n'); QString result; enum class LookingFor { FunctionBegin, Instruction } mode = LookingFor::FunctionBegin; const auto addrFormatted = address.toHexString().toUpper().replace(QRegExp("^0+"), ""); const auto addrTruncatedFormatted = (address & UINT32_MAX).toHexString().toUpper().replace(QRegExp("^0+"), ""); for (int L = 0; L < lines.size(); ++L) { const auto line = QString::fromUtf8(lines[L]); switch (mode) { case LookingFor::FunctionBegin: if (line.contains(QRegExp("^; Instruction set:"))) { result += line + '\n'; continue; } if (line.contains(QRegExp("^SECTION.* execute"))) { mode = LookingFor::Instruction; continue; } break; case LookingFor::Instruction: if (line.startsWith("; ")) { // Filter useless notes if (line.contains("Function does not end with ")) continue; if (line.contains("without relocation")) continue; result += line + '\n'; } if (line.contains(QRegExp(" +[^;]+; 0*" + addrFormatted + " _")) || line.contains(QRegExp(" +[^;]+; 0*" + addrTruncatedFormatted + " _"))) { // XXX: objconv truncates all addresses to 32 bits try { QString normalized; std::size_t insnLength; std::tie(normalized, insnLength) = normalizeOBJCONV(line, bits); if (insnLength != bytes.size()) { // Avoid getting wrong "Instruction set: " print due to subsequent bytes possibly forming // some extended-ISA instruction bytes.resize(insnLength); return runOBJCONV(bytes, address); } result += normalized; return result.toStdString(); } catch (NormalizeFailure &) { return "; !Failed to normalize\n" + QString::fromUtf8(output).toStdString(); } } if (line.contains(QRegExp("^ +db "))) { auto lines = result.split('\n'); for (int i = 0; i < result.size(); ++i) if (lines[i].startsWith("; Instruction set:")) { lines.removeAt(i); break; } return (lines.join("\n") + address.toHexString().toUpper() + " " + line.trimmed()).toStdString(); } break; } } return ("; Couldn't locate disassembly, stdout:\n\"" + output + "\"\nstderr:\n\"" + err + "\"").constData(); } else if (process.error() == QProcess::FailedToStart) { return "; Failed to start " + processName; } return "; Unknown error while running " + processName; } #endif } class Disassembler { private: csh csh_; cs_insn *insn_ = nullptr; public: struct InitFailure { const char *error; }; explicit Disassembler(cs_mode mode) { cs_err result = cs_open( #if defined(EDB_X86) || defined(EDB_X86_64) CS_ARCH_X86 #elif defined(EDB_ARM32) CS_ARCH_ARM #elif defined(EDB_ARM64) CS_ARCH_ARM64 #else #error "What to pass to capstone?" #endif , mode, &csh_); if (result != CS_ERR_OK) { throw InitFailure{cs_strerror(result)}; } cs_option(csh_, CS_OPT_DETAIL, CS_OPT_ON); cs_option(csh_, CS_OPT_SYNTAX, edb::v1::config().syntax == Configuration::Syntax::Intel ? CS_OPT_SYNTAX_INTEL : CS_OPT_SYNTAX_ATT); } cs_insn *disassemble(const std::uint8_t *buf, std::size_t size, edb::address_t address) { if (insn_) { cs_free(insn_, 1); } if (cs_disasm(csh_, buf, size, address, 1, &insn_)) { return insn_; } else { return nullptr; } } ~Disassembler() { if (insn_) { cs_free(insn_, 1); } cs_close(&csh_); } csh handle() const { return csh_; } }; /** * @brief Plugin::Plugin * @param parent */ Plugin::Plugin(QObject *parent) : QObject(parent), menuAction_(new QAction("Inspect instruction (Capstone info)", this)) { connect(menuAction_, &QAction::triggered, this, [this](bool) { showDialog(); }); } /** * @brief Plugin::menu * @return */ QMenu *Plugin::menu(QWidget *) { return nullptr; } /** * @brief Plugin::cpuContextMenu * @return */ QList Plugin::cpuContextMenu() { return {menuAction_}; } /** * @brief InstructionDialog::InstructionDialog * @param parent * @param f */ InstructionDialog::InstructionDialog(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f) { setWindowTitle("Instruction Inspector"); address_ = edb::v1::cpu_selected_address(); const cs_mode mode = #if defined(EDB_X86) || defined(EDB_X86_64) edb::v1::debuggeeIs32Bit() ? CS_MODE_32 : CS_MODE_64 #elif defined(EDB_ARM32) || defined(EDB_ARM64) // FIXME(ARM): we also have possible values: // * CS_MODE_ARM, // * CS_MODE_THUMB, // * CS_MODE_MCLASS, // * CS_MODE_V8, // and need to select the right one. Also need to choose from // * CS_MODE_LITTLE_ENDIAN and // * CS_MODE_BIG_ENDIAN static_cast((edb::v1::debugger_core->cpuMode() == IDebugger::CpuMode::Thumb ? CS_MODE_THUMB : CS_MODE_ARM) | CS_MODE_LITTLE_ENDIAN) #else #error "What value should mode have?" #endif ; disassembler_ = new Disassembler(mode); uint8_t buffer[edb::Instruction::MaxSize]; if (const int bufSize = edb::v1::get_instruction_bytes(address_, buffer)) { insnBytes_ = std::vector(buffer, buffer + bufSize); const auto insn = disassembler_->disassemble(buffer, bufSize, address_); insn_ = insn; tree_ = new QTreeWidget; layout_ = new QVBoxLayout; setLayout(layout_); layout_->addWidget(tree_); buttonCompare_ = new QPushButton("Compare disassemblers"); layout_->addWidget(buttonCompare_); connect(buttonCompare_, &QPushButton::clicked, this, [this](bool) { compareDisassemblers(); }); tree_->setUniformRowHeights(true); tree_->setColumnCount(2); tree_->setHeaderLabels({"Field", "Value"}); // Workaround for impossibility of default parameters in C++11 lambdas struct Add { QTreeWidget *const tree_; explicit Add(QTreeWidget *tree) : tree_(tree) { } void operator()(const QStringList &sl, QTreeWidgetItem *parent = nullptr) const { tree_->addTopLevelItem(new QTreeWidgetItem(parent, sl)); } } add(tree_); if (!insn) { add({"Bad instruction", "Failed to disassemble instruction at address " + edb::v1::format_pointer(address_)}); add({"Bytes", printBytes(&insnBytes_[0], insnBytes_.size()).c_str()}); } else { add({"Address", toHex(insn->address).c_str()}); add({"Bytes", printBytes(insn->bytes, insn->size).c_str()}); add({"Mnemonic", insn->mnemonic}); add({"Operands string", insn->op_str}); const auto groupNames = getGroupNames(disassembler_->handle(), insn); add({"Groups"}); auto *const groups = tree_->topLevelItem(tree_->topLevelItemCount() - 1); for (const auto &group : groupNames) { add({group.c_str()}, groups); } { add({"Regs implicitly read"}); auto *const regsRead = tree_->topLevelItem(tree_->topLevelItemCount() - 1); for (int r = 0; r < insn->detail->regs_read_count; ++r) { add({printReg(disassembler_->handle(), insn->detail->regs_read[r]).c_str()}, regsRead); } } { add({"Regs implicitly written"}); auto *const regsWritten = tree_->topLevelItem(tree_->topLevelItemCount() - 1); for (int r = 0; r < insn->detail->regs_write_count; ++r) { add({printReg(disassembler_->handle(), insn->detail->regs_write[r]).c_str()}, regsWritten); } } #if defined(EDB_X86) || defined(EDB_X86_64) add({"Prefixes", printBytes(insn->detail->x86.prefix, sizeof(insn->detail->x86.prefix), false).c_str()}); add({"Opcode", printBytes(insn->detail->x86.opcode, sizeof(insn->detail->x86.opcode)).c_str()}); if (insn->detail->x86.rex) { add({"REX", printBytes(&insn->detail->x86.rex, 1).c_str()}); } add({"AddrSize", std::to_string(+insn->detail->x86.addr_size).c_str()}); add({"ModRM", printBytes(&insn->detail->x86.modrm, 1).c_str()}); add({"SIB", printBytes(&insn->detail->x86.sib, 1).c_str()}); auto *const sib = tree_->topLevelItem(tree_->topLevelItemCount() - 1); add({"Displacement", toHex(insn->detail->x86.disp, true).c_str()}, sib); add({"index", printReg(disassembler_->handle(), insn->detail->x86.sib_index, true).c_str()}, sib); add({"scale", std::to_string(+insn->detail->x86.sib_scale).c_str()}, sib); add({"base", printReg(disassembler_->handle(), insn->detail->x86.sib_base, true).c_str()}, sib); #if CS_API_MAJOR >= 4 if (insn->detail->x86.xop_cc) { add({"XOP condition", printXOP_CC(insn->detail->x86.xop_cc).c_str()}); } #endif if (insn->detail->x86.sse_cc) { add({"SSE condition", printSSE_CC(insn->detail->x86.sse_cc).c_str()}); } if (insn->detail->x86.avx_cc) { add({"AVX condition", printAVX_CC(insn->detail->x86.avx_cc).c_str()}); } add({"SAE", insn->detail->x86.avx_sae ? "yes" : "no"}); if (insn->detail->x86.avx_rm) { add({"AVX rounding", printAVX_RM(insn->detail->x86.avx_rm).c_str()}); } #if CS_API_MAJOR >= 4 const auto changedEflagsNames = getChangedEFLAGSNames(insn->detail->x86.eflags); add({"EFLAGS"}); auto *const eflags = tree_->topLevelItem(tree_->topLevelItemCount() - 1); for (auto efl : changedEflagsNames) { add({efl.c_str()}, eflags); } #endif add({"Operands"}); auto *const operands = tree_->topLevelItem(tree_->topLevelItemCount() - 1); for (int op = 0; op < insn->detail->x86.op_count; ++op) { const auto &operand = insn->detail->x86.operands[op]; add({("#" + std::to_string(op + 1)).c_str()}, operands); auto *const curOpItem = operands->child(op); add({"Type", printOpType(operand.type).c_str()}, curOpItem); switch (operand.type) { case X86_OP_REG: add({"Register", printReg(disassembler_->handle(), operand.reg).c_str()}, curOpItem); break; case X86_OP_IMM: add({"Immediate", toHex(operand.imm).c_str()}, curOpItem); break; case X86_OP_MEM: add({"Segment", printReg(disassembler_->handle(), operand.mem.segment, true).c_str()}, curOpItem); add({"Base", printReg(disassembler_->handle(), operand.mem.base, true).c_str()}, curOpItem); add({"Index", printReg(disassembler_->handle(), operand.mem.index, true).c_str()}, curOpItem); add({"Scale", std::to_string(operand.mem.scale).c_str()}, curOpItem); add({"Displacement", toHex(operand.mem.disp, true).c_str()}, curOpItem); break; default: break; } add({"Size", (std::to_string(operand.size * 8) + " bit").c_str()}, curOpItem); #if CS_API_MAJOR >= 4 add({"Access", printAccessMode(operand.access).c_str()}, curOpItem); #endif if (operand.avx_bcast) { add({"AVX Broadcast", printAVX_Bcast(operand.avx_bcast).c_str()}, curOpItem); } add({"AVX opmask", (operand.avx_zero_opmask ? "zeroing" : "merging")}, curOpItem); } #elif defined(EDB_ARM32) add({"User mode regs", insn->detail->arm.usermode ? "True" : "False"}); add({"Vector size", std::to_string(insn->detail->arm.vector_size).c_str()}); // TODO: vector_data // TODO: cps_mode // TODO: cps_flag add({"Condition", printCond(insn->detail->arm.cc).c_str()}); add({"Updates flags", insn->detail->arm.update_flags ? "True" : "False"}); add({"Write-back", insn->detail->arm.writeback ? "True" : "False"}); // TODO: mem_barrier add({"Operands"}); auto *const operands = tree_->topLevelItem(tree_->topLevelItemCount() - 1); for (int op = 0; op < insn->detail->arm.op_count; ++op) { const auto &operand = insn->detail->arm.operands[op]; add({("#" + std::to_string(op + 1)).c_str()}, operands); auto *const curOpItem = operands->child(op); if (operand.vector_index != -1) { add({"Vector index", std::to_string(operand.vector_index).c_str()}, curOpItem); } if (operand.shift.type) { add({"Shift type", printShiftType(operand.shift.type).c_str()}, curOpItem); add({"Shift", std::to_string(operand.shift.value).c_str()}, curOpItem); } add({"Type", printOpType(operand.type).c_str()}, curOpItem); switch (operand.type) { case ARM_OP_SYSREG: case ARM_OP_REG: add({"Register", printReg(disassembler_->handle(), operand.reg).c_str()}, curOpItem); break; case ARM_OP_CIMM: case ARM_OP_PIMM: case ARM_OP_IMM: add({"Immediate", toHex(util::to_unsigned(operand.imm)).c_str()}, curOpItem); break; case ARM_OP_FP: add({"Float", toFloatString(operand.fp).c_str()}, curOpItem); break; case ARM_OP_MEM: add({"Base", printReg(disassembler_->handle(), operand.mem.base, true).c_str()}, curOpItem); add({"Index", printReg(disassembler_->handle(), operand.mem.index, true).c_str()}, curOpItem); add({"Scale", std::to_string(operand.mem.scale).c_str()}, curOpItem); add({"Displacement", toHex(operand.mem.disp, true).c_str()}, curOpItem); #if CS_API_MAJOR >= 4 add({"Left shift", std::to_string(operand.mem.lshift).c_str()}, curOpItem); #endif break; case ARM_OP_SETEND: add({"Type", printOpType(operand.setend).c_str()}, curOpItem); break; } add({"Subtracted", operand.subtracted ? "True" : "False"}, curOpItem); #if CS_API_MAJOR >= 4 add({"Access", printAccessMode(operand.access).c_str()}, curOpItem); if (operand.neon_lane != -1) { add({"NEON lane", std::to_string(operand.neon_lane).c_str()}, curOpItem); } #endif } #endif } tree_->expandAll(); tree_->resizeColumnToContents(0); } else { QMessageBox::critical( edb::v1::debugger_ui, tr("Error reading instruction"), tr("Failed to read instruction at address %1").arg(edb::v1::format_pointer(address_))); throw InstructionReadFailure{}; } } /** * @brief InstructionDialog::compareDisassemblers */ void InstructionDialog::compareDisassemblers() { std::ostringstream message; message << "capstone:\n"; if (const auto insn = static_cast(insn_)) { message << address_.toHexString().toUpper().toStdString() << " " << printBytes(insn->bytes, insn->size) << " " << insn->mnemonic << " " << insn->op_str; } else { message << address_.toHexString().toUpper().toStdString() << " " << printBytes(insnBytes_.data(), 1) << " db " << toHex(insnBytes_[0]); } #if defined(EDB_X86) || defined(EDB_X86_64) message << "\n\n"; message << "ndisasm:\n"; message << runNDISASM(insnBytes_, address_); #endif message << "\n\n"; message << "objdump:\n"; message << runOBJDUMP(insnBytes_, address_); #if defined(EDB_X86) || defined(EDB_X86_64) message << "\n\n"; message << "objconv:\n"; message << runOBJCONV(insnBytes_, address_); #endif buttonCompare_->deleteLater(); constexpr int initialTextBrowserSize = 200; auto splitter = new QSplitter(this); splitter->setOrientation(Qt::Vertical); splitter->addWidget(tree_); splitter->addWidget(disassemblies_ = new QTextBrowser); splitter->setSizes({height() - initialTextBrowserSize, initialTextBrowserSize}); layout_->addWidget(splitter); QFont font(disassemblies_->font()); font.setStyleHint(QFont::TypeWriter); font.setFamily("Monospace"); disassemblies_->setFont(font); disassemblies_->setText(message.str().c_str()); } /** * @brief InstructionDialog::~InstructionDialog */ InstructionDialog::~InstructionDialog() { delete disassembler_; } /** * @brief Plugin::showDialog */ void Plugin::showDialog() const { try { const auto dialog = new InstructionDialog(edb::v1::debugger_ui); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->resize(800, 500); // TODO: make it depend on size hints dialog->show(); } catch (Disassembler::InitFailure &ex) { QMessageBox::critical( edb::v1::debugger_ui, tr("Capstone error"), tr("Failed to initialize Capstone: %1").arg(ex.error)); } catch (...) { } } } edb-debugger/plugins/InstructionInspector/Plugin.h0000644000175000017500000000404213765535463022231 0ustar eteraneteran/* Copyright (C) 2016 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef INSTRUCTION_INSPECTOR_PLUGIN_H_20160418_ #define INSTRUCTION_INSPECTOR_PLUGIN_H_20160418_ #include "IPlugin.h" #include "edb.h" #include #include class QTreeWidget; class QPushButton; class QVBoxLayout; class QTextBrowser; namespace InstructionInspector { class Disassembler; class InstructionDialog : public QDialog { Q_OBJECT public: struct DisassemblyFailure {}; struct InstructionReadFailure {}; public: explicit InstructionDialog(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~InstructionDialog() override; private Q_SLOTS: void compareDisassemblers(); private: QTreeWidget *tree_ = nullptr; QPushButton *buttonCompare_ = nullptr; QVBoxLayout *layout_ = nullptr; QTextBrowser *disassemblies_ = nullptr; void *insn_ = nullptr; Disassembler *disassembler_ = nullptr; edb::address_t address_; std::vector insnBytes_; }; class Plugin : public QObject, public IPlugin { Q_OBJECT Q_INTERFACES(IPlugin) Q_PLUGIN_METADATA(IID "edb.IPlugin/1.0") Q_CLASSINFO("author", "Ruslan Kabatsayev") Q_CLASSINFO("email", "b7.10110111@gmail.com") public: explicit Plugin(QObject *parent = nullptr); QMenu *menu(QWidget *parent = nullptr) override; QList cpuContextMenu() override; private: void showDialog() const; private: QAction *menuAction_ = nullptr; }; } #endif edb-debugger/plugins/OpcodeSearcher/0000755000175000017500000000000013765535463017300 5ustar eteraneteranedb-debugger/plugins/OpcodeSearcher/DialogResults.cpp0000644000175000017500000000260513765535463022570 0ustar eteraneteran #include "DialogResults.h" #include "edb.h" #include namespace OpcodeSearcherPlugin { /** * @brief DialogResults::DialogResults * @param parent * @param f */ DialogResults::DialogResults(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f) { ui.setupUi(this); ui.tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); model_ = new ResultsModel(this); filterModel_ = new QSortFilterProxyModel(this); filterModel_->setFilterKeyColumn(1); filterModel_->setSourceModel(model_); ui.tableView->setModel(filterModel_); connect(ui.textFilter, &QLineEdit::textChanged, filterModel_, &QSortFilterProxyModel::setFilterFixedString); } /** * @brief DialogResults::addResult * @param result */ void DialogResults::addResult(const ResultsModel::Result &result) { model_->addResult(result); } /** * @brief DialogResults::on_tableView_doubleClicked * @param index */ void DialogResults::on_tableView_doubleClicked(const QModelIndex &index) { if (index.isValid()) { const QModelIndex realIndex = filterModel_->mapToSource(index); if (realIndex.isValid()) { if (auto item = static_cast(realIndex.internalPointer())) { edb::v1::jump_to_address(item->address); } } } } /** * @brief DialogResults::resultCount * @return */ int DialogResults::resultCount() const { return model_->rowCount(); } } edb-debugger/plugins/OpcodeSearcher/OpcodeSearcher.h0000644000175000017500000000252113765535463022337 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef OPCODE_SEARCHER_H_20060430_ #define OPCODE_SEARCHER_H_20060430_ #include "IPlugin.h" class QMenu; class QDialog; namespace OpcodeSearcherPlugin { class OpcodeSearcher : public QObject, public IPlugin { Q_OBJECT Q_INTERFACES(IPlugin) Q_PLUGIN_METADATA(IID "edb.IPlugin/1.0") Q_CLASSINFO("author", "Evan Teran") Q_CLASSINFO("url", "http://www.codef00.com") public: explicit OpcodeSearcher(QObject *parent = nullptr); ~OpcodeSearcher() override; public: QMenu *menu(QWidget *parent = nullptr) override; public Q_SLOTS: void showMenu(); private: QMenu *menu_ = nullptr; QPointer dialog_ = nullptr; }; } #endif edb-debugger/plugins/OpcodeSearcher/OpcodeSearcher.cpp0000644000175000017500000000303413765535463022672 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "OpcodeSearcher.h" #include "DialogOpcodes.h" #include "edb.h" #include namespace OpcodeSearcherPlugin { /** * @brief OpcodeSearcher::OpcodeSearcher * @param parent */ OpcodeSearcher::OpcodeSearcher(QObject *parent) : QObject(parent) { } /** * @brief OpcodeSearcher::~OpcodeSearcher */ OpcodeSearcher::~OpcodeSearcher() { delete dialog_; } /** * @brief OpcodeSearcher::menu * @param parent * @return */ QMenu *OpcodeSearcher::menu(QWidget *parent) { Q_ASSERT(parent); if (!menu_) { menu_ = new QMenu(tr("OpcodeSearcher"), parent); menu_->addAction(tr("&Opcode Search"), this, SLOT(showMenu()), QKeySequence(tr("Ctrl+O"))); } return menu_; } /** * @brief OpcodeSearcher::showMenu */ void OpcodeSearcher::showMenu() { if (!dialog_) { dialog_ = new DialogOpcodes(edb::v1::debugger_ui); } dialog_->show(); } } edb-debugger/plugins/OpcodeSearcher/DialogOpcodes.ui0000644000175000017500000001016413765535463022355 0ustar eteraneteran Evan Teran OpcodeSearcherPlugin::DialogOpcodes 0 0 750 400 Opcode Search Regions To Search: What To Search For 6 &Jump Equivalent true Qt::Vertical 20 40 Filter true Monospace QAbstractItemView::NoEditTriggers true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows true false true QDialogButtonBox::Close 0 Qt::Horizontal txtSearch tableView radioButton comboBox buttonBox accepted() OpcodeSearcherPlugin::DialogOpcodes accept() 190 294 172 277 buttonBox rejected() OpcodeSearcherPlugin::DialogOpcodes reject() 294 306 303 276 edb-debugger/plugins/OpcodeSearcher/ResultsModel.cpp0000644000175000017500000000744713765535463022442 0ustar eteraneteran/* Copyright (C) 2006 - 2019 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "ResultsModel.h" #include "edb.h" #include namespace OpcodeSearcherPlugin { /** * @brief ResultsModel::ResultsModel * @param parent */ ResultsModel::ResultsModel(QObject *parent) : QAbstractItemModel(parent) { } /** * @brief ResultsModel::headerData * @param section * @param orientation * @param role * @return */ QVariant ResultsModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { switch (section) { case 0: return tr("Address"); case 1: return tr("Instruction"); } } return QVariant(); } /** * @brief ResultsModel::data * @param index * @param role * @return */ QVariant ResultsModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } const Result &result = results_[index.row()]; if (role == Qt::DisplayRole) { switch (index.column()) { case 0: return edb::v1::format_pointer(result.address); case 1: return result.instruction; default: return QVariant(); } } return QVariant(); } /** * @brief ResultsModel::addResult * @param r */ void ResultsModel::addResult(const Result &r) { beginInsertRows(QModelIndex(), rowCount(), rowCount()); results_.push_back(r); endInsertRows(); } /** * @brief ResultsModel::index * @param row * @param column * @param parent * @return */ QModelIndex ResultsModel::index(int row, int column, const QModelIndex &parent) const { Q_UNUSED(parent) if (row >= results_.size()) { return QModelIndex(); } if (column >= 2) { return QModelIndex(); } if (row >= 0) { return createIndex(row, column, const_cast(&results_[row])); } else { return createIndex(row, column); } } /** * @brief ResultsModel::parent * @param index * @return */ QModelIndex ResultsModel::parent(const QModelIndex &index) const { Q_UNUSED(index) return QModelIndex(); } /** * @brief ResultsModel::rowCount * @param parent * @return */ int ResultsModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) return results_.size(); } /** * @brief ResultsModel::columnCount * @param parent * @return */ int ResultsModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent) return 2; } /** * @brief ResultsModel::sort * @param column * @param order */ void ResultsModel::sort(int column, Qt::SortOrder order) { if (order == Qt::AscendingOrder) { switch (column) { case 0: std::sort(results_.begin(), results_.end(), [](const Result &s1, const Result &s2) { return s1.address < s2.address; }); break; case 1: std::sort(results_.begin(), results_.end(), [](const Result &s1, const Result &s2) { return s1.instruction < s2.instruction; }); break; } } else { switch (column) { case 0: std::sort(results_.begin(), results_.end(), [](const Result &s1, const Result &s2) { return s1.address > s2.address; }); break; case 1: std::sort(results_.begin(), results_.end(), [](const Result &s1, const Result &s2) { return s1.instruction > s2.instruction; }); break; } } Q_EMIT dataChanged(createIndex(0, 0, nullptr), createIndex(-1, -1, nullptr)); } } edb-debugger/plugins/OpcodeSearcher/ResultsModel.h0000644000175000017500000000344613765535463022102 0ustar eteraneteran/* Copyright (C) 2006 - 2019 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef OPCODES_RESULTS_MODEL_H_20191119_ #define OPCODES_RESULTS_MODEL_H_20191119_ #include "Function.h" #include "Types.h" #include #include namespace OpcodeSearcherPlugin { class ResultsModel : public QAbstractItemModel { Q_OBJECT public: struct Result { edb::address_t address = 0; QString instruction; }; public: explicit ResultsModel(QObject *parent = nullptr); public: QVariant data(const QModelIndex &index, int role) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &index) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; public: void addResult(const Result &r); public: const QVector &results() const { return results_; } private: QVector results_; }; } #endif edb-debugger/plugins/OpcodeSearcher/CMakeLists.txt0000644000175000017500000000165113765535463022043 0ustar eteraneterancmake_minimum_required (VERSION 3.1) include("GNUInstallDirs") set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(PluginName "OpcodeSearcher") find_package(Qt5 5.0.0 REQUIRED Widgets) add_library(${PluginName} SHARED DialogOpcodes.cpp DialogOpcodes.h DialogOpcodes.ui DialogResults.cpp DialogResults.h DialogResults.ui OpcodeSearcher.cpp OpcodeSearcher.h ResultsModel.cpp ResultsModel.h ) target_link_libraries(${PluginName} Qt5::Widgets edb) install (TARGETS ${PluginName} DESTINATION ${CMAKE_INSTALL_LIBDIR}/edb) set_property(TARGET ${PluginName} PROPERTY CXX_EXTENSIONS OFF) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD 14) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD_REQUIRED ON) set_property(TARGET ${PluginName} PROPERTY LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) set_property(TARGET ${PluginName} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) edb-debugger/plugins/OpcodeSearcher/DialogResults.h0000644000175000017500000000135013765535463022231 0ustar eteraneteran #ifndef OPCODE_SEARCHER_DIALOG_RESULTS_H_20191119_ #define OPCODE_SEARCHER_DIALOG_RESULTS_H_20191119_ #include "ResultsModel.h" #include "ui_DialogResults.h" #include #include class QSortFilterProxyModel; namespace OpcodeSearcherPlugin { class DialogResults : public QDialog { Q_OBJECT public: explicit DialogResults(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); public: void addResult(const ResultsModel::Result &result); private Q_SLOTS: void on_tableView_doubleClicked(const QModelIndex &index); public: int resultCount() const; private: Ui::DialogResults ui; ResultsModel *model_ = nullptr; QSortFilterProxyModel *filterModel_ = nullptr; }; } #endif edb-debugger/plugins/OpcodeSearcher/DialogResults.ui0000644000175000017500000000546613765535463022433 0ustar eteraneteran OpcodeSearcherPlugin::DialogResults 0 0 690 315 Opcode Results Monospace Qt::CustomContextMenu QAbstractItemView::NoEditTriggers true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows true false true false Filter true Qt::Horizontal QDialogButtonBox::Close buttonBox accepted() OpcodeSearcherPlugin::DialogResults accept() 224 326 157 274 buttonBox rejected() OpcodeSearcherPlugin::DialogResults reject() 292 332 286 274 edb-debugger/plugins/OpcodeSearcher/DialogOpcodes.cpp0000644000175000017500000006172113765535463022527 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "DialogOpcodes.h" #include "DialogResults.h" #include "IDebugger.h" #include "IProcess.h" #include "IRegion.h" #include "Instruction.h" #include "MemoryRegions.h" #include "ResultsModel.h" #include "edb.h" #include "util/Container.h" #include "util/Math.h" #include #include #include #include #include #include #include #include namespace OpcodeSearcherPlugin { namespace { using InstructionList = std::vector; // we currently only support opcodes sequences up to 8 bytes big using OpcodeData = std::array; /** * @brief add_result * @param resultsDialog * @param instructions * @param rva */ void add_result(DialogResults *resultsDialog, const InstructionList &instructions, edb::address_t rva) { if (!instructions.empty()) { auto it = instructions.begin(); const edb::Instruction *inst1 = *it++; auto instruction_string = QString::fromStdString(edb::v1::formatter().toString(*inst1)); for (; it != instructions.end(); ++it) { const edb::Instruction *inst = *it; instruction_string.append(QString("; %1").arg(QString::fromStdString(edb::v1::formatter().toString(*inst)))); } resultsDialog->addResult({rva, instruction_string}); } } /** * @brief test_deref_reg_to_ip * @param resultsDialog * @param data * @param start_address */ template void test_deref_reg_to_ip(DialogResults *resultsDialog, const OpcodeData &data, edb::address_t start_address) { const uint8_t *p = data.data(); const uint8_t *last = p + sizeof(data); edb::Instruction inst(p, last, 0); if (inst) { if (is_call(inst) || is_jump(inst)) { const auto op1 = inst[0]; if (is_expression(op1)) { if (op1->mem.disp == 0) { if (op1->mem.base == Register && op1->mem.index == X86_REG_INVALID && op1->mem.scale == 1) { add_result(resultsDialog, {&inst}, start_address); return; } if (op1->mem.index == Register && op1->mem.base == X86_REG_INVALID && op1->mem.scale == 1) { add_result(resultsDialog, {&inst}, start_address); return; } } } } } } /** * @brief test_reg_to_ip * @param resultsDialog * @param data * @param start_address */ template void test_reg_to_ip(DialogResults *resultsDialog, const OpcodeData &data, edb::address_t start_address) { const uint8_t *p = data.data(); const uint8_t *last = p + sizeof(data); edb::Instruction inst(p, last, 0); if (inst) { if (is_call(inst) || is_jump(inst)) { const auto op1 = inst[0]; if (is_register(op1)) { if (op1->reg == Register) { add_result(resultsDialog, {&inst}, start_address); return; } } } else { const auto op1 = inst[0]; switch (inst.operation()) { case X86_INS_PUSH: if (is_register(op1)) { if (op1->reg == Register) { p += inst.byteSize(); edb::Instruction inst2(p, last, 0); if (inst2) { const auto op2 = inst2[0]; if (is_ret(inst2)) { add_result(resultsDialog, {&inst, &inst2}, start_address); } else { switch (inst2.operation()) { case X86_INS_JMP: case X86_INS_CALL: if (is_expression(op2)) { if (op2->mem.disp == 0) { if (op2->mem.base == StackRegister && op2->mem.index == X86_REG_INVALID) { add_result(resultsDialog, {&inst, &inst2}, start_address); return; } if (op2->mem.index == StackRegister && op2->mem.base == X86_REG_INVALID) { add_result(resultsDialog, {&inst, &inst2}, start_address); return; } } } break; default: break; } } } } } break; default: break; } } } } /** * @brief test_esp_add_0 * @param resultsDialog * @param data * @param start_address */ template void test_esp_add_0(DialogResults *resultsDialog, const OpcodeData &data, edb::address_t start_address) { const uint8_t *p = data.data(); const uint8_t *last = p + sizeof(data); edb::Instruction inst(p, last, 0); if (inst) { const auto op1 = inst[0]; if (is_ret(inst)) { add_result(resultsDialog, {&inst}, start_address); } else if (is_call(inst) || is_jump(inst)) { if (is_expression(op1)) { if (op1->mem.disp == 0) { if (op1->mem.base == StackRegister && op1->mem.index == X86_REG_INVALID) { add_result(resultsDialog, {&inst}, start_address); return; } if (op1->mem.index == StackRegister && op1->mem.base == X86_REG_INVALID) { add_result(resultsDialog, {&inst}, start_address); return; } } } } else { switch (inst.operation()) { case X86_INS_POP: if (is_register(op1)) { p += inst.byteSize(); edb::Instruction inst2(p, last, 0); if (inst2) { const auto op2 = inst2[0]; switch (inst2.operation()) { case X86_INS_JMP: case X86_INS_CALL: if (is_register(op2)) { if (op1->reg == op2->reg) { add_result(resultsDialog, {&inst, &inst2}, start_address); } } break; default: break; } } } break; default: break; } } } } /** * @brief test_esp_add_regx1 * @param resultsDialog * @param data * @param start_address */ template void test_esp_add_regx1(DialogResults *resultsDialog, const OpcodeData &data, edb::address_t start_address) { const uint8_t *p = data.data(); const uint8_t *last = p + sizeof(data); edb::Instruction inst(p, last, 0); if (inst) { const auto op1 = inst[0]; if (is_call(inst) || is_jump(inst)) { if (is_expression(op1)) { if (op1->mem.disp == 4) { if (op1->mem.base == StackRegister && op1->mem.index == X86_REG_INVALID) { add_result(resultsDialog, {&inst}, start_address); } else if (op1->mem.base == X86_REG_INVALID && op1->mem.index == StackRegister && op1->mem.scale == 1) { add_result(resultsDialog, {&inst}, start_address); } } } } else { switch (inst.operation()) { case X86_INS_POP: if (!is_register(op1) || op1->reg != StackRegister) { p += inst.byteSize(); edb::Instruction inst2(p, last, 0); if (inst2) { if (is_ret(inst2)) { add_result(resultsDialog, {&inst, &inst2}, start_address); } } } break; case X86_INS_SUB: if (is_register(op1) && op1->reg == StackRegister) { const auto op2 = inst[1]; if (is_expression(op2)) { if (op2->imm == -static_cast(sizeof(edb::reg_t))) { p += inst.byteSize(); edb::Instruction inst2(p, last, 0); if (inst2) { if (is_ret(inst2)) { add_result(resultsDialog, {&inst, &inst2}, start_address); } } } } } break; case X86_INS_ADD: if (is_register(op1) && op1->reg == StackRegister) { const auto op2 = inst[1]; if (is_expression(op2)) { if (op2->imm == sizeof(edb::reg_t)) { p += inst.byteSize(); edb::Instruction inst2(p, last, 0); if (inst2) { if (is_ret(inst2)) { add_result(resultsDialog, {&inst, &inst2}, start_address); } } } } } break; default: break; } } } } /** * @brief test_esp_add_regx2 * @param resultsDialog * @param data * @param start_address */ template void test_esp_add_regx2(DialogResults *resultsDialog, const OpcodeData &data, edb::address_t start_address) { const uint8_t *p = data.data(); const uint8_t *last = p + sizeof(data); edb::Instruction inst(p, last, 0); if (inst) { const auto op1 = inst[0]; if (is_call(inst) || is_jump(inst)) { if (is_expression(op1)) { if (op1->mem.disp == (sizeof(edb::reg_t) * 2)) { if (op1->mem.base == StackRegister && op1->mem.index == X86_REG_INVALID) { add_result(resultsDialog, {&inst}, start_address); } else if (op1->mem.base == X86_REG_INVALID && op1->mem.index == StackRegister && op1->mem.scale == 1) { add_result(resultsDialog, {&inst}, start_address); } } } } else { switch (inst.operation()) { case X86_INS_POP: if (!is_register(op1) || op1->reg != StackRegister) { p += inst.byteSize(); edb::Instruction inst2(p, last, 0); if (inst2) { const auto op2 = inst2[0]; switch (inst2.operation()) { case X86_INS_POP: if (!is_register(op2) || op2->reg != StackRegister) { p += inst2.byteSize(); edb::Instruction inst3(p, last, 0); if (inst3) { if (is_ret(inst3)) { add_result(resultsDialog, {&inst, &inst2, &inst3}, start_address); } } } break; default: break; } } } break; case X86_INS_SUB: if (is_register(op1) && op1->reg == StackRegister) { const auto op2 = inst[1]; if (is_expression(op2)) { if (op2->imm == -static_cast(sizeof(edb::reg_t) * 2)) { p += inst.byteSize(); edb::Instruction inst2(p, last, 0); if (inst2) { if (is_ret(inst2)) { add_result(resultsDialog, {&inst, &inst2}, start_address); } } } } } break; case X86_INS_ADD: if (is_register(op1) && op1->reg == StackRegister) { const auto op2 = inst[1]; if (is_expression(op2)) { if (op2->imm == (sizeof(edb::reg_t) * 2)) { p += inst.byteSize(); edb::Instruction inst2(p, last, 0); if (inst2) { if (is_ret(inst2)) { add_result(resultsDialog, {&inst, &inst2}, start_address); } } } } } break; default: break; } } } } /** * @brief test_esp_sub_regx1 * @param resultsDialog * @param data * @param start_address */ template void test_esp_sub_regx1(DialogResults *resultsDialog, const OpcodeData &data, edb::address_t start_address) { const uint8_t *p = data.data(); const uint8_t *last = p + sizeof(data); edb::Instruction inst(p, last, 0); if (inst) { const auto op1 = inst[0]; if (is_call(inst) || is_jump(inst)) { if (is_expression(op1)) { if (op1->mem.disp == -static_cast(sizeof(edb::reg_t))) { if (op1->mem.base == StackRegister && op1->mem.index == X86_REG_INVALID) { add_result(resultsDialog, {&inst}, start_address); } else if (op1->mem.base == X86_REG_INVALID && op1->mem.index == StackRegister && op1->mem.scale == 1) { add_result(resultsDialog, {&inst}, start_address); } } } } else { switch (inst.operation()) { case X86_INS_SUB: if (is_register(op1) && op1->reg == StackRegister) { const auto op2 = inst[1]; if (is_expression(op2)) { if (op2->imm == static_cast(sizeof(edb::reg_t))) { p += inst.byteSize(); edb::Instruction inst2(p, last, 0); if (inst2) { if (is_ret(inst2)) { add_result(resultsDialog, {&inst, &inst2}, start_address); } } } } } break; case X86_INS_ADD: if (is_register(op1) && op1->reg == StackRegister) { const auto op2 = inst[1]; if (is_expression(op2)) { if (op2->imm == -static_cast(sizeof(edb::reg_t))) { p += inst.byteSize(); edb::Instruction inst2(p, last, 0); if (inst2) { if (is_ret(inst2)) { add_result(resultsDialog, {&inst, &inst2}, start_address); } } } } } break; default: break; } } } } /** * @brief run_tests * @param resultsDialog * @param classtype * @param opcode * @param address */ void run_tests(DialogResults *resultsDialog, int classtype, const OpcodeData &opcode, edb::address_t address) { #if defined(EDB_X86) || defined(EDB_X86_64) if (edb::v1::debuggeeIs32Bit()) { switch (classtype) { case 1: test_reg_to_ip(resultsDialog, opcode, address); break; case 2: test_reg_to_ip(resultsDialog, opcode, address); break; case 3: test_reg_to_ip(resultsDialog, opcode, address); break; case 4: test_reg_to_ip(resultsDialog, opcode, address); break; case 5: test_reg_to_ip(resultsDialog, opcode, address); break; case 6: test_reg_to_ip(resultsDialog, opcode, address); break; case 7: test_reg_to_ip(resultsDialog, opcode, address); break; case 8: test_reg_to_ip(resultsDialog, opcode, address); break; case 17: test_reg_to_ip(resultsDialog, opcode, address); test_reg_to_ip(resultsDialog, opcode, address); test_reg_to_ip(resultsDialog, opcode, address); test_reg_to_ip(resultsDialog, opcode, address); test_reg_to_ip(resultsDialog, opcode, address); test_reg_to_ip(resultsDialog, opcode, address); test_reg_to_ip(resultsDialog, opcode, address); test_reg_to_ip(resultsDialog, opcode, address); break; case 18: // [ESP] -> EIP test_esp_add_0(resultsDialog, opcode, address); break; case 19: // [ESP + 4] -> EIP test_esp_add_regx1(resultsDialog, opcode, address); break; case 20: // [ESP + 8] -> EIP test_esp_add_regx2(resultsDialog, opcode, address); break; case 21: // [ESP - 4] -> EIP test_esp_sub_regx1(resultsDialog, opcode, address); break; } } else { switch (classtype) { case 1: test_reg_to_ip(resultsDialog, opcode, address); break; case 2: test_reg_to_ip(resultsDialog, opcode, address); break; case 3: test_reg_to_ip(resultsDialog, opcode, address); break; case 4: test_reg_to_ip(resultsDialog, opcode, address); break; case 5: test_reg_to_ip(resultsDialog, opcode, address); break; case 6: test_reg_to_ip(resultsDialog, opcode, address); break; case 7: test_reg_to_ip(resultsDialog, opcode, address); break; case 8: test_reg_to_ip(resultsDialog, opcode, address); break; case 9: test_reg_to_ip(resultsDialog, opcode, address); break; case 10: test_reg_to_ip(resultsDialog, opcode, address); break; case 11: test_reg_to_ip(resultsDialog, opcode, address); break; case 12: test_reg_to_ip(resultsDialog, opcode, address); break; case 13: test_reg_to_ip(resultsDialog, opcode, address); break; case 14: test_reg_to_ip(resultsDialog, opcode, address); break; case 15: test_reg_to_ip(resultsDialog, opcode, address); break; case 16: test_reg_to_ip(resultsDialog, opcode, address); break; case 17: test_reg_to_ip(resultsDialog, opcode, address); test_reg_to_ip(resultsDialog, opcode, address); test_reg_to_ip(resultsDialog, opcode, address); test_reg_to_ip(resultsDialog, opcode, address); test_reg_to_ip(resultsDialog, opcode, address); test_reg_to_ip(resultsDialog, opcode, address); test_reg_to_ip(resultsDialog, opcode, address); test_reg_to_ip(resultsDialog, opcode, address); test_reg_to_ip(resultsDialog, opcode, address); test_reg_to_ip(resultsDialog, opcode, address); test_reg_to_ip(resultsDialog, opcode, address); test_reg_to_ip(resultsDialog, opcode, address); test_reg_to_ip(resultsDialog, opcode, address); test_reg_to_ip(resultsDialog, opcode, address); test_reg_to_ip(resultsDialog, opcode, address); test_reg_to_ip(resultsDialog, opcode, address); break; case 18: // [ESP] -> EIP test_esp_add_0(resultsDialog, opcode, address); break; case 19: // [ESP + 4] -> EIP test_esp_add_regx1(resultsDialog, opcode, address); break; case 20: // [ESP + 8] -> EIP test_esp_add_regx2(resultsDialog, opcode, address); break; case 21: // [ESP - 4] -> EIP test_esp_sub_regx1(resultsDialog, opcode, address); break; case 22: test_deref_reg_to_ip(resultsDialog, opcode, address); break; case 23: test_deref_reg_to_ip(resultsDialog, opcode, address); break; case 24: test_deref_reg_to_ip(resultsDialog, opcode, address); break; case 25: test_deref_reg_to_ip(resultsDialog, opcode, address); break; case 26: test_deref_reg_to_ip(resultsDialog, opcode, address); break; case 28: test_deref_reg_to_ip(resultsDialog, opcode, address); break; case 29: test_deref_reg_to_ip(resultsDialog, opcode, address); break; case 30: test_deref_reg_to_ip(resultsDialog, opcode, address); break; case 31: test_deref_reg_to_ip(resultsDialog, opcode, address); break; case 32: test_deref_reg_to_ip(resultsDialog, opcode, address); break; case 33: test_deref_reg_to_ip(resultsDialog, opcode, address); break; case 34: test_deref_reg_to_ip(resultsDialog, opcode, address); break; case 35: test_deref_reg_to_ip(resultsDialog, opcode, address); break; case 36: test_deref_reg_to_ip(resultsDialog, opcode, address); break; case 37: test_deref_reg_to_ip(resultsDialog, opcode, address); break; } } #elif defined(EDB_ARM32) // TODO(eteran): implement #elif defined(EDB_ARM64) // TODO(eteran): implement #endif } } /** * @brief DialogOpcodes::DialogOpcodes * @param parent * @param f */ DialogOpcodes::DialogOpcodes(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f) { ui.setupUi(this); ui.tableView->verticalHeader()->hide(); ui.tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); filterModel_ = new QSortFilterProxyModel(this); connect(ui.txtSearch, &QLineEdit::textChanged, filterModel_, &QSortFilterProxyModel::setFilterFixedString); buttonFind_ = new QPushButton(QIcon::fromTheme("edit-find"), tr("Find")); connect(buttonFind_, &QPushButton::clicked, this, [this]() { buttonFind_->setEnabled(false); ui.progressBar->setValue(0); doFind(); ui.progressBar->setValue(100); buttonFind_->setEnabled(true); }); ui.buttonBox->addButton(buttonFind_, QDialogButtonBox::ActionRole); } /** * @brief DialogOpcodes::showEvent */ void DialogOpcodes::showEvent(QShowEvent *) { filterModel_->setFilterKeyColumn(3); filterModel_->setSourceModel(&edb::v1::memory_regions()); ui.tableView->setModel(filterModel_); ui.progressBar->setValue(0); ui.comboBox->clear(); #if defined(EDB_X86) || defined(EDB_X86_64) if (edb::v1::debuggeeIs64Bit()) { ui.comboBox->addItem("RAX -> RIP", 1); ui.comboBox->addItem("RBX -> RIP", 2); ui.comboBox->addItem("RCX -> RIP", 3); ui.comboBox->addItem("RDX -> RIP", 4); ui.comboBox->addItem("RBP -> RIP", 5); ui.comboBox->addItem("RSP -> RIP", 6); ui.comboBox->addItem("RSI -> RIP", 7); ui.comboBox->addItem("RDI -> RIP", 8); ui.comboBox->addItem("R8 -> RIP", 9); ui.comboBox->addItem("R9 -> RIP", 10); ui.comboBox->addItem("R10 -> RIP", 11); ui.comboBox->addItem("R11 -> RIP", 12); ui.comboBox->addItem("R12 -> RIP", 13); ui.comboBox->addItem("R13 -> RIP", 14); ui.comboBox->addItem("R14 -> RIP", 15); ui.comboBox->addItem("R15 -> RIP", 16); ui.comboBox->addItem("ANY REGISTER -> RIP", 17); ui.comboBox->addItem("[RSP] -> RIP", 18); ui.comboBox->addItem("[RSP + 8] -> RIP", 19); ui.comboBox->addItem("[RSP + 16] -> RIP", 20); ui.comboBox->addItem("[RSP - 8] -> RIP", 21); ui.comboBox->addItem("[RAX] -> RIP", 22); ui.comboBox->addItem("[RBX] -> RIP", 23); ui.comboBox->addItem("[RCX] -> RIP", 24); ui.comboBox->addItem("[RDX] -> RIP", 25); ui.comboBox->addItem("[RBP] -> RIP", 26); ui.comboBox->addItem("[RSI] -> RIP", 28); ui.comboBox->addItem("[RDI] -> RIP", 29); ui.comboBox->addItem("[R8] -> RIP", 30); ui.comboBox->addItem("[R9] -> RIP", 31); ui.comboBox->addItem("[R10] -> RIP", 32); ui.comboBox->addItem("[R11] -> RIP", 33); ui.comboBox->addItem("[R12] -> RIP", 34); ui.comboBox->addItem("[R13] -> RIP", 35); ui.comboBox->addItem("[R14] -> RIP", 36); ui.comboBox->addItem("[R15] -> RIP", 37); } else { ui.comboBox->addItem("EAX -> EIP", 1); ui.comboBox->addItem("EBX -> EIP", 2); ui.comboBox->addItem("ECX -> EIP", 3); ui.comboBox->addItem("EDX -> EIP", 4); ui.comboBox->addItem("EBP -> EIP", 5); ui.comboBox->addItem("ESP -> EIP", 6); ui.comboBox->addItem("ESI -> EIP", 7); ui.comboBox->addItem("EDI -> EIP", 8); ui.comboBox->addItem("ANY REGISTER -> EIP", 17); ui.comboBox->addItem("[ESP] -> EIP", 18); ui.comboBox->addItem("[ESP + 4] -> EIP", 19); ui.comboBox->addItem("[ESP + 8] -> EIP", 20); ui.comboBox->addItem("[ESP - 4] -> EIP", 21); ui.comboBox->addItem("[EAX] -> EIP", 22); ui.comboBox->addItem("[EBX] -> EIP", 23); ui.comboBox->addItem("[ECX] -> EIP", 24); ui.comboBox->addItem("[EDX] -> EIP", 25); ui.comboBox->addItem("[EBP] -> EIP", 26); ui.comboBox->addItem("[ESI] -> EIP", 28); ui.comboBox->addItem("[EDI] -> EIP", 29); } #elif defined(EDB_ARM32) // TODO(eteran): implement #elif defined(EDB_ARM64) // TODO(eteran): implement #endif } /** * @brief DialogOpcodes::doFind */ void DialogOpcodes::doFind() { const int classtype = ui.comboBox->itemData(ui.comboBox->currentIndex()).toInt(); const QItemSelectionModel *const selModel = ui.tableView->selectionModel(); const QModelIndexList sel = selModel->selectedRows(); if (sel.size() == 0) { QMessageBox::critical( this, tr("No Region Selected"), tr("You must select a region which is to be scanned for the desired opcode.")); return; } auto resultsDialog = new DialogResults(this); if (IProcess *process = edb::v1::debugger_core->process()) { for (const QModelIndex &selected_item : sel) { const QModelIndex index = filterModel_->mapToSource(selected_item); if (auto region = *reinterpret_cast *>(index.internalPointer())) { edb::address_t start_address = region->start(); edb::address_t address = region->start(); const edb::address_t end_address = region->end(); const edb::address_t orig_start = region->start(); OpcodeData shift_buffer = {}; // this will read the rest of the region size_t i = 0; while (start_address < end_address) { // create a reference to the bsa's data so we can pass it to the testXXXX functions // but only do so if we have read enough bytes to fill our shift buffer if (i >= shift_buffer.size()) { run_tests(resultsDialog, classtype, shift_buffer, address - shift_buffer.size()); } uint8_t byte; process->readBytes(start_address, &byte, 1); util::shl(shift_buffer, byte); ++start_address; ui.progressBar->setValue(util::percentage(address - orig_start, region->size())); ++address; ++i; } // test the stuff at the regions edge for (size_t i = 0; i < shift_buffer.size(); ++i) { // create a reference to the bsa's data so we can pass it to the testXXXX functions run_tests(resultsDialog, classtype, shift_buffer, address - shift_buffer.size()); // we just shift in 0's and hope it doesn't give false positives util::shl(shift_buffer, 0x00); ui.progressBar->setValue(util::percentage(address - orig_start, region->size())); ++address; } } } } if (resultsDialog->resultCount() == 0) { QMessageBox::information(this, tr("No Opcodes Found"), tr("No opcodes were found in the selected region.")); delete resultsDialog; } else { resultsDialog->show(); } } } edb-debugger/plugins/OpcodeSearcher/DialogOpcodes.h0000644000175000017500000000245213765535463022170 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DIALOG_OPCODES_H_20061101_ #define DIALOG_OPCODES_H_20061101_ #include "ui_DialogOpcodes.h" #include class QSortFilterProxyModel; namespace OpcodeSearcherPlugin { class DialogResults; class DialogOpcodes : public QDialog { Q_OBJECT public: explicit DialogOpcodes(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~DialogOpcodes() override = default; private: void doFind(); private: void showEvent(QShowEvent *event) override; private: Ui::DialogOpcodes ui; QSortFilterProxyModel *filterModel_ = nullptr; QPushButton *buttonFind_ = nullptr; }; } #endif edb-debugger/plugins/BinaryInfo/0000755000175000017500000000000013765535463016452 5ustar eteraneteranedb-debugger/plugins/BinaryInfo/OptionsPage.h0000644000175000017500000000237513765535463021062 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef OPTIONS_PAGE_H_20151113_ #define OPTIONS_PAGE_H_20151113_ #include "ui_OptionsPage.h" #include #include namespace BinaryInfoPlugin { class OptionsPage : public QWidget { Q_OBJECT public: explicit OptionsPage(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~OptionsPage() override = default; public: void showEvent(QShowEvent *event) override; public Q_SLOTS: void on_checkBox_toggled(bool checked = false); void on_txtDebugDir_textChanged(const QString &text); void on_btnDebugDir_clicked(); private: Ui::OptionsPage ui; }; } #endif edb-debugger/plugins/BinaryInfo/DialogHeader.h0000644000175000017500000000232313765535463021133 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DIALOG_HEADER_H_20190403_ #define DIALOG_HEADER_H_20190403_ #include "IRegion.h" #include "Types.h" #include "ui_DialogHeader.h" #include #include class QStringListModel; class QSortFilterProxyModel; class QModelIndex; namespace BinaryInfoPlugin { class DialogHeader : public QDialog { Q_OBJECT public: explicit DialogHeader(const std::shared_ptr ®ion, QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~DialogHeader() override = default; private: Ui::DialogHeader ui; }; } #endif edb-debugger/plugins/BinaryInfo/DialogRegions.cpp0000644000175000017500000000472213765535463021711 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "DialogRegions.h" #include "DialogHeader.h" #include "MemoryRegions.h" #include "edb.h" #include #include #include #include namespace BinaryInfoPlugin { /** * @brief DialogRegions::DialogRegions * @param parent */ DialogRegions::DialogRegions(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f) { ui.setupUi(this); ui.tableView->verticalHeader()->hide(); ui.tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); filterModel_ = new QSortFilterProxyModel(this); connect(ui.txtSearch, &QLineEdit::textChanged, filterModel_, &QSortFilterProxyModel::setFilterFixedString); buttonExplore_ = new QPushButton(QIcon::fromTheme("edit-find"), tr("Explore Header")); connect(buttonExplore_, &QPushButton::clicked, this, [this]() { const QItemSelectionModel *const selModel = ui.tableView->selectionModel(); const QModelIndexList sel = selModel->selectedRows(); if (sel.size() == 0) { QMessageBox::critical( this, tr("No Region Selected"), tr("You must select a region which is to be scanned for executable headers.")); } else { for (const QModelIndex &selected_item : sel) { const QModelIndex index = filterModel_->mapToSource(selected_item); if (auto region = *reinterpret_cast *>(index.internalPointer())) { auto dialog = new DialogHeader(region, this); dialog->show(); } } } }); ui.buttonBox->addButton(buttonExplore_, QDialogButtonBox::ActionRole); } /** * @brief DialogRegions::showEvent */ void DialogRegions::showEvent(QShowEvent *) { filterModel_->setFilterKeyColumn(3); filterModel_->setSourceModel(&edb::v1::memory_regions()); ui.tableView->setModel(filterModel_); } } edb-debugger/plugins/BinaryInfo/ELFXX.h0000644000175000017500000000264213765535463017515 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ELFXX_H_20070718_ #define ELFXX_H_20070718_ #include "IBinary.h" #include "libELF/elf_binary.h" namespace BinaryInfoPlugin { template class ELFXX : public IBinary { public: explicit ELFXX(const std::shared_ptr ®ion); ~ELFXX() override = default; public: bool native() const override; edb::address_t entryPoint() override; size_t headerSize() const override; const void *header() const override; std::vector
headers() const override; private: void validateHeader(); private: std::shared_ptr region_; ElfHeader header_; edb::address_t baseAddress_{0}; std::vector
headers_; }; using ELF32 = ELFXX; using ELF64 = ELFXX; } #endif edb-debugger/plugins/BinaryInfo/ELFXX.cpp0000644000175000017500000001200613765535463020043 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "ELFXX.h" #include "IDebugger.h" #include "IProcess.h" #include "IRegion.h" #include "MemoryRegions.h" #include "Util.h" #include "edb.h" #include "libELF/elf_phdr.h" #include "string_hash.h" #include #include #include #include #include namespace BinaryInfoPlugin { class ELFBinaryException : public std::exception { const char *what() const noexcept override = 0; }; class InvalidArguments : public ELFBinaryException { public: const char *what() const noexcept override { return "Invalid Arguments"; } }; class ReadFailure : public ELFBinaryException { public: const char *what() const noexcept override { return "Read Failure"; } }; class InvalidELF : public ELFBinaryException { public: const char *what() const noexcept override { return "Invalid ELF"; } }; class InvalidArchitecture : public ELFBinaryException { public: const char *what() const noexcept override { return "Invalid Architecture"; } }; template ELFXX::ELFXX(const std::shared_ptr ®ion) : region_(region) { using phdr_type = typename ElfHeader::elf_phdr; if (!region_) { throw InvalidArguments(); } IProcess *const process = edb::v1::debugger_core->process(); if (!process) { throw ReadFailure(); } if (!process->readBytes(region_->start(), &header_, sizeof(ElfHeader))) { throw ReadFailure(); } validateHeader(); headers_.push_back({region_->start(), header_.e_ehsize}); headers_.push_back({region_->start() + header_.e_phoff, static_cast(header_.e_phentsize * header_.e_phnum)}); auto phdr_size = header_.e_phentsize; if (phdr_size < sizeof(phdr_type)) { qDebug() << QString::number(region_->start(), 16) << "program header size less than expected"; baseAddress_ = region_->start(); return; } phdr_type phdr; auto phdr_base = region_->start() + header_.e_phoff; edb::address_t lowest = ULLONG_MAX; if (header_.e_type == ET_EXEC) { // iterate all of the program headers for (uint16_t entry = 0; entry < header_.e_phnum; ++entry) { if (!process->readBytes(phdr_base + (phdr_size * entry), &phdr, sizeof(phdr_type))) { qDebug() << "Failed to read program header"; break; } if (phdr.p_type == PT_LOAD && phdr.p_vaddr < lowest) { lowest = phdr.p_vaddr; // NOTE(eteran): they are defined to be in ascending order of vaddr break; } } } else if (header_.e_type == ET_DYN) { const QString process_executable = edb::v1::debugger_core->process()->name(); for (const std::shared_ptr &r : edb::v1::memory_regions().regions()) { if (r->executable() && r->name() == region->name()) { lowest = std::min(lowest, r->start()); } } } if (lowest == ULLONG_MAX) { qDebug() << "binary base address not found. Assuming " << QString::number(region_->start(), 16); baseAddress_ = region->start(); } else { baseAddress_ = lowest; } } template /** * @brief ELFXX::headerSize * @return the number of bytes in this executable's header */ size_t ELFXX::headerSize() const { size_t size = header_.e_ehsize; // Do the program headers immediately follow the ELF header? if (size == header_.e_phoff) { size += header_.e_phentsize * header_.e_phnum; } return size; } /** * @brief ELFXX::headers * @return a list of all headers in this binary */ template std::vector ELFXX::headers() const { return headers_; } /** * @brief ELFXX::validateHeader * * ensures that the header that we read was valid */ template void ELFXX::validateHeader() { if (std::memcmp(header_.e_ident, ELFMAG, SELFMAG) != 0) { throw InvalidELF(); } if (header_.e_ident[EI_CLASS] != ElfHeader::ELFCLASS) { throw InvalidArchitecture(); } } /** * @brief ELFXX::entryPoint * @return the entry point if any of the binary */ template edb::address_t ELFXX::entryPoint() { return header_.e_entry + baseAddress_; } /** * @brief ELFXX::header * @return a copy of the file header or nullptr if the region wasn't a valid, * known binary type */ template const void *ELFXX::header() const { return &header_; } // explicit instantiations template class ELFXX; template class ELFXX; } edb-debugger/plugins/BinaryInfo/demangle.h0000644000175000017500000000153013765535463020376 0ustar eteraneteran #ifndef EDB_DEMANGLE_H_20151113_ #define EDB_DEMANGLE_H_20151113_ #include #include #ifdef __GNUG__ #include #include #define DEMANGLING_SUPPORTED inline QString demangle(const QString &mangled) { if (!mangled.startsWith("_Z")) { return mangled; // otherwise we'll try to demangle C functions coinciding with types like "f" as "float", which is bad } int failed = 0; QStringList split = mangled.split("@"); // for cases like funcName@plt std::unique_ptr demangled(abi::__cxa_demangle(split.front().toStdString().c_str(), nullptr, nullptr, &failed), std::free); if (failed) { return mangled; } split.front() = QString(demangled.get()); return split.join("@"); } #else inline QString demangle(const QString &mangled) { return mangled; } #endif #endif edb-debugger/plugins/BinaryInfo/ELF32.cpp0000644000175000017500000000233613765535463017735 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "ELFXX.h" #include "IDebugger.h" #include "edb.h" #include "string_hash.h" namespace BinaryInfoPlugin { /** * @brief ELF32::native * @return true if this binary is native to the arch edb was built for */ template <> bool ELF32::native() const { #if defined(EDB_X86) || defined(EDB_X86_64) return edb::v1::debugger_core->cpuType() == edb::string_hash("x86"); #elif defined(EDB_ARM32) || defined(EDB_ARM64) return edb::v1::debugger_core->cpuType() == edb::string_hash("arm"); #else #error "Unsupported Architecture" #endif } } edb-debugger/plugins/BinaryInfo/CMakeLists.txt0000644000175000017500000000202013765535463021204 0ustar eteraneterancmake_minimum_required (VERSION 3.1) include("GNUInstallDirs") set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(PluginName "BinaryInfo") find_package(Qt5 5.0.0 REQUIRED Widgets) add_library(${PluginName} SHARED BinaryInfo.cpp BinaryInfo.h demangle.h DialogRegions.cpp DialogRegions.h DialogRegions.ui DialogHeader.ui DialogHeader.cpp DialogHeader.h ELF32.cpp ELF64.cpp ELFXX.cpp ELFXX.h OptionsPage.cpp OptionsPage.h OptionsPage.ui PE32.cpp PE32.h symbols.cpp symbols.h ) target_link_libraries(${PluginName} Qt5::Widgets PE ELF edb) install (TARGETS ${PluginName} DESTINATION ${CMAKE_INSTALL_LIBDIR}/edb) set_property(TARGET ${PluginName} PROPERTY CXX_EXTENSIONS OFF) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD 14) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD_REQUIRED ON) set_property(TARGET ${PluginName} PROPERTY LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) set_property(TARGET ${PluginName} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) edb-debugger/plugins/BinaryInfo/DialogRegions.ui0000644000175000017500000000532413765535463021543 0ustar eteraneteran Evan Teran BinaryInfoPlugin::DialogRegions 0 0 640 400 Header Explorer Regions: Filter true Monospace QAbstractItemView::NoEditTriggers true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows true false true QDialogButtonBox::Close txtSearch tableView buttonBox accepted() BinaryInfoPlugin::DialogRegions accept() 592 382 593 399 buttonBox rejected() BinaryInfoPlugin::DialogRegions reject() 557 369 540 399 edb-debugger/plugins/BinaryInfo/DialogRegions.h0000644000175000017500000000246413765535463021357 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DIALOG_REGIONS_H_20111128_ #define DIALOG_REGIONS_H_20111128_ #include "Types.h" #include "ui_DialogRegions.h" #include class QStringListModel; class QSortFilterProxyModel; class QModelIndex; namespace BinaryInfoPlugin { class DialogRegions : public QDialog { Q_OBJECT public: explicit DialogRegions(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~DialogRegions() override = default; private: void showEvent(QShowEvent *event) override; private: Ui::DialogRegions ui; QSortFilterProxyModel *filterModel_ = nullptr; QPushButton *buttonExplore_ = nullptr; }; } #endif edb-debugger/plugins/BinaryInfo/OptionsPage.ui0000644000175000017500000000306613765535463021246 0ustar eteraneteran BinaryInfoPlugin::OptionsPage 0 0 334 323 BinaryInfo Plugin Demangle auto-generated symbols Debug Info Directory txtDebugDir ... Qt::Vertical 20 262 edb-debugger/plugins/BinaryInfo/DialogHeader.cpp0000644000175000017500000002742013765535463021473 0ustar eteraneteran #include "DialogHeader.h" #include "ELFXX.h" #include "PE32.h" #include "QtHelper.h" #include "edb.h" namespace BinaryInfoPlugin { namespace { Q_DECLARE_NAMESPACE_TR(BinaryInfo) template QTreeWidgetItem *create_elf_magic(const Header *header) { auto item = new QTreeWidgetItem; item->setText(0, tr("Magic")); item->setText(1, QString("0x%1, %2, %3, %4") .arg(header->e_ident[EI_MAG0], 0, 16) .arg(static_cast(header->e_ident[EI_MAG1])) .arg(static_cast(header->e_ident[EI_MAG2])) .arg(static_cast(header->e_ident[EI_MAG3]))); return item; } template QTreeWidgetItem *create_elf_class(const Header *header) { auto item = new QTreeWidgetItem; item->setText(0, tr("Class")); switch (header->e_ident[EI_CLASS]) { case ELFCLASS32: item->setText(1, tr("32-bit")); break; case ELFCLASS64: item->setText(1, tr("64-bit")); break; default: item->setText(1, tr("Invalid")); break; } return item; } template QTreeWidgetItem *create_elf_data(const Header *header) { auto item = new QTreeWidgetItem; item->setText(0, tr("Data")); switch (header->e_ident[EI_DATA]) { case ELFDATA2LSB: item->setText(1, tr("2's complement, little endian")); break; case ELFDATA2MSB: item->setText(1, tr("2's complement, big endian")); break; default: item->setText(1, tr("Invalid")); break; } return item; } template QTreeWidgetItem *create_elf_version(const Header *header) { auto item = new QTreeWidgetItem; item->setText(0, tr("Version")); switch (header->e_ident[EI_VERSION]) { case EV_CURRENT: item->setText(1, tr("Current")); break; default: item->setText(1, tr("Invalid")); break; } return item; } template QTreeWidgetItem *create_elf_abi(const Header *header) { auto item = new QTreeWidgetItem; item->setText(0, tr("ABI")); switch (header->e_ident[EI_OSABI]) { case ELFOSABI_SYSV: //case ELFOSABI_NONE: // alias item->setText(1, tr("UNIX System V ABI")); break; case ELFOSABI_HPUX: item->setText(1, tr("HP-UX")); break; case ELFOSABI_NETBSD: item->setText(1, tr("NetBSD")); break; case ELFOSABI_GNU: // case ELFOSABI_LINUX: // alias item->setText(1, tr("GNU/Linux")); break; case ELFOSABI_SOLARIS: item->setText(1, tr("Sun Solaris")); break; case ELFOSABI_AIX: item->setText(1, tr("IBM AIX")); break; case ELFOSABI_IRIX: item->setText(1, tr("SGI Irix")); break; case ELFOSABI_FREEBSD: item->setText(1, tr("FreeBSD")); break; case ELFOSABI_TRU64: item->setText(1, tr("Compaq TRU64 UNIX")); break; case ELFOSABI_MODESTO: item->setText(1, tr("Novell Modesto")); break; case ELFOSABI_OPENBSD: item->setText(1, tr("OpenBSD")); break; case ELFOSABI_ARM_AEABI: item->setText(1, tr("ARM EABI")); break; case ELFOSABI_ARM: item->setText(1, tr("ARM")); break; case ELFOSABI_STANDALONE: item->setText(1, tr("Standalone (embedded) application")); break; default: item->setText(1, tr("Invalid")); break; } return item; } template QTreeWidgetItem *create_elf_abi_version(const Header *header) { auto item = new QTreeWidgetItem; item->setText(0, tr("ABI Version")); item->setText(1, QString("%1").arg(header->e_ident[EI_MAG0], 0, 10)); return item; } template QTreeWidgetItem *create_elf_type(const Header *header) { auto item = new QTreeWidgetItem; item->setText(0, tr("Type")); switch (header->e_type) { case ET_NONE: item->setText(1, tr("No file type")); break; case ET_REL: item->setText(1, tr("Relocatable file")); break; case ET_EXEC: item->setText(1, tr("Executable file")); break; case ET_DYN: item->setText(1, tr("Shared object file")); break; case ET_CORE: item->setText(1, tr("Core file")); break; default: item->setText(1, tr("")); break; } return item; } template QTreeWidgetItem *create_elf_machine(const Header *header) { auto item = new QTreeWidgetItem; item->setText(0, tr("Machine")); switch (header->e_machine) { case EM_NONE: item->setText(1, tr("No machine")); break; case EM_M32: item->setText(1, tr("AT&T WE 32100")); break; case EM_SPARC: item->setText(1, tr("SUN SPARC")); break; case EM_386: item->setText(1, tr("Intel 80386")); break; case EM_68K: item->setText(1, tr("Motorola m68k family")); break; case EM_88K: item->setText(1, tr("Motorola m88k family")); break; case EM_860: item->setText(1, tr("Intel 80860")); break; case EM_MIPS: item->setText(1, tr("MIPS R3000 big-endian")); break; case EM_S370: item->setText(1, tr("IBM System/370")); break; case EM_MIPS_RS3_LE: item->setText(1, tr("MIPS R3000 little-endian")); break; case EM_PARISC: item->setText(1, tr("HPPA")); break; case EM_VPP500: item->setText(1, tr("Fujitsu VPP500")); break; case EM_SPARC32PLUS: item->setText(1, tr("Sun's \"v8plus\"")); break; case EM_960: item->setText(1, tr("Intel 80960")); break; case EM_PPC: item->setText(1, tr("PowerPC")); break; case EM_PPC64: item->setText(1, tr("PowerPC 64-bit")); break; case EM_S390: item->setText(1, tr("IBM S390")); break; case EM_V800: item->setText(1, tr("NEC V800 series")); break; case EM_FR20: item->setText(1, tr("Fujitsu FR20")); break; case EM_RH32: item->setText(1, tr("TRW RH-32")); break; case EM_RCE: item->setText(1, tr("Motorola RCE")); break; case EM_ARM: item->setText(1, tr("ARM")); break; case EM_FAKE_ALPHA: item->setText(1, tr("Digital Alpha")); break; case EM_SH: item->setText(1, tr("Hitachi SH")); break; case EM_SPARCV9: item->setText(1, tr("SPARC v9 64-bit")); break; case EM_TRICORE: item->setText(1, tr("Siemens Tricore")); break; case EM_ARC: item->setText(1, tr("Argonaut RISC Core")); break; case EM_H8_300: item->setText(1, tr("Hitachi H8/300")); break; case EM_H8_300H: item->setText(1, tr("Hitachi H8/300H")); break; case EM_H8S: item->setText(1, tr("Hitachi H8S")); break; case EM_H8_500: item->setText(1, tr("Hitachi H8/500")); break; case EM_IA_64: item->setText(1, tr("Intel Merced")); break; case EM_MIPS_X: item->setText(1, tr("Stanford MIPS-X")); break; case EM_COLDFIRE: item->setText(1, tr("Motorola Coldfire")); break; case EM_68HC12: item->setText(1, tr("Motorola M68HC12")); break; case EM_MMA: item->setText(1, tr("Fujitsu MMA Multimedia Accelerator")); break; case EM_PCP: item->setText(1, tr("Siemens PCP")); break; case EM_NCPU: item->setText(1, tr("Sony nCPU embeeded RISC")); break; case EM_NDR1: item->setText(1, tr("Denso NDR1 microprocessor")); break; case EM_STARCORE: item->setText(1, tr("Motorola Start*Core processor")); break; case EM_ME16: item->setText(1, tr("Toyota ME16 processor")); break; case EM_ST100: item->setText(1, tr("STMicroelectronic ST100 processor")); break; case EM_TINYJ: item->setText(1, tr("Advanced Logic Corp. Tinyj emb.fam")); break; case EM_X86_64: item->setText(1, tr("AMD x86-64 architecture")); break; case EM_PDSP: item->setText(1, tr("Sony DSP Processor")); break; case EM_FX66: item->setText(1, tr("Siemens FX66 microcontroller")); break; case EM_ST9PLUS: item->setText(1, tr("STMicroelectronics ST9+ 8/16 mc")); break; case EM_ST7: item->setText(1, tr("STmicroelectronics ST7 8 bit mc")); break; case EM_68HC16: item->setText(1, tr("Motorola MC68HC16 microcontroller")); break; case EM_68HC11: item->setText(1, tr("Motorola MC68HC11 microcontroller")); break; case EM_68HC08: item->setText(1, tr("Motorola MC68HC08 microcontroller")); break; case EM_68HC05: item->setText(1, tr("Motorola MC68HC05 microcontroller")); break; case EM_SVX: item->setText(1, tr("Silicon Graphics SVx")); break; case EM_ST19: item->setText(1, tr("STMicroelectronics ST19 8 bit mc")); break; case EM_VAX: item->setText(1, tr("Digital VAX")); break; case EM_CRIS: item->setText(1, tr("Axis Communications 32-bit embedded processor")); break; case EM_JAVELIN: item->setText(1, tr("Infineon Technologies 32-bit embedded processor")); break; case EM_FIREPATH: item->setText(1, tr("Element 14 64-bit DSP Processor")); break; case EM_ZSP: item->setText(1, tr("LSI Logic 16-bit DSP Processor")); break; case EM_MMIX: item->setText(1, tr("Donald Knuth's educational 64-bit processor")); break; case EM_HUANY: item->setText(1, tr("Harvard University machine-independent object files")); break; case EM_PRISM: item->setText(1, tr("SiTera Prism")); break; case EM_AVR: item->setText(1, tr("Atmel AVR 8-bit microcontroller")); break; case EM_FR30: item->setText(1, tr("Fujitsu FR30")); break; case EM_D10V: item->setText(1, tr("Mitsubishi D10V")); break; case EM_D30V: item->setText(1, tr("Mitsubishi D30V")); break; case EM_V850: item->setText(1, tr("NEC v850")); break; case EM_M32R: item->setText(1, tr("Mitsubishi M32R")); break; case EM_MN10300: item->setText(1, tr("Matsushita MN10300")); break; case EM_MN10200: item->setText(1, tr("Matsushita MN10200")); break; case EM_PJ: item->setText(1, tr("picoJava")); break; case EM_OPENRISC: item->setText(1, tr("OpenRISC 32-bit embedded processor")); break; case EM_ARC_A5: item->setText(1, tr("ARC Cores Tangent-A5")); break; case EM_XTENSA: item->setText(1, tr("Tensilica Xtensa Architecture")); break; default: item->setText(1, tr("Unknown")); break; } return item; } template QTreeWidgetItem *create_elf_object_version(const Header *header) { auto item = new QTreeWidgetItem; item->setText(0, tr("Object File Version")); item->setText(1, QString("%1").arg(header->e_version, 0, 10)); return item; } template QTreeWidgetItem *create_elf_entry_point(const Header *header) { auto item = new QTreeWidgetItem; item->setText(0, tr("Entry Point")); item->setText(1, QString("0x%1").arg(header->e_entry, 0, 16)); return item; } } DialogHeader::DialogHeader(const std::shared_ptr ®ion, QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f) { ui.setupUi(this); if (std::unique_ptr binary_info = edb::v1::get_binary_info(region)) { if (auto elf32 = dynamic_cast(binary_info.get())) { auto header = reinterpret_cast(elf32->header()); auto root = new QTreeWidgetItem; root->setText(0, tr("ELF32")); root->addChild(create_elf_magic(header)); root->addChild(create_elf_class(header)); root->addChild(create_elf_data(header)); root->addChild(create_elf_version(header)); root->addChild(create_elf_abi(header)); root->addChild(create_elf_abi_version(header)); root->addChild(create_elf_type(header)); root->addChild(create_elf_machine(header)); root->addChild(create_elf_object_version(header)); root->addChild(create_elf_entry_point(header)); ui.treeWidget->insertTopLevelItem(0, root); } if (auto elf64 = dynamic_cast(binary_info.get())) { auto header = reinterpret_cast(elf64->header()); auto root = new QTreeWidgetItem; root->setText(0, tr("ELF64")); root->addChild(create_elf_magic(header)); root->addChild(create_elf_class(header)); root->addChild(create_elf_data(header)); root->addChild(create_elf_version(header)); root->addChild(create_elf_abi(header)); root->addChild(create_elf_abi_version(header)); root->addChild(create_elf_type(header)); root->addChild(create_elf_machine(header)); root->addChild(create_elf_object_version(header)); root->addChild(create_elf_entry_point(header)); ui.treeWidget->insertTopLevelItem(0, root); } if (auto pe32 = dynamic_cast(binary_info.get())) { Q_UNUSED(pe32) #if 0 auto header = reinterpret_cast(pe32->header()); #endif auto root = new QTreeWidgetItem; root->setText(0, tr("PE32")); ui.treeWidget->insertTopLevelItem(0, root); } } } } edb-debugger/plugins/BinaryInfo/BinaryInfo.cpp0000644000175000017500000000573313765535463021226 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "BinaryInfo.h" #include "DialogRegions.h" #include "ELFXX.h" #include "IBinary.h" #include "ISymbolManager.h" #include "OptionsPage.h" #include "PE32.h" #include "edb.h" #include "symbols.h" #include #include #include #include namespace BinaryInfoPlugin { /** * @brief BinaryInfo::BinaryInfo * @param parent */ BinaryInfo::BinaryInfo(QObject *parent) : QObject(parent) { } /** * @brief BinaryInfo::privateInit */ void BinaryInfo::privateInit() { edb::v1::register_binary_info([](const std::shared_ptr ®ion) { return std::unique_ptr(new ELF32(region)); }); edb::v1::register_binary_info([](const std::shared_ptr ®ion) { return std::unique_ptr(new ELF64(region)); }); edb::v1::register_binary_info([](const std::shared_ptr ®ion) { return std::unique_ptr(new PE32(region)); }); edb::v1::symbol_manager().setSymbolGenerator(this); } /** * @brief BinaryInfo::optionsPage * @return */ QWidget *BinaryInfo::optionsPage() { return new OptionsPage; } /** * @brief BinaryInfo::menu * @param parent * @return */ QMenu *BinaryInfo::menu(QWidget *parent) { Q_ASSERT(parent); if (!menu_) { menu_ = new QMenu(tr("Binary Info"), parent); menu_->addAction(tr("&Explore Binary Header"), this, SLOT(exploreHeader())); } return menu_; } /** * @brief BinaryInfo::exploreHeader */ void BinaryInfo::exploreHeader() { static auto dialog = new DialogRegions(edb::v1::debugger_ui); dialog->show(); } /** * @brief BinaryInfo::extraArguments * @return */ QString BinaryInfo::extraArguments() const { return " --symbols : generate symbols for and exit"; } /** * @brief BinaryInfo::parseArguments * @param args * @return */ IPlugin::ArgumentStatus BinaryInfo::parseArguments(QStringList &args) { if (args.size() == 3 && args[1] == "--symbols") { generate_symbols(args[2]); return ARG_EXIT; } return ARG_SUCCESS; } /** * @brief BinaryInfo::generateSymbolFile * @param filename * @param symbol_file * @return */ bool BinaryInfo::generateSymbolFile(const QString &filename, const QString &symbol_file) { std::ofstream file(qPrintable(symbol_file)); if (file) { if (generate_symbols(filename, file)) { return true; } } return false; } } edb-debugger/plugins/BinaryInfo/DialogHeader.ui0000644000175000017500000000336313765535463021326 0ustar eteraneteran BinaryInfoPlugin::DialogHeader 0 0 630 390 Binary Header Header Value Qt::Horizontal QDialogButtonBox::Close buttonBox rejected() BinaryInfoPlugin::DialogHeader reject() 588 380 5 278 buttonBox accepted() BinaryInfoPlugin::DialogHeader accept() 102 366 49 346 edb-debugger/plugins/BinaryInfo/OptionsPage.cpp0000644000175000017500000000345713765535463021417 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "OptionsPage.h" #include "demangle.h" #include #include namespace BinaryInfoPlugin { OptionsPage::OptionsPage(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f) { ui.setupUi(this); } void OptionsPage::showEvent(QShowEvent *) { QSettings settings; #ifdef DEMANGLING_SUPPORTED ui.checkBox->setChecked(settings.value("BinaryInfo/demangling_enabled", true).toBool()); #else ui.checkBox->setEnabled(false); ui.checkBox->setChecked(false); #endif ui.txtDebugDir->setText(settings.value("BinaryInfo/debug_info_path", "/usr/lib/debug").toString()); } void OptionsPage::on_checkBox_toggled(bool checked) { QSettings settings; settings.setValue("BinaryInfo/demangling_enabled", checked); } void OptionsPage::on_txtDebugDir_textChanged(const QString &text) { QSettings settings; settings.setValue("BinaryInfo/debug_info_path", text); } void OptionsPage::on_btnDebugDir_clicked() { QString dir = QFileDialog::getExistingDirectory( this, tr("Choose a directory"), QString(), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); if (!dir.isNull()) { ui.txtDebugDir->setText(dir); } } } edb-debugger/plugins/BinaryInfo/BinaryInfo.h0000644000175000017500000000307613765535463020671 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef BINARY_INFO_H_20061122_ #define BINARY_INFO_H_20061122_ #include "IPlugin.h" #include "ISymbolGenerator.h" #include "Types.h" class QMenu; namespace BinaryInfoPlugin { class BinaryInfo : public QObject, public IPlugin, public ISymbolGenerator { Q_OBJECT Q_INTERFACES(IPlugin) Q_PLUGIN_METADATA(IID "edb.IPlugin/1.0") Q_CLASSINFO("author", "Evan Teran") Q_CLASSINFO("url", "http://www.codef00.com") public: explicit BinaryInfo(QObject *parent = nullptr); private: void privateInit() override; QWidget *optionsPage() override; public: QMenu *menu(QWidget *parent = nullptr) override; QString extraArguments() const override; ArgumentStatus parseArguments(QStringList &args) override; public: bool generateSymbolFile(const QString &filename, const QString &symbol_file) override; public Q_SLOTS: void exploreHeader(); private: QMenu *menu_ = nullptr; }; } #endif edb-debugger/plugins/BinaryInfo/symbols.h0000644000175000017500000000162413765535463020316 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef SYMBOLS_H_20110312_ #define SYMBOLS_H_20110312_ class QString; #include namespace BinaryInfoPlugin { bool generate_symbols(const QString &filename, std::ostream &os = std::cout); } #endif edb-debugger/plugins/BinaryInfo/PE32.h0000644000175000017500000000311213765535463017271 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PE32_H_20070718_ #define PE32_H_20070718_ #include "IBinary.h" #include "libPE/pe_binary.h" namespace BinaryInfoPlugin { class PEBinaryException : public std::exception { public: enum Reason { INVALID_ARGUMENTS = 1, READ_FAILURE = 2, INVALID_PE = 3, INVALID_ARCHITECTURE = 4 }; public: explicit PEBinaryException(Reason reason); const char *what() const noexcept override; private: Reason reason_; }; class PE32 : public IBinary { public: explicit PE32(const std::shared_ptr ®ion); ~PE32() override = default; public: bool native() const override; edb::address_t entryPoint() override; size_t headerSize() const override; const void *header() const override; std::vector
headers() const override; private: std::shared_ptr region_; libPE::IMAGE_DOS_HEADER dos_ = {}; libPE::IMAGE_NT_HEADERS32 pe_ = {}; }; } #endif edb-debugger/plugins/BinaryInfo/symbols.cpp0000644000175000017500000004131113765535463020646 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "symbols.h" #include "demangle.h" #include "edb.h" #include #include #include #include #include #include #include #include #include #include #include "libELF/elf_header.h" #include "libELF/elf_model.h" #include "libELF/elf_rel.h" #include "libELF/elf_rela.h" #include "libELF/elf_shdr.h" #include "libELF/elf_sym.h" #include "libELF/elf_syminfo.h" #include "libELF/elf_types.h" namespace BinaryInfoPlugin { namespace { struct elf32_model : elf_model<32> { static constexpr size_t plt_entry_size = 0x10; static constexpr uint32_t elf_r_sym(uint32_t x) { return ELF32_R_SYM(x); } static constexpr uint32_t elf_r_type(uint32_t x) { return ELF32_R_TYPE(x); } static constexpr uint8_t elf_st_type(uint8_t x) { return ELF32_ST_TYPE(x); } static constexpr uint8_t elf_st_bind(uint8_t x) { return ELF32_ST_BIND(x); } struct symbol { elf_addr address; size_t size; QString name; char type; bool operator<(const symbol &rhs) const { return std::tie(address, name) < std::tie(rhs.address, rhs.name); } bool operator==(const symbol &rhs) const { return std::tie(address, name) == std::tie(rhs.address, rhs.name); } QString to_string() const { return QString("%1 %2 %3 %4").arg(edb::value32(address).toHexString(), edb::value32(size).toHexString()).arg(type).arg(name); } }; }; struct elf64_model : elf_model<64> { static constexpr size_t plt_entry_size = 0x10; static constexpr uint64_t elf_r_sym(uint64_t x) { return ELF64_R_SYM(x); } static constexpr uint64_t elf_r_type(uint64_t x) { return ELF64_R_TYPE(x); } static constexpr uint8_t elf_st_type(uint8_t x) { return ELF64_ST_TYPE(x); } static constexpr uint8_t elf_st_bind(uint8_t x) { return ELF64_ST_BIND(x); } struct symbol { elf_addr address; size_t size; QString name; char type; bool operator<(const symbol &rhs) const { return std::tie(address, name) < std::tie(rhs.address, rhs.name); } bool operator==(const symbol &rhs) const { return std::tie(address, name) == std::tie(rhs.address, rhs.name); } QString to_string() const { return QString("%1 %2 %3 %4").arg(edb::value64(address).toHexString(), edb::value32(size).toHexString()).arg(type).arg(name); } }; }; bool is_elf32(const void *ptr) { auto elf32_hdr = reinterpret_cast(ptr); if (std::memcmp(elf32_hdr->e_ident, ELFMAG, SELFMAG) == 0) { return elf32_hdr->e_ident[EI_CLASS] == ELFCLASS32; } return false; } bool is_elf64(const void *ptr) { auto elf64_hdr = reinterpret_cast(ptr); if (std::memcmp(elf64_hdr->e_ident, ELFMAG, SELFMAG) == 0) { return elf64_hdr->e_ident[EI_CLASS] == ELFCLASS64; } return false; } /* The symbol type. At least the following types are used; others are, as well, depending on the object file format. If lowercase, the symbol is local; if uppercase, the symbol is global (external). "A" The symbol's value is absolute, and will not be changed by further linking. "B" "b" The symbol is in the uninitialized data section (known as BSS). "C" The symbol is common. Common symbols are uninitialized data. When linking, multiple common symbols may appear with the same name. If the symbol is defined anywhere, the common symbols are treated as undefined references. "D" "d" The symbol is in the initialized data section. "G" "g" The symbol is in an initialized data section for small objects. Some object file formats permit more efficient access to small data objects, such as a global int variable as opposed to a large global array. "N" The symbol is a debugging symbol. "p" The symbols is in a stack unwind section. "R" "r" The symbol is in a read only data section. "S" "s" The symbol is in an uninitialized data section for small objects. "T" "t" The symbol is in the text (code) section. "U" The symbol is undefined. "u" The symbol is a unique global symbol. This is a GNU extension to the standard set of ELF symbol bindings. For such a symbol the dynamic linker will make sure that in the entire process there is just one symbol with this name and type in use. "V" "v" The symbol is a weak object. When a weak defined symbol is linked with a normal defined symbol, the normal defined symbol is used with no error. When a weak undefined symbol is linked and the symbol is not defined, the value of the weak symbol becomes zero with no error. On some systems, uppercase indicates that a default value has been specified. "W" "w" The symbol is a weak symbol that has not been specifically tagged as a weak object symbol. When a weak defined symbol is linked with a normal defined symbol, the normal defined symbol is used with no error. When a weak undefined symbol is linked and the symbol is not defined, the value of the symbol is determined in a system-specific manner without error. On some systems, upper- case indicates that a default value has been specified. "-" The symbol is a stabs symbol in an a.out object file. In this case, the next values printed are the stabs other field, the stabs desc field, and the stab type. Stabs symbols are used to hold debugging information. "?" The symbol type is unknown, or object file format specific. */ template void collect_symbols(const void *p, Size size, std::vector &symbols) { Q_UNUSED(size) using elf_addr = typename M::elf_addr; using elf_header = typename M::elf_header; using elf_shdr = typename M::elf_shdr; using elf_sym = typename M::elf_sym; using elf_rela = typename M::elf_rela; using elf_rel = typename M::elf_rel; using symbol = typename M::symbol; const auto base = reinterpret_cast(p); const auto header = static_cast(p); if (header->e_shnum == 0 || header->e_shentsize == 0) { return; } const auto sections_begin = reinterpret_cast(base + header->e_shoff); const elf_shdr *const sections_end = sections_begin + header->e_shnum; auto section_strings = reinterpret_cast(base + sections_begin[header->e_shstrndx].sh_offset); elf_addr plt_address = 0; elf_addr got_address = 0; std::set plt_addresses; // collect special section addresses for (const elf_shdr *section = sections_begin; section != sections_end; ++section) { if (strcmp(§ion_strings[section->sh_name], ".plt") == 0) { plt_address = section->sh_addr; } else if (strcmp(§ion_strings[section->sh_name], ".got") == 0) { got_address = section->sh_addr; } } // print out relocated symbols for special sections for (const elf_shdr *section = sections_begin; section != sections_end; ++section) { elf_addr base_address = 0; if (strcmp(§ion_strings[section->sh_name], ".rela.plt") == 0) { base_address = plt_address; } else if (strcmp(§ion_strings[section->sh_name], ".rel.plt") == 0) { base_address = plt_address; } else if (strcmp(§ion_strings[section->sh_name], ".rela.got") == 0) { base_address = got_address; } else if (strcmp(§ion_strings[section->sh_name], ".rel.got") == 0) { base_address = got_address; } else { continue; } switch (section->sh_type) { case SHT_RELA: { elf_addr n = 0; auto relocation = reinterpret_cast(base + section->sh_offset); if (section->sh_link == 0) { break; } for (size_t i = 0; i < section->sh_size / section->sh_entsize; ++i) { const size_t sym_index = M::elf_r_sym(relocation[i].r_info); const elf_shdr *linked = §ions_begin[section->sh_link]; auto symbol_tab = reinterpret_cast(base + linked->sh_offset); auto string_tab = reinterpret_cast(base + sections_begin[linked->sh_link].sh_offset); const elf_addr symbol_address = base_address + ++n * M::plt_entry_size; const char *sym_name = §ion_strings[section->sh_name]; if (strlen(sym_name) > (sizeof(".rela.") - 1) && memcmp(sym_name, ".rela.", (sizeof(".rela.") - 1)) == 0) { sym_name += 6; } plt_addresses.insert(symbol_address); symbol sym; sym.address = symbol_address; sym.size = (symbol_tab[sym_index].st_size ? symbol_tab[sym_index].st_size : 0x10); sym.name = &string_tab[symbol_tab[sym_index].st_name]; sym.name += "@"; sym.name += sym_name; sym.type = 'P'; symbols.push_back(sym); } } break; case SHT_REL: { elf_addr n = 0; auto relocation = reinterpret_cast(base + section->sh_offset); if (section->sh_link == 0) { break; } for (size_t i = 0; i < section->sh_size / section->sh_entsize; ++i) { const size_t sym_index = M::elf_r_sym(relocation[i].r_info); const elf_shdr *linked = §ions_begin[section->sh_link]; auto symbol_tab = reinterpret_cast(base + linked->sh_offset); auto string_tab = reinterpret_cast(base + sections_begin[linked->sh_link].sh_offset); const elf_addr symbol_address = base_address + ++n * M::plt_entry_size; const char *sym_name = §ion_strings[section->sh_name]; if (strlen(sym_name) > (sizeof(".rel.") - 1) && memcmp(sym_name, ".rel.", (sizeof(".rel.") - 1)) == 0) { sym_name += 5; } plt_addresses.insert(symbol_address); symbol sym; sym.address = symbol_address; sym.size = (symbol_tab[sym_index].st_size ? symbol_tab[sym_index].st_size : 0x10); sym.name = &string_tab[symbol_tab[sym_index].st_name]; sym.name += "@"; sym.name += sym_name; sym.type = 'P'; symbols.push_back(sym); } } break; } } // collect regular symbols for (const elf_shdr *section = sections_begin; section != sections_end; ++section) { switch (section->sh_type) { case SHT_SYMTAB: case SHT_DYNSYM: { auto symbol_tab = reinterpret_cast(base + section->sh_offset); auto string_tab = reinterpret_cast(base + sections_begin[section->sh_link].sh_offset); for (size_t i = 0; i < section->sh_size / section->sh_entsize; ++i) { const elf_shdr *related_section = nullptr; if (symbol_tab[i].st_shndx != SHN_UNDEF && symbol_tab[i].st_shndx < SHN_LORESERVE) { related_section = §ions_begin[symbol_tab[i].st_shndx]; } Q_UNUSED(related_section) if (plt_addresses.find(symbol_tab[i].st_value) == plt_addresses.end()) { if (symbol_tab[i].st_value && strlen(&string_tab[symbol_tab[i].st_name]) > 0) { symbol sym; sym.address = symbol_tab[i].st_value; sym.size = symbol_tab[i].st_size; sym.name = &string_tab[symbol_tab[i].st_name]; sym.type = (M::elf_st_type(symbol_tab[i].st_info) == STT_FUNC ? 'T' : 'D'); symbols.push_back(sym); } } } } break; } } // collect unnamed symbols for (const elf_shdr *section = sections_begin; section != sections_end; ++section) { switch (section->sh_type) { case SHT_SYMTAB: case SHT_DYNSYM: { auto symbol_tab = reinterpret_cast(base + section->sh_offset); auto string_tab = reinterpret_cast(base + sections_begin[section->sh_link].sh_offset); for (size_t i = 0; i < section->sh_size / section->sh_entsize; ++i) { const elf_shdr *related_section = nullptr; if (symbol_tab[i].st_shndx != SHN_UNDEF && symbol_tab[i].st_shndx < SHN_LORESERVE) { related_section = §ions_begin[symbol_tab[i].st_shndx]; } Q_UNUSED(related_section) if (plt_addresses.find(symbol_tab[i].st_value) == plt_addresses.end()) { if (symbol_tab[i].st_value && strlen(&string_tab[symbol_tab[i].st_name]) == 0) { symbol sym; sym.address = symbol_tab[i].st_value; sym.size = symbol_tab[i].st_size; for (const elf_shdr *section = sections_begin; section != sections_end; ++section) { if (sym.address >= section->sh_addr && sym.address + sym.size <= section->sh_addr + section->sh_size) { const std::int64_t offset = sym.address - section->sh_addr; const QString hexPrefix = std::abs(offset) > 9 ? "0x" : ""; const QString offsetStr = offset ? "+" + hexPrefix + QString::number(offset, 16) : ""; const QString sectionName(§ion_strings[section->sh_name]); if (!sectionName.isEmpty()) { sym.name = QString(sectionName + offsetStr); break; } } } if (sym.name.isEmpty()) { sym.name = QString("$sym_%1").arg(edb::v1::format_pointer(symbol_tab[i].st_value)); } sym.type = (M::elf_st_type(symbol_tab[i].st_info) == STT_FUNC ? 'T' : 'D'); symbols.push_back(sym); } } } } break; } } } //-------------------------------------------------------------------------- // Name: output_symbols // Desc: outputs the symbols to OS ensuring uniqueness and adding any // needed demangling //-------------------------------------------------------------------------- template void output_symbols(std::vector &symbols, std::ostream &os) { std::sort(symbols.begin(), symbols.end()); auto new_end = std::unique(symbols.begin(), symbols.end()); const auto demanglingEnabled = QSettings().value("BinaryInfo/demangling_enabled", true).toBool(); for (auto it = symbols.begin(); it != new_end; ++it) { if (demanglingEnabled) { it->name = demangle(it->name); } os << qPrintable(it->to_string()) << '\n'; } } //-------------------------------------------------------------------------- // Name: generate_symbols_internal // Desc: //-------------------------------------------------------------------------- bool generate_symbols_internal(QFile &file, std::shared_ptr &debugFile, std::ostream &os) { if (auto file_ptr = reinterpret_cast(file.map(0, file.size(), QFile::NoOptions))) { if (is_elf64(file_ptr)) { using symbol = typename elf64_model::symbol; std::vector symbols; collect_symbols(file_ptr, file.size(), symbols); // if there was a debug file if (debugFile) { // and we sucessfully opened it if (debugFile->open(QIODevice::ReadOnly)) { // map it and include it with the symbols if (auto debug_ptr = reinterpret_cast(debugFile->map(0, debugFile->size(), QFile::NoOptions))) { // this should never fail... but just being sure if (is_elf64(debug_ptr)) { collect_symbols(debug_ptr, debugFile->size(), symbols); } } } } output_symbols(symbols, os); return true; } else if (is_elf32(file_ptr)) { using symbol = typename elf32_model::symbol; std::vector symbols; collect_symbols(file_ptr, file.size(), symbols); // if there was a debug file if (debugFile) { // and we sucessfully opened it if (debugFile->open(QIODevice::ReadOnly)) { // map it and include it with the symbols if (auto debug_ptr = reinterpret_cast(debugFile->map(0, debugFile->size(), QFile::NoOptions))) { // this should never fail... but just being sure if (is_elf32(debug_ptr)) { collect_symbols(debug_ptr, debugFile->size(), symbols); } } } } output_symbols(symbols, os); return true; } else { qDebug() << "unknown file type"; } } return false; } } /** * @brief generate_symbols * @param filename * @param os * @return */ bool generate_symbols(const QString &filename, std::ostream &os) { QFile file(filename); if (file.open(QIODevice::ReadOnly)) { os << qPrintable(QDateTime::currentDateTimeUtc().toString(Qt::ISODate)) << " +0000" << '\n'; const QByteArray md5 = edb::v1::get_file_md5(filename); os << md5.toHex().data() << ' ' << qPrintable(QFileInfo(filename).absoluteFilePath()) << '\n'; const QString debugInfoPath = QSettings().value("BinaryInfo/debug_info_path", "/usr/lib/debug").toString(); std::shared_ptr debugFile; if (!debugInfoPath.isEmpty()) { debugFile = std::make_shared(QString("%1/%2.debug").arg(debugInfoPath, filename)); if (!debugFile->exists()) { // systems such as Ubuntu don't have .debug suffix, try without it debugFile = std::make_shared(QString("%1/%2").arg(debugInfoPath, filename)); } } return generate_symbols_internal(file, debugFile, os); } return false; } } edb-debugger/plugins/BinaryInfo/ELF64.cpp0000644000175000017500000000234513765535463017742 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "ELFXX.h" #include "IDebugger.h" #include "edb.h" #include "string_hash.h" namespace BinaryInfoPlugin { /** * @brief ELF64::native * @return true if this binary is native to the arch edb was built for */ template <> bool ELF64::native() const { #if defined(EDB_X86) || defined(EDB_X86_64) return edb::v1::debugger_core->cpuType() == edb::string_hash("x86-64"); #elif defined(EDB_ARM32) || defined(EDB_ARM64) return edb::v1::debugger_core->cpuType() == edb::string_hash("AArch64"); #else #error "Unsupported Architecture" #endif } } edb-debugger/plugins/BinaryInfo/PE32.cpp0000644000175000017500000000572013765535463017633 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "PE32.h" #include "IDebugger.h" #include "IProcess.h" #include "IRegion.h" #include "edb.h" #include "libPE/pe_binary.h" #include "string_hash.h" #include namespace BinaryInfoPlugin { PEBinaryException::PEBinaryException(Reason reason) : reason_(reason) { } const char *PEBinaryException::what() const noexcept { return "PEBinaryException"; } /** * @brief PE32::PE32 * @param region */ PE32::PE32(const std::shared_ptr ®ion) : region_(region) { #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ constexpr WORD DosMagic = 0x5A4D; constexpr LONG PeMagic = 0x00004550; #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ constexpr WORD dos_magic = 0x4D5A; constexpr LONG pe_magic = 0x50450000; #endif if (!region_) { throw PEBinaryException(PEBinaryException::Reason::INVALID_ARGUMENTS); } IProcess *process = edb::v1::debugger_core->process(); if (!process) { throw PEBinaryException(PEBinaryException::Reason::READ_FAILURE); } if (!process->readBytes(region_->start(), &dos_, sizeof(dos_))) { throw PEBinaryException(PEBinaryException::Reason::READ_FAILURE); } if (dos_.e_magic != DosMagic || dos_.e_lfanew == 0) { throw PEBinaryException(PEBinaryException::Reason::INVALID_PE); } if (!process->readBytes(region_->start() + dos_.e_lfanew, &pe_, sizeof(pe_))) { throw PEBinaryException(PEBinaryException::Reason::READ_FAILURE); } if (pe_.Signature != PeMagic) { throw PEBinaryException(PEBinaryException::Reason::INVALID_PE); } } /** * @brief PE32::entryPoint * @return */ edb::address_t PE32::entryPoint() { // TODO(eteran): relative to pe_.OptionalHeader.ImageBase;? return pe_.OptionalHeader.AddressOfEntryPoint; } /** * @brief PE32::native * @return */ bool PE32::native() const { return true; } /** * @brief PE32::headerSize * @return */ size_t PE32::headerSize() const { return sizeof(pe_) + dos_.e_lfanew; } /** * @brief PE32::headers * @return a list of all headers in this binary */ std::vector PE32::headers() const { std::vector
results = { {region_->start(), sizeof(pe_) + dos_.e_lfanew}}; return results; } /** * @brief PE32::header * @return a copy of the file header or nullptr if the region wasn't a valid, * known binary type */ const void *PE32::header() const { return nullptr; } } edb-debugger/plugins/ODbgRegisterView/0000755000175000017500000000000013765535463017565 5ustar eteraneteranedb-debugger/plugins/ODbgRegisterView/NumberEdit.h0000644000175000017500000000235213765535463021776 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef NUMBER_EDIT_H_20190412_ #define NUMBER_EDIT_H_20190412_ #include namespace ODbgRegisterView { class NumberEdit final : public QLineEdit { Q_OBJECT public: NumberEdit(int column, int colSpan, QWidget *parent = nullptr); ~NumberEdit() override = default; public: int column() const; int colSpan() const; void setNaturalWidthInChars(int nChars); public: QSize minimumSizeHint() const override; QSize sizeHint() const override; private: int naturalWidthInChars_ = 17; // default roughly as in QLineEdit int column_; int colSpan_; }; } #endif edb-debugger/plugins/ODbgRegisterView/BitFieldFormatter.h0000644000175000017500000000204413765535463023304 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef BIT_FIELD_FORMATTER_H_20191119_ #define BIT_FIELD_FORMATTER_H_20191119_ #include #include namespace ODbgRegisterView { struct BitFieldDescription; class BitFieldFormatter { public: explicit BitFieldFormatter(const BitFieldDescription &bfd); QString operator()(const QString &text) const; private: std::vector valueNames; }; } #endif edb-debugger/plugins/ODbgRegisterView/EntryGridKeyUpDownEventFilter.h0000644000175000017500000000043013765535463025620 0ustar eteraneteran #ifndef ENTRY_GRID_KEY_UP_DOWN_EVENT_FILTER_H_20170705_ #define ENTRY_GRID_KEY_UP_DOWN_EVENT_FILTER_H_20170705_ class QWidget; class QObject; class QEvent; namespace ODbgRegisterView { bool entry_grid_key_event_filter(QWidget *parent, QObject *obj, QEvent *event); } #endif edb-debugger/plugins/ODbgRegisterView/MultiBitFieldWidget.h0000644000175000017500000000107413765535463023601 0ustar eteraneteran #ifndef MULTI_BIT_FIELD_WIDGET_H_20191119_ #define MULTI_BIT_FIELD_WIDGET_H_20191119_ #include "ValueField.h" namespace ODbgRegisterView { struct BitFieldDescription; class MultiBitFieldWidget final : public ValueField { Q_OBJECT public: MultiBitFieldWidget(const QModelIndex &index, const BitFieldDescription &bfd, QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); public Q_SLOTS: void setValue(int value); void adjustToData() override; private: QList valueActions_; std::function equal_; }; } #endif edb-debugger/plugins/ODbgRegisterView/ODbgRV_Util.h0000644000175000017500000001015313765535463022016 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ODBG_REG_VIEW_UTIL_H_20170817_ #define ODBG_REG_VIEW_UTIL_H_20170817_ #include "FieldWidget.h" #include "RegisterGroup.h" #include "RegisterViewModelBase.h" #include "util/Font.h" #include namespace ODbgRegisterView { static constexpr int ModelNameColumn = RegisterViewModelBase::Model::NAME_COLUMN; static constexpr int ModelValueColumn = RegisterViewModelBase::Model::VALUE_COLUMN; static constexpr int ModelCommentColumn = RegisterViewModelBase::Model::COMMENT_COLUMN; template T valid_index(T index) { static_assert(std::is_same::type, const QModelIndex>::value || std::is_same::type, const QPersistentModelIndex>::value, "Wrong type passed to valid_index"); Q_ASSERT(index.isValid()); return index; } template T *checked_cast(P p) { Q_ASSERT(dynamic_cast(p)); return static_cast(p); } template constexpr T square(T v) { return v * v; } inline QPoint field_position(const FieldWidget *field) { // NOTE: mapToGlobal() is VERY slow, don't use it. Here we map to canvas, it's enough for all fields. return field->mapTo(field->parentWidget()->parentWidget(), QPoint()); } // Square of Euclidean distance between two points inline int distance_squared(const QPoint &w1, const QPoint &w2) { return square(w1.x() - w2.x()) + square(w1.y() - w2.y()); } inline QSize letter_size(const QFont &font) { const QFontMetrics fontMetrics(font); const int width = Font::maxWidth(fontMetrics); const int height = fontMetrics.height(); return QSize(width, height); } inline QAction *new_action_separator(QObject *parent) { const auto sep = new QAction(parent); sep->setSeparator(true); return sep; } template inline QAction *new_action(const QString &text, QObject *parent, Func func) { const auto action = new QAction(text, parent); QObject::connect(action, &QAction::triggered, parent, func); return action; } // TODO: switch from string-based search to enum-based one (add a new Role to model data) inline QModelIndex find_model_category(const RegisterViewModelBase::Model *model, const QString &catToFind) { for (int row = 0; row < model->rowCount(); ++row) { const QVariant cat = model->index(row, 0).data(ModelNameColumn); if (cat.isValid() && cat.toString() == catToFind) { return model->index(row, 0); } } return {}; } // TODO: switch from string-based search to enum-based one (add a new Role to model data) inline QModelIndex find_model_register(QModelIndex categoryIndex, const QString ®ToFind, int column = ModelNameColumn) { const auto model = categoryIndex.model(); for (int row = 0; row < model->rowCount(categoryIndex); ++row) { const auto regIndex = model->index(row, ModelNameColumn, categoryIndex); const auto name = model->data(regIndex).toString(); if (name.toUpper() == regToFind) { if (column == ModelNameColumn) return regIndex; return regIndex.sibling(regIndex.row(), column); } } return QModelIndex(); } inline QModelIndex comment_index(const QModelIndex &nameIndex) { Q_ASSERT(nameIndex.isValid()); return nameIndex.sibling(nameIndex.row(), ModelCommentColumn); } inline QModelIndex value_index(const QModelIndex &nameIndex) { Q_ASSERT(nameIndex.isValid()); return nameIndex.sibling(nameIndex.row(), ModelValueColumn); } inline const QVariant &valid_variant(const QVariant &variant) { Q_ASSERT(variant.isValid()); return variant; } } #endif edb-debugger/plugins/ODbgRegisterView/FieldWidget.cpp0000644000175000017500000000514513765535463022465 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "ODbgRV_Util.h" #include "RegisterView.h" namespace ODbgRegisterView { QString FieldWidget::text() const { if (!index_.isValid() && !this->isEnabled()) return QLabel::text(); const auto text = index_.data(); if (!text.isValid()) return QString(width() / letter_size(font()).width() - 1, QChar('?')); return text.toString(); } int FieldWidget::lineNumber() const { const auto charSize = letter_size(font()); return field_position(this).y() / charSize.height(); } int FieldWidget::columnNumber() const { const auto charSize = letter_size(font()); return field_position(this).x() / charSize.width(); } void FieldWidget::init(int fieldWidth) { setObjectName("FieldWidget"); const auto charSize = letter_size(font()); setFixedHeight(charSize.height()); if (fieldWidth > 0) setFixedWidth(fieldWidth * charSize.width()); setDisabled(true); } FieldWidget::FieldWidget(int fieldWidth, const QModelIndex &index, QWidget *parent, Qt::WindowFlags f) : QLabel("Fw???", parent, f), index_(index), fieldWidth_(fieldWidth) { init(fieldWidth); } FieldWidget::FieldWidget(int fieldWidth, const QString &fixedText, QWidget *parent, Qt::WindowFlags f) : QLabel(fixedText, parent, f), fieldWidth_(fieldWidth) { init(fieldWidth); // NOTE: fieldWidth!=fixedText.length() in general } FieldWidget::FieldWidget(const QString &fixedText, QWidget *parent, Qt::WindowFlags f) : QLabel(fixedText, parent, f), fieldWidth_(fixedText.length()) { init(fixedText.length()); } int FieldWidget::fieldWidth() const { return fieldWidth_; } void FieldWidget::adjustToData() { QLabel::setText(text()); adjustSize(); } ODBRegView *FieldWidget::regView() const { const auto parent = parentWidget() // group ->parentWidget() // canvas ->parentWidget() // viewport ->parentWidget(); // regview return checked_cast(parent); } RegisterGroup *FieldWidget::group() const { return checked_cast(parentWidget()); } } edb-debugger/plugins/ODbgRegisterView/BitFieldDescription.cpp0000644000175000017500000000064513765535463024164 0ustar eteraneteran #include "BitFieldDescription.h" #include namespace ODbgRegisterView { BitFieldDescription::BitFieldDescription(int textWidth, const std::vector &valueNames, const std::vector &setValueTexts, const std::function &valueEqualComparator) : textWidth(textWidth), valueNames(valueNames), setValueTexts(setValueTexts), valueEqualComparator(valueEqualComparator) { } } edb-debugger/plugins/ODbgRegisterView/GprEdit.h0000644000175000017500000000263113765535463021276 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef GPR_EDIT_H_20190412_ #define GPR_EDIT_H_20190412_ #include namespace ODbgRegisterView { class GprEdit final : public QLineEdit { Q_OBJECT public: enum class Format { Hex, Signed, Unsigned, Character }; public: GprEdit(std::size_t offsetInInteger, std::size_t integerSize, Format format, QWidget *parent = nullptr); public: void setGPRValue(std::uint64_t gprValue); void updateGPRValue(std::uint64_t &gpr) const; QSize minimumSizeHint() const override { return sizeHint(); } QSize sizeHint() const override; private: void setupFormat(Format newFormat); private: int naturalWidthInChars_; std::size_t integerSize_; std::size_t offsetInInteger_; Format format_; std::uint64_t signBit_; }; } #endif edb-debugger/plugins/ODbgRegisterView/CMakeLists.txt0000644000175000017500000000414313765535463022327 0ustar eteraneterancmake_minimum_required (VERSION 3.1) include("GNUInstallDirs") set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(PluginName "ODbgRegisterView") find_package(Qt5 5.0.0 REQUIRED Widgets) set(PluginSources BitFieldDescription.cpp BitFieldDescription.h BitFieldFormatter.cpp BitFieldFormatter.h Canvas.cpp Canvas.h DialogEditGPR.cpp DialogEditGPR.h DialogEditSimdRegister.cpp DialogEditSimdRegister.h EntryGridKeyUpDownEventFilter.cpp EntryGridKeyUpDownEventFilter.h FieldWidget.cpp FieldWidget.h FpuValueField.h GprEdit.cpp GprEdit.h MultiBitFieldWidget.cpp MultiBitFieldWidget.h NumberEdit.cpp NumberEdit.h ODbgRV_Common.h ODbgRV_Util.h Plugin.cpp Plugin.h RegisterGroup.cpp RegisterGroup.h RegisterView.cpp RegisterView.h SimdValueManager.cpp SimdValueManager.h ValueField.cpp ValueField.h VolatileNameField.cpp VolatileNameField.h ) if(TARGET_ARCH_FAMILY_X86) set(PLUGIN_INCLUDES arch/x86-generic ) set(PluginSources ${PluginSources} arch/x86-generic/x86Groups.cpp arch/x86-generic/x86Groups.h arch/x86-generic/x86FPUValueField.cpp arch/x86-generic/Float80Edit.cpp arch/x86-generic/Float80Edit.h arch/x86-generic/DialogEditFPU.cpp arch/x86-generic/DialogEditFPU.h arch/x86-generic/ODbgRV_x86Common.h ) elseif(TARGET_ARCH_FAMILY_ARM) set(PLUGIN_INCLUDES arch/arm-generic ) set(PluginSources ${PluginSources} arch/arm-generic/armGroups.cpp arch/arm-generic/armGroups.h ) endif() add_library(${PluginName} SHARED ${PluginSources}) target_include_directories(${PluginName} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${PLUGIN_INCLUDES} ) target_link_libraries(${PluginName} Qt5::Widgets edb) install (TARGETS ${PluginName} DESTINATION ${CMAKE_INSTALL_LIBDIR}/edb) set_property(TARGET ${PluginName} PROPERTY CXX_EXTENSIONS OFF) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD 14) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD_REQUIRED ON) set_property(TARGET ${PluginName} PROPERTY LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) set_property(TARGET ${PluginName} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) edb-debugger/plugins/ODbgRegisterView/Canvas.h0000644000175000017500000000174613765535463021161 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef CANVAS_H_20151031_ #define CANVAS_H_20151031_ #include namespace ODbgRegisterView { class Canvas : public QWidget { Q_OBJECT public: explicit Canvas(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); protected: void mousePressEvent(QMouseEvent *event) override; }; } #endif edb-debugger/plugins/ODbgRegisterView/SimdValueManager.h0000644000175000017500000000235713765535463023131 0ustar eteraneteran #ifndef SIMD_VALUE_MANAGER_H_20191119_ #define SIMD_VALUE_MANAGER_H_20191119_ #include "RegisterGroup.h" #include "RegisterViewModelBase.h" #include "Util.h" #include #include #include namespace ODbgRegisterView { class SimdValueManager : public QObject { Q_OBJECT private: QPersistentModelIndex regIndex_; int lineInGroup_; QList elements_; QList menuItems_; NumberDisplayMode intMode_ = NumberDisplayMode::Hex; enum MenuItemNumbers { VIEW_AS_BYTES, VIEW_AS_WORDS, VIEW_AS_DWORDS, VIEW_AS_QWORDS, VIEW_AS_FLOAT32, VIEW_AS_FLOAT64, VIEW_INT_AS_HEX, VIEW_INT_AS_SIGNED, VIEW_INT_AS_UNSIGNED, MENU_ITEMS_COUNT }; using Model = RegisterViewModelBase::Model; Model *model() const; RegisterGroup *group() const; Model::ElementSize currentSize() const; NumberDisplayMode currentFormat() const; void setupMenu(); void updateMenu(); void fillGroupMenu(); public: SimdValueManager(int lineInGroup, const QModelIndex &nameIndex, RegisterGroup *parent = nullptr); public Q_SLOTS: void displayFormatChanged(); private: void showAsInt(Model::ElementSize size); void showAsFloat(Model::ElementSize size); void setIntFormat(NumberDisplayMode format); }; } #endif edb-debugger/plugins/ODbgRegisterView/arch/0000755000175000017500000000000013765535463020502 5ustar eteraneteranedb-debugger/plugins/ODbgRegisterView/arch/arm-generic/0000755000175000017500000000000013765535463022673 5ustar eteraneteranedb-debugger/plugins/ODbgRegisterView/arch/arm-generic/armGroups.cpp0000644000175000017500000003472613765535463025372 0ustar eteraneteran/* Copyright (C) 2017 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "armGroups.h" #include "BitFieldDescription.h" #include "MultiBitFieldWidget.h" #include "ODbgRV_Util.h" #include "QtHelper.h" #include "ValueField.h" #include #include namespace ODbgRegisterView { Q_DECLARE_NAMESPACE_TR(ODbgRegisterView) namespace { const BitFieldDescription itBaseCondDescription = { 2, { "EQ", "HS", "MI", "VS", "HI", "GE", "GT", "AL", }, { tr("Set EQ"), tr("Set HS"), tr("Set MI"), tr("Set VS"), tr("Set HI"), tr("Set GE"), tr("Set GT"), tr("Set AL"), }, }; const BitFieldDescription fpscrSTRDescription = { 3, { " 1 ", "D=1", "D=2", " 2 ", }, { tr("Set stride to 1"), "", "", tr("Set stride to 2"), }, }; const BitFieldDescription fpscrLENDescription = { 1, { // FPSCR[18:16] = LEN-1, while we want to show LEN value itself "1", "2", "3", "4", "5", "6", "7", "8", }, { // FIXME: this is ugly. Maybe edit it as a number? tr("Set LEN to 1"), tr("Set LEN to 2"), tr("Set LEN to 3"), tr("Set LEN to 4"), tr("Set LEN to 5"), tr("Set LEN to 6"), tr("Set LEN to 7"), tr("Set LEN to 8"), }, }; const BitFieldDescription roundControlDescription = { 4, { "NEAR", "DOWN", " UP", "ZERO", }, { tr("Round to nearest"), tr("Round down"), tr("Round up"), tr("Round toward zero"), }, }; } RegisterGroup *createCPSR(RegisterViewModelBase::Model *model, QWidget *parent) { const auto catIndex = find_model_category(model, "General Status"); if (!catIndex.isValid()) return nullptr; auto nameIndex = find_model_register(catIndex, "CPSR"); if (!nameIndex.isValid()) return nullptr; const auto group = new RegisterGroup("CPS", parent); const int nameWidth = 3; int column = 0; group->insert(0, column, new FieldWidget("CPS", group)); const auto valueWidth = 8; const auto valueIndex = nameIndex.sibling(nameIndex.row(), ModelValueColumn); column += nameWidth + 1; group->insert(0, column, new ValueField( valueWidth, valueIndex, [](QString const &v) { return v.right(8); }, group)); const auto commentIndex = nameIndex.sibling(nameIndex.row(), ModelCommentColumn); column += valueWidth + 1; group->insert(0, column, new FieldWidget(0, commentIndex, group)); return group; } RegisterGroup *createExpandedCPSR(RegisterViewModelBase::Model *model, QWidget *parent) { using namespace RegisterViewModelBase; const auto catIndex = find_model_category(model, "General Status"); if (!catIndex.isValid()) return nullptr; auto regNameIndex = find_model_register(catIndex, "CPSR"); if (!regNameIndex.isValid()) return nullptr; const auto group = new RegisterGroup(tr("Expanded CPSR"), parent); static const std::unordered_map flagTooltips = { {'N', tr("Negative result flag")}, {'Z', tr("Zero result flag")}, {'C', tr("Carry flag")}, {'V', tr("Overflow flag")}, {'Q', tr("Sticky saturation/overflow flag")}, {'J', tr("Jazelle state flag")}, {'E', tr("Big endian state flag")}, {'T', tr("Thumb state flag")}, }; // NOTE: NZCV is intended to align with corresponding name/value fields in FPSCR for (int row = 0, groupCol = 28; row < model->rowCount(regNameIndex); ++row) { const auto flagNameIndex = model->index(row, ModelNameColumn, regNameIndex); const auto flagValueIndex = model->index(row, ModelValueColumn, regNameIndex); const auto flagName = model->data(flagNameIndex).toString().toUpper(); if (flagName.length() != 1) continue; static const int flagNameWidth = 1; static const int valueWidth = 1; const char name = flagName[0].toLatin1(); switch (name) { case 'N': case 'Z': case 'C': case 'V': case 'Q': case 'J': case 'E': case 'T': { const auto nameField = new FieldWidget(QChar(name), group); group->insert(0, groupCol, nameField); const auto valueField = new ValueField(valueWidth, flagValueIndex, group); group->insert(1, groupCol, valueField); groupCol -= 2; const auto tooltipStr = flagTooltips.at(name); nameField->setToolTip(tooltipStr); valueField->setToolTip(tooltipStr); break; } default: continue; } } { const auto geNameField = new FieldWidget(QLatin1String("GE"), group); geNameField->setToolTip(tr("Greater than or Equal flags")); group->insert(1, 0, geNameField); for (int geIndex = 3; geIndex > -1; --geIndex) { const int groupCol = 5 + 2 * (3 - geIndex); const auto tooltipStr = QString("GE%1").arg(geIndex); { const auto nameField = new FieldWidget(QString::number(geIndex), group); group->insert(0, groupCol, nameField); nameField->setToolTip(tooltipStr); } const auto indexInModel = find_model_register(regNameIndex, QString("GE%1").arg(geIndex)); if (!indexInModel.isValid()) continue; const auto valueIndex = indexInModel.sibling(indexInModel.row(), ModelValueColumn); if (!valueIndex.isValid()) continue; const auto valueField = new ValueField(1, valueIndex, group); group->insert(1, groupCol, valueField); valueField->setToolTip(tooltipStr); } } { int column = 0; enum { labelRow = 2, valueRow }; { const auto itNameField = new FieldWidget(QLatin1String("IT"), group); itNameField->setToolTip(tr("If-Then block state")); group->insert(valueRow, column, itNameField); column += 3; } { // Using textual names for instructions numbering to avoid confusion between base-0 and base-1 counting static const QString tooltips[] = { tr("Lowest bit of IT-block condition for first instruction"), tr("Lowest bit of IT-block condition for second instruction"), tr("Lowest bit of IT-block condition for third instruction"), tr("Lowest bit of IT-block condition for fourth instruction"), tr("Flag marking active four-instruction IT-block"), }; for (int i = 4; i >= 0; --i, column += 2) { const auto nameIndex = find_model_register(regNameIndex, QString("IT%1").arg(i)); const auto valueIndex = nameIndex.sibling(nameIndex.row(), ModelValueColumn); if (!valueIndex.isValid()) continue; const auto valueField = new ValueField(1, valueIndex, group); group->insert(valueRow, column, valueField); const auto tooltip = tooltips[4 - i]; valueField->setToolTip(tooltip); const auto nameField = new FieldWidget(QString::number(i), group); group->insert(labelRow, column, nameField); nameField->setToolTip(tooltip); } } { const auto itBaseCondNameIndex = find_model_register(regNameIndex, QString("ITbcond").toUpper()); const auto itBaseCondValueIndex = itBaseCondNameIndex.sibling(itBaseCondNameIndex.row(), ModelValueColumn); if (itBaseCondValueIndex.isValid()) { const auto itBaseCondField = new MultiBitFieldWidget(itBaseCondValueIndex, itBaseCondDescription, group); group->insert(valueRow, column, itBaseCondField); const auto tooltip = tr("IT base condition"); itBaseCondField->setToolTip(tooltip); const auto labelField = new FieldWidget("BC", group); group->insert(labelRow, column, labelField); labelField->setToolTip(tooltip); } else qWarning() << "Failed to find IT base condition index in the model"; column += 3; } } return group; } void addDXUOZI(RegisterGroup *const group, QModelIndex const &fpscrIndex, int const startRow, int const startColumn) { static const QString exceptions = "DXUOZI"; static const std::unordered_map> excNames = { {'D', {"ID", tr("Input Denormal")}}, {'X', {"IX", tr("Inexact")}}, {'U', {"UF", tr("Underflow")}}, {'O', {"OF", tr("Overflow")}}, {'Z', {"DZ", tr("Zero Divide")}}, {'I', {"IO", tr("Invalid Operation")}}, }; for (int exN = 0; exN < exceptions.length(); ++exN) { const QString ex = exceptions[exN]; const auto excAbbrevStart = excNames.at(ex[0].toLatin1()).first; const auto exAbbrev = excAbbrevStart + "C"; const auto enabAbbrev = excAbbrevStart + "E"; const auto excIndex = valid_index(find_model_register(fpscrIndex, exAbbrev)); const auto enabIndex = valid_index(find_model_register(fpscrIndex, enabAbbrev)); const int column = startColumn + exN * 2; const auto nameField = new FieldWidget(ex, group); group->insert(startRow, column, nameField); const auto excValueField = new ValueField(1, value_index(excIndex), group); group->insert(startRow + 1, column, excValueField); const auto enabValueField = new ValueField(1, value_index(enabIndex), group); group->insert(startRow + 2, column, enabValueField); const auto excName = excNames.at(ex[0].toLatin1()).second; nameField->setToolTip(excName); excValueField->setToolTip(excName + ' ' + tr("Exception flag") + " (" + exAbbrev + ")"); enabValueField->setToolTip(excName + ' ' + tr("Exception Enable flag") + " (" + enabAbbrev + ")"); } } RegisterGroup *createFPSCR(RegisterViewModelBase::Model *model, QWidget *parent) { using namespace RegisterViewModelBase; const auto catIndex = find_model_category(model, "VFP"); if (!catIndex.isValid()) return nullptr; const auto group = new RegisterGroup("FSC", parent); const QString fpscrName = "FSC"; const int fpscrRow = 0, nzcvLabelRow = fpscrRow; const int nzcvRow = fpscrRow, nzcvValueRow = nzcvRow + 1; int column = 0; const auto fpscrLabelField = new FieldWidget(fpscrName, group); fpscrLabelField->setToolTip(tr("Floating-point status and control register") + " (FPSCR)"); group->insert(fpscrRow, column, fpscrLabelField); column += fpscrName.length() + 1; const auto fpscrIndex = find_model_register(catIndex, "FPSCR", ModelValueColumn); const auto fpscrValueWidth = fpscrIndex.data(Model::FixedLengthRole).toInt(); assert(fpscrValueWidth > 0); group->insert(fpscrRow, column, new ValueField(fpscrValueWidth, fpscrIndex, group)); column += fpscrValueWidth + 2; { static const std::unordered_map nzcvDescriptions = { {'N', tr("LessThan flag")}, {'Z', tr("Equal operands flag")}, {'C', tr("GreaterThen/Equal/Unordered operands flag")}, {'V', tr("Unordered operands flag")}, }; static const QString nzcv = "NZCV"; for (int i = 0; i < nzcv.length(); ++i) { const auto flag = nzcv[i]; const auto flagIndex = valid_index(find_model_register(fpscrIndex, flag, ModelValueColumn)); const auto nameField = new FieldWidget(flag, group); group->insert(nzcvRow, column, nameField); const auto flagValueField = new ValueField(1, value_index(flagIndex), group); group->insert(nzcvValueRow, column, flagValueField); const auto descr = nzcvDescriptions.at(flag.toLatin1()); nameField->setToolTip(descr); flagValueField->setToolTip(descr); column += 2; } } column += 1; const auto excRow = fpscrRow + 1, enabRow = excRow + 1; group->insert(excRow, column, new FieldWidget("Err", group)); group->insert(enabRow, column, new FieldWidget("Enab", group)); column += 5; addDXUOZI(group, fpscrIndex, fpscrRow, column); { const int DXUOZIWidth = 6 * 2 - 1; group->insert(nzcvValueRow, column + DXUOZIWidth + 1, new FieldWidget(0, comment_index(fpscrIndex), group)); } const QString dnName = "DN", fzName = "FZ", strName = "STR", lenName = "LEN"; { column = fpscrName.length() - 1; const auto strNameField = new FieldWidget(strName, group); const auto strRow = excRow; group->insert(strRow, column, strNameField); const auto strIndex = find_model_register(fpscrIndex, "STR", ModelValueColumn); const auto strValueField = new MultiBitFieldWidget(strIndex, fpscrSTRDescription, group); column += strName.length(); group->insert(strRow, column, strValueField); const auto strTooltip = tr("Stride (distance between successive values in a vector)"); strNameField->setToolTip(strTooltip); strValueField->setToolTip(strTooltip); column += 3; } { const auto fzNameField = new FieldWidget(fzName, group); const auto fzRow = excRow; group->insert(fzRow, column, fzNameField); const auto fzIndex = find_model_register(fpscrIndex, "FZ", ModelValueColumn); const auto fzValueWidth = 1; const auto fzValueField = new ValueField(fzValueWidth, fzIndex, group); column += fzName.length() + 1; group->insert(fzRow, column, fzValueField); const auto fzTooltip = tr("Flush Denormals To Zero"); fzNameField->setToolTip(fzTooltip); fzValueField->setToolTip(fzTooltip); } { column = fpscrName.length() - 1; const auto lenNameField = new FieldWidget(lenName, group); const auto lenRow = enabRow; group->insert(lenRow, column, lenNameField); const auto lenIndex = find_model_register(fpscrIndex, "LEN-1", ModelValueColumn); const auto lenValueField = new MultiBitFieldWidget(lenIndex, fpscrLENDescription, group); column += lenName.length() + 1; group->insert(lenRow, column, lenValueField); const auto lenTooltip = tr("Number of registers used by each vector"); lenNameField->setToolTip(lenTooltip); lenValueField->setToolTip(lenTooltip); column += 2; } { const auto dnNameField = new FieldWidget(dnName, group); const auto dnRow = enabRow; group->insert(dnRow, column, dnNameField); const auto dnIndex = find_model_register(fpscrIndex, "DN", ModelValueColumn); const auto dnValueWidth = 1; const auto dnValueField = new ValueField(dnValueWidth, dnIndex, group); column += dnName.length() + 1; group->insert(dnRow, column, dnValueField); const auto dnTooltip = tr("Enable default NaN mode"); dnNameField->setToolTip(dnTooltip); dnValueField->setToolTip(dnTooltip); column += 2; } { column += 1; const QString rndName = "Rnd"; const auto rndRow = enabRow; group->insert(rndRow, column, new FieldWidget(rndName, group)); column += rndName.length() + 1; const auto rndValueField = new MultiBitFieldWidget( find_model_register(fpscrIndex, "RC", ModelValueColumn), roundControlDescription, group); group->insert(rndRow, column, rndValueField); rndValueField->setToolTip(tr("Rounding mode")); } return group; } } edb-debugger/plugins/ODbgRegisterView/arch/arm-generic/armGroups.h0000644000175000017500000000211613765535463025023 0ustar eteraneteran/* Copyright (C) 2017 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ODBG_REGISTER_VIEW_ARM_GROUPS_H_20170819_ #define ODBG_REGISTER_VIEW_ARM_GROUPS_H_20170819_ #include "RegisterView.h" namespace ODbgRegisterView { RegisterGroup *createCPSR(RegisterViewModelBase::Model *model, QWidget *parent); RegisterGroup *createExpandedCPSR(RegisterViewModelBase::Model *model, QWidget *parent); RegisterGroup *createFPSCR(RegisterViewModelBase::Model *model, QWidget *parent); } #endif edb-debugger/plugins/ODbgRegisterView/arch/x86-generic/0000755000175000017500000000000013765535463022541 5ustar eteraneteranedb-debugger/plugins/ODbgRegisterView/arch/x86-generic/x86Groups.h0000644000175000017500000000266513765535463024550 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ODBG_REGISTER_VIEW_X86_GROUPS_H_20170817_ #define ODBG_REGISTER_VIEW_X86_GROUPS_H_20170817_ #include "RegisterView.h" namespace ODbgRegisterView { RegisterGroup *create_eflags(RegisterViewModelBase::Model *model, QWidget *parent); RegisterGroup *create_expanded_eflags(RegisterViewModelBase::Model *model, QWidget *parent); RegisterGroup *create_fpu_data(RegisterViewModelBase::Model *model, QWidget *parent); RegisterGroup *create_fpu_words(RegisterViewModelBase::Model *model, QWidget *parent); RegisterGroup *create_fpu_last_op(RegisterViewModelBase::Model *model, QWidget *parent); RegisterGroup *create_debug_group(RegisterViewModelBase::Model *model, QWidget *parent); RegisterGroup *create_mxcsr(RegisterViewModelBase::Model *model, QWidget *parent); } #endif edb-debugger/plugins/ODbgRegisterView/arch/x86-generic/x86FPUValueField.cpp0000644000175000017500000000725713765535463026221 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "FpuValueField.h" #include "ODbgRV_Util.h" #include "ODbgRV_x86Common.h" #include "RegisterGroup.h" #include "RegisterView.h" namespace ODbgRegisterView { FpuValueField::FpuValueField(int fieldWidth, const QModelIndex ®ValueIndex, const QModelIndex &tagValueIndex, RegisterGroup *group, FieldWidget *commentWidget, int row, int column) : ValueField( fieldWidth, regValueIndex, [this](const QString &str) { if (str.length() != 20) return str; if (groupDigits) return str.left(4) + " " + str.mid(4, 8) + " " + str.right(8); return str; }, group), commentWidget(commentWidget), row(row), column(column), tagValueIndex(tagValueIndex) { Q_ASSERT(group); Q_ASSERT(commentWidget); showAsRawActionIndex = menuItems_.size(); menuItems_.push_back(new_action(tr("View FPU as raw values"), this, [this](bool) { showFPUAsRaw(); })); showAsFloatActionIndex = menuItems_.size(); menuItems_.push_back(new_action(tr("View FPU as floats"), this, [this](bool) { showFPUAsFloat(); })); group->insert(row, column, this); group->insert(commentWidget); // will be moved to its column in the next line group->setupPositionAndSize(row, 0, commentWidget); displayFormatChanged(); connect(index_.model(), SIGNAL(FPUDisplayFormatChanged()), this, SLOT(displayFormatChanged())); } void FpuValueField::showFPUAsRaw() { model()->setChosenFPUFormat(index_.parent(), NumberDisplayMode::Hex); } void FpuValueField::showFPUAsFloat() { model()->setChosenFPUFormat(index_.parent(), NumberDisplayMode::Float); } void FpuValueField::displayFormatChanged() { using RegisterViewModelBase::Model; const auto format = static_cast(valid_variant(index_.parent().data(Model::ChosenFPUFormatRole)).toInt()); switch (format) { case NumberDisplayMode::Hex: menuItems_[showAsRawActionIndex]->setVisible(false); menuItems_[showAsFloatActionIndex]->setVisible(true); break; case NumberDisplayMode::Float: menuItems_[showAsRawActionIndex]->setVisible(true); menuItems_[showAsFloatActionIndex]->setVisible(false); break; default: menuItems_[showAsRawActionIndex]->setVisible(true); menuItems_[showAsFloatActionIndex]->setVisible(true); break; } const auto margins = group()->getFieldMargins(); fieldWidth_ = valid_variant(index_.data(Model::FixedLengthRole)).toInt(); Q_ASSERT(fieldWidth_ > 0); if (format == NumberDisplayMode::Hex) { groupDigits = true; fieldWidth_ += 2; // add some room for spaces between groups } else { groupDigits = false; } const auto charWidth = letter_size(font()).width(); setFixedWidth(charWidth * fieldWidth_ + margins.left() + margins.right()); commentWidget->move(x() + maximumWidth(), commentWidget->y()); } void FpuValueField::updatePalette() { if (!changed() && tagValueIndex.data().toUInt() == FpuTagEmpty) { auto palette = group()->palette(); palette.setColor(foregroundRole(), palette.color(QPalette::Disabled, QPalette::Text)); setPalette(palette); QLabel::update(); } else { ValueField::updatePalette(); } } } edb-debugger/plugins/ODbgRegisterView/arch/x86-generic/Float80Edit.h0000644000175000017500000000212613765535463024736 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef FLOAT80_EDIT_H_20151031_ #define FLOAT80_EDIT_H_20151031_ #include "Types.h" #include namespace ODbgRegisterView { class Float80Edit : public QLineEdit { Q_OBJECT public: explicit Float80Edit(QWidget *parent = nullptr); void setValue(edb::value80 input); public: QSize sizeHint() const override; protected: void focusOutEvent(QFocusEvent *e) override; Q_SIGNALS: void defocussed(); }; } #endif edb-debugger/plugins/ODbgRegisterView/arch/x86-generic/DialogEditFPU.h0000644000175000017500000000260713765535463025277 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DIALOG_EDIT_FPU_H_20151031_ #define DIALOG_EDIT_FPU_H_20151031_ #include "Register.h" #include class QLineEdit; namespace ODbgRegisterView { class Float80Edit; class DialogEditFPU : public QDialog { Q_OBJECT public: explicit DialogEditFPU(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); Register value() const; void setValue(const Register ®); private Q_SLOTS: void onHexEdited(const QString &); void onFloatEdited(const QString &); void updateFloatEntry(); void updateHexEntry(); protected: bool eventFilter(QObject *, QEvent *) override; private: Register reg_; edb::value80 value_; Float80Edit *floatEntry_ = nullptr; QLineEdit *hexEntry_ = nullptr; }; } #endif edb-debugger/plugins/ODbgRegisterView/arch/x86-generic/Float80Edit.cpp0000644000175000017500000000251113765535463025267 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "Float80Edit.h" #include "FloatX.h" #include namespace ODbgRegisterView { Float80Edit::Float80Edit(QWidget *parent) : QLineEdit(parent) { setValidator(new FloatXValidator(this)); } void Float80Edit::setValue(edb::value80 input) { setText(format_float(input)); } QSize Float80Edit::sizeHint() const { const auto baseHint = QLineEdit::sizeHint(); // Default size hint gives space for about 15-20 chars. We need about 30. return QSize(baseHint.width() * 2, baseHint.height()).expandedTo(QApplication::globalStrut()); } void Float80Edit::focusOutEvent(QFocusEvent *e) { QLineEdit::focusOutEvent(e); Q_EMIT defocussed(); } } edb-debugger/plugins/ODbgRegisterView/arch/x86-generic/ODbgRV_x86Common.h0000644000175000017500000000164613765535463025662 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef X86_COMMON_H_20170817_ #define X86_COMMON_H_20170817_ static constexpr unsigned FpuTagEmpty = 3; static constexpr const char *FsrName = "FSR"; static constexpr const char *FcrName = "FCR"; static constexpr const char *FtrName = "FTR"; #endif edb-debugger/plugins/ODbgRegisterView/arch/x86-generic/x86Groups.cpp0000644000175000017500000007056513765535463025107 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "x86Groups.h" #include "BitFieldDescription.h" #include "FpuValueField.h" #include "MultiBitFieldWidget.h" #include "ODbgRV_Util.h" #include "ODbgRV_x86Common.h" #include "QtHelper.h" #include "RegisterGroup.h" #include "VolatileNameField.h" #include #include namespace ODbgRegisterView { Q_DECLARE_NAMESPACE_TR(ODbgRegisterView) namespace { const BitFieldDescription fpuTagDescription = { 7, { tr("valid"), tr("zero"), tr("special"), tr("empty"), }, { tr("Tag as used"), tr(""), tr(""), tr("Tag as empty"), }, [](unsigned a, unsigned b) { return (a == 3 || b == 3) ? (a == b) : true; }, }; const BitFieldDescription roundControlDescription = { 4, { tr("NEAR"), tr("DOWN"), tr(" UP"), tr("ZERO"), }, { tr("Round to nearest"), tr("Round down"), tr("Round up"), tr("Round toward zero"), }, }; const BitFieldDescription precisionControlDescription = { 2, { tr("24"), tr("??"), tr("53"), tr("64"), }, { tr("Set 24-bit precision"), tr(""), tr("Set 53-bit precision"), tr("Set 64-bit precision"), }, }; const BitFieldDescription debugRWDescription = { 5, { tr("EXEC"), tr("WRITE"), tr(" IO"), tr(" R/W"), }, { tr("Break on execution"), tr("Break on data write"), tr(""), tr("Break on data read/write"), }, }; const BitFieldDescription debugLenDescription = { 1, { tr("1"), tr("2"), tr("8"), tr("4"), }, { tr("Set 1-byte length"), tr("Set 2-byte length"), tr("Set 8-byte length"), tr("Set 4-byte length"), }, }; // Checks that FOP is in not in compatibility mode, i.e. is updated only on unmasked exception // This function would return false for e.g. Pentium III or Atom, but returns true since Pentium 4. // This can be made return false for such CPUs by setting bit 2 in IA32_MISC_ENABLE MSR. bool fop_is_compatible() { #ifdef __GNUG__ char fenv[28]; asm volatile("fldz\n" "fstp %%st(0)\n" "fstenv %0\n" : "=m"(fenv)::"%st"); std::uint16_t fop; std::memcpy(&fop, fenv + 18, sizeof(fop)); return fop == 0; #else // TODO(eteran): figure out a way to implement this for other compilers return true; #endif } void add_rounding_mode(RegisterGroup *group, const QModelIndex &index, int row, int column) { assert(index.isValid()); const auto rndValueField = new MultiBitFieldWidget(index, roundControlDescription, group); group->insert(row, column, rndValueField); rndValueField->setToolTip(tr("Rounding mode")); } void add_precision_mode(RegisterGroup *group, const QModelIndex &index, int row, int column) { assert(index.isValid()); const auto precValueField = new MultiBitFieldWidget(index, precisionControlDescription, group); group->insert(row, column, precValueField); precValueField->setToolTip(tr("Precision mode: effective mantissa length")); } void add_puozdi(RegisterGroup *group, const QModelIndex &excRegIndex, const QModelIndex &maskRegIndex, int startRow, int startColumn) { static const QString exceptions = tr("PUOZDI"); static const std::unordered_map excNames = { {'P', tr("Precision")}, {'U', tr("Underflow")}, {'O', tr("Overflow")}, {'Z', tr("Zero Divide")}, {'D', tr("Denormalized Operand")}, {'I', tr("Invalid Operation")}, }; for (int exN = 0; exN < exceptions.length(); ++exN) { const QString ex = exceptions[exN]; const QString exAbbrev = ex + "E"; const QString maskAbbrev = ex + "M"; const auto excIndex = valid_index(find_model_register(excRegIndex, exAbbrev)); const auto maskIndex = valid_index(find_model_register(maskRegIndex, maskAbbrev)); const int column = startColumn + exN * 2; const auto nameField = new FieldWidget(ex, group); group->insert(startRow, column, nameField); const auto excValueField = new ValueField(1, value_index(excIndex), group); group->insert(startRow + 1, column, excValueField); const auto maskValueField = new ValueField(1, value_index(maskIndex), group); group->insert(startRow + 2, column, maskValueField); const QString excName = excNames.at(ex[0].toLatin1()); nameField->setToolTip(excName); excValueField->setToolTip(excName + ' ' + tr("Exception") + " (" + exAbbrev + ")"); maskValueField->setToolTip(excName + ' ' + tr("Exception Mask") + " (" + maskAbbrev + ")"); } } } RegisterGroup *create_eflags(RegisterViewModelBase::Model *model, QWidget *parent) { const auto catIndex = find_model_category(model, tr("General Status")); if (!catIndex.isValid()) return nullptr; auto nameIndex = find_model_register(catIndex, tr("RFLAGS")); if (!nameIndex.isValid()) nameIndex = find_model_register(catIndex, tr("EFLAGS")); if (!nameIndex.isValid()) return nullptr; const auto group = new RegisterGroup(tr("EFL"), parent); constexpr int NameWidth = 3; int column = 0; group->insert(0, column, new FieldWidget(tr("EFL"), group)); constexpr int ValueWidth = 8; const auto valueIndex = nameIndex.sibling(nameIndex.row(), ModelValueColumn); column += NameWidth + 1; group->insert(0, column, new ValueField( ValueWidth, valueIndex, [](const QString &v) { return v.right(8); }, group)); const auto commentIndex = nameIndex.sibling(nameIndex.row(), ModelCommentColumn); column += ValueWidth + 1; group->insert(0, column, new FieldWidget(0, commentIndex, group)); return group; } RegisterGroup *create_expanded_eflags(RegisterViewModelBase::Model *model, QWidget *parent) { const auto catIndex = find_model_category(model, tr("General Status")); if (!catIndex.isValid()) return nullptr; auto regNameIndex = find_model_register(catIndex, "RFLAGS"); if (!regNameIndex.isValid()) regNameIndex = find_model_register(catIndex, "EFLAGS"); if (!regNameIndex.isValid()) return nullptr; const auto group = new RegisterGroup(tr("Expanded EFL"), parent); static const std::unordered_map flagTooltips = { {'C', tr("Carry flag") + " (CF)"}, {'P', tr("Parity flag") + " (PF)"}, {'A', tr("Auxiliary carry flag") + " (AF)"}, {'Z', tr("Zero flag") + " (ZF)"}, {'S', tr("Sign flag") + " (SF)"}, {'T', tr("Trap flag") + " (TF)"}, {'D', tr("Direction flag") + " (DF)"}, {'O', tr("Overflow flag") + " (OF)"}, }; for (int row = 0, groupRow = 0; row < model->rowCount(regNameIndex); ++row) { const auto flagNameIndex = model->index(row, ModelNameColumn, regNameIndex); const auto flagValueIndex = model->index(row, ModelValueColumn, regNameIndex); const auto flagName = model->data(flagNameIndex).toString().toUpper(); if (flagName.length() != 2 || flagName[1] != 'F') { continue; } constexpr int FlagNameWidth = 1; constexpr int ValueWidth = 1; const char name = flagName[0].toLatin1(); switch (name) { case 'C': case 'P': case 'A': case 'Z': case 'S': case 'T': case 'D': case 'O': { const auto nameField = new FieldWidget(QChar(name), group); group->insert(groupRow, 0, nameField); const auto valueField = new ValueField(ValueWidth, flagValueIndex, group); group->insert(groupRow, FlagNameWidth + 1, valueField); ++groupRow; const auto tooltipStr = flagTooltips.at(name); nameField->setToolTip(tooltipStr); valueField->setToolTip(tooltipStr); break; } default: continue; } } return group; } RegisterGroup *create_fpu_data(RegisterViewModelBase::Model *model, QWidget *parent) { using RegisterViewModelBase::Model; const auto catIndex = find_model_category(model, "FPU"); if (!catIndex.isValid()) { return nullptr; } const auto tagsIndex = find_model_register(catIndex, FtrName); if (!tagsIndex.isValid()) { qWarning() << "Warning: failed to find FTR in the model, refusing to continue making FPUData group"; return nullptr; } const auto group = new RegisterGroup(tr("FPU Data Registers"), parent); constexpr int FpuRegCount = 8; constexpr int NameWidth = 3; constexpr int TagWidth = 7; const auto fsrIndex = valid_index(find_model_register(catIndex, FsrName)); const QPersistentModelIndex topIndex = valid_index(find_model_register(fsrIndex, tr("TOP"), ModelValueColumn)); for (int row = 0; row < FpuRegCount; ++row) { int column = 0; const auto nameIndex = model->index(row, ModelNameColumn, catIndex); { const auto STiFormatter = [row, topIndex]() { const auto topByteArray = topIndex.data(Model::RawValueRole).toByteArray(); if (topByteArray.isEmpty()) return QString("R%1").arg(row); const auto top = topByteArray[0]; assert(top >= 0); Q_ASSERT(top < 8); const auto stI = (row + 8 - top) % 8; return QString("ST%1").arg(stI); }; const auto field = new VolatileNameField(NameWidth, STiFormatter, group); QObject::connect(model, &RegisterViewModelBase::Model::dataChanged, field, &VolatileNameField::adjustToData); group->insert(row, column, field); column += NameWidth + 1; } const auto tagValueIndex = valid_index(model->index(row, ModelValueColumn, tagsIndex)); group->insert(row, column, new MultiBitFieldWidget(tagValueIndex, fpuTagDescription, group)); column += TagWidth + 1; const auto regValueIndex = nameIndex.sibling(nameIndex.row(), ModelValueColumn); const int regValueWidth = regValueIndex.data(Model::FixedLengthRole).toInt(); Q_ASSERT(regValueWidth > 0); const auto regCommentIndex = model->index(row, ModelCommentColumn, catIndex); new FpuValueField(regValueWidth, regValueIndex, tagValueIndex, group, new FieldWidget(0, regCommentIndex, group), row, column); } return group; } RegisterGroup *create_fpu_words(RegisterViewModelBase::Model *model, QWidget *parent) { const auto catIndex = find_model_category(model, "FPU"); if (!catIndex.isValid()) { return nullptr; } const auto group = new RegisterGroup(tr("FPU Status&&Control Registers"), parent); group->appendNameValueComment(find_model_register(catIndex, FtrName), tr("FPU Tag Register"), false); constexpr int FsrRow = 1; const auto fsrIndex = find_model_register(catIndex, FsrName); group->appendNameValueComment(fsrIndex, tr("FPU Status Register"), false); constexpr int FcrRow = 2; const auto fcrIndex = find_model_register(catIndex, FcrName); group->appendNameValueComment(fcrIndex, tr("FPU Control Register"), false); constexpr int wordNameWidth = 3; constexpr int wordValWidth = 4; constexpr int condPrecLabelColumn = wordNameWidth + 1 + wordValWidth + 1 + 1; constexpr int condPrecLabelWidth = 4; group->insert(FsrRow, condPrecLabelColumn, new FieldWidget("Cond", group)); group->insert(FcrRow, condPrecLabelColumn, new FieldWidget("Prec", group)); constexpr int condPrecValColumn = condPrecLabelColumn + condPrecLabelWidth + 1; constexpr int roundModeWidth = 4, precModeWidth = 2; constexpr int roundModeColumn = condPrecValColumn; constexpr int precModeColumn = roundModeColumn + roundModeWidth + 1; // This must be inserted before precision&rounding value fields, since they overlap this label group->insert(FcrRow, precModeColumn - 1, new FieldWidget(",", group)); for (int condN = 3; condN >= 0; --condN) { const auto name = QString("C%1").arg(condN); const auto condNNameIndex = valid_index(find_model_register(fsrIndex, name)); const auto condNIndex = valid_index(condNNameIndex.sibling(condNNameIndex.row(), ModelValueColumn)); const int column = condPrecValColumn + 2 * (3 - condN); const auto nameField = new FieldWidget(QString("%1").arg(condN), group); group->insert(FsrRow - 1, column, nameField); const auto valueField = new ValueField(1, condNIndex, group); group->insert(FsrRow, column, valueField); nameField->setToolTip(name); valueField->setToolTip(name); } add_rounding_mode(group, find_model_register(fcrIndex, "RC", ModelValueColumn), FcrRow, roundModeColumn); add_precision_mode(group, find_model_register(fcrIndex, "PC", ModelValueColumn), FcrRow, precModeColumn); constexpr int ErrMaskColumn = precModeColumn + precModeWidth + 2; constexpr int ErrLabelWidth = 3; group->insert(FsrRow, ErrMaskColumn, new FieldWidget("Err", group)); group->insert(FcrRow, ErrMaskColumn, new FieldWidget("Mask", group)); constexpr int ESColumn = ErrMaskColumn + ErrLabelWidth + 1; constexpr int SFColumn = ESColumn + 2; const auto ESNameField = new FieldWidget("E", group); group->insert(FsrRow - 1, ESColumn, ESNameField); const auto SFNameField = new FieldWidget("S", group); group->insert(FsrRow - 1, SFColumn, SFNameField); const auto ESValueField = new ValueField(1, find_model_register(fsrIndex, "ES", ModelValueColumn), group); group->insert(FsrRow, ESColumn, ESValueField); const auto SFValueField = new ValueField(1, find_model_register(fsrIndex, "SF", ModelValueColumn), group); group->insert(FsrRow, SFColumn, SFValueField); { const auto ESTooltip = tr("Error Summary Status") + " (ES)"; ESNameField->setToolTip(ESTooltip); ESValueField->setToolTip(ESTooltip); } { const auto SFTooltip = tr("Stack Fault") + " (SF)"; SFNameField->setToolTip(SFTooltip); SFValueField->setToolTip(SFTooltip); } constexpr int PEPMColumn = SFColumn + 2; add_puozdi(group, fsrIndex, fcrIndex, FsrRow - 1, PEPMColumn); constexpr int PUOZDIWidth = 6 * 2 - 1; group->insert(FsrRow, PEPMColumn + PUOZDIWidth + 1, new FieldWidget(0, comment_index(fsrIndex), group)); return group; } RegisterGroup *create_fpu_last_op(RegisterViewModelBase::Model *model, QWidget *parent) { using RegisterViewModelBase::Model; const auto catIndex = find_model_category(model, "FPU"); if (!catIndex.isValid()) return nullptr; const auto FIPIndex = find_model_register(catIndex, "FIP", ModelValueColumn); if (!FIPIndex.isValid()) return nullptr; const auto FDPIndex = find_model_register(catIndex, "FDP", ModelValueColumn); if (!FDPIndex.isValid()) return nullptr; const auto group = new RegisterGroup(tr("FPU Last Operation Registers"), parent); enum { lastInsnRow, lastDataRow, lastOpcodeRow }; const QString lastInsnLabel = "Last insn"; const QString lastDataLabel = "Last data"; const QString lastOpcodeLabel = "Last opcode"; const auto lastInsnLabelField = new FieldWidget(lastInsnLabel, group); group->insert(lastInsnRow, 0, lastInsnLabelField); const auto lastDataLabelField = new FieldWidget(lastDataLabel, group); group->insert(lastDataRow, 0, lastDataLabelField); const auto lastOpcodeLabelField = new FieldWidget(lastOpcodeLabel, group); group->insert(lastOpcodeRow, 0, lastOpcodeLabelField); lastInsnLabelField->setToolTip(tr("Last FPU instruction address")); lastDataLabelField->setToolTip(tr("Last FPU memory operand address")); // FIS & FDS are not maintained in 64-bit mode; Linux64 always saves state from // 64-bit mode, losing the values for 32-bit apps even if the CPU doesn't deprecate them // We'll show zero offsets in 32 bit mode for consistency with 32-bit kernels // In 64-bit mode, since segments are not maintained, we'll just show offsets const auto FIPwidth = FDPIndex.data(Model::FixedLengthRole).toInt(); const auto segWidth = FIPwidth == 8 /*8chars=>32bit*/ ? 4 : 0; const auto segColumn = lastInsnLabel.length() + 1; if (segWidth) { // these two must be inserted first, because seg & offset value fields overlap these labels group->insert(lastInsnRow, segColumn + segWidth, new FieldWidget(":", group)); group->insert(lastDataRow, segColumn + segWidth, new FieldWidget(":", group)); const auto FISField = new ValueField(segWidth, find_model_register(catIndex, "FIS", ModelValueColumn), group); group->insert(lastInsnRow, segColumn, FISField); const auto FDSField = new ValueField(segWidth, find_model_register(catIndex, "FDS", ModelValueColumn), group); group->insert(lastDataRow, segColumn, FDSField); FISField->setToolTip(tr("Last FPU instruction selector")); FDSField->setToolTip(tr("Last FPU memory operand selector")); } const auto offsetWidth = FIPIndex.data(Model::FixedLengthRole).toInt(); assert(offsetWidth > 0); const auto offsetColumn = segColumn + segWidth + (segWidth ? 1 : 0); const auto FIPValueField = new ValueField(offsetWidth, FIPIndex, group); group->insert(lastInsnRow, offsetColumn, FIPValueField); const auto FDPValueField = new ValueField(offsetWidth, FDPIndex, group); group->insert(lastDataRow, offsetColumn, FDPValueField); FIPValueField->setToolTip(tr("Last FPU instruction offset")); FDPValueField->setToolTip(tr("Last FPU memory operand offset")); QPersistentModelIndex const FOPIndex = find_model_register(catIndex, "FOP", ModelValueColumn); QPersistentModelIndex const FSRIndex = find_model_register(catIndex, FsrName, ModelValueColumn); QPersistentModelIndex const FCRIndex = find_model_register(catIndex, FcrName, ModelValueColumn); bool fopRarelyUpdated = fop_is_compatible(); const auto FOPFormatter = [FOPIndex, FSRIndex, FCRIndex, FIPIndex, fopRarelyUpdated](const QString &str) -> QString { if (str.isEmpty() || str[0] == '?') return str; const auto rawFCR = FCRIndex.data(Model::RawValueRole).toByteArray(); assert(rawFCR.size() <= long(sizeof(edb::value16))); if (rawFCR.isEmpty()) return str; edb::value16 fcr(0); std::memcpy(&fcr, rawFCR.constData(), rawFCR.size()); const auto rawFSR = FSRIndex.data(Model::RawValueRole).toByteArray(); assert(rawFSR.size() <= long(sizeof(edb::value16))); if (rawFSR.isEmpty()) return str; edb::value16 fsr(0); std::memcpy(&fsr, rawFSR.constData(), rawFSR.size()); const auto rawFOP = FOPIndex.data(Model::RawValueRole).toByteArray(); edb::value16 fop(0); assert(rawFOP.size() <= long(sizeof(edb::value16))); if (rawFOP.isEmpty()) return str; if (rawFOP.size() != sizeof(edb::value16)) return QString("????"); std::memcpy(&fop, rawFOP.constData(), rawFOP.size()); const auto rawFIP = FIPIndex.data(Model::RawValueRole).toByteArray(); if (rawFIP.isEmpty()) return str; edb::address_t fip(0); assert(rawFIP.size() <= long(sizeof(fip))); std::memcpy(&fip, rawFIP.constData(), rawFIP.size()); const auto excMask = fcr & 0x3f; const auto excActive = fsr & 0x3f; const auto excActiveUnmasked = excActive & ~excMask; if (fop == 0 && ((fopRarelyUpdated && !excActiveUnmasked) || fip == 0)) return QString("00 00"); return edb::value8(0xd8 + rawFOP[1]).toHexString() + ' ' + edb::value8(rawFOP[0]).toHexString(); }; const auto FOPValueField = new ValueField(5, FOPIndex, FOPFormatter, group); group->insert(lastOpcodeRow, lastOpcodeLabel.length() + 1, FOPValueField); static const auto FOPTooltip = tr("Last FPU opcode"); lastOpcodeLabelField->setToolTip(FOPTooltip); FOPValueField->setToolTip(FOPTooltip); return group; } RegisterGroup *create_debug_group(RegisterViewModelBase::Model *model, QWidget *parent) { using RegisterViewModelBase::Model; const auto catIndex = find_model_category(model, "Debug"); if (!catIndex.isValid()) return nullptr; const auto group = new RegisterGroup(tr("Debug Registers"), parent); const auto dr6Index = valid_index(find_model_register(catIndex, "DR6")); const auto dr7Index = valid_index(find_model_register(catIndex, "DR7")); const auto nameWidth = 3; const auto valueWidth = value_index(dr6Index).data(Model::FixedLengthRole).toInt(); assert(valueWidth > 0); const auto bitsSpacing = 1; const auto BTooltip = tr("Breakpoint Condition Detected"); const auto LTooltip = tr("Local Breakpoint Enable"); const auto GTooltip = tr("Global Breakpoint Enable"); const auto typeTooltip = tr("Breakpoint condition"); const auto lenTooltip = tr("Data breakpoint length"); const auto lenDecodedStr = tr(" (bytes count from %1)"); int row = 0; { int column = nameWidth + 1 + valueWidth + 2; const auto BLabelField = new FieldWidget("B", group); BLabelField->setToolTip(BTooltip + " (B0..B3)"); group->insert(row, column, BLabelField); column += bitsSpacing + 1; const auto LLabelField = new FieldWidget("L", group); LLabelField->setToolTip(LTooltip + " (L0..L3)"); group->insert(row, column, LLabelField); column += bitsSpacing + 1; const auto GLabelField = new FieldWidget("G", group); GLabelField->setToolTip(GTooltip + " (G0..G3)"); group->insert(row, column, GLabelField); column += bitsSpacing + 1; const auto typeLabelField = new FieldWidget("Type", group); typeLabelField->setToolTip(typeTooltip + " (R/W0..R/W3)"); group->insert(row, column, typeLabelField); column += bitsSpacing + 4; const auto lenLabelField = new FieldWidget("Len", group); lenLabelField->setToolTip(lenTooltip + lenDecodedStr.arg("LEN0..LEN3")); group->insert(row, column, lenLabelField); column += bitsSpacing + 3; ++row; } for (int drI = 0; drI < 4; ++drI, ++row) { const auto name = QString("DR%1").arg(drI); const auto DRiValueIndex = valid_index(find_model_register(catIndex, name, ModelValueColumn)); int column = 0; group->insert(row, column, new FieldWidget(name, group)); column += nameWidth + 1; group->insert(row, column, new ValueField(valueWidth, DRiValueIndex, group)); column += valueWidth + 2; { const auto BiName = QString("B%1").arg(drI); const auto BiIndex = valid_index(find_model_register(dr6Index, BiName, ModelValueColumn)); const auto BiValueField = new ValueField(1, BiIndex, group); BiValueField->setToolTip(BTooltip + " (" + BiName + ")"); group->insert(row, column, BiValueField); column += bitsSpacing + 1; } { const auto LiName = QString("L%1").arg(drI); const auto LiIndex = valid_index(find_model_register(dr7Index, LiName, ModelValueColumn)); const auto LiValueField = new ValueField(1, LiIndex, group); LiValueField->setToolTip(LTooltip + " (" + LiName + ")"); group->insert(row, column, LiValueField); column += bitsSpacing + 1; } { const auto GiName = QString("G%1").arg(drI); const auto GiIndex = valid_index(find_model_register(dr7Index, GiName, ModelValueColumn)); const auto GiValueField = new ValueField(1, GiIndex, group); GiValueField->setToolTip(GTooltip + " (" + GiName + ")"); group->insert(row, column, GiValueField); column += bitsSpacing + 1; } { const auto RWiName = QString("R/W%1").arg(drI); const QPersistentModelIndex RWiIndex = valid_index(find_model_register(dr7Index, RWiName, ModelValueColumn)); const auto width = 5; const auto RWiValueField = new MultiBitFieldWidget(RWiIndex, debugRWDescription, group); RWiValueField->setToolTip(typeTooltip + " (" + RWiName + ")"); group->insert(row, column, RWiValueField); column += bitsSpacing + width; } { const auto LENiName = QString("LEN%1").arg(drI); const QPersistentModelIndex LENiIndex = valid_index(find_model_register(dr7Index, LENiName, ModelValueColumn)); const auto LENiValueField = new MultiBitFieldWidget(LENiIndex, debugLenDescription, group); LENiValueField->setToolTip(lenTooltip + lenDecodedStr.arg(LENiName)); group->insert(row, column, LENiValueField); } } { int column = 0; group->insert(row, column, new FieldWidget("DR6", group)); column += nameWidth + 1; group->insert(row, column, new ValueField(valueWidth, value_index(dr6Index), group)); column += valueWidth + 2; const QString bsName = "BS"; const auto bsWidth = bsName.length(); const auto BSNameField = new FieldWidget(bsName, group); const auto BSTooltip = tr("Single Step") + " (BS)"; BSNameField->setToolTip(BSTooltip); group->insert(row, column, BSNameField); column += bsWidth + 1; const auto bsIndex = find_model_register(dr6Index, bsName, ModelValueColumn); const auto BSValueField = new ValueField(1, bsIndex, group); BSValueField->setToolTip(BSTooltip); group->insert(row, column, BSValueField); ++row; } { int column = 0; group->insert(row, column, new FieldWidget("DR7", group)); column += nameWidth + 1; group->insert(row, column, new ValueField(valueWidth, value_index(dr7Index), group)); column += valueWidth + 2; { const QString leName = "LE"; const auto leWidth = leName.length(); const auto LENameField = new FieldWidget(leName, group); const auto LETooltip = tr("Local Exact Breakpoint Enable"); LENameField->setToolTip(LETooltip); group->insert(row, column, LENameField); column += leWidth + 1; const auto leIndex = find_model_register(dr7Index, leName, ModelValueColumn); const auto leValueWidth = 1; const auto LEValueField = new ValueField(leValueWidth, leIndex, group); LEValueField->setToolTip(LETooltip); group->insert(row, column, LEValueField); column += leValueWidth + 1; } { const QString geName = "GE"; const auto geWidth = geName.length(); const auto GENameField = new FieldWidget(geName, group); const auto GETooltip = tr("Global Exact Breakpoint Enable"); GENameField->setToolTip(GETooltip); group->insert(row, column, GENameField); column += geWidth + 1; const auto geIndex = find_model_register(dr7Index, geName, ModelValueColumn); const auto geValueWidth = 1; const auto GEValueField = new ValueField(geValueWidth, geIndex, group); GEValueField->setToolTip(GETooltip); group->insert(row, column, GEValueField); column += geValueWidth + 1; } } return group; } RegisterGroup *create_mxcsr(RegisterViewModelBase::Model *model, QWidget *parent) { using namespace RegisterViewModelBase; const auto catIndex = find_model_category(model, "SSE"); if (!catIndex.isValid()) return nullptr; const auto group = new RegisterGroup("MXCSR", parent); const QString mxcsrName = "MXCSR"; int column = 0; const int mxcsrRow = 1, fzRow = mxcsrRow, dazRow = mxcsrRow, excRow = mxcsrRow; const int rndRow = fzRow + 1; const int maskRow = rndRow; group->insert(mxcsrRow, column, new FieldWidget(mxcsrName, group)); column += mxcsrName.length() + 1; const auto mxcsrIndex = find_model_register(catIndex, "MXCSR", ModelValueColumn); const auto mxcsrValueWidth = mxcsrIndex.data(Model::FixedLengthRole).toInt(); assert(mxcsrValueWidth > 0); group->insert(mxcsrRow, column, new ValueField(mxcsrValueWidth, mxcsrIndex, group)); column += mxcsrValueWidth + 2; // XXX: Sacrificing understandability of DAZ->DZ to align PUOZDI with FPU's. // Also FZ value is one char away from DAZ name, which is also no good. // Maybe following OllyDbg example here isn't a good idea. const QString fzName = "FZ", dazName = "DZ"; const auto fzColumn = column; const auto fzNameField = new FieldWidget(fzName, group); group->insert(fzRow, fzColumn, fzNameField); column += fzName.length() + 1; const auto fzIndex = find_model_register(mxcsrIndex, "FZ", ModelValueColumn); const auto fzValueWidth = 1; const auto fzValueField = new ValueField(fzValueWidth, fzIndex, group); group->insert(fzRow, column, fzValueField); column += fzValueWidth + 1; const auto dazNameField = new FieldWidget(dazName, group); group->insert(dazRow, column, dazNameField); column += dazName.length() + 1; const auto dazIndex = find_model_register(mxcsrIndex, "DAZ", ModelValueColumn); const auto dazValueWidth = 1; const auto dazValueField = new ValueField(dazValueWidth, dazIndex, group); group->insert(dazRow, column, dazValueField); column += dazValueWidth + 2; const QString excName = "Err"; group->insert(excRow, column, new FieldWidget(excName, group)); const QString maskName = "Mask"; group->insert(maskRow, column, new FieldWidget(maskName, group)); column += maskName.length() + 1; add_puozdi(group, mxcsrIndex, mxcsrIndex, excRow - 1, column); const auto rndNameColumn = fzColumn; const QString rndName = "Rnd"; group->insert(rndRow, rndNameColumn, new FieldWidget(rndName, group)); const auto rndColumn = rndNameColumn + rndName.length() + 1; add_rounding_mode(group, find_model_register(mxcsrIndex, "RC", ModelValueColumn), rndRow, rndColumn); { const auto fzTooltip = tr("Flush Denormals To Zero (FTZ)"); fzNameField->setToolTip(fzTooltip); fzValueField->setToolTip(fzTooltip); } { const auto dazTooltip = tr("Denormals Are Zeros (DAZ)"); dazNameField->setToolTip(dazTooltip); dazValueField->setToolTip(dazTooltip); } return group; } } edb-debugger/plugins/ODbgRegisterView/arch/x86-generic/DialogEditFPU.cpp0000644000175000017500000001265513765535463025636 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "DialogEditFPU.h" #include "EntryGridKeyUpDownEventFilter.h" #include "Float80Edit.h" #include "util/Float.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace ODbgRegisterView { namespace { long double readFloat(const QString &strInput, bool &ok) { ok = false; const QString str(strInput.toLower().trimmed()); if (const auto value = util::full_string_to_float(str.toStdString())) { ok = true; return *value; } // OK, so either it is invalid/unfinished, or it's some special value // We still do want the user to be able to enter common special values long double value; static const std::array positiveInf{0, 0, 0, 0, 0, 0, 0, 0x80, 0xff, 0x7f, 0, 0, 0, 0, 0, 0}; static const std::array negativeInf{0, 0, 0, 0, 0, 0, 0, 0x80, 0xff, 0xff, 0, 0, 0, 0, 0, 0}; static const std::array positiveSNaN{0, 0, 0, 0, 0, 0, 0, 0x90, 0xff, 0x7f, 0, 0, 0, 0, 0, 0}; static const std::array negativeSNaN{0, 0, 0, 0, 0, 0, 0, 0x90, 0xff, 0xff, 0, 0, 0, 0, 0, 0}; // Indefinite values are used for QNaN static const std::array positiveQNaN{0, 0, 0, 0, 0, 0, 0, 0xc0, 0xff, 0x7f, 0, 0, 0, 0, 0, 0}; static const std::array negativeQNaN{0, 0, 0, 0, 0, 0, 0, 0xc0, 0xff, 0xff, 0, 0, 0, 0, 0, 0}; if (str == "+snan" || str == "snan") std::memcpy(&value, &positiveSNaN, sizeof(value)); else if (str == "-snan") std::memcpy(&value, &negativeSNaN, sizeof(value)); else if (str == "+qnan" || str == "qnan" || str == "nan") std::memcpy(&value, &positiveQNaN, sizeof(value)); else if (str == "-qnan") std::memcpy(&value, &negativeQNaN, sizeof(value)); else if (str == "+inf" || str == "inf") std::memcpy(&value, &positiveInf, sizeof(value)); else if (str == "-inf") std::memcpy(&value, &negativeInf, sizeof(value)); else return 0; ok = true; return value; } } DialogEditFPU::DialogEditFPU(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f), floatEntry_(new ODbgRegisterView::Float80Edit(this)), hexEntry_(new QLineEdit(this)) { setWindowTitle(tr("Modify Register")); setModal(true); const auto allContentsGrid = new QGridLayout(); allContentsGrid->addWidget(new QLabel(tr("Float"), this), 0, 0); allContentsGrid->addWidget(new QLabel(tr("Hex"), this), 1, 0); allContentsGrid->addWidget(floatEntry_, 0, 1); allContentsGrid->addWidget(hexEntry_, 1, 1); connect(floatEntry_, &Float80Edit::textEdited, this, &DialogEditFPU::onFloatEdited); connect(hexEntry_, &QLineEdit::textEdited, this, &DialogEditFPU::onHexEdited); hexEntry_->setValidator(new QRegExpValidator(QRegExp("[0-9a-fA-F ]{,20}"), this)); connect(floatEntry_, &Float80Edit::defocussed, this, &DialogEditFPU::updateFloatEntry); hexEntry_->installEventFilter(this); floatEntry_->installEventFilter(this); const auto okCancel = new QDialogButtonBox(this); okCancel->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); connect(okCancel, &QDialogButtonBox::accepted, this, &DialogEditFPU::accept); connect(okCancel, &QDialogButtonBox::rejected, this, &DialogEditFPU::reject); const auto dialogLayout = new QVBoxLayout(this); dialogLayout->addLayout(allContentsGrid); dialogLayout->addWidget(okCancel); setTabOrder(floatEntry_, hexEntry_); setTabOrder(hexEntry_, okCancel); } void DialogEditFPU::updateFloatEntry() { floatEntry_->setValue(value_); } void DialogEditFPU::updateHexEntry() { hexEntry_->setText(value_.toHexString()); } bool DialogEditFPU::eventFilter(QObject *obj, QEvent *event) { return entry_grid_key_event_filter(this, obj, event); } void DialogEditFPU::setValue(const Register &newReg) { reg_ = newReg; value_ = reg_.value(); updateFloatEntry(); updateHexEntry(); setWindowTitle(tr("Modify %1").arg(reg_.name().toUpper())); floatEntry_->setFocus(Qt::OtherFocusReason); } Register DialogEditFPU::value() const { Register ret(reg_); ret.setValueFrom(value_); return ret; } void DialogEditFPU::onHexEdited(const QString &input) { QString readable(input.trimmed()); readable.replace(' ', ""); while (readable.size() < 20) { readable = '0' + readable; } const auto byteArray = QByteArray::fromHex(readable.toLatin1()); auto source = byteArray.constData(); auto dest = reinterpret_cast(&value_); for (std::size_t i = 0; i < sizeof(value_); ++i) { dest[i] = source[sizeof(value_) - i - 1]; } updateFloatEntry(); } void DialogEditFPU::onFloatEdited(const QString &str) { bool ok; const long double value = readFloat(str, ok); if (ok) { value_ = edb::value80(value); } updateHexEntry(); } } edb-debugger/plugins/ODbgRegisterView/Canvas.cpp0000644000175000017500000000116213765535463021504 0ustar eteraneteran #include "Canvas.h" #include "ODbgRV_Util.h" #include #include namespace ODbgRegisterView { Canvas::Canvas(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f) { setObjectName("RegViewCanvas"); const auto canvasLayout = new QVBoxLayout(this); canvasLayout->setSpacing(letter_size(parent->font()).height() / 2); canvasLayout->setContentsMargins(parent->contentsMargins()); canvasLayout->setAlignment(Qt::AlignTop); setLayout(canvasLayout); setBackgroundRole(QPalette::Base); setAutoFillBackground(true); } void Canvas::mousePressEvent(QMouseEvent *event) { event->ignore(); } } edb-debugger/plugins/ODbgRegisterView/MultiBitFieldWidget.cpp0000644000175000017500000000444413765535463024140 0ustar eteraneteran #include "MultiBitFieldWidget.h" #include "BitFieldDescription.h" #include "BitFieldFormatter.h" #include "ODbgRV_Util.h" #include "RegisterView.h" namespace ODbgRegisterView { MultiBitFieldWidget::MultiBitFieldWidget(const QModelIndex &index, const BitFieldDescription &bfd, QWidget *parent, Qt::WindowFlags f) : ValueField(bfd.textWidth, index, BitFieldFormatter(bfd), parent, f), equal_(bfd.valueEqualComparator) { menuItems_.push_front(new_action_separator(this)); for (std::size_t i = bfd.valueNames.size(); i-- > 0;) { const auto &text = bfd.setValueTexts[i]; if (!text.isEmpty()) { auto action = new_action(text, this, [this, i]() { setValue(i); }); menuItems_.push_front(action); valueActions_.push_front(menuItems_.front()); } else valueActions_.push_front(nullptr); } } void MultiBitFieldWidget::setValue(int value) { using namespace RegisterViewModelBase; // TODO: Model: make it possible to set bit field itself, without manipulating parent directly // I.e. set value without knowing field offset, then setData(fieldIndex,word) const auto regIndex = index_.parent().sibling(index_.parent().row(), ModelValueColumn); auto byteArr = regIndex.data(Model::RawValueRole).toByteArray(); if (byteArr.isEmpty()) return; std::uint64_t word(0); std::memcpy(&word, byteArr.constData(), byteArr.size()); const auto mask = (1ull << (valid_variant(index_.data(Model::BitFieldLengthRole)).toInt() - 1)) * 2 - 1; const auto offset = valid_variant(index_.data(Model::BitFieldOffsetRole)).toInt(); word = (word & ~(mask << offset)) | (std::uint64_t(value) << offset); std::memcpy(byteArr.data(), &word, byteArr.size()); model()->setData(regIndex, byteArr, Model::RawValueRole); } void MultiBitFieldWidget::adjustToData() { using namespace RegisterViewModelBase; ValueField::adjustToData(); const auto byteArr = index_.data(Model::RawValueRole).toByteArray(); std::uint64_t word(0); assert(unsigned(byteArr.size()) <= sizeof(word)); std::memcpy(&word, byteArr.constData(), byteArr.size()); for (int value = 0; value < valueActions_.size(); ++value) { const auto action = valueActions_[value]; if (!action) continue; if (byteArr.isEmpty() || equal_(word, value)) action->setVisible(false); else action->setVisible(true); } } } edb-debugger/plugins/ODbgRegisterView/RegisterGroup.cpp0000644000175000017500000001174613765535463023103 0ustar eteraneteran #include "RegisterGroup.h" #include "ODbgRV_Util.h" #include "RegisterView.h" #include "ValueField.h" #include namespace ODbgRegisterView { RegisterGroup::RegisterGroup(const QString &name, QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f), name_(name) { setObjectName("RegisterGroup_" + name); { menuItems_.push_back(new_action_separator(this)); menuItems_.push_back(new_action(tr("Hide %1", "register group").arg(name), this, [this]() { hide(); regView()->groupHidden(this); })); } } void RegisterGroup::adjustWidth() { int widthNeeded = 0; Q_FOREACH (FieldWidget *field, fields()) { const auto widthToRequire = field->pos().x() + field->width(); if (widthToRequire > widthNeeded) widthNeeded = widthToRequire; } setMinimumWidth(widthNeeded); } void RegisterGroup::showMenu(const QPoint &position, const QList &additionalItems) const { return regView()->showMenu(position, additionalItems + menuItems_); } void RegisterGroup::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::RightButton) showMenu(event->globalPos(), menuItems_); else event->ignore(); } ODBRegView *RegisterGroup::regView() const { return checked_cast(parent() // canvas ->parent() // viewport ->parent() // regview ); } QMargins RegisterGroup::getFieldMargins() const { const auto charSize = letter_size(font()); const auto charWidth = charSize.width(); // extra space for highlighting rectangle, so that single-digit fields are easier to target const auto marginLeft = charWidth / 2; const auto marginRight = charWidth - marginLeft; return {marginLeft, 0, marginRight, 0}; } void RegisterGroup::insert(FieldWidget *widget) { if (const auto value = qobject_cast(widget)) { connect(value, &ValueField::selected, regView(), &ODBRegView::fieldSelected); } } void RegisterGroup::insert(int line, int column, FieldWidget *widget) { insert(widget); setupPositionAndSize(line, column, widget); widget->show(); } void RegisterGroup::setupPositionAndSize(int line, int column, FieldWidget *widget) { widget->adjustToData(); const auto margins = getFieldMargins(); const auto charSize = letter_size(font()); QPoint position(charSize.width() * column, charSize.height() * line); position -= QPoint(margins.left(), 0); QSize size(widget->size()); size += QSize(margins.left() + margins.right(), 0); widget->setMinimumSize(size); widget->move(position); // FIXME: why are e.g. regnames like FSR truncated without the -1? widget->setContentsMargins({margins.left(), margins.top(), margins.right() - 1, margins.bottom()}); const auto potentialNewWidth = widget->pos().x() + widget->width(); const auto potentialNewHeight = widget->pos().y() + widget->height(); const auto oldMinSize = minimumSize(); if (potentialNewWidth > oldMinSize.width() || potentialNewHeight > oldMinSize.height()) { setMinimumSize(std::max(potentialNewWidth, oldMinSize.width()), std::max(potentialNewHeight, oldMinSize.height())); } } int RegisterGroup::lineAfterLastField() const { const auto fields = this->fields(); const auto bottomField = std::max_element(fields.begin(), fields.end(), [](FieldWidget *l, FieldWidget *r) { return l->pos().y() < r->pos().y(); }); return bottomField == fields.end() ? 0 : (*bottomField)->pos().y() / (*bottomField)->height() + 1; } void RegisterGroup::appendNameValueComment(const QModelIndex &nameIndex, const QString &tooltip, bool insertComment) { assert(nameIndex.isValid()); using namespace RegisterViewModelBase; const auto nameWidth = nameIndex.data(Model::FixedLengthRole).toInt(); assert(nameWidth > 0); const auto valueIndex = nameIndex.sibling(nameIndex.row(), Model::VALUE_COLUMN); const auto valueWidth = valueIndex.data(Model::FixedLengthRole).toInt(); assert(valueWidth > 0); const int line = lineAfterLastField(); int column = 0; const auto nameField = new FieldWidget(nameWidth, nameIndex.data().toString(), this); insert(line, column, nameField); column += nameWidth + 1; const auto valueField = new ValueField(valueWidth, valueIndex, this); insert(line, column, valueField); if (!tooltip.isEmpty()) { nameField->setToolTip(tooltip); valueField->setToolTip(tooltip); } if (insertComment) { column += valueWidth + 1; const auto commentIndex = nameIndex.sibling(nameIndex.row(), Model::COMMENT_COLUMN); insert(line, column, new FieldWidget(0, commentIndex, this)); } } QList RegisterGroup::fields() const { const QObjectList children = this->children(); QList fields; for (QObject *child : children) { const auto field = qobject_cast(child); if (field) { fields.append(field); } } return fields; } QList RegisterGroup::valueFields() const { QList allValues; Q_FOREACH (FieldWidget *field, fields()) { const auto value = qobject_cast(field); if (value) { allValues.push_back(value); } } return allValues; } } edb-debugger/plugins/ODbgRegisterView/SimdValueManager.cpp0000644000175000017500000001645313765535463023466 0ustar eteraneteran #include "SimdValueManager.h" #include "ODbgRV_Util.h" #include "ValueField.h" #include "util/Container.h" namespace ODbgRegisterView { SimdValueManager::SimdValueManager(int lineInGroup, const QModelIndex &nameIndex, RegisterGroup *parent) : QObject(parent), regIndex_(nameIndex), lineInGroup_(lineInGroup) { setupMenu(); assert(nameIndex.isValid()); connect(nameIndex.model(), SIGNAL(SIMDDisplayFormatChanged()), this, SLOT(displayFormatChanged())); displayFormatChanged(); } void SimdValueManager::fillGroupMenu() { const auto group = this->group(); group->menuItems_.push_back(new_action_separator(this)); group->menuItems_.push_back(menuItems_[VIEW_AS_BYTES]); group->menuItems_.push_back(menuItems_[VIEW_AS_WORDS]); group->menuItems_.push_back(menuItems_[VIEW_AS_DWORDS]); group->menuItems_.push_back(menuItems_[VIEW_AS_QWORDS]); group->menuItems_.push_back(new_action_separator(this)); group->menuItems_.push_back(menuItems_[VIEW_AS_FLOAT32]); group->menuItems_.push_back(menuItems_[VIEW_AS_FLOAT64]); group->menuItems_.push_back(new_action_separator(this)); group->menuItems_.push_back(menuItems_[VIEW_INT_AS_HEX]); group->menuItems_.push_back(menuItems_[VIEW_INT_AS_SIGNED]); group->menuItems_.push_back(menuItems_[VIEW_INT_AS_UNSIGNED]); } auto SimdValueManager::model() const -> Model * { const auto model = static_cast(regIndex_.model()); // The model is not supposed to have been created as const object, // and our manipulations won't invalidate the index. // Thus cast the const away. return const_cast(model); } void SimdValueManager::showAsInt(Model::ElementSize size) { model()->setChosenSIMDSize(regIndex_.parent(), size); model()->setChosenSIMDFormat(regIndex_.parent(), intMode_); } void SimdValueManager::showAsFloat(Model::ElementSize size) { model()->setChosenSIMDFormat(regIndex_.parent(), NumberDisplayMode::Float); switch (size) { case Model::ElementSize::DWORD: model()->setChosenSIMDSize(regIndex_.parent(), Model::ElementSize::DWORD); break; case Model::ElementSize::QWORD: model()->setChosenSIMDSize(regIndex_.parent(), Model::ElementSize::QWORD); break; default: EDB_PRINT_AND_DIE("Unexpected size: ", size); } } void SimdValueManager::setIntFormat(NumberDisplayMode format) { model()->setChosenSIMDFormat(regIndex_.parent(), format); } void SimdValueManager::setupMenu() { const auto group = this->group(); const auto validFormats = valid_variant(regIndex_.parent().data(Model::ValidSIMDFormatsRole)).value>(); // Setup menu if we're the first value field creator if (group->valueFields().isEmpty()) { menuItems_.push_back(new_action(tr("View %1 as bytes").arg(group->name_), group, [this]() { showAsInt(Model::ElementSize::BYTE); })); menuItems_.push_back(new_action(tr("View %1 as words").arg(group->name_), group, [this]() { showAsInt(Model::ElementSize::WORD); })); menuItems_.push_back(new_action(tr("View %1 as doublewords").arg(group->name_), group, [this]() { showAsInt(Model::ElementSize::DWORD); })); menuItems_.push_back(new_action(tr("View %1 as quadwords").arg(group->name_), group, [this]() { showAsInt(Model::ElementSize::QWORD); })); if (util::contains(validFormats, NumberDisplayMode::Float)) { menuItems_.push_back(new_action(tr("View %1 as 32-bit floats").arg(group->name_), group, [this]() { showAsFloat(Model::ElementSize::DWORD); })); menuItems_.push_back(new_action(tr("View %1 as 64-bit floats").arg(group->name_), group, [this]() { showAsFloat(Model::ElementSize::QWORD); })); } else { // create empty elements to leave further items with correct indices menuItems_.push_back(new_action_separator(this)); menuItems_.push_back(new_action_separator(this)); } menuItems_.push_back(new_action(tr("View %1 integers as hex").arg(group->name_), group, [this]() { setIntFormat(NumberDisplayMode::Hex); })); menuItems_.push_back(new_action(tr("View %1 integers as signed").arg(group->name_), group, [this]() { setIntFormat(NumberDisplayMode::Signed); })); menuItems_.push_back(new_action(tr("View %1 integers as unsigned").arg(group->name_), group, [this]() { setIntFormat(NumberDisplayMode::Unsigned); })); fillGroupMenu(); } } void SimdValueManager::updateMenu() { if (menuItems_.isEmpty()) return; Q_FOREACH (auto item, menuItems_) item->setVisible(true); using RegisterViewModelBase::Model; switch (currentSize()) { case Model::ElementSize::BYTE: menuItems_[VIEW_AS_BYTES]->setVisible(false); break; case Model::ElementSize::WORD: menuItems_[VIEW_AS_WORDS]->setVisible(false); break; case Model::ElementSize::DWORD: if (currentFormat() != NumberDisplayMode::Float) menuItems_[VIEW_AS_DWORDS]->setVisible(false); else menuItems_[VIEW_AS_FLOAT32]->setVisible(false); break; case Model::ElementSize::QWORD: if (currentFormat() != NumberDisplayMode::Float) menuItems_[VIEW_AS_QWORDS]->setVisible(false); else menuItems_[VIEW_AS_FLOAT64]->setVisible(false); break; default: EDB_PRINT_AND_DIE("Unexpected current size: ", currentSize()); } switch (currentFormat()) { case NumberDisplayMode::Float: menuItems_[VIEW_INT_AS_HEX]->setVisible(false); menuItems_[VIEW_INT_AS_SIGNED]->setVisible(false); menuItems_[VIEW_INT_AS_UNSIGNED]->setVisible(false); break; case NumberDisplayMode::Hex: menuItems_[VIEW_INT_AS_HEX]->setVisible(false); break; case NumberDisplayMode::Signed: menuItems_[VIEW_INT_AS_SIGNED]->setVisible(false); break; case NumberDisplayMode::Unsigned: menuItems_[VIEW_INT_AS_UNSIGNED]->setVisible(false); break; } } RegisterGroup *SimdValueManager::group() const { return checked_cast(parent()); } void SimdValueManager::displayFormatChanged() { const auto newFormat = currentFormat(); if (newFormat != NumberDisplayMode::Float) { intMode_ = newFormat; } Q_FOREACH (const auto elem, elements_) { elem->deleteLater(); } elements_.clear(); using RegisterViewModelBase::Model; const auto model = regIndex_.model(); const int sizeRow = valid_variant(regIndex_.parent().data(Model::ChosenSIMDSizeRowRole)).toInt(); QModelIndex sizeIndex = model->index(sizeRow, ModelNameColumn, regIndex_); const auto elemCount = model->rowCount(sizeIndex); const auto regNameWidth = valid_variant(regIndex_.data(Model::FixedLengthRole)).toInt(); int column = regNameWidth + 1; const auto elemWidth = valid_variant(model->index(0, ModelValueColumn, sizeIndex).data(Model::FixedLengthRole)).toInt(); for (int elemN = elemCount - 1; elemN >= 0; --elemN) { const auto elemIndex = model->index(elemN, ModelValueColumn, sizeIndex); const auto field = new ValueField(elemWidth, elemIndex, group()); elements_.push_back(field); field->setAlignment(Qt::AlignRight); group()->insert(lineInGroup_, column, field); column += elemWidth + 1; } updateMenu(); } RegisterViewModelBase::Model::ElementSize SimdValueManager::currentSize() const { using RegisterViewModelBase::Model; const int size = valid_variant(regIndex_.parent().data(Model::ChosenSIMDSizeRole)).toInt(); return static_cast(size); } NumberDisplayMode SimdValueManager::currentFormat() const { using RegisterViewModelBase::Model; const int size = valid_variant(regIndex_.parent().data(Model::ChosenSIMDFormatRole)).toInt(); return static_cast(size); } } edb-debugger/plugins/ODbgRegisterView/GprEdit.cpp0000644000175000017500000001163313765535463021633 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "GprEdit.h" #include "QLongValidator.h" #include "QULongValidator.h" #include "util/Font.h" #include #include #include #include namespace ODbgRegisterView { namespace { const QRegExpValidator byteHexValidator(QRegExp("[0-9a-fA-F]{0,2}")); const QRegExpValidator wordHexValidator(QRegExp("[0-9a-fA-F]{0,4}")); const QRegExpValidator dwordHexValidator(QRegExp("[0-9a-fA-F]{0,8}")); const QRegExpValidator qwordHexValidator(QRegExp("[0-9a-fA-F]{0,16}")); const QLongValidator byteSignedValidator(INT8_MIN, INT8_MAX); const QLongValidator wordSignedValidator(INT16_MIN, INT16_MAX); const QLongValidator dwordSignedValidator(INT32_MIN, INT32_MAX); const QLongValidator qwordSignedValidator(INT64_MIN, INT64_MAX); const QULongValidator byteUnsignedValidator(0, UINT8_MAX); const QULongValidator wordUnsignedValidator(0, UINT16_MAX); const QULongValidator dwordUnsignedValidator(0, UINT32_MAX); const QULongValidator qwordUnsignedValidator(0, UINT64_MAX); const std::map hexValidators = { {1, &byteHexValidator}, {2, &wordHexValidator}, {4, &dwordHexValidator}, {8, &qwordHexValidator}}; const std::map signedValidators = { {1, &byteSignedValidator}, {2, &wordSignedValidator}, {4, &dwordSignedValidator}, {8, &qwordSignedValidator}}; const std::map unsignedValidators = { {1, &byteUnsignedValidator}, {2, &wordUnsignedValidator}, {4, &dwordUnsignedValidator}, {8, &qwordUnsignedValidator}}; } void GprEdit::setupFormat(Format newFormat) { format_ = newFormat; switch (format_) { case Format::Hex: setValidator(hexValidators.at(integerSize_)); naturalWidthInChars_ = 2 * integerSize_; break; case Format::Signed: setValidator(signedValidators.at(integerSize_)); naturalWidthInChars_ = 1 + std::lround(integerSize_ * std::log10(256.)); break; case Format::Unsigned: setValidator(unsignedValidators.at(integerSize_)); naturalWidthInChars_ = std::lround(integerSize_ * std::log10(256.)); break; case Format::Character: setMaxLength(1); break; default: Q_ASSERT("Unexpected format value" && 0); } } GprEdit::GprEdit(std::size_t offsetInInteger, std::size_t integerSize, Format format, QWidget *parent) : QLineEdit(parent), naturalWidthInChars_(2 * integerSize), integerSize_(integerSize), offsetInInteger_(offsetInInteger) { setupFormat(format); } void GprEdit::setGPRValue(std::uint64_t gprValue) { std::uint64_t value(0); signBit_ = format_ == Format::Signed ? 1ull << (8 * integerSize_ - 1) : 0; if ((gprValue >> 8 * offsetInInteger_) & signBit_) value = -1; std::memcpy(&value, reinterpret_cast(&gprValue) + offsetInInteger_, integerSize_); switch (format_) { case Format::Hex: setText(QString("%1").arg(value, naturalWidthInChars_, 16, QChar('0'))); break; case Format::Signed: setText(QString("%1").arg(static_cast(value))); break; case Format::Unsigned: setText(QString("%1").arg(value)); break; case Format::Character: setText(QChar(static_cast(value))); break; } } void GprEdit::updateGPRValue(std::uint64_t &gpr) const { bool ok; std::uint64_t value; switch (format_) { case Format::Hex: value = text().toULongLong(&ok, 16); break; case Format::Signed: value = text().toLongLong(&ok); break; case Format::Unsigned: value = text().toULongLong(&ok); break; case Format::Character: value = text().toStdString()[0]; break; default: Q_ASSERT("Unexpected format value" && 0); } std::memcpy(reinterpret_cast(&gpr) + offsetInInteger_, &value, integerSize_); } QSize GprEdit::sizeHint() const { const auto baseHint = QLineEdit::sizeHint(); // taking long enough reference char to make enough room even in presence of inner shadows like in Oxygen style const auto charWidth = Font::maxWidth(QFontMetrics(font())); const auto textMargins = this->textMargins(); const auto contentsMargins = this->contentsMargins(); int customWidth = charWidth * naturalWidthInChars_ + textMargins.left() + contentsMargins.left() + textMargins.right() + contentsMargins.right() + 1 * charWidth; // additional char to make edit field not too tight return QSize(customWidth, baseHint.height()).expandedTo(QApplication::globalStrut()); } } edb-debugger/plugins/ODbgRegisterView/ODbgRV_Common.h0000644000175000017500000000202613765535463022331 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ODBG_REGISTER_VIEW_COMMON_H_20170817_ #define ODBG_REGISTER_VIEW_COMMON_H_20170817_ #include namespace ODbgRegisterView { constexpr Qt::Key SetToZeroKey = Qt::Key_Z; constexpr Qt::Key DecrementKey = Qt::Key_Minus; constexpr Qt::Key IncrementKey = Qt::Key_Plus; static constexpr const char *GprCategoryName = "General Purpose"; } #endif edb-debugger/plugins/ODbgRegisterView/BitFieldDescription.h0000644000175000017500000000114513765535463023625 0ustar eteraneteran #ifndef BIT_FIELD_DESCRIPTION_H_20191119_ #define BIT_FIELD_DESCRIPTION_H_20191119_ #include #include class QString; namespace ODbgRegisterView { struct BitFieldDescription { int textWidth; std::vector valueNames; std::vector setValueTexts; std::function const valueEqualComparator; BitFieldDescription( int textWidth, const std::vector &valueNames, const std::vector &setValueTexts, const std::function &valueEqualComparator = [](unsigned a, unsigned b) { return a == b; }); }; } #endif edb-debugger/plugins/ODbgRegisterView/FieldWidget.h0000644000175000017500000000310513765535463022124 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef FIELD_WIDGET_H_20170818_ #define FIELD_WIDGET_H_20170818_ #include #include namespace ODbgRegisterView { class ODBRegView; class RegisterGroup; class FieldWidget : public QLabel { Q_OBJECT void init(int fieldWidth); protected: QPersistentModelIndex index_; int fieldWidth_; ODBRegView *regView() const; RegisterGroup *group() const; public: FieldWidget(int fieldWidth, const QModelIndex &index_, QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); FieldWidget(int fieldWidth, const QString &fixedText, QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); explicit FieldWidget(const QString &fixedText, QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); public: virtual QString text() const; int lineNumber() const; int columnNumber() const; int fieldWidth() const; public Q_SLOTS: virtual void adjustToData(); }; } #endif edb-debugger/plugins/ODbgRegisterView/NumberEdit.cpp0000644000175000017500000000213413765535463022327 0ustar eteraneteran #include "NumberEdit.h" #include "util/Font.h" #include namespace ODbgRegisterView { NumberEdit::NumberEdit(int column, int colSpan, QWidget *parent) : QLineEdit(parent), column_(column), colSpan_(colSpan) { } int NumberEdit::column() const { return column_; } int NumberEdit::colSpan() const { return colSpan_; } void NumberEdit::setNaturalWidthInChars(int nChars) { naturalWidthInChars_ = nChars; } QSize NumberEdit::minimumSizeHint() const { return sizeHint(); } QSize NumberEdit::sizeHint() const { const auto baseHint = QLineEdit::sizeHint(); // taking long enough reference char to make enough room even in presence of inner shadows like in Oxygen style const auto charWidth = Font::maxWidth(QFontMetrics(font())); const auto textMargins = this->textMargins(); const auto contentsMargins = this->contentsMargins(); int customWidth = charWidth * naturalWidthInChars_ + textMargins.left() + contentsMargins.left() + textMargins.right() + contentsMargins.right(); return QSize(customWidth, baseHint.height()).expandedTo(QApplication::globalStrut()); } } edb-debugger/plugins/ODbgRegisterView/FpuValueField.h0000644000175000017500000000147613765535463022441 0ustar eteraneteran #ifndef FPU_VALUE_FIELD_H_20191119_ #define FPU_VALUE_FIELD_H_20191119_ #include "ValueField.h" namespace ODbgRegisterView { #if defined(EDB_X86) || defined(EDB_X86_64) class FpuValueField final : public ValueField { Q_OBJECT private: int showAsRawActionIndex; int showAsFloatActionIndex; FieldWidget *commentWidget; int row; int column; QPersistentModelIndex tagValueIndex; bool groupDigits = false; public: // Will add itself and commentWidget to the group and renew their positions as needed FpuValueField(int fieldWidth, const QModelIndex ®ValueIndex, const QModelIndex &tagValueIndex, RegisterGroup *group, FieldWidget *commentWidget, int row, int column); public Q_SLOTS: void showFPUAsRaw(); void showFPUAsFloat(); void displayFormatChanged(); void updatePalette() override; }; #endif } #endif edb-debugger/plugins/ODbgRegisterView/VolatileNameField.cpp0000644000175000017500000000201013765535463023606 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "VolatileNameField.h" namespace ODbgRegisterView { VolatileNameField::VolatileNameField(int fieldWidth, const std::function &valueFormatter, QWidget *parent, Qt::WindowFlags f) : FieldWidget(fieldWidth, "", parent, f), valueFormatter(valueFormatter) { } QString VolatileNameField::text() const { return valueFormatter(); } } edb-debugger/plugins/ODbgRegisterView/DialogEditGPR.h0000644000175000017500000000443213765535463022317 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DIALOG_EDIT_GPR_H_20151011_ #define DIALOG_EDIT_GPR_H_20151011_ #include "Register.h" #include #include #include #include class QLabel; namespace ODbgRegisterView { class GprEdit; class DialogEditGPR : public QDialog { Q_OBJECT public: explicit DialogEditGPR(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); public: Register value() const; void setValue(const Register ®); private Q_SLOTS: void onTextEdited(const QString &); private: enum Column { FORMAT_LABELS_COL, FIRST_ENTRY_COL, GPR64_COL = FIRST_ENTRY_COL, GPR32_COL, GPR16_COL, GPR8H_COL, GPR8L_COL, TOTAL_COLS, ENTRY_COLS = TOTAL_COLS - 1, CHAR_COLS = 2 }; enum Row { GPR_LABELS_ROW, FIRST_ENTRY_ROW, HEX_ROW = FIRST_ENTRY_ROW, SIGNED_ROW, UNSIGNED_ROW, LAST_FULL_LENGTH_ROW = UNSIGNED_ROW, CHAR_ROW, ROW_AFTER_ENTRIES, FULL_LENGTH_ROWS = LAST_FULL_LENGTH_ROW - FIRST_ENTRY_ROW + 1, ENTRY_ROWS = ROW_AFTER_ENTRIES - FIRST_ENTRY_ROW }; protected: bool eventFilter(QObject *, QEvent *) override; private: void updateAllEntriesExcept(GprEdit *notUpdated); void hideColumn(Column col); void hideRow(Row row); void setupEntriesAndLabels(); void resetLayout(); QLabel *&columnLabel(Column col); QLabel *&rowLabel(Row row); GprEdit *&entry(Row row, Column col); void setupFocus(); private: std::array labels_ = {{nullptr}}; std::array entries_ = {{nullptr}}; std::uint64_t value_; std::size_t bitSize_ = 0; Register reg_; }; } #endif edb-debugger/plugins/ODbgRegisterView/ValueField.cpp0000644000175000017500000003361413765535463022320 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "ValueField.h" #include "DialogEditGPR.h" #include "DialogEditSimdRegister.h" #include "ODbgRV_Common.h" #include "ODbgRV_Util.h" #include "RegisterGroup.h" #include "RegisterView.h" #include "util/Font.h" #if defined(EDB_X86) || defined(EDB_X86_64) #include "DialogEditFPU.h" #include "ODbgRV_x86Common.h" #endif #include #include #include #include #include #include #include namespace ODbgRegisterView { namespace { QStyle *flatStyle = nullptr; } ValueField::ValueField(int fieldWidth, const QModelIndex &index, QWidget *parent, Qt::WindowFlags f) : ValueField( fieldWidth, index, [](const QString &s) { return s; }, parent, f) { } ValueField::ValueField(int fieldWidth, const QModelIndex &index, const std::function &valueFormatter, QWidget *parent, Qt::WindowFlags f) : FieldWidget(fieldWidth, index, parent, f), valueFormatter_(valueFormatter) { setObjectName("ValueField"); setDisabled(false); setMouseTracking(true); // Set some known style to avoid e.g. Oxygen's label transition animations, which // break updating of colors such as "register changed" when single-stepping frequently #define FLAT_STYLE_NAME "fusion" if (!flatStyle) flatStyle = QStyleFactory::create(FLAT_STYLE_NAME); assert(flatStyle); setStyle(flatStyle); using namespace RegisterViewModelBase; if (index.data(Model::IsNormalRegisterRole).toBool() || index.data(Model::IsSIMDElementRole).toBool()) { menuItems_.push_back(new_action(tr("&Modify") + QChar(0x2026), this, [this](bool) { defaultAction(); })); menuItems_.back()->setShortcut(QKeySequence(Qt::Key_Enter)); } else if (index.data(Model::IsBitFieldRole).toBool() && index.data(Model::BitFieldLengthRole).toInt() == 1) { menuItems_.push_back(new_action(tr("&Toggle"), this, [this](bool) { defaultAction(); })); menuItems_.back()->setShortcut(QKeySequence(Qt::Key_Enter)); } menuItems_.push_back(new_action(tr("&Copy to clipboard"), this, [this](bool) { copyToClipboard(); })); menuItems_.back()->setShortcut(QKeySequence::Copy); #if defined(EDB_X86) || defined(EDB_X86_64) if (index.sibling(index.row(), ModelNameColumn).data().toString() == FsrName) { menuItems_.push_back(new_action(tr("P&ush FPU stack"), this, [this](bool) { pushFPUStack(); })); menuItems_.push_back(new_action(tr("P&op FPU stack"), this, [this](bool) { popFPUStack(); })); } #endif if (index.parent().data().toString() == GprCategoryName) { // These should be above others, so prepending instead of appending menuItems_.push_front(new_action(tr("In&vert"), this, [this](bool) { invert(); })); setToOneAction_ = new_action(tr("Set to &1"), this, [this](bool) { setToOne(); }); menuItems_.push_front(setToOneAction_); setToZeroAction_ = new_action(tr("&Zero"), this, [this](bool) { setZero(); }); menuItems_.push_front(setToZeroAction_); menuItems_.front()->setShortcut(QKeySequence(SetToZeroKey)); menuItems_.push_front(new_action(tr("&Decrement"), this, [this](bool) { decrement(); })); menuItems_.front()->setShortcut(QKeySequence(DecrementKey)); menuItems_.push_front(new_action(tr("&Increment"), this, [this](bool) { increment(); })); menuItems_.front()->setShortcut(QKeySequence(IncrementKey)); } } RegisterViewModelBase::Model *ValueField::model() const { using namespace RegisterViewModelBase; const auto model = static_cast(index_.model()); // The model is not supposed to have been created as const object, // and our manipulations won't invalidate the index. // Thus cast the const away. return const_cast(model); } ValueField *ValueField::bestNeighbor(const std::function &firstIsBetter) const { ValueField *result = nullptr; Q_FOREACH (const auto neighbor, regView()->valueFields()) { if (neighbor->isVisible() && firstIsBetter(field_position(neighbor), result, field_position(this))) { result = neighbor; } } return result; } ValueField *ValueField::up() const { return bestNeighbor([](const QPoint &nPos, const ValueField *up, const QPoint &fPos) { return nPos.y() < fPos.y() && (!up || distance_squared(nPos, fPos) < distance_squared(field_position(up), fPos)); }); } ValueField *ValueField::down() const { return bestNeighbor([](const QPoint &nPos, const ValueField *down, const QPoint &fPos) { return nPos.y() > fPos.y() && (!down || distance_squared(nPos, fPos) < distance_squared(field_position(down), fPos)); }); } ValueField *ValueField::left() const { return bestNeighbor([](const QPoint &nPos, const ValueField *left, const QPoint &fPos) { return nPos.y() == fPos.y() && nPos.x() < fPos.x() && (!left || left->x() < nPos.x()); }); } ValueField *ValueField::right() const { return bestNeighbor([](const QPoint &nPos, const ValueField *right, const QPoint &fPos) { return nPos.y() == fPos.y() && nPos.x() > fPos.x() && (!right || right->x() > nPos.x()); }); } QString ValueField::text() const { return valueFormatter_(FieldWidget::text()); } bool ValueField::changed() const { if (!index_.isValid()) { return true; } return valid_variant(index_.data(RegisterViewModelBase::Model::RegisterChangedRole)).toBool(); } QColor ValueField::fgColorForChangedField() const { return Qt::red; // TODO: read from user palette } bool ValueField::isSelected() const { return selected_; } void ValueField::editNormalReg(const QModelIndex &indexToEdit, const QModelIndex &clickedIndex) const { using namespace RegisterViewModelBase; const auto rV = model()->data(indexToEdit, Model::ValueAsRegisterRole); if (!rV.isValid()) { return; } auto r = rV.value(); if (!r) { return; } if ((r.type() != Register::TYPE_SIMD) && r.bitSize() <= 64) { const auto gprEdit = regView()->gprEditDialog(); gprEdit->setValue(r); if (gprEdit->exec() == QDialog::Accepted) { r = gprEdit->value(); model()->setData(indexToEdit, QVariant::fromValue(r), Model::ValueAsRegisterRole); } } else if (r.type() == Register::TYPE_SIMD) { const auto simdEdit = regView()->simdEditDialog(); simdEdit->setValue(r); const int size = valid_variant(indexToEdit.parent().data(Model::ChosenSIMDSizeRole)).toInt(); const int format = valid_variant(indexToEdit.parent().data(Model::ChosenSIMDFormatRole)).toInt(); const int elementIndex = clickedIndex.row(); simdEdit->set_current_element(static_cast(size), static_cast(format), elementIndex); if (simdEdit->exec() == QDialog::Accepted) { r = simdEdit->value(); model()->setData(indexToEdit, QVariant::fromValue(r), Model::ValueAsRegisterRole); } } #if defined(EDB_X86) || defined(EDB_X86_64) else if (r.type() == Register::TYPE_FPU) { const auto fpuEdit = regView()->fpuEditDialog(); fpuEdit->setValue(r); if (fpuEdit->exec() == QDialog::Accepted) { r = fpuEdit->value(); model()->setData(indexToEdit, QVariant::fromValue(r), Model::ValueAsRegisterRole); } } #endif } QModelIndex ValueField::regIndex() const { using namespace RegisterViewModelBase; if (index_.data(Model::IsBitFieldRole).toBool()) { return index_; } if (index_.data(Model::IsNormalRegisterRole).toBool()) { return index_.sibling(index_.row(), ModelNameColumn); } return {}; } void ValueField::defaultAction() { using namespace RegisterViewModelBase; if (index_.data(Model::IsBitFieldRole).toBool() && index_.data(Model::BitFieldLengthRole).toInt() == 1) { // toggle // TODO: Model: make it possible to set bit field itself, without manipulating parent directly // I.e. set value without knowing field offset, then setData(fieldIndex,word) const auto regIndex = index_.parent().sibling(index_.parent().row(), ModelValueColumn); auto byteArr = regIndex.data(Model::RawValueRole).toByteArray(); if (byteArr.isEmpty()) return; std::uint64_t word(0); std::memcpy(&word, byteArr.constData(), byteArr.size()); const auto offset = valid_variant(index_.data(Model::BitFieldOffsetRole)).toInt(); word ^= 1ull << offset; std::memcpy(byteArr.data(), &word, byteArr.size()); model()->setData(regIndex, byteArr, Model::RawValueRole); } else if (index_.data(Model::IsNormalRegisterRole).toBool()) { editNormalReg(index_, index_); } else if (index_.data(Model::IsSIMDElementRole).toBool()) { editNormalReg(index_.parent().parent(), index_); } else if (index_.parent().data(Model::IsFPURegisterRole).toBool()) { editNormalReg(index_.parent(), index_); } } void ValueField::adjustToData() { if (index_.parent().data().toString() == GprCategoryName) { using RegisterViewModelBase::Model; auto byteArr = index_.data(Model::RawValueRole).toByteArray(); if (byteArr.isEmpty()) { return; } std::uint64_t value(0); assert(byteArr.size() <= int(sizeof(value))); std::memcpy(&value, byteArr.constData(), byteArr.size()); setToOneAction_->setVisible(value != 1u); setToZeroAction_->setVisible(value != 0u); } FieldWidget::adjustToData(); updatePalette(); } void ValueField::updatePalette() { if (changed()) { auto palette = this->palette(); const QColor changedFGColor = fgColorForChangedField(); palette.setColor(foregroundRole(), changedFGColor); palette.setColor(QPalette::HighlightedText, changedFGColor); setPalette(palette); } else setPalette(QApplication::palette()); QLabel::update(); } void ValueField::enterEvent(QEvent *) { hovered_ = true; updatePalette(); } void ValueField::leaveEvent(QEvent *) { hovered_ = false; updatePalette(); } void ValueField::select() { if (selected_) { return; } selected_ = true; model()->setActiveIndex(regIndex()); Q_EMIT selected(); updatePalette(); } void ValueField::showMenu(const QPoint &position) { group()->showMenu(position, menuItems_); } void ValueField::mousePressEvent(QMouseEvent *event) { if (event->button() & (Qt::LeftButton | Qt::RightButton)) { select(); } if (event->button() == Qt::RightButton && event->type() != QEvent::MouseButtonDblClick) { showMenu(event->globalPos()); } } void ValueField::unselect() { if (!selected_) { return; } selected_ = false; updatePalette(); } void ValueField::mouseDoubleClickEvent(QMouseEvent *event) { mousePressEvent(event); defaultAction(); } void ValueField::paintEvent(QPaintEvent *) { const auto regView = this->regView(); QPainter painter(this); QStyleOptionViewItem option; option.rect = rect(); option.showDecorationSelected = true; option.text = text(); option.font = font(); option.palette = palette(); option.textElideMode = Qt::ElideNone; option.state |= QStyle::State_Enabled; option.displayAlignment = alignment(); if (selected_) { option.state |= QStyle::State_Selected; } if (hovered_) { option.state |= QStyle::State_MouseOver; } if (regView->hasFocus()) { option.state |= QStyle::State_Active; } QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &option, &painter); } namespace { void add_to_top(RegisterViewModelBase::Model *model, const QModelIndex &fsrIndex, std::int16_t delta) { using namespace RegisterViewModelBase; // TODO: Model: make it possible to set bit field itself, without manipulating parent directly // I.e. set value without knowing field offset, then setData(fieldIndex,word) auto byteArr = fsrIndex.data(Model::RawValueRole).toByteArray(); if (byteArr.isEmpty()) return; std::uint16_t word(0); assert(byteArr.size() == sizeof(word)); std::memcpy(&word, byteArr.constData(), byteArr.size()); const auto value = (word >> 11) & 7; word = (word & ~0x3800) | (((value + delta) & 7) << 11); std::memcpy(byteArr.data(), &word, byteArr.size()); model->setData(fsrIndex, byteArr, Model::RawValueRole); } } #if defined(EDB_X86) || defined(EDB_X86_64) void ValueField::pushFPUStack() { assert(index_.sibling(index_.row(), ModelNameColumn).data().toString() == FsrName); add_to_top(model(), index_, -1); } void ValueField::popFPUStack() { assert(index_.sibling(index_.row(), ModelNameColumn).data().toString() == FsrName); add_to_top(model(), index_, +1); } #endif void ValueField::copyToClipboard() const { QApplication::clipboard()->setText(text()); } namespace { template void change_gpr(const QModelIndex &index, RegisterViewModelBase::Model *const model, const Op &change) { if (index.parent().data().toString() != GprCategoryName) { return; } using RegisterViewModelBase::Model; auto byteArr = index.data(Model::RawValueRole).toByteArray(); if (byteArr.isEmpty()) { return; } std::uint64_t value(0); assert(byteArr.size() <= int(sizeof(value))); std::memcpy(&value, byteArr.constData(), byteArr.size()); value = change(value); std::memcpy(byteArr.data(), &value, byteArr.size()); model->setData(index, byteArr, Model::RawValueRole); } } void ValueField::decrement() { change_gpr(index_, model(), [](std::uint64_t v) { return v - 1; }); } void ValueField::increment() { change_gpr(index_, model(), [](std::uint64_t v) { return v + 1; }); } void ValueField::invert() { change_gpr(index_, model(), [](std::uint64_t v) { return ~v; }); } void ValueField::setZero() { change_gpr(index_, model(), [](int) { return 0; }); } void ValueField::setToOne() { change_gpr(index_, model(), [](int) { return 1; }); } } edb-debugger/plugins/ODbgRegisterView/DialogEditSimdRegister.cpp0000644000175000017500000004330413765535463024624 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "DialogEditSimdRegister.h" #include "EntryGridKeyUpDownEventFilter.h" #include "FloatX.h" #include "NumberEdit.h" #include "QLongValidator.h" #include "QULongValidator.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace ODbgRegisterView { template void DialogEditSimdRegister::setupEntries(const QString &label, std::array &entries, int row, Func slot, int naturalWidthInChars) { auto contentsGrid = qobject_cast(layout()); contentsGrid->addWidget(new QLabel(label, this), row, ENTRIES_FIRST_COL - 1); for (std::size_t entryIndex = 0; entryIndex < NumEntries; ++entryIndex) { auto &entry = entries[entryIndex]; const int bytesPerEntry = NumBytes / NumEntries; entry = new NumberEdit(ENTRIES_FIRST_COL + bytesPerEntry * (NumEntries - 1 - entryIndex), bytesPerEntry, this); entry->setNaturalWidthInChars(naturalWidthInChars); connect(entry, &NumberEdit::textEdited, this, slot); entry->installEventFilter(this); } } DialogEditSimdRegister::DialogEditSimdRegister(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f), byteHexValidator_(new QRegExpValidator(QRegExp("[0-9a-fA-F]{0,2}"), this)), wordHexValidator_(new QRegExpValidator(QRegExp("[0-9a-fA-F]{0,4}"), this)), dwordHexValidator_(new QRegExpValidator(QRegExp("[0-9a-fA-F]{0,8}"), this)), qwordHexValidator_(new QRegExpValidator(QRegExp("[0-9a-fA-F]{0,16}"), this)), byteSignedValidator_(new QLongValidator(INT8_MIN, INT8_MAX, this)), wordSignedValidator_(new QLongValidator(INT16_MIN, INT16_MAX, this)), dwordSignedValidator_(new QLongValidator(INT32_MIN, INT32_MAX, this)), qwordSignedValidator_(new QLongValidator(INT64_MIN, INT64_MAX, this)), byteUnsignedValidator_(new QULongValidator(0, UINT8_MAX, this)), wordUnsignedValidator_(new QULongValidator(0, UINT16_MAX, this)), dwordUnsignedValidator_(new QULongValidator(0, UINT32_MAX, this)), qwordUnsignedValidator_(new QULongValidator(0, UINT64_MAX, this)), float32Validator_(new FloatXValidator(this)), float64Validator_(new FloatXValidator(this)), intMode_(NumberDisplayMode::Hex) { setWindowTitle(tr("Edit SIMD Register")); setModal(true); const auto allContentsGrid = new QGridLayout(this); for (int byteIndex = 0; byteIndex < NumBytes; ++byteIndex) { columnLabels_[byteIndex] = new QLabel(std::to_string(byteIndex).c_str(), this); columnLabels_[byteIndex]->setAlignment(Qt::AlignCenter); allContentsGrid->addWidget(columnLabels_[byteIndex], BYTE_INDICES_ROW, ENTRIES_FIRST_COL + NumBytes - 1 - byteIndex); } setupEntries( tr("Byte"), bytes_, BYTES_ROW, [this]() { onByteEdited(); }, 4); setupEntries( tr("Word"), words_, WORDS_ROW, [this]() { onWordEdited(); }, 6); setupEntries( tr("Doubleword"), dwords_, DWORDS_ROW, [this]() { onDwordEdited(); }, 11); setupEntries( tr("Quadword"), qwords_, QWORDS_ROW, [this]() { onQwordEdited(); }, 21); setupEntries( tr("float32"), floats32_, FLOATS32_ROW, [this]() { onFloat32Edited(); }, 14); setupEntries( tr("float64"), floats64_, FLOATS64_ROW, [this]() { onFloat64Edited(); }, 24); for (const auto &entry : floats32_) { entry->setValidator(float32Validator_); } for (const auto &entry : floats64_) { entry->setValidator(float64Validator_); } hexSignOKCancelLayout_ = new QHBoxLayout(); { const auto hexSignRadiosLayout = new QVBoxLayout(); radioHex_ = new QRadioButton(tr("Hexadecimal"), this); connect(radioHex_, &QRadioButton::toggled, this, &DialogEditSimdRegister::onHexToggled); // setChecked must be called after connecting of toggled() // in order to set validators for integer editors radioHex_->setChecked(true); hexSignRadiosLayout->addWidget(radioHex_); radioSigned_ = new QRadioButton(tr("Signed"), this); connect(radioSigned_, &QRadioButton::toggled, this, &DialogEditSimdRegister::onSignedToggled); hexSignRadiosLayout->addWidget(radioSigned_); radioUnsigned_ = new QRadioButton(tr("Unsigned"), this); connect(radioUnsigned_, &QRadioButton::toggled, this, &DialogEditSimdRegister::onUnsignedToggled); hexSignRadiosLayout->addWidget(radioUnsigned_); hexSignOKCancelLayout_->addLayout(hexSignRadiosLayout); } { const auto okCancelLayout = new QVBoxLayout(); okCancelLayout->addItem(new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding)); okCancel_ = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok, Qt::Horizontal, this); connect(okCancel_, &QDialogButtonBox::accepted, this, &DialogEditSimdRegister::accept); connect(okCancel_, &QDialogButtonBox::rejected, this, &DialogEditSimdRegister::reject); okCancelLayout->addWidget(okCancel_); hexSignOKCancelLayout_->addLayout(okCancelLayout); } resetLayout(); for (int byte = NumBytes - 1; byte > 0; --byte) { setTabOrder(bytes_[byte], bytes_[byte - 1]); } setTabOrder(bytes_.back(), words_.front()); for (int word = NumBytes / 2 - 1; word > 0; --word) { setTabOrder(words_[word], words_[word - 1]); } setTabOrder(words_.back(), dwords_.front()); for (int dword = NumBytes / 4 - 1; dword > 0; --dword) { setTabOrder(dwords_[dword], dwords_[dword - 1]); } setTabOrder(dwords_.back(), qwords_.front()); for (int qword = NumBytes / 8 - 1; qword > 0; --qword) { setTabOrder(qwords_[qword], qwords_[qword - 1]); } setTabOrder(qwords_.back(), floats32_.front()); for (int float32 = NumBytes / 4 - 1; float32 > 0; --float32) { setTabOrder(floats32_[float32], floats32_[float32 - 1]); } setTabOrder(floats32_.back(), floats64_.front()); for (int float64 = NumBytes / 8 - 1; float64 > 0; --float64) { setTabOrder(floats64_[float64], floats64_[float64 - 1]); } setTabOrder(floats64_.front(), radioHex_); setTabOrder(radioHex_, radioSigned_); setTabOrder(radioSigned_, radioUnsigned_); setTabOrder(radioUnsigned_, okCancel_); } template void DialogEditSimdRegister::updateFloatEntries(const std::array &entries, NumberEdit *notUpdated) { for (std::size_t i = 0; i < entries.size(); ++i) { if (entries[i] == notUpdated) { continue; } T value; std::memcpy(&value, &value_[i * sizeof(value)], sizeof(value)); entries[i]->setText(format_float(value)); } } template void DialogEditSimdRegister::updateIntegralEntries(const std::array &entries, NumberEdit *notUpdated) { for (std::size_t i = 0; i < entries.size(); ++i) { if (entries[i] == notUpdated) { continue; } T value; std::memcpy(&value, &value_[i * sizeof(value)], sizeof(value)); formatInteger(entries[i], value); } } void DialogEditSimdRegister::updateAllEntriesExcept(NumberEdit *notUpdated) { if (!reg_) { return; } updateIntegralEntries(bytes_, notUpdated); updateIntegralEntries(words_, notUpdated); updateIntegralEntries(dwords_, notUpdated); updateIntegralEntries(qwords_, notUpdated); updateFloatEntries(floats32_, notUpdated); updateFloatEntries(floats64_, notUpdated); } void DialogEditSimdRegister::resetLayout() { auto layout = qobject_cast(this->layout()); for (int col = ENTRIES_FIRST_COL; col < TOTAL_COLS; ++col) { int i = NumBytes - 1 - (col - ENTRIES_FIRST_COL); columnLabels_[i]->show(); const auto &byte = bytes_[i]; layout->addWidget(byte, BYTES_ROW, byte->column(), 1, byte->colSpan()); byte->show(); const auto &word = words_[i / 2]; layout->addWidget(word, WORDS_ROW, word->column(), 1, word->colSpan()); word->show(); const auto &dword = dwords_[i / 4]; layout->addWidget(dword, DWORDS_ROW, dword->column(), 1, dword->colSpan()); dword->show(); const auto &qword = qwords_[i / 8]; layout->addWidget(qword, QWORDS_ROW, qword->column(), 1, qword->colSpan()); qword->show(); const auto &float32 = floats32_[i / 4]; layout->addWidget(float32, FLOATS32_ROW, float32->column(), 1, float32->colSpan()); float32->show(); const auto &float64 = floats64_[i / 8]; layout->addWidget(float64, FLOATS64_ROW, float64->column(), 1, float64->colSpan()); float64->show(); } for (int row = ENTRIES_FIRST_ROW; row < ROW_AFTER_ENTRIES; ++row) layout->itemAtPosition(row, LABELS_COL)->widget()->show(); layout->removeItem(hexSignOKCancelLayout_); hexSignOKCancelLayout_->setParent(nullptr); layout->addLayout(hexSignOKCancelLayout_, ROW_AFTER_ENTRIES, ENTRIES_FIRST_COL, 1, NumBytes); } void DialogEditSimdRegister::hideColumns(EntriesCols afterLastToHide) { auto layout = qobject_cast(this->layout()); for (int col = ENTRIES_FIRST_COL; col < afterLastToHide; ++col) { int i = NumBytes - 1 - (col - ENTRIES_FIRST_COL); Q_ASSERT(0 < i && std::size_t(i) < bytes_.size()); columnLabels_[i]->hide(); // Spanned entries shouldn't just be hidden. If they are still in the grid, // then we get extra spacing between invisible columns, which is unwanted. // So we have to also remove them from the layout. layout->removeWidget(bytes_[i]); bytes_[i]->hide(); layout->removeWidget(words_[i / 2]); words_[i / 2]->hide(); layout->removeWidget(dwords_[i / 4]); dwords_[i / 4]->hide(); layout->removeWidget(qwords_[i / 8]); qwords_[i / 8]->hide(); layout->removeWidget(floats32_[i / 4]); floats32_[i / 4]->hide(); layout->removeWidget(floats64_[i / 8]); floats64_[i / 8]->hide(); } layout->removeItem(hexSignOKCancelLayout_); hexSignOKCancelLayout_->setParent(nullptr); layout->addLayout(hexSignOKCancelLayout_, ROW_AFTER_ENTRIES, afterLastToHide, 1, TOTAL_COLS - afterLastToHide); } void DialogEditSimdRegister::hideRows(EntriesRows rowToHide) { auto layout = qobject_cast(this->layout()); for (int col = 0; col < TOTAL_COLS; ++col) { const auto item = layout->itemAtPosition(rowToHide, col); if (item && item->widget()) { item->widget()->hide(); } } } bool DialogEditSimdRegister::eventFilter(QObject *obj, QEvent *event) { return entry_grid_key_event_filter(this, obj, event); } void DialogEditSimdRegister::setValue(const Register &newReg) { resetLayout(); assert(newReg.bitSize() <= 8 * sizeof(value_)); reg_ = newReg; util::mark_memory(&value_, value_.size()); if (QRegExp("mm[0-7]").exactMatch(reg_.name())) { const auto value = reg_.value(); std::memcpy(&value_, &value, sizeof(value)); hideColumns(MMX_FIRST_COL); // MMX registers are never used in float computations, so hide useless rows hideRows(FLOATS32_ROW); hideRows(FLOATS64_ROW); } else if (QRegExp("xmm[0-9]+").exactMatch(reg_.name())) { const auto value = reg_.value(); std::memcpy(&value_, &value, sizeof(value)); hideColumns(XMM_FIRST_COL); } else if (QRegExp("ymm[0-9]+").exactMatch(reg_.name())) { const auto value = reg_.value(); std::memcpy(&value_, &value, sizeof(value)); hideColumns(YMM_FIRST_COL); } else qCritical() << "DialogEditSimdRegister::setValue(" << reg_.name() << "): register type unsupported"; setWindowTitle(tr("Modify %1").arg(reg_.name().toUpper())); updateAllEntriesExcept(nullptr); } void DialogEditSimdRegister::set_current_element(RegisterViewModelBase::Model::ElementSize size, NumberDisplayMode format, int elementIndex) { using namespace RegisterViewModelBase; if (format != intMode_ && format != NumberDisplayMode::Float) { switch (format) { case NumberDisplayMode::Hex: radioHex_->setChecked(true); break; case NumberDisplayMode::Signed: radioSigned_->setChecked(true); break; case NumberDisplayMode::Unsigned: radioUnsigned_->setChecked(true); break; case NumberDisplayMode::Float: break; // silence the compiler, we'll never get here } } NumberEdit *edit = bytes_[0]; if (format == NumberDisplayMode::Float) { edit = floats32_[0]; if (size == Model::ElementSize::DWORD) edit = floats32_[elementIndex]; else if (size == Model::ElementSize::QWORD) edit = floats64_[elementIndex]; } else { switch (size) { case Model::ElementSize::BYTE: edit = bytes_[elementIndex]; break; case Model::ElementSize::WORD: edit = words_[elementIndex]; break; case Model::ElementSize::DWORD: edit = dwords_[elementIndex]; break; case Model::ElementSize::QWORD: edit = qwords_[elementIndex]; break; default: EDB_PRINT_AND_DIE("Unexpected size ", static_cast(size)); } } edit->setFocus(Qt::OtherFocusReason); } std::uint64_t DialogEditSimdRegister::readInteger(const NumberEdit *const edit) const { bool ok; switch (intMode_) { case NumberDisplayMode::Hex: return edit->text().toULongLong(&ok, 16); case NumberDisplayMode::Signed: return edit->text().toLongLong(&ok); case NumberDisplayMode::Unsigned: return edit->text().toULongLong(&ok); default: Q_ASSERT("Unexpected integer display mode" && 0); return 0xbadbadbadbadbad1; } } template void DialogEditSimdRegister::formatInteger(NumberEdit *const edit, Integer integer) const { switch (intMode_) { case NumberDisplayMode::Hex: edit->setText(QString("%1").arg(integer, 2 * sizeof(integer), 16, QChar('0'))); break; case NumberDisplayMode::Signed: using Int = typename std::remove_reference::type; using Signed = typename std::make_signed::type; edit->setText(QString("%1").arg(static_cast(integer))); break; case NumberDisplayMode::Unsigned: edit->setText(QString("%1").arg(integer)); break; default: Q_ASSERT("Unexpected integer display mode" && 0); return; } } template void DialogEditSimdRegister::onIntegerEdited(QObject *sender, const std::array &elements) { const auto changedElementEdit = qobject_cast(sender); std::size_t elementIndex = std::find(elements.begin(), elements.end(), changedElementEdit) - elements.begin(); Integer value = readInteger(elements[elementIndex]); std::memcpy(&value_[elementIndex * sizeof(value)], &value, sizeof(value)); updateAllEntriesExcept(elements[elementIndex]); } template void DialogEditSimdRegister::onFloatEdited(QObject *sender, const std::array &elements) { const auto changedFloatEdit = qobject_cast(sender); std::size_t floatIndex = std::find(elements.begin(), elements.end(), changedFloatEdit) - elements.begin(); bool ok = false; auto value = read_float(elements[floatIndex]->text(), ok); if (ok) { std::memcpy(&value_[floatIndex * sizeof(value)], &value, sizeof(value)); updateAllEntriesExcept(elements[floatIndex]); } } void DialogEditSimdRegister::onByteEdited() { onIntegerEdited(sender(), bytes_); } void DialogEditSimdRegister::onWordEdited() { onIntegerEdited(sender(), words_); } void DialogEditSimdRegister::onDwordEdited() { onIntegerEdited(sender(), dwords_); } void DialogEditSimdRegister::onQwordEdited() { onIntegerEdited(sender(), qwords_); } void DialogEditSimdRegister::onFloat32Edited() { onFloatEdited(sender(), floats32_); } void DialogEditSimdRegister::onFloat64Edited() { onFloatEdited(sender(), floats64_); } void DialogEditSimdRegister::onHexToggled(bool checked) { if ((checked && intMode_ != NumberDisplayMode::Hex) || !bytes_.front()->validator()) { intMode_ = NumberDisplayMode::Hex; for (const auto &byte : bytes_) byte->setValidator(byteHexValidator_); for (const auto &word : words_) word->setValidator(wordHexValidator_); for (const auto &dword : dwords_) dword->setValidator(dwordHexValidator_); for (const auto &qword : qwords_) qword->setValidator(qwordHexValidator_); updateAllEntriesExcept(nullptr); } } void DialogEditSimdRegister::onSignedToggled(bool checked) { if ((checked && intMode_ != NumberDisplayMode::Signed) || !bytes_.front()->validator()) { intMode_ = NumberDisplayMode::Signed; for (const auto &byte : bytes_) byte->setValidator(byteSignedValidator_); for (const auto &word : words_) word->setValidator(wordSignedValidator_); for (const auto &dword : dwords_) dword->setValidator(dwordSignedValidator_); for (const auto &qword : qwords_) qword->setValidator(qwordSignedValidator_); updateAllEntriesExcept(nullptr); } } void DialogEditSimdRegister::onUnsignedToggled(bool checked) { if ((checked && intMode_ != NumberDisplayMode::Unsigned) || !bytes_.front()->validator()) { intMode_ = NumberDisplayMode::Unsigned; for (const auto &byte : bytes_) byte->setValidator(byteUnsignedValidator_); for (const auto &word : words_) word->setValidator(wordUnsignedValidator_); for (const auto &dword : dwords_) dword->setValidator(dwordUnsignedValidator_); for (const auto &qword : qwords_) qword->setValidator(qwordUnsignedValidator_); updateAllEntriesExcept(nullptr); } } Register DialogEditSimdRegister::value() const { Register out(reg_); out.setValueFrom(value_); return out; } } edb-debugger/plugins/ODbgRegisterView/DialogEditSimdRegister.h0000644000175000017500000001057413765535463024274 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DIALOG_EDIT_SIMD_REGISTER_H_20151010_ #define DIALOG_EDIT_SIMD_REGISTER_H_20151010_ #include "Register.h" #include "RegisterViewModelBase.h" #include "Util.h" #include #include #include #include class QLabel; class QDialogButtonBox; class QHBoxLayout; class QLongValidator; class QRadioButton; class QRegExpValidator; class QULongValidator; class QValidator; namespace ODbgRegisterView { class NumberEdit; class DialogEditSimdRegister : public QDialog { Q_OBJECT private: static constexpr int NumBytes = 256 / 8; enum EntriesRows { BYTE_INDICES_ROW, BYTES_ROW, ENTRIES_FIRST_ROW = BYTES_ROW, WORDS_ROW, DWORDS_ROW, QWORDS_ROW, FLOATS32_ROW, FLOATS64_ROW, ROW_AFTER_ENTRIES }; enum EntriesCols { LABELS_COL, ENTRIES_FIRST_COL, YMM_FIRST_COL = ENTRIES_FIRST_COL, XMM_FIRST_COL = YMM_FIRST_COL + 16, MMX_FIRST_COL = XMM_FIRST_COL + 8, TOTAL_COLS = MMX_FIRST_COL + 8 }; public: explicit DialogEditSimdRegister(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); void setValue(const Register &value); void set_current_element(RegisterViewModelBase::Model::ElementSize size, NumberDisplayMode format, int elementIndex); Register value() const; protected: bool eventFilter(QObject *, QEvent *) override; private: template void setupEntries(const QString &label, std::array &entries, int row, Func slot, int naturalWidthInChars); std::uint64_t readInteger(const NumberEdit *edit) const; template void formatInteger(NumberEdit *edit, Integer integer) const; void updateAllEntriesExcept(NumberEdit *notUpdated); void hideColumns(EntriesCols preLast); void hideRows(EntriesRows rowToHide); void resetLayout(); private: template void onIntegerEdited(QObject *sender, const std::array &elements); template void onFloatEdited(QObject *sender, const std::array &elements); template void updateIntegralEntries(const std::array &entries, NumberEdit *notUpdated); template void updateFloatEntries(const std::array &entries, NumberEdit *notUpdated); private Q_SLOTS: void onByteEdited(); void onWordEdited(); void onDwordEdited(); void onQwordEdited(); void onFloat32Edited(); void onFloat64Edited(); void onHexToggled(bool checked); void onSignedToggled(bool checked); void onUnsignedToggled(bool checked); private: QHBoxLayout *hexSignOKCancelLayout_; QDialogButtonBox *okCancel_; QRadioButton *radioHex_; QRadioButton *radioSigned_; QRadioButton *radioUnsigned_; std::array floats64_; std::array floats32_; std::array qwords_; std::array dwords_; std::array words_; std::array bytes_; std::array columnLabels_; QRegExpValidator *byteHexValidator_; QRegExpValidator *wordHexValidator_; QRegExpValidator *dwordHexValidator_; QRegExpValidator *qwordHexValidator_; QLongValidator *byteSignedValidator_; QLongValidator *wordSignedValidator_; QLongValidator *dwordSignedValidator_; QLongValidator *qwordSignedValidator_; QULongValidator *byteUnsignedValidator_; QULongValidator *wordUnsignedValidator_; QULongValidator *dwordUnsignedValidator_; QULongValidator *qwordUnsignedValidator_; QValidator *float32Validator_; QValidator *float64Validator_; NumberDisplayMode intMode_; std::array value_; Register reg_; }; } #endif edb-debugger/plugins/ODbgRegisterView/VolatileNameField.h0000644000175000017500000000222513765535463023263 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef VOLATILE_NAME_FIELD_H_20151031_ #define VOLATILE_NAME_FIELD_H_20151031_ #include "FieldWidget.h" #include #include namespace ODbgRegisterView { class VolatileNameField : public FieldWidget { Q_OBJECT private: std::function valueFormatter; public: VolatileNameField(int fieldWidth, const std::function &valueFormatter, QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); QString text() const override; }; } #endif edb-debugger/plugins/ODbgRegisterView/Plugin.cpp0000644000175000017500000001735613765535463021543 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "Plugin.h" #include "ArchProcessor.h" #include "QtHelper.h" #include "RegisterView.h" #include "edb.h" #include #include #include #include #include #include namespace ODbgRegisterView { //Q_DECLARE_NAMESPACE_TR(ODbgRegisterView) namespace { const auto pluginName = QLatin1String("ODbgRegisterView"); const auto dockNameSuffixTemplate = QString(" <%1>"); const auto dockObjectNameTemplate = QString(pluginName + "-%1"); const auto views = QLatin1String("views"); } Plugin::Plugin(QObject *parent) : QObject(parent) { connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &Plugin::saveSettings); } QString Plugin::dockName() const { return tr("Registers"); } void Plugin::setupDocks() { QSettings settings; settings.beginGroup(pluginName); if (settings.value(views + "/size").isValid()) { const int size = settings.beginReadArray(views); for (int i = 0; i < size; ++i) { settings.setArrayIndex(i); createRegisterView(settings.group()); } } else { createRegisterView(); } } void Plugin::saveSettings() const { QSettings settings; const int size = registerViews_.size(); const auto arrayKey = pluginName + "/" + views; settings.remove(arrayKey); settings.beginWriteArray(arrayKey, size); for (int i = 0; i < size; ++i) { settings.setArrayIndex(i); registerViews_[i]->saveState(settings.group()); } } void Plugin::createRegisterView() { createRegisterView(""); } void Plugin::createRegisterView(const QString &settingsGroup) { if (auto *const mainWindow = qobject_cast(edb::v1::debugger_ui)) { const auto regView = new ODBRegView(settingsGroup, mainWindow); registerViews_.emplace_back(regView); regView->setModel(&edb::v1::arch_processor().registerViewModel()); const QString suffix = registerViews_.size() > 1 ? dockNameSuffixTemplate.arg(registerViews_.size()) : ""; auto *const regViewDockWidget = new QDockWidget(dockName() + suffix, mainWindow); const auto viewNumber = registerViews_.size(); regViewDockWidget->setObjectName(dockObjectNameTemplate.arg(viewNumber)); regViewDockWidget->setWidget(regView); mainWindow->addDockWidget(Qt::RightDockWidgetArea, regViewDockWidget); QList dockWidgets = mainWindow->findChildren(); for (QDockWidget *widget : dockWidgets) { if (widget != regViewDockWidget) { if (mainWindow->dockWidgetArea(widget) == Qt::RightDockWidgetArea) { mainWindow->tabifyDockWidget(widget, regViewDockWidget); // place the new doc widget OVER the one we tabbed with // register view is important... regViewDockWidget->show(); regViewDockWidget->raise(); break; } } } Q_ASSERT(menu_); const auto removeDockAction = new QAction(tr("Remove %1").arg(regViewDockWidget->windowTitle()), menu_); connect(removeDockAction, &QAction::triggered, this, [this, regViewDockWidget]() { removeDock(regViewDockWidget); }); menuDeleteRegViewActions_.emplace_back(removeDockAction); menu_->addAction(removeDockAction); } } void Plugin::renumerateDocks() const { for (std::size_t i = 0; i < registerViews_.size(); ++i) { const auto view = registerViews_[i]; Q_ASSERT(dynamic_cast(view->parentWidget())); QWidget *dock = view->parentWidget(); dock->setObjectName(dockObjectNameTemplate.arg(i + 1)); dock->setWindowTitle(dockName() + (i ? dockNameSuffixTemplate.arg(i + 1) : "")); } } void Plugin::removeDock(QWidget *whatToRemove) { Q_ASSERT(dynamic_cast(whatToRemove)); const auto dockToRemove = static_cast(whatToRemove); auto &views(registerViews_); const auto viewIter = std::find(views.begin(), views.end(), dockToRemove->widget()); const auto viewIndex = viewIter - views.begin(); const auto action = menuDeleteRegViewActions_[viewIndex]; whatToRemove->deleteLater(); action->deleteLater(); menu_->removeAction(action); views.erase(viewIter); menuDeleteRegViewActions_.erase(viewIndex + menuDeleteRegViewActions_.begin()); renumerateDocks(); } void Plugin::expandLSDown(bool checked) const { if (const auto mainWindow = qobject_cast(edb::v1::debugger_ui)) { mainWindow->setCorner(Qt::BottomLeftCorner, checked ? Qt::LeftDockWidgetArea : Qt::BottomDockWidgetArea); } } void Plugin::expandRSDown(bool checked) const { if (const auto mainWindow = qobject_cast(edb::v1::debugger_ui)) { mainWindow->setCorner(Qt::BottomRightCorner, checked ? Qt::RightDockWidgetArea : Qt::BottomDockWidgetArea); } } void Plugin::expandLSUp(bool checked) const { if (const auto mainWindow = qobject_cast(edb::v1::debugger_ui)) { mainWindow->setCorner(Qt::TopLeftCorner, checked ? Qt::LeftDockWidgetArea : Qt::TopDockWidgetArea); } } void Plugin::expandRSUp(bool checked) const { if (const auto mainWindow = qobject_cast(edb::v1::debugger_ui)) { mainWindow->setCorner(Qt::TopRightCorner, checked ? Qt::RightDockWidgetArea : Qt::TopDockWidgetArea); } } QMenu *Plugin::menu(QWidget *parent) { if (!menu_) { menu_ = new QMenu(tr("OllyDbg-like Register View"), parent); { const auto newRegisterView = new QAction(tr("New Register View"), menu_); connect(newRegisterView, &QAction::triggered, this, [this](bool) { createRegisterView(); }); menu_->addAction(newRegisterView); } // FIXME: setChecked calls currently don't really work, since at this stage mainWindow hasn't yet restored its state if (auto *const mainWindow = qobject_cast(edb::v1::debugger_ui)) { { const auto expandLeftSideUp = new QAction(tr("Expand Left-Hand Side Dock Up"), menu_); expandLeftSideUp->setCheckable(true); expandLeftSideUp->setChecked(mainWindow->corner(Qt::TopLeftCorner) == Qt::LeftDockWidgetArea); connect(expandLeftSideUp, &QAction::toggled, this, &Plugin::expandLSUp); menu_->addAction(expandLeftSideUp); } { const auto expandLeftSideDown = new QAction(tr("Expand Left-Hand Side Dock Down"), menu_); expandLeftSideDown->setCheckable(true); expandLeftSideDown->setChecked(mainWindow->corner(Qt::BottomLeftCorner) == Qt::LeftDockWidgetArea); connect(expandLeftSideDown, &QAction::toggled, this, &Plugin::expandLSDown); menu_->addAction(expandLeftSideDown); } { const auto expandRightSideUp = new QAction(tr("Expand Right-Hand Side Dock Up"), menu_); expandRightSideUp->setCheckable(true); expandRightSideUp->setChecked(mainWindow->corner(Qt::TopRightCorner) == Qt::RightDockWidgetArea); connect(expandRightSideUp, &QAction::toggled, this, &Plugin::expandRSUp); menu_->addAction(expandRightSideUp); } { const auto expandRightSideDown = new QAction(tr("Expand Right-Hand Side Dock Down"), menu_); expandRightSideDown->setCheckable(true); expandRightSideDown->setChecked(mainWindow->corner(Qt::BottomRightCorner) == Qt::RightDockWidgetArea); connect(expandRightSideDown, &QAction::toggled, this, &Plugin::expandRSDown); menu_->addAction(expandRightSideDown); } menu_->addSeparator(); } setupDocks(); } return menu_; } } edb-debugger/plugins/ODbgRegisterView/RegisterView.cpp0000644000175000017500000004163213765535463022716 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "RegisterView.h" #include "BitFieldFormatter.h" #include "Canvas.h" #include "Configuration.h" #include "DialogEditGPR.h" #include "DialogEditSimdRegister.h" #include "ODbgRV_Common.h" #include "ODbgRV_Util.h" #include "RegisterGroup.h" #include "RegisterViewModelBase.h" #include "SimdValueManager.h" #include "State.h" #include "ValueField.h" #include "VolatileNameField.h" #include "edb.h" #include "util/Container.h" #if defined(EDB_X86) || defined(EDB_X86_64) #include "DialogEditFPU.h" #include "ODbgRV_x86Common.h" #include "x86Groups.h" #elif defined(EDB_ARM32) #include "armGroups.h" #endif #include #include #include #include #include #include #include #include #include #include #include namespace ODbgRegisterView { namespace { // TODO: rFLAGS menu: Set Condition (O,P,NAE etc. - see ODB) // TODO: FSR: Set Condition: G,L,E,Unordered // TODO: Add option to show FPU in STi mode, both ST-ordered and R-ordered (physically) // TODO: Update register comments after editing values // TODO: Add a way to add back register group to RegView // TODO: all TODOs scattered around sources // TODO: "Undo" action, which returns to the state after last stopping of debuggee (visible only if register has been modified by the user) constexpr auto RegisterGroupTypeNames = util::make_array( #if defined(EDB_X86) || defined(EDB_X86_64) "GPR", "rIP", "ExpandedEFL", "Segment", "EFL", "FPUData", "FPUWords", "FPULastOp", "Debug", "MMX", "SSEData", "AVXData", "MXCSR" #elif defined(EDB_ARM32) "GPR", "CPSR", "ExpandedCPSR", "FPSCR" #else #error "Not implemented" #endif ); static_assert(RegisterGroupTypeNames.size() == ODBRegView::RegisterGroupType::NUM_GROUPS, "Mismatch between number of register group types and names"); const auto SETTINGS_GROUPS_ARRAY_NODE = QLatin1String("visibleGroups"); ODBRegView::RegisterGroupType findGroup(const QString &str) { const auto &names = RegisterGroupTypeNames; const auto foundIt = std::find(names.begin(), names.end(), str); if (foundIt == names.end()) return ODBRegView::RegisterGroupType::NUM_GROUPS; return ODBRegView::RegisterGroupType(foundIt - names.begin()); } RegisterGroup *createSIMDGroup(RegisterViewModelBase::Model *model, QWidget *parent, const QString &catName, const QString ®NamePrefix) { const auto catIndex = find_model_category(model, catName); if (!catIndex.isValid()) return nullptr; const auto group = new RegisterGroup(catName, parent); for (int row = 0; row < model->rowCount(catIndex); ++row) { const auto nameIndex = valid_index(model->index(row, ModelNameColumn, catIndex)); const auto name = regNamePrefix + QString("%1").arg(row); if (!valid_variant(nameIndex.data()).toString().toUpper().startsWith(regNamePrefix)) { if (row == 0) return nullptr; // don't want empty groups break; } group->insert(row, 0, new FieldWidget(name, group)); new SimdValueManager(row, nameIndex, group); } // This signal must be handled by group _after_ all `SimdValueManager`s handle their connection to this signal QObject::connect( model, &RegisterViewModelBase::Model::SIMDDisplayFormatChanged, group, [group]() { group->adjustWidth(); }, Qt::QueuedConnection); return group; } } // -------------------------------- ODBRegView impl ---------------------------------------- void ODBRegView::mousePressEvent(QMouseEvent *event) { if (event->type() != QEvent::MouseButtonPress) return; if (event->button() == Qt::RightButton) { showMenu(event->globalPos()); return; } if (event->button() == Qt::LeftButton) { Q_FOREACH (const auto field, valueFields()) { field->unselect(); } } } void ODBRegView::updateFont() { QFont font; if (!font.fromString(edb::v1::config().registers_font)) { font = QFont("Monospace"); font.setStyleHint(QFont::TypeWriter); } setFont(font); } void ODBRegView::fieldSelected() { Q_FOREACH (const auto field, valueFields()) if (sender() != field) field->unselect(); ensureWidgetVisible(static_cast(sender()), 0, 0); } void ODBRegView::showMenu(const QPoint &position, const QList &additionalItems) const { QMenu menu; auto items = additionalItems + menuItems_; if (model_->activeIndex().isValid()) { QList debuggerActions; QMetaObject::invokeMethod(edb::v1::debugger_ui, "currentRegisterContextMenuItems", Qt::DirectConnection, Q_RETURN_ARG(QList, debuggerActions)); items.push_back(nullptr); items.append(debuggerActions); } for (const auto action : items) if (action) menu.addAction(action); else menu.addSeparator(); menu.exec(position); } void ODBRegView::settingsUpdated() { // this slot is now triggered whenever the settings dialog is closed, // so it's a good spot to update the fonts and anything else which // may be affected by user config updateFont(); modelReset(); } ODBRegView::ODBRegView(const QString &settingsGroup, QWidget *parent) : QScrollArea(parent), dialogEditGpr_(new DialogEditGPR(this)), dialogEditSIMDReg_(new DialogEditSimdRegister(this)), #if defined(EDB_X86) || defined(EDB_X86_64) dialogEditFpu_(new DialogEditFPU(this)) #else dialogEditFpu_(nullptr) #endif { setObjectName("ODBRegView"); connect(&edb::v1::config(), &Configuration::settingsUpdated, this, &ODBRegView::settingsUpdated); updateFont(); const auto canvas = new Canvas(this); setWidget(canvas); setWidgetResizable(true); { const auto sep = new QAction(this); sep->setSeparator(true); menuItems_.push_back(sep); menuItems_.push_back(new_action(tr("Copy all registers"), this, [this](bool) { copyAllRegisters(); })); } QSettings settings; settings.beginGroup(settingsGroup); const auto groupListV = settings.value(SETTINGS_GROUPS_ARRAY_NODE); if (settings.group().isEmpty() || !groupListV.isValid()) { visibleGroupTypes_ = { #if defined(EDB_X86) || defined(EDB_X86_64) RegisterGroupType::GPR, RegisterGroupType::rIP, RegisterGroupType::ExpandedEFL, RegisterGroupType::Segment, RegisterGroupType::EFL, RegisterGroupType::FPUData, RegisterGroupType::FPUWords, RegisterGroupType::FPULastOp, RegisterGroupType::Debug, RegisterGroupType::MMX, RegisterGroupType::SSEData, RegisterGroupType::AVXData, RegisterGroupType::MXCSR, #elif defined(EDB_ARM32) RegisterGroupType::GPR, RegisterGroupType::CPSR, RegisterGroupType::ExpandedCPSR, RegisterGroupType::FPSCR, #else #error "Not implemented" #endif }; } else { Q_FOREACH (const auto &grp, groupListV.toStringList()) { const auto group = findGroup(grp); if (group >= RegisterGroupType::NUM_GROUPS) { qWarning() << qPrintable(QString("Warning: failed to understand group %1").arg(group)); continue; } visibleGroupTypes_.emplace_back(group); } } connect(new QShortcut(QKeySequence::Copy, this, nullptr, nullptr, Qt::WidgetShortcut), &QShortcut::activated, this, &ODBRegView::copyRegisterToClipboard); } void ODBRegView::copyRegisterToClipboard() const { const auto selected = selectedField(); if (selected) selected->copyToClipboard(); } DialogEditGPR *ODBRegView::gprEditDialog() const { return dialogEditGpr_; } DialogEditSimdRegister *ODBRegView::simdEditDialog() const { return dialogEditSIMDReg_; } DialogEditFPU *ODBRegView::fpuEditDialog() const { return dialogEditFpu_; } void ODBRegView::copyAllRegisters() { auto allFields = fields(); std::sort(allFields.begin(), allFields.end(), [](const FieldWidget *f1, const FieldWidget *f2) { const auto f1Pos = field_position(f1); const auto f2Pos = field_position(f2); if (f1Pos.y() < f2Pos.y()) return true; if (f1Pos.y() > f2Pos.y()) return false; return f1Pos.x() < f2Pos.x(); }); QString text; int textLine = 0; int textColumn = 0; for (const auto field : allFields) { while (field->lineNumber() > textLine) { ++textLine; textColumn = 0; text = text.trimmed() + '\n'; } while (field->columnNumber() > textColumn) { ++textColumn; text += ' '; } const QString fieldText = field->text(); if (field->alignment() == Qt::AlignRight) { const int fwidth = field->fieldWidth(); const int spaceWidth = fwidth - fieldText.length(); text += QString(spaceWidth, ' '); textColumn += spaceWidth; } text += fieldText; textColumn += fieldText.length(); } QApplication::clipboard()->setText(text.trimmed()); } void ODBRegView::groupHidden(RegisterGroup *group) { using namespace std; assert(util::contains(groups_, group)); const auto groupPtrIter = std::find(groups_.begin(), groups_.end(), group); auto &groupPtr = *groupPtrIter; groupPtr->deleteLater(); groupPtr = nullptr; auto &types(visibleGroupTypes_); const int groupType = groupPtrIter - groups_.begin(); types.erase(remove_if(types.begin(), types.end(), [=](int type) { return type == groupType; }), types.end()); } void ODBRegView::saveState(const QString &settingsGroup) const { QSettings settings; settings.beginGroup(settingsGroup); settings.remove(SETTINGS_GROUPS_ARRAY_NODE); QStringList groupTypes; for (auto type : visibleGroupTypes_) groupTypes << RegisterGroupTypeNames[type]; settings.setValue(SETTINGS_GROUPS_ARRAY_NODE, groupTypes); } void ODBRegView::setModel(RegisterViewModelBase::Model *model) { model_ = model; connect(model, &RegisterViewModelBase::Model::modelReset, this, &ODBRegView::modelReset); connect(model, &RegisterViewModelBase::Model::dataChanged, this, &ODBRegView::modelUpdated); modelReset(); } RegisterGroup *ODBRegView::makeGroup(RegisterGroupType type) { if (!model_->rowCount()) return nullptr; std::vector nameValCommentIndices; using RegisterViewModelBase::Model; QString groupName; switch (type) { case RegisterGroupType::GPR: { groupName = tr("GPRs"); const auto catIndex = find_model_category(model_, GprCategoryName); if (!catIndex.isValid()) break; for (int row = 0; row < model_->rowCount(catIndex); ++row) nameValCommentIndices.emplace_back(model_->index(row, ModelNameColumn, catIndex)); break; } #if defined(EDB_X86) || defined(EDB_X86_64) case RegisterGroupType::EFL: return create_eflags(model_, widget()); case RegisterGroupType::ExpandedEFL: return create_expanded_eflags(model_, widget()); case RegisterGroupType::FPUData: return create_fpu_data(model_, widget()); case RegisterGroupType::FPUWords: return create_fpu_words(model_, widget()); case RegisterGroupType::FPULastOp: return create_fpu_last_op(model_, widget()); case RegisterGroupType::Debug: return create_debug_group(model_, widget()); case RegisterGroupType::MXCSR: return create_mxcsr(model_, widget()); case RegisterGroupType::MMX: return createSIMDGroup(model_, widget(), "MMX", "MM"); case RegisterGroupType::SSEData: return createSIMDGroup(model_, widget(), "SSE", "XMM"); case RegisterGroupType::AVXData: return createSIMDGroup(model_, widget(), "AVX", "YMM"); case RegisterGroupType::Segment: { groupName = tr("Segment Registers"); const auto catIndex = find_model_category(model_, "Segment"); if (!catIndex.isValid()) break; for (int row = 0; row < model_->rowCount(catIndex); ++row) nameValCommentIndices.emplace_back(model_->index(row, ModelNameColumn, catIndex)); break; } case RegisterGroupType::rIP: { groupName = tr("Instruction Pointer"); const auto catIndex = find_model_category(model_, "General Status"); if (!catIndex.isValid()) break; nameValCommentIndices.emplace_back(find_model_register(catIndex, "RIP")); nameValCommentIndices.emplace_back(find_model_register(catIndex, "EIP")); break; } #elif defined(EDB_ARM32) case RegisterGroupType::CPSR: return createCPSR(model_, widget()); case RegisterGroupType::ExpandedCPSR: return createExpandedCPSR(model_, widget()); case RegisterGroupType::FPSCR: return createFPSCR(model_, widget()); #endif default: qWarning() << "Warning: unexpected register group type requested in" << Q_FUNC_INFO; return nullptr; } nameValCommentIndices.erase(std::remove_if(nameValCommentIndices.begin(), nameValCommentIndices.end(), [](const QModelIndex &index) { return !index.isValid(); }), nameValCommentIndices.end()); if (nameValCommentIndices.empty()) { qWarning() << "Warning: failed to get any useful register indices for regGroupType" << static_cast(type); return nullptr; } const auto group = new RegisterGroup(groupName, widget()); for (const auto &index : nameValCommentIndices) { group->appendNameValueComment(index); } return group; } void ODBRegView::modelReset() { widget()->hide(); // prevent flicker while groups are added to/removed from the layout // not all groups may be in the layout, so delete them individually Q_FOREACH (const auto group, groups_) { if (group) { group->deleteLater(); } } groups_.clear(); const auto layout = static_cast(widget()->layout()); flagsAndSegments_ = std::make_unique(); // (3/2+1/2)-letter - Total of 2-letter spacing. Fourth half-letter is from flag values extension. // Segment extensions at LHS of the widget don't influence minimumSize request, so no need to take // them into account. flagsAndSegments_->setSpacing(letter_size(this->font()).width() * 3 / 2); flagsAndSegments_->setContentsMargins(QMargins()); flagsAndSegments_->setAlignment(Qt::AlignLeft); bool flagsAndSegsInserted = false; for (int group = 0; group < RegisterGroupType::NUM_GROUPS; ++group) { const auto groupType = static_cast(group); if (util::contains(visibleGroupTypes_, groupType)) { const auto group = makeGroup(groupType); groups_.push_back(group); if (!group) continue; #if defined(EDB_X86) || defined(EDB_X86_64) if (groupType == RegisterGroupType::Segment || groupType == RegisterGroupType::ExpandedEFL) { flagsAndSegments_->addWidget(group); if (!flagsAndSegsInserted) { layout->addLayout(flagsAndSegments_.get()); flagsAndSegsInserted = true; } } else #endif layout->addWidget(group); } else groups_.push_back(nullptr); } widget()->show(); } void ODBRegView::modelUpdated() { Q_FOREACH (FieldWidget *field, fields()) { field->adjustToData(); } Q_FOREACH (RegisterGroup *group, groups_) { if (group) { group->adjustWidth(); } } } QList ODBRegView::fields() const { QList allFields; for (RegisterGroup *group : groups_) { if (group) { allFields.append(group->fields()); } } return allFields; } QList ODBRegView::valueFields() const { QList allValues; for (RegisterGroup *group : groups_) { if (group) { allValues.append(group->valueFields()); } } return allValues; } void ODBRegView::updateFieldsPalette() { Q_FOREACH (ValueField *field, valueFields()) field->updatePalette(); } ValueField *ODBRegView::selectedField() const { Q_FOREACH (ValueField *field, valueFields()) { if (field->isSelected()) { return field; } } return nullptr; } void ODBRegView::selectAField() { const QList fields = valueFields(); if (!fields.isEmpty()) { fields.front()->select(); } } void ODBRegView::keyPressEvent(QKeyEvent *event) { ValueField *selected = selectedField(); switch (event->key()) { case Qt::Key_Up: if (selected && selected->up()) { selected->up()->select(); return; } if (!selected) selectAField(); break; case Qt::Key_Down: if (selected && selected->down()) { selected->down()->select(); return; } if (!selected) selectAField(); break; case Qt::Key_Left: if (selected && selected->left()) { selected->left()->select(); return; } if (!selected) selectAField(); break; case Qt::Key_Right: if (selected && selected->right()) { selected->right()->select(); return; } if (!selected) selectAField(); break; case Qt::Key_Enter: case Qt::Key_Return: if (selected) { selected->defaultAction(); return; } break; case Qt::Key_Menu: if (selected) selected->showMenu(selected->mapToGlobal(selected->rect().bottomLeft())); else showMenu(mapToGlobal(QPoint())); break; case SetToZeroKey: if (selected) { selected->setZero(); return; } break; case IncrementKey: if (selected) { selected->increment(); return; } break; case DecrementKey: if (selected) { selected->decrement(); return; } break; } QScrollArea::keyPressEvent(event); } } edb-debugger/plugins/ODbgRegisterView/DialogEditGPR.cpp0000644000175000017500000002227013765535463022652 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "DialogEditGPR.h" #include "EntryGridKeyUpDownEventFilter.h" #include "GprEdit.h" #include "util/Container.h" #include #include #include #include #include #include #include #include namespace ODbgRegisterView { DialogEditGPR::DialogEditGPR(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f) { setWindowTitle(tr("Modify Register")); setModal(true); const auto allContentsGrid = new QGridLayout(); // Register name labels for (std::size_t c = 0; c < ENTRY_COLS; ++c) { auto &label = columnLabel(static_cast(FIRST_ENTRY_COL + c)); label = new QLabel(this); label->setAlignment(Qt::AlignCenter); allContentsGrid->addWidget(label, GPR_LABELS_ROW, FIRST_ENTRY_COL + c); } { static const auto formatNames = util::make_array(tr("Hexadecimal"), tr("Signed"), tr("Unsigned"), tr("Character")); // Format labels for (std::size_t f = 0; f < formatNames.size(); ++f) { auto &label = rowLabel(static_cast(FIRST_ENTRY_ROW + f)); label = new QLabel(formatNames[f], this); allContentsGrid->addWidget(label, FIRST_ENTRY_ROW + f, FORMAT_LABELS_COL); } } // All entries but char { static const auto offsetsInInteger = util::make_array(0u, 0u, 0u, 1u, 0u); static const auto integerSizes = util::make_array(8u, 4u, 2u, 1u, 1u); static_assert(std::tuple_size::value == DialogEditGPR::ENTRY_COLS, "integerSizes length doesn't equal ENTRY_COLS"); static_assert(std::tuple_size::value == DialogEditGPR::ENTRY_COLS, "offsetsInInteger length doesn't equal ENTRY_COLS"); static const auto formats = util::make_array(GprEdit::Format::Hex, GprEdit::Format::Signed, GprEdit::Format::Unsigned); for (std::size_t f = 0; f < formats.size(); ++f) { for (std::size_t c = 0; c < ENTRY_COLS; ++c) { auto &entry = this->entry(static_cast(FIRST_ENTRY_ROW + f), static_cast(FIRST_ENTRY_COL + c)); entry = new GprEdit(offsetsInInteger[c], integerSizes[c], formats[f], this); connect(entry, &GprEdit::textEdited, this, &DialogEditGPR::onTextEdited); entry->installEventFilter(this); allContentsGrid->addWidget(entry, FIRST_ENTRY_ROW + f, FIRST_ENTRY_COL + c); } } } // High byte char { auto &charHigh = entry(CHAR_ROW, GPR8H_COL); charHigh = new GprEdit(1, 1, GprEdit::Format::Character, this); connect(charHigh, &GprEdit::textEdited, this, &DialogEditGPR::onTextEdited); charHigh->installEventFilter(this); allContentsGrid->addWidget(charHigh, CHAR_ROW, GPR8H_COL); } // Low byte char { auto &charLow = entry(CHAR_ROW, GPR8L_COL); charLow = new GprEdit(0, 1, GprEdit::Format::Character, this); connect(charLow, &GprEdit::textEdited, this, &DialogEditGPR::onTextEdited); charLow->installEventFilter(this); allContentsGrid->addWidget(charLow, CHAR_ROW, GPR8L_COL); } resetLayout(); const auto okCancel = new QDialogButtonBox(this); okCancel->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); connect(okCancel, &QDialogButtonBox::accepted, this, &DialogEditGPR::accept); connect(okCancel, &QDialogButtonBox::rejected, this, &DialogEditGPR::reject); const auto dialogLayout = new QVBoxLayout(this); dialogLayout->addLayout(allContentsGrid); dialogLayout->addWidget(okCancel); for (std::size_t entry = 1; entry < entries_.size(); ++entry) { setTabOrder(entries_[entry - 1], entries_[entry]); } } GprEdit *&DialogEditGPR::entry(DialogEditGPR::Row row, DialogEditGPR::Column col) { if (row < ENTRY_ROWS) return entries_.at((row - FIRST_ENTRY_ROW) * ENTRY_COLS + (col - FIRST_ENTRY_COL)); if (col == GPR8H_COL) return *(entries_.end() - 2); if (col == GPR8L_COL) return entries_.back(); Q_ASSERT("Invalid row&col specified" && 0); return entries_.front(); // silence the compiler } void DialogEditGPR::updateAllEntriesExcept(GprEdit *notUpdated) { for (auto entry : entries_) { if (entry != notUpdated && !entry->isHidden()) { entry->setGPRValue(value_); } } } QLabel *&DialogEditGPR::columnLabel(DialogEditGPR::Column col) { return labels_.at(col - FIRST_ENTRY_COL); } QLabel *&DialogEditGPR::rowLabel(DialogEditGPR::Row row) { return labels_.at(ENTRY_COLS + row - FIRST_ENTRY_ROW); } void DialogEditGPR::hideColumn(DialogEditGPR::Column col) { Row fMax = col == GPR8L_COL || col == GPR8H_COL ? ENTRY_ROWS : FULL_LENGTH_ROWS; for (std::size_t f = 0; f < fMax; ++f) { entry(static_cast(FIRST_ENTRY_ROW + f), col)->hide(); } columnLabel(col)->hide(); } void DialogEditGPR::hideRow(Row row) { rowLabel(row)->hide(); if (row == CHAR_ROW) { entry(row, GPR8L_COL)->hide(); entry(row, GPR8H_COL)->hide(); } else { for (std::size_t c = 0; c < FULL_LENGTH_ROWS; ++c) { entry(row, static_cast(FIRST_ENTRY_COL + c))->hide(); } } } void DialogEditGPR::resetLayout() { for (auto entry : entries_) { entry->show(); } for (auto label : labels_) { label->show(); } static const auto colLabelStrings = util::make_array("R?X", "E?X", "?X", "?H", "?L"); static_assert(std::tuple_size::value == ENTRY_COLS, "Number of labels not equal to number of entry columns"); for (std::size_t c = 0; c < ENTRY_COLS; ++c) { columnLabel(static_cast(GPR64_COL + c))->setText(colLabelStrings[c]); } } void DialogEditGPR::setupEntriesAndLabels() { resetLayout(); switch (bitSize_) { case 8: hideColumn(GPR8H_COL); hideColumn(GPR16_COL); /* fallthrough */ case 16: hideColumn(GPR32_COL); /* fallthrough */ case 32: hideColumn(GPR64_COL); /* fallthrough */ case 64: break; default: Q_ASSERT("Unsupported bitSize" && 0); } const QString regName = reg_.name().toUpper(); if (bitSize_ == 64) columnLabel(GPR64_COL)->setText(regName); else if (bitSize_ == 32) columnLabel(GPR32_COL)->setText(regName); else if (bitSize_ == 16) columnLabel(GPR16_COL)->setText(regName); else columnLabel(GPR8L_COL)->setText(regName); static const auto x86GPRsWithHighBytesAddressable = util::make_array("EAX", "ECX", "EDX", "EBX", "RAX", "RCX", "RDX", "RBX"); static const auto x86GPRsWithHighBytesNotAddressable = util::make_array("ESP", "EBP", "ESI", "EDI", "RSP", "RBP", "RSI", "RDI"); static const auto upperGPRs64 = util::make_array("R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15"); bool x86GPR = false; bool upperGPR64 = false; using util::contains; if (contains(x86GPRsWithHighBytesNotAddressable, regName)) { x86GPR = true; hideColumn(GPR8H_COL); if (bitSize_ == 32) { hideColumn(GPR8L_COL); // In 32 bit mode low bytes also can't be addressed hideRow(CHAR_ROW); } } else if (contains(x86GPRsWithHighBytesAddressable, regName)) { x86GPR = true; } else if (contains(upperGPRs64, regName)) { upperGPR64 = true; } if (x86GPR) { if (bitSize_ == 64) { columnLabel(GPR32_COL)->setText("E" + regName.mid(1)); } columnLabel(GPR16_COL)->setText(regName.mid(1)); columnLabel(GPR8H_COL)->setText(regName.mid(1, 1) + "H"); if (bitSize_ == 64 && !contains(x86GPRsWithHighBytesAddressable, regName)) { columnLabel(GPR8L_COL)->setText(regName.mid(1) + "L"); } else { columnLabel(GPR8L_COL)->setText(regName.mid(1, 1) + "L"); } } else if (upperGPR64) { columnLabel(GPR32_COL)->setText(regName + "D"); columnLabel(GPR16_COL)->setText(regName + "W"); columnLabel(GPR8L_COL)->setText(regName + "B"); hideColumn(GPR8H_COL); } else { // These have hex only format hideColumn(GPR8H_COL); if (bitSize_ != 8) { hideColumn(GPR8L_COL); } if (bitSize_ != 16) { hideColumn(GPR16_COL); } if (bitSize_ != 32) { hideColumn(GPR32_COL); } hideRow(SIGNED_ROW); hideRow(UNSIGNED_ROW); hideRow(CHAR_ROW); } } void DialogEditGPR::setupFocus() { for (auto entry : entries_) { if (!entry->isHidden()) { entry->setFocus(Qt::OtherFocusReason); break; } } } bool DialogEditGPR::eventFilter(QObject *obj, QEvent *event) { return entry_grid_key_event_filter(this, obj, event); } void DialogEditGPR::setValue(const Register &newReg) { reg_ = newReg; value_ = reg_.valueAsInteger(); bitSize_ = reg_.bitSize(); setupEntriesAndLabels(); setWindowTitle(tr("Modify %1").arg(reg_.name().toUpper())); updateAllEntriesExcept(nullptr); setupFocus(); } Register DialogEditGPR::value() const { Register ret(reg_); ret.setScalarValue(value_); return ret; } void DialogEditGPR::onTextEdited(const QString &) { auto edit = dynamic_cast(sender()); edit->updateGPRValue(value_); updateAllEntriesExcept(edit); } } edb-debugger/plugins/ODbgRegisterView/RegisterGroup.h0000644000175000017500000000231313765535463022536 0ustar eteraneteran #ifndef REGISTER_GROUP_H_20191119_ #define REGISTER_GROUP_H_20191119_ #include namespace ODbgRegisterView { class SimdValueManager; class FieldWidget; class ValueField; class ODBRegView; class RegisterGroup : public QWidget { Q_OBJECT friend SimdValueManager; public: explicit RegisterGroup(const QString &name_, QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); QList fields() const; QList valueFields() const; void setIndices(const QList &indices); void insert(int line, int column, FieldWidget *widget); // Insert, but without moving to its place void insert(FieldWidget *widget); void setupPositionAndSize(int line, int column, FieldWidget *widget); void appendNameValueComment(const QModelIndex &nameIndex, const QString &tooltip = "", bool insertComment = true); void showMenu(const QPoint &position, const QList &additionalItems = {}) const; QMargins getFieldMargins() const; public Q_SLOTS: void adjustWidth(); protected: void mousePressEvent(QMouseEvent *event) override; private: int lineAfterLastField() const; ODBRegView *regView() const; private: QList menuItems_; QString name_; }; } #endif edb-debugger/plugins/ODbgRegisterView/EntryGridKeyUpDownEventFilter.cpp0000644000175000017500000000371613765535463026165 0ustar eteraneteran#include "EntryGridKeyUpDownEventFilter.h" #include #include #include #include #include #include namespace ODbgRegisterView { bool entry_grid_key_event_filter(QWidget *parent, QObject *obj, QEvent *event) { auto entry = qobject_cast(obj); if (!entry || event->type() != QEvent::KeyPress) { return false; } auto keyEvent = static_cast(event); const auto key = keyEvent->key(); if (key != Qt::Key_Up && key != Qt::Key_Down) return false; // subtraction of 1 from x prevents selection of entry to the right-top/bottom instead directly top/bottom const auto pos = entry->pos() - QPoint(1, 0); const auto children = parent->findChildren(); // Find the neighbors above/below the current entry std::vector neighbors; for (auto *const child : children) { if (!child->isVisible()) continue; if (key == Qt::Key_Up && child->y() >= pos.y()) continue; if (key == Qt::Key_Down && child->y() <= pos.y()) continue; neighbors.emplace_back(child); } if (neighbors.empty()) { return false; } // Bring the vertically closest neighbors to the front const auto y = pos.y(); std::sort(neighbors.begin(), neighbors.end(), [y](QLineEdit *a, QLineEdit *b) { return std::abs(y - a->y()) < std::abs(y - b->y()); }); // Remove those too far vertically, so that they don't interfere with later calculations const auto verticallyClosestY = neighbors.front()->y(); neighbors.erase(std::remove_if(neighbors.begin(), neighbors.end(), [verticallyClosestY](QLineEdit *e) { return e->y() != verticallyClosestY; }), neighbors.end()); assert(!neighbors.empty()); const auto x = pos.x(); const auto bestNeighbor = *std::min_element(neighbors.begin(), neighbors.end(), [x](QLineEdit *a, QLineEdit *b) { return std::abs(x - a->x()) < std::abs(x - b->x()); }); bestNeighbor->setFocus(Qt::TabFocusReason); return true; } } edb-debugger/plugins/ODbgRegisterView/BitFieldFormatter.cpp0000644000175000017500000000246013765535463023641 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "BitFieldFormatter.h" #include "BitFieldDescription.h" #include "RegisterView.h" namespace ODbgRegisterView { BitFieldFormatter::BitFieldFormatter(const BitFieldDescription &bfd) : valueNames(bfd.valueNames) { } QString BitFieldFormatter::operator()(const QString &str) const { assert(str.length()); if (str.isEmpty()) { return str; // for release builds have defined behavior } if (str[0] == '?') { return "????"; } bool parseOK = false; const int value = str.toInt(&parseOK); if (!parseOK) { return "????"; } assert(0 <= value); assert(std::size_t(value) < valueNames.size()); return valueNames[value]; } } edb-debugger/plugins/ODbgRegisterView/RegisterView.h0000644000175000017500000000535613765535463022366 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ODBG_REGISTER_VIEW_H_20151230_ #define ODBG_REGISTER_VIEW_H_20151230_ #include "RegisterViewModelBase.h" #include #include #include #include #include namespace ODbgRegisterView { class DialogEditSimdRegister; class DialogEditGPR; class DialogEditFPU; class RegisterGroup; class ValueField; class FieldWidget; class ODBRegView : public QScrollArea { Q_OBJECT public: enum RegisterGroupType : int { #if defined(EDB_X86) || defined(EDB_X86_64) GPR, rIP, ExpandedEFL, Segment, EFL, FPUData, FPUWords, FPULastOp, Debug, MMX, SSEData, AVXData, MXCSR, #elif defined(EDB_ARM32) GPR, CPSR, ExpandedCPSR, FPSCR, #else #error "Not implemented" #endif NUM_GROUPS }; public: explicit ODBRegView(const QString &settings, QWidget *parent = nullptr); void setModel(RegisterViewModelBase::Model *model); QList valueFields() const; QList fields() const; void showMenu(const QPoint &position, const QList &additionalItems = {}) const; void saveState(const QString &settings) const; void groupHidden(RegisterGroup *group); DialogEditGPR *gprEditDialog() const; DialogEditSimdRegister *simdEditDialog() const; DialogEditFPU *fpuEditDialog() const; void selectAField(); private: RegisterGroup *makeGroup(RegisterGroupType type); private: ValueField *selectedField() const; void updateFieldsPalette(); void keyPressEvent(QKeyEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void updateFont(); public Q_SLOTS: void fieldSelected(); private Q_SLOTS: void modelReset(); void modelUpdated(); void copyAllRegisters(); void copyRegisterToClipboard() const; void settingsUpdated(); private: RegisterViewModelBase::Model *model_ = nullptr; QList groups_; std::unique_ptr flagsAndSegments_; std::vector visibleGroupTypes_; QList menuItems_; DialogEditGPR *dialogEditGpr_; DialogEditSimdRegister *dialogEditSIMDReg_; DialogEditFPU *dialogEditFpu_; }; } #endif edb-debugger/plugins/ODbgRegisterView/Plugin.h0000644000175000017500000000327713765535463021205 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ODBG_REGISTER_VIEW_PLUGIN_H_20151230_ #define ODBG_REGISTER_VIEW_PLUGIN_H_20151230_ #include "IPlugin.h" #include namespace ODbgRegisterView { class ODBRegView; class Plugin : public QObject, public IPlugin { Q_OBJECT Q_INTERFACES(IPlugin) Q_PLUGIN_METADATA(IID "edb.IPlugin/1.0") Q_CLASSINFO("author", "Ruslan Kabatsayev") Q_CLASSINFO("email", "b7.10110111@gmail.com") public: explicit Plugin(QObject *parent = nullptr); QMenu *menu(QWidget *parent = nullptr) override; private: void setupDocks(); void createRegisterView(const QString &settingsGroup); void renumerateDocks() const; void removeDock(QWidget *); void saveSettings() const; void expandRSUp(bool checked) const; void expandRSDown(bool checked) const; void expandLSUp(bool checked) const; void expandLSDown(bool checked) const; QString dockName() const; private Q_SLOTS: void createRegisterView(); private: QMenu *menu_ = nullptr; std::vector registerViews_; std::vector menuDeleteRegViewActions_; }; } #endif edb-debugger/plugins/ODbgRegisterView/ValueField.h0000644000175000017500000000530613765535463021762 0ustar eteraneteran/* Copyright (C) 2015 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef VALUE_FIELD_H_20170818_ #define VALUE_FIELD_H_20170818_ #include "FieldWidget.h" #include "RegisterViewModelBase.h" #include class QAction; class QMouseEvent; namespace ODbgRegisterView { class ValueField : public FieldWidget { Q_OBJECT private: bool selected_ = false; bool hovered_ = false; std::function valueFormatter_; // For GPR QAction *setToZeroAction_ = nullptr; QAction *setToOneAction_ = nullptr; protected: QList menuItems_; private: void init(); QColor fgColorForChangedField() const; void editNormalReg(const QModelIndex &indexToEdit, const QModelIndex &clickedIndex) const; protected: RegisterViewModelBase::Model *model() const; bool changed() const; void enterEvent(QEvent *) override; void leaveEvent(QEvent *) override; void mousePressEvent(QMouseEvent *event) override; void mouseDoubleClickEvent(QMouseEvent *event) override; void paintEvent(QPaintEvent *event) override; ValueField *bestNeighbor(const std::function &firstIsBetter) const; public: ValueField(int fieldWidth, const QModelIndex &index_, const std::function &valueFormatter_, QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ValueField(int fieldWidth, const QModelIndex &index_, QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ValueField *up() const; ValueField *down() const; ValueField *left() const; ValueField *right() const; bool isSelected() const; void showMenu(const QPoint &position); QString text() const override; QModelIndex regIndex() const; public Q_SLOTS: void defaultAction(); #if defined(EDB_X86) || defined(EDB_X86_64) void pushFPUStack(); void popFPUStack(); #endif void adjustToData() override; void select(); void unselect(); virtual void updatePalette(); void copyToClipboard() const; void setZero(); void setToOne(); void increment(); void decrement(); void invert(); Q_SIGNALS: void selected(); }; } #endif edb-debugger/plugins/FasLoader/0000755000175000017500000000000013765535463016252 5ustar eteraneteranedb-debugger/plugins/FasLoader/CMakeLists.txt0000644000175000017500000000162013765535463021011 0ustar eteraneterancmake_minimum_required (VERSION 3.1) include("GNUInstallDirs") set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(PluginName "FasLoader") find_package(Qt5 5.0.0 REQUIRED Widgets) add_library(${PluginName} SHARED FasLoader.cpp FasLoader.hpp Fas/Core.cpp Fas/Core.hpp Fas/Exception.cpp Fas/Exception.hpp Fas/Header.hpp Fas/PluginSymbol.hpp Fas/Symbol.hpp ) target_link_libraries(${PluginName} Qt5::Widgets edb) install (TARGETS ${PluginName} DESTINATION ${CMAKE_INSTALL_LIBDIR}/edb) set_property(TARGET ${PluginName} PROPERTY CXX_EXTENSIONS OFF) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD 14) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD_REQUIRED ON) set_property(TARGET ${PluginName} PROPERTY LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) set_property(TARGET ${PluginName} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) edb-debugger/plugins/FasLoader/FasLoader.cpp0000644000175000017500000000411213765535463020614 0ustar eteraneteran/* Copyright (C) 2006 - 2015 * Evan Teran evan.teran@gmail.com * darkprof dark_prof@mail.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "FasLoader.hpp" #include "IDebugger.h" #include "IProcess.h" #include "ISymbolManager.h" #include "Symbol.h" #include "edb.h" #include #include namespace FasLoaderPlugin { /** * @brief FasLoader::FasLoader * @param parent */ FasLoader::FasLoader(QObject *parent) : QObject(parent) { } /** * @brief FasLoader::menu * @param parent * @return */ QMenu *FasLoader::menu(QWidget *parent) { Q_ASSERT(parent); if (!menu_) { menu_ = new QMenu(tr("FasLoader"), parent); menu_->addAction(tr("&Load *.fas symbols"), this, SLOT(load())); } return menu_; } /** * @brief FasLoader::load */ void FasLoader::load() { if (edb::v1::debugger_core) { if (IProcess *process = edb::v1::debugger_core->process()) { const QString fileName = process->executable(); QString fasName = fileName; fasName.append(".fas"); Fas::Core fasCore; fasCore.load(fasName.toUtf8().constData()); auto pluginSymbols = fasCore.getSymbols(); for (auto pluginSymbol : pluginSymbols) { auto symbol = std::make_shared(); symbol->file = fileName; symbol->address = pluginSymbol.value; symbol->name = QString::fromStdString(pluginSymbol.name); symbol->size = pluginSymbol.size; if (pluginSymbol.size > 0) { symbol->type = 'd'; } edb::v1::symbol_manager().addSymbol(symbol); } } } } } edb-debugger/plugins/FasLoader/Fas/0000755000175000017500000000000013765535463016763 5ustar eteraneteranedb-debugger/plugins/FasLoader/Fas/PluginSymbol.hpp0000644000175000017500000000022413765535463022116 0ustar eteraneteran#pragma once #include #include namespace Fas { struct PluginSymbol { uint64_t value; std::string name; uint8_t size; }; } edb-debugger/plugins/FasLoader/Fas/fas.txt0000644000175000017500000005571013765535463020305 0ustar eteraneteran flat assembler Symbolic information file format Table 1 Header /-------------------------------------------------------------------------\ | Offset | Size | Description | |========|=========|======================================================| | +0 | dword | Signature 1A736166h (little-endian). | |--------|---------|------------------------------------------------------| | +4 | byte | Major version of flat assembler. | |--------|---------|------------------------------------------------------| | +5 | byte | Minor version of flat assembler. | |--------|---------|------------------------------------------------------| | +6 | word | Length of header. | |--------|---------|------------------------------------------------------| | +8 | dword | Offset of input file name in the strings table. | |--------|---------|------------------------------------------------------| | +12 | dword | Offset of output file name in the strings table. | |--------|---------|------------------------------------------------------| | +16 | dword | Offset of strings table. | |--------|---------|------------------------------------------------------| | +20 | dword | Length of strings table. | |--------|---------|------------------------------------------------------| | +24 | dword | Offset of symbols table. | |--------|---------|------------------------------------------------------| | +28 | dword | Length of symbols table. | |--------|---------|------------------------------------------------------| | +32 | dword | Offset of preprocessed source. | |--------|---------|------------------------------------------------------| | +36 | dword | Length of preprocessed source. | |--------|---------|------------------------------------------------------| | +40 | dword | Offset of assembly dump. | |--------|---------|------------------------------------------------------| | +44 | dword | Length of assembly dump. | |--------|---------|------------------------------------------------------| | +48 | dword | Offset of section names table. | |--------|---------|------------------------------------------------------| | +52 | dword | Length of section names table. | |--------|---------|------------------------------------------------------| | +56 | dword | Offset of symbol references dump. | |--------|---------|------------------------------------------------------| | +60 | dword | Length of symbol references dump. | \-------------------------------------------------------------------------/ Notes: If header is shorter than 64 bytes, it comes from a version that does not support dumping some of the structures. It should then be interpreted that the data for missing structures could not be provided, not that the size of that data is zero. Offsets given in header generally mean positions in the file, however input and output file names are specified by offsets in the strings table, so you have to add their offset to the offset of strings table to obtain the positions of those strings in the file. The strings table contains just a sequence of ASCIIZ strings, which may be referred to by other parts of the file. It contains the names of main input file, the output file, and the names of the sections and external symbols if there were any. The symbols table is an array of 32-byte structures, each one in format specified by table 2. The preprocessed source is a sequence of preprocessed lines, each one in format as defined in table 3. The assembly dump contains an array of 28-byte structures, each one in format specified by table 4, and at the end of this array an additional double word containing the offset in output file at which the assembly was ended. It is possible that file does not contain assembly dump at all - this happens when some error occured and only the preprocessed source was dumped. If error occured during the preprocessing, only the source up to the point of error is provided. In such case (and only then) the field at offset 44 contains zero. The section names table exists only when the output format was an object file (ELF or COFF), and it is an array of 4-byte entries, each being an offset of the name of the section in the strings table. The index of section in this table is the same, as the index of section in the generated object file. The symbol references dump contains an array of 8-byte structures, each one describes an event of some symbol being used. The first double word of such structure contains an offset of symbol in the symbols table, and the second double word is an offset of structure in assembly dump, which specifies at what moment the symbol was referenced. Table 2 Symbol structure /-------------------------------------------------------------------------\ | Offset | Size | Description | |========|=======|========================================================| | +0 | qword | Value of symbol. | |--------|-------|--------------------------------------------------------| | +8 | word | Flags (table 2.1). | |--------|-------|--------------------------------------------------------| | +10 | byte | Size of data labelled by this symbol (zero means plain | | | | label without size attached). | |--------|-------|--------------------------------------------------------| | +11 | byte | Type of value (table 2.2). Any value other than zero | | | | means some kind of relocatable symbol. | |--------|-------|--------------------------------------------------------| | +12 | dword | Extended SIB, the first two bytes are register codes | | | | and the second two bytes are corresponding scales. | |--------|-------|--------------------------------------------------------| | +16 | word | Number of pass in which symbol was defined last time. | |--------|-------|--------------------------------------------------------| | +18 | word | Number of pass in which symbol was used last time. | |--------|-------|--------------------------------------------------------| | +20 | dword | If the symbol is relocatable, this field contains | | | | information about section or external symbol, to which | | | | it is relative - otherwise this field has no meaning. | | | | When the highest bit is cleared, the symbol is | | | | relative to a section, and the bits 0-30 contain | | | | the index (starting from 1) in the table of sections. | | | | When the highest bit is set, the symbol is relative to | | | | an external symbol, and the bits 0-30 contain the | | | | the offset of the name of this symbol in the strings | | | | table. | |--------|-------|--------------------------------------------------------| | +24 | dword | If the highest bit is cleared, the bits 0-30 contain | | | | the offset of symbol name in the preprocessed source. | | | | This name is a pascal-style string (byte length | | | | followed by string data). | | | | Zero in this field means an anonymous symbol. | | | | If the highest bit is set, the bits 0-30 contain the | | | | offset of the symbol name in the strings table, and | | | | this name is a zero-ended string in this case (as are | | | | all the strings there). | |--------|-------|--------------------------------------------------------| | +28 | dword | Offset in the preprocessed source of line that defined | | | | this symbol (see table 3). | \-------------------------------------------------------------------------/ Table 2.1 Symbol flags /-----------------------------------------------------------------\ | Bit | Value | Description | |=====|=======|===================================================| | 0 | 1 | Symbol was defined. | |-----|-------|---------------------------------------------------| | 1 | 2 | Symbol is an assembly-time variable. | |-----|-------|---------------------------------------------------| | 2 | 4 | Symbol cannot be forward-referenced. | |-----|-------|---------------------------------------------------| | 3 | 8 | Symbol was used. | |-----|-------|---------------------------------------------------| | 4 | 10h | The prediction was needed when checking | | | | whether the symbol was used. | |-----|-------|---------------------------------------------------| | 5 | 20h | Result of last predicted check for being used. | |-----|-------|---------------------------------------------------| | 6 | 40h | The prediction was needed when checking | | | | whether the symbol was defined. | |-----|-------|---------------------------------------------------| | 7 | 80h | Result of last predicted check for being defined. | |-----|-------|---------------------------------------------------| | 8 | 100h | The optimization adjustment is applied to | | | | the value of this symbol. | |-----|-------|---------------------------------------------------| | 9 | 200h | The value of symbol is negative number encoded | | | | as two's complement. | |-----|-------|---------------------------------------------------| | 10 | 400h | Symbol is a special marker and has no value. | \-----------------------------------------------------------------/ Notes: Some of those flags are listed here just for completness, as they have little use outside of the flat assembler. However the bit 0 is important, because the symbols table contains all the labels that occured in source, even if some of them were in the conditional blocks that did not get assembled. Table 2.2 Symbol value types /-------------------------------------------------------------------\ | Value | Description | |=======|===========================================================| | 0 | Absolute value. | |-------|-----------------------------------------------------------| | 1 | Relocatable segment address (only with MZ output). | |-------|-----------------------------------------------------------| | 2 | Relocatable 32-bit address. | |-------|-----------------------------------------------------------| | 3 | Relocatable relative 32-bit address (value valid only for | | | symbol used in the same place where it was calculated, | | | it should not occur in the symbol structure). | |-------|-----------------------------------------------------------| | 4 | Relocatable 64-bit address. | |-------|-----------------------------------------------------------| | 5 | [ELF only] GOT-relative 32-bit address. | |-------|-----------------------------------------------------------| | 6 | [ELF only] 32-bit address of PLT entry. | |-------|-----------------------------------------------------------| | 7 | [ELF only] Relative 32-bit address of PLT entry (value | | | valid only for symbol used in the same place where it | | | was calculated, it should not occur in the symbol | | | structure). | \-------------------------------------------------------------------/ Notes: The types 3 and 7 should never be encountered in the symbols dump, they are only used internally by the flat assembler. If type value is a negative number, it is an opposite of a value from this table and it means that the symbol of a given type has been negated. Table 2.3 Register codes for extended SIB /------------------\ | Value | Register | |=======|==========| | 23h | BX | |-------|----------| | 25h | BP | |-------|----------| | 26h | SI | |-------|----------| | 27h | DI | |-------|----------| | 40h | EAX | |-------|----------| | 41h | ECX | |-------|----------| | 42h | EDX | |-------|----------| | 43h | EBX | |-------|----------| | 44h | ESP | |-------|----------| | 45h | EBP | |-------|----------| | 46h | ESI | |-------|----------| | 47h | EDI | |-------|----------| | 48h | R8D | |-------|----------| | 49h | R9D | |-------|----------| | 4Ah | R10D | |-------|----------| | 4Bh | R11D | |-------|----------| | 4Ch | R12D | |-------|----------| | 4Dh | R13D | |-------|----------| | 4Eh | R14D | |-------|----------| | 4Fh | R15D | |-------|----------| | 80h | RAX | |-------|----------| | 81h | RCX | |-------|----------| | 82h | RDX | |-------|----------| | 83h | RBX | |-------|----------| | 84h | RSP | |-------|----------| | 85h | RBP | |-------|----------| | 86h | RSI | |-------|----------| | 87h | RDI | |-------|----------| | 88h | R8 | |-------|----------| | 89h | R9 | |-------|----------| | 8Ah | R10 | |-------|----------| | 8Bh | R11 | |-------|----------| | 8Ch | R12 | |-------|----------| | 8Dh | R13 | |-------|----------| | 8Eh | R14 | |-------|----------| | 8Fh | R15 | |-------|----------| | 94h | EIP | |-------|----------| | 98h | RIP | \------------------/ Table 3 Preprocessed line /--------------------------------------------------------------------------\ | Offset | Size | Value | |========|=================================================================| | +0 | dword | When the line was loaded from source, this field | | | | contains either zero (if it is the line from the main | | | | input file), or an offset inside the preprocessed | | | | source to the name of file, from which this line was | | | | loaded (the name of file is zero-ended string). | | | | When the line was generated by macroinstruction, this | | | | field contains offset inside the preprocessed source to | | | | the pascal-style string specifying the name of | | | | macroinstruction, which generated this line. | |--------|-------|---------------------------------------------------------| | +4 | dword | Bits 0-30 contain the number of this line. | | | | If the highest bit is zeroed, this line was loaded from | | | | source. | | | | If the highest bit is set, this line was generated by | | | | macroinstruction. | |--------|-------|---------------------------------------------------------| | +8 | dword | If the line was loaded from source, this field contains | | | | the position of the line inside the source file, from | | | | which it was loaded. | | | | If line was generated by macroinstruction, this field | | | | contains the offset of preprocessed line, which invoked | | | | the macroinstruction. | | | | If line was generated by instantaneous macro, this | | | | field is equal to the next one. | |--------|-------|---------------------------------------------------------| | +12 | dword | If the line was generated by macroinstruction, this | | | | field contains offset of the preprocessed line inside | | | | the definition of macro, from which this one was | | | | generated. | |--------|-------|---------------------------------------------------------| | +16 | ? | The tokenized contents of line. | \--------------------------------------------------------------------------/ Notes: To determine, whether this is the line loaded from source, or generated by macroinstruction, you need to check the highest bit of the second double word. The contents of line is no longer a text, which it was in source file, but a sequence of tokens, ended with a zero byte. Any chain of characters that aren't special ones, separated from other similar chains with spaces or some other special characters, is converted into symbol token. The first byte of this element has the value of 1Ah, the second byte is the count of characters, followed by this amount of bytes, which build the symbol. Some characters have a special meaning, and cannot occur inside the symbol, they split the symbols and are converted into separate tokens. For example, if source contains this line of text: mov ax,4 preprocessor converts it into the chain of bytes, shown here with their hexadecimal values (characters corresponding to some of those values are placed below the hexadecimal codes): 1A 03 6D 6F 76 1A 02 61 78 2C 1A 01 34 00 m o v a x , 4 The third type of token that can be found in preprocessed line is the quoted text. This element is created from chain of any bytes other than line breaks that are placed between the single or double quotes in the original text. First byte of such element is always 22h, it is followed by double word which specifies the number of bytes that follow, and the value of quoted text comes next. For example, this line from source: mov eax,'ABCD' is converted into (the notation used is the same as in previous sample): 1A 03 6D 6F 76 1A 03 65 61 78 2C 22 04 00 00 00 41 42 43 44 00 m o v e a x , A B C D This data defines two symbols followed by symbol character, quoted text and zero byte that marks end of line. There is also a special case of symbol token with first byte having the value 3Bh instead of 1Ah, such symbol means that all the line elements that follow, including this one, have already been interpreted by preprocessor and are ignored by assembler. Table 4 Row of the assembly dump /-------------------------------------------------------------------------\ | Offset | Size | Description | |========|=======|========================================================| | +0 | dword | Offset in output file. | |--------|-------|--------------------------------------------------------| | +4 | dword | Offset of line in preprocessed source. | |--------|-------|--------------------------------------------------------| | +8 | qword | Value of $ address. | |--------|-------|--------------------------------------------------------| | +16 | dword | Extended SIB for the $ address, the first two bytes | | | | are register codes and the second two bytes are | | | | corresponding scales. | |--------|-------|--------------------------------------------------------| | +20 | dword | If the $ address is relocatable, this field contains | | | | information about section or external symbol, to which | | | | it is relative - otherwise this field is zero. | | | | When the highest bit is cleared, the address is | | | | relative to a section, and the bits 0-30 contain | | | | the index (starting from 1) in the table of sections. | | | | When the highest bit is set, the address is relative | | | | to an external symbol, and the bits 0-30 contain the | | | | the offset of the name of this symbol in the strings | | | | table. | |--------|-------|--------------------------------------------------------| | +24 | byte | Type of $ address value (as in table 2.2). | |--------|-------|--------------------------------------------------------| | +25 | byte | Type of code - possible values are 16, 32, and 64. | |--------|-------|--------------------------------------------------------| | +26 | byte | If the bit 0 is set, then at this point the assembly | | | | was taking place inside the virtual block, and the | | | | offset in output file has no meaning here. | | | | If the bit 1 is set, the line was assembled at the | | | | point, which was not included in the output file for | | | | some other reasons (like inside the reserved data at | | | | the end of section). | |--------|-------|--------------------------------------------------------| | +27 | byte | The higher bits of value of $ address. | \-------------------------------------------------------------------------/ Notes: Each row of the assembly dump informs, that the given line of preprocessed source was assembled at the specified address (defined by its type, value and the extended SIB) and at the specified position in output file. edb-debugger/plugins/FasLoader/Fas/Exception.hpp0000644000175000017500000000207413765535463021435 0ustar eteraneteran/* * @file: Exception.hpp * * This file part of RT ( Reconstructive Tools ) * Copyright (c) 2018 darkprof * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . **/ #pragma once #include #include namespace Fas { class Exception : public std::exception { public: explicit Exception(const std::string &message); ~Exception() noexcept override = default; const char *what() const noexcept override; protected: std::string message_; }; } edb-debugger/plugins/FasLoader/Fas/Exception.cpp0000644000175000017500000000172413765535463021431 0ustar eteraneteran/* * @file: Exception.cpp * * This file part of RT ( Reconstructive Tools ) * Copyright (c) 2018 darkprof * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . **/ #include "Exception.hpp" namespace Fas { Exception::Exception(const std::string &message) : message_(message) { } const char *Exception::what() const noexcept { return message_.c_str(); } } edb-debugger/plugins/FasLoader/Fas/Symbol.hpp0000644000175000017500000000134213765535463020741 0ustar eteraneteran#pragma once #include namespace Fas { enum class ValueTypes : uint8_t { ABSOLUTE_VALUE = 0, RELOCATABLE_SEGMENT_ADDRESS, RELOCATABLE_32_BIT_ADDRESS, RELOCATABLE_RELATIVE_32_BIT_ADDRESS, RELOCATABLE_64_BIT_ADDRESS, GOT_RELATIVE_32_BIT_ADDRESS, _32_BIT_ADDRESS_OF_PLT_ENTRY, RELATIVE_32_BIT_ADDRESS_OF_PLT_ENTRY }; #pragma pack(push, 1) struct Symbol { uint64_t value : 63; uint64_t valueSign : 1; uint16_t flags; uint8_t sizeOfData; ValueTypes typeOfValue; uint32_t extendedSib; uint16_t numberOfPassDefined; uint16_t numberOfPassUsed; uint32_t section : 31; uint32_t sectionSign : 1; uint32_t preprocessed : 31; uint32_t preprocessedSign : 1; uint32_t offsetInPreprocessed; }; #pragma pack(pop) } edb-debugger/plugins/FasLoader/Fas/Core.hpp0000644000175000017500000000201213765535463020357 0ustar eteraneteran#pragma once #include "Header.hpp" #include "PluginSymbol.hpp" #include "Symbol.hpp" #include #include #include namespace Fas { using FasSymbols = std::vector; using PluginSymbols = std::vector; class Core { public: Core() = default; public: void load(const std::string &fileName_); const PluginSymbols &getSymbols(); private: void open(); void loadHeader(); void loadFasSymbols(); Symbol loadFasSymbol(); void deleteUndefinedSymbols(); void deleteCannotBeForwardReferenced(); void deleteAssemblyTimeVariable(); void deleteSpecialMarkers(); void deleteNegativeSymbols(); void deleteAnonymousSymbols(); void loadSymbols(); void checkAbsoluteValue(const Symbol &fasSymbol); void loadSymbolFromFasSymbol(const Symbol &fasSymbol); std::string pascal2string(const Symbol &fasSymbol); std::string cstr2string(const Symbol &fasSymbol); private: std::ifstream ifs_; std::string fileName_; Header header_; FasSymbols fasSymbols_; PluginSymbols symbols_; }; } edb-debugger/plugins/FasLoader/Fas/Header.hpp0000644000175000017500000000133213765535463020663 0ustar eteraneteran#pragma once #include namespace Fas { #pragma pack(push, 1) struct Header { uint32_t signature; uint8_t major; uint8_t minor; uint16_t lengthOfHeader; uint32_t offsetOfInputFileName; // base = strings table uint32_t offsetOfOutputFileName; // base = strings table uint32_t offsetOfStringsTable; uint32_t lengthOfStringsTable; uint32_t offsetOfSymbolsTable; uint32_t lengthOfSymbolsTable; uint32_t offsetOfPreprocessedSource; uint32_t lengthOfPreprocessedSource; uint32_t offsetOfAssemblyDump; uint32_t lengthOfAssemblyDump; uint32_t offsetOfSectionNamesTable; uint32_t lengthOfSectionNamesTable; uint32_t offsetOfSymbolReferencesDump; uint32_t lengthOfSymbolReferencesDump; }; #pragma pack(pop) } edb-debugger/plugins/FasLoader/Fas/Core.cpp0000644000175000017500000001117313765535463020362 0ustar eteraneteran #include "Core.hpp" #include "Exception.hpp" #include namespace { constexpr uint32_t Signature = 0x1A736166; } // TODO(eteran): this class is generally a good candiate for more RAII // and usage of the QFile class instead of std::ifstream namespace Fas { void Core::load(const std::string &fileName) { fileName_ = fileName; try { open(); loadHeader(); loadFasSymbols(); deleteUndefinedSymbols(); deleteAssemblyTimeVariable(); deleteCannotBeForwardReferenced(); deleteNegativeSymbols(); deleteSpecialMarkers(); qDebug() << fasSymbols_.size(); deleteAnonymousSymbols(); loadSymbols(); } catch (std::exception &e) { qWarning() << e.what(); } } void Core::open() { ifs_.open(fileName_, std::ios::binary); if (!ifs_.is_open()) { throw Exception("*.fas file not loaded."); } } void Core::loadHeader() { ifs_.seekg(0); if (!ifs_.read((char *)&header_, sizeof(Header))) { throw Exception("*.fas Header not loaded."); } if (header_.signature != Signature) { throw Exception("*.fas signature fail"); } if (header_.lengthOfHeader != 64) { throw Exception("*.fas header size not supported"); } } void Core::loadFasSymbols() { ifs_.seekg(header_.offsetOfSymbolsTable); auto size = header_.lengthOfSymbolsTable; auto count = size / sizeof(Fas::Symbol); for (uint i = 0; i < count; ++i) { auto symbol = loadFasSymbol(); fasSymbols_.push_back(symbol); } } Fas::Symbol Core::loadFasSymbol() { Fas::Symbol symbol; if (!ifs_.read((char *)&symbol, sizeof(Fas::Symbol))) { throw Exception("*.fas symbol not loaded"); } return symbol; } void Core::deleteUndefinedSymbols() { auto it = std::begin(fasSymbols_); while (it != std::end(fasSymbols_)) { uint16_t wasDefined = it->flags & 1; if (!wasDefined) { it = fasSymbols_.erase(it); } else { ++it; } } } void Core::deleteAssemblyTimeVariable() { auto it = std::begin(fasSymbols_); while (it != std::end(fasSymbols_)) { uint16_t isAssemblyTimeVariable = it->flags & 0x2; if (isAssemblyTimeVariable) { it = fasSymbols_.erase(it); } else { ++it; } } } void Core::deleteCannotBeForwardReferenced() { auto it = std::begin(fasSymbols_); while (it != std::end(fasSymbols_)) { uint16_t cannotBeForwardReferenced = it->flags & 0x4; if (cannotBeForwardReferenced) { it = fasSymbols_.erase(it); } else { ++it; } } } void Core::deleteSpecialMarkers() { auto it = std::begin(fasSymbols_); while (it != std::end(fasSymbols_)) { uint16_t isSpecialMarker = it->flags & 0x400; if (isSpecialMarker) { it = fasSymbols_.erase(it); } else { ++it; } } } void Core::deleteNegativeSymbols() { auto it = std::begin(fasSymbols_); while (it != std::end(fasSymbols_)) { if (it->valueSign) { it = fasSymbols_.erase(it); } else { ++it; } } } void Core::deleteAnonymousSymbols() { auto it = std::begin(fasSymbols_); while (it != std::end(fasSymbols_)) { bool isAnonymous = it->preprocessedSign == 0 && it->preprocessed == 0; if (isAnonymous) { it = fasSymbols_.erase(it); } else { ++it; } } } void Core::loadSymbols() { for (auto fasSymbol : fasSymbols_) { checkAbsoluteValue(fasSymbol); loadSymbolFromFasSymbol(fasSymbol); } } void Core::checkAbsoluteValue(const Fas::Symbol &fasSymbol) { if (fasSymbol.typeOfValue != ValueTypes::ABSOLUTE_VALUE) { throw Exception(" Support only absolute value"); } } void Core::loadSymbolFromFasSymbol(const Fas::Symbol &fasSymbol) { PluginSymbol symbol; symbol.value = fasSymbol.value; symbol.size = fasSymbol.sizeOfData; if (fasSymbol.preprocessedSign) { // in the strings table symbol.name = cstr2string(fasSymbol); } else { // in the preprocessed pascal style symbol.name = pascal2string(fasSymbol); } symbols_.push_back(symbol); } std::string Core::cstr2string(const Symbol &fasSymbol) { constexpr int MaxLength = 64; auto offset = header_.offsetOfSymbolsTable + fasSymbol.preprocessed; char cstr[MaxLength]; auto count = 0; ifs_.seekg(offset); char *c = cstr; while (true) { ifs_.read(c, 1); if (count >= (MaxLength - 1)) break; if (*c == 0) break; ++c; ++count; } cstr[count] = '\0'; std::string str = cstr; return str; } std::string Core::pascal2string(const Fas::Symbol &fasSymbol) { auto offset = header_.offsetOfPreprocessedSource + fasSymbol.preprocessed; uint8_t len; char pascal[64]; ifs_.seekg(offset); if (!ifs_.read((char *)&len, sizeof(len))) { throw Exception("Length of pascal string not loaded"); } if (!ifs_.read(pascal, len)) { throw Exception("Pascal string not loaded"); } pascal[len] = '\0'; std::string str = pascal; return str; } const PluginSymbols &Core::getSymbols() { return symbols_; } } edb-debugger/plugins/FasLoader/.gitignore0000644000175000017500000000124413765535463020243 0ustar eteraneteran# Compiled Object files *.slo *.lo *.o *.obj # MS compilers *.ilk *.pdb *.exp *.manifest *.filters x64/ *.msvcxproj *.sln *.vcxproj # Precompiled Headers *.gch *.pch # Compiled Dynamic libraries *.so *.so.* *.dylib *.dll # Fortran module files *.mod # Compiled Static libraries *.lai *.la *.a *.lib # Executables *.exe *.out *.app # Template files *~ *.log *.swp *.orig # temporary files to analyze changes in asm files # Dump files *.stackdump # cmake CMakeFiles/ CMakeCache.txt CTestTestfile.cmake Testing/ Makefile cmake_install.cmake install_manifest.txt *_autogen/ # tests *Test # ycm .ycm_extra_conf.py # vim Session.vim # eclipse .project # idea .idea/ edb-debugger/plugins/FasLoader/FasLoader.hpp0000644000175000017500000000102513765535463020621 0ustar eteraneteran#pragma once #include "Fas/Core.hpp" #include "IPlugin.h" class QMenu; namespace FasLoaderPlugin { class FasLoader : public QObject, public IPlugin { Q_OBJECT Q_INTERFACES(IPlugin) Q_PLUGIN_METADATA(IID "edb.IPlugin/1.0") Q_CLASSINFO("author", "darkprof") Q_CLASSINFO("url", "http://www.codef00.com") public: explicit FasLoader(QObject *parent = nullptr); ~FasLoader() override = default; public: QMenu *menu(QWidget *parent = nullptr) override; private Q_SLOTS: void load(); private: QMenu *menu_ = nullptr; }; } edb-debugger/plugins/References/0000755000175000017500000000000013765535463016473 5ustar eteraneteranedb-debugger/plugins/References/CMakeLists.txt0000644000175000017500000000151613765535463021236 0ustar eteraneterancmake_minimum_required (VERSION 3.1) include("GNUInstallDirs") set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(PluginName "References") find_package(Qt5 5.0.0 REQUIRED Widgets) add_library(${PluginName} SHARED DialogReferences.cpp DialogReferences.h DialogReferences.ui References.cpp References.h ) target_link_libraries(${PluginName} Qt5::Widgets edb) install (TARGETS ${PluginName} DESTINATION ${CMAKE_INSTALL_LIBDIR}/edb) set_property(TARGET ${PluginName} PROPERTY CXX_EXTENSIONS OFF) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD 14) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD_REQUIRED ON) set_property(TARGET ${PluginName} PROPERTY LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) set_property(TARGET ${PluginName} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) edb-debugger/plugins/References/DialogReferences.h0000644000175000017500000000260213765535463022045 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DIALOG_REFERENCES_H_20061101_ #define DIALOG_REFERENCES_H_20061101_ #include "IRegion.h" #include "Types.h" #include "ui_DialogReferences.h" #include class QListWidgetItem; namespace ReferencesPlugin { class DialogReferences : public QDialog { Q_OBJECT public: explicit DialogReferences(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~DialogReferences() override = default; public Q_SLOTS: void on_listWidget_itemDoubleClicked(QListWidgetItem *item); Q_SIGNALS: void updateProgress(int); private: void showEvent(QShowEvent *event) override; private: void doFind(); private: Ui::DialogReferences ui; QPushButton *buttonFind_ = nullptr; }; } #endif edb-debugger/plugins/References/DialogReferences.ui0000644000175000017500000000531313765535463022235 0ustar eteraneteran Evan Teran ReferencesPlugin::DialogReferences 0 0 376 383 References Search Find References To This Address: true Results: Monospace true Skip Regions With No Access Rights QDialogButtonBox::Close 0 Qt::Horizontal txtAddress listWidget chkSkipNoAccess buttonBox rejected() ReferencesPlugin::DialogReferences reject() 369 345 375 329 buttonBox accepted() ReferencesPlugin::DialogReferences accept() 136 331 232 307 edb-debugger/plugins/References/References.h0000644000175000017500000000246713765535463020736 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef REFERENCES_H_20060430_ #define REFERENCES_H_20060430_ #include "IPlugin.h" class QMenu; class QDialog; namespace ReferencesPlugin { class References : public QObject, public IPlugin { Q_OBJECT Q_INTERFACES(IPlugin) Q_PLUGIN_METADATA(IID "edb.IPlugin/1.0") Q_CLASSINFO("author", "Evan Teran") Q_CLASSINFO("url", "http://www.codef00.com") public: explicit References(QObject *parent = nullptr); ~References() override; public: QMenu *menu(QWidget *parent = nullptr) override; public Q_SLOTS: void showMenu(); private: QMenu *menu_ = nullptr; QPointer dialog_ = nullptr; }; } #endif edb-debugger/plugins/References/References.cpp0000644000175000017500000000276113765535463021266 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "References.h" #include "DialogReferences.h" #include "edb.h" #include namespace ReferencesPlugin { /** * @brief References::References * @param parent */ References::References(QObject *parent) : QObject(parent) { } /** * @brief References::~References */ References::~References() { delete dialog_; } /** * @brief References::menu * @param parent * @return */ QMenu *References::menu(QWidget *parent) { Q_ASSERT(parent); if (!menu_) { menu_ = new QMenu(tr("Reference Searcher"), parent); menu_->addAction(tr("&Reference Search"), this, SLOT(showMenu()), QKeySequence(tr("Ctrl+R"))); } return menu_; } /** * @brief References::showMenu */ void References::showMenu() { if (!dialog_) { dialog_ = new DialogReferences(edb::v1::debugger_ui); } dialog_->show(); } } edb-debugger/plugins/References/DialogReferences.cpp0000644000175000017500000001246213765535463022405 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "DialogReferences.h" #include "IDebugger.h" #include "MemoryRegions.h" #include "edb.h" #include "util/Math.h" #include #include #include namespace ReferencesPlugin { enum Role { TypeRole = Qt::UserRole + 0, AddressRole = Qt::UserRole + 1 }; /** * @brief DialogReferences::DialogReferences * @param parent * @param f */ DialogReferences::DialogReferences(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f) { ui.setupUi(this); connect(this, &DialogReferences::updateProgress, ui.progressBar, &QProgressBar::setValue); buttonFind_ = new QPushButton(QIcon::fromTheme("edit-find"), tr("Find")); connect(buttonFind_, &QPushButton::clicked, this, [this]() { buttonFind_->setEnabled(false); ui.progressBar->setValue(0); ui.listWidget->clear(); doFind(); ui.progressBar->setValue(100); buttonFind_->setEnabled(true); }); ui.buttonBox->addButton(buttonFind_, QDialogButtonBox::ActionRole); } /** * @brief DialogReferences::showEvent */ void DialogReferences::showEvent(QShowEvent *) { ui.listWidget->clear(); ui.progressBar->setValue(0); } /** * @brief DialogReferences::doFind */ void DialogReferences::doFind() { bool ok = false; edb::address_t address; const size_t page_size = edb::v1::debugger_core->pageSize(); const QString text = ui.txtAddress->text(); if (!text.isEmpty()) { ok = edb::v1::eval_expression(text, &address); } if (ok) { edb::v1::memory_regions().sync(); const QList> regions = edb::v1::memory_regions().regions(); int i = 0; for (const std::shared_ptr ®ion : regions) { // a short circut for speading things up if (region->accessible() || !ui.chkSkipNoAccess->isChecked()) { const size_t page_count = region->size() / page_size; const QVector pages = edb::v1::read_pages(region->start(), page_count); if (!pages.isEmpty()) { const uint8_t *p = &pages[0]; const uint8_t *const pages_end = &pages[0] + region->size(); while (p != pages_end) { if (pages_end - p < static_cast(edb::v1::pointer_size())) { break; } const edb::address_t addr = p - &pages[0] + region->start(); edb::address_t test_address(0); memcpy(&test_address, p, edb::v1::pointer_size()); if (test_address == address) { auto item = new QListWidgetItem(edb::v1::format_pointer(addr)); item->setData(TypeRole, 'D'); item->setData(AddressRole, addr.toQVariant()); ui.listWidget->addItem(item); } edb::Instruction inst(p, pages_end, addr); if (inst) { switch (inst.operation()) { case X86_INS_MOV: // instructions of the form: mov [ADDR], 0xNNNNNNNN Q_ASSERT(inst.operandCount() == 2); if (is_expression(inst[0])) { if (is_immediate(inst[1]) && static_cast(inst[1]->imm) == address) { auto item = new QListWidgetItem(edb::v1::format_pointer(addr)); item->setData(TypeRole, 'C'); item->setData(AddressRole, addr.toQVariant()); ui.listWidget->addItem(item); } } break; case X86_INS_PUSH: // instructions of the form: push 0xNNNNNNNN Q_ASSERT(inst.operandCount() == 1); if (is_immediate(inst[0]) && static_cast(inst[0]->imm) == address) { auto item = new QListWidgetItem(edb::v1::format_pointer(addr)); item->setData(TypeRole, 'C'); item->setData(AddressRole, addr.toQVariant()); ui.listWidget->addItem(item); } break; default: if (is_jump(inst) || is_call(inst)) { if (is_immediate(inst[0])) { if (static_cast(inst[0]->imm) == address) { auto item = new QListWidgetItem(edb::v1::format_pointer(addr)); item->setData(TypeRole, 'C'); item->setData(AddressRole, addr.toQVariant()); ui.listWidget->addItem(item); } } } break; } } Q_EMIT updateProgress(util::percentage(i, regions.size(), p - &pages[0], region->size())); ++p; } } } else { Q_EMIT updateProgress(util::percentage(i, regions.size())); } ++i; } } } /** * @brief DialogReferences::on_listWidget_itemDoubleClicked * * follows the found item in the data view * * @param item */ void DialogReferences::on_listWidget_itemDoubleClicked(QListWidgetItem *item) { const edb::address_t addr = item->data(AddressRole).toULongLong(); if (item->data(TypeRole).toChar() == 'D') { edb::v1::dump_data(addr, false); } else { edb::v1::jump_to_address(addr); } } } edb-debugger/plugins/DebuggerCore/0000755000175000017500000000000013765535463016747 5ustar eteraneteranedb-debugger/plugins/DebuggerCore/DebuggerCoreBase.cpp0000644000175000017500000001057513765535463022613 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "DebuggerCoreBase.h" #include "Breakpoint.h" #include "Configuration.h" #include "edb.h" #include namespace DebuggerCorePlugin { /** * removes all breakpoints * * @brief DebuggerCoreBase::clearBreakpoints */ void DebuggerCoreBase::clearBreakpoints() { if (attached()) { breakpoints_.clear(); } } /** * creates a new breakpoint (only if there isn't already one at the given address) * * @brief DebuggerCoreBase::addBreakpoint * @param address * @return the breakpoint which was created/found */ std::shared_ptr DebuggerCoreBase::addBreakpoint(edb::address_t address) { try { if (attached()) { if (std::shared_ptr bp = findBreakpoint(address)) { return bp; } auto bp = std::make_shared(address); breakpoints_[address] = bp; return bp; } return nullptr; } catch (const BreakpointCreationError &) { qDebug() << "Failed to create breakpoint"; return nullptr; } } /** * @brief DebuggerCoreBase::findBreakpoint * @param address * @return the breakpoint at the given address or std::shared_ptr() */ std::shared_ptr DebuggerCoreBase::findBreakpoint(edb::address_t address) { if (attached()) { auto it = breakpoints_.find(address); if (it != breakpoints_.end()) { return it.value(); } } return nullptr; } /** * similarly to findBreakpoint, finds a breakpoint near given address. But * unlike findBreakpoint, this function looks for a breakpoint which ends * up at this address after being triggered, instead of just starting there. * * @brief DebuggerCoreBase::findTriggeredBreakpoint * @param address * @return */ std::shared_ptr DebuggerCoreBase::findTriggeredBreakpoint(edb::address_t address) { if (attached()) { for (const size_t size : Breakpoint::possibleRewindSizes()) { const edb::address_t bpAddr = address - size; const std::shared_ptr bp = findBreakpoint(bpAddr); if (bp && bp->address() == bpAddr) { return bp; } } } return nullptr; } /** * Decrements the reference count for the breakpoint found at the given address. * If the refernce count goes to zero, then it is removed. * This is a no-op if there is no breakpoint present. * * @brief DebuggerCoreBase::removeBreakpoint * @param address */ void DebuggerCoreBase::removeBreakpoint(edb::address_t address) { // TODO(eteran): assert paused if (attached()) { auto it = breakpoints_.find(address); if (it != breakpoints_.end()) { breakpoints_.erase(it); } } } /** * Ends debug session, detaching from or killing debuggee according to user preferences * * @brief DebuggerCoreBase::endDebugSession */ void DebuggerCoreBase::endDebugSession() { if (attached()) { switch (edb::v1::config().close_behavior) { case Configuration::Detach: detach(); break; case Configuration::Kill: kill(); break; case Configuration::KillIfLaunchedDetachIfAttached: if (lastMeansOfCapture() == MeansOfCapture::Launch) { kill(); } else { detach(); } break; } } } /** * returns a copy of the BP list, these count as references to the BPs * preventing full removal until this list is destructed. * * @brief DebuggerCoreBase::backupBreakpoints * @return a list of shared_ptr's to the BPs */ DebuggerCoreBase::BreakpointList DebuggerCoreBase::backupBreakpoints() const { return breakpoints_; } /** * @brief DebuggerCoreBase::attached * @return */ bool DebuggerCoreBase::attached() const { return process() != nullptr; } /** * @brief DebuggerCoreBase::supportedBreakpointTypes * @return */ std::vector DebuggerCoreBase::supportedBreakpointTypes() const { return Breakpoint::supportedTypes(); } } edb-debugger/plugins/DebuggerCore/win32/0000755000175000017500000000000013765535463017711 5ustar eteraneteranedb-debugger/plugins/DebuggerCore/win32/DebuggerCore.cpp0000644000175000017500000003102613765535463022754 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "DebuggerCore.h" #include "edb.h" #include "MemoryRegions.h" #include "PlatformEvent.h" #include "PlatformProcess.h" #include "PlatformRegion.h" #include "PlatformState.h" #include "PlatformThread.h" #include "State.h" #include "string_hash.h" #include #include #include #include #include #include #include #include #ifdef _MSC_VER #pragma comment(lib, "Advapi32.lib") #pragma comment(lib, "Psapi.lib") #endif /* NOTE(eteran): from the MSDN: * Note that while reporting debug events, all threads within the reporting * process are frozen. Debuggers are expected to use the SuspendThread and * ResumeThread functions to limit the set of threads that can execute within a * process. By suspending all threads in a process except for the one reporting * a debug event, it is possible to "single step" a single thread. The other * threads are not released by a continue operation if they are suspended. */ namespace DebuggerCorePlugin { namespace { /* * Required to debug and adjust the memory of a process owned by another account. * OpenProcess quote (MSDN): * "If the caller has enabled the SeDebugPrivilege privilege, the requested access * is granted regardless of the contents of the security descriptor." * Needed to open system processes (user SYSTEM) * * NOTE: You need to be admin to enable this privilege * NOTE: You need to have the 'Debug programs' privilege set for the current user, * if the privilege is not present it can't be enabled! * NOTE: Detectable by antidebug code (changes debuggee privileges too) */ bool set_debug_privilege(HANDLE process, bool set) { HANDLE token; bool ok = false; //process must have PROCESS_QUERY_INFORMATION if (OpenProcessToken(process, TOKEN_ADJUST_PRIVILEGES, &token)) { LUID luid; if (LookupPrivilegeValue(nullptr, SE_DEBUG_NAME, &luid)) { TOKEN_PRIVILEGES tp; tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = set ? SE_PRIVILEGE_ENABLED : 0; ok = AdjustTokenPrivileges(token, false, &tp, NULL, nullptr, nullptr); } CloseHandle(token); } return ok; } } /** * @brief DebuggerCore::DebuggerCore */ DebuggerCore::DebuggerCore() { DebugSetProcessKillOnExit(false); SYSTEM_INFO sys_info; GetSystemInfo(&sys_info); pageSize_ = sys_info.dwPageSize; set_debug_privilege(GetCurrentProcess(), true); // gogo magic powers } /** * @brief DebuggerCore::~DebuggerCore */ DebuggerCore::~DebuggerCore() { detach(); set_debug_privilege(GetCurrentProcess(), false); } /** * @brief DebuggerCore::pageSize * @return the size of a page on this system */ size_t DebuggerCore::pageSize() const { return pageSize_; } /** * @brief DebuggerCore::hasExtension * @param ext * @return */ bool DebuggerCore::hasExtension(uint64_t ext) const { #if !defined(EDB_X86_64) switch (ext) { case edb::string_hash("MMX"): return IsProcessorFeaturePresent(PF_MMX_INSTRUCTIONS_AVAILABLE); case edb::string_hash("XMM"): return IsProcessorFeaturePresent(PF_XMMI_INSTRUCTIONS_AVAILABLE); default: return false; } #else switch (ext) { case edb::string_hash("MMX"): case edb::string_hash("XMM"): return true; default: return false; } #endif } /** * waits for a debug event, secs is a timeout (but is not yet respected) * * @brief DebuggerCore::waitDebugEvent * @param msecs * @return null if timeout occured */ std::shared_ptr DebuggerCore::waitDebugEvent(std::chrono::milliseconds msecs) { if (attached()) { DEBUG_EVENT de; while (WaitForDebugEvent(&de, msecs.count() == 0 ? INFINITE : msecs.count())) { Q_ASSERT(process_->pid() == de.dwProcessId); activeThread_ = de.dwThreadId; bool propagate = false; switch (de.dwDebugEventCode) { case CREATE_THREAD_DEBUG_EVENT: { auto newThread = std::make_shared(this, process_, &de.u.CreateThread); threads_.insert(activeThread_, newThread); break; } case EXIT_THREAD_DEBUG_EVENT: threads_.remove(activeThread_); break; case CREATE_PROCESS_DEBUG_EVENT: { CloseHandle(de.u.CreateProcessInfo.hFile); process_ = std::make_shared(this, de.u.CreateProcessInfo.hProcess); // fake a thread create event for the main thread.. CREATE_THREAD_DEBUG_INFO thread_info; thread_info.hThread = de.u.CreateProcessInfo.hThread; thread_info.lpStartAddress = de.u.CreateProcessInfo.lpStartAddress; thread_info.lpThreadLocalBase = de.u.CreateProcessInfo.lpThreadLocalBase; auto newThread = std::make_shared(this, process_, &thread_info); threads_.insert(activeThread_, newThread); break; } case LOAD_DLL_DEBUG_EVENT: CloseHandle(de.u.LoadDll.hFile); break; case EXIT_PROCESS_DEBUG_EVENT: process_->resume(edb::DEBUG_CONTINUE); process_ = nullptr; // handle_event_exited returns DEBUG_STOP, which in turn keeps the debugger from resuming the process // However, this is needed to close all internal handles etc. and finish the debugging session // So we do it manually here propagate = true; break; case EXCEPTION_DEBUG_EVENT: propagate = true; break; case RIP_EVENT: break; default: break; } if (auto p = static_cast(process_.get())) { p->lastEvent_ = de; } if (propagate) { // normal event auto e = std::make_shared(); e->event_ = de; return e; } process_->resume(edb::DEBUG_EXCEPTION_NOT_HANDLED); } } return nullptr; } /** * @brief DebuggerCore::attach * @param pid * @return */ Status DebuggerCore::attach(edb::pid_t pid) { detach(); if (DebugActiveProcess(pid)) { process_ = std::make_shared(this, pid); return Status::Ok; } return Status("Error DebuggerCore::attach"); } /** * @brief DebuggerCore::detach * @return */ Status DebuggerCore::detach() { if (attached()) { clearBreakpoints(); // Make sure exceptions etc. are passed ContinueDebugEvent(process_->pid(), active_thread(), DBG_CONTINUE); DebugActiveProcessStop(process_->pid()); process_ = nullptr; threads_.clear(); } return Status::Ok; } /** * @brief DebuggerCore::kill */ void DebuggerCore::kill() { if (auto p = static_cast(process_.get())) { TerminateProcess(p->hProcess_, -1); detach(); } } /** * @brief DebuggerCore::open * @param path * @param cwd * @param args * @param tty * @return */ Status DebuggerCore::open(const QString &path, const QString &cwd, const QList &args, const QString &input, const QString &output) { // TODO: Don't inherit security descriptors from this process (default values) // Is this even possible? Q_UNUSED(input) Q_UNUSED(output) Q_ASSERT(!path.isEmpty()); bool ok = false; detach(); // default to process's directory QString tcwd = cwd.isEmpty() ? QFileInfo(path).canonicalPath() : cwd; STARTUPINFO startup_info = {}; PROCESS_INFORMATION process_info = {}; const DWORD CREATE_FLAGS = DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS | CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE; wchar_t *const env_block = GetEnvironmentStringsW(); // Set up command line QString command_str = '\"' + QFileInfo(path).canonicalPath() + '\"'; // argv[0] = full path (explorer style) if (!args.isEmpty()) { for (QByteArray arg : args) { command_str += " "; command_str += arg; } } // CreateProcessW wants a writable copy of the command line :< auto command_path = new wchar_t[command_str.length() + sizeof(wchar_t)]; wcscpy_s(command_path, command_str.length() + 1, reinterpret_cast(command_str.utf16())); if (CreateProcessW( reinterpret_cast(path.utf16()), // exe command_path, // commandline nullptr, // default security attributes nullptr, // default thread security too FALSE, // inherit handles CREATE_FLAGS, env_block, // environment data reinterpret_cast(tcwd.utf16()), // working directory &startup_info, &process_info)) { activeThread_ = process_info.dwThreadId; CloseHandle(process_info.hThread); // We don't need the thread handle set_debug_privilege(process_info.hProcess, false); //process_info.hProcess has PROCESS_ALL_ACCESS process_ = std::make_shared(this, process_info.hProcess); ok = true; } delete[] command_path; FreeEnvironmentStringsW(env_block); if (ok) { return Status::Ok; } else { return Status("Error DebuggerCore::open"); } } /** * @brief DebuggerCore::createState * @return */ std::unique_ptr DebuggerCore::createState() const { return std::make_unique(); } /** * @brief DebuggerCore::sys_pointer_size * @return the size of a pointer on this arch */ int DebuggerCore::sys_pointer_size() const { return sizeof(void *); } /** * @brief DebuggerCore::enumerateProcesses * @return */ QMap> DebuggerCore::enumerateProcesses() const { QMap> ret; HANDLE handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (handle != INVALID_HANDLE_VALUE) { PROCESSENTRY32 lppe; std::memset(&lppe, 0, sizeof(lppe)); lppe.dwSize = sizeof(lppe); if (Process32First(handle, &lppe)) { do { // NOTE(eteran): the const_cast is reasonable here. // While we don't want THIS function to mutate the DebuggerCore object // we do want the associated PlatformProcess to be able to trigger // non-const operations in the future, at least hypothetically. auto pi = std::make_shared(const_cast(this), lppe.th32ProcessID); if (pi->hProcess_ == nullptr) { continue; } ret.insert(pi->pid(), pi); std::memset(&lppe, 0, sizeof(lppe)); lppe.dwSize = sizeof(lppe); } while (Process32Next(handle, &lppe)); } CloseHandle(handle); } return ret; } /** * @brief DebuggerCore::parentPid * @param pid * @return */ edb::pid_t DebuggerCore::parentPid(edb::pid_t pid) const { edb::pid_t parent = 1; // 1?? HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, pid); if (hProcessSnap != INVALID_HANDLE_VALUE) { PROCESSENTRY32W pe32; pe32.dwSize = sizeof(pe32); if (Process32FirstW(hProcessSnap, &pe32)) { do { if (pid == pe32.th32ProcessID) { parent = pe32.th32ParentProcessID; break; } } while (Process32NextW(hProcessSnap, &pe32)); } CloseHandle(hProcessSnap); } return parent; } /** * @brief DebuggerCore::exceptions * @return */ QMap DebuggerCore::exceptions() const { QMap exceptions; return exceptions; } /** * @brief DebuggerCore::cpuType * @return */ uint64_t DebuggerCore::cpuType() const { #ifdef EDB_X86 return edb::string_hash("x86"); #elif defined(EDB_X86_64) return edb::string_hash("x86-64"); #endif } /** * @brief DebuggerCore::stackPointer * @return */ QString DebuggerCore::stackPointer() const { #ifdef EDB_X86 return "esp"; #elif defined(EDB_X86_64) // TODO(eteran): WOW64 support return "rsp"; #endif } /** * @brief DebuggerCore::framePointer * @return */ QString DebuggerCore::framePointer() const { #ifdef EDB_X86 return "ebp"; #elif defined(EDB_X86_64) // TODO(eteran): WOW64 support return "rbp"; #endif } /** * @brief DebuggerCore::instructionPointer * @return */ QString DebuggerCore::instructionPointer() const { #ifdef EDB_X86 return "eip"; #elif defined(EDB_X86_64) // TODO(eteran): WOW64 support return "rip"; #endif } /** * @brief DebuggerCore::process * @return */ IProcess *DebuggerCore::process() const { return process_.get(); } /** * @brief DebuggerCore::nopFillByte * @return */ uint8_t DebuggerCore::nopFillByte() const { return 0x90; } } edb-debugger/plugins/DebuggerCore/win32/PlatformThread.cpp0000644000175000017500000001273213765535463023336 0ustar eteraneteran/* Copyright (C) 2015 - 2018 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "PlatformThread.h" #include "PlatformProcess.h" #include "PlatformState.h" #include "State.h" namespace DebuggerCorePlugin { /** * @brief PlatformThread::PlatformThread * @param core * @param process * @param hThread */ PlatformThread::PlatformThread(DebuggerCore *core, std::shared_ptr &process, const CREATE_THREAD_DEBUG_INFO *info) : core_(core), process_(process) { isWow64_ = static_cast(process.get())->isWow64(); DuplicateHandle( GetCurrentProcess(), info->hThread, GetCurrentProcess(), &hThread_, 0, FALSE, DUPLICATE_SAME_ACCESS); } /** * @brief PlatformThread::tid * @return */ edb::tid_t PlatformThread::tid() const { return GetThreadId(hThread_); } /** * @brief PlatformThread::name * @return */ QString PlatformThread::name() const { using GetThreadDescriptionType = HRESULT(WINAPI *)(HANDLE, PWSTR *); static auto fnGetThreadDescription = reinterpret_cast(GetProcAddress(GetModuleHandle(TEXT("kernel32")), "GetThreadDescription")); if (fnGetThreadDescription) { WCHAR *data; HRESULT hr = fnGetThreadDescription(hThread_, &data); if (SUCCEEDED(hr)) { auto name = QString::fromWCharArray(data); LocalFree(data); return name; } } return tr("Thread: %1").arg(tid()); } /** * @brief PlatformThread::priority * @return */ int PlatformThread::priority() const { return GetThreadPriority(hThread_); } /** * @brief PlatformThread::instructionPointer * @return */ edb::address_t PlatformThread::instructionPointer() const { #if defined(EDB_X86) CONTEXT context; context.ContextFlags = CONTEXT_CONTROL; GetThreadContext(hThread_, &context); return context.Eip; #elif defined(EDB_X86_64) if (isWow64_) { WOW64_CONTEXT context; context.ContextFlags = CONTEXT_CONTROL; Wow64GetThreadContext(hThread_, &context); return context.Eip; } else { CONTEXT context; context.ContextFlags = CONTEXT_CONTROL; GetThreadContext(hThread_, &context); return context.Rip; } #endif } /** * @brief PlatformThread::runState * @return */ QString PlatformThread::runState() const { return {}; } /** * @brief PlatformThread::get_state * @param state */ void PlatformThread::getState(State *state) { if (auto p = static_cast(state->impl_.get())) { p->getThreadState(hThread_, isWow64_); } } /** * @brief PlatformThread::set_state * @param state */ void PlatformThread::setState(const State &state) { if (auto p = static_cast(state.impl_.get())) { p->setThreadState(hThread_); } } /** * @brief PlatformThread::step * @return */ Status PlatformThread::step() { #if defined(EDB_X86) CONTEXT context; context.ContextFlags = CONTEXT_CONTROL; GetThreadContext(hThread_, &context); context.EFlags |= (1 << 8); // set the trap flag SetThreadContext(hThread_, &context); #elif defined(EDB_X86_64) if (isWow64_) { WOW64_CONTEXT context; context.ContextFlags = CONTEXT_CONTROL; Wow64GetThreadContext(hThread_, &context); context.EFlags |= (1 << 8); // set the trap flag Wow64SetThreadContext(hThread_, &context); } else { CONTEXT context; context.ContextFlags = CONTEXT_CONTROL; GetThreadContext(hThread_, &context); context.EFlags |= (1 << 8); // set the trap flag SetThreadContext(hThread_, &context); } #endif return resume(); } /** * @brief PlatformThread::step * @param status * @return */ Status PlatformThread::step(edb::EventStatus status) { #if defined(EDB_X86) CONTEXT context; context.ContextFlags = CONTEXT_CONTROL; GetThreadContext(hThread_, &context); context.EFlags |= (1 << 8); // set the trap flag SetThreadContext(hThread_, &context); #elif defined(EDB_X86_64) if (isWow64_) { WOW64_CONTEXT context; context.ContextFlags = CONTEXT_CONTROL; Wow64GetThreadContext(hThread_, &context); context.EFlags |= (1 << 8); // set the trap flag Wow64SetThreadContext(hThread_, &context); } else { CONTEXT context; context.ContextFlags = CONTEXT_CONTROL; GetThreadContext(hThread_, &context); context.EFlags |= (1 << 8); // set the trap flag SetThreadContext(hThread_, &context); } #endif return resume(status); } /** * @brief PlatformThread::resume * @return */ Status PlatformThread::resume() { // TODO(eteran): suspend the other threads, then basically just call process_->resume ContinueDebugEvent(process_->pid(), tid(), DBG_CONTINUE); return Status::Ok; } /** * @brief PlatformThread::resume * @param status * @return */ Status PlatformThread::resume(edb::EventStatus status) { // TODO(eteran): suspend the other threads, then basically just call process_->resume ContinueDebugEvent( process_->pid(), tid(), (status == edb::DEBUG_CONTINUE) ? (DBG_CONTINUE) : (DBG_EXCEPTION_NOT_HANDLED)); return Status::Ok; } /** * @brief PlatformThread::isPaused * @return */ bool PlatformThread::isPaused() const { return {}; } } edb-debugger/plugins/DebuggerCore/win32/DebuggerCore.h0000644000175000017500000000710213765535463022417 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DEBUGGER_CORE_H_20090529_ #define DEBUGGER_CORE_H_20090529_ #include "DebuggerCoreBase.h" #include "IRegion.h" #include "Module.h" #include namespace DebuggerCorePlugin { class PlatformProcess; class PlatformThread; class DebuggerCore : public DebuggerCoreBase { Q_OBJECT Q_PLUGIN_METADATA(IID "edb.IDebugger/1.0") Q_INTERFACES(IDebugger) Q_CLASSINFO("author", "Evan Teran") Q_CLASSINFO("url", "http://www.codef00.com") friend class PlatformProcess; friend class PlatformThread; public: DebuggerCore(); ~DebuggerCore() override; public: bool hasExtension(uint64_t ext) const override; size_t pageSize() const override; std::size_t pointerSize() const override { return sizeof(void *); } std::shared_ptr waitDebugEvent(std::chrono::milliseconds msecs) override; Status attach(edb::pid_t pid) override; Status detach() override; void kill() override; Status open(const QString &path, const QString &cwd, const QList &args, const QString &input, const QString &output) override; MeansOfCapture lastMeansOfCapture() const override { qDebug("TODO: Implement DebuggerCore::lastMeansOfCapture"); return MeansOfCapture::NeverCaptured; } int sys_pointer_size() const; QMap exceptions() const override; QString exceptionName(qlonglong value) override { qDebug("TODO: Implement DebuggerCore::exceptionName"); return ""; } qlonglong exceptionValue(const QString &name) override { qDebug("TODO: Implement DebuggerCore::exceptionValue"); return 0; } public: // thread support stuff (optional) QList thread_ids() const { return threads_.keys(); } edb::tid_t active_thread() const { return activeThread_; } void set_active_thread(edb::tid_t tid) { Q_ASSERT(threads_.contains(tid)); activeThread_ = tid; } public: // process properties edb::pid_t parentPid(edb::pid_t pid) const override; uint64_t cpuType() const override; CpuMode cpuMode() const override { qDebug("TODO: Implement DebuggerCore::cpu_mode"); return CpuMode::Unknown; } public: std::unique_ptr createState() const override; private: QMap> enumerateProcesses() const override; public: QString stackPointer() const override; QString framePointer() const override; QString instructionPointer() const override; QString flagRegister() const override { qDebug("TODO: Implement DebuggerCore::flag_register"); return ""; } void setIgnoredExceptions(const QList &exceptions) override { Q_UNUSED(exceptions) qDebug("TODO: Implement DebuggerCore::set_ignored_exceptions"); } public: uint8_t nopFillByte() const override; public: IProcess *process() const override; private: using threadmap_t = QHash>; private: size_t pageSize_ = 0; std::shared_ptr process_; threadmap_t threads_; edb::tid_t activeThread_; }; } #endif edb-debugger/plugins/DebuggerCore/win32/PlatformState.h0000644000175000017500000000766213765535463022662 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PLATFORM_STATE_H_20110330_ #define PLATFORM_STATE_H_20110330_ #include "IState.h" #include "Types.h" #include namespace DebuggerCorePlugin { class PlatformState : public IState { friend class DebuggerCore; friend class PlatformThread; public: PlatformState() = default; public: std::unique_ptr clone() const override; public: long double fpu_register(int n) const; quint64 mmx_register(int n) const; QByteArray xmm_register(int n) const; public: QString flagsToString() const override; QString flagsToString(edb::reg_t flags) const override; Register value(const QString ®) const override; Register instructionPointerRegister() const override; Register flagsRegister() const override; edb::address_t framePointer() const override; edb::address_t instructionPointer() const override; edb::address_t stackPointer() const override; edb::reg_t debugRegister(size_t n) const override; edb::reg_t flags() const override; void adjustStack(int bytes) override; void clear() override; bool empty() const override { qDebug("TODO: implement PlatformState::empty"); return true; } void setDebugRegister(size_t n, edb::reg_t value) override; void setFlags(edb::reg_t flags) override; void setInstructionPointer(edb::address_t value) override; void setRegister(const QString &name, edb::reg_t value) override; void setRegister(const Register ®) override { qDebug("TODO: implement PlatformState::set_register"); } Register archRegister(uint64_t type, size_t n) const override; Register mmxRegister(size_t n) const { qDebug("TODO: implement PlatformState::mmx_register"); return Register(); } Register xmmRegister(size_t n) const { qDebug("TODO: implement PlatformState::xmm_register"); return Register(); } Register ymmRegister(size_t n) const { qDebug("TODO: implement PlatformState::ymm_register"); return Register(); } Register gpRegister(size_t n) const override { qDebug("TODO: implement PlatformState::gp_register"); return Register(); } int fpuStackPointer() const override { qDebug("TODO: implement PlatformState::fpu_stack_pointer"); return 0; } edb::value80 fpuRegister(size_t n) const override { qDebug("TODO: implement PlatformState::fpu_register"); return edb::value80(); } bool fpuRegisterIsEmpty(size_t n) const override { qDebug("TODO: implement PlatformState::fpu_register_is_empty"); return true; } QString fpuRegisterTagString(size_t n) const override { qDebug("TODO: implement PlatformState::fpu_register_tag_string"); return ""; } edb::value16 fpuControlWord() const override { qDebug("TODO: implement PlatformState::fpu_control_word"); return edb::value16(); } edb::value16 fpuStatusWord() const override { qDebug("TODO: implement PlatformState::fpu_status_word"); return edb::value16(); } edb::value16 fpuTagWord() const override { qDebug("TODO: implement PlatformState::fpu_tag_word"); return edb::value16(); } public: void getThreadState(HANDLE hThread, bool isWow64); void setThreadState(HANDLE hThread) const; private: #if defined(EDB_X86_64) union { CONTEXT context64_ = {}; WOW64_CONTEXT context32_; }; bool isWow64_ = false; #else CONTEXT context32_; #endif edb::address_t fs_base_ = 0; edb::address_t gs_base_ = 0; }; } #endif edb-debugger/plugins/DebuggerCore/win32/PlatformEvent.cpp0000644000175000017500000002572613765535463023217 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "PlatformEvent.h" #include "edb.h" namespace DebuggerCorePlugin { /** * @brief PlatformEvent::clone * @return */ PlatformEvent *PlatformEvent::clone() const { return new PlatformEvent(*this); } /** * @brief PlatformEvent::errorDescription * @return */ IDebugEvent::Message PlatformEvent::errorDescription() const { Q_ASSERT(isError()); auto fault_address = static_cast(-1); if (event_.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) { fault_address = static_cast(event_.u.Exception.ExceptionRecord.ExceptionInformation[1]); } switch (code()) { case EXCEPTION_ACCESS_VIOLATION: return Message( tr("Illegal Access Fault"), tr( "

The debugged application encountered a segmentation fault.
The address 0x%1 could not be accessed.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

") .arg(edb::v1::format_pointer(fault_address)), tr("EXCEPTION_ACCESS_VIOLATION")); case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return Message( tr("Array Bounds Error"), tr( "

The debugged application tried to access an out of bounds array element.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

") .arg(edb::v1::format_pointer(fault_address)), tr("EXCEPTION_ARRAY_BOUNDS_EXCEEDED")); case EXCEPTION_DATATYPE_MISALIGNMENT: return Message( tr("Bus Error"), tr( "

The debugged application tried to read or write data that is misaligned.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

"), tr("EXCEPTION_DATATYPE_MISALIGNMENT")); case EXCEPTION_FLT_DENORMAL_OPERAND: return Message( tr("Floating Point Exception"), tr( "

One of the operands in a floating-point operation is denormal. A denormal value is one that is too small to represent as a standard floating-point value.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

"), tr("EXCEPTION_FLT_DENORMAL_OPERAND")); case EXCEPTION_FLT_DIVIDE_BY_ZERO: return Message( tr("Floating Point Exception"), tr( "

The debugged application tried to divide a floating-point value by a floating-point divisor of zero.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

"), tr("EXCEPTION_FLT_DIVIDE_BY_ZERO")); case EXCEPTION_FLT_INEXACT_RESULT: return Message( tr("Floating Point Exception"), tr( "

The result of a floating-point operation cannot be represented exactly as a decimal fraction.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

"), tr("EXCEPITION_FLT_INEXACT_RESULT")); case EXCEPTION_FLT_INVALID_OPERATION: return Message( tr("Floating Point Exception"), tr( "

The application attempted an invalid floating point operation.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

"), tr("EXCEPTION_FLT_INVALID_OPERATION")); case EXCEPTION_FLT_OVERFLOW: return Message( tr("Floating Point Exception"), tr( "

The exponent of a floating-point operation is greater than the magnitude allowed by the corresponding type.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

"), tr("EXCEPTION_FLT_OVERFLOW")); case EXCEPTION_FLT_STACK_CHECK: return Message( tr("Floating Point Exception"), tr( "

The stack overflowed or underflowed as the result of a floating-point operation.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

"), tr("EXCEPTION_FLT_STACK_CHECK")); case EXCEPTION_FLT_UNDERFLOW: return Message( tr("Floating Point Exception"), tr( "

The exponent of a floating-point operation is less than the magnitude allowed by the corresponding type.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

"), tr("EXCEPTION_FLT_UNDERFLOW")); case EXCEPTION_ILLEGAL_INSTRUCTION: return Message( tr("Illegal Instruction Fault"), tr( "

The debugged application attempted to execute an illegal instruction.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

"), tr("EXCEPTION_ILLEGAL_INSTRUCTION")); case EXCEPTION_IN_PAGE_ERROR: return Message( tr("Page Error"), tr( "

The debugged application tried to access a page that was not present, and the system was unable to load the page.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

"), tr("EXCEPTION_IN_PAGE_ERROR")); case EXCEPTION_INT_DIVIDE_BY_ZERO: return Message( tr("Divide By Zero"), tr( "

The debugged application tried to divide an integer value by an integer divisor of zero.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

"), tr("EXCEPTION_INT_DIVIDE_BY_ZERO")); case EXCEPTION_INT_OVERFLOW: return Message( tr("Integer Overflow"), tr( "

The result of an integer operation caused a carry out of the most significant bit of the result.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

"), tr("EXCEPTION_INT_OVERFLOW")); case EXCEPTION_INVALID_DISPOSITION: return Message( tr("Invalid Disposition"), tr( "

An exception handler returned an invalid disposition to the exception dispatcher.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

"), tr("EXCEPTION_INVALID_DISPOSITION")); case EXCEPTION_NONCONTINUABLE_EXCEPTION: return Message( tr("Non-Continuable Exception"), tr( "

The debugged application tried to continue execution after a non-continuable exception occurred.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

"), tr("EXCEPTION_NONCONTINUABLE_EXCEPTION")); case EXCEPTION_PRIV_INSTRUCTION: return Message( tr("Privileged Instruction"), tr( "

The debugged application tried to execute an instruction whose operation is not allowed in the current machine mode.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

"), tr("EXCEPTION_PRIV_INSTRUCTION")); case EXCEPTION_STACK_OVERFLOW: return Message( tr("Stack Overflow"), tr( "

The debugged application has exhausted its stack.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

"), tr("EXCEPTION_STACK_OVERFLOW")); default: return Message(); } } /** * @brief PlatformEvent::reason * @return */ IDebugEvent::REASON PlatformEvent::reason() const { switch (event_.dwDebugEventCode) { case EXCEPTION_DEBUG_EVENT: if (event_.u.Exception.ExceptionRecord.ExceptionFlags == EXCEPTION_NONCONTINUABLE) { return EVENT_TERMINATED; } else { return EVENT_STOPPED; } case EXIT_PROCESS_DEBUG_EVENT: return EVENT_EXITED; /* case CREATE_THREAD_DEBUG_EVENT: case CREATE_PROCESS_DEBUG_EVENT: case EXIT_THREAD_DEBUG_EVENT: case LOAD_DLL_DEBUG_EVENT: case UNLOAD_DLL_DEBUG_EVENT: case OUTPUT_DEBUG_STRING_EVENT: case RIP_EVENT: */ default: return EVENT_UNKNOWN; } } /** * @brief PlatformEvent::trapReason * @return */ IDebugEvent::TRAP_REASON PlatformEvent::trapReason() const { switch (event_.dwDebugEventCode) { case EXCEPTION_DEBUG_EVENT: switch (event_.u.Exception.ExceptionRecord.ExceptionCode) { case EXCEPTION_BREAKPOINT: return TRAP_BREAKPOINT; case EXCEPTION_SINGLE_STEP: return TRAP_STEPPING; } } return TRAP_BREAKPOINT; } /** * @brief PlatformEvent::exited * @return */ bool PlatformEvent::exited() const { return reason() == EVENT_EXITED; } /** * @brief PlatformEvent::isError * @return */ bool PlatformEvent::isError() const { switch (event_.dwDebugEventCode) { case EXCEPTION_DEBUG_EVENT: switch (event_.u.Exception.ExceptionRecord.ExceptionCode) { case EXCEPTION_ACCESS_VIOLATION: case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: case EXCEPTION_DATATYPE_MISALIGNMENT: case EXCEPTION_FLT_DENORMAL_OPERAND: case EXCEPTION_FLT_DIVIDE_BY_ZERO: case EXCEPTION_FLT_INEXACT_RESULT: case EXCEPTION_FLT_INVALID_OPERATION: case EXCEPTION_FLT_OVERFLOW: case EXCEPTION_FLT_STACK_CHECK: case EXCEPTION_FLT_UNDERFLOW: case EXCEPTION_ILLEGAL_INSTRUCTION: case EXCEPTION_INT_DIVIDE_BY_ZERO: case EXCEPTION_INT_OVERFLOW: case EXCEPTION_INVALID_DISPOSITION: case EXCEPTION_IN_PAGE_ERROR: case EXCEPTION_NONCONTINUABLE_EXCEPTION: case EXCEPTION_PRIV_INSTRUCTION: case EXCEPTION_STACK_OVERFLOW: return true; case EXCEPTION_BREAKPOINT: case EXCEPTION_SINGLE_STEP: return false; } /* case CREATE_THREAD_DEBUG_EVENT: case CREATE_PROCESS_DEBUG_EVENT: case EXIT_THREAD_DEBUG_EVENT: case EXIT_PROCESS_DEBUG_EVENT: case LOAD_DLL_DEBUG_EVENT: case UNLOAD_DLL_DEBUG_EVENT: case OUTPUT_DEBUG_STRING_EVENT: case RIP_EVENT: */ default: return false; } } /** * @brief PlatformEvent::isKill * @return */ bool PlatformEvent::isKill() const { return false; } /** * @brief PlatformEvent::isStop * @return */ bool PlatformEvent::isStop() const { return !isTrap(); } /** * @brief PlatformEvent::isTrap * @return */ bool PlatformEvent::isTrap() const { if (stopped()) { switch (event_.u.Exception.ExceptionRecord.ExceptionCode) { case EXCEPTION_SINGLE_STEP: case EXCEPTION_BREAKPOINT: return true; default: return false; } } return false; } /** * @brief PlatformEvent::terminated * @return */ bool PlatformEvent::terminated() const { return reason() == EVENT_TERMINATED; } /** * @brief PlatformEvent::stopped * @return */ bool PlatformEvent::stopped() const { return reason() == EVENT_STOPPED; } /** * @brief PlatformEvent::process * @return */ edb::pid_t PlatformEvent::process() const { return event_.dwProcessId; } /** * @brief PlatformEvent::thread * @return */ edb::tid_t PlatformEvent::thread() const { return event_.dwThreadId; } /** * @brief PlatformEvent::code * @return */ int64_t PlatformEvent::code() const { if (stopped()) { return event_.u.Exception.ExceptionRecord.ExceptionCode; } if (terminated()) { return event_.u.Exception.ExceptionRecord.ExceptionCode; } if (exited()) { return event_.u.ExitProcess.dwExitCode; } return 0; } } edb-debugger/plugins/DebuggerCore/win32/PlatformRegion.cpp0000644000175000017500000001126513765535463023352 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "PlatformRegion.h" #include "IDebugEventHandler.h" #include "IDebugger.h" #include "IProcess.h" #include "MemoryRegions.h" #include "State.h" #include "edb.h" #include namespace DebuggerCorePlugin { namespace { constexpr IRegion::permissions_t KnownPermissions = (PAGE_NOACCESS | PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY); } /** * @brief PlatformRegion::PlatformRegion * @param start * @param end * @param base * @param name * @param permissions */ PlatformRegion::PlatformRegion(edb::address_t start, edb::address_t end, edb::address_t base, const QString &name, permissions_t permissions) : start_(start), end_(end), base_(base), name_(name), permissions_(permissions) { } /** * @brief PlatformRegion::clone * @return */ IRegion *PlatformRegion::clone() const { return new PlatformRegion(start_, end_, base_, name_, permissions_); } /** * @brief PlatformRegion::accessible * @return */ bool PlatformRegion::accessible() const { return readable() || writable() || executable(); } /** * @brief PlatformRegion::readable * @return */ bool PlatformRegion::readable() const { switch (permissions_ & KnownPermissions) { // ignore modifiers case PAGE_EXECUTE_READ: case PAGE_EXECUTE_READWRITE: case PAGE_READONLY: case PAGE_READWRITE: return true; default: return false; } } /** * @brief PlatformRegion::writable * @return */ bool PlatformRegion::writable() const { switch (permissions_ & KnownPermissions) { // ignore modifiers case PAGE_EXECUTE_READWRITE: case PAGE_EXECUTE_WRITECOPY: case PAGE_READWRITE: case PAGE_WRITECOPY: return true; default: return false; } } /** * @brief PlatformRegion::executable * @return */ bool PlatformRegion::executable() const { switch (permissions_ & KnownPermissions) { // ignore modifiers case PAGE_EXECUTE: case PAGE_EXECUTE_READ: case PAGE_EXECUTE_READWRITE: case PAGE_EXECUTE_WRITECOPY: return true; default: return false; } } /** * @brief PlatformRegion::size * @return */ size_t PlatformRegion::size() const { return end_ - start_; } /** * @brief PlatformRegion::setPermissions * @param read * @param write * @param execute */ void PlatformRegion::setPermissions(bool read, bool write, bool execute) { if (HANDLE ph = OpenProcess(PROCESS_VM_OPERATION, FALSE, edb::v1::debugger_core->process()->pid())) { DWORD prot = PAGE_NOACCESS; switch ((static_cast(read) << 2) | (static_cast(write) << 1) | (static_cast(execute) << 0)) { case 0x0: prot = PAGE_NOACCESS; break; case 0x1: prot = PAGE_EXECUTE; break; case 0x2: prot = PAGE_WRITECOPY; break; case 0x3: prot = PAGE_EXECUTE_WRITECOPY; break; case 0x4: prot = PAGE_READONLY; break; case 0x5: prot = PAGE_EXECUTE_READ; break; case 0x6: prot = PAGE_READWRITE; break; case 0x7: prot = PAGE_EXECUTE_READWRITE; break; } prot |= permissions_ & ~KnownPermissions; // keep modifiers DWORD prev_prot; if (VirtualProtectEx(ph, reinterpret_cast(start().toUint()), size(), prot, &prev_prot)) { permissions_ = prot; } CloseHandle(ph); } } /** * @brief PlatformRegion::start * @return */ edb::address_t PlatformRegion::start() const { return start_; } /** * @brief PlatformRegion::end * @return */ edb::address_t PlatformRegion::end() const { return end_; } /** * @brief PlatformRegion::base * @return */ edb::address_t PlatformRegion::base() const { return base_; } /** * @brief PlatformRegion::name * @return */ QString PlatformRegion::name() const { return name_; } /** * @brief PlatformRegion::permissions * @return */ IRegion::permissions_t PlatformRegion::permissions() const { return permissions_; } /** * @brief PlatformRegion::setStart * @param address */ void PlatformRegion::setStart(edb::address_t address) { start_ = address; } /** * @brief PlatformRegion::setEnd * @param address */ void PlatformRegion::setEnd(edb::address_t address) { end_ = address; } } edb-debugger/plugins/DebuggerCore/win32/PlatformProcess.cpp0000644000175000017500000003523513765535463023550 0ustar eteraneteran/* Copyright (C) 2015 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "PlatformProcess.h" #include "PlatformRegion.h" #include "PlatformThread.h" #include "edb.h" #include namespace DebuggerCorePlugin { namespace { #pragma pack(push) #pragma pack(1) template struct LIST_ENTRY_T { T Flink; T Blink; }; template struct UNICODE_STRING_T { union { struct { WORD Length; WORD MaximumLength; }; T dummy; }; T _Buffer; }; template struct _PEB_T { union { struct { BYTE InheritedAddressSpace; BYTE ReadImageFileExecOptions; BYTE BeingDebugged; BYTE _SYSTEM_DEPENDENT_01; }; T dummy01; }; T Mutant; T ImageBaseAddress; T Ldr; T ProcessParameters; T SubSystemData; T ProcessHeap; T FastPebLock; T _SYSTEM_DEPENDENT_02; T _SYSTEM_DEPENDENT_03; T _SYSTEM_DEPENDENT_04; union { T KernelCallbackTable; T UserSharedInfoPtr; }; DWORD SystemReserved; DWORD _SYSTEM_DEPENDENT_05; T _SYSTEM_DEPENDENT_06; T TlsExpansionCounter; T TlsBitmap; DWORD TlsBitmapBits[2]; T ReadOnlySharedMemoryBase; T _SYSTEM_DEPENDENT_07; T ReadOnlyStaticServerData; T AnsiCodePageData; T OemCodePageData; T UnicodeCaseTableData; DWORD NumberOfProcessors; union { DWORD NtGlobalFlag; NGF dummy02; }; LARGE_INTEGER CriticalSectionTimeout; T HeapSegmentReserve; T HeapSegmentCommit; T HeapDeCommitTotalFreeThreshold; T HeapDeCommitFreeBlockThreshold; DWORD NumberOfHeaps; DWORD MaximumNumberOfHeaps; T ProcessHeaps; T GdiSharedHandleTable; T ProcessStarterHelper; T GdiDCAttributeList; T LoaderLock; DWORD OSMajorVersion; DWORD OSMinorVersion; WORD OSBuildNumber; WORD OSCSDVersion; DWORD OSPlatformId; DWORD ImageSubsystem; DWORD ImageSubsystemMajorVersion; T ImageSubsystemMinorVersion; union { T ImageProcessAffinityMask; T ActiveProcessAffinityMask; }; T GdiHandleBuffer[A]; T PostProcessInitRoutine; T TlsExpansionBitmap; DWORD TlsExpansionBitmapBits[32]; T SessionId; ULARGE_INTEGER AppCompatFlags; ULARGE_INTEGER AppCompatFlagsUser; T pShimData; T AppCompatInfo; UNICODE_STRING_T CSDVersion; T ActivationContextData; T ProcessAssemblyStorageMap; T SystemDefaultActivationContextData; T SystemAssemblyStorageMap; T MinimumStackCommit; }; #pragma pack(pop) typedef _PEB_T PEB32; typedef _PEB_T PEB64; typedef struct _PROCESS_BASIC_INFORMATION { PVOID Reserved1; PVOID PebBaseAddress; PVOID Reserved2[2]; ULONG_PTR UniqueProcessId; PVOID Reserved3; } PROCESS_BASIC_INFORMATION; typedef enum _PROCESSINFOCLASS { ProcessBasicInformation = 0, ProcessDebugPort = 7, ProcessWow64Information = 26, ProcessImageFileName = 27 } PROCESSINFOCLASS; bool getProcessEntry(edb::pid_t pid, PROCESSENTRY32 *entry) { bool ret = false; if (HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)) { PROCESSENTRY32 peInfo; peInfo.dwSize = sizeof(peInfo); // this line is REQUIRED if (Process32First(hSnapshot, &peInfo)) { do { if (peInfo.th32ProcessID == pid) { *entry = peInfo; ret = true; break; } } while (Process32Next(hSnapshot, &peInfo)); } CloseHandle(hSnapshot); } return ret; } } /** * @brief PlatformProcess::PlatformProcess * @param core * @param pid */ PlatformProcess::PlatformProcess(DebuggerCore *core, edb::pid_t pid) : core_(core) { hProcess_ = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); } /** * @brief PlatformProcess::PlatformProcess * @param core * @param handle */ PlatformProcess::PlatformProcess(DebuggerCore *core, HANDLE handle) : core_(core) { DuplicateHandle( GetCurrentProcess(), handle, GetCurrentProcess(), &hProcess_, 0, FALSE, DUPLICATE_SAME_ACCESS); } /** * @brief PlatformProcess::~PlatformProcess */ PlatformProcess::~PlatformProcess() { CloseHandle(hProcess_); } /** * @brief PlatformProcess::isWow64 * @return */ bool PlatformProcess::isWow64() const { #if defined(EDB_X86_64) BOOL wow64 = FALSE; using LPFN_ISWOW64PROCESS = BOOL(WINAPI *)(HANDLE, PBOOL); static auto fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process"); if (fnIsWow64Process && fnIsWow64Process(hProcess_, &wow64)) { return wow64; } #endif return false; } /** * @brief PlatformProcess::startTime * @return */ QDateTime PlatformProcess::startTime() const { Q_ASSERT(hProcess_); FILETIME create; FILETIME exit; FILETIME kernel; FILETIME user; if (GetProcessTimes(hProcess_, &create, &exit, &kernel, &user)) { ULARGE_INTEGER createTime; createTime.LowPart = create.dwLowDateTime; createTime.HighPart = create.dwHighDateTime; return QDateTime::fromMSecsSinceEpoch(createTime.QuadPart / 10000); } return QDateTime(); } /** * @brief PlatformProcess::pid * @return */ edb::pid_t PlatformProcess::pid() const { return GetProcessId(hProcess_); } /** * @brief PlatformProcess::name * @return */ QString PlatformProcess::name() const { PROCESSENTRY32 pEntry = {}; getProcessEntry(pid(), &pEntry); QString name = QString::fromWCharArray(pEntry.szExeFile); if (isWow64()) { name += " *32"; } return name; } /** * @brief PlatformProcess::user * @return */ QString PlatformProcess::user() const { QString user; HANDLE hToken; if (OpenProcessToken(hProcess_, TOKEN_QUERY, &hToken)) { DWORD needed; GetTokenInformation(hToken, TokenOwner, nullptr, 0, &needed); if (auto owner = static_cast(std::malloc(needed))) { if (GetTokenInformation(hToken, TokenOwner, owner, needed, &needed)) { WCHAR user_buf[MAX_PATH]; WCHAR domain[MAX_PATH]; DWORD user_sz = MAX_PATH; DWORD domain_sz = MAX_PATH; SID_NAME_USE snu; if (LookupAccountSid(nullptr, owner->Owner, user_buf, &user_sz, domain, &domain_sz, &snu) && snu == SidTypeUser) { user = QString::fromWCharArray(user_buf); } } std::free(owner); } CloseHandle(hToken); } return user; } /** * @brief PlatformProcess::parent * @return */ std::shared_ptr PlatformProcess::parent() const { edb::pid_t parent_pid = core_->parentPid(pid()); return std::make_shared(core_, parent_pid); } /** * @brief PlatformProcess::uid * @return */ edb::uid_t PlatformProcess::uid() const { Q_ASSERT(hProcess_); HANDLE token; TOKEN_USER user; DWORD length; // TODO(eteran): is this on the right track? if (OpenProcessToken(hProcess_, 0, &token)) { if (GetTokenInformation(token, TokenUser, &user, sizeof(user), &length)) { } } return 0; } /** * @brief PlatformProcess::patches * @return */ QMap PlatformProcess::patches() const { return patches_; } /** * @brief PlatformProcess::write_bytes * @param address * @param buf * @param len * @return */ std::size_t PlatformProcess::writeBytes(edb::address_t address, const void *buf, size_t len) { Q_ASSERT(buf); if (hProcess_) { if (len == 0) { return 0; } SIZE_T bytes_written = 0; if (WriteProcessMemory(hProcess_, reinterpret_cast(address.toUint()), buf, len, &bytes_written)) { return bytes_written; } } return 0; } /** * @brief PlatformProcess::readBytes * @param address * @param buf * @param len * @return */ std::size_t PlatformProcess::readBytes(edb::address_t address, void *buf, size_t len) const { Q_ASSERT(buf); if (hProcess_) { if (len == 0) { return 0; } memset(buf, 0xff, len); SIZE_T bytes_read = 0; if (ReadProcessMemory(hProcess_, reinterpret_cast(address.toUint()), buf, len, &bytes_read)) { // TODO(eteran): implement breakpoint stuff #if 0 for(const std::shared_ptr &bp: breakpoints_) { if(bp->address() >= address && bp->address() < address + bytes_read) { reinterpret_cast(buf)[bp->address() - address] = bp->original_bytes()[0]; } } #endif return bytes_read; } } return 0; } /** * @brief PlatformProcess::readPages * @param address * @param buf * @param count * @return */ std::size_t PlatformProcess::readPages(edb::address_t address, void *buf, size_t count) const { Q_ASSERT(address % core_->pageSize() == 0); return readBytes(address, buf, core_->pageSize() * count); } /** * @brief PlatformProcess::pause * @return */ Status PlatformProcess::pause() { Q_ASSERT(hProcess_); if (DebugBreakProcess(hProcess_)) { return Status::Ok; } // TODO(eteran): use GetLastError/FormatMessage return Status("Failed to pause"); } /** * @brief PlatformProcess::regions * @return */ QList> PlatformProcess::regions() const { QList> regions; if (hProcess_) { edb::address_t addr = 0; auto last_base = reinterpret_cast(-1); Q_FOREVER { MEMORY_BASIC_INFORMATION info; VirtualQueryEx(hProcess_, reinterpret_cast(addr.toUint()), &info, sizeof(info)); if (last_base == info.BaseAddress) { break; } last_base = info.BaseAddress; if (info.State == MEM_COMMIT) { const auto start = edb::address_t::fromZeroExtended(info.BaseAddress); const auto end = edb::address_t::fromZeroExtended(info.BaseAddress) + info.RegionSize; const auto base = edb::address_t::fromZeroExtended(info.AllocationBase); const QString name = QString(); const IRegion::permissions_t permissions = info.Protect; // let std::shared_ptr handle permissions and modifiers if (info.Type == MEM_IMAGE) { // set region.name to the module name } // get stack addresses, PEB, TEB, etc. and set name accordingly regions.push_back(std::make_shared(start, end, base, name, permissions)); } addr += info.RegionSize; } } return regions; } /** * @brief PlatformProcess::executable * @return */ QString PlatformProcess::executable() const { Q_ASSERT(hProcess_); // These functions don't work immediately after CreateProcess but only // after basic initialization, usually after the system breakpoint // The same applies to psapi/toolhelp, maybe using NtQueryXxxxxx is the way to go using QueryFullProcessImageNameWPtr = BOOL(WINAPI *)( HANDLE hProcess, DWORD dwFlags, LPWSTR lpExeName, PDWORD lpdwSize); HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll"); auto QueryFullProcessImageNameWFunc = (QueryFullProcessImageNameWPtr)GetProcAddress(kernel32, "QueryFullProcessImageNameW"); wchar_t name[MAX_PATH] = L""; if (QueryFullProcessImageNameWFunc /* && LOBYTE(GetVersion()) >= 6*/) { // Vista and up DWORD size = _countof(name); if (QueryFullProcessImageNameWFunc(hProcess_, 0, name, &size)) { return QString::fromWCharArray(name); } } return {}; } /** * @brief PlatformProcess::arguments * @return */ QList PlatformProcess::arguments() const { QList ret; if (hProcess_) { using ZwQueryInformationProcessPtr = NTSTATUS (*WINAPI)( HANDLE ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength, PULONG ReturnLength); HMODULE ntdll = GetModuleHandleW(L"ntdll.dll"); auto ZwQueryInformationProcessFunc = (ZwQueryInformationProcessPtr)GetProcAddress(ntdll, "NtQueryInformationProcess"); PROCESS_BASIC_INFORMATION ProcessInfo; if (ZwQueryInformationProcessFunc) { ULONG l; NTSTATUS r = ZwQueryInformationProcessFunc(hProcess_, ProcessBasicInformation, &ProcessInfo, sizeof(PROCESS_BASIC_INFORMATION), &l); printf("TODO(eteran): implement this\n"); } } return ret; } /** * @brief loadedModules * @return */ QList PlatformProcess::loadedModules() const { QList ret; HANDLE hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid()); if (hModuleSnap != INVALID_HANDLE_VALUE) { MODULEENTRY32 me32; me32.dwSize = sizeof(me32); if (Module32First(hModuleSnap, &me32)) { do { Module module; module.baseAddress = edb::address_t::fromZeroExtended(me32.modBaseAddr); module.name = QString::fromWCharArray(me32.szModule); ret.push_back(module); } while (Module32Next(hModuleSnap, &me32)); } } CloseHandle(hModuleSnap); return ret; } /** * @brief PlatformProcess::threads * @return */ QList> PlatformProcess::threads() const { Q_ASSERT(core_->process_.get() == this); QList> threadList; for (auto &thread : core_->threads_) { threadList.push_back(thread); } return threadList; } /** * @brief PlatformProcess::currentThread * @return */ std::shared_ptr PlatformProcess::currentThread() const { Q_ASSERT(core_->process_.get() == this); auto it = core_->threads_.find(core_->activeThread_); if (it != core_->threads_.end()) { return it.value(); } return nullptr; } /** * @brief PlatformProcess::setCurrentThread * @param thread */ void PlatformProcess::setCurrentThread(IThread &thread) { core_->activeThread_ = static_cast(&thread)->tid(); edb::v1::update_ui(); } Status PlatformProcess::step(edb::EventStatus status) { // TODO: assert that we are paused Q_ASSERT(core_->process_.get() == this); if (status != edb::DEBUG_STOP) { if (std::shared_ptr thread = currentThread()) { return thread->step(status); } } return Status::Ok; } bool PlatformProcess::isPaused() const { for (auto &thread : threads()) { if (!thread->isPaused()) { return false; } } return true; } /** * @brief PlatformProcess::resume * @param status * @return */ Status PlatformProcess::resume(edb::EventStatus status) { int ret; switch (status) { case edb::EventStatus::DEBUG_CONTINUE: ret = ContinueDebugEvent(lastEvent_.dwProcessId, lastEvent_.dwThreadId, DBG_CONTINUE); break; case edb::EventStatus::DEBUG_EXCEPTION_NOT_HANDLED: ret = ContinueDebugEvent(lastEvent_.dwProcessId, lastEvent_.dwThreadId, DBG_EXCEPTION_NOT_HANDLED); break; case edb::EventStatus::DEBUG_NEXT_HANDLER: case edb::EventStatus::DEBUG_CONTINUE_BP: case edb::EventStatus::DEBUG_CONTINUE_STEP: case edb::EventStatus::DEBUG_STOP: default: break; } if (ret) { return Status::Ok; } return Status(QObject::tr("Error Continuing: %1").arg(GetLastError())); } } edb-debugger/plugins/DebuggerCore/win32/PlatformProcess.h0000644000175000017500000000711513765535463023211 0ustar eteraneteran/* Copyright (C) 2015 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PLATFORM_PROCESS_H_20150517_ #define PLATFORM_PROCESS_H_20150517_ #include "DebuggerCore.h" #include "IProcess.h" #include "Module.h" #include #include namespace DebuggerCorePlugin { class DebuggerCore; class PlatformProcess : public IProcess { friend class DebuggerCore; friend class PlatformThread; public: PlatformProcess(DebuggerCore *core, edb::pid_t pid); PlatformProcess(DebuggerCore *core, HANDLE handle); ~PlatformProcess() override; public: // legal to call when not attached QDateTime startTime() const override; QList arguments() const override; QString stardardInput() const override { qDebug("TODO: implement PlatformProcess::stardardInput"); return QString(); } QString stardardOutput() const override { qDebug("TODO: implement PlatformProcess::currentWorkingDirectory"); return QString(); } QString currentWorkingDirectory() const override { qDebug("TODO: implement PlatformProcess::currentWorkingDirectory"); return QString(); } QString executable() const override; edb::address_t entryPoint() const override { qDebug("TODO: implement PlatformProcess::entryPoint"); return edb::address_t(); return 0; } edb::pid_t pid() const override; std::shared_ptr parent() const override; edb::address_t codeAddress() const override { qDebug("TODO: implement PlatformProcess::codeAddress"); return edb::address_t(); } edb::address_t dataAddress() const override { qDebug("TODO: implement PlatformProcess::dataAddress"); return edb::address_t(); } QList> regions() const override; edb::uid_t uid() const override; QString user() const override; QString name() const override; QList loadedModules() const override; public: // only legal to call when attached QList> threads() const override; std::shared_ptr currentThread() const override; void setCurrentThread(IThread &thread) override; Status pause() override; std::size_t writeBytes(edb::address_t address, const void *buf, size_t len) override; std::size_t readBytes(edb::address_t address, void *buf, size_t len) const override; std::size_t readPages(edb::address_t address, void *buf, size_t count) const override; std::size_t patchBytes(edb::address_t address, const void *buf, size_t len) override { Q_UNUSED(address) Q_UNUSED(buf) Q_UNUSED(len) qDebug("TODO: implement PlatformProcess::patchBytes"); return 0; } Status resume(edb::EventStatus status) override; Status step(edb::EventStatus status) override; bool isPaused() const override; QMap patches() const override; private: bool isWow64() const; private: edb::address_t startAddress_ = 0; edb::address_t imageBase_ = 0; DebuggerCore *core_ = nullptr; HANDLE hProcess_ = nullptr; QMap patches_; DEBUG_EVENT lastEvent_ = {}; }; } #endif edb-debugger/plugins/DebuggerCore/win32/PlatformThread.h0000644000175000017500000000336613765535463023006 0ustar eteraneteran/* Copyright (C) 2015 - 2018 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PLATFORM_THREAD_H_20191119_ #define PLATFORM_THREAD_H_20191119_ #include "DebuggerCore.h" #include "IProcess.h" #include "IThread.h" #include #include namespace DebuggerCorePlugin { class PlatformThread : public IThread { Q_DECLARE_TR_FUNCTIONS(PlatformThread) public: PlatformThread(DebuggerCore *core, std::shared_ptr &process, const CREATE_THREAD_DEBUG_INFO *info); ~PlatformThread() override = default; public: edb::tid_t tid() const override; QString name() const override; int priority() const override; edb::address_t instructionPointer() const override; QString runState() const override; public: void getState(State *state) override; void setState(const State &state) override; public: Status step() override; Status step(edb::EventStatus status) override; Status resume() override; Status resume(edb::EventStatus status) override; public: bool isPaused() const override; private: DebuggerCore *core_ = nullptr; std::shared_ptr process_; HANDLE hThread_ = nullptr; bool isWow64_ = false; }; } #endif edb-debugger/plugins/DebuggerCore/win32/PlatformEvent.h0000644000175000017500000000303513765535463022651 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PLATFORM_EVENT_H_20121005_ #define PLATFORM_EVENT_H_20121005_ #include "IDebugEvent.h" #include namespace DebuggerCorePlugin { class PlatformEvent : public IDebugEvent { Q_DECLARE_TR_FUNCTIONS(PlatformEvent) friend class DebuggerCore; public: PlatformEvent() = default; public: PlatformEvent *clone() const override; public: Message errorDescription() const override; REASON reason() const override; TRAP_REASON trapReason() const override; bool exited() const override; bool isError() const override; bool isKill() const override; bool isStop() const override; bool isTrap() const override; bool terminated() const override; bool stopped() const override; edb::pid_t process() const override; edb::tid_t thread() const override; int64_t code() const override; private: DEBUG_EVENT event_ = {}; }; } #endif edb-debugger/plugins/DebuggerCore/win32/PlatformState.cpp0000644000175000017500000007564213765535463023220 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "PlatformState.h" #include "edb.h" #include "string_hash.h" #include #include namespace DebuggerCorePlugin { namespace { /** * @brief read_float80 * @param buffer * @return */ double read_float80(const uint8_t buffer[10]) { // little-endian! //80 bit floating point value according to IEEE-754: //1 bit sign, 15 bit exponent, 64 bit mantissa constexpr uint16_t SIGNBIT = (1 << 15); constexpr uint16_t EXP_BIAS = (1 << 14) - 1; // 2^(n-1) - 1 = 16383 constexpr uint16_t SPECIALEXP = (1 << 15) - 1; // all bits set constexpr uint64_t HIGHBIT = 1ull << 63; constexpr uint64_t QUIETBIT = 1ull << 62; // Extract sign, exponent and mantissa auto exponent = *reinterpret_cast(&buffer[8]); const auto mantissa = *reinterpret_cast(&buffer[0]); const double sign = (exponent & SIGNBIT) ? -1.0 : 1.0; exponent &= ~SIGNBIT; // Check for undefined values if ((!exponent && (mantissa & HIGHBIT)) || (exponent && !(mantissa & HIGHBIT))) { return std::numeric_limits::quiet_NaN(); } // Check for special values (infinity, NaN) if (exponent == 0) { if (mantissa == 0) { return sign * 0.0; } else { // denormalized } } else if (exponent == SPECIALEXP) { if (!(mantissa & ~HIGHBIT)) { return sign * std::numeric_limits::infinity(); } else { if (mantissa & QUIETBIT) { return std::numeric_limits::quiet_NaN(); } else { return std::numeric_limits::signaling_NaN(); } } } //value = (-1)^s * (m / 2^63) * 2^(e - 16383) double significand = (static_cast(mantissa) / (1ull << 63)); return sign * ldexp(significand, exponent - EXP_BIAS); } } /** * @brief PlatformState::clone * @return a copy of the state object */ std::unique_ptr PlatformState::clone() const { return std::make_unique(*this); } /** * @brief PlatformState::flagsToString * @param flags * @return the flags in a string form appropriate for this platform */ QString PlatformState::flagsToString(edb::reg_t flags) const { char buf[14]; qsnprintf( buf, sizeof(buf), "%c %c %c %c %c %c %c", ((flags & 0x001) ? 'C' : 'c'), ((flags & 0x004) ? 'P' : 'p'), ((flags & 0x010) ? 'A' : 'a'), ((flags & 0x040) ? 'Z' : 'z'), ((flags & 0x080) ? 'S' : 's'), ((flags & 0x400) ? 'D' : 'd'), ((flags & 0x800) ? 'O' : 'o')); return buf; } /** * @brief PlatformState::flagsToString * @return the flags in a string form appropriate for this platform */ QString PlatformState::flagsToString() const { return flagsToString(flags()); } /** * @brief PlatformState::value * @param reg * @return a Register object which represents the register with the name supplied */ Register PlatformState::value(const QString ®) const { const QString lreg = reg.toLower(); #if defined(EDB_X86) if (lreg == "eax") return make_Register("eax", context32_.Eax, Register::TYPE_GPR); else if (lreg == "ebx") return make_Register("ebx", context32_.Ebx, Register::TYPE_GPR); else if (lreg == "ecx") return make_Register("ecx", context32_.Ecx, Register::TYPE_GPR); else if (lreg == "edx") return make_Register("edx", context32_.Edx, Register::TYPE_GPR); else if (lreg == "ebp") return make_Register("ebp", context32_.Ebp, Register::TYPE_GPR); else if (lreg == "esp") return make_Register("esp", context32_.Esp, Register::TYPE_GPR); else if (lreg == "esi") return make_Register("esi", context32_.Esi, Register::TYPE_GPR); else if (lreg == "edi") return make_Register("edi", context32_.Edi, Register::TYPE_GPR); else if (lreg == "eip") return make_Register("eip", context32_.Eip, Register::TYPE_IP); else if (lreg == "ax") return make_Register("ax", context32_.Eax & 0xffff, Register::TYPE_GPR); else if (lreg == "bx") return make_Register("bx", context32_.Ebx & 0xffff, Register::TYPE_GPR); else if (lreg == "cx") return make_Register("cx", context32_.Ecx & 0xffff, Register::TYPE_GPR); else if (lreg == "dx") return make_Register("dx", context32_.Edx & 0xffff, Register::TYPE_GPR); else if (lreg == "bp") return make_Register("bp", context32_.Ebp & 0xffff, Register::TYPE_GPR); else if (lreg == "sp") return make_Register("sp", context32_.Esp & 0xffff, Register::TYPE_GPR); else if (lreg == "si") return make_Register("si", context32_.Esi & 0xffff, Register::TYPE_GPR); else if (lreg == "di") return make_Register("di", context32_.Edi & 0xffff, Register::TYPE_GPR); else if (lreg == "al") return make_Register("al", context32_.Eax & 0xff, Register::TYPE_GPR); else if (lreg == "bl") return make_Register("bl", context32_.Ebx & 0xff, Register::TYPE_GPR); else if (lreg == "cl") return make_Register("cl", context32_.Ecx & 0xff, Register::TYPE_GPR); else if (lreg == "dl") return make_Register("dl", context32_.Edx & 0xff, Register::TYPE_GPR); else if (lreg == "ah") return make_Register("ah", (context32_.Eax >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "bh") return make_Register("bh", (context32_.Ebx >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "ch") return make_Register("ch", (context32_.Ecx >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "dh") return make_Register("dh", (context32_.Edx >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "cs") return make_Register("cs", context32_.SegCs, Register::TYPE_SEG); else if (lreg == "ds") return make_Register("ds", context32_.SegDs, Register::TYPE_SEG); else if (lreg == "es") return make_Register("es", context32_.SegEs, Register::TYPE_SEG); else if (lreg == "fs") return make_Register("fs", context32_.SegFs, Register::TYPE_SEG); else if (lreg == "gs") return make_Register("gs", context32_.SegGs, Register::TYPE_SEG); else if (lreg == "ss") return make_Register("ss", context32_.SegSs, Register::TYPE_SEG); else if (lreg == "fs_base") return make_Register("fs_base", fs_base_, Register::TYPE_SEG); else if (lreg == "gs_base") return make_Register("gs_base", gs_base_, Register::TYPE_SEG); else if (lreg == "eflags") return make_Register("eflags", context32_.EFlags, Register::TYPE_COND); #elif defined(EDB_X86_64) if (!isWow64_) { if (lreg == "rax") return make_Register("rax", context64_.Rax, Register::TYPE_GPR); else if (lreg == "rbx") return make_Register("rbx", context64_.Rbx, Register::TYPE_GPR); else if (lreg == "rcx") return make_Register("rcx", context64_.Rcx, Register::TYPE_GPR); else if (lreg == "rdx") return make_Register("rdx", context64_.Rdx, Register::TYPE_GPR); else if (lreg == "rbp") return make_Register("rbp", context64_.Rbp, Register::TYPE_GPR); else if (lreg == "rsp") return make_Register("rsp", context64_.Rsp, Register::TYPE_GPR); else if (lreg == "rsi") return make_Register("rsi", context64_.Rsi, Register::TYPE_GPR); else if (lreg == "rdi") return make_Register("rdi", context64_.Rdi, Register::TYPE_GPR); else if (lreg == "rip") return make_Register("rip", context64_.Rip, Register::TYPE_IP); else if (lreg == "r8") return make_Register("r8", context64_.R8, Register::TYPE_GPR); else if (lreg == "r9") return make_Register("r9", context64_.R9, Register::TYPE_GPR); else if (lreg == "r10") return make_Register("r10", context64_.R10, Register::TYPE_GPR); else if (lreg == "r11") return make_Register("r11", context64_.R11, Register::TYPE_GPR); else if (lreg == "r12") return make_Register("r12", context64_.R12, Register::TYPE_GPR); else if (lreg == "r13") return make_Register("r13", context64_.R13, Register::TYPE_GPR); else if (lreg == "r14") return make_Register("r14", context64_.R14, Register::TYPE_GPR); else if (lreg == "r15") return make_Register("r15", context64_.R15, Register::TYPE_GPR); else if (lreg == "eax") return make_Register("eax", context64_.Rax & 0xffffffff, Register::TYPE_GPR); else if (lreg == "ebx") return make_Register("ebx", context64_.Rbx & 0xffffffff, Register::TYPE_GPR); else if (lreg == "ecx") return make_Register("ecx", context64_.Rcx & 0xffffffff, Register::TYPE_GPR); else if (lreg == "edx") return make_Register("edx", context64_.Rdx & 0xffffffff, Register::TYPE_GPR); else if (lreg == "ebp") return make_Register("ebp", context64_.Rbp & 0xffffffff, Register::TYPE_GPR); else if (lreg == "esp") return make_Register("esp", context64_.Rsp & 0xffffffff, Register::TYPE_GPR); else if (lreg == "esi") return make_Register("esi", context64_.Rsi & 0xffffffff, Register::TYPE_GPR); else if (lreg == "edi") return make_Register("edi", context64_.Rdi & 0xffffffff, Register::TYPE_GPR); else if (lreg == "r8d") return make_Register("r8d", context64_.R8 & 0xffffffff, Register::TYPE_GPR); else if (lreg == "r9d") return make_Register("r9d", context64_.R9 & 0xffffffff, Register::TYPE_GPR); else if (lreg == "r10d") return make_Register("r10d", context64_.R10 & 0xffffffff, Register::TYPE_GPR); else if (lreg == "r11d") return make_Register("r11d", context64_.R11 & 0xffffffff, Register::TYPE_GPR); else if (lreg == "r12d") return make_Register("r12d", context64_.R12 & 0xffffffff, Register::TYPE_GPR); else if (lreg == "r13d") return make_Register("r13d", context64_.R13 & 0xffffffff, Register::TYPE_GPR); else if (lreg == "r14d") return make_Register("r14d", context64_.R14 & 0xffffffff, Register::TYPE_GPR); else if (lreg == "r15d") return make_Register("r15d", context64_.R15 & 0xffffffff, Register::TYPE_GPR); else if (lreg == "ax") return make_Register("ax", context64_.Rax & 0xffff, Register::TYPE_GPR); else if (lreg == "bx") return make_Register("bx", context64_.Rbx & 0xffff, Register::TYPE_GPR); else if (lreg == "cx") return make_Register("cx", context64_.Rcx & 0xffff, Register::TYPE_GPR); else if (lreg == "dx") return make_Register("dx", context64_.Rdx & 0xffff, Register::TYPE_GPR); else if (lreg == "bp") return make_Register("bp", context64_.Rbp & 0xffff, Register::TYPE_GPR); else if (lreg == "sp") return make_Register("sp", context64_.Rsp & 0xffff, Register::TYPE_GPR); else if (lreg == "si") return make_Register("si", context64_.Rsi & 0xffff, Register::TYPE_GPR); else if (lreg == "di") return make_Register("di", context64_.Rdi & 0xffff, Register::TYPE_GPR); else if (lreg == "r8w") return make_Register("r8w", context64_.R8 & 0xffff, Register::TYPE_GPR); else if (lreg == "r9w") return make_Register("r9w", context64_.R9 & 0xffff, Register::TYPE_GPR); else if (lreg == "r10w") return make_Register("r10w", context64_.R10 & 0xffff, Register::TYPE_GPR); else if (lreg == "r11w") return make_Register("r11w", context64_.R11 & 0xffff, Register::TYPE_GPR); else if (lreg == "r12w") return make_Register("r12w", context64_.R12 & 0xffff, Register::TYPE_GPR); else if (lreg == "r13w") return make_Register("r13w", context64_.R13 & 0xffff, Register::TYPE_GPR); else if (lreg == "r14w") return make_Register("r14w", context64_.R14 & 0xffff, Register::TYPE_GPR); else if (lreg == "r15w") return make_Register("r15w", context64_.R15 & 0xffff, Register::TYPE_GPR); else if (lreg == "al") return make_Register("al", context64_.Rax & 0xff, Register::TYPE_GPR); else if (lreg == "bl") return make_Register("bl", context64_.Rbx & 0xff, Register::TYPE_GPR); else if (lreg == "cl") return make_Register("cl", context64_.Rcx & 0xff, Register::TYPE_GPR); else if (lreg == "dl") return make_Register("dl", context64_.Rdx & 0xff, Register::TYPE_GPR); else if (lreg == "ah") return make_Register("ah", (context64_.Rax >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "bh") return make_Register("bh", (context64_.Rbx >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "ch") return make_Register("ch", (context64_.Rcx >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "dh") return make_Register("dh", (context64_.Rdx >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "spl") return make_Register("spl", (context64_.Rsp >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "bpl") return make_Register("bpl", (context64_.Rbp >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "sil") return make_Register("sil", (context64_.Rsi >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "dil") return make_Register("dil", (context64_.Rdi >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "r8b") return make_Register("r8b", context64_.R8 & 0xff, Register::TYPE_GPR); else if (lreg == "r9b") return make_Register("r9b", context64_.R9 & 0xff, Register::TYPE_GPR); else if (lreg == "r10b") return make_Register("r10b", context64_.R10 & 0xff, Register::TYPE_GPR); else if (lreg == "r11b") return make_Register("r11b", context64_.R11 & 0xff, Register::TYPE_GPR); else if (lreg == "r12b") return make_Register("r12b", context64_.R12 & 0xff, Register::TYPE_GPR); else if (lreg == "r13b") return make_Register("r13b", context64_.R13 & 0xff, Register::TYPE_GPR); else if (lreg == "r14b") return make_Register("r14b", context64_.R14 & 0xff, Register::TYPE_GPR); else if (lreg == "r15b") return make_Register("r15b", context64_.R15 & 0xff, Register::TYPE_GPR); else if (lreg == "cs") return make_Register("cs", context64_.SegCs, Register::TYPE_SEG); else if (lreg == "ds") return make_Register("ds", context64_.SegDs, Register::TYPE_SEG); else if (lreg == "es") return make_Register("es", context64_.SegEs, Register::TYPE_SEG); else if (lreg == "fs") return make_Register("fs", context64_.SegFs, Register::TYPE_SEG); else if (lreg == "gs") return make_Register("gs", context64_.SegGs, Register::TYPE_SEG); else if (lreg == "ss") return make_Register("ss", context64_.SegSs, Register::TYPE_SEG); else if (lreg == "fs_base") return make_Register("fs_base", fs_base_, Register::TYPE_SEG); else if (lreg == "gs_base") return make_Register("gs_base", gs_base_, Register::TYPE_SEG); else if (lreg == "rflags") return make_Register("rflags", context64_.EFlags, Register::TYPE_COND); } else { if (lreg == "eax") return make_Register("eax", context32_.Eax, Register::TYPE_GPR); else if (lreg == "ebx") return make_Register("ebx", context32_.Ebx, Register::TYPE_GPR); else if (lreg == "ecx") return make_Register("ecx", context32_.Ecx, Register::TYPE_GPR); else if (lreg == "edx") return make_Register("edx", context32_.Edx, Register::TYPE_GPR); else if (lreg == "ebp") return make_Register("ebp", context32_.Ebp, Register::TYPE_GPR); else if (lreg == "esp") return make_Register("esp", context32_.Esp, Register::TYPE_GPR); else if (lreg == "esi") return make_Register("esi", context32_.Esi, Register::TYPE_GPR); else if (lreg == "edi") return make_Register("edi", context32_.Edi, Register::TYPE_GPR); else if (lreg == "eip") return make_Register("eip", context32_.Eip, Register::TYPE_IP); else if (lreg == "ax") return make_Register("ax", context32_.Eax & 0xffff, Register::TYPE_GPR); else if (lreg == "bx") return make_Register("bx", context32_.Ebx & 0xffff, Register::TYPE_GPR); else if (lreg == "cx") return make_Register("cx", context32_.Ecx & 0xffff, Register::TYPE_GPR); else if (lreg == "dx") return make_Register("dx", context32_.Edx & 0xffff, Register::TYPE_GPR); else if (lreg == "bp") return make_Register("bp", context32_.Ebp & 0xffff, Register::TYPE_GPR); else if (lreg == "sp") return make_Register("sp", context32_.Esp & 0xffff, Register::TYPE_GPR); else if (lreg == "si") return make_Register("si", context32_.Esi & 0xffff, Register::TYPE_GPR); else if (lreg == "di") return make_Register("di", context32_.Edi & 0xffff, Register::TYPE_GPR); else if (lreg == "al") return make_Register("al", context32_.Eax & 0xff, Register::TYPE_GPR); else if (lreg == "bl") return make_Register("bl", context32_.Ebx & 0xff, Register::TYPE_GPR); else if (lreg == "cl") return make_Register("cl", context32_.Ecx & 0xff, Register::TYPE_GPR); else if (lreg == "dl") return make_Register("dl", context32_.Edx & 0xff, Register::TYPE_GPR); else if (lreg == "ah") return make_Register("ah", (context32_.Eax >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "bh") return make_Register("bh", (context32_.Ebx >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "ch") return make_Register("ch", (context32_.Ecx >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "dh") return make_Register("dh", (context32_.Edx >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "cs") return make_Register("cs", context32_.SegCs, Register::TYPE_SEG); else if (lreg == "ds") return make_Register("ds", context32_.SegDs, Register::TYPE_SEG); else if (lreg == "es") return make_Register("es", context32_.SegEs, Register::TYPE_SEG); else if (lreg == "fs") return make_Register("fs", context32_.SegFs, Register::TYPE_SEG); else if (lreg == "gs") return make_Register("gs", context32_.SegGs, Register::TYPE_SEG); else if (lreg == "ss") return make_Register("ss", context32_.SegSs, Register::TYPE_SEG); else if (lreg == "fs_base") return make_Register("fs_base", fs_base_, Register::TYPE_SEG); else if (lreg == "gs_base") return make_Register("gs_base", gs_base_, Register::TYPE_SEG); else if (lreg == "eflags") return make_Register("eflags", context32_.EFlags, Register::TYPE_COND); } #endif return Register(); } /** * @brief PlatformState::framePointer * @return what is conceptually the frame pointer for this platform */ edb::address_t PlatformState::framePointer() const { #if defined(EDB_X86) return context32_.Ebp; #elif defined(EDB_X86_64) return isWow64_ ? context32_.Ebp : context64_.Rbp; #endif } /** * @brief PlatformState::instructionPointer * @return the instruction pointer for this platform */ edb::address_t PlatformState::instructionPointer() const { #if defined(EDB_X86) return context32_.Eip; #elif defined(EDB_X86_64) return isWow64_ ? context32_.Eip : context64_.Rip; #endif } /** * @brief PlatformState::stackPointer * @return the stack pointer for this platform */ edb::address_t PlatformState::stackPointer() const { #if defined(EDB_X86) return context32_.Esp; #elif defined(EDB_X86_64) return isWow64_ ? context32_.Esp : context64_.Rsp; #endif } /** * @brief PlatformState::debugRegister * @param n * @return */ edb::reg_t PlatformState::debugRegister(size_t n) const { #if defined(EDB_X86) switch (n) { case 0: return context32_.Dr0; case 1: return context32_.Dr1; case 2: return context32_.Dr2; case 3: return context32_.Dr3; case 6: return context32_.Dr6; case 7: return context32_.Dr7; } #elif defined(EDB_X86_64) if (isWow64_) { switch (n) { case 0: return context32_.Dr0; case 1: return context32_.Dr1; case 2: return context32_.Dr2; case 3: return context32_.Dr3; case 6: return context32_.Dr6; case 7: return context32_.Dr7; } } else { switch (n) { case 0: return context64_.Dr0; case 1: return context64_.Dr1; case 2: return context64_.Dr2; case 3: return context64_.Dr3; case 6: return context64_.Dr6; case 7: return context64_.Dr7; } } #endif return 0; } /** * @brief PlatformState::flags * @return */ edb::reg_t PlatformState::flags() const { #if defined(EDB_X86) return context32_.EFlags; #elif defined(EDB_X86_64) return isWow64_ ? context32_.EFlags : context64_.EFlags; #endif } /** * @brief PlatformState::fpu_register * @param n * @return */ long double PlatformState::fpu_register(int n) const { double ret = 0.0; if (n >= 0 && n <= 7) { #if defined(EDB_X86) auto p = reinterpret_cast(&context32_.FloatSave.RegisterArea[n * 10]); if (sizeof(long double) == 10) { // can we check this at compile time? ret = *(reinterpret_cast(p)); } else { ret = read_float80(p); } #elif defined(EDB_X86_64) if (isWow64_) { auto p = reinterpret_cast(&context32_.FloatSave.RegisterArea[n * 10]); if (sizeof(long double) == 10) { // can we check this at compile time? ret = *(reinterpret_cast(p)); } else { ret = read_float80(p); } } else { auto p = reinterpret_cast(&context64_.FltSave.FloatRegisters[n]); if (sizeof(long double) == 10) { ret = *(reinterpret_cast(p)); } else { ret = read_float80(p); } } #endif } return ret; } /** * @brief PlatformState::mmx_register * @param n * @return */ quint64 PlatformState::mmx_register(int n) const { quint64 ret = 0; if (n >= 0 && n <= 7) { #if defined(EDB_X86) // MMX registers are an alias to the lower 64-bits of the FPU regs auto p = reinterpret_cast(&context32_.FloatSave.RegisterArea[n * 10]); ret = *p; // little endian! #elif defined(EDB_X86_64) if (isWow64_) { auto p = reinterpret_cast(&context32_.FloatSave.RegisterArea[n * 10]); ret = *p; // little endian! } else { auto p = reinterpret_cast(&context64_.FltSave.FloatRegisters[n]); ret = *p; } #endif } return ret; } /** * @brief PlatformState::xmm_register * @param n * @return */ QByteArray PlatformState::xmm_register(int n) const { QByteArray ret(16, 0); #if defined(EDB_X86) if (n >= 0 && n <= 7) { auto p = reinterpret_cast(&context32_.ExtendedRegisters[(10 + n) * 16]); ret = QByteArray(p, 16); std::reverse(ret.begin(), ret.end()); //little endian! } #elif defined(EDB_X86_64) if (n >= 0 && n <= 15) { if (isWow64_) { auto p = reinterpret_cast(&context32_.ExtendedRegisters[(10 + n) * 16]); ret = QByteArray(p, 16); std::reverse(ret.begin(), ret.end()); //little endian! } else { auto p = reinterpret_cast(&context64_.FltSave.XmmRegisters[n]); ret = QByteArray(p, sizeof(M128A)); std::reverse(ret.begin(), ret.end()); } } #endif return ret; } /** * @brief PlatformState::adjustStack * @param bytes */ void PlatformState::adjustStack(int bytes) { #if defined(EDB_X86) context32_.Esp += bytes; #elif defined(EDB_X86_64) if (isWow64_) { context32_.Esp += bytes; } else { context64_.Rsp += bytes; } #endif } /** * @brief PlatformState::clear */ void PlatformState::clear() { context32_ = {}; #if defined(EDB_X86_64) context64_ = {}; #endif fs_base_ = 0; gs_base_ = 0; } /** * @brief PlatformState::setDebugRegister * @param n * @param value */ void PlatformState::setDebugRegister(size_t n, edb::reg_t value) { #if defined(EDB_X86) switch (n) { case 0: context32_.Dr0 = value; break; case 1: context32_.Dr1 = value; break; case 2: context32_.Dr2 = value; break; case 3: context32_.Dr3 = value; break; case 6: context32_.Dr6 = value; break; case 7: context32_.Dr7 = value; break; default: break; } #elif defined(EDB_X86_64) if (isWow64_) { switch (n) { case 0: context32_.Dr0 = value; break; case 1: context32_.Dr1 = value; break; case 2: context32_.Dr2 = value; break; case 3: context32_.Dr3 = value; break; case 6: context32_.Dr6 = value; break; case 7: context32_.Dr7 = value; break; default: break; } } else { switch (n) { case 0: context64_.Dr0 = value; break; case 1: context64_.Dr1 = value; break; case 2: context64_.Dr2 = value; break; case 3: context64_.Dr3 = value; break; case 6: context64_.Dr6 = value; break; case 7: context64_.Dr7 = value; break; default: break; } } #endif } /** * @brief PlatformState::setFlags * @param flags */ void PlatformState::setFlags(edb::reg_t flags) { #if defined(EDB_X86) context32_.EFlags = flags; #elif defined(EDB_X86_64) if (isWow64_) { context32_.EFlags = flags; } else { context64_.EFlags = flags; } #endif } /** * @brief PlatformState::setInstructionPointer * @param value */ void PlatformState::setInstructionPointer(edb::address_t value) { #if defined(EDB_X86) context32_.Eip = value; #elif defined(EDB_X86_64) if (isWow64_) { context32_.Eip = static_cast(value); } else { context64_.Rip = value; } #endif } /** * @brief PlatformState::setRegister * @param name * @param value */ void PlatformState::setRegister(const QString &name, edb::reg_t value) { const QString lreg = name.toLower(); #if defined(EDB_X86) if (lreg == "eax") { context32_.Eax = value; } else if (lreg == "ebx") { context32_.Ebx = value; } else if (lreg == "ecx") { context32_.Ecx = value; } else if (lreg == "edx") { context32_.Edx = value; } else if (lreg == "ebp") { context32_.Ebp = value; } else if (lreg == "esp") { context32_.Esp = value; } else if (lreg == "esi") { context32_.Esi = value; } else if (lreg == "edi") { context32_.Edi = value; } else if (lreg == "eip") { context32_.Eip = value; } else if (lreg == "cs") { context32_.SegCs = value; } else if (lreg == "ds") { context32_.SegDs = value; } else if (lreg == "es") { context32_.SegEs = value; } else if (lreg == "fs") { context32_.SegFs = value; } else if (lreg == "gs") { context32_.SegGs = value; } else if (lreg == "ss") { context32_.SegSs = value; } else if (lreg == "eflags") { context32_.EFlags = value; } #elif defined(EDB_X86_64) if (!isWow64_) { if (lreg == "rax") { context64_.Rax = value; } else if (lreg == "rbx") { context64_.Rbx = value; } else if (lreg == "rcx") { context64_.Rcx = value; } else if (lreg == "rdx") { context64_.Rdx = value; } else if (lreg == "rbp") { context64_.Rbp = value; } else if (lreg == "rsp") { context64_.Rsp = value; } else if (lreg == "rsi") { context64_.Rsi = value; } else if (lreg == "rdi") { context64_.Rdi = value; } else if (lreg == "r8") { context64_.R8 = value; } else if (lreg == "r9") { context64_.R9 = value; } else if (lreg == "r10") { context64_.R10 = value; } else if (lreg == "r11") { context64_.R11 = value; } else if (lreg == "r12") { context64_.R12 = value; } else if (lreg == "r13") { context64_.R13 = value; } else if (lreg == "r14") { context64_.R14 = value; } else if (lreg == "r15") { context64_.R15 = value; } else if (lreg == "rip") { context64_.Rip = value; } else if (lreg == "cs") { context64_.SegCs = value; } else if (lreg == "ds") { context64_.SegDs = value; } else if (lreg == "es") { context64_.SegEs = value; } else if (lreg == "fs") { context64_.SegFs = value; } else if (lreg == "gs") { context64_.SegGs = value; } else if (lreg == "ss") { context64_.SegSs = value; } else if (lreg == "rflags") { context64_.EFlags = value; } } else { if (lreg == "eax") { context32_.Eax = value; } else if (lreg == "ebx") { context32_.Ebx = value; } else if (lreg == "ecx") { context32_.Ecx = value; } else if (lreg == "edx") { context32_.Edx = value; } else if (lreg == "ebp") { context32_.Ebp = value; } else if (lreg == "esp") { context32_.Esp = value; } else if (lreg == "esi") { context32_.Esi = value; } else if (lreg == "edi") { context32_.Edi = value; } else if (lreg == "eip") { context32_.Eip = value; } else if (lreg == "cs") { context32_.SegCs = value; } else if (lreg == "ds") { context32_.SegDs = value; } else if (lreg == "es") { context32_.SegEs = value; } else if (lreg == "fs") { context32_.SegFs = value; } else if (lreg == "gs") { context32_.SegGs = value; } else if (lreg == "ss") { context32_.SegSs = value; } else if (lreg == "eflags") { context32_.EFlags = value; } } #endif } /** * @brief PlatformState::instructionPointerRegister * @return */ Register PlatformState::instructionPointerRegister() const { #if defined(EDB_X86_64) if (!isWow64_) { return make_Register("rip", context64_.Rip, Register::TYPE_IP); } else { return make_Register("eip", context32_.Eip, Register::TYPE_IP); } #elif defined(EDB_X86) return make_Register("eip", context32_.Eip, Register::TYPE_IP); #endif } /** * @brief PlatformState::flagsRegister * @return */ Register PlatformState::flagsRegister() const { #if defined(EDB_X86_64) if (!isWow64_) { return make_Register("rflags", context64_.EFlags, Register::TYPE_IP); } else { return make_Register("eflags", context32_.EFlags, Register::TYPE_IP); } #elif defined(EDB_X86) return make_Register("eflags", context32_.EFlags, Register::TYPE_IP); #endif return Register(); } /** * @brief PlatformState::getThreadState * @param hThread * @param isWow64 */ void PlatformState::getThreadState(HANDLE hThread, bool isWow64) { #if defined(EDB_X86) context32_.ContextFlags = CONTEXT_ALL; //CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS | CONTEXT_FLOATING_POINT; GetThreadContext(hThread, &context32_); gs_base_ = 0; fs_base_ = 0; LDT_ENTRY ldt_entry; if (GetThreadSelectorEntry(hThread, context32_.SegGs, &ldt_entry)) { gs_base_ = ldt_entry.BaseLow | (ldt_entry.HighWord.Bits.BaseMid << 16) | (ldt_entry.HighWord.Bits.BaseHi << 24); } if (GetThreadSelectorEntry(hThread, context32_.SegFs, &ldt_entry)) { fs_base_ = ldt_entry.BaseLow | (ldt_entry.HighWord.Bits.BaseMid << 16) | (ldt_entry.HighWord.Bits.BaseHi << 24); } #elif defined(EDB_X86_64) gs_base_ = 0; fs_base_ = 0; if (isWow64) { context32_.ContextFlags = CONTEXT_ALL; //CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS | CONTEXT_FLOATING_POINT; Wow64GetThreadContext(hThread, &context32_); WOW64_LDT_ENTRY ldt_entry; if (Wow64GetThreadSelectorEntry(hThread, context32_.SegGs, &ldt_entry)) { gs_base_ = ldt_entry.BaseLow | (ldt_entry.HighWord.Bits.BaseMid << 16) | (ldt_entry.HighWord.Bits.BaseHi << 24); } if (Wow64GetThreadSelectorEntry(hThread, context32_.SegFs, &ldt_entry)) { fs_base_ = ldt_entry.BaseLow | (ldt_entry.HighWord.Bits.BaseMid << 16) | (ldt_entry.HighWord.Bits.BaseHi << 24); } } else { context64_.ContextFlags = CONTEXT_ALL; //CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS | CONTEXT_FLOATING_POINT; GetThreadContext(hThread, &context64_); // GetThreadSelectorEntry always returns false on x64 // on x64 gs_base == TEB, maybe we can use that somehow } isWow64_ = isWow64; #endif } /** * @brief PlatformState::setThreadState * @param hThread */ void PlatformState::setThreadState(HANDLE hThread) const { #if defined(EDB_X86) SetThreadContext(hThread, &context32_); #elif defined(EDB_X86_64) if (isWow64_) { Wow64SetThreadContext(hThread, &context32_); } else { SetThreadContext(hThread, &context64_); } #endif } /** * @brief PlatformState::archRegister * @param type * @param n * @return */ Register PlatformState::archRegister(uint64_t type, size_t n) const { switch (type) { case edb::string_hash("mmx"): return mmxRegister(n); case edb::string_hash("xmm"): return xmmRegister(n); case edb::string_hash("ymm"): return ymmRegister(n); default: break; } return Register(); } } edb-debugger/plugins/DebuggerCore/win32/PlatformRegion.h0000644000175000017500000000357313765535463023022 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PLATFORM_REGION_H_20120330_ #define PLATFORM_REGION_H_20120330_ #include "IRegion.h" #include #include namespace DebuggerCorePlugin { class PlatformRegion : public IRegion { Q_DECLARE_TR_FUNCTIONS(PlatformRegion) template friend class BackupInfo; public: PlatformRegion(edb::address_t start, edb::address_t end, edb::address_t base, const QString &name, permissions_t permissions); ~PlatformRegion() override = default; public: IRegion *clone() const override; public: bool accessible() const override; bool readable() const override; bool writable() const override; bool executable() const override; size_t size() const override; public: void setPermissions(bool read, bool write, bool execute) override; void setStart(edb::address_t address) override; void setEnd(edb::address_t address) override; public: edb::address_t start() const override; edb::address_t end() const override; edb::address_t base() const override; QString name() const override; permissions_t permissions() const override; private: edb::address_t start_; edb::address_t end_; edb::address_t base_; QString name_; permissions_t permissions_; }; } #endif edb-debugger/plugins/DebuggerCore/CMakeLists.txt0000644000175000017500000000761413765535463021517 0ustar eteraneterancmake_minimum_required (VERSION 3.1) include("GNUInstallDirs") set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(PluginName "DebuggerCore") find_package(Qt5 5.0.0 REQUIRED Widgets) set(DebuggerCore_SRCS DebuggerCoreBase.cpp DebuggerCoreBase.h ) if(TARGET_PLATFORM_LINUX) set(PLUGIN_INCLUDES ${PLUGIN_INCLUDES} unix/linux unix ) set(DebuggerCore_SRCS ${DebuggerCore_SRCS} unix/linux/DebuggerCore.cpp unix/linux/DebuggerCore.h unix/linux/DialogMemoryAccess.cpp unix/linux/DialogMemoryAccess.h unix/linux/DialogMemoryAccess.ui unix/linux/FeatureDetect.cpp unix/linux/FeatureDetect.h unix/linux/PlatformCommon.cpp unix/linux/PlatformCommon.h unix/linux/PlatformEvent.cpp unix/linux/PlatformEvent.h unix/linux/PlatformProcess.cpp unix/linux/PlatformProcess.h unix/linux/PlatformRegion.cpp unix/linux/PlatformRegion.h unix/linux/PlatformThread.cpp unix/linux/PlatformThread.h unix/linux/PrStatus.h unix/Posix.cpp unix/Posix.h unix/Unix.cpp unix/Unix.h ) elseif(TARGET_PLATFORM_WINDOWS) set(PLUGIN_INCLUDES ${PLUGIN_INCLUDES} win32 ) set(DebuggerCore_SRCS ${DebuggerCore_SRCS} win32/DebuggerCore.cpp win32/DebuggerCore.h win32/PlatformEvent.cpp win32/PlatformEvent.h win32/PlatformProcess.cpp win32/PlatformProcess.h win32/PlatformRegion.cpp win32/PlatformRegion.h win32/PlatformState.cpp win32/PlatformState.h win32/PlatformThread.cpp win32/PlatformThread.h ) elseif(TARGET_PLATFORM_FREEBSD) set(PLUGIN_INCLUDES ${PLUGIN_INCLUDES} unix/freebsd unix ) set(DebuggerCore_SRCS ${DebuggerCore_SRCS} unix/freebsd/DebuggerCore.cpp unix/freebsd/DebuggerCore.h unix/freebsd/PlatformCommon.cpp unix/freebsd/PlatformCommon.h unix/freebsd/PlatformEvent.cpp unix/freebsd/PlatformEvent.h unix/freebsd/PlatformProcess.cpp unix/freebsd/PlatformProcess.h unix/freebsd/PlatformRegion.cpp unix/freebsd/PlatformRegion.h unix/freebsd/PlatformThread.cpp unix/freebsd/PlatformThread.h ) endif() if(TARGET_ARCH_FAMILY_X86) set(PLUGIN_INCLUDES ${PLUGIN_INCLUDES} arch/x86-generic ) set(DebuggerCore_SRCS ${DebuggerCore_SRCS} arch/x86-generic/Breakpoint.cpp arch/x86-generic/Breakpoint.h ) if(TARGET_PLATFORM_LINUX) set(PLUGIN_INCLUDES ${PLUGIN_INCLUDES} unix/linux/arch/x86-generic ) set(DebuggerCore_SRCS ${DebuggerCore_SRCS} unix/linux/arch/x86-generic/PlatformState.cpp unix/linux/arch/x86-generic/PlatformState.h unix/linux/arch/x86-generic/PlatformThread.cpp ) endif() endif() if(TARGET_ARCH_FAMILY_ARM) set(PLUGIN_INCLUDES ${PLUGIN_INCLUDES} arch/arm-generic ) set(DebuggerCore_SRCS ${DebuggerCore_SRCS} arch/arm-generic/Breakpoint.cpp arch/arm-generic/Breakpoint.h ) if(TARGET_PLATFORM_LINUX) set(PLUGIN_INCLUDES ${PLUGIN_INCLUDES} unix/linux/arch/arm-generic ) set(DebuggerCore_SRCS ${DebuggerCore_SRCS} unix/linux/arch/arm-generic/PlatformState.cpp unix/linux/arch/arm-generic/PlatformState.h unix/linux/arch/arm-generic/PlatformThread.cpp ) endif() endif() add_library(${PluginName} SHARED ${DebuggerCore_SRCS} ) target_include_directories(${PluginName} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${PLUGIN_INCLUDES} ) target_link_libraries(${PluginName} Qt5::Widgets PE ELF edb) install (TARGETS ${PluginName} DESTINATION ${CMAKE_INSTALL_LIBDIR}/edb) set_property(TARGET ${PluginName} PROPERTY CXX_EXTENSIONS OFF) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD 14) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD_REQUIRED ON) set_property(TARGET ${PluginName} PROPERTY LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) set_property(TARGET ${PluginName} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) edb-debugger/plugins/DebuggerCore/unix/0000755000175000017500000000000013765535463017732 5ustar eteraneteranedb-debugger/plugins/DebuggerCore/unix/Unix.h0000644000175000017500000000063613765535463021033 0ustar eteraneteran #ifndef UNIX_H_20181211_ #define UNIX_H_20181211_ #include "Status.h" #include #include #include namespace DebuggerCorePlugin { namespace Unix { QMap exceptions(); QString exception_name(qlonglong value); qlonglong exception_value(const QString &name); Status execute_process(const QString &path, const QString &cwd, const QList &args); } } #endif edb-debugger/plugins/DebuggerCore/unix/openbsd/0000755000175000017500000000000013765535463021364 5ustar eteraneteranedb-debugger/plugins/DebuggerCore/unix/openbsd/DebuggerCore.cpp0000644000175000017500000005346113765535463024436 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "DebuggerCore.h" #include "PlatformEvent.h" #include "PlatformRegion.h" #include "PlatformState.h" #include "State.h" #include "string_hash.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define __need_process #include #include namespace DebuggerCore { namespace { void SET_OK(bool &ok, long value) { ok = (value != -1) || (errno == 0); } int resume_code(int status) { if (WIFSIGNALED(status)) { return WTERMSIG(status); } else if (WIFSTOPPED(status)) { return WSTOPSIG(status); } return 0; } #if defined(OpenBSD) && (OpenBSD > 201205) //------------------------------------------------------------------------------ // Name: load_vmmap_entries // Desc: Download vmmap_entries from the kernel into our address space. // We fix up the addr tree while downloading. // Returns the size of the tree on success, or -1 on failure. // On failure, *rptr needs to be passed to unload_vmmap_entries to free // the lot. //------------------------------------------------------------------------------ ssize_t load_vmmap_entries(kvm_t *kd, u_long kptr, struct vm_map_entry **rptr, struct vm_map_entry *parent) { struct vm_map_entry *entry; u_long left_kptr; u_long right_kptr; ssize_t left_sz; ssize_t right_sz; if (kptr == 0) return 0; /* Need space. */ entry = (struct vm_map_entry *)malloc(sizeof(*entry)); if (entry == NULL) return -1; /* Download entry at kptr. */ if (!kvm_read(kd, kptr, (char *)entry, sizeof(*entry))) { free(entry); return -1; } /* * Update addr pointers to have sane values in this address space. * We save the kernel pointers in {left,right}_kptr, so we have them * available to download children. */ left_kptr = (u_long)RB_LEFT(entry, daddrs.addr_entry); right_kptr = (u_long)RB_RIGHT(entry, daddrs.addr_entry); RB_LEFT(entry, daddrs.addr_entry) = RB_RIGHT(entry, daddrs.addr_entry) = NULL; /* Fill in parent pointer. */ RB_PARENT(entry, daddrs.addr_entry) = parent; /* * Consistent state reached, fill in *rptr. */ *rptr = entry; /* * Download left, right. * On failure, our map is in a state that can be handled by * unload_vmmap_entries. */ left_sz = load_vmmap_entries(kd, left_kptr, &RB_LEFT(entry, daddrs.addr_entry), entry); if (left_sz == -1) return -1; right_sz = load_vmmap_entries(kd, right_kptr, &RB_RIGHT(entry, daddrs.addr_entry), entry); if (right_sz == -1) return -1; return 1 + left_sz + right_sz; } //------------------------------------------------------------------------------ // Name: // Desc: Free the vmmap entries in the given tree. //------------------------------------------------------------------------------ void unload_vmmap_entries(struct vm_map_entry *entry) { if (entry == NULL) return; unload_vmmap_entries(RB_LEFT(entry, daddrs.addr_entry)); unload_vmmap_entries(RB_RIGHT(entry, daddrs.addr_entry)); free(entry); } //------------------------------------------------------------------------------ // Name: // Desc: Don't implement address comparison. //------------------------------------------------------------------------------ int no_impl(void *p, void *q) { Q_UNUSED(p) Q_UNUSED(q) Q_ASSERT(0); /* Should not be called. */ return 0; } #endif } #if defined(OpenBSD) && (OpenBSD > 201205) RB_GENERATE(uvm_map_addr, vm_map_entry, daddrs.addr_entry, no_impl) #endif //------------------------------------------------------------------------------ // Name: DebuggerCore // Desc: constructor //------------------------------------------------------------------------------ DebuggerCore::DebuggerCore() { #if defined(_SC_PAGESIZE) page_size_ = sysconf(_SC_PAGESIZE); #elif defined(_SC_PAGE_SIZE) page_size_ = sysconf(_SC_PAGE_SIZE); #else page_size_ = PAGE_SIZE; #endif } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ bool DebuggerCore::has_extension(quint64 ext) const { return false; } //------------------------------------------------------------------------------ // Name: page_size // Desc: returns the size of a page on this system //------------------------------------------------------------------------------ size_t DebuggerCore::page_size() const { return page_size_; } //------------------------------------------------------------------------------ // Name: ~DebuggerCore // Desc: //------------------------------------------------------------------------------ DebuggerCore::~DebuggerCore() { detach(); } //------------------------------------------------------------------------------ // Name: wait_debug_event // Desc: waits for a debug event, msecs is a timeout // it will return false if an error or timeout occurs //------------------------------------------------------------------------------ std::shared_ptr DebuggerCore::wait_debug_event(int msecs) { if (attached()) { int status; bool timeout; const edb::tid_t tid = native::waitpid_timeout(pid(), &status, 0, msecs, &timeout); if (!timeout) { if (tid > 0) { // normal event auto e = std::make_shared(); e->pid = pid(); e->tid = tid; e->status = status; char errbuf[_POSIX2_LINE_MAX]; if (kvm_t *const kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf)) { int rc; struct kinfo_proc *const kiproc = kvm_getprocs(kd, KERN_PROC_PID, pid(), sizeof(struct kinfo_proc), &rc); struct proc proc; kvm_read(kd, kiproc->p_paddr, &proc, sizeof(proc)); e->fault_code_ = proc.p_sicode; e->fault_address_ = proc.p_sigval.sival_ptr; //printf("ps_sig : %d\n", sigacts.ps_sig); //printf("ps_type : %d\n", sigacts.ps_type); kvm_close(kd); } else { e->fault_code_ = 0; e->fault_address_ = 0; } active_thread_ = tid; threads_[tid].status = status; return e; } } } return nullptr; } //------------------------------------------------------------------------------ // Name: read_data // Desc: //------------------------------------------------------------------------------ long DebuggerCore::read_data(edb::address_t address, bool *ok) { Q_ASSERT(ok); errno = 0; const long v = ptrace(PT_READ_D, pid(), reinterpret_cast(address), 0); SET_OK(*ok, v); return v; } //------------------------------------------------------------------------------ // Name: write_data // Desc: //------------------------------------------------------------------------------ bool DebuggerCore::write_data(edb::address_t address, long value) { return ptrace(PT_WRITE_D, pid(), reinterpret_cast(address), value) != -1; } //------------------------------------------------------------------------------ // Name: attach // Desc: //------------------------------------------------------------------------------ bool DebuggerCore::attach(edb::pid_t pid) { detach(); const long ret = ptrace(PT_ATTACH, pid, 0, 0); if (ret == 0) { pid_ = pid; active_thread_ = pid; threads_.clear(); threads_.insert(pid, thread_info()); // TODO: attach to all of the threads } return ret == 0; } //------------------------------------------------------------------------------ // Name: detach // Desc: //------------------------------------------------------------------------------ void DebuggerCore::detach() { if (attached()) { // TODO: do i need to stop each thread first, and wait for them? clear_breakpoints(); ptrace(PT_DETACH, pid(), 0, 0); pid_ = 0; threads_.clear(); } } //------------------------------------------------------------------------------ // Name: kill // Desc: //------------------------------------------------------------------------------ void DebuggerCore::kill() { if (attached()) { clear_breakpoints(); ptrace(PT_KILL, pid(), 0, 0); native::waitpid(pid(), 0, WAIT_ANY); pid_ = 0; threads_.clear(); } } //------------------------------------------------------------------------------ // Name: pause // Desc: stops *all* threads of a process //------------------------------------------------------------------------------ void DebuggerCore::pause() { if (attached()) { for (auto it = threads_.begin(); it != threads_.end(); ++it) { ::kill(it.key(), SIGSTOP); } } } //------------------------------------------------------------------------------ // Name: resume // Desc: //------------------------------------------------------------------------------ void DebuggerCore::resume(edb::EVENT_STATUS status) { // TODO: assert that we are paused if (attached()) { if (status != edb::DEBUG_STOP) { const edb::tid_t tid = active_thread(); const int code = (status == edb::DEBUG_EXCEPTION_NOT_HANDLED) ? resume_code(threads_[tid].status) : 0; ptrace(PT_CONTINUE, tid, reinterpret_cast(1), code); } } } //------------------------------------------------------------------------------ // Name: step // Desc: //------------------------------------------------------------------------------ void DebuggerCore::step(edb::EVENT_STATUS status) { // TODO: assert that we are paused if (attached()) { if (status != edb::DEBUG_STOP) { const edb::tid_t tid = active_thread(); const int code = (status == edb::DEBUG_EXCEPTION_NOT_HANDLED) ? resume_code(threads_[tid].status) : 0; ptrace(PT_STEP, tid, reinterpret_cast(1), code); } } } //------------------------------------------------------------------------------ // Name: get_state // Desc: //------------------------------------------------------------------------------ void DebuggerCore::get_state(State *state) { Q_ASSERT(state); // TODO: assert that we are paused auto state_impl = static_cast(state->impl_); if (attached()) { if (ptrace(PT_GETREGS, active_thread(), reinterpret_cast(&state_impl->regs_), 0) != -1) { // TODO state_impl->gs_base = 0; state_impl->fs_base = 0; } if (ptrace(PT_GETFPREGS, active_thread(), reinterpret_cast(&state_impl->fpregs_), 0) != -1) { } // TODO: Debug Registers } else { state->clear(); } } //------------------------------------------------------------------------------ // Name: set_state // Desc: //------------------------------------------------------------------------------ void DebuggerCore::set_state(const State &state) { // TODO: assert that we are paused auto state_impl = static_cast(state.impl_); if (attached()) { ptrace(PT_SETREGS, active_thread(), reinterpret_cast(&state_impl->regs_), 0); // TODO: FPU // TODO: Debug Registers } } //------------------------------------------------------------------------------ // Name: open // Desc: //------------------------------------------------------------------------------ bool DebuggerCore::open(const QString &path, const QString &cwd, const QList &args, const QString &tty) { detach(); pid_t pid; switch (pid = fork()) { case 0: // we are in the child now... // set ourselves (the child proc) up to be traced ptrace(PT_TRACE_ME, 0, 0, 0); // redirect it's I/O if (!tty.isEmpty()) { FILE *const std_out = freopen(qPrintable(tty), "r+b", stdout); FILE *const std_in = freopen(qPrintable(tty), "r+b", stdin); FILE *const std_err = freopen(qPrintable(tty), "r+b", stderr); Q_UNUSED(std_out) Q_UNUSED(std_in) Q_UNUSED(std_err) } // do the actual exec execute_process(path, cwd, args); // we should never get here! abort(); break; case -1: // error! pid_ = 0; return false; default: // parent do { threads_.clear(); int status; if (native::waitpid(pid, &status, 0) == -1) { return false; } // the very first event should be a STOP of type SIGTRAP if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP) { detach(); return false; } // setup the first event data for the primary thread threads_.insert(pid, thread_info()); pid_ = pid; active_thread_ = pid; threads_[pid].status = status; return true; } while (0); break; } } //------------------------------------------------------------------------------ // Name: set_active_thread // Desc: //------------------------------------------------------------------------------ void DebuggerCore::set_active_thread(edb::tid_t tid) { Q_ASSERT(threads_.contains(tid)); active_thread_ = tid; } //------------------------------------------------------------------------------ // Name: create_state // Desc: //------------------------------------------------------------------------------ std::unique_ptr DebuggerCore::create_state() const { return std::make_unique(); } //------------------------------------------------------------------------------ // Name: enumerate_processes // Desc: //------------------------------------------------------------------------------ QMap DebuggerCore::enumerate_processes() const { QMap ret; char ebuffer[_POSIX2_LINE_MAX]; int numprocs; if (kvm_t *const kaccess = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, ebuffer)) { if (struct kinfo_proc *const kprocaccess = kvm_getprocs(kaccess, KERN_PROC_ALL, 0, sizeof *kprocaccess, &numprocs)) { for (int i = 0; i < numprocs; ++i) { ProcessInfo procInfo; procInfo.pid = kprocaccess[i].p_pid; procInfo.uid = kprocaccess[i].p_uid; procInfo.name = kprocaccess[i].p_comm; ret.insert(procInfo.pid, procInfo); } } kvm_close(kaccess); } else { QMessageBox::warning(0, "Error Listing Processes", ebuffer); } return ret; } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ QString DebuggerCore::process_exe(edb::pid_t pid) const { QString ret; char errbuf[_POSIX2_LINE_MAX]; if (kvm_t *kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf)) { char p_comm[KI_MAXCOMLEN] = ""; int rc; if (struct kinfo_proc *const proc = kvm_getprocs(kd, KERN_PROC_PID, pid, sizeof(struct kinfo_proc), &rc)) { memcpy(p_comm, proc->p_comm, sizeof(p_comm)); } kvm_close(kd); return p_comm; } return ret; } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ QString DebuggerCore::process_cwd(edb::pid_t pid) const { // TODO: implement this return QString(); } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ edb::pid_t DebuggerCore::parent_pid(edb::pid_t pid) const { edb::pid_t ret = 0; char errbuf[_POSIX2_LINE_MAX]; if (kvm_t *kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf)) { int rc; struct kinfo_proc *const proc = kvm_getprocs(kd, KERN_PROC_PID, pid, sizeof *proc, &rc); ret = proc->p_ppid; kvm_close(kd); } return ret; } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ QList> DebuggerCore::memory_regions() const { QList> regions; if (pid_ != 0) { char err_buf[_POSIX2_LINE_MAX]; if (kvm_t *const kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, err_buf)) { int rc; struct kinfo_proc *const proc = kvm_getprocs(kd, KERN_PROC_PID, pid_, sizeof *proc, &rc); Q_ASSERT(proc); struct vmspace vmsp; kvm_read(kd, proc->p_vmspace, &vmsp, sizeof vmsp); #if defined(OpenBSD) && (OpenBSD > 201205) uvm_map_addr root; RB_INIT(&root); if (load_vmmap_entries(kd, (u_long)RB_ROOT(&vmsp.vm_map.addr), &RB_ROOT(&root), NULL) == -1) goto do_unload; struct vm_map_entry *e; RB_FOREACH(e, uvm_map_addr, &root) { const edb::address_t start = e->start; const edb::address_t end = e->end; const edb::address_t base = e->offset; const QString name = QString(); const IRegion::permissions_t permissions = ((e->protection & VM_PROT_READ) ? PROT_READ : 0) | ((e->protection & VM_PROT_WRITE) ? PROT_WRITE : 0) | ((e->protection & VM_PROT_EXECUTE) ? PROT_EXEC : 0); regions.push_back(std::make_shared(start, end, base, name, permissions)); } do_unload: unload_vmmap_entries(RB_ROOT(&root)); #else struct vm_map_entry e; if (vmsp.vm_map.header.next != 0) { kvm_read(kd, (u_long)vmsp.vm_map.header.next, &e, sizeof(e)); while (e.next != vmsp.vm_map.header.next) { const edb::address_t start = e.start; const edb::address_t end = e.end; const edb::address_t base = e.offset; const QString name = QString(); const IRegion::permissions_t permissions = ((e.protection & VM_PROT_READ) ? PROT_READ : 0) | ((e.protection & VM_PROT_WRITE) ? PROT_WRITE : 0) | ((e.protection & VM_PROT_EXECUTE) ? PROT_EXEC : 0); regions.push_back(std::make_shared(start, end, base, name, permissions)); kvm_read(kd, (u_long)e.next, &e, sizeof(e)); } } #endif kvm_close(kd); } else { fprintf(stderr, "sync: %s\n", err_buf); return QList>(); } } return regions; } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ QList DebuggerCore::process_args(edb::pid_t pid) const { QList ret; if (pid != 0) { // TODO: assert attached! char errbuf[_POSIX2_LINE_MAX]; if (kvm_t *kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf)) { int rc; if (struct kinfo_proc *const proc = kvm_getprocs(kd, KERN_PROC_PID, sizeof *proc, pid, &rc)) { char **argv = kvm_getargv(kd, proc, 0); char **p = argv; while (*p) { ret << *p++; } } kvm_close(kd); } } return ret; } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ edb::address_t DebuggerCore::process_code_address() const { qDebug() << "TODO: implement DebuggerCore::process_code_address"; return 0; } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ edb::address_t DebuggerCore::process_data_address() const { qDebug() << "TODO: implement DebuggerCore::process_data_address"; return 0; } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ QList DebuggerCore::loaded_modules() const { QList modules; qDebug() << "TODO: implement DebuggerCore::loaded_modules"; return modules; } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ QDateTime DebuggerCore::process_start(edb::pid_t pid) const { qDebug() << "TODO: implement DebuggerCore::process_start"; return QDateTime(); } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ quint64 DebuggerCore::cpu_type() const { #ifdef EDB_X86 return edb::string_hash<'x', '8', '6'>::value; #elif defined(EDB_X86_64) return edb::string_hash<'x', '8', '6', '-', '6', '4'>::value; #endif } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ QString DebuggerCore::format_pointer(edb::address_t address) const { char buf[32]; #ifdef EDB_X86 qsnprintf(buf, sizeof(buf), "%08x", address); #elif defined(EDB_X86_64) qsnprintf(buf, sizeof(buf), "%016llx", address); #endif return buf; } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ QString DebuggerCore::stack_pointer() const { #ifdef EDB_X86 return "esp"; #elif defined(EDB_X86_64) return "rsp"; #endif } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ QString DebuggerCore::frame_pointer() const { #ifdef EDB_X86 return "ebp"; #elif defined(EDB_X86_64) return "rbp"; #endif } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ QString DebuggerCore::instruction_pointer() const { #ifdef EDB_X86 return "eip"; #elif defined(EDB_X86_64) return "rip"; #endif } } edb-debugger/plugins/DebuggerCore/unix/openbsd/DebuggerCore.h0000644000175000017500000000617613765535463024104 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DEBUGGER_CORE_H_20090529_ #define DEBUGGER_CORE_H_20090529_ #include "DebuggerCoreUNIX.h" #include namespace DebuggerCore { class DebuggerCore : public DebuggerCoreUNIX { Q_OBJECT Q_INTERFACES(IDebugger) Q_CLASSINFO("author", "Evan Teran") Q_CLASSINFO("url", "http://www.codef00.com") public: DebuggerCore(); ~DebuggerCore() override; public: size_t page_size() const override; bool has_extension(quint64 ext) const override; std::shared_ptr wait_debug_event(int msecs) override; bool attach(edb::pid_t pid) override; void detach() override; void kill() override; void pause() override; void resume(edb::EVENT_STATUS status) override; void step(edb::EVENT_STATUS status) override; void get_state(State *state) override; void set_state(const State &state) override; bool open(const QString &path, const QString &cwd, const QList &args, const QString &tty) override; public: // thread support stuff (optional) QList thread_ids() const override { return threads_.keys(); } edb::tid_t active_thread() const override { return active_thread_; } void set_active_thread(edb::tid_t) override; public: QList> memory_regions() const override; edb::address_t process_code_address() const override; edb::address_t process_data_address() const override; public: std::unique_ptr create_state() const override; public: // process properties QList process_args(edb::pid_t pid) const override; QString process_exe(edb::pid_t pid) const override; QString process_cwd(edb::pid_t pid) const override; edb::pid_t parent_pid(edb::pid_t pid) const override; QDateTime process_start(edb::pid_t pid) const override; quint64 cpu_type() const override; private: QMap enumerate_processes() const override; QList loaded_modules() const override; public: QString stack_pointer() const override; QString frame_pointer() const override; QString instruction_pointer() const override; public: QString format_pointer(edb::address_t address) const override; private: long read_data(edb::address_t address, bool *ok) override; bool write_data(edb::address_t address, long value) override; private: struct thread_info { public: thread_info() : status(0) { } thread_info(int s) : status(s) { } int status; }; using threadmap_t = QHash; size_t page_size_; threadmap_t threads_; }; } #endif edb-debugger/plugins/DebuggerCore/unix/openbsd/PlatformState.h0000644000175000017500000000373313765535463024330 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PLATFORM_STATE_H_20110330_ #define PLATFORM_STATE_H_20110330_ #include "IState.h" #include "Types.h" #include #include namespace DebuggerCore { class PlatformState : public IState { friend class DebuggerCore; public: PlatformState(); public: std::unique_ptr clone() const override; public: QString flagsToString() const override; QString flagsToString(edb::reg_t flags) const override; Register value(const QString ®) const override; edb::address_t frame_pointer() const override; edb::address_t instruction_pointer() const override; edb::address_t stack_pointer() const override; edb::reg_t debug_register(int n) const override; edb::reg_t flags() const override; long double fpu_register(int n) const override; void adjust_stack(int bytes) override; void clear() override; void set_debug_register(int n, edb::reg_t value) override; void set_flags(edb::reg_t flags) override; void set_instruction_pointer(edb::address_t value) override; void set_register(const QString &name, edb::reg_t value) override; quint64 mmx_register(int n) const override; QByteArray xmm_register(int n) const override; private: struct reg regs_; struct fpreg fpregs_; edb::reg_t dr_[8]; edb::address_t fs_base; edb::address_t gs_base; }; } #endif edb-debugger/plugins/DebuggerCore/unix/openbsd/PlatformEvent.cpp0000644000175000017500000001732613765535463024667 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "PlatformEvent.h" #include "edb.h" #include #include #include #include #include // for the SIG* definitions #include #include #include #include #include #include #include #include #include #include #include #include namespace DebuggerCore { //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ PlatformEvent::PlatformEvent() : status(0), pid(-1), tid(-1), fault_address_(0), fault_code_(0) { } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ PlatformEvent *PlatformEvent::clone() const { return new PlatformEvent(*this); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ IDebugEvent::Message PlatformEvent::error_description() const { Q_ASSERT(is_error()); auto fault_address = reinterpret_cast(fault_address_); switch (code()) { case SIGSEGV: return Message( tr("Illegal Access Fault"), tr( "

The debugged application encountered a segmentation fault.
The address 0x%1 could not be accessed.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

") .arg(edb::v1::format_pointer(fault_address))); case SIGILL: return Message( tr("Illegal Instruction Fault"), tr( "

The debugged application attempted to execute an illegal instruction.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

")); case SIGFPE: switch (fault_code_) { case FPE_INTDIV: return Message( tr("Divide By Zero"), tr( "

The debugged application tried to divide an integer value by an integer divisor of zero.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

")); default: return Message( tr("Floating Point Exception"), tr( "

The debugged application encountered a floating-point exception.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

")); } case SIGABRT: return Message( tr("Application Aborted"), tr( "

The debugged application has aborted.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

")); case SIGBUS: return Message( tr("Bus Error"), tr( "

The debugged application tried to read or write data that is misaligned.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

")); #ifdef SIGSTKFLT case SIGSTKFLT: return Message( tr("Stack Fault"), tr( "

The debugged application encountered a stack fault.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

")); #endif case SIGPIPE: return Message( tr("Broken Pipe Fault"), tr( "

The debugged application encountered a broken pipe fault.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

")); default: return Message(); } } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ IDebugEvent::REASON PlatformEvent::reason() const { // this basically converts our value into a 'switchable' value for convenience if (stopped()) { return EVENT_STOPPED; } else if (terminated()) { return EVENT_TERMINATED; } else if (exited()) { return EVENT_EXITED; } else { return EVENT_UNKNOWN; } } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ IDebugEvent::TRAP_REASON PlatformEvent::trap_reason() const { switch (fault_code_) { case TRAP_TRACE: return TRAP_STEPPING; default: return TRAP_BREAKPOINT; } } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ bool PlatformEvent::exited() const { return WIFEXITED(status) != 0; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ bool PlatformEvent::is_error() const { if (stopped()) { switch (code()) { case SIGTRAP: case SIGSTOP: return false; case SIGSEGV: case SIGILL: case SIGFPE: case SIGABRT: case SIGBUS: #ifdef SIGSTKFLT case SIGSTKFLT: #endif case SIGPIPE: return true; default: return false; } } else { return false; } } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ bool PlatformEvent::is_kill() const { return stopped() && code() == SIGKILL; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ bool PlatformEvent::is_stop() const { return stopped() && code() == SIGSTOP; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ bool PlatformEvent::is_trap() const { return stopped() && code() == SIGTRAP; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ bool PlatformEvent::terminated() const { return WIFSIGNALED(status) != 0; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ bool PlatformEvent::stopped() const { return WIFSTOPPED(status) != 0; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ edb::pid_t PlatformEvent::process() const { return pid; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ edb::tid_t PlatformEvent::thread() const { return tid; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ int64_t PlatformEvent::code() const { if (stopped()) { return WSTOPSIG(status); } if (terminated()) { return WTERMSIG(status); } if (exited()) { return WEXITSTATUS(status); } return 0; } } edb-debugger/plugins/DebuggerCore/unix/openbsd/PlatformRegion.cpp0000644000175000017500000000442613765535463025026 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "PlatformRegion.h" #include "IDebugEventHandler.h" #include "IDebugger.h" #include "MemoryRegions.h" #include "State.h" #include "edb.h" #include #include #include namespace DebuggerCore { PlatformRegion::PlatformRegion(edb::address_t start, edb::address_t end, edb::address_t base, const QString &name, permissions_t permissions) : start_(start), end_(end), base_(base), name_(name), permissions_(permissions) { } IRegion *PlatformRegion::clone() const { return new PlatformRegion(start_, end_, base_, name_, permissions_); } bool PlatformRegion::accessible() const { return readable() || writable() || executable(); } bool PlatformRegion::readable() const { return (permissions_ & PROT_READ) != 0; } bool PlatformRegion::writable() const { return (permissions_ & PROT_WRITE) != 0; } bool PlatformRegion::executable() const { return (permissions_ & PROT_EXEC) != 0; } size_t PlatformRegion::size() const { return end_ - start_; } void PlatformRegion::set_permissions(bool read, bool write, bool execute) { Q_UNUSED(read) Q_UNUSED(write) Q_UNUSED(execute) } edb::address_t PlatformRegion::start() const { return start_; } edb::address_t PlatformRegion::end() const { return end_; } edb::address_t PlatformRegion::base() const { return base_; } QString PlatformRegion::name() const { return name_; } IRegion::permissions_t PlatformRegion::permissions() const { return permissions_; } void PlatformRegion::set_start(edb::address_t address) { start_ = address; } void PlatformRegion::set_end(edb::address_t address) { end_ = address; } } edb-debugger/plugins/DebuggerCore/unix/openbsd/PlatformProcess.cpp0000644000175000017500000000133513765535463025215 0ustar eteraneteran/* Copyright (C) 2015 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "PlatformProcess.h" edb-debugger/plugins/DebuggerCore/unix/openbsd/PlatformProcess.h0000644000175000017500000000152713765535463024665 0ustar eteraneteran/* Copyright (C) 2015 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PLATFORM_PROCESS_H_20150517_ #define PLATFORM_PROCESS_H_20150517_ #include "IProcess.h" class PlatformProcess : public IProcess { }; #endif edb-debugger/plugins/DebuggerCore/unix/openbsd/PlatformEvent.h0000644000175000017500000000311313765535463024321 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PLATFORM_EVENT_H_20121005_ #define PLATFORM_EVENT_H_20121005_ #include "IDebugEvent.h" #include namespace DebuggerCore { class PlatformEvent : IDebugEvent { Q_DECLARE_TR_FUNCTIONS(PlatformEvent) friend class DebuggerCore; public: PlatformEvent(); public: PlatformEvent *clone() const override; public: Message error_description() const override; REASON reason() const override; TRAP_REASON trap_reason() const override; bool exited() const override; bool is_error() const override; bool is_kill() const override; bool is_stop() const override; bool is_trap() const override; bool terminated() const override; bool stopped() const override; edb::pid_t process() const override; edb::tid_t thread() const override; int64_t code() const override; private: int status; edb::pid_t pid; edb::tid_t tid; void *fault_address_; long fault_code_; }; } #endif edb-debugger/plugins/DebuggerCore/unix/openbsd/PlatformState.cpp0000644000175000017500000004647513765535463024675 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "PlatformState.h" namespace DebuggerCore { //------------------------------------------------------------------------------ // Name: PlatformState // Desc: //------------------------------------------------------------------------------ PlatformState::PlatformState() { memset(®s_, 0, sizeof(regs_)); memset(&fpregs_, 0, sizeof(fpregs_)); memset(&dr_, 0, sizeof(dr_)); fs_base = 0; gs_base = 0; } //------------------------------------------------------------------------------ // Name: PlatformState::clone // Desc: makes a copy of the state object //------------------------------------------------------------------------------ std::unique_ptr PlatformState::clone() const { return std::make_unique(*this); } //------------------------------------------------------------------------------ // Name: flags_to_string // Desc: returns the flags in a string form appropriate for this platform //------------------------------------------------------------------------------ QString PlatformState::flags_to_string(edb::reg_t flags) const { char buf[14]; qsnprintf( buf, sizeof(buf), "%c %c %c %c %c %c %c", ((flags & 0x001) ? 'C' : 'c'), ((flags & 0x004) ? 'P' : 'p'), ((flags & 0x010) ? 'A' : 'a'), ((flags & 0x040) ? 'Z' : 'z'), ((flags & 0x080) ? 'S' : 's'), ((flags & 0x400) ? 'D' : 'd'), ((flags & 0x800) ? 'O' : 'o')); return buf; } //------------------------------------------------------------------------------ // Name: flags_to_string // Desc: returns the flags in a string form appropriate for this platform //------------------------------------------------------------------------------ QString PlatformState::flags_to_string() const { return flags_to_string(flags()); } //------------------------------------------------------------------------------ // Name: value // Desc: returns a Register object which represents the register with the name // supplied //------------------------------------------------------------------------------ Register PlatformState::value(const QString ®) const { const QString lreg = reg.toLower(); #if defined(EDB_X86) if (lreg == "eax") return Register("eax", regs_.r_eax, Register::TYPE_GPR); else if (lreg == "ebx") return Register("ebx", regs_.r_ebx, Register::TYPE_GPR); else if (lreg == "ecx") return Register("ecx", regs_.r_ecx, Register::TYPE_GPR); else if (lreg == "edx") return Register("edx", regs_.r_edx, Register::TYPE_GPR); else if (lreg == "ebp") return Register("ebp", regs_.r_ebp, Register::TYPE_GPR); else if (lreg == "esp") return Register("esp", regs_.r_esp, Register::TYPE_GPR); else if (lreg == "esi") return Register("esi", regs_.r_esi, Register::TYPE_GPR); else if (lreg == "edi") return Register("edi", regs_.r_edi, Register::TYPE_GPR); else if (lreg == "eip") return Register("eip", regs_.r_eip, Register::TYPE_IP); else if (lreg == "ax") return Register("ax", regs_.r_eax & 0xffff, Register::TYPE_GPR); else if (lreg == "bx") return Register("bx", regs_.r_ebx & 0xffff, Register::TYPE_GPR); else if (lreg == "cx") return Register("cx", regs_.r_ecx & 0xffff, Register::TYPE_GPR); else if (lreg == "dx") return Register("dx", regs_.r_edx & 0xffff, Register::TYPE_GPR); else if (lreg == "bp") return Register("bp", regs_.r_ebp & 0xffff, Register::TYPE_GPR); else if (lreg == "sp") return Register("sp", regs_.r_esp & 0xffff, Register::TYPE_GPR); else if (lreg == "si") return Register("si", regs_.r_esi & 0xffff, Register::TYPE_GPR); else if (lreg == "di") return Register("di", regs_.r_edi & 0xffff, Register::TYPE_GPR); else if (lreg == "al") return Register("al", regs_.r_eax & 0xff, Register::TYPE_GPR); else if (lreg == "bl") return Register("bl", regs_.r_ebx & 0xff, Register::TYPE_GPR); else if (lreg == "cl") return Register("cl", regs_.r_ecx & 0xff, Register::TYPE_GPR); else if (lreg == "dl") return Register("dl", regs_.r_edx & 0xff, Register::TYPE_GPR); else if (lreg == "ah") return Register("ah", (regs_.r_eax >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "bh") return Register("bh", (regs_.r_ebx >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "ch") return Register("ch", (regs_.r_ecx >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "dh") return Register("dh", (regs_.r_edx >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "cs") return Register("cs", regs_.r_cs, Register::TYPE_SEG); else if (lreg == "ds") return Register("ds", regs_.r_ds, Register::TYPE_SEG); else if (lreg == "es") return Register("es", regs_.r_es, Register::TYPE_SEG); else if (lreg == "fs") return Register("fs", regs_.r_fs, Register::TYPE_SEG); else if (lreg == "gs") return Register("gs", regs_.r_gs, Register::TYPE_SEG); else if (lreg == "ss") return Register("ss", regs_.r_ss, Register::TYPE_SEG); else if (lreg == "fs_base") return Register("fs_base", fs_base, Register::TYPE_SEG); else if (lreg == "gs_base") return Register("gs_base", gs_base, Register::TYPE_SEG); else if (lreg == "eflags") return Register("eflags", regs_.r_eflags, Register::TYPE_COND); #elif defined(EDB_X86_64) if (lreg == "rax") return Register("rax", regs_.r_rax, Register::TYPE_GPR); else if (lreg == "rbx") return Register("rbx", regs_.r_rbx, Register::TYPE_GPR); else if (lreg == "rcx") return Register("rcx", regs_.r_rcx, Register::TYPE_GPR); else if (lreg == "rdx") return Register("rdx", regs_.r_rdx, Register::TYPE_GPR); else if (lreg == "rbp") return Register("rbp", regs_.r_rbp, Register::TYPE_GPR); else if (lreg == "rsp") return Register("rsp", regs_.r_rsp, Register::TYPE_GPR); else if (lreg == "rsi") return Register("rsi", regs_.r_rsi, Register::TYPE_GPR); else if (lreg == "rdi") return Register("rdi", regs_.r_rdi, Register::TYPE_GPR); else if (lreg == "rip") return Register("rip", regs_.r_rip, Register::TYPE_IP); else if (lreg == "r8") return Register("r8", regs_.r_r8, Register::TYPE_GPR); else if (lreg == "r9") return Register("r9", regs_.r_r9, Register::TYPE_GPR); else if (lreg == "r10") return Register("r10", regs_.r_r10, Register::TYPE_GPR); else if (lreg == "r11") return Register("r11", regs_.r_r11, Register::TYPE_GPR); else if (lreg == "r12") return Register("r12", regs_.r_r12, Register::TYPE_GPR); else if (lreg == "r13") return Register("r13", regs_.r_r13, Register::TYPE_GPR); else if (lreg == "r14") return Register("r14", regs_.r_r14, Register::TYPE_GPR); else if (lreg == "r15") return Register("r15", regs_.r_r15, Register::TYPE_GPR); else if (lreg == "eax") return Register("eax", regs_.r_rax & 0xffffffff, Register::TYPE_GPR); else if (lreg == "ebx") return Register("ebx", regs_.r_rbx & 0xffffffff, Register::TYPE_GPR); else if (lreg == "ecx") return Register("ecx", regs_.r_rcx & 0xffffffff, Register::TYPE_GPR); else if (lreg == "edx") return Register("edx", regs_.r_rdx & 0xffffffff, Register::TYPE_GPR); else if (lreg == "ebp") return Register("ebp", regs_.r_rbp & 0xffffffff, Register::TYPE_GPR); else if (lreg == "esp") return Register("esp", regs_.r_rsp & 0xffffffff, Register::TYPE_GPR); else if (lreg == "esi") return Register("esi", regs_.r_rsi & 0xffffffff, Register::TYPE_GPR); else if (lreg == "edi") return Register("edi", regs_.r_rdi & 0xffffffff, Register::TYPE_GPR); else if (lreg == "r8d") return Register("r8d", regs_.r_r8 & 0xffffffff, Register::TYPE_GPR); else if (lreg == "r9d") return Register("r9d", regs_.r_r9 & 0xffffffff, Register::TYPE_GPR); else if (lreg == "r10d") return Register("r10d", regs_.r_r10 & 0xffffffff, Register::TYPE_GPR); else if (lreg == "r11d") return Register("r11d", regs_.r_r11 & 0xffffffff, Register::TYPE_GPR); else if (lreg == "r12d") return Register("r12d", regs_.r_r12 & 0xffffffff, Register::TYPE_GPR); else if (lreg == "r13d") return Register("r13d", regs_.r_r13 & 0xffffffff, Register::TYPE_GPR); else if (lreg == "r14d") return Register("r14d", regs_.r_r14 & 0xffffffff, Register::TYPE_GPR); else if (lreg == "r15d") return Register("r15d", regs_.r_r15 & 0xffffffff, Register::TYPE_GPR); else if (lreg == "ax") return Register("ax", regs_.r_rax & 0xffff, Register::TYPE_GPR); else if (lreg == "bx") return Register("bx", regs_.r_rbx & 0xffff, Register::TYPE_GPR); else if (lreg == "cx") return Register("cx", regs_.r_rcx & 0xffff, Register::TYPE_GPR); else if (lreg == "dx") return Register("dx", regs_.r_rdx & 0xffff, Register::TYPE_GPR); else if (lreg == "bp") return Register("bp", regs_.r_rbp & 0xffff, Register::TYPE_GPR); else if (lreg == "sp") return Register("sp", regs_.r_rsp & 0xffff, Register::TYPE_GPR); else if (lreg == "si") return Register("si", regs_.r_rsi & 0xffff, Register::TYPE_GPR); else if (lreg == "di") return Register("di", regs_.r_rdi & 0xffff, Register::TYPE_GPR); else if (lreg == "r8w") return Register("r8w", regs_.r_r8 & 0xffff, Register::TYPE_GPR); else if (lreg == "r9w") return Register("r9w", regs_.r_r9 & 0xffff, Register::TYPE_GPR); else if (lreg == "r10w") return Register("r10w", regs_.r_r10 & 0xffff, Register::TYPE_GPR); else if (lreg == "r11w") return Register("r11w", regs_.r_r11 & 0xffff, Register::TYPE_GPR); else if (lreg == "r12w") return Register("r12w", regs_.r_r12 & 0xffff, Register::TYPE_GPR); else if (lreg == "r13w") return Register("r13w", regs_.r_r13 & 0xffff, Register::TYPE_GPR); else if (lreg == "r14w") return Register("r14w", regs_.r_r14 & 0xffff, Register::TYPE_GPR); else if (lreg == "r15w") return Register("r15w", regs_.r_r15 & 0xffff, Register::TYPE_GPR); else if (lreg == "al") return Register("al", regs_.r_rax & 0xff, Register::TYPE_GPR); else if (lreg == "bl") return Register("bl", regs_.r_rbx & 0xff, Register::TYPE_GPR); else if (lreg == "cl") return Register("cl", regs_.r_rcx & 0xff, Register::TYPE_GPR); else if (lreg == "dl") return Register("dl", regs_.r_rdx & 0xff, Register::TYPE_GPR); else if (lreg == "ah") return Register("ah", (regs_.r_rax >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "bh") return Register("bh", (regs_.r_rbx >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "ch") return Register("ch", (regs_.r_rcx >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "dh") return Register("dh", (regs_.r_rdx >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "spl") return Register("spl", (regs_.r_rsp >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "bpl") return Register("bpl", (regs_.r_rbp >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "sil") return Register("sil", (regs_.r_rsi >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "dil") return Register("dil", (regs_.r_rdi >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "r8b") return Register("r8b", regs_.r_r8 & 0xff, Register::TYPE_GPR); else if (lreg == "r9b") return Register("r9b", regs_.r_r9 & 0xff, Register::TYPE_GPR); else if (lreg == "r10b") return Register("r10b", regs_.r_r10 & 0xff, Register::TYPE_GPR); else if (lreg == "r11b") return Register("r11b", regs_.r_r11 & 0xff, Register::TYPE_GPR); else if (lreg == "r12b") return Register("r12b", regs_.r_r12 & 0xff, Register::TYPE_GPR); else if (lreg == "r13b") return Register("r13b", regs_.r_r13 & 0xff, Register::TYPE_GPR); else if (lreg == "r14b") return Register("r14b", regs_.r_r14 & 0xff, Register::TYPE_GPR); else if (lreg == "r15b") return Register("r15b", regs_.r_r15 & 0xff, Register::TYPE_GPR); else if (lreg == "cs") return Register("cs", regs_.r_cs, Register::TYPE_SEG); else if (lreg == "ds") return Register("ds", regs_.r_ds, Register::TYPE_SEG); else if (lreg == "es") return Register("es", regs_.r_es, Register::TYPE_SEG); else if (lreg == "fs") return Register("fs", regs_.r_fs, Register::TYPE_SEG); else if (lreg == "gs") return Register("gs", regs_.r_gs, Register::TYPE_SEG); else if (lreg == "ss") return Register("ss", regs_.r_ss, Register::TYPE_SEG); else if (lreg == "fs_base") return Register("fs_base", fs_base, Register::TYPE_SEG); else if (lreg == "gs_base") return Register("gs_base", gs_base, Register::TYPE_SEG); else if (lreg == "rflags") return Register("rflags", regs_.r_rflags, Register::TYPE_COND); #endif return Register(); } //------------------------------------------------------------------------------ // Name: frame_pointer // Desc: returns what is conceptually the frame pointer for this platform //------------------------------------------------------------------------------ edb::address_t PlatformState::frame_pointer() const { #if defined(EDB_X86) return regs_.r_ebp; #elif defined(EDB_X86_64) return regs_.r_rbp; #endif } //------------------------------------------------------------------------------ // Name: instruction_pointer // Desc: returns the instruction pointer for this platform //------------------------------------------------------------------------------ edb::address_t PlatformState::instruction_pointer() const { #if defined(EDB_X86) return regs_.r_eip; #elif defined(EDB_X86_64) return regs_.r_rip; #endif } //------------------------------------------------------------------------------ // Name: stack_pointer // Desc: returns the stack pointer for this platform //------------------------------------------------------------------------------ edb::address_t PlatformState::stack_pointer() const { #if defined(EDB_X86) return regs_.r_esp; #elif defined(EDB_X86_64) return regs_.r_rsp; #endif } //------------------------------------------------------------------------------ // Name: debug_register // Desc: //------------------------------------------------------------------------------ edb::reg_t PlatformState::debug_register(int n) const { return dr_[n]; } //------------------------------------------------------------------------------ // Name: flags // Desc: //------------------------------------------------------------------------------ edb::reg_t PlatformState::flags() const { #if defined(EDB_X86) return regs_.r_eflags; #elif defined(EDB_X86_64) return regs_.r_rflags; #endif } //------------------------------------------------------------------------------ // Name: fpu_register // Desc: //------------------------------------------------------------------------------ long double PlatformState::fpu_register(int n) const { return reinterpret_cast(&fpregs_)[n]; } //------------------------------------------------------------------------------ // Name: adjust_stack // Desc: //------------------------------------------------------------------------------ void PlatformState::adjust_stack(int bytes) { #if defined(EDB_X86) regs_.r_esp += bytes; #elif defined(EDB_X86_64) regs_.r_rsp += bytes; #endif } //------------------------------------------------------------------------------ // Name: clear // Desc: //------------------------------------------------------------------------------ void PlatformState::clear() { memset(®s_, 0, sizeof(regs_)); memset(&fpregs_, 0, sizeof(fpregs_)); memset(&dr_, 0, sizeof(dr_)); #if defined(EDB_X86) fs_base = 0; gs_base = 0; #endif } //------------------------------------------------------------------------------ // Name: set_debug_register // Desc: //------------------------------------------------------------------------------ void PlatformState::set_debug_register(int n, edb::reg_t value) { dr_[n] = value; } //------------------------------------------------------------------------------ // Name: set_flags // Desc: //------------------------------------------------------------------------------ void PlatformState::set_flags(edb::reg_t flags) { #if defined(EDB_X86) regs_.r_eflags = flags; #elif defined(EDB_X86_64) regs_.r_rflags = flags; #endif } //------------------------------------------------------------------------------ // Name: set_instruction_pointer // Desc: //------------------------------------------------------------------------------ void PlatformState::set_instruction_pointer(edb::address_t value) { #if defined(EDB_X86) regs_.r_eip = value; #elif defined(EDB_X86_64) regs_.r_rip = value; #endif } //------------------------------------------------------------------------------ // Name: set_register // Desc: //------------------------------------------------------------------------------ void PlatformState::set_register(const QString &name, edb::reg_t value) { const QString lreg = name.toLower(); #if defined(EDB_X86) if (lreg == "eax") { regs_.r_eax = value; } else if (lreg == "ebx") { regs_.r_ebx = value; } else if (lreg == "ecx") { regs_.r_ecx = value; } else if (lreg == "edx") { regs_.r_edx = value; } else if (lreg == "ebp") { regs_.r_ebp = value; } else if (lreg == "esp") { regs_.r_esp = value; } else if (lreg == "esi") { regs_.r_esi = value; } else if (lreg == "edi") { regs_.r_edi = value; } else if (lreg == "eip") { regs_.r_eip = value; } else if (lreg == "cs") { regs_.r_cs = value; } else if (lreg == "ds") { regs_.r_ds = value; } else if (lreg == "es") { regs_.r_es = value; } else if (lreg == "fs") { regs_.r_fs = value; } else if (lreg == "gs") { regs_.r_gs = value; } else if (lreg == "ss") { regs_.r_ss = value; } else if (lreg == "eflags") { regs_.r_eflags = value; } #elif defined(EDB_X86_64) if (lreg == "rax") { regs_.r_rax = value; } else if (lreg == "rbx") { regs_.r_rbx = value; } else if (lreg == "rcx") { regs_.r_rcx = value; } else if (lreg == "rdx") { regs_.r_rdx = value; } else if (lreg == "rbp") { regs_.r_rbp = value; } else if (lreg == "rsp") { regs_.r_rsp = value; } else if (lreg == "rsi") { regs_.r_rsi = value; } else if (lreg == "rdi") { regs_.r_rdi = value; } else if (lreg == "r8") { regs_.r_r8 = value; } else if (lreg == "r9") { regs_.r_r9 = value; } else if (lreg == "r10") { regs_.r_r10 = value; } else if (lreg == "r11") { regs_.r_r11 = value; } else if (lreg == "r12") { regs_.r_r12 = value; } else if (lreg == "r13") { regs_.r_r13 = value; } else if (lreg == "r14") { regs_.r_r14 = value; } else if (lreg == "r15") { regs_.r_r15 = value; } else if (lreg == "rip") { regs_.r_rip = value; } else if (lreg == "cs") { regs_.r_cs = value; } else if (lreg == "ds") { regs_.r_ds = value; } else if (lreg == "es") { regs_.r_es = value; } else if (lreg == "fs") { regs_.r_fs = value; } else if (lreg == "gs") { regs_.r_gs = value; } else if (lreg == "ss") { regs_.r_ss = value; } else if (lreg == "rflags") { regs_.r_rflags = value; } #endif } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ quint64 PlatformState::mmx_register(int n) const { Q_UNUSED(n) return 0; } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ QByteArray PlatformState::xmm_register(int n) const { Q_UNUSED(n) return QByteArray(); } } edb-debugger/plugins/DebuggerCore/unix/openbsd/PlatformRegion.h0000644000175000017500000000351013765535463024464 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PLATFORM_REGION_H_20120330_ #define PLATFORM_REGION_H_20120330_ #include "IRegion.h" #include #include namespace DebuggerCore { class PlatformRegion : public IRegion { Q_DECLARE_TR_FUNCTIONS(PlatformRegion) public: PlatformRegion(edb::address_t start, edb::address_t end, edb::address_t base, const QString &name, permissions_t permissions); ~PlatformRegion() override = default; public: IRegion *clone() const override; public: bool accessible() const override; bool readable() const override; bool writable() const override; bool executable() const override; size_t size() const override; public: void set_permissions(bool read, bool write, bool execute) override; void set_start(edb::address_t address) override; void set_end(edb::address_t address) override; public: edb::address_t start() const override; edb::address_t end() const override; edb::address_t base() const override; QString name() const override; permissions_t permissions() const override; private: edb::address_t start_; edb::address_t end_; edb::address_t base_; QString name_; permissions_t permissions_; }; } #endif edb-debugger/plugins/DebuggerCore/unix/Posix.cpp0000644000175000017500000001251313765535463021542 0ustar eteraneteran #include "Posix.h" #include #include #include #include #include #include #include #ifdef Q_OS_LINUX #include // being very conservative for now, technically this could be // as low as 2.6.22 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) #define USE_SIGTIMEDWAIT #endif #endif namespace DebuggerCorePlugin { namespace { #if !defined(USE_SIGTIMEDWAIT) int selfpipe[2]; struct sigaction old_action; /** * @brief sigchld_handler * @param sig * @param info * @param p */ void sigchld_handler(int sig, siginfo_t *info, void *p) { if (sig == SIGCHLD) { Posix::write(selfpipe[1], " ", sizeof(char)); } // load as volatile volatile struct sigaction *vsa = &old_action; if (old_action.sa_flags & SA_SIGINFO) { using old_action_type = void (*)(int, siginfo_t *, void *); old_action_type prev_action = vsa->sa_sigaction; if (prev_action) { prev_action(sig, info, p); } } else { using old_action_type = void (*)(int); old_action_type prev_action = vsa->sa_handler; if (prev_action && prev_action != SIG_IGN) { prev_action(sig); } } } struct timeval duration_to_timeval(std::chrono::milliseconds msecs) { struct timeval tv; tv.tv_sec = (msecs.count() / 1000); tv.tv_usec = (msecs.count() % 1000) * 1000; return tv; } #else timespec duration_to_timespec(std::chrono::milliseconds msecs) { struct timespec ts; ts.tv_sec = (msecs.count() / 1000); ts.tv_nsec = (msecs.count() % 1000) * 1000000; return ts; } #endif } namespace detail { namespace { /** * @brief sigtimedwait * @param set * @param info * @param timeout * @return */ int sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec *timeout) { int ret; do { ret = ::sigtimedwait(set, info, timeout); } while (ret == -1 && errno == EINTR); return ret; } } } /** * @brief Posix::read * @param fd * @param buf * @param count * @return */ ssize_t Posix::read(int fd, void *buf, size_t count) { ssize_t ret; do { ret = ::read(fd, buf, count); } while (ret == -1 && errno == EINTR); return ret; } /** * @brief Posix::write * @param fd * @param buf * @param count * @return */ ssize_t Posix::write(int fd, const void *buf, size_t count) { ssize_t ret; do { ret = ::write(fd, buf, count); } while (ret == -1 && errno == EINTR); return ret; } /** * @brief Posix::select * @param nfds * @param readfds * @param writefds * @param exceptfds * @param timeout * @return */ int Posix::select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) { int ret; do { ret = ::select(nfds, readfds, writefds, exceptfds, timeout); } while (ret == -1 && errno == EINTR); return ret; } /** * @brief Posix::waitpid * @param pid * @param status * @param options * @return */ pid_t Posix::waitpid(pid_t pid, int *status, int options) { pid_t ret; do { ret = ::waitpid(pid, status, options); } while (ret == -1 && errno == EINTR); return ret; } #if !defined(USE_SIGTIMEDWAIT) /** * similar to select but has the timeout specified as a quantity of msecs * * @brief Posix::select_ex * @param nfds * @param readfds * @param writefds * @param exceptfds * @param msecs - 0 means wait forever. * @return */ int Posix::select_ex(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, std::chrono::milliseconds msecs) { if (msecs.count() != 0) { struct timeval tv = duration_to_timeval(msecs); return Posix::select(nfds, readfds, writefds, exceptfds, &tv); } else { return Posix::select(nfds, readfds, writefds, exceptfds, nullptr); } } #endif /** * @brief Posix::wait_for_sigchld * @param msecs * @return */ bool Posix::wait_for_sigchld(std::chrono::milliseconds msecs) { #if !defined(USE_SIGTIMEDWAIT) fd_set rfds; FD_ZERO(&rfds); FD_SET(selfpipe[0], &rfds); if (Posix::select_ex(selfpipe[0] + 1, &rfds, nullptr, nullptr, msecs) == 0) { return true; } char ch; if (Posix::read(selfpipe[0], &ch, sizeof(char)) == -1) { return true; } return false; #else sigset_t mask; siginfo_t info; struct timespec ts = duration_to_timespec(msecs); sigemptyset(&mask); sigaddset(&mask, SIGCHLD); return detail::sigtimedwait(&mask, &info, &ts) == SIGCHLD; #endif } /** * @brief Posix::initialize */ void Posix::initialize() { #if !defined(USE_SIGTIMEDWAIT) // HACK(eteran): so, the first time we create a QProcess, it will hook SIGCHLD // unfortunately, in Qt5 it doesn't seem to call our handler // so we do this to force it to hook BEFORE we do, letting us // get the first crack at the signal, then we call the one that // Qt installed. auto p = new QProcess(nullptr); p->start("/bin/true"); // create a pipe and make it non-blocking int r = ::pipe(selfpipe); Q_UNUSED(r) ::fcntl(selfpipe[0], F_SETFL, ::fcntl(selfpipe[0], F_GETFL) | O_NONBLOCK); ::fcntl(selfpipe[1], F_SETFL, ::fcntl(selfpipe[1], F_GETFL) | O_NONBLOCK); // setup a signal handler struct sigaction new_action = {}; new_action.sa_sigaction = sigchld_handler; new_action.sa_flags = SA_RESTART | SA_SIGINFO; sigemptyset(&new_action.sa_mask); sigaction(SIGCHLD, &new_action, &old_action); #else // TODO(eteran): the man pages mention blocking the signal we want to catch // but I'm not sure if it is necessary for this use case... #endif } } edb-debugger/plugins/DebuggerCore/unix/freebsd/0000755000175000017500000000000013765535463021344 5ustar eteraneteranedb-debugger/plugins/DebuggerCore/unix/freebsd/DebuggerCore.cpp0000644000175000017500000003010613765535463024405 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "DebuggerCore.h" #include "PlatformEvent.h" #include "PlatformRegion.h" #include "PlatformState.h" #include "State.h" #include "string_hash.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace DebuggerCorePlugin { namespace { constexpr uint64_t PageSize = 0x1000; void SET_OK(bool &ok, long value) { ok = (value != -1) || (errno == 0); } int resume_code(int status) { if (WIFSIGNALED(status)) { return WTERMSIG(status); } else if (WIFSTOPPED(status)) { return WSTOPSIG(status); } return 0; } } //------------------------------------------------------------------------------ // Name: DebuggerCore // Desc: constructor //------------------------------------------------------------------------------ DebuggerCore::DebuggerCore() { #if defined(_SC_PAGESIZE) page_size_ = sysconf(_SC_PAGESIZE); #elif defined(_SC_PAGE_SIZE) page_size_ = sysconf(_SC_PAGE_SIZE); #else page_size_ = PageSize; #endif } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ bool DebuggerCore::has_extension(quint64 ext) const { Q_UNUSED(ext) return false; } //------------------------------------------------------------------------------ // Name: page_size // Desc: returns the size of a page on this system //------------------------------------------------------------------------------ size_t DebuggerCore::page_size() const { return page_size_; } //------------------------------------------------------------------------------ // Name: ~DebuggerCore // Desc: //------------------------------------------------------------------------------ DebuggerCore::~DebuggerCore() { detach(); } //------------------------------------------------------------------------------ // Name: wait_debug_event // Desc: waits for a debug event, msecs is a timeout // it will return false if an error or timeout occurs //------------------------------------------------------------------------------ std::shared_ptr DebuggerCore::wait_debug_event(int msecs) { if (attached()) { int status; bool timeout; const edb::tid_t tid = Posix::waitpid_timeout(pid(), &status, 0, msecs, &timeout); if (!timeout) { if (tid > 0) { // normal event auto e = std::make_shared(); e->pid = pid(); e->tid = tid; e->status = status; char errbuf[_POSIX2_LINE_MAX]; if (kvm_t *const kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf)) { int rc; struct kinfo_proc *const proc = kvm_getprocs(kd, KERN_PROC_PID, pid(), &rc); struct proc p; kvm_read(kd, (unsigned long)proc->ki_paddr, &p, sizeof(p)); struct ksiginfo siginfo; kvm_read(kd, (unsigned long)p.p_ksi, &siginfo, sizeof(siginfo)); // TODO: why doesn't this get the fault address correctly? // perhaps I need to target the tid instead? e->fault_code_ = siginfo.ksi_code; e->fault_address_ = siginfo.ksi_addr; //printf("ps_sig : %d\n", siginfo.ksi_signo); //printf("ps_type : %d\n", p.p_stype); kvm_close(kd); } else { e->fault_code_ = 0; e->fault_address_ = 0; } active_thread_ = tid; threads_[tid].status = status; return e; } } } return nullptr; } //------------------------------------------------------------------------------ // Name: read_data // Desc: //------------------------------------------------------------------------------ long DebuggerCore::read_data(edb::address_t address, bool *ok) { Q_ASSERT(ok); errno = 0; const long v = ptrace(PT_READ_D, pid(), reinterpret_cast(address), 0); SET_OK(*ok, v); return v; } //------------------------------------------------------------------------------ // Name: write_data // Desc: //------------------------------------------------------------------------------ bool DebuggerCore::write_data(edb::address_t address, long value) { return ptrace(PT_WRITE_D, pid(), reinterpret_cast(address), value) != -1; } //------------------------------------------------------------------------------ // Name: attach // Desc: //------------------------------------------------------------------------------ bool DebuggerCore::attach(edb::pid_t pid) { detach(); const long ret = ptrace(PT_ATTACH, pid, 0, 0); if (ret == 0) { pid_ = pid; active_thread_ = pid; threads_.clear(); threads_.insert(pid, thread_info()); // TODO: attach to all of the threads } return ret == 0; } //------------------------------------------------------------------------------ // Name: detach // Desc: //------------------------------------------------------------------------------ void DebuggerCore::detach() { if (attached()) { // TODO: do i need to stop each thread first, and wait for them? clear_breakpoints(); for (auto it = threads_.begin(); it != threads_.end(); ++it) { ptrace(PT_DETACH, it.key(), 0, 0); } pid_ = 0; threads_.clear(); } } //------------------------------------------------------------------------------ // Name: kill // Desc: //------------------------------------------------------------------------------ void DebuggerCore::kill() { if (attached()) { clear_breakpoints(); ptrace(PT_KILL, pid(), 0, 0); Posix::waitpid(pid(), 0, WAIT_ANY); pid_ = 0; threads_.clear(); } } //------------------------------------------------------------------------------ // Name: pause // Desc: stops *all* threads of a process //------------------------------------------------------------------------------ void DebuggerCore::pause() { if (attached()) { for (auto it = threads_.begin(); it != threads_.end(); ++it) { ::kill(it.key(), SIGSTOP); } } } //------------------------------------------------------------------------------ // Name: resume // Desc: //------------------------------------------------------------------------------ void DebuggerCore::resume(edb::EVENT_STATUS status) { // TODO: assert that we are paused if (attached()) { if (status != edb::DEBUG_STOP) { const edb::tid_t tid = active_thread(); const int code = (status == edb::DEBUG_EXCEPTION_NOT_HANDLED) ? resume_code(threads_[tid].status) : 0; ptrace(PT_CONTINUE, tid, reinterpret_cast(1), code); } } } //------------------------------------------------------------------------------ // Name: open // Desc: //------------------------------------------------------------------------------ bool DebuggerCore::open(const QString &path, const QString &cwd, const QList &args, const QString &tty) { detach(); pid_t pid; switch (pid = fork()) { case 0: // we are in the child now... // set ourselves (the child proc) up to be traced ptrace(PT_TRACE_ME, 0, 0, 0); // redirect it's I/O if (!tty.isEmpty()) { FILE *const std_out = freopen(qPrintable(tty), "r+b", stdout); FILE *const std_in = freopen(qPrintable(tty), "r+b", stdin); FILE *const std_err = freopen(qPrintable(tty), "r+b", stderr); Q_UNUSED(std_out) Q_UNUSED(std_in) Q_UNUSED(std_err) } // do the actual exec execute_process(path, cwd, args); // we should never get here! abort(); break; case -1: // error! pid_ = 0; return false; default: // parent do { threads_.clear(); int status; if (Posix::waitpid(pid, &status, 0) == -1) { return false; } // the very first event should be a STOP of type SIGTRAP if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP) { detach(); return false; } // setup the first event data for the primary thread threads_.insert(pid, thread_info()); pid_ = pid; active_thread_ = pid; threads_[pid].status = status; return true; } while (0); break; } } //------------------------------------------------------------------------------ // Name: set_active_thread // Desc: //------------------------------------------------------------------------------ void DebuggerCore::set_active_thread(edb::tid_t tid) { Q_ASSERT(threads_.contains(tid)); active_thread_ = tid; } //------------------------------------------------------------------------------ // Name: create_state // Desc: //------------------------------------------------------------------------------ std::unique_ptr DebuggerCore::create_state() const { return std::make_unique(); } //------------------------------------------------------------------------------ // Name: enumerate_processes // Desc: //------------------------------------------------------------------------------ QMap DebuggerCore::enumerate_processes() const { QMap ret; char ebuffer[_POSIX2_LINE_MAX]; int numprocs; if (kvm_t *const kaccess = kvm_openfiles(_PATH_DEVNULL, _PATH_DEVNULL, 0, O_RDONLY, ebuffer)) { if (struct kinfo_proc *const kprocaccess = kvm_getprocs(kaccess, KERN_PROC_ALL, 0, &numprocs)) { for (int i = 0; i < numprocs; ++i) { ProcessInfo procInfo; procInfo.pid = kprocaccess[i].ki_pid; procInfo.uid = kprocaccess[i].ki_uid; procInfo.name = kprocaccess[i].ki_comm; ret.insert(procInfo.pid, procInfo); } } kvm_close(kaccess); } else { QMessageBox::warning(0, "Error Listing Processes", ebuffer); } return ret; } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ edb::pid_t DebuggerCore::parent_pid(edb::pid_t pid) const { // TODO: implement this return -1; } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ quint64 DebuggerCore::cpu_type() const { #ifdef EDB_X86 return edb::string_hash<'x', '8', '6'>::value; #elif defined(EDB_X86_64) return edb::string_hash<'x', '8', '6', '-', '6', '4'>::value; #endif } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ QString DebuggerCore::format_pointer(edb::address_t address) const { char buf[32]; #ifdef EDB_X86 qsnprintf(buf, sizeof(buf), "%08x", address); #elif defined(EDB_X86_64) qsnprintf(buf, sizeof(buf), "%016llx", address); #endif return buf; } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ QString DebuggerCore::stack_pointer() const { #ifdef EDB_X86 return "esp"; #elif defined(EDB_X86_64) return "rsp"; #endif } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ QString DebuggerCore::frame_pointer() const { #ifdef EDB_X86 return "ebp"; #elif defined(EDB_X86_64) return "rbp"; #endif } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ QString DebuggerCore::instruction_pointer() const { #ifdef EDB_X86 return "eip"; #elif defined(EDB_X86_64) return "rip"; #endif } } edb-debugger/plugins/DebuggerCore/unix/freebsd/PlatformThread.cpp0000644000175000017500000000324113765535463024764 0ustar eteraneteran/* Copyright (C) 2018 - 2018 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "PlatformThread.h" namespace DebuggerCorePlugin { void PlatformThread::get_state(State *state) { auto state_impl = static_cast(state->impl_); ptrace(PT_GETREGS, tid_, reinterpret_cast(&state_impl->regs_), 0); } void PlatformThread::set_state(const State &state) { auto state_impl = static_cast(state->impl_); ptrace(PT_SETREGS, tid_, reinterpret_cast(&state_impl->regs_), 0); } Status PlatformThread::step(edb::EVENT_STATUS status) { if (status != edb::DEBUG_STOP) { const int code = (status == edb::DEBUG_EXCEPTION_NOT_HANDLED) ? resume_code(status_) : 0; ptrace(PT_STEP, tid_, reinterpret_cast(1), code); } return Status::Ok; } Status PlatformThread::resume(edb::EVENT_STATUS status) { if (status != edb::DEBUG_STOP) { const int code = (status == edb::DEBUG_EXCEPTION_NOT_HANDLED) ? resume_code(status_) : 0; ptrace(PT_CONTINUE, tid_, reinterpret_cast(1), code); } return Status::Ok; } } edb-debugger/plugins/DebuggerCore/unix/freebsd/PlatformCommon.h0000644000175000017500000000012113765535463024444 0ustar eteraneteran #ifndef PLATFORM_COMMON_H_20181225_ #define PLATFORM_COMMON_H_20181225_ #endif edb-debugger/plugins/DebuggerCore/unix/freebsd/DebuggerCore.h0000644000175000017500000000531313765535463024054 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DEBUGGER_CORE_H_20090529_ #define DEBUGGER_CORE_H_20090529_ #include "DebuggerCoreBase.h" #include namespace DebuggerCorePlugin { class DebuggerCore : public DebuggerCoreBase { Q_OBJECT Q_PLUGIN_METADATA(IID "edb.IDebugger/1.0") Q_INTERFACES(IDebugger) Q_CLASSINFO("author", "Evan Teran") Q_CLASSINFO("url", "http://www.codef00.com") friend class PlatformProcess; friend class PlatformThread; public: DebuggerCore(); ~DebuggerCore() override; public: std::size_t pointer_size() const override; size_t page_size() const override; bool has_extension(quint64 ext) const override; std::shared_ptr wait_debug_event(int msecs) override; Status attach(edb::pid_t pid) override; Status detach() override; void kill() override; Status open(const QString &path, const QString &cwd, const QList &args, const QString &tty) override; MeansOfCapture lastMeansOfCapture() const override; void set_ignored_exceptions(const QList &exceptions) override; public: QMap exceptions() const override; QString exceptionName(qlonglong value) override; qlonglong exceptionValue(const QString &name) override; public: edb::pid_t parent_pid(edb::pid_t pid) const override; public: std::unique_ptr create_state() const override; public: quint64 cpu_type() const override; private: QMap> enumerate_processes() const override; public: QString stack_pointer() const override; QString frame_pointer() const override; QString instruction_pointer() const override; QString flag_register() const override; public: IProcess *process() const override; private: virtual long read_data(edb::address_t address, bool *ok); virtual bool write_data(edb::address_t address, long value); private: struct thread_info { public: thread_info() = default; thread_info(int s) : status(s) { } int status = 0; }; using threadmap_t = QHash; edb::address_t page_size_; threadmap_t threads_; }; } #endif edb-debugger/plugins/DebuggerCore/unix/freebsd/PlatformState.h0000644000175000017500000000504313765535463024304 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PLATFORM_STATE_H_20110330_ #define PLATFORM_STATE_H_20110330_ #include "IState.h" #include "Types.h" #include "edb.h" #include #include #include namespace DebuggerCorePlugin { class PlatformState : public IState { friend class DebuggerCore; friend class PlatformThread; public: PlatformState(); public: std::unique_ptr clone() const override; public: QString flagsToString() const override; QString flagsToString(edb::reg_t flags) const override; Register value(const QString ®) const override; Register instructionPointeRregister() const override; Register flags_register() const override; edb::address_t frame_pointer() const override; edb::address_t instruction_pointer() const override; edb::address_t stack_pointer() const override; edb::reg_t debug_register(size_t n) const override; edb::reg_t flags() const override; int fpu_stack_pointer() const override; edb::value80 fpu_register(size_t n) const override; bool fpu_register_is_empty(size_t n) const override; QString fpu_register_tag_string(size_t n) const override; edb::value16 fpu_control_word() const override; edb::value16 fpu_status_word() const override; edb::value16 fpu_tag_word() const override; void adjust_stack(int bytes) override; void clear() override; bool empty() const override; void set_debug_register(size_t n, edb::reg_t value) override; void set_flags(edb::reg_t flags) override; void set_instruction_pointer(edb::address_t value) override; void set_register(const Register ®) override; void set_register(const QString &name, edb::reg_t value) override; Register mmx_register(size_t n) const override; Register xmm_register(size_t n) const override; Register ymm_register(size_t n) const override; Register gp_register(size_t n) const override; private: reg regs_; fpreg fpregs_; dbreg dbregs_; }; } #endif edb-debugger/plugins/DebuggerCore/unix/freebsd/PlatformEvent.cpp0000644000175000017500000001730313765535463024642 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "PlatformEvent.h" #include "edb.h" #include #include #include #include #include // for the SIG* definitions #include #include #include #include #include #include #include #include #include #include #include namespace DebuggerCorePlugin { //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ PlatformEvent::PlatformEvent() : status(0), pid(-1), tid(-1), fault_address_(0), fault_code_(0) { } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ PlatformEvent *PlatformEvent::clone() const { return new PlatformEvent(*this); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ IDebugEvent::Message PlatformEvent::error_description() const { Q_ASSERT(is_error()); auto fault_address = reinterpret_cast(fault_address_); switch (code()) { case SIGSEGV: return Message( tr("Illegal Access Fault"), tr( "

The debugged application encountered a segmentation fault.
The address 0x%1 could not be accessed.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

") .arg(edb::v1::format_pointer(fault_address))); case SIGILL: return Message( tr("Illegal Instruction Fault"), tr( "

The debugged application attempted to execute an illegal instruction.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

")); case SIGFPE: switch (fault_code_) { case FPE_INTDIV: return Message( tr("Divide By Zero"), tr( "

The debugged application tried to divide an integer value by an integer divisor of zero.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

")); default: return Message( tr("Floating Point Exception"), tr( "

The debugged application encountered a floating-point exception.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

")); } case SIGABRT: return Message( tr("Application Aborted"), tr( "

The debugged application has aborted.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

")); case SIGBUS: return Message( tr("Bus Error"), tr( "

The debugged application tried to read or write data that is misaligned.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

")); #ifdef SIGSTKFLT case SIGSTKFLT: return Message( tr("Stack Fault"), tr( "

The debugged application encountered a stack fault.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

")); #endif case SIGPIPE: return Message( tr("Broken Pipe Fault"), tr( "

The debugged application encountered a broken pipe fault.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

")); default: return Message(); } } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ IDebugEvent::REASON PlatformEvent::reason() const { // this basically converts our value into a 'switchable' value for convenience if (stopped()) { return EVENT_STOPPED; } else if (terminated()) { return EVENT_TERMINATED; } else if (exited()) { return EVENT_EXITED; } else { return EVENT_UNKNOWN; } } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ IDebugEvent::TRAP_REASON PlatformEvent::trap_reason() const { switch (fault_code_) { case TRAP_TRACE: return TRAP_STEPPING; default: return TRAP_BREAKPOINT; } } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ bool PlatformEvent::exited() const { return WIFEXITED(status) != 0; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ bool PlatformEvent::is_error() const { if (stopped()) { switch (code()) { case SIGTRAP: case SIGSTOP: return false; case SIGSEGV: case SIGILL: case SIGFPE: case SIGABRT: case SIGBUS: #ifdef SIGSTKFLT case SIGSTKFLT: #endif case SIGPIPE: return true; default: return false; } } else { return false; } } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ bool PlatformEvent::is_kill() const { return stopped() && code() == SIGKILL; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ bool PlatformEvent::is_stop() const { return stopped() && code() == SIGSTOP; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ bool PlatformEvent::is_trap() const { return stopped() && code() == SIGTRAP; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ bool PlatformEvent::terminated() const { return WIFSIGNALED(status) != 0; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ bool PlatformEvent::stopped() const { return WIFSTOPPED(status) != 0; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ edb::pid_t PlatformEvent::process() const { return pid; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ edb::tid_t PlatformEvent::thread() const { return tid; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ int64_t PlatformEvent::code() const { if (stopped()) { return WSTOPSIG(status); } if (terminated()) { return WTERMSIG(status); } if (exited()) { return WEXITSTATUS(status); } return 0; } } edb-debugger/plugins/DebuggerCore/unix/freebsd/PlatformRegion.cpp0000644000175000017500000000443413765535463025005 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "PlatformRegion.h" #include "IDebugEventHandler.h" #include "IDebugger.h" #include "MemoryRegions.h" #include "State.h" #include "edb.h" #include #include #include namespace DebuggerCorePlugin { PlatformRegion::PlatformRegion(edb::address_t start, edb::address_t end, edb::address_t base, const QString &name, permissions_t permissions) : start_(start), end_(end), base_(base), name_(name), permissions_(permissions) { } IRegion *PlatformRegion::clone() const { return new PlatformRegion(start_, end_, base_, name_, permissions_); } bool PlatformRegion::accessible() const { return readable() || writable() || executable(); } bool PlatformRegion::readable() const { return (permissions_ & PROT_READ) != 0; } bool PlatformRegion::writable() const { return (permissions_ & PROT_WRITE) != 0; } bool PlatformRegion::executable() const { return (permissions_ & PROT_EXEC) != 0; } size_t PlatformRegion::size() const { return end_ - start_; } void PlatformRegion::set_permissions(bool read, bool write, bool execute) { Q_UNUSED(read) Q_UNUSED(write) Q_UNUSED(execute) } edb::address_t PlatformRegion::start() const { return start_; } edb::address_t PlatformRegion::end() const { return end_; } edb::address_t PlatformRegion::base() const { return base_; } QString PlatformRegion::name() const { return name_; } IRegion::permissions_t PlatformRegion::permissions() const { return permissions_; } void PlatformRegion::set_start(edb::address_t address) { start_ = address; } void PlatformRegion::set_end(edb::address_t address) { end_ = address; } } edb-debugger/plugins/DebuggerCore/unix/freebsd/PlatformProcess.cpp0000644000175000017500000000526113765535463025177 0ustar eteraneteran/* Copyright (C) 2015 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "PlatformProcess.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace DebuggerCorePlugin { QString PlatformProcess::executable() const { // TODO: implement this return QString(); } QString PlatformProcess::current_working_directory() const { // TODO(eteran): implement this return QString(); } QDateTime PlatformProcess::start_time() const { // TODO(eteran): implement this return QDateTime(); } QList PlatformProcess::loaded_modules() const { QList modules; // TODO(eteran): implement this return modules; } edb::address_t PlatformProcess::code_address() const { // TODO(eteran): implement this return 0; } edb::address_t PlatformProcess::data_address() const { // TODO(eteran): implement this return 0; } QList PlatformProcess::arguments() const { QList ret; // TODO(eteran): implement this return ret; } QList> PlatformProcess::regions() const { QList> regions; if (pid_ != 0) { char buffer[PATH_MAX] = {}; struct ptrace_vm_entry vm_entry; memset(&vm_entry, 0, sizeof(vm_entry)); vm_entry.pve_entry = 0; while (ptrace(PT_VM_ENTRY, pid_, reinterpret_cast(&vm_entry), NULL) == 0) { vm_entry.pve_path = buffer; vm_entry.pve_pathlen = sizeof(buffer); const edb::address_t start = vm_entry.pve_start; const edb::address_t end = vm_entry.pve_end; const edb::address_t base = vm_entry.pve_start - vm_entry.pve_offset; const QString name = vm_entry.pve_path; const IRegion::permissions_t permissions = vm_entry.pve_prot; regions.push_back(std::make_shared(start, end, base, name, permissions)); memset(buffer, 0, sizeof(buffer)); } } return regions; } } edb-debugger/plugins/DebuggerCore/unix/freebsd/PlatformProcess.h0000644000175000017500000000457013765535463024646 0ustar eteraneteran/* Copyright (C) 2015 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PLATFORM_PROCESS_H_20150517_ #define PLATFORM_PROCESS_H_20150517_ #include "IProcess.h" class PlatformProcess : public IProcess { public: // legal to call when not attached QDateTime start_time() const override; QList arguments() const override; QString current_working_directory() const override; QString executable() const override; edb::pid_t pid() const override; std::shared_ptr parent() const override; edb::address_t code_address() const override; edb::address_t data_address() const override; edb::address_t entry_point() const override; QList> regions() const override; edb::uid_t uid() const override; QString user() const override; QString name() const override; QList loaded_modules() const override; public: edb::address_t debug_pointer() const override; edb::address_t calculate_main() const override; public: // only legal to call when attached QList> threads() const override; std::shared_ptr current_thread() const override; void set_current_thread(IThread &thread) override; std::size_t write_bytes(edb::address_t address, const void *buf, size_t len) override; std::size_t patch_bytes(edb::address_t address, const void *buf, size_t len) override; std::size_t read_bytes(edb::address_t address, void *buf, size_t len) const override; std::size_t read_pages(edb::address_t address, void *buf, size_t count) const override; Status pause() override; Status resume(edb::EVENT_STATUS status) override; Status step(edb::EVENT_STATUS status) override; bool isPaused() const override; QMap patches() const override; private: edb::pid_t pid_; }; #endif edb-debugger/plugins/DebuggerCore/unix/freebsd/PlatformThread.h0000644000175000017500000000327413765535463024437 0ustar eteraneteran/* Copyright (C) 2018 - 2018 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PLATFORM_THREAD_H_20181225_ #define PLATFORM_THREAD_H_20181225_ #include "IBreakpoint.h" #include "IThread.h" #include #include class IProcess; namespace DebuggerCorePlugin { class DebuggerCore; class PlatformState; class PlatformThread : public IThread { Q_DECLARE_TR_FUNCTIONS(PlatformThread) friend class DebuggerCore; friend class PlatformProcess; public: edb::tid_t tid() const override; QString name() const override; int priority() const override; edb::address_t instruction_pointer() const override; QString runState() const override; public: void get_state(State *state) override; void set_state(const State &state) override; public: Status step() override; Status step(edb::EVENT_STATUS status) override; Status resume() override; Status resume(edb::EVENT_STATUS status) override; public: bool isPaused() const override; private: DebuggerCore *core_ = nullptr; std::shared_ptr process_; edb::tid_t tid_; int status_ = 0; }; } #endif edb-debugger/plugins/DebuggerCore/unix/freebsd/PlatformEvent.h0000644000175000017500000000312113765535463024300 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PLATFORM_EVENT_H_20121005_ #define PLATFORM_EVENT_H_20121005_ #include "IDebugEvent.h" #include namespace DebuggerCorePlugin { class PlatformEvent : IDebugEvent { Q_DECLARE_TR_FUNCTIONS(PlatformEvent) friend class DebuggerCore; public: PlatformEvent(); public: PlatformEvent *clone() const override; public: Message error_description() const override; REASON reason() const override; TRAP_REASON trap_reason() const override; bool exited() const override; bool is_error() const override; bool is_kill() const override; bool is_stop() const override; bool is_trap() const override; bool terminated() const override; bool stopped() const override; edb::pid_t process() const override; edb::tid_t thread() const override; int64_t code() const override; private: int status; edb::pid_t pid; edb::tid_t tid; void *fault_address_; long fault_code_; }; } #endif edb-debugger/plugins/DebuggerCore/unix/freebsd/PlatformState.cpp0000644000175000017500000000137613765535463024644 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "PlatformState.h" namespace DebuggerCorePlugin { } edb-debugger/plugins/DebuggerCore/unix/freebsd/PlatformCommon.cpp0000644000175000017500000000003513765535463025003 0ustar eteraneteran #include "PlatformCommon.h" edb-debugger/plugins/DebuggerCore/unix/freebsd/PlatformRegion.h0000644000175000017500000000351613765535463024452 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PLATFORM_REGION_H_20120330_ #define PLATFORM_REGION_H_20120330_ #include "IRegion.h" #include #include namespace DebuggerCorePlugin { class PlatformRegion : public IRegion { Q_DECLARE_TR_FUNCTIONS(PlatformRegion) public: PlatformRegion(edb::address_t start, edb::address_t end, edb::address_t base, const QString &name, permissions_t permissions); ~PlatformRegion() override = default; public: IRegion *clone() const override; public: bool accessible() const override; bool readable() const override; bool writable() const override; bool executable() const override; size_t size() const override; public: void set_permissions(bool read, bool write, bool execute) override; void set_start(edb::address_t address) override; void set_end(edb::address_t address) override; public: edb::address_t start() const override; edb::address_t end() const override; edb::address_t base() const override; QString name() const override; permissions_t permissions() const override; private: edb::address_t start_; edb::address_t end_; edb::address_t base_; QString name_; permissions_t permissions_; }; } #endif edb-debugger/plugins/DebuggerCore/unix/Posix.h0000644000175000017500000000115113765535463021203 0ustar eteraneteran #ifndef POSIX_H_20181211_ #define POSIX_H_20181211_ #include "OSTypes.h" #include #include namespace DebuggerCorePlugin { namespace Posix { void initialize(); int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); int select_ex(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, std::chrono::milliseconds msecs); pid_t waitpid(pid_t pid, int *status, int options); ssize_t read(int fd, void *buf, size_t count); ssize_t write(int fd, const void *buf, size_t count); bool wait_for_sigchld(std::chrono::milliseconds msecs); } } #endif edb-debugger/plugins/DebuggerCore/unix/Unix.cpp0000644000175000017500000000756613765535463021377 0ustar eteraneteran #include "Unix.h" #include #include namespace DebuggerCorePlugin { namespace { struct Exception { qlonglong value; const char *const name; }; constexpr Exception Exceptions[] = { #ifdef SIGABRT {SIGABRT, "SIGABRT"}, #endif #ifdef SIGALRM {SIGALRM, "SIGALRM"}, #endif #ifdef SIGVTALRM {SIGVTALRM, "SIGVTALRM"}, #endif #ifdef SIGPROF {SIGPROF, "SIGPROF"}, #endif #ifdef SIGBUS {SIGBUS, "SIGBUS"}, #endif #ifdef SIGCHLD {SIGCHLD, "SIGCHLD"}, #endif #ifdef SIGCONT {SIGCONT, "SIGCONT"}, #endif #ifdef SIGFPE {SIGFPE, "SIGFPE"}, #endif #ifdef SIGHUP {SIGHUP, "SIGHUP"}, #endif #ifdef SIGILL {SIGILL, "SIGILL"}, #endif #ifdef SIGINT {SIGINT, "SIGINT"}, #endif #ifdef SIGKILL {SIGKILL, "SIGKILL"}, #endif #ifdef SIGPIPE {SIGPIPE, "SIGPIPE"}, #endif #ifdef SIGQUIT {SIGQUIT, "SIGQUIT"}, #endif #ifdef SIGSEGV {SIGSEGV, "SIGSEGV"}, #endif #ifdef SIGSTOP {SIGSTOP, "SIGSTOP"}, #endif #ifdef SIGTERM {SIGTERM, "SIGTERM"}, #endif #ifdef SIGTSTP {SIGTSTP, "SIGTSTP"}, #endif #ifdef SIGTTIN {SIGTTIN, "SIGTTIN"}, #endif #ifdef SIGTTOU {SIGTTOU, "SIGTTOU"}, #endif #ifdef SIGUSR1 {SIGUSR1, "SIGUSR1"}, #endif #ifdef SIGUSR2 {SIGUSR2, "SIGUSR2"}, #endif #ifdef SIGPOLL {SIGPOLL, "SIGPOLL"}, #endif #ifdef SIGSYS {SIGSYS, "SIGSYS"}, #endif #ifdef SIGTRAP {SIGTRAP, "SIGTRAP"}, #endif #ifdef SIGURG {SIGURG, "SIGURG"}, #endif #ifdef SIGXCPU {SIGXCPU, "SIGXCPU"}, #endif #ifdef SIGXFSZ {SIGXFSZ, "SIGXFSZ"}, #endif #ifdef SIGIO {SIGIO, "SIGIO"}, #endif #ifdef SIGSTKFLT {SIGSTKFLT, "SIGSTKFLT"}, #endif #ifdef SIGWINCH {SIGWINCH, "SIGWINCH"}, #endif }; /** * @brief copyString * @param str * @return */ char *copyString(const QByteArray &str) { char *p = new char[str.length() + 1]; std::strcpy(p, str.constData()); return p; } } /** * @brief Unix::exceptions * @return */ QMap Unix::exceptions() { QMap exceptions; for (Exception e : Exceptions) { exceptions.insert(e.value, e.name); } return exceptions; } /** * @brief Unix::exception_name * @param value * @return */ QString Unix::exception_name(qlonglong value) { auto it = std::find_if(std::begin(Exceptions), std::end(Exceptions), [value](const Exception &ex) { return ex.value == value; }); if (it != std::end(Exceptions)) { return it->name; } return QString(); } /** * @brief Unix::exception_value * @param name * @return */ qlonglong Unix::exception_value(const QString &name) { auto it = std::find_if(std::begin(Exceptions), std::end(Exceptions), [&name](const Exception &ex) { return ex.name == name; }); if (it != std::end(Exceptions)) { return it->value; } return -1; } /** * @brief Unix::execute_process * @param path * @param cwd * @param args * @return */ Status Unix::execute_process(const QString &path, const QString &cwd, const QList &args) { QString errorString = "internal error"; // change to the desired working directory if (::chdir(qPrintable(cwd)) == 0) { // allocate space for all the arguments auto argv_pointers = new char *[args.count() + 2]; char **p = argv_pointers; *p++ = copyString(path.toLocal8Bit()); for (int i = 0; i < args.count(); ++i) { *p++ = copyString(args[i]); } *p = nullptr; // NOTE: it's a bad idea to use execvp and similar functions searching in // $PATH. At least on Linux, if the file is corrupted/unsupported, they // instead appear to launch shell const int ret = execv(argv_pointers[0], argv_pointers); // should be no need to cleanup, the process which allocated all that // space no longer exists! // if we get here...execv failed! if (ret == -1) { errorString = QString("execv() failed: %1").arg(strerror(errno)); p = argv_pointers; while (*p) { delete[] * p++; } delete[] argv_pointers; } } // frankly, any return is technically an error I think // this is only executed from a fork return Status(errorString); } } edb-debugger/plugins/DebuggerCore/unix/linux/0000755000175000017500000000000013765535463021071 5ustar eteraneteranedb-debugger/plugins/DebuggerCore/unix/linux/DebuggerCore.cpp0000644000175000017500000007336513765535463024150 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "DebuggerCore.h" #include "Configuration.h" #include "DialogMemoryAccess.h" #include "FeatureDetect.h" #include "MemoryRegions.h" #include "PlatformCommon.h" #include "PlatformEvent.h" #include "PlatformProcess.h" #include "PlatformRegion.h" #include "PlatformState.h" #include "PlatformThread.h" #include "Posix.h" #include "State.h" #include "Unix.h" #include "edb.h" #include "string_hash.h" #include "util/Container.h" #include "util/String.h" #include #include #include #include #include #ifndef _GNU_SOURCE #define _GNU_SOURCE /* or _BSD_SOURCE or _SVID_SOURCE */ #endif #if defined(EDB_X86) || defined(EDB_X86_64) #include #endif #include #include #include #include /* For SYS_xxx definitions */ #include // doesn't always seem to be defined in the headers #ifndef PTRACE_GETSIGINFO #define PTRACE_GETSIGINFO static_cast<__ptrace_request>(0x4202) #endif #ifndef PTRACE_EVENT_CLONE #define PTRACE_EVENT_CLONE 3 #endif #ifndef PTRACE_O_TRACECLONE #define PTRACE_O_TRACECLONE (1 << PTRACE_EVENT_CLONE) #endif #ifndef PTRACE_O_EXITKILL #define PTRACE_O_EXITKILL (1 << 20) #endif #ifndef PTRACE_O_TRACEEXIT #define PTRACE_O_TRACEEXIT (1 << PTRACE_EVENT_EXIT) #endif namespace DebuggerCorePlugin { namespace { constexpr size_t PageSize = 0x1000; /** * @brief disable_aslr */ void disable_aslr() { const int current = ::personality(UINT32_MAX); // This shouldn't fail, but let's at least perror if it does anyway if (current == -1) { perror("Failed to get current personality"); } else if (::personality(current | ADDR_NO_RANDOMIZE) == -1) { perror("Failed to disable ASLR"); } } /** * @brief disable_lazy_binding */ void disable_lazy_binding() { if (setenv("LD_BIND_NOW", "1", true) == -1) { perror("Failed to disable lazy binding"); } } /** * @brief is_clone_event * @param status * @return */ constexpr bool is_clone_event(int status) { return (status >> 8 == (SIGTRAP | (PTRACE_EVENT_CLONE << 8))); } /** * @brief is_exit_trace_event * @param status * @return */ constexpr bool is_exit_trace_event(int status) { return (status >> 8 == (SIGTRAP | (PTRACE_EVENT_EXIT << 8))); } #if defined(EDB_X86) || defined(EDB_X86_64) /** * @brief in_64bit_segment * @return */ bool in_64bit_segment() { bool edbIsIn64BitSegment; // Check that we're running in 64 bit segment: this can be in cases // of LP64 and ILP32 programming models, so we can't rely on sizeof(void*) __asm__(R"( .byte 0x33,0xc0 # XOR EAX,EAX .byte 0x48 # DEC EAX for 32 bit, REX prefix for 64 bit .byte 0xff,0xc0 # INC EAX for 32 bit, INC RAX due to REX.W in 64 bit )" : "=a"(edbIsIn64BitSegment)); return edbIsIn64BitSegment; } /** * @brief os_is_64_bit * @param edbIsIn64BitSegment * @return */ bool os_is_64_bit() { bool edbIsIn64BitSegment = in_64bit_segment(); bool osIs64Bit; if (edbIsIn64BitSegment) { osIs64Bit = true; } else { // We want to be really sure the OS is 32 bit, so we can't rely on such easy // to (even unintentionally) fake mechanisms as uname(2) (e.g. see setarch(8)) __asm__(R"(.intel_syntax noprefix mov eax,cs cmp ax,0x23 # this value is set for 32-bit processes on 64-bit kernel mov ah,0 # not sure this is really needed: usually the compiler will do # MOVZX EAX,AL, but we have to be certain the result is correct sete al .att_syntax # restore default syntax )" : "=a"(osIs64Bit)); } return osIs64Bit; } #endif } /** * @brief DebuggerCore::DebuggerCore */ DebuggerCore::DebuggerCore() #if defined(EDB_X86) || defined(EDB_X86_64) : osIs64Bit_(os_is_64_bit()), userCodeSegment32_(osIs64Bit_ ? 0x23 : 0x73), userCodeSegment64_(osIs64Bit_ ? 0x33 : 0xfff8), // RPL 0 can't appear in user segment registers, so 0xfff8 is safe userStackSegment_(osIs64Bit_ ? 0x2b : 0x7b) #endif { Posix::initialize(); feature::detect_proc_access(&procMemReadBroken_, &procMemWriteBroken_); if (procMemReadBroken_ || procMemWriteBroken_) { qDebug() << "Detect that read /proc//mem works = " << !procMemReadBroken_; qDebug() << "Detect that write /proc//mem works = " << !procMemWriteBroken_; QSettings settings; const bool warn = settings.value("DebuggerCore/warn_on_broken_proc_mem.enabled", true).toBool(); if (warn) { auto dialog = std::make_unique(nullptr); dialog->exec(); settings.setValue("DebuggerCore/warn_on_broken_proc_mem.enabled", dialog->warnNextTime()); } } } /** * @brief DebuggerCore::hasExtension * @param ext * @return */ bool DebuggerCore::hasExtension(uint64_t ext) const { #if defined(EDB_X86) || defined(EDB_X86_64) static constexpr auto mmxHash = edb::string_hash("MMX"); static constexpr auto xmmHash = edb::string_hash("XMM"); static constexpr auto ymmHash = edb::string_hash("YMM"); #if defined(EDB_X86_64) if (ext == xmmHash || ext == mmxHash) { return true; } #endif uint32_t eax; uint32_t ebx; uint32_t ecx; uint32_t edx; __cpuid(1, eax, ebx, ecx, edx); switch (ext) { case mmxHash: return (edx & bit_MMX); case xmmHash: return (edx & bit_SSE); case ymmHash: { // Check OSXSAVE and AVX feature flags if ((ecx & 0x18000000) != 0x18000000) { return false; } // Get XCR0, must be exactly after OSXSAVE feature check, otherwise #UD __asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(0)); // Check that the OS has enabled XMM and YMM state support if ((eax & 0x6) != 0x6) { return false; } return true; } default: return false; } #else Q_UNUSED(ext) return false; #endif } /** * @brief DebuggerCore::pageSize * @return the size of a page on this system */ size_t DebuggerCore::pageSize() const { return PageSize; } /** * @brief DebuggerCore::pointerSize * @return */ std::size_t DebuggerCore::pointerSize() const { return pointerSize_; } /** * @brief DebuggerCore::~DebuggerCore */ DebuggerCore::~DebuggerCore() { endDebugSession(); } /** * @brief DebuggerCore::ptraceGetSigInfo * @param tid * @param siginfo * @return */ Status DebuggerCore::ptraceGetSigInfo(edb::tid_t tid, siginfo_t *siginfo) { Q_ASSERT(siginfo); if (ptrace(PTRACE_GETSIGINFO, tid, 0, siginfo) == -1) { const char *const strError = strerror(errno); qWarning() << "Unable to get signal info for thread" << tid << ": PTRACE_GETSIGINFO failed:" << strError; return Status(strError); } return Status::Ok; } /** * @brief DebuggerCore::ptraceTraceme * @return */ long DebuggerCore::ptraceTraceme() { return ptrace(PTRACE_TRACEME, 0, 0, 0); } /** * @brief DebuggerCore::ptraceContinue * @param tid * @param status * @return */ Status DebuggerCore::ptraceContinue(edb::tid_t tid, long status) { // TODO(eteran): perhaps address this at a higher layer? // I would like to not have these events show up // in the first place if we aren't stopped on this TID :-( if (util::contains(waitedThreads_, tid)) { Q_ASSERT(tid != 0); if (ptrace(PTRACE_CONT, tid, 0, status) == -1) { const char *const strError = strerror(errno); qWarning() << "Unable to continue thread" << tid << ": PTRACE_CONT failed:" << strError; return Status(strError); } waitedThreads_.erase(tid); return Status::Ok; } return Status(tr("ptrace_continue(): waited_threads_ doesn't contain tid %1").arg(tid)); } /** * @brief DebuggerCore::ptraceStep * @param tid * @param status * @return */ Status DebuggerCore::ptraceStep(edb::tid_t tid, long status) { // TODO(eteran): perhaps address this at a higher layer? // I would like to not have these events show up // in the first place if we aren't stopped on this TID :-( if (util::contains(waitedThreads_, tid)) { Q_ASSERT(tid != 0); if (ptrace(PTRACE_SINGLESTEP, tid, 0, status) == -1) { const char *const strError = strerror(errno); qWarning() << "Unable to step thread" << tid << ": PTRACE_SINGLESTEP failed:" << strError; return Status(strError); } waitedThreads_.erase(tid); return Status::Ok; } return Status(tr("ptrace_step(): waited_threads_ doesn't contain tid %1").arg(tid)); } /** * @brief DebuggerCore::ptraceSetOptions * @param tid * @param options * @return */ Status DebuggerCore::ptraceSetOptions(edb::tid_t tid, long options) { Q_ASSERT(util::contains(waitedThreads_, tid)); Q_ASSERT(tid != 0); if (ptrace(PTRACE_SETOPTIONS, tid, 0, options) == -1) { const char *const strError = strerror(errno); qWarning() << "Unable to set ptrace options for thread" << tid << ": PTRACE_SETOPTIONS failed:" << strError; return Status(strError); } return Status::Ok; } /** * @brief DebuggerCore::ptraceGetEventMessage * @param tid * @param message * @return */ Status DebuggerCore::ptraceGetEventMessage(edb::tid_t tid, unsigned long *message) { Q_ASSERT(util::contains(waitedThreads_, tid)); Q_ASSERT(tid != 0); Q_ASSERT(message); if (ptrace(PTRACE_GETEVENTMSG, tid, 0, message) == -1) { const char *const strError = strerror(errno); qWarning() << "Unable to get event message for thread" << tid << ": PTRACE_GETEVENTMSG failed:" << strError; return Status(strError); } return Status::Ok; } /** * @brief DebuggerCore::ptraceOptions * @return */ long DebuggerCore::ptraceOptions() const { // we want to trace clone (thread) creation events long options = PTRACE_O_TRACECLONE; // if applicable, we want an auto SIGKILL sent to the child // process and its threads switch (edb::v1::config().close_behavior) { case Configuration::Kill: options |= PTRACE_O_EXITKILL; break; case Configuration::KillIfLaunchedDetachIfAttached: if (lastMeansOfCapture() == MeansOfCapture::Launch) { options |= PTRACE_O_EXITKILL; } break; default: break; } #if 0 // TODO(eteran): research this option for issue #46 options |= PTRACE_O_TRACEEXIT; #endif return options; } /** * @brief DebuggerCore::handleThreadExit * @param tid * @param status */ void DebuggerCore::handleThreadExit(edb::tid_t tid, int status) { Q_UNUSED(status) threads_.remove(tid); waitedThreads_.erase(tid); } /** * @brief DebuggerCore::handleThreadCreate * @param tid * @param status * @return */ std::shared_ptr DebuggerCore::handleThreadCreate(edb::tid_t tid, int status) { Q_UNUSED(status) unsigned long message; if (ptraceGetEventMessage(tid, &message)) { auto new_tid = static_cast(message); auto new_thread = std::make_shared(this, process_, new_tid); threads_.insert(new_tid, new_thread); int thread_status = 0; if (!util::contains(waitedThreads_, new_tid)) { if (Posix::waitpid(new_tid, &thread_status, __WALL) > 0) { waitedThreads_.insert(new_tid); } } // A new thread could exit before we have fully created it, no event then since it can't be the last thread if (WIFEXITED(thread_status)) { handleThreadExit(tid, thread_status); return nullptr; } if (!WIFSTOPPED(thread_status) || WSTOPSIG(thread_status) != SIGSTOP) { qWarning("handle_event(): new thread [%d] received an event besides SIGSTOP: status=0x%x", static_cast(new_tid), thread_status); } new_thread->status_ = thread_status; // copy the hardware debug registers from the current thread to the new thread if (process_) { if (auto cur_thread = process_->currentThread()) { auto old_thread = std::static_pointer_cast(cur_thread); for (size_t i = 0; i < 8; ++i) { new_thread->setDebugRegister(i, old_thread->getDebugRegister(i)); } } } new_thread->resume(); } ptraceContinue(tid, 0); return nullptr; } /** * @brief DebuggerCore::handleEvent * @param tid * @param status * @return */ std::shared_ptr DebuggerCore::handleEvent(edb::tid_t tid, int status) { // note that we have waited on this thread waitedThreads_.insert(tid); // was it a thread exit event? if (WIFEXITED(status)) { handleThreadExit(tid, status); // if this was the last thread, return nullptr // so we report it to the user. // if this wasn't, then we should silently // procceed. if (!threads_.empty()) { return nullptr; } } if (is_exit_trace_event(status)) { } // was it a thread create event? if (is_clone_event(status)) { return handleThreadCreate(tid, status); } // normal event auto e = std::make_shared(); e->pid_ = process_->pid(); e->tid_ = tid; e->status_ = status; if (!ptraceGetSigInfo(tid, &e->siginfo_)) { // TODO: handle no info? } // if necessary, just ignore this event if (util::contains(ignoredExceptions_, e->code())) { ptraceContinue(tid, resume_code(status)); } /* NOTE(eteran): OK, so when we get an event, we generally want to stop * any other threads as well. So we will call stopThreads() below * which sends a SIGSTOP. * * We need to be very careful to avoid those future events causing the * active thread to be set, because we want it to remain set to the thread * which recieved the initial signal. This is all so that later when the * user clicks resume, that the correct active thread gets (or doesn't) * get signaled, and the rest get resumed properly. * * To do this, we simply only alter the activeThread_ variable if this * event was the first we saw after a resume/run (phew!).*/ if (waitedThreads_.size() == 1) { activeThread_ = tid; } auto it = threads_.find(tid); if (it != threads_.end()) { it.value()->status_ = status; } stopThreads(); // Some breakpoint types result in SIGILL or SIGSEGV. We'll transform the // event into breakpoint event if such a breakpoint has triggered. if (it != threads_.end() && WIFSTOPPED(status)) { const auto signo = WSTOPSIG(status); if (signo == SIGILL || signo == SIGSEGV) { // no need to peekuser for SIGILL, but have to for SIGSEGV const auto address = signo == SIGILL ? edb::address_t::fromZeroExtended(e->siginfo_.si_addr) : (*it)->instructionPointer(); if (edb::v1::find_triggered_breakpoint(address)) { e->status_ = SIGTRAP << 8 | 0x7f; e->siginfo_.si_signo = SIGTRAP; e->siginfo_.si_code = TRAP_BRKPT; } } } #if defined(EDB_ARM32) if (it != threads_.end()) { const auto &thread = *it; if (thread->singleStepBreakpoint) { removeBreakpoint(thread->singleStepBreakpoint->address()); thread->singleStepBreakpoint = nullptr; assert(e->siginfo_.si_signo == SIGTRAP); // signo must have already be converted to SIGTRAP if needed e->siginfo_.si_code = TRAP_TRACE; } } #endif return e; } /** * @brief DebuggerCore::stopThreads * @return */ Status DebuggerCore::stopThreads() { QString errorMessage; if (process_) { for (auto &thread : process_->threads()) { const edb::tid_t tid = thread->tid(); if (!util::contains(waitedThreads_, tid)) { if (auto thread_ptr = std::static_pointer_cast(thread)) { if (syscall(SYS_tgkill, process_->pid(), thread->tid(), SIGSTOP) == -1) { const char *const error = strerror(errno); errorMessage += tr("Failed to stop thread %1: %2\n").arg(tid).arg(error); } int thread_status; if (Posix::waitpid(thread->tid(), &thread_status, __WALL /* | WNOHANG*/) > 0) { waitedThreads_.insert(tid); thread_ptr->status_ = thread_status; // A thread could have exited between previous waitpid and the latest one... if (WIFEXITED(thread_status)) { handleThreadExit(tid, thread_status); } // ..., otherwise it must have stopped. else if (!WIFSTOPPED(thread_status) || WSTOPSIG(thread_status) != SIGSTOP) { qWarning("stop_threads(): paused thread [%d] received an event besides SIGSTOP: status=0x%x", tid, thread_status); } } } } } } if (errorMessage.isEmpty()) { return Status::Ok; } qWarning() << qPrintable(errorMessage); return Status("\n" + errorMessage); } /** * waits for a debug event, witha timeout specified in milliseconds * * @brief DebuggerCore::waitDebugEvent * @param msecs * @return nullptr if an error or timeout occurs */ std::shared_ptr DebuggerCore::waitDebugEvent(std::chrono::milliseconds msecs) { if (process_) { if (!Posix::wait_for_sigchld(msecs)) { for (auto &thread : process_->threads()) { int status; const edb::tid_t tid = Posix::waitpid(thread->tid(), &status, __WALL | WNOHANG); if (tid > 0) { return handleEvent(tid, status); } } } } return nullptr; } /** * @brief DebuggerCore::attachThread * @param tid * @return 0 if successful, errno if failed */ int DebuggerCore::attachThread(edb::tid_t tid) { if (ptrace(PTRACE_ATTACH, tid, 0, 0) == 0) { int status; const int ret = Posix::waitpid(tid, &status, __WALL); if (ret > 0) { auto newThread = std::make_shared(this, process_, tid); newThread->status_ = status; threads_.insert(tid, newThread); waitedThreads_.insert(tid); const long options = ptraceOptions(); const auto setoptStatus = ptraceSetOptions(tid, options); if (!setoptStatus) { qDebug() << "[DebuggerCore] failed to set ptrace options: [" << tid << "]" << setoptStatus.error(); } return 0; } else if (ret == -1) { return errno; } else { return -1; // unknown error } } else { return errno; } } /** * @brief DebuggerCore::attach * @param pid * @return */ Status DebuggerCore::attach(edb::pid_t pid) { endDebugSession(); lastMeansOfCapture_ = MeansOfCapture::Attach; // create this, so the threads created can refer to it process_ = std::make_shared(this, pid); int lastErr = attachThread(pid); // Fail early if we are going to if (lastErr) { process_ = nullptr; return Status(std::strerror(lastErr)); } lastErr = -2; bool attached; do { attached = false; QDir proc_directory(QString("/proc/%1/task/").arg(pid)); for (const QString &s : proc_directory.entryList(QDir::NoDotAndDotDot | QDir::Dirs)) { // this can get tricky if the threads decide to spawn new threads // when we are attaching. I wish that linux had an atomic way to do this // all in one shot const edb::tid_t tid = s.toInt(); if (!threads_.contains(tid)) { const auto errnum = attachThread(tid); if (errnum == 0) { attached = true; } else { lastErr = errnum; } } } } while (attached); if (!threads_.empty()) { activeThread_ = pid; detectCpuMode(); return Status::Ok; } process_ = nullptr; return Status(std::strerror(lastErr)); } /** * @brief DebuggerCore::detach * @return */ Status DebuggerCore::detach() { QString errorMessage; if (process_) { stopThreads(); clearBreakpoints(); for (auto &thread : process_->threads()) { if (ptrace(PTRACE_DETACH, thread->tid(), 0, 0) == -1) { const char *const error = strerror(errno); errorMessage += tr("Unable to detach from thread %1: PTRACE_DETACH failed: %2\n").arg(thread->tid()).arg(error); } } process_ = nullptr; reset(); } if (errorMessage.isEmpty()) { return Status::Ok; } qWarning() << errorMessage.toStdString().c_str(); return Status(errorMessage); } /** * @brief DebuggerCore::kill */ void DebuggerCore::kill() { if (attached()) { clearBreakpoints(); ::kill(process_->pid(), SIGKILL); pid_t ret; while ((ret = Posix::waitpid(-1, nullptr, __WALL)) != process_->pid() && ret != -1) ; process_ = nullptr; reset(); } } /** * @brief DebuggerCore::detectCpuMode */ void DebuggerCore::detectCpuMode() { #if defined(EDB_X86) || defined(EDB_X86_64) #if defined(EDB_X86) constexpr size_t Offset = offsetof(UserRegsStructX86, xcs); #elif defined(EDB_X86_64) constexpr size_t Offset = offsetof(UserRegsStructX86_64, cs); #endif errno = 0; const edb::seg_reg_t cs = ptrace(PTRACE_PEEKUSER, activeThread_, Offset, 0); if (!errno) { if (cs == userCodeSegment32_) { if (pointerSize_ == sizeof(uint64_t)) { qDebug() << "Debuggee is now 32 bit"; cpuMode_ = CpuMode::x86_32; CapstoneEDB::init(CapstoneEDB::Architecture::ARCH_X86); } pointerSize_ = sizeof(uint32_t); return; } else if (cs == userCodeSegment64_) { if (pointerSize_ == sizeof(uint32_t)) { qDebug() << "Debuggee is now 64 bit"; cpuMode_ = CpuMode::x86_64; CapstoneEDB::init(CapstoneEDB::Architecture::ARCH_AMD64); } pointerSize_ = sizeof(uint64_t); return; } } #elif defined(EDB_ARM32) errno = 0; const auto cpsr = ptrace(PTRACE_PEEKUSER, activeThread_, sizeof(long) * 16, 0L); if (!errno) { const bool thumb = cpsr & 0x20; if (thumb) { cpuMode_ = CpuMode::Thumb; CapstoneEDB::init(CapstoneEDB::Architecture::ARCH_ARM32_THUMB); } else { cpuMode_ = CpuMode::ARM32; CapstoneEDB::init(CapstoneEDB::Architecture::ARCH_ARM32_ARM); } } pointerSize_ = sizeof(uint32_t); #elif defined(EDB_ARM64) cpuMode_ = CpuMode::ARM64; CapstoneEDB::init(CapstoneEDB::Architecture::ARCH_ARM64); pointerSize_ = sizeof(uint64_t); #else #error "Unsupported Architecture" #endif } /** * @brief DebuggerCore::open * @param path * @param cwd * @param args * @param tty * @return */ Status DebuggerCore::open(const QString &path, const QString &cwd, const QList &args, const QString &input, const QString &output) { endDebugSession(); lastMeansOfCapture_ = MeansOfCapture::Launch; constexpr std::size_t SharedMemSize = 4096; void *const ptr = ::mmap(nullptr, SharedMemSize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); const auto sharedMem = static_cast(ptr); std::memset(ptr, 0, SharedMemSize); switch (pid_t pid = fork()) { case 0: { // we are in the child now... // set ourselves (the child proc) up to be traced ptraceTraceme(); // redirect it's I/O FILE *std_in = nullptr; FILE *std_out = nullptr; FILE *std_err = nullptr; if (!input.isEmpty()) { std_in = freopen(qPrintable(input), "rb", stdin); } if (!output.isEmpty()) { std_out = freopen(qPrintable(output), "wb", stdout); std_err = freopen(qPrintable(output), "wb", stderr); } Q_UNUSED(std_in) Q_UNUSED(std_out) Q_UNUSED(std_err) if (edb::v1::config().disableASLR) { disable_aslr(); } if (edb::v1::config().disableLazyBinding) { disable_lazy_binding(); } // do the actual exec const Status status = Unix::execute_process(path, cwd, args); #if defined __GNUG__ && __GNUC__ >= 5 || !defined __GNUG__ || defined __clang__ && __clang_major__ * 100 + __clang_minor__ >= 306 static_assert(std::is_trivially_copyable::value, "Can't copy string of QChar to shared memory"); #endif QString error = status.error(); std::memcpy(sharedMem, error.constData(), std::min(sizeof(QChar) * error.size(), SharedMemSize - sizeof(QChar) /*prevent overwriting of last null*/)); // we should never get here! abort(); } case -1: // error! for some reason we couldn't fork reset(); return Status(tr("Failed to fork")); default: // parent { reset(); int status; const auto wpidRet = Posix::waitpid(pid, &status, __WALL); const QString childError(sharedMem); ::munmap(sharedMem, SharedMemSize); if (wpidRet == -1) { return Status(tr("waitpid() failed: %1").arg(std::strerror(errno)) + (childError.isEmpty() ? "" : tr(".\nError returned by child:\n%1.").arg(childError))); } if (WIFEXITED(status)) { return Status(tr("The child unexpectedly exited with code %1. Error returned by child:\n%2").arg(WEXITSTATUS(status)).arg(childError)); } if (WIFSIGNALED(status)) { return Status(tr("The child was unexpectedly killed by signal %1. Error returned by child:\n%2").arg(WTERMSIG(status)).arg(childError)); } // This happens when exec failed, but just in case it's something another return some description. if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGABRT) { return Status(childError.isEmpty() ? tr("The child unexpectedly aborted") : childError); } // the very first event should be a STOP of type SIGTRAP if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP) { endDebugSession(); return Status(tr("First event after waitpid() should be a STOP of type SIGTRAP, but wasn't, instead status=0x%1") .arg(status, 0, 16) + (childError.isEmpty() ? "" : tr(".\nError returned by child:\n%1.").arg(childError))); } waitedThreads_.insert(pid); const long options = ptraceOptions(); // enable following clones (threads) and other options we are concerned with const auto setoptStatus = ptraceSetOptions(pid, options); if (!setoptStatus) { endDebugSession(); return Status(tr("[DebuggerCore] failed to set ptrace options: %1").arg(setoptStatus.error())); } // create the process process_ = std::make_shared(this, pid); // the PID == primary TID auto newThread = std::make_shared(this, process_, pid); newThread->status_ = status; threads_.insert(pid, newThread); activeThread_ = pid; detectCpuMode(); return Status::Ok; } } } /** * @brief DebuggerCore::lastMeansOfCapture * @return how the last process was captured to debug */ DebuggerCore::MeansOfCapture DebuggerCore::lastMeansOfCapture() const { return lastMeansOfCapture_; } /** * @brief DebuggerCore::reset */ void DebuggerCore::reset() { threads_.clear(); waitedThreads_.clear(); activeThread_ = 0; } /** * @brief DebuggerCore::createState * @return */ std::unique_ptr DebuggerCore::createState() const { return std::make_unique(); } /** * @brief DebuggerCore::enumerateProcesses * @return */ QMap> DebuggerCore::enumerateProcesses() const { QMap> ret; QDir proc_directory("/proc/"); QFileInfoList entries = proc_directory.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); for (const QFileInfo &info : entries) { const QString filename = info.fileName(); if (util::is_numeric(filename)) { const edb::pid_t pid = filename.toInt(); // NOTE(eteran): the const_cast is reasonable here. // While we don't want THIS function to mutate the DebuggerCore object // we do want the associated PlatformProcess to be able to trigger // non-const operations in the future, at least hypothetically. ret.insert(pid, std::make_shared(const_cast(this), pid)); } } return ret; } /** * @brief DebuggerCore::parentPid * @param pid * @return */ edb::pid_t DebuggerCore::parentPid(edb::pid_t pid) const { struct user_stat user_stat; int n = get_user_stat(pid, &user_stat); if (n >= 4) { return user_stat.ppid; } return 0; } /** * @brief DebuggerCore::cpuType * @return edb's native CPU type */ uint64_t DebuggerCore::cpuType() const { #if defined(EDB_X86_64) return edb::string_hash("x86-64"); #elif defined(EDB_X86) return edb::string_hash("x86"); #elif defined(EDB_ARM32) return edb::string_hash("arm"); #elif defined(EDB_ARM64) return edb::string_hash("AArch64"); #else #error "Unsupported Architecture" #endif } /** * @brief DebuggerCore::stackPointer * @return */ QString DebuggerCore::stackPointer() const { #if defined(EDB_X86) || defined(EDB_X86_64) if (edb::v1::debuggeeIs32Bit()) { return "esp"; } else { return "rsp"; } #elif defined(EDB_ARM32) || defined(EDB_ARM64) return "sp"; #else #error "Unsupported Architecture" #endif } /** * @brief DebuggerCore::framePointer * @return */ QString DebuggerCore::framePointer() const { #if defined(EDB_X86) || defined(EDB_X86_64) if (edb::v1::debuggeeIs32Bit()) { return "ebp"; } else { return "rbp"; } #elif defined(EDB_ARM32) || defined(EDB_ARM64) return "fp"; #else #error "Unsupported Architecture" #endif } /** * @brief DebuggerCore::instructionPointer * @return */ QString DebuggerCore::instructionPointer() const { #if defined(EDB_X86) || defined(EDB_X86_64) if (edb::v1::debuggeeIs32Bit()) { return "eip"; } else { return "rip"; } #elif defined(EDB_ARM32) || defined(EDB_ARM64) return "pc"; #else #error "Unsupported Architecture" #endif } /** * @brief DebuggerCore::flagRegister * @return the name of the flag register */ QString DebuggerCore::flagRegister() const { #if defined(EDB_X86) || defined(EDB_X86_64) if (edb::v1::debuggeeIs32Bit()) { return "eflags"; } else { return "rflags"; } #elif defined(EDB_ARM32) || defined(EDB_ARM64) return "cpsr"; #else #error "Unsupported Architecture" #endif } /** * @brief DebuggerCore::process * @return */ IProcess *DebuggerCore::process() const { return process_.get(); } /** * @brief DebuggerCore::setIgnoredExceptions * @param exceptions */ void DebuggerCore::setIgnoredExceptions(const QList &exceptions) { ignoredExceptions_ = exceptions; } /** * @brief DebuggerCore::exceptions * @return */ QMap DebuggerCore::exceptions() const { return Unix::exceptions(); } /** * @brief DebuggerCore::exceptionName * @param value * @return */ QString DebuggerCore::exceptionName(qlonglong value) { return Unix::exception_name(value); } /** * @brief DebuggerCore::exceptionValue * @param name * @return */ qlonglong DebuggerCore::exceptionValue(const QString &name) { return Unix::exception_value(name); } /** * @brief DebuggerCore::nopFillByte * @return */ uint8_t DebuggerCore::nopFillByte() const { #if defined(EDB_X86) || defined(EDB_X86_64) return 0x90; #elif defined(EDB_ARM32) || defined(EDB_ARM64) // TODO(eteran): does this concept even make sense for a multi-byte instruction encoding? return 0x00; #else #error "Unsupported Architecture" #endif } } edb-debugger/plugins/DebuggerCore/unix/linux/PlatformThread.cpp0000644000175000017500000000724213765535463024516 0ustar eteraneteran/* Copyright (C) 2015 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "PlatformThread.h" #include "DebuggerCore.h" #include "IProcess.h" #include "PlatformCommon.h" #include "util/Container.h" #include #ifndef _GNU_SOURCE #define _GNU_SOURCE /* or _BSD_SOURCE or _SVID_SOURCE */ #endif namespace DebuggerCorePlugin { /** * @brief PlatformThread::PlatformThread * @param core * @param process * @param tid */ PlatformThread::PlatformThread(DebuggerCore *core, std::shared_ptr &process, edb::tid_t tid) : core_(core), process_(process), tid_(tid) { assert(process); assert(core); } /** * @brief PlatformThread::tid * @return */ edb::tid_t PlatformThread::tid() const { return tid_; } /** * @brief PlatformThread::name * @return */ QString PlatformThread::name() const { struct user_stat thread_stat; int n = get_user_task_stat(process_->pid(), tid_, &thread_stat); if (n >= 2) { return thread_stat.comm; } return QString(); } /** * @brief PlatformThread::priority * @return */ int PlatformThread::priority() const { struct user_stat thread_stat; int n = get_user_task_stat(process_->pid(), tid_, &thread_stat); if (n >= 18) { return thread_stat.priority; } return 0; } /** * @brief PlatformThread::runState * @return */ QString PlatformThread::runState() const { struct user_stat thread_stat; int n = get_user_task_stat(process_->pid(), tid_, &thread_stat); if (n >= 3) { switch (thread_stat.state) { // 03 case 'R': return tr("%1 (Running)").arg(thread_stat.state); case 'S': return tr("%1 (Sleeping)").arg(thread_stat.state); case 'D': return tr("%1 (Disk Sleep)").arg(thread_stat.state); case 'T': return tr("%1 (Stopped)").arg(thread_stat.state); case 't': return tr("%1 (Tracing Stop)").arg(thread_stat.state); case 'Z': return tr("%1 (Zombie)").arg(thread_stat.state); case 'X': case 'x': return tr("%1 (Dead)").arg(thread_stat.state); case 'W': return tr("%1 (Waking/Paging)").arg(thread_stat.state); case 'K': return tr("%1 (Wakekill)").arg(thread_stat.state); case 'P': return tr("%1 (Parked)").arg(thread_stat.state); default: return tr("%1").arg(thread_stat.state); } } return tr("Unknown"); } /** * resumes this thread, passing the signal that stopped it * (unless the signal was SIGSTOP) * * @brief PlatformThread::resume * @return */ Status PlatformThread::resume() { return core_->ptraceContinue(tid_, resume_code(status_)); } /** * resumes this thread, passing the signal that stopped it * (unless the signal was SIGSTOP, or the passed status != DEBUG_EXCEPTION_NOT_HANDLED) * @brief PlatformThread::resume * @param status * @return */ Status PlatformThread::resume(edb::EventStatus status) { const int code = (status == edb::DEBUG_EXCEPTION_NOT_HANDLED) ? resume_code(status_) : 0; return core_->ptraceContinue(tid_, code); } /** * @brief PlatformThread::isPaused * @return true if this thread is currently in the debugger's wait list */ bool PlatformThread::isPaused() const { return util::contains(core_->waitedThreads_, tid_); } } edb-debugger/plugins/DebuggerCore/unix/linux/PlatformCommon.h0000644000175000017500000000557513765535463024213 0ustar eteraneteran/* Copyright (C) 2015 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PLATFORM_COMMON_H_20151011_ #define PLATFORM_COMMON_H_20151011_ #include "OSTypes.h" #include "edb.h" class QString; namespace DebuggerCorePlugin { struct user_stat { /* 01 */ int pid; /* 02 */ char comm[256]; /* 03 */ char state; /* 04 */ int ppid; /* 05 */ int pgrp; /* 06 */ int session; /* 07 */ int tty_nr; /* 08 */ int tpgid; /* 09 */ unsigned flags; /* 10 */ unsigned long long minflt; /* 11 */ unsigned long long cminflt; /* 12 */ unsigned long long majflt; /* 13 */ unsigned long long cmajflt; /* 14 */ unsigned long long utime; /* 15 */ unsigned long long stime; /* 16 */ long long cutime; /* 17 */ long long cstime; /* 18 */ long long priority; /* 19 */ long long nice; /* 20 */ long long num_threads; /* 21 */ long long itrealvalue; /* 22 */ unsigned long long starttime; /* 23 */ unsigned long long vsize; /* 24 */ long long rss; /* 25 */ unsigned long long rsslim; /* 26 */ unsigned long long startcode; /* 27 */ unsigned long long endcode; /* 28 */ unsigned long long startstack; /* 29 */ unsigned long long kstkesp; /* 30 */ unsigned long long kstkeip; /* 31 */ unsigned long long signal; /* 32 */ unsigned long long blocked; /* 33 */ unsigned long long sigignore; /* 34 */ unsigned long long sigcatch; /* 35 */ unsigned long long wchan; /* 36 */ unsigned long long nswap; /* 37 */ unsigned long long cnswap; /* 38 */ int exit_signal; /* 39 */ int processor; /* 40 */ unsigned rt_priority; /* 41 */ unsigned policy; // Linux 2.6.18 /* 42 */ unsigned long long delayacct_blkio_ticks; // Linux 2.6.24 /* 43 */ unsigned long long guest_time; /* 44 */ long long cguest_time; // Linux 3.3 /* 45 */ unsigned long long start_data; /* 46 */ unsigned long long end_data; /* 47 */ unsigned long long start_brk; // Linux 3.5 /* 48 */ unsigned long long arg_start; /* 49 */ unsigned long long arg_end; /* 50 */ unsigned long long env_start; /* 51 */ unsigned long long env_end; /* 52 */ int exit_code; }; int get_user_stat(const char *path, struct user_stat *user_stat); int get_user_stat(edb::pid_t pid, struct user_stat *user_stat); int get_user_task_stat(edb::pid_t pid, edb::tid_t tid, struct user_stat *user_stat); int resume_code(int status); } #endif edb-debugger/plugins/DebuggerCore/unix/linux/DialogMemoryAccess.h0000644000175000017500000000222313765535463024753 0ustar eteraneteran/* Copyright (C) 2016 - 2016 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DIALOG_MEMORY_ACCESS_H_20160930_ #define DIALOG_MEMORY_ACCESS_H_20160930_ #include "ui_DialogMemoryAccess.h" #include namespace DebuggerCorePlugin { class DialogMemoryAccess final : public QDialog { Q_OBJECT public: explicit DialogMemoryAccess(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~DialogMemoryAccess() override = default; public: bool warnNextTime() const; private: Ui::DialogMemoryAccess ui; }; } #endif edb-debugger/plugins/DebuggerCore/unix/linux/FeatureDetect.h0000644000175000017500000000161013765535463023764 0ustar eteraneteran/* Copyright (C) 2016 - 2016 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef FEATURE_DETECT_H_20191119_ #define FEATURE_DETECT_H_20191119_ namespace DebuggerCorePlugin { namespace feature { bool detect_proc_access(bool *read_broken, bool *write_broken); } } #endif edb-debugger/plugins/DebuggerCore/unix/linux/DebuggerCore.h0000644000175000017500000000773713765535463023615 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DEBUGGER_CORE_H_20090529_ #define DEBUGGER_CORE_H_20090529_ #include "DebuggerCoreBase.h" #include #include #include #include #include class IBinary; class Status; namespace DebuggerCorePlugin { class PlatformThread; class DebuggerCore final : public DebuggerCoreBase { Q_OBJECT Q_PLUGIN_METADATA(IID "edb.IDebugger/1.0") Q_INTERFACES(IDebugger) Q_CLASSINFO("author", "Evan Teran") Q_CLASSINFO("url", "http://www.codef00.com") friend class PlatformProcess; friend class PlatformThread; CpuMode cpuMode() const override { return cpuMode_; } public: DebuggerCore(); ~DebuggerCore() override; public: MeansOfCapture lastMeansOfCapture() const override; Status attach(edb::pid_t pid) override; Status detach() override; Status open(const QString &path, const QString &cwd, const QList &args, const QString &input, const QString &output) override; bool hasExtension(uint64_t ext) const override; size_t pageSize() const override; std::shared_ptr waitDebugEvent(std::chrono::milliseconds msecs) override; std::size_t pointerSize() const override; uint8_t nopFillByte() const override; void kill() override; void setIgnoredExceptions(const QList &exceptions) override; public: QMap exceptions() const override; QString exceptionName(qlonglong value) override; qlonglong exceptionValue(const QString &name) override; public: edb::pid_t parentPid(edb::pid_t pid) const override; public: std::unique_ptr createState() const override; public: uint64_t cpuType() const override; private: QMap> enumerateProcesses() const override; public: QString flagRegister() const override; QString framePointer() const override; QString instructionPointer() const override; QString stackPointer() const override; public: IProcess *process() const override; private: Status ptraceContinue(edb::tid_t tid, long status); Status ptraceGetEventMessage(edb::tid_t tid, unsigned long *message); Status ptraceGetSigInfo(edb::tid_t tid, siginfo_t *siginfo); Status ptraceSetOptions(edb::tid_t tid, long options); Status ptraceStep(edb::tid_t tid, long status); long ptraceTraceme(); private: Status stopThreads(); int attachThread(edb::tid_t tid); long ptraceOptions() const; std::shared_ptr handleEvent(edb::tid_t tid, int status); std::shared_ptr handleThreadCreate(edb::tid_t tid, int status); void detectCpuMode(); void handleThreadExit(edb::tid_t tid, int status); void reset(); private: using threads_type = QHash>; private: // TODO(eteran): a few of these logically belong in PlatformProcess... CpuMode cpuMode_ = CpuMode::Unknown; MeansOfCapture lastMeansOfCapture_ = MeansOfCapture::NeverCaptured; QList ignoredExceptions_; std::set waitedThreads_; edb::tid_t activeThread_; std::shared_ptr process_; threads_type threads_; bool procMemReadBroken_ = true; bool procMemWriteBroken_ = true; std::size_t pointerSize_ = sizeof(void *); #if defined(EDB_X86) || defined(EDB_X86_64) const bool osIs64Bit_; const edb::seg_reg_t userCodeSegment32_; const edb::seg_reg_t userCodeSegment64_; const edb::seg_reg_t userStackSegment_; #endif }; } #endif edb-debugger/plugins/DebuggerCore/unix/linux/arch/0000755000175000017500000000000013765535463022006 5ustar eteraneteranedb-debugger/plugins/DebuggerCore/unix/linux/arch/arm-generic/0000755000175000017500000000000013765535463024177 5ustar eteraneteranedb-debugger/plugins/DebuggerCore/unix/linux/arch/arm-generic/PlatformThread.cpp0000644000175000017500000002712413765535463027625 0ustar eteraneteran/* Copyright (C) 2015 - 2015 Evan Teran evan.teran@gmail.com Copyright (C) 2017 Ruslan Kabatsayev b7.10110111@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "PlatformThread.h" #include "ArchProcessor.h" #include "Breakpoint.h" #include "DebuggerCore.h" #include "IProcess.h" #include "Instruction.h" #include "PlatformCommon.h" #include "PlatformState.h" #include "State.h" #include "Types.h" #include #ifndef _GNU_SOURCE #define _GNU_SOURCE /* or _BSD_SOURCE or _SVID_SOURCE */ #endif #include #include #include #include // doesn't always seem to be defined in the headers #ifndef PTRACE_GET_THREAD_AREA #define PTRACE_GET_THREAD_AREA static_cast<__ptrace_request>(22) #endif #ifndef PTRACE_GETSIGINFO #define PTRACE_GETSIGINFO static_cast<__ptrace_request>(0x4202) #endif #ifndef PTRACE_GETREGSET #define PTRACE_GETREGSET static_cast<__ptrace_request>(0x4204) #endif #ifndef PTRACE_SETREGSET #define PTRACE_SETREGSET static_cast<__ptrace_request>(0x4205) #endif #ifndef PTRACE_GETWMMXREGS #define PTRACE_GETWMMXREGS static_cast<__ptrace_request>(18) #define PTRACE_SETWMMXREGS static_cast<__ptrace_request>(19) #endif #ifndef PTRACE_GETVFPREGS #define PTRACE_GETVFPREGS static_cast<__ptrace_request>(27) #define PTRACE_SETVFPREGS static_cast<__ptrace_request>(28) #endif #ifndef PTRACE_GETHBPREGS #define PTRACE_GETHBPREGS static_cast<__ptrace_request>(29) #define PTRACE_SETHBPREGS static_cast<__ptrace_request>(30) #endif namespace DebuggerCorePlugin { /** * @brief PlatformThread::fillStateFromPrStatus * @param state * @return */ bool PlatformThread::fillStateFromPrStatus(PlatformState *state) { return false; } /** * @brief PlatformThread::fillStateFromSimpleRegs * @param state * @return */ bool PlatformThread::fillStateFromSimpleRegs(PlatformState *state) { user_regs regs; if (ptrace(PTRACE_GETREGS, tid_, 0, ®s) != -1) { state->fillFrom(regs); return true; } else { perror("PTRACE_GETREGS failed"); return false; } } /** * @brief PlatformThread::fillStateFromVFPRegs * @param state * @return */ bool PlatformThread::fillStateFromVFPRegs(PlatformState *state) { user_vfp fpr; if (ptrace(PTRACE_GETVFPREGS, tid_, 0, &fpr) != -1) { for (unsigned i = 0; i < sizeof fpr.fpregs / sizeof *fpr.fpregs; ++i) state->fillFrom(fpr); return true; } else { perror("PTRACE_GETVFPREGS failed"); return false; } } /** * @brief PlatformThread::getState * @param state */ void PlatformThread::getState(State *state) { // TODO: assert that we are paused core_->detectCpuMode(); if (auto state_impl = static_cast(state->impl_.get())) { fillStateFromSimpleRegs(state_impl); fillStateFromVFPRegs(state_impl); } } /** * @brief PlatformThread::setState * @param state */ void PlatformThread::setState(const State &state) { // TODO: assert that we are paused if (auto state_impl = static_cast(state.impl_.get())) { user_regs regs; state_impl->fillStruct(regs); if (ptrace(PTRACE_SETREGS, tid_, 0, ®s) == -1) { perror("PTRACE_SETREGS failed"); } user_vfp fpr; state_impl->fillStruct(fpr); if (ptrace(PTRACE_SETVFPREGS, tid_, 0, &fpr) == -1) { perror("PTRACE_SETVFPREGS failed"); } } } /** * @brief PlatformThread::getDebugRegister * @param n * @return */ unsigned long PlatformThread::getDebugRegister(std::size_t n) { return 0; } /** * @brief PlatformThread::setDebugRegister * @param n * @param value * @return */ long PlatformThread::setDebugRegister(std::size_t n, long value) { return 0; } /** * @brief PlatformThread::instructionPointer * @return */ edb::address_t PlatformThread::instructionPointer() const { return 0; } /** * @brief PlatformThread::doStep * @param tid * @param status * @return */ Status PlatformThread::doStep(const edb::tid_t tid, const long status) { constexpr auto AddressSize = 4; // The code here is ARM32-specific anyway... State state; getState(&state); if (state.empty()) return Status(tr("failed to get thread state.")); const auto pc = state.instructionPointer(); const auto flags = state.flags(); enum { CPSR_Tbit = 1 << 5, CPSR_ITbits72 = 1 << 10, CPSR_ITmask72 = 0xfc00, CPSR_Jbit = 1 << 24, CPSR_ITbits10 = 1 << 25, CPSR_ITmask10 = 0x06000000, }; if (flags & CPSR_Jbit) return Status(tr("EDB doesn't yet support single-stepping in Jazelle state.")); if (flags & CPSR_Tbit && flags & (CPSR_ITmask10 | CPSR_ITmask72)) return Status(tr("EDB doesn't yet support single-stepping inside Thumb-2 IT-block.")); quint8 buffer[4]; if (const int size = edb::v1::get_instruction_bytes(pc, buffer)) { if (const auto insn = edb::Instruction(buffer, buffer + size, pc)) { const auto op = insn.operation(); edb::address_t addrAfterInsn = pc + insn.byteSize(); auto targetMode = core_->cpuMode(); if (modifies_pc(insn) && edb::v1::arch_processor().isExecuted(insn, state)) { if (op == ARM_INS_BXJ) return Status(tr("EDB doesn't yet support single-stepping into Jazelle state.")); const auto opCount = insn.operandCount(); if (opCount == 0) return Status(tr("instruction %1 isn't supported yet.").arg(insn.mnemonic().c_str())); switch (op) { case ARM_INS_LDR: { const auto destOperand = insn.operand(0); if (!is_register(destOperand) || destOperand->reg != ARM_REG_PC) return Status(tr("instruction %1 with non-PC destination isn't supported yet.").arg(insn.mnemonic().c_str())); const auto srcOperand = insn.operand(1); if (!is_expression(srcOperand)) return Status(tr("unexpected type of second operand of LDR instruction.")); const auto effAddrR = edb::v1::arch_processor().getEffectiveAddress(insn, srcOperand, state); if (!effAddrR) return Status(effAddrR.error()); const auto effAddr = effAddrR.value(); if (process_->readBytes(effAddr, &addrAfterInsn, AddressSize) != AddressSize) return Status(tr("failed to read memory referred to by LDR operand (address %1).").arg(effAddr.toPointerString())); // FIXME: for ARMv5 or below (without "T" in the name) bits [1:0] are simply ignored, without any mode change if (addrAfterInsn & 1) targetMode = IDebugger::CpuMode::Thumb; else targetMode = IDebugger::CpuMode::ARM32; switch (edb::v1::debugger_core->cpuMode()) { case IDebugger::CpuMode::Thumb: addrAfterInsn &= -2; break; case IDebugger::CpuMode::ARM32: addrAfterInsn &= -4; break; default: return Status(tr("single-stepping LDR instruction in modes other than ARM or Thumb is not supported yet.")); } break; } case ARM_INS_POP: { int i = 0; for (; i < opCount; ++i) { const auto operand = insn.operand(i); if (is_register(operand) && operand->reg == ARM_REG_PC) { #if CS_API_MAJOR >= 4 assert(operand->access == CS_AC_WRITE); #endif const auto sp = state.gpRegister(PlatformState::GPR::SP); if (!sp) return Status(tr("failed to get value of SP register")); if (process_->readBytes(sp.valueAsAddress() + AddressSize * i, &addrAfterInsn, AddressSize) != AddressSize) return Status(tr("failed to read thread stack")); break; } } if (i == opCount) return Status(tr("internal EDB error: failed to locate PC in the instruction operand list")); break; } case ARM_INS_BX: case ARM_INS_BLX: case ARM_INS_B: case ARM_INS_BL: { if (opCount != 1) return Status(tr("unexpected form of instruction %1 with %2 operands.").arg(insn.mnemonic().c_str()).arg(opCount)); const auto &operand = insn.operand(0); assert(operand); if (is_immediate(operand)) { addrAfterInsn = edb::address_t(util::to_unsigned(operand->imm)); if (op == ARM_INS_BX || op == ARM_INS_BLX) { if (targetMode == IDebugger::CpuMode::ARM32) targetMode = IDebugger::CpuMode::Thumb; else targetMode = IDebugger::CpuMode::ARM32; } break; } else if (is_register(operand)) { if (operand->reg == ARM_REG_PC && (op == ARM_INS_BX || op == ARM_INS_BLX)) return Status(tr("unpredictable instruction")); // This may happen only with BX or BLX: B and BL require an immediate operand const auto result = edb::v1::arch_processor().getEffectiveAddress(insn, operand, state); if (!result) return Status(result.error()); addrAfterInsn = result.value(); if (addrAfterInsn & 1) targetMode = IDebugger::CpuMode::Thumb; else targetMode = IDebugger::CpuMode::ARM32; addrAfterInsn &= ~1; if (addrAfterInsn & 0x3 && targetMode != IDebugger::CpuMode::Thumb) return Status(tr("won't try to set breakpoint at unaligned address")); break; } return Status(tr("bad operand for %1 instruction.").arg(insn.mnemonic().c_str())); } default: return Status(tr("instruction %1 modifies PC, but isn't a branch instruction known to EDB's single-stepper.").arg(insn.mnemonic().c_str())); } } if (singleStepBreakpoint) return Status(tr("internal EDB error: single-step breakpoint still present")); if (const auto oldBP = core_->findBreakpoint(addrAfterInsn)) { // TODO: EDB should support overlapping breakpoints if (!oldBP->enabled()) return Status(tr("a disabled breakpoint is present at address %1, can't set one for single step.").arg(addrAfterInsn.toPointerString())); } else { singleStepBreakpoint = core_->addBreakpoint(addrAfterInsn); if (!singleStepBreakpoint) return Status(tr("failed to set breakpoint at address %1.").arg(addrAfterInsn.toPointerString())); const auto bp = std::static_pointer_cast(singleStepBreakpoint); if (targetMode != core_->cpuMode()) { switch (targetMode) { case IDebugger::CpuMode::ARM32: bp->setType(Breakpoint::TypeId::ARM32); break; case IDebugger::CpuMode::Thumb: bp->setType(Breakpoint::TypeId::Thumb2Byte); break; } } singleStepBreakpoint->setOneTime(true); // TODO: don't forget to remove it once we've paused after this, even if the BP wasn't hit (e.g. due to an exception on current instruction) singleStepBreakpoint->setInternal(true); } return core_->ptraceContinue(tid, status); } return Status(tr("failed to disassemble instruction at address %1.").arg(pc.toPointerString())); } return Status(tr("failed to get instruction bytes at address %1.").arg(pc.toPointerString())); } /** * steps this thread one instruction, passing the signal that stopped it * (unless the signal was SIGSTOP) * * @brief PlatformThread::step * @return */ Status PlatformThread::step() { return doStep(tid_, resume_code(status_)); } /** * steps this thread one instruction, passing the signal that stopped it * (unless the signal was SIGSTOP, or the passed status != DEBUG_EXCEPTION_NOT_HANDLED) * * @brief PlatformThread::step * @param status * @return */ Status PlatformThread::step(edb::EVENT_STATUS status) { const int code = (status == edb::DEBUG_EXCEPTION_NOT_HANDLED) ? resume_code(status_) : 0; return doStep(tid_, code); } } edb-debugger/plugins/DebuggerCore/unix/linux/arch/arm-generic/PlatformState.h0000644000175000017500000000640513765535463027142 0ustar eteraneteran/* Copyright (C) 2017 Ruslan Kabatsayev b7.1010111@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PLATFORM_STATE_H_20170806_ #define PLATFORM_STATE_H_20170806_ #include "IState.h" #include "PrStatus.h" #include "Types.h" #include "edb.h" #include #include #include namespace DebuggerCorePlugin { using std::size_t; static constexpr size_t GPR_COUNT = 16; static constexpr size_t VFPR_COUNT = 32; struct user_vfp { unsigned long long fpregs[32]; unsigned long fpscr; }; class PlatformState final : public IState { Q_DECLARE_TR_FUNCTIONS(PlatformThread) friend class DebuggerCore; friend class PlatformThread; public: PlatformState(); public: std::unique_ptr clone() const override; QString flagsToString() const override; QString flagsToString(edb::reg_t flags) const override; Register value(const QString ®) const override; Register instructionPointerRegister() const override; Register flagsRegister() const override; edb::address_t framePointer() const override; edb::address_t instructionPointer() const override; edb::address_t stackPointer() const override; edb::reg_t debugRegister(size_t n) const override; edb::reg_t flags() const override; void adjustStack(int bytes) override; void clear() override; bool empty() const override; void setDebugRegister(size_t n, edb::reg_t value) override; void setFlags(edb::reg_t flags) override; void setInstructionPointer(edb::address_t value) override; void setRegister(const Register ®) override; void setRegister(const QString &name, edb::reg_t value) override; Register gpRegister(size_t n) const override; Register archRegister(uint64_t type, size_t n) const override { return Register(); } void fillFrom(const user_regs ®s); void fillFrom(const user_vfp ®s); void fillStruct(user_regs ®s) const; void fillStruct(user_vfp ®s) const; private: struct GPR { enum NamedGPRIndex : size_t { SB = 9, // historical name, but still printed by modern disassemblers SL = 10, // historical name, but still printed by modern disassemblers FP = 11, // conventionally, but much like rBP on x86 IP = 12, // conventionally, intra-procedure scratch register SP = 13, LR = 14, PC = 15, }; using RegNameVariants = std::vector; static const std::array GPRegNames; bool filled = false; std::array GPRegs; edb::reg_t cpsr; public: void clear(); bool empty() const; } gpr; struct VFP { std::array d; edb::value32 fpscr; bool filled = false; } vfp; private: auto findGPR(const QString &name) const -> decltype(gpr.GPRegNames.begin()); }; } #endif edb-debugger/plugins/DebuggerCore/unix/linux/arch/arm-generic/PlatformState.cpp0000644000175000017500000001655613765535463027505 0ustar eteraneteran/* Copyright (C) 2017 Ruslan Kabatsayev b7.10110111@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "PlatformState.h" namespace DebuggerCorePlugin { const std::array PlatformState::GPR::GPRegNames = { RegNameVariants{"r0", "a1"}, RegNameVariants{"r1", "a2"}, RegNameVariants{"r2", "a3"}, RegNameVariants{"r3", "a4"}, RegNameVariants{"r4", "v1"}, RegNameVariants{"r5", "v2"}, RegNameVariants{"r6", "v3"}, RegNameVariants{"r7", "v4"}, RegNameVariants{"r8", "v5"}, RegNameVariants{"r9", "sb", "v6"}, RegNameVariants{"r10", "sl", "v7"}, RegNameVariants{"r11", "fp", "v8"}, RegNameVariants{"r12", "ip", "v6"}, RegNameVariants{"sp", "r13"}, RegNameVariants{"lr", "r14"}, RegNameVariants{"pc", "r15"}}; /** * @brief PlatformState::PlatformState */ PlatformState::PlatformState() { clear(); } /** * @brief PlatformState::clone * @return */ std::unique_ptr PlatformState::clone() const { auto copy = std::make_unique(); copy->gpr = gpr; return copy; } /** * @brief PlatformState::flagsToString * @return */ QString PlatformState::flagsToString() const { return flagsToString(flagsRegister().valueAsInteger()); } /** * @brief PlatformState::flagsToString * @param flags * @return */ QString PlatformState::flagsToString(edb::reg_t flags) const { return "flags string"; // FIXME: stub } /** * @brief PlatformState::findGPR * @param name */ auto PlatformState::findGPR(QString const &name) const -> decltype(gpr.GPRegNames.begin()) { return std::find_if(GPR::GPRegNames.begin(), GPR::GPRegNames.end(), [&name](const GPR::RegNameVariants &variants) { for (const char *const var : variants) { if (var == name) { return true; } } return false; }); } /** * @brief PlatformState::value * @param reg * @return */ Register PlatformState::value(const QString ®) const { const QString name = reg.toLower(); if (name == "cpsr") { return flagsRegister(); } if (vfp.filled && name == "fpscr") { return make_Register<32>("fpscr", vfp.fpscr, Register::TYPE_FPU); } const auto gprFoundIt = findGPR(name); if (gprFoundIt != GPR::GPRegNames.end()) { return gpRegister(gprFoundIt - GPR::GPRegNames.begin()); } return Register(); } /** * @brief PlatformState::instructionPointerRegister * @return */ Register PlatformState::instructionPointerRegister() const { #ifdef EDB_ARM32 return gpRegister(GPR::PC); #else return Register(); // FIXME: stub #endif } /** * @brief PlatformState::flagsRegister * @return */ Register PlatformState::flagsRegister() const { #ifdef EDB_ARM32 if (!gpr.filled) return Register(); return make_Register<32>("cpsr", gpr.cpsr, Register::TYPE_GPR); #else return Register(); // FIXME: stub #endif } /** * @brief PlatformState::framePointer * @return */ edb::address_t PlatformState::framePointer() const { return gpr.GPRegs[GPR::FP]; } /** * @brief PlatformState::instructionPointer * @return */ edb::address_t PlatformState::instructionPointer() const { return gpr.GPRegs[GPR::PC]; } /** * @brief PlatformState::stackPointer * @return */ edb::address_t PlatformState::stackPointer() const { return gpr.GPRegs[GPR::SP]; } /** * @brief PlatformState::debugRegister * @param n * @return */ edb::reg_t PlatformState::debugRegister(size_t n) const { return 0; // FIXME: stub } /** * @brief PlatformState::flags * @return */ edb::reg_t PlatformState::flags() const { return gpr.cpsr; } /** * @brief PlatformState::adjustStack * @param bytes */ void PlatformState::adjustStack(int bytes) { gpr.GPRegs[GPR::SP] += bytes; } /** * @brief PlatformState::clear */ void PlatformState::clear() { gpr.clear(); } /** * @brief PlatformState::empty * @return */ bool PlatformState::empty() const { return gpr.empty(); } /** * @brief PlatformState::GPR::empty * @return */ bool PlatformState::GPR::empty() const { return !filled; } /** * @brief PlatformState::GPR::clear */ void PlatformState::GPR::clear() { util::mark_memory(this, sizeof(*this)); filled = false; } /** * @brief PlatformState::setDebugRegister * @param n * @param value */ void PlatformState::setDebugRegister(size_t n, edb::reg_t value) { // FIXME: stub } /** * @brief PlatformState::setFlags * @param flags */ void PlatformState::setFlags(edb::reg_t flags) { gpr.cpsr = flags; } /** * @brief PlatformState::setInstructionPointer * @param value */ void PlatformState::setInstructionPointer(edb::address_t value) { gpr.GPRegs[GPR::PC] = value; } /** * @brief PlatformState::setRegister * @param reg */ void PlatformState::setRegister(const Register ®) { if (!reg) { return; } const QString name = reg.name().toLower(); if (name == "cpsr") { setFlags(reg.value()); return; } if (name == "fpscr") { vfp.fpscr = reg.value(); return; } const auto gprFoundIt = findGPR(name); if (gprFoundIt != GPR::GPRegNames.end()) { const auto index = gprFoundIt - GPR::GPRegNames.begin(); assert(index < 16); gpr.GPRegs[index] = reg.value(); return; } } /** * @brief PlatformState::setRegister * @param name * @param value */ void PlatformState::setRegister(const QString &name, edb::reg_t value) { #ifdef EDB_ARM32 const QString regName = name.toLower(); setRegister(make_Register<32>(regName, value, Register::TYPE_GPR)); // FIXME: this doesn't take into account any 64-bit registers - possibly FPU data? #endif } /** * @brief PlatformState::gpRegister * @param n * @return */ Register PlatformState::gpRegister(size_t n) const { #ifdef EDB_ARM32 if (n < GPR::GPRegNames.size()) return make_Register<32>(gpr.GPRegNames[n].front(), gpr.GPRegs[n], Register::TYPE_GPR); return Register(); #else return Register(); // FIXME: stub #endif } /** * @brief PlatformState::fillFrom * @param regs */ void PlatformState::fillFrom(user_regs const ®s) { for (unsigned i = 0; i < gpr.GPRegs.size(); ++i) { gpr.GPRegs[i] = regs.uregs[i]; } gpr.cpsr = regs.uregs[16]; gpr.filled = true; } /** * @brief PlatformState::fillFrom * @param regs */ void PlatformState::fillFrom(user_vfp const ®s) { for (unsigned i = 0; i < vfp.d.size(); ++i) { vfp.d[i] = regs.fpregs[i]; } vfp.fpscr = regs.fpscr; vfp.filled = true; } /** * @brief PlatformState::fillStruct * @param regs */ void PlatformState::fillStruct(user_regs ®s) const { util::mark_memory(®s, sizeof(regs)); if (gpr.filled) { for (unsigned i = 0; i < gpr.GPRegs.size(); ++i) { regs.uregs[i] = gpr.GPRegs[i]; } regs.uregs[16] = gpr.cpsr; // FIXME: uregs[17] is not filled } } /** * @brief PlatformState::fillStruct * @param regs */ void PlatformState::fillStruct(user_vfp ®s) const { util::mark_memory(®s, sizeof(regs)); if (vfp.filled) { for (unsigned i = 0; i < vfp.d.size(); ++i) { regs.fpregs[i] = vfp.d[i]; } regs.fpscr = vfp.fpscr; } } } edb-debugger/plugins/DebuggerCore/unix/linux/arch/x86-generic/0000755000175000017500000000000013765535463024045 5ustar eteraneteranedb-debugger/plugins/DebuggerCore/unix/linux/arch/x86-generic/PlatformThread.cpp0000644000175000017500000002357213765535463027476 0ustar eteraneteran/* Copyright (C) 2015 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "PlatformThread.h" #include "DebuggerCore.h" #include "IProcess.h" #include "PlatformCommon.h" #include "PlatformState.h" #include "State.h" #include #ifndef _GNU_SOURCE #define _GNU_SOURCE /* or _BSD_SOURCE or _SVID_SOURCE */ #endif #include #include #include #include #include // doesn't always seem to be defined in the headers #ifndef PTRACE_GET_THREAD_AREA #define PTRACE_GET_THREAD_AREA static_cast<__ptrace_request>(25) #endif #ifndef PTRACE_SET_THREAD_AREA #define PTRACE_SET_THREAD_AREA static_cast<__ptrace_request>(26) #endif #ifndef PTRACE_GETSIGINFO #define PTRACE_GETSIGINFO static_cast<__ptrace_request>(0x4202) #endif #ifndef PTRACE_GETREGSET #define PTRACE_GETREGSET static_cast<__ptrace_request>(0x4204) #endif #ifndef PTRACE_SETREGSET #define PTRACE_SETREGSET static_cast<__ptrace_request>(0x4205) #endif namespace DebuggerCorePlugin { /** * @brief PlatformThread::fillSegmentBases * @param state */ void PlatformThread::fillSegmentBases(PlatformState *state) { struct user_desc desc = {}; for (size_t sregIndex = 0; sregIndex < state->seg_reg_count(); ++sregIndex) { const edb::seg_reg_t reg = state->x86.segRegs[sregIndex]; if (!reg) { continue; } bool fromGDT = !(reg & 0x04); // otherwise the selector picks descriptor from LDT if (!fromGDT) { continue; } if (ptrace(PTRACE_GET_THREAD_AREA, tid_, reg.toUint() / LDT_ENTRY_SIZE, &desc) != -1) { state->x86.segRegBases[sregIndex] = desc.base_addr; state->x86.segRegBasesFilled[sregIndex] = true; } } for (size_t sregIndex = 0; sregIndex < state->seg_reg_count(); ++sregIndex) { const edb::seg_reg_t sreg = state->x86.segRegs[sregIndex]; if (sreg == core_->userCodeSegment32_ || sreg == core_->userCodeSegment64_ || sreg == core_->userStackSegment_ || (state->is64Bit() && sregIndex < PlatformState::X86::FS)) { state->x86.segRegBases[sregIndex] = 0; state->x86.segRegBasesFilled[sregIndex] = true; } } } /** * @brief PlatformThread::fillStateFromPrStatus * @param state * @return */ bool PlatformThread::fillStateFromPrStatus(PlatformState *state) { static bool prStatusSupported = true; if (!prStatusSupported) { return false; } PrStatus_X86_64 prstat64; iovec prstat_iov = {&prstat64, sizeof(prstat64)}; if (ptrace(PTRACE_GETREGSET, tid_, NT_PRSTATUS, &prstat_iov) != -1) { switch (prstat_iov.iov_len) { case sizeof(PrStatus_X86_64): state->fillFrom(prstat64); break; case sizeof(PrStatus_X86): // In this case the actual structure returned is PrStatus_X86, // so copy it to the correct container (reinterpret_cast would // cause UB in any case). Good compiler should be able to optimize this out. PrStatus_X86 prstat32; std::memcpy(&prstat32, &prstat64, sizeof(prstat32)); state->fillFrom(prstat32); break; default: prStatusSupported = false; qWarning() << "PTRACE_GETREGSET(NT_PRSTATUS) returned unexpected length " << prstat_iov.iov_len; return false; } } else { prStatusSupported = false; perror("PTRACE_GETREGSET(NT_PRSTATUS) failed"); return false; } fillSegmentBases(state); return true; } /** * @brief PlatformThread::fillStateFromSimpleRegs * @param state * @return */ bool PlatformThread::fillStateFromSimpleRegs(PlatformState *state) { user_regs_struct regs; if (ptrace(PTRACE_GETREGS, tid_, 0, ®s) != -1) { state->fillFrom(regs); fillSegmentBases(state); return true; } else { perror("PTRACE_GETREGS failed"); return false; } } /** * @brief PlatformThread::getState * @param state */ void PlatformThread::getState(State *state) { // TODO: assert that we are paused core_->detectCpuMode(); if (auto state_impl = static_cast(state->impl_.get())) { // State must be cleared before filling to zero all presence flags, otherwise something // may remain not updated. Also, this way we'll mark all the unfilled values. state_impl->clear(); if (EDB_IS_64_BIT) { // 64-bit GETREGS call always returns 64-bit state, so use it fillStateFromSimpleRegs(state_impl); } else if (!fillStateFromPrStatus(state_impl)) { // if EDB is 32 bit, use GETREGSET so that we get 64-bit state for 64-bit debuggee fillStateFromSimpleRegs(state_impl); // failing that, try to just get what we can } // First try to get full XSTATE X86XState xstate; struct iovec iov = {&xstate, sizeof(xstate)}; long status = ptrace(PTRACE_GETREGSET, tid_, NT_X86_XSTATE, &iov); if (status == -1 || !state_impl->fillFrom(xstate, iov.iov_len)) { // No XSTATE available, get just floating point and SSE registers static bool getFPXRegsSupported = EDB_IS_32_BIT; UserFPXRegsStructX86 fpxregs; // This should be automatically optimized out on amd64. If not, not a big deal. // Avoiding conditional compilation to facilitate syntax error checking if (getFPXRegsSupported) { getFPXRegsSupported = (ptrace(PTRACE_GETFPXREGS, tid_, 0, &fpxregs) != -1); } if (getFPXRegsSupported) { state_impl->fillFrom(fpxregs); } else { // No GETFPXREGS: on x86 this means SSE is not supported // on x86_64 FPREGS already contain SSE state struct user_fpregs_struct fpregs; status = ptrace(PTRACE_GETFPREGS, tid_, 0, &fpregs); if (status != -1) { state_impl->fillFrom(fpregs); } else { perror("PTRACE_GETFPREGS failed"); } } } // debug registers for (std::size_t i = 0; i < 8; ++i) { state_impl->x86.dbgRegs[i] = getDebugRegister(i); } } } /** * @brief PlatformThread::setState * @param state */ void PlatformThread::setState(const State &state) { // TODO: assert that we are paused if (auto state_impl = static_cast(state.impl_.get())) { bool setPrStatusDone = false; if (EDB_IS_32_BIT && state_impl->is64Bit()) { // Try to set 64-bit state PrStatus_X86_64 prstat64; state_impl->fillStruct(prstat64); struct iovec prstat_iov = {&prstat64, sizeof(prstat64)}; if (ptrace(PTRACE_SETREGSET, tid_, NT_PRSTATUS, &prstat_iov) != -1) { setPrStatusDone = true; } else { perror("PTRACE_SETREGSET failed"); } } // Fallback to setting 32-bit set if (!setPrStatusDone) { struct user_regs_struct regs; state_impl->fillStruct(regs); ptrace(PTRACE_SETREGS, tid_, 0, ®s); } // debug registers for (std::size_t i = 0; i < 8; ++i) { setDebugRegister(i, state_impl->x86.dbgRegs[i]); } // hope for the best, adjust for reality static bool xsaveSupported = true; if (xsaveSupported) { X86XState xstate; const auto size = state_impl->fillStruct(xstate); struct iovec iov = {&xstate, size}; if (ptrace(PTRACE_SETREGSET, tid_, NT_X86_XSTATE, &iov) == -1) { xsaveSupported = false; } } // If xsave/xrstor appears unsupported, fallback to fxrstor // NOTE: it's not "else", it's an independent check for possibly modified flag if (!xsaveSupported) { static bool setFPXRegsSupported = EDB_IS_32_BIT; if (setFPXRegsSupported) { UserFPXRegsStructX86 fpxregs; state_impl->fillStruct(fpxregs); setFPXRegsSupported = (ptrace(PTRACE_SETFPXREGS, tid_, 0, &fpxregs) != -1); } if (!setFPXRegsSupported) { // No SETFPXREGS: on x86 this means SSE is not supported // on x86_64 FPREGS already contain SSE state // Just set fpregs then struct user_fpregs_struct fpregs; state_impl->fillStruct(fpregs); if (ptrace(PTRACE_SETFPREGS, tid_, 0, &fpregs) == -1) { perror("PTRACE_SETFPREGS failed"); } } } } } /** * @brief PlatformThread::instructionPointer * @return */ edb::address_t PlatformThread::instructionPointer() const { #if defined(EDB_X86) return ptrace(PTRACE_PEEKUSER, tid_, offsetof(UserRegsStructX86, eip), 0); #elif defined(EDB_X86_64) // NOTE(eteran): even when we debug a 32-bit app on a 64-bit debugger, // we use the 64-bit register struct here return ptrace(PTRACE_PEEKUSER, tid_, offsetof(UserRegsStructX86_64, rip), 0); #elif defined(EDB_ARM32) return 0; #elif defined(EDB_ARM64) return 0; #endif } /** * @brief PlatformThread::getDebugRegister * @param n * @return */ unsigned long PlatformThread::getDebugRegister(std::size_t n) { size_t drOffset = offsetof(struct user, u_debugreg) + n * sizeof(user::u_debugreg[0]); return ptrace(PTRACE_PEEKUSER, tid_, drOffset, 0); } /** * @brief PlatformThread::setDebugRegister * @param n * @param value * @return */ long PlatformThread::setDebugRegister(std::size_t n, unsigned long value) { size_t drOffset = offsetof(struct user, u_debugreg) + n * sizeof(user::u_debugreg[0]); return ptrace(PTRACE_POKEUSER, tid_, drOffset, value); } /** * steps this thread one instruction, passing the signal that stopped it * (unless the signal was SIGSTOP) * * @brief PlatformThread::step * @return */ Status PlatformThread::step() { return core_->ptraceStep(tid_, resume_code(status_)); } /** * steps this thread one instruction, passing the signal that stopped it * (unless the signal was SIGSTOP, or the passed status != DEBUG_EXCEPTION_NOT_HANDLED) * * @brief PlatformThread::step * @param status * @return */ Status PlatformThread::step(edb::EventStatus status) { const int code = (status == edb::DEBUG_EXCEPTION_NOT_HANDLED) ? resume_code(status_) : 0; return core_->ptraceStep(tid_, code); } } edb-debugger/plugins/DebuggerCore/unix/linux/arch/x86-generic/PlatformState.h0000644000175000017500000003600413765535463027006 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PLATFORM_STATE_H_20110330_ #define PLATFORM_STATE_H_20110330_ #include "IState.h" #include "PrStatus.h" #include "Types.h" #include "edb.h" #include #include namespace DebuggerCorePlugin { using std::size_t; static constexpr size_t IA32_GPR_COUNT = 8; static constexpr size_t IA32_GPR_LOW_ADDRESSABLE_COUNT = 4; // al,cl,dl,bl static constexpr size_t AMD64_GPR_COUNT = 16; static constexpr size_t AMD64_GPR_LOW_ADDRESSABLE_COUNT = 16; // all GPRs' low bytes are addressable in 64 bit mode static constexpr size_t IA32_XMM_REG_COUNT = IA32_GPR_COUNT; static constexpr size_t AMD64_XMM_REG_COUNT = AMD64_GPR_COUNT; static constexpr size_t IA32_YMM_REG_COUNT = IA32_GPR_COUNT; static constexpr size_t AMD64_YMM_REG_COUNT = AMD64_GPR_COUNT; static constexpr size_t IA32_ZMM_REG_COUNT = IA32_GPR_COUNT; static constexpr size_t AMD64_ZMM_REG_COUNT = 32; static constexpr size_t MAX_GPR_COUNT = AMD64_GPR_COUNT; static constexpr size_t MAX_GPR_LOW_ADDRESSABLE_COUNT = AMD64_GPR_LOW_ADDRESSABLE_COUNT; static constexpr size_t MAX_GPR_HIGH_ADDRESSABLE_COUNT = 4; // ah,ch,dh,bh static constexpr size_t MAX_DBG_REG_COUNT = 8; static constexpr size_t MAX_SEG_REG_COUNT = 6; static constexpr size_t MAX_FPU_REG_COUNT = 8; static constexpr size_t MAX_MMX_REG_COUNT = MAX_FPU_REG_COUNT; static constexpr size_t MAX_XMM_REG_COUNT = AMD64_XMM_REG_COUNT; static constexpr size_t MAX_YMM_REG_COUNT = AMD64_YMM_REG_COUNT; static constexpr size_t MAX_ZMM_REG_COUNT = AMD64_ZMM_REG_COUNT; #if defined(EDB_X86) using UserRegsStructX86 = struct user_regs_struct; using UserFPRegsStructX86 = struct user_fpregs_struct; using UserFPXRegsStructX86 = struct user_fpxregs_struct; // Dummies to avoid missing compile-time checks for conversion code. // Actual layout is irrelevant since the code is not going to be executed struct UserFPRegsStructX86_64 { uint16_t cwd; uint16_t swd; uint16_t ftw; uint16_t fop; // last instruction opcode uint64_t rip; // last instruction EIP uint64_t rdp; // last operand pointer uint32_t mxcsr; uint32_t mxcr_mask; uint32_t st_space[32]; uint32_t xmm_space[64]; uint32_t padding[24]; }; struct UserRegsStructX86_64 { uint64_t r15; uint64_t r14; uint64_t r13; uint64_t r12; uint64_t rbp; uint64_t rbx; uint64_t r11; uint64_t r10; uint64_t r9; uint64_t r8; uint64_t rax; uint64_t rcx; uint64_t rdx; uint64_t rsi; uint64_t rdi; uint64_t orig_rax; uint64_t rip; uint64_t cs; uint64_t eflags; uint64_t rsp; uint64_t ss; uint64_t fs_base; uint64_t gs_base; uint64_t ds; uint64_t es; uint64_t fs; uint64_t gs; }; #elif defined(EDB_X86_64) using UserRegsStructX86_64 = user_regs_struct; using UserFPRegsStructX86_64 = user_fpregs_struct; // Dummies to avoid missing compile-time checks for conversion code // Actual layout is irrelevant since the code is not going to be executed struct UserRegsStructX86 { uint32_t ebx; uint32_t ecx; uint32_t edx; uint32_t esi; uint32_t edi; uint32_t ebp; uint32_t eax; uint32_t xds; uint32_t xes; uint32_t xfs; uint32_t xgs; uint32_t orig_eax; uint32_t eip; uint32_t xcs; uint32_t eflags; uint32_t esp; uint32_t xss; }; struct UserFPXRegsStructX86 { uint16_t cwd; uint16_t swd; uint16_t twd; uint16_t fop; // last instruction opcode uint32_t fip; // last instruction EIP uint32_t fcs; // last instruction CS uint32_t foo; // last operand offset uint32_t fos; // last operand selector uint32_t mxcsr; uint32_t reserved; uint32_t st_space[32]; /* 8*16 bytes for each FP-reg = 128 bytes */ uint32_t xmm_space[32]; /* 8*16 bytes for each XMM-reg = 128 bytes */ uint32_t padding[56]; }; struct UserFPRegsStructX86 { uint32_t cwd; uint32_t swd; uint32_t twd; uint32_t fip; // last instruction EIP uint32_t fcs; // last instruction CS uint32_t foo; // last operand offset uint32_t fos; // last operand selector uint32_t st_space[20]; }; #endif // Masks for XCR0 feature enabled bits #define X86_XSTATE_X87_MASK X87_XSTATE_X87 #define X86_XSTATE_SSE_MASK (X87_XSTATE_X87 | X87_XSTATE_SSE) struct X86XState { uint16_t cwd; uint16_t swd; uint16_t twd; uint16_t fop; // last instruction opcode uint32_t fioff; // last instruction EIP uint32_t fiseg; // last instruction CS in 32 bit mode, high 32 bits of RIP in 64 bit mode uint32_t fooff; // last operand offset uint32_t foseg; // last operand selector in 32 bit mode, high 32 bits of FDP in 64 bit mode uint32_t mxcsr; uint32_t mxcsr_mask; // FIXME uint8_t st_space[16 * 8]; // 8 16-byte fields uint8_t xmm_space[16 * 16]; // 16 16-byte fields, regardless of XMM_REG_COUNT uint8_t padding[48]; union { uint64_t xcr0; uint8_t sw_usable_bytes[48]; }; union { uint64_t xstate_bv; uint8_t xstate_hdr_bytes[64]; }; uint8_t ymmh_space[16 * 16]; // The extended state feature bits enum FeatureBit : uint64_t { FEATURE_X87 = 1 << 0, FEATURE_SSE = 1 << 1, FEATURE_AVX = 1 << 2, // MPX adds two feature bits FEATURE_BNDREGS = 1 << 3, FEATURE_BNDCFG = 1 << 4, FEATURE_MPX = FEATURE_BNDREGS | FEATURE_BNDCFG, // AVX-512 adds three feature bits FEATURE_K = 1 << 5, FEATURE_ZMM_H = 1 << 6, FEATURE_ZMM = 1 << 7, FEATURE_AVX512 = FEATURE_K | FEATURE_ZMM_H | FEATURE_ZMM, }; // Possible sizes of X86_XSTATE static constexpr size_t XSAVE_NONEXTENDED_SIZE = 576; static constexpr size_t SSE_SIZE = XSAVE_NONEXTENDED_SIZE; static constexpr size_t AVX_SIZE = 832; static constexpr size_t BNDREGS_SIZE = 1024; static constexpr size_t BNDCFG_SIZE = 1088; static constexpr size_t AVX512_SIZE = 2688; static constexpr size_t MAX_SIZE = 2688; }; static_assert(std::is_standard_layout::value, "X86XState struct is supposed to have standard layout"); static_assert(offsetof(X86XState, st_space) == 32, "ST space should appear at offset 32"); static_assert(offsetof(X86XState, xmm_space) == 160, "XMM space should appear at offset 160"); static_assert(offsetof(X86XState, xcr0) == 464, "XCR0 should appear at offset 464"); static_assert(offsetof(X86XState, ymmh_space) == 576, "YMM_H space should appear at offset 576"); class PlatformState final : public IState { friend class DebuggerCore; friend class PlatformThread; public: PlatformState(); public: std::unique_ptr clone() const override; public: QString flagsToString() const override; QString flagsToString(edb::reg_t flags) const override; Register value(const QString ®) const override; Register instructionPointerRegister() const override; Register flagsRegister() const override; edb::address_t framePointer() const override; edb::address_t instructionPointer() const override; edb::address_t stackPointer() const override; edb::reg_t debugRegister(size_t n) const override; edb::reg_t flags() const override; int fpuStackPointer() const override; edb::value80 fpuRegister(size_t n) const override; bool fpuRegisterIsEmpty(size_t n) const override; QString fpuRegisterTagString(size_t n) const override; edb::value16 fpuControlWord() const override; edb::value16 fpuStatusWord() const override; edb::value16 fpuTagWord() const override; void adjustStack(int bytes) override; void clear() override; bool empty() const override; void setDebugRegister(size_t n, edb::reg_t value) override; void setFlags(edb::reg_t flags) override; void setInstructionPointer(edb::address_t value) override; void setRegister(const Register ®) override; void setRegister(const QString &name, edb::reg_t value) override; Register archRegister(uint64_t type, size_t n) const override; Register gpRegister(size_t n) const override; bool is64Bit() const { return edb::v1::debuggeeIs64Bit(); } bool is32Bit() const { return edb::v1::debuggeeIs32Bit(); } size_t dbg_reg_count() const { return MAX_DBG_REG_COUNT; } size_t seg_reg_count() const { return MAX_SEG_REG_COUNT; } size_t fpu_reg_count() const { return MAX_FPU_REG_COUNT; } size_t mmx_reg_count() const { return MAX_MMX_REG_COUNT; } size_t xmm_reg_count() const { return is64Bit() ? AMD64_XMM_REG_COUNT : IA32_XMM_REG_COUNT; } size_t ymm_reg_count() const { return is64Bit() ? AMD64_YMM_REG_COUNT : IA32_YMM_REG_COUNT; } size_t zmm_reg_count() const { return is64Bit() ? AMD64_ZMM_REG_COUNT : IA32_ZMM_REG_COUNT; } size_t gpr64_count() const { return is64Bit() ? AMD64_GPR_COUNT : 0; } size_t gpr_count() const { return is64Bit() ? AMD64_GPR_COUNT : IA32_GPR_COUNT; } size_t gpr_low_addressable_count() const { return is64Bit() ? AMD64_GPR_LOW_ADDRESSABLE_COUNT : IA32_GPR_LOW_ADDRESSABLE_COUNT; } size_t gpr_high_addressable_count() const { return MAX_GPR_HIGH_ADDRESSABLE_COUNT; } const char *IPName() const { return is64Bit() ? x86.IP64Name : x86.IP32Name; } const char *flagsName() const { return is64Bit() ? x86.flags64Name : x86.flags32Name; } const std::array &GPRegNames() const { return is64Bit() ? x86.GPReg64Names : x86.GPReg32Names; } private: Register mmx_register(size_t n) const; Register xmm_register(size_t n) const; Register ymm_register(size_t n) const; private: // The whole AVX* state. XMM and YMM registers are lower parts of ZMM ones. struct AVX { public: static constexpr const char *mxcsrName = "mxcsr"; public: std::array zmmStorage; edb::value32 mxcsr; edb::value32 mxcsrMask; edb::value64 xcr0; bool xmmFilledIA32 = false; bool xmmFilledAMD64 = false; // This can be false when filled from e.g. FPXregs bool ymmFilled = false; bool zmmFilled = false; bool mxcsrMaskFilled = false; public: void clear(); bool empty() const; edb::value128 xmm(size_t index) const; void setXMM(size_t index, edb::value128); edb::value256 ymm(size_t index) const; void setYMM(size_t index, edb::value256); void setYMM(size_t index, edb::value128 low, edb::value128 high); edb::value512 zmm(size_t index) const; void setZMM(size_t index, edb::value512); } avx; // x87 state struct X87 { public: enum Tag { TAG_VALID = 0, TAG_ZERO = 1, TAG_SPECIAL = 2, TAG_EMPTY = 3 }; public: std::array R; // Rx registers edb::address_t instPtrOffset; edb::address_t dataPtrOffset; edb::value16 instPtrSelector; edb::value16 dataPtrSelector; edb::value16 controlWord; edb::value16 statusWord; edb::value16 tagWord; edb::value16 opCode; bool filled = false; bool opCodeFilled = false; public: void clear(); bool empty() const; size_t stackPointer() const; // Convert from ST(n) index n to Rx index x size_t RIndexToSTIndex(size_t index) const; size_t STIndexToRIndex(size_t index) const; // Restore the full FPU Tag Word from the ptrace-filtered version edb::value16 restoreTagWord(uint16_t twd) const; std::uint16_t reducedTagWord() const; int tag(size_t n) const; edb::value80 st(size_t n) const; edb::value80 &st(size_t n); private: int recreateTag(const edb::value80 value) const; int makeTag(size_t n, uint16_t twd) const; } x87; // i386-inherited (and expanded on x86_64) state struct X86 { public: enum GPRIndex : size_t { EAX, RAX = EAX, ECX, RCX = ECX, EDX, RDX = EDX, EBX, RBX = EBX, ESP, RSP = ESP, EBP, RBP = EBP, ESI, RSI = ESI, EDI, RDI = EDI, R8, R9, R10, R11, R12, R13, R14, R15 }; enum SegRegIndex : size_t { ES, CS, SS, DS, FS, GS }; static constexpr const char *origEAXName = "orig_eax"; static constexpr const char *origRAXName = "orig_rax"; static constexpr const char *IP64Name = "rip"; static constexpr const char *IP32Name = "eip"; static constexpr const char *IP16Name = "ip"; static constexpr const char *flags64Name = "rflags"; static constexpr const char *flags32Name = "eflags"; static constexpr const char *flags16Name = "flags"; // gcc 4.8 fails to understand inline initialization of std::array, so define these the old way static const std::array GPReg64Names; static const std::array GPReg32Names; static const std::array GPReg16Names; static const std::array GPReg8LNames; static const std::array GPReg8HNames; static const std::array segRegNames; public: std::array GPRegs; std::array dbgRegs; edb::reg_t orig_ax; edb::reg_t flags; // whole flags register: EFLAGS/RFLAGS edb::address_t IP; // program counter: EIP/RIP std::array segRegs; std::array segRegBases; std::array segRegBasesFilled = {{false}}; bool gpr64Filled = false; bool gpr32Filled = false; public: void clear(); bool empty() const; } x86; bool dbgIndexValid(size_t n) const { return n < dbg_reg_count(); } bool gprIndexValid(size_t n) const { return n < gpr_count(); } bool fpuIndexValid(size_t n) const { return n < fpu_reg_count(); } bool mmxIndexValid(size_t n) const { return n < mmx_reg_count(); } bool xmmIndexValid(size_t n) const { return n < xmm_reg_count(); } bool ymmIndexValid(size_t n) const { return n < ymm_reg_count(); } bool zmmIndexValid(size_t n) const { return n < zmm_reg_count(); } void fillFrom(const UserRegsStructX86 ®s); void fillFrom(const UserRegsStructX86_64 ®s); void fillFrom(const PrStatus_X86 ®s); void fillFrom(const PrStatus_X86_64 ®s); void fillFrom(const UserFPRegsStructX86 ®s); void fillFrom(const UserFPRegsStructX86_64 ®s); void fillFrom(const UserFPXRegsStructX86 ®s); bool fillFrom(const X86XState ®s, size_t sizeFromKernel); void fillStruct(UserRegsStructX86 ®s) const; void fillStruct(UserRegsStructX86_64 ®s) const; void fillStruct(PrStatus_X86_64 ®s) const; void fillStruct(UserFPRegsStructX86 ®s) const; void fillStruct(UserFPRegsStructX86_64 ®s) const; void fillStruct(UserFPXRegsStructX86 ®s) const; size_t fillStruct(X86XState ®s) const; }; } #endif edb-debugger/plugins/DebuggerCore/unix/linux/arch/x86-generic/PlatformState.cpp0000644000175000017500000012300213765535463027334 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "PlatformState.h" #include "FloatX.h" #include "Util.h" #include "string_hash.h" #include #include #include namespace DebuggerCorePlugin { constexpr const char *PlatformState::AVX::mxcsrName; constexpr const char *PlatformState::X86::IP64Name; constexpr const char *PlatformState::X86::IP32Name; constexpr const char *PlatformState::X86::IP16Name; constexpr const char *PlatformState::X86::origEAXName; constexpr const char *PlatformState::X86::origRAXName; constexpr const char *PlatformState::X86::flags64Name; constexpr const char *PlatformState::X86::flags32Name; constexpr const char *PlatformState::X86::flags16Name; const std::array PlatformState::X86::GPReg64Names = { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", }; const std::array PlatformState::X86::GPReg32Names = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d", }; const std::array PlatformState::X86::GPReg16Names = { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di", "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w", }; const std::array PlatformState::X86::GPReg8LNames = { "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil", "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b", }; const std::array PlatformState::X86::GPReg8HNames = { "ah", "ch", "dh", "bh", }; const std::array PlatformState::X86::segRegNames = { "es", "cs", "ss", "ds", "fs", "gs", }; void PlatformState::fillFrom(const UserRegsStructX86 ®s) { // Don't touch higher parts to avoid zeroing out bad value mark, so use load x86.GPRegs[X86::EAX].load(regs.eax); x86.GPRegs[X86::ECX].load(regs.ecx); x86.GPRegs[X86::EDX].load(regs.edx); x86.GPRegs[X86::EBX].load(regs.ebx); x86.GPRegs[X86::ESP].load(regs.esp); x86.GPRegs[X86::EBP].load(regs.ebp); x86.GPRegs[X86::ESI].load(regs.esi); x86.GPRegs[X86::EDI].load(regs.edi); x86.orig_ax.load(regs.orig_eax); x86.flags.load(regs.eflags); x86.IP.load(regs.eip); x86.segRegs[X86::ES] = regs.xes; x86.segRegs[X86::CS] = regs.xcs; x86.segRegs[X86::SS] = regs.xss; x86.segRegs[X86::DS] = regs.xds; x86.segRegs[X86::FS] = regs.xfs; x86.segRegs[X86::GS] = regs.xgs; x86.gpr32Filled = true; } size_t PlatformState::X87::stackPointer() const { return (statusWord & 0x3800) >> 11; } size_t PlatformState::X87::RIndexToSTIndex(size_t n) const { n = (n + 8 - stackPointer()) % 8; return n; } size_t PlatformState::X87::STIndexToRIndex(size_t n) const { n = (n + stackPointer()) % 8; return n; } int PlatformState::X87::recreateTag(edb::value80 value) const { switch (float_type(value)) { case FloatValueClass::Zero: return TAG_ZERO; case FloatValueClass::Normal: return TAG_VALID; default: return TAG_SPECIAL; } } edb::value80 PlatformState::X87::st(size_t n) const { return R[STIndexToRIndex(n)]; } edb::value80 &PlatformState::X87::st(size_t n) { return R[STIndexToRIndex(n)]; } int PlatformState::X87::makeTag(size_t n, uint16_t twd) const { int minitag = (twd >> n) & 0x1; return minitag ? recreateTag(R[n]) : TAG_EMPTY; } int PlatformState::X87::tag(size_t n) const { return (tagWord >> (2 * n)) & 0x3; } edb::value16 PlatformState::X87::restoreTagWord(uint16_t twd) const { uint16_t tagWord = 0; for (size_t n = 0; n < MAX_FPU_REG_COUNT; ++n) { tagWord |= makeTag(n, twd) << (2 * n); } return edb::value16(tagWord); } std::uint16_t PlatformState::X87::reducedTagWord() const { // Algorithm is the same as in linux/arch/x86/kernel/i387.c: twd_i387_to_fxsr() // Transforming each pair of bits into 01 (valid) or 00 (empty) unsigned int result = ~tagWord; result = (result | (result >> 1)) & 0x5555; // 0102030405060708 // moving the valid bits to the lower byte result = (result | (result >> 1)) & 0x3333; // 0012003400560078 result = (result | (result >> 2)) & 0x0f0f; // 0000123400005678 result = (result | (result >> 4)) & 0x00ff; // 0000000012345678 return result; } void PlatformState::fillFrom(const UserFPRegsStructX86 ®s) { x87.statusWord = regs.swd; // should be first for RIndexToSTIndex() to work for (size_t n = 0; n < MAX_FPU_REG_COUNT; ++n) { x87.R[n] = edb::value80(regs.st_space, 10 * x87.RIndexToSTIndex(n)); } x87.controlWord = regs.cwd; x87.tagWord = regs.twd; // This is the true tag word, unlike in FPX regs and x86-64 FP regs structs x87.instPtrOffset = edb::address_t::fromZeroExtended(regs.fip); x87.dataPtrOffset = edb::address_t::fromZeroExtended(regs.foo); x87.instPtrSelector = regs.fcs; x87.dataPtrSelector = regs.fos; x87.opCode = 0; // not present in the given structure x87.filled = true; } void PlatformState::fillFrom(const UserFPXRegsStructX86 ®s) { x87.statusWord = regs.swd; // should be first for RIndexToSTIndex() to work for (size_t n = 0; n < MAX_FPU_REG_COUNT; ++n) { x87.R[n] = edb::value80(regs.st_space, 16 * x87.RIndexToSTIndex(n)); } x87.controlWord = regs.cwd; x87.tagWord = x87.restoreTagWord(regs.twd); x87.instPtrOffset = edb::address_t::fromZeroExtended(regs.fip); x87.dataPtrOffset = edb::address_t::fromZeroExtended(regs.foo); x87.instPtrSelector = regs.fcs; x87.dataPtrSelector = regs.fos; x87.opCode = regs.fop; x87.filled = true; x87.opCodeFilled = true; for (size_t n = 0; n < IA32_XMM_REG_COUNT; ++n) { avx.setXMM(n, edb::value128(regs.xmm_space, 16 * n)); } avx.mxcsr = regs.mxcsr; avx.xmmFilledIA32 = true; } void PlatformState::fillFrom(const UserRegsStructX86_64 ®s) { // On 32 bit OS this code would access beyond the length of the array, but it won't ever execute there assert(x86.GPRegs.size() == 16); x86.GPRegs[X86::RAX] = regs.rax; x86.GPRegs[X86::RCX] = regs.rcx; x86.GPRegs[X86::RDX] = regs.rdx; x86.GPRegs[X86::RBX] = regs.rbx; x86.GPRegs[X86::RSP] = regs.rsp; x86.GPRegs[X86::RBP] = regs.rbp; x86.GPRegs[X86::RSI] = regs.rsi; x86.GPRegs[X86::RDI] = regs.rdi; x86.GPRegs[X86::R8] = regs.r8; x86.GPRegs[X86::R9] = regs.r9; x86.GPRegs[X86::R10] = regs.r10; x86.GPRegs[X86::R11] = regs.r11; x86.GPRegs[X86::R12] = regs.r12; x86.GPRegs[X86::R13] = regs.r13; x86.GPRegs[X86::R14] = regs.r14; x86.GPRegs[X86::R15] = regs.r15; x86.orig_ax = regs.orig_rax; x86.flags = regs.eflags; x86.IP = regs.rip; x86.segRegs[X86::ES] = regs.es; x86.segRegs[X86::CS] = regs.cs; x86.segRegs[X86::SS] = regs.ss; x86.segRegs[X86::DS] = regs.ds; x86.segRegs[X86::FS] = regs.fs; x86.segRegs[X86::GS] = regs.gs; x86.gpr32Filled = true; x86.gpr64Filled = true; if (is64Bit()) { // 32-bit processes get always zeros here, which may be wrong or meaningless if (x86.segRegs[X86::FS] == 0) { x86.segRegBases[X86::FS] = regs.fs_base; x86.segRegBasesFilled[X86::FS] = true; } if (x86.segRegs[X86::GS] == 0) { x86.segRegBases[X86::GS] = regs.gs_base; x86.segRegBasesFilled[X86::GS] = true; } } } void PlatformState::fillFrom(const UserFPRegsStructX86_64 ®s) { x87.statusWord = regs.swd; // should be first for RIndexToSTIndex() to work for (size_t n = 0; n < MAX_FPU_REG_COUNT; ++n) { x87.R[n] = edb::value80(regs.st_space, 16 * x87.RIndexToSTIndex(n)); } x87.controlWord = regs.cwd; x87.tagWord = x87.restoreTagWord(regs.ftw); x87.instPtrOffset = regs.rip; x87.dataPtrOffset = regs.rdp; x87.instPtrSelector = 0; x87.dataPtrSelector = 0; x87.opCode = regs.fop; x87.filled = true; x87.opCodeFilled = true; for (size_t n = 0; n < MAX_XMM_REG_COUNT; ++n) { avx.setXMM(n, edb::value128(regs.xmm_space, 16 * n)); } avx.mxcsr = regs.mxcsr; avx.mxcsrMask = regs.mxcr_mask; avx.mxcsrMaskFilled = true; avx.xmmFilledIA32 = true; avx.xmmFilledAMD64 = true; } void PlatformState::fillFrom(const PrStatus_X86 ®s) { // Don't touch higher parts to avoid zeroing out bad value mark, so use load x86.GPRegs[X86::EAX].load(regs.eax); x86.GPRegs[X86::ECX].load(regs.ecx); x86.GPRegs[X86::EDX].load(regs.edx); x86.GPRegs[X86::EBX].load(regs.ebx); x86.GPRegs[X86::ESP].load(regs.esp); x86.GPRegs[X86::EBP].load(regs.ebp); x86.GPRegs[X86::ESI].load(regs.esi); x86.GPRegs[X86::EDI].load(regs.edi); x86.orig_ax.load(regs.orig_eax); x86.flags.load(regs.eflags); x86.IP.load(regs.eip); x86.segRegs[X86::ES] = regs.es; x86.segRegs[X86::CS] = regs.cs; x86.segRegs[X86::SS] = regs.ss; x86.segRegs[X86::DS] = regs.ds; x86.segRegs[X86::FS] = regs.fs; x86.segRegs[X86::GS] = regs.gs; x86.gpr32Filled = true; } void PlatformState::fillFrom(const PrStatus_X86_64 ®s) { x86.GPRegs[X86::RAX] = regs.rax; x86.GPRegs[X86::RCX] = regs.rcx; x86.GPRegs[X86::RDX] = regs.rdx; x86.GPRegs[X86::RBX] = regs.rbx; x86.GPRegs[X86::RSP] = regs.rsp; x86.GPRegs[X86::RBP] = regs.rbp; x86.GPRegs[X86::RSI] = regs.rsi; x86.GPRegs[X86::RDI] = regs.rdi; x86.GPRegs[X86::R8] = regs.r8; x86.GPRegs[X86::R9] = regs.r9; x86.GPRegs[X86::R10] = regs.r10; x86.GPRegs[X86::R11] = regs.r11; x86.GPRegs[X86::R12] = regs.r12; x86.GPRegs[X86::R13] = regs.r13; x86.GPRegs[X86::R14] = regs.r14; x86.GPRegs[X86::R15] = regs.r15; x86.orig_ax = regs.orig_rax; x86.flags = regs.rflags; x86.IP = regs.rip; x86.segRegs[X86::ES] = regs.es; x86.segRegs[X86::CS] = regs.cs; x86.segRegs[X86::SS] = regs.ss; x86.segRegs[X86::DS] = regs.ds; x86.segRegs[X86::FS] = regs.fs; x86.segRegs[X86::GS] = regs.gs; x86.gpr32Filled = true; x86.gpr64Filled = true; x86.segRegBases[X86::FS] = regs.fs_base; x86.segRegBasesFilled[X86::FS] = true; x86.segRegBases[X86::GS] = regs.gs_base; x86.segRegBasesFilled[X86::GS] = true; } bool PlatformState::fillFrom(const X86XState ®s, size_t sizeFromKernel) { if (sizeFromKernel < X86XState::XSAVE_NONEXTENDED_SIZE) { // Shouldn't ever happen. XSAVE area must at least have an XSAVE header. qDebug() << "Size of X86_XSTATE returned from the kernel appears less than expected: " << sizeFromKernel; return false; } avx.xcr0 = regs.xcr0; bool statePresentX87 = regs.xstate_bv & X86XState::FEATURE_X87; bool statePresentSSE = regs.xstate_bv & X86XState::FEATURE_SSE; bool statePresentAVX = regs.xstate_bv & X86XState::FEATURE_AVX; // Due to the lazy saving the feature bits may be unset in XSTATE_BV if the app // has not touched the corresponding registers yet. But once the registers are // touched, they are initialized to zero by the OS (not control/tag ones). To the app // it looks as if the registers have always been zero. Thus we should provide the same // illusion to the user. if (statePresentX87) { x87.statusWord = regs.swd; // should be first for RIndexToSTIndex() to work for (size_t n = 0; n < MAX_FPU_REG_COUNT; ++n) { x87.R[n] = edb::value80(regs.st_space, 16 * x87.RIndexToSTIndex(n)); } x87.controlWord = regs.cwd; x87.tagWord = x87.restoreTagWord(regs.twd); x87.instPtrOffset = regs.fioff; x87.dataPtrOffset = regs.fooff; if (is64Bit()) { std::memcpy(reinterpret_cast(&x87.instPtrOffset) + 4, ®s.fiseg, sizeof(regs.fiseg)); std::memcpy(reinterpret_cast(&x87.dataPtrOffset) + 4, ®s.foseg, sizeof(regs.foseg)); x87.instPtrSelector = 0; x87.dataPtrSelector = 0; } else { x87.instPtrSelector = regs.fiseg; x87.dataPtrSelector = regs.foseg; } x87.opCode = regs.fop; x87.filled = true; x87.opCodeFilled = true; } else { std::memset(reinterpret_cast(&x87), 0, sizeof(x87)); x87.controlWord = regs.cwd; // this appears always present x87.tagWord = 0xffff; x87.filled = true; x87.opCodeFilled = true; } if (statePresentAVX) { for (size_t n = 0; n < MAX_YMM_REG_COUNT; ++n) { avx.setYMM(n, edb::value128(regs.xmm_space, 16 * n), edb::value128(regs.ymmh_space, 16 * n)); } avx.mxcsr = regs.mxcsr; avx.mxcsrMask = regs.mxcsr_mask; avx.mxcsrMaskFilled = true; avx.xmmFilledIA32 = true; avx.xmmFilledAMD64 = true; avx.ymmFilled = true; } else if (statePresentSSE) { // If AVX state management has been enabled by the OS, // the state may be not present due to lazy saving, // so initialize the space with zeros if (avx.xcr0 & X86XState::FEATURE_AVX) { for (size_t n = 0; n < MAX_YMM_REG_COUNT; ++n) { avx.setYMM(n, edb::value256::fromZeroExtended(0)); } avx.ymmFilled = true; } // Now we can fill in the XMM registers for (size_t n = 0; n < MAX_XMM_REG_COUNT; ++n) { avx.setXMM(n, edb::value128(regs.xmm_space, 16 * n)); } avx.mxcsr = regs.mxcsr; avx.mxcsrMask = regs.mxcsr_mask; avx.mxcsrMaskFilled = true; avx.xmmFilledIA32 = true; avx.xmmFilledAMD64 = true; } else { avx.mxcsr = regs.mxcsr; avx.mxcsrMask = regs.mxcsr_mask; avx.mxcsrMaskFilled = true; // Only fill the registers which are actually supported, leave invalidity marks intact for other parts if (avx.xcr0 & X86XState::FEATURE_AVX) { // If AVX state management has been enabled by the OS for (size_t n = 0; n < MAX_YMM_REG_COUNT; ++n) { avx.setYMM(n, edb::value256::fromZeroExtended(0)); } avx.xmmFilledIA32 = true; avx.xmmFilledAMD64 = true; avx.ymmFilled = true; } else if (avx.xcr0 & X86XState::FEATURE_SSE) { // If SSE state management has been enabled by the OS for (size_t n = 0; n < MAX_YMM_REG_COUNT; ++n) { avx.setYMM(n, edb::value256::fromZeroExtended(0)); } avx.xmmFilledIA32 = true; avx.xmmFilledAMD64 = true; } } return true; } void PlatformState::fillStruct(UserRegsStructX86 ®s) const { util::mark_memory(®s, sizeof(regs)); if (x86.gpr32Filled) { regs.eax = x86.GPRegs[X86::EAX]; regs.ecx = x86.GPRegs[X86::ECX]; regs.edx = x86.GPRegs[X86::EDX]; regs.ebx = x86.GPRegs[X86::EBX]; regs.esp = x86.GPRegs[X86::ESP]; regs.ebp = x86.GPRegs[X86::EBP]; regs.esi = x86.GPRegs[X86::ESI]; regs.edi = x86.GPRegs[X86::EDI]; regs.xes = x86.segRegs[X86::ES]; regs.xcs = x86.segRegs[X86::CS]; regs.xss = x86.segRegs[X86::SS]; regs.xds = x86.segRegs[X86::DS]; regs.xfs = x86.segRegs[X86::FS]; regs.xgs = x86.segRegs[X86::GS]; regs.orig_eax = x86.orig_ax; regs.eflags = x86.flags; regs.eip = x86.IP; } } void PlatformState::fillStruct(UserRegsStructX86_64 ®s) const { // If 64-bit part is not filled in state, we'll set marked values if (x86.gpr64Filled || x86.gpr32Filled) { regs.rax = x86.GPRegs[X86::RAX]; regs.rcx = x86.GPRegs[X86::RCX]; regs.rdx = x86.GPRegs[X86::RDX]; regs.rbx = x86.GPRegs[X86::RBX]; regs.rsp = x86.GPRegs[X86::RSP]; regs.rbp = x86.GPRegs[X86::RBP]; regs.rsi = x86.GPRegs[X86::RSI]; regs.rdi = x86.GPRegs[X86::RDI]; regs.r8 = x86.GPRegs[X86::R8]; regs.r9 = x86.GPRegs[X86::R9]; regs.r10 = x86.GPRegs[X86::R10]; regs.r11 = x86.GPRegs[X86::R11]; regs.r12 = x86.GPRegs[X86::R12]; regs.r13 = x86.GPRegs[X86::R13]; regs.r14 = x86.GPRegs[X86::R14]; regs.r15 = x86.GPRegs[X86::R15]; regs.es = x86.segRegs[X86::ES]; regs.cs = x86.segRegs[X86::CS]; regs.ss = x86.segRegs[X86::SS]; regs.ds = x86.segRegs[X86::DS]; regs.fs = x86.segRegs[X86::FS]; regs.gs = x86.segRegs[X86::GS]; regs.fs_base = x86.segRegBases[X86::FS]; regs.gs_base = x86.segRegBases[X86::GS]; regs.orig_rax = x86.orig_ax; regs.eflags = x86.flags; regs.rip = x86.IP; } } void PlatformState::fillStruct(PrStatus_X86_64 ®s) const { // If 64-bit part is not filled in state, we'll set marked values if (x86.gpr64Filled || x86.gpr32Filled) { regs.rax = x86.GPRegs[X86::RAX]; regs.rcx = x86.GPRegs[X86::RCX]; regs.rdx = x86.GPRegs[X86::RDX]; regs.rbx = x86.GPRegs[X86::RBX]; regs.rsp = x86.GPRegs[X86::RSP]; regs.rbp = x86.GPRegs[X86::RBP]; regs.rsi = x86.GPRegs[X86::RSI]; regs.rdi = x86.GPRegs[X86::RDI]; regs.r8 = x86.GPRegs[X86::R8]; regs.r9 = x86.GPRegs[X86::R9]; regs.r10 = x86.GPRegs[X86::R10]; regs.r11 = x86.GPRegs[X86::R11]; regs.r12 = x86.GPRegs[X86::R12]; regs.r13 = x86.GPRegs[X86::R13]; regs.r14 = x86.GPRegs[X86::R14]; regs.r15 = x86.GPRegs[X86::R15]; regs.orig_rax = x86.orig_ax; regs.rflags = x86.flags; regs.rip = x86.IP; regs.es = x86.segRegs[X86::ES]; regs.cs = x86.segRegs[X86::CS]; regs.ss = x86.segRegs[X86::SS]; regs.ds = x86.segRegs[X86::DS]; regs.fs = x86.segRegs[X86::FS]; regs.gs = x86.segRegs[X86::GS]; regs.fs_base = x86.segRegBases[X86::FS]; regs.gs_base = x86.segRegBases[X86::GS]; } } void PlatformState::fillStruct(UserFPRegsStructX86 ®s) const { util::mark_memory(®s, sizeof(regs)); if (x87.filled) { regs.swd = x87.statusWord; regs.cwd = x87.controlWord; regs.twd = x87.tagWord; regs.fip = x87.instPtrOffset; regs.foo = x87.dataPtrOffset; regs.fcs = x87.instPtrSelector; regs.fos = x87.dataPtrSelector; for (size_t n = 0; n < MAX_FPU_REG_COUNT; ++n) { std::memcpy(reinterpret_cast(regs.st_space) + 10 * x87.RIndexToSTIndex(n), &x87.R[n], sizeof(x87.R[n])); } } } void PlatformState::fillStruct(UserFPRegsStructX86_64 ®s) const { util::mark_memory(®s, sizeof(regs)); if (x87.filled) { regs.swd = x87.statusWord; regs.cwd = x87.controlWord; regs.ftw = x87.reducedTagWord(); regs.rip = x87.instPtrOffset; regs.rdp = x87.dataPtrOffset; if (x87.opCodeFilled) { regs.fop = x87.opCode; } for (size_t n = 0; n < MAX_FPU_REG_COUNT; ++n) { std::memcpy(reinterpret_cast(regs.st_space) + 16 * x87.RIndexToSTIndex(n), &x87.R[n], sizeof(x87.R[n])); } if (avx.xmmFilledIA32 || avx.xmmFilledAMD64) { for (size_t n = 0; n < MAX_XMM_REG_COUNT; ++n) { std::memcpy(reinterpret_cast(regs.xmm_space) + 16 * n, &avx.zmmStorage[n], sizeof(edb::value128)); } regs.mxcsr = avx.mxcsr; } if (avx.mxcsrMaskFilled) { regs.mxcr_mask = avx.mxcsrMask; } } } void PlatformState::fillStruct(UserFPXRegsStructX86 ®s) const { util::mark_memory(®s, sizeof(regs)); if (x87.filled) { regs.swd = x87.statusWord; regs.twd = x87.reducedTagWord(); regs.cwd = x87.controlWord; regs.fip = x87.instPtrOffset; regs.foo = x87.dataPtrOffset; regs.fcs = x87.instPtrSelector; regs.fos = x87.dataPtrSelector; regs.fop = x87.opCode; for (size_t n = 0; n < MAX_FPU_REG_COUNT; ++n) { std::memcpy(reinterpret_cast(®s.st_space) + 16 * x87.RIndexToSTIndex(n), &x87.R[n], sizeof(x87.R[n])); } } if (avx.xmmFilledIA32) { regs.mxcsr = avx.mxcsr; for (size_t n = 0; n < IA32_XMM_REG_COUNT; ++n) { std::memcpy(reinterpret_cast(®s.xmm_space) + 16 * n, &avx.zmmStorage[n], sizeof(edb::value128)); } } } size_t PlatformState::fillStruct(X86XState ®s) const { util::mark_memory(®s, sizeof(regs)); // Zero out reserved bytes; set xstate_bv to 0 std::memset(regs.xstate_hdr_bytes, 0, sizeof(regs.xstate_hdr_bytes)); regs.xcr0 = avx.xcr0; if (x87.filled) { regs.swd = x87.statusWord; regs.cwd = x87.controlWord; regs.twd = x87.reducedTagWord(); regs.fioff = x87.instPtrOffset; regs.fooff = x87.dataPtrOffset; if (is64Bit()) { std::memcpy(®s.fiseg, reinterpret_cast(&x87.instPtrOffset) + 4, 4); std::memcpy(®s.foseg, reinterpret_cast(&x87.dataPtrOffset) + 4, 4); } else { regs.fiseg = x87.instPtrSelector; regs.foseg = x87.dataPtrSelector; } regs.fop = x87.opCode; for (size_t n = 0; n < MAX_FPU_REG_COUNT; ++n) { std::memcpy(reinterpret_cast(®s.st_space) + 16 * x87.RIndexToSTIndex(n), &x87.R[n], sizeof(x87.R[n])); } regs.xstate_bv |= X86XState::FEATURE_X87; } if (avx.xmmFilledIA32) { const auto nMax = avx.xmmFilledAMD64 ? AMD64_XMM_REG_COUNT : IA32_XMM_REG_COUNT; for (size_t n = 0; n < nMax; ++n) { std::memcpy(reinterpret_cast(®s.xmm_space) + 16 * n, &avx.zmmStorage[n], sizeof(edb::value128)); } regs.mxcsr = avx.mxcsr; regs.mxcsr_mask = avx.mxcsrMask; regs.xstate_bv |= X86XState::FEATURE_SSE; } if (avx.ymmFilled) { // In this case SSE state is already filled by the code writing XMMx&MXCSR registers for (size_t n = 0; n < MAX_YMM_REG_COUNT; ++n) { std::memcpy(reinterpret_cast(®s.ymmh_space) + 16 * n, reinterpret_cast(&avx.zmmStorage[n]) + sizeof(edb::value128), sizeof(edb::value128)); } regs.xstate_bv |= X86XState::FEATURE_AVX; } size_t size; if (regs.xstate_bv & X86XState::FEATURE_AVX) { size = X86XState::AVX_SIZE; } else if (regs.xstate_bv & (X86XState::FEATURE_X87 | X86XState::FEATURE_SSE)) { size = X86XState::SSE_SIZE; // minimum size: legacy state + xsave header } else { size = 0; } return size; } edb::value128 PlatformState::AVX::xmm(size_t index) const { return edb::value128(zmmStorage[index]); } edb::value256 PlatformState::AVX::ymm(size_t index) const { return edb::value256(zmmStorage[index]); } edb::value512 PlatformState::AVX::zmm(size_t index) const { return zmmStorage[index]; } void PlatformState::AVX::setXMM(size_t index, edb::value128 value) { // leave upper part unchanged. zmmStorage[index].load(value); } void PlatformState::AVX::setYMM(size_t index, edb::value128 low, edb::value128 high) { // leave upper part unchanged. std::memcpy(reinterpret_cast(&zmmStorage[index]) + 0, &low, sizeof(low)); std::memcpy(reinterpret_cast(&zmmStorage[index]) + 16, &high, sizeof(high)); } void PlatformState::AVX::setYMM(size_t index, edb::value256 value) { // leave upper part unchanged. zmmStorage[index].load(value); } void PlatformState::AVX::setZMM(size_t index, edb::value512 value) { zmmStorage[index] = value; } void PlatformState::X86::clear() { util::mark_memory(this, sizeof(*this)); gpr32Filled = false; gpr64Filled = false; std::fill(segRegBasesFilled.begin(), segRegBasesFilled.end(), false); } bool PlatformState::X86::empty() const { return !gpr32Filled; } void PlatformState::X87::clear() { util::mark_memory(this, sizeof(*this)); filled = false; opCodeFilled = false; } bool PlatformState::X87::empty() const { return !filled; } void PlatformState::AVX::clear() { util::mark_memory(this, sizeof(*this)); xmmFilledIA32 = false; xmmFilledAMD64 = false; ymmFilled = false; zmmFilled = false; } bool PlatformState::AVX::empty() const { return !xmmFilledIA32; } /** * @brief PlatformState::PlatformState */ PlatformState::PlatformState() { this->clear(); } /** * makes a copy of the state object * * @brief PlatformState::clone * @return */ std::unique_ptr PlatformState::clone() const { return std::make_unique(*this); } /** * @brief PlatformState::flagsToString * @param flags * @return the flags in a string form appropriate for this platform */ QString PlatformState::flagsToString(edb::reg_t flags) const { char buf[32]; qsnprintf(buf, sizeof(buf), "%c %c %c %c %c %c %c %c %c", ((flags & 0x001) ? 'C' : 'c'), ((flags & 0x004) ? 'P' : 'p'), ((flags & 0x010) ? 'A' : 'a'), ((flags & 0x040) ? 'Z' : 'z'), ((flags & 0x080) ? 'S' : 's'), ((flags & 0x100) ? 'T' : 't'), ((flags & 0x200) ? 'I' : 'i'), ((flags & 0x400) ? 'D' : 'd'), ((flags & 0x800) ? 'O' : 'o')); return QString::fromLatin1(buf); } /** * @brief PlatformState::flagsToString * @return the flags in a string form appropriate for this platform */ QString PlatformState::flagsToString() const { return flagsToString(flags()); } /** * @brief findRegisterValue * @param names * @param regs * @param regName * @param type * @param maxNames * @param shift * @return */ template Register findRegisterValue(const Names &names, const Regs ®s, const QString ®Name, Register::Type type, size_t maxNames, int shift = 0) { const auto end = names.begin() + maxNames; auto regNameFoundIter = std::find(names.begin(), end, regName); if (regNameFoundIter != end) { return make_Register(regName, regs[regNameFoundIter - names.begin()] >> shift, type); } else { return Register(); } } /** * @brief PlatformState::value * @param reg * @return a Register object which represents the register with the name supplied */ Register PlatformState::value(const QString ®) const { const QString regName = reg.toLower(); // TODO: make use of string_hash and switch-case construct to make things easier to understand // All register names are always less than 9 chars in length, so string_hash would work. Register found; // don't return valid Register with garbage value if (x86.gpr32Filled) { if (reg == x86.origRAXName && x86.gpr64Filled && is64Bit()) return make_Register<64>(x86.origRAXName, x86.orig_ax, Register::TYPE_GPR); if (reg == x86.origEAXName) return make_Register<32>(x86.origEAXName, x86.orig_ax, Register::TYPE_GPR); if (x86.gpr64Filled && is64Bit() && !!(found = findRegisterValue(x86.GPReg64Names, x86.GPRegs, regName, Register::TYPE_GPR, gpr64_count()))) return found; if (!!(found = findRegisterValue<32>(x86.GPReg32Names, x86.GPRegs, regName, Register::TYPE_GPR, gpr_count()))) return found; if (!!(found = findRegisterValue<16>(x86.GPReg16Names, x86.GPRegs, regName, Register::TYPE_GPR, gpr_count()))) return found; if (!!(found = findRegisterValue<8>(x86.GPReg8LNames, x86.GPRegs, regName, Register::TYPE_GPR, gpr_low_addressable_count()))) return found; if (!!(found = findRegisterValue<8>(x86.GPReg8HNames, x86.GPRegs, regName, Register::TYPE_GPR, gpr_high_addressable_count(), 8))) return found; if (!!(found = findRegisterValue(x86.segRegNames, x86.segRegs, regName, Register::TYPE_SEG, seg_reg_count()))) return found; if (regName.mid(1) == "s_base") { const QString segRegName = regName.mid(0, 2); const auto end = x86.segRegNames.end(); const auto regNameFoundIter = std::find(x86.segRegNames.begin(), end, segRegName); if (regNameFoundIter != end) { const size_t index = regNameFoundIter - x86.segRegNames.begin(); if (!x86.segRegBasesFilled[index]) { return Register(); } const auto value = x86.segRegBases[index]; if (is64Bit()) { return make_Register(regName, value, Register::TYPE_SEG); } else { return make_Register<32>(regName, value, Register::TYPE_SEG); } } } if (is64Bit() && regName == x86.flags64Name) return make_Register(x86.flags64Name, x86.flags, Register::TYPE_COND); if (regName == x86.flags32Name) return make_Register<32>(x86.flags32Name, x86.flags, Register::TYPE_COND); if (regName == x86.flags16Name) return make_Register<16>(x86.flags16Name, x86.flags, Register::TYPE_COND); if (is64Bit() && regName == x86.IP64Name) return make_Register(x86.IP64Name, x86.IP, Register::TYPE_IP); if (regName == x86.IP32Name) return make_Register<32>(x86.IP32Name, x86.IP, Register::TYPE_IP); if (regName == x86.IP16Name) return make_Register<16>(x86.IP16Name, x86.IP, Register::TYPE_IP); } if (x86.gpr32Filled) { QRegExp DRx("^dr([0-7])$"); if (DRx.indexIn(regName) != -1) { QChar digit = DRx.cap(1).at(0); assert(digit.isDigit()); char digitChar = digit.toLatin1(); size_t i = digitChar - '0'; assert(dbgIndexValid(i)); if (is64Bit() && x86.gpr64Filled) { return make_Register(regName, x86.dbgRegs[i], Register::TYPE_COND); } else { return make_Register<32>(regName, x86.dbgRegs[i], Register::TYPE_COND); } } } if (x87.filled) { QRegExp Rx("^r([0-7])$"); if (Rx.indexIn(regName) != -1) { QChar digit = Rx.cap(1).at(0); assert(digit.isDigit()); char digitChar = digit.toLatin1(); size_t i = digitChar - '0'; assert(fpuIndexValid(i)); return make_Register(regName, x87.R[i], Register::TYPE_FPU); } } if (x87.filled) { QRegExp STx("^st\\(?([0-7])\\)?$"); if (STx.indexIn(regName) != -1) { QChar digit = STx.cap(1).at(0); assert(digit.isDigit()); char digitChar = digit.toLatin1(); size_t i = digitChar - '0'; assert(fpuIndexValid(i)); return make_Register(regName, x87.st(i), Register::TYPE_FPU); } } if (x87.filled) { if (regName == "fip" || regName == "fdp") { const edb::address_t addr = regName == "fip" ? x87.instPtrOffset : x87.dataPtrOffset; if (is64Bit()) { return make_Register<64>(regName, addr, Register::TYPE_FPU); } else { return make_Register<32>(regName, addr, Register::TYPE_FPU); } } if (regName == "fis" || regName == "fds") { const edb::value16 val = regName == "fis" ? x87.instPtrSelector : x87.dataPtrSelector; return make_Register<16>(regName, val, Register::TYPE_FPU); } if (regName == "fopcode" || regName == "fop") { return make_Register<16>(regName, x87.opCode, Register::TYPE_FPU); } if (regName == "ftr" || regName == "ftw") { return make_Register<16>(regName, x87.tagWord, Register::TYPE_FPU); } if (regName == "fsr" || regName == "fsw") { return make_Register<16>(regName, x87.statusWord, Register::TYPE_FPU); } if (regName == "fcr" || regName == "fcw") { return make_Register<16>(regName, x87.controlWord, Register::TYPE_FPU); } } if (x87.filled) { QRegExp MMx("^mm([0-7])$"); if (MMx.indexIn(regName) != -1) { QChar digit = MMx.cap(1).at(0); assert(digit.isDigit()); char digitChar = digit.toLatin1(); size_t i = digitChar - '0'; assert(mmxIndexValid(i)); return make_Register(regName, x87.R[i].mantissa(), Register::TYPE_SIMD); } } if (avx.xmmFilledIA32) { QRegExp XMMx("^xmm([0-9]|1[0-5])$"); if (XMMx.indexIn(regName) != -1) { bool ok = false; size_t i = XMMx.cap(1).toUShort(&ok); assert(ok); if (i >= IA32_XMM_REG_COUNT && !avx.xmmFilledAMD64) { return Register(); } if (xmmIndexValid(i)) { // May be invalid but legitimate for a disassembler: e.g. XMM13 but 32 bit mode return make_Register(regName, avx.xmm(i), Register::TYPE_SIMD); } } } if (avx.ymmFilled) { QRegExp YMMx("^ymm([0-9]|1[0-5])$"); if (YMMx.indexIn(regName) != -1) { bool ok = false; size_t i = YMMx.cap(1).toUShort(&ok); assert(ok); if (ymmIndexValid(i)) { // May be invalid but legitimate for a disassembler: e.g. YMM13 but 32 bit mode return make_Register(regName, avx.ymm(i), Register::TYPE_SIMD); } } } if (avx.xmmFilledIA32 && regName == avx.mxcsrName) { return make_Register(avx.mxcsrName, avx.mxcsr, Register::TYPE_COND); } return Register(); } /** * @brief PlatformState::instructionPointerRegister * @return */ Register PlatformState::instructionPointerRegister() const { if (x86.gpr64Filled && is64Bit()) { return make_Register(x86.IP64Name, x86.IP, Register::TYPE_GPR); } else if (x86.gpr32Filled) { return make_Register<32>(x86.IP32Name, x86.IP, Register::TYPE_GPR); } return Register(); } /** * @brief PlatformState::framePointer * @return what is conceptually the frame pointer for this platform */ edb::address_t PlatformState::framePointer() const { return gpRegister(X86::RBP).valueAsAddress(); } /** * @brief PlatformState::instructionPointer * @return the instruction pointer for this platform */ edb::address_t PlatformState::instructionPointer() const { return instructionPointerRegister().valueAsAddress(); } /** * @brief PlatformState::stackPointer * @return stack pointer for this platform */ edb::address_t PlatformState::stackPointer() const { return gpRegister(X86::RSP).valueAsAddress(); } /** * @brief PlatformState::debugRegister * @param n * @return */ edb::reg_t PlatformState::debugRegister(size_t n) const { assert(dbgIndexValid(n)); return x86.dbgRegs[n]; } /** * @brief PlatformState::flagsRegister * @return */ Register PlatformState::flagsRegister() const { if (x86.gpr64Filled && is64Bit()) { return make_Register(x86.flags64Name, x86.flags, Register::TYPE_GPR); } else if (x86.gpr32Filled) { return make_Register<32>(x86.flags32Name, x86.flags, Register::TYPE_GPR); } return Register(); } /** * @brief PlatformState::flags * @return */ edb::reg_t PlatformState::flags() const { return flagsRegister().valueAsInteger(); } /** * @brief PlatformState::fpuStackPointer * @return */ int PlatformState::fpuStackPointer() const { return x87.stackPointer(); } /** * @brief PlatformState::fpuRegister * @param n * @return */ edb::value80 PlatformState::fpuRegister(size_t n) const { assert(fpuIndexValid(n)); if (!x87.filled) { edb::value80 v; constexpr std::uint64_t Mant = 0x0badbad1bad1bad1; constexpr std::uint16_t Exp = 0x0bad; std::memcpy(reinterpret_cast(&v), &Mant, sizeof(Mant)); std::memcpy(reinterpret_cast(&v) + sizeof(Mant), &Exp, sizeof(Exp)); return v; } return x87.R[n]; } /** * @brief PlatformState::fpuRegisterIsEmpty * @param n * @return true if Rn register is empty when treated in terms of FPU stack */ bool PlatformState::fpuRegisterIsEmpty(size_t n) const { return x87.tag(n) == X87::TAG_EMPTY; } /** * @brief PlatformState::fpuRegisterTagString * @param n * @return */ QString PlatformState::fpuRegisterTagString(size_t n) const { int tag = x87.tag(n); static const std::unordered_map names{ {X87::TAG_VALID, "Valid"}, {X87::TAG_ZERO, "Zero"}, {X87::TAG_SPECIAL, "Special"}, {X87::TAG_EMPTY, "Empty"}, }; return names.at(tag); } /** * @brief PlatformState::fpuControlWord * @return */ edb::value16 PlatformState::fpuControlWord() const { return x87.controlWord; } /** * @brief PlatformState::fpuStatusWord * @return */ edb::value16 PlatformState::fpuStatusWord() const { return x87.statusWord; } /** * @brief PlatformState::fpuTagWord * @return */ edb::value16 PlatformState::fpuTagWord() const { return x87.tagWord; } /** * @brief PlatformState::adjustStack * @param bytes */ void PlatformState::adjustStack(int bytes) { x86.GPRegs[X86::RSP] += bytes; } /** * @brief PlatformState::clear */ void PlatformState::clear() { x86.clear(); x87.clear(); avx.clear(); } /** * @brief PlatformState::empty * @return */ bool PlatformState::empty() const { return x86.empty() && x87.empty() && avx.empty(); } /** * @brief PlatformState::setDebugRegister * @param n * @param value */ void PlatformState::setDebugRegister(size_t n, edb::reg_t value) { assert(dbgIndexValid(n)); x86.dbgRegs[n] = value; } /** * @brief PlatformState::setFlags * @param flags */ void PlatformState::setFlags(edb::reg_t flags) { x86.flags = flags; } /** * @brief PlatformState::setInstructionPointer * @param value */ void PlatformState::setInstructionPointer(edb::address_t value) { x86.IP = value; x86.orig_ax = -1; } /** * @brief PlatformState::gpRegister * @param n * @return */ Register PlatformState::gpRegister(size_t n) const { if (gprIndexValid(n)) { if (x86.gpr64Filled && is64Bit()) { return make_Register(x86.GPReg64Names[n], x86.GPRegs[n], Register::TYPE_GPR); } else if (x86.gpr32Filled && n < IA32_GPR_COUNT) { return make_Register<32>(x86.GPReg32Names[n], x86.GPRegs[n], Register::TYPE_GPR); } } return Register(); } /** * @brief PlatformState::setRegister * @param reg */ void PlatformState::setRegister(const Register ®) { const QString regName = reg.name().toLower(); const auto gpr_end = GPRegNames().begin() + gpr_count(); const auto GPRegNameFoundIter = std::find(GPRegNames().begin(), gpr_end, regName); if (GPRegNameFoundIter != gpr_end) { size_t index = GPRegNameFoundIter - GPRegNames().begin(); x86.GPRegs[index] = reg.value(); return; } auto segRegNameFoundIter = std::find(x86.segRegNames.begin(), x86.segRegNames.end(), regName); if (segRegNameFoundIter != x86.segRegNames.end()) { size_t index = segRegNameFoundIter - x86.segRegNames.begin(); x86.segRegs[index] = reg.value(); return; } if (regName == IPName()) { x86.IP = reg.value(); return; } if (regName == flagsName()) { x86.flags = reg.value(); return; } if (regName == avx.mxcsrName) { avx.mxcsr = reg.value(); return; } { // TODO(eteran): these memcpy's which have the size set to the SOURCE and // not the dest look suspicious/potentially dangerous QRegExp MMx("^mm([0-7])$"); if (MMx.indexIn(regName) != -1) { QChar digit = MMx.cap(1).at(0); assert(digit.isDigit()); char digitChar = digit.toLatin1(); size_t i = digitChar - '0'; assert(mmxIndexValid(i)); const auto value = reg.value(); std::memcpy(reinterpret_cast(&x87.R[i]), &value, sizeof(value)); const uint16_t RiUpper = 0xffff; std::memcpy(reinterpret_cast(&x87.R[i]) + sizeof(value), &RiUpper, sizeof(RiUpper)); return; } } { QRegExp Rx("^r([0-7])$"); if (Rx.indexIn(regName) != -1) { QChar digit = Rx.cap(1).at(0); assert(digit.isDigit()); char digitChar = digit.toLatin1(); size_t i = digitChar - '0'; assert(fpuIndexValid(i)); const auto value = reg.value(); std::memcpy(&x87.R[i], &value, sizeof(value)); return; } } { QRegExp Rx("^st\\(?([0-7])\\)?$"); if (Rx.indexIn(regName) != -1) { QChar digit = Rx.cap(1).at(0); assert(digit.isDigit()); char digitChar = digit.toLatin1(); size_t i = digitChar - '0'; assert(fpuIndexValid(i)); const auto value = reg.value(); std::memcpy(&x87.st(i), &value, sizeof(value)); return; } } { QRegExp XMMx("^xmm([12]?[0-9]|3[01])$"); if (XMMx.indexIn(regName) != -1) { const auto value = reg.value(); bool indexReadOK = false; size_t i = XMMx.cap(1).toInt(&indexReadOK); assert(indexReadOK && xmmIndexValid(i)); avx.zmmStorage[i].load(value); return; } } { QRegExp YMMx("^ymm([12]?[0-9]|3[01])$"); if (YMMx.indexIn(regName) != -1) { const auto value = reg.value(); bool indexReadOK = false; size_t i = YMMx.cap(1).toInt(&indexReadOK); assert(indexReadOK && ymmIndexValid(i)); avx.zmmStorage[i].load(value); return; } } if (regName == "ftr" || regName == "ftw") { x87.tagWord = reg.value(); return; } if (regName == "fsr" || regName == "fsw") { x87.statusWord = reg.value(); return; } if (regName == "fcr" || regName == "fcw") { x87.controlWord = reg.value(); return; } if (regName == "fis" || regName == "fds") { (regName == "fis" ? x87.instPtrSelector : x87.dataPtrSelector) = reg.value(); return; } if (regName == "fip" || regName == "fdp") { (regName == "fip" ? x87.instPtrOffset : x87.dataPtrOffset) = reg.valueAsAddress(); return; } if (regName == "fopcode" || regName == "fop") { x87.opCode = reg.value(); return; } { QRegExp DRx("^dr([0-7])$"); if (DRx.indexIn(regName) != -1) { QChar digit = DRx.cap(1).at(0); assert(digit.isDigit()); char digitChar = digit.toLatin1(); size_t i = digitChar - '0'; assert(dbgIndexValid(i)); x86.dbgRegs[i] = reg.valueAsAddress(); return; } } qDebug().nospace() << "fixme: set_register(0x" << qPrintable(reg.toHexString()) << "): didn't set register " << reg.name(); } /** * @brief PlatformState::setRegister * @param name * @param value */ void PlatformState::setRegister(const QString &name, edb::reg_t value) { const QString regName = name.toLower(); setRegister(make_Register<64>(regName, value, Register::TYPE_GPR)); } /** * @brief PlatformState::archRegister * @param type * @param n * @return */ Register PlatformState::archRegister(uint64_t type, size_t n) const { switch (type) { case edb::string_hash("mmx"): return mmx_register(n); case edb::string_hash("xmm"): return xmm_register(n); case edb::string_hash("ymm"): return ymm_register(n); default: break; } return Register(); } /** * @brief PlatformState::mmx_register * @param n * @return */ Register PlatformState::mmx_register(size_t n) const { if (!mmxIndexValid(n)) { return Register(); } edb::value64 value(x87.R[n].mantissa()); return make_Register(QString("mm%1").arg(n), value, Register::TYPE_SIMD); } /** * @brief PlatformState::xmm_register * @param n * @return */ Register PlatformState::xmm_register(size_t n) const { if (!xmmIndexValid(n) || !avx.xmmFilledIA32) { return Register(); } if (n >= IA32_XMM_REG_COUNT && !avx.xmmFilledAMD64) { return Register(); } edb::value128 value(avx.xmm(n)); return make_Register(QString("xmm%1").arg(n), value, Register::TYPE_SIMD); } /** * @brief PlatformState::ymm_register * @param n * @return */ Register PlatformState::ymm_register(size_t n) const { if (!ymmIndexValid(n) || !avx.ymmFilled) { return Register(); } edb::value256 value(avx.ymm(n)); return make_Register(QString("ymm%1").arg(n), value, Register::TYPE_SIMD); } } edb-debugger/plugins/DebuggerCore/unix/linux/PrStatus.h0000644000175000017500000000210113765535463023021 0ustar eteraneteran #ifndef PR_STATUS_H_20191119_ #define PR_STATUS_H_20191119_ #include struct PrStatus_X86 { uint32_t ebx; uint32_t ecx; uint32_t edx; uint32_t esi; uint32_t edi; uint32_t ebp; uint32_t eax; uint32_t ds; uint32_t es; uint32_t fs; uint32_t gs; uint32_t orig_eax; uint32_t eip; uint32_t cs; uint32_t eflags; uint32_t esp; uint32_t ss; }; static_assert(sizeof(PrStatus_X86) == 68, "PrStatus_X86 is messed up!"); struct PrStatus_X86_64 { uint64_t r15; uint64_t r14; uint64_t r13; uint64_t r12; uint64_t rbp; uint64_t rbx; uint64_t r11; uint64_t r10; uint64_t r9; uint64_t r8; uint64_t rax; uint64_t rcx; uint64_t rdx; uint64_t rsi; uint64_t rdi; uint64_t orig_rax; uint64_t rip; uint64_t cs; uint64_t rflags; uint64_t rsp; uint64_t ss; uint64_t fs_base; uint64_t gs_base; uint64_t ds; uint64_t es; uint64_t fs; uint64_t gs; }; static_assert(sizeof(PrStatus_X86_64) == 216, "PrStatus_X86_64 is messed up!"); struct PrStatus_ARM { uint32_t regs[18]; }; static_assert(sizeof(PrStatus_ARM) == 72, "PrStatus_ARM is messed up!"); #endif edb-debugger/plugins/DebuggerCore/unix/linux/PlatformEvent.cpp0000644000175000017500000002430213765535463024364 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "PlatformEvent.h" #include "edb.h" namespace DebuggerCorePlugin { /** * @brief PlatformEvent::clone * @return */ IDebugEvent *PlatformEvent::clone() const { return new PlatformEvent(*this); } /** * @brief PlatformEvent::createUnexpectedSignalMessage * @param name * @param number * @return */ IDebugEvent::Message PlatformEvent::createUnexpectedSignalMessage(const QString &name, int number) { return Message( tr("Unexpected Signal Encountered"), tr("

The debugged application encountered a %1 (%2).

").arg(name).arg(number), tr("% received").arg(name)); } /** * @brief PlatformEvent::errorDescription * @return */ IDebugEvent::Message PlatformEvent::errorDescription() const { Q_ASSERT(isError()); auto fault_address = edb::address_t::fromZeroExtended(siginfo_.si_addr); std::size_t debuggeePtrSize = edb::v1::pointer_size(); bool fullAddressKnown = debuggeePtrSize <= sizeof(void *); const QString addressString = fault_address.toPointerString(fullAddressKnown); Message message; switch (code()) { case SIGSEGV: switch (siginfo_.si_code) { case SEGV_MAPERR: message = Message( tr("Illegal Access Fault"), tr("

The debugged application encountered a segmentation fault.
The address %1 does not appear to be mapped.

").arg(addressString), tr("SIGSEGV: SEGV_MAPERR: Accessed address %1 not mapped").arg(addressString)); break; case SEGV_ACCERR: message = Message( tr("Illegal Access Fault"), tr("

The debugged application encountered a segmentation fault.
The address %1 could not be accessed.

").arg(addressString), tr("SIGSEGV: SEGV_ACCERR: Access to address %1 not permitted").arg(addressString)); break; default: message = Message( tr("Illegal Access Fault"), tr("

The debugged application encountered a segmentation fault.
The instruction could not be executed.

"), tr("SIGSEGV: Segmentation fault")); break; } break; case SIGILL: message = Message( tr("Illegal Instruction Fault"), tr("

The debugged application attempted to execute an illegal instruction.

"), tr("SIGILL: Illegal instruction")); break; case SIGFPE: switch (siginfo_.si_code) { case FPE_INTDIV: message = Message( tr("Divide By Zero"), tr("

The debugged application tried to divide an integer value by an integer divisor of zero or encountered integer division overflow.

"), tr("SIGFPE: FPE_INTDIV: Integer division by zero or division overflow")); break; case FPE_FLTDIV: message = Message( tr("Divide By Zero"), tr("

The debugged application tried to divide an floating-point value by a floating-point divisor of zero.

"), tr("SIGFPE: FPE_FLTDIV: Floating-point division by zero")); break; case FPE_FLTOVF: message = Message( tr("Numeric Overflow"), tr("

The debugged application encountered a numeric overflow while performing a floating-point computation.

"), tr("SIGFPE: FPE_FLTOVF: Numeric overflow exception")); break; case FPE_FLTUND: message = Message( tr("Numeric Underflow"), tr("

The debugged application encountered a numeric underflow while performing a floating-point computation.

"), tr("SIGFPE: FPE_FLTUND: Numeric underflow exception")); break; case FPE_FLTRES: message = Message( tr("Inexact Result"), tr("

The debugged application encountered an inexact result of a floating-point computation it was performing.

"), tr("SIGFPE: FPE_FLTRES: Inexact result exception")); break; case FPE_FLTINV: message = Message( tr("Invalid Operation"), tr("

The debugged application attempted to perform an invalid floating-point operation.

"), tr("SIGFPE: FPE_FLTINV: Invalid floating-point operation")); break; default: message = Message( tr("Floating Point Exception"), tr("

The debugged application encountered a floating-point exception.

"), tr("SIGFPE: Floating-point exception")); break; } break; case SIGABRT: message = Message( tr("Application Aborted"), tr("

The debugged application has aborted.

"), tr("SIGABRT: Application aborted")); break; case SIGBUS: message = Message( tr("Bus Error"), tr("

The debugged application received a bus error. Typically, this means that it tried to read or write data that is misaligned.

"), tr("SIGBUS: Bus error")); break; #ifdef SIGSTKFLT case SIGSTKFLT: message = Message( tr("Stack Fault"), tr("

The debugged application encountered a stack fault.

"), tr("SIGSTKFLT: Stack fault")); break; #endif case SIGPIPE: message = Message( tr("Broken Pipe Fault"), tr("

The debugged application encountered a broken pipe fault.

"), tr("SIGPIPE: Pipe broken")); break; #ifdef SIGHUP case SIGHUP: message = createUnexpectedSignalMessage("SIGHUP", SIGHUP); break; #endif #ifdef SIGINT case SIGINT: message = createUnexpectedSignalMessage("SIGINT", SIGINT); break; #endif #ifdef SIGQUIT case SIGQUIT: message = createUnexpectedSignalMessage("SIGQUIT", SIGQUIT); break; #endif #ifdef SIGTRAP case SIGTRAP: message = createUnexpectedSignalMessage("SIGTRAP", SIGTRAP); break; #endif #ifdef SIGKILL case SIGKILL: message = createUnexpectedSignalMessage("SIGKILL", SIGKILL); break; #endif #ifdef SIGUSR1 case SIGUSR1: message = createUnexpectedSignalMessage("SIGUSR1", SIGUSR1); break; #endif #ifdef SIGUSR2 case SIGUSR2: message = createUnexpectedSignalMessage("SIGUSR2", SIGUSR2); break; #endif #ifdef SIGALRM case SIGALRM: message = createUnexpectedSignalMessage("SIGALRM", SIGALRM); break; #endif #ifdef SIGTERM case SIGTERM: message = createUnexpectedSignalMessage("SIGTERM", SIGTERM); break; #endif #ifdef SIGCHLD case SIGCHLD: message = createUnexpectedSignalMessage("SIGCHLD", SIGCHLD); break; #endif #ifdef SIGCONT case SIGCONT: message = createUnexpectedSignalMessage("SIGCONT", SIGCONT); break; #endif #ifdef SIGSTOP case SIGSTOP: message = createUnexpectedSignalMessage("SIGSTOP", SIGSTOP); break; #endif #ifdef SIGTSTP case SIGTSTP: message = createUnexpectedSignalMessage("SIGTSTP", SIGTSTP); break; #endif #ifdef SIGTTIN case SIGTTIN: message = createUnexpectedSignalMessage("SIGTTIN", SIGTTIN); break; #endif #ifdef SIGTTOU case SIGTTOU: message = createUnexpectedSignalMessage("SIGTTOU", SIGTTOU); break; #endif #ifdef SIGURG case SIGURG: message = createUnexpectedSignalMessage("SIGURG", SIGURG); break; #endif #ifdef SIGXCPU case SIGXCPU: message = createUnexpectedSignalMessage("SIGXCPU", SIGXCPU); break; #endif #ifdef SIGXFSZ case SIGXFSZ: message = createUnexpectedSignalMessage("SIGXFSZ", SIGXFSZ); break; #endif #ifdef SIGVTALRM case SIGVTALRM: message = createUnexpectedSignalMessage("SIGVTALRM", SIGVTALRM); break; #endif #ifdef SIGPROF case SIGPROF: message = createUnexpectedSignalMessage("SIGPROF", SIGPROF); break; #endif #ifdef SIGWINCH case SIGWINCH: message = createUnexpectedSignalMessage("SIGWINCH", SIGWINCH); break; #endif #ifdef SIGIO case SIGIO: message = createUnexpectedSignalMessage("SIGIO", SIGIO); break; #endif default: return Message(); } message.message += "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

"; message.statusMessage += ". Shift+Run/Step to pass signal to the program"; return message; } /** * @brief PlatformEvent::reason * @return */ IDebugEvent::REASON PlatformEvent::reason() const { // this basically converts our value into a 'switchable' value for convenience if (stopped()) { return EVENT_STOPPED; } else if (terminated()) { return EVENT_TERMINATED; } else if (exited()) { return EVENT_EXITED; } else { return EVENT_UNKNOWN; } } /** * @brief PlatformEvent::trapReason * @return */ IDebugEvent::TRAP_REASON PlatformEvent::trapReason() const { switch (siginfo_.si_code) { case TRAP_TRACE: return TRAP_STEPPING; default: return TRAP_BREAKPOINT; } } /** * @brief PlatformEvent::exited * @return */ bool PlatformEvent::exited() const { return WIFEXITED(status_) != 0; } /** * @brief PlatformEvent::isError * @return */ bool PlatformEvent::isError() const { if (stopped()) { switch (code()) { case SIGTRAP: case SIGSTOP: return false; case SIGSEGV: case SIGILL: case SIGFPE: case SIGABRT: case SIGBUS: #ifdef SIGSTKFLT case SIGSTKFLT: #endif case SIGPIPE: return true; default: return false; } } else { return false; } } /** * @brief PlatformEvent::isKill * @return */ bool PlatformEvent::isKill() const { return stopped() && code() == SIGKILL; } /** * @brief PlatformEvent::isStop * @return */ bool PlatformEvent::isStop() const { return stopped() && code() == SIGSTOP; } /** * @brief PlatformEvent::isTrap * @return */ bool PlatformEvent::isTrap() const { return stopped() && code() == SIGTRAP; } /** * @brief PlatformEvent::terminated * @return */ bool PlatformEvent::terminated() const { return WIFSIGNALED(status_) != 0; } /** * @brief PlatformEvent::stopped * @return */ bool PlatformEvent::stopped() const { return WIFSTOPPED(status_) != 0; } /** * @brief PlatformEvent::process * @return */ edb::pid_t PlatformEvent::process() const { return pid_; } /** * @brief PlatformEvent::thread * @return */ edb::tid_t PlatformEvent::thread() const { return tid_; } /** * @brief PlatformEvent::code * @return */ int64_t PlatformEvent::code() const { if (stopped()) { return WSTOPSIG(status_); } if (terminated()) { return WTERMSIG(status_); } if (exited()) { return WEXITSTATUS(status_); } return 0; } } edb-debugger/plugins/DebuggerCore/unix/linux/PlatformRegion.cpp0000644000175000017500000002405313765535463024531 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "PlatformRegion.h" #include "IDebugEventHandler.h" #include "IDebugger.h" #include "IProcess.h" #include "IThread.h" #include "MemoryRegions.h" #include "State.h" #include "edb.h" #include #include #include namespace DebuggerCorePlugin { namespace { /** * @brief permissions_value * @param read * @param write * @param execute * @return */ IRegion::permissions_t permissions_value(bool read, bool write, bool execute) { IRegion::permissions_t perms = 0; if (read) perms |= PROT_READ; if (write) perms |= PROT_WRITE; if (execute) perms |= PROT_EXEC; return perms; } } template class BackupInfo : public IDebugEventHandler { public: BackupInfo(edb::address_t address, IRegion::permissions_t perms, PlatformRegion *region); ~BackupInfo() override; BackupInfo(const BackupInfo &) = delete; BackupInfo &operator=(const BackupInfo &) = delete; public: IRegion::permissions_t perms() const { return premissions_; } bool locked() { return !lock_.testAndSetAcquire(0, 1); } public: bool backup(); bool restore(); public: edb::EventStatus handleEvent(const std::shared_ptr &event) override; private: QAtomicInt lock_ = 1; edb::address_t address_; IRegion::permissions_t premissions_; State state_; uint8_t buffer_[N]; PlatformRegion *const region_; }; /** * @brief BackupInfo::BackupInfo * @param address * @param perms * @param region */ template BackupInfo::BackupInfo(edb::address_t address, IRegion::permissions_t perms, PlatformRegion *region) : address_(address), premissions_(perms), region_(region) { edb::v1::add_debug_event_handler(this); } /** * @brief BackupInfo::~BackupInfo */ template BackupInfo::~BackupInfo() { edb::v1::remove_debug_event_handler(this); } /** * @brief BackupInfo::backup * @return */ template bool BackupInfo::backup() { if (IProcess *process = edb::v1::debugger_core->process()) { if (std::shared_ptr thread = process->currentThread()) { thread->getState(&state_); } return process->readBytes(address_, buffer_, N); } return false; } /** * @brief BackupInfo::restore * @return */ template bool BackupInfo::restore() { if (IProcess *process = edb::v1::debugger_core->process()) { if (std::shared_ptr thread = process->currentThread()) { thread->setState(state_); } return process->writeBytes(address_, buffer_, N); } return false; } /** * @brief BackupInfo::handleEvent * @param event * @return */ template edb::EventStatus BackupInfo::handleEvent(const std::shared_ptr &event) { Q_UNUSED(event) lock_.testAndSetRelease(1, 0); // restore the original code and register state restore(); // update permissions mask region_->permissions_ = perms(); // really shouldn't matter since the return value isn't used at all // we simply want tot catch the event and set the lock to 0 return edb::DEBUG_STOP; } /** * @brief PlatformRegion::PlatformRegion * @param start * @param end * @param base * @param name * @param permissions */ PlatformRegion::PlatformRegion(edb::address_t start, edb::address_t end, edb::address_t base, const QString &name, permissions_t permissions) : start_(start), end_(end), base_(base), name_(name), permissions_(permissions) { } /** * @brief PlatformRegion::clone * @return */ IRegion *PlatformRegion::clone() const { return new PlatformRegion(start_, end_, base_, name_, permissions_); } /** * @brief PlatformRegion::accessible * @return */ bool PlatformRegion::accessible() const { return readable() || writable() || executable(); } /** * @brief PlatformRegion::readable * @return */ bool PlatformRegion::readable() const { return (permissions_ & PROT_READ) != 0; } /** * @brief PlatformRegion::writable * @return */ bool PlatformRegion::writable() const { return (permissions_ & PROT_WRITE) != 0; } /** * @brief PlatformRegion::executable * @return */ bool PlatformRegion::executable() const { return (permissions_ & PROT_EXEC) != 0; } /** * @brief PlatformRegion::size * @return */ size_t PlatformRegion::size() const { return end_ - start_; } /** * @brief PlatformRegion::setPermissions * @param read * @param write * @param execute */ void PlatformRegion::setPermissions(bool read, bool write, bool execute) { edb::address_t temp_address = 0; int count = 0; int ret = QMessageBox::Yes; const QList> ®ions = edb::v1::memory_regions().regions(); // search for an executable region to run our shell code for (const std::shared_ptr ®ion : regions) { if (region->executable()) { if (temp_address == 0) { temp_address = region->start(); } if (++count > 1) { break; } } } if (executable() && count == 1 && !execute) { ret = QMessageBox::question(nullptr, tr("Removing Execute Permissions On Last Executable std::shared_ptr"), tr("You are about to remove execute permissions from the last executable region. Because of the need " "to run code in the process to change permissions, there will be no way to undo this. In addition, " "the process will no longer be able to run as it will have no execute permissions in any regions. " "Odds are this is not what you want to do." "Are you sure you want to remove execute permissions from this region?"), QMessageBox::Yes, QMessageBox::No); } if (ret == QMessageBox::Yes) { if (temp_address != 0) { setPermissions(read, write, execute, temp_address); } else { QMessageBox::critical( nullptr, tr("No Suitable Address Found"), tr("This feature relies on running shellcode in the debugged process, no executable memory region was found. Unfortunately, this means that no more region permission changes can be made (it also means that there is nothing the process can continue to do since it cannot execute at all).")); } } } /** * @brief PlatformRegion::start * @return */ edb::address_t PlatformRegion::start() const { return start_; } /** * @brief PlatformRegion::end * @return */ edb::address_t PlatformRegion::end() const { return end_; } /** * @brief PlatformRegion::base * @return */ edb::address_t PlatformRegion::base() const { return base_; } /** * @brief PlatformRegion::name * @return */ QString PlatformRegion::name() const { return name_; } /** * @brief PlatformRegion::permissions * @return */ IRegion::permissions_t PlatformRegion::permissions() const { return permissions_; } /** * @brief PlatformRegion::setPermissions * @param read * @param write * @param execute * @param temp_address */ void PlatformRegion::setPermissions(bool read, bool write, bool execute, edb::address_t temp_address) { const permissions_t perms = permissions_value(read, write, execute); const edb::address_t len = size(); const edb::address_t addr = start(); // I wish there was a clean way to get the value of this system call for either target // but nothing obvious comes to mind. We may have to do something crazy // with macros, but for now, we just hard code it :-/ const edb::address_t syscallnum = edb::v1::debuggeeIs32Bit() ? 125 : 10; //__NR_mprotect; #if defined(EDB_X86) || defined(EDB_X86_64) // start of nowhere near portable code const uint8_t shellcode32[] = { "\xcd\x80" // int $0x80 "\xf4" // hlt }; const uint8_t shellcode64[] = { "\x0f\x05" // syscall "\xf4" // hlt }; uint8_t shellcode[3]; if (edb::v1::debuggeeIs32Bit()) { memcpy(shellcode, shellcode32, sizeof(shellcode)); } else { memcpy(shellcode, shellcode64, sizeof(shellcode)); } // end nowhere near portable code using BI = BackupInfo; if (IProcess *process = edb::v1::debugger_core->process()) { if (std::shared_ptr thread = process->currentThread()) { try { BI backup_info(temp_address, perms, this); if (backup_info.backup()) { // write out our shellcode if (process->writeBytes(temp_address, shellcode, sizeof(shellcode))) { State state; thread->getState(&state); state.setInstructionPointer(temp_address); if (edb::v1::debuggeeIs32Bit()) { state.setRegister("ecx", len); state.setRegister("ebx", addr); state.setRegister("edx", perms); state.setRegister("eax", syscallnum); } else { state.setRegister("rsi", len); state.setRegister("rdi", addr); state.setRegister("rdx", perms); state.setRegister("rax", syscallnum); } thread->setState(state); // run the system call instruction and wait for the trap thread->step(edb::DEBUG_CONTINUE); // we use a spinlock here because we want to be able to // process events while waiting while (backup_info.locked()) { QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents); } } } } catch (const std::bad_alloc &) { QMessageBox::critical( nullptr, tr("Memory Allocation Error"), tr("Unable to satisfy memory allocation request for backup code.")); } } } #endif } /** * @brief PlatformRegion::setStart * @param address */ void PlatformRegion::setStart(edb::address_t address) { start_ = address; } /** * @brief PlatformRegion::setEnd * @param address */ void PlatformRegion::setEnd(edb::address_t address) { end_ = address; } } edb-debugger/plugins/DebuggerCore/unix/linux/PlatformProcess.cpp0000644000175000017500000007275513765535463024740 0ustar eteraneteran/* Copyright (C) 2015 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _LARGEFILE64_SOURCE #define _LARGEFILE64_SOURCE #endif #include "PlatformProcess.h" #include "ByteShiftArray.h" #include "DebuggerCore.h" #include "IBreakpoint.h" #include "MemoryRegions.h" #include "Module.h" #include "PlatformCommon.h" #include "PlatformRegion.h" #include "PlatformThread.h" #include "edb.h" #include "libELF/elf_binary.h" #include "libELF/elf_model.h" #include "linker.h" #include "util/Container.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace DebuggerCorePlugin { namespace { // Used as size of ptrace word constexpr size_t WordSize = sizeof(long); /** * @brief set_ok * @param value */ bool set_ok(long value) { return (value != -1) || (errno == 0); } /** * @brief split_max * @param str * @param maxparts * @return */ QStringList split_max(const QString &str, int maxparts) { int prev_idx = 0; int idx = 0; QStringList items; for (const QChar &c : str) { if (c == ' ') { if (prev_idx < idx) { if (items.size() < maxparts - 1) items << str.mid(prev_idx, idx - prev_idx); else { items << str.right(str.size() - prev_idx); break; } } prev_idx = idx + 1; } ++idx; } if (prev_idx < str.size() && items.size() < maxparts) { items << str.right(str.size() - prev_idx); } return items; } /** * parses the data from a line of a memory map file * * @brief process_map_line * @param line * @return */ std::shared_ptr process_map_line(const QString &line) { edb::address_t start; edb::address_t end; edb::address_t base; IRegion::permissions_t permissions; QString name; const QStringList items = split_max(line, 6); if (items.size() >= 3) { bool ok; const QStringList bounds = items[0].split("-"); if (bounds.size() == 2) { start = edb::address_t::fromHexString(bounds[0], &ok); if (ok) { end = edb::address_t::fromHexString(bounds[1], &ok); if (ok) { base = edb::address_t::fromHexString(items[2], &ok); if (ok) { const QString perms = items[1]; permissions = 0; if (perms[0] == 'r') permissions |= PROT_READ; if (perms[1] == 'w') permissions |= PROT_WRITE; if (perms[2] == 'x') permissions |= PROT_EXEC; if (items.size() >= 6) { name = items[5]; } return std::make_shared(start, end, base, name, permissions); } } } } } return nullptr; } /** * @brief get_loaded_modules * @param process * @return */ template QList get_loaded_modules(const IProcess *process) { QList ret; edb::linux_struct::r_debug dynamic_info; if (process) { if (const edb::address_t debug_pointer = process->debugPointer()) { if (process->readBytes(debug_pointer, &dynamic_info, sizeof(dynamic_info))) { if (dynamic_info.r_map) { auto link_address = edb::address_t::fromZeroExtended(dynamic_info.r_map); while (link_address) { edb::linux_struct::link_map map; if (process->readBytes(link_address, &map, sizeof(map))) { char path[PATH_MAX]; if (!process->readBytes(edb::address_t::fromZeroExtended(map.l_name), &path, sizeof(path))) { path[0] = '\0'; } if (map.l_addr) { Module module; module.name = path; module.baseAddress = map.l_addr; ret.push_back(module); } link_address = edb::address_t::fromZeroExtended(map.l_next); } else { break; } } } } } } // fallback if (ret.isEmpty()) { const QList> r = edb::v1::memory_regions().regions(); QSet found_modules; for (const std::shared_ptr ®ion : r) { // we assume that modules will be listed by absolute path if (region->name().startsWith("/")) { if (!util::contains(found_modules, region->name())) { Module module; module.name = region->name(); module.baseAddress = region->start(); found_modules.insert(region->name()); ret.push_back(module); } } } } return ret; } /** * seeks memory file to given address, taking possible negativity of the * address into account * * @brief seek_addr * @param file * @param address */ void seek_addr(QFile &file, edb::address_t address) { if (address <= UINT64_MAX / 2) { file.seek(address); } else { const int fd = file.handle(); // Seek in two parts to avoid specifying negative offset: off64_t is a signed type const off64_t halfAddressTruncated = address >> 1; lseek64(fd, halfAddressTruncated, SEEK_SET); const off64_t secondHalfAddress = address - halfAddressTruncated; lseek64(fd, secondHalfAddress, SEEK_CUR); } } } /** * @brief PlatformProcess::PlatformProcess * @param core * @param pid */ PlatformProcess::PlatformProcess(DebuggerCore *core, edb::pid_t pid) : core_(core), pid_(pid) { if (!core_->procMemReadBroken_) { auto memory_file = std::make_shared(QString("/proc/%1/mem").arg(pid_)); QIODevice::OpenMode flags = QIODevice::ReadOnly | QIODevice::Unbuffered; if (!core_->procMemWriteBroken_) { flags |= QIODevice::WriteOnly; } if (memory_file->open(flags)) { readOnlyMemFile_ = memory_file; if (!core_->procMemWriteBroken_) { readWriteMemFile_ = memory_file; } } } } /** * reads bytes into starting at
* * @brief PlatformProcess::readBytes * @param address * @param buf * @param len * @return */ std::size_t PlatformProcess::readBytes(edb::address_t address, void *buf, std::size_t len) const { // NOTE(eteran): returns the number of bytes read // NOTE(eteran): if the read is short, only the first bytes are defined quint64 read = 0; Q_ASSERT(buf); Q_ASSERT(core_->process_.get() == this); auto ptr = reinterpret_cast(buf); if (len != 0) { // small reads take the fast path if (len == 1) { auto it = core_->breakpoints_.find(address); if (it != core_->breakpoints_.end()) { *ptr = (*it)->originalBytes()[0]; return 1; } if (readOnlyMemFile_) { seek_addr(*readOnlyMemFile_, address); read = readOnlyMemFile_->read(ptr, 1); if (read == 1) { return 1; } return 0; } else { bool ok; uint8_t x = ptraceReadByte(address, &ok); if (ok) { *ptr = x; return 1; } return 0; } } if (readOnlyMemFile_) { seek_addr(*readOnlyMemFile_, address); read = readOnlyMemFile_->read(ptr, len); if (read == 0 || read == quint64(-1)) { return 0; } } else { for (std::size_t index = 0; index < len; ++index) { // read a byte, if we failed, we are done bool ok; const uint8_t x = ptraceReadByte(address + index, &ok); if (!ok) { break; } // store it reinterpret_cast(buf)[index] = x; ++read; } } // replace any breakpoints Q_FOREACH (const std::shared_ptr &bp, core_->breakpoints_) { auto bpBytes = bp->originalBytes(); const edb::address_t bpAddr = bp->address(); // show the original bytes in the buffer.. for (size_t i = 0; i < bp->size(); ++i) { if (bpAddr + i >= address && bpAddr + i < address + read) { ptr[bpAddr + i - address] = bpBytes[i]; } } } } return read; } /** * same as writeBytes, except that it also records the original data that was * found at the address being written to. * * @brief PlatformProcess::patchBytes * @param address * @param buf * @param len * @return */ std::size_t PlatformProcess::patchBytes(edb::address_t address, const void *buf, size_t len) { // NOTE(eteran): Unlike the read_bytes, write_bytes functions, this will // not apply the write if we could not properly backup // bytes as requested. // NOTE(eteran): On the off chance that we can READ bytes, but can't // WRITE bytes, we will return the number of bytes // written, but record bytes of patch data. Q_ASSERT(buf); Q_ASSERT(core_->process_.get() == this); Patch patch; patch.address = address; patch.origBytes.resize(len); patch.newBytes = QByteArray(static_cast(buf), len); size_t read_ret = readBytes(address, patch.origBytes.data(), len); if (read_ret != len) { return 0; } patches_.insert(address, patch); return writeBytes(address, buf, len); } /** * writes bytes from starting at
* * @brief PlatformProcess::writeBytes * @param address * @param buf * @param len * @return */ std::size_t PlatformProcess::writeBytes(edb::address_t address, const void *buf, std::size_t len) { quint64 written = 0; Q_ASSERT(buf); Q_ASSERT(core_->process_.get() == this); if (len != 0) { if (readWriteMemFile_) { seek_addr(*readWriteMemFile_, address); written = readWriteMemFile_->write(reinterpret_cast(buf), len); if (written == 0 || written == quint64(-1)) { return 0; } } else { // TODO write whole words at a time using ptrace_poke. for (std::size_t byteIndex = 0; byteIndex < len; ++byteIndex) { bool ok = false; ptraceWriteByte(address + byteIndex, *(reinterpret_cast(buf) + byteIndex), &ok); if (!ok) return written; ++written; } } } return written; } /** * reads pages from the process starting at
* * @brief PlatformProcess::readPages * @param address - must be page aligned. * @param buf - sizeof(buf) must be >= count * core_->page_size() * @param count - number of pages * @return */ std::size_t PlatformProcess::readPages(edb::address_t address, void *buf, std::size_t count) const { Q_ASSERT(buf); Q_ASSERT(core_->process_.get() == this); return readBytes(address, buf, count * core_->pageSize()) / core_->pageSize(); } /** * @brief PlatformProcess::startTime * @return */ QDateTime PlatformProcess::startTime() const { QFileInfo info(QString("/proc/%1/stat").arg(pid_)); #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) return info.birthTime(); #else return info.created(); #endif } /** * @brief PlatformProcess::arguments * @return */ QList PlatformProcess::arguments() const { QList ret; if (pid_ != 0) { const QString command_line_file(QString("/proc/%1/cmdline").arg(pid_)); QFile file(command_line_file); if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { QTextStream in(&file); QByteArray s; QChar ch; while (in.status() == QTextStream::Ok) { in >> ch; if (ch.isNull()) { if (!s.isEmpty()) { ret << s; } s.clear(); } else { s += ch; } } if (!s.isEmpty()) { ret << s; } } } return ret; } /** * @brief PlatformProcess::currentWorkingDirectory * @return */ QString PlatformProcess::currentWorkingDirectory() const { return edb::v1::symlink_target(QString("/proc/%1/cwd").arg(pid_)); } /** * @brief PlatformProcess::executable * @return */ QString PlatformProcess::executable() const { return edb::v1::symlink_target(QString("/proc/%1/exe").arg(pid_)); } /** * @brief PlatformProcess::pid * @return */ edb::pid_t PlatformProcess::pid() const { return pid_; } /** * @brief PlatformProcess::parent * @return */ std::shared_ptr PlatformProcess::parent() const { struct user_stat user_stat; int n = get_user_stat(pid_, &user_stat); if (n >= 4) { return std::make_shared(core_, user_stat.ppid); } return nullptr; } /** * @brief PlatformProcess::codeAddress * @return */ edb::address_t PlatformProcess::codeAddress() const { struct user_stat user_stat; int n = get_user_stat(pid_, &user_stat); if (n >= 26) { return user_stat.startcode; } return 0; } /** * @brief PlatformProcess::dataAddress * @return */ edb::address_t PlatformProcess::dataAddress() const { struct user_stat user_stat; int n = get_user_stat(pid_, &user_stat); if (n >= 27) { return user_stat.endcode + 1; // endcode == startdata ? } return 0; } /** * @brief PlatformProcess::regions * @return */ QList> PlatformProcess::regions() const { static QList> regions; const QString map_file(QString("/proc/%1/maps").arg(pid_)); // hash the region file to see if it changed or not { static size_t totalHash = 0; std::ifstream mf(map_file.toStdString()); size_t newHash = 0; std::string line; while (std::getline(mf, line)) { boost::hash_combine(newHash, line); } if (totalHash == newHash) { return regions; } totalHash = newHash; regions.clear(); } // it changed, so let's process it QFile file(map_file); if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { QTextStream in(&file); QString line = in.readLine(); while (!line.isNull()) { if (std::shared_ptr region = process_map_line(line)) { regions.push_back(region); } line = in.readLine(); } } return regions; } /** * @brief PlatformProcess::ptraceReadByte * @param address * @param ok * @return */ uint8_t PlatformProcess::ptraceReadByte(edb::address_t address, bool *ok) const { // TODO(eteran): assert that we are paused Q_ASSERT(ok); Q_ASSERT(core_->process_.get() == this); *ok = false; // if this spot is unreadable, then just return 0xff, otherwise // continue as normal. // core_->page_size() - 1 will always be 0xf* because pagesizes // are always 0x10*, so the masking works // range of nBytesToNextPage is [1..n] where n=pagesize, and we have to adjust // if nByteToNextPage < wordsize const size_t nBytesToNextPage = core_->pageSize() - (address & (core_->pageSize() - 1)); // Avoid crossing page boundary, since next page may be unreadable const size_t addressShift = nBytesToNextPage < WordSize ? WordSize - nBytesToNextPage : 0; address -= addressShift; const long value = ptracePeek(address, ok); if (*ok) { uint8_t result; // We aren't interested in `value` as in number, it's just a buffer, so no endianness magic. // Just have to compensate for `addressShift` when reading it. std::memcpy(&result, reinterpret_cast(&value) + addressShift, sizeof(result)); return result; } return 0xff; } /** * writes a single byte at a given address via ptrace API. * * @brief PlatformProcess::ptraceWriteByte * @param address * @param value * @param ok */ void PlatformProcess::ptraceWriteByte(edb::address_t address, uint8_t value, bool *ok) { // TODO(eteran): assert that we are paused // NOTE(eteran): assumes the this will not trample any breakpoints, must // be handled in calling code! Q_ASSERT(ok); Q_ASSERT(core_->process_.get() == this); *ok = false; // core_->page_size() - 1 will always be 0xf* because pagesizes // are always 0x10*, so the masking works // range of nBytesToNextPage is [1..n] where n=pagesize, and we have to adjust // if nBytesToNextPage < wordsize const size_t nBytesToNextPage = core_->pageSize() - (address & (core_->pageSize() - 1)); // Avoid crossing page boundary, since next page may be inaccessible const size_t addressShift = nBytesToNextPage < WordSize ? WordSize - nBytesToNextPage : 0; address -= addressShift; long word = ptracePeek(address, ok); if (!*ok) { return; } // We aren't interested in `value` as in number, it's just a buffer, so no endianness magic. // Just have to compensate for `addressShift` when writing it. std::memcpy(reinterpret_cast(&word) + addressShift, &value, sizeof(value)); *ok = ptracePoke(address, word); } /** * @brief PlatformProcess::ptracePeek * @param address * @param ok * @return */ long PlatformProcess::ptracePeek(edb::address_t address, bool *ok) const { // NOTE(eteran): this will fail on newer versions of linux if called from a // different thread than the one which attached to process Q_ASSERT(ok); Q_ASSERT(core_->process_.get() == this); if (EDB_IS_32_BIT && address > 0xffffffffULL) { // 32 bit ptrace can't handle such long addresses *ok = false; return 0; } errno = 0; // NOTE: on some Linux systems ptrace prototype has ellipsis instead of third and fourth arguments // Thus we can't just pass address as is on IA32 systems: it'd put 64 bit integer on stack and cause UB auto nativeAddress = reinterpret_cast(address.toUint()); const long v = ptrace(PTRACE_PEEKTEXT, pid_, nativeAddress, 0); *ok = set_ok(v); return v; } /** * @brief PlatformProcess::ptracePoke * @param address * @param value * @return */ bool PlatformProcess::ptracePoke(edb::address_t address, long value) { Q_ASSERT(core_->process_.get() == this); if (EDB_IS_32_BIT && address > 0xffffffffULL) { // 32 bit ptrace can't handle such long addresses return false; } // NOTE: on some Linux systems ptrace prototype has ellipsis instead of third and fourth arguments // Thus we can't just pass address as is on IA32 systems: it'd put 64 bit integer on stack and cause UB auto nativeAddress = reinterpret_cast(address.toUint()); return ptrace(PTRACE_POKETEXT, pid_, nativeAddress, value) != -1; } /** * @brief PlatformProcess::threads * @return */ QList> PlatformProcess::threads() const { Q_ASSERT(core_->process_.get() == this); QList> threadList; threadList.reserve(core_->threads_.size()); std::copy(core_->threads_.begin(), core_->threads_.end(), std::back_inserter(threadList)); return threadList; } /** * @brief PlatformProcess::currentThread * @return */ std::shared_ptr PlatformProcess::currentThread() const { Q_ASSERT(core_->process_.get() == this); auto it = core_->threads_.find(core_->activeThread_); if (it != core_->threads_.end()) { return it.value(); } return nullptr; } /** * @brief PlatformProcess::setCurrentThread * @param thread */ void PlatformProcess::setCurrentThread(IThread &thread) { core_->activeThread_ = static_cast(&thread)->tid(); edb::v1::update_ui(); } /** * @brief PlatformProcess::uid * @return */ edb::uid_t PlatformProcess::uid() const { const QFileInfo info(QString("/proc/%1").arg(pid_)); return info.ownerId(); } /** * @brief PlatformProcess::user * @return */ QString PlatformProcess::user() const { if (const struct passwd *const pwd = ::getpwuid(uid())) { return pwd->pw_name; } return QString(); } /** * @brief PlatformProcess::name * @return */ QString PlatformProcess::name() const { struct user_stat user_stat; const int n = get_user_stat(pid_, &user_stat); if (n >= 2) { return user_stat.comm; } return QString(); } /** * @brief PlatformProcess::loadedModules * @return */ QList PlatformProcess::loadedModules() const { if (edb::v1::debuggeeIs64Bit()) { return get_loaded_modules(this); } else if (edb::v1::debuggeeIs32Bit()) { return get_loaded_modules(this); } else { return QList(); } } /** * stops *all* threads of a process * * @brief PlatformProcess::pause * @return */ Status PlatformProcess::pause() { // belive it or not, I belive that this is sufficient for all threads. // This is because in the debug event handler, a SIGSTOP is sent // to all threads when any event arrives, so no need to explicitly do // it here. We just need any thread to stop. So we'll just target the // pid_ which will send it to any one of the threads in the process. if (::kill(pid_, SIGSTOP) == -1) { const char *const strError = strerror(errno); qWarning() << "Unable to pause process" << pid_ << ": kill(SIGSTOP) failed:" << strError; return Status(strError); } return Status::Ok; } /** * resumes ALL threads * * @brief PlatformProcess::resume * @param status * @return */ Status PlatformProcess::resume(edb::EventStatus status) { // NOTE(eteran): OK, this is very tricky. When the user wants to resume // while ignoring a signal (DEBUG_CONTINUE), we need to know which thread // needs to have the signal ignored, and which need to have their signals // passed during the resume // TODO: assert that we are paused Q_ASSERT(core_->process_.get() == this); QString errorMessage; if (status != edb::DEBUG_STOP) { if (std::shared_ptr thread = currentThread()) { const auto resumeStatus = thread->resume(status); if (!resumeStatus) { errorMessage += tr("Failed to resume thread %1: %2\n").arg(thread->tid()).arg(resumeStatus.error()); } // resume the other threads passing the signal they originally reported had for (auto &other_thread : threads()) { if (util::contains(core_->waitedThreads_, other_thread->tid())) { const auto resumeStatus = other_thread->resume(); if (!resumeStatus) { errorMessage += tr("Failed to resume thread %1: %2\n").arg(thread->tid()).arg(resumeStatus.error()); } } } } } if (errorMessage.isEmpty()) { return Status::Ok; } qWarning() << errorMessage.toStdString().c_str(); return Status("\n" + errorMessage); } /** * steps the currently active thread * * @brief PlatformProcess::step * @param status * @return */ Status PlatformProcess::step(edb::EventStatus status) { // TODO: assert that we are paused Q_ASSERT(core_->process_.get() == this); if (status != edb::DEBUG_STOP) { if (std::shared_ptr thread = currentThread()) { return thread->step(status); } } return Status::Ok; } /** * @brief PlatformProcess::isPaused * @return true if ALL threads are currently in the debugger's wait list */ bool PlatformProcess::isPaused() const { for (auto &thread : threads()) { if (!thread->isPaused()) { return false; } } return true; } /** * @brief PlatformProcess::patches * @return any patches applied to this process */ QMap PlatformProcess::patches() const { return patches_; } /** * @brief PlatformProcess::entry_point * @return */ edb::address_t PlatformProcess::entryPoint() const { QFile auxv(QString("/proc/%1/auxv").arg(pid_)); if (auxv.open(QIODevice::ReadOnly)) { if (edb::v1::debuggeeIs64Bit()) { elf64_auxv_t entry; while (auxv.read(reinterpret_cast(&entry), sizeof(entry))) { if (entry.a_type == AT_ENTRY) { return entry.a_un.a_val; } } } else if (edb::v1::debuggeeIs32Bit()) { elf32_auxv_t entry; while (auxv.read(reinterpret_cast(&entry), sizeof(entry))) { if (entry.a_type == AT_ENTRY) { return entry.a_un.a_val; } } } } return edb::address_t{}; } /** * @brief get_program_headers * @param process * @param phdr_memaddr * @param num_phdr * @return */ bool get_program_headers(const IProcess *process, edb::address_t *phdr_memaddr, int *num_phdr) { *phdr_memaddr = edb::address_t{}; *num_phdr = 0; QFile auxv(QString("/proc/%1/auxv").arg(process->pid())); if (auxv.open(QIODevice::ReadOnly)) { if (edb::v1::debuggeeIs64Bit()) { elf64_auxv_t entry; while (auxv.read(reinterpret_cast(&entry), sizeof(entry))) { switch (entry.a_type) { case AT_PHDR: *phdr_memaddr = entry.a_un.a_val; break; case AT_PHNUM: *num_phdr = entry.a_un.a_val; break; } } } else if (edb::v1::debuggeeIs32Bit()) { elf32_auxv_t entry; while (auxv.read(reinterpret_cast(&entry), sizeof(entry))) { switch (entry.a_type) { case AT_PHDR: *phdr_memaddr = entry.a_un.a_val; break; case AT_PHNUM: *num_phdr = entry.a_un.a_val; break; } } } } return (*phdr_memaddr != 0 && *num_phdr != 0); } /** * @brief get_debug_pointer * @param process * @param phdr_memaddr * @param count * @param relocation * @return */ template edb::address_t get_debug_pointer(const IProcess *process, edb::address_t phdr_memaddr, int count, edb::address_t relocation) { using elf_phdr = typename Model::elf_phdr; elf_phdr phdr; for (int i = 0; i < count; ++i) { if (process->readBytes(phdr_memaddr + i * sizeof(elf_phdr), &phdr, sizeof(elf_phdr))) { if (phdr.p_type == PT_DYNAMIC) { try { auto buf = std::make_unique(phdr.p_memsz); if (process->readBytes(phdr.p_vaddr + relocation, &buf[0], phdr.p_memsz)) { auto dynamic = reinterpret_cast(&buf[0]); while (dynamic->d_tag != DT_NULL) { if (dynamic->d_tag == DT_DEBUG) { return dynamic->d_un.d_val; } ++dynamic; } } } catch (const std::bad_alloc &) { qDebug() << "[get_debug_pointer] no more memory"; return 0; } } } } return 0; } /** * @brief get_relocation * @param process * @param phdr_memaddr * @param i * @return */ template edb::address_t get_relocation(const IProcess *process, edb::address_t phdr_memaddr, int i) { using elf_phdr = typename Model::elf_phdr; elf_phdr phdr; if (process->readBytes(phdr_memaddr + i * sizeof(elf_phdr), &phdr, sizeof(elf_phdr))) { if (phdr.p_type == PT_PHDR) { return phdr_memaddr - phdr.p_vaddr; } } return -1; } /** * attempts to locate the ELF debug pointer in the target process and returns * it, 0 of not found * * @brief PlatformProcess::debug_pointer * @return */ edb::address_t PlatformProcess::debugPointer() const { // NOTE(eteran): some of this code is from or inspired by code in // gdb/gdbserver/linux-low.c edb::address_t phdr_memaddr; int num_phdr; if (get_program_headers(this, &phdr_memaddr, &num_phdr)) { /* Compute relocation: it is expected to be 0 for "regular" executables, * non-zero for PIE ones. */ edb::address_t relocation = -1; for (int i = 0; relocation == -1 && i < num_phdr; i++) { if (edb::v1::debuggeeIs64Bit()) { relocation = get_relocation>(this, phdr_memaddr, i); } else if (edb::v1::debuggeeIs32Bit()) { relocation = get_relocation>(this, phdr_memaddr, i); } } if (relocation == -1) { /* PT_PHDR is optional, but necessary for PIE in general. * Fortunately any real world executables, including PIE * executables, have always PT_PHDR present. PT_PHDR is not * present in some shared libraries or in fpc (Free Pascal 2.4) * binaries but neither of those have a need for or present * DT_DEBUG anyway (fpc binaries are statically linked). * * Therefore if there exists DT_DEBUG there is always also PT_PHDR. * * GDB could find RELOCATION also from AT_ENTRY - e_entry. */ return 0; } if (edb::v1::debuggeeIs64Bit()) { return get_debug_pointer>(this, phdr_memaddr, num_phdr, relocation); } else if (edb::v1::debuggeeIs32Bit()) { return get_debug_pointer>(this, phdr_memaddr, num_phdr, relocation); } } return edb::address_t{}; } /** * @brief PlatformProcess::calculateMain * @return */ edb::address_t PlatformProcess::calculateMain() const { if (edb::v1::debuggeeIs64Bit()) { ByteShiftArray ba(14); edb::address_t entry_point = this->entryPoint(); for (int i = 0; i < 50; ++i) { uint8_t byte; if (readBytes(entry_point + i, &byte, sizeof(byte))) { ba << byte; edb::address_t address = 0; if (ba.size() >= 13) { // beginning of a call preceeded by a 64-bit mov and followed by a hlt if (ba[0] == 0x48 && ba[1] == 0xc7 && ba[7] == 0xe8 && ba[12] == 0xf4) { // Seems that this 64-bit mov still has a 32-bit immediate address = *reinterpret_cast(ba.data() + 3) & 0xffffffff; } // same heuristic except for PIC binaries else if (ba.size() >= 14 && ba[0] == 0x48 && ba[1] == 0x8d && ba[2] == 0x3d && ba[7] == 0xFF && ba[8] == 0x15 && ba[13] == 0xf4) { // It's signed relative! auto rel = *reinterpret_cast(ba.data() + 3); // ba[0] is entry_point + i - 13. instruction is 7 bytes long. address = rel + entry_point + i - 13 + 7; } if (address) { // TODO: make sure that this address resides in an executable region qDebug() << "No main symbol found, calculated it to be " << edb::v1::format_pointer(address) << " using heuristic"; return address; } } } else { break; } } } else if (edb::v1::debuggeeIs32Bit()) { ByteShiftArray ba(11); edb::address_t entry_point = this->entryPoint(); for (int i = 0; i < 50; ++i) { uint8_t byte; if (readBytes(entry_point + i, &byte, sizeof(byte))) { ba << byte; if (ba.size() >= 11) { // beginning of a call preceeded by a push and followed by a hlt if (ba[0] == 0x68 && ba[5] == 0xe8 && ba[10] == 0xf4) { edb::address_t address(0); auto to = reinterpret_cast(&address); std::memcpy(to, ba.data() + 1, sizeof(uint32_t)); // TODO: make sure that this address resides in an executable region qDebug() << "No main symbol found, calculated it to be " << edb::v1::format_pointer(address) << " using heuristic"; return address; } } } else { break; } } } return 0; } /** * @brief PlatformProcess::stardardInput * @return */ QString PlatformProcess::stardardInput() const { return input_; } /** * @brief PlatformProcess::stardardOutput * @return */ QString PlatformProcess::stardardOutput() const { return output_; } } edb-debugger/plugins/DebuggerCore/unix/linux/PlatformProcess.h0000644000175000017500000000635213765535463024373 0ustar eteraneteran/* Copyright (C) 2015 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PLATFORM_PROCESS_H_20150517_ #define PLATFORM_PROCESS_H_20150517_ #include "IProcess.h" #include "Status.h" #include #include namespace DebuggerCorePlugin { class DebuggerCore; class PlatformProcess final : public IProcess { Q_DECLARE_TR_FUNCTIONS(PlatformProcess) friend class PlatformThread; public: PlatformProcess(DebuggerCore *core, edb::pid_t pid); ~PlatformProcess() override = default; PlatformProcess(const PlatformProcess &) = delete; PlatformProcess &operator=(const PlatformProcess &) = delete; public: QDateTime startTime() const override; QList arguments() const override; QString currentWorkingDirectory() const override; QString executable() const override; QString stardardInput() const override; QString stardardOutput() const override; edb::pid_t pid() const override; std::shared_ptr parent() const override; edb::address_t codeAddress() const override; edb::address_t dataAddress() const override; edb::address_t entryPoint() const override; QList> regions() const override; QList> threads() const override; std::shared_ptr currentThread() const override; void setCurrentThread(IThread &thread) override; edb::uid_t uid() const override; QString user() const override; QString name() const override; QList loadedModules() const override; public: edb::address_t debugPointer() const override; edb::address_t calculateMain() const override; public: Status pause() override; Status resume(edb::EventStatus status) override; Status step(edb::EventStatus status) override; bool isPaused() const override; public: std::size_t writeBytes(edb::address_t address, const void *buf, size_t len) override; std::size_t patchBytes(edb::address_t address, const void *buf, size_t len) override; std::size_t readBytes(edb::address_t address, void *buf, size_t len) const override; std::size_t readPages(edb::address_t address, void *buf, size_t count) const override; QMap patches() const override; private: bool ptracePoke(edb::address_t address, long value); long ptracePeek(edb::address_t address, bool *ok) const; uint8_t ptraceReadByte(edb::address_t address, bool *ok) const; void ptraceWriteByte(edb::address_t address, uint8_t value, bool *ok); private: DebuggerCore *core_ = nullptr; edb::pid_t pid_; std::shared_ptr readOnlyMemFile_; std::shared_ptr readWriteMemFile_; QMap patches_; QString input_; QString output_; }; } #endif edb-debugger/plugins/DebuggerCore/unix/linux/FeatureDetect.cpp0000644000175000017500000000732313765535463024326 0ustar eteraneteran/* Copyright (C) 2016 - 2016 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "FeatureDetect.h" #include "version.h" #include #include #include #include #include #include #include #include namespace DebuggerCorePlugin { namespace feature { namespace { // Custom class to work with files, since various wrappers // appear to be unreliable to check whether writes succeeded class File { public: explicit File(const std::string &filename) { fd_ = ::open(filename.c_str(), O_RDWR); success_ = fd_ != -1; } ssize_t write(const void *buf, size_t count) { const ssize_t result = ::write(fd_, buf, count); success_ = result != -1; return result; } ssize_t read(void *buf, size_t count) { const ssize_t result = ::read(fd_, buf, count); success_ = result != -1; return result; } off_t seekp(size_t offset) { const off_t result = ::lseek(fd_, offset, SEEK_SET); success_ = result != -1; return result; } ~File() { close(fd_); } explicit operator bool() { return success_; } private: int fd_ = -1; bool success_ = false; }; /** * @brief kill_child * @param pid */ void kill_child(int pid) { if (kill(pid, SIGKILL) == -1) { perror("failed to kill child"); } } } /** * detects whether or not reads/writes through /proc//mem work correctly * * @brief detect_proc_access * @param read_broken * @param write_broken * @return */ bool detect_proc_access(bool *read_broken, bool *write_broken) { switch (pid_t pid = fork()) { case 0: if (ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) { perror("child: PTRACE_TRACEME failed"); abort(); } // force a signal raise(SIGCONT); for (;;) { sleep(10); } abort(); case -1: perror("fork"); return false; default: { int status; if (waitpid(pid, &status, __WALL) == -1) { perror("parent: waitpid failed"); kill_child(pid); return false; } if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGCONT) { std::cerr << "unexpected status returned by waitpid: 0x" << std::hex << status << "\n"; kill_child(pid); return false; } File file("/proc/" + std::to_string(pid) + "/mem"); if (!file) { perror("failed to open memory file"); kill_child(pid); return false; } const auto pageAlignMask = ~(sysconf(_SC_PAGESIZE) - 1); const auto addr = reinterpret_cast(&edb::version) & pageAlignMask; file.seekp(addr); if (!file) { perror("failed to seek to address to read"); kill_child(pid); return false; } int buf = 0x12345678; { file.read(&buf, sizeof(buf)); if (!file) { *read_broken = true; *write_broken = true; kill_child(pid); return false; } } file.seekp(addr); if (!file) { perror("failed to seek to address to write"); kill_child(pid); return false; } { file.write(&buf, sizeof(buf)); if (!file) { *read_broken = false; *write_broken = true; } else { *read_broken = false; *write_broken = false; } } kill_child(pid); return true; } } } } } edb-debugger/plugins/DebuggerCore/unix/linux/PlatformThread.h0000644000175000017500000000464713765535463024171 0ustar eteraneteran/* Copyright (C) 2015 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PLATFORM_THREAD_H_20151013_ #define PLATFORM_THREAD_H_20151013_ #include "IBreakpoint.h" #include "IThread.h" #include #include class IProcess; namespace DebuggerCorePlugin { class DebuggerCore; class PlatformState; class PlatformThread final : public IThread { Q_DECLARE_TR_FUNCTIONS(PlatformThread) friend class DebuggerCore; public: PlatformThread(DebuggerCore *core, std::shared_ptr &process, edb::tid_t tid); ~PlatformThread() override = default; PlatformThread(const PlatformThread &) = delete; PlatformThread &operator=(const PlatformThread &) = delete; public: edb::tid_t tid() const override; QString name() const override; int priority() const override; edb::address_t instructionPointer() const override; QString runState() const override; public: void getState(State *state) override; void setState(const State &state) override; public: Status step() override; Status step(edb::EventStatus status) override; Status resume() override; Status resume(edb::EventStatus status) override; public: bool isPaused() const override; private: void fillSegmentBases(PlatformState *state); bool fillStateFromPrStatus(PlatformState *state); bool fillStateFromSimpleRegs(PlatformState *state); #if defined(EDB_ARM32) bool fillStateFromVFPRegs(PlatformState *state); #endif private: unsigned long getDebugRegister(std::size_t n); long setDebugRegister(std::size_t n, unsigned long value); private: DebuggerCore *core_ = nullptr; std::shared_ptr process_; edb::tid_t tid_; int status_ = 0; #if defined(EDB_ARM32) || defined(EDB_ARM64) private: Status doStep(edb::tid_t tid, long status); std::shared_ptr singleStepBreakpoint; #endif }; } #endif edb-debugger/plugins/DebuggerCore/unix/linux/DialogMemoryAccess.ui0000644000175000017500000000751313765535463025150 0ustar eteraneteran DebuggerCorePlugin::DialogMemoryAccess 0 0 360 276 Memory Access Error true <html><head/><body><p>EDB has detected that on this kernel, access to debugee memory <br/>does not work through <span style=" font-family:'Courier New,courier';">/proc/&lt;pid&gt;/mem</span>. </p><p>Possible causes include: </p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">EDB's feature detection has a bug</li></ul><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The kernel is older and restricts writes to <span style=" font-family:'Courier New,courier';">/proc/&lt;pid&gt;/mem</span></li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The kernel has <span style=" font-weight:600;">grsecurity</span> enabled</li></ul><p>EDB has a fallback method of accessing debugee memory, <br/>but it may have a negative impact on performance. <br/>If you experience poor performance, please file a bug report at:</p><p><a href="http://github.com/eteran/edb-debugger/issues"><span style=" text-decoration: underline; color:#2980b9;">http://github.com/eteran/edb-debugger/issues</span></a>.</p></body></html> Qt::RichText false Never show this message again true Qt::Horizontal QDialogButtonBox::Ok buttonBox accepted() DebuggerCorePlugin::DialogMemoryAccess accept() 248 254 157 274 buttonBox rejected() DebuggerCorePlugin::DialogMemoryAccess reject() 316 260 286 274 edb-debugger/plugins/DebuggerCore/unix/linux/PlatformEvent.h0000644000175000017500000000357313765535463024040 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PLATFORM_EVENT_H_20121005_ #define PLATFORM_EVENT_H_20121005_ #include "IDebugEvent.h" #include #include // for the SIG* definitions namespace DebuggerCorePlugin { class PlatformEvent final : public IDebugEvent { Q_DECLARE_TR_FUNCTIONS(PlatformEvent) friend class DebuggerCore; public: PlatformEvent() = default; private: PlatformEvent(const PlatformEvent &) = default; PlatformEvent &operator=(const PlatformEvent &) = default; public: IDebugEvent *clone() const override; public: Message errorDescription() const override; REASON reason() const override; TRAP_REASON trapReason() const override; bool exited() const override; bool isError() const override; bool isKill() const override; bool isStop() const override; bool isTrap() const override; bool stopped() const override; bool terminated() const override; edb::pid_t process() const override; edb::tid_t thread() const override; int64_t code() const override; private: static IDebugEvent::Message createUnexpectedSignalMessage(const QString &name, int number); private: siginfo_t siginfo_ = {}; edb::pid_t pid_ = 0; edb::tid_t tid_ = 0; int status_ = 0; }; } #endif edb-debugger/plugins/DebuggerCore/unix/linux/DialogMemoryAccess.cpp0000644000175000017500000000224313765535463025310 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "DialogMemoryAccess.h" namespace DebuggerCorePlugin { /** * @brief DialogMemoryAccess::DialogMemoryAccess * @param parent * @param f */ DialogMemoryAccess::DialogMemoryAccess(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f) { ui.setupUi(this); adjustSize(); setFixedSize(width(), height()); } /** * @brief DialogMemoryAccess::warnNextTime * @return */ bool DialogMemoryAccess::warnNextTime() const { return !ui.checkNeverShowAgain->isChecked(); } } edb-debugger/plugins/DebuggerCore/unix/linux/PlatformCommon.cpp0000644000175000017500000001077513765535463024544 0ustar eteraneteran/* Copyright (C) 2015 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "PlatformCommon.h" #include #include #include #include namespace DebuggerCorePlugin { /** * @brief resume_code * @param status * @return */ int resume_code(int status) { if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) { return 0; } if (WIFSIGNALED(status)) { return WTERMSIG(status); } if (WIFSTOPPED(status)) { return WSTOPSIG(status); } return 0; } /** * gets the contents of /proc//stat * * @brief get_user_stat * @param path * @param user_stat * @return the number of elements successfully parsed */ int get_user_stat(const char *path, struct user_stat *user_stat) { Q_ASSERT(user_stat); std::ifstream stream(path); std::string line; if (std::getline(stream, line)) { // the comm field is wrapped with "(" and ")", so we look for the closing one size_t left = line.find_first_of('('); size_t right = line.find_last_of(')'); if (right == std::string::npos || left == std::string::npos) { return -1; } int r = sscanf(&line[right + 2], "%c %d %d %d %d %d %u %llu %llu %llu %llu %llu %llu %lld %lld %lld %lld %lld %lld %llu %llu %lld %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %d %d %u %u %llu %llu %lld %llu %llu %llu %llu %llu %llu %llu %d", &user_stat->state, &user_stat->ppid, &user_stat->pgrp, &user_stat->session, &user_stat->tty_nr, &user_stat->tpgid, &user_stat->flags, &user_stat->minflt, &user_stat->cminflt, &user_stat->majflt, &user_stat->cmajflt, &user_stat->utime, &user_stat->stime, &user_stat->cutime, &user_stat->cstime, &user_stat->priority, &user_stat->nice, &user_stat->num_threads, &user_stat->itrealvalue, &user_stat->starttime, &user_stat->vsize, &user_stat->rss, &user_stat->rsslim, &user_stat->startcode, &user_stat->endcode, &user_stat->startstack, &user_stat->kstkesp, &user_stat->kstkeip, &user_stat->signal, &user_stat->blocked, &user_stat->sigignore, &user_stat->sigcatch, &user_stat->wchan, &user_stat->nswap, &user_stat->cnswap, &user_stat->exit_signal, &user_stat->processor, &user_stat->rt_priority, &user_stat->policy, // Linux 2.6.18 &user_stat->delayacct_blkio_ticks, // Linux 2.6.24 &user_stat->guest_time, &user_stat->cguest_time, // Linux 3.3 &user_stat->start_data, &user_stat->end_data, &user_stat->start_brk, // Linux 3.5 &user_stat->arg_start, &user_stat->arg_end, &user_stat->env_start, &user_stat->env_end, &user_stat->exit_code); // fill in the pid r += sscanf(&line[0], "%d", &user_stat->pid); // fill in the comm field const size_t len = std::min(sizeof(user_stat->comm), (right - left) - 1); line.copy(user_stat->comm, len, left + 1); user_stat->comm[len] = '\0'; ++r; return r; } return -1; } /** * gets the contents of /proc//stat * * @brief get_user_stat * @param pid * @param user_stat * @return the number of elements or -1 on error */ int get_user_stat(edb::pid_t pid, struct user_stat *user_stat) { char path[PATH_MAX]; snprintf(path, sizeof(path), "/proc/%d/stat", pid); return get_user_stat(path, user_stat); } /** * gets the contents of /proc//task//stat * * @brief get_user_task_stat * @param pid * @param tid * @param user_stat * @return the number of elements or -1 on error */ int get_user_task_stat(edb::pid_t pid, edb::tid_t tid, struct user_stat *user_stat) { char path[PATH_MAX]; snprintf(path, sizeof(path), "/proc/%d/task/%d/stat", pid, tid); return get_user_stat(path, user_stat); } } edb-debugger/plugins/DebuggerCore/unix/linux/PlatformRegion.h0000644000175000017500000000374313765535463024201 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PLATFORM_REGION_H_20120330_ #define PLATFORM_REGION_H_20120330_ #include "IRegion.h" #include #include namespace DebuggerCorePlugin { class PlatformRegion final : public IRegion { Q_DECLARE_TR_FUNCTIONS(PlatformRegion) template friend class BackupInfo; public: PlatformRegion(edb::address_t start, edb::address_t end, edb::address_t base, const QString &name, permissions_t permissions); ~PlatformRegion() override = default; public: IRegion *clone() const override; public: bool accessible() const override; bool readable() const override; bool writable() const override; bool executable() const override; size_t size() const override; public: void setPermissions(bool read, bool write, bool execute) override; void setStart(edb::address_t address) override; void setEnd(edb::address_t address) override; public: edb::address_t start() const override; edb::address_t end() const override; edb::address_t base() const override; QString name() const override; permissions_t permissions() const override; private: void setPermissions(bool read, bool write, bool execute, edb::address_t temp_address); private: edb::address_t start_; edb::address_t end_; edb::address_t base_; QString name_; permissions_t permissions_; }; } #endif edb-debugger/plugins/DebuggerCore/unix/osx/0000755000175000017500000000000013765535463020543 5ustar eteraneteranedb-debugger/plugins/DebuggerCore/unix/osx/DebuggerCore.cpp0000644000175000017500000005502413765535463023612 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "DebuggerCore.h" #include "PlatformEvent.h" #include "PlatformRegion.h" #include "PlatformState.h" #include "State.h" #include "string_hash.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace DebuggerCore { namespace { int resume_code(int status) { if (WIFSIGNALED(status)) { return WTERMSIG(status); } else if (WIFSTOPPED(status)) { return WSTOPSIG(status); } return 0; } } //------------------------------------------------------------------------------ // Name: DebuggerCore // Desc: constructor //------------------------------------------------------------------------------ DebuggerCore::DebuggerCore() { page_size_ = 0x1000; } //------------------------------------------------------------------------------ // Name: ~DebuggerCore // Desc: //------------------------------------------------------------------------------ DebuggerCore::~DebuggerCore() { detach(); } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ bool DebuggerCore::has_extension(quint64 ext) const { return false; } //------------------------------------------------------------------------------ // Name: page_size // Desc: returns the size of a page on this system //------------------------------------------------------------------------------ size_t DebuggerCore::page_size() const { return page_size_; } //------------------------------------------------------------------------------ // Name: wait_debug_event // Desc: waits for a debug event, secs is a timeout (but is not yet respected) // ok will be set to false if the timeout expires //------------------------------------------------------------------------------ std::shared_ptr DebuggerCore::wait_debug_event(int msecs) { if (attached()) { int status; bool timeout; const edb::tid_t tid = native::waitpid_timeout(pid(), &status, 0, msecs, &timeout); if (!timeout) { if (tid > 0) { // normal event auto e = std::make_shared(); e->pid = pid(); e->tid = tid; e->status = status; active_thread_ = tid; threads_[tid].status = status; return e; } } } return nullptr; } //------------------------------------------------------------------------------ // Name: read_data // Desc: //------------------------------------------------------------------------------ long DebuggerCore::read_data(edb::address_t address, bool *ok) { Q_ASSERT(ok); mach_port_t task; kern_return_t err = task_for_pid(mach_task_self(), pid(), &task); if (err != KERN_SUCCESS) { qDebug("task_for_pid() failed with %x [%d]", err, pid()); *ok = false; return -1; } long x; vm_size_t size; *ok = vm_read_overwrite(task, address, sizeof(long), (vm_address_t)&x, &size) == 0; return x; } //------------------------------------------------------------------------------ // Name: write_data // Desc: //------------------------------------------------------------------------------ bool DebuggerCore::write_data(edb::address_t address, long value) { return ptrace(PT_WRITE_D, pid(), (char *)address, value) != -1; } //------------------------------------------------------------------------------ // Name: attach // Desc: //------------------------------------------------------------------------------ bool DebuggerCore::attach(edb::pid_t pid) { detach(); const long ret = ptrace(PT_ATTACH, pid, 0, 0); if (ret == 0) { pid_ = pid; active_thread_ = pid; threads_.clear(); threads_.insert(pid, thread_info()); // TODO: attach to all of the threads } return ret == 0; } //------------------------------------------------------------------------------ // Name: detach // Desc: //------------------------------------------------------------------------------ void DebuggerCore::detach() { if (attached()) { // TODO: do i need to stop each thread first, and wait for them? clear_breakpoints(); for (auto it = threads_.begin(); it != threads_.end(); ++it) { ptrace(PT_DETACH, it.key(), 0, 0); } pid_ = 0; threads_.clear(); } } //------------------------------------------------------------------------------ // Name: kill // Desc: //------------------------------------------------------------------------------ void DebuggerCore::kill() { if (attached()) { clear_breakpoints(); ptrace(PT_KILL, pid(), 0, 0); native::waitpid(pid(), 0, WAIT_ANY); pid_ = 0; threads_.clear(); } } //------------------------------------------------------------------------------ // Name: pause // Desc: stops *all* threads of a process //------------------------------------------------------------------------------ void DebuggerCore::pause() { if (attached()) { for (auto it = threads_.begin(); it != threads_.end(); ++it) { ::kill(it.key(), SIGSTOP); } } } //------------------------------------------------------------------------------ // Name: resume // Desc: //------------------------------------------------------------------------------ void DebuggerCore::resume(edb::EVENT_STATUS status) { // TODO: assert that we are paused if (attached()) { if (status != edb::DEBUG_STOP) { const edb::tid_t tid = active_thread(); const int code = (status == edb::DEBUG_EXCEPTION_NOT_HANDLED) ? resume_code(threads_[tid].status) : 0; ptrace(PT_CONTINUE, tid, reinterpret_cast(1), code); } } } //------------------------------------------------------------------------------ // Name: step // Desc: //------------------------------------------------------------------------------ void DebuggerCore::step(edb::EVENT_STATUS status) { // TODO: assert that we are paused if (attached()) { if (status != edb::DEBUG_STOP) { const edb::tid_t tid = active_thread(); const int code = (status == edb::DEBUG_EXCEPTION_NOT_HANDLED) ? resume_code(threads_[tid].status) : 0; ptrace(PT_STEP, tid, reinterpret_cast(1), code); } } } //------------------------------------------------------------------------------ // Name: get_state // Desc: //------------------------------------------------------------------------------ void DebuggerCore::get_state(State *state) { Q_ASSERT(state); // TODO: assert that we are paused auto state_impl = static_cast(state->impl_); if (attached()) { /* Get the mach task for the target process */ mach_port_t task; kern_return_t err = task_for_pid(mach_task_self(), pid(), &task); if (err != KERN_SUCCESS) { qDebug("task_for_pid() failed with %x [%d]", err, pid()); return; } /* Suspend the target process */ err = task_suspend(task); if (err != KERN_SUCCESS) { qDebug("task_suspend() failed"); return; } /* Get all threads in the specified task */ thread_act_port_array_t thread_list; mach_msg_type_number_t thread_count; err = task_threads(task, &thread_list, &thread_count); if (err != KERN_SUCCESS) { qDebug("task_threads() failed"); err = task_resume(task); if (err != KERN_SUCCESS) { qDebug("task_resume() failed"); } } Q_ASSERT(thread_count > 0); #ifdef EDB_X86 mach_msg_type_number_t state_count = x86_THREAD_STATE32_COUNT; const thread_state_flavor_t flavor = x86_THREAD_STATE32; const thread_state_flavor_t debug_flavor = x86_DEBUG_STATE32; const thread_state_flavor_t fpu_flavor = x86_FLOAT_STATE32; const thread_state_flavor_t exception_flavor = x86_EXCEPTION_STATE32; #elif defined(EDB_X86_64) mach_msg_type_number_t state_count = x86_THREAD_STATE64_COUNT; const thread_state_flavor_t flavor = x86_THREAD_STATE64; const thread_state_flavor_t debug_flavor = x86_DEBUG_STATE64; const thread_state_flavor_t fpu_flavor = x86_FLOAT_STATE64; const thread_state_flavor_t exception_flavor = x86_EXCEPTION_STATE64; #endif // TODO Get all threads, not just the first one. err = thread_get_state( thread_list[0], flavor, (thread_state_t)&state_impl->thread_state_, &state_count); if (err != KERN_SUCCESS) { qDebug("thread_get_state() failed with %.08x", err); err = task_resume(task); if (err != KERN_SUCCESS) { qDebug("task_resume() failed"); } return; } err = thread_get_state( thread_list[0], debug_flavor, (thread_state_t)&state_impl->debug_state_, &state_count); if (err != KERN_SUCCESS) { qDebug("thread_get_state() failed with %.08x", err); err = task_resume(task); if (err != KERN_SUCCESS) { qDebug("task_resume() failed"); } return; } err = thread_get_state( thread_list[0], fpu_flavor, (thread_state_t)&state_impl->float_state_, &state_count); if (err != KERN_SUCCESS) { qDebug("thread_get_state() failed with %.08x", err); err = task_resume(task); if (err != KERN_SUCCESS) { qDebug("task_resume() failed"); } return; } err = thread_get_state( thread_list[0], exception_flavor, (thread_state_t)&state_impl->exception_state_, &state_count); if (err != KERN_SUCCESS) { qDebug("thread_get_state() failed with %.08x", err); err = task_resume(task); if (err != KERN_SUCCESS) { qDebug("task_resume() failed"); } return; } } else { state->clear(); } } //------------------------------------------------------------------------------ // Name: set_state // Desc: //------------------------------------------------------------------------------ void DebuggerCore::set_state(const State &state) { // TODO: assert that we are paused auto state_impl = static_cast(state.impl_); if (attached()) { /* Get the mach task for the target process */ mach_port_t task; kern_return_t err = task_for_pid(mach_task_self(), pid(), &task); if (err != KERN_SUCCESS) { qDebug("task_for_pid() failed with %x [%d]", err, pid()); return; } /* Suspend the target process */ err = task_suspend(task); if (err != KERN_SUCCESS) { qDebug("task_suspend() failed"); } /* Get all threads in the specified task */ thread_act_port_array_t thread_list; mach_msg_type_number_t thread_count; err = task_threads(task, &thread_list, &thread_count); if (err != KERN_SUCCESS) { qDebug("task_threads() failed"); err = task_resume(task); if (err != KERN_SUCCESS) { qDebug("task_resume() failed"); } } Q_ASSERT(thread_count > 0); #ifdef EDB_X86 mach_msg_type_number_t state_count = x86_THREAD_STATE32_COUNT; const thread_state_flavor_t flavor = x86_THREAD_STATE32; const thread_state_flavor_t debug_flavor = x86_DEBUG_STATE32; //const thread_state_flavor_t fpu_flavor = x86_FLOAT_STATE32; //const thread_state_flavor_t exception_flavor = x86_EXCEPTION_STATE32; #elif defined(EDB_X86_64) mach_msg_type_number_t state_count = x86_THREAD_STATE64_COUNT; const thread_state_flavor_t flavor = x86_THREAD_STATE64; const thread_state_flavor_t debug_flavor = x86_DEBUG_STATE64; //const thread_state_flavor_t fpu_flavor = x86_FLOAT_STATE64; //const thread_state_flavor_t exception_flavor = x86_EXCEPTION_STATE64; #endif // TODO Set for specific thread, not first one err = thread_set_state( thread_list[0], flavor, (thread_state_t)&state_impl->thread_state_, state_count); if (err != KERN_SUCCESS) { qDebug("thread_set_state() failed with %.08x", err); err = task_resume(task); if (err != KERN_SUCCESS) { qDebug("task_resume() failed"); } return; } err = thread_set_state( thread_list[0], debug_flavor, (thread_state_t)&state_impl->debug_state_, state_count); if (err != KERN_SUCCESS) { qDebug("thread_set_state() failed with %.08x", err); err = task_resume(task); if (err != KERN_SUCCESS) { qDebug("task_resume() failed"); } return; } } } //------------------------------------------------------------------------------ // Name: open // Desc: //------------------------------------------------------------------------------ bool DebuggerCore::open(const QString &path, const QString &cwd, const QList &args, const QString &tty) { detach(); pid_t pid; switch (pid = fork()) { case 0: // we are in the child now... // set ourselves (the child proc) up to be traced ptrace(PT_TRACE_ME, 0, 0, 0); // redirect it's I/O if (!tty.isEmpty()) { FILE *const std_out = freopen(qPrintable(tty), "r+b", stdout); FILE *const std_in = freopen(qPrintable(tty), "r+b", stdin); FILE *const std_err = freopen(qPrintable(tty), "r+b", stderr); Q_UNUSED(std_out) Q_UNUSED(std_in) Q_UNUSED(std_err) } // do the actual exec execute_process(path, cwd, args); // we should never get here! abort(); break; case -1: // error! pid_ = 0; return false; default: // parent do { threads_.clear(); int status; if (native::waitpid(pid, &status, 0) == -1) { return false; } // the very first event should be a STOP of type SIGTRAP if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP) { detach(); return false; } // setup the first event data for the primary thread threads_.insert(pid, thread_info()); pid_ = pid; active_thread_ = pid; threads_[pid].status = status; return true; } while (0); break; } } //------------------------------------------------------------------------------ // Name: set_active_thread // Desc: //------------------------------------------------------------------------------ void DebuggerCore::set_active_thread(edb::tid_t tid) { Q_ASSERT(threads_.contains(tid)); active_thread_ = tid; } //------------------------------------------------------------------------------ // Name: create_state // Desc: //------------------------------------------------------------------------------ std::unique_ptr DebuggerCore::create_state() const { return std::make_unique(); } //------------------------------------------------------------------------------ // Name: enumerate_processes // Desc: //------------------------------------------------------------------------------ QMap DebuggerCore::enumerate_processes() const { QMap ret; static const int name[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0}; size_t length = 0; sysctl(const_cast(name), (sizeof(name) / sizeof(*name)) - 1, 0, &length, 0, 0); auto proc_info = static_cast(malloc(length)); sysctl(const_cast(name), (sizeof(name) / sizeof(*name)) - 1, proc_info, &length, 0, 0); size_t count = length / sizeof(struct kinfo_proc); for (size_t i = 0; i < count; ++i) { ProcessInfo procInfo; procInfo.pid = proc_info[i].kp_proc.p_pid; procInfo.uid = proc_info[i].kp_eproc.e_ucred.cr_uid; procInfo.name = proc_info[i].kp_proc.p_comm; ret.insert(procInfo.pid, procInfo); } free(proc_info); return ret; } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ QString DebuggerCore::process_exe(edb::pid_t pid) const { // TODO: implement this return QString(); } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ QString DebuggerCore::process_cwd(edb::pid_t pid) const { // TODO: implement this return QString(); } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ edb::pid_t DebuggerCore::parent_pid(edb::pid_t pid) const { // TODO: implement this return -1; } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ QList> DebuggerCore::memory_regions() const { #if 0 static const char *inheritance_strings[] = { "SHARE", "COPY", "NONE", "DONATE_COPY", }; static const char *behavior_strings[] = { "DEFAULT", "RANDOM", "SEQUENTIAL", "RESQNTL", "WILLNEED", "DONTNEED", }; #endif QList> regions; if (pid_ != 0) { task_t the_task; kern_return_t kr = task_for_pid(mach_task_self(), pid_, &the_task); if (kr != KERN_SUCCESS) { qDebug("task_for_pid failed"); return QList>(); } vm_size_t vmsize; vm_address_t address; vm_region_basic_info_data_64_t info; mach_msg_type_number_t info_count; vm_region_flavor_t flavor; memory_object_name_t object; kr = KERN_SUCCESS; address = 0; do { flavor = VM_REGION_BASIC_INFO_64; info_count = VM_REGION_BASIC_INFO_COUNT_64; kr = vm_region_64(the_task, &address, &vmsize, flavor, (vm_region_info_64_t)&info, &info_count, &object); if (kr == KERN_SUCCESS) { const edb::address_t start = address; const edb::address_t end = address + vmsize; const edb::address_t base = address; const QString name = QString(); const IRegion::permissions_t permissions = ((info.protection & VM_PROT_READ) ? PROT_READ : 0) | ((info.protection & VM_PROT_WRITE) ? PROT_WRITE : 0) | ((info.protection & VM_PROT_EXECUTE) ? PROT_EXEC : 0); regions.push_back(std::make_shared(start, end, base, name, permissions)); /* printf("%016llx-%016llx %8uK %c%c%c/%c%c%c %11s %6s %10s uwir=%hu sub=%u\n", address, (address + vmsize), (vmsize >> 10), (info.protection & VM_PROT_READ) ? 'r' : '-', (info.protection & VM_PROT_WRITE) ? 'w' : '-', (info.protection & VM_PROT_EXECUTE) ? 'x' : '-', (info.max_protection & VM_PROT_READ) ? 'r' : '-', (info.max_protection & VM_PROT_WRITE) ? 'w' : '-', (info.max_protection & VM_PROT_EXECUTE) ? 'x' : '-', inheritance_strings[info.inheritance], (info.shared) ? "shared" : "-", behavior_strings[info.behavior], info.user_wired_count, info.reserved); */ address += vmsize; } else if (kr != KERN_INVALID_ADDRESS) { if (the_task != MACH_PORT_NULL) { mach_port_deallocate(mach_task_self(), the_task); } return QList>(); } } while (kr != KERN_INVALID_ADDRESS); if (the_task != MACH_PORT_NULL) { mach_port_deallocate(mach_task_self(), the_task); } } return regions; } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ QList DebuggerCore::process_args(edb::pid_t pid) const { QList ret; if (pid != 0) { // TODO: assert attached! qDebug() << "TODO: implement edb::v1::get_process_args"; } return ret; } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ edb::address_t DebuggerCore::process_code_address() const { qDebug() << "TODO: implement DebuggerCore::process_code_address"; return 0; } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ edb::address_t DebuggerCore::process_data_address() const { qDebug() << "TODO: implement DebuggerCore::process_data_address"; return 0; } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ QList DebuggerCore::loaded_modules() const { QList modules; qDebug() << "TODO: implement DebuggerCore::loaded_modules"; return modules; } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ QDateTime DebuggerCore::process_start(edb::pid_t pid) const { qDebug() << "TODO: implement DebuggerCore::process_start"; return QDateTime(); } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ quint64 DebuggerCore::cpu_type() const { #ifdef EDB_X86 return edb::string_hash<'x', '8', '6'>::value; #elif defined(EDB_X86_64) return edb::string_hash<'x', '8', '6', '-', '6', '4'>::value; #endif } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ QString DebuggerCore::format_pointer(edb::address_t address) const { char buf[32]; #ifdef EDB_X86 qsnprintf(buf, sizeof(buf), "%08x", address); #elif defined(EDB_X86_64) qsnprintf(buf, sizeof(buf), "%016llx", address); #endif return buf; } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ QString DebuggerCore::stack_pointer() const { #ifdef EDB_X86 return "esp"; #elif defined(EDB_X86_64) return "rsp"; #endif } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ QString DebuggerCore::frame_pointer() const { #ifdef EDB_X86 return "ebp"; #elif defined(EDB_X86_64) return "rbp"; #endif } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ QString DebuggerCore::instruction_pointer() const { #ifdef EDB_X86 return "eip"; #elif defined(EDB_X86_64) return "rip"; #endif } } edb-debugger/plugins/DebuggerCore/unix/osx/DebuggerCore.h0000644000175000017500000000617613765535463023263 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DEBUGGER_CORE_H_20090529_ #define DEBUGGER_CORE_H_20090529_ #include "DebuggerCoreUNIX.h" #include namespace DebuggerCore { class DebuggerCore : public DebuggerCoreUNIX { Q_OBJECT Q_INTERFACES(IDebugger) Q_CLASSINFO("author", "Evan Teran") Q_CLASSINFO("url", "http://www.codef00.com") public: DebuggerCore(); ~DebuggerCore() override; public: size_t page_size() const override; bool has_extension(quint64 ext) const override; std::shared_ptr wait_debug_event(int msecs) override; bool attach(edb::pid_t pid) override; void detach() override; void kill() override; void pause() override; void resume(edb::EVENT_STATUS status) override; void step(edb::EVENT_STATUS status) override; void get_state(State *state) override; void set_state(const State &state) override; bool open(const QString &path, const QString &cwd, const QList &args, const QString &tty) override; public: // thread support stuff (optional) QList thread_ids() const override { return threads_.keys(); } edb::tid_t active_thread() const override { return active_thread_; } void set_active_thread(edb::tid_t) override; public: QList> memory_regions() const override; edb::address_t process_code_address() const override; edb::address_t process_data_address() const override; public: // process properties QList process_args(edb::pid_t pid) const override; QString process_exe(edb::pid_t pid) const override; QString process_cwd(edb::pid_t pid) const override; edb::pid_t parent_pid(edb::pid_t pid) const override; QDateTime process_start(edb::pid_t pid) const override; quint64 cpu_type() const override; public: std::unique_ptr create_state() const override; private: QMap enumerate_processes() const override; QList loaded_modules() const override; public: QString stack_pointer() const override; QString frame_pointer() const override; QString instruction_pointer() const override; public: QString format_pointer(edb::address_t address) const override; private: long read_data(edb::address_t address, bool *ok) override; bool write_data(edb::address_t address, long value) override; private: struct thread_info { public: thread_info() : status(0) { } thread_info(int s) : status(s) { } int status; }; using threadmap_t = QHash; size_t page_size_; threadmap_t threads_; }; } #endif edb-debugger/plugins/DebuggerCore/unix/osx/PlatformState.h0000644000175000017500000000431213765535463023501 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PLATFORM_STATE_H_20110330_ #define PLATFORM_STATE_H_20110330_ #include "IState.h" #include "Types.h" #include #include namespace DebuggerCore { class PlatformState : public IState { friend class DebuggerCore; public: PlatformState(); public: std::unique_ptr clone() const override; public: QString flagsToString() const override; QString flagsToString(edb::reg_t flags) const override; Register value(const QString ®) const override; edb::address_t frame_pointer() const override; edb::address_t instruction_pointer() const override; edb::address_t stack_pointer() const override; edb::reg_t debug_register(int n) const override; edb::reg_t flags() const override; long double fpu_register(int n) const override; void adjust_stack(int bytes) override; void clear() override; void set_debug_register(int n, edb::reg_t value) override; void set_flags(edb::reg_t flags) override; void set_instruction_pointer(edb::address_t value) override; void set_register(const QString &name, edb::reg_t value) override; quint64 mmx_register(int n) const override; QByteArray xmm_register(int n) const override; private: #if defined(EDB_X86) x86_thread_state32_t thread_state_; x86_float_state32_t float_state_; x86_debug_state32_t debug_state_; x86_exception_state32_t exception_state_; #elif defined(EDB_X86_64) x86_thread_state64_t thread_state_; x86_float_state64_t float_state_; x86_debug_state64_t debug_state_; x86_exception_state64_t exception_state_; #endif }; } #endif edb-debugger/plugins/DebuggerCore/unix/osx/PlatformEvent.cpp0000644000175000017500000001672413765535463024047 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "PlatformEvent.h" #include "edb.h" #include #include #include // for the SIG* definitions #include #include #include namespace DebuggerCore { //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ PlatformEvent::PlatformEvent() : status(0), pid(-1), tid(-1) { } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ PlatformEvent *PlatformEvent::clone() const { return new PlatformEvent(*this); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ IDebugEvent::Message PlatformEvent::error_description() const { Q_ASSERT(is_error()); // TODO: figure out the fault address const edb::address_t fault_address = 0; switch (code()) { case SIGSEGV: return Message( tr("Illegal Access Fault"), tr( "

The debugged application encountered a segmentation fault.
The address 0x%1 could not be accessed.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

") .arg(edb::v1::format_pointer(fault_address))); case SIGILL: return Message( tr("Illegal Instruction Fault"), tr( "

The debugged application attempted to execute an illegal instruction.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

")); case SIGFPE: // TODO: figure out the fault code for FPU stuff switch (0) { case FPE_INTDIV: return Message( tr("Divide By Zero"), tr( "

The debugged application tried to divide an integer value by an integer divisor of zero.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

")); default: return Message( tr("Floating Point Exception"), tr( "

The debugged application encountered a floating-point exception.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

")); } case SIGABRT: return Message( tr("Application Aborted"), tr( "

The debugged application has aborted.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

")); case SIGBUS: return Message( tr("Bus Error"), tr( "

The debugged application tried to read or write data that is misaligned.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

")); #ifdef SIGSTKFLT case SIGSTKFLT: return Message( tr("Stack Fault"), tr( "

The debugged application encountered a stack fault.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

")); #endif case SIGPIPE: return Message( tr("Broken Pipe Fault"), tr( "

The debugged application encountered a broken pipe fault.

" "

If you would like to pass this exception to the application press Shift+[F7/F8/F9]

")); default: return Message(); } } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ IDebugEvent::REASON PlatformEvent::reason() const { // this basically converts our value into a 'switchable' value for convenience if (stopped()) { return EVENT_STOPPED; } else if (terminated()) { return EVENT_TERMINATED; } else if (exited()) { return EVENT_EXITED; } else { return EVENT_UNKNOWN; } } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ IDebugEvent::TRAP_REASON PlatformEvent::trap_reason() const { // TODO: figure out how to detect if it is a step return TRAP_BREAKPOINT; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ bool PlatformEvent::exited() const { return WIFEXITED(status) != 0; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ bool PlatformEvent::is_error() const { if (stopped()) { switch (code()) { case SIGTRAP: case SIGSTOP: return false; case SIGSEGV: case SIGILL: case SIGFPE: case SIGABRT: case SIGBUS: #ifdef SIGSTKFLT case SIGSTKFLT: #endif case SIGPIPE: return true; default: return false; } } else { return false; } } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ bool PlatformEvent::is_kill() const { return stopped() && code() == SIGKILL; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ bool PlatformEvent::is_stop() const { return stopped() && code() == SIGSTOP; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ bool PlatformEvent::is_trap() const { return stopped() && code() == SIGTRAP; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ bool PlatformEvent::terminated() const { return WIFSIGNALED(status) != 0; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ bool PlatformEvent::stopped() const { return WIFSTOPPED(status) != 0; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ edb::pid_t PlatformEvent::process() const { return pid; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ edb::tid_t PlatformEvent::thread() const { return tid; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ int64_t PlatformEvent::code() const { if (stopped()) { return WSTOPSIG(status); } if (terminated()) { return WTERMSIG(status); } if (exited()) { return WEXITSTATUS(status); } return 0; } } edb-debugger/plugins/DebuggerCore/unix/osx/PlatformRegion.cpp0000644000175000017500000000442613765535463024205 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "PlatformRegion.h" #include "IDebugEventHandler.h" #include "IDebugger.h" #include "MemoryRegions.h" #include "State.h" #include "edb.h" #include #include #include namespace DebuggerCore { PlatformRegion::PlatformRegion(edb::address_t start, edb::address_t end, edb::address_t base, const QString &name, permissions_t permissions) : start_(start), end_(end), base_(base), name_(name), permissions_(permissions) { } IRegion *PlatformRegion::clone() const { return new PlatformRegion(start_, end_, base_, name_, permissions_); } bool PlatformRegion::accessible() const { return readable() || writable() || executable(); } bool PlatformRegion::readable() const { return (permissions_ & PROT_READ) != 0; } bool PlatformRegion::writable() const { return (permissions_ & PROT_WRITE) != 0; } bool PlatformRegion::executable() const { return (permissions_ & PROT_EXEC) != 0; } size_t PlatformRegion::size() const { return end_ - start_; } void PlatformRegion::set_permissions(bool read, bool write, bool execute) { Q_UNUSED(read) Q_UNUSED(write) Q_UNUSED(execute) } edb::address_t PlatformRegion::start() const { return start_; } edb::address_t PlatformRegion::end() const { return end_; } edb::address_t PlatformRegion::base() const { return base_; } QString PlatformRegion::name() const { return name_; } IRegion::permissions_t PlatformRegion::permissions() const { return permissions_; } void PlatformRegion::set_start(edb::address_t address) { start_ = address; } void PlatformRegion::set_end(edb::address_t address) { end_ = address; } } edb-debugger/plugins/DebuggerCore/unix/osx/PlatformProcess.cpp0000644000175000017500000000133513765535463024374 0ustar eteraneteran/* Copyright (C) 2015 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "PlatformProcess.h" edb-debugger/plugins/DebuggerCore/unix/osx/PlatformProcess.h0000644000175000017500000000152713765535463024044 0ustar eteraneteran/* Copyright (C) 2015 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PLATFORM_PROCESS_H_20150517_ #define PLATFORM_PROCESS_H_20150517_ #include "IProcess.h" class PlatformProcess : public IProcess { }; #endif edb-debugger/plugins/DebuggerCore/unix/osx/PlatformEvent.h0000644000175000017500000000304113765535463023500 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PLATFORM_EVENT_H_20121005_ #define PLATFORM_EVENT_H_20121005_ #include "IDebugEvent.h" #include namespace DebuggerCore { class PlatformEvent : IDebugEvent { Q_DECLARE_TR_FUNCTIONS(PlatformEvent) friend class DebuggerCore; public: PlatformEvent(); public: PlatformEvent *clone() const override; public: Message error_description() const override; REASON reason() const override; TRAP_REASON trap_reason() const override; bool exited() const override; bool is_error() const override; bool is_kill() const override; bool is_stop() const override; bool is_trap() const override; bool terminated() const override; bool stopped() const override; edb::pid_t process() const override; edb::tid_t thread() const override; int64_t code() const override; private: int status; edb::pid_t pid; edb::tid_t tid; }; } #endif edb-debugger/plugins/DebuggerCore/unix/osx/PlatformState.cpp0000644000175000017500000005435313765535463024046 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "PlatformState.h" #if __DARWIN_UNIX03 #define REG(x) __##x #else #define REG(x) x #endif namespace DebuggerCore { //------------------------------------------------------------------------------ // Name: PlatformState // Desc: //------------------------------------------------------------------------------ PlatformState::PlatformState() { memset(&thread_state_, 0, sizeof(thread_state_)); memset(&float_state_, 0, sizeof(float_state_)); memset(&debug_state_, 0, sizeof(debug_state_)); memset(&exception_state_, 0, sizeof(exception_state_)); } //------------------------------------------------------------------------------ // Name: PlatformState::clone // Desc: makes a copy of the state object //------------------------------------------------------------------------------ std::unique_ptr PlatformState::clone() const { return std::make_unique(*this); } //------------------------------------------------------------------------------ // Name: flags_to_string // Desc: returns the flags in a string form appropriate for this platform //------------------------------------------------------------------------------ QString PlatformState::flags_to_string(edb::reg_t flags) const { char buf[14]; qsnprintf( buf, sizeof(buf), "%c %c %c %c %c %c %c", ((flags & 0x001) ? 'C' : 'c'), ((flags & 0x004) ? 'P' : 'p'), ((flags & 0x010) ? 'A' : 'a'), ((flags & 0x040) ? 'Z' : 'z'), ((flags & 0x080) ? 'S' : 's'), ((flags & 0x400) ? 'D' : 'd'), ((flags & 0x800) ? 'O' : 'o')); return buf; } //------------------------------------------------------------------------------ // Name: flags_to_string // Desc: returns the flags in a string form appropriate for this platform //------------------------------------------------------------------------------ QString PlatformState::flags_to_string() const { return flags_to_string(flags()); } //------------------------------------------------------------------------------ // Name: value // Desc: returns a Register object which represents the register with the name // supplied //------------------------------------------------------------------------------ Register PlatformState::value(const QString ®) const { const QString lreg = reg.toLower(); #if defined(EDB_X86) if (lreg == "eax") return Register("eax", thread_state_.REG(eax), Register::TYPE_GPR); else if (lreg == "ebx") return Register("ebx", thread_state_.REG(ebx), Register::TYPE_GPR); else if (lreg == "ecx") return Register("ecx", thread_state_.REG(ecx), Register::TYPE_GPR); else if (lreg == "edx") return Register("edx", thread_state_.REG(edx), Register::TYPE_GPR); else if (lreg == "ebp") return Register("ebp", thread_state_.REG(ebp), Register::TYPE_GPR); else if (lreg == "esp") return Register("esp", thread_state_.REG(esp), Register::TYPE_GPR); else if (lreg == "esi") return Register("esi", thread_state_.REG(esi), Register::TYPE_GPR); else if (lreg == "edi") return Register("edi", thread_state_.REG(edi), Register::TYPE_GPR); else if (lreg == "eip") return Register("eip", thread_state_.REG(eip), Register::TYPE_IP); else if (lreg == "ax") return Register("ax", thread_state_.REG(eax) & 0xffff, Register::TYPE_GPR); else if (lreg == "bx") return Register("bx", thread_state_.REG(ebx) & 0xffff, Register::TYPE_GPR); else if (lreg == "cx") return Register("cx", thread_state_.REG(ecx) & 0xffff, Register::TYPE_GPR); else if (lreg == "dx") return Register("dx", thread_state_.REG(edx) & 0xffff, Register::TYPE_GPR); else if (lreg == "bp") return Register("bp", thread_state_.REG(ebp) & 0xffff, Register::TYPE_GPR); else if (lreg == "sp") return Register("sp", thread_state_.REG(esp) & 0xffff, Register::TYPE_GPR); else if (lreg == "si") return Register("si", thread_state_.REG(esi) & 0xffff, Register::TYPE_GPR); else if (lreg == "di") return Register("di", thread_state_.REG(edi) & 0xffff, Register::TYPE_GPR); else if (lreg == "al") return Register("al", thread_state_.REG(eax) & 0xff, Register::TYPE_GPR); else if (lreg == "bl") return Register("bl", thread_state_.REG(ebx) & 0xff, Register::TYPE_GPR); else if (lreg == "cl") return Register("cl", thread_state_.REG(ecx) & 0xff, Register::TYPE_GPR); else if (lreg == "dl") return Register("dl", thread_state_.REG(edx) & 0xff, Register::TYPE_GPR); else if (lreg == "ah") return Register("ah", (thread_state_.REG(eax) >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "bh") return Register("bh", (thread_state_.REG(ebx) >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "ch") return Register("ch", (thread_state_.REG(ecx) >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "dh") return Register("dh", (thread_state_.REG(edx) >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "cs") return Register("cs", thread_state_.REG(cs), Register::TYPE_SEG); else if (lreg == "ds") return Register("ds", thread_state_.REG(ds), Register::TYPE_SEG); else if (lreg == "es") return Register("es", thread_state_.REG(es), Register::TYPE_SEG); else if (lreg == "fs") return Register("fs", thread_state_.REG(fs), Register::TYPE_SEG); else if (lreg == "gs") return Register("gs", thread_state_.REG(gs), Register::TYPE_SEG); else if (lreg == "ss") return Register("ss", thread_state_.REG(ss), Register::TYPE_SEG); else if (lreg == "fs_base") return Register("fs_base", 0 /* TODO fs_base */, Register::TYPE_SEG); else if (lreg == "gs_base") return Register("gs_base", 0 /* TODO gs_base */, Register::TYPE_SEG); else if (lreg == "eflags") return Register("eflags", thread_state_.REG(eflags), Register::TYPE_COND); #elif defined(EDB_X86_64) if (lreg == "rax") return Register("rax", thread_state_.REG(rax), Register::TYPE_GPR); else if (lreg == "rbx") return Register("rbx", thread_state_.REG(rbx), Register::TYPE_GPR); else if (lreg == "rcx") return Register("rcx", thread_state_.REG(rcx), Register::TYPE_GPR); else if (lreg == "rdx") return Register("rdx", thread_state_.REG(rdx), Register::TYPE_GPR); else if (lreg == "rbp") return Register("rbp", thread_state_.REG(rbp), Register::TYPE_GPR); else if (lreg == "rsp") return Register("rsp", thread_state_.REG(rsp), Register::TYPE_GPR); else if (lreg == "rsi") return Register("rsi", thread_state_.REG(rsi), Register::TYPE_GPR); else if (lreg == "rdi") return Register("rdi", thread_state_.REG(rdi), Register::TYPE_GPR); else if (lreg == "rip") return Register("rip", thread_state_.REG(rip), Register::TYPE_IP); else if (lreg == "r8") return Register("r8", thread_state_.REG(r8), Register::TYPE_GPR); else if (lreg == "r9") return Register("r9", thread_state_.REG(r9), Register::TYPE_GPR); else if (lreg == "r10") return Register("r10", thread_state_.REG(r10), Register::TYPE_GPR); else if (lreg == "r11") return Register("r11", thread_state_.REG(r11), Register::TYPE_GPR); else if (lreg == "r12") return Register("r12", thread_state_.REG(r12), Register::TYPE_GPR); else if (lreg == "r13") return Register("r13", thread_state_.REG(r13), Register::TYPE_GPR); else if (lreg == "r14") return Register("r14", thread_state_.REG(r14), Register::TYPE_GPR); else if (lreg == "r15") return Register("r15", thread_state_.REG(r15), Register::TYPE_GPR); else if (lreg == "eax") return Register("eax", thread_state_.REG(rax) & 0xffffffff, Register::TYPE_GPR); else if (lreg == "ebx") return Register("ebx", thread_state_.REG(rbx) & 0xffffffff, Register::TYPE_GPR); else if (lreg == "ecx") return Register("ecx", thread_state_.REG(rcx) & 0xffffffff, Register::TYPE_GPR); else if (lreg == "edx") return Register("edx", thread_state_.REG(rdx) & 0xffffffff, Register::TYPE_GPR); else if (lreg == "ebp") return Register("ebp", thread_state_.REG(rbp) & 0xffffffff, Register::TYPE_GPR); else if (lreg == "esp") return Register("esp", thread_state_.REG(rsp) & 0xffffffff, Register::TYPE_GPR); else if (lreg == "esi") return Register("esi", thread_state_.REG(rsi) & 0xffffffff, Register::TYPE_GPR); else if (lreg == "edi") return Register("edi", thread_state_.REG(rdi) & 0xffffffff, Register::TYPE_GPR); else if (lreg == "r8d") return Register("r8d", thread_state_.REG(r8) & 0xffffffff, Register::TYPE_GPR); else if (lreg == "r9d") return Register("r9d", thread_state_.REG(r9) & 0xffffffff, Register::TYPE_GPR); else if (lreg == "r10d") return Register("r10d", thread_state_.REG(r10) & 0xffffffff, Register::TYPE_GPR); else if (lreg == "r11d") return Register("r11d", thread_state_.REG(r11) & 0xffffffff, Register::TYPE_GPR); else if (lreg == "r12d") return Register("r12d", thread_state_.REG(r12) & 0xffffffff, Register::TYPE_GPR); else if (lreg == "r13d") return Register("r13d", thread_state_.REG(r13) & 0xffffffff, Register::TYPE_GPR); else if (lreg == "r14d") return Register("r14d", thread_state_.REG(r14) & 0xffffffff, Register::TYPE_GPR); else if (lreg == "r15d") return Register("r15d", thread_state_.REG(r15) & 0xffffffff, Register::TYPE_GPR); else if (lreg == "ax") return Register("ax", thread_state_.REG(rax) & 0xffff, Register::TYPE_GPR); else if (lreg == "bx") return Register("bx", thread_state_.REG(rbx) & 0xffff, Register::TYPE_GPR); else if (lreg == "cx") return Register("cx", thread_state_.REG(rcx) & 0xffff, Register::TYPE_GPR); else if (lreg == "dx") return Register("dx", thread_state_.REG(rdx) & 0xffff, Register::TYPE_GPR); else if (lreg == "bp") return Register("bp", thread_state_.REG(rbp) & 0xffff, Register::TYPE_GPR); else if (lreg == "sp") return Register("sp", thread_state_.REG(rsp) & 0xffff, Register::TYPE_GPR); else if (lreg == "si") return Register("si", thread_state_.REG(rsi) & 0xffff, Register::TYPE_GPR); else if (lreg == "di") return Register("di", thread_state_.REG(rdi) & 0xffff, Register::TYPE_GPR); else if (lreg == "r8w") return Register("r8w", thread_state_.REG(r8) & 0xffff, Register::TYPE_GPR); else if (lreg == "r9w") return Register("r9w", thread_state_.REG(r9) & 0xffff, Register::TYPE_GPR); else if (lreg == "r10w") return Register("r10w", thread_state_.REG(r10) & 0xffff, Register::TYPE_GPR); else if (lreg == "r11w") return Register("r11w", thread_state_.REG(r11) & 0xffff, Register::TYPE_GPR); else if (lreg == "r12w") return Register("r12w", thread_state_.REG(r12) & 0xffff, Register::TYPE_GPR); else if (lreg == "r13w") return Register("r13w", thread_state_.REG(r13) & 0xffff, Register::TYPE_GPR); else if (lreg == "r14w") return Register("r14w", thread_state_.REG(r14) & 0xffff, Register::TYPE_GPR); else if (lreg == "r15w") return Register("r15w", thread_state_.REG(r15) & 0xffff, Register::TYPE_GPR); else if (lreg == "al") return Register("al", thread_state_.REG(rax) & 0xff, Register::TYPE_GPR); else if (lreg == "bl") return Register("bl", thread_state_.REG(rbx) & 0xff, Register::TYPE_GPR); else if (lreg == "cl") return Register("cl", thread_state_.REG(rcx) & 0xff, Register::TYPE_GPR); else if (lreg == "dl") return Register("dl", thread_state_.REG(rdx) & 0xff, Register::TYPE_GPR); else if (lreg == "ah") return Register("ah", (thread_state_.REG(rax) >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "bh") return Register("bh", (thread_state_.REG(rbx) >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "ch") return Register("ch", (thread_state_.REG(rcx) >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "dh") return Register("dh", (thread_state_.REG(rdx) >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "spl") return Register("spl", (thread_state_.REG(rsp) >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "bpl") return Register("bpl", (thread_state_.REG(rbp) >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "sil") return Register("sil", (thread_state_.REG(rsi) >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "dil") return Register("dil", (thread_state_.REG(rdi) >> 8) & 0xff, Register::TYPE_GPR); else if (lreg == "r8b") return Register("r8b", thread_state_.REG(r8) & 0xff, Register::TYPE_GPR); else if (lreg == "r9b") return Register("r9b", thread_state_.REG(r9) & 0xff, Register::TYPE_GPR); else if (lreg == "r10b") return Register("r10b", thread_state_.REG(r10) & 0xff, Register::TYPE_GPR); else if (lreg == "r11b") return Register("r11b", thread_state_.REG(r11) & 0xff, Register::TYPE_GPR); else if (lreg == "r12b") return Register("r12b", thread_state_.REG(r12) & 0xff, Register::TYPE_GPR); else if (lreg == "r13b") return Register("r13b", thread_state_.REG(r13) & 0xff, Register::TYPE_GPR); else if (lreg == "r14b") return Register("r14b", thread_state_.REG(r14) & 0xff, Register::TYPE_GPR); else if (lreg == "r15b") return Register("r15b", thread_state_.REG(r15) & 0xff, Register::TYPE_GPR); else if (lreg == "cs") return Register("cs", thread_state_.REG(cs), Register::TYPE_SEG); else if (lreg == "fs") return Register("fs", thread_state_.REG(fs), Register::TYPE_SEG); else if (lreg == "gs") return Register("gs", thread_state_.REG(gs), Register::TYPE_SEG); else if (lreg == "fs_base") return Register("fs_base", 0 /* TODO fs_base */, Register::TYPE_SEG); else if (lreg == "gs_base") return Register("gs_base", 0 /* TODO gs_base */, Register::TYPE_SEG); else if (lreg == "rflags") return Register("rflags", thread_state_.REG(rflags), Register::TYPE_COND); #endif return Register(); } //------------------------------------------------------------------------------ // Name: frame_pointer // Desc: returns what is conceptually the frame pointer for this platform //------------------------------------------------------------------------------ edb::address_t PlatformState::frame_pointer() const { #if defined(EDB_X86) return thread_state_.REG(ebp); #elif defined(EDB_X86_64) return thread_state_.REG(rbp); #endif } //------------------------------------------------------------------------------ // Name: instruction_pointer // Desc: returns the instruction pointer for this platform //------------------------------------------------------------------------------ edb::address_t PlatformState::instruction_pointer() const { #if defined(EDB_X86) return thread_state_.REG(eip); #elif defined(EDB_X86_64) return thread_state_.REG(rip); #endif } //------------------------------------------------------------------------------ // Name: stack_pointer // Desc: returns the stack pointer for this platform //------------------------------------------------------------------------------ edb::address_t PlatformState::stack_pointer() const { #if defined(EDB_X86) return thread_state_.REG(esp); #elif defined(EDB_X86_64) return thread_state_.REG(rsp); #endif } //------------------------------------------------------------------------------ // Name: debug_register // Desc: //------------------------------------------------------------------------------ edb::reg_t PlatformState::debug_register(int n) const { switch (n) { case 0: return debug_state_.REG(dr0); case 1: return debug_state_.REG(dr1); case 2: return debug_state_.REG(dr2); case 3: return debug_state_.REG(dr3); case 4: return debug_state_.REG(dr4); case 5: return debug_state_.REG(dr5); case 6: return debug_state_.REG(dr6); case 7: return debug_state_.REG(dr7); } return 0; } //------------------------------------------------------------------------------ // Name: flags // Desc: //------------------------------------------------------------------------------ edb::reg_t PlatformState::flags() const { #if defined(EDB_X86) return thread_state_.REG(eflags); #elif defined(EDB_X86_64) return thread_state_.REG(rflags); #endif } //------------------------------------------------------------------------------ // Name: fpu_register // Desc: //------------------------------------------------------------------------------ long double PlatformState::fpu_register(int n) const { /* switch(n) { case 0: return static_cast(float_state_.REG(fpu_stmm0).REG(mmst_reg)); case 1: return static_cast(float_state_.REG(fpu_stmm1).REG(mmst_reg)); case 2: return static_cast(float_state_.REG(fpu_stmm2).REG(mmst_reg)); case 3: return static_cast(float_state_.REG(fpu_stmm3).REG(mmst_reg)); case 4: return static_cast(float_state_.REG(fpu_stmm4).REG(mmst_reg)); case 5: return static_cast(float_state_.REG(fpu_stmm5).REG(mmst_reg)); case 6: return static_cast(float_state_.REG(fpu_stmm6).REG(mmst_reg)); case 7: return static_cast(float_state_.REG(fpu_stmm7).REG(mmst_reg)); } */ return 0.0; } //------------------------------------------------------------------------------ // Name: adjust_stack // Desc: //------------------------------------------------------------------------------ void PlatformState::adjust_stack(int bytes) { #if defined(EDB_X86) thread_state_.REG(esp) += bytes; #elif defined(EDB_X86_64) thread_state_.REG(rsp) += bytes; #endif } //------------------------------------------------------------------------------ // Name: clear // Desc: //------------------------------------------------------------------------------ void PlatformState::clear() { memset(&thread_state_, 0, sizeof(thread_state_)); memset(&float_state_, 0, sizeof(float_state_)); memset(&debug_state_, 0, sizeof(debug_state_)); memset(&exception_state_, 0, sizeof(exception_state_)); } //------------------------------------------------------------------------------ // Name: set_debug_register // Desc: //------------------------------------------------------------------------------ void PlatformState::set_debug_register(int n, edb::reg_t value) { switch (n) { case 0: debug_state_.REG(dr0) = value; break; case 1: debug_state_.REG(dr1) = value; break; case 2: debug_state_.REG(dr2) = value; break; case 3: debug_state_.REG(dr3) = value; break; case 4: debug_state_.REG(dr4) = value; break; case 5: debug_state_.REG(dr5) = value; break; case 6: debug_state_.REG(dr6) = value; break; case 7: debug_state_.REG(dr7) = value; break; default: break; } } //------------------------------------------------------------------------------ // Name: set_flags // Desc: //------------------------------------------------------------------------------ void PlatformState::set_flags(edb::reg_t flags) { #if defined(EDB_X86) thread_state_.REG(eflags) = flags; #elif defined(EDB_X86_64) thread_state_.REG(rflags) = flags; #endif } //------------------------------------------------------------------------------ // Name: set_instruction_pointer // Desc: //------------------------------------------------------------------------------ void PlatformState::set_instruction_pointer(edb::address_t value) { #if defined(EDB_X86) thread_state_.REG(eip) = value; #elif defined(EDB_X86_64) thread_state_.REG(rip) = value; #endif } //------------------------------------------------------------------------------ // Name: set_register // Desc: //------------------------------------------------------------------------------ void PlatformState::set_register(const QString &name, edb::reg_t value) { const QString lreg = name.toLower(); #if defined(EDB_X86) if (lreg == "eax") { thread_state_.REG(eax) = value; } else if (lreg == "ebx") { thread_state_.REG(ebx) = value; } else if (lreg == "ecx") { thread_state_.REG(ecx) = value; } else if (lreg == "edx") { thread_state_.REG(edx) = value; } else if (lreg == "ebp") { thread_state_.REG(ebp) = value; } else if (lreg == "esp") { thread_state_.REG(esp) = value; } else if (lreg == "esi") { thread_state_.REG(esi) = value; } else if (lreg == "edi") { thread_state_.REG(edi) = value; } else if (lreg == "eip") { thread_state_.REG(eip) = value; } else if (lreg == "cs") { thread_state_.REG(cs) = value; } else if (lreg == "ds") { thread_state_.REG(ds) = value; } else if (lreg == "es") { thread_state_.REG(es) = value; } else if (lreg == "fs") { thread_state_.REG(fs) = value; } else if (lreg == "gs") { thread_state_.REG(gs) = value; } else if (lreg == "ss") { thread_state_.REG(ss) = value; } else if (lreg == "eflags") { thread_state_.REG(eflags) = value; } #elif defined(EDB_X86_64) if (lreg == "rax") { thread_state_.REG(rax) = value; } else if (lreg == "rbx") { thread_state_.REG(rbx) = value; } else if (lreg == "rcx") { thread_state_.REG(rcx) = value; } else if (lreg == "rdx") { thread_state_.REG(rdx) = value; } else if (lreg == "rbp") { thread_state_.REG(rbp) = value; } else if (lreg == "rsp") { thread_state_.REG(rsp) = value; } else if (lreg == "rsi") { thread_state_.REG(rsi) = value; } else if (lreg == "rdi") { thread_state_.REG(rdi) = value; } else if (lreg == "r8") { thread_state_.REG(r8) = value; } else if (lreg == "r9") { thread_state_.REG(r9) = value; } else if (lreg == "r10") { thread_state_.REG(r10) = value; } else if (lreg == "r11") { thread_state_.REG(r11) = value; } else if (lreg == "r12") { thread_state_.REG(r12) = value; } else if (lreg == "r13") { thread_state_.REG(r13) = value; } else if (lreg == "r14") { thread_state_.REG(r14) = value; } else if (lreg == "r15") { thread_state_.REG(r15) = value; } else if (lreg == "rip") { thread_state_.REG(rip) = value; } else if (lreg == "cs") { thread_state_.REG(cs) = value; } else if (lreg == "fs") { thread_state_.REG(fs) = value; } else if (lreg == "gs") { thread_state_.REG(gs) = value; } else if (lreg == "rflags") { thread_state_.REG(rflags) = value; } #endif } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ quint64 PlatformState::mmx_register(int n) const { Q_UNUSED(n) return 0; } //------------------------------------------------------------------------------ // Name: // Desc: //------------------------------------------------------------------------------ QByteArray PlatformState::xmm_register(int n) const { Q_UNUSED(n) return QByteArray(); } } edb-debugger/plugins/DebuggerCore/unix/osx/PlatformRegion.h0000644000175000017500000000351013765535463023643 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PLATFORM_REGION_H_20120330_ #define PLATFORM_REGION_H_20120330_ #include "IRegion.h" #include #include namespace DebuggerCore { class PlatformRegion : public IRegion { Q_DECLARE_TR_FUNCTIONS(PlatformRegion) public: PlatformRegion(edb::address_t start, edb::address_t end, edb::address_t base, const QString &name, permissions_t permissions); ~PlatformRegion() override = default; public: IRegion *clone() const override; public: bool accessible() const override; bool readable() const override; bool writable() const override; bool executable() const override; size_t size() const override; public: void set_permissions(bool read, bool write, bool execute) override; void set_start(edb::address_t address) override; void set_end(edb::address_t address) override; public: edb::address_t start() const override; edb::address_t end() const override; edb::address_t base() const override; QString name() const override; permissions_t permissions() const override; private: edb::address_t start_; edb::address_t end_; edb::address_t base_; QString name_; permissions_t permissions_; }; } #endif edb-debugger/plugins/DebuggerCore/arch/0000755000175000017500000000000013765535463017664 5ustar eteraneteranedb-debugger/plugins/DebuggerCore/arch/arm-generic/0000755000175000017500000000000013765535463022055 5ustar eteraneteranedb-debugger/plugins/DebuggerCore/arch/arm-generic/Breakpoint.h0000644000175000017500000000441513765535463024330 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef BREAKPOINT_H_20060720_ #define BREAKPOINT_H_20060720_ #include "IBreakpoint.h" #include "Util.h" #include #include namespace DebuggerCorePlugin { class Breakpoint final : public IBreakpoint { public: enum class TypeId { Automatic = static_cast(IBreakpoint::TypeId::Automatic), ARM32, Thumb2Byte, Thumb4Byte, UniversalThumbARM32, ARM32BKPT, ThumbBKPT, TYPE_COUNT }; using Type = util::AbstractEnumData; public: explicit Breakpoint(edb::address_t address); ~Breakpoint() override; public: edb::address_t address() const override { return address_; } uint64_t hitCount() const override { return hitCount_; } bool enabled() const override { return enabled_; } bool oneTime() const override { return oneTime_; } bool internal() const override { return internal_; } size_t size() const override { return originalBytes_.size(); } const uint8_t *originalBytes() const override { return &originalBytes_[0]; } IBreakpoint::TypeId type() const override { return type_; } static std::vector supportedTypes(); static std::vector possibleRewindSizes(); public: bool enable() override; bool disable() override; void hit() override; void setOneTime(bool value) override; void setInternal(bool value) override; void setType(IBreakpoint::TypeId type) override; void setType(TypeId type); private: std::vector originalBytes_; edb::address_t address_; uint64_t hitCount_ = 0; bool enabled_ = false; bool oneTime_ = false; bool internal_ = false; Type type_; }; } #endif edb-debugger/plugins/DebuggerCore/arch/arm-generic/Breakpoint.cpp0000644000175000017500000001465213765535463024667 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "Breakpoint.h" #include "Configuration.h" #include "IDebugger.h" #include "IProcess.h" #include "edb.h" namespace DebuggerCorePlugin { namespace { const std::vector BreakpointInstructionARM_LE = {0xf0, 0x01, 0xf0, 0xe7}; // udf #0x10 const std::vector BreakpointInstructionThumb_LE = {0x01, 0xde}; // udf #1 // We have to sometimes use a 32-bit Thumb-2 breakpoint. For explanation how to // correctly use it see GDB's thumb_get_next_pcs_raw function and comments // around arm_linux_thumb2_le_breakpoint array. const std::vector BreakpointInstructionThumb2_LE = {0xf0, 0xf7, 0x00, 0xa0}; // udf.w #0 // This one generates SIGILL both in ARM32 and Thumb mode. In ARM23 mode it's decoded as UDF 0xDDE0, while // in Thumb it's a sequence `1: UDF 0xF0; B 1b`, which does stop the process even if it lands in the // middle (on the second half-word), although the signal still occurs at the first half-word. const std::vector BreakpointInstructionUniversalThumbARM_LE = {0xf0, 0xde, 0xfd, 0xe7}; const std::vector BreakpointInstructionThumbBKPT_LE = {0x00, 0xbe}; // bkpt #0 const std::vector BreakpointInstructionARM32BKPT_LE = {0x70, 0x00, 0x20, 0xe1}; // bkpt #0 } //------------------------------------------------------------------------------ // Name: Breakpoint // Desc: constructor //------------------------------------------------------------------------------ Breakpoint::Breakpoint(edb::address_t address) : address_(address), type_(edb::v1::config().default_breakpoint_type) { if (!enable()) { throw BreakpointCreationError(); } } auto Breakpoint::supportedTypes() -> std::vector { std::vector types = { BreakpointType{Type{TypeId::Automatic}, QObject::tr("Automatic")}, BreakpointType{Type{TypeId::ARM32}, QObject::tr("Always ARM32 UDF")}, BreakpointType{Type{TypeId::Thumb2Byte}, QObject::tr("Always Thumb UDF")}, BreakpointType{Type{TypeId::Thumb4Byte}, QObject::tr("Always Thumb2 UDF.W")}, BreakpointType{Type{TypeId::UniversalThumbARM32}, QObject::tr("Universal ARM/Thumb UDF(+B .-2)")}, BreakpointType{Type{TypeId::ARM32BKPT}, QObject::tr("ARM32 BKPT (may be slow)")}, BreakpointType{Type{TypeId::ThumbBKPT}, QObject::tr("Thumb BKPT (may be slow)")}, }; return types; } void Breakpoint::setType(TypeId type) { disable(); type_ = type; if (!enable()) { throw BreakpointCreationError(); } } void Breakpoint::setType(IBreakpoint::TypeId type) { disable(); if (Type{type} >= TypeId::TYPE_COUNT) throw BreakpointCreationError(); setType(type); } //------------------------------------------------------------------------------ // Name: ~Breakpoint // Desc: //------------------------------------------------------------------------------ Breakpoint::~Breakpoint() { disable(); } //------------------------------------------------------------------------------ // Name: enable // Desc: //------------------------------------------------------------------------------ bool Breakpoint::enable() { if (!enabled()) { if (IProcess *process = edb::v1::debugger_core->process()) { std::vector prev(4); prev.resize(process->readBytes(address(), &prev[0], prev.size())); if (prev.size()) { originalBytes_ = prev; const std::vector *bpBytes = nullptr; switch (TypeId{type_}) { case TypeId::Automatic: if (edb::v1::debugger_core->cpuMode() == IDebugger::CpuMode::Thumb) { bpBytes = &BreakpointInstructionThumb_LE; } else { bpBytes = &BreakpointInstructionARM_LE; } break; case TypeId::ARM32: bpBytes = &BreakpointInstructionARM_LE; break; case TypeId::Thumb2Byte: bpBytes = &BreakpointInstructionThumb_LE; break; case TypeId::Thumb4Byte: bpBytes = &BreakpointInstructionThumb2_LE; break; case TypeId::UniversalThumbARM32: bpBytes = &BreakpointInstructionUniversalThumbARM_LE; break; case TypeId::ARM32BKPT: bpBytes = &BreakpointInstructionARM32BKPT_LE; break; case TypeId::ThumbBKPT: bpBytes = &BreakpointInstructionThumbBKPT_LE; break; } assert(bpBytes); assert(originalBytes_.size() >= bpBytes->size()); originalBytes_.resize(bpBytes->size()); // FIXME: we don't check whether this breakpoint will overlap any of the existing breakpoints if (process->writeBytes(address(), bpBytes->data(), bpBytes->size())) { enabled_ = true; return true; } } } } return false; } //------------------------------------------------------------------------------ // Name: disable // Desc: //------------------------------------------------------------------------------ bool Breakpoint::disable() { if (enabled()) { if (IProcess *process = edb::v1::debugger_core->process()) { if (process->writeBytes(address(), &originalBytes_[0], originalBytes_.size())) { enabled_ = false; return true; } } } return false; } //------------------------------------------------------------------------------ // Name: hit // Desc: //------------------------------------------------------------------------------ void Breakpoint::hit() { ++hitCount_; } //------------------------------------------------------------------------------ // Name: setOneTime // Desc: //------------------------------------------------------------------------------ void Breakpoint::setOneTime(bool value) { oneTime_ = value; } //------------------------------------------------------------------------------ // Name: setInternal // Desc: //------------------------------------------------------------------------------ void Breakpoint::setInternal(bool value) { internal_ = value; } std::vector Breakpoint::possibleRewindSizes() { return {0}; // Even BKPT stops before the instruction, let alone UDF } } edb-debugger/plugins/DebuggerCore/arch/x86-generic/0000755000175000017500000000000013765535463021723 5ustar eteraneteranedb-debugger/plugins/DebuggerCore/arch/x86-generic/Breakpoint.h0000644000175000017500000000452413765535463024177 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef X86_BREAKPOINT_H_20060720_ #define X86_BREAKPOINT_H_20060720_ #include "IBreakpoint.h" #include "Util.h" #include #include #include namespace DebuggerCorePlugin { class Breakpoint final : public IBreakpoint { Q_DECLARE_TR_FUNCTIONS(Breakpoint) public: enum class TypeId { Automatic = static_cast(IBreakpoint::TypeId::Automatic), INT3, INT1, HLT, CLI, STI, INSB, INSD, OUTSB, OUTSD, UD2, UD0, TYPE_COUNT }; using Type = util::AbstractEnumData; public: explicit Breakpoint(edb::address_t address); ~Breakpoint() override; public: edb::address_t address() const override { return address_; } uint64_t hitCount() const override { return hitCount_; } bool enabled() const override { return enabled_; } bool oneTime() const override { return oneTime_; } bool internal() const override { return internal_; } size_t size() const override { return originalBytes_.size(); } const uint8_t *originalBytes() const override { return &originalBytes_[0]; } IBreakpoint::TypeId type() const override { return type_; } static std::vector supportedTypes(); static std::vector possibleRewindSizes(); public: bool enable() override; bool disable() override; void hit() override; void setOneTime(bool value) override; void setInternal(bool value) override; void setType(IBreakpoint::TypeId type) override; void setType(TypeId type); private: std::vector originalBytes_; edb::address_t address_; uint64_t hitCount_ = 0; bool enabled_ = false; bool oneTime_ = false; bool internal_ = false; Type type_; }; } #endif edb-debugger/plugins/DebuggerCore/arch/x86-generic/Breakpoint.cpp0000644000175000017500000001263113765535463024530 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "Breakpoint.h" #include "Configuration.h" #include "IDebugger.h" #include "IProcess.h" #include "edb.h" namespace DebuggerCorePlugin { namespace { const std::vector BreakpointInstructionINT3 = {0xcc}; const std::vector BreakpointInstructionINT1 = {0xf1}; const std::vector BreakpointInstructionHLT = {0xf4}; const std::vector BreakpointInstructionCLI = {0xfa}; const std::vector BreakpointInstructionSTI = {0xfb}; const std::vector BreakpointInstructionINSB = {0x6c}; const std::vector BreakpointInstructionINSD = {0x6d}; const std::vector BreakpointInstructionOUTSB = {0x6e}; const std::vector BreakpointInstructionOUTSD = {0x6f}; const std::vector BreakpointInstructionUD2 = {0x0f, 0x0b}; const std::vector BreakpointInstructionUD0 = {0x0f, 0xff}; } /** * @brief Breakpoint::Breakpoint * @param address */ Breakpoint::Breakpoint(edb::address_t address) : address_(address), type_(edb::v1::config().default_breakpoint_type) { if (!this->enable()) { throw BreakpointCreationError(); } } /** * @brief Breakpoint::supportedTypes * @return */ auto Breakpoint::supportedTypes() -> std::vector { std::vector types = { BreakpointType{Type{TypeId::Automatic}, tr("Automatic")}, BreakpointType{Type{TypeId::INT3}, tr("INT3")}, BreakpointType{Type{TypeId::INT1}, tr("INT1 (ICEBP)")}, BreakpointType{Type{TypeId::HLT}, tr("HLT")}, BreakpointType{Type{TypeId::CLI}, tr("CLI")}, BreakpointType{Type{TypeId::STI}, tr("STI")}, BreakpointType{Type{TypeId::INSB}, tr("INSB")}, BreakpointType{Type{TypeId::INSD}, tr("INSD")}, BreakpointType{Type{TypeId::OUTSB}, tr("OUTSB")}, BreakpointType{Type{TypeId::OUTSD}, tr("OUTSD")}, BreakpointType{Type{TypeId::UD2}, tr("UD2 (2-byte)")}, BreakpointType{Type{TypeId::UD0}, tr("UD0 (2-byte)")}, }; return types; } /** * @brief Breakpoint::setType * @param type */ void Breakpoint::setType(TypeId type) { disable(); type_ = type; if (!enable()) { throw BreakpointCreationError(); } } /** * @brief Breakpoint::setType * @param type */ void Breakpoint::setType(IBreakpoint::TypeId type) { disable(); if (Type{type} >= TypeId::TYPE_COUNT) { throw BreakpointCreationError(); } setType(type); } /** * @brief Breakpoint::~Breakpoint */ Breakpoint::~Breakpoint() { this->disable(); } /** * @brief Breakpoint::enable * @return */ bool Breakpoint::enable() { if (!enabled()) { if (IProcess *process = edb::v1::debugger_core->process()) { std::vector prev(2); if (process->readBytes(address(), &prev[0], prev.size())) { originalBytes_ = prev; const std::vector *bpBytes = nullptr; switch (TypeId{type_}) { case TypeId::Automatic: case TypeId::INT3: bpBytes = &BreakpointInstructionINT3; break; case TypeId::INT1: bpBytes = &BreakpointInstructionINT1; break; case TypeId::HLT: bpBytes = &BreakpointInstructionHLT; break; case TypeId::CLI: bpBytes = &BreakpointInstructionCLI; break; case TypeId::STI: bpBytes = &BreakpointInstructionSTI; break; case TypeId::INSB: bpBytes = &BreakpointInstructionINSB; break; case TypeId::INSD: bpBytes = &BreakpointInstructionINSD; break; case TypeId::OUTSB: bpBytes = &BreakpointInstructionOUTSB; break; case TypeId::OUTSD: bpBytes = &BreakpointInstructionOUTSD; break; case TypeId::UD2: bpBytes = &BreakpointInstructionUD2; break; case TypeId::UD0: bpBytes = &BreakpointInstructionUD0; break; default: return false; } assert(bpBytes); assert(originalBytes_.size() >= bpBytes->size()); originalBytes_.resize(bpBytes->size()); if (process->writeBytes(address(), bpBytes->data(), bpBytes->size())) { enabled_ = true; return true; } } } } return false; } /** * @brief Breakpoint::disable * @return */ bool Breakpoint::disable() { if (enabled()) { if (IProcess *process = edb::v1::debugger_core->process()) { if (process->writeBytes(address(), &originalBytes_[0], originalBytes_.size())) { enabled_ = false; return true; } } } return false; } /** * @brief Breakpoint::hit */ void Breakpoint::hit() { ++hitCount_; } /** * @brief Breakpoint::setOneTime * @param value */ void Breakpoint::setOneTime(bool value) { oneTime_ = value; } /** * @brief Breakpoint::setInternal * @param value */ void Breakpoint::setInternal(bool value) { internal_ = value; } /** * @brief Breakpoint::possibleRewindSizes * @return */ std::vector Breakpoint::possibleRewindSizes() { return {1, 0, 2}; // e.g. int3/int1, cli/sti/hlt/etc., int 0x1/int 0x3 } } edb-debugger/plugins/DebuggerCore/DebuggerCoreBase.h0000644000175000017500000000325413765535463022254 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DEBUGGER_CORE_BASE_H_20090529_ #define DEBUGGER_CORE_BASE_H_20090529_ #include "IDebugger.h" class Status; namespace DebuggerCorePlugin { class DebuggerCoreBase : public QObject, public IDebugger { public: ~DebuggerCoreBase() override = default; public: enum class MeansOfCapture { NeverCaptured, Attach, Launch }; virtual MeansOfCapture lastMeansOfCapture() const = 0; public: BreakpointList backupBreakpoints() const override; std::shared_ptr addBreakpoint(edb::address_t address) override; std::shared_ptr findBreakpoint(edb::address_t address) override; std::shared_ptr findTriggeredBreakpoint(edb::address_t address) override; void clearBreakpoints() override; void removeBreakpoint(edb::address_t address) override; void endDebugSession() override; std::vector supportedBreakpointTypes() const override; protected: bool attached() const; protected: BreakpointList breakpoints_; }; } #endif edb-debugger/plugins/DebuggerErrorConsole/0000755000175000017500000000000013765535463020473 5ustar eteraneteranedb-debugger/plugins/DebuggerErrorConsole/CMakeLists.txt0000644000175000017500000000142113765535463023231 0ustar eteraneterancmake_minimum_required (VERSION 3.1) include("GNUInstallDirs") set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(PluginName "DebuggerErrorConsole") find_package(Qt5 5.0.0 REQUIRED Widgets) add_library(${PluginName} SHARED Plugin.cpp Plugin.h ) target_link_libraries(${PluginName} Qt5::Widgets edb) install (TARGETS ${PluginName} DESTINATION ${CMAKE_INSTALL_LIBDIR}/edb) set_property(TARGET ${PluginName} PROPERTY CXX_EXTENSIONS OFF) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD 14) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD_REQUIRED ON) set_property(TARGET ${PluginName} PROPERTY LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) set_property(TARGET ${PluginName} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) edb-debugger/plugins/DebuggerErrorConsole/Plugin.cpp0000644000175000017500000000667613765535463022454 0ustar eteraneteran/* Copyright (C) 2017 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "Plugin.h" #include "edb.h" #include #include #include #include #include #include namespace DebuggerErrorConsolePlugin { namespace { QPointer instance = nullptr; } /** * @brief Plugin::debugMessageIntercept * @param type * @param message */ void Plugin::debugMessageIntercept(QtMsgType type, const QMessageLogContext &, const QString &message) { if (!instance) { return; } const QString text = [type, &message]() { switch (type) { case QtDebugMsg: return tr("DEBUG %1").arg(message); #if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) case QtInfoMsg: return tr("INFO %1").arg(message); #endif case QtWarningMsg: return tr("WARN %1").arg(message); case QtCriticalMsg: return tr("ERROR %1").arg(message); case QtFatalMsg: return tr("FATAL %1").arg(message); default: Q_UNREACHABLE(); } }(); instance->textWidget_->appendPlainText(text); std::cerr << message.toUtf8().constData() << "\n"; // this may be useful as a crash log } /** * @brief Plugin::Plugin * @param parent */ Plugin::Plugin(QObject *parent) : QObject(parent) { instance = this; textWidget_ = new QPlainTextEdit; textWidget_->setReadOnly(true); QFont font("monospace"); font.setStyleHint(QFont::TypeWriter); textWidget_->setFont(font); textWidget_->setWordWrapMode(QTextOption::WrapMode::NoWrap); textWidget_->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); qInstallMessageHandler(debugMessageIntercept); } /** * @brief Plugin::menu * @param parent * @return */ QMenu *Plugin::menu(QWidget *parent) { if (!menu_) { if (auto mainWindow = qobject_cast(edb::v1::debugger_ui)) { auto dockWidget = new QDockWidget(tr("Debugger Error Console"), mainWindow); dockWidget->setObjectName(QString::fromUtf8("Debugger Error Console")); dockWidget->setWidget(textWidget_); mainWindow->addDockWidget(Qt::BottomDockWidgetArea, dockWidget); menu_ = new QMenu(tr("Debugger Error Console"), parent); menu_->addAction(dockWidget->toggleViewAction()); auto docks = mainWindow->findChildren(); // We want to put it to the same area as Stack dock // Stupid QList doesn't have a reverse iterator for (auto it = docks.end() - 1;; --it) { QDockWidget *const widget = *it; if (widget != dockWidget && mainWindow->dockWidgetArea(widget) == Qt::BottomDockWidgetArea) { mainWindow->tabifyDockWidget(widget, dockWidget); widget->show(); widget->raise(); break; } if (it == docks.begin()) { break; } } } } return menu_; } /** * @brief DebuggerErrorConsole::DebuggerErrorConsole * @param parent * @param f */ DebuggerErrorConsole::DebuggerErrorConsole(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f) { } } edb-debugger/plugins/DebuggerErrorConsole/Plugin.h0000644000175000017500000000320113765535463022076 0ustar eteraneteran/* Copyright (C) 2017 Ruslan Kabatsayev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DEBUGGER_ERROR_CONSOLE_PLUGIN_H_20170808_ #define DEBUGGER_ERROR_CONSOLE_PLUGIN_H_20170808_ #include "IPlugin.h" #include "edb.h" #include #include class QPlainTextEdit; namespace DebuggerErrorConsolePlugin { class DebuggerErrorConsole : public QDialog { Q_OBJECT public: explicit DebuggerErrorConsole(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); }; class Plugin : public QObject, public IPlugin { Q_OBJECT Q_INTERFACES(IPlugin) Q_PLUGIN_METADATA(IID "edb.IPlugin/1.0") Q_CLASSINFO("author", "Ruslan Kabatsayev") Q_CLASSINFO("email", "b7.10110111@gmail.com") public: explicit Plugin(QObject *parent = nullptr); ~Plugin() override = default; public: QMenu *menu(QWidget *parent = nullptr) override; private: static void debugMessageIntercept(QtMsgType type, const QMessageLogContext &, const QString &message); private: QPlainTextEdit *textWidget_ = nullptr; QMenu *menu_ = nullptr; }; } #endif edb-debugger/plugins/DumpState/0000755000175000017500000000000013765535463016320 5ustar eteraneteranedb-debugger/plugins/DumpState/OptionsPage.h0000644000175000017500000000241313765535463020721 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef OPTIONS_PAGE_H_20090706_ #define OPTIONS_PAGE_H_20090706_ #include "ui_OptionsPage.h" #include namespace DumpStatePlugin { class OptionsPage : public QWidget { Q_OBJECT public: explicit OptionsPage(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~OptionsPage() override = default; public: void showEvent(QShowEvent *event) override; public Q_SLOTS: void on_instructionsBeforeIP_valueChanged(int i); void on_instructionsAfterIP_valueChanged(int i); void on_colorizeOutput_toggled(bool value); private: Ui::OptionsPage ui; }; } #endif edb-debugger/plugins/DumpState/DumpState.h0000644000175000017500000000303713765535463020402 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DUMP_STATE_H_20061122_ #define DUMP_STATE_H_20061122_ #include "IPlugin.h" #include "Types.h" class QMenu; class State; namespace DumpStatePlugin { class DumpState : public QObject, public IPlugin { Q_OBJECT Q_INTERFACES(IPlugin) Q_PLUGIN_METADATA(IID "edb.IPlugin/1.0") Q_CLASSINFO("author", "Evan Teran") Q_CLASSINFO("url", "http://www.codef00.com") public: explicit DumpState(QObject *parent = nullptr); ~DumpState() override = default; public: QMenu *menu(QWidget *parent = nullptr) override; public Q_SLOTS: void showMenu(); private: QWidget *optionsPage() override; private: void dumpCode(const State &state); void dumpRegisters(const State &state); void dumpStack(const State &state); void dumpData(edb::address_t address); void dumpLines(edb::address_t address, int lines); private: QMenu *menu_ = nullptr; }; } #endif edb-debugger/plugins/DumpState/CMakeLists.txt0000644000175000017500000000147413765535463021066 0ustar eteraneterancmake_minimum_required (VERSION 3.1) include("GNUInstallDirs") set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(PluginName "DumpState") find_package(Qt5 5.0.0 REQUIRED Widgets) add_library(${PluginName} SHARED DumpState.cpp DumpState.h OptionsPage.cpp OptionsPage.h OptionsPage.ui ) target_link_libraries(${PluginName} Qt5::Widgets edb) install (TARGETS ${PluginName} DESTINATION ${CMAKE_INSTALL_LIBDIR}/edb) set_property(TARGET ${PluginName} PROPERTY CXX_EXTENSIONS OFF) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD 14) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD_REQUIRED ON) set_property(TARGET ${PluginName} PROPERTY LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) set_property(TARGET ${PluginName} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) edb-debugger/plugins/DumpState/DumpState.cpp0000644000175000017500000002404613765535463020740 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "DumpState.h" #include "IDebugger.h" #include "IProcess.h" #include "IThread.h" #include "Instruction.h" #include "OptionsPage.h" #include "Register.h" #include "State.h" #include "Util.h" #include "edb.h" #include #include #include #include #include #include #include namespace DumpStatePlugin { namespace { /** * @brief hex_string * @param value * @return */ template std::string hex_string(const T &value) { return value.toHexString().toStdString(); } constexpr const char Reset[] = "\x1B[0m"; constexpr const char Red[] = "\x1B[91m"; constexpr const char Yellow[] = "\x1B[93m"; constexpr const char Cyan[] = "\x1B[96m"; constexpr const char Blue[] = "\x1B[94m"; constexpr const char Green[] = "\x1B[92m"; constexpr const char Purple[] = "\x1B[95m"; /** * @brief format_register * @param value * @return */ template std::string format_register(const T &value) { QSettings settings; const int colorize = settings.value("DumpState/colorize", true).toBool(); if (colorize) { return Blue + hex_string(value) + Reset; } else { return hex_string(value); } } /** * @brief format_segment * @param value * @return */ template std::string format_segment(const T &value) { QSettings settings; const int colorize = settings.value("DumpState/colorize", true).toBool(); if (colorize) { return Green + hex_string(value) + Reset; } else { return hex_string(value); } } /** * @brief format_address * @param value * @return */ template std::string format_address(const T &value) { QSettings settings; const int colorize = settings.value("DumpState/colorize", true).toBool(); if (colorize) { return Purple + hex_string(value) + Reset; } else { return hex_string(value); } } } /** * @brief DumpState::DumpState * @param parent */ DumpState::DumpState(QObject *parent) : QObject(parent) { } /** * @brief DumpState::menu * @param parent * @return */ QMenu *DumpState::menu(QWidget *parent) { Q_ASSERT(parent); if (!menu_) { menu_ = new QMenu(tr("DumpState"), parent); menu_->addAction(tr("&Dump Current State"), this, SLOT(showMenu()), QKeySequence(tr("Ctrl+D"))); } return menu_; } /** * @brief DumpState::dumpCode * @param state */ void DumpState::dumpCode(const State &state) { QSettings settings; const int instructions_to_print = settings.value("DumpState/instructions_after_ip", 6).toInt(); const edb::address_t ip = state.instructionPointer(); edb::address_t address = ip; for (int i = 0; i < instructions_to_print + 1; ++i) { uint8_t buf[edb::Instruction::MaxSize]; if (const int size = edb::v1::get_instruction_bytes(address, buf)) { edb::Instruction inst(buf, buf + size, address); if (inst) { std::cout << ((address == ip) ? "> " : " ") << hex_string(address) << ": " << edb::v1::formatter().toString(inst) << "\n"; } else { break; } address += inst.byteSize(); } else { break; } } } /** * @brief DumpState::dumpRegisters * @param state */ void DumpState::dumpRegisters(const State &state) { using std::cout; if (edb::v1::debuggeeIs32Bit()) { // TODO: check if state itself is 32 bit, not current debuggee. Generally it's not the same. cout << " eax:" << format_register(state["eax"]); cout << " ecx:" << format_register(state["ecx"]); cout << " edx:" << format_register(state["edx"]); cout << " ebx:" << format_register(state["ebx"]); cout << " eflags:" << format_register(state["eflags"]); cout << "\n"; cout << " esp:" << format_register(state["esp"]); cout << " ebp:" << format_register(state["ebp"]); cout << " esi:" << format_register(state["esi"]); cout << " edi:" << format_register(state["edi"]); cout << " eip:" << format_register(state["eip"]); cout << "\n"; cout << " es:" << format_segment(state["es"]); cout << " cs:" << format_segment(state["cs"]); cout << " ss:" << format_segment(state["ss"]); cout << " ds:" << format_segment(state["ds"]); cout << " fs:" << format_segment(state["fs"]); cout << " gs:" << format_segment(state["gs"]); cout << " "; const Register eflagsR = state["eflags"]; if (eflagsR) { const auto eflags = eflagsR.value(); cout << ((eflags & (1 << 11)) != 0 ? 'O' : 'o') << ' '; cout << ((eflags & (1 << 10)) != 0 ? 'D' : 'd') << ' '; cout << ((eflags & (1 << 9)) != 0 ? 'I' : 'i') << ' '; cout << ((eflags & (1 << 8)) != 0 ? 'T' : 't') << ' '; cout << ((eflags & (1 << 7)) != 0 ? 'S' : 's') << ' '; cout << ((eflags & (1 << 6)) != 0 ? 'Z' : 'z') << ' '; cout << ((eflags & (1 << 4)) != 0 ? 'A' : 'a') << ' '; cout << ((eflags & (1 << 2)) != 0 ? 'P' : 'p') << ' '; cout << ((eflags & (1 << 0)) != 0 ? 'C' : 'c'); } cout << "\n"; } else { cout << " rax:" << format_register(state["rax"]); cout << " rcx:" << format_register(state["rcx"]); cout << " rdx:" << format_register(state["rdx"]); cout << " rbx:" << format_register(state["rbx"]); cout << " rflags:" << format_register(state["rflags"]); cout << "\n"; cout << " rsp:" << format_register(state["rsp"]); cout << " rbp:" << format_register(state["rbp"]); cout << " rsi:" << format_register(state["rsi"]); cout << " rdi:" << format_register(state["rdi"]); cout << " rip:" << format_register(state["rip"]); cout << "\n"; cout << " r8:" << format_register(state["r8"]); cout << " r9:" << format_register(state["r9"]); cout << " r10:" << format_register(state["r10"]); cout << " r11:" << format_register(state["r11"]); cout << " "; const Register rflagsR = state["rflags"]; if (rflagsR) { const auto rflags = rflagsR.value(); cout << ((rflags & (1 << 11)) != 0 ? 'O' : 'o') << ' '; cout << ((rflags & (1 << 10)) != 0 ? 'D' : 'd') << ' '; cout << ((rflags & (1 << 9)) != 0 ? 'I' : 'i') << ' '; cout << ((rflags & (1 << 8)) != 0 ? 'T' : 't') << ' '; cout << ((rflags & (1 << 7)) != 0 ? 'S' : 's') << ' '; cout << ((rflags & (1 << 6)) != 0 ? 'Z' : 'z') << ' '; cout << ((rflags & (1 << 4)) != 0 ? 'A' : 'a') << ' '; cout << ((rflags & (1 << 2)) != 0 ? 'P' : 'p') << ' '; cout << ((rflags & (1 << 0)) != 0 ? 'C' : 'c'); } cout << "\n"; cout << " r12:" << format_register(state["r12"]); cout << " r13:" << format_register(state["r13"]); cout << " r14:" << format_register(state["r14"]); cout << " r15:" << format_register(state["r15"]); cout << "\n"; cout << " es:" << format_segment(state["es"]); cout << " cs:" << format_segment(state["cs"]); cout << " ss:" << format_segment(state["ss"]); cout << " ds:" << format_segment(state["ds"]); cout << " fs:" << format_segment(state["fs"]); cout << " gs:" << format_segment(state["gs"]); cout << "\n"; } } /** * @brief DumpState::dumpLines * @param address * @param lines */ void DumpState::dumpLines(edb::address_t address, int lines) { if (IProcess *process = edb::v1::debugger_core->process()) { for (int i = 0; i < lines; ++i) { edb::value8 buf[16]; if (process->readBytes(address, buf, sizeof(buf))) { std::cout << hex_string(address) << " : "; for (int j = 0x00; j < 0x04; ++j) std::cout << hex_string(buf[j]) << " "; std::cout << " "; for (int j = 0x04; j < 0x08; ++j) std::cout << hex_string(buf[j]) << " "; std::cout << "- "; for (int j = 0x08; j < 0x0c; ++j) std::cout << hex_string(buf[j]) << " "; std::cout << " "; for (int j = 0x0c; j < 0x10; ++j) std::cout << hex_string(buf[j]) << " "; for (int j = 0; j < 16; ++j) { // TODO(eteran): why won't this compile with MSVC? const uint8_t ch = buf[j].toUint(); std::cout << ((std::isprint(ch) || (std::isspace(ch) && (ch != '\f' && ch != '\t' && ch != '\r' && ch != '\n' && ch != '\x0b') && ch < 0x80)) ? static_cast(ch) : '.'); } std::cout << "\n"; } else { break; } address += 16; } } } /** * @brief DumpState::dumpStack * @param state */ void DumpState::dumpStack(const State &state) { dumpLines(state.stackPointer(), 4); } /** * @brief DumpState::dumpData * @param address */ void DumpState::dumpData(edb::address_t address) { dumpLines(address, 2); } /** * @brief DumpState::showMenu */ void DumpState::showMenu() { if (IProcess *process = edb::v1::debugger_core->process()) { if (std::shared_ptr thread = process->currentThread()) { State state; thread->getState(&state); std::cout << "------------------------------------------------------------------------------\n"; dumpRegisters(state); std::cout << "[" << format_segment(state["ss"]) << ":" << format_address(state.stackPointer()) << "]---------------------------------------------------------[stack]\n"; dumpStack(state); const edb::address_t data_address = edb::v1::current_data_view_address(); std::cout << "[" << format_segment(state["ds"]) << ":" << format_address(data_address) << "]---------------------------------------------------------[ data]\n"; dumpData(data_address); std::cout << "[" << format_segment(state["cs"]) << ":" << format_address(state.instructionPointer()) << "]---------------------------------------------------------[ code]\n"; dumpCode(state); std::cout << "------------------------------------------------------------------------------\n"; } } } /** * @brief DumpState::optionsPage * @return */ QWidget *DumpState::optionsPage() { return new OptionsPage; } } edb-debugger/plugins/DumpState/OptionsPage.ui0000644000175000017500000000342113765535463021107 0ustar eteraneteran DumpStatePlugin::OptionsPage 0 0 443 259 Dump State Plugin Instructions To Display Before Instruction Pointer: false Instructions To Display After Instruction Pointer: 6 Colorize Output Qt::Vertical 20 190 edb-debugger/plugins/DumpState/OptionsPage.cpp0000644000175000017500000000377613765535463021271 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "OptionsPage.h" #include namespace DumpStatePlugin { /** * @brief OptionsPage::OptionsPage * @param parent * @param f */ OptionsPage::OptionsPage(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f) { ui.setupUi(this); } /** * @brief OptionsPage::showEvent * @param event */ void OptionsPage::showEvent(QShowEvent *event) { Q_UNUSED(event) QSettings settings; ui.instructionsBeforeIP->setValue(settings.value("DumpState/instructions_before_ip", 0).toInt()); ui.instructionsAfterIP->setValue(settings.value("DumpState/instructions_after_ip", 5).toInt()); ui.colorizeOutput->setChecked(settings.value("DumpState/colorize", true).toBool()); } /** * @brief OptionsPage::on_instructionsBeforeIP_valueChanged * @param i */ void OptionsPage::on_instructionsBeforeIP_valueChanged(int i) { QSettings settings; settings.setValue("DumpState/instructions_before_ip", i); } /** * @brief OptionsPage::on_instructionsAfterIP_valueChanged * @param i */ void OptionsPage::on_instructionsAfterIP_valueChanged(int i) { QSettings settings; settings.setValue("DumpState/instructions_after_ip", i); } /** * @brief OptionsPage::on_colorizeOutput_toggled * @param value */ void OptionsPage::on_colorizeOutput_toggled(bool value) { QSettings settings; settings.setValue("DumpState/colorize", value); } } edb-debugger/plugins/HeapAnalyzer/0000755000175000017500000000000013765535463016775 5ustar eteraneteranedb-debugger/plugins/HeapAnalyzer/DialogHeap.cpp0000644000175000017500000004335113765535463021504 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "DialogHeap.h" #include "Configuration.h" #include "IDebugger.h" #include "IProcess.h" #include "IRegion.h" #include "ISymbolManager.h" #include "MemoryRegions.h" #include "Module.h" #include "ResultViewModel.h" #include "Symbol.h" #include "edb.h" #include "util/Math.h" #ifdef ENABLE_GRAPH #include "GraphEdge.h" #include "GraphNode.h" #include "GraphWidget.h" #endif #include #include #include #include #include #include #include #include #include #include #include namespace HeapAnalyzerPlugin { namespace { constexpr int PreviousInUse = 0x1; constexpr int IsMMapped = 0x2; constexpr int NonMainArena = 0x4; constexpr int SizeBits = (PreviousInUse | IsMMapped | NonMainArena); // NOTE: the details of this structure are 32/64-bit sensitive! template struct malloc_chunk { using ULong = MallocChunkPtr; // ulong has the same size ULong prevSize; /* Size of previous chunk (if free). */ ULong size; /* Size in bytes, including overhead. */ MallocChunkPtr fd; /* double links -- used only if free. */ MallocChunkPtr bk; edb::address_t chunkSize() const { return edb::address_t::fromZeroExtended(size & ~(SizeBits)); } bool prevInUse() const { return size & PreviousInUse; } }; template edb::address_t next_chunk(edb::address_t p, const malloc_chunk &c) { return p + c.chunkSize(); } /** * @brief block_start * @param pointer * @return */ edb::address_t block_start(edb::address_t pointer) { return pointer + edb::v1::pointer_size() * 2; // pointer_size() is malloc_chunk* } /** * @brief block_start * @param result * @return */ edb::address_t block_start(const ResultViewModel::Result &result) { return block_start(result.address); } /** * @brief get_library_names * @param libcName * @param ldName */ void get_library_names(QString *libcName, QString *ldName) { Q_ASSERT(libcName); Q_ASSERT(ldName); if (edb::v1::debugger_core) { if (IProcess *process = edb::v1::debugger_core->process()) { const QList libs = process->loadedModules(); for (const Module &module : libs) { if (!ldName->isEmpty() && !libcName->isEmpty()) { break; } const QFileInfo fileinfo(module.name); // this tries its best to cover all possible libc library versioning // possibilities we need to find out if this is 100% accurate, so far // seems correct based on my system if (fileinfo.completeBaseName().startsWith("libc-")) { *libcName = fileinfo.completeBaseName() + "." + fileinfo.suffix(); qDebug() << "[Heap Analyzer] libc library appears to be:" << *libcName; continue; } if (fileinfo.completeBaseName().startsWith("libc.so")) { *libcName = fileinfo.completeBaseName() + "." + fileinfo.suffix(); qDebug() << "[Heap Analyzer] libc library appears to be:" << *libcName; continue; } if (fileinfo.completeBaseName().startsWith("ld-")) { *ldName = fileinfo.completeBaseName() + "." + fileinfo.suffix(); qDebug() << "[Heap Analyzer] ld library appears to be:" << *ldName; continue; } } } } } } /** * @brief DialogHeap::DialogHeap * @param parent * @param f */ DialogHeap::DialogHeap(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f) { ui.setupUi(this); model_ = new ResultViewModel(this); filterModel_ = new QSortFilterProxyModel(this); connect(ui.lineEdit, &QLineEdit::textChanged, filterModel_, &QSortFilterProxyModel::setFilterFixedString); filterModel_->setFilterKeyColumn(3); filterModel_->setSourceModel(model_); ui.tableView->setModel(filterModel_); ui.tableView->verticalHeader()->hide(); ui.tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); buttonAnalyze_ = new QPushButton(QIcon::fromTheme("edit-find"), tr("Analyze")); buttonGraph_ = new QPushButton(QIcon::fromTheme("distribute-graph"), tr("&Graph Selected Blocks")); connect(buttonAnalyze_, &QPushButton::clicked, this, [this]() { buttonAnalyze_->setEnabled(false); ui.progressBar->setValue(0); ui.tableView->setUpdatesEnabled(false); if (edb::v1::debuggeeIs32Bit()) { doFind(); } else { doFind(); } ui.tableView->setUpdatesEnabled(true); ui.progressBar->setValue(100); buttonAnalyze_->setEnabled(true); }); connect(buttonGraph_, &QPushButton::clicked, this, [this]() { #ifdef ENABLE_GRAPH constexpr int MaxNodes = 5000; auto graph = new GraphWidget(nullptr); graph->setAttribute(Qt::WA_DeleteOnClose); do { QMap nodes; QStack result_stack; QSet seen_results; QMap result_map = createResultMap(); // seed our search with the selected blocks const QItemSelectionModel *const selModel = ui.tableView->selectionModel(); const QModelIndexList sel = selModel->selectedRows(); if (sel.size() != 0) { for (const QModelIndex &index : sel) { const QModelIndex idx = filterModel_->mapToSource(index); auto item = static_cast(idx.internalPointer()); result_stack.push(item); seen_results.insert(item); } } while (!result_stack.isEmpty()) { const ResultViewModel::Result *const result = result_stack.pop(); GraphNode *node = new GraphNode(graph, edb::v1::format_pointer(result->address), result->type == ResultViewModel::Result::Busy ? Qt::lightGray : Qt::red); nodes.insert(result->address, node); for (edb::address_t pointer : result->pointers) { const ResultViewModel::Result *next_result = result_map[pointer]; if (!seen_results.contains(next_result)) { seen_results.insert(next_result); result_stack.push(next_result); } } } qDebug("[Heap Analyzer] Done Processing %d Nodes", nodes.size()); if (nodes.size() > MaxNodes) { qDebug("[Heap Analyzer] Too Many Nodes! (%d)", nodes.size()); delete graph; return; } Q_FOREACH (const ResultViewModel::Result *result, result_map) { const edb::address_t addr = result->address; if (nodes.contains(addr)) { for (edb::address_t pointer : result->pointers) { new GraphEdge(nodes[addr], nodes[pointer]); } } } qDebug("[Heap Analyzer] Done Processing Edges"); } while (0); graph->layout(); graph->show(); #endif }); ui.buttonBox->addButton(buttonGraph_, QDialogButtonBox::ActionRole); ui.buttonBox->addButton(buttonAnalyze_, QDialogButtonBox::ActionRole); #ifdef ENABLE_GRAPH buttonGraph_->setEnabled(true); #else buttonGraph_->setEnabled(false); #endif } /** * @brief DialogHeap::showEvent */ void DialogHeap::showEvent(QShowEvent *) { model_->clearResults(); ui.progressBar->setValue(0); } /** * @brief DialogHeap::on_tableView_doubleClicked * @param index */ void DialogHeap::on_tableView_doubleClicked(const QModelIndex &index) { const QModelIndex idx = filterModel_->mapToSource(index); if (auto item = static_cast(idx.internalPointer())) { edb::v1::dump_data_range(item->address, item->address + item->size, false); } } /** * @brief DialogHeap::processPotentialPointers * @param targets * @param index */ void DialogHeap::processPotentialPointers(const QHash &targets, const QModelIndex &index) { if (auto result = static_cast(index.internalPointer())) { std::vector pointers; if (IProcess *process = edb::v1::debugger_core->process()) { if (result->dataType == ResultViewModel::Result::Unknown) { edb::address_t pointer(0); edb::address_t block_ptr = block_start(*result); edb::address_t block_end = block_ptr + result->size; while (block_ptr < block_end) { if (process->readBytes(block_ptr, &pointer, edb::v1::pointer_size())) { auto it = targets.find(pointer); if (it != targets.end()) { pointers.push_back(it.value()); } } block_ptr += edb::v1::pointer_size(); } if (!pointers.empty()) { model_->setPointerData(index, pointers); } } } } } /** * @brief DialogHeap::detectPointers */ void DialogHeap::detectPointers() { qDebug() << "[Heap Analyzer] detecting pointers in heap blocks"; QHash targets; qDebug() << "[Heap Analyzer] collecting possible targets addresses"; for (int row = 0; row < model_->rowCount(); ++row) { QModelIndex index = model_->index(row, 0); if (auto result = static_cast(index.internalPointer())) { edb::address_t block_ptr = block_start(*result); edb::address_t block_end = block_ptr + result->size; while (block_ptr < block_end) { targets.insert(block_ptr, result->address); block_ptr += edb::v1::pointer_size(); } } } qDebug() << "[Heap Analyzer] linking blocks to taget addresses"; for (int row = 0; row < model_->rowCount(); ++row) { QModelIndex index = model_->index(row, 0); processPotentialPointers(targets, index); } } /** * @brief DialogHeap::collectBlocks * @param start_address * @param end_address */ template void DialogHeap::collectBlocks(edb::address_t start_address, edb::address_t end_address) { model_->clearResults(); ui.labelFree->setText(tr("Free Blocks: ?")); ui.labelBusy->setText(tr("Busy Blocks: ?")); ui.labelTotal->setText(tr("Total: ?")); int64_t freeBlocks = 0; int64_t busyBlocks = 0; if (IProcess *process = edb::v1::debugger_core->process()) { const int min_string_length = edb::v1::config().min_string_length; if (start_address != 0 && end_address != 0) { #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) malloc_chunk currentChunk; malloc_chunk nextChunk; edb::address_t currentChunkAddress = start_address; const edb::address_t how_many = end_address - start_address; while (currentChunkAddress != end_address) { // read in the current chunk.. process->readBytes(currentChunkAddress, ¤tChunk, sizeof(currentChunk)); // figure out the address of the next chunk const edb::address_t nextChunkAddress = next_chunk(currentChunkAddress, currentChunk); // is this the last chunk (if so, it's the 'top') if (nextChunkAddress == end_address) { model_->addResult({currentChunkAddress, currentChunk.chunkSize(), ResultViewModel::Result::Top, ResultViewModel::Result::Unknown, {}, {}}); } else { // make sure we aren't following a broken heap... if (nextChunkAddress > end_address || nextChunkAddress < start_address) { break; } QString data; ResultViewModel::Result::DataType data_type = ResultViewModel::Result::Unknown; // read in the next chunk process->readBytes(nextChunkAddress, &nextChunk, sizeof(nextChunk)); // if this block is a container for an ascii string, display it... // there is a lot of room for improvement here, but it's a start QString asciiData; QString utf16Data; int asciisz; int utf16sz; if (edb::v1::get_ascii_string_at_address( block_start(currentChunkAddress), asciiData, min_string_length, currentChunk.chunkSize(), asciisz)) { data = asciiData; data_type = ResultViewModel::Result::Ascii; } else if (edb::v1::get_utf16_string_at_address( block_start(currentChunkAddress), utf16Data, min_string_length, currentChunk.chunkSize(), utf16sz)) { data = utf16Data; data_type = ResultViewModel::Result::Utf16; } else { using std::memcmp; uint8_t bytes[16]; process->readBytes(block_start(currentChunkAddress), bytes, sizeof(bytes)); if (memcmp(bytes, "\x89\x50\x4e\x47", 4) == 0) { data_type = ResultViewModel::Result::Png; } else if (memcmp(bytes, "\x2f\x2a\x20\x58\x50\x4d\x20\x2a\x2f", 9) == 0) { data_type = ResultViewModel::Result::Xpm; } else if (memcmp(bytes, "\x42\x5a", 2) == 0) { data_type = ResultViewModel::Result::Bzip; } else if (memcmp(bytes, "\x1f\x9d", 2) == 0) { data_type = ResultViewModel::Result::Compress; } else if (memcmp(bytes, "\x1f\x8b", 2) == 0) { data_type = ResultViewModel::Result::Gzip; } } // TODO(eteran): should this be unsigned int? Or should it be sizeof(value32)/sizeof(value64)? const ResultViewModel::Result r{ currentChunkAddress, currentChunk.chunkSize() + sizeof(unsigned int), nextChunk.prevInUse() ? ResultViewModel::Result::Busy : ResultViewModel::Result::Free, data_type, data, {}}; if (nextChunk.prevInUse()) { ++busyBlocks; } else { ++freeBlocks; } model_->addResult(r); } // avoif self referencing blocks if (currentChunkAddress == nextChunkAddress) { break; } currentChunkAddress = nextChunkAddress; ui.progressBar->setValue(util::percentage(currentChunkAddress - start_address, how_many)); } detectPointers(); ui.labelFree->setText(tr("Free Blocks: %1").arg(freeBlocks)); ui.labelBusy->setText(tr("Busy Blocks: %1").arg(busyBlocks)); ui.labelTotal->setText(tr("Total: %1").arg(freeBlocks + busyBlocks)); #else #error "Unsupported Platform" #endif } } } /** * @brief DialogHeap::findHeapStartHeuristic * @param end_address * @param offset * @return */ edb::address_t DialogHeap::findHeapStartHeuristic(edb::address_t end_address, size_t offset) const { const edb::address_t start_address = end_address - offset; const edb::address_t heap_symbol = start_address - 4 * edb::v1::pointer_size(); edb::address_t test_addr(0); if (IProcess *process = edb::v1::debugger_core->process()) { process->readBytes(heap_symbol, &test_addr, edb::v1::pointer_size()); if (test_addr != edb::v1::debugger_core->pageSize()) { return 0; } return start_address; } return 0; } /** * @brief DialogHeap::do_find */ template void DialogHeap::doFind() { // get both the libc and ld symbols of __curbrk // this will be the 'before/after libc' addresses if (IProcess *process = edb::v1::debugger_core->process()) { edb::address_t start_address = 0; edb::address_t end_address = 0; QString libcName; QString ldName; get_library_names(&libcName, &ldName); #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) if (std::shared_ptr s = edb::v1::symbol_manager().find(libcName + "::__curbrk")) { end_address = s->address; } else { qDebug() << "[Heap Analyzer] __curbrk symbol not found in libc, falling back on heuristic! This may or may not work."; } if (std::shared_ptr s = edb::v1::symbol_manager().find(ldName + "::__curbrk")) { start_address = s->address; } else { qDebug() << "[Heap Analyzer] __curbrk symbol not found in ld, falling back on heuristic! This may or may not work."; for (edb::address_t offset = 0x0000; offset != 0x1000; offset += edb::v1::pointer_size()) { start_address = findHeapStartHeuristic(end_address, offset); if (start_address != 0) { break; } } } if (start_address != 0 && end_address != 0) { qDebug() << "[Heap Analyzer] heap start symbol : " << edb::v1::format_pointer(start_address); qDebug() << "[Heap Analyzer] heap end symbol : " << edb::v1::format_pointer(end_address); // read the contents of those symbols process->readBytes(end_address, &end_address, edb::v1::pointer_size()); process->readBytes(start_address, &start_address, edb::v1::pointer_size()); } // just assume it's the bounds of the [heap] memory region for now if (start_address == 0 || end_address == 0) { const QList> ®ions = edb::v1::memory_regions().regions(); auto it = std::find_if(regions.begin(), regions.end(), [](const std::shared_ptr ®ion) { return region->name() == "[heap]"; }); if (it != regions.end()) { qDebug() << "Found a memory region named '[heap]', assuming that it provides sane bounds"; if (start_address == 0) { start_address = (*it)->start(); } if (end_address == 0) { end_address = (*it)->end(); } } } // ok, I give up if (start_address == 0 || end_address == 0) { QMessageBox::critical(this, tr("Could not calculate heap bounds"), tr("Failed to calculate the bounds of the heap.")); return; } #else #error "Unsupported Platform" #endif qDebug() << "[Heap Analyzer] heap start : " << edb::v1::format_pointer(start_address); qDebug() << "[Heap Analyzer] heap end : " << edb::v1::format_pointer(end_address); collectBlocks(start_address, end_address); } } /** * @brief DialogHeap::createResultMap * @return */ QMap DialogHeap::createResultMap() const { const QVector &results = model_->results(); QMap result_map; // first we make a nice index for our results, this is likely redundant, // but won't take long for (const ResultViewModel::Result &result : results) { result_map.insert(result.address, &result); } return result_map; } } edb-debugger/plugins/HeapAnalyzer/ResultViewModel.h0000644000175000017500000000415313765535463022243 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef RESULT_VIEW_MODEL_H_20070419_ #define RESULT_VIEW_MODEL_H_20070419_ #include "Types.h" #include #include #include namespace HeapAnalyzerPlugin { class ResultViewModel : public QAbstractItemModel { Q_OBJECT public: struct Result { enum DataType { Unknown, Pointer, Png, Xpm, Bzip, Compress, Gzip, Ascii, Utf16 }; enum NodeType { Top, Free, Busy }; edb::address_t address = 0; edb::address_t size = 0; NodeType type; DataType dataType = Unknown; QString data; std::vector pointers; }; public: explicit ResultViewModel(QObject *parent = nullptr); public: QVariant data(const QModelIndex &index, int role) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &index) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; public: void addResult(const Result &r); void clearResults(); void setPointerData(const QModelIndex &index, const std::vector &pointers); public: const QVector &results() const { return results_; } private: QVector results_; }; } #endif edb-debugger/plugins/HeapAnalyzer/CMakeLists.txt0000644000175000017500000000160513765535463021537 0ustar eteraneterancmake_minimum_required (VERSION 3.1) include("GNUInstallDirs") set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(PluginName "HeapAnalyzer") find_package(Qt5 5.0.0 REQUIRED Widgets Concurrent) add_library(${PluginName} SHARED DialogHeap.cpp DialogHeap.h DialogHeap.ui HeapAnalyzer.cpp HeapAnalyzer.h ResultViewModel.cpp ResultViewModel.h ) target_link_libraries(${PluginName} Qt5::Widgets Qt5::Concurrent edb) install (TARGETS ${PluginName} DESTINATION ${CMAKE_INSTALL_LIBDIR}/edb) set_property(TARGET ${PluginName} PROPERTY CXX_EXTENSIONS OFF) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD 14) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD_REQUIRED ON) set_property(TARGET ${PluginName} PROPERTY LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) set_property(TARGET ${PluginName} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) edb-debugger/plugins/HeapAnalyzer/ResultViewModel.cpp0000644000175000017500000001146613765535463022603 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "ResultViewModel.h" #include "edb.h" #include namespace HeapAnalyzerPlugin { /** * @brief ResultViewModel::ResultViewModel * @param parent */ ResultViewModel::ResultViewModel(QObject *parent) : QAbstractItemModel(parent) { } /** * @brief ResultViewModel::headerData * @param section * @param orientation * @param role * @return */ QVariant ResultViewModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { switch (section) { case 0: return tr("Block"); case 1: return tr("Size"); case 2: return tr("Type"); case 3: return tr("Data"); } } return QVariant(); } /** * @brief ResultViewModel::data * @param index * @param role * @return */ QVariant ResultViewModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } if (role != Qt::DisplayRole) { return QVariant(); } const Result &result = results_[index.row()]; switch (index.column()) { case 0: return edb::v1::format_pointer(result.address); case 1: return edb::v1::format_pointer(result.size); case 2: switch (result.type) { case Result::Top: return tr("Top"); case Result::Busy: return tr("Busy"); case Result::Free: return tr("Free"); } return QVariant(); case 3: { switch (result.dataType) { case Result::Pointer: { QStringList pointers; if (edb::v1::debuggeeIs32Bit()) { std::transform(result.pointers.begin(), result.pointers.end(), std::back_inserter(pointers), [](const edb::address_t pointer) -> QString { return QString("dword ptr [%1]").arg(edb::v1::format_pointer(pointer)); }); } else { std::transform(result.pointers.begin(), result.pointers.end(), std::back_inserter(pointers), [](const edb::address_t pointer) -> QString { return QString("qword ptr [%1]").arg(edb::v1::format_pointer(pointer)); }); } return pointers.join("|"); } case Result::Png: return tr("PNG IMAGE"); case Result::Xpm: return tr("XPM IMAGE"); case Result::Bzip: return tr("BZIP FILE"); case Result::Compress: return tr("COMPRESS FILE"); case Result::Gzip: return tr("GZIP FILE"); case Result::Ascii: return tr("ASCII \"%1\"").arg(result.data); case Result::Utf16: return tr("UTF-16 \"%1\"").arg(result.data); case Result::Unknown: return QVariant(); } return QVariant(); } default: return QVariant(); } } /** * @brief ResultViewModel::addResult * @param r */ void ResultViewModel::addResult(const Result &r) { beginInsertRows(QModelIndex(), rowCount(), rowCount()); results_.push_back(r); endInsertRows(); } /** * @brief ResultViewModel::clearResults */ void ResultViewModel::clearResults() { beginResetModel(); results_.clear(); endResetModel(); } /** * @brief ResultViewModel::index * @param row * @param column * @param parent * @return */ QModelIndex ResultViewModel::index(int row, int column, const QModelIndex &parent) const { Q_UNUSED(parent) if (row >= results_.size()) { return QModelIndex(); } if (column >= 4) { return QModelIndex(); } if (row >= 0) { return createIndex(row, column, const_cast(&results_[row])); } else { return createIndex(row, column); } } /** * @brief ResultViewModel::parent * @param index * @return */ QModelIndex ResultViewModel::parent(const QModelIndex &index) const { Q_UNUSED(index) return QModelIndex(); } /** * @brief ResultViewModel::rowCount * @param parent * @return */ int ResultViewModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) return results_.size(); } /** * @brief ResultViewModel::columnCount * @param parent * @return */ int ResultViewModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent) return 4; } /** * @brief ResultViewModel::setPointerData * @param index * @param data */ void ResultViewModel::setPointerData(const QModelIndex &index, const std::vector &pointers) { if (!index.isValid()) { return; } Result &result = results_[index.row()]; result.pointers = pointers; result.dataType = Result::Pointer; Q_EMIT dataChanged(index, index); } } edb-debugger/plugins/HeapAnalyzer/HeapAnalyzer.h0000644000175000017500000000250513765535463021533 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef HEAP_ANALYZER_H_20060430_ #define HEAP_ANALYZER_H_20060430_ #include "IPlugin.h" class QMenu; class QDialog; namespace HeapAnalyzerPlugin { class HeapAnalyzer : public QObject, public IPlugin { Q_OBJECT Q_INTERFACES(IPlugin) Q_PLUGIN_METADATA(IID "edb.IPlugin/1.0") Q_CLASSINFO("author", "Evan Teran") Q_CLASSINFO("url", "http://www.codef00.com") public: explicit HeapAnalyzer(QObject *parent = nullptr); ~HeapAnalyzer() override; public: QMenu *menu(QWidget *parent = nullptr) override; public Q_SLOTS: void showMenu(); private: QMenu *menu_ = nullptr; QPointer dialog_ = nullptr; }; } #endif edb-debugger/plugins/HeapAnalyzer/DialogHeap.h0000644000175000017500000000362313765535463021147 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DIALOG_HEAP_H_20061101_ #define DIALOG_HEAP_H_20061101_ #include "ResultViewModel.h" #include "Types.h" #include "ui_DialogHeap.h" #include class QSortFilterProxyModel; namespace HeapAnalyzerPlugin { class DialogHeap : public QDialog { Q_OBJECT public: explicit DialogHeap(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~DialogHeap() override = default; public Q_SLOTS: void on_tableView_doubleClicked(const QModelIndex &index); private: void showEvent(QShowEvent *event) override; private: void detectPointers(); void processPotentialPointers(const QHash &targets, const QModelIndex &index); edb::address_t findHeapStartHeuristic(edb::address_t end_address, size_t offset) const; QMap createResultMap() const; private: template void collectBlocks(edb::address_t start_address, edb::address_t end_address); template void doFind(); private: Ui::DialogHeap ui; ResultViewModel *model_ = nullptr; QSortFilterProxyModel *filterModel_ = nullptr; QPushButton *buttonAnalyze_ = nullptr; QPushButton *buttonGraph_ = nullptr; }; } #endif edb-debugger/plugins/HeapAnalyzer/DialogHeap.ui0000644000175000017500000001011413765535463021326 0ustar eteraneteran Evan Teran HeapAnalyzerPlugin::DialogHeap 0 0 792 454 Heap Analyzer Results: Free Blocks: ? Qt::Horizontal 40 20 Busy Blocks: ? Qt::Horizontal 40 20 Total: ? Filter true Monospace QAbstractItemView::NoEditTriggers true QAbstractItemView::ContiguousSelection QAbstractItemView::SelectRows false true QDialogButtonBox::Close 0 Qt::Horizontal tableView buttonBox accepted() HeapAnalyzerPlugin::DialogHeap accept() 723 356 668 339 buttonBox rejected() HeapAnalyzerPlugin::DialogHeap reject() 758 355 353 339 edb-debugger/plugins/HeapAnalyzer/HeapAnalyzer.cpp0000644000175000017500000000277013765535463022072 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "HeapAnalyzer.h" #include "DialogHeap.h" #include "edb.h" #include namespace HeapAnalyzerPlugin { /** * @brief HeapAnalyzer::HeapAnalyzer * @param parent */ HeapAnalyzer::HeapAnalyzer(QObject *parent) : QObject(parent) { } /** * @brief HeapAnalyzer::~HeapAnalyzer */ HeapAnalyzer::~HeapAnalyzer() { delete dialog_; } /** * @brief HeapAnalyzer::menu * @param parent * @return */ QMenu *HeapAnalyzer::menu(QWidget *parent) { Q_ASSERT(parent); if (!menu_) { menu_ = new QMenu(tr("HeapAnalyzer"), parent); menu_->addAction(tr("&Heap Analyzer"), this, SLOT(showMenu()), QKeySequence(tr("Ctrl+H"))); } return menu_; } /** * @brief HeapAnalyzer::showMenu */ void HeapAnalyzer::showMenu() { if (!dialog_) { dialog_ = new DialogHeap(edb::v1::debugger_ui); } dialog_->show(); } } edb-debugger/plugins/Analyzer/0000755000175000017500000000000013765535463016177 5ustar eteraneteranedb-debugger/plugins/Analyzer/OptionsPage.h0000644000175000017500000000223613765535463020603 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef OPTIONS_PAGE_H_20090706_ #define OPTIONS_PAGE_H_20090706_ #include "ui_OptionsPage.h" #include namespace AnalyzerPlugin { class OptionsPage : public QWidget { Q_OBJECT public: explicit OptionsPage(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~OptionsPage() override = default; public: void showEvent(QShowEvent *event) override; private: void checkBoxToggled(bool checked = false); private: Ui::OptionsPage ui; }; } #endif edb-debugger/plugins/Analyzer/AnalyzerWidget.h0000644000175000017500000000246613765535463021311 0ustar eteraneteran/* Copyright (C) 2013 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ANALYZER_WIDGET_H_20190412_ #define ANALYZER_WIDGET_H_20190412_ #include #include class QPixmap; namespace AnalyzerPlugin { class AnalyzerWidget final : public QWidget { Q_OBJECT public: AnalyzerWidget(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); protected: void paintEvent(QPaintEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; private: std::unique_ptr cache_; bool mousePressed_ = false; int cacheNumFuncs_ = 0; }; } #endif edb-debugger/plugins/Analyzer/DialogXRefs.ui0000644000175000017500000000331413765535463020706 0ustar eteraneteran Evan Teran AnalyzerPlugin::DialogXRefs 0 0 432 308 X-Refs For %1 Monospace Qt::Horizontal QDialogButtonBox::Close buttonBox rejected() AnalyzerPlugin::DialogXRefs reject() 371 285 3 251 buttonBox accepted() AnalyzerPlugin::DialogXRefs accept() 139 287 105 265 edb-debugger/plugins/Analyzer/SpecifiedFunctions.h0000644000175000017500000000275313765535463022143 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DIALOG_SPECIFIED_FUNCTIONS_H_20070705_ #define DIALOG_SPECIFIED_FUNCTIONS_H_20070705_ #include "ui_SpecifiedFunctions.h" #include class QStringListModel; class QSortFilterProxyModel; class QModelIndex; namespace AnalyzerPlugin { class SpecifiedFunctions : public QDialog { Q_OBJECT public: explicit SpecifiedFunctions(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~SpecifiedFunctions() override = default; public Q_SLOTS: void on_function_list_doubleClicked(const QModelIndex &index); private: void showEvent(QShowEvent *event) override; private: void doFind(); private: Ui::SpecifiedFunctions ui; QStringListModel *model_ = nullptr; QSortFilterProxyModel *filterModel_ = nullptr; QPushButton *buttonRefresh_ = nullptr; }; } #endif edb-debugger/plugins/Analyzer/CMakeLists.txt0000644000175000017500000000172413765535463020743 0ustar eteraneterancmake_minimum_required (VERSION 3.1) include("GNUInstallDirs") set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(PluginName "Analyzer") find_package(Qt5 5.0.0 REQUIRED Widgets) add_library(${PluginName} SHARED Analyzer.cpp Analyzer.h AnalyzerWidget.cpp AnalyzerWidget.h DialogXRefs.cpp DialogXRefs.h DialogXRefs.ui OptionsPage.cpp OptionsPage.h OptionsPage.ui SpecifiedFunctions.cpp SpecifiedFunctions.h SpecifiedFunctions.ui ) target_link_libraries(${PluginName} Qt5::Widgets edb) install (TARGETS ${PluginName} DESTINATION ${CMAKE_INSTALL_LIBDIR}/edb) set_property(TARGET ${PluginName} PROPERTY CXX_EXTENSIONS OFF) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD 14) set_property(TARGET ${PluginName} PROPERTY CXX_STANDARD_REQUIRED ON) set_property(TARGET ${PluginName} PROPERTY LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) set_property(TARGET ${PluginName} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) edb-debugger/plugins/Analyzer/Analyzer.h0000644000175000017500000000661513765535463020145 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ANALYZER_H_20080630_ #define ANALYZER_H_20080630_ #include "BasicBlock.h" #include "IAnalyzer.h" #include "IPlugin.h" #include "IRegion.h" #include "Symbol.h" #include "Types.h" #include #include #include #include #include class QMenu; namespace AnalyzerPlugin { class AnalyzerWidget; class Analyzer final : public QObject, public IAnalyzer, public IPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "edb.IPlugin/1.0") Q_INTERFACES(IPlugin) Q_CLASSINFO("author", "Evan Teran") Q_CLASSINFO("url", "http://www.codef00.com") private: struct RegionData; public: explicit Analyzer(QObject *parent = nullptr); public: QMenu *menu(QWidget *parent = nullptr) override; QList cpuContextMenu() override; private: void privateInit() override; QWidget *optionsPage() override; public: AddressCategory category(edb::address_t address) const override; FunctionMap functions(const std::shared_ptr ®ion) const override; FunctionMap functions() const override; QSet specifiedFunctions() const override { return specifiedFunctions_; } Result findContainingFunction(edb::address_t address) const override; void analyze(const std::shared_ptr ®ion) override; void invalidateAnalysis() override; void invalidateAnalysis(const std::shared_ptr ®ion) override; bool forFuncsInRange(edb::address_t start, edb::address_t end, std::function functor) const override; private: bool findContainingFunction(edb::address_t address, Function *function) const; void bonusEntryPoint(RegionData *data) const; void bonusMain(RegionData *data) const; void bonusMarkedFunctions(RegionData *data); void bonusSymbols(RegionData *data); void collectFunctions(RegionData *data); void collectFuzzyFunctions(RegionData *data); void doAnalysis(const std::shared_ptr ®ion); void identHeader(Analyzer::RegionData *data); void invalidateDynamicAnalysis(const std::shared_ptr ®ion); Q_SIGNALS: void updateProgress(int); public Q_SLOTS: void doIpAnalysis(); void doViewAnalysis(); void gotoFunctionStart(); void gotoFunctionEnd(); void markFunctionStart(); void showXrefs(); void showSpecified(); private: struct RegionData { QSet knownFunctions; QSet fuzzyFunctions; FunctionMap functions; QHash basicBlocks; QByteArray md5; bool fuzzy; std::shared_ptr region; // a copy of the whole region QVector memory; }; QMenu *menu_ = nullptr; AnalyzerWidget *analyzerWidget_ = nullptr; QHash analysisInfo_; QSet specifiedFunctions_; }; } #endif edb-debugger/plugins/Analyzer/DialogXRefs.h0000644000175000017500000000113313765535463020515 0ustar eteraneteran #ifndef DIALOG_XREFS_H_20191119_ #define DIALOG_XREFS_H_20191119_ #include "edb.h" #include "ui_DialogXRefs.h" #include #include class QListWidgetItem; namespace AnalyzerPlugin { class DialogXRefs : public QDialog { Q_OBJECT public: explicit DialogXRefs(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~DialogXRefs() override = default; public Q_SLOTS: void on_listReferences_itemDoubleClicked(QListWidgetItem *item); public: void addReference(const std::pair &reference); private: Ui::DialogXRefs ui; }; } #endif edb-debugger/plugins/Analyzer/DialogXRefs.cpp0000644000175000017500000000175313765535463021060 0ustar eteraneteran #include "DialogXRefs.h" namespace AnalyzerPlugin { /** * @brief DialogXRefs::DialogXRefs * @param parent */ DialogXRefs::DialogXRefs(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f) { ui.setupUi(this); } /** * @brief DialogXRefs::on_listReferences_itemDoubleClicked * @param item */ void DialogXRefs::on_listReferences_itemDoubleClicked(QListWidgetItem *item) { edb::address_t site = item->data(Qt::UserRole).toULongLong(); edb::v1::jump_to_address(site); } /** * @brief DialogXRefs::addReference * @param ref */ void DialogXRefs::addReference(const std::pair &ref) { int offset; QString sym = edb::v1::find_function_symbol(ref.first, ref.first.toPointerString(), &offset); auto string = tr("%1. %2 -> %3").arg(ui.listReferences->count() + 1, 2, 10, QChar('0')).arg(sym).arg(ref.second.toPointerString()); auto item = new QListWidgetItem(string, ui.listReferences); item->setData(Qt::UserRole, static_cast(ref.first)); } } edb-debugger/plugins/Analyzer/Analyzer.cpp0000644000175000017500000005614013765535463020476 0ustar eteraneteran/* Copyright (C) 2006 - 2015 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "Analyzer.h" #include "AnalyzerWidget.h" #include "Configuration.h" #include "DialogXRefs.h" #include "Function.h" #include "IBinary.h" #include "IDebugger.h" #include "IProcess.h" #include "ISymbolManager.h" #include "IThread.h" #include "Instruction.h" #include "MemoryRegions.h" #include "OptionsPage.h" #include "Prototype.h" #include "SpecifiedFunctions.h" #include "State.h" #include "edb.h" #include "util/Math.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace AnalyzerPlugin { namespace { constexpr int MinRefCount = 2; /** * @brief will_return * @param address * @return */ bool will_return(edb::address_t address) { const std::shared_ptr symbol = edb::v1::symbol_manager().find(address); if (symbol) { const QString symname = symbol->name_no_prefix; const QString func_name = symname.mid(0, symname.indexOf("@")); if (const edb::Prototype *const info = edb::v1::get_function_info(func_name)) { if (info->noreturn) { return false; } } } return true; } /** * @brief is_entrypoint * @param sym * @return */ bool is_entrypoint(const Symbol &sym) { #ifdef Q_OS_UNIX return sym.name_no_prefix == "_start"; #else return false; #endif } /** * @brief is_thunk * @param address * @return true if the first instruction of the function is a jmp */ bool is_thunk(edb::address_t address) { uint8_t buf[edb::Instruction::MaxSize]; if (const int buf_size = edb::v1::get_instruction_bytes(address, buf)) { const edb::Instruction inst(buf, buf + buf_size, address); return is_unconditional_jump(inst); } return false; } /** * @brief set_function_types * @param results */ void set_function_types(IAnalyzer::FunctionMap *results) { Q_ASSERT(results); // give bonus if we have a symbol for the address std::for_each(results->begin(), results->end(), [](Function &function) { if (is_thunk(function.entryAddress())) { function.setType(Function::Thunk); } else { function.setType(Function::Standard); } }); } /** * @brief module_entry_point * @param region * @return */ edb::address_t module_entry_point(const std::shared_ptr ®ion) { if (std::unique_ptr binary_info = edb::v1::get_binary_info(region)) { return binary_info->entryPoint(); } return 0; } } /** * @brief Analyzer::Analyzer * @param parent */ Analyzer::Analyzer(QObject *parent) : QObject(parent) { } /** * @brief Analyzer::optionsPage * @return */ QWidget *Analyzer::optionsPage() { return new OptionsPage; } /** * @brief Analyzer::menu * @param parent * @return */ QMenu *Analyzer::menu(QWidget *parent) { Q_ASSERT(parent); if (!menu_) { menu_ = new QMenu(tr("Analyzer"), parent); menu_->addAction(tr("Show &Specified Functions"), this, SLOT(showSpecified())); if (edb::v1::debugger_core) { menu_->addAction(tr("&Analyze %1's Region").arg(edb::v1::debugger_core->instructionPointer().toUpper()), this, SLOT(doIpAnalysis()), QKeySequence(tr("Ctrl+A"))); } menu_->addAction(tr("&Analyze Viewed Region"), this, SLOT(doViewAnalysis()), QKeySequence(tr("Ctrl+Shift+A"))); // if we are dealing with a main window (and we are...) // add the dock object if (auto main_window = qobject_cast(edb::v1::debugger_ui)) { analyzerWidget_ = new AnalyzerWidget; // make the toolbar widget and _name_ it, it is important to name it so // that it's state is saved in the GUI info auto toolbar = new QToolBar(tr("Region Analysis"), main_window); toolbar->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea); toolbar->setObjectName(QString::fromUtf8("Region Analysis")); toolbar->addWidget(analyzerWidget_); // add it to the dock main_window->addToolBar(Qt::TopToolBarArea, toolbar); // make the menu and add the show/hide toggle for the widget menu_->addAction(toolbar->toggleViewAction()); } } return menu_; } /** * @brief Analyzer::privateInit */ void Analyzer::privateInit() { edb::v1::set_analyzer(this); } /** * @brief Analyzer::showSpecified */ void Analyzer::showSpecified() { static auto dialog = new SpecifiedFunctions(edb::v1::debugger_ui); dialog->show(); } /** * @brief Analyzer::doIpAnalysis */ void Analyzer::doIpAnalysis() { if (IProcess *process = edb::v1::debugger_core->process()) { if (std::shared_ptr thread = process->currentThread()) { State state; thread->getState(&state); const edb::address_t address = state.instructionPointer(); if (std::shared_ptr region = edb::v1::memory_regions().findRegion(address)) { doAnalysis(region); } } } } /** * @brief Analyzer::doViewAnalysis */ void Analyzer::doViewAnalysis() { doAnalysis(edb::v1::current_cpu_view_region()); } /** * @brief Analyzer::markFunctionStart */ void Analyzer::markFunctionStart() { const edb::address_t address = edb::v1::cpu_selected_address(); if (std::shared_ptr region = edb::v1::memory_regions().findRegion(address)) { qDebug("Added %s to the list of known functions", qPrintable(address.toPointerString())); specifiedFunctions_.insert(address); invalidateDynamicAnalysis(region); } } /** * @brief Analyzer::showXrefs */ void Analyzer::showXrefs() { const edb::address_t address = edb::v1::cpu_selected_address(); auto dialog = new DialogXRefs(edb::v1::debugger_ui); for (const RegionData &data : analysisInfo_) { for (const BasicBlock &bb : data.basicBlocks) { const std::vector> refs = bb.references(); for (auto it = refs.begin(); it != refs.end(); ++it) { if (it->second == address) { dialog->addReference(*it); } } } } dialog->setWindowTitle(tr("X-Refs For %1").arg(address.toPointerString())); dialog->show(); } /** * @brief Analyzer::gotoFunctionStart */ void Analyzer::gotoFunctionStart() { const edb::address_t address = edb::v1::cpu_selected_address(); Function function; if (findContainingFunction(address, &function)) { edb::v1::jump_to_address(function.entryAddress()); return; } QMessageBox::critical( nullptr, tr("Goto Function Start"), tr("The selected instruction is not inside of a known function. Have you run an analysis of this region?")); } /** * @brief Analyzer::gotoFunctionEnd */ void Analyzer::gotoFunctionEnd() { const edb::address_t address = edb::v1::cpu_selected_address(); Function function; if (findContainingFunction(address, &function)) { edb::v1::jump_to_address(function.lastInstruction()); return; } QMessageBox::critical( nullptr, tr("Goto Function End"), tr("The selected instruction is not inside of a known function. Have you run an analysis of this region?")); } /** * @brief Analyzer::cpuContextMenu * @return */ QList Analyzer::cpuContextMenu() { QList ret; auto action_find = new QAction(tr("Analyze Here"), this); auto action_goto_function_start = new QAction(tr("Goto Function Start"), this); auto action_goto_function_end = new QAction(tr("Goto Function End"), this); auto action_mark_function_start = new QAction(tr("Mark As Function Start"), this); auto action_xrefs = new QAction(tr("Show X-Refs"), this); connect(action_find, &QAction::triggered, this, &Analyzer::doViewAnalysis); connect(action_goto_function_start, &QAction::triggered, this, &Analyzer::gotoFunctionStart); connect(action_goto_function_end, &QAction::triggered, this, &Analyzer::gotoFunctionEnd); connect(action_mark_function_start, &QAction::triggered, this, &Analyzer::markFunctionStart); connect(action_xrefs, &QAction::triggered, this, &Analyzer::showXrefs); ret << action_find << action_goto_function_start << action_goto_function_end << action_mark_function_start << action_xrefs; return ret; } /** * @brief Analyzer::doAnalysis * @param region */ void Analyzer::doAnalysis(const std::shared_ptr ®ion) { if (region && region->size() != 0) { QProgressDialog progress(tr("Performing Analysis"), nullptr, 0, 100, edb::v1::debugger_ui); connect(this, &Analyzer::updateProgress, &progress, &QProgressDialog::setValue); progress.show(); progress.setValue(0); analyze(region); edb::v1::repaint_cpu_view(); } } /** * @brief Analyzer::bonusMain * @param data */ void Analyzer::bonusMain(RegionData *data) const { Q_ASSERT(data); const QString s = edb::v1::debugger_core->process()->executable(); if (!s.isEmpty()) { if (const edb::address_t main = edb::v1::locate_main_function()) { if (data->region->contains(main)) { data->knownFunctions.insert(main); } } } } /** * @brief Analyzer::bonusSymbols * @param data */ void Analyzer::bonusSymbols(RegionData *data) { Q_ASSERT(data); // give bonus if we have a symbol for the address const std::vector> symbols = edb::v1::symbol_manager().symbols(); for (const std::shared_ptr &sym : symbols) { const edb::address_t addr = sym->address; // NOTE(eteran): we special case the module entry point because while we bonus the // application's entry point in bonusEntryPoint, each module can have one which // is called on load by the linker, including the linker itself! And unfortunately // at least on some systems, it is a data symbol, not a code symbol if (data->region->contains(addr) && (sym->isCode() || is_entrypoint(*sym))) { qDebug("[Analyzer] adding: %s <%s>", qPrintable(sym->name), qPrintable(addr.toPointerString())); data->knownFunctions.insert(addr); } } } /** * @brief Analyzer::bonusMarkedFunctions * @param data */ void Analyzer::bonusMarkedFunctions(RegionData *data) { Q_ASSERT(data); Q_FOREACH (const edb::address_t addr, specifiedFunctions_) { if (data->region->contains(addr)) { qDebug("[Analyzer] adding user marked function: <%s>", qPrintable(addr.toPointerString())); data->knownFunctions.insert(addr); } } } /** * @brief Analyzer::identHeader * @param data */ void Analyzer::identHeader(Analyzer::RegionData *data) { Q_UNUSED(data) } /** * @brief Analyzer::collectFunctions * @param data */ void Analyzer::collectFunctions(Analyzer::RegionData *data) { Q_ASSERT(data); // results QHash basic_blocks; FunctionMap functions; // push all known functions onto a stack QStack known_functions; Q_FOREACH (const edb::address_t function, data->knownFunctions) { known_functions.push(function); } // push all fuzzy function too... Q_FOREACH (const edb::address_t function, data->fuzzyFunctions) { known_functions.push(function); } // process all functions that are known while (!known_functions.empty()) { const edb::address_t function_address = known_functions.pop(); if (!functions.contains(function_address)) { QStack blocks; blocks.push(function_address); Function func; // process are basic blocks that are known while (!blocks.empty()) { const edb::address_t block_address = blocks.pop(); edb::address_t address = block_address; BasicBlock block; if (!basic_blocks.contains(block_address)) { while (data->region->contains(address)) { uint8_t buffer[edb::Instruction::MaxSize]; const int buf_size = edb::v1::get_instruction_bytes(address, buffer); if (buf_size == 0) { break; } auto inst = std::make_shared(buffer, buffer + buf_size, address); if (!inst->valid()) { break; } block.push_back(inst); if (is_call(*inst)) { // note the destination and move on // we special case some simple things. // also this is an opportunity to find call tables. const edb::Operand op = inst->operand(0); if (is_immediate(op)) { const edb::address_t ea = op->imm; // skip over ones which are: "call