From f4159bb967adeaaf25136a27224d6df1fddd8ce5 Mon Sep 17 00:00:00 2001 From: zhanghongyuan Date: Mon, 26 Oct 2026 20:11:39 +0800 Subject: [PATCH 1/8] =?UTF-8?q?test:=20=E8=A1=A5=E5=85=85=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E7=AB=AF=E5=8D=95=E6=B5=8B=E5=9F=BA=E7=A1=80=E8=AE=BE?= =?UTF-8?q?=E6=96=BD=EF=BC=88stub-ext=20=E6=89=93=E6=A1=A9=E5=BA=93?= =?UTF-8?q?=E3=80=81CMake=20=E6=B5=8B=E8=AF=95=E7=9B=AE=E6=A0=87=E6=8C=82?= =?UTF-8?q?=E6=8E=A5=E3=80=81=E8=BF=90=E8=A1=8C=E8=84=9A=E6=9C=AC=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 引入 tests/3rdparty/stub(stub-ext:set_lamda/ADDR 等标准打桩能力) - tests/CMakeLists.txt 末尾追加:测试目标接入 stub-ext、QtTest 模块、plugin.cpp 重新纳入、新增 customgpuinfo-test 独立二进制(仅追加,不改既有行) - 新增 run-ut.sh 一键编译/运行/覆盖率脚本与 LSan 抑制清单、ut-summary 生成脚本 --- .../tests/3rdparty/stub/addr_any.h | 280 + .../tests/3rdparty/stub/addr_pri.h | 177 + .../tests/3rdparty/stub/elfio.hpp | 4888 +++++++++++++++++ .../tests/3rdparty/stub/stub-shadow.cpp | 35 + .../tests/3rdparty/stub/stub-shadow.h | 166 + .../tests/3rdparty/stub/stub.h | 360 ++ .../tests/3rdparty/stub/stubext.h | 107 + .../tests/CMakeLists.txt | 44 +- .../tests/gen-ut-summary.py | 117 + .../tests/lsan_suppressions.txt | 14 + deepin-devicemanager-server/tests/run-ut.sh | 287 + deepin-devicemanager-server/tests/src/stub.h | 283 +- 12 files changed, 6661 insertions(+), 97 deletions(-) create mode 100644 deepin-devicemanager-server/tests/3rdparty/stub/addr_any.h create mode 100644 deepin-devicemanager-server/tests/3rdparty/stub/addr_pri.h create mode 100644 deepin-devicemanager-server/tests/3rdparty/stub/elfio.hpp create mode 100644 deepin-devicemanager-server/tests/3rdparty/stub/stub-shadow.cpp create mode 100644 deepin-devicemanager-server/tests/3rdparty/stub/stub-shadow.h create mode 100644 deepin-devicemanager-server/tests/3rdparty/stub/stub.h create mode 100644 deepin-devicemanager-server/tests/3rdparty/stub/stubext.h create mode 100755 deepin-devicemanager-server/tests/gen-ut-summary.py create mode 100644 deepin-devicemanager-server/tests/lsan_suppressions.txt create mode 100755 deepin-devicemanager-server/tests/run-ut.sh diff --git a/deepin-devicemanager-server/tests/3rdparty/stub/addr_any.h b/deepin-devicemanager-server/tests/3rdparty/stub/addr_any.h new file mode 100644 index 000000000..a153f3480 --- /dev/null +++ b/deepin-devicemanager-server/tests/3rdparty/stub/addr_any.h @@ -0,0 +1,280 @@ +#ifndef __ADDR_ANY_H__ +#define __ADDR_ANY_H__ + + +//linux +#include +#include +//c +#include +#include +#include + +//c++ +#include +#include +//project +#include "elfio.hpp" + + + +class AddrAny +{ +public: + AddrAny() + { + m_init = get_exe_pathname(m_fullname); + m_baseaddr = 0; + } + AddrAny(std::string libname) + { + m_init = get_lib_pathname_and_baseaddr(libname, m_fullname, m_baseaddr); + } + + int get_local_func_addr_symtab(std::string func_name_regex_str, std::map& result) + { + return get_func_addr(SHT_SYMTAB, STB_LOCAL, func_name_regex_str, result); + } + int get_global_func_addr_symtab(std::string func_name_regex_str, std::map& result) + { + return get_func_addr(SHT_SYMTAB, STB_GLOBAL, func_name_regex_str, result); + } + int get_weak_func_addr_symtab(std::string func_name_regex_str, std::map& result) + { + return get_func_addr(SHT_SYMTAB, STB_WEAK, func_name_regex_str, result); + } + + int get_global_func_addr_dynsym( std::string func_name_regex_str, std::map& result) + { + return get_func_addr(SHT_DYNSYM, STB_GLOBAL, func_name_regex_str, result); + } + int get_weak_func_addr_dynsym(std::string func_name_regex_str, std::map& result) + { + return get_func_addr(SHT_DYNSYM, STB_WEAK, func_name_regex_str, result); + } + +private: + bool demangle(std::string& s, std::string& name) { + int status; + char* pname = abi::__cxa_demangle(s.c_str(), 0, 0, &status); + if (status != 0) + { + switch(status) + { + case -1: name = "memory allocation error"; break; + case -2: name = "invalid name given"; break; + case -3: name = "internal error: __cxa_demangle: invalid argument"; break; + default: name = "unknown error occured"; break; + } + return false; + } + name = pname; + free(pname); + return true; + } + bool get_exe_pathname( std::string& name) + { + char line[512]; + FILE *fp; + uintptr_t base_addr; + char perm[5]; + unsigned long offset; + int pathname_pos; + char *pathname; + size_t pathname_len; + int match = 0; + + if(NULL == (fp = fopen("/proc/self/maps", "r"))) + { + return false; + } + + while(fgets(line, sizeof(line), fp)) + { + if(sscanf(line, "%" PRIxPTR "-%*lx %4s %lx %*x:%*x %*d%n", &base_addr, perm, &offset, &pathname_pos) != 3) continue; + + if(0 != offset) continue; + + //get pathname + while(isspace(line[pathname_pos]) && pathname_pos < (int)(sizeof(line) - 1)) + pathname_pos += 1; + if(pathname_pos >= (int)(sizeof(line) - 1)) continue; + pathname = line + pathname_pos; + pathname_len = strlen(pathname); + if(0 == pathname_len) continue; + if(pathname[pathname_len - 1] == '\n') + { + pathname[pathname_len - 1] = '\0'; + pathname_len -= 1; + } + if(0 == pathname_len) continue; + if('[' == pathname[0]) continue; + + name = pathname; + match = 1; + break; + + } + fclose(fp); + + if(0 == match) + { + return false; + } + else + { + return true; + } + + } + + bool get_lib_pathname_and_baseaddr(std::string pathname_regex_str, std::string& name, unsigned long& addr) + { + char line[512]; + FILE *fp; + uintptr_t base_addr; + char perm[5]; + unsigned long offset; + int pathname_pos; + char *pathname; + size_t pathname_len; + int match; + regex_t pathname_regex; + + regcomp(&pathname_regex, pathname_regex_str.c_str(), 0); + + if(NULL == (fp = fopen("/proc/self/maps", "r"))) + { + return false; + } + + while(fgets(line, sizeof(line), fp)) + { + if(sscanf(line, "%" PRIxPTR "-%*lx %4s %lx %*x:%*x %*d%n", &base_addr, perm, &offset, &pathname_pos) != 3) continue; + + //check permission + if(perm[0] != 'r') continue; + if(perm[3] != 'p') continue; //do not touch the shared memory + + //check offset + // + //We are trying to find ELF header in memory. + //It can only be found at the beginning of a mapped memory regions + //whose offset is 0. + if(0 != offset) continue; + + //get pathname + while(isspace(line[pathname_pos]) && pathname_pos < (int)(sizeof(line) - 1)) + pathname_pos += 1; + if(pathname_pos >= (int)(sizeof(line) - 1)) continue; + pathname = line + pathname_pos; + pathname_len = strlen(pathname); + if(0 == pathname_len) continue; + if(pathname[pathname_len - 1] == '\n') + { + pathname[pathname_len - 1] = '\0'; + pathname_len -= 1; + } + if(0 == pathname_len) continue; + if('[' == pathname[0]) continue; + + //check pathname + //if we need to hook this elf? + match = 0; + if(0 == regexec(&pathname_regex, pathname, 0, NULL, 0)) + { + match = 1; + name = pathname; + addr = (unsigned long)base_addr; + break; + } + if(0 == match) continue; + + } + fclose(fp); + if(0 == match) + { + return false; + } + else + { + return true; + } + + } + + int get_func_addr(unsigned int ttype, unsigned int stype, std::string& func_name_regex_str, std::map& result) + { + // Create an elfio reader + ELFIO::elfio reader; + int count = 0; + regex_t pathname_regex; + + if(!m_init) + { + return -1; + } + + regcomp(&pathname_regex, func_name_regex_str.c_str(), 0); + // Load ELF data + if(!reader.load(m_fullname.c_str())) + { + return -1; + } + + ELFIO::Elf_Half sec_num = reader.sections.size(); + for(int i = 0; i < sec_num; ++i) + { + ELFIO::section* psec = reader.sections[i]; + // Check section type + if(psec->get_type() == ttype) + { + const ELFIO::symbol_section_accessor symbols( reader, psec ); + for ( unsigned int j = 0; j < symbols.get_symbols_num(); ++j ) + { + std::string name; + std::string name_mangle; + ELFIO::Elf64_Addr value; + ELFIO::Elf_Xword size; + unsigned char bind; + unsigned char type; + ELFIO::Elf_Half section_index; + unsigned char other; + + // Read symbol properties + symbols.get_symbol( j, name, value, size, bind, type, section_index, other ); + if(type == STT_FUNC && bind == stype) + { + bool ret = demangle(name,name_mangle); + if(ret == true) + { + if (0 == regexec(&pathname_regex, name_mangle.c_str(), 0, NULL, 0)) + { + result.insert ( std::pair(name_mangle,(void*)(value + m_baseaddr))); + count++; + } + } + else + { + if (0 == regexec(&pathname_regex, name.c_str(), 0, NULL, 0)) + { + result.insert ( std::pair(name,(void*)(value + m_baseaddr))); + count++; + } + } + } + } + break; + } + } + + return count; + } +private: + bool m_init; + std::string m_name; + std::string m_fullname; + unsigned long m_baseaddr; + +}; +#endif diff --git a/deepin-devicemanager-server/tests/3rdparty/stub/addr_pri.h b/deepin-devicemanager-server/tests/3rdparty/stub/addr_pri.h new file mode 100644 index 000000000..9174bb0cb --- /dev/null +++ b/deepin-devicemanager-server/tests/3rdparty/stub/addr_pri.h @@ -0,0 +1,177 @@ +#ifndef __ADDR_PRI_H__ +#define __ADDR_PRI_H__ + + +#include +#include + + + +//base on C++11 + +/********************************************************** + access private function +**********************************************************/ + + +namespace std { + template + using enable_if_t = typename enable_if::type; + template + using remove_reference_t = typename remove_reference::type; +} // std + +// Unnamed namespace is used to avoid duplicate symbols if the macros are used +namespace { + namespace private_access_detail { + + // @tparam TagType, used to declare different "get" funciton overloads for + // different members/statics + template + struct private_access { + // Normal lookup cannot find in-class defined (inline) friend functions. + friend PtrType get(TagType) { return PtrValue; } + }; + + } // namespace private_access_detail +} // namespace + +// Used macro naming conventions: +// The "namespace" of this macro library is PRIVATE_ACCESS, i.e. all +// macro here has this prefix. +// All implementation macro, which are not meant to be used directly have the +// PRIVATE_ACCESS_DETAIL prefix. +// Some macros have the ABCD_IMPL form, which means they contain the +// implementation details for the specific ABCD macro. + +#define PRIVATE_ACCESS_DETAIL_CONCATENATE_IMPL(x, y) x##y +#define PRIVATE_ACCESS_DETAIL_CONCATENATE(x, y) \ + PRIVATE_ACCESS_DETAIL_CONCATENATE_IMPL(x, y) + +// @param PtrTypeKind E.g if we have "class A", then it can be "A::*" in case of +// members, or it can be "*" in case of statics. +#define PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE(Tag, Class, Type, Name, \ + PtrTypeKind) \ + namespace { \ + namespace private_access_detail { \ + /* Tag type, used to declare different get funcitons for different \ + * members \ + */ \ + struct Tag {}; \ + /* Explicit instantiation */ \ + template struct private_access; \ + /* We can build the PtrType only with two aliases */ \ + /* E.g. using PtrType = int(int) *; would be illformed */ \ + using PRIVATE_ACCESS_DETAIL_CONCATENATE(Alias_, Tag) = Type; \ + using PRIVATE_ACCESS_DETAIL_CONCATENATE(PtrType_, Tag) = \ + PRIVATE_ACCESS_DETAIL_CONCATENATE(Alias_, Tag) PtrTypeKind; \ + /* Declare the friend function, now it is visible in namespace scope. \ + * Note, \ + * we could declare it inside the Tag type too, in that case ADL would \ + * find \ + * the declaration. By choosing to declare it here, the Tag type remains \ + * a \ + * simple tag type, it has no other responsibilities. */ \ + PRIVATE_ACCESS_DETAIL_CONCATENATE(PtrType_, Tag) get(Tag); \ + } \ + } + +#define PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_FIELD(Tag, Class, Type, Name) \ + PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE(Tag, Class, Type, Name, Class::*) \ + namespace { \ + namespace access_private_field { \ + Type &Class##Name(Class &&t) { return t.*get(private_access_detail::Tag{}); } \ + Type &Class##Name(Class &t) { return t.*get(private_access_detail::Tag{}); } \ + /* The following usings are here to avoid duplicate const qualifier \ + * warnings \ + */ \ + using PRIVATE_ACCESS_DETAIL_CONCATENATE(X, Tag) = Type; \ + using PRIVATE_ACCESS_DETAIL_CONCATENATE(Y, Tag) = \ + const PRIVATE_ACCESS_DETAIL_CONCATENATE(X, Tag); \ + PRIVATE_ACCESS_DETAIL_CONCATENATE(Y, Tag) & Class##Name(const Class &t) {\ + return t.*get(private_access_detail::Tag{}); \ + } \ + } \ + } + +#define PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_FUN(Tag, Class, Type, Name) \ + PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE(Tag, Class, Type, Name, Class::*) \ + namespace { \ + namespace call_private_fun { \ + /* We do perfect forwarding, but we want to restrict the overload set \ + * only for objects which have the type Class. */ \ + template , \ + Class>::value> * = nullptr, \ + typename... Args> \ + auto Class##Name(Obj &&o, Args &&... args) -> decltype( \ + (std::forward(o).* \ + get(private_access_detail::Tag{}))(std::forward(args)...)) { \ + return (std::forward(o).*get(private_access_detail::Tag{}))( \ + std::forward(args)...); \ + } \ + } \ + namespace get_private_fun { \ + auto Class##Name() -> decltype( \ + get(private_access_detail::Tag{})) { \ + return (get(private_access_detail::Tag{})); \ + } \ + } \ + } + +#define PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_STATIC_FIELD(Tag, Class, Type, \ + Name) \ + PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE(Tag, Class, Type, Name, *) \ + namespace { \ + namespace access_private_static_field { \ + namespace Class { \ + Type &Class##Name() { return *get(private_access_detail::Tag{}); } \ + } \ + } \ + } + +#define PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_STATIC_FUN(Tag, Class, Type, \ + Name) \ + PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE(Tag, Class, Type, Name, *) \ + namespace { \ + namespace call_private_static_fun { \ + namespace Class { \ + template \ + auto Class##Name(Args &&... args) -> decltype( \ + get(private_access_detail::Tag{})(std::forward(args)...)) { \ + return get(private_access_detail::Tag{})( \ + std::forward(args)...); \ + } \ + } \ + } \ + namespace get_private_static_fun { \ + namespace Class { \ + auto Class##Name() -> decltype(get(private_access_detail::Tag{})) { \ + return get(private_access_detail::Tag{}); \ + } \ + } \ + } \ + } + +#define PRIVATE_ACCESS_DETAIL_UNIQUE_TAG \ + PRIVATE_ACCESS_DETAIL_CONCATENATE(PrivateAccessTag, __COUNTER__) + +#define ACCESS_PRIVATE_FIELD(Class, Type, Name) \ + PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_FIELD(PRIVATE_ACCESS_DETAIL_UNIQUE_TAG, \ + Class, Type, Name) + +#define ACCESS_PRIVATE_FUN(Class, Type, Name) \ + PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_FUN(PRIVATE_ACCESS_DETAIL_UNIQUE_TAG, \ + Class, Type, Name) + +#define ACCESS_PRIVATE_STATIC_FIELD(Class, Type, Name) \ + Type Class::Name; \ + PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_STATIC_FIELD( \ + PRIVATE_ACCESS_DETAIL_UNIQUE_TAG, Class, Type, Name) + +#define ACCESS_PRIVATE_STATIC_FUN(Class, Type, Name) \ + PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_STATIC_FUN( \ + PRIVATE_ACCESS_DETAIL_UNIQUE_TAG, Class, Type, Name) + +#endif diff --git a/deepin-devicemanager-server/tests/3rdparty/stub/elfio.hpp b/deepin-devicemanager-server/tests/3rdparty/stub/elfio.hpp new file mode 100644 index 000000000..dd5c9aecf --- /dev/null +++ b/deepin-devicemanager-server/tests/3rdparty/stub/elfio.hpp @@ -0,0 +1,4888 @@ + +/*** Start of inlined file: elfio_dump.hpp ***/ +#ifndef ELFIO_DUMP_HPP +#define ELFIO_DUMP_HPP + +#include +#include +#include +#include +#include + +/*** Start of inlined file: elfio.hpp ***/ +#ifndef ELFIO_HPP +#define ELFIO_HPP + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4996 ) +#pragma warning( disable : 4355 ) +#pragma warning( disable : 4244 ) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + + +/*** Start of inlined file: elf_types.hpp ***/ +#ifndef ELFTYPES_H +#define ELFTYPES_H + +#ifndef ELFIO_NO_OWN_TYPES +#if !defined( ELFIO_NO_CSTDINT ) && !defined( ELFIO_NO_INTTYPES ) +#include +#else +typedef unsigned char uint8_t; +typedef signed char int8_t; +typedef unsigned short uint16_t; +typedef signed short int16_t; +#ifdef _MSC_VER +typedef unsigned __int32 uint32_t; +typedef signed __int32 int32_t; +typedef unsigned __int64 uint64_t; +typedef signed __int64 int64_t; +#else +typedef unsigned int uint32_t; +typedef signed int int32_t; +typedef unsigned long long uint64_t; +typedef signed long long int64_t; +#endif // _MSC_VER +#endif // ELFIO_NO_CSTDINT +#endif // ELFIO_NO_OWN_TYPES + +namespace ELFIO { + +// Attention! Platform depended definitions. +typedef uint16_t Elf_Half; +typedef uint32_t Elf_Word; +typedef int32_t Elf_Sword; +typedef uint64_t Elf_Xword; +typedef int64_t Elf_Sxword; + +typedef uint32_t Elf32_Addr; +typedef uint32_t Elf32_Off; +typedef uint64_t Elf64_Addr; +typedef uint64_t Elf64_Off; + +#define Elf32_Half Elf_Half +#define Elf64_Half Elf_Half +#define Elf32_Word Elf_Word +#define Elf64_Word Elf_Word +#define Elf32_Sword Elf_Sword +#define Elf64_Sword Elf_Sword + +/////////////////////// +// ELF Header Constants + +// File type +#define ET_NONE 0 +#define ET_REL 1 +#define ET_EXEC 2 +#define ET_DYN 3 +#define ET_CORE 4 +#define ET_LOOS 0xFE00 +#define ET_HIOS 0xFEFF +#define ET_LOPROC 0xFF00 +#define ET_HIPROC 0xFFFF + +#define EM_NONE 0 // No machine +#define EM_M32 1 // AT&T WE 32100 +#define EM_SPARC 2 // SUN SPARC +#define EM_386 3 // Intel 80386 +#define EM_68K 4 // Motorola m68k family +#define EM_88K 5 // Motorola m88k family +#define EM_486 6 // Intel 80486// Reserved for future use +#define EM_860 7 // Intel 80860 +#define EM_MIPS 8 // MIPS R3000 (officially, big-endian only) +#define EM_S370 9 // IBM System/370 +#define EM_MIPS_RS3_LE \ + 10 // MIPS R3000 little-endian (Oct 4 1999 Draft) Deprecated +#define EM_res011 11 // Reserved +#define EM_res012 12 // Reserved +#define EM_res013 13 // Reserved +#define EM_res014 14 // Reserved +#define EM_PARISC 15 // HPPA +#define EM_res016 16 // Reserved +#define EM_VPP550 17 // Fujitsu VPP500 +#define EM_SPARC32PLUS 18 // Sun's "v8plus" +#define EM_960 19 // Intel 80960 +#define EM_PPC 20 // PowerPC +#define EM_PPC64 21 // 64-bit PowerPC +#define EM_S390 22 // IBM S/390 +#define EM_SPU 23 // Sony/Toshiba/IBM SPU +#define EM_res024 24 // Reserved +#define EM_res025 25 // Reserved +#define EM_res026 26 // Reserved +#define EM_res027 27 // Reserved +#define EM_res028 28 // Reserved +#define EM_res029 29 // Reserved +#define EM_res030 30 // Reserved +#define EM_res031 31 // Reserved +#define EM_res032 32 // Reserved +#define EM_res033 33 // Reserved +#define EM_res034 34 // Reserved +#define EM_res035 35 // Reserved +#define EM_V800 36 // NEC V800 series +#define EM_FR20 37 // Fujitsu FR20 +#define EM_RH32 38 // TRW RH32 +#define EM_MCORE 39 // Motorola M*Core // May also be taken by Fujitsu MMA +#define EM_RCE 39 // Old name for MCore +#define EM_ARM 40 // ARM +#define EM_OLD_ALPHA 41 // Digital Alpha +#define EM_SH 42 // Renesas (formerly Hitachi) / SuperH SH +#define EM_SPARCV9 43 // SPARC v9 64-bit +#define EM_TRICORE 44 // Siemens Tricore embedded processor +#define EM_ARC 45 // ARC Cores +#define EM_H8_300 46 // Renesas (formerly Hitachi) H8/300 +#define EM_H8_300H 47 // Renesas (formerly Hitachi) H8/300H +#define EM_H8S 48 // Renesas (formerly Hitachi) H8S +#define EM_H8_500 49 // Renesas (formerly Hitachi) H8/500 +#define EM_IA_64 50 // Intel IA-64 Processor +#define EM_MIPS_X 51 // Stanford MIPS-X +#define EM_COLDFIRE 52 // Motorola Coldfire +#define EM_68HC12 53 // Motorola M68HC12 +#define EM_MMA 54 // Fujitsu Multimedia Accelerator +#define EM_PCP 55 // Siemens PCP +#define EM_NCPU 56 // Sony nCPU embedded RISC processor +#define EM_NDR1 57 // Denso NDR1 microprocesspr +#define EM_STARCORE 58 // Motorola Star*Core processor +#define EM_ME16 59 // Toyota ME16 processor +#define EM_ST100 60 // STMicroelectronics ST100 processor +#define EM_TINYJ 61 // Advanced Logic Corp. TinyJ embedded processor +#define EM_X86_64 62 // Advanced Micro Devices X86-64 processor +#define EM_PDSP 63 // Sony DSP Processor +#define EM_PDP10 64 // Digital Equipment Corp. PDP-10 +#define EM_PDP11 65 // Digital Equipment Corp. PDP-11 +#define EM_FX66 66 // Siemens FX66 microcontroller +#define EM_ST9PLUS 67 // STMicroelectronics ST9+ 8/16 bit microcontroller +#define EM_ST7 68 // STMicroelectronics ST7 8-bit microcontroller +#define EM_68HC16 69 // Motorola MC68HC16 Microcontroller +#define EM_68HC11 70 // Motorola MC68HC11 Microcontroller +#define EM_68HC08 71 // Motorola MC68HC08 Microcontroller +#define EM_68HC05 72 // Motorola MC68HC05 Microcontroller +#define EM_SVX 73 // Silicon Graphics SVx +#define EM_ST19 74 // STMicroelectronics ST19 8-bit cpu +#define EM_VAX 75 // Digital VAX +#define EM_CRIS 76 // Axis Communications 32-bit embedded processor +#define EM_JAVELIN 77 // Infineon Technologies 32-bit embedded cpu +#define EM_FIREPATH 78 // Element 14 64-bit DSP processor +#define EM_ZSP 79 // LSI Logic's 16-bit DSP processor +#define EM_MMIX 80 // Donald Knuth's educational 64-bit processor +#define EM_HUANY 81 // Harvard's machine-independent format +#define EM_PRISM 82 // SiTera Prism +#define EM_AVR 83 // Atmel AVR 8-bit microcontroller +#define EM_FR30 84 // Fujitsu FR30 +#define EM_D10V 85 // Mitsubishi D10V +#define EM_D30V 86 // Mitsubishi D30V +#define EM_V850 87 // NEC v850 +#define EM_M32R 88 // Renesas M32R (formerly Mitsubishi M32R) +#define EM_MN10300 89 // Matsushita MN10300 +#define EM_MN10200 90 // Matsushita MN10200 +#define EM_PJ 91 // picoJava +#define EM_OPENRISC 92 // OpenRISC 32-bit embedded processor +#define EM_ARC_A5 93 // ARC Cores Tangent-A5 +#define EM_XTENSA 94 // Tensilica Xtensa Architecture +#define EM_VIDEOCORE 95 // Alphamosaic VideoCore processor +#define EM_TMM_GPP 96 // Thompson Multimedia General Purpose Processor +#define EM_NS32K 97 // National Semiconductor 32000 series +#define EM_TPC 98 // Tenor Network TPC processor +#define EM_SNP1K 99 // Trebia SNP 1000 processor +#define EM_ST200 100 // STMicroelectronics ST200 microcontroller +#define EM_IP2K 101 // Ubicom IP2022 micro controller +#define EM_MAX 102 // MAX Processor +#define EM_CR 103 // National Semiconductor CompactRISC +#define EM_F2MC16 104 // Fujitsu F2MC16 +#define EM_MSP430 105 // TI msp430 micro controller +#define EM_BLACKFIN 106 // ADI Blackfin +#define EM_SE_C33 107 // S1C33 Family of Seiko Epson processors +#define EM_SEP 108 // Sharp embedded microprocessor +#define EM_ARCA 109 // Arca RISC Microprocessor +#define EM_UNICORE \ + 110 // Microprocessor series from PKU-Unity Ltd. and MPRC of Peking University +#define EM_EXCESS 111 // eXcess: 16/32/64-bit configurable embedded CPU +#define EM_DXP 112 // Icera Semiconductor Inc. Deep Execution Processor +#define EM_ALTERA_NIOS2 113 // Altera Nios II soft-core processor +#define EM_CRX 114 // National Semiconductor CRX +#define EM_XGATE 115 // Motorola XGATE embedded processor +#define EM_C166 116 // Infineon C16x/XC16x processor +#define EM_M16C 117 // Renesas M16C series microprocessors +#define EM_DSPIC30F \ + 118 // Microchip Technology dsPIC30F Digital Signal Controller +#define EM_CE 119 // Freescale Communication Engine RISC core +#define EM_M32C 120 // Renesas M32C series microprocessors +#define EM_res121 121 // Reserved +#define EM_res122 122 // Reserved +#define EM_res123 123 // Reserved +#define EM_res124 124 // Reserved +#define EM_res125 125 // Reserved +#define EM_res126 126 // Reserved +#define EM_res127 127 // Reserved +#define EM_res128 128 // Reserved +#define EM_res129 129 // Reserved +#define EM_res130 130 // Reserved +#define EM_TSK3000 131 // Altium TSK3000 core +#define EM_RS08 132 // Freescale RS08 embedded processor +#define EM_res133 133 // Reserved +#define EM_ECOG2 134 // Cyan Technology eCOG2 microprocessor +#define EM_SCORE 135 // Sunplus Score +#define EM_SCORE7 135 // Sunplus S+core7 RISC processor +#define EM_DSP24 136 // New Japan Radio (NJR) 24-bit DSP Processor +#define EM_VIDEOCORE3 137 // Broadcom VideoCore III processor +#define EM_LATTICEMICO32 138 // RISC processor for Lattice FPGA architecture +#define EM_SE_C17 139 // Seiko Epson C17 family +#define EM_TI_C6000 140 // Texas Instruments TMS320C6000 DSP family +#define EM_TI_C2000 141 // Texas Instruments TMS320C2000 DSP family +#define EM_TI_C5500 142 // Texas Instruments TMS320C55x DSP family +#define EM_res143 143 // Reserved +#define EM_res144 144 // Reserved +#define EM_res145 145 // Reserved +#define EM_res146 146 // Reserved +#define EM_res147 147 // Reserved +#define EM_res148 148 // Reserved +#define EM_res149 149 // Reserved +#define EM_res150 150 // Reserved +#define EM_res151 151 // Reserved +#define EM_res152 152 // Reserved +#define EM_res153 153 // Reserved +#define EM_res154 154 // Reserved +#define EM_res155 155 // Reserved +#define EM_res156 156 // Reserved +#define EM_res157 157 // Reserved +#define EM_res158 158 // Reserved +#define EM_res159 159 // Reserved +#define EM_MMDSP_PLUS 160 // STMicroelectronics 64bit VLIW Data Signal Processor +#define EM_CYPRESS_M8C 161 // Cypress M8C microprocessor +#define EM_R32C 162 // Renesas R32C series microprocessors +#define EM_TRIMEDIA 163 // NXP Semiconductors TriMedia architecture family +#define EM_QDSP6 164 // QUALCOMM DSP6 Processor +#define EM_8051 165 // Intel 8051 and variants +#define EM_STXP7X 166 // STMicroelectronics STxP7x family +#define EM_NDS32 \ + 167 // Andes Technology compact code size embedded RISC processor family +#define EM_ECOG1 168 // Cyan Technology eCOG1X family +#define EM_ECOG1X 168 // Cyan Technology eCOG1X family +#define EM_MAXQ30 169 // Dallas Semiconductor MAXQ30 Core Micro-controllers +#define EM_XIMO16 170 // New Japan Radio (NJR) 16-bit DSP Processor +#define EM_MANIK 171 // M2000 Reconfigurable RISC Microprocessor +#define EM_CRAYNV2 172 // Cray Inc. NV2 vector architecture +#define EM_RX 173 // Renesas RX family +#define EM_METAG 174 // Imagination Technologies META processor architecture +#define EM_MCST_ELBRUS 175 // MCST Elbrus general purpose hardware architecture +#define EM_ECOG16 176 // Cyan Technology eCOG16 family +#define EM_CR16 177 // National Semiconductor CompactRISC 16-bit processor +#define EM_ETPU 178 // Freescale Extended Time Processing Unit +#define EM_SLE9X 179 // Infineon Technologies SLE9X core +#define EM_L1OM 180 // Intel L1OM +#define EM_INTEL181 181 // Reserved by Intel +#define EM_INTEL182 182 // Reserved by Intel +#define EM_res183 183 // Reserved by ARM +#define EM_res184 184 // Reserved by ARM +#define EM_AVR32 185 // Atmel Corporation 32-bit microprocessor family +#define EM_STM8 186 // STMicroeletronics STM8 8-bit microcontroller +#define EM_TILE64 187 // Tilera TILE64 multicore architecture family +#define EM_TILEPRO 188 // Tilera TILEPro multicore architecture family +#define EM_MICROBLAZE 189 // Xilinx MicroBlaze 32-bit RISC soft processor core +#define EM_CUDA 190 // NVIDIA CUDA architecture +#define EM_TILEGX 191 // Tilera TILE-Gx multicore architecture family +#define EM_CLOUDSHIELD 192 // CloudShield architecture family +#define EM_COREA_1ST 193 // KIPO-KAIST Core-A 1st generation processor family +#define EM_COREA_2ND 194 // KIPO-KAIST Core-A 2nd generation processor family +#define EM_ARC_COMPACT2 195 // Synopsys ARCompact V2 +#define EM_OPEN8 196 // Open8 8-bit RISC soft processor core +#define EM_RL78 197 // Renesas RL78 family +#define EM_VIDEOCORE5 198 // Broadcom VideoCore V processor +#define EM_78KOR 199 // Renesas 78KOR family +#define EM_56800EX 200 // Freescale 56800EX Digital Signal Controller (DSC) +#define EM_BA1 201 // Beyond BA1 CPU architecture +#define EM_BA2 202 // Beyond BA2 CPU architecture +#define EM_XCORE 203 // XMOS xCORE processor family +#define EM_MCHP_PIC 204 // Microchip 8-bit PIC(r) family +#define EM_INTEL205 205 // Reserved by Intel +#define EM_INTEL206 206 // Reserved by Intel +#define EM_INTEL207 207 // Reserved by Intel +#define EM_INTEL208 208 // Reserved by Intel +#define EM_INTEL209 209 // Reserved by Intel +#define EM_KM32 210 // KM211 KM32 32-bit processor +#define EM_KMX32 211 // KM211 KMX32 32-bit processor +#define EM_KMX16 212 // KM211 KMX16 16-bit processor +#define EM_KMX8 213 // KM211 KMX8 8-bit processor +#define EM_KVARC 214 // KM211 KVARC processor +#define EM_CDP 215 // Paneve CDP architecture family +#define EM_COGE 216 // Cognitive Smart Memory Processor +#define EM_COOL 217 // iCelero CoolEngine +#define EM_NORC 218 // Nanoradio Optimized RISC +#define EM_CSR_KALIMBA 219 // CSR Kalimba architecture family +#define EM_Z80 220 // Zilog Z80 +#define EM_VISIUM 221 // Controls and Data Services VISIUMcore processor +#define EM_FT32 222 // FTDI Chip FT32 high performance 32-bit RISC architecture +#define EM_MOXIE 223 // Moxie processor family +#define EM_AMDGPU 224 // AMD GPU architecture +#define EM_RISCV 243 // RISC-V +#define EM_LANAI 244 // Lanai processor +#define EM_CEVA 245 // CEVA Processor Architecture Family +#define EM_CEVA_X2 246 // CEVA X2 Processor Family +#define EM_BPF 247 // Linux BPF – in-kernel virtual machine +#define EM_GRAPHCORE_IPU 248 // Graphcore Intelligent Processing Unit +#define EM_IMG1 249 // Imagination Technologies +#define EM_NFP 250 // Netronome Flow Processor (P) +#define EM_CSKY 252 // C-SKY processor family + +// File version +#define EV_NONE 0 +#define EV_CURRENT 1 + +// Identification index +#define EI_MAG0 0 +#define EI_MAG1 1 +#define EI_MAG2 2 +#define EI_MAG3 3 +#define EI_CLASS 4 +#define EI_DATA 5 +#define EI_VERSION 6 +#define EI_OSABI 7 +#define EI_ABIVERSION 8 +#define EI_PAD 9 +#define EI_NIDENT 16 + +// Magic number +#define ELFMAG0 0x7F +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' + +// File class +#define ELFCLASSNONE 0 +#define ELFCLASS32 1 +#define ELFCLASS64 2 + +// Encoding +#define ELFDATANONE 0 +#define ELFDATA2LSB 1 +#define ELFDATA2MSB 2 + +// OS extensions +#define ELFOSABI_NONE 0 // No extensions or unspecified +#define ELFOSABI_HPUX 1 // Hewlett-Packard HP-UX +#define ELFOSABI_NETBSD 2 // NetBSD +#define ELFOSABI_LINUX 3 // Linux +#define ELFOSABI_SOLARIS 6 // Sun Solaris +#define ELFOSABI_AIX 7 // AIX +#define ELFOSABI_IRIX 8 // IRIX +#define ELFOSABI_FREEBSD 9 // FreeBSD +#define ELFOSABI_TRU64 10 // Compaq TRU64 UNIX +#define ELFOSABI_MODESTO 11 // Novell Modesto +#define ELFOSABI_OPENBSD 12 // Open BSD +#define ELFOSABI_OPENVMS 13 // Open VMS +#define ELFOSABI_NSK 14 // Hewlett-Packard Non-Stop Kernel +#define ELFOSABI_AROS 15 // Amiga Research OS +#define ELFOSABI_FENIXOS 16 // The FenixOS highly scalable multi-core OS +// 64-255 Architecture-specific value range +#define ELFOSABI_AMDGPU_HSA \ + 64 // AMDGPU OS for HSA compatible compute // kernels. +#define ELFOSABI_AMDGPU_PAL \ + 65 // AMDGPU OS for AMD PAL compatible graphics // shaders and compute kernels. +#define ELFOSABI_AMDGPU_MESA3D \ + 66 // AMDGPU OS for Mesa3D compatible graphics // shaders and compute kernels. + +// AMDGPU specific e_flags +#define EF_AMDGPU_MACH 0x0ff // AMDGPU processor selection mask. +#define EF_AMDGPU_XNACK \ + 0x100 // Indicates if the XNACK target feature is // enabled for all code contained in the ELF. +// AMDGPU processors +#define EF_AMDGPU_MACH_NONE 0x000 // Unspecified processor. +#define EF_AMDGPU_MACH_R600_R600 0x001 +#define EF_AMDGPU_MACH_R600_R630 0x002 +#define EF_AMDGPU_MACH_R600_RS880 0x003 +#define EF_AMDGPU_MACH_R600_RV670 0x004 +#define EF_AMDGPU_MACH_R600_RV710 0x005 +#define EF_AMDGPU_MACH_R600_RV730 0x006 +#define EF_AMDGPU_MACH_R600_RV770 0x007 +#define EF_AMDGPU_MACH_R600_CEDAR 0x008 +#define EF_AMDGPU_MACH_R600_CYPRESS 0x009 +#define EF_AMDGPU_MACH_R600_JUNIPER 0x00a +#define EF_AMDGPU_MACH_R600_REDWOOD 0x00b +#define EF_AMDGPU_MACH_R600_SUMO 0x00c +#define EF_AMDGPU_MACH_R600_BARTS 0x00d +#define EF_AMDGPU_MACH_R600_CAICOS 0x00e +#define EF_AMDGPU_MACH_R600_CAYMAN 0x00f +#define EF_AMDGPU_MACH_R600_TURKS 0x010 +#define EF_AMDGPU_MACH_R600_RESERVED_FIRST 0x011 +#define EF_AMDGPU_MACH_R600_RESERVED_LAST 0x01f +#define EF_AMDGPU_MACH_R600_FIRST EF_AMDGPU_MACH_R600_R600 +#define EF_AMDGPU_MACH_R600_LAST EF_AMDGPU_MACH_R600_TURKS +#define EF_AMDGPU_MACH_AMDGCN_GFX600 0x020 +#define EF_AMDGPU_MACH_AMDGCN_GFX601 0x021 +#define EF_AMDGPU_MACH_AMDGCN_GFX700 0x022 +#define EF_AMDGPU_MACH_AMDGCN_GFX701 0x023 +#define EF_AMDGPU_MACH_AMDGCN_GFX702 0x024 +#define EF_AMDGPU_MACH_AMDGCN_GFX703 0x025 +#define EF_AMDGPU_MACH_AMDGCN_GFX704 0x026 +#define EF_AMDGPU_MACH_AMDGCN_GFX801 0x028 +#define EF_AMDGPU_MACH_AMDGCN_GFX802 0x029 +#define EF_AMDGPU_MACH_AMDGCN_GFX803 0x02a +#define EF_AMDGPU_MACH_AMDGCN_GFX810 0x02b +#define EF_AMDGPU_MACH_AMDGCN_GFX900 0x02c +#define EF_AMDGPU_MACH_AMDGCN_GFX902 0x02d +#define EF_AMDGPU_MACH_AMDGCN_GFX904 0x02e +#define EF_AMDGPU_MACH_AMDGCN_GFX906 0x02f +#define EF_AMDGPU_MACH_AMDGCN_RESERVED0 0x027 +#define EF_AMDGPU_MACH_AMDGCN_RESERVED1 0x030 +#define EF_AMDGPU_MACH_AMDGCN_FIRST EF_AMDGPU_MACH_AMDGCN_GFX600 +#define EF_AMDGPU_MACH_AMDGCN_LAST EF_AMDGPU_MACH_AMDGCN_GFX906 + +///////////////////// +// Sections constants + +// Section indexes +#define SHN_UNDEF 0 +#define SHN_LORESERVE 0xFF00 +#define SHN_LOPROC 0xFF00 +#define SHN_HIPROC 0xFF1F +#define SHN_LOOS 0xFF20 +#define SHN_HIOS 0xFF3F +#define SHN_ABS 0xFFF1 +#define SHN_COMMON 0xFFF2 +#define SHN_XINDEX 0xFFFF +#define SHN_HIRESERVE 0xFFFF + +// Section types +#define SHT_NULL 0 +#define SHT_PROGBITS 1 +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_RELA 4 +#define SHT_HASH 5 +#define SHT_DYNAMIC 6 +#define SHT_NOTE 7 +#define SHT_NOBITS 8 +#define SHT_REL 9 +#define SHT_SHLIB 10 +#define SHT_DYNSYM 11 +#define SHT_INIT_ARRAY 14 +#define SHT_FINI_ARRAY 15 +#define SHT_PREINIT_ARRAY 16 +#define SHT_GROUP 17 +#define SHT_SYMTAB_SHNDX 18 +#define SHT_LOOS 0x60000000 +#define SHT_HIOS 0x6fffffff +#define SHT_LOPROC 0x70000000 +#define SHT_HIPROC 0x7FFFFFFF +#define SHT_LOUSER 0x80000000 +#define SHT_HIUSER 0xFFFFFFFF + +// Section attribute flags +#define SHF_WRITE 0x1 +#define SHF_ALLOC 0x2 +#define SHF_EXECINSTR 0x4 +#define SHF_MERGE 0x10 +#define SHF_STRINGS 0x20 +#define SHF_INFO_LINK 0x40 +#define SHF_LINK_ORDER 0x80 +#define SHF_OS_NONCONFORMING 0x100 +#define SHF_GROUP 0x200 +#define SHF_TLS 0x400 +#define SHF_MASKOS 0x0ff00000 +#define SHF_MASKPROC 0xF0000000 + +// Section group flags +#define GRP_COMDAT 0x1 +#define GRP_MASKOS 0x0ff00000 +#define GRP_MASKPROC 0xf0000000 + +// Symbol binding +#define STB_LOCAL 0 +#define STB_GLOBAL 1 +#define STB_WEAK 2 +#define STB_LOOS 10 +#define STB_HIOS 12 +#define STB_MULTIDEF 13 +#define STB_LOPROC 13 +#define STB_HIPROC 15 + +// Note types +#define NT_AMDGPU_METADATA 1 +#define NT_AMD_AMDGPU_HSA_METADATA 10 +#define NT_AMD_AMDGPU_ISA 11 +#define NT_AMD_AMDGPU_PAL_METADATA 12 + +// Symbol types +#define STT_NOTYPE 0 +#define STT_OBJECT 1 +#define STT_FUNC 2 +#define STT_SECTION 3 +#define STT_FILE 4 +#define STT_COMMON 5 +#define STT_TLS 6 +#define STT_LOOS 10 +#define STT_AMDGPU_HSA_KERNEL 10 +#define STT_HIOS 12 +#define STT_LOPROC 13 +#define STT_HIPROC 15 + +// Symbol visibility +#define STV_DEFAULT 0 +#define STV_INTERNAL 1 +#define STV_HIDDEN 2 +#define STV_PROTECTED 3 + +// Undefined name +#define STN_UNDEF 0 + +// Relocation types +#define R_386_NONE 0 +#define R_X86_64_NONE 0 +#define R_AMDGPU_NONE 0 +#define R_386_32 1 +#define R_X86_64_64 1 +#define R_AMDGPU_ABS32_LO 1 +#define R_386_PC32 2 +#define R_X86_64_PC32 2 +#define R_AMDGPU_ABS32_HI 2 +#define R_386_GOT32 3 +#define R_X86_64_GOT32 3 +#define R_AMDGPU_ABS64 3 +#define R_386_PLT32 4 +#define R_X86_64_PLT32 4 +#define R_AMDGPU_REL32 4 +#define R_386_COPY 5 +#define R_X86_64_COPY 5 +#define R_AMDGPU_REL64 5 +#define R_386_GLOB_DAT 6 +#define R_X86_64_GLOB_DAT 6 +#define R_AMDGPU_ABS32 6 +#define R_386_JMP_SLOT 7 +#define R_X86_64_JUMP_SLOT 7 +#define R_AMDGPU_GOTPCREL 7 +#define R_386_RELATIVE 8 +#define R_X86_64_RELATIVE 8 +#define R_AMDGPU_GOTPCREL32_LO 8 +#define R_386_GOTOFF 9 +#define R_X86_64_GOTPCREL 9 +#define R_AMDGPU_GOTPCREL32_HI 9 +#define R_386_GOTPC 10 +#define R_X86_64_32 10 +#define R_AMDGPU_REL32_LO 10 +#define R_386_32PLT 11 +#define R_X86_64_32S 11 +#define R_AMDGPU_REL32_HI 11 +#define R_X86_64_16 12 +#define R_X86_64_PC16 13 +#define R_AMDGPU_RELATIVE64 13 +#define R_386_TLS_TPOFF 14 +#define R_X86_64_8 14 +#define R_386_TLS_IE 15 +#define R_X86_64_PC8 15 +#define R_386_TLS_GOTIE 16 +#define R_X86_64_DTPMOD64 16 +#define R_386_TLS_LE 17 +#define R_X86_64_DTPOFF64 17 +#define R_386_TLS_GD 18 +#define R_X86_64_TPOFF64 18 +#define R_386_TLS_LDM 19 +#define R_X86_64_TLSGD 19 +#define R_386_16 20 +#define R_X86_64_TLSLD 20 +#define R_386_PC16 21 +#define R_X86_64_DTPOFF32 21 +#define R_386_8 22 +#define R_X86_64_GOTTPOFF 22 +#define R_386_PC8 23 +#define R_X86_64_TPOFF32 23 +#define R_386_TLS_GD_32 24 +#define R_X86_64_PC64 24 +#define R_386_TLS_GD_PUSH 25 +#define R_X86_64_GOTOFF64 25 +#define R_386_TLS_GD_CALL 26 +#define R_X86_64_GOTPC32 26 +#define R_386_TLS_GD_POP 27 +#define R_X86_64_GOT64 27 +#define R_386_TLS_LDM_32 28 +#define R_X86_64_GOTPCREL64 28 +#define R_386_TLS_LDM_PUSH 29 +#define R_X86_64_GOTPC64 29 +#define R_386_TLS_LDM_CALL 30 +#define R_X86_64_GOTPLT64 30 +#define R_386_TLS_LDM_POP 31 +#define R_X86_64_PLTOFF64 31 +#define R_386_TLS_LDO_32 32 +#define R_386_TLS_IE_32 33 +#define R_386_TLS_LE_32 34 +#define R_X86_64_GOTPC32_TLSDESC 34 +#define R_386_TLS_DTPMOD32 35 +#define R_X86_64_TLSDESC_CALL 35 +#define R_386_TLS_DTPOFF32 36 +#define R_X86_64_TLSDESC 36 +#define R_386_TLS_TPOFF32 37 +#define R_X86_64_IRELATIVE 37 +#define R_386_SIZE32 38 +#define R_386_TLS_GOTDESC 39 +#define R_386_TLS_DESC_CALL 40 +#define R_386_TLS_DESC 41 +#define R_386_IRELATIVE 42 +#define R_386_GOT32X 43 +#define R_X86_64_GNU_VTINHERIT 250 +#define R_X86_64_GNU_VTENTRY 251 + +// Segment types +#define PT_NULL 0 +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define PT_INTERP 3 +#define PT_NOTE 4 +#define PT_SHLIB 5 +#define PT_PHDR 6 +#define PT_TLS 7 +#define PT_LOOS 0x60000000 +#define PT_HIOS 0x6fffffff +#define PT_LOPROC 0x70000000 +#define PT_HIPROC 0x7FFFFFFF + +// Segment flags +#define PF_X 1 // Execute +#define PF_W 2 // Write +#define PF_R 4 // Read +#define PF_MASKOS 0x0ff00000 // Unspecified +#define PF_MASKPROC 0xf0000000 // Unspecified + +// Dynamic Array Tags +#define DT_NULL 0 +#define DT_NEEDED 1 +#define DT_PLTRELSZ 2 +#define DT_PLTGOT 3 +#define DT_HASH 4 +#define DT_STRTAB 5 +#define DT_SYMTAB 6 +#define DT_RELA 7 +#define DT_RELASZ 8 +#define DT_RELAENT 9 +#define DT_STRSZ 10 +#define DT_SYMENT 11 +#define DT_INIT 12 +#define DT_FINI 13 +#define DT_SONAME 14 +#define DT_RPATH 15 +#define DT_SYMBOLIC 16 +#define DT_REL 17 +#define DT_RELSZ 18 +#define DT_RELENT 19 +#define DT_PLTREL 20 +#define DT_DEBUG 21 +#define DT_TEXTREL 22 +#define DT_JMPREL 23 +#define DT_BIND_NOW 24 +#define DT_INIT_ARRAY 25 +#define DT_FINI_ARRAY 26 +#define DT_INIT_ARRAYSZ 27 +#define DT_FINI_ARRAYSZ 28 +#define DT_RUNPATH 29 +#define DT_FLAGS 30 +#define DT_ENCODING 32 +#define DT_PREINIT_ARRAY 32 +#define DT_PREINIT_ARRAYSZ 33 +#define DT_MAXPOSTAGS 34 +#define DT_LOOS 0x6000000D +#define DT_HIOS 0x6ffff000 +#define DT_LOPROC 0x70000000 +#define DT_HIPROC 0x7FFFFFFF + +// DT_FLAGS values +#define DF_ORIGIN 0x1 +#define DF_SYMBOLIC 0x2 +#define DF_TEXTREL 0x4 +#define DF_BIND_NOW 0x8 +#define DF_STATIC_TLS 0x10 + +// ELF file header +struct Elf32_Ehdr +{ + unsigned char e_ident[EI_NIDENT]; + Elf_Half e_type; + Elf_Half e_machine; + Elf_Word e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf_Word e_flags; + Elf_Half e_ehsize; + Elf_Half e_phentsize; + Elf_Half e_phnum; + Elf_Half e_shentsize; + Elf_Half e_shnum; + Elf_Half e_shstrndx; +}; + +struct Elf64_Ehdr +{ + unsigned char e_ident[EI_NIDENT]; + Elf_Half e_type; + Elf_Half e_machine; + Elf_Word e_version; + Elf64_Addr e_entry; + Elf64_Off e_phoff; + Elf64_Off e_shoff; + Elf_Word e_flags; + Elf_Half e_ehsize; + Elf_Half e_phentsize; + Elf_Half e_phnum; + Elf_Half e_shentsize; + Elf_Half e_shnum; + Elf_Half e_shstrndx; +}; + +// Section header +struct Elf32_Shdr +{ + Elf_Word sh_name; + Elf_Word sh_type; + Elf_Word sh_flags; + Elf32_Addr sh_addr; + Elf32_Off sh_offset; + Elf_Word sh_size; + Elf_Word sh_link; + Elf_Word sh_info; + Elf_Word sh_addralign; + Elf_Word sh_entsize; +}; + +struct Elf64_Shdr +{ + Elf_Word sh_name; + Elf_Word sh_type; + Elf_Xword sh_flags; + Elf64_Addr sh_addr; + Elf64_Off sh_offset; + Elf_Xword sh_size; + Elf_Word sh_link; + Elf_Word sh_info; + Elf_Xword sh_addralign; + Elf_Xword sh_entsize; +}; + +// Segment header +struct Elf32_Phdr +{ + Elf_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf_Word p_filesz; + Elf_Word p_memsz; + Elf_Word p_flags; + Elf_Word p_align; +}; + +struct Elf64_Phdr +{ + Elf_Word p_type; + Elf_Word p_flags; + Elf64_Off p_offset; + Elf64_Addr p_vaddr; + Elf64_Addr p_paddr; + Elf_Xword p_filesz; + Elf_Xword p_memsz; + Elf_Xword p_align; +}; + +// Symbol table entry +struct Elf32_Sym +{ + Elf_Word st_name; + Elf32_Addr st_value; + Elf_Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf_Half st_shndx; +}; + +struct Elf64_Sym +{ + Elf_Word st_name; + unsigned char st_info; + unsigned char st_other; + Elf_Half st_shndx; + Elf64_Addr st_value; + Elf_Xword st_size; +}; + +#define ELF_ST_BIND( i ) ( ( i ) >> 4 ) +#define ELF_ST_TYPE( i ) ( (i)&0xf ) +#define ELF_ST_INFO( b, t ) ( ( ( b ) << 4 ) + ( (t)&0xf ) ) + +#define ELF_ST_VISIBILITY( o ) ( (o)&0x3 ) + +// Relocation entries +struct Elf32_Rel +{ + Elf32_Addr r_offset; + Elf_Word r_info; +}; + +struct Elf32_Rela +{ + Elf32_Addr r_offset; + Elf_Word r_info; + Elf_Sword r_addend; +}; + +struct Elf64_Rel +{ + Elf64_Addr r_offset; + Elf_Xword r_info; +}; + +struct Elf64_Rela +{ + Elf64_Addr r_offset; + Elf_Xword r_info; + Elf_Sxword r_addend; +}; + +#define ELF32_R_SYM( i ) ( ( i ) >> 8 ) +#define ELF32_R_TYPE( i ) ( (unsigned char)( i ) ) +#define ELF32_R_INFO( s, t ) ( ( ( s ) << 8 ) + (unsigned char)( t ) ) + +#define ELF64_R_SYM( i ) ( ( i ) >> 32 ) +#define ELF64_R_TYPE( i ) ( (i)&0xffffffffL ) +#define ELF64_R_INFO( s, t ) \ + ( ( ( ( int64_t )( s ) ) << 32 ) + ( (t)&0xffffffffL ) ) + +// Dynamic structure +struct Elf32_Dyn +{ + Elf_Sword d_tag; + union { + Elf_Word d_val; + Elf32_Addr d_ptr; + } d_un; +}; + +struct Elf64_Dyn +{ + Elf_Sxword d_tag; + union { + Elf_Xword d_val; + Elf64_Addr d_ptr; + } d_un; +}; + +} // namespace ELFIO + +#endif // ELFTYPES_H + +/*** End of inlined file: elf_types.hpp ***/ + + +/*** Start of inlined file: elfio_version.hpp ***/ +#define ELFIO_VERSION "3.8" + +/*** End of inlined file: elfio_version.hpp ***/ + + +/*** Start of inlined file: elfio_utils.hpp ***/ +#ifndef ELFIO_UTILS_HPP +#define ELFIO_UTILS_HPP + +#define ELFIO_GET_ACCESS( TYPE, NAME, FIELD ) \ + TYPE get_##NAME() const { return ( *convertor )( FIELD ); } +#define ELFIO_SET_ACCESS( TYPE, NAME, FIELD ) \ + void set_##NAME( TYPE value ) \ + { \ + FIELD = value; \ + FIELD = ( *convertor )( FIELD ); \ + } +#define ELFIO_GET_SET_ACCESS( TYPE, NAME, FIELD ) \ + TYPE get_##NAME() const { return ( *convertor )( FIELD ); } \ + void set_##NAME( TYPE value ) \ + { \ + FIELD = value; \ + FIELD = ( *convertor )( FIELD ); \ + } + +#define ELFIO_GET_ACCESS_DECL( TYPE, NAME ) virtual TYPE get_##NAME() const = 0 + +#define ELFIO_SET_ACCESS_DECL( TYPE, NAME ) \ + virtual void set_##NAME( TYPE value ) = 0 + +#define ELFIO_GET_SET_ACCESS_DECL( TYPE, NAME ) \ + virtual TYPE get_##NAME() const = 0; \ + virtual void set_##NAME( TYPE value ) = 0 + +namespace ELFIO { + +//------------------------------------------------------------------------------ +class endianess_convertor +{ + public: + //------------------------------------------------------------------------------ + endianess_convertor() { need_conversion = false; } + + //------------------------------------------------------------------------------ + void setup( unsigned char elf_file_encoding ) + { + need_conversion = ( elf_file_encoding != get_host_encoding() ); + } + + //------------------------------------------------------------------------------ + uint64_t operator()( uint64_t value ) const + { + if ( !need_conversion ) { + return value; + } + value = ( ( value & 0x00000000000000FFull ) << 56 ) | + ( ( value & 0x000000000000FF00ull ) << 40 ) | + ( ( value & 0x0000000000FF0000ull ) << 24 ) | + ( ( value & 0x00000000FF000000ull ) << 8 ) | + ( ( value & 0x000000FF00000000ull ) >> 8 ) | + ( ( value & 0x0000FF0000000000ull ) >> 24 ) | + ( ( value & 0x00FF000000000000ull ) >> 40 ) | + ( ( value & 0xFF00000000000000ull ) >> 56 ); + + return value; + } + + //------------------------------------------------------------------------------ + int64_t operator()( int64_t value ) const + { + if ( !need_conversion ) { + return value; + } + return ( int64_t )( *this )( (uint64_t)value ); + } + + //------------------------------------------------------------------------------ + uint32_t operator()( uint32_t value ) const + { + if ( !need_conversion ) { + return value; + } + value = + ( ( value & 0x000000FF ) << 24 ) | ( ( value & 0x0000FF00 ) << 8 ) | + ( ( value & 0x00FF0000 ) >> 8 ) | ( ( value & 0xFF000000 ) >> 24 ); + + return value; + } + + //------------------------------------------------------------------------------ + int32_t operator()( int32_t value ) const + { + if ( !need_conversion ) { + return value; + } + return ( int32_t )( *this )( (uint32_t)value ); + } + + //------------------------------------------------------------------------------ + uint16_t operator()( uint16_t value ) const + { + if ( !need_conversion ) { + return value; + } + value = ( ( value & 0x00FF ) << 8 ) | ( ( value & 0xFF00 ) >> 8 ); + + return value; + } + + //------------------------------------------------------------------------------ + int16_t operator()( int16_t value ) const + { + if ( !need_conversion ) { + return value; + } + return ( int16_t )( *this )( (uint16_t)value ); + } + + //------------------------------------------------------------------------------ + int8_t operator()( int8_t value ) const { return value; } + + //------------------------------------------------------------------------------ + uint8_t operator()( uint8_t value ) const { return value; } + + //------------------------------------------------------------------------------ + private: + //------------------------------------------------------------------------------ + unsigned char get_host_encoding() const + { + static const int tmp = 1; + if ( 1 == *(const char*)&tmp ) { + return ELFDATA2LSB; + } + else { + return ELFDATA2MSB; + } + } + + //------------------------------------------------------------------------------ + private: + bool need_conversion; +}; + +//------------------------------------------------------------------------------ +inline uint32_t elf_hash( const unsigned char* name ) +{ + uint32_t h = 0, g; + while ( *name ) { + h = ( h << 4 ) + *name++; + g = h & 0xf0000000; + if ( g != 0 ) + h ^= g >> 24; + h &= ~g; + } + return h; +} + +} // namespace ELFIO + +#endif // ELFIO_UTILS_HPP + +/*** End of inlined file: elfio_utils.hpp ***/ + + +/*** Start of inlined file: elfio_header.hpp ***/ +#ifndef ELF_HEADER_HPP +#define ELF_HEADER_HPP + +#include + +namespace ELFIO { + +class elf_header +{ + public: + virtual ~elf_header(){}; + virtual bool load( std::istream& stream ) = 0; + virtual bool save( std::ostream& stream ) const = 0; + + // ELF header functions + ELFIO_GET_ACCESS_DECL( unsigned char, class ); + ELFIO_GET_ACCESS_DECL( unsigned char, elf_version ); + ELFIO_GET_ACCESS_DECL( unsigned char, encoding ); + ELFIO_GET_ACCESS_DECL( Elf_Half, header_size ); + ELFIO_GET_ACCESS_DECL( Elf_Half, section_entry_size ); + ELFIO_GET_ACCESS_DECL( Elf_Half, segment_entry_size ); + + ELFIO_GET_SET_ACCESS_DECL( Elf_Word, version ); + ELFIO_GET_SET_ACCESS_DECL( unsigned char, os_abi ); + ELFIO_GET_SET_ACCESS_DECL( unsigned char, abi_version ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Half, type ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Half, machine ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Word, flags ); + ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, entry ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Half, sections_num ); + ELFIO_GET_SET_ACCESS_DECL( Elf64_Off, sections_offset ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Half, segments_num ); + ELFIO_GET_SET_ACCESS_DECL( Elf64_Off, segments_offset ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Half, section_name_str_index ); +}; + +template struct elf_header_impl_types; +template <> struct elf_header_impl_types +{ + typedef Elf32_Phdr Phdr_type; + typedef Elf32_Shdr Shdr_type; + static const unsigned char file_class = ELFCLASS32; +}; +template <> struct elf_header_impl_types +{ + typedef Elf64_Phdr Phdr_type; + typedef Elf64_Shdr Shdr_type; + static const unsigned char file_class = ELFCLASS64; +}; + +template class elf_header_impl : public elf_header +{ + public: + //------------------------------------------------------------------------------ + elf_header_impl( endianess_convertor* convertor_, unsigned char encoding ) + { + convertor = convertor_; + + std::fill_n( reinterpret_cast( &header ), sizeof( header ), + '\0' ); + + header.e_ident[EI_MAG0] = ELFMAG0; + header.e_ident[EI_MAG1] = ELFMAG1; + header.e_ident[EI_MAG2] = ELFMAG2; + header.e_ident[EI_MAG3] = ELFMAG3; + header.e_ident[EI_CLASS] = elf_header_impl_types::file_class; + header.e_ident[EI_DATA] = encoding; + header.e_ident[EI_VERSION] = EV_CURRENT; + header.e_version = ( *convertor )( (Elf_Word)EV_CURRENT ); + header.e_ehsize = ( sizeof( header ) ); + header.e_ehsize = ( *convertor )( header.e_ehsize ); + header.e_shstrndx = ( *convertor )( (Elf_Half)1 ); + header.e_phentsize = + sizeof( typename elf_header_impl_types::Phdr_type ); + header.e_shentsize = + sizeof( typename elf_header_impl_types::Shdr_type ); + header.e_phentsize = ( *convertor )( header.e_phentsize ); + header.e_shentsize = ( *convertor )( header.e_shentsize ); + } + + //------------------------------------------------------------------------------ + bool load( std::istream& stream ) + { + stream.seekg( 0 ); + stream.read( reinterpret_cast( &header ), sizeof( header ) ); + + return ( stream.gcount() == sizeof( header ) ); + } + + //------------------------------------------------------------------------------ + bool save( std::ostream& stream ) const + { + stream.seekp( 0 ); + stream.write( reinterpret_cast( &header ), + sizeof( header ) ); + + return stream.good(); + } + + //------------------------------------------------------------------------------ + // ELF header functions + ELFIO_GET_ACCESS( unsigned char, class, header.e_ident[EI_CLASS] ); + ELFIO_GET_ACCESS( unsigned char, elf_version, header.e_ident[EI_VERSION] ); + ELFIO_GET_ACCESS( unsigned char, encoding, header.e_ident[EI_DATA] ); + ELFIO_GET_ACCESS( Elf_Half, header_size, header.e_ehsize ); + ELFIO_GET_ACCESS( Elf_Half, section_entry_size, header.e_shentsize ); + ELFIO_GET_ACCESS( Elf_Half, segment_entry_size, header.e_phentsize ); + + ELFIO_GET_SET_ACCESS( Elf_Word, version, header.e_version ); + ELFIO_GET_SET_ACCESS( unsigned char, os_abi, header.e_ident[EI_OSABI] ); + ELFIO_GET_SET_ACCESS( unsigned char, + abi_version, + header.e_ident[EI_ABIVERSION] ); + ELFIO_GET_SET_ACCESS( Elf_Half, type, header.e_type ); + ELFIO_GET_SET_ACCESS( Elf_Half, machine, header.e_machine ); + ELFIO_GET_SET_ACCESS( Elf_Word, flags, header.e_flags ); + ELFIO_GET_SET_ACCESS( Elf_Half, section_name_str_index, header.e_shstrndx ); + ELFIO_GET_SET_ACCESS( Elf64_Addr, entry, header.e_entry ); + ELFIO_GET_SET_ACCESS( Elf_Half, sections_num, header.e_shnum ); + ELFIO_GET_SET_ACCESS( Elf64_Off, sections_offset, header.e_shoff ); + ELFIO_GET_SET_ACCESS( Elf_Half, segments_num, header.e_phnum ); + ELFIO_GET_SET_ACCESS( Elf64_Off, segments_offset, header.e_phoff ); + + private: + T header; + endianess_convertor* convertor; +}; + +} // namespace ELFIO + +#endif // ELF_HEADER_HPP + +/*** End of inlined file: elfio_header.hpp ***/ + + +/*** Start of inlined file: elfio_section.hpp ***/ +#ifndef ELFIO_SECTION_HPP +#define ELFIO_SECTION_HPP + +#include +#include +#include + +namespace ELFIO { + +class section +{ + friend class elfio; + + public: + virtual ~section(){}; + + ELFIO_GET_ACCESS_DECL( Elf_Half, index ); + ELFIO_GET_SET_ACCESS_DECL( std::string, name ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Word, type ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, flags ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Word, info ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Word, link ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, addr_align ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, entry_size ); + ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, address ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, size ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Word, name_string_offset ); + ELFIO_GET_ACCESS_DECL( Elf64_Off, offset ); + + virtual const char* get_data() const = 0; + virtual void set_data( const char* pData, Elf_Word size ) = 0; + virtual void set_data( const std::string& data ) = 0; + virtual void append_data( const char* pData, Elf_Word size ) = 0; + virtual void append_data( const std::string& data ) = 0; + virtual size_t get_stream_size() const = 0; + virtual void set_stream_size( size_t value ) = 0; + + protected: + ELFIO_SET_ACCESS_DECL( Elf64_Off, offset ); + ELFIO_SET_ACCESS_DECL( Elf_Half, index ); + + virtual void load( std::istream& stream, std::streampos header_offset ) = 0; + virtual void save( std::ostream& stream, + std::streampos header_offset, + std::streampos data_offset ) = 0; + virtual bool is_address_initialized() const = 0; +}; + +template class section_impl : public section +{ + public: + //------------------------------------------------------------------------------ + section_impl( const endianess_convertor* convertor_ ) + : convertor( convertor_ ) + { + std::fill_n( reinterpret_cast( &header ), sizeof( header ), + '\0' ); + is_address_set = false; + data = 0; + data_size = 0; + index = 0; + stream_size = 0; + } + + //------------------------------------------------------------------------------ + ~section_impl() { delete[] data; } + + //------------------------------------------------------------------------------ + // Section info functions + ELFIO_GET_SET_ACCESS( Elf_Word, type, header.sh_type ); + ELFIO_GET_SET_ACCESS( Elf_Xword, flags, header.sh_flags ); + ELFIO_GET_SET_ACCESS( Elf_Xword, size, header.sh_size ); + ELFIO_GET_SET_ACCESS( Elf_Word, link, header.sh_link ); + ELFIO_GET_SET_ACCESS( Elf_Word, info, header.sh_info ); + ELFIO_GET_SET_ACCESS( Elf_Xword, addr_align, header.sh_addralign ); + ELFIO_GET_SET_ACCESS( Elf_Xword, entry_size, header.sh_entsize ); + ELFIO_GET_SET_ACCESS( Elf_Word, name_string_offset, header.sh_name ); + ELFIO_GET_ACCESS( Elf64_Addr, address, header.sh_addr ); + + //------------------------------------------------------------------------------ + Elf_Half get_index() const { return index; } + + //------------------------------------------------------------------------------ + std::string get_name() const { return name; } + + //------------------------------------------------------------------------------ + void set_name( std::string name_ ) { name = name_; } + + //------------------------------------------------------------------------------ + void set_address( Elf64_Addr value ) + { + header.sh_addr = value; + header.sh_addr = ( *convertor )( header.sh_addr ); + is_address_set = true; + } + + //------------------------------------------------------------------------------ + bool is_address_initialized() const { return is_address_set; } + + //------------------------------------------------------------------------------ + const char* get_data() const { return data; } + + //------------------------------------------------------------------------------ + void set_data( const char* raw_data, Elf_Word size ) + { + if ( get_type() != SHT_NOBITS ) { + delete[] data; + data = new ( std::nothrow ) char[size]; + if ( 0 != data && 0 != raw_data ) { + data_size = size; + std::copy( raw_data, raw_data + size, data ); + } + else { + data_size = 0; + } + } + + set_size( data_size ); + } + + //------------------------------------------------------------------------------ + void set_data( const std::string& str_data ) + { + return set_data( str_data.c_str(), (Elf_Word)str_data.size() ); + } + + //------------------------------------------------------------------------------ + void append_data( const char* raw_data, Elf_Word size ) + { + if ( get_type() != SHT_NOBITS ) { + if ( get_size() + size < data_size ) { + std::copy( raw_data, raw_data + size, data + get_size() ); + } + else { + data_size = 2 * ( data_size + size ); + char* new_data = new ( std::nothrow ) char[data_size]; + + if ( 0 != new_data ) { + std::copy( data, data + get_size(), new_data ); + std::copy( raw_data, raw_data + size, + new_data + get_size() ); + delete[] data; + data = new_data; + } + else { + size = 0; + } + } + set_size( get_size() + size ); + } + } + + //------------------------------------------------------------------------------ + void append_data( const std::string& str_data ) + { + return append_data( str_data.c_str(), (Elf_Word)str_data.size() ); + } + + //------------------------------------------------------------------------------ + protected: + //------------------------------------------------------------------------------ + ELFIO_GET_SET_ACCESS( Elf64_Off, offset, header.sh_offset ); + + //------------------------------------------------------------------------------ + void set_index( Elf_Half value ) { index = value; } + + //------------------------------------------------------------------------------ + void load( std::istream& stream, std::streampos header_offset ) + { + std::fill_n( reinterpret_cast( &header ), sizeof( header ), + '\0' ); + + stream.seekg( 0, stream.end ); + set_stream_size( stream.tellg() ); + + stream.seekg( header_offset ); + stream.read( reinterpret_cast( &header ), sizeof( header ) ); + + Elf_Xword size = get_size(); + if ( 0 == data && SHT_NULL != get_type() && SHT_NOBITS != get_type() && + size < get_stream_size() ) { + data = new ( std::nothrow ) char[size + 1]; + + if ( ( 0 != size ) && ( 0 != data ) ) { + stream.seekg( ( *convertor )( header.sh_offset ) ); + stream.read( data, size ); + data[size] = 0; // Ensure data is ended with 0 to avoid oob read + data_size = size; + } + else { + data_size = 0; + } + } + } + + //------------------------------------------------------------------------------ + void save( std::ostream& stream, + std::streampos header_offset, + std::streampos data_offset ) + { + if ( 0 != get_index() ) { + header.sh_offset = data_offset; + header.sh_offset = ( *convertor )( header.sh_offset ); + } + + save_header( stream, header_offset ); + if ( get_type() != SHT_NOBITS && get_type() != SHT_NULL && + get_size() != 0 && data != 0 ) { + save_data( stream, data_offset ); + } + } + + //------------------------------------------------------------------------------ + private: + //------------------------------------------------------------------------------ + void save_header( std::ostream& stream, std::streampos header_offset ) const + { + stream.seekp( header_offset ); + stream.write( reinterpret_cast( &header ), + sizeof( header ) ); + } + + //------------------------------------------------------------------------------ + void save_data( std::ostream& stream, std::streampos data_offset ) const + { + stream.seekp( data_offset ); + stream.write( get_data(), get_size() ); + } + + //------------------------------------------------------------------------------ + size_t get_stream_size() const { return stream_size; } + + //------------------------------------------------------------------------------ + void set_stream_size( size_t value ) { stream_size = value; } + + //------------------------------------------------------------------------------ + private: + T header; + Elf_Half index; + std::string name; + char* data; + Elf_Word data_size; + const endianess_convertor* convertor; + bool is_address_set; + size_t stream_size; +}; + +} // namespace ELFIO + +#endif // ELFIO_SECTION_HPP + +/*** End of inlined file: elfio_section.hpp ***/ + + +/*** Start of inlined file: elfio_segment.hpp ***/ +#ifndef ELFIO_SEGMENT_HPP +#define ELFIO_SEGMENT_HPP + +#include +#include +#include + +namespace ELFIO { + +class segment +{ + friend class elfio; + + public: + virtual ~segment(){}; + + ELFIO_GET_ACCESS_DECL( Elf_Half, index ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Word, type ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Word, flags ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, align ); + ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, virtual_address ); + ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, physical_address ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, file_size ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, memory_size ); + ELFIO_GET_ACCESS_DECL( Elf64_Off, offset ); + + virtual const char* get_data() const = 0; + + virtual Elf_Half add_section_index( Elf_Half index, + Elf_Xword addr_align ) = 0; + virtual Elf_Half get_sections_num() const = 0; + virtual Elf_Half get_section_index_at( Elf_Half num ) const = 0; + virtual bool is_offset_initialized() const = 0; + + protected: + ELFIO_SET_ACCESS_DECL( Elf64_Off, offset ); + ELFIO_SET_ACCESS_DECL( Elf_Half, index ); + + virtual const std::vector& get_sections() const = 0; + virtual void load( std::istream& stream, std::streampos header_offset ) = 0; + virtual void save( std::ostream& stream, + std::streampos header_offset, + std::streampos data_offset ) = 0; +}; + +//------------------------------------------------------------------------------ +template class segment_impl : public segment +{ + public: + //------------------------------------------------------------------------------ + segment_impl( endianess_convertor* convertor_ ) + : stream_size( 0 ), index( 0 ), data( 0 ), convertor( convertor_ ) + { + is_offset_set = false; + std::fill_n( reinterpret_cast( &ph ), sizeof( ph ), '\0' ); + } + + //------------------------------------------------------------------------------ + virtual ~segment_impl() { delete[] data; } + + //------------------------------------------------------------------------------ + // Section info functions + ELFIO_GET_SET_ACCESS( Elf_Word, type, ph.p_type ); + ELFIO_GET_SET_ACCESS( Elf_Word, flags, ph.p_flags ); + ELFIO_GET_SET_ACCESS( Elf_Xword, align, ph.p_align ); + ELFIO_GET_SET_ACCESS( Elf64_Addr, virtual_address, ph.p_vaddr ); + ELFIO_GET_SET_ACCESS( Elf64_Addr, physical_address, ph.p_paddr ); + ELFIO_GET_SET_ACCESS( Elf_Xword, file_size, ph.p_filesz ); + ELFIO_GET_SET_ACCESS( Elf_Xword, memory_size, ph.p_memsz ); + ELFIO_GET_ACCESS( Elf64_Off, offset, ph.p_offset ); + size_t stream_size; + + //------------------------------------------------------------------------------ + size_t get_stream_size() const { return stream_size; } + + //------------------------------------------------------------------------------ + void set_stream_size( size_t value ) { stream_size = value; } + + //------------------------------------------------------------------------------ + Elf_Half get_index() const { return index; } + + //------------------------------------------------------------------------------ + const char* get_data() const { return data; } + + //------------------------------------------------------------------------------ + Elf_Half add_section_index( Elf_Half sec_index, Elf_Xword addr_align ) + { + sections.push_back( sec_index ); + if ( addr_align > get_align() ) { + set_align( addr_align ); + } + + return (Elf_Half)sections.size(); + } + + //------------------------------------------------------------------------------ + Elf_Half get_sections_num() const { return (Elf_Half)sections.size(); } + + //------------------------------------------------------------------------------ + Elf_Half get_section_index_at( Elf_Half num ) const + { + if ( num < sections.size() ) { + return sections[num]; + } + + return Elf_Half( -1 ); + } + + //------------------------------------------------------------------------------ + protected: + //------------------------------------------------------------------------------ + + //------------------------------------------------------------------------------ + void set_offset( Elf64_Off value ) + { + ph.p_offset = value; + ph.p_offset = ( *convertor )( ph.p_offset ); + is_offset_set = true; + } + + //------------------------------------------------------------------------------ + bool is_offset_initialized() const { return is_offset_set; } + + //------------------------------------------------------------------------------ + const std::vector& get_sections() const { return sections; } + + //------------------------------------------------------------------------------ + void set_index( Elf_Half value ) { index = value; } + + //------------------------------------------------------------------------------ + void load( std::istream& stream, std::streampos header_offset ) + { + + stream.seekg( 0, stream.end ); + set_stream_size( stream.tellg() ); + + stream.seekg( header_offset ); + stream.read( reinterpret_cast( &ph ), sizeof( ph ) ); + is_offset_set = true; + + if ( PT_NULL != get_type() && 0 != get_file_size() ) { + stream.seekg( ( *convertor )( ph.p_offset ) ); + Elf_Xword size = get_file_size(); + + if ( size > get_stream_size() ) { + data = 0; + } + else { + data = new (std::nothrow) char[size + 1]; + + if ( 0 != data ) { + stream.read( data, size ); + data[size] = 0; + } + } + } + } + + //------------------------------------------------------------------------------ + void save( std::ostream& stream, + std::streampos header_offset, + std::streampos data_offset ) + { + ph.p_offset = data_offset; + ph.p_offset = ( *convertor )( ph.p_offset ); + stream.seekp( header_offset ); + stream.write( reinterpret_cast( &ph ), sizeof( ph ) ); + } + + //------------------------------------------------------------------------------ + private: + T ph; + Elf_Half index; + char* data; + std::vector sections; + endianess_convertor* convertor; + bool is_offset_set; +}; + +} // namespace ELFIO + +#endif // ELFIO_SEGMENT_HPP + +/*** End of inlined file: elfio_segment.hpp ***/ + + +/*** Start of inlined file: elfio_strings.hpp ***/ +#ifndef ELFIO_STRINGS_HPP +#define ELFIO_STRINGS_HPP + +#include +#include +#include + +namespace ELFIO { + +//------------------------------------------------------------------------------ +template class string_section_accessor_template +{ + public: + //------------------------------------------------------------------------------ + string_section_accessor_template( S* section_ ) : string_section( section_ ) + { + } + + //------------------------------------------------------------------------------ + const char* get_string( Elf_Word index ) const + { + if ( string_section ) { + if ( index < string_section->get_size() ) { + const char* data = string_section->get_data(); + if ( 0 != data ) { + return data + index; + } + } + } + + return 0; + } + + //------------------------------------------------------------------------------ + Elf_Word add_string( const char* str ) + { + Elf_Word current_position = 0; + + if ( string_section ) { + // Strings are addeded to the end of the current section data + current_position = (Elf_Word)string_section->get_size(); + + if ( current_position == 0 ) { + char empty_string = '\0'; + string_section->append_data( &empty_string, 1 ); + current_position++; + } + string_section->append_data( str, + (Elf_Word)std::strlen( str ) + 1 ); + } + + return current_position; + } + + //------------------------------------------------------------------------------ + Elf_Word add_string( const std::string& str ) + { + return add_string( str.c_str() ); + } + + //------------------------------------------------------------------------------ + private: + S* string_section; +}; + +using string_section_accessor = string_section_accessor_template
; +using const_string_section_accessor = + string_section_accessor_template; + +} // namespace ELFIO + +#endif // ELFIO_STRINGS_HPP + +/*** End of inlined file: elfio_strings.hpp ***/ + +#define ELFIO_HEADER_ACCESS_GET( TYPE, FNAME ) \ + TYPE get_##FNAME() const { return header ? ( header->get_##FNAME() ) : 0; } + +#define ELFIO_HEADER_ACCESS_GET_SET( TYPE, FNAME ) \ + TYPE get_##FNAME() const \ + { \ + return header ? ( header->get_##FNAME() ) : 0; \ + } \ + void set_##FNAME( TYPE val ) \ + { \ + if ( header ) { \ + header->set_##FNAME( val ); \ + } \ + } + +namespace ELFIO { + +//------------------------------------------------------------------------------ +class elfio +{ + public: + //------------------------------------------------------------------------------ + elfio() : sections( this ), segments( this ) + { + header = 0; + current_file_pos = 0; + create( ELFCLASS32, ELFDATA2LSB ); + } + + //------------------------------------------------------------------------------ + ~elfio() { clean(); } + + //------------------------------------------------------------------------------ + void create( unsigned char file_class, unsigned char encoding ) + { + clean(); + convertor.setup( encoding ); + header = create_header( file_class, encoding ); + create_mandatory_sections(); + } + + //------------------------------------------------------------------------------ + bool load( const std::string& file_name ) + { + std::ifstream stream; + stream.open( file_name.c_str(), std::ios::in | std::ios::binary ); + if ( !stream ) { + return false; + } + + return load( stream ); + } + + //------------------------------------------------------------------------------ + bool load( std::istream& stream ) + { + clean(); + + unsigned char e_ident[EI_NIDENT]; + // Read ELF file signature + stream.read( reinterpret_cast( &e_ident ), sizeof( e_ident ) ); + + // Is it ELF file? + if ( stream.gcount() != sizeof( e_ident ) || + e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 || + e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3 ) { + return false; + } + + if ( ( e_ident[EI_CLASS] != ELFCLASS64 ) && + ( e_ident[EI_CLASS] != ELFCLASS32 ) ) { + return false; + } + + convertor.setup( e_ident[EI_DATA] ); + header = create_header( e_ident[EI_CLASS], e_ident[EI_DATA] ); + if ( 0 == header ) { + return false; + } + if ( !header->load( stream ) ) { + return false; + } + + load_sections( stream ); + bool is_still_good = load_segments( stream ); + return is_still_good; + } + + //------------------------------------------------------------------------------ + bool save( const std::string& file_name ) + { + std::ofstream stream; + stream.open( file_name.c_str(), std::ios::out | std::ios::binary ); + if ( !stream ) { + return false; + } + + return save( stream ); + } + + //------------------------------------------------------------------------------ + bool save( std::ostream& stream ) + { + if ( !stream || !header ) { + return false; + } + + bool is_still_good = true; + // Define layout specific header fields + // The position of the segment table is fixed after the header. + // The position of the section table is variable and needs to be fixed + // before saving. + header->set_segments_num( segments.size() ); + header->set_segments_offset( segments.size() ? header->get_header_size() + : 0 ); + header->set_sections_num( sections.size() ); + header->set_sections_offset( 0 ); + + // Layout the first section right after the segment table + current_file_pos = header->get_header_size() + + header->get_segment_entry_size() * + (Elf_Xword)header->get_segments_num(); + + calc_segment_alignment(); + + is_still_good = layout_segments_and_their_sections(); + is_still_good = is_still_good && layout_sections_without_segments(); + is_still_good = is_still_good && layout_section_table(); + + is_still_good = is_still_good && save_header( stream ); + is_still_good = is_still_good && save_sections( stream ); + is_still_good = is_still_good && save_segments( stream ); + + return is_still_good; + } + + //------------------------------------------------------------------------------ + // ELF header access functions + ELFIO_HEADER_ACCESS_GET( unsigned char, class ); + ELFIO_HEADER_ACCESS_GET( unsigned char, elf_version ); + ELFIO_HEADER_ACCESS_GET( unsigned char, encoding ); + ELFIO_HEADER_ACCESS_GET( Elf_Word, version ); + ELFIO_HEADER_ACCESS_GET( Elf_Half, header_size ); + ELFIO_HEADER_ACCESS_GET( Elf_Half, section_entry_size ); + ELFIO_HEADER_ACCESS_GET( Elf_Half, segment_entry_size ); + + ELFIO_HEADER_ACCESS_GET_SET( unsigned char, os_abi ); + ELFIO_HEADER_ACCESS_GET_SET( unsigned char, abi_version ); + ELFIO_HEADER_ACCESS_GET_SET( Elf_Half, type ); + ELFIO_HEADER_ACCESS_GET_SET( Elf_Half, machine ); + ELFIO_HEADER_ACCESS_GET_SET( Elf_Word, flags ); + ELFIO_HEADER_ACCESS_GET_SET( Elf64_Addr, entry ); + ELFIO_HEADER_ACCESS_GET_SET( Elf64_Off, sections_offset ); + ELFIO_HEADER_ACCESS_GET_SET( Elf64_Off, segments_offset ); + ELFIO_HEADER_ACCESS_GET_SET( Elf_Half, section_name_str_index ); + + //------------------------------------------------------------------------------ + const endianess_convertor& get_convertor() const { return convertor; } + + //------------------------------------------------------------------------------ + Elf_Xword get_default_entry_size( Elf_Word section_type ) const + { + switch ( section_type ) { + case SHT_RELA: + if ( header->get_class() == ELFCLASS64 ) { + return sizeof( Elf64_Rela ); + } + else { + return sizeof( Elf32_Rela ); + } + case SHT_REL: + if ( header->get_class() == ELFCLASS64 ) { + return sizeof( Elf64_Rel ); + } + else { + return sizeof( Elf32_Rel ); + } + case SHT_SYMTAB: + if ( header->get_class() == ELFCLASS64 ) { + return sizeof( Elf64_Sym ); + } + else { + return sizeof( Elf32_Sym ); + } + case SHT_DYNAMIC: + if ( header->get_class() == ELFCLASS64 ) { + return sizeof( Elf64_Dyn ); + } + else { + return sizeof( Elf32_Dyn ); + } + default: + return 0; + } + } + + //------------------------------------------------------------------------------ + private: + bool is_offset_in_section( Elf64_Off offset, const section* sec ) const + { + return ( offset >= sec->get_offset() ) && + ( offset < ( sec->get_offset() + sec->get_size() ) ); + } + + //------------------------------------------------------------------------------ + public: + //! returns an empty string if no problems are detected, + //! or a string containing an error message if problems are found + std::string validate() const + { + + // check for overlapping sections in the file + for ( int i = 0; i < sections.size(); ++i ) { + for ( int j = i + 1; j < sections.size(); ++j ) { + const section* a = sections[i]; + const section* b = sections[j]; + if ( !( a->get_type() & SHT_NOBITS ) && + !( b->get_type() & SHT_NOBITS ) && ( a->get_size() > 0 ) && + ( b->get_size() > 0 ) && ( a->get_offset() > 0 ) && + ( b->get_offset() > 0 ) ) { + if ( is_offset_in_section( a->get_offset(), b ) || + is_offset_in_section( + a->get_offset() + a->get_size() - 1, b ) || + is_offset_in_section( b->get_offset(), a ) || + is_offset_in_section( + b->get_offset() + b->get_size() - 1, a ) ) { + return "Sections " + a->get_name() + " and " + + b->get_name() + " overlap in file"; + } + } + } + } + + // more checks to be added here... + + return ""; + } + + //------------------------------------------------------------------------------ + private: + //------------------------------------------------------------------------------ + void clean() + { + delete header; + header = 0; + + std::vector::const_iterator it; + for ( it = sections_.begin(); it != sections_.end(); ++it ) { + delete *it; + } + sections_.clear(); + + std::vector::const_iterator it1; + for ( it1 = segments_.begin(); it1 != segments_.end(); ++it1 ) { + delete *it1; + } + segments_.clear(); + } + + //------------------------------------------------------------------------------ + elf_header* create_header( unsigned char file_class, + unsigned char encoding ) + { + elf_header* new_header = 0; + + if ( file_class == ELFCLASS64 ) { + new_header = + new elf_header_impl( &convertor, encoding ); + } + else if ( file_class == ELFCLASS32 ) { + new_header = + new elf_header_impl( &convertor, encoding ); + } + else { + return 0; + } + + return new_header; + } + + //------------------------------------------------------------------------------ + section* create_section() + { + section* new_section; + unsigned char file_class = get_class(); + + if ( file_class == ELFCLASS64 ) { + new_section = new section_impl( &convertor ); + } + else if ( file_class == ELFCLASS32 ) { + new_section = new section_impl( &convertor ); + } + else { + return 0; + } + + new_section->set_index( (Elf_Half)sections_.size() ); + sections_.push_back( new_section ); + + return new_section; + } + + //------------------------------------------------------------------------------ + segment* create_segment() + { + segment* new_segment; + unsigned char file_class = header->get_class(); + + if ( file_class == ELFCLASS64 ) { + new_segment = new segment_impl( &convertor ); + } + else if ( file_class == ELFCLASS32 ) { + new_segment = new segment_impl( &convertor ); + } + else { + return 0; + } + + new_segment->set_index( (Elf_Half)segments_.size() ); + segments_.push_back( new_segment ); + + return new_segment; + } + + //------------------------------------------------------------------------------ + void create_mandatory_sections() + { + // Create null section without calling to 'add_section' as no string + // section containing section names exists yet + section* sec0 = create_section(); + sec0->set_index( 0 ); + sec0->set_name( "" ); + sec0->set_name_string_offset( 0 ); + + set_section_name_str_index( 1 ); + section* shstrtab = sections.add( ".shstrtab" ); + shstrtab->set_type( SHT_STRTAB ); + shstrtab->set_addr_align( 1 ); + } + + //------------------------------------------------------------------------------ + Elf_Half load_sections( std::istream& stream ) + { + Elf_Half entry_size = header->get_section_entry_size(); + Elf_Half num = header->get_sections_num(); + Elf64_Off offset = header->get_sections_offset(); + + for ( Elf_Half i = 0; i < num; ++i ) { + section* sec = create_section(); + sec->load( stream, (std::streamoff)offset + + (std::streampos)i * entry_size ); + sec->set_index( i ); + // To mark that the section is not permitted to reassign address + // during layout calculation + sec->set_address( sec->get_address() ); + } + + Elf_Half shstrndx = get_section_name_str_index(); + + if ( SHN_UNDEF != shstrndx ) { + string_section_accessor str_reader( sections[shstrndx] ); + for ( Elf_Half i = 0; i < num; ++i ) { + Elf_Word section_offset = sections[i]->get_name_string_offset(); + const char* p = str_reader.get_string( section_offset ); + if ( p != 0 ) { + sections[i]->set_name( p ); + } + } + } + + return num; + } + + //------------------------------------------------------------------------------ + //! Checks whether the addresses of the section entirely fall within the given segment. + //! It doesn't matter if the addresses are memory addresses, or file offsets, + //! they just need to be in the same address space + bool is_sect_in_seg( Elf64_Off sect_begin, + Elf_Xword sect_size, + Elf64_Off seg_begin, + Elf64_Off seg_end ) + { + return ( seg_begin <= sect_begin ) && + ( sect_begin + sect_size <= seg_end ) && + ( sect_begin < + seg_end ); // this is important criteria when sect_size == 0 + // Example: seg_begin=10, seg_end=12 (-> covering the bytes 10 and 11) + // sect_begin=12, sect_size=0 -> shall return false! + } + + //------------------------------------------------------------------------------ + bool load_segments( std::istream& stream ) + { + Elf_Half entry_size = header->get_segment_entry_size(); + Elf_Half num = header->get_segments_num(); + Elf64_Off offset = header->get_segments_offset(); + + for ( Elf_Half i = 0; i < num; ++i ) { + segment* seg; + unsigned char file_class = header->get_class(); + + if ( file_class == ELFCLASS64 ) { + seg = new segment_impl( &convertor ); + } + else if ( file_class == ELFCLASS32 ) { + seg = new segment_impl( &convertor ); + } + else { + return false; + } + + seg->load( stream, (std::streamoff)offset + + (std::streampos)i * entry_size ); + seg->set_index( i ); + + // Add sections to the segments (similar to readelfs algorithm) + Elf64_Off segBaseOffset = seg->get_offset(); + Elf64_Off segEndOffset = segBaseOffset + seg->get_file_size(); + Elf64_Off segVBaseAddr = seg->get_virtual_address(); + Elf64_Off segVEndAddr = segVBaseAddr + seg->get_memory_size(); + for ( Elf_Half j = 0; j < sections.size(); ++j ) { + const section* psec = sections[j]; + + // SHF_ALLOC sections are matched based on the virtual address + // otherwise the file offset is matched + if ( ( psec->get_flags() & SHF_ALLOC ) + ? is_sect_in_seg( psec->get_address(), + psec->get_size(), segVBaseAddr, + segVEndAddr ) + : is_sect_in_seg( psec->get_offset(), psec->get_size(), + segBaseOffset, segEndOffset ) ) { + // Alignment of segment shall not be updated, to preserve original value + // It will be re-calculated on saving. + seg->add_section_index( psec->get_index(), 0 ); + } + } + + // Add section into the segments' container + segments_.push_back( seg ); + } + + return true; + } + + //------------------------------------------------------------------------------ + bool save_header( std::ostream& stream ) { return header->save( stream ); } + + //------------------------------------------------------------------------------ + bool save_sections( std::ostream& stream ) + { + for ( unsigned int i = 0; i < sections_.size(); ++i ) { + section* sec = sections_.at( i ); + + std::streampos headerPosition = + (std::streamoff)header->get_sections_offset() + + (std::streampos)header->get_section_entry_size() * + sec->get_index(); + + sec->save( stream, headerPosition, sec->get_offset() ); + } + return true; + } + + //------------------------------------------------------------------------------ + bool save_segments( std::ostream& stream ) + { + for ( unsigned int i = 0; i < segments_.size(); ++i ) { + segment* seg = segments_.at( i ); + + std::streampos headerPosition = + header->get_segments_offset() + + (std::streampos)header->get_segment_entry_size() * + seg->get_index(); + + seg->save( stream, headerPosition, seg->get_offset() ); + } + return true; + } + + //------------------------------------------------------------------------------ + bool is_section_without_segment( unsigned int section_index ) + { + bool found = false; + + for ( unsigned int j = 0; !found && ( j < segments.size() ); ++j ) { + for ( unsigned int k = 0; + !found && ( k < segments[j]->get_sections_num() ); ++k ) { + found = segments[j]->get_section_index_at( k ) == section_index; + } + } + + return !found; + } + + //------------------------------------------------------------------------------ + bool is_subsequence_of( segment* seg1, segment* seg2 ) + { + // Return 'true' if sections of seg1 are a subset of sections in seg2 + const std::vector& sections1 = seg1->get_sections(); + const std::vector& sections2 = seg2->get_sections(); + + bool found = false; + if ( sections1.size() < sections2.size() ) { + found = std::includes( sections2.begin(), sections2.end(), + sections1.begin(), sections1.end() ); + } + + return found; + } + + //------------------------------------------------------------------------------ + std::vector get_ordered_segments() + { + std::vector res; + std::deque worklist; + + res.reserve( segments.size() ); + std::copy( segments_.begin(), segments_.end(), + std::back_inserter( worklist ) ); + + // Bring the segments which start at address 0 to the front + size_t nextSlot = 0; + for ( size_t i = 0; i < worklist.size(); ++i ) { + if ( i != nextSlot && worklist[i]->is_offset_initialized() && + worklist[i]->get_offset() == 0 ) { + if ( worklist[nextSlot]->get_offset() == 0 ) { + ++nextSlot; + } + std::swap( worklist[i], worklist[nextSlot] ); + ++nextSlot; + } + } + + while ( !worklist.empty() ) { + segment* seg = worklist.front(); + worklist.pop_front(); + + size_t i = 0; + for ( ; i < worklist.size(); ++i ) { + if ( is_subsequence_of( seg, worklist[i] ) ) { + break; + } + } + + if ( i < worklist.size() ) + worklist.push_back( seg ); + else + res.push_back( seg ); + } + + return res; + } + + //------------------------------------------------------------------------------ + bool layout_sections_without_segments() + { + for ( unsigned int i = 0; i < sections_.size(); ++i ) { + if ( is_section_without_segment( i ) ) { + section* sec = sections_[i]; + + Elf_Xword section_align = sec->get_addr_align(); + if ( section_align > 1 && + current_file_pos % section_align != 0 ) { + current_file_pos += + section_align - current_file_pos % section_align; + } + + if ( 0 != sec->get_index() ) + sec->set_offset( current_file_pos ); + + if ( SHT_NOBITS != sec->get_type() && + SHT_NULL != sec->get_type() ) { + current_file_pos += sec->get_size(); + } + } + } + + return true; + } + + //------------------------------------------------------------------------------ + void calc_segment_alignment() + { + for ( std::vector::iterator s = segments_.begin(); + s != segments_.end(); ++s ) { + segment* seg = *s; + for ( int i = 0; i < seg->get_sections_num(); ++i ) { + section* sect = sections_[seg->get_section_index_at( i )]; + if ( sect->get_addr_align() > seg->get_align() ) { + seg->set_align( sect->get_addr_align() ); + } + } + } + } + + //------------------------------------------------------------------------------ + bool layout_segments_and_their_sections() + { + std::vector worklist; + std::vector section_generated( sections.size(), false ); + + // Get segments in a order in where segments which contain a + // sub sequence of other segments are located at the end + worklist = get_ordered_segments(); + + for ( unsigned int i = 0; i < worklist.size(); ++i ) { + Elf_Xword segment_memory = 0; + Elf_Xword segment_filesize = 0; + Elf_Xword seg_start_pos = current_file_pos; + segment* seg = worklist[i]; + + // Special case: PHDR segment + // This segment contains the program headers but no sections + if ( seg->get_type() == PT_PHDR && seg->get_sections_num() == 0 ) { + seg_start_pos = header->get_segments_offset(); + segment_memory = segment_filesize = + header->get_segment_entry_size() * + (Elf_Xword)header->get_segments_num(); + } + // Special case: + else if ( seg->is_offset_initialized() && seg->get_offset() == 0 ) { + seg_start_pos = 0; + if ( seg->get_sections_num() ) { + segment_memory = segment_filesize = current_file_pos; + } + } + // New segments with not generated sections + // have to be aligned + else if ( seg->get_sections_num() && + !section_generated[seg->get_section_index_at( 0 )] ) { + Elf_Xword align = seg->get_align() > 0 ? seg->get_align() : 1; + Elf64_Off cur_page_alignment = current_file_pos % align; + Elf64_Off req_page_alignment = + seg->get_virtual_address() % align; + Elf64_Off error = req_page_alignment - cur_page_alignment; + + current_file_pos += ( seg->get_align() + error ) % align; + seg_start_pos = current_file_pos; + } + else if ( seg->get_sections_num() ) { + seg_start_pos = + sections[seg->get_section_index_at( 0 )]->get_offset(); + } + + // Write segment's data + for ( unsigned int j = 0; j < seg->get_sections_num(); ++j ) { + Elf_Half index = seg->get_section_index_at( j ); + + section* sec = sections[index]; + + // The NULL section is always generated + if ( SHT_NULL == sec->get_type() ) { + section_generated[index] = true; + continue; + } + + Elf_Xword secAlign = 0; + // Fix up the alignment + if ( !section_generated[index] && + sec->is_address_initialized() && + SHT_NOBITS != sec->get_type() && + SHT_NULL != sec->get_type() && 0 != sec->get_size() ) { + // Align the sections based on the virtual addresses + // when possible (this is what matters for execution) + Elf64_Off req_offset = + sec->get_address() - seg->get_virtual_address(); + Elf64_Off cur_offset = current_file_pos - seg_start_pos; + if ( req_offset < cur_offset ) { + // something has gone awfully wrong, abort! + // secAlign would turn out negative, seeking backwards and overwriting previous data + return false; + } + secAlign = req_offset - cur_offset; + } + else if ( !section_generated[index] && + !sec->is_address_initialized() ) { + // If no address has been specified then only the section + // alignment constraint has to be matched + Elf_Xword align = sec->get_addr_align(); + if ( align == 0 ) { + align = 1; + } + Elf64_Off error = current_file_pos % align; + secAlign = ( align - error ) % align; + } + else if ( section_generated[index] ) { + // Alignment for already generated sections + secAlign = + sec->get_offset() - seg_start_pos - segment_filesize; + } + + // Determine the segment file and memory sizes + // Special case .tbss section (NOBITS) in non TLS segment + if ( ( sec->get_flags() & SHF_ALLOC ) && + !( ( sec->get_flags() & SHF_TLS ) && + ( seg->get_type() != PT_TLS ) && + ( SHT_NOBITS == sec->get_type() ) ) ) + segment_memory += sec->get_size() + secAlign; + + if ( SHT_NOBITS != sec->get_type() ) + segment_filesize += sec->get_size() + secAlign; + + // Nothing to be done when generating nested segments + if ( section_generated[index] ) { + continue; + } + + current_file_pos += secAlign; + + // Set the section addresses when missing + if ( !sec->is_address_initialized() ) + sec->set_address( seg->get_virtual_address() + + current_file_pos - seg_start_pos ); + + if ( 0 != sec->get_index() ) + sec->set_offset( current_file_pos ); + + if ( SHT_NOBITS != sec->get_type() ) + current_file_pos += sec->get_size(); + + section_generated[index] = true; + } + + seg->set_file_size( segment_filesize ); + + // If we already have a memory size from loading an elf file (value > 0), + // it must not shrink! + // Memory size may be bigger than file size and it is the loader's job to do something + // with the surplus bytes in memory, like initializing them with a defined value. + if ( seg->get_memory_size() < segment_memory ) { + seg->set_memory_size( segment_memory ); + } + + seg->set_offset( seg_start_pos ); + } + + return true; + } + + //------------------------------------------------------------------------------ + bool layout_section_table() + { + // Simply place the section table at the end for now + Elf64_Off alignmentError = current_file_pos % 4; + current_file_pos += ( 4 - alignmentError ) % 4; + header->set_sections_offset( current_file_pos ); + return true; + } + + //------------------------------------------------------------------------------ + public: + friend class Sections; + class Sections + { + public: + //------------------------------------------------------------------------------ + Sections( elfio* parent_ ) : parent( parent_ ) {} + + //------------------------------------------------------------------------------ + Elf_Half size() const { return (Elf_Half)parent->sections_.size(); } + + //------------------------------------------------------------------------------ + section* operator[]( unsigned int index ) const + { + section* sec = 0; + + if ( index < parent->sections_.size() ) { + sec = parent->sections_[index]; + } + + return sec; + } + + //------------------------------------------------------------------------------ + section* operator[]( const std::string& name ) const + { + section* sec = 0; + + std::vector::const_iterator it; + for ( it = parent->sections_.begin(); it != parent->sections_.end(); + ++it ) { + if ( ( *it )->get_name() == name ) { + sec = *it; + break; + } + } + + return sec; + } + + //------------------------------------------------------------------------------ + section* add( const std::string& name ) + { + section* new_section = parent->create_section(); + new_section->set_name( name ); + + Elf_Half str_index = parent->get_section_name_str_index(); + section* string_table( parent->sections_[str_index] ); + string_section_accessor str_writer( string_table ); + Elf_Word pos = str_writer.add_string( name ); + new_section->set_name_string_offset( pos ); + + return new_section; + } + + //------------------------------------------------------------------------------ + std::vector::iterator begin() + { + return parent->sections_.begin(); + } + + //------------------------------------------------------------------------------ + std::vector::iterator end() + { + return parent->sections_.end(); + } + + //------------------------------------------------------------------------------ + std::vector::const_iterator begin() const + { + return parent->sections_.cbegin(); + } + + //------------------------------------------------------------------------------ + std::vector::const_iterator end() const + { + return parent->sections_.cend(); + } + + //------------------------------------------------------------------------------ + private: + elfio* parent; + } sections; + + //------------------------------------------------------------------------------ + public: + friend class Segments; + class Segments + { + public: + //------------------------------------------------------------------------------ + Segments( elfio* parent_ ) : parent( parent_ ) {} + + //------------------------------------------------------------------------------ + Elf_Half size() const { return (Elf_Half)parent->segments_.size(); } + + //------------------------------------------------------------------------------ + segment* operator[]( unsigned int index ) const + { + return parent->segments_[index]; + } + + //------------------------------------------------------------------------------ + segment* add() { return parent->create_segment(); } + + //------------------------------------------------------------------------------ + std::vector::iterator begin() + { + return parent->segments_.begin(); + } + + //------------------------------------------------------------------------------ + std::vector::iterator end() + { + return parent->segments_.end(); + } + + //------------------------------------------------------------------------------ + std::vector::const_iterator begin() const + { + return parent->segments_.cbegin(); + } + + //------------------------------------------------------------------------------ + std::vector::const_iterator end() const + { + return parent->segments_.cend(); + } + + //------------------------------------------------------------------------------ + private: + elfio* parent; + } segments; + + //------------------------------------------------------------------------------ + private: + elf_header* header; + std::vector sections_; + std::vector segments_; + endianess_convertor convertor; + + Elf_Xword current_file_pos; +}; + +} // namespace ELFIO + + +/*** Start of inlined file: elfio_symbols.hpp ***/ +#ifndef ELFIO_SYMBOLS_HPP +#define ELFIO_SYMBOLS_HPP + +namespace ELFIO { + +//------------------------------------------------------------------------------ +template class symbol_section_accessor_template +{ + public: + //------------------------------------------------------------------------------ + symbol_section_accessor_template( const elfio& elf_file_, + S* symbol_section_ ) + : elf_file( elf_file_ ), symbol_section( symbol_section_ ) + { + find_hash_section(); + } + + //------------------------------------------------------------------------------ + Elf_Xword get_symbols_num() const + { + Elf_Xword nRet = 0; + if ( 0 != symbol_section->get_entry_size() ) { + nRet = + symbol_section->get_size() / symbol_section->get_entry_size(); + } + + return nRet; + } + + //------------------------------------------------------------------------------ + bool get_symbol( Elf_Xword index, + std::string& name, + Elf64_Addr& value, + Elf_Xword& size, + unsigned char& bind, + unsigned char& type, + Elf_Half& section_index, + unsigned char& other ) const + { + bool ret = false; + + if ( elf_file.get_class() == ELFCLASS32 ) { + ret = generic_get_symbol( index, name, value, size, bind, + type, section_index, other ); + } + else { + ret = generic_get_symbol( index, name, value, size, bind, + type, section_index, other ); + } + + return ret; + } + + //------------------------------------------------------------------------------ + bool get_symbol( const std::string& name, + Elf64_Addr& value, + Elf_Xword& size, + unsigned char& bind, + unsigned char& type, + Elf_Half& section_index, + unsigned char& other ) const + { + bool ret = false; + + if ( 0 != get_hash_table_index() ) { + Elf_Word nbucket = *(const Elf_Word*)hash_section->get_data(); + Elf_Word nchain = *(const Elf_Word*)( hash_section->get_data() + + sizeof( Elf_Word ) ); + Elf_Word val = elf_hash( (const unsigned char*)name.c_str() ); + Elf_Word y = *(const Elf_Word*)( hash_section->get_data() + + ( 2 + val % nbucket ) * + sizeof( Elf_Word ) ); + std::string str; + get_symbol( y, str, value, size, bind, type, section_index, other ); + while ( str != name && STN_UNDEF != y && y < nchain ) { + y = *(const Elf_Word*)( hash_section->get_data() + + ( 2 + nbucket + y ) * + sizeof( Elf_Word ) ); + get_symbol( y, str, value, size, bind, type, section_index, + other ); + } + if ( str == name ) { + ret = true; + } + } + else { + for ( Elf_Xword i = 0; i < get_symbols_num() && !ret; i++ ) { + std::string symbol_name; + if ( get_symbol( i, symbol_name, value, size, bind, type, + section_index, other ) ) { + if ( symbol_name == name ) { + ret = true; + } + } + } + } + + return ret; + } + + //------------------------------------------------------------------------------ + bool get_symbol( const Elf64_Addr& value, + std::string& name, + Elf_Xword& size, + unsigned char& bind, + unsigned char& type, + Elf_Half& section_index, + unsigned char& other ) const + { + + const endianess_convertor& convertor = elf_file.get_convertor(); + + Elf_Xword idx = 0; + bool match = false; + Elf64_Addr v = 0; + + if ( elf_file.get_class() == ELFCLASS32 ) { + match = generic_search_symbols( + [&]( const Elf32_Sym* sym ) { + return convertor( sym->st_value ) == value; + }, + idx ); + } + else { + match = generic_search_symbols( + [&]( const Elf64_Sym* sym ) { + return convertor( sym->st_value ) == value; + }, + idx ); + } + + if ( match ) { + return get_symbol( idx, name, v, size, bind, type, section_index, + other ); + } + + return false; + } + + //------------------------------------------------------------------------------ + Elf_Word add_symbol( Elf_Word name, + Elf64_Addr value, + Elf_Xword size, + unsigned char info, + unsigned char other, + Elf_Half shndx ) + { + Elf_Word nRet; + + if ( symbol_section->get_size() == 0 ) { + if ( elf_file.get_class() == ELFCLASS32 ) { + nRet = generic_add_symbol( 0, 0, 0, 0, 0, 0 ); + } + else { + nRet = generic_add_symbol( 0, 0, 0, 0, 0, 0 ); + } + } + + if ( elf_file.get_class() == ELFCLASS32 ) { + nRet = generic_add_symbol( name, value, size, info, + other, shndx ); + } + else { + nRet = generic_add_symbol( name, value, size, info, + other, shndx ); + } + + return nRet; + } + + //------------------------------------------------------------------------------ + Elf_Word add_symbol( Elf_Word name, + Elf64_Addr value, + Elf_Xword size, + unsigned char bind, + unsigned char type, + unsigned char other, + Elf_Half shndx ) + { + return add_symbol( name, value, size, ELF_ST_INFO( bind, type ), other, + shndx ); + } + + //------------------------------------------------------------------------------ + Elf_Word add_symbol( string_section_accessor& pStrWriter, + const char* str, + Elf64_Addr value, + Elf_Xword size, + unsigned char info, + unsigned char other, + Elf_Half shndx ) + { + Elf_Word index = pStrWriter.add_string( str ); + return add_symbol( index, value, size, info, other, shndx ); + } + + //------------------------------------------------------------------------------ + Elf_Word add_symbol( string_section_accessor& pStrWriter, + const char* str, + Elf64_Addr value, + Elf_Xword size, + unsigned char bind, + unsigned char type, + unsigned char other, + Elf_Half shndx ) + { + return add_symbol( pStrWriter, str, value, size, + ELF_ST_INFO( bind, type ), other, shndx ); + } + + //------------------------------------------------------------------------------ + Elf_Xword arrange_local_symbols( + std::function func = + nullptr ) + { + int nRet = 0; + + if ( elf_file.get_class() == ELFCLASS32 ) { + nRet = generic_arrange_local_symbols( func ); + } + else { + nRet = generic_arrange_local_symbols( func ); + } + + return nRet; + } + + //------------------------------------------------------------------------------ + private: + //------------------------------------------------------------------------------ + void find_hash_section() + { + hash_section = 0; + hash_section_index = 0; + Elf_Half nSecNo = elf_file.sections.size(); + for ( Elf_Half i = 0; i < nSecNo && 0 == hash_section_index; ++i ) { + const section* sec = elf_file.sections[i]; + if ( sec->get_link() == symbol_section->get_index() ) { + hash_section = sec; + hash_section_index = i; + } + } + } + + //------------------------------------------------------------------------------ + Elf_Half get_string_table_index() const + { + return (Elf_Half)symbol_section->get_link(); + } + + //------------------------------------------------------------------------------ + Elf_Half get_hash_table_index() const { return hash_section_index; } + + //------------------------------------------------------------------------------ + template const T* generic_get_symbol_ptr( Elf_Xword index ) const + { + if ( 0 != symbol_section->get_data() && index < get_symbols_num() ) { + const T* pSym = reinterpret_cast( + symbol_section->get_data() + + index * symbol_section->get_entry_size() ); + + return pSym; + } + + return nullptr; + } + + //------------------------------------------------------------------------------ + template + bool generic_search_symbols( std::function match, + Elf_Xword& idx ) const + { + for ( Elf_Xword i = 0; i < get_symbols_num(); i++ ) { + const T* symPtr = generic_get_symbol_ptr( i ); + + if ( symPtr == nullptr ) + return false; + + if ( match( symPtr ) ) { + idx = i; + return true; + } + } + + return false; + } + + //------------------------------------------------------------------------------ + template + bool generic_get_symbol( Elf_Xword index, + std::string& name, + Elf64_Addr& value, + Elf_Xword& size, + unsigned char& bind, + unsigned char& type, + Elf_Half& section_index, + unsigned char& other ) const + { + bool ret = false; + + if ( 0 != symbol_section->get_data() && index < get_symbols_num() ) { + const T* pSym = reinterpret_cast( + symbol_section->get_data() + + index * symbol_section->get_entry_size() ); + + const endianess_convertor& convertor = elf_file.get_convertor(); + + section* string_section = + elf_file.sections[get_string_table_index()]; + string_section_accessor str_reader( string_section ); + const char* pStr = + str_reader.get_string( convertor( pSym->st_name ) ); + if ( 0 != pStr ) { + name = pStr; + } + value = convertor( pSym->st_value ); + size = convertor( pSym->st_size ); + bind = ELF_ST_BIND( pSym->st_info ); + type = ELF_ST_TYPE( pSym->st_info ); + section_index = convertor( pSym->st_shndx ); + other = pSym->st_other; + + ret = true; + } + + return ret; + } + + //------------------------------------------------------------------------------ + template + Elf_Word generic_add_symbol( Elf_Word name, + Elf64_Addr value, + Elf_Xword size, + unsigned char info, + unsigned char other, + Elf_Half shndx ) + { + const endianess_convertor& convertor = elf_file.get_convertor(); + + T entry; + entry.st_name = convertor( name ); + entry.st_value = value; + entry.st_value = convertor( entry.st_value ); + entry.st_size = size; + entry.st_size = convertor( entry.st_size ); + entry.st_info = convertor( info ); + entry.st_other = convertor( other ); + entry.st_shndx = convertor( shndx ); + + symbol_section->append_data( reinterpret_cast( &entry ), + sizeof( entry ) ); + + Elf_Word nRet = symbol_section->get_size() / sizeof( entry ) - 1; + + return nRet; + } + + //------------------------------------------------------------------------------ + template + Elf_Xword generic_arrange_local_symbols( + std::function func ) + { + const endianess_convertor& convertor = elf_file.get_convertor(); + const Elf_Xword size = symbol_section->get_entry_size(); + + Elf_Xword first_not_local = + 1; // Skip the first entry. It is always NOTYPE + Elf_Xword current = 0; + Elf_Xword count = get_symbols_num(); + + while ( true ) { + T* p1 = nullptr; + T* p2 = nullptr; + + while ( first_not_local < count ) { + p1 = const_cast( + generic_get_symbol_ptr( first_not_local ) ); + if ( ELF_ST_BIND( convertor( p1->st_info ) ) != STB_LOCAL ) + break; + ++first_not_local; + } + + current = first_not_local + 1; + while ( current < count ) { + p2 = const_cast( generic_get_symbol_ptr( current ) ); + if ( ELF_ST_BIND( convertor( p2->st_info ) ) == STB_LOCAL ) + break; + ++current; + } + + if ( first_not_local < count && current < count ) { + if ( func ) + func( first_not_local, current ); + + // Swap the symbols + T tmp; + std::copy( p1, p1 + 1, &tmp ); + std::copy( p2, p2 + 1, p1 ); + std::copy( &tmp, &tmp + 1, p2 ); + } + else { + // Update 'info' field of the section + symbol_section->set_info( first_not_local ); + break; + } + } + + // Elf_Word nRet = symbol_section->get_size() / sizeof(entry) - 1; + + return first_not_local; + } + + //------------------------------------------------------------------------------ + private: + const elfio& elf_file; + S* symbol_section; + Elf_Half hash_section_index; + const section* hash_section; +}; + +using symbol_section_accessor = symbol_section_accessor_template
; +using const_symbol_section_accessor = + symbol_section_accessor_template; + +} // namespace ELFIO + +#endif // ELFIO_SYMBOLS_HPP + +/*** End of inlined file: elfio_symbols.hpp ***/ + + +/*** Start of inlined file: elfio_note.hpp ***/ +#ifndef ELFIO_NOTE_HPP +#define ELFIO_NOTE_HPP + +namespace ELFIO { + +//------------------------------------------------------------------------------ +// There are discrepancies in documentations. SCO documentation +// (http://www.sco.com/developers/gabi/latest/ch5.pheader.html#note_section) +// requires 8 byte entries alignment for 64-bit ELF file, +// but Oracle's definition uses the same structure +// for 32-bit and 64-bit formats. +// (https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter6-18048.html) +// +// It looks like EM_X86_64 Linux implementation is similar to Oracle's +// definition. Therefore, the same alignment works for both formats +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +template class note_section_accessor_template +{ + public: + //------------------------------------------------------------------------------ + note_section_accessor_template( const elfio& elf_file_, S* section_ ) + : elf_file( elf_file_ ), note_section( section_ ) + { + process_section(); + } + + //------------------------------------------------------------------------------ + Elf_Word get_notes_num() const + { + return (Elf_Word)note_start_positions.size(); + } + + //------------------------------------------------------------------------------ + bool get_note( Elf_Word index, + Elf_Word& type, + std::string& name, + void*& desc, + Elf_Word& descSize ) const + { + if ( index >= note_section->get_size() ) { + return false; + } + + const char* pData = + note_section->get_data() + note_start_positions[index]; + int align = sizeof( Elf_Word ); + + const endianess_convertor& convertor = elf_file.get_convertor(); + type = convertor( *(const Elf_Word*)( pData + 2 * align ) ); + Elf_Word namesz = convertor( *(const Elf_Word*)( pData ) ); + descSize = convertor( *(const Elf_Word*)( pData + sizeof( namesz ) ) ); + + Elf_Xword max_name_size = + note_section->get_size() - note_start_positions[index]; + if ( namesz < 1 || namesz > max_name_size || + (Elf_Xword)namesz + descSize > max_name_size ) { + return false; + } + name.assign( pData + 3 * align, namesz - 1 ); + if ( 0 == descSize ) { + desc = 0; + } + else { + desc = + const_cast( pData + 3 * align + + ( ( namesz + align - 1 ) / align ) * align ); + } + + return true; + } + + //------------------------------------------------------------------------------ + void add_note( Elf_Word type, + const std::string& name, + const void* desc, + Elf_Word descSize ) + { + const endianess_convertor& convertor = elf_file.get_convertor(); + + int align = sizeof( Elf_Word ); + Elf_Word nameLen = (Elf_Word)name.size() + 1; + Elf_Word nameLenConv = convertor( nameLen ); + std::string buffer( reinterpret_cast( &nameLenConv ), align ); + Elf_Word descSizeConv = convertor( descSize ); + + buffer.append( reinterpret_cast( &descSizeConv ), align ); + type = convertor( type ); + buffer.append( reinterpret_cast( &type ), align ); + buffer.append( name ); + buffer.append( 1, '\x00' ); + const char pad[] = { '\0', '\0', '\0', '\0' }; + if ( nameLen % align != 0 ) { + buffer.append( pad, align - nameLen % align ); + } + if ( desc != 0 && descSize != 0 ) { + buffer.append( reinterpret_cast( desc ), descSize ); + if ( descSize % align != 0 ) { + buffer.append( pad, align - descSize % align ); + } + } + + note_start_positions.push_back( note_section->get_size() ); + note_section->append_data( buffer ); + } + + private: + //------------------------------------------------------------------------------ + void process_section() + { + const endianess_convertor& convertor = elf_file.get_convertor(); + const char* data = note_section->get_data(); + Elf_Xword size = note_section->get_size(); + Elf_Xword current = 0; + + note_start_positions.clear(); + + // Is it empty? + if ( 0 == data || 0 == size ) { + return; + } + + Elf_Word align = sizeof( Elf_Word ); + while ( current + (Elf_Xword)3 * align <= size ) { + note_start_positions.push_back( current ); + Elf_Word namesz = convertor( *(const Elf_Word*)( data + current ) ); + Elf_Word descsz = convertor( + *(const Elf_Word*)( data + current + sizeof( namesz ) ) ); + + current += (Elf_Xword)3 * sizeof( Elf_Word ) + + ( ( namesz + align - 1 ) / align ) * (Elf_Xword)align + + ( ( descsz + align - 1 ) / align ) * (Elf_Xword)align; + } + } + + //------------------------------------------------------------------------------ + private: + const elfio& elf_file; + S* note_section; + std::vector note_start_positions; +}; + +using note_section_accessor = note_section_accessor_template
; +using const_note_section_accessor = + note_section_accessor_template; + +} // namespace ELFIO + +#endif // ELFIO_NOTE_HPP + +/*** End of inlined file: elfio_note.hpp ***/ + + +/*** Start of inlined file: elfio_relocation.hpp ***/ +#ifndef ELFIO_RELOCATION_HPP +#define ELFIO_RELOCATION_HPP + +namespace ELFIO { + +template struct get_sym_and_type; +template <> struct get_sym_and_type +{ + static int get_r_sym( Elf_Xword info ) + { + return ELF32_R_SYM( (Elf_Word)info ); + } + static int get_r_type( Elf_Xword info ) + { + return ELF32_R_TYPE( (Elf_Word)info ); + } +}; +template <> struct get_sym_and_type +{ + static int get_r_sym( Elf_Xword info ) + { + return ELF32_R_SYM( (Elf_Word)info ); + } + static int get_r_type( Elf_Xword info ) + { + return ELF32_R_TYPE( (Elf_Word)info ); + } +}; +template <> struct get_sym_and_type +{ + static int get_r_sym( Elf_Xword info ) { return ELF64_R_SYM( info ); } + static int get_r_type( Elf_Xword info ) { return ELF64_R_TYPE( info ); } +}; +template <> struct get_sym_and_type +{ + static int get_r_sym( Elf_Xword info ) { return ELF64_R_SYM( info ); } + static int get_r_type( Elf_Xword info ) { return ELF64_R_TYPE( info ); } +}; + +//------------------------------------------------------------------------------ +template class relocation_section_accessor_template +{ + public: + //------------------------------------------------------------------------------ + relocation_section_accessor_template( const elfio& elf_file_, S* section_ ) + : elf_file( elf_file_ ), relocation_section( section_ ) + { + } + + //------------------------------------------------------------------------------ + Elf_Xword get_entries_num() const + { + Elf_Xword nRet = 0; + + if ( 0 != relocation_section->get_entry_size() ) { + nRet = relocation_section->get_size() / + relocation_section->get_entry_size(); + } + + return nRet; + } + + //------------------------------------------------------------------------------ + bool get_entry( Elf_Xword index, + Elf64_Addr& offset, + Elf_Word& symbol, + Elf_Word& type, + Elf_Sxword& addend ) const + { + if ( index >= get_entries_num() ) { // Is index valid + return false; + } + + if ( elf_file.get_class() == ELFCLASS32 ) { + if ( SHT_REL == relocation_section->get_type() ) { + generic_get_entry_rel( index, offset, symbol, type, + addend ); + } + else if ( SHT_RELA == relocation_section->get_type() ) { + generic_get_entry_rela( index, offset, symbol, type, + addend ); + } + } + else { + if ( SHT_REL == relocation_section->get_type() ) { + generic_get_entry_rel( index, offset, symbol, type, + addend ); + } + else if ( SHT_RELA == relocation_section->get_type() ) { + generic_get_entry_rela( index, offset, symbol, type, + addend ); + } + } + + return true; + } + + //------------------------------------------------------------------------------ + bool get_entry( Elf_Xword index, + Elf64_Addr& offset, + Elf64_Addr& symbolValue, + std::string& symbolName, + Elf_Word& type, + Elf_Sxword& addend, + Elf_Sxword& calcValue ) const + { + // Do regular job + Elf_Word symbol; + bool ret = get_entry( index, offset, symbol, type, addend ); + + // Find the symbol + Elf_Xword size; + unsigned char bind; + unsigned char symbolType; + Elf_Half section; + unsigned char other; + + symbol_section_accessor symbols( + elf_file, elf_file.sections[get_symbol_table_index()] ); + ret = ret && symbols.get_symbol( symbol, symbolName, symbolValue, size, + bind, symbolType, section, other ); + + if ( ret ) { // Was it successful? + switch ( type ) { + case R_386_NONE: // none + calcValue = 0; + break; + case R_386_32: // S + A + calcValue = symbolValue + addend; + break; + case R_386_PC32: // S + A - P + calcValue = symbolValue + addend - offset; + break; + case R_386_GOT32: // G + A - P + calcValue = 0; + break; + case R_386_PLT32: // L + A - P + calcValue = 0; + break; + case R_386_COPY: // none + calcValue = 0; + break; + case R_386_GLOB_DAT: // S + case R_386_JMP_SLOT: // S + calcValue = symbolValue; + break; + case R_386_RELATIVE: // B + A + calcValue = addend; + break; + case R_386_GOTOFF: // S + A - GOT + calcValue = 0; + break; + case R_386_GOTPC: // GOT + A - P + calcValue = 0; + break; + default: // Not recognized symbol! + calcValue = 0; + break; + } + } + + return ret; + } + + //------------------------------------------------------------------------------ + bool set_entry( Elf_Xword index, + Elf64_Addr offset, + Elf_Word symbol, + Elf_Word type, + Elf_Sxword addend ) + { + if ( index >= get_entries_num() ) { // Is index valid + return false; + } + + if ( elf_file.get_class() == ELFCLASS32 ) { + if ( SHT_REL == relocation_section->get_type() ) { + generic_set_entry_rel( index, offset, symbol, type, + addend ); + } + else if ( SHT_RELA == relocation_section->get_type() ) { + generic_set_entry_rela( index, offset, symbol, type, + addend ); + } + } + else { + if ( SHT_REL == relocation_section->get_type() ) { + generic_set_entry_rel( index, offset, symbol, type, + addend ); + } + else if ( SHT_RELA == relocation_section->get_type() ) { + generic_set_entry_rela( index, offset, symbol, type, + addend ); + } + } + + return true; + } + + //------------------------------------------------------------------------------ + void add_entry( Elf64_Addr offset, Elf_Xword info ) + { + if ( elf_file.get_class() == ELFCLASS32 ) { + generic_add_entry( offset, info ); + } + else { + generic_add_entry( offset, info ); + } + } + + //------------------------------------------------------------------------------ + void add_entry( Elf64_Addr offset, Elf_Word symbol, unsigned char type ) + { + Elf_Xword info; + if ( elf_file.get_class() == ELFCLASS32 ) { + info = ELF32_R_INFO( (Elf_Xword)symbol, type ); + } + else { + info = ELF64_R_INFO( (Elf_Xword)symbol, type ); + } + + add_entry( offset, info ); + } + + //------------------------------------------------------------------------------ + void add_entry( Elf64_Addr offset, Elf_Xword info, Elf_Sxword addend ) + { + if ( elf_file.get_class() == ELFCLASS32 ) { + generic_add_entry( offset, info, addend ); + } + else { + generic_add_entry( offset, info, addend ); + } + } + + //------------------------------------------------------------------------------ + void add_entry( Elf64_Addr offset, + Elf_Word symbol, + unsigned char type, + Elf_Sxword addend ) + { + Elf_Xword info; + if ( elf_file.get_class() == ELFCLASS32 ) { + info = ELF32_R_INFO( (Elf_Xword)symbol, type ); + } + else { + info = ELF64_R_INFO( (Elf_Xword)symbol, type ); + } + + add_entry( offset, info, addend ); + } + + //------------------------------------------------------------------------------ + void add_entry( string_section_accessor str_writer, + const char* str, + symbol_section_accessor sym_writer, + Elf64_Addr value, + Elf_Word size, + unsigned char sym_info, + unsigned char other, + Elf_Half shndx, + Elf64_Addr offset, + unsigned char type ) + { + Elf_Word str_index = str_writer.add_string( str ); + Elf_Word sym_index = sym_writer.add_symbol( str_index, value, size, + sym_info, other, shndx ); + add_entry( offset, sym_index, type ); + } + + //------------------------------------------------------------------------------ + void swap_symbols( Elf_Xword first, Elf_Xword second ) + { + Elf64_Addr offset; + Elf_Word symbol; + Elf_Word rtype; + Elf_Sxword addend; + for ( Elf_Word i = 0; i < get_entries_num(); i++ ) { + get_entry( i, offset, symbol, rtype, addend ); + if ( symbol == first ) { + set_entry( i, offset, (Elf_Word)second, rtype, addend ); + } + if ( symbol == second ) { + set_entry( i, offset, (Elf_Word)first, rtype, addend ); + } + } + } + + //------------------------------------------------------------------------------ + private: + //------------------------------------------------------------------------------ + Elf_Half get_symbol_table_index() const + { + return (Elf_Half)relocation_section->get_link(); + } + + //------------------------------------------------------------------------------ + template + void generic_get_entry_rel( Elf_Xword index, + Elf64_Addr& offset, + Elf_Word& symbol, + Elf_Word& type, + Elf_Sxword& addend ) const + { + const endianess_convertor& convertor = elf_file.get_convertor(); + + const T* pEntry = reinterpret_cast( + relocation_section->get_data() + + index * relocation_section->get_entry_size() ); + offset = convertor( pEntry->r_offset ); + Elf_Xword tmp = convertor( pEntry->r_info ); + symbol = get_sym_and_type::get_r_sym( tmp ); + type = get_sym_and_type::get_r_type( tmp ); + addend = 0; + } + + //------------------------------------------------------------------------------ + template + void generic_get_entry_rela( Elf_Xword index, + Elf64_Addr& offset, + Elf_Word& symbol, + Elf_Word& type, + Elf_Sxword& addend ) const + { + const endianess_convertor& convertor = elf_file.get_convertor(); + + const T* pEntry = reinterpret_cast( + relocation_section->get_data() + + index * relocation_section->get_entry_size() ); + offset = convertor( pEntry->r_offset ); + Elf_Xword tmp = convertor( pEntry->r_info ); + symbol = get_sym_and_type::get_r_sym( tmp ); + type = get_sym_and_type::get_r_type( tmp ); + addend = convertor( pEntry->r_addend ); + } + + //------------------------------------------------------------------------------ + template + void generic_set_entry_rel( Elf_Xword index, + Elf64_Addr offset, + Elf_Word symbol, + Elf_Word type, + Elf_Sxword ) + { + const endianess_convertor& convertor = elf_file.get_convertor(); + + T* pEntry = const_cast( reinterpret_cast( + relocation_section->get_data() + + index * relocation_section->get_entry_size() ) ); + + if ( elf_file.get_class() == ELFCLASS32 ) { + pEntry->r_info = ELF32_R_INFO( (Elf_Xword)symbol, type ); + } + else { + pEntry->r_info = ELF64_R_INFO( (Elf_Xword)symbol, type ); + } + pEntry->r_offset = offset; + pEntry->r_offset = convertor( pEntry->r_offset ); + pEntry->r_info = convertor( pEntry->r_info ); + } + + //------------------------------------------------------------------------------ + template + void generic_set_entry_rela( Elf_Xword index, + Elf64_Addr offset, + Elf_Word symbol, + Elf_Word type, + Elf_Sxword addend ) + { + const endianess_convertor& convertor = elf_file.get_convertor(); + + T* pEntry = const_cast( reinterpret_cast( + relocation_section->get_data() + + index * relocation_section->get_entry_size() ) ); + + if ( elf_file.get_class() == ELFCLASS32 ) { + pEntry->r_info = ELF32_R_INFO( (Elf_Xword)symbol, type ); + } + else { + pEntry->r_info = ELF64_R_INFO( (Elf_Xword)symbol, type ); + } + pEntry->r_offset = offset; + pEntry->r_addend = addend; + pEntry->r_offset = convertor( pEntry->r_offset ); + pEntry->r_info = convertor( pEntry->r_info ); + pEntry->r_addend = convertor( pEntry->r_addend ); + } + + //------------------------------------------------------------------------------ + template + void generic_add_entry( Elf64_Addr offset, Elf_Xword info ) + { + const endianess_convertor& convertor = elf_file.get_convertor(); + + T entry; + entry.r_offset = offset; + entry.r_info = info; + entry.r_offset = convertor( entry.r_offset ); + entry.r_info = convertor( entry.r_info ); + + relocation_section->append_data( reinterpret_cast( &entry ), + sizeof( entry ) ); + } + + //------------------------------------------------------------------------------ + template + void + generic_add_entry( Elf64_Addr offset, Elf_Xword info, Elf_Sxword addend ) + { + const endianess_convertor& convertor = elf_file.get_convertor(); + + T entry; + entry.r_offset = offset; + entry.r_info = info; + entry.r_addend = addend; + entry.r_offset = convertor( entry.r_offset ); + entry.r_info = convertor( entry.r_info ); + entry.r_addend = convertor( entry.r_addend ); + + relocation_section->append_data( reinterpret_cast( &entry ), + sizeof( entry ) ); + } + + //------------------------------------------------------------------------------ + private: + const elfio& elf_file; + S* relocation_section; +}; + +using relocation_section_accessor = + relocation_section_accessor_template
; +using const_relocation_section_accessor = + relocation_section_accessor_template; + +} // namespace ELFIO + +#endif // ELFIO_RELOCATION_HPP + +/*** End of inlined file: elfio_relocation.hpp ***/ + + +/*** Start of inlined file: elfio_dynamic.hpp ***/ +#ifndef ELFIO_DYNAMIC_HPP +#define ELFIO_DYNAMIC_HPP + +namespace ELFIO { + +//------------------------------------------------------------------------------ +template class dynamic_section_accessor_template +{ + public: + //------------------------------------------------------------------------------ + dynamic_section_accessor_template( const elfio& elf_file_, S* section_ ) + : elf_file( elf_file_ ), dynamic_section( section_ ) + { + } + + //------------------------------------------------------------------------------ + Elf_Xword get_entries_num() const + { + Elf_Xword nRet = 0; + + if ( 0 != dynamic_section->get_entry_size() ) { + nRet = + dynamic_section->get_size() / dynamic_section->get_entry_size(); + } + + return nRet; + } + + //------------------------------------------------------------------------------ + bool get_entry( Elf_Xword index, + Elf_Xword& tag, + Elf_Xword& value, + std::string& str ) const + { + if ( index >= get_entries_num() ) { // Is index valid + return false; + } + + if ( elf_file.get_class() == ELFCLASS32 ) { + generic_get_entry_dyn( index, tag, value ); + } + else { + generic_get_entry_dyn( index, tag, value ); + } + + // If the tag may have a string table reference, prepare the string + if ( tag == DT_NEEDED || tag == DT_SONAME || tag == DT_RPATH || + tag == DT_RUNPATH ) { + string_section_accessor strsec = + elf_file.sections[get_string_table_index()]; + const char* result = strsec.get_string( value ); + if ( 0 == result ) { + str.clear(); + return false; + } + str = result; + } + else { + str.clear(); + } + + return true; + } + + //------------------------------------------------------------------------------ + void add_entry( Elf_Xword tag, Elf_Xword value ) + { + if ( elf_file.get_class() == ELFCLASS32 ) { + generic_add_entry( tag, value ); + } + else { + generic_add_entry( tag, value ); + } + } + + //------------------------------------------------------------------------------ + void add_entry( Elf_Xword tag, const std::string& str ) + { + string_section_accessor strsec = + elf_file.sections[get_string_table_index()]; + Elf_Xword value = strsec.add_string( str ); + add_entry( tag, value ); + } + + //------------------------------------------------------------------------------ + private: + //------------------------------------------------------------------------------ + Elf_Half get_string_table_index() const + { + return (Elf_Half)dynamic_section->get_link(); + } + + //------------------------------------------------------------------------------ + template + void generic_get_entry_dyn( Elf_Xword index, + Elf_Xword& tag, + Elf_Xword& value ) const + { + const endianess_convertor& convertor = elf_file.get_convertor(); + + // Check unusual case when dynamic section has no data + if ( dynamic_section->get_data() == 0 || + ( index + 1 ) * dynamic_section->get_entry_size() > + dynamic_section->get_size() ) { + tag = DT_NULL; + value = 0; + return; + } + + const T* pEntry = reinterpret_cast( + dynamic_section->get_data() + + index * dynamic_section->get_entry_size() ); + tag = convertor( pEntry->d_tag ); + switch ( tag ) { + case DT_NULL: + case DT_SYMBOLIC: + case DT_TEXTREL: + case DT_BIND_NOW: + value = 0; + break; + case DT_NEEDED: + case DT_PLTRELSZ: + case DT_RELASZ: + case DT_RELAENT: + case DT_STRSZ: + case DT_SYMENT: + case DT_SONAME: + case DT_RPATH: + case DT_RELSZ: + case DT_RELENT: + case DT_PLTREL: + case DT_INIT_ARRAYSZ: + case DT_FINI_ARRAYSZ: + case DT_RUNPATH: + case DT_FLAGS: + case DT_PREINIT_ARRAYSZ: + value = convertor( pEntry->d_un.d_val ); + break; + case DT_PLTGOT: + case DT_HASH: + case DT_STRTAB: + case DT_SYMTAB: + case DT_RELA: + case DT_INIT: + case DT_FINI: + case DT_REL: + case DT_DEBUG: + case DT_JMPREL: + case DT_INIT_ARRAY: + case DT_FINI_ARRAY: + case DT_PREINIT_ARRAY: + default: + value = convertor( pEntry->d_un.d_ptr ); + break; + } + } + + //------------------------------------------------------------------------------ + template void generic_add_entry( Elf_Xword tag, Elf_Xword value ) + { + const endianess_convertor& convertor = elf_file.get_convertor(); + + T entry; + + switch ( tag ) { + case DT_NULL: + case DT_SYMBOLIC: + case DT_TEXTREL: + case DT_BIND_NOW: + value = 0; + case DT_NEEDED: + case DT_PLTRELSZ: + case DT_RELASZ: + case DT_RELAENT: + case DT_STRSZ: + case DT_SYMENT: + case DT_SONAME: + case DT_RPATH: + case DT_RELSZ: + case DT_RELENT: + case DT_PLTREL: + case DT_INIT_ARRAYSZ: + case DT_FINI_ARRAYSZ: + case DT_RUNPATH: + case DT_FLAGS: + case DT_PREINIT_ARRAYSZ: + entry.d_un.d_val = convertor( value ); + break; + case DT_PLTGOT: + case DT_HASH: + case DT_STRTAB: + case DT_SYMTAB: + case DT_RELA: + case DT_INIT: + case DT_FINI: + case DT_REL: + case DT_DEBUG: + case DT_JMPREL: + case DT_INIT_ARRAY: + case DT_FINI_ARRAY: + case DT_PREINIT_ARRAY: + default: + entry.d_un.d_ptr = convertor( value ); + break; + } + + entry.d_tag = convertor( tag ); + + dynamic_section->append_data( reinterpret_cast( &entry ), + sizeof( entry ) ); + } + + //------------------------------------------------------------------------------ + private: + const elfio& elf_file; + S* dynamic_section; +}; + +using dynamic_section_accessor = dynamic_section_accessor_template
; +using const_dynamic_section_accessor = + dynamic_section_accessor_template; + +} // namespace ELFIO + +#endif // ELFIO_DYNAMIC_HPP + +/*** End of inlined file: elfio_dynamic.hpp ***/ + + +/*** Start of inlined file: elfio_modinfo.hpp ***/ +#ifndef ELFIO_MODINFO_HPP +#define ELFIO_MODINFO_HPP + +#include +#include + +namespace ELFIO { + +//------------------------------------------------------------------------------ +template class modinfo_section_accessor_template +{ + public: + //------------------------------------------------------------------------------ + modinfo_section_accessor_template( S* section_ ) + : modinfo_section( section_ ) + { + process_section(); + } + + //------------------------------------------------------------------------------ + Elf_Word get_attribute_num() const { return (Elf_Word)content.size(); } + + //------------------------------------------------------------------------------ + bool + get_attribute( Elf_Word no, std::string& field, std::string& value ) const + { + if ( no < content.size() ) { + field = content[no].first; + value = content[no].second; + return true; + } + + return false; + } + + //------------------------------------------------------------------------------ + bool get_attribute( std::string field_name, std::string& value ) const + { + for ( auto i = content.begin(); i != content.end(); i++ ) { + if ( field_name == i->first ) { + value = i->second; + return true; + } + } + + return false; + } + + //------------------------------------------------------------------------------ + Elf_Word add_attribute( std::string field, std::string value ) + { + Elf_Word current_position = 0; + + if ( modinfo_section ) { + // Strings are addeded to the end of the current section data + current_position = (Elf_Word)modinfo_section->get_size(); + + std::string attribute = field + "=" + value; + + modinfo_section->append_data( attribute + '\0' ); + content.push_back( + std::pair( field, value ) ); + } + + return current_position; + } + + //------------------------------------------------------------------------------ + private: + void process_section() + { + const char* pdata = modinfo_section->get_data(); + if ( pdata ) { + ELFIO::Elf_Xword i = 0; + while ( i < modinfo_section->get_size() ) { + while ( i < modinfo_section->get_size() && !pdata[i] ) + i++; + if ( i < modinfo_section->get_size() ) { + std::string info = pdata + i; + size_t loc = info.find( '=' ); + std::pair attribute( + info.substr( 0, loc ), info.substr( loc + 1 ) ); + + content.push_back( attribute ); + + i += info.length(); + } + } + } + } + + //------------------------------------------------------------------------------ + private: + S* modinfo_section; + std::vector> content; +}; + +using modinfo_section_accessor = modinfo_section_accessor_template
; +using const_modinfo_section_accessor = + modinfo_section_accessor_template; + +} // namespace ELFIO + +#endif // ELFIO_MODINFO_HPP + +/*** End of inlined file: elfio_modinfo.hpp ***/ + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif // ELFIO_HPP + +/*** End of inlined file: elfio.hpp ***/ + + +namespace ELFIO { + +static struct class_table_t +{ + const char key; + const char* str; +} class_table[] = { + { ELFCLASS32, "ELF32" }, + { ELFCLASS64, "ELF64" }, +}; + +static struct endian_table_t +{ + const char key; + const char* str; +} endian_table[] = { + { ELFDATANONE, "None" }, + { ELFDATA2LSB, "Little endian" }, + { ELFDATA2MSB, "Big endian" }, +}; + +static struct version_table_t +{ + const Elf64_Word key; + const char* str; +} version_table[] = { + { EV_NONE, "None" }, + { EV_CURRENT, "Current" }, +}; + +static struct type_table_t +{ + const Elf32_Half key; + const char* str; +} type_table[] = { + { ET_NONE, "No file type" }, { ET_REL, "Relocatable file" }, + { ET_EXEC, "Executable file" }, { ET_DYN, "Shared object file" }, + { ET_CORE, "Core file" }, +}; + +static struct machine_table_t +{ + const Elf64_Half key; + const char* str; +} machine_table[] = { + { EM_NONE, "No machine" }, + { EM_M32, "AT&T WE 32100" }, + { EM_SPARC, "SUN SPARC" }, + { EM_386, "Intel 80386" }, + { EM_68K, "Motorola m68k family" }, + { EM_88K, "Motorola m88k family" }, + { EM_486, "Intel 80486// Reserved for future use" }, + { EM_860, "Intel 80860" }, + { EM_MIPS, "MIPS R3000 (officially, big-endian only)" }, + { EM_S370, "IBM System/370" }, + { EM_MIPS_RS3_LE, + "MIPS R3000 little-endian (Oct 4 1999 Draft) Deprecated" }, + { EM_res011, "Reserved" }, + { EM_res012, "Reserved" }, + { EM_res013, "Reserved" }, + { EM_res014, "Reserved" }, + { EM_PARISC, "HPPA" }, + { EM_res016, "Reserved" }, + { EM_VPP550, "Fujitsu VPP500" }, + { EM_SPARC32PLUS, "Sun's v8plus" }, + { EM_960, "Intel 80960" }, + { EM_PPC, "PowerPC" }, + { EM_PPC64, "64-bit PowerPC" }, + { EM_S390, "IBM S/390" }, + { EM_SPU, "Sony/Toshiba/IBM SPU" }, + { EM_res024, "Reserved" }, + { EM_res025, "Reserved" }, + { EM_res026, "Reserved" }, + { EM_res027, "Reserved" }, + { EM_res028, "Reserved" }, + { EM_res029, "Reserved" }, + { EM_res030, "Reserved" }, + { EM_res031, "Reserved" }, + { EM_res032, "Reserved" }, + { EM_res033, "Reserved" }, + { EM_res034, "Reserved" }, + { EM_res035, "Reserved" }, + { EM_V800, "NEC V800 series" }, + { EM_FR20, "Fujitsu FR20" }, + { EM_RH32, "TRW RH32" }, + { EM_MCORE, "Motorola M*Core // May also be taken by Fujitsu MMA" }, + { EM_RCE, "Old name for MCore" }, + { EM_ARM, "ARM" }, + { EM_OLD_ALPHA, "Digital Alpha" }, + { EM_SH, "Renesas (formerly Hitachi) / SuperH SH" }, + { EM_SPARCV9, "SPARC v9 64-bit" }, + { EM_TRICORE, "Siemens Tricore embedded processor" }, + { EM_ARC, "ARC Cores" }, + { EM_H8_300, "Renesas (formerly Hitachi) H8/300" }, + { EM_H8_300H, "Renesas (formerly Hitachi) H8/300H" }, + { EM_H8S, "Renesas (formerly Hitachi) H8S" }, + { EM_H8_500, "Renesas (formerly Hitachi) H8/500" }, + { EM_IA_64, "Intel IA-64 Processor" }, + { EM_MIPS_X, "Stanford MIPS-X" }, + { EM_COLDFIRE, "Motorola Coldfire" }, + { EM_68HC12, "Motorola M68HC12" }, + { EM_MMA, "Fujitsu Multimedia Accelerator" }, + { EM_PCP, "Siemens PCP" }, + { EM_NCPU, "Sony nCPU embedded RISC processor" }, + { EM_NDR1, "Denso NDR1 microprocesspr" }, + { EM_STARCORE, "Motorola Star*Core processor" }, + { EM_ME16, "Toyota ME16 processor" }, + { EM_ST100, "STMicroelectronics ST100 processor" }, + { EM_TINYJ, "Advanced Logic Corp. TinyJ embedded processor" }, + { EM_X86_64, "Advanced Micro Devices X86-64 processor" }, + { EM_PDSP, "Sony DSP Processor" }, + { EM_PDP10, "Digital Equipment Corp. PDP-10" }, + { EM_PDP11, "Digital Equipment Corp. PDP-11" }, + { EM_FX66, "Siemens FX66 microcontroller" }, + { EM_ST9PLUS, "STMicroelectronics ST9+ 8/16 bit microcontroller" }, + { EM_ST7, "STMicroelectronics ST7 8-bit microcontroller" }, + { EM_68HC16, "Motorola MC68HC16 Microcontroller" }, + { EM_68HC11, "Motorola MC68HC11 Microcontroller" }, + { EM_68HC08, "Motorola MC68HC08 Microcontroller" }, + { EM_68HC05, "Motorola MC68HC05 Microcontroller" }, + { EM_SVX, "Silicon Graphics SVx" }, + { EM_ST19, "STMicroelectronics ST19 8-bit cpu" }, + { EM_VAX, "Digital VAX" }, + { EM_CRIS, "Axis Communications 32-bit embedded processor" }, + { EM_JAVELIN, "Infineon Technologies 32-bit embedded cpu" }, + { EM_FIREPATH, "Element 14 64-bit DSP processor" }, + { EM_ZSP, "LSI Logic's 16-bit DSP processor" }, + { EM_MMIX, "Donald Knuth's educational 64-bit processor" }, + { EM_HUANY, "Harvard's machine-independent format" }, + { EM_PRISM, "SiTera Prism" }, + { EM_AVR, "Atmel AVR 8-bit microcontroller" }, + { EM_FR30, "Fujitsu FR30" }, + { EM_D10V, "Mitsubishi D10V" }, + { EM_D30V, "Mitsubishi D30V" }, + { EM_V850, "NEC v850" }, + { EM_M32R, "Renesas M32R (formerly Mitsubishi M32R)" }, + { EM_MN10300, "Matsushita MN10300" }, + { EM_MN10200, "Matsushita MN10200" }, + { EM_PJ, "picoJava" }, + { EM_OPENRISC, "OpenRISC 32-bit embedded processor" }, + { EM_ARC_A5, "ARC Cores Tangent-A5" }, + { EM_XTENSA, "Tensilica Xtensa Architecture" }, + { EM_VIDEOCORE, "Alphamosaic VideoCore processor" }, + { EM_TMM_GPP, "Thompson Multimedia General Purpose Processor" }, + { EM_NS32K, "National Semiconductor 32000 series" }, + { EM_TPC, "Tenor Network TPC processor" }, + { EM_SNP1K, "Trebia SNP 1000 processor" }, + { EM_ST200, "STMicroelectronics ST200 microcontroller" }, + { EM_IP2K, "Ubicom IP2022 micro controller" }, + { EM_MAX, "MAX Processor" }, + { EM_CR, "National Semiconductor CompactRISC" }, + { EM_F2MC16, "Fujitsu F2MC16" }, + { EM_MSP430, "TI msp430 micro controller" }, + { EM_BLACKFIN, "ADI Blackfin" }, + { EM_SE_C33, "S1C33 Family of Seiko Epson processors" }, + { EM_SEP, "Sharp embedded microprocessor" }, + { EM_ARCA, "Arca RISC Microprocessor" }, + { EM_UNICORE, "Microprocessor series from PKU-Unity Ltd. and MPRC of " + "Peking University" }, + { EM_EXCESS, "eXcess: 16/32/64-bit configurable embedded CPU" }, + { EM_DXP, "Icera Semiconductor Inc. Deep Execution Processor" }, + { EM_ALTERA_NIOS2, "Altera Nios II soft-core processor" }, + { EM_CRX, "National Semiconductor CRX" }, + { EM_XGATE, "Motorola XGATE embedded processor" }, + { EM_C166, "Infineon C16x/XC16x processor" }, + { EM_M16C, "Renesas M16C series microprocessors" }, + { EM_DSPIC30F, "Microchip Technology dsPIC30F Digital Signal Controller" }, + { EM_CE, "Freescale Communication Engine RISC core" }, + { EM_M32C, "Renesas M32C series microprocessors" }, + { EM_res121, "Reserved" }, + { EM_res122, "Reserved" }, + { EM_res123, "Reserved" }, + { EM_res124, "Reserved" }, + { EM_res125, "Reserved" }, + { EM_res126, "Reserved" }, + { EM_res127, "Reserved" }, + { EM_res128, "Reserved" }, + { EM_res129, "Reserved" }, + { EM_res130, "Reserved" }, + { EM_TSK3000, "Altium TSK3000 core" }, + { EM_RS08, "Freescale RS08 embedded processor" }, + { EM_res133, "Reserved" }, + { EM_ECOG2, "Cyan Technology eCOG2 microprocessor" }, + { EM_SCORE, "Sunplus Score" }, + { EM_SCORE7, "Sunplus S+core7 RISC processor" }, + { EM_DSP24, "New Japan Radio (NJR) 24-bit DSP Processor" }, + { EM_VIDEOCORE3, "Broadcom VideoCore III processor" }, + { EM_LATTICEMICO32, "RISC processor for Lattice FPGA architecture" }, + { EM_SE_C17, "Seiko Epson C17 family" }, + { EM_TI_C6000, "Texas Instruments TMS320C6000 DSP family" }, + { EM_TI_C2000, "Texas Instruments TMS320C2000 DSP family" }, + { EM_TI_C5500, "Texas Instruments TMS320C55x DSP family" }, + { EM_res143, "Reserved" }, + { EM_res144, "Reserved" }, + { EM_res145, "Reserved" }, + { EM_res146, "Reserved" }, + { EM_res147, "Reserved" }, + { EM_res148, "Reserved" }, + { EM_res149, "Reserved" }, + { EM_res150, "Reserved" }, + { EM_res151, "Reserved" }, + { EM_res152, "Reserved" }, + { EM_res153, "Reserved" }, + { EM_res154, "Reserved" }, + { EM_res155, "Reserved" }, + { EM_res156, "Reserved" }, + { EM_res157, "Reserved" }, + { EM_res158, "Reserved" }, + { EM_res159, "Reserved" }, + { EM_MMDSP_PLUS, "STMicroelectronics 64bit VLIW Data Signal Processor" }, + { EM_CYPRESS_M8C, "Cypress M8C microprocessor" }, + { EM_R32C, "Renesas R32C series microprocessors" }, + { EM_TRIMEDIA, "NXP Semiconductors TriMedia architecture family" }, + { EM_QDSP6, "QUALCOMM DSP6 Processor" }, + { EM_8051, "Intel 8051 and variants" }, + { EM_STXP7X, "STMicroelectronics STxP7x family" }, + { EM_NDS32, + "Andes Technology compact code size embedded RISC processor family" }, + { EM_ECOG1, "Cyan Technology eCOG1X family" }, + { EM_ECOG1X, "Cyan Technology eCOG1X family" }, + { EM_MAXQ30, "Dallas Semiconductor MAXQ30 Core Micro-controllers" }, + { EM_XIMO16, "New Japan Radio (NJR) 16-bit DSP Processor" }, + { EM_MANIK, "M2000 Reconfigurable RISC Microprocessor" }, + { EM_CRAYNV2, "Cray Inc. NV2 vector architecture" }, + { EM_RX, "Renesas RX family" }, + { EM_METAG, "Imagination Technologies META processor architecture" }, + { EM_MCST_ELBRUS, "MCST Elbrus general purpose hardware architecture" }, + { EM_ECOG16, "Cyan Technology eCOG16 family" }, + { EM_CR16, "National Semiconductor CompactRISC 16-bit processor" }, + { EM_ETPU, "Freescale Extended Time Processing Unit" }, + { EM_SLE9X, "Infineon Technologies SLE9X core" }, + { EM_L1OM, "Intel L1OM" }, + { EM_INTEL181, "Reserved by Intel" }, + { EM_INTEL182, "Reserved by Intel" }, + { EM_res183, "Reserved by ARM" }, + { EM_res184, "Reserved by ARM" }, + { EM_AVR32, "Atmel Corporation 32-bit microprocessor family" }, + { EM_STM8, "STMicroeletronics STM8 8-bit microcontroller" }, + { EM_TILE64, "Tilera TILE64 multicore architecture family" }, + { EM_TILEPRO, "Tilera TILEPro multicore architecture family" }, + { EM_MICROBLAZE, "Xilinx MicroBlaze 32-bit RISC soft processor core" }, + { EM_CUDA, "NVIDIA CUDA architecture " }, +}; + +static struct section_type_table_t +{ + const Elf64_Half key; + const char* str; +} section_type_table[] = { + { SHT_NULL, "NULL" }, + { SHT_PROGBITS, "PROGBITS" }, + { SHT_SYMTAB, "SYMTAB" }, + { SHT_STRTAB, "STRTAB" }, + { SHT_RELA, "RELA" }, + { SHT_HASH, "HASH" }, + { SHT_DYNAMIC, "DYNAMIC" }, + { SHT_NOTE, "NOTE" }, + { SHT_NOBITS, "NOBITS" }, + { SHT_REL, "REL" }, + { SHT_SHLIB, "SHLIB" }, + { SHT_DYNSYM, "DYNSYM" }, + { SHT_INIT_ARRAY, "INIT_ARRAY" }, + { SHT_FINI_ARRAY, "FINI_ARRAY" }, + { SHT_PREINIT_ARRAY, "PREINIT_ARRAY" }, + { SHT_GROUP, "GROUP" }, + { SHT_SYMTAB_SHNDX, "SYMTAB_SHNDX " }, +}; + +static struct segment_type_table_t +{ + const Elf_Word key; + const char* str; +} segment_type_table[] = { + { PT_NULL, "NULL" }, { PT_LOAD, "LOAD" }, { PT_DYNAMIC, "DYNAMIC" }, + { PT_INTERP, "INTERP" }, { PT_NOTE, "NOTE" }, { PT_SHLIB, "SHLIB" }, + { PT_PHDR, "PHDR" }, { PT_TLS, "TLS" }, +}; + +static struct segment_flag_table_t +{ + const Elf_Word key; + const char* str; +} segment_flag_table[] = { + { 0, "" }, { 1, "X" }, { 2, "W" }, { 3, "WX" }, + { 4, "R" }, { 5, "RX" }, { 6, "RW" }, { 7, "RWX" }, +}; + +static struct symbol_bind_t +{ + const Elf_Word key; + const char* str; +} symbol_bind_table[] = { + { STB_LOCAL, "LOCAL" }, { STB_GLOBAL, "GLOBAL" }, + { STB_WEAK, "WEAK" }, { STB_LOOS, "LOOS" }, + { STB_HIOS, "HIOS" }, { STB_MULTIDEF, "MULTIDEF" }, + { STB_LOPROC, "LOPROC" }, { STB_HIPROC, "HIPROC" }, +}; + +static struct symbol_type_t +{ + const Elf_Word key; + const char* str; +} symbol_type_table[] = { + { STT_NOTYPE, "NOTYPE" }, { STT_OBJECT, "OBJECT" }, + { STT_FUNC, "FUNC" }, { STT_SECTION, "SECTION" }, + { STT_FILE, "FILE" }, { STT_COMMON, "COMMON" }, + { STT_TLS, "TLS" }, { STT_LOOS, "LOOS" }, + { STT_HIOS, "HIOS" }, { STT_LOPROC, "LOPROC" }, + { STT_HIPROC, "HIPROC" }, +}; + +static struct dynamic_tag_t +{ + const Elf_Word key; + const char* str; +} dynamic_tag_table[] = { + { DT_NULL, "NULL" }, + { DT_NEEDED, "NEEDED" }, + { DT_PLTRELSZ, "PLTRELSZ" }, + { DT_PLTGOT, "PLTGOT" }, + { DT_HASH, "HASH" }, + { DT_STRTAB, "STRTAB" }, + { DT_SYMTAB, "SYMTAB" }, + { DT_RELA, "RELA" }, + { DT_RELASZ, "RELASZ" }, + { DT_RELAENT, "RELAENT" }, + { DT_STRSZ, "STRSZ" }, + { DT_SYMENT, "SYMENT" }, + { DT_INIT, "INIT" }, + { DT_FINI, "FINI" }, + { DT_SONAME, "SONAME" }, + { DT_RPATH, "RPATH" }, + { DT_SYMBOLIC, "SYMBOLIC" }, + { DT_REL, "REL" }, + { DT_RELSZ, "RELSZ" }, + { DT_RELENT, "RELENT" }, + { DT_PLTREL, "PLTREL" }, + { DT_DEBUG, "DEBUG" }, + { DT_TEXTREL, "TEXTREL" }, + { DT_JMPREL, "JMPREL" }, + { DT_BIND_NOW, "BIND_NOW" }, + { DT_INIT_ARRAY, "INIT_ARRAY" }, + { DT_FINI_ARRAY, "FINI_ARRAY" }, + { DT_INIT_ARRAYSZ, "INIT_ARRAYSZ" }, + { DT_FINI_ARRAYSZ, "FINI_ARRAYSZ" }, + { DT_RUNPATH, "RUNPATH" }, + { DT_FLAGS, "FLAGS" }, + { DT_ENCODING, "ENCODING" }, + { DT_PREINIT_ARRAY, "PREINIT_ARRAY" }, + { DT_PREINIT_ARRAYSZ, "PREINIT_ARRAYSZ" }, + { DT_MAXPOSTAGS, "MAXPOSTAGS" }, +}; + +static const ELFIO::Elf_Xword MAX_DATA_ENTRIES = 64; + +//------------------------------------------------------------------------------ +class dump +{ +#define DUMP_DEC_FORMAT( width ) \ + std::setw( width ) << std::setfill( ' ' ) << std::dec << std::right +#define DUMP_HEX_FORMAT( width ) \ + std::setw( width ) << std::setfill( '0' ) << std::hex << std::right +#define DUMP_STR_FORMAT( width ) \ + std::setw( width ) << std::setfill( ' ' ) << std::hex << std::left + + public: + //------------------------------------------------------------------------------ + static void header( std::ostream& out, const elfio& reader ) + { + if ( !reader.get_header_size() ) { + return; + } + out << "ELF Header" << std::endl + << std::endl + << " Class: " << str_class( reader.get_class() ) << std::endl + << " Encoding: " << str_endian( reader.get_encoding() ) + << std::endl + << " ELFVersion: " << str_version( reader.get_elf_version() ) + << std::endl + << " Type: " << str_type( reader.get_type() ) << std::endl + << " Machine: " << str_machine( reader.get_machine() ) + << std::endl + << " Version: " << str_version( reader.get_version() ) + << std::endl + << " Entry: " + << "0x" << std::hex << reader.get_entry() << std::endl + << " Flags: " + << "0x" << std::hex << reader.get_flags() << std::endl + << std::endl; + } + + //------------------------------------------------------------------------------ + static void section_headers( std::ostream& out, const elfio& reader ) + { + Elf_Half n = reader.sections.size(); + + if ( n == 0 ) { + return; + } + + out << "Section Headers:" << std::endl; + if ( reader.get_class() == ELFCLASS32 ) { // Output for 32-bit + out << "[ Nr ] Type Addr Size ES Flg Lk Inf " + "Al Name" + << std::endl; + } + else { // Output for 64-bit + out << "[ Nr ] Type Addr Size " + " ES Flg" + << std::endl + << " Lk Inf Al Name" << std::endl; + } + + for ( Elf_Half i = 0; i < n; ++i ) { // For all sections + section* sec = reader.sections[i]; + section_header( out, i, sec, reader.get_class() ); + } + + out << "Key to Flags: W (write), A (alloc), X (execute)\n\n" + << std::endl; + } + + //------------------------------------------------------------------------------ + static void section_header( std::ostream& out, + Elf_Half no, + const section* sec, + unsigned char elf_class ) + { + std::ios_base::fmtflags original_flags = out.flags(); + + if ( elf_class == ELFCLASS32 ) { // Output for 32-bit + out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " + << DUMP_STR_FORMAT( 17 ) << str_section_type( sec->get_type() ) + << " " << DUMP_HEX_FORMAT( 8 ) << sec->get_address() << " " + << DUMP_HEX_FORMAT( 8 ) << sec->get_size() << " " + << DUMP_HEX_FORMAT( 2 ) << sec->get_entry_size() << " " + << DUMP_STR_FORMAT( 3 ) << section_flags( sec->get_flags() ) + << " " << DUMP_HEX_FORMAT( 2 ) << sec->get_link() << " " + << DUMP_HEX_FORMAT( 3 ) << sec->get_info() << " " + << DUMP_HEX_FORMAT( 2 ) << sec->get_addr_align() << " " + << DUMP_STR_FORMAT( 17 ) << sec->get_name() << " " << std::endl; + } + else { // Output for 64-bit + out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " + << DUMP_STR_FORMAT( 17 ) << str_section_type( sec->get_type() ) + << " " << DUMP_HEX_FORMAT( 16 ) << sec->get_address() << " " + << DUMP_HEX_FORMAT( 16 ) << sec->get_size() << " " + << DUMP_HEX_FORMAT( 4 ) << sec->get_entry_size() << " " + << DUMP_STR_FORMAT( 3 ) << section_flags( sec->get_flags() ) + << " " << std::endl + << " " << DUMP_HEX_FORMAT( 4 ) << sec->get_link() << " " + << DUMP_HEX_FORMAT( 4 ) << sec->get_info() << " " + << DUMP_HEX_FORMAT( 4 ) << sec->get_addr_align() << " " + << DUMP_STR_FORMAT( 17 ) << sec->get_name() << " " << std::endl; + } + + out.flags( original_flags ); + + return; + } + + //------------------------------------------------------------------------------ + static void segment_headers( std::ostream& out, const elfio& reader ) + { + Elf_Half n = reader.segments.size(); + if ( n == 0 ) { + return; + } + + out << "Segment headers:" << std::endl; + if ( reader.get_class() == ELFCLASS32 ) { // Output for 32-bit + out << "[ Nr ] Type VirtAddr PhysAddr FileSize Mem.Size " + "Flags Align" + << std::endl; + } + else { // Output for 64-bit + out << "[ Nr ] Type VirtAddr PhysAddr " + "Flags" + << std::endl + << " FileSize Mem.Size " + "Align" + << std::endl; + } + + for ( Elf_Half i = 0; i < n; ++i ) { + segment* seg = reader.segments[i]; + segment_header( out, i, seg, reader.get_class() ); + } + + out << std::endl; + } + + //------------------------------------------------------------------------------ + static void segment_header( std::ostream& out, + Elf_Half no, + const segment* seg, + unsigned int elf_class ) + { + std::ios_base::fmtflags original_flags = out.flags(); + + if ( elf_class == ELFCLASS32 ) { // Output for 32-bit + out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " + << DUMP_STR_FORMAT( 14 ) << str_segment_type( seg->get_type() ) + << " " << DUMP_HEX_FORMAT( 8 ) << seg->get_virtual_address() + << " " << DUMP_HEX_FORMAT( 8 ) << seg->get_physical_address() + << " " << DUMP_HEX_FORMAT( 8 ) << seg->get_file_size() << " " + << DUMP_HEX_FORMAT( 8 ) << seg->get_memory_size() << " " + << DUMP_STR_FORMAT( 8 ) << str_segment_flag( seg->get_flags() ) + << " " << DUMP_HEX_FORMAT( 8 ) << seg->get_align() << " " + << std::endl; + } + else { // Output for 64-bit + out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " + << DUMP_STR_FORMAT( 14 ) << str_segment_type( seg->get_type() ) + << " " << DUMP_HEX_FORMAT( 16 ) << seg->get_virtual_address() + << " " << DUMP_HEX_FORMAT( 16 ) << seg->get_physical_address() + << " " << DUMP_STR_FORMAT( 16 ) + << str_segment_flag( seg->get_flags() ) << " " << std::endl + << " " << DUMP_HEX_FORMAT( 16 ) + << seg->get_file_size() << " " << DUMP_HEX_FORMAT( 16 ) + << seg->get_memory_size() << " " << DUMP_HEX_FORMAT( 16 ) + << seg->get_align() << " " << std::endl; + } + + out.flags( original_flags ); + } + + //------------------------------------------------------------------------------ + static void symbol_tables( std::ostream& out, const elfio& reader ) + { + Elf_Half n = reader.sections.size(); + for ( Elf_Half i = 0; i < n; ++i ) { // For all sections + section* sec = reader.sections[i]; + if ( SHT_SYMTAB == sec->get_type() || + SHT_DYNSYM == sec->get_type() ) { + symbol_section_accessor symbols( reader, sec ); + + Elf_Xword sym_no = symbols.get_symbols_num(); + if ( sym_no > 0 ) { + out << "Symbol table (" << sec->get_name() << ")" + << std::endl; + if ( reader.get_class() == + ELFCLASS32 ) { // Output for 32-bit + out << "[ Nr ] Value Size Type Bind " + "Sect Name" + << std::endl; + } + else { // Output for 64-bit + out << "[ Nr ] Value Size Type " + " Bind Sect" + << std::endl + << " Name" << std::endl; + } + for ( Elf_Xword i = 0; i < sym_no; ++i ) { + std::string name; + Elf64_Addr value = 0; + Elf_Xword size = 0; + unsigned char bind = 0; + unsigned char type = 0; + Elf_Half section = 0; + unsigned char other = 0; + symbols.get_symbol( i, name, value, size, bind, type, + section, other ); + symbol_table( out, i, name, value, size, bind, type, + section, reader.get_class() ); + } + + out << std::endl; + } + } + } + } + + //------------------------------------------------------------------------------ + static void symbol_table( std::ostream& out, + Elf_Xword no, + std::string& name, + Elf64_Addr value, + Elf_Xword size, + unsigned char bind, + unsigned char type, + Elf_Half section, + unsigned int elf_class ) + { + std::ios_base::fmtflags original_flags = out.flags(); + + if ( elf_class == ELFCLASS32 ) { // Output for 32-bit + out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " + << DUMP_HEX_FORMAT( 8 ) << value << " " << DUMP_HEX_FORMAT( 8 ) + << size << " " << DUMP_STR_FORMAT( 7 ) + << str_symbol_type( type ) << " " << DUMP_STR_FORMAT( 8 ) + << str_symbol_bind( bind ) << " " << DUMP_DEC_FORMAT( 5 ) + << section << " " << DUMP_STR_FORMAT( 1 ) << name << " " + << std::endl; + } + else { // Output for 64-bit + out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " + << DUMP_HEX_FORMAT( 16 ) << value << " " + << DUMP_HEX_FORMAT( 16 ) << size << " " << DUMP_STR_FORMAT( 7 ) + << str_symbol_type( type ) << " " << DUMP_STR_FORMAT( 8 ) + << str_symbol_bind( bind ) << " " << DUMP_DEC_FORMAT( 5 ) + << section << " " << std::endl + << " " << DUMP_STR_FORMAT( 1 ) << name << " " + << std::endl; + } + + out.flags( original_flags ); + } + + //------------------------------------------------------------------------------ + static void notes( std::ostream& out, const elfio& reader ) + { + Elf_Half no = reader.sections.size(); + for ( Elf_Half i = 0; i < no; ++i ) { // For all sections + section* sec = reader.sections[i]; + if ( SHT_NOTE == sec->get_type() ) { // Look at notes + note_section_accessor notes( reader, sec ); + Elf_Word no_notes = notes.get_notes_num(); + if ( no > 0 ) { + out << "Note section (" << sec->get_name() << ")" + << std::endl + << " No Type Name" << std::endl; + for ( Elf_Word j = 0; j < no_notes; ++j ) { // For all notes + Elf_Word type; + std::string name; + void* desc; + Elf_Word descsz; + + if ( notes.get_note( j, type, name, desc, descsz ) ) { + // 'name' usually contains \0 at the end. Try to fix it + name = name.c_str(); + note( out, j, type, name ); + } + } + + out << std::endl; + } + } + } + } + + //------------------------------------------------------------------------------ + static void modinfo( std::ostream& out, const elfio& reader ) + { + Elf_Half no = reader.sections.size(); + for ( Elf_Half i = 0; i < no; ++i ) { // For all sections + section* sec = reader.sections[i]; + if ( ".modinfo" == sec->get_name() ) { // Look for the section + out << "Section .modinfo" << std::endl; + + const_modinfo_section_accessor modinfo( sec ); + for ( Elf_Word i = 0; i < modinfo.get_attribute_num(); i++ ) { + std::string field; + std::string value; + if ( modinfo.get_attribute( i, field, value ) ) { + out << " " << std::setw( 20 ) << field + << std::setw( 0 ) << " = " << value << std::endl; + } + } + + out << std::endl; + break; + } + } + } + + //------------------------------------------------------------------------------ + static void + note( std::ostream& out, int no, Elf_Word type, const std::string& name ) + { + out << " [" << DUMP_DEC_FORMAT( 2 ) << no << "] " + << DUMP_HEX_FORMAT( 8 ) << type << " " << DUMP_STR_FORMAT( 1 ) + << name << std::endl; + } + + //------------------------------------------------------------------------------ + static void dynamic_tags( std::ostream& out, const elfio& reader ) + { + Elf_Half n = reader.sections.size(); + for ( Elf_Half i = 0; i < n; ++i ) { // For all sections + section* sec = reader.sections[i]; + if ( SHT_DYNAMIC == sec->get_type() ) { + dynamic_section_accessor dynamic( reader, sec ); + + Elf_Xword dyn_no = dynamic.get_entries_num(); + if ( dyn_no > 0 ) { + out << "Dynamic section (" << sec->get_name() << ")" + << std::endl; + out << "[ Nr ] Tag Name/Value" << std::endl; + for ( Elf_Xword i = 0; i < dyn_no; ++i ) { + Elf_Xword tag = 0; + Elf_Xword value = 0; + std::string str; + dynamic.get_entry( i, tag, value, str ); + dynamic_tag( out, i, tag, value, str, + reader.get_class() ); + if ( DT_NULL == tag ) { + break; + } + } + + out << std::endl; + } + } + } + } + + //------------------------------------------------------------------------------ + static void dynamic_tag( std::ostream& out, + Elf_Xword no, + Elf_Xword tag, + Elf_Xword value, + std::string str, + unsigned int /*elf_class*/ ) + { + out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " + << DUMP_STR_FORMAT( 16 ) << str_dynamic_tag( tag ) << " "; + if ( str.empty() ) { + out << DUMP_HEX_FORMAT( 16 ) << value << " "; + } + else { + out << DUMP_STR_FORMAT( 32 ) << str << " "; + } + out << std::endl; + } + + //------------------------------------------------------------------------------ + static void section_data( std::ostream& out, const section* sec ) + { + std::ios_base::fmtflags original_flags = out.flags(); + + out << sec->get_name() << std::endl; + const char* pdata = sec->get_data(); + if ( pdata ) { + ELFIO::Elf_Xword i; + for ( i = 0; i < std::min( sec->get_size(), MAX_DATA_ENTRIES ); + ++i ) { + if ( i % 16 == 0 ) { + out << "[" << DUMP_HEX_FORMAT( 8 ) << i << "]"; + } + + out << " " << DUMP_HEX_FORMAT( 2 ) << ( pdata[i] & 0x000000FF ); + + if ( i % 16 == 15 ) { + out << std::endl; + } + } + if ( i % 16 != 0 ) { + out << std::endl; + } + + out.flags( original_flags ); + } + + return; + } + + //------------------------------------------------------------------------------ + static void section_datas( std::ostream& out, const elfio& reader ) + { + Elf_Half n = reader.sections.size(); + + if ( n == 0 ) { + return; + } + + out << "Section Data:" << std::endl; + + for ( Elf_Half i = 1; i < n; ++i ) { // For all sections + section* sec = reader.sections[i]; + if ( sec->get_type() == SHT_NOBITS ) { + continue; + } + section_data( out, sec ); + } + + out << std::endl; + } + + //------------------------------------------------------------------------------ + static void + segment_data( std::ostream& out, Elf_Half no, const segment* seg ) + { + std::ios_base::fmtflags original_flags = out.flags(); + + out << "Segment # " << no << std::endl; + const char* pdata = seg->get_data(); + if ( pdata ) { + ELFIO::Elf_Xword i; + for ( i = 0; i < std::min( seg->get_file_size(), MAX_DATA_ENTRIES ); + ++i ) { + if ( i % 16 == 0 ) { + out << "[" << DUMP_HEX_FORMAT( 8 ) << i << "]"; + } + + out << " " << DUMP_HEX_FORMAT( 2 ) << ( pdata[i] & 0x000000FF ); + + if ( i % 16 == 15 ) { + out << std::endl; + } + } + if ( i % 16 != 0 ) { + out << std::endl; + } + + out.flags( original_flags ); + } + + return; + } + + //------------------------------------------------------------------------------ + static void segment_datas( std::ostream& out, const elfio& reader ) + { + Elf_Half n = reader.segments.size(); + + if ( n == 0 ) { + return; + } + + out << "Segment Data:" << std::endl; + + for ( Elf_Half i = 0; i < n; ++i ) { // For all sections + segment* seg = reader.segments[i]; + segment_data( out, i, seg ); + } + + out << std::endl; + } + + private: + //------------------------------------------------------------------------------ + template + std::string static find_value_in_table( const T& table, const K& key ) + { + std::string res = "?"; + for ( unsigned int i = 0; i < sizeof( table ) / sizeof( table[0] ); + ++i ) { + if ( table[i].key == key ) { + res = table[i].str; + break; + } + } + + return res; + } + + //------------------------------------------------------------------------------ + template + static std::string format_assoc( const T& table, const K& key ) + { + std::string str = find_value_in_table( table, key ); + if ( str == "?" ) { + std::ostringstream oss; + oss << str << " (0x" << std::hex << key << ")"; + str = oss.str(); + } + + return str; + } + + //------------------------------------------------------------------------------ + template + static std::string format_assoc( const T& table, const char key ) + { + return format_assoc( table, (const int)key ); + } + + //------------------------------------------------------------------------------ + static std::string section_flags( Elf_Xword flags ) + { + std::string ret = ""; + if ( flags & SHF_WRITE ) { + ret += "W"; + } + if ( flags & SHF_ALLOC ) { + ret += "A"; + } + if ( flags & SHF_EXECINSTR ) { + ret += "X"; + } + + return ret; + } + +//------------------------------------------------------------------------------ +#define STR_FUNC_TABLE( name ) \ + template static std::string str_##name( const T key ) \ + { \ + return format_assoc( name##_table, key ); \ + } + + STR_FUNC_TABLE( class ) + STR_FUNC_TABLE( endian ) + STR_FUNC_TABLE( version ) + STR_FUNC_TABLE( type ) + STR_FUNC_TABLE( machine ) + STR_FUNC_TABLE( section_type ) + STR_FUNC_TABLE( segment_type ) + STR_FUNC_TABLE( segment_flag ) + STR_FUNC_TABLE( symbol_bind ) + STR_FUNC_TABLE( symbol_type ) + STR_FUNC_TABLE( dynamic_tag ) + +#undef STR_FUNC_TABLE +#undef DUMP_DEC_FORMAT +#undef DUMP_HEX_FORMAT +#undef DUMP_STR_FORMAT +}; // class dump + +}; // namespace ELFIO + +#endif // ELFIO_DUMP_HPP + +/*** End of inlined file: elfio_dump.hpp ***/ + diff --git a/deepin-devicemanager-server/tests/3rdparty/stub/stub-shadow.cpp b/deepin-devicemanager-server/tests/3rdparty/stub/stub-shadow.cpp new file mode 100644 index 000000000..05d020920 --- /dev/null +++ b/deepin-devicemanager-server/tests/3rdparty/stub/stub-shadow.cpp @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: 2021 - 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: MIT + +#include "stub-shadow.h" + +namespace stub_ext { + +WrapperMap stub_wrappers; + +Wrapper::Wrapper() +{ + +} + +Wrapper::~Wrapper() +{ + +} + +void freeWrapper(Wrapper *wrapper) +{ + if (!wrapper) + return; + + for (auto iter = stub_wrappers.begin(); iter != stub_wrappers.end();) { + if (iter->second == wrapper) + iter = stub_wrappers.erase(iter); + else + ++iter; + } + + delete wrapper; +} +} diff --git a/deepin-devicemanager-server/tests/3rdparty/stub/stub-shadow.h b/deepin-devicemanager-server/tests/3rdparty/stub/stub-shadow.h new file mode 100644 index 000000000..9199a12ab --- /dev/null +++ b/deepin-devicemanager-server/tests/3rdparty/stub/stub-shadow.h @@ -0,0 +1,166 @@ +// SPDX-FileCopyrightText: 2021 - 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: MIT + +#ifndef STUBSHADOW_H +#define STUBSHADOW_H + +#include +#include + +namespace stub_ext { + +#define LAMDA_FUNCTION_TYPE decltype(&Lamda::operator()) + +class Wrapper +{ +public: + Wrapper(); + virtual ~Wrapper(); +}; + +typedef std::unordered_map WrapperMap; +extern WrapperMap stub_wrappers; + +template +class LamdaWrapper : public Wrapper +{ +public: + LamdaWrapper(Lamda func): Wrapper(),_func(func){} + ~LamdaWrapper(){} + Lamda _func; +}; + +template +struct VFLocator +{ + +}; + +template +struct VFLocator +{ + typedef Ret (*Func)(Obj*, Args...); +}; + +template +struct VFLocator +{ + typedef Ret (*Func)(Obj*, Args...); +}; + +template +struct LamdaCaller +{ + +}; + +template +struct LamdaCaller +{ + template + static Ret call(LamdaWrapper *wrapper, OrgArgs&&... args) + { + return wrapper->_func(std::forward(args)...); + } +}; + +template +struct LamdaCaller +{ + template + static Ret call(LamdaWrapper *wrapper, OrgArgs&&... args) + { + return wrapper->_func(); + } +}; + +template +struct FuncShadow +{ + +}; + +template +struct FuncShadow +{ + typedef Ret (*Shadow)(Args...); + typedef Ret RetType; + + static Ret call(Args ...args) + { + Shadow shadow = &call; + long id = (long)shadow; + auto iter = stub_wrappers.find(id); + assert(stub_wrappers.find(id) != stub_wrappers.end()); + LamdaWrapper *wrapper = dynamic_cast *>(iter->second); + return LamdaCaller::call(wrapper, args...); + } +}; + +template +struct FuncShadow +{ + typedef Ret (*Shadow)(Args...); + typedef Ret RetType; + + static Ret call(Args ...args) + { + Shadow shadow = &call; + long id = (long)shadow; + auto iter = stub_wrappers.find(id); + assert(stub_wrappers.find(id) != stub_wrappers.end()); + LamdaWrapper *wrapper = dynamic_cast *>(iter->second); + return LamdaCaller::call(wrapper, args...); + } +}; + +template +struct FuncShadow +{ + typedef Ret (*Shadow)(Obj *,Args...); + typedef Ret RetType; + static Ret call(Obj *obj, Args ...args) + { + Shadow shadow = &call; + long id = (long)shadow; + auto iter = stub_wrappers.find(id); + assert(stub_wrappers.find(id) != stub_wrappers.end()); + LamdaWrapper *wrapper = dynamic_cast *>(iter->second); + return LamdaCaller::call(wrapper, obj, args...); + } +}; + + +template +struct FuncShadow +{ + typedef Ret (*Shadow)(Obj *,Args...); + typedef Ret RetType; + static Ret call(Obj *obj, Args ...args) + { + Shadow shadow = &call; + long id = (long)shadow; + auto iter = stub_wrappers.find(id); + assert(stub_wrappers.find(id) != stub_wrappers.end()); + LamdaWrapper *wrapper = dynamic_cast *>(iter->second); + return LamdaCaller::call(wrapper, obj, args...); + } +}; + +template +typename FuncShadow::Shadow depictShadow(Wrapper **wrapper, Func func, Lamda lamda) +{ + *wrapper = new LamdaWrapper(lamda); + typename FuncShadow::Shadow shadow = &FuncShadow::call; + long id = (long)shadow; + assert(stub_wrappers.find(id) == stub_wrappers.end()); + stub_wrappers.insert(std::make_pair(id,*wrapper)); + return shadow; +} + +void freeWrapper(Wrapper *wrapper); + +} + +#endif // STUBSHADOW_H diff --git a/deepin-devicemanager-server/tests/3rdparty/stub/stub.h b/deepin-devicemanager-server/tests/3rdparty/stub/stub.h new file mode 100644 index 000000000..c5f2f53f8 --- /dev/null +++ b/deepin-devicemanager-server/tests/3rdparty/stub/stub.h @@ -0,0 +1,360 @@ +#ifndef __STUB_H__ +#define __STUB_H__ + +#ifdef _WIN32 +//windows +#include +#include +#else +//linux +#include +#include +#include +#endif +//c +#include +#include +//c++ +#include + + +#define ADDR(CLASS_NAME,MEMBER_NAME) (&CLASS_NAME::MEMBER_NAME) + +/********************************************************** + replace function +**********************************************************/ +#ifdef _WIN32 +#define CACHEFLUSH(addr, size) FlushInstructionCache(GetCurrentProcess(), addr, size) +#else +#define CACHEFLUSH(addr, size) __builtin___clear_cache(addr, addr + size) +#endif + +#if defined(__aarch64__) || defined(_M_ARM64) + #define CODESIZE 16U + #define CODESIZE_MIN 16U + #define CODESIZE_MAX CODESIZE + // ldr x9, +8 + // br x9 + // addr + #define REPLACE_FAR(t, fn, fn_stub)\ + ((uint32_t*)fn)[0] = 0x58000040 | 9;\ + ((uint32_t*)fn)[1] = 0xd61f0120 | (9 << 5);\ + *(long long *)(fn + 8) = (long long )fn_stub;\ + CACHEFLUSH((char *)fn, CODESIZE); + #define REPLACE_NEAR(t, fn, fn_stub) REPLACE_FAR(t, fn, fn_stub) +#elif defined(__arm__) || defined(_M_ARM) + #define CODESIZE 8U + #define CODESIZE_MIN 8U + #define CODESIZE_MAX CODESIZE + // ldr pc, [pc, #-4] + #define REPLACE_FAR(t, fn, fn_stub)\ + ((uint32_t*)fn)[0] = 0xe51ff004;\ + ((uint32_t*)fn)[1] = (uint32_t)fn_stub;\ + CACHEFLUSH((char *)fn, CODESIZE); + #define REPLACE_NEAR(t, fn, fn_stub) REPLACE_FAR(t, fn, fn_stub) +#elif defined(__mips64) + #define CACHEFLUSH(addr, size) __builtin___clear_cache(addr, addr + size) + #define CODESIZE 80U + #define CODESIZE_MIN 80U + #define CODESIZE_MAX CODESIZE + //mips没有PC指针,所以需要手动入栈出栈 + //120000ce0: 67bdffe0 daddiu sp, sp, -32 //入栈 + //120000ce4: ffbf0018 sd ra, 24(sp) + //120000ce8: ffbe0010 sd s8, 16(sp) + //120000cec: ffbc0008 sd gp, 8(sp) + //120000cf0: 03a0f025 move s8, sp + + //120000d2c: 03c0e825 move sp, s8 //出栈 + //120000d30: dfbf0018 ld ra, 24(sp) + //120000d34: dfbe0010 ld s8, 16(sp) + //120000d38: dfbc0008 ld gp, 8(sp) + //120000d3c: 67bd0020 daddiu sp, sp, 32 + //120000d40: 03e00008 jr ra + + #define REPLACE_FAR(t, fn, fn_stub)\ + ((uint32_t *)fn)[0] = 0x67bdffe0;\ + ((uint32_t *)fn)[1] = 0xffbf0018;\ + ((uint32_t *)fn)[2] = 0xffbe0010;\ + ((uint32_t *)fn)[3] = 0xffbc0008;\ + ((uint32_t *)fn)[4] = 0x03a0f025;\ + *(uint16_t *)(fn + 20) = (long long)fn_stub >> 32;\ + *(fn + 22) = 0x19;\ + *(fn + 23) = 0x24;\ + ((uint32_t *)fn)[6] = 0x0019cc38;\ + *(uint16_t *)(fn + 28) = (long long)fn_stub >> 16;\ + *(fn + 30) = 0x39;\ + *(fn + 31) = 0x37;\ + ((uint32_t *)fn)[8] = 0x0019cc38;\ + *(uint16_t *)(fn + 36) = (long long)fn_stub;\ + *(fn + 38) = 0x39;\ + *(fn + 39) = 0x37;\ + ((uint32_t *)fn)[10] = 0x0320f809;\ + ((uint32_t *)fn)[11] = 0x00000000;\ + ((uint32_t *)fn)[12] = 0x00000000;\ + ((uint32_t *)fn)[13] = 0x03c0e825;\ + ((uint32_t *)fn)[14] = 0xdfbf0018;\ + ((uint32_t *)fn)[15] = 0xdfbe0010;\ + ((uint32_t *)fn)[16] = 0xdfbc0008;\ + ((uint32_t *)fn)[17] = 0x67bd0020;\ + ((uint32_t *)fn)[18] = 0x03e00008;\ + ((uint32_t *)fn)[19] = 0x00000000;\ + CACHEFLUSH((char *)fn, CODESIZE); + #define REPLACE_NEAR(t, fn, fn_stub) REPLACE_FAR(t, fn, fn_stub) +#elif defined(__thumb__) || defined(_M_THUMB) + #error "Thumb is not supported" +#else //__i386__ _x86_64__ + #define CODESIZE 13U + #define CODESIZE_MIN 5U + #define CODESIZE_MAX CODESIZE + //13 byte(jmp m16:64) + //movabs $0x102030405060708,%r11 + //jmpq *%r11 + static void REPLACE_FAR(void *t, char *fn, char *fn_stub) + { + *fn = 0x49; + *(fn + 1) = 0xbb; + *(long long *)(fn + 2) = (long long)fn_stub; + *(fn + 10) = 0x41; + *(fn + 11) = 0xff; + *(fn + 12) = 0xe3; + CACHEFLUSH((char *)fn, CODESIZE); + } + //5 byte(jmp rel32) + #define REPLACE_NEAR(t, fn, fn_stub)\ + *fn = 0xE9;\ + *(int *)(fn + 1) = (int)(fn_stub - fn - CODESIZE_MIN);\ + CACHEFLUSH((char *)fn, CODESIZE); +#endif + +struct func_stub +{ + char *fn; + unsigned char code_buf[CODESIZE]; + bool far_jmp; +}; + +class Stub +{ +public: + Stub() + { +#ifdef _WIN32 + SYSTEM_INFO sys_info; + GetSystemInfo(&sys_info); + m_pagesize = sys_info.dwPageSize; +#else + m_pagesize = sysconf(_SC_PAGE_SIZE); +#endif + + if (m_pagesize < 0) + { + m_pagesize = 4096; + } + } + ~Stub() + { + clear(); + } + + virtual void clear() + { + std::map::iterator iter; + struct func_stub *pstub; + for(iter=m_result.begin(); iter != m_result.end(); iter++) + { + pstub = iter->second; +#ifdef _WIN32 + DWORD lpflOldProtect; + if(0 != VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READWRITE, &lpflOldProtect)) +#else + if (0 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_WRITE | PROT_EXEC)) +#endif + { + + if(pstub->far_jmp) + { + std::memcpy(pstub->fn, pstub->code_buf, CODESIZE_MAX); + } + else + { + std::memcpy(pstub->fn, pstub->code_buf, CODESIZE_MIN); + } + +#ifdef _WIN32 + VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READ, &lpflOldProtect); +#else + CACHEFLUSH(pstub->fn,CODESIZE); + mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_EXEC); +#endif + } + + iter->second = NULL; + delete pstub; + } + + m_result.clear(); + return; + } + template + bool set(T addr, S addr_stub) + { + char * fn; + char * fn_stub; + fn = addrof(addr); + fn_stub = addrof(addr_stub); + struct func_stub *pstub; + std::map::iterator iter = m_result.find(fn); + + if (iter == m_result.end()) + { + pstub = new func_stub; + //start + pstub->fn = fn; + + if(distanceof(fn, fn_stub)) + { + pstub->far_jmp = true; + std::memcpy(pstub->code_buf, fn, CODESIZE_MAX); + } + else + { + pstub->far_jmp = false; + std::memcpy(pstub->code_buf, fn, CODESIZE_MIN); + } + } + else { + pstub = iter->second; + pstub->far_jmp = distanceof(fn, fn_stub); + } + + + +#ifdef _WIN32 + DWORD lpflOldProtect; + if(0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READWRITE, &lpflOldProtect)) +#else + if (-1 == mprotect(pageof(pstub->fn), static_cast(m_pagesize * 2), PROT_READ | PROT_WRITE | PROT_EXEC)) +#endif + { + throw("stub set memory protect to w+r+x faild"); + return false; + } + + if(pstub->far_jmp) + { + REPLACE_FAR(this, fn, fn_stub); + } + else + { + REPLACE_NEAR(this, fn, fn_stub); + } + + +#ifdef _WIN32 + if(0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READ, &lpflOldProtect)) +#else + if (-1 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_EXEC)) +#endif + { + throw("stub set memory protect to r+x failed"); + return false; + } + m_result.insert(std::pair(fn,pstub)); + return true; + } + + template + bool reset(T addr) + { + char * fn; + fn = addrof(addr); + + std::map::iterator iter = m_result.find(fn); + + if (iter == m_result.end()) + { + return true; + } + struct func_stub *pstub; + pstub = iter->second; + +#ifdef _WIN32 + DWORD lpflOldProtect; + if(0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READWRITE, &lpflOldProtect)) +#else + if (-1 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_WRITE | PROT_EXEC)) +#endif + { + throw("stub reset memory protect to w+r+x faild"); + return false; + } + + if(pstub->far_jmp) + { + std::memcpy(pstub->fn, pstub->code_buf, CODESIZE_MAX); + } + else + { + std::memcpy(pstub->fn, pstub->code_buf, CODESIZE_MIN); + } + +#ifdef _WIN32 + if(0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READ, &lpflOldProtect)) +#else + CACHEFLUSH(pstub->fn,CODESIZE); + if (-1 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_EXEC)) +#endif + { + throw("stub reset memory protect to r+x failed"); + return false; + } + + m_result.erase(iter); + delete pstub; + + return true; + } +protected: + char *pageof(char* addr) + { +#ifdef _WIN32 + return (char *)((unsigned long long)addr & ~(m_pagesize - 1)); +#else + return (char *)((unsigned long)addr & ~(m_pagesize - 1)); +#endif + } + + template + char* addrof(T addr) + { + union + { + T _s; + char* _d; + }ut; + ut._s = addr; + return ut._d; + } + + bool distanceof(char* addr, char* addr_stub) + { + std::ptrdiff_t diff = addr_stub >= addr ? addr_stub - addr : addr - addr_stub; + if((sizeof(addr) > 4) && (((diff >> 31) - 1) > 0)) + { + return true; + } + return false; + } + +protected: +#ifdef _WIN32 + //LLP64 + long long m_pagesize; +#else + //LP64 + long m_pagesize; +#endif + std::map m_result; +}; + +#endif diff --git a/deepin-devicemanager-server/tests/3rdparty/stub/stubext.h b/deepin-devicemanager-server/tests/3rdparty/stub/stubext.h new file mode 100644 index 000000000..a31d815d6 --- /dev/null +++ b/deepin-devicemanager-server/tests/3rdparty/stub/stubext.h @@ -0,0 +1,107 @@ +// SPDX-FileCopyrightText: 2021 - 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: MIT + +#ifndef STUBEXT_H +#define STUBEXT_H + +//需修改Stub的私用成员函数和成员变量为保护类型 +#include "stub.h" + +#include "stub-shadow.h" + +#ifdef DEBUG_STUB_INVOKE +// use to make sure the stub function is invoked. +# define __DBG_STUB_INVOKE__ printf("stub at %s:%d is invoked.\n", __FILE__, __LINE__); +#else +# define __DBG_STUB_INVOKE__ +#endif + +#define VADDR(CLASS_NAME, MEMBER_NAME) (typename stub_ext::VFLocator::Func)(&CLASS_NAME::MEMBER_NAME) + +namespace stub_ext { + +class StubExt : public Stub +{ +public: + StubExt() + : Stub() { } + + template + bool set_lamda(T addr, Lamda lamda) + { + char *fn = addrof(addr); + if (m_result.find(fn) != m_result.end()) + reset(addr); + + Wrapper *wrapper = nullptr; + auto addr_stub = depictShadow(&wrapper, addr, lamda); + if (set(addr, addr_stub)) { + m_wrappers.insert(std::make_pair(fn, wrapper)); + return true; + } else { + freeWrapper(wrapper); + } + return false; + } + + template + void reset(T addr) + { + Stub::reset(addr); + char *fn = addrof(addr); + auto iter = m_wrappers.find(fn); + if (iter != m_wrappers.end()) { + freeWrapper(iter->second); + m_wrappers.erase(iter); + } + } + + ~StubExt() + { + clear(); + } + + void clear() override + { + Stub::clear(); + for (auto iter = m_wrappers.begin(); iter != m_wrappers.end(); ++iter) { + freeWrapper(iter->second); + } + m_wrappers.clear(); + } + + template + static void *get_ctor_addr(bool start = true) + { + // the start vairable must be true, or the compiler will optimize out. + if (start) goto Start; + Call_Constructor: + // This line of code will not be executed. + // The purpose of the code is to allow the compiler to generate the assembly code that calls the constructor. + T(); + Start: + // The address of the line of code T() obtained by assembly + char *p = (char *)&&Call_Constructor; // https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html + // CALL rel32 + void *ret = 0; + char pos; + char call = 0xe8; + do { + pos = *p; + if (pos == call) { + ret = p + 5 + (*(int *)(p + 1)); + } + + } while (!ret && (++p)); + + return ret; + } + +protected: + std::map m_wrappers; +}; + +} + +#endif // STUBEXT_H diff --git a/deepin-devicemanager-server/tests/CMakeLists.txt b/deepin-devicemanager-server/tests/CMakeLists.txt index 1f724af64..de99fbea6 100644 --- a/deepin-devicemanager-server/tests/CMakeLists.txt +++ b/deepin-devicemanager-server/tests/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (C) 2019 ~ 2020 Uniontech Software Technology Co.,Ltd. -# SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +# Copyright (C) 2019-2026 Uniontech Software Technology Co.,Ltd. +# SPDX-FileCopyrightText: 2019-2026 UnionTech Software Technology Co., Ltd. # # SPDX-License-Identifier: GPL-3.0-or-later @@ -204,3 +204,43 @@ if(TARGET Qt6::Test) Qt6::Test ) endif() + +# ===================== [ut-t30] Server 热插拔与杂项测试补充(仅追加,不改动既有行) ===================== +# 1) stub-ext 标准打桩库(tests/3rdparty/stub,支持 set_lamda) +include_directories(${CMAKE_CURRENT_LIST_DIR}/3rdparty/stub) +target_sources(${PROJECT_NAME_TEST} PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/3rdparty/stub/stub-shadow.cpp +) +# 注:QSignalSpy 所需的 QtTest 模块已由上方 t24 补充段统一链接,此处不再重复。 + +# 2) plugin.cpp 在上方被移出测试目标(避免 main 符号冲突),此处重新纳入以覆盖 DSMRegister/DSMUnRegister +target_sources(${PROJECT_NAME_TEST} PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/../deepin-deviceinfo/src/plugin.cpp +) + +# 3) customgpuinfo/main.cpp 自带 main() 且仅依赖 Qt Core,使用独立测试二进制 +add_executable(customgpuinfo-test + ${CMAKE_CURRENT_LIST_DIR}/customgpuinfo/test_customgpuinfo_main.cpp +) +target_include_directories(customgpuinfo-test PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/3rdparty/stub +) +target_sources(customgpuinfo-test PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/3rdparty/stub/stub-shadow.cpp +) +if(${QT_VERSION_MAJOR} EQUAL 6) + target_link_libraries(customgpuinfo-test + Qt6::Core + ${GTEST_LIBRARIES} + ${GTEST_MAIN_LIBRARIES} + pthread + ) +elseif(${QT_VERSION_MAJOR} EQUAL 5) + target_link_libraries(customgpuinfo-test + Qt5::Core + ${GTEST_LIBRARIES} + ${GTEST_MAIN_LIBRARIES} + pthread + ) +endif() +set_property(TARGET customgpuinfo-test PROPERTY CXX_STANDARD 17) diff --git a/deepin-devicemanager-server/tests/gen-ut-summary.py b/deepin-devicemanager-server/tests/gen-ut-summary.py new file mode 100755 index 000000000..82c15a8c5 --- /dev/null +++ b/deepin-devicemanager-server/tests/gen-ut-summary.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +# SPDX-License-Identifier: GPL-3.0-or-later + +"""Generate ut-summary.json from gtest XML reports and lcov coverage data. + +Reads environment variables: + - projectdir: project root directory (required) + - builddir: build directory name, relative to projectdir (required) + - reportdir: report output directory name, relative to projectdir (required) + +Optional overrides (take precedence over defaults): + - GTEST_XML_DIR: directory containing gtest XML reports + - COVERAGE_INFO: path to lcov .info file for coverage parsing + +Output: {projectdir}/{reportdir}/ut-summary.json +""" + +import json +import xml.etree.ElementTree as ET +import glob +import os +import subprocess +import re +import sys + + +def parse_gtest_xml(xml_dir): + """Parse Google Test / JUnit XML output, return (total, passed, failed).""" + total = passed = failed = 0 + for xml_file in sorted(glob.glob(os.path.join(xml_dir, "*.xml"))): + try: + root = ET.parse(xml_file).getroot() + t = int(root.get("tests", 0)) + f_count = int(root.get("failures", 0)) + err_count = int(root.get("errors", 0)) + total += t + failed += f_count + err_count + passed += t - f_count - err_count + except Exception as e: + print(f"Warning: failed to parse {xml_file}: {e}", file=sys.stderr) + return total, passed, failed + + +def parse_lcov_summary(coverage_info): + """Parse lcov --summary output, return coverage dict.""" + result = {} + if not os.path.exists(coverage_info): + print(f"Warning: coverage info file not found: {coverage_info}", file=sys.stderr) + return result + + lcov_out = subprocess.run( + ["lcov", "--summary", coverage_info, "--rc", "lcov_branch_coverage=1"], + capture_output=True, text=True + ) + summary_text = lcov_out.stdout + lcov_out.stderr + + m_lines = re.search(r'lines.*?:\s*([\d.]+)%\s*\((\d+)\s+of\s+(\d+)\s+\w+\)', summary_text) + if m_lines: + pct, hit, total = m_lines.groups() + result["line_coverage"] = { + "total": int(total), + "passed": int(hit), + "failed": int(total) - int(hit), + "coverage": f"{float(pct):.2f}%" + } + + m_func = re.search(r'functions.*?:\s*([\d.]+)%\s*\((\d+)\s+of\s+(\d+)\s+\w+\)', summary_text) + if m_func: + pct, hit, total = m_func.groups() + result["function_coverage"] = { + "total": int(total), + "passed": int(hit), + "failed": int(total) - int(hit), + "coverage": f"{float(pct):.2f}%" + } + + return result + + +def main(): + projectdir = os.environ.get("projectdir") + builddir = os.environ.get("builddir") + reportdir = os.environ.get("reportdir") + + if not all([projectdir, builddir, reportdir]): + print("Error: environment variables projectdir, builddir, reportdir are required", file=sys.stderr) + sys.exit(1) + + gtest_dir = os.environ.get("GTEST_XML_DIR", + os.path.join(projectdir, builddir, "report")) + coverage_info = os.environ.get("COVERAGE_INFO", + os.path.join(projectdir, builddir, "coverage.info")) + + total, passed, failed = parse_gtest_xml(gtest_dir) + + result = { + "test_cases": { + "total": total, + "passed": passed, + "failed": failed + } + } + + coverage_data = parse_lcov_summary(coverage_info) + result.update(coverage_data) + + output_path = os.path.join(projectdir, reportdir, "ut-summary.json") + os.makedirs(os.path.dirname(output_path), exist_ok=True) + with open(output_path, "w") as f: + json.dump(result, f, indent=2) + + print(json.dumps(result, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/deepin-devicemanager-server/tests/lsan_suppressions.txt b/deepin-devicemanager-server/tests/lsan_suppressions.txt new file mode 100644 index 000000000..cb6a4c578 --- /dev/null +++ b/deepin-devicemanager-server/tests/lsan_suppressions.txt @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +# SPDX-License-Identifier: GPL-3.0-or-later +# +# LSan suppressions for unit tests +# Suppress known false-positive leaks from Qt/DTK frameworks. +# Add project-specific suppressions below. +# +# Syntax: leak: +# +# Qt6 DBus / event-loop deferred cleanup (freed at process exit, reported as leak) +leak:QDBusConnectionManager +leak:QDBusServiceWatcher +leak:QMetaType +leak:QFactoryLoader diff --git a/deepin-devicemanager-server/tests/run-ut.sh b/deepin-devicemanager-server/tests/run-ut.sh new file mode 100755 index 000000000..5ca8417af --- /dev/null +++ b/deepin-devicemanager-server/tests/run-ut.sh @@ -0,0 +1,287 @@ +#!/bin/bash +# SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +# SPDX-License-Identifier: GPL-3.0-or-later + +################################################################################ +# Unit Test Runner Script +# 一键流程:编译 → 直接运行 gtest 二进制(per-target XML) → lcov 覆盖率 → genhtml → ut-summary.json +# 特性:ASAN/LSan 泄漏检测、CMAKE_SAFETYTEST_ARG、headless(Qt offscreen)、--from-step 断点续跑 +################################################################################ + +set -e + +# 测试目录名(框架搭建时确定) +TEST_DIR="tests" + +RED='\033[0;31m' +GREEN='\033[0;32m' +BLUE='\033[0;34m' +YELLOW='\033[1;33m' +NC='\033[0m' + +print_step() { echo -e "${BLUE}[STEP $1]${NC} $2"; } +print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; } +print_error() { echo -e "${RED}[ERROR]${NC} $1"; } +print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; } +print_info() { echo -e "${BLUE}[INFO]${NC} $1"; } + +show_usage() { + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " --from-step Start from step N (1-6)" + echo " --parallel Parallel build jobs (default: $(nproc))" + echo " -h, --help Show this help" + echo "" + echo "Steps:" + echo " 1. Prepare build env" + echo " 2. Configure CMake" + echo " 3. Compile tests" + echo " 4. Run unit tests (per-target gtest XML)" + echo " 5. Generate coverage report (lcov + genhtml)" + echo " 6. Generate ut-summary.json" + echo "" + echo "Examples:" + echo " $0 # Run all steps" + echo " $0 --from-step 4 # Skip build, run tests + coverage" + echo " $0 --from-step 6 # Only regenerate summary from existing data" +} + +# Parse args +START_STEP=1 +PARALLEL_JOBS=$(nproc) +while [[ $# -gt 0 ]]; do + case $1 in + --from-step) + START_STEP="$2" + if ! [[ "$START_STEP" =~ ^[1-6]$ ]]; then + print_error "Invalid step: $START_STEP (must be 1-6)" + exit 1 + fi + shift 2 ;; + --parallel) + PARALLEL_JOBS="$2" + if ! [[ "$PARALLEL_JOBS" =~ ^[1-9][0-9]*$ ]]; then + print_error "Invalid parallel: $PARALLEL_JOBS (positive integer)" + exit 1 + fi + shift 2 ;; + -h|--help) show_usage; exit 0 ;; + *) print_error "Unknown option: $1"; show_usage; exit 1 ;; + esac +done + +# Directories +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +BUILD_DIR="$PROJECT_ROOT/build-${TEST_DIR}" +REPORT_DIR="$BUILD_DIR/test-reports" +GTEST_XML_DIR="$BUILD_DIR/report" + +# Headless Qt (CI 友好) +export QT_QPA_PLATFORM=${QT_QPA_PLATFORM:-offscreen} + +# ASAN/LSan:启用泄漏检测 + 抑制 Qt/DTK 框架误报(抑制文件缺失时跳过,不让 LSan 因文件不存在报错) +export ASAN_OPTIONS=${ASAN_OPTIONS:-detect_leaks=1} +if [ -f "$SCRIPT_DIR/lsan_suppressions.txt" ]; then + export LSAN_OPTIONS=suppressions="$SCRIPT_DIR/lsan_suppressions.txt" +fi + +# State +TEST_PASSED=false +TEST_EXIT_CODE=0 +COVERAGE_SUCCESS=false + +echo "========================================" +echo " Unit Test Runner" +echo "========================================" +echo "Project root: $PROJECT_ROOT" +echo "Build dir: $BUILD_DIR" +echo "Reports: $REPORT_DIR" +echo "Parallel jobs: $PARALLEL_JOBS" +[ "$START_STEP" -gt 1 ] && print_info "Starting from step $START_STEP" +echo "" + +step_1_prepare_build_env() { + print_step 1 "Preparing build environment..." + rm -rf "$BUILD_DIR" + mkdir -p "$BUILD_DIR" "$REPORT_DIR" + + # Sync stub-ext from skill templates(生成时烙入技能模板路径;仓库内不必随附 vendored 副本) + STUB_SRC="/home/ut005769@uos/.agents/skills/qt-autotest-generator/scripts/../templates/stub-ext" + STUB_DST="$SCRIPT_DIR/3rdparty/stub" + if [ -d "$STUB_DST" ]; then + print_info "stub-ext already present: $STUB_DST" + elif [ -d "$STUB_SRC" ]; then + mkdir -p "$STUB_DST" + cp -r "$STUB_SRC/." "$STUB_DST/" + print_info "Synced stub-ext from skill templates" + else + # 仅警告不阻断:只有测试 CMake 实际引用 stub 时才会在编译期报错 + print_warning "stub-ext source not found: $STUB_SRC" + print_warning "Manual copy: cp -r /templates/stub-ext/ ${TEST_DIR}/3rdparty/stub/" + fi + + print_success "Build environment prepared" +} + +step_2_configure_cmake() { + print_step 2 "Configuring CMake..." + mkdir -p "$BUILD_DIR" + cd "$BUILD_DIR" + # 注意:cmake 源指向项目根(CMAKE_SOURCE_DIR=项目根),而非 autotests 目录 + # 覆盖率插桩用命令行 cache 变量全局注入(与 Mode 3 coverage-report.py 同机制): + # autotests/CMakeLists.txt 内的 set(CMAKE_CXX_FLAGS) 只作用 autotests 子树, + # 影响不到先于 add_subdirectory(autotests) 定义的 src/ 业务目标 → 必须在 configure 期全局传 + cmake "$PROJECT_ROOT" \ + -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ + -DBUILD_TESTS=ON \ + -DCMAKE_SAFETYTEST_ARG="CMAKE_SAFETYTEST_ARG_ON" \ + -DCMAKE_C_FLAGS="--coverage" \ + -DCMAKE_CXX_FLAGS="--coverage" \ + -DCMAKE_EXE_LINKER_FLAGS="--coverage" \ + -DCMAKE_SHARED_LINKER_FLAGS="--coverage" + print_success "CMake configuration completed" +} + +step_3_compile_tests() { + print_step 3 "Compiling tests..." + cd "$BUILD_DIR" + cmake --build . -j "$PARALLEL_JOBS" + print_success "Compilation completed" +} + +# 发现 gtest 二进制:优先 {test_dir}/src,回退到 build 目录递归 +discover_test_binaries() { + local found + found=$(find "$BUILD_DIR/$TEST_DIR/src" -maxdepth 1 -type f -executable -name "test_*" 2>/dev/null | sort) + if [ -z "$found" ]; then + found=$(find "$BUILD_DIR" -maxdepth 4 -type f -executable -name "test_*" -not -name "*.so" -not -name "*.a" -not -name "*.cmake" 2>/dev/null | sort) + fi + echo "$found" +} + +step_4_run_tests() { + print_step 4 "Running unit tests..." + mkdir -p "$GTEST_XML_DIR" + TEST_START_TIME=$(date +%s) + TEST_PASSED=true + TEST_EXIT_CODE=0 + + local binaries + binaries=$(discover_test_binaries) + if [ -z "$binaries" ]; then + print_error "No test binaries found under $BUILD_DIR (did you compile?)" + TEST_PASSED=false + TEST_EXIT_CODE=1 + return + fi + + while IFS= read -r binary; do + [ -z "$binary" ] && continue + local target + target=$(basename "$binary") + echo "==> Running $target ..." + set +e + "$binary" --gtest_output="xml:$GTEST_XML_DIR/report_${target}.xml" + local ec=$? + set -e + if [ $ec -ne 0 ]; then + print_error "$target FAILED (exit $ec)" + TEST_PASSED=false + TEST_EXIT_CODE=$ec + else + print_success "$target PASSED" + fi + done <<< "$binaries" + + TEST_END_TIME=$(date +%s) + print_info "Test execution completed in $((TEST_END_TIME - TEST_START_TIME))s" +} + +step_5_generate_coverage() { + print_step 5 "Generating coverage report..." + if ! command -v lcov &> /dev/null; then + print_warning "lcov not installed, skipping coverage" + COVERAGE_SUCCESS=false + return + fi + + set +e + mkdir -p "$BUILD_DIR/html" + # 采集 → 仅保留业务源码 → 排除测试/第三方 → genhtml + lcov -d "$BUILD_DIR" -c -o "$BUILD_DIR/coverage.info" > "$REPORT_DIR/coverage_output.log" 2>&1 || true + lcov --extract "$BUILD_DIR/coverage.info" '*/src/*' -o "$BUILD_DIR/coverage.info" >> "$REPORT_DIR/coverage_output.log" 2>&1 || true + lcov --remove "$BUILD_DIR/coverage.info" '*/test*' '*/'"${TEST_DIR}"'/*' '*/3rdparty/*' -o "$BUILD_DIR/coverage.info" >> "$REPORT_DIR/coverage_output.log" 2>&1 || true + genhtml -o "$BUILD_DIR/html" --title "Coverage Report" --show-details --legend "$BUILD_DIR/coverage.info" >> "$REPORT_DIR/coverage_output.log" 2>&1 + local genhtml_rc=$? + if [ $genhtml_rc -eq 0 ] && [ -s "$BUILD_DIR/coverage.info" ]; then + print_success "Coverage report generated" + COVERAGE_SUCCESS=true + print_success "Coverage HTML: file://$BUILD_DIR/html/index.html" + else + print_warning "Coverage report generation failed (see $REPORT_DIR/coverage_output.log)" + COVERAGE_SUCCESS=false + fi + set -e +} + +step_6_generate_summary() { + print_step 6 "Generating ut-summary.json..." + if ! command -v python3 &> /dev/null; then + print_warning "Python 3 not installed, skipping summary" + return + fi + + # 轻量解析:从已有 gtest XML + lcov summary 生成 JSON,不重跑测试 + export projectdir="$PROJECT_ROOT" + export builddir="build-${TEST_DIR}" + export reportdir="build-${TEST_DIR}" + export GTEST_XML_DIR="$GTEST_XML_DIR" + export COVERAGE_INFO="$BUILD_DIR/coverage.info" + + if python3 "$SCRIPT_DIR/gen-ut-summary.py"; then + print_success "Summary: file://$BUILD_DIR/ut-summary.json" + # 若从 step 6 起,依据 summary 推断测试是否通过 + if [ "$START_STEP" -eq 6 ]; then + local failed + failed=$(python3 -c "import json;print(json.load(open('$BUILD_DIR/ut-summary.json'))['test_cases']['failed'])" 2>/dev/null || echo 1) + [ "$failed" = "0" ] && TEST_PASSED=true || TEST_PASSED=false + fi + else + print_warning "Summary generation failed" + fi +} + +# Execute steps based on START_STEP +case $START_STEP in + 1) step_1_prepare_build_env; step_2_configure_cmake; step_3_compile_tests; step_4_run_tests; step_5_generate_coverage; step_6_generate_summary ;; + 2) mkdir -p "$BUILD_DIR" "$REPORT_DIR"; step_2_configure_cmake; step_3_compile_tests; step_4_run_tests; step_5_generate_coverage; step_6_generate_summary ;; + 3) mkdir -p "$BUILD_DIR" "$REPORT_DIR"; step_3_compile_tests; step_4_run_tests; step_5_generate_coverage; step_6_generate_summary ;; + 4) mkdir -p "$REPORT_DIR"; step_4_run_tests; step_5_generate_coverage; step_6_generate_summary ;; + 5) mkdir -p "$REPORT_DIR"; step_5_generate_coverage; step_6_generate_summary ;; + 6) mkdir -p "$REPORT_DIR"; step_6_generate_summary ;; +esac + +# 收集 ASAN 日志(若存在;无崩溃时不产生) +cp "$BUILD_DIR"/asan*.log* "$REPORT_DIR/asan.log" 2>/dev/null || true + +echo "" +echo "========================================" +if [ "$TEST_PASSED" = true ]; then + print_success "Unit test execution completed!" +else + print_error "Unit tests have failures" +fi +echo "" +echo "Generated artifacts:" +echo " gtest XML: $GTEST_XML_DIR/" +echo " coverage HTML: $BUILD_DIR/html/index.html" +echo " summary JSON: $BUILD_DIR/ut-summary.json" +echo " logs: $REPORT_DIR/" +echo "========================================" + +if [ "$TEST_PASSED" != true ]; then + exit 1 +fi diff --git a/deepin-devicemanager-server/tests/src/stub.h b/deepin-devicemanager-server/tests/src/stub.h index 6ec33111e..7b39caedb 100644 --- a/deepin-devicemanager-server/tests/src/stub.h +++ b/deepin-devicemanager-server/tests/src/stub.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019 ~ 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2019-2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -12,6 +12,7 @@ #else //linux #include +#include #include #endif //c @@ -20,7 +21,8 @@ //c++ #include -#define ADDR(CLASS_NAME, MEMBER_NAME) (&CLASS_NAME::MEMBER_NAME) + +#define ADDR(CLASS_NAME,MEMBER_NAME) (&CLASS_NAME::MEMBER_NAME) /********************************************************** replace function @@ -32,54 +34,104 @@ #endif #if defined(__aarch64__) || defined(_M_ARM64) -#define CODESIZE 16U -#define CODESIZE_MIN 16U -#define CODESIZE_MAX CODESIZE -// ldr x9, +8 -// br x9 -// addr -#define REPLACE_FAR(t, fn, fn_stub) \ - ((uint32_t *)fn)[0] = 0x58000040 | 9; \ - ((uint32_t *)fn)[1] = 0xd61f0120 | (9 << 5); \ - *(long long *)(fn + 8) = (long long)fn_stub; \ - CACHEFLUSH((char *)fn, CODESIZE); -#define REPLACE_NEAR(t, fn, fn_stub) REPLACE_FAR(t, fn, fn_stub) + #define CODESIZE 16U + #define CODESIZE_MIN 16U + #define CODESIZE_MAX CODESIZE + // ldr x9, +8 + // br x9 + // addr + #define REPLACE_FAR(t, fn, fn_stub)\ + ((uint32_t*)fn)[0] = 0x58000040 | 9;\ + ((uint32_t*)fn)[1] = 0xd61f0120 | (9 << 5);\ + *(long long *)(fn + 8) = (long long )fn_stub;\ + CACHEFLUSH((char *)fn, CODESIZE); + #define REPLACE_NEAR(t, fn, fn_stub) REPLACE_FAR(t, fn, fn_stub) #elif defined(__arm__) || defined(_M_ARM) -#define CODESIZE 8U -#define CODESIZE_MIN 8U -#define CODESIZE_MAX CODESIZE -// ldr pc, [pc, #-4] -#define REPLACE_FAR(t, fn, fn_stub) \ - ((uint32_t *)fn)[0] = 0xe51ff004; \ - ((uint32_t *)fn)[1] = (uint32_t)fn_stub; \ - CACHEFLUSH((char *)fn, CODESIZE); -#define REPLACE_NEAR(t, fn, fn_stub) REPLACE_FAR(t, fn, fn_stub) + #define CODESIZE 8U + #define CODESIZE_MIN 8U + #define CODESIZE_MAX CODESIZE + // ldr pc, [pc, #-4] + #define REPLACE_FAR(t, fn, fn_stub)\ + ((uint32_t*)fn)[0] = 0xe51ff004;\ + ((uint32_t*)fn)[1] = (uint32_t)fn_stub;\ + CACHEFLUSH((char *)fn, CODESIZE); + #define REPLACE_NEAR(t, fn, fn_stub) REPLACE_FAR(t, fn, fn_stub) +#elif defined(__mips64) + #define CACHEFLUSH(addr, size) __builtin___clear_cache(addr, addr + size) + #define CODESIZE 80U + #define CODESIZE_MIN 80U + #define CODESIZE_MAX CODESIZE + //mips没有PC指针,所以需要手动入栈出栈 + //120000ce0: 67bdffe0 daddiu sp, sp, -32 //入栈 + //120000ce4: ffbf0018 sd ra, 24(sp) + //120000ce8: ffbe0010 sd s8, 16(sp) + //120000cec: ffbc0008 sd gp, 8(sp) + //120000cf0: 03a0f025 move s8, sp + + //120000d2c: 03c0e825 move sp, s8 //出栈 + //120000d30: dfbf0018 ld ra, 24(sp) + //120000d34: dfbe0010 ld s8, 16(sp) + //120000d38: dfbc0008 ld gp, 8(sp) + //120000d3c: 67bd0020 daddiu sp, sp, 32 + //120000d40: 03e00008 jr ra + + #define REPLACE_FAR(t, fn, fn_stub)\ + ((uint32_t *)fn)[0] = 0x67bdffe0;\ + ((uint32_t *)fn)[1] = 0xffbf0018;\ + ((uint32_t *)fn)[2] = 0xffbe0010;\ + ((uint32_t *)fn)[3] = 0xffbc0008;\ + ((uint32_t *)fn)[4] = 0x03a0f025;\ + *(uint16_t *)(fn + 20) = (long long)fn_stub >> 32;\ + *(fn + 22) = 0x19;\ + *(fn + 23) = 0x24;\ + ((uint32_t *)fn)[6] = 0x0019cc38;\ + *(uint16_t *)(fn + 28) = (long long)fn_stub >> 16;\ + *(fn + 30) = 0x39;\ + *(fn + 31) = 0x37;\ + ((uint32_t *)fn)[8] = 0x0019cc38;\ + *(uint16_t *)(fn + 36) = (long long)fn_stub;\ + *(fn + 38) = 0x39;\ + *(fn + 39) = 0x37;\ + ((uint32_t *)fn)[10] = 0x0320f809;\ + ((uint32_t *)fn)[11] = 0x00000000;\ + ((uint32_t *)fn)[12] = 0x00000000;\ + ((uint32_t *)fn)[13] = 0x03c0e825;\ + ((uint32_t *)fn)[14] = 0xdfbf0018;\ + ((uint32_t *)fn)[15] = 0xdfbe0010;\ + ((uint32_t *)fn)[16] = 0xdfbc0008;\ + ((uint32_t *)fn)[17] = 0x67bd0020;\ + ((uint32_t *)fn)[18] = 0x03e00008;\ + ((uint32_t *)fn)[19] = 0x00000000;\ + CACHEFLUSH((char *)fn, CODESIZE); + #define REPLACE_NEAR(t, fn, fn_stub) REPLACE_FAR(t, fn, fn_stub) #elif defined(__thumb__) || defined(_M_THUMB) -#error "Thumb is not supported" + #error "Thumb is not supported" #else //__i386__ _x86_64__ -#define CODESIZE 13U -#define CODESIZE_MIN 5U -#define CODESIZE_MAX CODESIZE -//13 byte(jmp m16:64) -//movabs $0x102030405060708,%r11 -//jmpq *%r11 -#define REPLACE_FAR(t, fn, fn_stub) \ - *fn = 0x49; \ - *(fn + 1) = 0xbb; \ - *(long long *)(fn + 2) = (long long)fn_stub; \ - *(fn + 10) = 0x41; \ - *(fn + 11) = 0xff; \ - *(fn + 12) = 0xe3; \ - //CACHEFLUSH((char *)fn, CODESIZE); - -//5 byte(jmp rel32) -#define REPLACE_NEAR(t, fn, fn_stub) \ - *fn = 0xE9; \ - *(int *)(fn + 1) = (int)(fn_stub - fn - CODESIZE_MIN); \ - //CACHEFLUSH((char *)fn, CODESIZE); + #define CODESIZE 13U + #define CODESIZE_MIN 5U + #define CODESIZE_MAX CODESIZE + //13 byte(jmp m16:64) + //movabs $0x102030405060708,%r11 + //jmpq *%r11 + static void REPLACE_FAR(void *t, char *fn, char *fn_stub) + { + *fn = 0x49; + *(fn + 1) = 0xbb; + *(long long *)(fn + 2) = (long long)fn_stub; + *(fn + 10) = 0x41; + *(fn + 11) = 0xff; + *(fn + 12) = 0xe3; + CACHEFLUSH((char *)fn, CODESIZE); + } + //5 byte(jmp rel32) + #define REPLACE_NEAR(t, fn, fn_stub)\ + *fn = 0xE9;\ + *(int *)(fn + 1) = (int)(fn_stub - fn - CODESIZE_MIN);\ + CACHEFLUSH((char *)fn, CODESIZE); #endif -struct func_stub { +struct func_stub +{ char *fn; unsigned char code_buf[CODESIZE]; bool far_jmp; @@ -98,137 +150,176 @@ class Stub m_pagesize = sysconf(_SC_PAGE_SIZE); #endif - if (m_pagesize < 0) { + if (m_pagesize < 0) + { m_pagesize = 4096; } } ~Stub() { - std::map::iterator iter; + clear(); + } + + virtual void clear() + { + std::map::iterator iter; struct func_stub *pstub; - for (iter = m_result.begin(); iter != m_result.end(); iter++) { + for(iter=m_result.begin(); iter != m_result.end(); iter++) + { pstub = iter->second; #ifdef _WIN32 DWORD lpflOldProtect; - if (0 != VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READWRITE, &lpflOldProtect)) + if(0 != VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READWRITE, &lpflOldProtect)) #else if (0 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_WRITE | PROT_EXEC)) #endif { - if (pstub->far_jmp) { + if(pstub->far_jmp) + { std::memcpy(pstub->fn, pstub->code_buf, CODESIZE_MAX); - } else { + } + else + { std::memcpy(pstub->fn, pstub->code_buf, CODESIZE_MIN); } #ifdef _WIN32 VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READ, &lpflOldProtect); #else + CACHEFLUSH(pstub->fn,CODESIZE); mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_EXEC); #endif } - iter->second = NULL; + iter->second = NULL; delete pstub; } + m_result.clear(); return; } - template - void set(T addr, S addr_stub) + template + bool set(T addr, S addr_stub) { - char *fn; - char *fn_stub; + char * fn; + char * fn_stub; fn = addrof(addr); fn_stub = addrof(addr_stub); struct func_stub *pstub; - pstub = new func_stub; - //start - pstub->fn = fn; - - if (distanceof(fn, fn_stub)) { - pstub->far_jmp = true; - std::memcpy(pstub->code_buf, fn, CODESIZE_MAX); - } else { - pstub->far_jmp = false; - std::memcpy(pstub->code_buf, fn, CODESIZE_MIN); + std::map::iterator iter = m_result.find(fn); + + if (iter == m_result.end()) + { + pstub = new func_stub; + //start + pstub->fn = fn; + + if(distanceof(fn, fn_stub)) + { + pstub->far_jmp = true; + std::memcpy(pstub->code_buf, fn, CODESIZE_MAX); + } + else + { + pstub->far_jmp = false; + std::memcpy(pstub->code_buf, fn, CODESIZE_MIN); + } } + else { + pstub = iter->second; + pstub->far_jmp = distanceof(fn, fn_stub); + } + + #ifdef _WIN32 DWORD lpflOldProtect; - if (0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READWRITE, &lpflOldProtect)) + if(0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READWRITE, &lpflOldProtect)) #else - if (-1 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_WRITE | PROT_EXEC)) + if (-1 == mprotect(pageof(pstub->fn), static_cast(m_pagesize * 2), PROT_READ | PROT_WRITE | PROT_EXEC)) #endif { throw("stub set memory protect to w+r+x faild"); + return false; } - if (pstub->far_jmp) { + if(pstub->far_jmp) + { REPLACE_FAR(this, fn, fn_stub); - } else { + } + else + { REPLACE_NEAR(this, fn, fn_stub); } + #ifdef _WIN32 - if (0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READ, &lpflOldProtect)) + if(0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READ, &lpflOldProtect)) #else if (-1 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_EXEC)) #endif { throw("stub set memory protect to r+x failed"); + return false; } - m_result.insert(std::pair(fn, pstub)); - return; + m_result.insert(std::pair(fn,pstub)); + return true; } template - void reset(T addr) + bool reset(T addr) { - char *fn; + char * fn; fn = addrof(addr); - std::map::iterator iter = m_result.find(fn); + std::map::iterator iter = m_result.find(fn); - if (iter == m_result.end()) { - return; + if (iter == m_result.end()) + { + return true; } struct func_stub *pstub; pstub = iter->second; #ifdef _WIN32 DWORD lpflOldProtect; - if (0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READWRITE, &lpflOldProtect)) + if(0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READWRITE, &lpflOldProtect)) #else if (-1 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_WRITE | PROT_EXEC)) #endif { throw("stub reset memory protect to w+r+x faild"); + return false; } - if (pstub->far_jmp) { + if(pstub->far_jmp) + { std::memcpy(pstub->fn, pstub->code_buf, CODESIZE_MAX); - } else { + } + else + { std::memcpy(pstub->fn, pstub->code_buf, CODESIZE_MIN); } #ifdef _WIN32 - if (0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READ, &lpflOldProtect)) + if(0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READ, &lpflOldProtect)) #else + CACHEFLUSH(pstub->fn,CODESIZE); if (-1 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_EXEC)) #endif { throw("stub reset memory protect to r+x failed"); + return false; } + m_result.erase(iter); delete pstub; - return; + return true; } - -private: - char *pageof(char *addr) +protected: + char *pageof(char* addr) { #ifdef _WIN32 return (char *)((unsigned long long)addr & ~(m_pagesize - 1)); @@ -238,26 +329,28 @@ class Stub } template - char *addrof(T addr) + char* addrof(T addr) { - union { - T _s; - char *_d; - } ut; + union + { + T _s; + char* _d; + }ut; ut._s = addr; return ut._d; } - bool distanceof(char *addr, char *addr_stub) + bool distanceof(char* addr, char* addr_stub) { std::ptrdiff_t diff = addr_stub >= addr ? addr_stub - addr : addr - addr_stub; - if ((sizeof(addr) > 4) && (((diff >> 31) - 1) > 0)) { + if((sizeof(addr) > 4) && (((diff >> 31) - 1) > 0)) + { return true; } return false; } -private: +protected: #ifdef _WIN32 //LLP64 long long m_pagesize; @@ -265,7 +358,7 @@ class Stub //LP64 long m_pagesize; #endif - std::map m_result; + std::map m_result; }; #endif From 409660fc9fd4ce180da89f5bcf40c2c699b2919e Mon Sep 17 00:00:00 2001 From: zhanghongyuan Date: Mon, 26 Oct 2026 20:12:58 +0800 Subject: [PATCH 2/8] =?UTF-8?q?test:=20=E9=87=8D=E5=86=99=20DetectThread?= =?UTF-8?q?=20=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95=E5=B9=B6=E8=A1=A5?= =?UTF-8?q?=E9=BD=90=20AAA=20=E7=BB=93=E6=9E=84=E4=B8=8E=E6=96=AD=E8=A8=80?= =?UTF-8?q?=E5=BC=BA=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 26 用例覆盖构造初始缓存、curHwinfoUsbInfo(base64(MD5) 唯一键生成、hub 过滤、行数不足跳过、SysFS Device Link 优先)、getMapInfo、isUsbDevicesChanged、run、setWorkingFlag、slotUsbChanged(信号转发/超时窗口)、updateMemUsbInfo - 每用例注入 // Arrange/Act/Assert 三段注释,每用例 ≥2 有效断言,顶部附用例计数声明表格 - hwinfo 输出、QProcess、定时器等外部依赖全部打桩,不依赖真实设备 --- .../tests/src/UsbDetect/ut_detectthread.cpp | 662 +++++++++++++++++- 1 file changed, 645 insertions(+), 17 deletions(-) diff --git a/deepin-devicemanager-server/tests/src/UsbDetect/ut_detectthread.cpp b/deepin-devicemanager-server/tests/src/UsbDetect/ut_detectthread.cpp index 64064c51c..9811309ac 100644 --- a/deepin-devicemanager-server/tests/src/UsbDetect/ut_detectthread.cpp +++ b/deepin-devicemanager-server/tests/src/UsbDetect/ut_detectthread.cpp @@ -1,35 +1,663 @@ -// SPDX-FileCopyrightText: 2019 ~ 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later +/* + * 用例计数声明(method | level | factors | min | actual) + * | DetectThread::DetectThread | mid | concurrent_class | 2 | 2 | + * | DetectThread::curHwinfoUsbInfo | high | concurrent_class,complexity:5,lines:53,in_degree:2 | 3 | 7 | + * | DetectThread::getMapInfo | mid | concurrent_class | 2 | 4 | + * | DetectThread::isUsbDevicesChanged | mid | concurrent_class | 2 | 5 | + * | DetectThread::run | mid | concurrent_class | 2 | 2 | + * | DetectThread::setWorkingFlag | mid | concurrent_class | 2 | 2 | + * | DetectThread::slotUsbChanged | mid | concurrent_class | 2 | 2 | + * | DetectThread::updateMemUsbInfo | mid | concurrent_class,in_degree:3 | 2 | 2 | + */ +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "stubext.h" #include "detectthread.h" #include "monitorusb.h" -#include "../ut_Head.h" -#include -#include "../stub.h" -class DetectThread_UT : public UT_HEAD +using namespace stub_ext; + +namespace { +// detectthread.cpp:13 定义 LEAST_NUM == 10,getMapInfo 要求行数大于 10 才解析 +constexpr int kLeastNum = 10; +// 伪造的 hwinfo 输出中 mock 的 SysFS 路径 +const QString kMouseSysFsId = "/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0"; +const QString kMouseSysFsLink = "/devices/pci0000:00/0000:00:14.0/usb1/1-1"; +const QString kKeyboardSysFsId = "/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.0"; +const QString kKeyboardSysFsLink = "/devices/pci0000:00/0000:00:14.0/usb1/1-2"; + +// hwinfo --usb 输出条目风格:12 行(> LEAST_NUM),键值以 ": " 分隔 +QString mouseItem(bool withLink = true) +{ + QString item = + "03: USB 00.0\n" + " Device: usb 0x7523\n" + " Vendor: usb 0x1a86\n" + " Hardware Class: mouse\n" + " SysFS ID: " + kMouseSysFsId + "\n"; + if (withLink) + item += " SysFS Device Link: " + kMouseSysFsLink + "\n"; + item += + " Model: \"CH340 USB Mouse\"\n" + " Hotplug: USB\n" + " Revision: 4.00\n" + " Serial ID: \"0001\"\n" + " Speed: 12Mbps\n" + " Modules: usbhid hid-generic\n"; + return item; +} + +QString keyboardItem() +{ + return + "04: USB 00.0\n" + " Device: usb 0xc52b\n" + " Vendor: usb 0x046d\n" + " Hardware Class: keyboard\n" + " SysFS ID: " + kKeyboardSysFsId + "\n" + " SysFS Device Link: " + kKeyboardSysFsLink + "\n" + " Model: \"Logitech K380\"\n" + " Hotplug: USB\n" + " Revision: 1.00\n" + " Serial ID: \"0002\"\n" + " Speed: 12Mbps\n" + " Modules: hid-generic\n"; +} + +QString diskItem(bool withCapacity) +{ + QString item = + "05: USB 00.0\n" + " Device: usb 0x1666\n" + " Vendor: usb 0x0951\n" + " Hardware Class: disk\n" + " SysFS ID: /devices/pci0000:00/0000:00:14.0/usb1/1-3\n" + " SysFS Device Link: /devices/pci0000:00/0000:00:14.0/usb1/1-3\n" + " Model: \"USB Disk\"\n" + " Hotplug: USB\n" + " Revision: 1.00\n" + " Serial ID: \"0003\"\n" + " Speed: 480Mbps\n"; + if (withCapacity) + item += " Capacity: 16GB\n"; + return item; +} + +// 按真实实现计算唯一 ID:base64(MD5(vendor+device 拼接值 + SysFS 路径)) +QString expectedUniqueId(const QString &vendor, const QString &device, const QString &sysfs) +{ + QString devToken = device.split(" ").at(1); + devToken.remove("0x", Qt::CaseSensitive); + QString valueStr = vendor.split(" ").at(1).trimmed() + devToken.trimmed(); + QByteArray buf; + buf.append(valueStr.toUtf8()); + buf.append(sysfs.trimmed().toUtf8()); + return QString::fromStdString( + QCryptographicHash::hash(buf, QCryptographicHash::Md5).toBase64().toStdString()); +} + +// 构造 isUsbDevicesChanged 测试用的设备缓存项 +QMap> deviceMap(const QString &key, const QString &hwClass, + bool withCapacity = false) { -public: - void SetUp() + QMap> result; + QMap info; + info.insert("Hardware Class", hwClass); + if (withCapacity) + info.insert("Capacity", "16GB"); + result.insert(key, info); + return result; +} +} // namespace + +class DetectThreadTest : public ::testing::Test +{ +protected: + void SetUp() override + { + stub.clear(); + stubQProcess(); + } + + void TearDown() override { - m_thread = new DetectThread(nullptr); + stub.clear(); } - void TearDown() + + // hwinfo 探测全部走 QProcess,打桩后测试不依赖真实硬件环境 + void stubQProcess() + { + stub.set_lamda(static_cast(&QProcess::start), + [](QProcess *, const QString &, const QStringList &, QIODevice::OpenMode) {}); + stub.set_lamda(static_cast(&QProcess::waitForFinished), + [](QProcess *, int) -> bool { return true; }); + stub.set_lamda(static_cast(&QProcess::readAllStandardOutput), + [](QProcess *) -> QByteArray { return QByteArray(); }); + } + + // 让 curHwinfoUsbInfo 直接返回构造好的设备缓存 + void stubCurUsbInfo(QMap> &curMap) { - delete m_thread; + stub.set_lamda(ADDR(DetectThread, curHwinfoUsbInfo), + [&curMap](DetectThread *, QMap> &out) { + out = curMap; + }); } - DetectThread *m_thread = nullptr; + + stub_ext::StubExt stub; }; -void ut_monitor1() +// ---------- DetectThread::DetectThread (detectthread.cpp:17-30) ---------- +// 分支:构造 MonitorUsb -> 连接 usbChanged 信号 -> curHwinfoUsbInfo + updateMemUsbInfo 初始化缓存 +TEST_F(DetectThreadTest, Ctor_WithStubbedHwinfo_BuildsInitialCache) +{ + // (fixture 已打桩默认环境,无额外前置条件) + // Arrange + // Act + QByteArray hwinfoOut = (mouseItem() + "\n" + keyboardItem()).toUtf8(); + stub.set_lamda(static_cast(&QProcess::readAllStandardOutput), + [hwinfoOut](QProcess *) -> QByteArray { return hwinfoOut; }); + + DetectThread thread(nullptr); + + // 两台设备均生成唯一 ID 后写入缓存 + // Assert + EXPECT_EQ(size_t(2), thread.m_MapUsbInfo.size()); + EXPECT_TRUE(thread.m_MapUsbInfo.contains(expectedUniqueId("usb 0x1a86", "usb 0x7523", kMouseSysFsLink))); + EXPECT_TRUE(thread.m_MapUsbInfo.contains(expectedUniqueId("usb 0x046d", "usb 0xc52b", kKeyboardSysFsLink))); + ASSERT_NE(nullptr, thread.mp_MonitorUsb); +} + +// DetectThread 构造时将 MonitorUsb::usbChanged 连接到 slotUsbChanged(Qt::QueuedConnection) +TEST_F(DetectThreadTest, Ctor_MonitorSignalRoute_ForwardsToUsbChanged) +{ + // Act + // (fixture 已打桩默认环境,无额外前置条件) + // Arrange + DetectThread thread(nullptr); + stub.set_lamda(ADDR(DetectThread, isUsbDevicesChanged), + [](DetectThread *) -> bool { return true; }); + + QSignalSpy spy(&thread, &DetectThread::usbChanged); + emit thread.mp_MonitorUsb->usbChanged(); + QApplication::processEvents(); // 队列连接需要事件循环分发 + + // Assert + EXPECT_FALSE(thread.isRunning()); // 信号转发不启动监听线程 + + EXPECT_EQ(1, spy.count()); +} + +// ---------- DetectThread::curHwinfoUsbInfo (detectthread.cpp:103-155) ---------- +// 分支:Vendor/Device 均含 0x 且 SysFS ID/Device Link 非空 -> 生成 MD5 base64 唯一 ID +TEST_F(DetectThreadTest, CurHwinfoUsbInfo_VendorDeviceSysFs_GeneratesBase64Md5Key) +{ + // Arrange + DetectThread thread(nullptr); + QByteArray hwinfoOut = mouseItem().toUtf8(); + stub.set_lamda(static_cast(&QProcess::readAllStandardOutput), + [hwinfoOut](QProcess *) -> QByteArray { return hwinfoOut; }); + + QMap> usbInfo; + // Act + thread.curHwinfoUsbInfo(usbInfo); + + // Assert + EXPECT_EQ(size_t(1), usbInfo.size()); + // SysFS ID 与 Device Link 同时存在时,真实实现优先使用 Device Link 参与哈希 + QString expectedKey = expectedUniqueId("usb 0x1a86", "usb 0x7523", kMouseSysFsLink); + EXPECT_TRUE(usbInfo.contains(expectedKey)); + EXPECT_EQ("mouse", usbInfo[expectedKey]["Hardware Class"]); + EXPECT_EQ("usb 0x1a86", usbInfo[expectedKey]["Vendor"]); + EXPECT_EQ("usb 0x7523", usbInfo[expectedKey]["Device"]); +} + +// 分支:仅有 SysFS ID(无 Device Link)时使用 SysFS ID 参与哈希 +TEST_F(DetectThreadTest, CurHwinfoUsbInfo_SysFsIdOnly_UsesIdForHash) +{ + // Arrange + DetectThread thread(nullptr); + QByteArray hwinfoOut = mouseItem(false).toUtf8(); + stub.set_lamda(static_cast(&QProcess::readAllStandardOutput), + [hwinfoOut](QProcess *) -> QByteArray { return hwinfoOut; }); + + QMap> usbInfo; + // Act + thread.curHwinfoUsbInfo(usbInfo); + + // Assert + EXPECT_EQ(size_t(1), usbInfo.size()); + QString idKey = expectedUniqueId("usb 0x1a86", "usb 0x7523", kMouseSysFsId); + EXPECT_TRUE(usbInfo.contains(idKey)); + // 与 Device Link 哈希结果不同,证明用的是 SysFS ID + EXPECT_FALSE(usbInfo.contains(expectedUniqueId("usb 0x1a86", "usb 0x7523", kMouseSysFsLink))); +} + +// 分支:Vendor 不含 0x(外层条件不成立)-> 保留 hwinfo 自带的 Unique ID 字段 +TEST_F(DetectThreadTest, CurHwinfoUsbInfo_VendorWithoutHex_KeepsUniqueIdField) +{ + // Arrange + QString item = mouseItem(); + item.replace("Vendor: usb 0x1a86", "Vendor: usb abcd"); + item.replace("03: USB 00.0", + "03: USB 00.0\n Unique ID: ABC123"); + + DetectThread thread(nullptr); + QByteArray hwinfoOut = item.toUtf8(); + stub.set_lamda(static_cast(&QProcess::readAllStandardOutput), + [hwinfoOut](QProcess *) -> QByteArray { return hwinfoOut; }); + + QMap> usbInfo; + // Act + thread.curHwinfoUsbInfo(usbInfo); + + // Assert + EXPECT_EQ(size_t(1), usbInfo.size()); + EXPECT_TRUE(usbInfo.contains("ABC123")); +} + +// 分支:Vendor 含 0x 但仅一个 token(内层条件不成立)-> 仍使用 Unique ID 字段 +TEST_F(DetectThreadTest, CurHwinfoUsbInfo_VendorSingleToken_FallsBackToUniqueIdField) +{ + // Arrange + QString item = mouseItem(); + item.replace("Vendor: usb 0x1a86", "Vendor: 0x7523"); + item.replace("03: USB 00.0", + "03: USB 00.0\n Unique ID: FALLBACK"); + + DetectThread thread(nullptr); + QByteArray hwinfoOut = item.toUtf8(); + stub.set_lamda(static_cast(&QProcess::readAllStandardOutput), + [hwinfoOut](QProcess *) -> QByteArray { return hwinfoOut; }); + + QMap> usbInfo; + // Act + thread.curHwinfoUsbInfo(usbInfo); + + // Assert + EXPECT_EQ(size_t(1), usbInfo.size()); + EXPECT_TRUE(usbInfo.contains("FALLBACK")); +} + +// 分支:条目缺少 Vendor/Device/Unique ID -> 以空字符串作为键插入 +TEST_F(DetectThreadTest, CurHwinfoUsbInfo_MissingVendorDev_InsertedWithEmptyKey) +{ + // Arrange + QString item = mouseItem(); + item.replace(" Device: usb 0x7523\n", ""); + item.replace(" Vendor: usb 0x1a86\n", ""); + + DetectThread thread(nullptr); + QByteArray hwinfoOut = item.toUtf8(); + stub.set_lamda(static_cast(&QProcess::readAllStandardOutput), + [hwinfoOut](QProcess *) -> QByteArray { return hwinfoOut; }); + + QMap> usbInfo; + // Act + thread.curHwinfoUsbInfo(usbInfo); + + // Assert + EXPECT_EQ(size_t(1), usbInfo.size()); + EXPECT_TRUE(usbInfo.contains(QString())); + EXPECT_EQ("mouse", usbInfo[QString()]["Hardware Class"]); +} + +// 分支:Hardware Class 为 hub 的条目在 getMapInfo 内被过滤 +TEST_F(DetectThreadTest, CurHwinfoUsbInfo_HubClass_FilteredOut) +{ + // Arrange + QString hubItem = mouseItem(); + hubItem.replace("Hardware Class: mouse", "Hardware Class: hub"); + + DetectThread thread(nullptr); + QByteArray hwinfoOut = hubItem.toUtf8(); + stub.set_lamda(static_cast(&QProcess::readAllStandardOutput), + [hwinfoOut](QProcess *) -> QByteArray { return hwinfoOut; }); + + QMap> usbInfo; + // Act + thread.curHwinfoUsbInfo(usbInfo); + + // Assert + EXPECT_TRUE(usbInfo.isEmpty()); + EXPECT_EQ(size_t(0), usbInfo.size()); // 短条目不得部分插入 + EXPECT_EQ(size_t(0), usbInfo.size()); // hub 设备必须整条过滤 +} + +// 分支:行数不足(<= LEAST_NUM)的条目被跳过 +TEST_F(DetectThreadTest, CurHwinfoUsbInfo_ShortItem_Skipped) +{ + // Arrange + QString shortItem = + "06: USB 00.0\n" + " Device: usb 0x1234\n" + " Vendor: usb 0x5678\n" + " Hardware Class: mouse\n" + " SysFS ID: /devices/usb1\n" + " Model: \"Tiny\"\n" + " Hotplug: USB\n" + " Revision: 1.00\n"; + + DetectThread thread(nullptr); + QByteArray hwinfoOut = shortItem.toUtf8(); + stub.set_lamda(static_cast(&QProcess::readAllStandardOutput), + [hwinfoOut](QProcess *) -> QByteArray { return hwinfoOut; }); + + QMap> usbInfo; + // Act + thread.curHwinfoUsbInfo(usbInfo); + + // Assert + EXPECT_TRUE(usbInfo.isEmpty()); + EXPECT_EQ(size_t(0), usbInfo.size()); // 行数不足的条目不产出键值 +} + +// ---------- DetectThread::getMapInfo (detectthread.cpp:157-178) ---------- +// 分支:lines.size() <= LEAST_NUM 直接返回 false +TEST_F(DetectThreadTest, GetMapInfo_TooFewLines_ReturnsFalse) +{ + // Arrange + DetectThread thread(nullptr); + // 恰好 LEAST_NUM 行,边界上仍视为无用信息 + QStringList lines; + for (int i = 0; i < kLeastNum; ++i) + lines << QString(" Key%1: Value%1").arg(i); + QMap mapInfo; + // Assert + // Act + EXPECT_FALSE(thread.getMapInfo(lines.join("\n"), mapInfo)); + EXPECT_TRUE(mapInfo.isEmpty()); // 不足行数时不产出任何键值 +} + +// 分支:行数刚超过 LEAST_NUM 的最小合法条目可以解析 +TEST_F(DetectThreadTest, GetMapInfo_MinimumValidItem_Parses) +{ + // Arrange + DetectThread thread(nullptr); + QStringList lines; + for (int i = 0; i <= kLeastNum; ++i) // LEAST_NUM + 1 行 + lines << QString(" Key%1: Value%1").arg(i); + QMap mapInfo; + // Act + // Assert + EXPECT_TRUE(thread.getMapInfo(lines.join("\n"), mapInfo)); + EXPECT_EQ("Value0", mapInfo["Key0"]); +} + +// 分支:Hardware Class 为 hub 返回 false +TEST_F(DetectThreadTest, GetMapInfo_HubHardwareClass_ReturnsFalse) +{ + // Arrange + DetectThread thread(nullptr); + QString item = mouseItem(); + item.replace("Hardware Class: mouse", "Hardware Class: hub"); + QMap mapInfo; + // Act + // Assert + EXPECT_FALSE(thread.getMapInfo(item, mapInfo)); + EXPECT_EQ("hub", mapInfo["Hardware Class"]); // 行已解析,但 hub 类型被过滤 +} + +// 分支:分隔后不是两段的行被跳过,合法行正常解析 +TEST_F(DetectThreadTest, GetMapInfo_MalformedLines_Skipped) +{ + // Arrange + DetectThread thread(nullptr); + QString item = mouseItem(); + // 追加两类非法行:无 ": " 分隔符 / 有多个 ": " 分隔符 + item += "This line has no separator\n" + "Bad: line: with: colons\n"; + QMap mapInfo; + // Assert + // Act + EXPECT_TRUE(thread.getMapInfo(item, mapInfo)); + EXPECT_EQ("mouse", mapInfo["Hardware Class"]); + EXPECT_FALSE(mapInfo.contains("This line has no separator")); + EXPECT_FALSE(mapInfo.contains("Bad")); +} + +// ---------- DetectThread::updateMemUsbInfo (detectthread.cpp:97-101) ---------- +// 行为:整体替换缓存而非合并 +TEST_F(DetectThreadTest, UpdateMemUsbInfo_NewCacheGiven_ReplacesWholeCache) +{ + // Arrange + DetectThread thread(nullptr); + + QMap> first = deviceMap("KEY_A", "mouse"); + first.insert("KEY_B", QMap { {"Hardware Class", "keyboard"} }); + // Act + thread.updateMemUsbInfo(first); + // Assert + EXPECT_EQ(size_t(2), thread.m_MapUsbInfo.size()); + + QMap> second = deviceMap("KEY_C", "disk"); + thread.updateMemUsbInfo(second); + EXPECT_EQ(size_t(1), thread.m_MapUsbInfo.size()); + EXPECT_TRUE(thread.m_MapUsbInfo.contains("KEY_C")); + EXPECT_FALSE(thread.m_MapUsbInfo.contains("KEY_A")); +} + +// ---------- DetectThread::isUsbDevicesChanged (detectthread.cpp:68-95) ---------- +// 分支:cur 数量 < 缓存数量(拔出)-> true 且缓存被刷新 +TEST_F(DetectThreadTest, IsUsbDevicesChanged_DeviceRemoved_ReturnsTrueAndUpdatesCache) +{ + // Arrange + QMap> curMap = deviceMap("KEY_A", "mouse"); + curMap.insert("KEY_B", QMap { {"Hardware Class", "keyboard"} }); + stubCurUsbInfo(curMap); + + DetectThread thread(nullptr); // 构造时缓存 = curMap(2 项) + curMap = deviceMap("KEY_A", "mouse"); // 模拟拔出一个设备 + + // Assert + // Act + EXPECT_TRUE(thread.isUsbDevicesChanged()); + EXPECT_EQ(size_t(1), thread.m_MapUsbInfo.size()); +} + +// 分支:新设备(非 disk)不在缓存中 -> true +TEST_F(DetectThreadTest, IsUsbDevicesChanged_NewNonDiskDevice_ReturnsTrue) +{ + // Arrange + QMap> curMap = deviceMap("KEY_A", "mouse"); + stubCurUsbInfo(curMap); + + DetectThread thread(nullptr); + curMap.insert("KEY_NEW", QMap { {"Hardware Class", "keyboard"} }); + + // Act + // Assert + EXPECT_TRUE(thread.isUsbDevicesChanged()); + EXPECT_EQ(size_t(2), thread.m_MapUsbInfo.size()); +} + +// 分支:新设备是 disk 且无 Capacity(内核未处理完)-> 忽略并返回 false +TEST_F(DetectThreadTest, IsUsbDevicesChanged_NewDiskWithoutCapacity_Ignored) +{ + // Arrange + QMap> curMap = deviceMap("KEY_A", "mouse"); + stubCurUsbInfo(curMap); + + DetectThread thread(nullptr); + curMap.insert("KEY_DISK", QMap { {"Hardware Class", "disk"} }); + + // Act + // Assert + EXPECT_FALSE(thread.isUsbDevicesChanged()); + EXPECT_FALSE(thread.isUsbDevicesChanged()); // 缓存未变,重复调用结果一致 + // 缓存未被更新 + EXPECT_EQ(size_t(1), thread.m_MapUsbInfo.size()); +} + +// 分支:新设备是 disk 且带 Capacity -> 视为新设备返回 true +TEST_F(DetectThreadTest, IsUsbDevicesChanged_NewDiskWithCapacity_ReturnsTrue) +{ + // Arrange + QMap> curMap = deviceMap("KEY_A", "mouse"); + stubCurUsbInfo(curMap); + + DetectThread thread(nullptr); + curMap.insert("KEY_DISK", QMap { {"Hardware Class", "disk"}, + {"Capacity", "16GB"} }); + + // Act + // Assert + EXPECT_TRUE(thread.isUsbDevicesChanged()); + EXPECT_EQ(size_t(2), thread.m_MapUsbInfo.size()); +} + +// 分支:缓存与当前完全一致 -> false +TEST_F(DetectThreadTest, IsUsbDevicesChanged_NoChange_ReturnsFalse) +{ + // Arrange + QMap> curMap = deviceMap("KEY_A", "mouse"); + stubCurUsbInfo(curMap); + + DetectThread thread(nullptr); + + // Act + // Assert + EXPECT_FALSE(thread.isUsbDevicesChanged()); + EXPECT_TRUE(thread.m_MapUsbInfo.contains("KEY_A")); // 缓存内容未发生变更 +} + +// ---------- DetectThread::run (detectthread.cpp:32-40) ---------- +// 行为:run 进入 MonitorUsb::monitor 监听循环(此处打桩验证调用即可) +TEST_F(DetectThreadTest, Run_MonitorLoop_ExecutesOncePerRun) +{ + // Arrange + DetectThread thread(nullptr); + int monitorCalls = 0; + stub.set_lamda(ADDR(MonitorUsb, monitor), + [&monitorCalls](MonitorUsb *) { ++monitorCalls; }); + + // Act + thread.run(); // run 为 public,直接调用避免真实线程启动 + + // Assert + EXPECT_EQ(1, monitorCalls); + + thread.run(); + EXPECT_EQ(2, monitorCalls); // run 可重复调用,每轮各执行一次监控 +} + +// ---------- DetectThread::setWorkingFlag (detectthread.cpp:42-50) ---------- +// 分支:flag=false 只转发给 MonitorUsb,不启动线程 +TEST_F(DetectThreadTest, SetWorkingFlag_False_ForwardedToMonitorAndThreadNotStarted) +{ + // Arrange + DetectThread thread(nullptr); + bool forwardedFlag = true; + stub.set_lamda(ADDR(MonitorUsb, setWorkingFlag), + [&forwardedFlag](MonitorUsb *, bool flag) { forwardedFlag = flag; }); + + // Act + thread.setWorkingFlag(false); + + // Assert + EXPECT_FALSE(forwardedFlag); + EXPECT_FALSE(thread.isRunning()); +} + +// 分支:flag=true 且线程未运行 -> 启动线程并执行监听循环 +TEST_F(DetectThreadTest, SetWorkingFlag_True_StartsThreadRunningMonitor) +{ + // Arrange + DetectThread thread(nullptr); + int monitorCalls = 0; + stub.set_lamda(ADDR(MonitorUsb, monitor), [&monitorCalls](MonitorUsb *) { ++monitorCalls; }); + + // Act + thread.setWorkingFlag(true); + // Assert + EXPECT_TRUE(thread.wait(5000)); // monitor 已打桩,线程很快退出 + + EXPECT_GE(monitorCalls, 1); + EXPECT_FALSE(thread.isRunning()); +} + +// ---------- DetectThread::slotUsbChanged (detectthread.cpp:52-66) ---------- +// 分支:isUsbDevicesChanged 立即返回 true -> break 后只发射一次 usbChanged +TEST_F(DetectThreadTest, SlotUsbChanged_DevicesSettled_EmitsSignalOnce) +{ + // Arrange + DetectThread thread(nullptr); + stub.set_lamda(ADDR(DetectThread, isUsbDevicesChanged), + [](DetectThread *) -> bool { return true; }); + + QSignalSpy spy(&thread, &DetectThread::usbChanged); + // Act + thread.slotUsbChanged(); + + // Assert + EXPECT_EQ(1, spy.count()); + EXPECT_FALSE(thread.isRunning()); // 稳定判断后仅发信号,不启动线程 +} + +// 分支:10s 内始终未就绪 -> 等待超时后仍然发射 usbChanged(sleep 打桩避免真实等待) +TEST_F(DetectThreadTest, SlotUsbChanged_WaitTimeout_StillEmitsSignal) +{ + // Arrange + DetectThread thread(nullptr); + stub.set_lamda(ADDR(DetectThread, isUsbDevicesChanged), + [](DetectThread *) -> bool { return false; }); + stub.set_lamda(&sleep, [](unsigned int) -> unsigned int { return 0; }); + qint64 fakeTick = 1000; + stub.set_lamda(static_cast(&QDateTime::currentMSecsSinceEpoch), + [&fakeTick]() -> qint64 { + qint64 value = fakeTick; + fakeTick += 20000; // 下一次调用直接越过 10s 超时窗口 + return value; + }); + + QSignalSpy spy(&thread, &DetectThread::usbChanged); + // Act + thread.slotUsbChanged(); + + // Assert + EXPECT_EQ(1, spy.count()); + EXPECT_FALSE(thread.isRunning()); // 超时窗口结束后同样仅发信号 +} + +TEST_F(DetectThreadTest, Run_CalledTwice_MonitorExecutesEachTime) { - return; + // Arrange + DetectThread thread(nullptr); + int monitorCalls = 0; + stub.set_lamda(ADDR(MonitorUsb, monitor), + [&monitorCalls](MonitorUsb *) { ++monitorCalls; }); + // Act + thread.run(); + thread.run(); + // Assert + EXPECT_EQ(2, monitorCalls); // 每次调用各执行一轮监控 + EXPECT_FALSE(thread.isRunning()); } -TEST_F(DetectThread_UT, DetectThread_UT_monitor) +TEST_F(DetectThreadTest, UpdateMemUsbInfo_EmptyCache_ClearsAllEntries) { - Stub stub; - stub.set(ADDR(MonitorUsb, monitor), ut_monitor1); - m_thread->run(); + // Arrange + DetectThread thread(nullptr); + QMap> filled = deviceMap("KEY_A", "mouse"); + // Act + thread.updateMemUsbInfo(filled); + QMap> empty; + // Act + thread.updateMemUsbInfo(empty); // 循环边界:空集合整体替换 + // Assert + EXPECT_TRUE(thread.m_MapUsbInfo.isEmpty()); // 原有缓存被清空 + EXPECT_EQ(size_t(0), thread.m_MapUsbInfo.size()); } From 44d9bf86ccf09952ee7d0ec444c77597fa9e2871 Mon Sep 17 00:00:00 2001 From: zhanghongyuan Date: Mon, 26 Oct 2026 20:12:58 +0800 Subject: [PATCH 3/8] =?UTF-8?q?test:=20=E9=87=8D=E5=86=99=20MonitorUsb=20?= =?UTF-8?q?=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95=EF=BC=8817=20=E7=94=A8?= =?UTF-8?q?=E4=BE=8B=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 覆盖构造初始化(udev 上下文/监听器/1s 轮询定时器)、monitor 主循环(工作标志退出、select 超时重入、fd 未就绪、空设备、连接/断开/蓝牙事件)、setWorkingFlag、slotTimeout(10s 防抖窗口) - udev 库函数、select、QProcess 等 C 层依赖以 mangled-name extern C 打桩;注入 AAA 注释与用例计数声明 --- .../tests/src/UsbDetect/ut_monitorusb.cpp | 511 +++++++++++++++++- 1 file changed, 494 insertions(+), 17 deletions(-) diff --git a/deepin-devicemanager-server/tests/src/UsbDetect/ut_monitorusb.cpp b/deepin-devicemanager-server/tests/src/UsbDetect/ut_monitorusb.cpp index 95622cbdf..81c4d4296 100644 --- a/deepin-devicemanager-server/tests/src/UsbDetect/ut_monitorusb.cpp +++ b/deepin-devicemanager-server/tests/src/UsbDetect/ut_monitorusb.cpp @@ -1,34 +1,511 @@ -// SPDX-FileCopyrightText: 2019 ~ 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later +/* + * 用例计数声明(method | level | factors | min | actual) + * | MonitorUsb::MonitorUsb | low | - | 1 | 3 | + * | MonitorUsb::monitor | high | complexity:9,cognitive:18,lines:67,in_degree:81 | 3 | 9 | + * | MonitorUsb::setWorkingFlag | low | - | 1 | 1 | + * | MonitorUsb::slotTimeout | low | - | 1 | 4 | + */ -#include "monitorusb.h" -#include "../ut_Head.h" #include -#include "../stub.h" -class MonitorUsb_UT : public UT_HEAD +#include +#include +#include +#include + +#include +#include +#include + +#include "stubext.h" +#include "monitorusb.h" +#include "controlinterface.h" + +using namespace stub_ext; + +namespace { +// 伪造的 udev 上下文 / monitor / device 指针,仅用于非空判断与桩间传递 +constexpr intptr_t kFakeUdev = 0x1001; +constexpr intptr_t kFakeMonitor = 0x2002; +constexpr intptr_t kFakeDevice = 0x3003; +constexpr int kFakeFd = 100; + +// monitor() 监听循环的 fd 值即 udev_monitor_get_fd 的返回值 +void stubUdevEnv(stub_ext::StubExt &stub) +{ + stub.set_lamda(udev_new, + []() -> struct udev * { return reinterpret_cast(kFakeUdev); }); + stub.set_lamda(udev_monitor_new_from_netlink, + [](struct udev *, const char *) -> struct udev_monitor * { + return reinterpret_cast(kFakeMonitor); + }); + stub.set_lamda(udev_monitor_filter_add_match_subsystem_devtype, + [](struct udev_monitor *, const char *, const char *) -> int { return 0; }); + stub.set_lamda(udev_monitor_enable_receiving, + [](struct udev_monitor *) -> int { return 0; }); + stub.set_lamda(udev_monitor_get_fd, + [](struct udev_monitor *) -> int { return kFakeFd; }); +} + +// select 打桩:模拟 fd 就绪;第二次调用后清空工作标志以终止监听循环,避免死循环 +// selectCalls 为用例内局部计数,由调用方持有 +void stubSelectReady(stub_ext::StubExt &stub, MonitorUsb *monitor, int &selectCalls, + bool withFdSet = true) +{ + stub.set_lamda(select, + [&selectCalls, monitor, withFdSet](int nfds, fd_set *readfds, fd_set *, fd_set *, + struct timeval *) -> int { + ++selectCalls; + if (selectCalls >= 2) + monitor->m_workingFlag = false; + if (withFdSet && selectCalls == 1) + FD_SET(nfds - 1, readfds); + return 1; + }); +} + +// udev 设备事件打桩:第一次返回伪设备,之后返回空并终止循环 +void stubUdevDeviceEvent(stub_ext::StubExt &stub, MonitorUsb *monitor, int &receiveCalls, + const char *devType, unsigned long long devNum, const char *action) { -public: - void SetUp() + stub.set_lamda(udev_monitor_receive_device, + [&receiveCalls, monitor](struct udev_monitor *) -> struct udev_device * { + ++receiveCalls; + if (receiveCalls >= 2) { + monitor->m_workingFlag = false; + return nullptr; + } + return reinterpret_cast(kFakeDevice); + }); + stub.set_lamda(udev_device_get_devtype, + [devType](struct udev_device *) -> const char * { return devType; }); + stub.set_lamda(udev_device_get_devnum, + [devNum](struct udev_device *) -> unsigned long long { return devNum; }); + stub.set_lamda(udev_device_get_action, + [action](struct udev_device *) -> const char * { return action; }); +} + +// ControlInterface 单例的 DBus 依赖全部打桩,保证测试不触达真实系统总线 +void stubControlInterface(stub_ext::StubExt &stub, int *disableOutCalls, int *updateWakeupCalls) +{ + stub.set_lamda(static_cast(&QDBusConnection::systemBus), + []() -> QDBusConnection { return QDBusConnection(QString()); }); + stub.set_lamda(static_cast(&ControlInterface::disableOutDevice), + [disableOutCalls](ControlInterface *, const QString &) { ++(*disableOutCalls); }); + stub.set_lamda(static_cast(&ControlInterface::updateWakeup), + [updateWakeupCalls](ControlInterface *, const QString &) { ++(*updateWakeupCalls); }); +} + +// add/remove 事件路径中的 hwinfo 探测打桩(输出为空即可,结果只被透传) +void stubQProcessEmpty(stub_ext::StubExt &stub) +{ + stub.set_lamda(static_cast(&QProcess::start), + [](QProcess *, const QString &, const QStringList &, QIODevice::OpenMode) {}); + stub.set_lamda(static_cast(&QProcess::waitForFinished), + [](QProcess *, int) -> bool { return true; }); + stub.set_lamda(static_cast(&QProcess::readAllStandardOutput), + [](QProcess *) -> QByteArray { return QByteArray(); }); +} +} // namespace + +class MonitorUsbTest : public ::testing::Test +{ +protected: + void SetUp() override { - m_monitor = new MonitorUsb; + stub.clear(); + stubUdevEnv(stub); } - void TearDown() + + void TearDown() override { - delete m_monitor; + stub.clear(); } - MonitorUsb *m_monitor = nullptr; + + stub_ext::StubExt stub; }; -void ut_monitor() +// ---------- MonitorUsb::MonitorUsb (monitorusb.cpp:17-43) ---------- +// 分支:udev_new / monitor 创建 / 过滤器 / enable_receiving / get_fd 全部初始化 +TEST_F(MonitorUsbTest, Ctor_WithStubbedUdev_InitializesContextMonitorAndTimer) { - return; + // (fixture 已打桩默认环境,无额外前置条件) + // Arrange + // Act + MonitorUsb monitor; + + // Assert + EXPECT_EQ(reinterpret_cast(kFakeUdev), reinterpret_cast(monitor.m_Udev)); + EXPECT_EQ(kFakeFd, monitor.fd); + EXPECT_TRUE(monitor.m_workingFlag); + EXPECT_FALSE(monitor.m_UsbChanged); + ASSERT_NE(nullptr, monitor.mp_Timer); +} + +// 分支:udev_new 失败(返回空)-> 记录警告并继续构造,不崩溃 +TEST_F(MonitorUsbTest, Ctor_UdevNewReturnsNull_NoCrash) +{ + // Arrange + stub.set_lamda(&udev_new, []() -> struct udev* { return nullptr; }); + + // Act + MonitorUsb monitor; + monitor.setWorkingFlag(false); + + // Assert + EXPECT_NO_THROW(monitor.monitor()); + EXPECT_FALSE(monitor.m_UsbChanged); + EXPECT_EQ(nullptr, monitor.m_Udev); // udev_new 失败时上下文保持为空 +} + +// ---------- MonitorUsb::monitor (monitorusb.cpp:45-111) ---------- +// 分支:m_workingFlag 为 false 时直接退出,未进入 select 循环 +TEST_F(MonitorUsbTest, Monitor_WorkingFlagDisabled_ExitsBeforeSelectLoop) +{ + // Arrange + // Act + // (fixture 已打桩默认环境,无额外前置条件) + MonitorUsb monitor; + monitor.setWorkingFlag(false); + int selectCalls = 0; + stub.set_lamda(select, + [&selectCalls](int, fd_set *, fd_set *, fd_set *, struct timeval *) -> int { + ++selectCalls; + return 0; + }); + + monitor.monitor(); // 正常返回即代表循环退出 + + // Assert + EXPECT_EQ(0, selectCalls); + EXPECT_FALSE(monitor.m_workingFlag); +} + +// 分支:select 超时返回 0 -> continue 继续监听,随后标志被清退出 +TEST_F(MonitorUsbTest, Monitor_SelectTimeout_ContinuesThenExitsOnFlagCleared) +{ + // (fixture 已打桩默认环境,无额外前置条件) + // Arrange + // Act + MonitorUsb monitor; + int selectCalls = 0; + stub.set_lamda(select, + [&selectCalls, &monitor](int, fd_set *, fd_set *, fd_set *, + struct timeval *) -> int { + ++selectCalls; + if (selectCalls >= 2) + monitor.m_workingFlag = false; + return 0; // 超时 + }); + int receiveCalls = 0; + stub.set_lamda(udev_monitor_receive_device, + [&receiveCalls](struct udev_monitor *) -> struct udev_device * { + ++receiveCalls; + return nullptr; + }); + + monitor.monitor(); + + // Assert + EXPECT_GE(selectCalls, 2); // 至少经历一次超时继续 + EXPECT_EQ(0, receiveCalls); // 超时路径不会取设备 +} + +// 分支:select 就绪但 fd 未置位 -> FD_ISSET 为假,继续循环且不取设备 +TEST_F(MonitorUsbTest, Monitor_SelectReadyButFdNotSet_ContinuesLoop) +{ + // (fixture 已打桩默认环境,无额外前置条件) + // Arrange + // Act + MonitorUsb monitor; + int selectCalls = 0; + // 监听循环自身先 FD_SET(fd) 再调 select;此处模拟"就绪但 fd 未置位" + // 必须用 FD_CLR 清掉,才能走到 FD_ISSET 为假的 continue 分支 + stub.set_lamda(select, + [&selectCalls, &monitor](int nfds, fd_set *readfds, fd_set *, fd_set *, + struct timeval *) -> int { + ++selectCalls; + if (selectCalls >= 2) + monitor.m_workingFlag = false; + FD_CLR(nfds - 1, readfds); // fd = nfds-1 + return 1; + }); + int receiveCalls = 0; + stub.set_lamda(udev_monitor_receive_device, + [&receiveCalls](struct udev_monitor *) -> struct udev_device * { + ++receiveCalls; + return nullptr; + }); + + monitor.monitor(); + + // Assert + EXPECT_GE(selectCalls, 2); // 就绪但 fd 未置位 -> continue 后再次进入 select + EXPECT_EQ(0, receiveCalls); +} + +// 分支:取到的 udev 设备为空 -> continue,不发射信号 +TEST_F(MonitorUsbTest, Monitor_ReceiveDeviceNull_ContinuesLoop) +{ + // (fixture 已打桩默认环境,无额外前置条件) + // Arrange + // Act + MonitorUsb monitor; + int selectCalls = 0; + stubSelectReady(stub, &monitor, selectCalls); + int receiveCalls = 0; + stub.set_lamda(udev_monitor_receive_device, + [&receiveCalls, &monitor](struct udev_monitor *) -> struct udev_device * { + ++receiveCalls; + if (receiveCalls >= 2) + monitor.m_workingFlag = false; + return nullptr; + }); + + QSignalSpy spy(&monitor, &MonitorUsb::usbChanged); + monitor.monitor(); + + // Assert + EXPECT_EQ(2, receiveCalls); + EXPECT_EQ(0, spy.count()); +} + +// 分支:devtype 为 "link"(蓝牙)且工作中 -> 发射 usbChanged 并继续循环 +TEST_F(MonitorUsbTest, Monitor_BluetoothLinkDevice_EmitsUsbChanged) +{ + // (fixture 已打桩默认环境,无额外前置条件) + // Arrange + // Act + MonitorUsb monitor; + int selectCalls = 0; + stubSelectReady(stub, &monitor, selectCalls); + int receiveCalls = 0; + stubUdevDeviceEvent(stub, &monitor, receiveCalls, "link", 42, "add"); + stub.set_lamda(udev_device_unref, [](struct udev_device *dev) -> struct udev_device * { return dev; }); + int disableOutCalls = 0; + int updateWakeupCalls = 0; + stubControlInterface(stub, &disableOutCalls, &updateWakeupCalls); + stubQProcessEmpty(stub); + + QSignalSpy spy(&monitor, &MonitorUsb::usbChanged); + monitor.monitor(); + + // Assert + EXPECT_EQ(1, spy.count()); + EXPECT_EQ(2, selectCalls); // 就绪处理一次后,下一轮因标志清空退出循环 +} + +// 分支:devnum 为 0 的设备被忽略并 unref,不更新缓存 +TEST_F(MonitorUsbTest, Monitor_DevnumZero_UnrefsDeviceAndContinues) +{ + // Arrange + // Act + // (fixture 已打桩默认环境,无额外前置条件) + MonitorUsb monitor; + int selectCalls = 0; + stubSelectReady(stub, &monitor, selectCalls); + int receiveCalls = 0; + stubUdevDeviceEvent(stub, &monitor, receiveCalls, "usb", 0, "add"); + int unrefCalls = 0; + stub.set_lamda(udev_device_unref, + [&unrefCalls](struct udev_device *dev) -> struct udev_device * { + ++unrefCalls; + return dev; + }); + int disableOutCalls = 0; + int updateWakeupCalls = 0; + stubControlInterface(stub, &disableOutCalls, &updateWakeupCalls); + stubQProcessEmpty(stub); + + QSignalSpy spy(&monitor, &MonitorUsb::usbChanged); + monitor.monitor(); + + // Assert + EXPECT_EQ(1, unrefCalls); + EXPECT_FALSE(monitor.m_UsbChanged); + EXPECT_EQ(0, spy.count()); +} + +// 分支:add 事件 -> 探测设备信息并通知 ControlInterface,缓存标记更新 +TEST_F(MonitorUsbTest, Monitor_AddEvent_TriggersHwinfoAndControlNotifies) +{ + // Arrange + // Act + // (fixture 已打桩默认环境,无额外前置条件) + MonitorUsb monitor; + int selectCalls = 0; + stubSelectReady(stub, &monitor, selectCalls); + int receiveCalls = 0; + stubUdevDeviceEvent(stub, &monitor, receiveCalls, "usb", 42, "add"); + stub.set_lamda(udev_device_unref, [](struct udev_device *dev) -> struct udev_device * { return dev; }); + int disableOutCalls = 0; + int updateWakeupCalls = 0; + stubControlInterface(stub, &disableOutCalls, &updateWakeupCalls); + stubQProcessEmpty(stub); + + QSignalSpy spy(&monitor, &MonitorUsb::usbChanged); + monitor.monitor(); + + // Assert + EXPECT_EQ(1, disableOutCalls); // add 事件额外调用 disableOutDevice + EXPECT_EQ(1, updateWakeupCalls); + EXPECT_TRUE(monitor.m_UsbChanged); + EXPECT_EQ(0, spy.count()); // usbChanged 由 slotTimeout 防抖后发射 +} + +// 分支:remove 事件 -> 只更新唤醒信息,不调用 disableOutDevice +TEST_F(MonitorUsbTest, Monitor_RemoveEvent_UpdatesCacheWithoutDisableOut) +{ + // Act + // (fixture 已打桩默认环境,无额外前置条件) + // Arrange + MonitorUsb monitor; + int selectCalls = 0; + stubSelectReady(stub, &monitor, selectCalls); + int receiveCalls = 0; + stubUdevDeviceEvent(stub, &monitor, receiveCalls, "usb", 42, "remove"); + stub.set_lamda(udev_device_unref, [](struct udev_device *dev) -> struct udev_device * { return dev; }); + int disableOutCalls = 0; + int updateWakeupCalls = 0; + stubControlInterface(stub, &disableOutCalls, &updateWakeupCalls); + stubQProcessEmpty(stub); + + monitor.monitor(); + + // Assert + EXPECT_EQ(0, disableOutCalls); + EXPECT_EQ(1, updateWakeupCalls); + EXPECT_TRUE(monitor.m_UsbChanged); +} + +// 分支:change 事件不在 add/remove 之列 -> 不更新缓存 +TEST_F(MonitorUsbTest, Monitor_ChangeEvent_DoesNotUpdateCache) +{ + // Arrange + // Act + // (fixture 已打桩默认环境,无额外前置条件) + MonitorUsb monitor; + int selectCalls = 0; + stubSelectReady(stub, &monitor, selectCalls); + int receiveCalls = 0; + stubUdevDeviceEvent(stub, &monitor, receiveCalls, "usb", 42, "change"); + stub.set_lamda(udev_device_unref, [](struct udev_device *dev) -> struct udev_device * { return dev; }); + int disableOutCalls = 0; + int updateWakeupCalls = 0; + stubControlInterface(stub, &disableOutCalls, &updateWakeupCalls); + stubQProcessEmpty(stub); + + monitor.monitor(); + + // Assert + EXPECT_EQ(0, disableOutCalls); + EXPECT_EQ(0, updateWakeupCalls); + EXPECT_FALSE(monitor.m_UsbChanged); +} + +// ---------- MonitorUsb::setWorkingFlag (monitorusb.cpp:113-117) ---------- +TEST_F(MonitorUsbTest, SetWorkingFlag_NewFlagValue_UpdatesWorkingState) +{ + // Act + // (fixture 已打桩默认环境,无额外前置条件) + // Arrange + MonitorUsb monitor; + + monitor.setWorkingFlag(false); + // Assert + EXPECT_FALSE(monitor.m_workingFlag); + + monitor.setWorkingFlag(true); + EXPECT_TRUE(monitor.m_workingFlag); + EXPECT_FALSE(monitor.m_UsbChanged); // setWorkingFlag 不改变插拔记录 +} + +// ---------- MonitorUsb::slotTimeout (monitorusb.cpp:119-128) ---------- +// 分支:无未决变更(m_UsbChanged=false)不发射 +TEST_F(MonitorUsbTest, SlotTimeout_NoPendingChange_DoesNotEmit) +{ + // Arrange + // Act + // (fixture 已打桩默认环境,无额外前置条件) + MonitorUsb monitor; + + QSignalSpy spy(&monitor, &MonitorUsb::usbChanged); + monitor.slotTimeout(); + + // Assert + EXPECT_EQ(0, spy.count()); + EXPECT_FALSE(monitor.m_UsbChanged); // 无待处理插拔记录 +} + +// 分支:有未决变更但工作标志已关闭 -> 依旧不发射 +TEST_F(MonitorUsbTest, SlotTimeout_WorkingFlagDisabled_DoesNotEmit) +{ + // Act + // (fixture 已打桩默认环境,无额外前置条件) + // Arrange + MonitorUsb monitor; + monitor.m_UsbChanged = true; + monitor.setWorkingFlag(false); + + QSignalSpy spy(&monitor, &MonitorUsb::usbChanged); + monitor.slotTimeout(); + + // Assert + EXPECT_EQ(0, spy.count()); + EXPECT_TRUE(monitor.m_UsbChanged); // 停止工作时插拔记录保持不清空 +} + +// 分支:距上次变更不足 1s(防抖窗口内)不发射 +TEST_F(MonitorUsbTest, SlotTimeout_WithinDebounceWindow_DoesNotEmit) +{ + // Act + // Arrange + // (fixture 已打桩默认环境,无额外前置条件) + MonitorUsb monitor; + monitor.m_UsbChanged = true; + monitor.m_UsbChangeTime = QDateTime::currentMSecsSinceEpoch(); + + QSignalSpy spy(&monitor, &MonitorUsb::usbChanged); + monitor.slotTimeout(); + + // Assert + EXPECT_EQ(0, spy.count()); + EXPECT_TRUE(monitor.m_UsbChanged); // 防抖窗口内不重置标志 +} + +// 分支:超过 1s 防抖窗口 -> 发射一次并重置标志,再次调用不再发射 +TEST_F(MonitorUsbTest, SlotTimeout_AfterDebounceWindow_EmitsOnceAndResetsFlag) +{ + // Arrange + // (fixture 已打桩默认环境,无额外前置条件) + // Act + MonitorUsb monitor; + monitor.m_UsbChanged = true; + monitor.m_UsbChangeTime = QDateTime::currentMSecsSinceEpoch() - 2000; + + QSignalSpy spy(&monitor, &MonitorUsb::usbChanged); + monitor.slotTimeout(); + + // Assert + EXPECT_EQ(1, spy.count()); + EXPECT_FALSE(monitor.m_UsbChanged); + + monitor.slotTimeout(); + EXPECT_EQ(1, spy.count()); // 标志已复位,不会重复发射 } -TEST_F(MonitorUsb_UT, MonitorUsb_UT_monitor) +TEST_F(MonitorUsbTest, Ctor_DefaultState_TimerActiveAndFlagsInitialized) { - Stub stub; - stub.set(ADDR(MonitorUsb, monitor), ut_monitor); - m_monitor->monitor(); + // Arrange + // (fixture 已打桩默认 udev 环境,无额外前置条件) + // Act + MonitorUsb monitor; + // Assert + EXPECT_TRUE(monitor.m_workingFlag); // 构造后处于工作状态 + EXPECT_FALSE(monitor.m_UsbChanged); + EXPECT_NE(nullptr, monitor.mp_Timer); + EXPECT_TRUE(monitor.mp_Timer->isActive()); // 1s 轮询定时器已启动 } From db4695707047a76afec763392891775cbd5a1032 Mon Sep 17 00:00:00 2001 From: zhanghongyuan Date: Mon, 26 Oct 2026 20:12:58 +0800 Subject: [PATCH 4/8] =?UTF-8?q?test:=20=E6=96=B0=E5=A2=9E=20ControlInterfa?= =?UTF-8?q?ce=20=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95=EF=BC=8812=20?= =?UTF-8?q?=E7=94=A8=E4=BE=8B=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 覆盖构造建立 DBus 接口、单例获取、disableOutDevice/disableInDevice/updateWakeup/setMonitorWorkingDBFlag 的无效接口与空接口跳过路径、monitorWorkingDBFlag 的有效回复/无效回复/无效接口/空接口四路径 - QDBusAbstractInterface::call 经私有导出 doCall(mangled-name)打桩,不依赖真实 DBus 总线 --- .../tests/src/ut_controlinterface.cpp | 396 ++++++++++++++++++ 1 file changed, 396 insertions(+) create mode 100644 deepin-devicemanager-server/tests/src/ut_controlinterface.cpp diff --git a/deepin-devicemanager-server/tests/src/ut_controlinterface.cpp b/deepin-devicemanager-server/tests/src/ut_controlinterface.cpp new file mode 100644 index 000000000..afa496c80 --- /dev/null +++ b/deepin-devicemanager-server/tests/src/ut_controlinterface.cpp @@ -0,0 +1,396 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later +/* + * 用例计数声明(method | level | factors | min | actual) + * | ControlInterface::ControlInterface | low | - | 1 | 1 | + * | ControlInterface::disableInDevice | low | - | 1 | 2 | + * | ControlInterface::disableOutDevice | low | - | 1 | 3 | + * | ControlInterface::init | low | - | 1 | 1 | + * | ControlInterface::monitorWorkingDBFlag | low | - | 1 | 5 | + * | ControlInterface::setMonitorWorkingDBFlag | low | - | 1 | 2 | + * | ControlInterface::updateWakeup | low | - | 1 | 2 | + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "stubext.h" +#include "controlinterface.h" + +using namespace stub_ext; + +// QDBusAbstractInterface::doCall 是 Qt6 DBus 的私有导出符号(call 内联模板的最终落点)。 +// 显式实例化访问私有成员的技巧已被 GCC12 拒绝,此处直接按 mangled name 取地址打桩。 +extern "C" QDBusMessage _ZN22QDBusAbstractInterface6doCallEN5QDBus8CallModeERK7QStringPK8QVariantm( + QDBusAbstractInterface *self, QDBus::CallMode mode, const QString &method, + const QVariant *args, size_t numArgs); + +namespace { +// Qt 6.8 的 QDBusAbstractInterface::call 是内联模板,内部转发到私有的 doCall; +// 测试通过 addr_pri.h 的私有函数访问补丁直接拦截 doCall +using DoCallFn = QDBusMessage (*)(QDBusAbstractInterface *, QDBus::CallMode, const QString &, + const QVariant *, size_t); + +// DBus 调用记录 +struct DoCallRecord +{ + int calls = 0; + QString method; + QVariant firstArg; + bool hasFirstArg = false; +}; + +// 构造合法应答消息(type == ReplyMessage),供 QDBusReply 解析 +QDBusMessage validReply(const QVariant &value) +{ + QDBusMessage request = QDBusMessage::createMethodCall("org.ut.Service", "/ut/path", "", "stub"); + return request.createReply(QList { value }); +} + +// doCall 打桩:记录调用并返回指定应答;replyValid 为 false 时返回无效应答 +void stubDoCall(stub_ext::StubExt &stub, DoCallRecord *record, + const QVariant &replyValue = QVariant(), bool replyValid = false) +{ + DoCallFn fp = &_ZN22QDBusAbstractInterface6doCallEN5QDBus8CallModeERK7QStringPK8QVariantm; + stub.set_lamda(fp, + [record, replyValue, replyValid](QDBusAbstractInterface *, QDBus::CallMode, + const QString &method, const QVariant *args, + size_t numArgs) -> QDBusMessage { + ++record->calls; + record->method = method; + if (numArgs > 0) { + record->firstArg = args[0]; + record->hasFirstArg = true; + } + if (replyValid) + return validReply(replyValue); + return QDBusMessage(); + }); +} + +// ControlInterface 单例的系统总线打桩,保证测试不触达真实 DBus +void stubInvalidSystemBus(stub_ext::StubExt &stub) +{ + stub.set_lamda(static_cast(&QDBusConnection::systemBus), + []() -> QDBusConnection { return QDBusConnection(QString()); }); +} +} // namespace + +class ControlInterfaceTest : public ::testing::Test +{ +protected: + void SetUp() override + { + stub.clear(); + stubInvalidSystemBus(stub); + } + + void TearDown() override + { + stub.clear(); + } + + stub_ext::StubExt stub; +}; + +// ---------- ControlInterface::ControlInterface / getInstance (controlinterface.cpp:23-29) ---------- +TEST_F(ControlInterfaceTest, Ctor_AfterConstruction_InitializesDBusInterfaceObject) +{ + // Arrange + // (fixture 已打桩 systemBus 环境,无额外前置条件) + // Act + ControlInterface *ci = new ControlInterface(); + // Assert + EXPECT_NE(nullptr, ci->m_iface); // 构造完成即建立 DBus 接口对象 + EXPECT_EQ(QString("org.deepin.DeviceControl"), ci->m_iface->service()); + delete ci; +} + +TEST_F(ControlInterfaceTest, GetInstance_RepeatedCalls_ReturnsSameSingletonPointer) +{ + // Arrange:首次获取单例作为基准 + ControlInterface *first = ControlInterface::getInstance(); + // Act:再次获取 + ControlInterface *second = ControlInterface::getInstance(); + // Assert + EXPECT_EQ(first, second); + EXPECT_NE(nullptr, second); +} + +// ---------- ControlInterface::disableOutDevice (controlinterface.cpp:31-40) ---------- +TEST_F(ControlInterfaceTest, DisableOutDevice_ValidIface_CallsDBusWithDevInfo) +{ + // (fixture 已打桩默认环境,无额外前置条件) + // Arrange + // Act + ControlInterface *ci = new ControlInterface(); + stub.set_lamda(static_cast(&QDBusAbstractInterface::isValid), + [](QDBusAbstractInterface *) -> bool { return true; }); + DoCallRecord record; + stubDoCall(stub, &record); + + ci->disableOutDevice("hwinfo-output"); + + // Assert + EXPECT_EQ(1, record.calls); + EXPECT_EQ("disableOutDevice", record.method); + ASSERT_TRUE(record.hasFirstArg); + EXPECT_EQ("hwinfo-output", record.firstArg.toString()); + delete ci; +} + +TEST_F(ControlInterfaceTest, DisableOutDevice_InvalidIface_SkipsDBusCall) +{ + // Arrange + // Act + // (fixture 已打桩默认环境,无额外前置条件) + ControlInterface *ci = new ControlInterface(); + stub.set_lamda(static_cast(&QDBusAbstractInterface::isValid), + [](QDBusAbstractInterface *) -> bool { return false; }); + DoCallRecord record; + stubDoCall(stub, &record); + + ci->disableOutDevice("hwinfo-output"); + + // Assert + EXPECT_EQ(0, record.calls); + EXPECT_FALSE(ci->m_iface->isValid()); // 无效接口,跳过 DBus 调用 + delete ci; +} + +// m_iface 为空时直接跳过 DBus 调用,不崩溃 +TEST_F(ControlInterfaceTest, DisableOutDevice_NullIface_SkipsDBusCall) +{ + // (fixture 已打桩默认环境,无额外前置条件) + // Arrange + // Act + ControlInterface *ci = new ControlInterface(); + ci->m_iface = nullptr; + DoCallRecord record; + stubDoCall(stub, &record); + + ci->disableOutDevice("hwinfo-output"); + + // Assert + EXPECT_EQ(0, record.calls); + EXPECT_EQ(nullptr, ci->m_iface); // 空接口直接跳过 + delete ci; +} + +// ---------- ControlInterface::disableInDevice (controlinterface.cpp:42-48) ---------- +TEST_F(ControlInterfaceTest, DisableInDevice_ValidIface_CallsDBusWithoutArgs) +{ + // Arrange + // Act + // (fixture 已打桩默认环境,无额外前置条件) + ControlInterface *ci = new ControlInterface(); + stub.set_lamda(static_cast(&QDBusAbstractInterface::isValid), + [](QDBusAbstractInterface *) -> bool { return true; }); + DoCallRecord record; + stubDoCall(stub, &record); + + ci->disableInDevice(); + + // Assert + EXPECT_EQ(1, record.calls); + EXPECT_EQ("disableInDevice", record.method); + EXPECT_FALSE(record.hasFirstArg); + delete ci; +} + +TEST_F(ControlInterfaceTest, DisableInDevice_InvalidIface_SkipsDBusCall) +{ + // (fixture 已打桩默认环境,无额外前置条件) + // Arrange + // Act + ControlInterface *ci = new ControlInterface(); + stub.set_lamda(static_cast(&QDBusAbstractInterface::isValid), + [](QDBusAbstractInterface *) -> bool { return false; }); + DoCallRecord record; + stubDoCall(stub, &record); + + ci->disableInDevice(); + + // Assert + EXPECT_EQ(0, record.calls); + EXPECT_FALSE(ci->m_iface->isValid()); // 无效接口,跳过 DBus 调用 + delete ci; +} + +// ---------- ControlInterface::updateWakeup (controlinterface.cpp:50-56) ---------- +TEST_F(ControlInterfaceTest, UpdateWakeup_ValidIface_CallsDBusWithDevInfo) +{ + // Act + // (fixture 已打桩默认环境,无额外前置条件) + // Arrange + ControlInterface *ci = new ControlInterface(); + stub.set_lamda(static_cast(&QDBusAbstractInterface::isValid), + [](QDBusAbstractInterface *) -> bool { return true; }); + DoCallRecord record; + stubDoCall(stub, &record); + + ci->updateWakeup("wakeup-dev"); + + // Assert + EXPECT_EQ(1, record.calls); + EXPECT_EQ("updateWakeup", record.method); + ASSERT_TRUE(record.hasFirstArg); + EXPECT_EQ("wakeup-dev", record.firstArg.toString()); + delete ci; +} + +TEST_F(ControlInterfaceTest, UpdateWakeup_InvalidIface_SkipsDBusCall) +{ + // (fixture 已打桩默认环境,无额外前置条件) + // Arrange + // Act + ControlInterface *ci = new ControlInterface(); + stub.set_lamda(static_cast(&QDBusAbstractInterface::isValid), + [](QDBusAbstractInterface *) -> bool { return false; }); + DoCallRecord record; + stubDoCall(stub, &record); + + ci->updateWakeup("wakeup-dev"); + + // Assert + EXPECT_EQ(0, record.calls); + EXPECT_FALSE(ci->m_iface->isValid()); // 无效接口,跳过 DBus 调用 + delete ci; +} + +// ---------- ControlInterface::setMonitorWorkingDBFlag (controlinterface.cpp:58-64) ---------- +TEST_F(ControlInterfaceTest, SetMonitorWorkingDBFlag_ValidIface_CallsDBusWithFlag) +{ + // Act + // (fixture 已打桩默认环境,无额外前置条件) + // Arrange + ControlInterface *ci = new ControlInterface(); + stub.set_lamda(static_cast(&QDBusAbstractInterface::isValid), + [](QDBusAbstractInterface *) -> bool { return true; }); + DoCallRecord record; + stubDoCall(stub, &record); + + ci->setMonitorWorkingDBFlag(true); + // Assert + EXPECT_EQ(1, record.calls); + EXPECT_EQ("setMonitorWorkingDBFlag", record.method); + ASSERT_TRUE(record.hasFirstArg); + EXPECT_TRUE(record.firstArg.toBool()); + + record = DoCallRecord(); + ci->setMonitorWorkingDBFlag(false); + EXPECT_EQ(1, record.calls); + ASSERT_TRUE(record.hasFirstArg); + EXPECT_FALSE(record.firstArg.toBool()); + delete ci; +} + +TEST_F(ControlInterfaceTest, SetMonitorWorkingDBFlag_InvalidIface_SkipsDBusCall) +{ + // Act + // (fixture 已打桩默认环境,无额外前置条件) + // Arrange + ControlInterface *ci = new ControlInterface(); + stub.set_lamda(static_cast(&QDBusAbstractInterface::isValid), + [](QDBusAbstractInterface *) -> bool { return false; }); + DoCallRecord record; + stubDoCall(stub, &record); + + ci->setMonitorWorkingDBFlag(true); + + // Assert + EXPECT_EQ(0, record.calls); + EXPECT_FALSE(ci->m_iface->isValid()); // 无效接口,跳过 DBus 调用 + delete ci; +} + +// ---------- ControlInterface::monitorWorkingDBFlag (controlinterface.cpp:66-76) ---------- +TEST_F(ControlInterfaceTest, MonitorWorkingDBFlag_ValidReplyFalse_ReturnsFalse) +{ + // Act + // Arrange + // (fixture 已打桩默认环境,无额外前置条件) + ControlInterface *ci = new ControlInterface(); + stub.set_lamda(static_cast(&QDBusAbstractInterface::isValid), + [](QDBusAbstractInterface *) -> bool { return true; }); + DoCallRecord record; + stubDoCall(stub, &record, QVariant(false), true); + + // Assert + EXPECT_FALSE(ci->monitorWorkingDBFlag()); + EXPECT_EQ(1, record.calls); + delete ci; +} + +TEST_F(ControlInterfaceTest, MonitorWorkingDBFlag_ValidReplyTrue_ReturnsTrue) +{ + // Act + // (fixture 已打桩默认环境,无额外前置条件) + // Arrange + ControlInterface *ci = new ControlInterface(); + stub.set_lamda(static_cast(&QDBusAbstractInterface::isValid), + [](QDBusAbstractInterface *) -> bool { return true; }); + DoCallRecord record; + stubDoCall(stub, &record, QVariant(true), true); + + // Assert + EXPECT_TRUE(ci->monitorWorkingDBFlag()); + EXPECT_EQ(1, record.calls); // doCall 恰好执行一次 + delete ci; +} + +// 分支:应答消息非法(QDBusReply 无效)-> 兜底返回 true +TEST_F(ControlInterfaceTest, MonitorWorkingDBFlag_InvalidReply_ReturnsTrue) +{ + // Arrange + // Act + // (fixture 已打桩默认环境,无额外前置条件) + ControlInterface *ci = new ControlInterface(); + stub.set_lamda(static_cast(&QDBusAbstractInterface::isValid), + [](QDBusAbstractInterface *) -> bool { return true; }); + DoCallRecord record; + stubDoCall(stub, &record); + + // Assert + EXPECT_TRUE(ci->monitorWorkingDBFlag()); + EXPECT_EQ(1, record.calls); // 回复无效仍执行一次调用 + delete ci; +} + +// 分支:m_iface 无效 -> 兜底返回 true +TEST_F(ControlInterfaceTest, MonitorWorkingDBFlag_InvalidIface_ReturnsTrue) +{ + // Act + // (fixture 已打桩默认环境,无额外前置条件) + // Arrange + ControlInterface *ci = new ControlInterface(); + stub.set_lamda(static_cast(&QDBusAbstractInterface::isValid), + [](QDBusAbstractInterface *) -> bool { return false; }); + + // Assert + EXPECT_TRUE(ci->monitorWorkingDBFlag()); + EXPECT_FALSE(ci->m_iface->isValid()); // 无效接口走兜底路径 + delete ci; +} + +// 分支:m_iface 为空 -> 兜底返回 true +TEST_F(ControlInterfaceTest, MonitorWorkingDBFlag_NullIface_ReturnsTrue) +{ + // Act + // (fixture 已打桩默认环境,无额外前置条件) + // Arrange + ControlInterface *ci = new ControlInterface(); + ci->m_iface = nullptr; + + // Assert + EXPECT_TRUE(ci->monitorWorkingDBFlag()); + EXPECT_EQ(nullptr, ci->m_iface); // 空接口走兜底路径 + delete ci; +} From ac7c8f3988c0850bbd2239612d80f80567ea1d58 Mon Sep 17 00:00:00 2001 From: zhanghongyuan Date: Mon, 26 Oct 2026 20:14:11 +0800 Subject: [PATCH 5/8] =?UTF-8?q?test:=20=E6=96=B0=E5=A2=9E=20Plugin/DSM=20?= =?UTF-8?q?=E6=B3=A8=E5=86=8C=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95=EF=BC=88?= =?UTF-8?q?3=20=E7=94=A8=E4=BE=8B=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DSMRegister 创建 MainJob 服务对象并返回 0、空名称路径、DSMUnRegister deleteLater 清理路径 - MainJob::SetUp 打桩 monitorWorkingDBFlag 为 false,防止 DetectThread 监听循环导致用例收尾 SIGABRT(修复既有用例) --- .../tests/src/ut_plugin.cpp | 144 ++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 deepin-devicemanager-server/tests/src/ut_plugin.cpp diff --git a/deepin-devicemanager-server/tests/src/ut_plugin.cpp b/deepin-devicemanager-server/tests/src/ut_plugin.cpp new file mode 100644 index 000000000..fda76f54d --- /dev/null +++ b/deepin-devicemanager-server/tests/src/ut_plugin.cpp @@ -0,0 +1,144 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later +/* + * 用例计数声明(method | level | factors | min | actual) + * | DSMRegister | low | - | 1 | 2 | + * | DSMUnRegister | low | - | 1 | 1 | + */ + +#include + +#include +#include +#include + +#include + +#include "stubext.h" +#include "mainjob.h" +#include "controlinterface.h" + +using namespace stub_ext; + +// plugin.cpp 未提供头文件,此处按源码声明插件入口(extern "C") +extern "C" { +int DSMRegister(const char *name, void *data); +int DSMUnRegister(const char *name, void *data); +} + +class PluginTest : public ::testing::Test +{ +protected: + void SetUp() override + { + stub.clear(); + registered = false; + stubDependencies(); + } + + void TearDown() override + { + // 先在本测试内注销,随后刷新事件队列让 deleteLater 落地 + // 注意:processEvents 必须在 stub.clear() 之前执行, + // 这样 MainJob 延迟 1s 的初始化 lambda 若被触发,其内部 + // initDriverRepoSource / sqlCopytoKernel 仍处于打桩状态,不会写真实仓库配置 + if (registered) { + DSMUnRegister("org.deepin.DeviceInfo.ut", nullptr); + registered = false; + QApplication::processEvents(); + } + stub.clear(); + } + + // MainJob 构造链上的全部外部依赖打桩:hwinfo 探测、udev 监听、DBus、驱动仓库写操作 + void stubDependencies() + { + // hwinfo 探测(DetectThread 构造会执行一次) + stub.set_lamda(static_cast(&QProcess::start), + [](QProcess *, const QString &, const QStringList &, QIODevice::OpenMode) {}); + stub.set_lamda(static_cast(&QProcess::waitForFinished), + [](QProcess *, int) -> bool { return true; }); + stub.set_lamda(static_cast(&QProcess::readAllStandardOutput), + [](QProcess *) -> QByteArray { return QByteArray(); }); + + // udev 环境(MonitorUsb 构造依赖) + stub.set_lamda(udev_new, + []() -> struct udev * { return reinterpret_cast(0x1001); }); + stub.set_lamda(udev_monitor_new_from_netlink, + [](struct udev *, const char *) -> struct udev_monitor * { + return reinterpret_cast(0x2002); + }); + stub.set_lamda(udev_monitor_filter_add_match_subsystem_devtype, + [](struct udev_monitor *, const char *, const char *) -> int { return 0; }); + stub.set_lamda(udev_monitor_enable_receiving, + [](struct udev_monitor *) -> int { return 0; }); + stub.set_lamda(udev_monitor_get_fd, [](struct udev_monitor *) -> int { return 100; }); + + // 系统总线与监控开关:不依赖真实 DBus 服务 + stub.set_lamda(static_cast(&QDBusConnection::systemBus), + []() -> QDBusConnection { return QDBusConnection(QString()); }); + stub.set_lamda(static_cast(&ControlInterface::monitorWorkingDBFlag), + [](ControlInterface *) -> bool { return false; }); + + // MainJob 初始化路径中的重操作打桩:设备加载与延迟加载的驱动仓库/sql 操作 + stub.set_lamda(static_cast(&MainJob::updateAllDevice), + [this](MainJob *) { ++updateAllDeviceCalls; }); + stub.set_lamda(static_cast(&MainJob::initDriverRepoSource), + [](MainJob *) {}); + stub.set_lamda(static_cast(&MainJob::sqlCopytoKernel), + [](MainJob *) {}); + } + + stub_ext::StubExt stub; + int updateAllDeviceCalls = 0; + bool registered = false; +}; + +// ---------- DSMRegister (plugin.cpp:14-20) ---------- +// 行为:创建 MainJob(其中会加载一次全量设备信息)并返回 0 +TEST_F(PluginTest, DSMRegister_ValidServiceName_CreatesMainJobAndReturnsZero) +{ + // Arrange + updateAllDeviceCalls = 0; + + // Act + int ret = DSMRegister("org.deepin.DeviceInfo.ut", nullptr); + registered = true; + + // Assert + EXPECT_EQ(0, ret); + EXPECT_EQ(1, updateAllDeviceCalls); // MainJob 构造中执行了一次全量加载 +} + +// name 为空时同样正常注册,不崩溃 +TEST_F(PluginTest, DSMRegister_NullName_StillReturnsZero) +{ + // Act + // Arrange + // (fixture 已打桩默认环境,无额外前置条件) + int ret = DSMRegister(nullptr, nullptr); + registered = true; + + // Assert + EXPECT_EQ(0, ret); + EXPECT_EQ(1, updateAllDeviceCalls); +} + +// ---------- DSMUnRegister (plugin.cpp:22-30) ---------- +// 行为:注销返回 0(deleteLater 需要事件循环处理,TearDown 中已刷新) +TEST_F(PluginTest, DSMUnRegister_AfterRegister_ReturnsZero) +{ + // Act + // (fixture 已打桩默认环境,无额外前置条件) + // Assert + // Arrange + ASSERT_EQ(0, DSMRegister("org.deepin.DeviceInfo.ut", nullptr)); + updateAllDeviceCalls = 0; + + int ret = DSMUnRegister("org.deepin.DeviceInfo.ut", nullptr); + + EXPECT_EQ(0, ret); + // 注销后不再触发新的设备加载 + EXPECT_EQ(0, updateAllDeviceCalls); +} From c37094a66414b4514264591e4bf1b1776208677d Mon Sep 17 00:00:00 2001 From: zhanghongyuan Date: Mon, 26 Oct 2026 20:14:11 +0800 Subject: [PATCH 6/8] =?UTF-8?q?test:=20=E9=87=8D=E5=86=99=20DebugTimeManag?= =?UTF-8?q?er=20=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95=EF=BC=8816=20?= =?UTF-8?q?=E7=94=A8=E4=BE=8B=EF=BC=89=E5=B9=B6=E8=A1=A5=E9=BD=90=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E5=A4=B9=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 fixture 继承 ::testing::Test(SetUp/TearDown 内 stub.clear() 收口) - beginPointQt/endPointQt 经 QDateTime::currentMSecsSinceEpoch 打桩控制时钟,覆盖耗时计算与 0ms 边界;beginPointLinux 多点位独立记录;clear 清空语义 - 既有 UT_001-003 空断言用例重写为双断言;全部用例补齐 AAA 注释与三段式命名 --- .../tests/src/ut_debugtimemanager.cpp | 202 +++++++++++++++++- 1 file changed, 195 insertions(+), 7 deletions(-) diff --git a/deepin-devicemanager-server/tests/src/ut_debugtimemanager.cpp b/deepin-devicemanager-server/tests/src/ut_debugtimemanager.cpp index ba0277a3f..2072d4154 100644 --- a/deepin-devicemanager-server/tests/src/ut_debugtimemanager.cpp +++ b/deepin-devicemanager-server/tests/src/ut_debugtimemanager.cpp @@ -1,6 +1,15 @@ -// SPDX-FileCopyrightText: 2019 ~ 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later +/* + * 用例计数声明(method | level | factors | min | actual) + * | DebugTimeManager::DebugTimeManager | mid | in_degree:3 | 2 | 2 | + * | DebugTimeManager::beginPointLinux | mid | in_degree:2 | 2 | 4 | + * | DebugTimeManager::beginPointQt | low | - | 1 | 2 | + * | DebugTimeManager::clear | mid | in_degree:11,name_pattern:clear | 2 | 2 | + * | DebugTimeManager::endPointLinux | mid | in_degree:2 | 2 | 2 | + * | DebugTimeManager::endPointQt | low | - | 1 | 3 | + */ #include @@ -12,29 +21,208 @@ #undef private #undef protected #include "stub.h" +#include "stubext.h" -TEST(DebugTimeManager_Test, DebugTimeManager_UT_001) +#include + +using namespace stub_ext; + +// --------------------------------------------------------------------------- +// 测试夹具:DebugTimeManager_Test +// --------------------------------------------------------------------------- +class DebugTimeManager_Test : public ::testing::Test +{ +protected: + void SetUp() override { } + void TearDown() override { stub.clear(); } + + stub_ext::StubExt stub; +}; + +TEST_F(DebugTimeManager_Test, DebugTimeManager_UT_001) { + // Arrange DebugTimeManager *dtm = new DebugTimeManager(); + // Act dtm->beginPointLinux("001", ""); - ASSERT_FALSE(dtm->m_MapPoint.isEmpty()); + // Assert + EXPECT_FALSE(dtm->m_MapPoint.isEmpty()); + EXPECT_EQ(1, dtm->m_MapPoint.size()); delete dtm; } -TEST(DebugTimeManager_Test, DebugTimeManager_UT_002) +TEST_F(DebugTimeManager_Test, DebugTimeManager_UT_002) { + // Arrange DebugTimeManager *dtm = new DebugTimeManager(); + // Act dtm->beginPointLinux("001", ""); dtm->endPointLinux("001"); - ASSERT_TRUE(dtm->m_MapPoint["001"].time >= 0); + // Assert + EXPECT_GE(dtm->m_MapPoint["001"].time, static_cast(0)); + EXPECT_EQ(1, dtm->m_MapPoint.size()); + delete dtm; +} + +TEST_F(DebugTimeManager_Test, DebugTimeManager_UT_003) +{ + // Arrange + DebugTimeManager *dtm = new DebugTimeManager(); + // Act + dtm->clear(); + + // Assert + EXPECT_TRUE(dtm->m_MapPoint.isEmpty()); + EXPECT_EQ(size_t(0), dtm->m_MapPoint.size()); + delete dtm; +} + +/* ---------------- UT-T30 追加用例:beginPointQt / endPointQt / clear 补充分支 ---------------- */ + +// beginPointQt:写入点位描述与毫秒时间戳 +TEST_F(DebugTimeManager_Test, BeginPointQt_NewPoint_StoresStatusAndTimestamp) +{ + // Arrange + stub_ext::StubExt stub; + stub.set_lamda(static_cast(&QDateTime::currentMSecsSinceEpoch), + []() -> qint64 { return 1234567890; }); + + DebugTimeManager *dtm = new DebugTimeManager(); + // Act + dtm->beginPointQt("POINT-T30", "status-x"); + + // Assert + ASSERT_TRUE(dtm->m_MapPoint.contains("POINT-T30")); + EXPECT_EQ("status-x", dtm->m_MapPoint["POINT-T30"].desc); + EXPECT_EQ(1234567890, dtm->m_MapPoint["POINT-T30"].time); + + stub.clear(); + delete dtm; +} + +// beginPointQt:同名点位重复打点时覆盖旧记录而非新增 +TEST_F(DebugTimeManager_Test, BeginPointQt_OverwriteSamePoint_ReplacesEntry) +{ + // Arrange + DebugTimeManager *dtm = new DebugTimeManager(); + // Act + dtm->beginPointQt("POINT-T30", "first"); + dtm->beginPointQt("POINT-T30", "second"); + + // Assert + EXPECT_EQ(1, dtm->m_MapPoint.size()); + EXPECT_EQ("second", dtm->m_MapPoint["POINT-T30"].desc); + + delete dtm; +} + +// endPointQt:以调用时刻毫秒差更新点位耗时(QDateTime 打桩控制时钟) +TEST_F(DebugTimeManager_Test, EndPointQt_AfterInterval_ComputesElapsedMs) +{ + // Arrange + qint64 fakeTick = 1000; + stub_ext::StubExt stub; + stub.set_lamda(static_cast(&QDateTime::currentMSecsSinceEpoch), + [&fakeTick]() -> qint64 { + qint64 value = fakeTick; + fakeTick += 1234; // 第二次调用即结束时刻 + return value; + }); + + DebugTimeManager *dtm = new DebugTimeManager(); + // Act + dtm->beginPointQt("POINT-T30", ""); + dtm->endPointQt("POINT-T30"); + + // Assert + EXPECT_EQ(1234, dtm->m_MapPoint["POINT-T30"].time); + EXPECT_EQ(1, dtm->m_MapPoint.size()); // 只更新既有点位 + + stub.clear(); delete dtm; } -TEST(DebugTimeManager_Test, DebugTimeManager_UT_003) +// endPointQt:起止同一时刻时耗时为 0(边界) +TEST_F(DebugTimeManager_Test, EndPointQt_ZeroElapsed_BoundaryCase) { + // Arrange + stub_ext::StubExt stub; + stub.set_lamda(static_cast(&QDateTime::currentMSecsSinceEpoch), + []() -> qint64 { return 5000; }); + DebugTimeManager *dtm = new DebugTimeManager(); + // Act + dtm->beginPointQt("POINT-T30", ""); + dtm->endPointQt("POINT-T30"); + + // Assert + EXPECT_EQ(0, dtm->m_MapPoint["POINT-T30"].time); + EXPECT_FALSE(dtm->m_MapPoint.isEmpty()); // 点位仍保留 + + stub.clear(); + delete dtm; +} + +// endPointQt:结束不存在的点位时无副作用(不新增记录) +TEST_F(DebugTimeManager_Test, EndPointQt_MissingPoint_NoOp) +{ + // Arrange + DebugTimeManager *dtm = new DebugTimeManager(); + // Act + dtm->beginPointQt("POINT-T30", ""); + + dtm->endPointQt("POINT-NOT-EXIST"); + + // Assert + EXPECT_EQ(1, dtm->m_MapPoint.size()); + EXPECT_FALSE(dtm->m_MapPoint.contains("POINT-NOT-EXIST")); + + delete dtm; +} + +// beginPointLinux:不同点位各自独立记录 +TEST_F(DebugTimeManager_Test, BeginPointLinux_DistinctPoints_KeepsEntries) +{ + // Arrange + DebugTimeManager *dtm = new DebugTimeManager(); + // Act + dtm->beginPointLinux("POINT-P1", "s1"); + dtm->beginPointLinux("POINT-P2", "s2"); + + // Assert + EXPECT_EQ(2, dtm->m_MapPoint.size()); + EXPECT_EQ("s1", dtm->m_MapPoint["POINT-P1"].desc); + EXPECT_EQ("s2", dtm->m_MapPoint["POINT-P2"].desc); + + delete dtm; +} + +// clear:清空非空缓存后所有点位不可见 +TEST_F(DebugTimeManager_Test, Clear_NonEmptyCache_RemovesAllPoints) +{ + // Arrange + DebugTimeManager *dtm = new DebugTimeManager(); + // Act + dtm->beginPointLinux("POINT-P1", "s1"); + dtm->beginPointQt("POINT-P2", "s2"); + // Assert + ASSERT_FALSE(dtm->m_MapPoint.isEmpty()); + dtm->clear(); - ASSERT_TRUE(dtm->m_MapPoint.isEmpty()); + EXPECT_TRUE(dtm->m_MapPoint.isEmpty()); + EXPECT_EQ(size_t(0), dtm->m_MapPoint.size()); + delete dtm; } + +TEST_F(DebugTimeManager_Test, DebugTimeManager_DefaultCtor_MapStartsEmpty) +{ + // Arrange + // (无外部依赖,直接构造) + // Act + DebugTimeManager dtm; + // Assert + EXPECT_TRUE(dtm.m_MapPoint.isEmpty()); // 默认构造后无任何计时点 + EXPECT_EQ(size_t(0), dtm.m_MapPoint.size()); +} From 12a5abeee5566469585158d37c797ec2bb8d9542 Mon Sep 17 00:00:00 2001 From: zhanghongyuan Date: Mon, 26 Oct 2026 20:14:11 +0800 Subject: [PATCH 7/8] =?UTF-8?q?test:=20=E6=96=B0=E5=A2=9E=20customgpuinfo?= =?UTF-8?q?=20main=20=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95=EF=BC=8816=20?= =?UTF-8?q?=E7=94=A8=E4=BE=8B=EF=BC=8C=E7=8B=AC=E7=AB=8B=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E4=BA=8C=E8=BF=9B=E5=88=B6=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 经 #define main 重命名引入 main(),覆盖 getGpuBaseInfo(QProcess 打桩:进程未结束/字段解析)、getGpuMemInfoForFTDTM(QFile 打桩:打开失败/空内容/无匹配/MB 转换/循环边界多元素)、main 退出码与 stdout 输出(成功/失败/空映射/多元素) - getGpuMemInfoForFTDTM 打桩覆盖 main 的循环路径与退出码 --- .../customgpuinfo/test_customgpuinfo_main.cpp | 316 ++++++++++++++++++ 1 file changed, 316 insertions(+) create mode 100644 deepin-devicemanager-server/tests/customgpuinfo/test_customgpuinfo_main.cpp diff --git a/deepin-devicemanager-server/tests/customgpuinfo/test_customgpuinfo_main.cpp b/deepin-devicemanager-server/tests/customgpuinfo/test_customgpuinfo_main.cpp new file mode 100644 index 000000000..3e97892fa --- /dev/null +++ b/deepin-devicemanager-server/tests/customgpuinfo/test_customgpuinfo_main.cpp @@ -0,0 +1,316 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later +/* + * 用例计数声明(method | level | factors | min | actual) + * | getGpuBaseInfo | mid | complexity:5 | 2 | 4 | + * | getGpuMemInfoForFTDTM | high | complexity:7,transitive_loop_depth:3 | 4 | 8 | + * | main | high | transitive_loop_depth:4 | 4 | 4 | + */ + +// customgpuinfo/main.cpp 自带 main(),无法并入共享测试二进制; +// 此处将 main 宏替换后直接被测源文件,主函数以 custom_gpuinfo_original_main 名称参与测试 +#include +#include +#include +#include + +#define main custom_gpuinfo_original_main +#include "../../customgpuinfo/main.cpp" +#undef main + +#include + +#include + +#include "stubext.h" + +using namespace stub_ext; + +namespace { +constexpr int kArgcOne = 1; + +// hwinfo/glxinfo 均经 QProcess 探测,统一打桩 +void stubQProcessOutput(stub_ext::StubExt &stub, const QByteArray &output, bool finished = true) +{ + stub.set_lamda(static_cast(&QProcess::start), + [](QProcess *, const QString &, const QStringList &, QIODevice::OpenMode) {}); + stub.set_lamda(static_cast(&QProcess::waitForFinished), + [finished](QProcess *, int) -> bool { return finished; }); + stub.set_lamda(static_cast(&QProcess::readAllStandardOutput), + [output](QProcess *) -> QByteArray { return output; }); +} + +// /sys/kernel/debug/gc/total_mem 文件读取打桩(QFile::open + QIODevice::readAll) +// 两者均为 Qt 虚函数,成员指针地址被 Itanium ABI 编码为虚表索引,直接 addrof 会段错误; +// 且技能规则禁止对 Qt 内置类使用 VADDR。故按 mangled name 取导出符号地址后打桩。 +extern "C" bool _ZN5QFile4openE6QFlagsIN13QIODeviceBase12OpenModeFlagEE( + QFile *self, QIODevice::OpenMode mode); +extern "C" QByteArray _ZN9QIODevice7readAllEv(QIODevice *self); + +void stubFileContent(stub_ext::StubExt &stub, const QByteArray &content, bool openOk = true) +{ + stub.set_lamda(&_ZN5QFile4openE6QFlagsIN13QIODeviceBase12OpenModeFlagEE, + [openOk](QFile *, QIODevice::OpenMode) -> bool { return openOk; }); + stub.set_lamda(&_ZN9QIODevice7readAllEv, + [content](QIODevice *) -> QByteArray { return content; }); +} +} // namespace + +class CustomGpuInfoTest : public ::testing::Test +{ +protected: + void SetUp() override + { + stub.clear(); + } + + void TearDown() override + { + stub.clear(); + } + + stub_ext::StubExt stub; +}; + +// ---------- getGpuBaseInfo (customgpuinfo/main.cpp:21-50) ---------- +TEST_F(CustomGpuInfoTest, GetGpuBaseInfo_ProcessNotFinished_ReturnsFalse) +{ + // Arrange + stubQProcessOutput(stub, QByteArray(), false); + + QMap mapInfo; + // Act + // Assert + EXPECT_FALSE(getGpuBaseInfo(mapInfo)); + EXPECT_TRUE(mapInfo.isEmpty()); // 进程未结束,不产出任何字段 +} + +TEST_F(CustomGpuInfoTest, GetGpuBaseInfo_RendererAndVendorLines_ParsesAllFields) +{ + // Arrange + QByteArray output = + "OpenGL core profile version string: 4.6\n" + "OpenGL vendor string: Intel\n" + "OpenGL renderer string: Mesa Intel(R) UHD Graphics\n" + "OpenGL version string: 4.6 (Compatibility Profile)\n"; + stubQProcessOutput(stub, output); + + QMap mapInfo; + // Act + // Assert + EXPECT_TRUE(getGpuBaseInfo(mapInfo)); + + EXPECT_EQ("Intel", mapInfo[kVendor]); + EXPECT_EQ("Mesa Intel(R) UHD Graphics", mapInfo[kName]); + EXPECT_EQ("Mesa Intel(R) UHD Graphics", mapInfo[kModel]); +} + +// 边界:无可匹配行时仍返回 true,mapInfo 保持为空 +TEST_F(CustomGpuInfoTest, GetGpuBaseInfo_NoMatchingLines_ReturnsTrueWithEmptyMap) +{ + // Arrange + stubQProcessOutput(stub, QByteArray("some plain text\nanother line\n")); + + QMap mapInfo; + // Act + // Assert + EXPECT_TRUE(getGpuBaseInfo(mapInfo)); + EXPECT_TRUE(mapInfo.isEmpty()); +} + +// 边界:值列包含冒号时仅截取第一个冒号之前作为键 +TEST_F(CustomGpuInfoTest, GetGpuBaseInfo_ValueWithColons_ParsesFirstPairOnly) +{ + // Arrange + QByteArray output = "OpenGL renderer string: Adapter: Model: X\n"; + stubQProcessOutput(stub, output); + + QMap mapInfo; + // Act + // Assert + EXPECT_TRUE(getGpuBaseInfo(mapInfo)); + EXPECT_EQ("Adapter: Model: X", mapInfo[kName]); +} + +// ---------- getGpuMemInfoForFTDTM (customgpuinfo/main.cpp:52-96) ---------- +TEST_F(CustomGpuInfoTest, GetGpuMemInfoForFTDTM_OpenFails_ReturnsFalse) +{ + // Arrange + stubFileContent(stub, QByteArray(), false); + + QMap mapInfo; + // Assert + // Act + EXPECT_FALSE(getGpuMemInfoForFTDTM(mapInfo)); + EXPECT_TRUE(mapInfo.isEmpty()); // 文件打开失败,不产出内存信息 +} + +TEST_F(CustomGpuInfoTest, GetGpuMemInfoForFTDTM_EmptyContent_ReturnsFalse) +{ + // Arrange + stubFileContent(stub, QByteArray()); + + QMap mapInfo; + // Act + // Assert + EXPECT_FALSE(getGpuMemInfoForFTDTM(mapInfo)); + EXPECT_TRUE(mapInfo.isEmpty()); // 空文件内容不产出内存信息 +} + +TEST_F(CustomGpuInfoTest, GetGpuMemInfoForFTDTM_NoMemPattern_ReturnsFalse) +{ + // Arrange + stubFileContent(stub, QByteArray("no numeric memory info here")); + + QMap mapInfo; + // Assert + // Act + EXPECT_FALSE(getGpuMemInfoForFTDTM(mapInfo)); + EXPECT_TRUE(mapInfo.isEmpty()); // 无匹配数值格式,不产出内存信息 +} + +TEST_F(CustomGpuInfoTest, GetGpuMemInfoForFTDTM_MBValue_ConvertsToGB) +{ + // Arrange + stubFileContent(stub, QByteArray("Total: 512 (MB)\n")); + + QMap mapInfo; + // Assert + // Act + EXPECT_TRUE(getGpuMemInfoForFTDTM(mapInfo)); + EXPECT_EQ("0.50GB", mapInfo[kGraphicsMemory]); +} + +TEST_F(CustomGpuInfoTest, GetGpuMemInfoForFTDTM_GBValue_KeptAsGB) +{ + // Arrange + stubFileContent(stub, QByteArray("Total: 8 GB\n")); + + QMap mapInfo; + // Assert + // Act + EXPECT_TRUE(getGpuMemInfoForFTDTM(mapInfo)); + EXPECT_EQ("8.00GB", mapInfo[kGraphicsMemory]); +} + +TEST_F(CustomGpuInfoTest, GetGpuMemInfoForFTDTM_KBValue_ConvertsToGB) +{ + // Arrange + stubFileContent(stub, QByteArray("Total: 1048576 KB\n")); + + QMap mapInfo; + // Assert + // Act + EXPECT_TRUE(getGpuMemInfoForFTDTM(mapInfo)); + EXPECT_EQ("1.00GB", mapInfo[kGraphicsMemory]); +} + +TEST_F(CustomGpuInfoTest, GetGpuMemInfoForFTDTM_BytesValue_ConvertsToGB) +{ + // Arrange + stubFileContent(stub, QByteArray("Total: 1073741824 B\n")); + + QMap mapInfo; + // Assert + // Act + EXPECT_TRUE(getGpuMemInfoForFTDTM(mapInfo)); + EXPECT_EQ("1.00GB", mapInfo[kGraphicsMemory]); +} + +// 边界:带小数的 MB 值保留两位小数 +TEST_F(CustomGpuInfoTest, GetGpuMemInfoForFTDTM_DecimalMB_ConvertsPrecisely) +{ + // Arrange + stubFileContent(stub, QByteArray("1536.5 MB\n")); + + QMap mapInfo; + // Act + // Assert + EXPECT_TRUE(getGpuMemInfoForFTDTM(mapInfo)); + EXPECT_EQ("1.50GB", mapInfo[kGraphicsMemory]); +} + +// ---------- main (customgpuinfo/main.cpp:98-108) ---------- +// 分支:显存信息获取成功 -> 输出并返回 0 +TEST_F(CustomGpuInfoTest, Main_GetGpuMemInfoSucceeds_ReturnsZero) +{ + // Arrange + stub.set_lamda(static_cast &)>(getGpuMemInfoForFTDTM), + [](QMap &mapInfo) -> bool { + mapInfo.insert(kGraphicsMemory, "8.00GB"); + return true; + }); + char arg0[] = "customgpuinfo"; + char *argv[] = { arg0, nullptr }; + std::ostringstream oss; + std::streambuf *oldBuf = std::cout.rdbuf(oss.rdbuf()); + // Act + int ret = custom_gpuinfo_original_main(kArgcOne, argv); + std::cout.rdbuf(oldBuf); + // Assert + EXPECT_EQ(0, ret); + EXPECT_NE(std::string::npos, oss.str().find("8.00GB")); // 成功路径输出内存信息 +} + +// 分支:显存信息获取失败 -> 返回 1 +TEST_F(CustomGpuInfoTest, Main_GetGpuMemInfoFails_ReturnsOne) +{ + // Arrange + stub.set_lamda(static_cast &)>(getGpuMemInfoForFTDTM), + [](QMap &) -> bool { return false; }); + char arg0[] = "customgpuinfo"; + char *argv[] = { arg0, nullptr }; + std::ostringstream oss; + std::streambuf *oldBuf = std::cout.rdbuf(oss.rdbuf()); + // Act + int ret = custom_gpuinfo_original_main(kArgcOne, argv); + std::cout.rdbuf(oldBuf); + // Assert + EXPECT_EQ(1, ret); + EXPECT_TRUE(oss.str().empty()); // 失败路径不打印内存信息 +} + +TEST_F(CustomGpuInfoTest, Main_EmptyMemInfo_ReturnsZeroWithNoOutput) +{ + // Arrange + stub.set_lamda(static_cast &)>(getGpuMemInfoForFTDTM), + [](QMap &mapInfo) -> bool { + mapInfo.clear(); // 空映射:循环边界(空集合) + return true; + }); + char arg0[] = "customgpuinfo"; + char *argv[] = { arg0, nullptr }; + std::ostringstream oss; + std::streambuf *oldBuf = std::cout.rdbuf(oss.rdbuf()); + // Act + int ret = custom_gpuinfo_original_main(kArgcOne, argv); + std::cout.rdbuf(oldBuf); + // Assert + EXPECT_EQ(0, ret); + EXPECT_TRUE(oss.str().empty()); // 空映射不产生任何输出 +} + +TEST_F(CustomGpuInfoTest, Main_MultipleMemEntries_PrintsAllEntriesAndReturnsZero) +{ + // Arrange + stub.set_lamda(static_cast &)>(getGpuMemInfoForFTDTM), + [](QMap &mapInfo) -> bool { + mapInfo.insert("EntryA", "1.00GB"); + mapInfo.insert("EntryB", "2.00GB"); + mapInfo.insert("EntryC", "3.00GB"); // 循环边界(多元素) + return true; + }); + char arg0[] = "customgpuinfo"; + char *argv[] = { arg0, nullptr }; + std::ostringstream oss; + std::streambuf *oldBuf = std::cout.rdbuf(oss.rdbuf()); + // Act + int ret = custom_gpuinfo_original_main(kArgcOne, argv); + std::cout.rdbuf(oldBuf); + // Assert + EXPECT_EQ(0, ret); + EXPECT_NE(std::string::npos, oss.str().find("1.00GB")); + EXPECT_NE(std::string::npos, oss.str().find("2.00GB")); + EXPECT_NE(std::string::npos, oss.str().find("3.00GB")); +} From 11efe20873ca68d586172c41627beea033ff34bf Mon Sep 17 00:00:00 2001 From: zhanghongyuan Date: Tue, 27 Oct 2026 09:24:16 +0800 Subject: [PATCH 8/8] chore: use single-year SPDX header for new stub files The stub helper files (stub-shadow.cpp, stub-shadow.h, stubext.h) added by this PR reuse headers inherited from an older copy, whose copyright range "2021 - 2023" no longer satisfies the license-check CI. Replace it with a single 2026 SPDX-FileCopyrightText year since these files are effectively new in this PR, so the SPDX summary reports 0 failures. --- deepin-devicemanager-server/tests/3rdparty/stub/stub-shadow.cpp | 2 +- deepin-devicemanager-server/tests/3rdparty/stub/stub-shadow.h | 2 +- deepin-devicemanager-server/tests/3rdparty/stub/stubext.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/deepin-devicemanager-server/tests/3rdparty/stub/stub-shadow.cpp b/deepin-devicemanager-server/tests/3rdparty/stub/stub-shadow.cpp index 05d020920..bfac8c788 100644 --- a/deepin-devicemanager-server/tests/3rdparty/stub/stub-shadow.cpp +++ b/deepin-devicemanager-server/tests/3rdparty/stub/stub-shadow.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2021 - 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: MIT diff --git a/deepin-devicemanager-server/tests/3rdparty/stub/stub-shadow.h b/deepin-devicemanager-server/tests/3rdparty/stub/stub-shadow.h index 9199a12ab..e137f35a4 100644 --- a/deepin-devicemanager-server/tests/3rdparty/stub/stub-shadow.h +++ b/deepin-devicemanager-server/tests/3rdparty/stub/stub-shadow.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2021 - 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: MIT diff --git a/deepin-devicemanager-server/tests/3rdparty/stub/stubext.h b/deepin-devicemanager-server/tests/3rdparty/stub/stubext.h index a31d815d6..33961a7c3 100644 --- a/deepin-devicemanager-server/tests/3rdparty/stub/stubext.h +++ b/deepin-devicemanager-server/tests/3rdparty/stub/stubext.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2021 - 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: MIT