├── .gitignore ├── .gitmodules ├── Makefile ├── README.md ├── docs ├── BASIC_USAGE.md └── HACKING.md ├── includes ├── fmt │ ├── chrono.h │ ├── color.h │ ├── compile.h │ ├── core.h │ ├── format-inl.h │ ├── format.h │ ├── locale.h │ ├── os.h │ ├── ostream.h │ ├── posix.h │ ├── printf.h │ └── ranges.h ├── kern_context.h └── sysvers.h ├── kern_func.cpp ├── kern_func.hpp ├── kerninfra.hpp ├── kerninfra.mm ├── kernstructs.hpp ├── kernstructtest.cpp ├── libs ├── libkernrw.tbd └── libkrw.tbd ├── patchfinder ├── libdimentio.c └── libdimentio.h ├── rw_prov ├── rw_prov.h ├── rw_prov_libkernrw │ └── rw_prov_libkernrw.c ├── rw_prov_libkrw │ └── rw_prov_libkrw.c └── rw_prov_tfp0 │ └── rw_prov_tfp0.c └── rw_wrap ├── rw_wrap.cpp └── rw_wrap.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | .theos 2 | .vscode 3 | .theos 4 | .ccls-cache 5 | packages -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "rw_prov/rw_prov_libkrw/libkrw"] 2 | path = rw_prov/rw_prov_libkrw/libkrw 3 | url = https://github.com/Siguza/libkrw 4 | [submodule "rw_prov/rw_prov_libkernrw/libkernrw"] 5 | path = rw_prov/rw_prov_libkernrw/libkernrw 6 | url = https://github.com/ProcursusTeam/libkernrw 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ARCHS = arm64 arm64e 2 | #TARGET := iphone:clang:14.3:7.0 3 | 4 | include $(THEOS)/makefiles/common.mk 5 | 6 | # USE_TFP0 = 1 7 | # USE_LIBKRW = 1 8 | # USE_LIBKERNRW = 1 9 | 10 | SUBPROJECT_NAME = kerninfra 11 | LIBRARY_NAME = kerninfra 12 | LIB_DIR := lib 13 | 14 | kerninfra_FILES = patchfinder/libdimentio.c rw_wrap/rw_wrap.cpp kern_func.cpp kerninfra.mm 15 | kerninfra_CFLAGS = -fobjc-arc -Iincludes 16 | kerninfra_CCFLAGS = -std=c++2a -fvisibility=hidden 17 | kerninfra_FRAMEWORKS = IOKit 18 | #kerninfra_INSTALL_PATH = /usr/local/lib 19 | #kerninfra_LINKAGE_TYPE = static 20 | #kerninfra_LDFLAGS = -lcompression 21 | kerninfra_LIBRARIES = compression 22 | 23 | ifdef USE_TFP0 24 | kerninfra_FILES += rw_prov/rw_prov_tfp0/rw_prov_tfp0.c 25 | kerninfra_CFLAGS += -DUSE_TFP0 26 | endif 27 | ifdef USE_LIBKRW 28 | #kerninfra_FRAMEWORKS += kerninfra/libkrw 29 | kerninfra_LIBRARIES += krw 30 | kerninfra_LDFLAGS += -Lrw_prov/rw_prov_libkrw/libkrw 31 | kerninfra_FILES += rw_prov/rw_prov_libkrw/rw_prov_libkrw.c 32 | kerninfra_CFLAGS += -DUSE_LIBKRW 33 | endif 34 | ifdef USE_LIBKERNRW 35 | #kerninfra_FRAMEWORKS += kerninfra/libkernrw 36 | kerninfra_LIBRARIES += kernrw 37 | kerninfra_LDFLAGS += -Lrw_prov/rw_prov_libkernrw/libkernrw 38 | kerninfra_FILES += rw_prov/rw_prov_libkernrw/rw_prov_libkernrw.c 39 | kerninfra_CFLAGS += -DUSE_LIBKERNRW 40 | endif 41 | 42 | include $(THEOS_MAKE_PATH)/subproject.mk 43 | #include $(THEOS_MAKE_PATH)/library.mk 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KernInfra 2 | 3 | KernInfra is a developer-friendly kernel read-write framework. 4 | 5 | ## Why KernInfra 6 | 7 | KernInfra is built to address the following engineering issues: 8 | - kernel chain RW problem: for operations like `vnode_from_fd = proc_t->p_fd->fd_ofiles[0]->f_fglob->fg_data` 9 | - PAC pointer handling: tons of kxpacd calls 10 | - kernel read address checking: no more ~~Kernel Data Abort~~ 11 | - kernel offset management: no more `off_XXX_YYY` 12 | - multiple kern R/W provider: it's 2021, bye-bye tfp0, and hello libkrw/libkernrw 13 | - various simple but useful kernel helper functions: we need patchfinder to find proc, vnodes, tasks, etc. 14 | 15 | ## How does it look like 16 | 17 | - Before KernInfra: R/W `vnode->v_mount->mnt_flag` 18 | ```cpp 19 | printf("got vp: %llx\n", orig); 20 | uint64_t mount = kernel_read64(orig + off_v_mount); 21 | uint64_t kxpacd_mount = kxpacd(mount); 22 | printf(" %s: mount %llx\n", path, kxpacd_mount); 23 | uint32_t oriflag = kernel_read32(kxpacd_mount + off_mnt_flag); 24 | printf(" %s: oriflag %x\n", path, oriflag); 25 | kernel_write32(kxpacd_mount + off_mnt_flag, newflag); 26 | ``` 27 | 28 | - After KernInfra: RW `proc->task->map->page_shift` ( also compatible with Intellisense ;) ) 29 | ```cpp 30 | auto curp = proc_t_p(current_proc()); 31 | auto vPageShift = curp.task()._map().page_shift(); 32 | DLOG("original page shift: %d", vPageShift.load()); 33 | vPageShift.store(12); 34 | DLOG("new page shift: %d", vPageShift.load()); 35 | ``` 36 | 37 | ## Installation & Usage 38 | 39 | 1. `git submodule add https://github.com/NyaMisty/kerninfra && git submodule update --init --recursive` 40 | 2. insert these lines into theos makefile 41 | ```Makefile 42 | XXXX_SUBPROJECTS = kerninfra 43 | XXXX_LDFLAGS += -Lkerninfra/libs 44 | XXXX_CCFLAGS += -std=c++2a 45 | ``` 46 | 3. enable one of the kern R/W providers in your theos makefile 47 | ```Makefile 48 | export USE_TFP0=1 49 | export USE_LIBKRW=1 50 | export USE_LIBKERNRW=1 51 | ``` 52 | 4. include it: `#include "kerninfra/kerninfra.hpp"` 53 | 5. call init func: 54 | ```cpp 55 | if (!!init_kerninfra()) { 56 | fprintf(stderr, "Failed to init kerninfra!!\n"); 57 | exit(1); 58 | } else { 59 | DLOG("successfully initialized kerninfra!"); 60 | } 61 | ``` 62 | 6. profit~, see my fouldecrypt for a simple example, documents are located in docs/ directory ;) 63 | 64 | ## Contribute 65 | 66 | - You can add more offset into our code, it will benefit EVERYONE. (And maybe we can steal some offset from Odyssey :P) 67 | - You can implement more kernel helper functions 68 | - see more in docs/HACKING.md 69 | 70 | ## Credits 71 | - includes/fmt: it's fmt.dev's fmt, thanks a lot 72 | - libkrw: thanks @Siguza 73 | - libkernrw: thanks @CoolStar 74 | - libdimento: thanks @0x7ff (original dev) & @ichitaso (contributor) 75 | -------------------------------------------------------------------------------- /docs/BASIC_USAGE.md: -------------------------------------------------------------------------------- 1 | # Basic usage of KernInfra 2 | 3 | Lets begin with a sample code from kern_func.cpp: 4 | 5 | ```cpp 6 | addr_t proc_of_pid(pid_t pid) { 7 | addr_t procAddr = kernel_read64(allproc); 8 | uint64_t current_pid = 0; 9 | 10 | while (procAddr) { 11 | auto proc = proc_t_p(procAddr); 12 | current_pid = proc.p_pid().load(); 13 | if (current_pid == pid) return procAddr; 14 | procAddr = proc.nextproc().load(); 15 | printf("proc_of_pid: %llx %llu\n", procAddr, current_pid); 16 | } 17 | return 0; 18 | } 19 | ``` 20 | 21 | ## 1. initialize a remote object 22 | 23 | To begin with, we'll need to create an object like this: `auto proc = proc_t_p(procAddr)` 24 | 25 | With this statement, we created a proc object, which represents a proc_t object located at procAddr in kernel. 26 | (Note: The proc_t_p here is a short hand for proc_t<>) 27 | 28 | ## 2. access the field of a remote object 29 | 30 | With this object, you can access the remote fields using something like proc.p_pid(). 31 | This will create a new object represents a pid located at proc's p_pid offset. But as you can feel from my explaination, the proc.p_pid() call is NOT actually reading the p_pid field. Instead, it's more like a &proc->p_pid, which just store a offset from the parent pointer. 32 | 33 | The reason to do this, is that in this way we can build a reference to a field, which then enable us to do various things like chaining these field access. 34 | 35 | So to actually reading the pid, you would have to do proc.p_pid().load(), and correspondingly, use proc.p_pid().store(12345) to store a new value. 36 | 37 | ## 3. chaining 38 | 39 | Often you have to access several levels of structs, but only to retrive a single field. So in these cases, you can chain these operations, like `proc.p_fd().fd_ofiles().load()`. 40 | 41 | This will goes into the deepest level of field, and then retrive the value. The actual kernel R/W only happens at the last .load() call, (i.e. all previous calls are building references based on pointers and offsets.). 42 | 43 | ## 4. caching 44 | 45 | Kernel R/W is really expensive operation, so we can't simply chaining all the time. In KernInfra, each object has its pointer cache. Once an object has been used, it will save the pointer value retrived from parent struct's field, so next time it will not query the long chain again. 46 | 47 | Example: 48 | ```cpp 49 | auto curp = proc_t_p(current_proc()); 50 | auto vPageShift = curp.task()._map().page_shift(); 51 | DLOG("original page shift: %d", vPageShift.load()); 52 | vPageShift.store(12); 53 | DLOG("new page shift: %d", vPageShift.load()); 54 | ``` 55 | 56 | In this examle we need to use page_shift multiple time, so we saved the object representing pageShift, and then use it multiple times. 57 | 58 | On the first time (being loaded in DLOG), KernInfra will resolve vPageShift's actual address in kernel memory, and then load the value. 59 | Then in the later call, KernInfra will directly use the resolved address to perform R/W operations. 60 | 61 | ## 5. address checking and PAC handling 62 | 63 | When doing kernel research, one of the most frustrating thing is panicking devices due to accidentally operating on a NULL or userspace pointer, or forgetting to remove the PAC pointer. The only thing we got is blaming ourselves and wasting time on re-jailbreaking. 64 | 65 | With KernInfra, this will no longer be the problem. On each kernel read/write, KernInfra will check if the address looks like a kernel address (not Null, top 25 bit is totally 1), on invalid access, it will throw an exception telling which address is invalid, 66 | 67 | For PAC, in iOS14, quite a few data structures are protected by a so-called technique "data-PAC", which is encrypting some crucial kernel pointer in the struct. As the AAPL intentionally deleted all PAC-related information in kernel, it's laborious to check if a pointer is PAC-ed and removetp the PAC tag accordingly. 68 | 69 | With KernInfra, we offer you a special load_addr method. Using this method, KernInfra will automatically check the address, and return you an unPACed pointer. If you want to get the original pointer value, just use the original load() method. 70 | 71 | ## 6. offsets 72 | 73 | KernInfra will also manages struct's offsets for you. In kernstructs.hpp, you can see the field definitions of a remote struct. The offset is automatically setup by comparing kCFCoreFoundationVersionNumber. 74 | 75 | If you want to add missing offsets, see HACKING.md, and welcome to submit a PR if you are sure about the offset, 76 | 77 | -------------------------------------------------------------------------------- /docs/HACKING.md: -------------------------------------------------------------------------------- 1 | # Hacking and contributing to KernInfra 2 | 3 | ## Adding more offset / types 4 | 5 | It's pretty simple to add types, just define it like this: 6 | ``` 7 | REMOTETYPE(proc_t, 8 | REMOTE_FIELD(kuint32_t, p_pid, 9 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_12_0, 0x60) 10 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_0, 0x68) 11 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0_b1, 0x68) 12 | ); 13 | 14 | REMOTE_FIELD(uthread, p_uthlist, 15 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0_b1, 0x98) 16 | ); 17 | ) 18 | ``` 19 | the VERSION_OFF is actually parsed to `if (version > XXX) offset = YYY;`, so in general you can directly add corresponding offset by inserting more VERSION_OFF line while keeping sorted. 20 | 21 | -------------------------------------------------------------------------------- /includes/fmt/color.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - color support 2 | // 3 | // Copyright (c) 2018 - present, Victor Zverovich and fmt contributors 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_COLOR_H_ 9 | #define FMT_COLOR_H_ 10 | 11 | #include "format.h" 12 | 13 | FMT_BEGIN_NAMESPACE 14 | 15 | enum class color : uint32_t { 16 | alice_blue = 0xF0F8FF, // rgb(240,248,255) 17 | antique_white = 0xFAEBD7, // rgb(250,235,215) 18 | aqua = 0x00FFFF, // rgb(0,255,255) 19 | aquamarine = 0x7FFFD4, // rgb(127,255,212) 20 | azure = 0xF0FFFF, // rgb(240,255,255) 21 | beige = 0xF5F5DC, // rgb(245,245,220) 22 | bisque = 0xFFE4C4, // rgb(255,228,196) 23 | black = 0x000000, // rgb(0,0,0) 24 | blanched_almond = 0xFFEBCD, // rgb(255,235,205) 25 | blue = 0x0000FF, // rgb(0,0,255) 26 | blue_violet = 0x8A2BE2, // rgb(138,43,226) 27 | brown = 0xA52A2A, // rgb(165,42,42) 28 | burly_wood = 0xDEB887, // rgb(222,184,135) 29 | cadet_blue = 0x5F9EA0, // rgb(95,158,160) 30 | chartreuse = 0x7FFF00, // rgb(127,255,0) 31 | chocolate = 0xD2691E, // rgb(210,105,30) 32 | coral = 0xFF7F50, // rgb(255,127,80) 33 | cornflower_blue = 0x6495ED, // rgb(100,149,237) 34 | cornsilk = 0xFFF8DC, // rgb(255,248,220) 35 | crimson = 0xDC143C, // rgb(220,20,60) 36 | cyan = 0x00FFFF, // rgb(0,255,255) 37 | dark_blue = 0x00008B, // rgb(0,0,139) 38 | dark_cyan = 0x008B8B, // rgb(0,139,139) 39 | dark_golden_rod = 0xB8860B, // rgb(184,134,11) 40 | dark_gray = 0xA9A9A9, // rgb(169,169,169) 41 | dark_green = 0x006400, // rgb(0,100,0) 42 | dark_khaki = 0xBDB76B, // rgb(189,183,107) 43 | dark_magenta = 0x8B008B, // rgb(139,0,139) 44 | dark_olive_green = 0x556B2F, // rgb(85,107,47) 45 | dark_orange = 0xFF8C00, // rgb(255,140,0) 46 | dark_orchid = 0x9932CC, // rgb(153,50,204) 47 | dark_red = 0x8B0000, // rgb(139,0,0) 48 | dark_salmon = 0xE9967A, // rgb(233,150,122) 49 | dark_sea_green = 0x8FBC8F, // rgb(143,188,143) 50 | dark_slate_blue = 0x483D8B, // rgb(72,61,139) 51 | dark_slate_gray = 0x2F4F4F, // rgb(47,79,79) 52 | dark_turquoise = 0x00CED1, // rgb(0,206,209) 53 | dark_violet = 0x9400D3, // rgb(148,0,211) 54 | deep_pink = 0xFF1493, // rgb(255,20,147) 55 | deep_sky_blue = 0x00BFFF, // rgb(0,191,255) 56 | dim_gray = 0x696969, // rgb(105,105,105) 57 | dodger_blue = 0x1E90FF, // rgb(30,144,255) 58 | fire_brick = 0xB22222, // rgb(178,34,34) 59 | floral_white = 0xFFFAF0, // rgb(255,250,240) 60 | forest_green = 0x228B22, // rgb(34,139,34) 61 | fuchsia = 0xFF00FF, // rgb(255,0,255) 62 | gainsboro = 0xDCDCDC, // rgb(220,220,220) 63 | ghost_white = 0xF8F8FF, // rgb(248,248,255) 64 | gold = 0xFFD700, // rgb(255,215,0) 65 | golden_rod = 0xDAA520, // rgb(218,165,32) 66 | gray = 0x808080, // rgb(128,128,128) 67 | green = 0x008000, // rgb(0,128,0) 68 | green_yellow = 0xADFF2F, // rgb(173,255,47) 69 | honey_dew = 0xF0FFF0, // rgb(240,255,240) 70 | hot_pink = 0xFF69B4, // rgb(255,105,180) 71 | indian_red = 0xCD5C5C, // rgb(205,92,92) 72 | indigo = 0x4B0082, // rgb(75,0,130) 73 | ivory = 0xFFFFF0, // rgb(255,255,240) 74 | khaki = 0xF0E68C, // rgb(240,230,140) 75 | lavender = 0xE6E6FA, // rgb(230,230,250) 76 | lavender_blush = 0xFFF0F5, // rgb(255,240,245) 77 | lawn_green = 0x7CFC00, // rgb(124,252,0) 78 | lemon_chiffon = 0xFFFACD, // rgb(255,250,205) 79 | light_blue = 0xADD8E6, // rgb(173,216,230) 80 | light_coral = 0xF08080, // rgb(240,128,128) 81 | light_cyan = 0xE0FFFF, // rgb(224,255,255) 82 | light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210) 83 | light_gray = 0xD3D3D3, // rgb(211,211,211) 84 | light_green = 0x90EE90, // rgb(144,238,144) 85 | light_pink = 0xFFB6C1, // rgb(255,182,193) 86 | light_salmon = 0xFFA07A, // rgb(255,160,122) 87 | light_sea_green = 0x20B2AA, // rgb(32,178,170) 88 | light_sky_blue = 0x87CEFA, // rgb(135,206,250) 89 | light_slate_gray = 0x778899, // rgb(119,136,153) 90 | light_steel_blue = 0xB0C4DE, // rgb(176,196,222) 91 | light_yellow = 0xFFFFE0, // rgb(255,255,224) 92 | lime = 0x00FF00, // rgb(0,255,0) 93 | lime_green = 0x32CD32, // rgb(50,205,50) 94 | linen = 0xFAF0E6, // rgb(250,240,230) 95 | magenta = 0xFF00FF, // rgb(255,0,255) 96 | maroon = 0x800000, // rgb(128,0,0) 97 | medium_aquamarine = 0x66CDAA, // rgb(102,205,170) 98 | medium_blue = 0x0000CD, // rgb(0,0,205) 99 | medium_orchid = 0xBA55D3, // rgb(186,85,211) 100 | medium_purple = 0x9370DB, // rgb(147,112,219) 101 | medium_sea_green = 0x3CB371, // rgb(60,179,113) 102 | medium_slate_blue = 0x7B68EE, // rgb(123,104,238) 103 | medium_spring_green = 0x00FA9A, // rgb(0,250,154) 104 | medium_turquoise = 0x48D1CC, // rgb(72,209,204) 105 | medium_violet_red = 0xC71585, // rgb(199,21,133) 106 | midnight_blue = 0x191970, // rgb(25,25,112) 107 | mint_cream = 0xF5FFFA, // rgb(245,255,250) 108 | misty_rose = 0xFFE4E1, // rgb(255,228,225) 109 | moccasin = 0xFFE4B5, // rgb(255,228,181) 110 | navajo_white = 0xFFDEAD, // rgb(255,222,173) 111 | navy = 0x000080, // rgb(0,0,128) 112 | old_lace = 0xFDF5E6, // rgb(253,245,230) 113 | olive = 0x808000, // rgb(128,128,0) 114 | olive_drab = 0x6B8E23, // rgb(107,142,35) 115 | orange = 0xFFA500, // rgb(255,165,0) 116 | orange_red = 0xFF4500, // rgb(255,69,0) 117 | orchid = 0xDA70D6, // rgb(218,112,214) 118 | pale_golden_rod = 0xEEE8AA, // rgb(238,232,170) 119 | pale_green = 0x98FB98, // rgb(152,251,152) 120 | pale_turquoise = 0xAFEEEE, // rgb(175,238,238) 121 | pale_violet_red = 0xDB7093, // rgb(219,112,147) 122 | papaya_whip = 0xFFEFD5, // rgb(255,239,213) 123 | peach_puff = 0xFFDAB9, // rgb(255,218,185) 124 | peru = 0xCD853F, // rgb(205,133,63) 125 | pink = 0xFFC0CB, // rgb(255,192,203) 126 | plum = 0xDDA0DD, // rgb(221,160,221) 127 | powder_blue = 0xB0E0E6, // rgb(176,224,230) 128 | purple = 0x800080, // rgb(128,0,128) 129 | rebecca_purple = 0x663399, // rgb(102,51,153) 130 | red = 0xFF0000, // rgb(255,0,0) 131 | rosy_brown = 0xBC8F8F, // rgb(188,143,143) 132 | royal_blue = 0x4169E1, // rgb(65,105,225) 133 | saddle_brown = 0x8B4513, // rgb(139,69,19) 134 | salmon = 0xFA8072, // rgb(250,128,114) 135 | sandy_brown = 0xF4A460, // rgb(244,164,96) 136 | sea_green = 0x2E8B57, // rgb(46,139,87) 137 | sea_shell = 0xFFF5EE, // rgb(255,245,238) 138 | sienna = 0xA0522D, // rgb(160,82,45) 139 | silver = 0xC0C0C0, // rgb(192,192,192) 140 | sky_blue = 0x87CEEB, // rgb(135,206,235) 141 | slate_blue = 0x6A5ACD, // rgb(106,90,205) 142 | slate_gray = 0x708090, // rgb(112,128,144) 143 | snow = 0xFFFAFA, // rgb(255,250,250) 144 | spring_green = 0x00FF7F, // rgb(0,255,127) 145 | steel_blue = 0x4682B4, // rgb(70,130,180) 146 | tan = 0xD2B48C, // rgb(210,180,140) 147 | teal = 0x008080, // rgb(0,128,128) 148 | thistle = 0xD8BFD8, // rgb(216,191,216) 149 | tomato = 0xFF6347, // rgb(255,99,71) 150 | turquoise = 0x40E0D0, // rgb(64,224,208) 151 | violet = 0xEE82EE, // rgb(238,130,238) 152 | wheat = 0xF5DEB3, // rgb(245,222,179) 153 | white = 0xFFFFFF, // rgb(255,255,255) 154 | white_smoke = 0xF5F5F5, // rgb(245,245,245) 155 | yellow = 0xFFFF00, // rgb(255,255,0) 156 | yellow_green = 0x9ACD32 // rgb(154,205,50) 157 | }; // enum class color 158 | 159 | enum class terminal_color : uint8_t { 160 | black = 30, 161 | red, 162 | green, 163 | yellow, 164 | blue, 165 | magenta, 166 | cyan, 167 | white, 168 | bright_black = 90, 169 | bright_red, 170 | bright_green, 171 | bright_yellow, 172 | bright_blue, 173 | bright_magenta, 174 | bright_cyan, 175 | bright_white 176 | }; 177 | 178 | enum class emphasis : uint8_t { 179 | bold = 1, 180 | italic = 1 << 1, 181 | underline = 1 << 2, 182 | strikethrough = 1 << 3 183 | }; 184 | 185 | // rgb is a struct for red, green and blue colors. 186 | // Using the name "rgb" makes some editors show the color in a tooltip. 187 | struct rgb { 188 | FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {} 189 | FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {} 190 | FMT_CONSTEXPR rgb(uint32_t hex) 191 | : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {} 192 | FMT_CONSTEXPR rgb(color hex) 193 | : r((uint32_t(hex) >> 16) & 0xFF), 194 | g((uint32_t(hex) >> 8) & 0xFF), 195 | b(uint32_t(hex) & 0xFF) {} 196 | uint8_t r; 197 | uint8_t g; 198 | uint8_t b; 199 | }; 200 | 201 | namespace detail { 202 | 203 | // color is a struct of either a rgb color or a terminal color. 204 | struct color_type { 205 | FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {} 206 | FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true), 207 | value{} { 208 | value.rgb_color = static_cast(rgb_color); 209 | } 210 | FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} { 211 | value.rgb_color = (static_cast(rgb_color.r) << 16) | 212 | (static_cast(rgb_color.g) << 8) | rgb_color.b; 213 | } 214 | FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(), 215 | value{} { 216 | value.term_color = static_cast(term_color); 217 | } 218 | bool is_rgb; 219 | union color_union { 220 | uint8_t term_color; 221 | uint32_t rgb_color; 222 | } value; 223 | }; 224 | } // namespace detail 225 | 226 | // Experimental text formatting support. 227 | class text_style { 228 | public: 229 | FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT 230 | : set_foreground_color(), 231 | set_background_color(), 232 | ems(em) {} 233 | 234 | FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) { 235 | if (!set_foreground_color) { 236 | set_foreground_color = rhs.set_foreground_color; 237 | foreground_color = rhs.foreground_color; 238 | } else if (rhs.set_foreground_color) { 239 | if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) 240 | FMT_THROW(format_error("can't OR a terminal color")); 241 | foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color; 242 | } 243 | 244 | if (!set_background_color) { 245 | set_background_color = rhs.set_background_color; 246 | background_color = rhs.background_color; 247 | } else if (rhs.set_background_color) { 248 | if (!background_color.is_rgb || !rhs.background_color.is_rgb) 249 | FMT_THROW(format_error("can't OR a terminal color")); 250 | background_color.value.rgb_color |= rhs.background_color.value.rgb_color; 251 | } 252 | 253 | ems = static_cast(static_cast(ems) | 254 | static_cast(rhs.ems)); 255 | return *this; 256 | } 257 | 258 | friend FMT_CONSTEXPR text_style operator|(text_style lhs, 259 | const text_style& rhs) { 260 | return lhs |= rhs; 261 | } 262 | 263 | FMT_CONSTEXPR text_style& operator&=(const text_style& rhs) { 264 | if (!set_foreground_color) { 265 | set_foreground_color = rhs.set_foreground_color; 266 | foreground_color = rhs.foreground_color; 267 | } else if (rhs.set_foreground_color) { 268 | if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) 269 | FMT_THROW(format_error("can't AND a terminal color")); 270 | foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color; 271 | } 272 | 273 | if (!set_background_color) { 274 | set_background_color = rhs.set_background_color; 275 | background_color = rhs.background_color; 276 | } else if (rhs.set_background_color) { 277 | if (!background_color.is_rgb || !rhs.background_color.is_rgb) 278 | FMT_THROW(format_error("can't AND a terminal color")); 279 | background_color.value.rgb_color &= rhs.background_color.value.rgb_color; 280 | } 281 | 282 | ems = static_cast(static_cast(ems) & 283 | static_cast(rhs.ems)); 284 | return *this; 285 | } 286 | 287 | friend FMT_CONSTEXPR text_style operator&(text_style lhs, 288 | const text_style& rhs) { 289 | return lhs &= rhs; 290 | } 291 | 292 | FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT { 293 | return set_foreground_color; 294 | } 295 | FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT { 296 | return set_background_color; 297 | } 298 | FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT { 299 | return static_cast(ems) != 0; 300 | } 301 | FMT_CONSTEXPR detail::color_type get_foreground() const FMT_NOEXCEPT { 302 | FMT_ASSERT(has_foreground(), "no foreground specified for this style"); 303 | return foreground_color; 304 | } 305 | FMT_CONSTEXPR detail::color_type get_background() const FMT_NOEXCEPT { 306 | FMT_ASSERT(has_background(), "no background specified for this style"); 307 | return background_color; 308 | } 309 | FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT { 310 | FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); 311 | return ems; 312 | } 313 | 314 | private: 315 | FMT_CONSTEXPR text_style(bool is_foreground, 316 | detail::color_type text_color) FMT_NOEXCEPT 317 | : set_foreground_color(), 318 | set_background_color(), 319 | ems() { 320 | if (is_foreground) { 321 | foreground_color = text_color; 322 | set_foreground_color = true; 323 | } else { 324 | background_color = text_color; 325 | set_background_color = true; 326 | } 327 | } 328 | 329 | friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground) 330 | FMT_NOEXCEPT; 331 | friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background) 332 | FMT_NOEXCEPT; 333 | 334 | detail::color_type foreground_color; 335 | detail::color_type background_color; 336 | bool set_foreground_color; 337 | bool set_background_color; 338 | emphasis ems; 339 | }; 340 | 341 | FMT_CONSTEXPR text_style fg(detail::color_type foreground) FMT_NOEXCEPT { 342 | return text_style(/*is_foreground=*/true, foreground); 343 | } 344 | 345 | FMT_CONSTEXPR text_style bg(detail::color_type background) FMT_NOEXCEPT { 346 | return text_style(/*is_foreground=*/false, background); 347 | } 348 | 349 | FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT { 350 | return text_style(lhs) | rhs; 351 | } 352 | 353 | namespace detail { 354 | 355 | template struct ansi_color_escape { 356 | FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, 357 | const char* esc) FMT_NOEXCEPT { 358 | // If we have a terminal color, we need to output another escape code 359 | // sequence. 360 | if (!text_color.is_rgb) { 361 | bool is_background = esc == detail::data::background_color; 362 | uint32_t value = text_color.value.term_color; 363 | // Background ASCII codes are the same as the foreground ones but with 364 | // 10 more. 365 | if (is_background) value += 10u; 366 | 367 | size_t index = 0; 368 | buffer[index++] = static_cast('\x1b'); 369 | buffer[index++] = static_cast('['); 370 | 371 | if (value >= 100u) { 372 | buffer[index++] = static_cast('1'); 373 | value %= 100u; 374 | } 375 | buffer[index++] = static_cast('0' + value / 10u); 376 | buffer[index++] = static_cast('0' + value % 10u); 377 | 378 | buffer[index++] = static_cast('m'); 379 | buffer[index++] = static_cast('\0'); 380 | return; 381 | } 382 | 383 | for (int i = 0; i < 7; i++) { 384 | buffer[i] = static_cast(esc[i]); 385 | } 386 | rgb color(text_color.value.rgb_color); 387 | to_esc(color.r, buffer + 7, ';'); 388 | to_esc(color.g, buffer + 11, ';'); 389 | to_esc(color.b, buffer + 15, 'm'); 390 | buffer[19] = static_cast(0); 391 | } 392 | FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT { 393 | uint8_t em_codes[4] = {}; 394 | uint8_t em_bits = static_cast(em); 395 | if (em_bits & static_cast(emphasis::bold)) em_codes[0] = 1; 396 | if (em_bits & static_cast(emphasis::italic)) em_codes[1] = 3; 397 | if (em_bits & static_cast(emphasis::underline)) em_codes[2] = 4; 398 | if (em_bits & static_cast(emphasis::strikethrough)) 399 | em_codes[3] = 9; 400 | 401 | size_t index = 0; 402 | for (int i = 0; i < 4; ++i) { 403 | if (!em_codes[i]) continue; 404 | buffer[index++] = static_cast('\x1b'); 405 | buffer[index++] = static_cast('['); 406 | buffer[index++] = static_cast('0' + em_codes[i]); 407 | buffer[index++] = static_cast('m'); 408 | } 409 | buffer[index++] = static_cast(0); 410 | } 411 | FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; } 412 | 413 | FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; } 414 | FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT { 415 | return buffer + std::char_traits::length(buffer); 416 | } 417 | 418 | private: 419 | Char buffer[7u + 3u * 4u + 1u]; 420 | 421 | static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, 422 | char delimiter) FMT_NOEXCEPT { 423 | out[0] = static_cast('0' + c / 100); 424 | out[1] = static_cast('0' + c / 10 % 10); 425 | out[2] = static_cast('0' + c % 10); 426 | out[3] = static_cast(delimiter); 427 | } 428 | }; 429 | 430 | template 431 | FMT_CONSTEXPR ansi_color_escape make_foreground_color( 432 | detail::color_type foreground) FMT_NOEXCEPT { 433 | return ansi_color_escape(foreground, detail::data::foreground_color); 434 | } 435 | 436 | template 437 | FMT_CONSTEXPR ansi_color_escape make_background_color( 438 | detail::color_type background) FMT_NOEXCEPT { 439 | return ansi_color_escape(background, detail::data::background_color); 440 | } 441 | 442 | template 443 | FMT_CONSTEXPR ansi_color_escape make_emphasis(emphasis em) FMT_NOEXCEPT { 444 | return ansi_color_escape(em); 445 | } 446 | 447 | template 448 | inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT { 449 | std::fputs(chars, stream); 450 | } 451 | 452 | template <> 453 | inline void fputs(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT { 454 | std::fputws(chars, stream); 455 | } 456 | 457 | template inline void reset_color(FILE* stream) FMT_NOEXCEPT { 458 | fputs(detail::data::reset_color, stream); 459 | } 460 | 461 | template <> inline void reset_color(FILE* stream) FMT_NOEXCEPT { 462 | fputs(detail::data::wreset_color, stream); 463 | } 464 | 465 | template 466 | inline void reset_color(buffer& buffer) FMT_NOEXCEPT { 467 | const char* begin = data::reset_color; 468 | const char* end = begin + sizeof(data::reset_color) - 1; 469 | buffer.append(begin, end); 470 | } 471 | 472 | template 473 | void vformat_to(buffer& buf, const text_style& ts, 474 | basic_string_view format_str, 475 | basic_format_args>> args) { 476 | bool has_style = false; 477 | if (ts.has_emphasis()) { 478 | has_style = true; 479 | auto emphasis = detail::make_emphasis(ts.get_emphasis()); 480 | buf.append(emphasis.begin(), emphasis.end()); 481 | } 482 | if (ts.has_foreground()) { 483 | has_style = true; 484 | auto foreground = detail::make_foreground_color(ts.get_foreground()); 485 | buf.append(foreground.begin(), foreground.end()); 486 | } 487 | if (ts.has_background()) { 488 | has_style = true; 489 | auto background = detail::make_background_color(ts.get_background()); 490 | buf.append(background.begin(), background.end()); 491 | } 492 | detail::vformat_to(buf, format_str, args); 493 | if (has_style) detail::reset_color(buf); 494 | } 495 | } // namespace detail 496 | 497 | template > 498 | void vprint(std::FILE* f, const text_style& ts, const S& format, 499 | basic_format_args>> args) { 500 | basic_memory_buffer buf; 501 | detail::vformat_to(buf, ts, to_string_view(format), args); 502 | buf.push_back(Char(0)); 503 | detail::fputs(buf.data(), f); 504 | } 505 | 506 | /** 507 | \rst 508 | Formats a string and prints it to the specified file stream using ANSI 509 | escape sequences to specify text formatting. 510 | 511 | **Example**:: 512 | 513 | fmt::print(fmt::emphasis::bold | fg(fmt::color::red), 514 | "Elapsed time: {0:.2f} seconds", 1.23); 515 | \endrst 516 | */ 517 | template ::value)> 519 | void print(std::FILE* f, const text_style& ts, const S& format_str, 520 | const Args&... args) { 521 | vprint(f, ts, format_str, 522 | fmt::make_args_checked(format_str, args...)); 523 | } 524 | 525 | /** 526 | Formats a string and prints it to stdout using ANSI escape sequences to 527 | specify text formatting. 528 | Example: 529 | fmt::print(fmt::emphasis::bold | fg(fmt::color::red), 530 | "Elapsed time: {0:.2f} seconds", 1.23); 531 | */ 532 | template ::value)> 534 | void print(const text_style& ts, const S& format_str, const Args&... args) { 535 | return print(stdout, ts, format_str, args...); 536 | } 537 | 538 | template > 539 | inline std::basic_string vformat( 540 | const text_style& ts, const S& format_str, 541 | basic_format_args>> args) { 542 | basic_memory_buffer buf; 543 | detail::vformat_to(buf, ts, to_string_view(format_str), args); 544 | return fmt::to_string(buf); 545 | } 546 | 547 | /** 548 | \rst 549 | Formats arguments and returns the result as a string using ANSI 550 | escape sequences to specify text formatting. 551 | 552 | **Example**:: 553 | 554 | #include 555 | std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), 556 | "The answer is {}", 42); 557 | \endrst 558 | */ 559 | template > 560 | inline std::basic_string format(const text_style& ts, const S& format_str, 561 | const Args&... args) { 562 | return vformat(ts, to_string_view(format_str), 563 | fmt::make_args_checked(format_str, args...)); 564 | } 565 | 566 | /** 567 | Formats a string with the given text_style and writes the output to ``out``. 568 | */ 569 | template ::value)> 571 | OutputIt vformat_to( 572 | OutputIt out, const text_style& ts, basic_string_view format_str, 573 | basic_format_args>> args) { 574 | decltype(detail::get_buffer(out)) buf(detail::get_buffer_init(out)); 575 | detail::vformat_to(buf, ts, format_str, args); 576 | return detail::get_iterator(buf); 577 | } 578 | 579 | /** 580 | \rst 581 | Formats arguments with the given text_style, writes the result to the output 582 | iterator ``out`` and returns the iterator past the end of the output range. 583 | 584 | **Example**:: 585 | 586 | std::vector out; 587 | fmt::format_to(std::back_inserter(out), 588 | fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); 589 | \endrst 590 | */ 591 | template >::value&& 593 | detail::is_string::value> 594 | inline auto format_to(OutputIt out, const text_style& ts, const S& format_str, 595 | Args&&... args) -> 596 | typename std::enable_if::type { 597 | return vformat_to(out, ts, to_string_view(format_str), 598 | fmt::make_args_checked(format_str, args...)); 599 | } 600 | 601 | FMT_END_NAMESPACE 602 | 603 | #endif // FMT_COLOR_H_ 604 | -------------------------------------------------------------------------------- /includes/fmt/compile.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - experimental format string compilation 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich and fmt contributors 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_COMPILE_H_ 9 | #define FMT_COMPILE_H_ 10 | 11 | #include 12 | 13 | #include "format.h" 14 | 15 | FMT_BEGIN_NAMESPACE 16 | namespace detail { 17 | 18 | // A compile-time string which is compiled into fast formatting code. 19 | class compiled_string {}; 20 | 21 | template 22 | struct is_compiled_string : std::is_base_of {}; 23 | 24 | /** 25 | \rst 26 | Converts a string literal *s* into a format string that will be parsed at 27 | compile time and converted into efficient formatting code. Requires C++17 28 | ``constexpr if`` compiler support. 29 | 30 | **Example**:: 31 | 32 | // Converts 42 into std::string using the most efficient method and no 33 | // runtime format string processing. 34 | std::string s = fmt::format(FMT_COMPILE("{}"), 42); 35 | \endrst 36 | */ 37 | #define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::detail::compiled_string) 38 | 39 | template 40 | const T& first(const T& value, const Tail&...) { 41 | return value; 42 | } 43 | 44 | // Part of a compiled format string. It can be either literal text or a 45 | // replacement field. 46 | template struct format_part { 47 | enum class kind { arg_index, arg_name, text, replacement }; 48 | 49 | struct replacement { 50 | arg_ref arg_id; 51 | dynamic_format_specs specs; 52 | }; 53 | 54 | kind part_kind; 55 | union value { 56 | int arg_index; 57 | basic_string_view str; 58 | replacement repl; 59 | 60 | FMT_CONSTEXPR value(int index = 0) : arg_index(index) {} 61 | FMT_CONSTEXPR value(basic_string_view s) : str(s) {} 62 | FMT_CONSTEXPR value(replacement r) : repl(r) {} 63 | } val; 64 | // Position past the end of the argument id. 65 | const Char* arg_id_end = nullptr; 66 | 67 | FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {}) 68 | : part_kind(k), val(v) {} 69 | 70 | static FMT_CONSTEXPR format_part make_arg_index(int index) { 71 | return format_part(kind::arg_index, index); 72 | } 73 | static FMT_CONSTEXPR format_part make_arg_name(basic_string_view name) { 74 | return format_part(kind::arg_name, name); 75 | } 76 | static FMT_CONSTEXPR format_part make_text(basic_string_view text) { 77 | return format_part(kind::text, text); 78 | } 79 | static FMT_CONSTEXPR format_part make_replacement(replacement repl) { 80 | return format_part(kind::replacement, repl); 81 | } 82 | }; 83 | 84 | template struct part_counter { 85 | unsigned num_parts = 0; 86 | 87 | FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { 88 | if (begin != end) ++num_parts; 89 | } 90 | 91 | FMT_CONSTEXPR int on_arg_id() { return ++num_parts, 0; } 92 | FMT_CONSTEXPR int on_arg_id(int) { return ++num_parts, 0; } 93 | FMT_CONSTEXPR int on_arg_id(basic_string_view) { 94 | return ++num_parts, 0; 95 | } 96 | 97 | FMT_CONSTEXPR void on_replacement_field(int, const Char*) {} 98 | 99 | FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin, 100 | const Char* end) { 101 | // Find the matching brace. 102 | unsigned brace_counter = 0; 103 | for (; begin != end; ++begin) { 104 | if (*begin == '{') { 105 | ++brace_counter; 106 | } else if (*begin == '}') { 107 | if (brace_counter == 0u) break; 108 | --brace_counter; 109 | } 110 | } 111 | return begin; 112 | } 113 | 114 | FMT_CONSTEXPR void on_error(const char*) {} 115 | }; 116 | 117 | // Counts the number of parts in a format string. 118 | template 119 | FMT_CONSTEXPR unsigned count_parts(basic_string_view format_str) { 120 | part_counter counter; 121 | parse_format_string(format_str, counter); 122 | return counter.num_parts; 123 | } 124 | 125 | template 126 | class format_string_compiler : public error_handler { 127 | private: 128 | using part = format_part; 129 | 130 | PartHandler handler_; 131 | part part_; 132 | basic_string_view format_str_; 133 | basic_format_parse_context parse_context_; 134 | 135 | public: 136 | FMT_CONSTEXPR format_string_compiler(basic_string_view format_str, 137 | PartHandler handler) 138 | : handler_(handler), 139 | format_str_(format_str), 140 | parse_context_(format_str) {} 141 | 142 | FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { 143 | if (begin != end) 144 | handler_(part::make_text({begin, to_unsigned(end - begin)})); 145 | } 146 | 147 | FMT_CONSTEXPR int on_arg_id() { 148 | part_ = part::make_arg_index(parse_context_.next_arg_id()); 149 | return 0; 150 | } 151 | 152 | FMT_CONSTEXPR int on_arg_id(int id) { 153 | parse_context_.check_arg_id(id); 154 | part_ = part::make_arg_index(id); 155 | return 0; 156 | } 157 | 158 | FMT_CONSTEXPR int on_arg_id(basic_string_view id) { 159 | part_ = part::make_arg_name(id); 160 | return 0; 161 | } 162 | 163 | FMT_CONSTEXPR void on_replacement_field(int, const Char* ptr) { 164 | part_.arg_id_end = ptr; 165 | handler_(part_); 166 | } 167 | 168 | FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin, 169 | const Char* end) { 170 | auto repl = typename part::replacement(); 171 | dynamic_specs_handler> handler( 172 | repl.specs, parse_context_); 173 | auto it = parse_format_specs(begin, end, handler); 174 | if (*it != '}') on_error("missing '}' in format string"); 175 | repl.arg_id = part_.part_kind == part::kind::arg_index 176 | ? arg_ref(part_.val.arg_index) 177 | : arg_ref(part_.val.str); 178 | auto part = part::make_replacement(repl); 179 | part.arg_id_end = begin; 180 | handler_(part); 181 | return it; 182 | } 183 | }; 184 | 185 | // Compiles a format string and invokes handler(part) for each parsed part. 186 | template 187 | FMT_CONSTEXPR void compile_format_string(basic_string_view format_str, 188 | PartHandler handler) { 189 | parse_format_string( 190 | format_str, 191 | format_string_compiler(format_str, handler)); 192 | } 193 | 194 | template 195 | void format_arg( 196 | basic_format_parse_context& parse_ctx, 197 | Context& ctx, Id arg_id) { 198 | ctx.advance_to(visit_format_arg( 199 | arg_formatter(ctx, &parse_ctx), 200 | ctx.arg(arg_id))); 201 | } 202 | 203 | // vformat_to is defined in a subnamespace to prevent ADL. 204 | namespace cf { 205 | template 206 | auto vformat_to(OutputIt out, CompiledFormat& cf, 207 | basic_format_args args) -> typename Context::iterator { 208 | using char_type = typename Context::char_type; 209 | basic_format_parse_context parse_ctx( 210 | to_string_view(cf.format_str_)); 211 | Context ctx(out, args); 212 | 213 | const auto& parts = cf.parts(); 214 | for (auto part_it = std::begin(parts); part_it != std::end(parts); 215 | ++part_it) { 216 | const auto& part = *part_it; 217 | const auto& value = part.val; 218 | 219 | using format_part_t = format_part; 220 | switch (part.part_kind) { 221 | case format_part_t::kind::text: { 222 | const auto text = value.str; 223 | auto output = ctx.out(); 224 | auto&& it = reserve(output, text.size()); 225 | it = std::copy_n(text.begin(), text.size(), it); 226 | ctx.advance_to(output); 227 | break; 228 | } 229 | 230 | case format_part_t::kind::arg_index: 231 | advance_to(parse_ctx, part.arg_id_end); 232 | detail::format_arg(parse_ctx, ctx, value.arg_index); 233 | break; 234 | 235 | case format_part_t::kind::arg_name: 236 | advance_to(parse_ctx, part.arg_id_end); 237 | detail::format_arg(parse_ctx, ctx, value.str); 238 | break; 239 | 240 | case format_part_t::kind::replacement: { 241 | const auto& arg_id_value = value.repl.arg_id.val; 242 | const auto arg = value.repl.arg_id.kind == arg_id_kind::index 243 | ? ctx.arg(arg_id_value.index) 244 | : ctx.arg(arg_id_value.name); 245 | 246 | auto specs = value.repl.specs; 247 | 248 | handle_dynamic_spec(specs.width, specs.width_ref, ctx); 249 | handle_dynamic_spec(specs.precision, 250 | specs.precision_ref, ctx); 251 | 252 | error_handler h; 253 | numeric_specs_checker checker(h, arg.type()); 254 | if (specs.align == align::numeric) checker.require_numeric_argument(); 255 | if (specs.sign != sign::none) checker.check_sign(); 256 | if (specs.alt) checker.require_numeric_argument(); 257 | if (specs.precision >= 0) checker.check_precision(); 258 | 259 | advance_to(parse_ctx, part.arg_id_end); 260 | ctx.advance_to( 261 | visit_format_arg(arg_formatter( 262 | ctx, nullptr, &specs), 263 | arg)); 264 | break; 265 | } 266 | } 267 | } 268 | return ctx.out(); 269 | } 270 | } // namespace cf 271 | 272 | struct basic_compiled_format {}; 273 | 274 | template 275 | struct compiled_format_base : basic_compiled_format { 276 | using char_type = char_t; 277 | using parts_container = std::vector>; 278 | 279 | parts_container compiled_parts; 280 | 281 | explicit compiled_format_base(basic_string_view format_str) { 282 | compile_format_string(format_str, 283 | [this](const format_part& part) { 284 | compiled_parts.push_back(part); 285 | }); 286 | } 287 | 288 | const parts_container& parts() const { return compiled_parts; } 289 | }; 290 | 291 | template struct format_part_array { 292 | format_part data[N] = {}; 293 | FMT_CONSTEXPR format_part_array() = default; 294 | }; 295 | 296 | template 297 | FMT_CONSTEXPR format_part_array compile_to_parts( 298 | basic_string_view format_str) { 299 | format_part_array parts; 300 | unsigned counter = 0; 301 | // This is not a lambda for compatibility with older compilers. 302 | struct { 303 | format_part* parts; 304 | unsigned* counter; 305 | FMT_CONSTEXPR void operator()(const format_part& part) { 306 | parts[(*counter)++] = part; 307 | } 308 | } collector{parts.data, &counter}; 309 | compile_format_string(format_str, collector); 310 | if (counter < N) { 311 | parts.data[counter] = 312 | format_part::make_text(basic_string_view()); 313 | } 314 | return parts; 315 | } 316 | 317 | template constexpr const T& constexpr_max(const T& a, const T& b) { 318 | return (a < b) ? b : a; 319 | } 320 | 321 | template 322 | struct compiled_format_base::value>> 323 | : basic_compiled_format { 324 | using char_type = char_t; 325 | 326 | FMT_CONSTEXPR explicit compiled_format_base(basic_string_view) {} 327 | 328 | // Workaround for old compilers. Format string compilation will not be 329 | // performed there anyway. 330 | #if FMT_USE_CONSTEXPR 331 | static FMT_CONSTEXPR_DECL const unsigned num_format_parts = 332 | constexpr_max(count_parts(to_string_view(S())), 1u); 333 | #else 334 | static const unsigned num_format_parts = 1; 335 | #endif 336 | 337 | using parts_container = format_part[num_format_parts]; 338 | 339 | const parts_container& parts() const { 340 | static FMT_CONSTEXPR_DECL const auto compiled_parts = 341 | compile_to_parts( 342 | detail::to_string_view(S())); 343 | return compiled_parts.data; 344 | } 345 | }; 346 | 347 | template 348 | class compiled_format : private compiled_format_base { 349 | public: 350 | using typename compiled_format_base::char_type; 351 | 352 | private: 353 | basic_string_view format_str_; 354 | 355 | template 356 | friend auto cf::vformat_to(OutputIt out, CompiledFormat& cf, 357 | basic_format_args args) -> 358 | typename Context::iterator; 359 | 360 | public: 361 | compiled_format() = delete; 362 | explicit constexpr compiled_format(basic_string_view format_str) 363 | : compiled_format_base(format_str), format_str_(format_str) {} 364 | }; 365 | 366 | #ifdef __cpp_if_constexpr 367 | template struct type_list {}; 368 | 369 | // Returns a reference to the argument at index N from [first, rest...]. 370 | template 371 | constexpr const auto& get([[maybe_unused]] const T& first, 372 | [[maybe_unused]] const Args&... rest) { 373 | static_assert(N < 1 + sizeof...(Args), "index is out of bounds"); 374 | if constexpr (N == 0) 375 | return first; 376 | else 377 | return get(rest...); 378 | } 379 | 380 | template struct get_type_impl; 381 | 382 | template struct get_type_impl> { 383 | using type = remove_cvref_t(std::declval()...))>; 384 | }; 385 | 386 | template 387 | using get_type = typename get_type_impl::type; 388 | 389 | template struct is_compiled_format : std::false_type {}; 390 | 391 | template struct text { 392 | basic_string_view data; 393 | using char_type = Char; 394 | 395 | template 396 | OutputIt format(OutputIt out, const Args&...) const { 397 | return write(out, data); 398 | } 399 | }; 400 | 401 | template 402 | struct is_compiled_format> : std::true_type {}; 403 | 404 | template 405 | constexpr text make_text(basic_string_view s, size_t pos, 406 | size_t size) { 407 | return {{&s[pos], size}}; 408 | } 409 | 410 | template struct code_unit { 411 | Char value; 412 | using char_type = Char; 413 | 414 | template 415 | OutputIt format(OutputIt out, const Args&...) const { 416 | return write(out, value); 417 | } 418 | }; 419 | 420 | template 421 | struct is_compiled_format> : std::true_type {}; 422 | 423 | // A replacement field that refers to argument N. 424 | template struct field { 425 | using char_type = Char; 426 | 427 | template 428 | OutputIt format(OutputIt out, const Args&... args) const { 429 | // This ensures that the argument type is convertile to `const T&`. 430 | const T& arg = get(args...); 431 | return write(out, arg); 432 | } 433 | }; 434 | 435 | template 436 | struct is_compiled_format> : std::true_type {}; 437 | 438 | // A replacement field that refers to argument N and has format specifiers. 439 | template struct spec_field { 440 | using char_type = Char; 441 | mutable formatter fmt; 442 | 443 | template 444 | OutputIt format(OutputIt out, const Args&... args) const { 445 | // This ensures that the argument type is convertile to `const T&`. 446 | const T& arg = get(args...); 447 | const auto& vargs = 448 | make_format_args>(args...); 449 | basic_format_context ctx(out, vargs); 450 | return fmt.format(arg, ctx); 451 | } 452 | }; 453 | 454 | template 455 | struct is_compiled_format> : std::true_type {}; 456 | 457 | template struct concat { 458 | L lhs; 459 | R rhs; 460 | using char_type = typename L::char_type; 461 | 462 | template 463 | OutputIt format(OutputIt out, const Args&... args) const { 464 | out = lhs.format(out, args...); 465 | return rhs.format(out, args...); 466 | } 467 | }; 468 | 469 | template 470 | struct is_compiled_format> : std::true_type {}; 471 | 472 | template 473 | constexpr concat make_concat(L lhs, R rhs) { 474 | return {lhs, rhs}; 475 | } 476 | 477 | struct unknown_format {}; 478 | 479 | template 480 | constexpr size_t parse_text(basic_string_view str, size_t pos) { 481 | for (size_t size = str.size(); pos != size; ++pos) { 482 | if (str[pos] == '{' || str[pos] == '}') break; 483 | } 484 | return pos; 485 | } 486 | 487 | template 488 | constexpr auto compile_format_string(S format_str); 489 | 490 | template 491 | constexpr auto parse_tail(T head, S format_str) { 492 | if constexpr (POS != 493 | basic_string_view(format_str).size()) { 494 | constexpr auto tail = compile_format_string(format_str); 495 | if constexpr (std::is_same, 496 | unknown_format>()) 497 | return tail; 498 | else 499 | return make_concat(head, tail); 500 | } else { 501 | return head; 502 | } 503 | } 504 | 505 | template struct parse_specs_result { 506 | formatter fmt; 507 | size_t end; 508 | int next_arg_id; 509 | }; 510 | 511 | template 512 | constexpr parse_specs_result parse_specs(basic_string_view str, 513 | size_t pos, int arg_id) { 514 | str.remove_prefix(pos); 515 | auto ctx = basic_format_parse_context(str, {}, arg_id + 1); 516 | auto f = formatter(); 517 | auto end = f.parse(ctx); 518 | return {f, pos + (end - str.data()) + 1, ctx.next_arg_id()}; 519 | } 520 | 521 | // Compiles a non-empty format string and returns the compiled representation 522 | // or unknown_format() on unrecognized input. 523 | template 524 | constexpr auto compile_format_string(S format_str) { 525 | using char_type = typename S::char_type; 526 | constexpr basic_string_view str = format_str; 527 | if constexpr (str[POS] == '{') { 528 | if (POS + 1 == str.size()) 529 | throw format_error("unmatched '{' in format string"); 530 | if constexpr (str[POS + 1] == '{') { 531 | return parse_tail(make_text(str, POS, 1), format_str); 532 | } else if constexpr (str[POS + 1] == '}') { 533 | using type = get_type; 534 | return parse_tail(field(), 535 | format_str); 536 | } else if constexpr (str[POS + 1] == ':') { 537 | using type = get_type; 538 | constexpr auto result = parse_specs(str, POS + 2, ID); 539 | return parse_tail( 540 | spec_field{result.fmt}, format_str); 541 | } else { 542 | return unknown_format(); 543 | } 544 | } else if constexpr (str[POS] == '}') { 545 | if (POS + 1 == str.size()) 546 | throw format_error("unmatched '}' in format string"); 547 | return parse_tail(make_text(str, POS, 1), format_str); 548 | } else { 549 | constexpr auto end = parse_text(str, POS + 1); 550 | if constexpr (end - POS > 1) { 551 | return parse_tail(make_text(str, POS, end - POS), 552 | format_str); 553 | } else { 554 | return parse_tail(code_unit{str[POS]}, 555 | format_str); 556 | } 557 | } 558 | } 559 | 560 | template ::value || 562 | detail::is_compiled_string::value)> 563 | constexpr auto compile(S format_str) { 564 | constexpr basic_string_view str = format_str; 565 | if constexpr (str.size() == 0) { 566 | return detail::make_text(str, 0, 0); 567 | } else { 568 | constexpr auto result = 569 | detail::compile_format_string, 0, 0>( 570 | format_str); 571 | if constexpr (std::is_same, 572 | detail::unknown_format>()) { 573 | return detail::compiled_format(to_string_view(format_str)); 574 | } else { 575 | return result; 576 | } 577 | } 578 | } 579 | #else 580 | template ::value)> 582 | constexpr auto compile(S format_str) -> detail::compiled_format { 583 | return detail::compiled_format(to_string_view(format_str)); 584 | } 585 | #endif // __cpp_if_constexpr 586 | 587 | // Compiles the format string which must be a string literal. 588 | template 589 | auto compile(const Char (&format_str)[N]) 590 | -> detail::compiled_format { 591 | return detail::compiled_format( 592 | basic_string_view(format_str, N - 1)); 593 | } 594 | } // namespace detail 595 | 596 | // DEPRECATED! use FMT_COMPILE instead. 597 | template 598 | FMT_DEPRECATED auto compile(const Args&... args) 599 | -> decltype(detail::compile(args...)) { 600 | return detail::compile(args...); 601 | } 602 | 603 | #if FMT_USE_CONSTEXPR 604 | # ifdef __cpp_if_constexpr 605 | 606 | template ::value)> 609 | FMT_INLINE std::basic_string format(const CompiledFormat& cf, 610 | const Args&... args) { 611 | basic_memory_buffer buffer; 612 | cf.format(detail::buffer_appender(buffer), args...); 613 | return to_string(buffer); 614 | } 615 | 616 | template ::value)> 618 | OutputIt format_to(OutputIt out, const CompiledFormat& cf, 619 | const Args&... args) { 620 | return cf.format(out, args...); 621 | } 622 | # endif // __cpp_if_constexpr 623 | #endif // FMT_USE_CONSTEXPR 624 | 625 | template ::value)> 629 | std::basic_string format(const CompiledFormat& cf, const Args&... args) { 630 | basic_memory_buffer buffer; 631 | using context = buffer_context; 632 | detail::cf::vformat_to(detail::buffer_appender(buffer), cf, 633 | make_format_args(args...)); 634 | return to_string(buffer); 635 | } 636 | 637 | template ::value)> 639 | FMT_INLINE std::basic_string format(const S&, 640 | Args&&... args) { 641 | #ifdef __cpp_if_constexpr 642 | if constexpr (std::is_same::value) { 643 | constexpr basic_string_view str = S(); 644 | if (str.size() == 2 && str[0] == '{' && str[1] == '}') 645 | return fmt::to_string(detail::first(args...)); 646 | } 647 | #endif 648 | constexpr auto compiled = detail::compile(S()); 649 | return format(compiled, std::forward(args)...); 650 | } 651 | 652 | template ::value)> 655 | OutputIt format_to(OutputIt out, const CompiledFormat& cf, 656 | const Args&... args) { 657 | using char_type = typename CompiledFormat::char_type; 658 | using context = format_context_t; 659 | return detail::cf::vformat_to(out, cf, 660 | make_format_args(args...)); 661 | } 662 | 663 | template ::value)> 665 | OutputIt format_to(OutputIt out, const S&, const Args&... args) { 666 | constexpr auto compiled = detail::compile(S()); 667 | return format_to(out, compiled, args...); 668 | } 669 | 670 | template 671 | auto format_to_n(OutputIt out, size_t n, const CompiledFormat& cf, 672 | const Args&... args) -> 673 | typename std::enable_if< 674 | detail::is_output_iterator::value && 676 | std::is_base_of::value, 678 | format_to_n_result>::type { 679 | auto it = 680 | format_to(detail::truncating_iterator(out, n), cf, args...); 681 | return {it.base(), it.count()}; 682 | } 683 | 684 | template ::value)> 686 | format_to_n_result format_to_n(OutputIt out, size_t n, const S&, 687 | const Args&... args) { 688 | constexpr auto compiled = detail::compile(S()); 689 | auto it = format_to(detail::truncating_iterator(out, n), compiled, 690 | args...); 691 | return {it.base(), it.count()}; 692 | } 693 | 694 | template 695 | size_t formatted_size(const CompiledFormat& cf, const Args&... args) { 696 | return format_to(detail::counting_iterator(), cf, args...).count(); 697 | } 698 | 699 | FMT_END_NAMESPACE 700 | 701 | #endif // FMT_COMPILE_H_ 702 | -------------------------------------------------------------------------------- /includes/fmt/locale.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - std::locale support 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_LOCALE_H_ 9 | #define FMT_LOCALE_H_ 10 | 11 | #include 12 | 13 | #include "format.h" 14 | 15 | FMT_BEGIN_NAMESPACE 16 | 17 | namespace detail { 18 | template 19 | std::basic_string vformat( 20 | const std::locale& loc, basic_string_view format_str, 21 | basic_format_args>> args) { 22 | basic_memory_buffer buffer; 23 | detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc)); 24 | return fmt::to_string(buffer); 25 | } 26 | } // namespace detail 27 | 28 | template > 29 | inline std::basic_string vformat( 30 | const std::locale& loc, const S& format_str, 31 | basic_format_args>> args) { 32 | return detail::vformat(loc, to_string_view(format_str), args); 33 | } 34 | 35 | template > 36 | inline std::basic_string format(const std::locale& loc, 37 | const S& format_str, Args&&... args) { 38 | return detail::vformat(loc, to_string_view(format_str), 39 | fmt::make_args_checked(format_str, args...)); 40 | } 41 | 42 | template , 44 | FMT_ENABLE_IF(detail::is_output_iterator::value)> 45 | inline OutputIt vformat_to( 46 | OutputIt out, const std::locale& loc, const S& format_str, 47 | basic_format_args>> args) { 48 | decltype(detail::get_buffer(out)) buf(detail::get_buffer_init(out)); 49 | vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc)); 50 | return detail::get_iterator(buf); 51 | } 52 | 53 | template >::value> 55 | inline auto format_to(OutputIt out, const std::locale& loc, 56 | const S& format_str, Args&&... args) -> 57 | typename std::enable_if::type { 58 | const auto& vargs = fmt::make_args_checked(format_str, args...); 59 | return vformat_to(out, loc, to_string_view(format_str), vargs); 60 | } 61 | 62 | FMT_END_NAMESPACE 63 | 64 | #endif // FMT_LOCALE_H_ 65 | -------------------------------------------------------------------------------- /includes/fmt/os.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - optional OS-specific functionality 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_OS_H_ 9 | #define FMT_OS_H_ 10 | 11 | #if defined(__MINGW32__) || defined(__CYGWIN__) 12 | // Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/. 13 | # undef __STRICT_ANSI__ 14 | #endif 15 | 16 | #include 17 | #include // for locale_t 18 | #include 19 | #include 20 | #include // for strtod_l 21 | 22 | #if defined __APPLE__ || defined(__FreeBSD__) 23 | # include // for LC_NUMERIC_MASK on OS X 24 | #endif 25 | 26 | #include "format.h" 27 | 28 | // UWP doesn't provide _pipe. 29 | #if FMT_HAS_INCLUDE("winapifamily.h") 30 | # include 31 | #endif 32 | #if (FMT_HAS_INCLUDE() || defined(__APPLE__) || \ 33 | defined(__linux__)) && \ 34 | (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) 35 | # include // for O_RDONLY 36 | # define FMT_USE_FCNTL 1 37 | #else 38 | # define FMT_USE_FCNTL 0 39 | #endif 40 | 41 | #ifndef FMT_POSIX 42 | # if defined(_WIN32) && !defined(__MINGW32__) 43 | // Fix warnings about deprecated symbols. 44 | # define FMT_POSIX(call) _##call 45 | # else 46 | # define FMT_POSIX(call) call 47 | # endif 48 | #endif 49 | 50 | // Calls to system functions are wrapped in FMT_SYSTEM for testability. 51 | #ifdef FMT_SYSTEM 52 | # define FMT_POSIX_CALL(call) FMT_SYSTEM(call) 53 | #else 54 | # define FMT_SYSTEM(call) ::call 55 | # ifdef _WIN32 56 | // Fix warnings about deprecated symbols. 57 | # define FMT_POSIX_CALL(call) ::_##call 58 | # else 59 | # define FMT_POSIX_CALL(call) ::call 60 | # endif 61 | #endif 62 | 63 | // Retries the expression while it evaluates to error_result and errno 64 | // equals to EINTR. 65 | #ifndef _WIN32 66 | # define FMT_RETRY_VAL(result, expression, error_result) \ 67 | do { \ 68 | (result) = (expression); \ 69 | } while ((result) == (error_result) && errno == EINTR) 70 | #else 71 | # define FMT_RETRY_VAL(result, expression, error_result) result = (expression) 72 | #endif 73 | 74 | #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) 75 | 76 | FMT_BEGIN_NAMESPACE 77 | 78 | /** 79 | \rst 80 | A reference to a null-terminated string. It can be constructed from a C 81 | string or ``std::string``. 82 | 83 | You can use one of the following type aliases for common character types: 84 | 85 | +---------------+-----------------------------+ 86 | | Type | Definition | 87 | +===============+=============================+ 88 | | cstring_view | basic_cstring_view | 89 | +---------------+-----------------------------+ 90 | | wcstring_view | basic_cstring_view | 91 | +---------------+-----------------------------+ 92 | 93 | This class is most useful as a parameter type to allow passing 94 | different types of strings to a function, for example:: 95 | 96 | template 97 | std::string format(cstring_view format_str, const Args & ... args); 98 | 99 | format("{}", 42); 100 | format(std::string("{}"), 42); 101 | \endrst 102 | */ 103 | template class basic_cstring_view { 104 | private: 105 | const Char* data_; 106 | 107 | public: 108 | /** Constructs a string reference object from a C string. */ 109 | basic_cstring_view(const Char* s) : data_(s) {} 110 | 111 | /** 112 | \rst 113 | Constructs a string reference from an ``std::string`` object. 114 | \endrst 115 | */ 116 | basic_cstring_view(const std::basic_string& s) : data_(s.c_str()) {} 117 | 118 | /** Returns the pointer to a C string. */ 119 | const Char* c_str() const { return data_; } 120 | }; 121 | 122 | using cstring_view = basic_cstring_view; 123 | using wcstring_view = basic_cstring_view; 124 | 125 | // An error code. 126 | class error_code { 127 | private: 128 | int value_; 129 | 130 | public: 131 | explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {} 132 | 133 | int get() const FMT_NOEXCEPT { return value_; } 134 | }; 135 | 136 | #ifdef _WIN32 137 | namespace detail { 138 | // A converter from UTF-16 to UTF-8. 139 | // It is only provided for Windows since other systems support UTF-8 natively. 140 | class utf16_to_utf8 { 141 | private: 142 | memory_buffer buffer_; 143 | 144 | public: 145 | utf16_to_utf8() {} 146 | FMT_API explicit utf16_to_utf8(wstring_view s); 147 | operator string_view() const { return string_view(&buffer_[0], size()); } 148 | size_t size() const { return buffer_.size() - 1; } 149 | const char* c_str() const { return &buffer_[0]; } 150 | std::string str() const { return std::string(&buffer_[0], size()); } 151 | 152 | // Performs conversion returning a system error code instead of 153 | // throwing exception on conversion error. This method may still throw 154 | // in case of memory allocation error. 155 | FMT_API int convert(wstring_view s); 156 | }; 157 | 158 | FMT_API void format_windows_error(buffer& out, int error_code, 159 | string_view message) FMT_NOEXCEPT; 160 | } // namespace detail 161 | 162 | /** A Windows error. */ 163 | class windows_error : public system_error { 164 | private: 165 | FMT_API void init(int error_code, string_view format_str, format_args args); 166 | 167 | public: 168 | /** 169 | \rst 170 | Constructs a :class:`fmt::windows_error` object with the description 171 | of the form 172 | 173 | .. parsed-literal:: 174 | **: ** 175 | 176 | where ** is the formatted message and ** is the 177 | system message corresponding to the error code. 178 | *error_code* is a Windows error code as given by ``GetLastError``. 179 | If *error_code* is not a valid error code such as -1, the system message 180 | will look like "error -1". 181 | 182 | **Example**:: 183 | 184 | // This throws a windows_error with the description 185 | // cannot open file 'madeup': The system cannot find the file specified. 186 | // or similar (system message may vary). 187 | const char *filename = "madeup"; 188 | LPOFSTRUCT of = LPOFSTRUCT(); 189 | HFILE file = OpenFile(filename, &of, OF_READ); 190 | if (file == HFILE_ERROR) { 191 | throw fmt::windows_error(GetLastError(), 192 | "cannot open file '{}'", filename); 193 | } 194 | \endrst 195 | */ 196 | template 197 | windows_error(int error_code, string_view message, const Args&... args) { 198 | init(error_code, message, make_format_args(args...)); 199 | } 200 | }; 201 | 202 | // Reports a Windows error without throwing an exception. 203 | // Can be used to report errors from destructors. 204 | FMT_API void report_windows_error(int error_code, 205 | string_view message) FMT_NOEXCEPT; 206 | #endif // _WIN32 207 | 208 | // A buffered file. 209 | class buffered_file { 210 | private: 211 | FILE* file_; 212 | 213 | friend class file; 214 | 215 | explicit buffered_file(FILE* f) : file_(f) {} 216 | 217 | public: 218 | buffered_file(const buffered_file&) = delete; 219 | void operator=(const buffered_file&) = delete; 220 | 221 | // Constructs a buffered_file object which doesn't represent any file. 222 | buffered_file() FMT_NOEXCEPT : file_(nullptr) {} 223 | 224 | // Destroys the object closing the file it represents if any. 225 | FMT_API ~buffered_file() FMT_NOEXCEPT; 226 | 227 | public: 228 | buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) { 229 | other.file_ = nullptr; 230 | } 231 | 232 | buffered_file& operator=(buffered_file&& other) { 233 | close(); 234 | file_ = other.file_; 235 | other.file_ = nullptr; 236 | return *this; 237 | } 238 | 239 | // Opens a file. 240 | FMT_API buffered_file(cstring_view filename, cstring_view mode); 241 | 242 | // Closes the file. 243 | FMT_API void close(); 244 | 245 | // Returns the pointer to a FILE object representing this file. 246 | FILE* get() const FMT_NOEXCEPT { return file_; } 247 | 248 | // We place parentheses around fileno to workaround a bug in some versions 249 | // of MinGW that define fileno as a macro. 250 | FMT_API int(fileno)() const; 251 | 252 | void vprint(string_view format_str, format_args args) { 253 | fmt::vprint(file_, format_str, args); 254 | } 255 | 256 | template 257 | inline void print(string_view format_str, const Args&... args) { 258 | vprint(format_str, make_format_args(args...)); 259 | } 260 | }; 261 | 262 | #if FMT_USE_FCNTL 263 | // A file. Closed file is represented by a file object with descriptor -1. 264 | // Methods that are not declared with FMT_NOEXCEPT may throw 265 | // fmt::system_error in case of failure. Note that some errors such as 266 | // closing the file multiple times will cause a crash on Windows rather 267 | // than an exception. You can get standard behavior by overriding the 268 | // invalid parameter handler with _set_invalid_parameter_handler. 269 | class file { 270 | private: 271 | int fd_; // File descriptor. 272 | 273 | // Constructs a file object with a given descriptor. 274 | explicit file(int fd) : fd_(fd) {} 275 | 276 | public: 277 | // Possible values for the oflag argument to the constructor. 278 | enum { 279 | RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only. 280 | WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. 281 | RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing. 282 | CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist. 283 | APPEND = FMT_POSIX(O_APPEND) // Open in append mode. 284 | }; 285 | 286 | // Constructs a file object which doesn't represent any file. 287 | file() FMT_NOEXCEPT : fd_(-1) {} 288 | 289 | // Opens a file and constructs a file object representing this file. 290 | FMT_API file(cstring_view path, int oflag); 291 | 292 | public: 293 | file(const file&) = delete; 294 | void operator=(const file&) = delete; 295 | 296 | file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; } 297 | 298 | file& operator=(file&& other) FMT_NOEXCEPT { 299 | close(); 300 | fd_ = other.fd_; 301 | other.fd_ = -1; 302 | return *this; 303 | } 304 | 305 | // Destroys the object closing the file it represents if any. 306 | FMT_API ~file() FMT_NOEXCEPT; 307 | 308 | // Returns the file descriptor. 309 | int descriptor() const FMT_NOEXCEPT { return fd_; } 310 | 311 | // Closes the file. 312 | FMT_API void close(); 313 | 314 | // Returns the file size. The size has signed type for consistency with 315 | // stat::st_size. 316 | FMT_API long long size() const; 317 | 318 | // Attempts to read count bytes from the file into the specified buffer. 319 | FMT_API size_t read(void* buffer, size_t count); 320 | 321 | // Attempts to write count bytes from the specified buffer to the file. 322 | FMT_API size_t write(const void* buffer, size_t count); 323 | 324 | // Duplicates a file descriptor with the dup function and returns 325 | // the duplicate as a file object. 326 | FMT_API static file dup(int fd); 327 | 328 | // Makes fd be the copy of this file descriptor, closing fd first if 329 | // necessary. 330 | FMT_API void dup2(int fd); 331 | 332 | // Makes fd be the copy of this file descriptor, closing fd first if 333 | // necessary. 334 | FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT; 335 | 336 | // Creates a pipe setting up read_end and write_end file objects for reading 337 | // and writing respectively. 338 | FMT_API static void pipe(file& read_end, file& write_end); 339 | 340 | // Creates a buffered_file object associated with this file and detaches 341 | // this file object from the file. 342 | FMT_API buffered_file fdopen(const char* mode); 343 | }; 344 | 345 | // Returns the memory page size. 346 | long getpagesize(); 347 | 348 | namespace detail { 349 | 350 | struct buffer_size { 351 | size_t value = 0; 352 | buffer_size operator=(size_t val) const { 353 | auto bs = buffer_size(); 354 | bs.value = val; 355 | return bs; 356 | } 357 | }; 358 | 359 | struct ostream_params { 360 | int oflag = file::WRONLY | file::CREATE; 361 | size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768; 362 | 363 | ostream_params() {} 364 | 365 | template 366 | ostream_params(T... params, int oflag) : ostream_params(params...) { 367 | this->oflag = oflag; 368 | } 369 | 370 | template 371 | ostream_params(T... params, detail::buffer_size bs) 372 | : ostream_params(params...) { 373 | this->buffer_size = bs.value; 374 | } 375 | }; 376 | } // namespace detail 377 | 378 | static constexpr detail::buffer_size buffer_size; 379 | 380 | // A fast output stream which is not thread-safe. 381 | class ostream final : private detail::buffer { 382 | private: 383 | file file_; 384 | 385 | void flush() { 386 | if (size() == 0) return; 387 | file_.write(data(), size()); 388 | clear(); 389 | } 390 | 391 | FMT_API void grow(size_t) override final; 392 | 393 | ostream(cstring_view path, const detail::ostream_params& params) 394 | : file_(path, params.oflag) { 395 | set(new char[params.buffer_size], params.buffer_size); 396 | } 397 | 398 | public: 399 | ostream(ostream&& other) 400 | : detail::buffer(other.data(), other.size(), other.capacity()), 401 | file_(std::move(other.file_)) { 402 | other.set(nullptr, 0); 403 | } 404 | ~ostream() { 405 | flush(); 406 | delete[] data(); 407 | } 408 | 409 | template 410 | friend ostream output_file(cstring_view path, T... params); 411 | 412 | void close() { 413 | flush(); 414 | file_.close(); 415 | } 416 | 417 | template 418 | void print(const S& format_str, const Args&... args) { 419 | format_to(detail::buffer_appender(*this), format_str, args...); 420 | } 421 | }; 422 | 423 | /** 424 | Opens a file for writing. Supported parameters passed in `params`: 425 | * ````: Output flags (``file::WRONLY | file::CREATE`` by default) 426 | * ``buffer_size=``: Output buffer size 427 | */ 428 | template 429 | inline ostream output_file(cstring_view path, T... params) { 430 | return {path, detail::ostream_params(params...)}; 431 | } 432 | #endif // FMT_USE_FCNTL 433 | 434 | #ifdef FMT_LOCALE 435 | // A "C" numeric locale. 436 | class locale { 437 | private: 438 | # ifdef _WIN32 439 | using locale_t = _locale_t; 440 | 441 | static void freelocale(locale_t loc) { _free_locale(loc); } 442 | 443 | static double strtod_l(const char* nptr, char** endptr, _locale_t loc) { 444 | return _strtod_l(nptr, endptr, loc); 445 | } 446 | # endif 447 | 448 | locale_t locale_; 449 | 450 | public: 451 | using type = locale_t; 452 | locale(const locale&) = delete; 453 | void operator=(const locale&) = delete; 454 | 455 | locale() { 456 | # ifndef _WIN32 457 | locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr)); 458 | # else 459 | locale_ = _create_locale(LC_NUMERIC, "C"); 460 | # endif 461 | if (!locale_) FMT_THROW(system_error(errno, "cannot create locale")); 462 | } 463 | ~locale() { freelocale(locale_); } 464 | 465 | type get() const { return locale_; } 466 | 467 | // Converts string to floating-point number and advances str past the end 468 | // of the parsed input. 469 | double strtod(const char*& str) const { 470 | char* end = nullptr; 471 | double result = strtod_l(str, &end, locale_); 472 | str = end; 473 | return result; 474 | } 475 | }; 476 | using Locale FMT_DEPRECATED_ALIAS = locale; 477 | #endif // FMT_LOCALE 478 | FMT_END_NAMESPACE 479 | 480 | #endif // FMT_OS_H_ 481 | -------------------------------------------------------------------------------- /includes/fmt/ostream.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - std::ostream support 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_OSTREAM_H_ 9 | #define FMT_OSTREAM_H_ 10 | 11 | #include 12 | 13 | #include "format.h" 14 | 15 | FMT_BEGIN_NAMESPACE 16 | 17 | template class basic_printf_parse_context; 18 | template class basic_printf_context; 19 | 20 | namespace detail { 21 | 22 | template class formatbuf : public std::basic_streambuf { 23 | private: 24 | using int_type = typename std::basic_streambuf::int_type; 25 | using traits_type = typename std::basic_streambuf::traits_type; 26 | 27 | buffer& buffer_; 28 | 29 | public: 30 | formatbuf(buffer& buf) : buffer_(buf) {} 31 | 32 | protected: 33 | // The put-area is actually always empty. This makes the implementation 34 | // simpler and has the advantage that the streambuf and the buffer are always 35 | // in sync and sputc never writes into uninitialized memory. The obvious 36 | // disadvantage is that each call to sputc always results in a (virtual) call 37 | // to overflow. There is no disadvantage here for sputn since this always 38 | // results in a call to xsputn. 39 | 40 | int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE { 41 | if (!traits_type::eq_int_type(ch, traits_type::eof())) 42 | buffer_.push_back(static_cast(ch)); 43 | return ch; 44 | } 45 | 46 | std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE { 47 | buffer_.append(s, s + count); 48 | return count; 49 | } 50 | }; 51 | 52 | struct converter { 53 | template ::value)> converter(T); 54 | }; 55 | 56 | template struct test_stream : std::basic_ostream { 57 | private: 58 | void_t<> operator<<(converter); 59 | }; 60 | 61 | // Hide insertion operators for built-in types. 62 | template 63 | void_t<> operator<<(std::basic_ostream&, Char); 64 | template 65 | void_t<> operator<<(std::basic_ostream&, char); 66 | template 67 | void_t<> operator<<(std::basic_ostream&, char); 68 | template 69 | void_t<> operator<<(std::basic_ostream&, signed char); 70 | template 71 | void_t<> operator<<(std::basic_ostream&, unsigned char); 72 | 73 | // Checks if T has a user-defined operator<< (e.g. not a member of 74 | // std::ostream). 75 | template class is_streamable { 76 | private: 77 | template 78 | static bool_constant&>() 79 | << std::declval()), 80 | void_t<>>::value> 81 | test(int); 82 | 83 | template static std::false_type test(...); 84 | 85 | using result = decltype(test(0)); 86 | 87 | public: 88 | static const bool value = result::value; 89 | }; 90 | 91 | // Write the content of buf to os. 92 | template 93 | void write_buffer(std::basic_ostream& os, buffer& buf) { 94 | const Char* buf_data = buf.data(); 95 | using unsigned_streamsize = std::make_unsigned::type; 96 | unsigned_streamsize size = buf.size(); 97 | unsigned_streamsize max_size = to_unsigned(max_value()); 98 | do { 99 | unsigned_streamsize n = size <= max_size ? size : max_size; 100 | os.write(buf_data, static_cast(n)); 101 | buf_data += n; 102 | size -= n; 103 | } while (size != 0); 104 | } 105 | 106 | template 107 | void format_value(buffer& buf, const T& value, 108 | locale_ref loc = locale_ref()) { 109 | formatbuf format_buf(buf); 110 | std::basic_ostream output(&format_buf); 111 | #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) 112 | if (loc) output.imbue(loc.get()); 113 | #endif 114 | output << value; 115 | output.exceptions(std::ios_base::failbit | std::ios_base::badbit); 116 | buf.try_resize(buf.size()); 117 | } 118 | 119 | // Formats an object of type T that has an overloaded ostream operator<<. 120 | template 121 | struct fallback_formatter::value>> 122 | : private formatter, Char> { 123 | FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) 124 | -> decltype(ctx.begin()) { 125 | return formatter, Char>::parse(ctx); 126 | } 127 | template >::value)> 130 | auto parse(ParseCtx& ctx) -> decltype(ctx.begin()) { 131 | return ctx.begin(); 132 | } 133 | 134 | template 135 | auto format(const T& value, basic_format_context& ctx) 136 | -> OutputIt { 137 | basic_memory_buffer buffer; 138 | format_value(buffer, value, ctx.locale()); 139 | basic_string_view str(buffer.data(), buffer.size()); 140 | return formatter, Char>::format(str, ctx); 141 | } 142 | template 143 | auto format(const T& value, basic_printf_context& ctx) 144 | -> OutputIt { 145 | basic_memory_buffer buffer; 146 | format_value(buffer, value, ctx.locale()); 147 | return std::copy(buffer.begin(), buffer.end(), ctx.out()); 148 | } 149 | }; 150 | } // namespace detail 151 | 152 | template 153 | void vprint(std::basic_ostream& os, basic_string_view format_str, 154 | basic_format_args>> args) { 155 | basic_memory_buffer buffer; 156 | detail::vformat_to(buffer, format_str, args); 157 | detail::write_buffer(os, buffer); 158 | } 159 | 160 | /** 161 | \rst 162 | Prints formatted data to the stream *os*. 163 | 164 | **Example**:: 165 | 166 | fmt::print(cerr, "Don't {}!", "panic"); 167 | \endrst 168 | */ 169 | template ::value, char_t>> 171 | void print(std::basic_ostream& os, const S& format_str, Args&&... args) { 172 | vprint(os, to_string_view(format_str), 173 | fmt::make_args_checked(format_str, args...)); 174 | } 175 | FMT_END_NAMESPACE 176 | 177 | #endif // FMT_OSTREAM_H_ 178 | -------------------------------------------------------------------------------- /includes/fmt/posix.h: -------------------------------------------------------------------------------- 1 | #include "os.h" 2 | #warning "fmt/posix.h is deprecated; use fmt/os.h instead" 3 | -------------------------------------------------------------------------------- /includes/fmt/printf.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - legacy printf implementation 2 | // 3 | // Copyright (c) 2012 - 2016, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_PRINTF_H_ 9 | #define FMT_PRINTF_H_ 10 | 11 | #include // std::max 12 | #include // std::numeric_limits 13 | 14 | #include "ostream.h" 15 | 16 | FMT_BEGIN_NAMESPACE 17 | namespace detail { 18 | 19 | // Checks if a value fits in int - used to avoid warnings about comparing 20 | // signed and unsigned integers. 21 | template struct int_checker { 22 | template static bool fits_in_int(T value) { 23 | unsigned max = max_value(); 24 | return value <= max; 25 | } 26 | static bool fits_in_int(bool) { return true; } 27 | }; 28 | 29 | template <> struct int_checker { 30 | template static bool fits_in_int(T value) { 31 | return value >= (std::numeric_limits::min)() && 32 | value <= max_value(); 33 | } 34 | static bool fits_in_int(int) { return true; } 35 | }; 36 | 37 | class printf_precision_handler { 38 | public: 39 | template ::value)> 40 | int operator()(T value) { 41 | if (!int_checker::is_signed>::fits_in_int(value)) 42 | FMT_THROW(format_error("number is too big")); 43 | return (std::max)(static_cast(value), 0); 44 | } 45 | 46 | template ::value)> 47 | int operator()(T) { 48 | FMT_THROW(format_error("precision is not integer")); 49 | return 0; 50 | } 51 | }; 52 | 53 | // An argument visitor that returns true iff arg is a zero integer. 54 | class is_zero_int { 55 | public: 56 | template ::value)> 57 | bool operator()(T value) { 58 | return value == 0; 59 | } 60 | 61 | template ::value)> 62 | bool operator()(T) { 63 | return false; 64 | } 65 | }; 66 | 67 | template struct make_unsigned_or_bool : std::make_unsigned {}; 68 | 69 | template <> struct make_unsigned_or_bool { using type = bool; }; 70 | 71 | template class arg_converter { 72 | private: 73 | using char_type = typename Context::char_type; 74 | 75 | basic_format_arg& arg_; 76 | char_type type_; 77 | 78 | public: 79 | arg_converter(basic_format_arg& arg, char_type type) 80 | : arg_(arg), type_(type) {} 81 | 82 | void operator()(bool value) { 83 | if (type_ != 's') operator()(value); 84 | } 85 | 86 | template ::value)> 87 | void operator()(U value) { 88 | bool is_signed = type_ == 'd' || type_ == 'i'; 89 | using target_type = conditional_t::value, U, T>; 90 | if (const_check(sizeof(target_type) <= sizeof(int))) { 91 | // Extra casts are used to silence warnings. 92 | if (is_signed) { 93 | arg_ = detail::make_arg( 94 | static_cast(static_cast(value))); 95 | } else { 96 | using unsigned_type = typename make_unsigned_or_bool::type; 97 | arg_ = detail::make_arg( 98 | static_cast(static_cast(value))); 99 | } 100 | } else { 101 | if (is_signed) { 102 | // glibc's printf doesn't sign extend arguments of smaller types: 103 | // std::printf("%lld", -42); // prints "4294967254" 104 | // but we don't have to do the same because it's a UB. 105 | arg_ = detail::make_arg(static_cast(value)); 106 | } else { 107 | arg_ = detail::make_arg( 108 | static_cast::type>(value)); 109 | } 110 | } 111 | } 112 | 113 | template ::value)> 114 | void operator()(U) {} // No conversion needed for non-integral types. 115 | }; 116 | 117 | // Converts an integer argument to T for printf, if T is an integral type. 118 | // If T is void, the argument is converted to corresponding signed or unsigned 119 | // type depending on the type specifier: 'd' and 'i' - signed, other - 120 | // unsigned). 121 | template 122 | void convert_arg(basic_format_arg& arg, Char type) { 123 | visit_format_arg(arg_converter(arg, type), arg); 124 | } 125 | 126 | // Converts an integer argument to char for printf. 127 | template class char_converter { 128 | private: 129 | basic_format_arg& arg_; 130 | 131 | public: 132 | explicit char_converter(basic_format_arg& arg) : arg_(arg) {} 133 | 134 | template ::value)> 135 | void operator()(T value) { 136 | arg_ = detail::make_arg( 137 | static_cast(value)); 138 | } 139 | 140 | template ::value)> 141 | void operator()(T) {} // No conversion needed for non-integral types. 142 | }; 143 | 144 | // An argument visitor that return a pointer to a C string if argument is a 145 | // string or null otherwise. 146 | template struct get_cstring { 147 | template const Char* operator()(T) { return nullptr; } 148 | const Char* operator()(const Char* s) { return s; } 149 | }; 150 | 151 | // Checks if an argument is a valid printf width specifier and sets 152 | // left alignment if it is negative. 153 | template class printf_width_handler { 154 | private: 155 | using format_specs = basic_format_specs; 156 | 157 | format_specs& specs_; 158 | 159 | public: 160 | explicit printf_width_handler(format_specs& specs) : specs_(specs) {} 161 | 162 | template ::value)> 163 | unsigned operator()(T value) { 164 | auto width = static_cast>(value); 165 | if (detail::is_negative(value)) { 166 | specs_.align = align::left; 167 | width = 0 - width; 168 | } 169 | unsigned int_max = max_value(); 170 | if (width > int_max) FMT_THROW(format_error("number is too big")); 171 | return static_cast(width); 172 | } 173 | 174 | template ::value)> 175 | unsigned operator()(T) { 176 | FMT_THROW(format_error("width is not integer")); 177 | return 0; 178 | } 179 | }; 180 | 181 | template 182 | void vprintf(buffer& buf, basic_string_view format, 183 | basic_format_args args) { 184 | Context(buffer_appender(buf), format, args).format(); 185 | } 186 | } // namespace detail 187 | 188 | // For printing into memory_buffer. 189 | template 190 | FMT_DEPRECATED void printf(detail::buffer& buf, 191 | basic_string_view format, 192 | basic_format_args args) { 193 | return detail::vprintf(buf, format, args); 194 | } 195 | using detail::vprintf; 196 | 197 | template 198 | class basic_printf_parse_context : public basic_format_parse_context { 199 | using basic_format_parse_context::basic_format_parse_context; 200 | }; 201 | template class basic_printf_context; 202 | 203 | /** 204 | \rst 205 | The ``printf`` argument formatter. 206 | \endrst 207 | */ 208 | template 209 | class printf_arg_formatter : public detail::arg_formatter_base { 210 | public: 211 | using iterator = OutputIt; 212 | 213 | private: 214 | using char_type = Char; 215 | using base = detail::arg_formatter_base; 216 | using context_type = basic_printf_context; 217 | 218 | context_type& context_; 219 | 220 | void write_null_pointer(char) { 221 | this->specs()->type = 0; 222 | this->write("(nil)"); 223 | } 224 | 225 | void write_null_pointer(wchar_t) { 226 | this->specs()->type = 0; 227 | this->write(L"(nil)"); 228 | } 229 | 230 | public: 231 | using format_specs = typename base::format_specs; 232 | 233 | /** 234 | \rst 235 | Constructs an argument formatter object. 236 | *buffer* is a reference to the output buffer and *specs* contains format 237 | specifier information for standard argument types. 238 | \endrst 239 | */ 240 | printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx) 241 | : base(iter, &specs, detail::locale_ref()), context_(ctx) {} 242 | 243 | template ::value)> 244 | iterator operator()(T value) { 245 | // MSVC2013 fails to compile separate overloads for bool and char_type so 246 | // use std::is_same instead. 247 | if (std::is_same::value) { 248 | format_specs& fmt_specs = *this->specs(); 249 | if (fmt_specs.type != 's') return base::operator()(value ? 1 : 0); 250 | fmt_specs.type = 0; 251 | this->write(value != 0); 252 | } else if (std::is_same::value) { 253 | format_specs& fmt_specs = *this->specs(); 254 | if (fmt_specs.type && fmt_specs.type != 'c') 255 | return (*this)(static_cast(value)); 256 | fmt_specs.sign = sign::none; 257 | fmt_specs.alt = false; 258 | fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types. 259 | // align::numeric needs to be overwritten here since the '0' flag is 260 | // ignored for non-numeric types 261 | if (fmt_specs.align == align::none || fmt_specs.align == align::numeric) 262 | fmt_specs.align = align::right; 263 | return base::operator()(value); 264 | } else { 265 | return base::operator()(value); 266 | } 267 | return this->out(); 268 | } 269 | 270 | template ::value)> 271 | iterator operator()(T value) { 272 | return base::operator()(value); 273 | } 274 | 275 | /** Formats a null-terminated C string. */ 276 | iterator operator()(const char* value) { 277 | if (value) 278 | base::operator()(value); 279 | else if (this->specs()->type == 'p') 280 | write_null_pointer(char_type()); 281 | else 282 | this->write("(null)"); 283 | return this->out(); 284 | } 285 | 286 | /** Formats a null-terminated wide C string. */ 287 | iterator operator()(const wchar_t* value) { 288 | if (value) 289 | base::operator()(value); 290 | else if (this->specs()->type == 'p') 291 | write_null_pointer(char_type()); 292 | else 293 | this->write(L"(null)"); 294 | return this->out(); 295 | } 296 | 297 | iterator operator()(basic_string_view value) { 298 | return base::operator()(value); 299 | } 300 | 301 | iterator operator()(monostate value) { return base::operator()(value); } 302 | 303 | /** Formats a pointer. */ 304 | iterator operator()(const void* value) { 305 | if (value) return base::operator()(value); 306 | this->specs()->type = 0; 307 | write_null_pointer(char_type()); 308 | return this->out(); 309 | } 310 | 311 | /** Formats an argument of a custom (user-defined) type. */ 312 | iterator operator()(typename basic_format_arg::handle handle) { 313 | handle.format(context_.parse_context(), context_); 314 | return this->out(); 315 | } 316 | }; 317 | 318 | template struct printf_formatter { 319 | printf_formatter() = delete; 320 | 321 | template 322 | auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 323 | return ctx.begin(); 324 | } 325 | 326 | template 327 | auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) { 328 | detail::format_value(detail::get_container(ctx.out()), value); 329 | return ctx.out(); 330 | } 331 | }; 332 | 333 | /** 334 | This template formats data and writes the output through an output iterator. 335 | */ 336 | template class basic_printf_context { 337 | public: 338 | /** The character type for the output. */ 339 | using char_type = Char; 340 | using iterator = OutputIt; 341 | using format_arg = basic_format_arg; 342 | using parse_context_type = basic_printf_parse_context; 343 | template using formatter_type = printf_formatter; 344 | 345 | private: 346 | using format_specs = basic_format_specs; 347 | 348 | OutputIt out_; 349 | basic_format_args args_; 350 | parse_context_type parse_ctx_; 351 | 352 | static void parse_flags(format_specs& specs, const Char*& it, 353 | const Char* end); 354 | 355 | // Returns the argument with specified index or, if arg_index is -1, the next 356 | // argument. 357 | format_arg get_arg(int arg_index = -1); 358 | 359 | // Parses argument index, flags and width and returns the argument index. 360 | int parse_header(const Char*& it, const Char* end, format_specs& specs); 361 | 362 | public: 363 | /** 364 | \rst 365 | Constructs a ``printf_context`` object. References to the arguments are 366 | stored in the context object so make sure they have appropriate lifetimes. 367 | \endrst 368 | */ 369 | basic_printf_context(OutputIt out, basic_string_view format_str, 370 | basic_format_args args) 371 | : out_(out), args_(args), parse_ctx_(format_str) {} 372 | 373 | OutputIt out() { return out_; } 374 | void advance_to(OutputIt it) { out_ = it; } 375 | 376 | detail::locale_ref locale() { return {}; } 377 | 378 | format_arg arg(int id) const { return args_.get(id); } 379 | 380 | parse_context_type& parse_context() { return parse_ctx_; } 381 | 382 | FMT_CONSTEXPR void on_error(const char* message) { 383 | parse_ctx_.on_error(message); 384 | } 385 | 386 | /** Formats stored arguments and writes the output to the range. */ 387 | template > 388 | OutputIt format(); 389 | }; 390 | 391 | template 392 | void basic_printf_context::parse_flags(format_specs& specs, 393 | const Char*& it, 394 | const Char* end) { 395 | for (; it != end; ++it) { 396 | switch (*it) { 397 | case '-': 398 | specs.align = align::left; 399 | break; 400 | case '+': 401 | specs.sign = sign::plus; 402 | break; 403 | case '0': 404 | specs.fill[0] = '0'; 405 | break; 406 | case ' ': 407 | if (specs.sign != sign::plus) { 408 | specs.sign = sign::space; 409 | } 410 | break; 411 | case '#': 412 | specs.alt = true; 413 | break; 414 | default: 415 | return; 416 | } 417 | } 418 | } 419 | 420 | template 421 | typename basic_printf_context::format_arg 422 | basic_printf_context::get_arg(int arg_index) { 423 | if (arg_index < 0) 424 | arg_index = parse_ctx_.next_arg_id(); 425 | else 426 | parse_ctx_.check_arg_id(--arg_index); 427 | return detail::get_arg(*this, arg_index); 428 | } 429 | 430 | template 431 | int basic_printf_context::parse_header(const Char*& it, 432 | const Char* end, 433 | format_specs& specs) { 434 | int arg_index = -1; 435 | char_type c = *it; 436 | if (c >= '0' && c <= '9') { 437 | // Parse an argument index (if followed by '$') or a width possibly 438 | // preceded with '0' flag(s). 439 | detail::error_handler eh; 440 | int value = parse_nonnegative_int(it, end, eh); 441 | if (it != end && *it == '$') { // value is an argument index 442 | ++it; 443 | arg_index = value; 444 | } else { 445 | if (c == '0') specs.fill[0] = '0'; 446 | if (value != 0) { 447 | // Nonzero value means that we parsed width and don't need to 448 | // parse it or flags again, so return now. 449 | specs.width = value; 450 | return arg_index; 451 | } 452 | } 453 | } 454 | parse_flags(specs, it, end); 455 | // Parse width. 456 | if (it != end) { 457 | if (*it >= '0' && *it <= '9') { 458 | detail::error_handler eh; 459 | specs.width = parse_nonnegative_int(it, end, eh); 460 | } else if (*it == '*') { 461 | ++it; 462 | specs.width = static_cast(visit_format_arg( 463 | detail::printf_width_handler(specs), get_arg())); 464 | } 465 | } 466 | return arg_index; 467 | } 468 | 469 | template 470 | template 471 | OutputIt basic_printf_context::format() { 472 | auto out = this->out(); 473 | const Char* start = parse_ctx_.begin(); 474 | const Char* end = parse_ctx_.end(); 475 | auto it = start; 476 | while (it != end) { 477 | char_type c = *it++; 478 | if (c != '%') continue; 479 | if (it != end && *it == c) { 480 | out = std::copy(start, it, out); 481 | start = ++it; 482 | continue; 483 | } 484 | out = std::copy(start, it - 1, out); 485 | 486 | format_specs specs; 487 | specs.align = align::right; 488 | 489 | // Parse argument index, flags and width. 490 | int arg_index = parse_header(it, end, specs); 491 | if (arg_index == 0) on_error("argument not found"); 492 | 493 | // Parse precision. 494 | if (it != end && *it == '.') { 495 | ++it; 496 | c = it != end ? *it : 0; 497 | if ('0' <= c && c <= '9') { 498 | detail::error_handler eh; 499 | specs.precision = parse_nonnegative_int(it, end, eh); 500 | } else if (c == '*') { 501 | ++it; 502 | specs.precision = static_cast( 503 | visit_format_arg(detail::printf_precision_handler(), get_arg())); 504 | } else { 505 | specs.precision = 0; 506 | } 507 | } 508 | 509 | format_arg arg = get_arg(arg_index); 510 | // For d, i, o, u, x, and X conversion specifiers, if a precision is 511 | // specified, the '0' flag is ignored 512 | if (specs.precision >= 0 && arg.is_integral()) 513 | specs.fill[0] = 514 | ' '; // Ignore '0' flag for non-numeric types or if '-' present. 515 | if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) { 516 | auto str = visit_format_arg(detail::get_cstring(), arg); 517 | auto str_end = str + specs.precision; 518 | auto nul = std::find(str, str_end, Char()); 519 | arg = detail::make_arg(basic_string_view( 520 | str, 521 | detail::to_unsigned(nul != str_end ? nul - str : specs.precision))); 522 | } 523 | if (specs.alt && visit_format_arg(detail::is_zero_int(), arg)) 524 | specs.alt = false; 525 | if (specs.fill[0] == '0') { 526 | if (arg.is_arithmetic() && specs.align != align::left) 527 | specs.align = align::numeric; 528 | else 529 | specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-' 530 | // flag is also present. 531 | } 532 | 533 | // Parse length and convert the argument to the required type. 534 | c = it != end ? *it++ : 0; 535 | char_type t = it != end ? *it : 0; 536 | using detail::convert_arg; 537 | switch (c) { 538 | case 'h': 539 | if (t == 'h') { 540 | ++it; 541 | t = it != end ? *it : 0; 542 | convert_arg(arg, t); 543 | } else { 544 | convert_arg(arg, t); 545 | } 546 | break; 547 | case 'l': 548 | if (t == 'l') { 549 | ++it; 550 | t = it != end ? *it : 0; 551 | convert_arg(arg, t); 552 | } else { 553 | convert_arg(arg, t); 554 | } 555 | break; 556 | case 'j': 557 | convert_arg(arg, t); 558 | break; 559 | case 'z': 560 | convert_arg(arg, t); 561 | break; 562 | case 't': 563 | convert_arg(arg, t); 564 | break; 565 | case 'L': 566 | // printf produces garbage when 'L' is omitted for long double, no 567 | // need to do the same. 568 | break; 569 | default: 570 | --it; 571 | convert_arg(arg, c); 572 | } 573 | 574 | // Parse type. 575 | if (it == end) FMT_THROW(format_error("invalid format string")); 576 | specs.type = static_cast(*it++); 577 | if (arg.is_integral()) { 578 | // Normalize type. 579 | switch (specs.type) { 580 | case 'i': 581 | case 'u': 582 | specs.type = 'd'; 583 | break; 584 | case 'c': 585 | visit_format_arg(detail::char_converter(arg), 586 | arg); 587 | break; 588 | } 589 | } 590 | 591 | start = it; 592 | 593 | // Format argument. 594 | out = visit_format_arg(ArgFormatter(out, specs, *this), arg); 595 | } 596 | return std::copy(start, it, out); 597 | } 598 | 599 | template 600 | using basic_printf_context_t = 601 | basic_printf_context, Char>; 602 | 603 | using printf_context = basic_printf_context_t; 604 | using wprintf_context = basic_printf_context_t; 605 | 606 | using printf_args = basic_format_args; 607 | using wprintf_args = basic_format_args; 608 | 609 | /** 610 | \rst 611 | Constructs an `~fmt::format_arg_store` object that contains references to 612 | arguments and can be implicitly converted to `~fmt::printf_args`. 613 | \endrst 614 | */ 615 | template 616 | inline format_arg_store make_printf_args( 617 | const Args&... args) { 618 | return {args...}; 619 | } 620 | 621 | /** 622 | \rst 623 | Constructs an `~fmt::format_arg_store` object that contains references to 624 | arguments and can be implicitly converted to `~fmt::wprintf_args`. 625 | \endrst 626 | */ 627 | template 628 | inline format_arg_store make_wprintf_args( 629 | const Args&... args) { 630 | return {args...}; 631 | } 632 | 633 | template > 634 | inline std::basic_string vsprintf( 635 | const S& format, 636 | basic_format_args>> args) { 637 | basic_memory_buffer buffer; 638 | vprintf(buffer, to_string_view(format), args); 639 | return to_string(buffer); 640 | } 641 | 642 | /** 643 | \rst 644 | Formats arguments and returns the result as a string. 645 | 646 | **Example**:: 647 | 648 | std::string message = fmt::sprintf("The answer is %d", 42); 649 | \endrst 650 | */ 651 | template ::value, char_t>> 653 | inline std::basic_string sprintf(const S& format, const Args&... args) { 654 | using context = basic_printf_context_t; 655 | return vsprintf(to_string_view(format), make_format_args(args...)); 656 | } 657 | 658 | template > 659 | inline int vfprintf( 660 | std::FILE* f, const S& format, 661 | basic_format_args>> args) { 662 | basic_memory_buffer buffer; 663 | vprintf(buffer, to_string_view(format), args); 664 | size_t size = buffer.size(); 665 | return std::fwrite(buffer.data(), sizeof(Char), size, f) < size 666 | ? -1 667 | : static_cast(size); 668 | } 669 | 670 | /** 671 | \rst 672 | Prints formatted data to the file *f*. 673 | 674 | **Example**:: 675 | 676 | fmt::fprintf(stderr, "Don't %s!", "panic"); 677 | \endrst 678 | */ 679 | template ::value, char_t>> 681 | inline int fprintf(std::FILE* f, const S& format, const Args&... args) { 682 | using context = basic_printf_context_t; 683 | return vfprintf(f, to_string_view(format), 684 | make_format_args(args...)); 685 | } 686 | 687 | template > 688 | inline int vprintf( 689 | const S& format, 690 | basic_format_args>> args) { 691 | return vfprintf(stdout, to_string_view(format), args); 692 | } 693 | 694 | /** 695 | \rst 696 | Prints formatted data to ``stdout``. 697 | 698 | **Example**:: 699 | 700 | fmt::printf("Elapsed time: %.2f seconds", 1.23); 701 | \endrst 702 | */ 703 | template ::value)> 705 | inline int printf(const S& format_str, const Args&... args) { 706 | using context = basic_printf_context_t>; 707 | return vprintf(to_string_view(format_str), 708 | make_format_args(args...)); 709 | } 710 | 711 | template > 712 | inline int vfprintf( 713 | std::basic_ostream& os, const S& format, 714 | basic_format_args>> args) { 715 | basic_memory_buffer buffer; 716 | vprintf(buffer, to_string_view(format), args); 717 | detail::write_buffer(os, buffer); 718 | return static_cast(buffer.size()); 719 | } 720 | 721 | /** Formats arguments and writes the output to the range. */ 722 | template > 725 | typename ArgFormatter::iterator vprintf( 726 | detail::buffer& out, basic_string_view format_str, 727 | basic_format_args> args) { 728 | typename ArgFormatter::iterator iter(out); 729 | Context(iter, format_str, args).template format(); 730 | return iter; 731 | } 732 | 733 | /** 734 | \rst 735 | Prints formatted data to the stream *os*. 736 | 737 | **Example**:: 738 | 739 | fmt::fprintf(cerr, "Don't %s!", "panic"); 740 | \endrst 741 | */ 742 | template > 743 | inline int fprintf(std::basic_ostream& os, const S& format_str, 744 | const Args&... args) { 745 | using context = basic_printf_context_t; 746 | return vfprintf(os, to_string_view(format_str), 747 | make_format_args(args...)); 748 | } 749 | FMT_END_NAMESPACE 750 | 751 | #endif // FMT_PRINTF_H_ 752 | -------------------------------------------------------------------------------- /includes/fmt/ranges.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - experimental range support 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | // 8 | // Copyright (c) 2018 - present, Remotion (Igor Schulz) 9 | // All Rights Reserved 10 | // {fmt} support for ranges, containers and types tuple interface. 11 | 12 | #ifndef FMT_RANGES_H_ 13 | #define FMT_RANGES_H_ 14 | 15 | #include 16 | #include 17 | 18 | #include "format.h" 19 | 20 | // output only up to N items from the range. 21 | #ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT 22 | # define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256 23 | #endif 24 | 25 | FMT_BEGIN_NAMESPACE 26 | 27 | template struct formatting_base { 28 | template 29 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 30 | return ctx.begin(); 31 | } 32 | }; 33 | 34 | template 35 | struct formatting_range : formatting_base { 36 | static FMT_CONSTEXPR_DECL const size_t range_length_limit = 37 | FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the 38 | // range. 39 | Char prefix; 40 | Char delimiter; 41 | Char postfix; 42 | formatting_range() : prefix('{'), delimiter(','), postfix('}') {} 43 | static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true; 44 | static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; 45 | }; 46 | 47 | template 48 | struct formatting_tuple : formatting_base { 49 | Char prefix; 50 | Char delimiter; 51 | Char postfix; 52 | formatting_tuple() : prefix('('), delimiter(','), postfix(')') {} 53 | static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true; 54 | static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; 55 | }; 56 | 57 | namespace detail { 58 | 59 | template 60 | OutputIterator copy(const RangeT& range, OutputIterator out) { 61 | for (auto it = range.begin(), end = range.end(); it != end; ++it) 62 | *out++ = *it; 63 | return out; 64 | } 65 | 66 | template 67 | OutputIterator copy(const char* str, OutputIterator out) { 68 | while (*str) *out++ = *str++; 69 | return out; 70 | } 71 | 72 | template 73 | OutputIterator copy(char ch, OutputIterator out) { 74 | *out++ = ch; 75 | return out; 76 | } 77 | 78 | /// Return true value if T has std::string interface, like std::string_view. 79 | template class is_like_std_string { 80 | template 81 | static auto check(U* p) 82 | -> decltype((void)p->find('a'), p->length(), (void)p->data(), int()); 83 | template static void check(...); 84 | 85 | public: 86 | static FMT_CONSTEXPR_DECL const bool value = 87 | is_string::value || !std::is_void(nullptr))>::value; 88 | }; 89 | 90 | template 91 | struct is_like_std_string> : std::true_type {}; 92 | 93 | template struct conditional_helper {}; 94 | 95 | template struct is_range_ : std::false_type {}; 96 | 97 | #if !FMT_MSC_VER || FMT_MSC_VER > 1800 98 | template 99 | struct is_range_< 100 | T, conditional_t().begin()), 102 | decltype(std::declval().end())>, 103 | void>> : std::true_type {}; 104 | #endif 105 | 106 | /// tuple_size and tuple_element check. 107 | template class is_tuple_like_ { 108 | template 109 | static auto check(U* p) -> decltype(std::tuple_size::value, int()); 110 | template static void check(...); 111 | 112 | public: 113 | static FMT_CONSTEXPR_DECL const bool value = 114 | !std::is_void(nullptr))>::value; 115 | }; 116 | 117 | // Check for integer_sequence 118 | #if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900 119 | template 120 | using integer_sequence = std::integer_sequence; 121 | template using index_sequence = std::index_sequence; 122 | template using make_index_sequence = std::make_index_sequence; 123 | #else 124 | template struct integer_sequence { 125 | using value_type = T; 126 | 127 | static FMT_CONSTEXPR size_t size() { return sizeof...(N); } 128 | }; 129 | 130 | template using index_sequence = integer_sequence; 131 | 132 | template 133 | struct make_integer_sequence : make_integer_sequence {}; 134 | template 135 | struct make_integer_sequence : integer_sequence {}; 136 | 137 | template 138 | using make_index_sequence = make_integer_sequence; 139 | #endif 140 | 141 | template 142 | void for_each(index_sequence, Tuple&& tup, F&& f) FMT_NOEXCEPT { 143 | using std::get; 144 | // using free function get(T) now. 145 | const int _[] = {0, ((void)f(get(tup)), 0)...}; 146 | (void)_; // blocks warnings 147 | } 148 | 149 | template 150 | FMT_CONSTEXPR make_index_sequence::value> get_indexes( 151 | T const&) { 152 | return {}; 153 | } 154 | 155 | template void for_each(Tuple&& tup, F&& f) { 156 | const auto indexes = get_indexes(tup); 157 | for_each(indexes, std::forward(tup), std::forward(f)); 158 | } 159 | 160 | template 161 | using value_type = remove_cvref_t().begin())>; 162 | 163 | template ::type>::value)> 165 | FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) { 166 | return add_space ? " {}" : "{}"; 167 | } 168 | 169 | template ::type>::value)> 171 | FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) { 172 | return add_space ? " \"{}\"" : "\"{}\""; 173 | } 174 | 175 | FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) { 176 | return add_space ? " \"{}\"" : "\"{}\""; 177 | } 178 | FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) { 179 | return add_space ? L" \"{}\"" : L"\"{}\""; 180 | } 181 | 182 | FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) { 183 | return add_space ? " '{}'" : "'{}'"; 184 | } 185 | FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) { 186 | return add_space ? L" '{}'" : L"'{}'"; 187 | } 188 | } // namespace detail 189 | 190 | template struct is_tuple_like { 191 | static FMT_CONSTEXPR_DECL const bool value = 192 | detail::is_tuple_like_::value && !detail::is_range_::value; 193 | }; 194 | 195 | template 196 | struct formatter::value>> { 197 | private: 198 | // C++11 generic lambda for format() 199 | template struct format_each { 200 | template void operator()(const T& v) { 201 | if (i > 0) { 202 | if (formatting.add_prepostfix_space) { 203 | *out++ = ' '; 204 | } 205 | out = detail::copy(formatting.delimiter, out); 206 | } 207 | out = format_to(out, 208 | detail::format_str_quoted( 209 | (formatting.add_delimiter_spaces && i > 0), v), 210 | v); 211 | ++i; 212 | } 213 | 214 | formatting_tuple& formatting; 215 | size_t& i; 216 | typename std::add_lvalue_reference().out())>::type out; 218 | }; 219 | 220 | public: 221 | formatting_tuple formatting; 222 | 223 | template 224 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 225 | return formatting.parse(ctx); 226 | } 227 | 228 | template 229 | auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) { 230 | auto out = ctx.out(); 231 | size_t i = 0; 232 | detail::copy(formatting.prefix, out); 233 | 234 | detail::for_each(values, format_each{formatting, i, out}); 235 | if (formatting.add_prepostfix_space) { 236 | *out++ = ' '; 237 | } 238 | detail::copy(formatting.postfix, out); 239 | 240 | return ctx.out(); 241 | } 242 | }; 243 | 244 | template struct is_range { 245 | static FMT_CONSTEXPR_DECL const bool value = 246 | detail::is_range_::value && !detail::is_like_std_string::value && 247 | !std::is_convertible>::value && 248 | !std::is_constructible, T>::value; 249 | }; 250 | 251 | template 252 | struct formatter< 253 | T, Char, 254 | enable_if_t::value 255 | // Workaround a bug in MSVC 2017 and earlier. 256 | #if !FMT_MSC_VER || FMT_MSC_VER >= 1927 257 | && 258 | (has_formatter, format_context>::value || 259 | detail::has_fallback_formatter, 260 | format_context>::value) 261 | #endif 262 | >> { 263 | formatting_range formatting; 264 | 265 | template 266 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 267 | return formatting.parse(ctx); 268 | } 269 | 270 | template 271 | typename FormatContext::iterator format(const T& values, FormatContext& ctx) { 272 | auto out = detail::copy(formatting.prefix, ctx.out()); 273 | size_t i = 0; 274 | auto it = values.begin(); 275 | auto end = values.end(); 276 | for (; it != end; ++it) { 277 | if (i > 0) { 278 | if (formatting.add_prepostfix_space) *out++ = ' '; 279 | out = detail::copy(formatting.delimiter, out); 280 | } 281 | out = format_to(out, 282 | detail::format_str_quoted( 283 | (formatting.add_delimiter_spaces && i > 0), *it), 284 | *it); 285 | if (++i > formatting.range_length_limit) { 286 | out = format_to(out, " ... "); 287 | break; 288 | } 289 | } 290 | if (formatting.add_prepostfix_space) *out++ = ' '; 291 | return detail::copy(formatting.postfix, out); 292 | } 293 | }; 294 | 295 | template struct tuple_arg_join : detail::view { 296 | const std::tuple& tuple; 297 | basic_string_view sep; 298 | 299 | tuple_arg_join(const std::tuple& t, basic_string_view s) 300 | : tuple{t}, sep{s} {} 301 | }; 302 | 303 | template 304 | struct formatter, Char> { 305 | template 306 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 307 | return ctx.begin(); 308 | } 309 | 310 | template 311 | typename FormatContext::iterator format( 312 | const tuple_arg_join& value, FormatContext& ctx) { 313 | return format(value, ctx, detail::make_index_sequence{}); 314 | } 315 | 316 | private: 317 | template 318 | typename FormatContext::iterator format( 319 | const tuple_arg_join& value, FormatContext& ctx, 320 | detail::index_sequence) { 321 | return format_args(value, ctx, std::get(value.tuple)...); 322 | } 323 | 324 | template 325 | typename FormatContext::iterator format_args( 326 | const tuple_arg_join&, FormatContext& ctx) { 327 | // NOTE: for compilers that support C++17, this empty function instantiation 328 | // can be replaced with a constexpr branch in the variadic overload. 329 | return ctx.out(); 330 | } 331 | 332 | template 333 | typename FormatContext::iterator format_args( 334 | const tuple_arg_join& value, FormatContext& ctx, 335 | const Arg& arg, const Args&... args) { 336 | using base = formatter::type, Char>; 337 | auto out = ctx.out(); 338 | out = base{}.format(arg, ctx); 339 | if (sizeof...(Args) > 0) { 340 | out = std::copy(value.sep.begin(), value.sep.end(), out); 341 | ctx.advance_to(out); 342 | return format_args(value, ctx, args...); 343 | } 344 | return out; 345 | } 346 | }; 347 | 348 | /** 349 | \rst 350 | Returns an object that formats `tuple` with elements separated by `sep`. 351 | 352 | **Example**:: 353 | 354 | std::tuple t = {1, 'a'}; 355 | fmt::print("{}", fmt::join(t, ", ")); 356 | // Output: "1, a" 357 | \endrst 358 | */ 359 | template 360 | FMT_CONSTEXPR tuple_arg_join join(const std::tuple& tuple, 361 | string_view sep) { 362 | return {tuple, sep}; 363 | } 364 | 365 | template 366 | FMT_CONSTEXPR tuple_arg_join join(const std::tuple& tuple, 367 | wstring_view sep) { 368 | return {tuple, sep}; 369 | } 370 | 371 | /** 372 | \rst 373 | Returns an object that formats `initializer_list` with elements separated by 374 | `sep`. 375 | 376 | **Example**:: 377 | 378 | fmt::print("{}", fmt::join({1, 2, 3}, ", ")); 379 | // Output: "1, 2, 3" 380 | \endrst 381 | */ 382 | template 383 | arg_join join(std::initializer_list list, 384 | string_view sep) { 385 | return join(std::begin(list), std::end(list), sep); 386 | } 387 | 388 | template 389 | arg_join join(std::initializer_list list, 390 | wstring_view sep) { 391 | return join(std::begin(list), std::end(list), sep); 392 | } 393 | 394 | FMT_END_NAMESPACE 395 | 396 | #endif // FMT_RANGES_H_ 397 | -------------------------------------------------------------------------------- /includes/kern_context.h: -------------------------------------------------------------------------------- 1 | #ifdef __cplusplus 2 | extern "C" { 3 | #endif 4 | 5 | #include 6 | #include 7 | 8 | typedef uint64_t addr_t; 9 | 10 | typedef enum { 11 | KERNLOG_NONE = 0, 12 | KERNLOG_FUNC, 13 | KERNLOG_INIT, 14 | KERNLOG_KERNRW, 15 | } KernInfraLogLevel; 16 | 17 | typedef struct { 18 | void (*remote_reader)(addr_t addr, void *buf, size_t size); 19 | void (*remote_writer)(addr_t addr, const void *buf, size_t size); 20 | addr_t (*addr_reader)(addr_t addr); 21 | void (*addr_writer)(addr_t addr, addr_t val); 22 | addr_t (*addr_processor)(addr_t addr); 23 | void (*vDoLog)(const char * format, va_list args); 24 | KernInfraLogLevel logLevel; 25 | } KernInfraContext; 26 | 27 | extern KernInfraContext kerninfra_context; 28 | 29 | extern void kerninfra_log(int ll, const char * format, ...); 30 | 31 | #ifdef __cplusplus 32 | } 33 | #endif 34 | 35 | #define KERNINFRA_LOG(ll, ...) \ 36 | do {if (ll <= kerninfra_context.logLevel) kerninfra_log(ll, __VA_ARGS__); } while(0); -------------------------------------------------------------------------------- /includes/sysvers.h: -------------------------------------------------------------------------------- 1 | extern "C" { 2 | extern double kCFCoreFoundationVersionNumber; 3 | } 4 | 5 | #pragma clang diagnostic push 6 | #pragma clang diagnostic ignored "-Wmacro-redefined" 7 | 8 | #define kCFCoreFoundationVersionNumber_iOS_4_3 550.58 9 | 10 | #define kCFCoreFoundationVersionNumber_iOS_5_0 675.00 11 | 12 | #define kCFCoreFoundationVersionNumber_iOS_5_1 690.10 13 | 14 | #define kCFCoreFoundationVersionNumber_iOS_6_0 793.00 15 | 16 | #define kCFCoreFoundationVersionNumber_iOS_6_1 793.00 17 | 18 | #define kCFCoreFoundationVersionNumber_iOS_7_0 847.20 19 | 20 | #define kCFCoreFoundationVersionNumber_iOS_7_0_3 847.21 21 | 22 | #define kCFCoreFoundationVersionNumber_iOS_7_1 847.26 23 | 24 | #define kCFCoreFoundationVersionNumber_iOS_8_0 1140.10 25 | 26 | #define kCFCoreFoundationVersionNumber_iOS_8_1 1141.14 27 | 28 | #define kCFCoreFoundationVersionNumber_iOS_8_2 1142.16 29 | 30 | #define kCFCoreFoundationVersionNumber_iOS_8_3 1144.17 31 | 32 | #define kCFCoreFoundationVersionNumber_iOS_8_4 1145.15 33 | 34 | #define kCFCoreFoundationVersionNumber_iOS_9_0 1240.1 35 | 36 | #define kCFCoreFoundationVersionNumber_iOS_9_1 1241.11 37 | 38 | #define kCFCoreFoundationVersionNumber_iOS_9_2 1242.13 39 | 40 | #define kCFCoreFoundationVersionNumber_iOS_9_3 1280.30 41 | 42 | #define kCFCoreFoundationVersionNumber_iOS_10_0 1348.00 43 | 44 | #define kCFCoreFoundationVersionNumber_iOS_10_1 1348.00 45 | 46 | #define kCFCoreFoundationVersionNumber_iOS_10_2 1348.22 47 | 48 | #define kCFCoreFoundationVersionNumber_iOS_10_3 1349.56 49 | 50 | #define kCFCoreFoundationVersionNumber_iOS_11_0_b1 1429.15 51 | 52 | #define kCFCoreFoundationVersionNumber_iOS_11_0 1443.00 53 | 54 | #define kCFCoreFoundationVersionNumber_iOS_11_1 1445.32 55 | 56 | #define kCFCoreFoundationVersionNumber_iOS_11_2 1450.14 57 | 58 | #define kCFCoreFoundationVersionNumber_iOS_11_3 1452.23 59 | 60 | #define kCFCoreFoundationVersionNumber_iOS_11_4 1452.23 61 | 62 | #define kCFCoreFoundationVersionNumber_iOS_12_0_b1 1535.13 63 | 64 | #define kCFCoreFoundationVersionNumber_iOS_12_0 1556.00 65 | 66 | #define kCFCoreFoundationVersionNumber_iOS_12_1 1560.10 67 | 68 | #define kCFCoreFoundationVersionNumber_iOS_12_2 1570.15 69 | 70 | #define kCFCoreFoundationVersionNumber_iOS_12_3 1575.13 71 | 72 | #define kCFCoreFoundationVersionNumber_iOS_12_4 1575.17 73 | 74 | #define kCFCoreFoundationVersionNumber_iOS_12_5 1575.23 75 | 76 | #define kCFCoreFoundationVersionNumber_iOS_13_0_b1 1652.20 77 | 78 | #define kCFCoreFoundationVersionNumber_iOS_13_0 1665.15 79 | 80 | #define kCFCoreFoundationVersionNumber_iOS_13_1 1671.101 81 | 82 | #define kCFCoreFoundationVersionNumber_iOS_13_2 1673.126 83 | 84 | #define kCFCoreFoundationVersionNumber_iOS_13_3 1674.102 85 | 86 | #define kCFCoreFoundationVersionNumber_iOS_13_4 1675.129 87 | 88 | #define kCFCoreFoundationVersionNumber_iOS_13_5 1676.104 89 | 90 | #define kCFCoreFoundationVersionNumber_iOS_13_6 1677.104 91 | 92 | #define kCFCoreFoundationVersionNumber_iOS_13_7 1677.104 93 | 94 | #define kCFCoreFoundationVersionNumber_iOS_14_0_b1 1740.0 95 | 96 | #define kCFCoreFoundationVersionNumber_iOS_14_0 1751.108 97 | 98 | #define kCFCoreFoundationVersionNumber_iOS_14_1 1751.108 99 | 100 | #define kCFCoreFoundationVersionNumber_iOS_14_2 1770.106 101 | 102 | #define kCFCoreFoundationVersionNumber_iOS_14_3_b1 1770.300 103 | 104 | #define kCFCoreFoundationVersionNumber_iOS_14_3 1770.300 105 | 106 | #define kCFCoreFoundationVersionNumber_iOS_14_4 1774.101 107 | 108 | #define kCFCoreFoundationVersionNumber_iOS_14_5 1775.118 109 | 110 | #define kCFCoreFoundationVersionNumber10_10 1151.16 111 | 112 | #pragma clang diagnostic pop 113 | -------------------------------------------------------------------------------- /kern_func.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "kerninfra.hpp" 6 | 7 | #define FLOG(...) KERNINFRA_LOG(KERNLOG_FUNC, __VA_ARGS__) 8 | 9 | addr_t proc_of_pid(pid_t pid) { 10 | addr_t procAddr = kernel_read64(allproc); 11 | uint64_t current_pid = 0; 12 | 13 | while (procAddr) { 14 | auto proc = proc_t_p(procAddr); 15 | current_pid = proc.p_pid().load(); 16 | if (current_pid == pid) return procAddr; 17 | procAddr = proc.nextproc().load(); 18 | FLOG("proc_of_pid: proc %llx pid %llu", procAddr, current_pid); 19 | } 20 | throw std::runtime_error(fmt::format("proc_of_pid failed to find proc for {}", pid)); 21 | } 22 | 23 | addr_t current_proc() { 24 | addr_t proc = proc_of_pid(getpid()); 25 | //printf("proc: %llx\n", proc); 26 | FLOG("current_proc: found proc %llx", proc); 27 | return proc; 28 | } 29 | 30 | 31 | addr_t vnode_from_fd(int fd) { 32 | auto proc = proc_t_p(current_proc()); 33 | addr_t ofiles = proc.p_fd().fd_ofiles().load(); 34 | FLOG("vnode_from_fd: got ofiles array %llx", ofiles); 35 | auto fp = fileproc_p(kpointer_t<>(ofiles + 8 * fd).load_addr()); 36 | return fp.f_fglob().fg_data().load(); 37 | } 38 | 39 | addr_t lookup_vm_map_entry(addr_t _vmmap, addr_t va) { 40 | auto vmmap = _vm_map_t_p(_vmmap); 41 | int n = vmmap.nentries().load(); 42 | FLOG("lookup_vm_map_entry: Totally %d vm entries", n); 43 | addr_t curEntry = vmmap.link_next().load(); 44 | for (int i = 0; i < n; i++) { 45 | auto _curEntry = _vm_map_entry_p(curEntry); 46 | FLOG("lookup_vm_map_entry: VM Entry: %p - %p", (void *)_curEntry.start().load(), (void *)_curEntry.end().load()); 47 | if (va >= _curEntry.start().load() && va < _curEntry.end().load()) { 48 | return curEntry; 49 | } 50 | curEntry = _curEntry.link_next().load(); 51 | } 52 | return 0; 53 | } -------------------------------------------------------------------------------- /kern_func.hpp: -------------------------------------------------------------------------------- 1 | uint64_t proc_of_pid(pid_t pid); 2 | uint64_t current_proc(); 3 | 4 | addr_t vnode_from_fd(int fd); 5 | 6 | addr_t lookup_vm_map_entry(addr_t _vmmap, addr_t va); -------------------------------------------------------------------------------- /kerninfra.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "includes/sysvers.h" 3 | 4 | #include "includes/kern_context.h" 5 | 6 | //#include "rw_prov/rw_prov.h" 7 | //#include "patchfinder/libdimentio.h" 8 | extern "C" { 9 | 10 | #include "rw_prov/rw_prov.h" 11 | #include "patchfinder/libdimentio.h" 12 | 13 | } 14 | 15 | 16 | #include "kernstructs.hpp" 17 | #include "kern_func.hpp" 18 | 19 | int init_kerninfra(int logLevel, void (*vDoLog)(const char *, va_list args) = NULL); -------------------------------------------------------------------------------- /kerninfra.mm: -------------------------------------------------------------------------------- 1 | #include "kerninfra.hpp" 2 | 3 | #include 4 | 5 | void kernVPrintf(const char *fmtstr, va_list args) { 6 | std::string fmtstr_ = fmtstr; 7 | fmtstr_ += "\n"; 8 | vprintf(fmtstr_.c_str(), args); 9 | } 10 | 11 | KernInfraContext kerninfra_context = { 12 | .vDoLog = (decltype(kerninfra_context.vDoLog))kernVPrintf, 13 | .logLevel = KERNLOG_NONE, 14 | }; 15 | 16 | void kerninfra_log(int ll, const char * format, ...) { 17 | if (ll <= kerninfra_context.logLevel) { 18 | va_list ap; 19 | va_start(ap, format); 20 | kerninfra_context.vDoLog(format, ap); 21 | va_end(ap); 22 | } 23 | } 24 | 25 | //int init_kerninfra(int logLevel, void (*vDoLog)(const char *, va_list args) = (decltype(vDoLog))vprintf) { 26 | int init_kerninfra(int logLevel, void (*vDoLog)(const char *, va_list args)) { 27 | kerninfra_context.logLevel = (decltype(kerninfra_context.logLevel))logLevel; 28 | if (!!vDoLog) { 29 | kerninfra_context.vDoLog = vDoLog; 30 | } 31 | 32 | if (rw_prov_init() != 0) { 33 | printf("failed rw provider's init!\n"); 34 | return 1; 35 | } 36 | if (patchfinder_init() != KERN_SUCCESS) { 37 | printf("failed patchfinder dimentio's init!\n"); 38 | return 1; 39 | } 40 | prepare_rw_wrap(&kerninfra_context); 41 | 42 | return 0; 43 | } -------------------------------------------------------------------------------- /kernstructs.hpp: -------------------------------------------------------------------------------- 1 | #include "rw_wrap/rw_wrap.hpp" 2 | 3 | #define VERSION_OFF(curver, curoff) if (kCFCoreFoundationVersionNumber >= curver) off = curoff; 4 | 5 | 6 | REMOTETYPE(ubc_info, 7 | REMOTE_FIELD(kpointer_t, cs_blobs, // can be found in ubc_cs_blob_get, which is usually the next function of _csvnode_print_debug (identifiable by "(unknown vnode name)") 8 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_5, 0x50) 9 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x50) 10 | ); 11 | ) 12 | 13 | REMOTETYPE(vnode_t, 14 | REMOTE_FIELD(kuint32_t, v_usecount, 15 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_12_0, 0x60) 16 | ); 17 | REMOTE_FIELD(kuint32_t, v_iocount, 18 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_12_0, 0x64) 19 | ); 20 | 21 | REMOTE_FIELD(kuint16_t, v_type, // can be found in ubc_cs_blob_get 22 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_4, 0x70) 23 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_5, 0x70) 24 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x70) 25 | ); 26 | 27 | REMOTE_FIELD(kuint16_t, v_tag, 28 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x72) 29 | ); 30 | 31 | REMOTE_FIELD(kuint16_t, v_id, 32 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x74) 33 | ); 34 | 35 | REMOTE_FIELD(kpointer_t, v_un, // can be found in ubc_cs_blob_get 36 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_4, 0x78) 37 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_5, 0x78) 38 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x78) 39 | ); 40 | 41 | REMOTE_FIELD(mount_t, v_mount, // pretty easy 42 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_12_0, 0xd8) 43 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_5, 0xd8) 44 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0xd8) 45 | ); 46 | 47 | REMOTE_FIELD(kpointer_t, v_name, // (identifiable by "(unknown vnode name)") 48 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_4, 0xb8) 49 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_5, 0xb8) 50 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0xb8) 51 | ); 52 | 53 | ) 54 | 55 | REMOTETYPE(mount_t, 56 | REMOTE_FIELD(kuint32_t, mnt_flag, 57 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_12_0, 0x70) 58 | ); 59 | REMOTE_FIELD(kuint32_t, mnt_kern_flag, 60 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_12_0, 0x74) 61 | ); 62 | 63 | REMOTE_FIELD(vnode_t, mnt_devvp, 64 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x980) 65 | ); 66 | ) 67 | 68 | REMOTETYPE(fileglob, 69 | REMOTE_FIELD(vnode_t, fg_data, 70 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x38) 71 | ); 72 | ) 73 | 74 | 75 | REMOTETYPE(fileproc, 76 | REMOTE_FIELD(fileglob, f_fglob, 77 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x10) 78 | ); 79 | ) 80 | 81 | REMOTETYPE(filedesc, 82 | REMOTE_FIELD(kpointer_t, fd_ofiles, 83 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0) 84 | ); 85 | REMOTE_FIELD(vnode_t, fd_rdir, 86 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_12_0, 0x38) 87 | ); 88 | REMOTE_FIELD(vnode_t, fd_cdir, 89 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_12_0, 0x40) 90 | ); 91 | ) 92 | 93 | REMOTETYPE(apple_protect_pager_t, // extends from memory_object_t 94 | // all offsets can be found in apple_protect_pager_create, which is identifiable using apple_protect_pager_ops (which is a method table that can be easily found with "apple_protect_pager" string) 95 | REMOTE_FIELD(kpointer_t, backing_object, 96 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_4, 0x38) 97 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_5, 0x38) 98 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x38) 99 | ); 100 | REMOTE_FIELD(kuint64_t, backing_offset, 101 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_4, 0x40) 102 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_5, 0x40) 103 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x40) 104 | ); 105 | REMOTE_FIELD(kuint64_t, crypto_backing_offset, 106 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_4, 0x48) 107 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_5, 0x48) 108 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x48) 109 | ); 110 | REMOTE_FIELD(kuint64_t, crypto_start, 111 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_4, 0x50) 112 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_5, 0x50) 113 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x50) 114 | ); 115 | REMOTE_FIELD(kuint64_t, crypto_end, 116 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_4, 0x58) 117 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_5, 0x58) 118 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x58) 119 | ); 120 | REMOTE_FIELD(kpointer_t, crypt_info, 121 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_4, 0x60) 122 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_5, 0x60) 123 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x60) 124 | ); 125 | 126 | ) 127 | 128 | REMOTETYPE(_memory_object_t, 129 | REMOTE_FIELD(kpointer_t, mo_pager_ops, // can be found in apple_protect_pager_create 130 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_4, 0x8) 131 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_5, 0x8) 132 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x8) 133 | ); 134 | ) 135 | 136 | REMOTETYPE(vm_object_t, 137 | REMOTE_FIELD(vm_object_t, shadow, // can be found in vm_object_shadow (identified by panic string) 138 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_4, 0x40) 139 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_5, 0x40) 140 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x40) 141 | ); 142 | REMOTE_FIELD(_memory_object_t, pager, // can be found in functions with reference to vnode_pager_ops (usually doing object->pager->mo_pager_ops)) 143 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_4, 0x48) 144 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_5, 0x48) 145 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x48) 146 | ); 147 | ) 148 | 149 | REMOTETYPE(_vm_map_entry, 150 | // all offsets can be found in vm_map_entry_insert (identifiable via reference to vm_map_entry_create, usually the first ref to it) 151 | REMOTE_FIELD(_vm_map_entry, link_prev, // infered from struct definition 152 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_12_0, 0x0) 153 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x0) 154 | ); 155 | REMOTE_FIELD(_vm_map_entry, link_next, // infered from struct definition 156 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_12_0, 0x8) 157 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x8) 158 | ); 159 | REMOTE_FIELD(kuint64_t, start, // infered from struct definition 160 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_12_0, 0x10) 161 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x10) 162 | ); 163 | REMOTE_FIELD(kuint64_t, end, // infered from struct definition 164 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_12_0, 0x18) 165 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x18) 166 | ); 167 | REMOTE_FIELD(vm_object_t, vme_object, // can be found in kmem_realloc (identifiable with panic string) 168 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_4, 0x38) 169 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_5, 0x38) 170 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x38) 171 | ); 172 | REMOTE_FIELD(kuint64_t, vme_offset, // can be found in kmem_alloc's VME_OFFSET_SET pattern 173 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_4, 0x40) 174 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_5, 0x40) 175 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x40) 176 | ); 177 | REMOTE_FIELD(kuint32_t, vme_flags, // inferred from struct definition 178 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_4, 0x48) 179 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_5, 0x48) 180 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x48) 181 | ); 182 | ) 183 | 184 | REMOTETYPE(_vm_map_t, 185 | // all offsets can be found in vm_map_create_options 186 | REMOTE_FIELD(_vm_map_entry, link_prev, // infered from struct definition 187 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_12_0, 0x10) 188 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x10) 189 | ); 190 | REMOTE_FIELD(_vm_map_entry, link_next, // infered from struct definition 191 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_12_0, 0x18) 192 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x18) 193 | ); 194 | REMOTE_FIELD(kuint32_t, nentries, // can be found in vm_map_create_options (identifiable using panic string) 195 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_4, 0x30) 196 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_5, 0x30) 197 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x30) 198 | ); 199 | REMOTE_FIELD(kuint32_t, page_shift, // can be found in vm_map_create_options (identifiable using panic string) 200 | // can also be cross-validated with vm_map_page_mask() (also presents in mremap_encrypted) 201 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_4, 0x40) 202 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_5, 0x40) 203 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x40) 204 | ); 205 | REMOTE_FIELD(kpointer_t, pmap, // can be found in vm_map_create_options (identifiable using panic string) 206 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_4, 0x48) 207 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_5, 0x48) 208 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x48) 209 | ); 210 | ) 211 | 212 | 213 | REMOTETYPE(_thread_t, 214 | REMOTE_FIELD(_vm_map_t, _map, // can be found via current_map() call (for example from mremap_encrypted()) 215 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_4, 0x338) 216 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_5, 0x320) 217 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x348) 218 | ); 219 | ) 220 | REMOTETYPE(_task_t, 221 | REMOTE_FIELD(_vm_map_t, _map, // can be found in task_init() (identifiable with panic string) 222 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_4, 0x28) 223 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_5, 0x28) 224 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x28) 225 | ); 226 | ) 227 | 228 | REMOTETYPE(uthread, 229 | REMOTE_FIELD(_thread_t, uu_thread, // can't be easily found without symbol... 230 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x120) 231 | ); 232 | ) 233 | 234 | REMOTETYPE(proc_t, 235 | REMOTE_FIELD(proc_t, nextproc, // from struct definition 236 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_12_0, 0) 237 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0) 238 | ); 239 | 240 | REMOTE_FIELD(_task_t, task, // from struct definition 241 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_12_0, 0x10) 242 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0, 0x10) 243 | ); 244 | 245 | REMOTE_FIELD(kuint32_t, p_pid, 246 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_12_0, 0x60) 247 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_0, 0x68) 248 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0_b1, 0x68) 249 | ); 250 | 251 | REMOTE_FIELD(uthread, p_uthlist, // can be found in bsd_init 252 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_4, 0x98) 253 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_5, 0x98) 254 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0_b1, 0x98) 255 | ); 256 | 257 | REMOTE_FIELD(kpointer_t, p_ucred, // can be found in bsd_init 258 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_4, 0x100) 259 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_5, 0x100) 260 | ); 261 | 262 | REMOTE_FIELD(filedesc, p_fd, 263 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_12_0, 0x100) 264 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_13_0, 0x108) 265 | VERSION_OFF(kCFCoreFoundationVersionNumber_iOS_14_0_b1, 0xf8) 266 | ); 267 | ) 268 | 269 | -------------------------------------------------------------------------------- /kernstructtest.cpp: -------------------------------------------------------------------------------- 1 | // tester for rw-wrap 2 | // compile: clang++ -Iincludes kernstructtest.cpp 3 | #include "sysvers.h" 4 | extern double kCFCoreFoundationVersionNumber; 5 | 6 | #include "kernstructs.hpp" 7 | 8 | double kCFCoreFoundationVersionNumber = 123456.0; 9 | 10 | addr_t remote_addr_reader_impl(addr_t addr) { 11 | printf("kread_addr(0x%llx)\n", addr); 12 | return (addr & ~0xffff) + 0x10000; 13 | } 14 | 15 | void remote_addr_writer_impl(addr_t addr, addr_t val) { 16 | printf("kwrite_addr(0x%llx)\n", addr); 17 | } 18 | 19 | void remote_reader_impl(addr_t addr, void *buf, size_t size) { 20 | printf("kread(0x%llx, 0x%zx)\n", addr, size); 21 | } 22 | 23 | void remote_writer_impl(addr_t addr, const void *buf, size_t size) { 24 | printf("kwrite(0x%llx, 0x%zx)\n", addr, size); 25 | } 26 | 27 | void (*remote_reader)(addr_t addr, void *buf, size_t size) = remote_reader_impl; 28 | void (*remote_writer)(addr_t addr, const void *buf, size_t size) = remote_writer_impl; 29 | addr_t (*addr_reader)(addr_t addr) = remote_addr_reader_impl; 30 | void (*addr_writer)(addr_t addr, addr_t val) = remote_addr_writer_impl; 31 | 32 | int main() { 33 | auto p = proc_t_p(0x2000); 34 | auto np = p.nextproc().load(); 35 | printf("type(np): %s, np: %llx\n", typeid(np).name(), np); 36 | p.p_fd().fd_cdir().v_usecount().load(); 37 | } -------------------------------------------------------------------------------- /libs/libkernrw.tbd: -------------------------------------------------------------------------------- 1 | --- !tapi-tbd-v2 2 | archs: [ arm64, arm64e ] 3 | platform: ios 4 | flags: [ not_app_extension_safe ] 5 | install-name: '/usr/lib/libkernrw.0.dylib' 6 | exports: 7 | - archs: [ arm64, arm64e ] 8 | symbols: [ _requestKernRw, _kernRW_read32, _kernRW_read64, _kernRW_write32, _kernRW_write64, 9 | _kernRW_readbuf, _kernRW_writebuf, _kernRW_getKernelBase, _kernRW_getKernelProc, 10 | _kernRW_getAllProc ] 11 | ... 12 | -------------------------------------------------------------------------------- /libs/libkrw.tbd: -------------------------------------------------------------------------------- 1 | ../rw_prov/rw_prov_libkrw/libkrw/libkrw.tbd -------------------------------------------------------------------------------- /patchfinder/libdimentio.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2020 0x7ff 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | #include "libdimentio.h" 16 | #include "../rw_prov/rw_prov.h" 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "kern_context.h" 25 | 26 | typedef uint64_t addr_t; 27 | #define kaddr_t addr_t 28 | 29 | #define PREBOOT_PATH "/private/preboot/" 30 | #define BOOT_PATH "/System/Library/Caches/com.apple.kernelcaches/kernelcache" 31 | 32 | #define LZSS_F (18) 33 | #define LZSS_N (4096) 34 | #define LZSS_THRESHOLD (2) 35 | #define KCOMP_HDR_PAD_SZ (0x16C) 36 | 37 | #define DER_INT (0x2U) 38 | #define DER_SEQ (0x30U) 39 | #define DER_IA5_STR (0x16U) 40 | #define DER_OCTET_STR (0x4U) 41 | #define RD(a) extract32(a, 0, 5) 42 | #define RN(a) extract32(a, 5, 5) 43 | #define KCOMP_HDR_MAGIC (0x636F6D70U) 44 | #define ADRP_ADDR(a) ((a) & ~0xFFFULL) 45 | #define ADRP_IMM(a) (ADR_IMM(a) << 12U) 46 | #define IO_OBJECT_NULL ((io_object_t)0) 47 | #define ADD_X_IMM(a) extract32(a, 10, 12) 48 | #define kIODeviceTreePlane "IODeviceTree" 49 | #define KCOMP_HDR_TYPE_LZSS (0x6C7A7373U) 50 | #define LDR_X_IMM(a) (sextract64(a, 5, 19) << 2U) 51 | #define IS_ADR(a) (((a) & 0x9F000000U) == 0x10000000U) 52 | #define IS_ADRP(a) (((a) & 0x9F000000U) == 0x90000000U) 53 | #define IS_LDR_X(a) (((a) & 0xFF000000U) == 0x58000000U) 54 | #define IS_ADD_X(a) (((a) & 0xFFC00000U) == 0x91000000U) 55 | #define LDR_W_UNSIGNED_IMM(a) (extract32(a, 10, 12) << 2U) 56 | #define LDR_X_UNSIGNED_IMM(a) (extract32(a, 10, 12) << 3U) 57 | #define IS_LDR_W_UNSIGNED_IMM(a) (((a) & 0xFFC00000U) == 0xB9400000U) 58 | #define IS_LDR_X_UNSIGNED_IMM(a) (((a) & 0xFFC00000U) == 0xF9400000U) 59 | #define ADR_IMM(a) ((sextract64(a, 5, 19) << 2U) | extract32(a, 29, 2)) 60 | 61 | #ifndef SECT_CSTRING 62 | # define SECT_CSTRING "__cstring" 63 | #endif 64 | 65 | #ifndef SEG_TEXT_EXEC 66 | # define SEG_TEXT_EXEC "__TEXT_EXEC" 67 | #endif 68 | 69 | typedef char io_string_t[512]; 70 | typedef mach_port_t io_object_t; 71 | typedef uint32_t IOOptionBits, ipc_entry_num_t; 72 | typedef io_object_t io_service_t, io_connect_t, io_registry_entry_t; 73 | 74 | typedef struct { 75 | struct section_64 s64; 76 | const char *data; 77 | } sec_64_t; 78 | 79 | typedef struct { 80 | struct symtab_command cmd_symtab; 81 | sec_64_t sec_text, sec_cstring; 82 | kaddr_t base, kslide; 83 | const char *kernel; 84 | size_t kernel_sz; 85 | char *data; 86 | } pfinder_t; 87 | 88 | kern_return_t 89 | IOServiceClose(io_connect_t); 90 | 91 | kern_return_t 92 | IOObjectRelease(io_object_t); 93 | 94 | CFMutableDictionaryRef 95 | IOServiceMatching(const char *); 96 | 97 | CFDictionaryRef 98 | OSKextCopyLoadedKextInfo(CFArrayRef, CFArrayRef); 99 | 100 | io_registry_entry_t 101 | IORegistryEntryFromPath(mach_port_t, const io_string_t); 102 | 103 | io_service_t 104 | IOServiceGetMatchingService(mach_port_t, CFDictionaryRef); 105 | 106 | kern_return_t 107 | IOServiceOpen(io_service_t, task_port_t, uint32_t, io_connect_t *); 108 | 109 | kern_return_t 110 | IORegistryEntrySetCFProperty(io_registry_entry_t, CFStringRef, CFTypeRef); 111 | 112 | kern_return_t 113 | mach_vm_write(vm_map_t, mach_vm_address_t, vm_offset_t, mach_msg_type_number_t); 114 | 115 | kern_return_t 116 | IOConnectCallStructMethod(io_connect_t, uint32_t, const void *, size_t, void *, size_t *); 117 | 118 | CFTypeRef 119 | IORegistryEntryCreateCFProperty(io_registry_entry_t, CFStringRef, CFAllocatorRef, IOOptionBits); 120 | 121 | kern_return_t 122 | mach_vm_read_overwrite(vm_map_t, mach_vm_address_t, mach_vm_size_t, mach_vm_address_t, mach_vm_size_t *); 123 | 124 | kern_return_t 125 | mach_vm_machine_attribute(vm_map_t, mach_vm_address_t, mach_vm_size_t, vm_machine_attribute_t, vm_machine_attribute_val_t *); 126 | 127 | kern_return_t 128 | mach_vm_region(vm_map_t, mach_vm_address_t *, mach_vm_size_t *, vm_region_flavor_t, vm_region_info_t, mach_msg_type_number_t *, mach_port_t *); 129 | 130 | extern const mach_port_t kIOMasterPortDefault; 131 | 132 | 133 | kern_return_t 134 | kread_buf(kaddr_t addr, void *buf, mach_vm_size_t sz) { 135 | kernel_read(addr, buf, sz); 136 | return KERN_SUCCESS; 137 | } 138 | 139 | kern_return_t 140 | kread_addr(kaddr_t addr, kaddr_t *val) { 141 | return kread_buf(addr, val, sizeof(*val)); 142 | } 143 | 144 | kern_return_t 145 | kwrite_buf(kaddr_t addr, const void *buf, mach_msg_type_number_t sz) { 146 | kernel_write(addr, buf, sz); 147 | return KERN_SUCCESS; 148 | } 149 | 150 | kaddr_t p_kbase = 0, p_kslide = 0, allproc = 0; 151 | static kaddr_t kernproc; 152 | 153 | static uint32_t 154 | extract32(uint32_t val, unsigned start, unsigned len) { 155 | return (val >> start) & (~0U >> (32U - len)); 156 | } 157 | 158 | static uint64_t 159 | sextract64(uint64_t val, unsigned start, unsigned len) { 160 | return (uint64_t)((int64_t)(val << (64U - len - start)) >> (64U - len)); 161 | } 162 | 163 | static size_t 164 | decompress_lzss(const uint8_t *src, size_t src_len, uint8_t *dst, size_t dst_len) { 165 | const uint8_t *src_end = src + src_len, *dst_start = dst, *dst_end = dst + dst_len; 166 | uint16_t i, r = LZSS_N - LZSS_F, flags = 0; 167 | uint8_t text_buf[LZSS_N + LZSS_F - 1], j; 168 | 169 | memset(text_buf, ' ', r); 170 | while(src != src_end && dst != dst_end) { 171 | if(((flags >>= 1U) & 0x100U) == 0) { 172 | flags = *src++ | 0xFF00U; 173 | if(src == src_end) { 174 | break; 175 | } 176 | } 177 | if((flags & 1U) != 0) { 178 | text_buf[r++] = *dst++ = *src++; 179 | r &= LZSS_N - 1U; 180 | } else { 181 | i = *src++; 182 | if(src == src_end) { 183 | break; 184 | } 185 | j = *src++; 186 | i |= (j & 0xF0U) << 4U; 187 | j = (j & 0xFU) + LZSS_THRESHOLD; 188 | do { 189 | *dst++ = text_buf[r++] = text_buf[i++ & (LZSS_N - 1U)]; 190 | r &= LZSS_N - 1U; 191 | } while(j-- != 0 && dst != dst_end); 192 | } 193 | } 194 | return (size_t)(dst - dst_start); 195 | } 196 | 197 | static const uint8_t * 198 | der_decode(uint8_t tag, const uint8_t *der, const uint8_t *der_end, size_t *out_len) { 199 | size_t der_len; 200 | 201 | if(der_end - der > 2 && tag == *der++) { 202 | if(((der_len = *der++) & 0x80U) != 0) { 203 | *out_len = 0; 204 | if((der_len &= 0x7FU) <= sizeof(*out_len) && (size_t)(der_end - der) >= der_len) { 205 | while(der_len-- != 0) { 206 | *out_len = (*out_len << 8U) | *der++; 207 | } 208 | } 209 | } else { 210 | *out_len = der_len; 211 | } 212 | if(*out_len != 0 && (size_t)(der_end - der) >= *out_len) { 213 | return der; 214 | } 215 | } 216 | return NULL; 217 | } 218 | 219 | static const uint8_t * 220 | der_decode_seq(const uint8_t *der, const uint8_t *der_end, const uint8_t **seq_end) { 221 | size_t der_len; 222 | 223 | if((der = der_decode(DER_SEQ, der, der_end, &der_len)) != NULL) { 224 | *seq_end = der + der_len; 225 | } 226 | return der; 227 | } 228 | 229 | static const uint8_t * 230 | der_decode_uint64(const uint8_t *der, const uint8_t *der_end, uint64_t *r) { 231 | size_t der_len; 232 | 233 | if((der = der_decode(DER_INT, der, der_end, &der_len)) != NULL && (*der & 0x80U) == 0 && (der_len <= sizeof(*r) || (--der_len == sizeof(*r) && *der++ == 0))) { 234 | *r = 0; 235 | while(der_len-- != 0) { 236 | *r = (*r << 8U) | *der++; 237 | } 238 | return der; 239 | } 240 | return NULL; 241 | } 242 | 243 | static void * 244 | kdecompress(const void *src, size_t src_len, size_t *dst_len) { 245 | const uint8_t *der, *octet, *der_end, *src_end = (const uint8_t *)src + src_len; 246 | struct { 247 | uint32_t magic, type, adler32, uncomp_sz, comp_sz; 248 | uint8_t pad[KCOMP_HDR_PAD_SZ]; 249 | } kcomp_hdr; 250 | size_t der_len; 251 | uint64_t r; 252 | void *dst; 253 | 254 | if((der = der_decode_seq(src, src_end, &der_end)) != NULL && (der = der_decode(DER_IA5_STR, der, der_end, &der_len)) != NULL && der_len == 4 && (memcmp(der, "IMG4", der_len) != 0 || ((der = der_decode_seq(der + der_len, src_end, &der_end)) != NULL && (der = der_decode(DER_IA5_STR, der, der_end, &der_len)) != NULL && der_len == 4)) && memcmp(der, "IM4P", der_len) == 0 && (der = der_decode(DER_IA5_STR, der + der_len, der_end, &der_len)) != NULL && der_len == 4 && memcmp(der, "krnl", der_len) == 0 && (der = der_decode(DER_IA5_STR, der + der_len, der_end, &der_len)) != NULL && (der = der_decode(DER_OCTET_STR, der + der_len, der_end, &der_len)) != NULL && der_len > sizeof(kcomp_hdr)) { 255 | octet = der; 256 | memcpy(&kcomp_hdr, octet, sizeof(kcomp_hdr)); 257 | if(kcomp_hdr.magic == __builtin_bswap32(KCOMP_HDR_MAGIC)) { 258 | if(kcomp_hdr.type == __builtin_bswap32(KCOMP_HDR_TYPE_LZSS) && (kcomp_hdr.comp_sz = __builtin_bswap32(kcomp_hdr.comp_sz)) <= der_len - sizeof(kcomp_hdr) && (kcomp_hdr.uncomp_sz = __builtin_bswap32(kcomp_hdr.uncomp_sz)) != 0 && (dst = malloc(kcomp_hdr.uncomp_sz)) != NULL) { 259 | if(decompress_lzss(octet + sizeof(kcomp_hdr), kcomp_hdr.comp_sz, dst, kcomp_hdr.uncomp_sz) == kcomp_hdr.uncomp_sz) { 260 | *dst_len = kcomp_hdr.uncomp_sz; 261 | return dst; 262 | } 263 | free(dst); 264 | } 265 | } else if((der = der_decode_seq(der + der_len, src_end, &der_end)) != NULL && (der = der_decode_uint64(der, der_end, &r)) != NULL && r == 1 && der_decode_uint64(der, der_end, &r) != NULL && r != 0 && (dst = malloc(r)) != NULL) { 266 | if(compression_decode_buffer(dst, r, octet, der_len, NULL, COMPRESSION_LZFSE) == r) { 267 | *dst_len = r; 268 | return dst; 269 | } 270 | free(dst); 271 | } 272 | } 273 | return NULL; 274 | } 275 | 276 | static kern_return_t 277 | find_section(const char *p, struct segment_command_64 sg64, const char *sect_name, struct section_64 *sp) { 278 | for(; sg64.nsects-- != 0; p += sizeof(*sp)) { 279 | memcpy(sp, p, sizeof(*sp)); 280 | if((sp->flags & SECTION_TYPE) != S_ZEROFILL) { 281 | if(sp->offset < sg64.fileoff || sp->size > sg64.filesize || sp->offset - sg64.fileoff > sg64.filesize - sp->size) { 282 | break; 283 | } 284 | if(sp->size != 0 && strncmp(sp->segname, sg64.segname, sizeof(sp->segname)) == 0 && strncmp(sp->sectname, sect_name, sizeof(sp->sectname)) == 0) { 285 | return KERN_SUCCESS; 286 | } 287 | } 288 | } 289 | return KERN_FAILURE; 290 | } 291 | 292 | static void 293 | sec_reset(sec_64_t *sec) { 294 | memset(&sec->s64, '\0', sizeof(sec->s64)); 295 | sec->data = NULL; 296 | } 297 | 298 | static kern_return_t 299 | sec_read_buf(sec_64_t sec, kaddr_t addr, void *buf, size_t sz) { 300 | size_t off; 301 | 302 | if(addr < sec.s64.addr || sz > sec.s64.size || (off = addr - sec.s64.addr) > sec.s64.size - sz) { 303 | return KERN_FAILURE; 304 | } 305 | memcpy(buf, sec.data + off, sz); 306 | return KERN_SUCCESS; 307 | } 308 | 309 | static void 310 | pfinder_reset(pfinder_t *pfinder) { 311 | pfinder->base = 0; 312 | pfinder->kslide = 0; 313 | pfinder->data = NULL; 314 | pfinder->kernel = NULL; 315 | pfinder->kernel_sz = 0; 316 | sec_reset(&pfinder->sec_text); 317 | sec_reset(&pfinder->sec_cstring); 318 | memset(&pfinder->cmd_symtab, '\0', sizeof(pfinder->cmd_symtab)); 319 | } 320 | 321 | static void 322 | pfinder_term(pfinder_t *pfinder) { 323 | free(pfinder->data); 324 | pfinder_reset(pfinder); 325 | } 326 | 327 | static kern_return_t 328 | pfinder_init_file(pfinder_t *pfinder, const char *filename) { 329 | struct symtab_command cmd_symtab; 330 | kern_return_t ret = KERN_FAILURE; 331 | struct segment_command_64 sg64; 332 | struct mach_header_64 mh64; 333 | struct load_command lc; 334 | struct section_64 s64; 335 | struct fat_header fh; 336 | struct stat stat_buf; 337 | struct fat_arch fa; 338 | const char *p, *e; 339 | size_t len; 340 | void *m; 341 | int fd; 342 | 343 | pfinder_reset(pfinder); 344 | if((fd = open(filename, O_RDONLY | O_CLOEXEC)) != -1) { 345 | if(fstat(fd, &stat_buf) != -1 && S_ISREG(stat_buf.st_mode) && stat_buf.st_size > 0) { 346 | len = (size_t)stat_buf.st_size; 347 | if((m = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0)) != MAP_FAILED) { 348 | if((pfinder->data = kdecompress(m, len, &pfinder->kernel_sz)) != NULL && pfinder->kernel_sz > sizeof(fh) + sizeof(mh64)) { 349 | pfinder->kernel = pfinder->data; 350 | memcpy(&fh, pfinder->kernel, sizeof(fh)); 351 | if(fh.magic == __builtin_bswap32(FAT_MAGIC) && (fh.nfat_arch = __builtin_bswap32(fh.nfat_arch)) < (pfinder->kernel_sz - sizeof(fh)) / sizeof(fa)) { 352 | for(p = pfinder->kernel + sizeof(fh); fh.nfat_arch-- != 0; p += sizeof(fa)) { 353 | memcpy(&fa, p, sizeof(fa)); 354 | if(fa.cputype == (cpu_type_t)__builtin_bswap32(CPU_TYPE_ARM64) && (fa.offset = __builtin_bswap32(fa.offset)) < pfinder->kernel_sz && (fa.size = __builtin_bswap32(fa.size)) <= pfinder->kernel_sz - fa.offset && fa.size > sizeof(mh64)) { 355 | pfinder->kernel_sz = fa.size; 356 | pfinder->kernel += fa.offset; 357 | break; 358 | } 359 | } 360 | } 361 | memcpy(&mh64, pfinder->kernel, sizeof(mh64)); 362 | if(mh64.magic == MH_MAGIC_64 && mh64.cputype == CPU_TYPE_ARM64 && mh64.filetype == MH_EXECUTE && mh64.sizeofcmds < pfinder->kernel_sz - sizeof(mh64)) { 363 | for(p = pfinder->kernel + sizeof(mh64), e = p + mh64.sizeofcmds; mh64.ncmds-- != 0 && (size_t)(e - p) >= sizeof(lc); p += lc.cmdsize) { 364 | memcpy(&lc, p, sizeof(lc)); 365 | if(lc.cmdsize < sizeof(lc) || (size_t)(e - p) < lc.cmdsize) { 366 | break; 367 | } 368 | if(lc.cmd == LC_SEGMENT_64) { 369 | if(lc.cmdsize < sizeof(sg64)) { 370 | break; 371 | } 372 | memcpy(&sg64, p, sizeof(sg64)); 373 | if(sg64.vmsize == 0) { 374 | continue; 375 | } 376 | if(sg64.nsects != (lc.cmdsize - sizeof(sg64)) / sizeof(s64) || sg64.fileoff > pfinder->kernel_sz || sg64.filesize > pfinder->kernel_sz - sg64.fileoff) { 377 | break; 378 | } 379 | if(sg64.fileoff == 0 && sg64.filesize != 0) { 380 | if(pfinder->base != 0) { 381 | break; 382 | } 383 | pfinder->base = sg64.vmaddr; 384 | KERNINFRA_LOG(KERNLOG_INIT, "base: " KADDR_FMT "\n", sg64.vmaddr); 385 | } 386 | if(strncmp(sg64.segname, SEG_TEXT_EXEC, sizeof(sg64.segname)) == 0) { 387 | if(find_section(p + sizeof(sg64), sg64, SECT_TEXT, &s64) != KERN_SUCCESS) { 388 | break; 389 | } 390 | pfinder->sec_text.s64 = s64; 391 | pfinder->sec_text.data = pfinder->kernel + s64.offset; 392 | KERNINFRA_LOG(KERNLOG_INIT, "sec_text_addr: " KADDR_FMT ", sec_text_off: 0x%" PRIX32 ", sec_text_sz: 0x%" PRIX64 "\n", s64.addr, s64.offset, s64.size); 393 | } else if(strncmp(sg64.segname, SEG_TEXT, sizeof(sg64.segname)) == 0) { 394 | if(find_section(p + sizeof(sg64), sg64, SECT_CSTRING, &s64) != KERN_SUCCESS || pfinder->kernel[s64.offset + s64.size - 1] != '\0') { 395 | break; 396 | } 397 | pfinder->sec_cstring.s64 = s64; 398 | pfinder->sec_cstring.data = pfinder->kernel + s64.offset; 399 | KERNINFRA_LOG(KERNLOG_INIT, "sec_cstring_addr: " KADDR_FMT ", sec_cstring_off: 0x%" PRIX32 ", sec_cstring_sz: 0x%" PRIX64 "\n", s64.addr, s64.offset, s64.size); 400 | } 401 | } else if(lc.cmd == LC_SYMTAB) { 402 | if(lc.cmdsize != sizeof(cmd_symtab)) { 403 | break; 404 | } 405 | memcpy(&cmd_symtab, p, sizeof(cmd_symtab)); 406 | KERNINFRA_LOG(KERNLOG_INIT, "cmd_symtab_symoff: 0x%" PRIX32 ", cmd_symtab_nsyms: 0x%" PRIX32 ", cmd_symtab_stroff: 0x%" PRIX32 "\n", cmd_symtab.symoff, cmd_symtab.nsyms, cmd_symtab.stroff); 407 | if(cmd_symtab.nsyms != 0 && (cmd_symtab.symoff > pfinder->kernel_sz || cmd_symtab.nsyms > (pfinder->kernel_sz - cmd_symtab.symoff) / sizeof(struct nlist_64) || cmd_symtab.stroff > pfinder->kernel_sz || cmd_symtab.strsize > pfinder->kernel_sz - cmd_symtab.stroff || cmd_symtab.strsize == 0 || pfinder->kernel[cmd_symtab.stroff + cmd_symtab.strsize - 1] != '\0')) { 408 | break; 409 | } 410 | pfinder->cmd_symtab = cmd_symtab; 411 | } 412 | if(pfinder->base != 0 && pfinder->sec_text.s64.size != 0 && pfinder->sec_cstring.s64.size != 0 && pfinder->cmd_symtab.cmdsize != 0) { 413 | ret = KERN_SUCCESS; 414 | break; 415 | } 416 | } 417 | } 418 | } 419 | munmap(m, len); 420 | } 421 | } 422 | close(fd); 423 | } 424 | if(ret != KERN_SUCCESS) { 425 | pfinder_term(pfinder); 426 | } 427 | return ret; 428 | } 429 | 430 | static kaddr_t 431 | pfinder_xref_rd(pfinder_t pfinder, uint32_t rd, kaddr_t start, kaddr_t to) { 432 | kaddr_t x[32] = { 0 }; 433 | uint32_t insn; 434 | 435 | for(; sec_read_buf(pfinder.sec_text, start, &insn, sizeof(insn)) == KERN_SUCCESS; start += sizeof(insn)) { 436 | if(IS_LDR_X(insn)) { 437 | x[RD(insn)] = start + LDR_X_IMM(insn); 438 | } else if(IS_ADR(insn)) { 439 | x[RD(insn)] = start + ADR_IMM(insn); 440 | } else if(IS_ADD_X(insn)) { 441 | x[RD(insn)] = x[RN(insn)] + ADD_X_IMM(insn); 442 | } else if(IS_LDR_W_UNSIGNED_IMM(insn)) { 443 | x[RD(insn)] = x[RN(insn)] + LDR_W_UNSIGNED_IMM(insn); 444 | } else if(IS_LDR_X_UNSIGNED_IMM(insn)) { 445 | x[RD(insn)] = x[RN(insn)] + LDR_X_UNSIGNED_IMM(insn); 446 | } else { 447 | if(IS_ADRP(insn)) { 448 | x[RD(insn)] = ADRP_ADDR(start) + ADRP_IMM(insn); 449 | } 450 | continue; 451 | } 452 | if(RD(insn) == rd) { 453 | if(to == 0) { 454 | if(x[rd] < pfinder.base) { 455 | break; 456 | } 457 | return x[rd]; 458 | } 459 | if(x[rd] == to) { 460 | return start; 461 | } 462 | } 463 | } 464 | return 0; 465 | } 466 | 467 | static kaddr_t 468 | pfinder_xref_str(pfinder_t pfinder, const char *str, uint32_t rd) { 469 | const char *p, *e; 470 | size_t len; 471 | 472 | for(p = pfinder.sec_cstring.data, e = p + pfinder.sec_cstring.s64.size; p != e; p += len) { 473 | len = strlen(p) + 1; 474 | if(strncmp(str, p, len) == 0) { 475 | return pfinder_xref_rd(pfinder, rd, pfinder.sec_text.s64.addr, pfinder.sec_cstring.s64.addr + (kaddr_t)(p - pfinder.sec_cstring.data)); 476 | } 477 | } 478 | return 0; 479 | } 480 | 481 | static kaddr_t 482 | pfinder_sym(pfinder_t pfinder, const char *sym) { 483 | const char *p, *strtab = pfinder.kernel + pfinder.cmd_symtab.stroff; 484 | struct nlist_64 nl64; 485 | 486 | for(p = pfinder.kernel + pfinder.cmd_symtab.symoff; pfinder.cmd_symtab.nsyms-- != 0; p += sizeof(nl64)) { 487 | memcpy(&nl64, p, sizeof(nl64)); 488 | if(nl64.n_un.n_strx != 0 && nl64.n_un.n_strx < pfinder.cmd_symtab.strsize && (nl64.n_type & (N_STAB | N_TYPE)) == N_SECT && nl64.n_value >= pfinder.base && strcmp(strtab + nl64.n_un.n_strx, sym) == 0) { 489 | return nl64.n_value + pfinder.kslide; 490 | } 491 | } 492 | return 0; 493 | } 494 | 495 | static kaddr_t 496 | pfinder_kernproc(pfinder_t pfinder) { 497 | kaddr_t ref = pfinder_sym(pfinder, "_kernproc"); 498 | uint32_t insns[2]; 499 | 500 | if(ref != 0) { 501 | return ref; 502 | } 503 | for(ref = pfinder_xref_str(pfinder, "\"Should never have an EVFILT_READ except for reg or fifo.\"", 0); sec_read_buf(pfinder.sec_text, ref, insns, sizeof(insns)) == KERN_SUCCESS; ref -= sizeof(*insns)) { 504 | if(IS_ADRP(insns[0]) && IS_LDR_X_UNSIGNED_IMM(insns[1]) && RD(insns[1]) == 3) { 505 | return pfinder_xref_rd(pfinder, RD(insns[1]), ref, 0); 506 | } 507 | } 508 | return 0; 509 | } 510 | 511 | static kaddr_t 512 | pfinder_allproc(pfinder_t pfinder) { 513 | kaddr_t ref = pfinder_xref_str(pfinder, "shutdownwait", 2); 514 | 515 | if(ref == 0) { 516 | ref = pfinder_xref_str(pfinder, "shutdownwait", 3); /* msleep */ 517 | } 518 | return pfinder_xref_rd(pfinder, 8, ref, 0); 519 | } 520 | 521 | static kaddr_t 522 | pfinder_init_kbase(pfinder_t *pfinder) { 523 | struct mach_header_64 mh64; 524 | 525 | if(pfinder->base + pfinder->kslide > pfinder->base && kread_buf(pfinder->base + pfinder->kslide, &mh64, sizeof(mh64)) == KERN_SUCCESS && mh64.magic == MH_MAGIC_64 && mh64.cputype == CPU_TYPE_ARM64 && mh64.filetype == MH_EXECUTE) { 526 | pfinder->sec_text.s64.addr += pfinder->kslide; 527 | pfinder->sec_cstring.s64.addr += pfinder->kslide; 528 | KERNINFRA_LOG(KERNLOG_INIT, "kbase: " KADDR_FMT ", kslide: " KADDR_FMT "\n", pfinder->base + pfinder->kslide, pfinder->kslide); 529 | p_kbase = pfinder->base + pfinder->kslide; 530 | p_kslide = pfinder->kslide; 531 | return KERN_SUCCESS; 532 | } 533 | return KERN_FAILURE; 534 | } 535 | 536 | static char * 537 | get_boot_path(void) { 538 | size_t hash_len, path_len = sizeof(BOOT_PATH); 539 | io_registry_entry_t chosen; 540 | struct stat stat_buf; 541 | const uint8_t *hash; 542 | CFDataRef hash_cf; 543 | char *path = NULL; 544 | 545 | if(stat(PREBOOT_PATH, &stat_buf) != -1 && S_ISDIR(stat_buf.st_mode) && (chosen = IORegistryEntryFromPath(kIOMasterPortDefault, kIODeviceTreePlane ":/chosen")) != IO_OBJECT_NULL) { 546 | if((hash_cf = IORegistryEntryCreateCFProperty(chosen, CFSTR("boot-manifest-hash"), kCFAllocatorDefault, kNilOptions)) != NULL) { 547 | if(CFGetTypeID(hash_cf) == CFDataGetTypeID() && (hash_len = (size_t)CFDataGetLength(hash_cf) << 1U) != 0) { 548 | path_len += strlen(PREBOOT_PATH) + hash_len; 549 | if((path = malloc(path_len)) != NULL) { 550 | memcpy(path, PREBOOT_PATH, strlen(PREBOOT_PATH)); 551 | for(hash = CFDataGetBytePtr(hash_cf); hash_len-- != 0; ) { 552 | path[strlen(PREBOOT_PATH) + hash_len] = "0123456789ABCDEF"[(hash[hash_len >> 1U] >> ((~hash_len & 1U) << 2U)) & 0xFU]; 553 | } 554 | } 555 | } 556 | CFRelease(hash_cf); 557 | } 558 | IOObjectRelease(chosen); 559 | } 560 | if(path == NULL) { 561 | path_len = sizeof(BOOT_PATH); 562 | path = malloc(path_len); 563 | } 564 | if(path != NULL) { 565 | memcpy(path + (path_len - sizeof(BOOT_PATH)), BOOT_PATH, sizeof(BOOT_PATH)); 566 | } 567 | return path; 568 | } 569 | 570 | kern_return_t 571 | pfinder_init_offsets(void) { 572 | kern_return_t ret = KERN_FAILURE; 573 | pfinder_t pfinder; 574 | char *boot_path; 575 | 576 | if((boot_path = get_boot_path()) != NULL) { 577 | KERNINFRA_LOG(KERNLOG_INIT, "boot_path: %s\n", boot_path); 578 | if(pfinder_init_file(&pfinder, boot_path) == KERN_SUCCESS) { 579 | //pfinder.kslide = kslide; 580 | uint64_t prov_kslide = 0, prov_kbase = 0; 581 | kernel_getbase(&prov_kbase, &prov_kslide); 582 | if (prov_kbase) { 583 | pfinder.kslide = prov_kbase - pfinder.base; 584 | } else if (prov_kslide) { 585 | pfinder.kslide = prov_kslide; 586 | } else { 587 | KERNINFRA_LOG(KERNLOG_INIT, "failed to retrive kslide!!!"); 588 | return KERN_FAILURE; 589 | } 590 | 591 | KERNINFRA_LOG(KERNLOG_INIT, "got kslide: %llx\n", pfinder.kslide); 592 | if(pfinder_init_kbase(&pfinder) == KERN_SUCCESS && (kernproc = pfinder_kernproc(pfinder)) != 0) { 593 | KERNINFRA_LOG(KERNLOG_INIT, "kernproc: " KADDR_FMT "\n", kernproc); 594 | if((allproc = pfinder_allproc(pfinder)) != 0) { 595 | KERNINFRA_LOG(KERNLOG_INIT, "allproc: " KADDR_FMT "\n", allproc); 596 | ret = KERN_SUCCESS; 597 | } 598 | } 599 | pfinder_term(&pfinder); 600 | } 601 | free(boot_path); 602 | } 603 | return ret; 604 | } 605 | 606 | void 607 | patchfinder_term(void) { 608 | setpriority(PRIO_PROCESS, 0, 0); 609 | } 610 | 611 | kern_return_t 612 | patchfinder_init(void) { 613 | if(setpriority(PRIO_PROCESS, 0, PRIO_MIN) != -1 && pfinder_init_offsets() == KERN_SUCCESS) { 614 | return KERN_SUCCESS; 615 | } 616 | patchfinder_term(); 617 | return KERN_FAILURE; 618 | } -------------------------------------------------------------------------------- /patchfinder/libdimentio.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2020 0x7ff 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | #ifndef LIBDIMENTIO_H 16 | #include "../rw_prov/rw_prov.h" 17 | # define LIBDIMENTIO_H 18 | # include 19 | # include 20 | # include 21 | # define KADDR_FMT "0x%" PRIX64 22 | # ifndef MIN 23 | # define MIN(a, b) ((a) < (b) ? (a) : (b)) 24 | # endif 25 | extern addr_t p_kbase, p_kslide, allproc; 26 | 27 | void 28 | patchfinder_term(void); 29 | 30 | kern_return_t 31 | patchfinder_init(void); 32 | 33 | kern_return_t 34 | pfinder_init_offsets(void); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /rw_prov/rw_prov.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | typedef uint64_t addr_t; 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | //read kernel 11 | uint32_t kernel_read32(uint64_t); 12 | uint64_t kernel_read64(uint64_t); 13 | 14 | //write kernel 15 | void kernel_write32(uint64_t, uint32_t); 16 | void kernel_write64(uint64_t, uint64_t); 17 | 18 | void kernel_read(uint64_t where, void *buf, size_t size); 19 | void kernel_write(uint64_t where, const void *buf, size_t size); 20 | 21 | void kernel_getbase(uint64_t *retkbase, uint64_t *retkslide); 22 | 23 | // 0 - success, other - implementation related 24 | int rw_prov_init(); 25 | 26 | #ifdef __cplusplus 27 | } 28 | #endif 29 | -------------------------------------------------------------------------------- /rw_prov/rw_prov_libkernrw/rw_prov_libkernrw.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "libkernrw/libkernrw.h" 3 | 4 | uint32_t kernel_read32(uint64_t where) { 5 | uint32_t out = 0; 6 | kernRW_read32(where, &out); 7 | return out; 8 | } 9 | 10 | uint64_t kernel_read64(uint64_t where) { 11 | uint64_t out = 0; 12 | kernRW_read64(where, &out); 13 | return out; 14 | } 15 | 16 | void kernel_write32(uint64_t where, uint32_t what) { 17 | kernRW_write32(where, what); 18 | } 19 | 20 | void kernel_write64(uint64_t where, uint64_t what) { 21 | kernRW_write64(where, what); 22 | } 23 | 24 | void kernel_write(uint64_t where, const void *buf, size_t size) { 25 | kernRW_writebuf(where, buf, size); 26 | } 27 | 28 | void kernel_read(uint64_t where, void *buf, size_t size) { 29 | kernRW_readbuf(where, buf, size); 30 | } 31 | 32 | void kernel_getbase(uint64_t *retkbase, uint64_t *retkslide) { 33 | uint64_t curKbase = 0; 34 | kernRW_getKernelBase(&curKbase); 35 | *retkbase = curKbase; 36 | *retkslide = 0; 37 | } 38 | 39 | int rw_prov_init() { 40 | return requestKernRw(); 41 | } -------------------------------------------------------------------------------- /rw_prov/rw_prov_libkrw/rw_prov_libkrw.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "libkrw/include/libkrw.h" 3 | 4 | uint32_t kernel_read32(uint64_t where) { 5 | uint32_t out = 0; 6 | kread(where, &out, sizeof(uint32_t)); 7 | return out; 8 | } 9 | 10 | uint64_t kernel_read64(uint64_t where) { 11 | uint64_t out = 0; 12 | kread(where, &out, sizeof(uint64_t)); 13 | return out; 14 | } 15 | 16 | void kernel_write32(uint64_t where, uint32_t what) { 17 | uint32_t _what = what; 18 | kwrite((void *)&_what, where, sizeof(uint32_t)); 19 | } 20 | 21 | void kernel_write64(uint64_t where, uint64_t what) { 22 | uint64_t _what = what; 23 | kwrite((void *)&_what, where, sizeof(uint64_t)); 24 | } 25 | 26 | void kernel_write(uint64_t where, const void *buf, size_t size) { 27 | kwrite((void *)buf, where, size); 28 | } 29 | 30 | void kernel_read(uint64_t where, void *buf, size_t size) { 31 | kread(where, buf, size); 32 | } 33 | 34 | void kernel_getbase(uint64_t *retkbase, uint64_t *retkslide) { 35 | uint64_t curKbase = 0; 36 | kbase(&curKbase); 37 | *retkbase = curKbase; 38 | *retkslide = 0; 39 | } 40 | 41 | int rw_prov_init() { 42 | return 0; 43 | } -------------------------------------------------------------------------------- /rw_prov/rw_prov_tfp0/rw_prov_tfp0.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | typedef uint64_t kaddr_t; 10 | task_t tfp0 = TASK_NULL; 11 | kaddr_t kslide; 12 | 13 | kern_return_t 14 | init_tfp0(void) { 15 | kern_return_t ret = task_for_pid(mach_task_self(), 0, &tfp0); 16 | mach_port_t host; 17 | 18 | if(ret != KERN_SUCCESS) { 19 | host = mach_host_self(); 20 | if(MACH_PORT_VALID(host)) { 21 | printf("host: 0x%" PRIX32 "\n", host); 22 | ret = host_get_special_port(host, HOST_LOCAL_NODE, 4, &tfp0); 23 | mach_port_deallocate(mach_task_self(), host); 24 | } 25 | } 26 | if(ret == KERN_SUCCESS && MACH_PORT_VALID(tfp0)) { 27 | return ret; 28 | } 29 | return KERN_FAILURE; 30 | } 31 | 32 | 33 | kern_return_t 34 | mach_vm_write(vm_map_t, mach_vm_address_t, vm_offset_t, mach_msg_type_number_t); 35 | 36 | kern_return_t 37 | mach_vm_read_overwrite(vm_map_t, mach_vm_address_t, mach_vm_size_t, mach_vm_address_t, mach_vm_size_t *); 38 | 39 | kern_return_t 40 | mach_vm_machine_attribute(vm_map_t, mach_vm_address_t, mach_vm_size_t, vm_machine_attribute_t, vm_machine_attribute_val_t *); 41 | 42 | kern_return_t 43 | mach_vm_region(vm_map_t, mach_vm_address_t *, mach_vm_size_t *, vm_region_flavor_t, vm_region_info_t, mach_msg_type_number_t *, mach_port_t *); 44 | 45 | # ifndef MIN 46 | # define MIN(a, b) ((a) < (b) ? (a) : (b)) 47 | # endif 48 | 49 | # define KADDR_FMT "0x%" PRIX64 50 | 51 | static kern_return_t 52 | __kread_buf_tfp0(kaddr_t addr, void *buf, mach_vm_size_t sz) { 53 | mach_vm_address_t p = (mach_vm_address_t)buf; 54 | mach_vm_size_t read_sz, out_sz = 0; 55 | 56 | while(sz != 0) { 57 | read_sz = MIN(sz, vm_kernel_page_size - (addr & vm_kernel_page_mask)); 58 | if(mach_vm_read_overwrite(tfp0, addr, read_sz, p, &out_sz) != KERN_SUCCESS || out_sz != read_sz) { 59 | return KERN_FAILURE; 60 | } 61 | p += read_sz; 62 | sz -= read_sz; 63 | addr += read_sz; 64 | } 65 | return KERN_SUCCESS; 66 | } 67 | 68 | static kern_return_t 69 | __kwrite_buf_tfp0(kaddr_t addr, const void *buf, mach_msg_type_number_t sz) { 70 | vm_machine_attribute_val_t mattr_val = MATTR_VAL_CACHE_FLUSH; 71 | mach_vm_address_t p = (mach_vm_address_t)buf; 72 | mach_msg_type_number_t write_sz; 73 | 74 | while(sz != 0) { 75 | write_sz = (mach_msg_type_number_t)MIN(sz, vm_kernel_page_size - (addr & vm_kernel_page_mask)); 76 | if(mach_vm_write(tfp0, addr, p, write_sz) != KERN_SUCCESS || mach_vm_machine_attribute(tfp0, addr, write_sz, MATTR_CACHE, &mattr_val) != KERN_SUCCESS) { 77 | return KERN_FAILURE; 78 | } 79 | p += write_sz; 80 | sz -= write_sz; 81 | addr += write_sz; 82 | } 83 | return KERN_SUCCESS; 84 | } 85 | 86 | #define LOADED_KEXT_SUMMARY_HDR_NAME_OFF (0x10) 87 | #define LOADED_KEXT_SUMMARY_HDR_ADDR_OFF (0x60) 88 | #define VM_KERN_MEMORY_OSKEXT (5) 89 | #define kOSBundleLoadAddressKey "OSBundleLoadAddress" 90 | 91 | CFDictionaryRef 92 | OSKextCopyLoadedKextInfo(CFArrayRef, CFArrayRef); 93 | 94 | kern_return_t 95 | init_kslide(void) { 96 | mach_msg_type_number_t cnt = TASK_DYLD_INFO_COUNT; 97 | vm_region_extended_info_data_t extended_info; 98 | kaddr_t addr, kext_addr, kext_addr_slid; 99 | CFDictionaryRef kexts_info, kext_info; 100 | task_dyld_info_data_t dyld_info; 101 | char kext_name[KMOD_MAX_NAME]; 102 | CFStringRef kext_name_cf; 103 | CFNumberRef kext_addr_cf; 104 | mach_port_t object_name; 105 | CFArrayRef kext_names; 106 | mach_vm_size_t sz; 107 | 108 | if(task_info(tfp0, TASK_DYLD_INFO, (task_info_t)&dyld_info, &cnt) == KERN_SUCCESS) { 109 | kslide = dyld_info.all_image_info_size; 110 | } 111 | if(kslide == 0) { 112 | cnt = VM_REGION_EXTENDED_INFO_COUNT; 113 | for(addr = 0; mach_vm_region(tfp0, &addr, &sz, VM_REGION_EXTENDED_INFO, (vm_region_info_t)&extended_info, &cnt, &object_name) == KERN_SUCCESS; addr += sz) { 114 | mach_port_deallocate(mach_task_self(), object_name); 115 | if(extended_info.protection == VM_PROT_READ && extended_info.user_tag == VM_KERN_MEMORY_OSKEXT) { 116 | if(__kread_buf_tfp0(addr + LOADED_KEXT_SUMMARY_HDR_NAME_OFF, kext_name, sizeof(kext_name)) == KERN_SUCCESS) { 117 | printf("kext_name: %s\n", kext_name); 118 | if(__kread_buf_tfp0(addr + LOADED_KEXT_SUMMARY_HDR_ADDR_OFF, &kext_addr_slid, sizeof(kaddr_t)) == KERN_SUCCESS) { 119 | printf("kext_addr_slid: " KADDR_FMT "\n", kext_addr_slid); 120 | if((kext_name_cf = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, kext_name, kCFStringEncodingUTF8, kCFAllocatorNull)) != NULL) { 121 | if((kext_names = CFArrayCreate(kCFAllocatorDefault, (const void **)&kext_name_cf, 1, &kCFTypeArrayCallBacks)) != NULL) { 122 | if((kexts_info = OSKextCopyLoadedKextInfo(kext_names, NULL)) != NULL) { 123 | if(CFGetTypeID(kexts_info) == CFDictionaryGetTypeID() && CFDictionaryGetCount(kexts_info) == 1 && (kext_info = CFDictionaryGetValue(kexts_info, kext_name_cf)) != NULL && CFGetTypeID(kext_info) == CFDictionaryGetTypeID() && (kext_addr_cf = CFDictionaryGetValue(kext_info, CFSTR(kOSBundleLoadAddressKey))) != NULL && CFGetTypeID(kext_addr_cf) == CFNumberGetTypeID() && CFNumberGetValue(kext_addr_cf, kCFNumberSInt64Type, &kext_addr) && kext_addr_slid > kext_addr) { 124 | kslide = kext_addr_slid - kext_addr; 125 | } 126 | CFRelease(kexts_info); 127 | } 128 | CFRelease(kext_names); 129 | } 130 | CFRelease(kext_name_cf); 131 | } 132 | } 133 | } 134 | break; 135 | } 136 | } 137 | } 138 | if (!kslide) { 139 | return KERN_FAILURE; 140 | } 141 | return KERN_SUCCESS; 142 | } 143 | 144 | 145 | void kernel_getbase(uint64_t *retkbase, uint64_t *retkslide) { 146 | *retkbase = 0; 147 | *retkslide = kslide; 148 | } 149 | 150 | uint32_t kernel_read32(uint64_t where) { 151 | uint32_t out = 0; 152 | __kread_buf_tfp0(where, &out, sizeof(uint32_t)); 153 | return out; 154 | } 155 | 156 | uint64_t kernel_read64(uint64_t where) { 157 | uint64_t out = 0; 158 | __kread_buf_tfp0(where, &out, sizeof(uint64_t)); 159 | return out; 160 | } 161 | 162 | void kernel_write32(uint64_t where, uint32_t what) { 163 | uint32_t _what = what; 164 | __kwrite_buf_tfp0(where, (void *)&_what, sizeof(uint32_t)); 165 | } 166 | 167 | void kernel_write64(uint64_t where, uint64_t what) { 168 | uint64_t _what = what; 169 | __kwrite_buf_tfp0(where, (void *)&_what, sizeof(uint64_t)); 170 | } 171 | 172 | void kernel_write(uint64_t where, const void *buf, size_t size) { 173 | __kwrite_buf_tfp0(where, buf, size); 174 | } 175 | 176 | void kernel_read(uint64_t where, void *buf, size_t size) { 177 | __kread_buf_tfp0(where, buf, size); 178 | } 179 | 180 | int rw_prov_init() { 181 | kern_return_t ret = init_tfp0(); 182 | if (ret != KERN_SUCCESS) { 183 | return ret; 184 | } 185 | ret = init_kslide(); 186 | if (ret != KERN_SUCCESS) { 187 | return ret; 188 | } 189 | return ret; 190 | } -------------------------------------------------------------------------------- /rw_wrap/rw_wrap.cpp: -------------------------------------------------------------------------------- 1 | #include "../rw_prov/rw_prov.h" 2 | 3 | #include 4 | #include "rw_wrap.hpp" 5 | #include 6 | #include 7 | #define FMT_HEADER_ONLY 8 | #include 9 | //#include 10 | 11 | #define MIN_KERNEL_ADDRESS 0xffffff0000000000 12 | #define MAX_KERNEL_ADDRESS 0xfffffffff0000000 13 | 14 | void check_addr_valid(addr_t addr, size_t size) { 15 | //fmt::print("kernel access: {:#x}\n", addr); 16 | if (addr < MIN_KERNEL_ADDRESS || addr + size < MIN_KERNEL_ADDRESS || addr + size > MAX_KERNEL_ADDRESS) { 17 | throw std::out_of_range(fmt::format("kernel read out-of-range: {:#x}", addr)); 18 | } 19 | } 20 | 21 | constexpr int t1sz_boot = 25; 22 | constexpr addr_t PACMASK = ~((1ULL << (64U - t1sz_boot)) - 1U); 23 | uint64_t kxpacd(uint64_t pacPtr) { 24 | if(t1sz_boot != 0) { 25 | pacPtr |= PACMASK; 26 | } 27 | return pacPtr; 28 | } 29 | 30 | 31 | addr_t remote_addr_processor_impl(addr_t addr) { 32 | if (!addr) { 33 | return addr; 34 | } 35 | if (!(addr & PACMASK)) { 36 | throw std::out_of_range(fmt::format("kernel got out-of-range pointer: {:#x}", addr)); 37 | } 38 | return kxpacd(addr); 39 | } 40 | 41 | addr_t remote_addr_reader_impl(addr_t addr) { 42 | check_addr_valid(addr, sizeof(addr_t)); 43 | addr_t ret = kernel_read64(addr); 44 | return remote_addr_processor_impl(ret); 45 | } 46 | 47 | void remote_addr_writer_impl(addr_t addr, addr_t val) { 48 | check_addr_valid(addr, sizeof(addr_t)); 49 | //addr_t pacaddr = kxpacd(addr); 50 | //kernel_write64(pacaddr, val); 51 | kernel_write64(addr, val); 52 | } 53 | 54 | void remote_reader_impl(addr_t addr, void *buf, size_t size) { 55 | check_addr_valid(addr, size); 56 | kernel_read(addr, buf, size); 57 | } 58 | 59 | void remote_writer_impl(addr_t addr, const void *buf, size_t size) { 60 | check_addr_valid(addr, size); 61 | kernel_write(addr, buf, size); 62 | } 63 | 64 | void prepare_rw_wrap(KernInfraContext *kerninfra_context) { 65 | kerninfra_context->remote_reader = remote_reader_impl, 66 | kerninfra_context->remote_writer = remote_writer_impl, 67 | kerninfra_context->addr_reader = remote_addr_reader_impl, 68 | kerninfra_context->addr_writer = remote_addr_writer_impl, 69 | kerninfra_context->addr_processor = remote_addr_processor_impl, 70 | 0; 71 | return; 72 | } 73 | -------------------------------------------------------------------------------- /rw_wrap/rw_wrap.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | typedef uint64_t addr_t; 11 | void prepare_rw_wrap(KernInfraContext *kerninfra_context); 12 | 13 | 14 | #define BADADDR (addr_t)(-1) 15 | 16 | // extern void (*remote_reader)(addr_t addr, void *buf, size_t size); 17 | // extern void (*remote_writer)(addr_t addr, const void *buf, size_t size); 18 | // extern addr_t (*addr_reader)(addr_t addr); 19 | // extern void (*addr_writer)(addr_t addr, addr_t val); 20 | // extern addr_t (*addr_processor)(addr_t addr); 21 | 22 | template 23 | struct RemotePointer {}; 24 | 25 | template 26 | struct RemotePointer { 27 | std::string m_name; 28 | addr_t m_pointer; 29 | P &m_parent; 30 | addr_t m_off; 31 | 32 | RemotePointer(P &parent, addr_t off, std::string name) : m_name(name), m_pointer(BADADDR), m_parent(parent), m_off(off) {} 33 | 34 | addr_t addr() { 35 | if (m_pointer == BADADDR) { 36 | //printf("kread(parent.addr() + m_off)\n"); 37 | //m_pointer = addr_reader(m_parent.addr() + m_off); 38 | m_pointer = m_parent.load_addr() + m_off; 39 | return m_pointer; 40 | } else { 41 | return m_pointer; 42 | } 43 | } 44 | 45 | addr_t load() { 46 | addr_t ret; 47 | kerninfra_context.remote_reader(addr(), &ret, sizeof(ret)); 48 | return ret; 49 | //return addr_reader(addr()); 50 | } 51 | 52 | addr_t load_addr() { 53 | // addr_t ret; 54 | // remote_reader(addr(), &ret, sizeof(ret)); 55 | // return ret; 56 | return kerninfra_context.addr_reader(addr()); 57 | } 58 | 59 | void store(addr_t val) { 60 | kerninfra_context.addr_writer(addr(), val); 61 | } 62 | }; 63 | 64 | template 65 | struct RemotePointer { 66 | std::string m_name; 67 | addr_t m_pointer; 68 | 69 | // for root pointer, we ignore pac 70 | RemotePointer(addr_t pointer, std::string name = "root") : m_name(name), m_pointer(pointer) {} 71 | 72 | addr_t addr() { 73 | return m_pointer; 74 | } 75 | 76 | addr_t load() { 77 | return addr(); 78 | } 79 | 80 | addr_t load_addr() { 81 | return addr(); 82 | } 83 | }; 84 | 85 | 86 | template 87 | struct RemoteSimpleType : public RemotePointer { 88 | using RemotePointer::RemotePointer; 89 | using RemotePointer::addr; 90 | T load() { 91 | auto ret = T(); 92 | kerninfra_context.remote_reader(addr(), &ret, sizeof(ret)); 93 | return ret; 94 | } 95 | 96 | void store(T val) { 97 | kerninfra_context.remote_writer(addr(), &val, sizeof(val)); 98 | } 99 | }; 100 | 101 | template 102 | struct RemoteRawPointer : public RemoteSimpleType { 103 | using RemoteSimpleType::RemoteSimpleType; 104 | using RemotePointer::addr; 105 | using RemoteSimpleType::load; 106 | using RemoteSimpleType::store; 107 | 108 | addr_t load_addr() { 109 | // addr_t ret; 110 | // remote_reader(addr(), &ret, sizeof(ret)); 111 | // return ret; 112 | return kerninfra_context.addr_reader(addr()); 113 | } 114 | }; 115 | 116 | template 117 | using kuint8_t = RemoteSimpleType; 118 | 119 | template 120 | using kuint16_t = RemoteSimpleType; 121 | 122 | template 123 | using kuint32_t = RemoteSimpleType; 124 | 125 | template 126 | using kuint64_t = RemoteSimpleType; 127 | 128 | template 129 | using kpointer_t = RemoteRawPointer; 130 | 131 | #define THISTYPE typename std::remove_pointer::type 132 | 133 | #define REMOTETYPE(tpname, tpdef) \ 134 | template \ 135 | struct tpname : public RemotePointer { \ 136 | using RemotePointer::RemotePointer; \ 137 | tpdef; \ 138 | }; \ 139 | using tpname##_p = tpname<>; 140 | 141 | #define REMOTE_FIELD(fieldtype, fieldname, offs) \ 142 | auto fieldname() { \ 143 | addr_t off = -1; \ 144 | offs; \ 145 | if (off == -1) throw std::runtime_error(std::string("failed to get off for ") + this->m_name + "(" #fieldtype ")" #fieldname); \ 146 | return fieldtype(*this, off, #fieldname); \ 147 | } 148 | --------------------------------------------------------------------------------